simple-web-app

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit fac5c193cb0faed029c049eaa4538f8bf9269144
parent ae4c3f76eef6a15a54860d6283eec80960e22651
Author: Silas Brack <silasbrack@gmail.com>
Date:   Sun,  7 Jun 2026 14:25:43 +0200

feat: implement user registration via trailbase API

- Call POST /api/auth/v1/register instead of login for registration
- Add reqwest dependency for direct HTTP calls to trailbase
- Add password confirmation field to register form
- Validate passwords match before submitting

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Diffstat:
MCargo.lock | 192++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
MCargo.toml | 1+
Msrc/handlers/auth.rs | 5+++++
Msrc/trailbase.rs | 29+++++++++++++++++++++++++++--
Mtemplates/register.html | 5+++++
Mtraildepot/config.textproto | 4++--
6 files changed, 231 insertions(+), 5 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -517,6 +517,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] name = "eventsource-stream" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -562,6 +572,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] name = "form_urlencoded" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -833,6 +858,22 @@ dependencies = [ ] [[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] name = "hyper-util" version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1125,6 +1166,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] name = "litemap" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1197,6 +1244,23 @@ dependencies = [ ] [[package]] +name = "native-tls" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1265,12 +1329,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] +name = "openssl" +version = "0.10.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] name = "openssl-probe" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] +name = "openssl-sys" +version = "0.9.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] name = "p256" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1366,6 +1467,12 @@ dependencies = [ ] [[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] name = "potential_utf" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1548,6 +1655,46 @@ dependencies = [ [[package]] name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "reqwest" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" @@ -1647,6 +1794,19 @@ dependencies = [ ] [[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] name = "rustls" version = "0.23.39" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1912,6 +2072,7 @@ dependencies = [ "axum", "axum-extra", "chrono", + "reqwest 0.12.28", "serde", "serde_json", "thiserror 2.0.18", @@ -2024,6 +2185,19 @@ dependencies = [ ] [[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2146,6 +2320,16 @@ dependencies = [ ] [[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] name = "tokio-rustls" version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2260,7 +2444,7 @@ dependencies = [ "http-body-util", "jsonwebtoken", "parking_lot", - "reqwest", + "reqwest 0.13.2", "serde", "serde_json", "serde_repr", @@ -2318,6 +2502,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] name = "version_check" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" diff --git a/Cargo.toml b/Cargo.toml @@ -13,5 +13,6 @@ serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "2" tracing = "0.1" +reqwest = { version = "0.12", features = ["json"] } trailbase-client = "0.8" url = "2" diff --git a/src/handlers/auth.rs b/src/handlers/auth.rs @@ -82,6 +82,7 @@ pub struct RegisterForm { pub username: String, pub email: String, pub password: String, + pub password_confirm: String, } pub async fn register( @@ -113,6 +114,10 @@ pub async fn register( )); } + if form.password != form.password_confirm { + return Err(render_register_error(&form, "Passwords do not match")); + } + let client = trailbase::new_client(&state.trailbase_url).map_err(|e| { render_register_error(&form, &format!("Client error: {}", e)) })?; diff --git a/src/trailbase.rs b/src/trailbase.rs @@ -644,8 +644,33 @@ pub async fn login(client: &Client, email: &str, password: &str) -> Result<Store } pub async fn register(client: &Client, email: &str, password: &str) -> Result<StoredTokens, ClientError> { - // Try to register - the client might expose this or we handle it differently - // For now, attempt login which may auto-create if configured + #[derive(Serialize)] + struct RegisterRequest<'a> { + email: &'a str, + password: &'a str, + password_repeat: &'a str, + } + + let url = format!("{}api/auth/v1/register", client.base_url()); + let http_client = reqwest::Client::new(); + let response = http_client + .post(&url) + .json(&RegisterRequest { + email, + password, + password_repeat: password, + }) + .send() + .await + .map_err(|e| ClientError::Auth(format!("HTTP error: {}", e)))?; + + if !response.status().is_success() { + let status = response.status(); + let body = response.text().await.unwrap_or_default(); + return Err(ClientError::Auth(format!("Registration failed ({}): {}", status, body))); + } + + // After registration, log in to get tokens client.login(email, password).await?; let tokens = client.tokens().ok_or_else(|| { diff --git a/templates/register.html b/templates/register.html @@ -24,6 +24,11 @@ <input type="password" id="password" name="password" required minlength="8" autocomplete="new-password" /> </div> + <div class="form-group"> + <label for="password_confirm">Confirm Password</label> + <input type="password" id="password_confirm" name="password_confirm" required minlength="8" autocomplete="new-password" /> + </div> + <div class="form-actions"> <button type="submit">Register</button> </div> diff --git a/traildepot/config.textproto b/traildepot/config.textproto @@ -36,7 +36,6 @@ record_apis: [{ }, { name: "vote" table_name: "vote" - acl_world: [] acl_authenticated: [READ, CREATE, DELETE] delete_access_rule: "_ROW_.user_id = _USER_.id" }, { @@ -45,4 +44,4 @@ record_apis: [{ acl_world: [READ] acl_authenticated: [READ, CREATE, UPDATE] update_access_rule: "_ROW_.user_id = _USER_.id" -}] +}] +\ No newline at end of file