use axum::{ body::HttpBody, http::{Request, StatusCode}, middleware::{self, Next}, response::Response, RequestExt, Router, extract::DefaultBodyLimit, }; use axum_client_ip::ClientIp; use std::{net::SocketAddr, process::exit, fs}; use tower_cookies::CookieManagerLayer; use tracing::{error, info, metadata::LevelFilter}; use tracing_subscriber::{ filter::filter_fn, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, Layer, }; use types::http::ResponseCode; use crate::api::{pages, image}; mod admin; mod api; mod console; mod database; mod types; async fn serve(req: Request, next: Next) -> Response where B: Send + Sync + 'static + HttpBody, { let uri = req.uri(); let file = ResponseCode::Success.file(&uri.to_string()).await; if file.status() != StatusCode::OK { return next.run(req).await; } file } async fn log(mut req: Request, next: Next) -> Response where B: Send + Sync + 'static + HttpBody, { let Ok(ClientIp(ip)) = req.extract_parts::().await else { return next.run(req).await }; console::log(ip, req.method().clone(), req.uri().clone(), None, None).await; next.run(req).await } async fn not_found() -> Response { ResponseCode::NotFound.file("/404.html").await } #[tokio::main] async fn main() { let fmt_layer = tracing_subscriber::fmt::layer(); tracing_subscriber::registry() .with( fmt_layer .with_filter(LevelFilter::TRACE) .with_filter(filter_fn(|metadata| { metadata.target().starts_with("xssbook") })), ) .init(); if database::init().is_err() { error!("Failed to connect to the sqlite database"); exit(1) }; fs::create_dir_all("./public/image/custom").expect("Coudn't make custom data directory"); fs::create_dir_all("./public/image/custom/avatar").expect("Coudn't make custom avatar directory"); fs::create_dir_all("./public/image/custom/banner").expect("Coudn't make custom banner directory"); let app = Router::new() .fallback(not_found) .layer(middleware::from_fn(log)) .layer(middleware::from_fn(serve)) .nest("/", pages::router()) .nest("/api", api::router()) .nest("/image", image::router()) .layer(CookieManagerLayer::new()) .layer(DefaultBodyLimit::max(512_000)); let Ok(addr) = "[::]:8080".parse::() else { error!("Failed to parse port binding"); exit(1) }; info!("listening on {}", addr); axum::Server::bind(&addr) .serve(app.into_make_service_with_connect_info::()) .await .unwrap_or(()); }