feat: migrate imphnen-backend-qr into workspace as imphnen-qr crate

Ports the Go QR campaign overlay service to a self-contained Rust crate
nested at /v1/qr/... in the gateway.

Features:
- Auth: register, login, Google OAuth, JWT refresh (bcrypt compat with Go DB)
- Users: profile management + admin CRUD (list/role/delete)
- Campaigns: create (auto-generates QR PNG via qrcode crate), list,
  activate, delete; process-image endpoint overlays active campaign QR
  onto uploaded images (bottom-right corner, image crate)
- QR pool connects to imphnen_qr database via QR_DATABASE_URL

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
maulanasdqn
2026-04-02 16:03:39 +07:00
co-authored by Claude Sonnet 4.6
parent 5715e75593
commit 4bba182ea3
51 changed files with 1942 additions and 0 deletions
+2
View File
@@ -5,6 +5,8 @@ edition = "2024"
[dependencies]
imphnen-hackathon.workspace = true
imphnen-qr.workspace = true
sqlx.workspace = true
imphnen-iam.workspace = true
imphnen-libs.workspace = true
imphnen-utils.workspace = true
+10
View File
@@ -17,6 +17,7 @@ use imphnen_dimentorin::{
};
use imphnen_gacha::gacha_router;
use imphnen_hackathon::{hackathon_router, HackathonConfig};
use imphnen_qr::{qr_router, QrConfig};
use imphnen_iam::{
auth_public_routes,
permissions_protected_routes,
@@ -45,6 +46,14 @@ pub async fn gateway_service(
let db = state.postgres_connection.conn.clone();
let state_arc = Arc::new(state.clone());
let hackathon_config = Arc::new(HackathonConfig::from_env());
let qr_config = Arc::new(QrConfig::from_env());
let qr_pool = Arc::new(
sqlx::PgPool::connect(
&std::env::var("QR_DATABASE_URL").expect("QR_DATABASE_URL must be set"),
)
.await
.expect("Failed to connect to QR database"),
);
let public_routes = Router::new()
.merge(auth_public_routes(db.clone(), Arc::clone(&state_arc)).layer(from_fn(rate_limiting_middleware)))
@@ -68,6 +77,7 @@ pub async fn gateway_service(
.route("/", get(Redirect::to("/docs")))
.nest("/v1", public_routes.merge(protected_routes))
.nest("/v1/hackathon", hackathon_router(db.clone(), hackathon_config))
.nest("/v1/qr", qr_router(qr_pool, qr_config))
.merge(SwaggerUi::new("/docs").url("/openapi.json", docs_router()))
.layer(cors_middleware())
.layer(from_fn(security_headers_middleware))