pub mod api; pub mod config; pub mod db; pub mod error_handle; pub mod state; use axum::{ extract::State, http::{ header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE, ORIGIN}, HeaderValue, Method, StatusCode, Uri, }, middleware, response::{IntoResponse, Json, Response}, routing::{get, post}, Router, }; use diesel::RunQueryDsl; use std::net::SocketAddr; use std::sync::Arc; use std::{env, net::Ipv4Addr}; use tower_http::{ cors::{Any, CorsLayer}, trace::{self, TraceLayer}, }; use tracing::Level; use crate::config::Config; use crate::db::{create_user, models::User}; use crate::error_handle::*; use crate::state::AppState; #[tokio::main] async fn main() { //init_tracing(); tracing_subscriber::fmt() .with_target(false) .compact() .init(); let config = Config::new(); let database_url = format!( "postgres://{}:{}@{}:{}/{}", config.database.user, config.database.password, config.database.host, config.database.port, config.database.name ); let pool = db::create_pool(database_url); db::run_migrations(&pool).await; let state = Arc::new(AppState { database: pool.clone(), config: config.clone(), }); let address: SocketAddr = format!("{}:{}", config.server.address, config.server.port) .parse() .unwrap(); //SocketAddr::from((Ipv4Addr::UNSPECIFIED, 54600)); let lister = tokio::net::TcpListener::bind(&address).await.unwrap(); let cors = CorsLayer::new() .allow_methods([Method::GET, Method::POST]) .allow_headers(Any) //vec![ORIGIN, AUTHORIZATION, ACCEPT]) .allow_origin(Any); //.allow_credentials(true); //"http://localhost:5173".parse::().unwrap()); let app = Router::new() .route("/", get(home)) .route("/assets/*file", get(static_handler)) .route("/api/v1/register_user", post(api::v1::register_user)) .route("/api/v1/login_user", post(api::v1::login_user)) .layer(cors) .route("/api/v1/healthcheck", get(api::v1::healthcheck)) .route("/api/v1/users", get(users)) .route("/api/v1/logout_user", get(api::v1::logout_user)) .route( "/api/v1/me", get(api::v1::me).route_layer(middleware::from_fn_with_state( state.clone(), api::v1::jwt_auth, )), ) .layer( TraceLayer::new_for_http() .make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO)) .on_response(trace::DefaultOnResponse::new().level(Level::INFO)), ) .with_state(state); println!("listening on http://{}", address); axum::serve(lister, app.into_make_service()) .await .map_err(internal_error) .unwrap(); } async fn home() -> impl IntoResponse { frontend::HomeTemplate } async fn users( State(state): State>, ) -> Result>, (StatusCode, Json)> { use db::schema::users::dsl::*; let conn = state.database.get().await.unwrap(); let result = conn .interact(move |conn| users.load(conn)) .await .map_err(internal_error)? .map_err(internal_error)?; Ok(Json(result)) } async fn static_handler(uri: Uri) -> impl IntoResponse { let mut path = uri.path().trim_start_matches('/').to_string(); if path.starts_with("assets/") { path = path.replace("assets/", ""); } StaticFile(path) } pub struct StaticFile(pub T); impl IntoResponse for StaticFile where T: Into, { fn into_response(self) -> Response { let path = self.0.into(); match frontend::Assets::get(path.as_str()) { Some(content) => { let mime = mime_guess::from_path(path).first_or_octet_stream(); ([(CONTENT_TYPE, mime.as_ref())], content.data).into_response() } None => (StatusCode::NOT_FOUND, "404 Not Found").into_response(), } } } /* create_user( connection, "L-Nafaryus", "asdasd", "L-Nafaryus", "l.nafaryus@elnafo.ru", true, ); let results = users .select(User::as_select()) .load(connection) .expect("Error loading users"); println!("Found {} users", results.len()); */