diff --git a/Cargo.lock b/Cargo.lock index 075a08b..1b248de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -290,6 +290,61 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.2", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde 1.0.210", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.2", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "az" version = "1.2.1" @@ -1883,6 +1938,29 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.1.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.9.5" @@ -1913,7 +1991,7 @@ dependencies = [ "futures-util", "h2", "http 0.2.12", - "http-body", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1925,6 +2003,25 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -1932,12 +2029,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.30", "native-tls", "tokio", "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-util", + "http 1.1.0", + "http-body 1.0.1", + "hyper 1.5.2", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "iana-time-zone" version = "0.1.61" @@ -2284,6 +2397,12 @@ dependencies = [ "value-bag", ] +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "md-5" version = "0.10.6" @@ -3118,8 +3237,8 @@ dependencies = [ "futures-util", "h2", "http 0.2.12", - "http-body", - "hyper", + "http-body 0.4.6", + "hyper 0.14.30", "hyper-tls", "ipnet", "js-sys", @@ -3133,7 +3252,7 @@ dependencies = [ "serde 1.0.210", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", @@ -3240,6 +3359,12 @@ dependencies = [ "base64 0.21.7", ] +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + [[package]] name = "rusty-fork" version = "0.3.0" @@ -3369,6 +3494,16 @@ dependencies = [ "serde 1.0.210", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde 1.0.210", +] + [[package]] name = "serde_spanned" version = "0.6.8" @@ -3800,6 +3935,12 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + [[package]] name = "system-configuration" version = "0.5.1" @@ -4065,6 +4206,28 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 1.0.2", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -4336,9 +4499,9 @@ dependencies = [ "async-std", "async-trait", "authdb", + "axum", "cool_asserts", "futures", - "http 1.1.0", "include_dir", "lazy_static", "mime", @@ -4355,7 +4518,6 @@ dependencies = [ "typeshare", "urlencoding", "uuid 1.11.0", - "warp", ] [[package]] @@ -4387,7 +4549,7 @@ dependencies = [ "futures-util", "headers", "http 0.2.12", - "hyper", + "hyper 0.14.30", "log", "mime", "mime_guess", diff --git a/visions/server/Cargo.toml b/visions/server/Cargo.toml index c9339a8..fd52875 100644 --- a/visions/server/Cargo.toml +++ b/visions/server/Cargo.toml @@ -9,13 +9,13 @@ edition = "2021" async-std = { version = "1.13.0" } async-trait = { version = "0.1.83" } authdb = { path = "../../authdb/" } +axum = { version = "0.7.9" } futures = { version = "0.3.31" } -http = { version = "1" } include_dir = { version = "0.7.4" } lazy_static = { version = "1.5.0" } mime = { version = "0.3.17" } mime_guess = { version = "2.0.5" } -pretty_env_logger = "0.5.0" +pretty_env_logger = { version = "0.5.0" } result-extended = { path = "../../result-extended" } rusqlite = { version = "0.32.1" } rusqlite_migration = { version = "1.3.1", features = ["from-directory"] } @@ -27,7 +27,6 @@ tokio-stream = { version = "0.1.16" } typeshare = { version = "1.0.4" } urlencoding = { version = "2.1.3" } uuid = { version = "1.11.0", features = ["v4"] } -warp = { version = "0.3" } [dev-dependencies] cool_asserts = "2.0.3" diff --git a/visions/server/src/filters/mod.rs b/visions/server/src/filters/mod.rs index 8964fb0..a433057 100644 --- a/visions/server/src/filters/mod.rs +++ b/visions/server/src/filters/mod.rs @@ -1,19 +1,9 @@ mod user_management; -pub use user_management::routes_user_management; - -use warp::{ - filters::cors::Builder, - http::{header::CONTENT_TYPE, HeaderName, Method, Response}, - reply::*, - Filter, -}; +// pub use user_management::routes_user_management; use crate::{ asset_db::AssetId, core::Core, - handlers::{ - handle_available_images, handle_check_password, handle_connect_websocket, handle_file, handle_get_charsheet, handle_register_client, handle_server_status, handle_set_background_image, handle_unregister_client, RegisterRequest - }, }; @@ -26,6 +16,7 @@ use crate::{ // // The login function does not require authentication, but it should return a session ID +/* fn cors(methods: Vec, headers: Vec) -> Builder where M: Into, @@ -36,7 +27,9 @@ where .allow_methods(methods) .allow_headers(headers) } +*/ +/* pub fn route_healthcheck() -> impl Filter + Clone { warp::path!("api" / "v1" / "healthcheck") @@ -142,3 +135,4 @@ pub fn route_authenticate( }) .with(cors::(vec![Method::PUT], vec![])) } +*/ diff --git a/visions/server/src/filters/user_management.rs b/visions/server/src/filters/user_management.rs index 6daca25..1a80bb9 100644 --- a/visions/server/src/filters/user_management.rs +++ b/visions/server/src/filters/user_management.rs @@ -1,17 +1,10 @@ use std::{convert::Infallible, future::Future}; -use warp::{ - http::{header::CONTENT_TYPE, HeaderName, Method, Response, StatusCode}, - reject, - reply::Reply, - Filter, -}; - use crate::{ core::Core, - handlers::{handle_check_password, handle_get_users, handle_set_admin_password}, }; +/* async fn handle_rejection(err: warp::Rejection) -> Result { println!("handle_rejection: {:?}", err); if let Some(Unauthorized) = err.find() { @@ -26,7 +19,9 @@ async fn handle_rejection(err: warp::Rejection) -> Result impl Filter + Clone { route_get_users(core.clone()).or(route_set_admin_password(core.clone())) } +*/ +/* pub fn route_check_password( core: Core, ) -> impl Filter + Clone { @@ -74,7 +71,9 @@ pub fn route_check_password( .then({ move |body| handle_check_password(core.clone(), body) }) .with(cors::(vec![Method::PUT], vec![])) } +*/ +/* #[cfg(test)] mod test { use std::{collections::HashMap, path::PathBuf}; @@ -180,3 +179,4 @@ mod test { unimplemented!(); } } +*/ diff --git a/visions/server/src/handlers.rs b/visions/server/src/handlers/mod.rs similarity index 89% rename from visions/server/src/handlers.rs rename to visions/server/src/handlers/mod.rs index 672ce13..b7ad690 100644 --- a/visions/server/src/handlers.rs +++ b/visions/server/src/handlers/mod.rs @@ -4,15 +4,47 @@ use futures::{SinkExt, StreamExt}; use result_extended::{error, ok, return_error, ResultExt}; use serde::{Deserialize, Serialize}; use typeshare::typeshare; -use warp::{http::Response, http::StatusCode, reply::Reply, ws::Message}; +use axum::{http::StatusCode, Json}; use crate::{ asset_db::AssetId, core::Core, - database::{CharacterId, UserId}, + database::{CharacterId, UserId, SessionId}, types::{AppError, FatalError}, }; +#[derive(Serialize)] +struct HealthCheck { + ok: bool, +} + +pub async fn healthcheck(core: Core) -> Vec { + match core.status().await { + ResultExt::Ok(s) => serde_json::to_vec(&HealthCheck { + ok: s.admin_enabled, + }) + .unwrap(), + ResultExt::Err(_) => serde_json::to_vec(&HealthCheck { ok: false }).unwrap(), + ResultExt::Fatal(err) => panic!("{}", err), + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[typeshare] +pub struct AuthRequest { + username: String, + password: String, +} + +pub async fn check_password(core: Core, req: Json) -> (StatusCode, Json>) { + let Json(AuthRequest { username, password }) = req; + match core.auth(&username, &password).await { + ResultExt::Ok(session_id) => (StatusCode::OK, Json(Some(session_id))), + ResultExt::Err(err) => (StatusCode::UNAUTHORIZED, Json(None)), + ResultExt::Fatal(err) => panic!("Fatal: {}", err), + } +} + /* pub async fn handle_auth( auth_ctx: &AuthDB, @@ -37,6 +69,7 @@ pub async fn handle_auth( } */ +/* pub async fn handler(f: F) -> impl Reply where F: Future>, AppError, FatalError>>, @@ -285,3 +318,4 @@ pub async fn handle_check_password(core: Core, auth_request: AuthRequest) -> imp }) .await } +*/ diff --git a/visions/server/src/main.rs b/visions/server/src/main.rs index 6f2d518..2ed2b87 100644 --- a/visions/server/src/main.rs +++ b/visions/server/src/main.rs @@ -1,90 +1,47 @@ -use std::{ - convert::Infallible, - net::{IpAddr, Ipv4Addr, SocketAddr}, - path::PathBuf, -}; +use core::Core; +use std::path::PathBuf; use asset_db::FsAssets; -use authdb::AuthError; +use axum::{routing::{get, post}, Router, Json}; use database::DbConn; -use filters::{route_authenticate, route_healthcheck, route_image}; -use warp::{http::StatusCode, reply::Reply, Filter}; mod asset_db; mod core; mod database; mod filters; mod handlers; +use handlers::{ healthcheck, check_password, AuthRequest}; mod types; -/* -fn with_session( - auth_ctx: Arc, -) -> impl Filter + Clone { - header("authentication").and_then({ - move |value: String| { - let auth_ctx = auth_ctx.clone(); - async move { - match auth_ctx.validate_session(SessionToken::from(value)).await { - Ok(Some(username)) => Ok(username), - Ok(None) => Err(warp::reject::custom(Unauthorized)), - Err(err) => Err(warp::reject::custom(AuthDBError(err))), - } - } - } - }) -} - -fn route_echo_unauthenticated() -> impl Filter + Clone { - warp::path!("api" / "v1" / "echo" / String).map(|param: String| { - println!("param: {}", param); - warp::reply::json(&vec!["unauthenticated", param.as_str()]) - }) -} - -fn route_authenticate( - auth_ctx: Arc, -) -> impl Filter + Clone { - let auth_ctx = auth_ctx.clone(); - warp::path!("api" / "v1" / "auth") - .and(warp::post()) - .and(warp::body::json()) - .map(move |param: AuthToken| { - let res = handle_auth(&auth_ctx, param.clone()); - warp::reply::json(¶m) - }) -} - -fn route_echo_authenticated( - auth_ctx: Arc, -) -> impl Filter + Clone { - warp::path!("api" / "v1" / "echo" / String) - .and(with_session(auth_ctx.clone())) - .map(move |param: String, username: Username| { - println!("param: {:?}", username); - println!("param: {}", param); - warp::reply::json(&vec!["authenticated", username.as_str(), param.as_str()]) - }) -} -*/ - #[tokio::main] pub async fn main() { + /* pretty_env_logger::init(); - let conn = DbConn::new(Some("/home/savanni/game.db")); - - let core = core::Core::new(FsAssets::new(PathBuf::from("/home/savanni/Pictures")), conn); - let unauthenticated_endpoints = route_healthcheck().or(route_authenticate(core.clone())); let authenticated_endpoints = route_image(core.clone()); + */ - let server = warp::serve( - unauthenticated_endpoints - .or(authenticated_endpoints) - .with(warp::log("visions")) + let conn = DbConn::new(Some("/home/savanni/game.db")); + let core = Core::new(FsAssets::new(PathBuf::from("/home/savanni/Pictures")), conn); + + let app = Router::new().route( + "/api/v1/health", + get({ + let core = core.clone(); + move || healthcheck(core) + }), + ).route( + "/api/v1/auth", + post({ + let core = core.clone(); + move |req: Json| handlers::check_password(core, req) + }), ); - server - .run(SocketAddr::new(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)), 8001)) - .await; + + let listener = tokio::net::TcpListener::bind("127.0.0.1:8001") + .await + .unwrap(); + + axum::serve(listener, app).await.unwrap(); }