init
This commit is contained in:
@@ -1 +1 @@
|
||||
pub struct User {}
|
||||
// Ce fichier est conservé pour la structure.
|
||||
|
||||
+25
-6
@@ -1,10 +1,29 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CreateUserRequest {}
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct CreateUserRequest {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
pub pub_key: String,
|
||||
pub is_superuser: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UpdateUserRequest {}
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct UpdateUserRequest {
|
||||
pub username: String,
|
||||
pub pub_key: String,
|
||||
pub is_superuser: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UserResponse {}
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct UserResponse {
|
||||
pub id: Uuid,
|
||||
pub username: String,
|
||||
pub pub_key: String,
|
||||
pub is_superuser: bool,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
+172
-12
@@ -1,22 +1,182 @@
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
use crate::core::state::AppState;
|
||||
use crate::http::context::Superuser;
|
||||
use crate::http::error::HTTPError;
|
||||
use crate::routes::user::dto::{CreateUserRequest, UpdateUserRequest, UserResponse};
|
||||
use crate::routes::user::mapper;
|
||||
use axum::{
|
||||
Json,
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub async fn get_all() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
/// Liste tous les utilisateurs
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/users",
|
||||
responses(
|
||||
(status = 200, description = "Liste des utilisateurs récupérée avec succès", body = [UserResponse]),
|
||||
(status = 401, description = "Non autorisé"),
|
||||
(status = 403, description = "Interdit"),
|
||||
(status = 500, description = "Erreur interne du serveur")
|
||||
),
|
||||
tag = "Users"
|
||||
)]
|
||||
pub async fn get_all(
|
||||
_admin: Superuser,
|
||||
State(state): State<AppState>,
|
||||
) -> Result<Json<Vec<UserResponse>>, HTTPError> {
|
||||
let users = state.repositories.user.get_all().await?;
|
||||
Ok(Json(
|
||||
users
|
||||
.into_iter()
|
||||
.map(mapper::user_model_to_user_response)
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn get_by_id() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
/// Récupère un utilisateur par son ID
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/users/{id}",
|
||||
responses(
|
||||
(status = 200, description = "Utilisateur trouvé", body = UserResponse),
|
||||
(status = 401, description = "Non autorisé"),
|
||||
(status = 403, description = "Interdit"),
|
||||
(status = 404, description = "Utilisateur non trouvé"),
|
||||
(status = 500, description = "Erreur interne du serveur")
|
||||
),
|
||||
params(
|
||||
("id" = Uuid, Path, description = "ID de l'utilisateur")
|
||||
),
|
||||
tag = "Users"
|
||||
)]
|
||||
pub async fn get_by_id(
|
||||
_admin: Superuser,
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<Json<UserResponse>, HTTPError> {
|
||||
let user = state
|
||||
.repositories
|
||||
.user
|
||||
.get_by_id(id)
|
||||
.await?
|
||||
.ok_or(HTTPError::NotFound)?;
|
||||
|
||||
Ok(Json(mapper::user_model_to_user_response(user)))
|
||||
}
|
||||
|
||||
pub async fn create() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
/// Crée un nouvel utilisateur
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/users",
|
||||
request_body = CreateUserRequest,
|
||||
responses(
|
||||
(status = 201, description = "Utilisateur créé avec succès", body = UserResponse),
|
||||
(status = 400, description = "Requête invalide"),
|
||||
(status = 401, description = "Non autorisé"),
|
||||
(status = 403, description = "Interdit"),
|
||||
(status = 500, description = "Erreur interne du serveur")
|
||||
),
|
||||
tag = "Users"
|
||||
)]
|
||||
pub async fn create(
|
||||
_admin: Superuser,
|
||||
State(state): State<AppState>,
|
||||
Json(payload): Json<CreateUserRequest>,
|
||||
) -> Result<(StatusCode, Json<UserResponse>), HTTPError> {
|
||||
// Vérifier si le nom d'utilisateur existe déjà
|
||||
if state
|
||||
.repositories
|
||||
.user
|
||||
.username_exists(&payload.username)
|
||||
.await?
|
||||
{
|
||||
return Err(HTTPError::BadRequest("Username already exists".to_string()));
|
||||
}
|
||||
|
||||
let active_model = mapper::create_request_to_am(payload)
|
||||
.map_err(|e| HTTPError::InternalServerError(e.to_string()))?;
|
||||
|
||||
let user = state.repositories.user.create(active_model).await?;
|
||||
Ok((
|
||||
StatusCode::CREATED,
|
||||
Json(mapper::user_model_to_user_response(user)),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn update() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
/// Met à jour un utilisateur existant
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/users/{id}",
|
||||
request_body = UpdateUserRequest,
|
||||
responses(
|
||||
(status = 200, description = "Utilisateur mis à jour avec succès", body = UserResponse),
|
||||
(status = 401, description = "Non autorisé"),
|
||||
(status = 403, description = "Interdit"),
|
||||
(status = 404, description = "Utilisateur non trouvé"),
|
||||
(status = 500, description = "Erreur interne du serveur")
|
||||
),
|
||||
params(
|
||||
("id" = Uuid, Path, description = "ID de l'utilisateur")
|
||||
),
|
||||
tag = "Users"
|
||||
)]
|
||||
pub async fn update(
|
||||
_admin: Superuser,
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
Json(payload): Json<UpdateUserRequest>,
|
||||
) -> Result<Json<UserResponse>, HTTPError> {
|
||||
// Vérifier l'existence
|
||||
let user = state
|
||||
.repositories
|
||||
.user
|
||||
.get_by_id(id)
|
||||
.await?
|
||||
.ok_or(HTTPError::NotFound)?;
|
||||
|
||||
// On pourrait aussi vérifier si le nouveau username existe déjà s'il a changé
|
||||
if payload.username != user.username
|
||||
&& state
|
||||
.repositories
|
||||
.user
|
||||
.username_exists(&payload.username)
|
||||
.await?
|
||||
{
|
||||
return Err(HTTPError::BadRequest("Username already exists".to_string()));
|
||||
}
|
||||
|
||||
let active_model = mapper::update_request_to_am(user.id, payload);
|
||||
let user = state.repositories.user.update(active_model).await?;
|
||||
|
||||
Ok(Json(mapper::user_model_to_user_response(user)))
|
||||
}
|
||||
|
||||
pub async fn delete() -> impl IntoResponse {
|
||||
StatusCode::NOT_IMPLEMENTED
|
||||
/// Supprime un utilisateur
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/users/{id}",
|
||||
responses(
|
||||
(status = 204, description = "Utilisateur supprimé avec succès"),
|
||||
(status = 401, description = "Non autorisé"),
|
||||
(status = 403, description = "Interdit"),
|
||||
(status = 404, description = "Utilisateur non trouvé"),
|
||||
(status = 500, description = "Erreur interne du serveur")
|
||||
),
|
||||
params(
|
||||
("id" = Uuid, Path, description = "ID de l'utilisateur")
|
||||
),
|
||||
tag = "Users"
|
||||
)]
|
||||
pub async fn delete(
|
||||
_admin: Superuser,
|
||||
State(state): State<AppState>,
|
||||
Path(id): Path<Uuid>,
|
||||
) -> Result<StatusCode, HTTPError> {
|
||||
if state.repositories.user.delete(id).await? {
|
||||
Ok(StatusCode::NO_CONTENT)
|
||||
} else {
|
||||
Err(HTTPError::NotFound)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,41 @@
|
||||
use super::{domain::User, dto::UserResponse};
|
||||
use crate::auth::password::hash_password;
|
||||
use crate::models::user;
|
||||
use crate::routes::user::dto::{CreateUserRequest, UpdateUserRequest, UserResponse};
|
||||
use anyhow::Result as AnyResult;
|
||||
use sea_orm::{NotSet, Set};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub fn to_response(_user: User) -> UserResponse {
|
||||
todo!()
|
||||
pub fn user_model_to_user_response(model: user::Model) -> UserResponse {
|
||||
UserResponse {
|
||||
id: model.id,
|
||||
username: model.username,
|
||||
pub_key: model.pub_key,
|
||||
is_superuser: model.is_superuser,
|
||||
created_at: model.created_at,
|
||||
updated_at: model.updated_at,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_request_to_am(payload: CreateUserRequest) -> AnyResult<user::ActiveModel> {
|
||||
Ok(user::ActiveModel {
|
||||
id: Set(Uuid::new_v4()),
|
||||
username: Set(payload.username),
|
||||
password: Set(hash_password(&payload.password)?),
|
||||
pub_key: Set(payload.pub_key),
|
||||
is_superuser: Set(payload.is_superuser),
|
||||
created_at: Set(chrono::Utc::now()),
|
||||
updated_at: Set(chrono::Utc::now()),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn update_request_to_am(id: Uuid, payload: UpdateUserRequest) -> user::ActiveModel {
|
||||
user::ActiveModel {
|
||||
id: Set(id),
|
||||
username: Set(payload.username),
|
||||
password: NotSet, // On ne change pas le password via PUT général
|
||||
pub_key: Set(payload.pub_key),
|
||||
is_superuser: Set(payload.is_superuser),
|
||||
updated_at: Set(chrono::Utc::now()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
use crate::core::state::AppState;
|
||||
use axum::{routing::get, Router};
|
||||
|
||||
use super::handlers;
|
||||
|
||||
pub fn router() -> Router {
|
||||
pub fn router() -> Router<AppState> {
|
||||
Router::new()
|
||||
.route("/users", get(handlers::get_all).post(handlers::create))
|
||||
.route(
|
||||
|
||||
@@ -1,21 +1 @@
|
||||
use super::domain::User;
|
||||
|
||||
pub async fn find_all() -> Vec<User> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn find_by_id(_id: u64) -> Option<User> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn create(_user: User) -> User {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update(_id: u64, _user: User) -> Option<User> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(_id: u64) -> bool {
|
||||
todo!()
|
||||
}
|
||||
// Ce fichier est conservé pour la structure mais la logique est directement dans les handlers.
|
||||
|
||||
Reference in New Issue
Block a user