use axum::{ extract::{Path, State}, http::StatusCode, Json, }; use axum_login::AuthSession; use sea_orm::{DatabaseConnection, EntityTrait, ActiveModelTrait, Set}; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; use tower_sessions::Session; use crate::auth::AuthBackend; use crate::models::family::Model as FamilyModel; use crate::models::{user, User}; use crate::services::FamilyService; #[derive(Debug, Deserialize, ToSchema)] #[schema(example = json!({"name": "Smith Family", "password": "secret123"}))] pub struct CreateFamilyRequest { pub name: String, pub password: String, } #[derive(Debug, Deserialize, ToSchema)] #[schema(example = json!({"name": "Smith Family", "password": "secret123"}))] pub struct CreateMyFamilyRequest { pub name: String, #[serde(default)] pub password: Option, } #[derive(Debug, Deserialize, ToSchema)] #[schema(example = json!({"password": "secret123"}))] pub struct VerifyFamilyPasswordRequest { pub password: String, } #[derive(Debug, Serialize, ToSchema)] #[schema(example = json!({"valid": true}))] pub struct VerifyFamilyPasswordResponse { pub valid: bool, } #[derive(Debug, Deserialize, ToSchema)] #[schema(example = json!({"name": "Updated Family Name"}))] pub struct UpdateFamilyRequest { pub name: String, } #[utoipa::path( post, path = "/families", tag = "families", request_body = CreateFamilyRequest, responses( (status = 200, description = "Family created successfully", body = FamilyModel), (status = 500, description = "Internal server error") ) )] pub async fn create_family( State(db): State, Json(payload): Json, ) -> Result, StatusCode> { FamilyService::create(&db, payload.name, payload.password) .await .map(Json) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) } #[utoipa::path( get, path = "/families/{id}", tag = "families", params( ("id" = i32, Path, description = "Family ID") ), responses( (status = 200, description = "Family found", body = FamilyModel), (status = 404, description = "Family not found"), (status = 500, description = "Internal server error") ) )] pub async fn get_family( State(db): State, Path(id): Path, ) -> Result, StatusCode> { FamilyService::find_by_id(&db, id) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? .map(Json) .ok_or(StatusCode::NOT_FOUND) } #[utoipa::path( get, path = "/families", tag = "families", responses( (status = 200, description = "List of all families", body = Vec), (status = 500, description = "Internal server error") ) )] pub async fn get_all_families( State(db): State, ) -> Result>, StatusCode> { FamilyService::find_all(&db) .await .map(Json) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) } #[utoipa::path( put, path = "/families/{id}", tag = "families", params( ("id" = i32, Path, description = "Family ID") ), request_body = UpdateFamilyRequest, responses( (status = 200, description = "Family updated successfully", body = FamilyModel), (status = 500, description = "Internal server error") ) )] pub async fn update_family( State(db): State, Path(id): Path, Json(payload): Json, ) -> Result, StatusCode> { FamilyService::update(&db, id, payload.name) .await .map(Json) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) } #[utoipa::path( delete, path = "/families/{id}", tag = "families", params( ("id" = i32, Path, description = "Family ID") ), responses( (status = 204, description = "Family deleted successfully"), (status = 500, description = "Internal server error") ) )] pub async fn delete_family( State(db): State, Path(id): Path, ) -> Result { FamilyService::delete(&db, id) .await .map(|_| StatusCode::NO_CONTENT) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) } #[utoipa::path( post, path = "/families/{id}/verify", tag = "families", params( ("id" = i32, Path, description = "Family ID") ), request_body = VerifyFamilyPasswordRequest, responses( (status = 200, description = "Password verified", body = VerifyFamilyPasswordResponse), (status = 401, description = "Invalid password"), (status = 500, description = "Internal server error") ) )] pub async fn verify_family_password( State(db): State, Path(id): Path, session: Session, Json(payload): Json, ) -> Result, StatusCode> { let valid = FamilyService::verify_password(&db, id, payload.password) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; if valid { let mut authorized_families: Vec = session .get("authorized_families") .await .unwrap_or(None) .unwrap_or_default(); if !authorized_families.contains(&id) { authorized_families.push(id); session .insert("authorized_families", authorized_families) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; } Ok(Json(VerifyFamilyPasswordResponse { valid: true })) } else { Err(StatusCode::UNAUTHORIZED) } } #[derive(Debug, Serialize, ToSchema)] pub struct CreateMyFamilyResponse { pub family: FamilyModel, pub user_id: i32, pub family_id: i32, } #[utoipa::path( post, path = "/my-family", tag = "families", request_body = CreateMyFamilyRequest, responses( (status = 200, description = "Family created and linked to user", body = CreateMyFamilyResponse), (status = 401, description = "Not authenticated"), (status = 409, description = "User already has a family"), (status = 500, description = "Internal server error") ) )] pub async fn create_my_family( auth_session: AuthSession, session: Session, State(db): State, Json(payload): Json, ) -> Result, StatusCode> { let current_user = auth_session.user.ok_or(StatusCode::UNAUTHORIZED)?; if current_user.family_id.is_some() { return Err(StatusCode::CONFLICT); } let password = payload.password.unwrap_or_default(); let family = FamilyService::create(&db, payload.name, password) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; let mut active_user: user::ActiveModel = User::find_by_id(current_user.id) .one(&db) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? .ok_or(StatusCode::NOT_FOUND)? .into(); active_user.family_id = Set(Some(family.id)); active_user.update(&db) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; let mut authorized_families: Vec = session .get("authorized_families") .await .unwrap_or(None) .unwrap_or_default(); authorized_families.push(family.id); session .insert("authorized_families", authorized_families) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; Ok(Json(CreateMyFamilyResponse { family_id: family.id, user_id: current_user.id, family, })) }