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