feat: v0.3.0 — standardize codebase, centralize infra, merge QR into CMS
- Enforce axum best practices across all 13 workspace crates (max 200 LOC/file, no comments, no unwrap, clean architecture) - Fix domain→infrastructure dependency inversions in imphnen-iam and imphnen-dimentorin - Extract imphnen-storage (MinIO) and imphnen-email (Lettre) as standalone crates - Centralize all config in ENV struct: CDN_URL, CORS_ALLOWED_ORIGINS - Centralize SMTP through imphnen-email; remove dead HackathonConfig - Centralize database: QR crate now shares main DB pool (single DATABASE_URL) - Rename QR users table to qr_users to avoid collision with main users table - Merge imphnen-qr into imphnen-cms/src/qr (13 crates, down from 14) - Restructure imphnen-hackathon flat modules into clean architecture - Remove all stale env vars from .env.example (SurrealDB, QR_JWT, Hackathon infra) - Fix Dockerfile to include all current workspace crates - Bump all crate versions 0.2.0 → 0.3.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
2ae43b3bcc
commit
331a4a4e88
@@ -0,0 +1,69 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::{
|
||||
body::Body,
|
||||
extract::Request,
|
||||
middleware::Next,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use imphnen_libs::decode_access_token;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::PgPool;
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct QrAuthUser {
|
||||
pub user_id: Uuid,
|
||||
pub role: String,
|
||||
}
|
||||
|
||||
pub async fn qr_auth_middleware(
|
||||
axum::Extension(pool): axum::Extension<Arc<PgPool>>,
|
||||
mut request: Request<Body>,
|
||||
next: Next,
|
||||
) -> Result<Response, Response> {
|
||||
let auth_header = request
|
||||
.headers()
|
||||
.get("Authorization")
|
||||
.and_then(|h| h.to_str().ok())
|
||||
.ok_or_else(|| {
|
||||
(StatusCode::UNAUTHORIZED, "Missing Authorization header").into_response()
|
||||
})?;
|
||||
|
||||
let token = auth_header.strip_prefix("Bearer ").ok_or_else(|| {
|
||||
(
|
||||
StatusCode::UNAUTHORIZED,
|
||||
"Invalid Authorization header format",
|
||||
)
|
||||
.into_response()
|
||||
})?;
|
||||
|
||||
let token_data = decode_access_token(token).map_err(|_| {
|
||||
(StatusCode::UNAUTHORIZED, "Invalid or expired token").into_response()
|
||||
})?;
|
||||
|
||||
let user_id = Uuid::parse_str(&token_data.claims.user_id).map_err(|_| {
|
||||
(StatusCode::UNAUTHORIZED, "Invalid user ID in token").into_response()
|
||||
})?;
|
||||
|
||||
let _ = sqlx::query(
|
||||
"INSERT INTO qr_users (id, email, name, role, provider) VALUES ($1, $2, $2, 'user', 'external') ON CONFLICT (id) DO NOTHING"
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(&token_data.claims.sub)
|
||||
.execute(pool.as_ref())
|
||||
.await;
|
||||
|
||||
let role: String = sqlx::query_scalar("SELECT role FROM qr_users WHERE id = $1")
|
||||
.bind(user_id)
|
||||
.fetch_optional(pool.as_ref())
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.unwrap_or_else(|| "user".to_string());
|
||||
|
||||
request
|
||||
.extensions_mut()
|
||||
.insert(QrAuthUser { user_id, role });
|
||||
Ok(next.run(request).await)
|
||||
}
|
||||
Reference in New Issue
Block a user