diff --git a/backend/src/handlers/auth.rs b/backend/src/handlers/auth.rs index 38be493..063ddd2 100644 --- a/backend/src/handlers/auth.rs +++ b/backend/src/handlers/auth.rs @@ -304,6 +304,84 @@ pub async fn update_user( Ok(Json(response)) } +pub async fn check_admin_exists( + State(data): State>, +) -> Result)> { + let admin_count = sqlx::query_scalar::<_, i64>(r#"SELECT COUNT(*) FROM users WHERE is_admin = true"#) + .fetch_one(&data.db) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({"status": "error", "message": format!("{:?}", e)})), + ) + })?; + + let has_admin = admin_count > 0; + Ok(Json(json!({"has_admin": has_admin}))) +} + +pub async fn setup_initial_admin( + State(data): State>, + Json(request): Json, +) -> Result)> { + // Check if any admin already exists + let admin_count = sqlx::query_scalar::<_, i64>(r#"SELECT COUNT(*) FROM users WHERE is_admin = true"#) + .fetch_one(&data.db) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({"status": "error", "message": format!("{:?}", e)})), + ) + })?; + + if admin_count > 0 { + return Err(( + StatusCode::BAD_REQUEST, + Json(json!({"status": "error", "message": "Admin user already exists"})), + )); + } + + if request.username.is_empty() || request.pwd.is_empty() { + return Err(( + StatusCode::BAD_REQUEST, + Json(json!({"status": "error", "message": "Missing credential"})), + )); + } + + let argon = Argon2::default(); + let salt = SaltString::generate(&mut OsRng); + let hashed_pwd = match argon.hash_password(request.pwd.clone().as_bytes(), &salt) { + Ok(h) => h.to_string(), + Err(e) => panic!("Error hashing {:}", e), + }; + + let user = sqlx::query("INSERT INTO users (username, pwd, first_name, last_name, is_admin) VALUES ($1, $2, $3, $4, $5)") + .bind(request.username) + .bind(&hashed_pwd) + .bind(request.first_name) + .bind(request.last_name) + .bind(true) + .execute(&data.db) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({"status": "error", "message": format!("{}", e)})), + ) + })?; + + if user.rows_affected() < 1 { + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + Json(json!({"status": "error", "message": "Error creating admin user"})), + )); + } else { + Ok(Json(json!({"status": "success", "result": "Admin user created"}))) + } +} + pub fn filter_user(user: &User) -> FilteredUser { FilteredUser { id: user.id, diff --git a/backend/src/router.rs b/backend/src/router.rs index 96f7c59..908e200 100644 --- a/backend/src/router.rs +++ b/backend/src/router.rs @@ -10,8 +10,8 @@ use crate::{ cookie::validation::{validate_admin, validate_token}, handlers::{ auth::{ - create_user, delete_user, get_current_user, get_user_by_id, get_users, login, logout, - update_user, + check_admin_exists, create_user, delete_user, get_current_user, get_user_by_id, get_users, login, logout, + setup_initial_admin, update_user, }, ticket::{create_ticket, delete_ticket, edit_ticket, get_ticket_by_id, get_tickets}, }, @@ -50,5 +50,7 @@ pub fn create_router(state: Arc) -> Router { Router::new() .merge(protected_routes) .route("/api/login", post(login)) + .route("/api/check-admin", get(check_admin_exists)) + .route("/api/setup-admin", post(setup_initial_admin)) .with_state(state) }