slave 0.2.0

Laravel-inspired CLI for Rust web apps: scaffold Axum projects with built-in auth, users, roles and Turso migrations
//! HTTP handlers for roles. Business rules live in `RoleService`.

use axum::Json;
use axum::extract::{Path, State};

use crate::base::auth::{AdminUser, AuthUser};
use crate::base::error::{AppError, AppResult};
use crate::modules::role::dto::role::{AssignRoleRequest, RoleResponse};
use crate::modules::role::models::role::Role;
use crate::modules::role::services::role_service::RoleService;

/// `GET /roles` (admin only).
pub async fn index(
    _admin: AdminUser,
    State(service): State<RoleService>,
) -> AppResult<Json<Vec<RoleResponse>>> {
    Ok(respond(service.list().await?))
}

/// `GET /users/{id}/roles`: admins can view anyone's roles, users their own.
pub async fn user_roles(
    user: AuthUser,
    State(service): State<RoleService>,
    Path(id): Path<i64>,
) -> AppResult<Json<Vec<RoleResponse>>> {
    if user.id != id && !user.is_admin() {
        return Err(AppError::Forbidden);
    }
    Ok(respond(service.roles_of(id).await?))
}

/// `POST /users/{id}/roles` with `{"role": "admin"}` (admin only).
pub async fn assign(
    _admin: AdminUser,
    State(service): State<RoleService>,
    Path(id): Path<i64>,
    Json(input): Json<AssignRoleRequest>,
) -> AppResult<Json<Vec<RoleResponse>>> {
    Ok(respond(service.assign(id, input.role.trim()).await?))
}

/// `DELETE /users/{id}/roles/{role}` (admin only).
pub async fn revoke(
    _admin: AdminUser,
    State(service): State<RoleService>,
    Path((id, role)): Path<(i64, String)>,
) -> AppResult<Json<Vec<RoleResponse>>> {
    Ok(respond(service.revoke(id, &role).await?))
}

fn respond(roles: Vec<Role>) -> Json<Vec<RoleResponse>> {
    Json(roles.into_iter().map(RoleResponse::from).collect())
}