User profile reads/writes routed through backend API instead of Supabase direct
marklok replaces direct Supabase queries in `UserProfileContext.tsx` with `GET` and `PATCH /user/profile` backend endpoints. The PATCH handler adds an explicit column allowlist, narrowing what the browser can touch compared to the previous RLS-only approach.
The new GET /user/profile returns the calling user's row from user_profiles. PATCH /user/profile accepts only fields on an explicit allowlist: display_name, organisation, tabular_model, claude_api_key, gemini_api_key, message_credits_used, credits_reset_date. Anything else is silently dropped. That's a security improvement over the old pattern, where the browser wrote directly to Supabase and RLS was the only guard.
Two things to examine before adopting. The allowlist includes claude_api_key and gemini_api_key, meaning API keys transit as request body through the backend. The diff shows no log-redaction, so those values could end up in server logs. message_credits_used is also client-writable through the same endpoint, which lets a client manipulate their own credit count unless something downstream (RLS, triggers) prevents it - nothing in this diff shows that protection.
The context-side changes are mechanical. 215 lines removed, 137 added. mapProfileRow, getAccessToken, and apiFetch are extracted as module-level helpers, and the credit-reset math moves into mapProfileRow where it's easier to read than the old inline version.
The pattern here fits a broader architectural direction: moving Supabase-direct browser access behind the backend, probably for audit logging, validation, and eventual multi-tenant use. Worth watching whether marklok does the same for other tables.
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?