ROUTES_MOD

Constant ROUTES_MOD 

Source
pub const ROUTES_MOD: &str = r#"use crate::auth::{authorize_user, hash_password};
use crate::guards::AuthClaims;
use crate::models::{ErrorResponse, SuccessResponse, UserInfo};
use crate::models::{LoginCredentials, RegistrationCredentials, User, UserDocument};
use crate::repositories::UserRepository;

use rocket::http::Status;
use rocket::http::{Cookie, CookieJar, SameSite};
use rocket::serde::json::Json;
use rocket::{State, delete, get, post, put, routes};

use std::sync::Arc;

/// Registers a new user.
#[post("/register", data = "<credentials>")]
pub async fn register(
    repo: &State<Arc<UserRepository>>,
    credentials: Json<RegistrationCredentials>,
) -> Result<Json<SuccessResponse>, Json<ErrorResponse>> {
    if let Ok(Some(_)) = repo.get_user_by_email(&credentials.email).await {
        return Err(Json(ErrorResponse {
            status: Status::Conflict.code,
            message: "A user with this email already exists".to_string(),
        }));
    }

    let hashed_password = match hash_password(credentials.password.clone()) {
        Ok(hash) => hash,
        Err(_) => {
            return Err(Json(ErrorResponse {
                status: Status::InternalServerError.code,
                message: "Something went wrong, please try again later".to_string(),
            }));
        }
    };

    let _ = match repo
        .create_user(&credentials.username, &credentials.email, &hashed_password)
        .await
    {
        Ok(user) => user,
        Err(_) => {
            return Err(Json(ErrorResponse {
                status: Status::InternalServerError.code,
                message: "Failed to register account".to_string(),
            }));
        }
    };

    Ok(Json(SuccessResponse {
        status: Status::Ok.code,
        message: "User registered successfully".to_string(),
    }))
}

/// Authenticates a user and sets an authentication cookie.
#[post("/login", data = "<credentials>")]
pub async fn login(
    repo: &State<Arc<UserRepository>>,
    credentials: Json<LoginCredentials>,
    cookies: &CookieJar<'_>,
) -> Result<Json<SuccessResponse>, Json<ErrorResponse>> {
    let user_document = match repo.get_user_by_email(&credentials.email).await {
        Ok(Some(user_document)) => user_document,
        Ok(None) => {
            return Err(Json(ErrorResponse {
                status: Status::Unauthorized.code,
                message: "Invalid email or password".to_string(),
            }));
        }
        Err(_) => {
            return Err(Json(ErrorResponse {
                status: Status::InternalServerError.code,
                message: "Something went wrong, please try again later".to_string(),
            }));
        }
    };

    let user = User {
        id: user_document.id.to_string(),
        username: user_document.username.to_string(),
        email: user_document.email.clone(),
        password: user_document.password.clone(),
        created_at: user_document.created_at.to_rfc3339(),
    };

    let token = match authorize_user(&user, &credentials).await {
        Ok(token) => token,
        Err(_) => {
            return Err(Json(ErrorResponse {
                status: Status::Unauthorized.code,
                message: "Invalid email or password".to_string(),
            }));
        }
    };

    // Set the token cookie (HTTP-only, secure)
    #[allow(deprecated)]
    let cookie = Cookie::build(("auth_token", token.clone()))
        .http_only(true)
        .secure(false) // Ensure your app uses HTTPS
        .same_site(SameSite::Lax)
        .path("/")
        .finish();

    cookies.add(cookie);

    Ok(Json(SuccessResponse {
        status: Status::Ok.code,
        message: "Login successful".to_string(),
    }))
}

/// Logs out the current user by removing the authentication cookie.
#[post("/logout")]
pub fn logout(cookies: &CookieJar<'_>) -> Json<SuccessResponse> {
    cookies.remove(Cookie::build(("auth_token", "")).path("/").build());
    Json(SuccessResponse {
        status: 200,
        message: "Logged out successfully".to_string(),
    })
}

/// Retrieves a single user by ID (requires authentication).
#[get("/users/<id>")]
pub async fn get_user(
    _auth: AuthClaims,
    repo: &State<Arc<UserRepository>>,
    id: &str,
) -> Result<Json<UserDocument>, Json<ErrorResponse>> {
    let user = match repo.get_user_by_id(&id).await {
        Ok(Some(user)) => user,
        Ok(None) => {
            return Err(Json(ErrorResponse {
                status: Status::NotFound.code,
                message: "User not found".to_string(),
            }));
        }
        Err(_) => {
            return Err(Json(ErrorResponse {
                status: Status::InternalServerError.code,
                message: "Something went wrong, please try again later".to_string(),
            }));
        }
    };

    Ok(Json(user))
}

/// Retrieves a single user by email (requires authentication).
#[get("/user/<email>")]
pub async fn get_user_by_email(
    _auth: AuthClaims,
    repo: &State<Arc<UserRepository>>,
    email: &str,
) -> Result<Json<UserInfo>, Json<ErrorResponse>> {
    let user = match repo.get_user_by_email(&email).await {
        Ok(Some(user)) => user,
        Ok(None) => {
            return Err(Json(ErrorResponse {
                status: Status::NotFound.code,
                message: "User not found".to_string(),
            }));
        }
        Err(_) => {
            return Err(Json(ErrorResponse {
                status: Status::InternalServerError.code,
                message: "Something went wrong, please try again later".to_string(),
            }));
        }
    };

    Ok(Json(UserInfo {
        id: user.id.to_string(),
        username: user.username,
        email: user.email,
        created_at: user.created_at.to_string(),
    }))
}

/// Updates an existing user's information by ID (requires authentication).
#[put("/update/<id>", data = "<credentials>")]
pub async fn update_user(
    _auth: AuthClaims,
    repo: &State<Arc<UserRepository>>,
    id: &str,
    credentials: Json<RegistrationCredentials>,
) -> Result<Json<UserDocument>, Json<ErrorResponse>> {
    // Check if the email is already in use by another user
    if let Ok(Some(existing_user)) = repo.get_user_by_email(&credentials.email).await {
        // If the email exists and it's not the user being updated
        if existing_user.id.to_string() != id {
            return Err(Json(ErrorResponse {
                status: Status::Conflict.code,
                message: "A user with this email already exists".to_string(),
            }));
        }
    }

    let hashed_password = match hash_password(credentials.password.clone()) {
        Ok(hash) => hash,
        Err(_) => {
            return Err(Json(ErrorResponse {
                status: Status::InternalServerError.code,
                message: "Something went wrong, please try again later".to_string(),
            }));
        }
    };

    let user = match repo
        .update_user(
            &id,
            Some(&credentials.username),
            Some(&credentials.email),
            Some(&hashed_password),
        )
        .await
    {
        Ok(Some(user)) => user,
        Ok(None) => {
            return Err(Json(ErrorResponse {
                status: Status::NotFound.code,
                message: "User not found".to_string(),
            }));
        }
        Err(_) => {
            return Err(Json(ErrorResponse {
                status: Status::InternalServerError.code,
                message: "Something went wrong, please try again later".to_string(),
            }));
        }
    };

    Ok(Json(user))
}

/// Deletes a user by ID (requires authentication).
#[delete("/delete/<id>")]
pub async fn delete_user(
    _auth: AuthClaims,
    repo: &State<Arc<UserRepository>>,
    id: &str,
) -> Result<Json<SuccessResponse>, Json<ErrorResponse>> {
    match repo.delete_user(&id).await {
        Ok(Some(_)) => Ok(Json(SuccessResponse {
            status: Status::Ok.code,
            message: "User deleted successfully".to_string(),
        })),
        Ok(None) => {
            return Err(Json(ErrorResponse {
                status: Status::NotFound.code,
                message: "User not found".to_string(),
            }));
        }
        Err(_) => {
            return Err(Json(ErrorResponse {
                status: Status::InternalServerError.code,
                message: "Something went wrong, please try again later".to_string(),
            }));
        }
    }
}

/// Collects all user-related routes for mounting.
pub fn user_routes() -> Vec<rocket::Route> {
    routes![
        register,
        login,
        logout,
        get_user,
        get_user_by_email,
        update_user,
        delete_user
    ]
}
"#;