mod auth; mod db; mod endpoints; mod errors; mod file_storage; mod prelude; use std::{env, net::Ipv4Addr}; use axum::{routing::get, Router}; use file_storage::FileStorage; use tokio::net::TcpListener; type Pool = sqlx::postgres::PgPool; #[derive(Clone)] struct AppState { pool: Pool, storage: FileStorage, } async fn create_user(user_name: &str, user_email: &str, pool: &Pool) -> anyhow::Result { let id = sqlx::query!( "INSERT INTO users(username, email) VALUES ($1, $2) RETURNING user_id", user_name, user_email ) .fetch_one(pool) .await? .user_id; sqlx::query!( "INSERT INTO folders(owner_id, folder_name) VALUES ($1, $2)", id, "ROOT" ) .execute(pool) .await?; Ok(id) } async fn create_debug_users(pool: &Pool) -> anyhow::Result<()> { let count = sqlx::query!("SELECT count(user_id) FROM users") .fetch_one(pool) .await? .count .unwrap_or(0); if count > 0 { return Ok(()); } tokio::try_join!( create_user("Test1", "test1@example.com", pool), create_user("Test2", "test2@example.com", pool) )?; Ok(()) } #[tokio::main] async fn main() -> anyhow::Result<()> { // TODO: add utoipa and utoipauto for swagger let _ = dotenvy::dotenv(); tracing_subscriber::fmt::init(); let pool = match env::var("DATABASE_URL") { Ok(url) => Pool::connect(&url).await?, Err(err) => anyhow::bail!("Error getting database url: {err}"), }; sqlx::migrate!().run(&pool).await?; create_debug_users(&pool).await?; let storage = file_storage::FileStorage::new()?; let state = AppState { pool, storage }; let router = app(state); let addr = (Ipv4Addr::UNSPECIFIED, 3000); let listener = TcpListener::bind(addr).await?; axum::serve(listener, router).await?; Ok(()) } fn app(state: AppState) -> Router { use axum::http::header; use endpoints::{file, folder}; use tower_http::ServiceBuilderExt as _; let sensitive_headers = [header::AUTHORIZATION, header::COOKIE]; let middleware = tower::ServiceBuilder::new() .sensitive_headers(sensitive_headers) .trace_for_http() .compression(); // Build route service Router::new() .route( "/files", get(file::download::download) .post(file::upload::upload) .delete(file::delete::delete), ) .route( "/folders", get(folder::list::list) .post(folder::create::create) .delete(folder::delete::delete), ) .layer(middleware) .with_state(state) }