This commit is contained in:
arrelin
2025-12-09 16:36:43 +03:00
parent db32adf840
commit b5e4e48420
2 changed files with 148 additions and 133 deletions

143
src/lib.rs Normal file
View File

@@ -0,0 +1,143 @@
use axum::{
routing::{get, post, put, delete},
Router, middleware as axum_middleware,
};
use sea_orm::{Database, DatabaseConnection, DbErr};
use sea_orm_migration::prelude::*;
use std::net::SocketAddr;
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;
use tower_sessions::{Expiry, SessionManagerLayer};
use tower_sessions_sqlx_store::PostgresStore;
use axum_login::AuthManagerLayerBuilder;
use time::Duration;
pub mod models;
pub mod services;
pub mod migration;
pub mod routes;
pub mod auth;
pub mod middleware;
pub use auth::AuthBackend;
pub use middleware::require_admin;
#[derive(OpenApi)]
#[openapi(
paths(
routes::auth::login,
routes::auth::logout,
routes::family::create_family,
routes::family::get_family,
routes::family::get_all_families,
routes::family::update_family,
routes::family::delete_family,
routes::category::create_category,
routes::category::get_category,
routes::category::get_categories_by_family,
routes::category::update_category,
routes::category::delete_category,
routes::expense::create_expense,
routes::expense::get_expense,
routes::expense::get_expenses_by_category,
routes::expense::update_expense,
routes::expense::delete_expense,
routes::expense::get_remaining_limit,
),
components(
schemas(
models::family::Model,
models::category::Model,
models::expense::Model,
routes::auth::LoginRequest,
routes::auth::LoginResponse,
routes::family::CreateFamilyRequest,
routes::family::UpdateFamilyRequest,
routes::category::CreateCategoryRequest,
routes::category::UpdateCategoryRequest,
routes::expense::CreateExpenseRequest,
routes::expense::UpdateExpenseRequest,
routes::expense::RemainingLimitResponse,
)
),
tags(
(name = "auth", description = "Authentication endpoints"),
(name = "families", description = "Family management endpoints"),
(name = "categories", description = "Category management endpoints"),
(name = "expenses", description = "Expense management endpoints")
),
info(
title = "Family Budget API",
version = "0.1.0",
description = "REST API"
)
)]
struct ApiDoc;
pub async fn establish_connection() -> Result<DatabaseConnection, DbErr> {
dotenvy::dotenv().ok();
let database_url = std::env::var("DATABASE_URL")
.expect("DATABASE_URL must be set in .env file");
Database::connect(&database_url).await
}
pub async fn create_app(db: DatabaseConnection) -> Result<Router, DbErr> {
let database_url = std::env::var("DATABASE_URL")
.expect("DATABASE_URL must be set in .env file");
let session_store = PostgresStore::new(
sqlx::PgPool::connect(&database_url)
.await
.expect("Failed to connect to database for sessions"),
);
session_store
.migrate()
.await
.expect("Failed to run session store migrations");
let session_layer = SessionManagerLayer::new(session_store)
.with_secure(false)
.with_expiry(Expiry::OnInactivity(Duration::days(1)));
let backend = auth::AuthBackend { db: db.clone() };
let auth_layer = AuthManagerLayerBuilder::new(backend, session_layer).build();
let protected_routes = Router::new()
.route("/families", post(routes::family::create_family))
.route_layer(axum_middleware::from_fn(middleware::require_admin));
let api_routes = Router::new()
.route("/login", post(routes::auth::login))
.route("/logout", post(routes::auth::logout))
.merge(protected_routes)
.route("/families", get(routes::family::get_all_families))
.route("/families/{id}", get(routes::family::get_family))
.route("/families/{id}", put(routes::family::update_family))
.route("/families/{id}", delete(routes::family::delete_family))
.route("/families/{family_id}/categories", post(routes::category::create_category))
.route("/families/{family_id}/categories", get(routes::category::get_categories_by_family))
.route("/families/{family_id}/categories/{category_id}", get(routes::category::get_category))
.route("/families/{family_id}/categories/{category_id}", put(routes::category::update_category))
.route("/families/{family_id}/categories/{category_id}", delete(routes::category::delete_category))
.route("/families/{family_id}/categories/{category_id}/expenses", post(routes::expense::create_expense))
.route("/families/{family_id}/categories/{category_id}/expenses", get(routes::expense::get_expenses_by_category))
.route("/families/{family_id}/categories/{category_id}/expenses/{expense_id}", get(routes::expense::get_expense))
.route("/families/{family_id}/categories/{category_id}/expenses/{expense_id}", put(routes::expense::update_expense))
.route("/families/{family_id}/categories/{category_id}/expenses/{expense_id}", delete(routes::expense::delete_expense))
.route("/families/{family_id}/categories/{category_id}/remaining", get(routes::expense::get_remaining_limit))
.layer(auth_layer)
.with_state(db);
let swagger_ui = SwaggerUi::new("/swagger-ui")
.url("/api-docs/openapi.json", ApiDoc::openapi());
let app = api_routes.merge(swagger_ui);
Ok(app)
}
pub fn server_address() -> SocketAddr {
SocketAddr::from(([0, 0, 0, 0], 8080))
}