simple-web-app

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

commit ecd11e20284f89142f972d1bc56fa5baec352b16
parent 72e86b9d67844790a1eede1a0cd5e03b6d6d302b
Author: Silas Brack <silasbrack@gmail.com>
Date:   Sat, 25 Apr 2026 15:03:47 +0200

fix: use client.user().sub for vote user_id

- TrailBase client provides the user ID via client.user()
- Remove manual JWT parsing and base64 dependency
- Pass user_id from client directly to CreateVote

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Diffstat:
MCargo.lock | 1-
MCargo.toml | 1-
Msrc/trailbase.rs | 22+++-------------------
Mtraildepot/config.textproto | 2+-
4 files changed, 4 insertions(+), 22 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -1911,7 +1911,6 @@ dependencies = [ "askama", "axum", "axum-extra", - "base64", "chrono", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" axum = "0.8" axum-extra = { version = "0.10", features = ["cookie"] } askama = "0.12" -base64 = "0.22" chrono = { version = "0.4", features = ["serde"] } tokio = { version = "1", features = ["macros", "rt-multi-thread", "net"] } serde = { version = "1", features = ["derive"] } diff --git a/src/trailbase.rs b/src/trailbase.rs @@ -31,25 +31,6 @@ pub struct StoredTokens { pub refresh_token: Option<String>, } -/// Extract user ID from JWT auth token -pub fn extract_user_id_from_token(tokens: &StoredTokens) -> Option<String> { - use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD}; - - // JWT format: header.payload.signature - let parts: Vec<&str> = tokens.auth_token.split('.').collect(); - if parts.len() != 3 { - return None; - } - - // Decode the payload (second part) - let payload = URL_SAFE_NO_PAD.decode(parts[1]).ok()?; - let payload_str = String::from_utf8(payload).ok()?; - - // Parse as JSON and extract "sub" claim - let json: serde_json::Value = serde_json::from_str(&payload_str).ok()?; - json.get("sub").and_then(|v| v.as_str()).map(|s| s.to_string()) -} - /// Create a new unauthenticated client pub fn new_client(base_url: &str) -> Result<Client, ClientError> { Ok(Client::new(base_url, None)?) @@ -291,12 +272,15 @@ pub async fn calculate_comment_path( /// Create a vote #[derive(Debug, Serialize)] struct CreateVote { + user_id: String, target_type: i64, target_id: i64, } pub async fn vote(client: &Client, target_type: i64, target_id: i64) -> Result<(), ClientError> { + let user = client.user().ok_or_else(|| ClientError::Auth("Not authenticated".to_string()))?; let vote_data = CreateVote { + user_id: user.sub, target_type, target_id, }; diff --git a/traildepot/config.textproto b/traildepot/config.textproto @@ -40,7 +40,7 @@ record_apis: [{ table_name: "vote" acl_world: [] acl_authenticated: [READ, CREATE, DELETE] - autofill_missing_user_id_column: "user_id" + create_access_rule: "_REQ_.user_id = _USER_.id" delete_access_rule: "_ROW_.user_id = _USER_.id" }, { name: "user_profile"