раскидал структуру для монорепозитория
This commit is contained in:
75
backend/src/routes/auth.rs
Normal file
75
backend/src/routes/auth.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use axum::{
|
||||
extract::State,
|
||||
http::StatusCode,
|
||||
Json,
|
||||
};
|
||||
use axum_login::AuthSession;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::auth::{AuthBackend, Credentials};
|
||||
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
pub struct LoginRequest {
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
pub struct LoginResponse {
|
||||
pub success: bool,
|
||||
pub is_admin: bool,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/login",
|
||||
tag = "auth",
|
||||
request_body = LoginRequest,
|
||||
responses(
|
||||
(status = 200, description = "Login successful", body = LoginResponse),
|
||||
(status = 401, description = "Invalid credentials")
|
||||
)
|
||||
)]
|
||||
pub async fn login(
|
||||
mut auth_session: AuthSession<AuthBackend>,
|
||||
Json(payload): Json<LoginRequest>,
|
||||
) -> Result<Json<LoginResponse>, StatusCode> {
|
||||
let user = auth_session
|
||||
.authenticate(Credentials {
|
||||
username: payload.username,
|
||||
password: payload.password,
|
||||
})
|
||||
.await
|
||||
.map_err(|_| StatusCode::UNAUTHORIZED)?
|
||||
.ok_or(StatusCode::UNAUTHORIZED)?;
|
||||
|
||||
auth_session
|
||||
.login(&user)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
Ok(Json(LoginResponse {
|
||||
success: true,
|
||||
is_admin: user.is_admin,
|
||||
}))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/logout",
|
||||
tag = "auth",
|
||||
responses(
|
||||
(status = 200, description = "Logout successful")
|
||||
)
|
||||
)]
|
||||
pub async fn logout(
|
||||
mut auth_session: AuthSession<AuthBackend>,
|
||||
) -> Result<StatusCode, StatusCode> {
|
||||
auth_session
|
||||
.logout()
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
170
backend/src/routes/category.rs
Normal file
170
backend/src/routes/category.rs
Normal file
@@ -0,0 +1,170 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
Json,
|
||||
};
|
||||
use sea_orm::{prelude::Decimal, DatabaseConnection};
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::models::category::Model as CategoryModel;
|
||||
use crate::services::CategoryService;
|
||||
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
#[schema(example = json!({"name": "Groceries", "limit_amount": 5000.00}))]
|
||||
pub struct CreateCategoryRequest {
|
||||
pub name: String,
|
||||
pub limit_amount: Decimal,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
#[schema(example = json!({"name": "Monthly Groceries", "limit_amount": 6000.00}))]
|
||||
pub struct UpdateCategoryRequest {
|
||||
pub name: Option<String>,
|
||||
pub limit_amount: Option<Decimal>,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/families/{family_id}/categories",
|
||||
tag = "categories",
|
||||
params(
|
||||
("family_id" = i32, Path, description = "Family ID")
|
||||
),
|
||||
request_body = CreateCategoryRequest,
|
||||
responses(
|
||||
(status = 200, description = "Category created successfully", body = CategoryModel),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn create_category(
|
||||
State(db): State<DatabaseConnection>,
|
||||
Path(family_id): Path<i32>,
|
||||
Json(payload): Json<CreateCategoryRequest>,
|
||||
) -> Result<Json<CategoryModel>, StatusCode> {
|
||||
CategoryService::create(&db, family_id, payload.name, payload.limit_amount)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/families/{family_id}/categories/{category_id}",
|
||||
tag = "categories",
|
||||
params(
|
||||
("family_id" = i32, Path, description = "Family ID"),
|
||||
("category_id" = i32, Path, description = "Category ID")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Category found", body = CategoryModel),
|
||||
(status = 404, description = "Category not found"),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn get_category(
|
||||
State(db): State<DatabaseConnection>,
|
||||
Path((family_id, category_id)): Path<(i32, i32)>,
|
||||
) -> Result<Json<CategoryModel>, StatusCode> {
|
||||
let category = CategoryService::find_by_id(&db, category_id)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.ok_or(StatusCode::NOT_FOUND)?;
|
||||
|
||||
if category.family_id != family_id {
|
||||
return Err(StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
Ok(Json(category))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/families/{family_id}/categories",
|
||||
tag = "categories",
|
||||
params(
|
||||
("family_id" = i32, Path, description = "Family ID")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "List of categories for the family", body = Vec<CategoryModel>),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn get_categories_by_family(
|
||||
State(db): State<DatabaseConnection>,
|
||||
Path(family_id): Path<i32>,
|
||||
) -> Result<Json<Vec<CategoryModel>>, StatusCode> {
|
||||
CategoryService::find_by_family_id(&db, family_id)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/families/{family_id}/categories/{category_id}",
|
||||
tag = "categories",
|
||||
params(
|
||||
("family_id" = i32, Path, description = "Family ID"),
|
||||
("category_id" = i32, Path, description = "Category ID")
|
||||
),
|
||||
request_body = UpdateCategoryRequest,
|
||||
responses(
|
||||
(status = 200, description = "Category updated successfully", body = CategoryModel),
|
||||
(status = 404, description = "Category not found"),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn update_category(
|
||||
State(db): State<DatabaseConnection>,
|
||||
Path((family_id, category_id)): Path<(i32, i32)>,
|
||||
Json(payload): Json<UpdateCategoryRequest>,
|
||||
) -> Result<Json<CategoryModel>, StatusCode> {
|
||||
let category = CategoryService::find_by_id(&db, category_id)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.ok_or(StatusCode::NOT_FOUND)?;
|
||||
|
||||
if category.family_id != family_id {
|
||||
return Err(StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
CategoryService::update(&db, category_id, payload.name, payload.limit_amount)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/families/{family_id}/categories/{category_id}",
|
||||
tag = "categories",
|
||||
params(
|
||||
("family_id" = i32, Path, description = "Family ID"),
|
||||
("category_id" = i32, Path, description = "Category ID")
|
||||
),
|
||||
responses(
|
||||
(status = 204, description = "Category deleted successfully"),
|
||||
(status = 404, description = "Category not found"),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn delete_category(
|
||||
State(db): State<DatabaseConnection>,
|
||||
Path((family_id, category_id)): Path<(i32, i32)>,
|
||||
) -> Result<StatusCode, StatusCode> {
|
||||
let category = CategoryService::find_by_id(&db, category_id)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.ok_or(StatusCode::NOT_FOUND)?;
|
||||
|
||||
if category.family_id != family_id {
|
||||
return Err(StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
CategoryService::delete(&db, category_id)
|
||||
.await
|
||||
.map(|_| StatusCode::NO_CONTENT)
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
266
backend/src/routes/expense.rs
Normal file
266
backend/src/routes/expense.rs
Normal file
@@ -0,0 +1,266 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
Json,
|
||||
};
|
||||
use sea_orm::{prelude::Decimal, DatabaseConnection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::models::expense::Model as ExpenseModel;
|
||||
use crate::services::{CategoryService, ExpenseService};
|
||||
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
#[schema(example = json!({"amount": 150.50, "description": "Weekly grocery shopping"}))]
|
||||
pub struct CreateExpenseRequest {
|
||||
pub amount: Decimal,
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
#[schema(example = json!({"amount": 200.00, "description": "Updated expense description"}))]
|
||||
pub struct UpdateExpenseRequest {
|
||||
pub amount: Option<Decimal>,
|
||||
pub description: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
#[schema(example = json!({"category_id": 1, "remaining_limit": 4500.00}))]
|
||||
pub struct RemainingLimitResponse {
|
||||
pub category_id: i32,
|
||||
pub remaining_limit: Decimal,
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
post,
|
||||
path = "/families/{family_id}/categories/{category_id}/expenses",
|
||||
tag = "expenses",
|
||||
params(
|
||||
("family_id" = i32, Path, description = "Family ID"),
|
||||
("category_id" = i32, Path, description = "Category ID")
|
||||
),
|
||||
request_body = CreateExpenseRequest,
|
||||
responses(
|
||||
(status = 200, description = "Expense created successfully", body = ExpenseModel),
|
||||
(status = 404, description = "Category not found"),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn create_expense(
|
||||
State(db): State<DatabaseConnection>,
|
||||
Path((family_id, category_id)): Path<(i32, i32)>,
|
||||
Json(payload): Json<CreateExpenseRequest>,
|
||||
) -> Result<Json<ExpenseModel>, StatusCode> {
|
||||
let category = CategoryService::find_by_id(&db, category_id)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.ok_or(StatusCode::NOT_FOUND)?;
|
||||
|
||||
if category.family_id != family_id {
|
||||
return Err(StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
ExpenseService::create(&db, category_id, payload.amount, payload.description)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/families/{family_id}/categories/{category_id}/expenses/{expense_id}",
|
||||
tag = "expenses",
|
||||
params(
|
||||
("family_id" = i32, Path, description = "Family ID"),
|
||||
("category_id" = i32, Path, description = "Category ID"),
|
||||
("expense_id" = i32, Path, description = "Expense ID")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Expense found", body = ExpenseModel),
|
||||
(status = 404, description = "Expense not found"),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn get_expense(
|
||||
State(db): State<DatabaseConnection>,
|
||||
Path((family_id, category_id, expense_id)): Path<(i32, i32, i32)>,
|
||||
) -> Result<Json<ExpenseModel>, StatusCode> {
|
||||
let category = CategoryService::find_by_id(&db, category_id)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.ok_or(StatusCode::NOT_FOUND)?;
|
||||
|
||||
if category.family_id != family_id {
|
||||
return Err(StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
let expense = ExpenseService::find_by_id(&db, expense_id)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.ok_or(StatusCode::NOT_FOUND)?;
|
||||
|
||||
if expense.category_id != category_id {
|
||||
return Err(StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
Ok(Json(expense))
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/families/{family_id}/categories/{category_id}/expenses",
|
||||
tag = "expenses",
|
||||
params(
|
||||
("family_id" = i32, Path, description = "Family ID"),
|
||||
("category_id" = i32, Path, description = "Category ID")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "List of expenses for the category", body = Vec<ExpenseModel>),
|
||||
(status = 404, description = "Category not found"),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn get_expenses_by_category(
|
||||
State(db): State<DatabaseConnection>,
|
||||
Path((family_id, category_id)): Path<(i32, i32)>,
|
||||
) -> Result<Json<Vec<ExpenseModel>>, StatusCode> {
|
||||
let category = CategoryService::find_by_id(&db, category_id)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.ok_or(StatusCode::NOT_FOUND)?;
|
||||
|
||||
if category.family_id != family_id {
|
||||
return Err(StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
ExpenseService::find_by_category_id(&db, category_id)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
put,
|
||||
path = "/families/{family_id}/categories/{category_id}/expenses/{expense_id}",
|
||||
tag = "expenses",
|
||||
params(
|
||||
("family_id" = i32, Path, description = "Family ID"),
|
||||
("category_id" = i32, Path, description = "Category ID"),
|
||||
("expense_id" = i32, Path, description = "Expense ID")
|
||||
),
|
||||
request_body = UpdateExpenseRequest,
|
||||
responses(
|
||||
(status = 200, description = "Expense updated successfully", body = ExpenseModel),
|
||||
(status = 404, description = "Expense not found"),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn update_expense(
|
||||
State(db): State<DatabaseConnection>,
|
||||
Path((family_id, category_id, expense_id)): Path<(i32, i32, i32)>,
|
||||
Json(payload): Json<UpdateExpenseRequest>,
|
||||
) -> Result<Json<ExpenseModel>, StatusCode> {
|
||||
let category = CategoryService::find_by_id(&db, category_id)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.ok_or(StatusCode::NOT_FOUND)?;
|
||||
|
||||
if category.family_id != family_id {
|
||||
return Err(StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
let expense = ExpenseService::find_by_id(&db, expense_id)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.ok_or(StatusCode::NOT_FOUND)?;
|
||||
|
||||
if expense.category_id != category_id {
|
||||
return Err(StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
ExpenseService::update(&db, expense_id, payload.amount, payload.description)
|
||||
.await
|
||||
.map(Json)
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
delete,
|
||||
path = "/families/{family_id}/categories/{category_id}/expenses/{expense_id}",
|
||||
tag = "expenses",
|
||||
params(
|
||||
("family_id" = i32, Path, description = "Family ID"),
|
||||
("category_id" = i32, Path, description = "Category ID"),
|
||||
("expense_id" = i32, Path, description = "Expense ID")
|
||||
),
|
||||
responses(
|
||||
(status = 204, description = "Expense deleted successfully"),
|
||||
(status = 404, description = "Expense not found"),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn delete_expense(
|
||||
State(db): State<DatabaseConnection>,
|
||||
Path((family_id, category_id, expense_id)): Path<(i32, i32, i32)>,
|
||||
) -> Result<StatusCode, StatusCode> {
|
||||
let category = CategoryService::find_by_id(&db, category_id)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.ok_or(StatusCode::NOT_FOUND)?;
|
||||
|
||||
if category.family_id != family_id {
|
||||
return Err(StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
let expense = ExpenseService::find_by_id(&db, expense_id)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.ok_or(StatusCode::NOT_FOUND)?;
|
||||
|
||||
if expense.category_id != category_id {
|
||||
return Err(StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
ExpenseService::delete(&db, expense_id)
|
||||
.await
|
||||
.map(|_| StatusCode::NO_CONTENT)
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
|
||||
}
|
||||
|
||||
#[utoipa::path(
|
||||
get,
|
||||
path = "/families/{family_id}/categories/{category_id}/remaining",
|
||||
tag = "expenses",
|
||||
params(
|
||||
("family_id" = i32, Path, description = "Family ID"),
|
||||
("category_id" = i32, Path, description = "Category ID")
|
||||
),
|
||||
responses(
|
||||
(status = 200, description = "Remaining budget limit", body = RemainingLimitResponse),
|
||||
(status = 404, description = "Category not found"),
|
||||
(status = 500, description = "Internal server error")
|
||||
)
|
||||
)]
|
||||
pub async fn get_remaining_limit(
|
||||
State(db): State<DatabaseConnection>,
|
||||
Path((family_id, category_id)): Path<(i32, i32)>,
|
||||
) -> Result<Json<RemainingLimitResponse>, StatusCode> {
|
||||
let category = CategoryService::find_by_id(&db, category_id)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.ok_or(StatusCode::NOT_FOUND)?;
|
||||
|
||||
if category.family_id != family_id {
|
||||
return Err(StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
let remaining = ExpenseService::calculate_remaining_limit(&db, category_id)
|
||||
.await
|
||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||
|
||||
Ok(Json(RemainingLimitResponse {
|
||||
category_id,
|
||||
remaining_limit: remaining,
|
||||
}))
|
||||
}
|
||||
132
backend/src/routes/family.rs
Normal file
132
backend/src/routes/family.rs
Normal file
@@ -0,0 +1,132 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
http::StatusCode,
|
||||
Json,
|
||||
};
|
||||
use sea_orm::DatabaseConnection;
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use utoipa::ToSchema;
|
||||
|
||||
use crate::models::family::Model as FamilyModel;
|
||||
use crate::services::FamilyService;
|
||||
|
||||
#[derive(Debug, Deserialize, ToSchema)]
|
||||
#[schema(example = json!({"name": "Smith Family"}))]
|
||||
pub struct CreateFamilyRequest {
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[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)
|
||||
.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)
|
||||
}
|
||||
4
backend/src/routes/mod.rs
Normal file
4
backend/src/routes/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod family;
|
||||
pub mod category;
|
||||
pub mod expense;
|
||||
pub mod auth;
|
||||
Reference in New Issue
Block a user