use axum::extract::{Path, State}; use axum::{http::StatusCode, Json}; use uuid::Uuid; use crate::auth::{AuthenticatedUser, TenantMembershipUser}; use crate::error::AppResult; use crate::http::responders::JsonResponse; use crate::services::auth::{AuthService, TenantSnippet}; use crate::services::tenants::{ TenantApiService, TenantUserListResponse, TenantUserSummary, UpdateTenantRequest, UpdateTenantUserRequest, }; use crate::state::AppState; #[utoipa::path( get, path = "/api/tenants", responses((status = 200, body = [TenantSnippet], description = "Tenant memberships for the current user")), tag = "Tenants" )] pub async fn list_tenants( State(state): State, user: TenantMembershipUser, ) -> AppResult>> { let response = AuthService::new(&state).list_tenants(user.user_id)?; Ok(Json(response.into_inner().tenants)) } #[utoipa::path( get, path = "/api/tenants/{tenant_id}", params(("tenant_id" = Uuid, Path, description = "Tenant identifier")), responses((status = 200, body = TenantSnippet, description = "Tenant details")), tag = "Tenants" )] pub async fn get_tenant( State(state): State, Path(tenant_id): Path, user: TenantMembershipUser, ) -> AppResult> { AuthService::new(&state).get_tenant(user.user_id, tenant_id) } #[utoipa::path( patch, path = "/api/tenants/{tenant_id}", params(("tenant_id" = Uuid, Path, description = "Tenant identifier")), request_body = UpdateTenantRequest, responses((status = 200, body = TenantSnippet, description = "Updated tenant")), tag = "Tenants" )] pub async fn update_tenant( State(state): State, Path(tenant_id): Path, user: AuthenticatedUser, Json(payload): Json, ) -> AppResult> { TenantApiService::new(&state).update_name(user, tenant_id, payload) } #[utoipa::path( get, path = "/api/tenants/{tenant_id}/users", params(("tenant_id" = Uuid, Path, description = "Tenant ID")), responses((status = 200, body = [TenantUserSummary], description = "All users for the tenant")), tag = "Tenants" )] pub async fn list_tenant_users( State(state): State, Path(tenant_id): Path, user: AuthenticatedUser, ) -> AppResult>> { let response = TenantApiService::new(&state).list_users(&user, tenant_id)?; Ok(Json(response.into_inner().users)) } #[utoipa::path( get, path = "/api/tenants/{tenant_id}/users/{user_id}", params( ("tenant_id" = Uuid, Path, description = "Tenant ID"), ("user_id" = Uuid, Path, description = "User ID") ), responses((status = 200, body = TenantUserSummary, description = "Tenant user details")), tag = "Tenants" )] pub async fn get_tenant_user( State(state): State, Path((tenant_id, target_user_id)): Path<(Uuid, Uuid)>, user: AuthenticatedUser, ) -> AppResult> { TenantApiService::new(&state).get_user(&user, tenant_id, target_user_id) } #[utoipa::path( patch, path = "/api/tenants/{tenant_id}/users/{user_id}", params( ("tenant_id" = Uuid, Path, description = "Tenant ID"), ("user_id" = Uuid, Path, description = "User ID") ), request_body = UpdateTenantUserRequest, responses((status = 200, body = TenantUserSummary, description = "Updated tenant user")), tag = "Tenants" )] pub async fn update_tenant_user( State(state): State, Path((tenant_id, target_user_id)): Path<(Uuid, Uuid)>, user: AuthenticatedUser, Json(payload): Json, ) -> AppResult> { TenantApiService::new(&state).update_user(&user, tenant_id, target_user_id, payload) } #[utoipa::path( delete, path = "/api/tenants/{tenant_id}/users/{user_id}", params( ("tenant_id" = Uuid, Path, description = "Tenant ID"), ("user_id" = Uuid, Path, description = "User ID") ), responses((status = 204, description = "Membership removed")), tag = "Tenants" )] pub async fn delete_tenant_user( State(state): State, Path((tenant_id, target_user_id)): Path<(Uuid, Uuid)>, user: AuthenticatedUser, ) -> AppResult { TenantApiService::new(&state).remove_user(&user, tenant_id, target_user_id)?; Ok(StatusCode::NO_CONTENT) } #[derive(utoipa::OpenApi)] #[openapi( paths( list_tenants, get_tenant, update_tenant, list_tenant_users, get_tenant_user, update_tenant_user, delete_tenant_user, ), components(schemas( crate::services::auth::TenantListResponse, crate::services::auth::TenantSnippet, UpdateTenantRequest, UpdateTenantUserRequest, TenantUserListResponse, TenantUserSummary, )) )] pub struct TenantsApiDoc;