use axum::{ error_handling::HandleErrorLayer, http::StatusCode, response::IntoResponse, routing::{delete, get, post}, Extension, Json, Router, }; use errors::ServiceError; use serde::{Deserialize, Serialize}; use sqlx::{postgres::PgPoolOptions, query, Connection, PgConnection, PgPool}; use std::{net::SocketAddr, str::FromStr, sync::Arc, time::Duration}; use tower::{BoxError, ServiceBuilder}; use tower_http::trace::TraceLayer; use tracing_subscriber::prelude::*; mod errors; mod handlers; pub struct State { pub conn: PgPool, } #[tokio::main] async fn main() { kankyo::init().unwrap(); color_eyre::install().unwrap(); tracing_subscriber::registry() .with(tracing_subscriber::EnvFilter::new( std::env::var("RUST_LOG").unwrap_or_else(|_| "homeworld=info,tower_http=debug".into()), )) .with(tracing_subscriber::fmt::layer()) .init(); let conn = PgPoolOptions::new() .max_connections(5) .connect( &std::env::var("DATABASE_URL") .unwrap_or("postgres://postgres@localhost/homeworld".to_string()), ) .await .unwrap(); sqlx::migrate!("./migrations").run(&conn).await.unwrap(); let shared_state = Arc::new(State { conn }); let app = Router::new() .route("/health", get(health_check)) .route("/ships/list", get(handlers::ships::list)) .route("/ships/new", post(handlers::ships::new)) .route("/ships/delete/:shasum", delete(handlers::ships::delete)) .route("/ships/get/:shasum", get(handlers::ships::get)) .route("/users/list", get(handlers::colonies::list_users)) .route("/users/add", post(handlers::colonies::create_user)) .route("/:uuid/user-data", get(handlers::colonies::user_data)) .route("/meta/:uuid/:hostname", post(handlers::colonies::add_metadata)) .route("/:uuid/meta-data", get(handlers::colonies::meta_data)) .layer( ServiceBuilder::new() .layer(HandleErrorLayer::new(|error: BoxError| async move { if error.is::() { Ok(StatusCode::REQUEST_TIMEOUT) } else { Err(( StatusCode::INTERNAL_SERVER_ERROR, format!("Unhandled internal error: {}", error), )) } })) .timeout(Duration::from_secs(10)) .layer(TraceLayer::new_for_http()) .into_inner(), ) .layer(Extension(shared_state)); let addr = SocketAddr::from_str(std::env::var("BIND_ADDR").unwrap().as_str().into()).unwrap(); tracing::info!("Listening on {}", addr); axum::Server::bind(&addr) .serve(app.into_make_service()) .await .unwrap(); } async fn health_check() -> &'static str { "OK" }