diff --git a/apps/api/src/routes/api/openapi.rs b/apps/api/src/routes/api/openapi.rs index d7c4d9d..f37db25 100644 --- a/apps/api/src/routes/api/openapi.rs +++ b/apps/api/src/routes/api/openapi.rs @@ -2,6 +2,7 @@ pub mod tag { /// Health tag constant pub const HEALTH_TAG: &str = "Health"; pub const AUTH_TAG: &str = "Authentication"; + pub const USER_TAG: &str = "User"; } #[derive(utoipa::OpenApi)] @@ -11,16 +12,21 @@ pub mod tag { // Authentication paths crate::routes::api::auth::login::login, crate::routes::api::auth::init_admin::init_admin, + // User management paths + crate::routes::api::restricted::user::me::get_user_info, ), components( schemas(crate::routes::api::health::info::HealthInfo), // Authentication schemas schemas(crate::routes::api::auth::login::LoginRequest), schemas(crate::routes::api::auth::init_admin::AdminInitRequest), + // User management schemas + schemas(crate::routes::api::restricted::user::me::UserInfo), ), tags( (name = tag::HEALTH_TAG, description = "Health information API"), - (name = tag::AUTH_TAG, description = "Authentication API") + (name = tag::AUTH_TAG, description = "Authentication API"), + (name = tag::USER_TAG, description = "User management API") ) )] pub struct ApiDoc; diff --git a/apps/api/src/routes/api/restricted.rs b/apps/api/src/routes/api/restricted.rs index a5ec9f4..17be841 100644 --- a/apps/api/src/routes/api/restricted.rs +++ b/apps/api/src/routes/api/restricted.rs @@ -1,3 +1,5 @@ +pub mod user; + use std::sync::Arc; use axum::Router; @@ -6,8 +8,7 @@ use crate::{middlewares::require_auth::require_auth, routes::AppState}; pub fn get_restricted_router(state: Arc) -> Router { Router::new() - // - // + .nest("/user", user::get_user_router(state.clone())) .layer(axum::middleware::from_fn_with_state( state.clone(), require_auth, diff --git a/apps/api/src/routes/api/restricted/user.rs b/apps/api/src/routes/api/restricted/user.rs new file mode 100644 index 0000000..77e1167 --- /dev/null +++ b/apps/api/src/routes/api/restricted/user.rs @@ -0,0 +1,13 @@ +pub mod me; + +use std::sync::Arc; + +use axum::Router; + +use crate::routes::AppState; + +pub fn get_user_router(state: Arc) -> Router { + Router::new() + .route("/me", axum::routing::get(me::get_user_info)) + .with_state(state) +} diff --git a/apps/api/src/routes/api/restricted/user/me.rs b/apps/api/src/routes/api/restricted/user/me.rs new file mode 100644 index 0000000..a95d5de --- /dev/null +++ b/apps/api/src/routes/api/restricted/user/me.rs @@ -0,0 +1,64 @@ +use std::sync::Arc; + +use axum::{ + Extension, Json, + extract::State, + http::StatusCode, + response::{IntoResponse, Response}, +}; +use serde::{Deserialize, Serialize}; +use tracing::error; + +use crate::{ + middlewares::request_info::RequestInfo, + routes::{AppState, api::openapi::tag::USER_TAG}, +}; + +/// System health information +#[derive(Serialize, Deserialize, utoipa::ToSchema)] +pub struct UserInfo { + /// User ID + pub id: uuid::Uuid, + /// Username + pub username: String, +} + +/// Get current user information +/// +/// Returns the information of the currently authenticated user. +#[utoipa::path( + get, + path = "/api/user/me", + responses( + (status = 200, description = "User information retrieved successfully", body = UserInfo), + (status = 401, description = "Unauthorized"), + (status = 500, description = "Internal server error"), + ), + tag = USER_TAG, + )] +pub async fn get_user_info( + State(app_state): State>, + request_info: Extension>, +) -> Response { + let user_id = match request_info.user_id { + Some(id) => id, + None => { + error!("User ID not found in request info"); + return (StatusCode::UNAUTHORIZED).into_response(); + } + }; + + match app_state.service.user.get_user_by_id(user_id, None).await { + Ok(user) => { + let user_info = UserInfo { + id: user.id, + username: user.username, + }; + (StatusCode::OK, Json(user_info)).into_response() + } + Err(err) => { + error!("Error fetching user info: {}", err); + (StatusCode::INTERNAL_SERVER_ERROR).into_response() + } + } +} diff --git a/apps/api/swagger.json b/apps/api/swagger.json index dc65ac2..cf7fddc 100644 --- a/apps/api/swagger.json +++ b/apps/api/swagger.json @@ -105,6 +105,34 @@ } } } + }, + "/api/user/me": { + "get": { + "tags": [ + "User" + ], + "summary": "Get current user information", + "description": "Returns the information of the currently authenticated user.", + "operationId": "get_user_info", + "responses": { + "200": { + "description": "User information retrieved successfully", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserInfo" + } + } + } + }, + "401": { + "description": "Unauthorized" + }, + "500": { + "description": "Internal server error" + } + } + } } }, "components": { @@ -183,6 +211,25 @@ "type": "string" } } + }, + "UserInfo": { + "type": "object", + "description": "System health information", + "required": [ + "id", + "username" + ], + "properties": { + "id": { + "type": "string", + "format": "uuid", + "description": "User ID" + }, + "username": { + "type": "string", + "description": "Username" + } + } } } }, @@ -194,6 +241,10 @@ { "name": "Authentication", "description": "Authentication API" + }, + { + "name": "User", + "description": "User management API" } ] } \ No newline at end of file diff --git a/apps/frontend/app/generated/api-client/api-client.ts b/apps/frontend/app/generated/api-client/api-client.ts index 5ebf513..0b47c95 100644 --- a/apps/frontend/app/generated/api-client/api-client.ts +++ b/apps/frontend/app/generated/api-client/api-client.ts @@ -9,6 +9,7 @@ export namespace Schemas { version: string; }; export type LoginRequest = { password: string; username: string }; + export type UserInfo = { id: string; username: string }; // } @@ -41,6 +42,13 @@ export namespace Endpoints { parameters: never; responses: { 200: Schemas.HealthInfo; 404: unknown }; }; + export type get_Get_user_info = { + method: "GET"; + path: "/api/user/me"; + requestFormat: "json"; + parameters: never; + responses: { 200: Schemas.UserInfo; 401: unknown; 500: unknown }; + }; // } @@ -53,6 +61,7 @@ export type EndpointByMethod = { }; get: { "/api/health/info": Endpoints.get_Get_health_info; + "/api/user/me": Endpoints.get_Get_user_info; }; };