nxtgauge-backend-rust/apps/users/src/handlers/config.rs

240 lines
8.2 KiB
Rust
Raw Normal View History

use crate::AppState;
use axum::{
extract::{Path, Query, State},
http::StatusCode,
response::IntoResponse,
routing::{get, post},
Json, Router,
};
use db::models::config::{
ConfigRepository, CreateDashboardConfigPayload, CreateOnboardingConfigPayload,
CreateRuntimeConfigPayload,
};
use db::models::user::UserRepository;
use serde::Deserialize;
use uuid::Uuid;
pub fn onboarding_router() -> Router<AppState> {
Router::new()
.route("/", get(list_onboarding_configs).post(create_onboarding_config))
.route("/{role_id}", get(get_active_onboarding_config))
.route("/by-key/{role_key}", get(get_onboarding_config_by_key))
}
pub fn dashboard_router() -> Router<AppState> {
Router::new()
.route("/", get(list_dashboard_configs).post(create_dashboard_config))
.route("/{role_id}", get(get_active_dashboard_config))
.route("/by-key/{role_key}", get(get_dashboard_config_by_key))
}
pub fn runtime_router() -> Router<AppState> {
Router::new()
.route("/", get(get_my_runtime_config).post(create_runtime_config))
.route("/{role_id}", get(get_active_runtime_config))
}
async fn get_my_runtime_config(
auth: contracts::auth_middleware::AuthUser,
State(state): State<AppState>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
let role_key = auth.claims.active_role.clone();
let config = ConfigRepository::get_active_runtime_by_role_key(&state.pool, &role_key)
.await
.map_err(|e| match e {
sqlx::Error::RowNotFound => (
StatusCode::NOT_FOUND,
format!("No runtime config found for role {}", role_key),
),
e => (StatusCode::INTERNAL_SERVER_ERROR, format!("Database error: {}", e)),
})?;
// Fetch live user data to merge into the response so the `user` field is always fresh.
let user = UserRepository::get_by_id(&state.pool, auth.user_id)
.await
.map_err(|_| (StatusCode::UNAUTHORIZED, "User not found".to_string()))?;
let roles = UserRepository::get_user_role_keys(&state.pool, auth.user_id)
.await
.unwrap_or_default();
// Merge the stored config_json with live user data.
// `config_json` holds role/modules/flags/permissions; we add the `user` sub-object.
let mut response = config.config_json.clone();
if let Some(obj) = response.as_object_mut() {
obj.insert(
"user".to_string(),
serde_json::json!({
"id": user.id.to_string(),
"full_name": user.full_name.unwrap_or_default(),
"email": user.email,
"roles": roles,
"active_role": role_key,
}),
);
// Ensure role is always set from the JWT active_role (authoritative)
obj.insert("role".to_string(), serde_json::Value::String(role_key));
}
Ok((StatusCode::OK, Json(response)))
}
async fn create_onboarding_config(
State(state): State<AppState>,
Json(payload): Json<CreateOnboardingConfigPayload>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
match ConfigRepository::create_onboarding_config(&state.pool, payload).await {
Ok(config) => Ok((StatusCode::CREATED, Json(config))),
Err(e) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Database error: {}", e),
)),
}
}
async fn get_active_onboarding_config(
State(state): State<AppState>,
Path(role_id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
match ConfigRepository::get_active_onboarding_config(&state.pool, role_id).await {
Ok(config) => Ok((StatusCode::OK, Json(config))),
Err(sqlx::Error::RowNotFound) => Err((
StatusCode::NOT_FOUND,
"Active onboarding config not found".to_string(),
)),
Err(e) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Database error: {}", e),
)),
}
}
async fn list_onboarding_configs(
State(state): State<AppState>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
match ConfigRepository::get_all_onboarding_configs(&state.pool).await {
Ok(configs) => Ok((StatusCode::OK, Json(configs))),
Err(e) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Database error: {}", e),
)),
}
}
async fn create_dashboard_config(
State(state): State<AppState>,
Json(payload): Json<CreateDashboardConfigPayload>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
match ConfigRepository::create_dashboard_config(&state.pool, payload).await {
Ok(config) => Ok((StatusCode::CREATED, Json(config))),
Err(e) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Database error: {}", e),
)),
}
}
#[derive(Deserialize)]
struct DashboardQuery {
audience: String,
}
async fn get_active_dashboard_config(
State(state): State<AppState>,
Path(role_id): Path<Uuid>,
Query(query): Query<DashboardQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
match ConfigRepository::get_active_dashboard_config(&state.pool, role_id, &query.audience).await {
Ok(config) => Ok((StatusCode::OK, Json(config))),
Err(sqlx::Error::RowNotFound) => Err((
StatusCode::NOT_FOUND,
"Active dashboard config not found".to_string(),
)),
Err(e) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Database error: {}", e),
)),
}
}
async fn list_dashboard_configs(
State(state): State<AppState>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
match ConfigRepository::get_all_dashboard_configs(&state.pool).await {
Ok(configs) => Ok((StatusCode::OK, Json(configs))),
Err(e) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Database error: {}", e),
)),
}
}
async fn create_runtime_config(
State(state): State<AppState>,
Json(payload): Json<CreateRuntimeConfigPayload>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
match ConfigRepository::create_runtime_config(&state.pool, payload).await {
Ok(config) => Ok((StatusCode::CREATED, Json(config))),
Err(e) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Database error: {}", e),
)),
}
}
async fn get_active_runtime_config(
State(state): State<AppState>,
Path(role_id): Path<Uuid>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
match ConfigRepository::get_active_runtime_config(&state.pool, role_id).await {
Ok(config) => Ok((StatusCode::OK, Json(config))),
Err(sqlx::Error::RowNotFound) => Err((
StatusCode::NOT_FOUND,
"Active runtime config not found".to_string(),
)),
Err(e) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Database error: {}", e),
)),
}
}
async fn get_onboarding_config_by_key(
State(state): State<AppState>,
Path(role_key): Path<String>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
match ConfigRepository::get_active_onboarding_by_role_key(&state.pool, &role_key).await {
Ok(config) => Ok((StatusCode::OK, Json(config))),
Err(sqlx::Error::RowNotFound) => Err((
StatusCode::NOT_FOUND,
format!("Active onboarding config for role '{}' not found", role_key),
)),
Err(e) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Database error: {}", e),
)),
}
}
async fn get_dashboard_config_by_key(
State(state): State<AppState>,
Path(role_key): Path<String>,
Query(query): Query<DashboardQuery>,
) -> Result<impl IntoResponse, (StatusCode, String)> {
match ConfigRepository::get_active_dashboard_by_role_key(&state.pool, &role_key, &query.audience).await {
Ok(config) => Ok((StatusCode::OK, Json(config))),
Err(sqlx::Error::RowNotFound) => Err((
StatusCode::NOT_FOUND,
format!("Active dashboard config for role '{}' not found", role_key),
)),
Err(e) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
format!("Database error: {}", e),
)),
}
}