268 lines
7.7 KiB
Rust
268 lines
7.7 KiB
Rust
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<String>,
|
|
}
|
|
|
|
#[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<DatabaseConnection>,
|
|
Json(payload): Json<CreateFamilyRequest>,
|
|
) -> Result<Json<FamilyModel>, 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<DatabaseConnection>,
|
|
Path(id): Path<i32>,
|
|
) -> Result<Json<FamilyModel>, 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<FamilyModel>),
|
|
(status = 500, description = "Internal server error")
|
|
)
|
|
)]
|
|
pub async fn get_all_families(
|
|
State(db): State<DatabaseConnection>,
|
|
) -> Result<Json<Vec<FamilyModel>>, 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<DatabaseConnection>,
|
|
Path(id): Path<i32>,
|
|
Json(payload): Json<UpdateFamilyRequest>,
|
|
) -> Result<Json<FamilyModel>, 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<DatabaseConnection>,
|
|
Path(id): Path<i32>,
|
|
) -> Result<StatusCode, StatusCode> {
|
|
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<DatabaseConnection>,
|
|
Path(id): Path<i32>,
|
|
session: Session,
|
|
Json(payload): Json<VerifyFamilyPasswordRequest>,
|
|
) -> Result<Json<VerifyFamilyPasswordResponse>, StatusCode> {
|
|
let valid = FamilyService::verify_password(&db, id, payload.password)
|
|
.await
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
|
|
|
if valid {
|
|
let mut authorized_families: Vec<i32> = 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<AuthBackend>,
|
|
session: Session,
|
|
State(db): State<DatabaseConnection>,
|
|
Json(payload): Json<CreateMyFamilyRequest>,
|
|
) -> Result<Json<CreateMyFamilyResponse>, 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<i32> = 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,
|
|
}))
|
|
}
|