diff --git a/Cargo.lock b/Cargo.lock index 48347c0..a142fd5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -758,6 +758,33 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "autometrics" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fd5718a452b060adfb2402004a5ebd67ea90e926da637259748de0ca654ea37" +dependencies = [ + "autometrics-macros", + "cfg_aliases", + "once_cell", + "spez", + "thiserror", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "autometrics-macros" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beb4214dae229373b9f437afe2c954a1c8cfb8a64ac753a47285a759ae80284" +dependencies = [ + "percent-encoding", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "backtrace" version = "0.3.68" @@ -894,7 +921,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" dependencies = [ "borsh-derive", - "hashbrown 0.13.2", + "hashbrown 0.12.3", ] [[package]] @@ -1027,6 +1054,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + [[package]] name = "chrono" version = "0.4.26" @@ -1669,15 +1702,6 @@ dependencies = [ "ahash 0.7.6", ] -[[package]] -name = "hashbrown" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" -dependencies = [ - "ahash 0.8.3", -] - [[package]] name = "hashbrown" version = "0.14.0" @@ -2296,6 +2320,8 @@ dependencies = [ "actix-web", "argon2", "askama", + "askama_actix", + "autometrics", "futures", "jsonwebtoken", "oswilno-contract", @@ -3294,6 +3320,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "spez" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87e960f4dca2788eeb86bbdde8dd246be8948790b7618d656e68f9b720a86e8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.28", +] + [[package]] name = "spin" version = "0.5.2" diff --git a/crates/migration/Cargo.toml b/crates/migration/Cargo.toml index 51e049d..c37d9af 100644 --- a/crates/migration/Cargo.toml +++ b/crates/migration/Cargo.toml @@ -10,9 +10,9 @@ path = "src/lib.rs" [dependencies] async-std = { version = "1", features = ["attributes", "tokio1"] } -tokio = { version = "1.29.1", features = ["full"] } oswilno-contract = { path = "../oswilno-contract" } sea-orm = { version = "0.11", features = ["runtime-actix-rustls", "sqlx-postgres", "postgres-array", "sqlx"] } +tokio = { version = "1.29.1", features = ["full"] } [dependencies.sea-orm-migration] version = "0.11.0" diff --git a/crates/oswilno-admin/Cargo.toml b/crates/oswilno-admin/Cargo.toml index cd0ec10..50613ed 100644 --- a/crates/oswilno-admin/Cargo.toml +++ b/crates/oswilno-admin/Cargo.toml @@ -11,6 +11,6 @@ actix-web = "4.3.1" actix-web-grants = "3.0.2" askama = "0.12.0" chrono = "0.4.26" +oswilno-contract = { path = "../oswilno-contract" } tera = "1.17.1" uuid = { version = "1.4.1", features = ["v4"] } -oswilno-contract = { path = "../oswilno-contract" } diff --git a/crates/oswilno-contract/Cargo.toml b/crates/oswilno-contract/Cargo.toml index 46d7c72..8fbc3a4 100644 --- a/crates/oswilno-contract/Cargo.toml +++ b/crates/oswilno-contract/Cargo.toml @@ -10,8 +10,8 @@ actix = "0.13.0" actix-admin = { git = "https://code.ita-prog.pl/Tsumanu/actix-admin.git", features = ['enable-tracing'] } actix-rt = { version = "2.8.0", features = [] } chrono = "0.4.26" +oswilno-actix-admin = { path = "../oswilno-actix-admin" } regex = "1.9.1" sea-orm = { version = "0.11", features = ["postgres-array", "runtime-actix-rustls", "sqlx-postgres", "macros", "sqlx"] } serde = { version = "1.0.175", features = ["derive"] } uuid = { version = "1.4.1", features = ["v4"] } -oswilno-actix-admin = { path = "../oswilno-actix-admin" } diff --git a/crates/oswilno-parking-space/Cargo.toml b/crates/oswilno-parking-space/Cargo.toml index 1066415..8923341 100644 --- a/crates/oswilno-parking-space/Cargo.toml +++ b/crates/oswilno-parking-space/Cargo.toml @@ -8,9 +8,9 @@ edition = "2021" actix = "0.13.0" actix-web = "4.3.1" askama = { version = "0.12.0", features = ["serde", "with-actix-web", "comrak", "mime"] } -tokio = { version = "1.29.1", features = ["full"] } +askama_actix = "0.14.0" oswilno-contract = { path = "../oswilno-contract" } sea-orm = { version = "0.11.1", features = ["runtime-actix-rustls", "sqlx", "sqlx-postgres", "debug-print"] } serde = { version = "1.0.176", features = ["derive"] } serde_json = "1.0.104" -askama_actix = "0.14.0" +tokio = { version = "1.29.1", features = ["full"] } diff --git a/crates/oswilno-parking-space/src/lib.rs b/crates/oswilno-parking-space/src/lib.rs index d710ceb..3849062 100644 --- a/crates/oswilno-parking-space/src/lib.rs +++ b/crates/oswilno-parking-space/src/lib.rs @@ -23,6 +23,7 @@ struct AllParkingSpace { parking_space_by_id: HashMap, account_by_id: HashMap, } + #[derive(Template)] #[template(path = "../templates/parking-spaces/all-partial.html")] struct AllPartialParkingSpace { diff --git a/crates/oswilno-parking-space/templates/parking-spaces/all.html b/crates/oswilno-parking-space/templates/parking-spaces/all.html index c376133..3467c29 100644 --- a/crates/oswilno-parking-space/templates/parking-spaces/all.html +++ b/crates/oswilno-parking-space/templates/parking-spaces/all.html @@ -1,4 +1,5 @@ {% extends "../base.html" %} + {% block body %} {% include "./all-partial.html" %} {% endblock %} diff --git a/crates/oswilno-server/Cargo.toml b/crates/oswilno-server/Cargo.toml index 5257b02..ceac560 100644 --- a/crates/oswilno-server/Cargo.toml +++ b/crates/oswilno-server/Cargo.toml @@ -10,19 +10,19 @@ actix-web = "4.3.1" actix-web-grants = "3.0.2" askama = { version = "0.12.0", features = ["serde", "with-actix-web", "comrak", "mime"] } futures = { version = "0.3.28", features = ["futures-executor"] } +oswilno-admin = { path = "../oswilno-admin" } oswilno-config = { path = "../oswilno-config" } oswilno-parking-space = { path = "../oswilno-parking-space" } -oswilno-view = { path = "../oswilno-view" } -oswilno-admin = { path = "../oswilno-admin" } oswilno-session = { path = "../oswilno-session" } -web-assets = { path = "../web-assets" } +oswilno-view = { path = "../oswilno-view" } sea-orm = { version = "0.11", features = ["postgres-array", "runtime-actix-rustls", "sqlx-postgres"] } serde = { version = "1.0.175", features = ["derive"] } serde_json = "1.0.103" tokio = { version = "1.29.1", features = ["full"] } toml = "0.7.6" -tracing-subscriber = { version = "0.3.17", features = ["env-filter", "json"] } tracing = "0.1.37" +tracing-subscriber = { version = "0.3.17", features = ["env-filter", "json"] } +web-assets = { path = "../web-assets" } [dev-dependencies] insta = { version = "1.31.0", features = ["ron"] } diff --git a/crates/oswilno-session/Cargo.toml b/crates/oswilno-session/Cargo.toml index 5e2ca8b..9017826 100644 --- a/crates/oswilno-session/Cargo.toml +++ b/crates/oswilno-session/Cargo.toml @@ -7,17 +7,19 @@ edition = "2021" actix-http = "3.3.1" actix-jwt-authc = { version = "0.2.0", features = ["tracing", "session"] } actix-web = "4.3.1" +argon2 = "0.5.1" +askama = { version = "0.12.0", features = ["serde", "with-actix-web", "comrak", "mime"] } +askama_actix = { version = "0.14.0" } +autometrics = { version = "0.5.0", features = ["tracing", "tracing-subscriber", "thiserror"] } futures = { version = "0.3.28", features = ["thread-pool"] } jsonwebtoken = "8.3.0" +oswilno-contract = { path = "../oswilno-contract" } +oswilno-view = { path = "../oswilno-view" } rand = "0.8.5" ring = "0.16.20" +sea-orm = { version = "0.11", features = ["postgres-array", "runtime-actix-rustls", "sqlx-postgres", "macros", "sqlx"] } serde = { version = "1.0.180", features = ["derive"] } time = "0.3.24" tokio = { version = "1.29.1", features = ["full"] } -uuid = { version = "1.4.1", features = ["v4"] } -sea-orm = { version = "0.11", features = ["postgres-array", "runtime-actix-rustls", "sqlx-postgres", "macros", "sqlx"] } -oswilno-contract = { path = "../oswilno-contract" } tracing = "0.1.37" -argon2 = "0.5.1" -askama = { version = "0.12.0", features = ["serde", "with-actix-web", "comrak", "mime"] } -oswilno-view = { path = "../oswilno-view" } +uuid = { version = "1.4.1", features = ["v4"] } diff --git a/crates/oswilno-session/src/lib.rs b/crates/oswilno-session/src/lib.rs index 904ee65..3db344e 100644 --- a/crates/oswilno-session/src/lib.rs +++ b/crates/oswilno-session/src/lib.rs @@ -2,8 +2,9 @@ use std::ops::Add; use std::sync::Arc; use actix_jwt_authc::*; -use actix_web::web::{Data, Json, ServiceConfig}; +use actix_web::web::{Data, Json, ServiceConfig, Query, Form}; use actix_web::{get, post, HttpResponse}; +use autometrics::autometrics; use futures::channel::{mpsc, mpsc::Sender}; use futures::stream::Stream; use futures::SinkExt; @@ -15,6 +16,7 @@ use serde::{Deserialize, Serialize}; use time::ext::*; use time::OffsetDateTime; use tokio::sync::Mutex; +use askama_actix::Template; mod hashing; @@ -68,6 +70,7 @@ impl SessionConfigurator { .app_data(self.encoding_key) .app_data(self.jwt_ttl) .service(login) + .service(login_view) .service(logout) .service(session_info); } @@ -77,13 +80,18 @@ impl SessionConfigurator { } pub fn translations(&self, l10n: &mut oswilno_view::TranslationStorage) { - l10n.with_lang("en") + l10n + // English + .with_lang("en") .add("Sign in", "Sign in") .add("Sign up", "Sign up") + .add("Bad credentials", "Bad credentials") .done() + // Polish .with_lang("pl") .add("Sign in", "Logowanie") .add("Sign up", "Rejestracja") + .add("Bad credentials", "Złe dane uwierzytelniające") .done(); } @@ -110,21 +118,61 @@ impl SessionConfigurator { } } -#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] -pub struct SignInPayload { - login: String, - password: String, +#[derive(Template)] +#[template(path = "./sign-in/full.html")] +struct SignInTemplate { + form: SignInPayload, } +#[derive(Template)] +#[template(path = "./sign-in/partial.html")] +struct SignInPartialTemplate { + form: SignInPayload, +} + +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Default)] +pub struct SignInPayload { + #[serde(skip, default)] + errors: Vec, + + login: String, + #[serde(skip, default)] + login_errors: Vec, + + password: String, + #[serde(skip, default)] + password_errors: Vec, +} + +#[get("/login")] +async fn login_view() -> SignInTemplate { + SignInTemplate { form: SignInPayload::default() } +} + +#[autometrics] #[post("/login")] async fn login( jwt_encoding_key: Data, jwt_ttl: Data, db: Data, - payload: Json, + payload: Form, + t: Data, ) -> Result { + match login_inner(jwt_encoding_key, jwt_ttl, db, payload, t).await { + Ok(res) => Ok(HttpResponse::Ok().json(res)), + Err(form) => Ok(HttpResponse::Ok().body(SignInPartialTemplate { form }.render().unwrap())), + } +} + +async fn login_inner( + jwt_encoding_key: Data, + jwt_ttl: Data, + db: Data, + payload: Form, + t: Data, +) -> Result { let db = db.into_inner(); - let payload = payload.into_inner(); + let mut payload = payload.into_inner(); let iat = OffsetDateTime::now_utc().unix_timestamp() as usize; let expires_at = OffsetDateTime::now_utc().add(jwt_ttl.0); @@ -138,15 +186,18 @@ async fn login( { Ok(Some(a)) => a, Ok(None) => { - return Err(Error::InternalError); + payload.errors.push(t.to_lang("pl", "Bad credentials")); + return Err(payload); } Err(e) => { tracing::warn!("Failed to find account: {e}"); - return Err(Error::InternalError); + payload.errors.push(t.to_lang("pl", "Bad credentials")); + return Err(payload); } }; if hashing::verify(account.pass_hash.as_str(), payload.password.as_str()).is_err() { - return Ok(HttpResponse::Unauthorized().body("")); + payload.errors.push(t.to_lang("pl", "Bad credentials")); + return Err(payload); } let jwt_claims = Claims { @@ -161,20 +212,23 @@ async fn login( &jwt_claims, &jwt_encoding_key, ) - .map_err(|_| Error::InternalError)?; - let login_response = LoginResponse { + .map_err(|_| { + payload.errors.push(t.to_lang("pl", "Bad credentials")); + payload + })?; + Ok(LoginResponse { bearer_token: jwt_token, claims: jwt_claims, - }; - - Ok(HttpResponse::Ok().json(login_response)) + }) } +#[autometrics] #[get("/session")] async fn session_info(authenticated: UserSession) -> Result { Ok(HttpResponse::Ok().json(authenticated)) } +#[autometrics] #[get("/logout")] async fn logout( invalidated_jwts: Data, @@ -190,10 +244,11 @@ struct AccountInfo { password: String, } +#[autometrics] #[post("/register")] async fn register( db: Data, - payload: Json, + payload: Query, ) -> Result { use oswilno_contract::accounts::*; use sea_orm::ActiveValue::NotSet; diff --git a/crates/oswilno-session/template/base.html b/crates/oswilno-session/template/base.html deleted file mode 100644 index da61042..0000000 --- a/crates/oswilno-session/template/base.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - OS Wilno - - - - {% block head %} - {% endblock %} - - - -
- {% block body %} - {% endblock %} -
- - diff --git a/crates/oswilno-session/template/register/parial.html b/crates/oswilno-session/template/register/parial.html deleted file mode 100644 index 5ff46ce..0000000 --- a/crates/oswilno-session/template/register/parial.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "../base.html" %} -{% block body %} -
-
-
- - -
-
- - -
-
- - -
-
- -
-
-
-{% endblock %} diff --git a/crates/oswilno-session/template/sign-in/partial.html b/crates/oswilno-session/template/sign-in/partial.html deleted file mode 100644 index 9020317..0000000 --- a/crates/oswilno-session/template/sign-in/partial.html +++ /dev/null @@ -1,18 +0,0 @@ -{% extends "../base.html" %} -{% block body %} -
-
-
- - -
-
- - -
-
- -
-
-
-{% endblock %} diff --git a/crates/web-assets/Cargo.toml b/crates/web-assets/Cargo.toml index 5b8bbd2..f41de82 100644 --- a/crates/web-assets/Cargo.toml +++ b/crates/web-assets/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" actix = "0.13.0" actix-web = "4.3.1" askama = { version = "0.12.0", features = ["serde", "with-actix-web", "comrak", "mime"] } -tokio = { version = "1.29.1", features = ["full"] } serde = { version = "1.0.176", features = ["derive"] } serde_json = "1.0.104" +tokio = { version = "1.29.1", features = ["full"] } diff --git a/crates/web-assets/assets/app.js b/crates/web-assets/assets/app.js index 612836b..347e012 100644 --- a/crates/web-assets/assets/app.js +++ b/crates/web-assets/assets/app.js @@ -1 +1,2 @@ import './elements/oswilno-price.js'; +import("https://unpkg.com/htmx.org@1.9.4/dist/htmx.min.js"); diff --git a/crates/web-assets/build.sh b/crates/web-assets/build.sh index 4c9253f..010e69e 100755 --- a/crates/web-assets/build.sh +++ b/crates/web-assets/build.sh @@ -9,5 +9,6 @@ cd ${ROOT} mkdir assets cd ${WEB_ASSETS_ROOT} -npm install esbuild +yarn add esbuild +yarn add htmx ${WEB_ASSETS_ROOT}/node_modules/.bin/esbuild --bundle ${WEB_ASSETS_ROOT}/assets/app.js --outfile=${ROOT}/assets/build.js --minify --sourcemap diff --git a/crates/web-assets/package.json b/crates/web-assets/package.json index 94a541f..a2ecaf8 100644 --- a/crates/web-assets/package.json +++ b/crates/web-assets/package.json @@ -1,5 +1,7 @@ { "dependencies": { - "esbuild": "^0.18.17" + "esbuild": "^0.18.17", + "htmlx": "^0.1.0", + "htmx": "^0.0.2" } } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 91b6fc8..b83a18b 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,8 +1,7 @@ [toolchain] -# channel = "nightly-2022-10-29" -channel = "stable" -components = ['rust-analyzer', "rustfmt"] -targets = [] -profile = "default" - # rustc 1.66.0-nightly (5e9772042 2022-10-29) +# channel = "nightly-2023-08-02" +channel = "stable" +# channel = "stable-2022-09-22" +components = ['rust-analyzer', "rustfmt"] +