cleanup
This commit is contained in:
143
src/lib.rs
Normal file
143
src/lib.rs
Normal 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))
|
||||||
|
}
|
||||||
138
src/main.rs
138
src/main.rs
@@ -1,147 +1,19 @@
|
|||||||
use axum::{
|
use family_budget::*;
|
||||||
routing::{get, post, put, delete},
|
use sea_orm::DbErr;
|
||||||
Router, middleware as axum_middleware,
|
|
||||||
};
|
|
||||||
use sea_orm::{Database, DatabaseConnection, DbErr};
|
|
||||||
use sea_orm_migration::prelude::*;
|
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;
|
|
||||||
|
|
||||||
mod models;
|
|
||||||
mod services;
|
|
||||||
mod migration;
|
|
||||||
mod routes;
|
|
||||||
mod auth;
|
|
||||||
mod middleware;
|
|
||||||
|
|
||||||
#[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;
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<(), DbErr> {
|
async fn main() -> Result<(), DbErr> {
|
||||||
let db = establish_connection().await?;
|
let db = establish_connection().await?;
|
||||||
|
|
||||||
println!("Successfully connected to database!");
|
println!("Successfully connected to database!");
|
||||||
|
|
||||||
println!("Running migrations...");
|
println!("Running migrations...");
|
||||||
crate::migration::Migrator::up(&db, None).await?;
|
migration::Migrator::up(&db, None).await?;
|
||||||
println!("Migrations completed!");
|
println!("Migrations completed!");
|
||||||
|
|
||||||
let database_url = std::env::var("DATABASE_URL")
|
let app = create_app(db).await?;
|
||||||
.expect("DATABASE_URL must be set in .env file");
|
|
||||||
|
|
||||||
let session_store = PostgresStore::new(
|
let addr = server_address();
|
||||||
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);
|
|
||||||
|
|
||||||
let addr = SocketAddr::from(([0, 0, 0, 0], 8080));
|
|
||||||
println!("Server running on http://{}", addr);
|
println!("Server running on http://{}", addr);
|
||||||
println!("Swagger UI available at http://{}/swagger-ui", addr);
|
println!("Swagger UI available at http://{}/swagger-ui", addr);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user