fix(gallery): implement voting JS + fix change-vote server path
Voting was completely broken: gallery.el referenced d8251f5e5aa1.js which was never written. Buttons rendered disabled with no JS to enable them, load vote state, or call /api/vote. Fix 1 — client: inline the voting script directly in gallery.el. Initializes Supabase client from window.NEURON_CFG, calls /api/vote-state/<id> on load (with JWT if signed in) to populate scores and active states, wires vote buttons with toggle logic (same direction = undo/none), handles sign-in modal with magic-link flow, re-loads all vote states on auth state change. Fix 2 — server: replace supabase_upsert_user (upsert via user JWT) with delete-then-insert. Upsert requires both INSERT + UPDATE RLS policies; the UPDATE policy is typically absent on share_votes. Delete (user JWT, RLS-safe) + insert (service key, user already auth-validated) is reliable for both new votes and vote changes.
This commit is contained in:
+10
-4
@@ -1448,11 +1448,17 @@ fn handle_request(method: String, path: String, body: String) -> String {
|
||||
let del_url: String = v_sb_url + "/rest/v1/share_votes?share_id=eq." + v_id + "&user_id=eq." + v_uid
|
||||
let _del_resp: String = http_delete_auth(del_url, v_jwt, v_anon)
|
||||
} else {
|
||||
// up/down - upsert. PostgREST resolves on the (share_id, user_id) PK
|
||||
// when on_conflict matches. The trigger fires on insert and update.
|
||||
// up/down — delete any existing vote first (idempotent), then insert fresh.
|
||||
// Upsert via user JWT requires both INSERT + UPDATE RLS policies; the
|
||||
// UPDATE policy is often absent. Delete-then-insert is more reliable:
|
||||
// user JWT covers the DELETE (auth.uid()=user_id RLS passes),
|
||||
// service key covers the INSERT (bypasses RLS; user identity already
|
||||
// validated above via supabase_auth_user). The recalc trigger fires on
|
||||
// both operations and keeps share_cards.score accurate.
|
||||
let del_url: String = v_sb_url + "/rest/v1/share_votes?share_id=eq." + v_id + "&user_id=eq." + v_uid
|
||||
let _del_r: String = http_delete_auth(del_url, v_jwt, v_anon)
|
||||
let row: String = "{\"share_id\":\"" + v_id + "\",\"user_id\":\"" + v_uid + "\",\"direction\":\"" + v_dir + "\"}"
|
||||
let up_path: String = "share_votes?on_conflict=share_id,user_id"
|
||||
let _up_resp: String = supabase_upsert_user(v_sb_url, v_anon, v_jwt, up_path, row)
|
||||
let _ins_r: String = supabase_insert(v_sb_url, v_service, "share_votes", row)
|
||||
}
|
||||
// Re-fetch fresh aggregate from share_cards (service key - public read).
|
||||
// PostgREST returns a JSON array; use json_array_get(0) then json_get.
|
||||
|
||||
Reference in New Issue
Block a user