koprogo_api/infrastructure/web/handlers/
auth_handlers.rs1use crate::application::dto::{
2 LoginRequest, RefreshTokenRequest, RegisterRequest, SwitchRoleRequest,
3};
4use crate::application::error::AppError;
5use crate::infrastructure::web::{AppState, AuthenticatedUser};
6use actix_web::{get, post, web, HttpRequest, HttpResponse, Responder};
7use validator::Validate;
8
9#[utoipa::path(
10 post,
11 path = "/auth/login",
12 tag = "Auth",
13 summary = "Login",
14 request_body = LoginRequest,
15 responses(
16 (status = 201, description = "Resource created successfully"),
17 (status = 400, description = "Bad Request"),
18 (status = 404, description = "Not Found"),
19 (status = 500, description = "Internal Server Error"),
20 ),
21)]
22#[post("/auth/login")]
23pub async fn login(
24 data: web::Data<AppState>,
25 request: web::Json<LoginRequest>,
26) -> Result<HttpResponse, AppError> {
27 request
28 .validate()
29 .map_err(|errors| AppError::Validation(errors.to_string()))?;
30
31 let response = data.auth_use_cases.login(request.into_inner()).await?;
32 Ok(HttpResponse::Ok().json(response))
33}
34
35#[utoipa::path(
36 post,
37 path = "/auth/register",
38 tag = "Auth",
39 summary = "Register",
40 request_body = RegisterRequest,
41 responses(
42 (status = 201, description = "Resource created successfully"),
43 (status = 400, description = "Bad Request"),
44 (status = 404, description = "Not Found"),
45 (status = 500, description = "Internal Server Error"),
46 ),
47)]
48#[post("/auth/register")]
49pub async fn register(
50 data: web::Data<AppState>,
51 request: web::Json<RegisterRequest>,
52) -> impl Responder {
53 if let Err(errors) = request.validate() {
55 return HttpResponse::BadRequest().json(serde_json::json!({
56 "error": "Validation failed",
57 "details": errors.to_string()
58 }));
59 }
60
61 match data.auth_use_cases.register(request.into_inner()).await {
62 Ok(response) => HttpResponse::Created().json(response),
63 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({
64 "error": e
65 })),
66 }
67}
68
69#[utoipa::path(
70 get,
71 path = "/auth/me",
72 tag = "Auth",
73 summary = "Get Current User",
74 responses(
75 (status = 200, description = "Success"),
76 (status = 400, description = "Bad Request"),
77 (status = 404, description = "Not Found"),
78 (status = 500, description = "Internal Server Error"),
79 ),
80)]
81#[get("/auth/me")]
82pub async fn get_current_user(
83 data: web::Data<AppState>,
84 req: HttpRequest,
85) -> Result<HttpResponse, AppError> {
86 let auth_header = req
87 .headers()
88 .get("Authorization")
89 .ok_or(AppError::Unauthorized)?
90 .to_str()
91 .map_err(|_| AppError::Validation("invalid authorization header".to_string()))?;
92
93 let token = auth_header.trim_start_matches("Bearer ").trim();
94
95 let claims = data.auth_use_cases.verify_token(token)?;
96
97 let user_id = uuid::Uuid::parse_str(&claims.sub)
98 .map_err(|e| AppError::Validation(format!("invalid user id in token: {}", e)))?;
99
100 let user = data.auth_use_cases.get_user_by_id(user_id).await?;
101 Ok(HttpResponse::Ok().json(user))
102}
103
104#[utoipa::path(
105 post,
106 path = "/auth/refresh",
107 tag = "Auth",
108 summary = "Refresh Token",
109 request_body = RefreshTokenRequest,
110 responses(
111 (status = 201, description = "Resource created successfully"),
112 (status = 400, description = "Bad Request"),
113 (status = 404, description = "Not Found"),
114 (status = 500, description = "Internal Server Error"),
115 ),
116)]
117#[post("/auth/refresh")]
118pub async fn refresh_token(
119 data: web::Data<AppState>,
120 request: web::Json<RefreshTokenRequest>,
121) -> Result<HttpResponse, AppError> {
122 let response = data
123 .auth_use_cases
124 .refresh_token(request.into_inner())
125 .await?;
126 Ok(HttpResponse::Ok().json(response))
127}
128
129#[utoipa::path(
130 post,
131 path = "/auth/switch-role",
132 tag = "Auth",
133 summary = "Switch Role",
134 request_body = SwitchRoleRequest,
135 responses(
136 (status = 201, description = "Resource created successfully"),
137 (status = 400, description = "Bad Request"),
138 (status = 404, description = "Not Found"),
139 (status = 500, description = "Internal Server Error"),
140 ),
141)]
142#[post("/auth/switch-role")]
143pub async fn switch_role(
144 data: web::Data<AppState>,
145 user: AuthenticatedUser,
146 request: web::Json<SwitchRoleRequest>,
147) -> impl Responder {
148 let payload = request.into_inner();
149
150 match data
151 .auth_use_cases
152 .switch_active_role(user.user_id, payload.role_id)
153 .await
154 {
155 Ok(response) => HttpResponse::Ok().json(response),
156 Err(e) => HttpResponse::BadRequest().json(serde_json::json!({ "error": e })),
157 }
158}