Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

All notable changes to Workout Lens are documented here.

## [Unreleased]

### Developer / Infrastructure
- **Fix sporty sync writes blocked by missing User-Agent** — Supabase rejects POST and DELETE requests from the `sb_secret` service role key when no `User-Agent` header is present (treats the request as a browser). Azure Functions' built-in `fetch` sends no User-Agent, so the cleanup DELETE and upsert POST in `sportySync.js` were silently failing after each sync — the cleanup wiped future rows, then the upsert failed to re-insert them, leaving `gym_calendar` empty from June 6 onwards. Added `User-Agent: WorkoutLens/1.0 sporty-sync (Azure Functions)` to both requests. A post-deploy manual backfill is needed to restore June data.

## [1.5.16] — 2026-05-19

### Accessibility
Expand Down
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,6 @@ Preview branches apply all migrations on a fresh empty DB. Delta migrations fail

### #237 — Excess anon grants + duplicate RLS policies
Default `GRANT ALL` gives anon TRUNCATE (bypasses RLS). **Only grant what PostgREST needs.** Always `DROP POLICY IF EXISTS` old policies when replacing them.

### #268 — Supabase blocks sb_secret writes without User-Agent
Supabase treats POST/DELETE with an `sb_secret` service role key and no `User-Agent` as a browser request and returns 403 "Forbidden use of secret API key in browser". Azure Functions' built-in `fetch` sends no User-Agent by default. **Always add `'User-Agent': 'WorkoutLens/1.0 sporty-sync (Azure Functions)'` to every write request (POST, DELETE, PATCH) that uses the service role key.** GET requests are unaffected.
2 changes: 2 additions & 0 deletions app/api/sportySync.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ async function syncGymCalendar(context, { shiftDays = 0, daysBack = 0 } = {}) {
'apikey': serviceKey,
'Authorization': `Bearer ${serviceKey}`,
'Prefer': 'return=minimal',
'User-Agent': 'WorkoutLens/1.0 sporty-sync (Azure Functions)',
},
}
);
Expand All @@ -132,6 +133,7 @@ async function syncGymCalendar(context, { shiftDays = 0, daysBack = 0 } = {}) {
'apikey': serviceKey,
'Authorization': `Bearer ${serviceKey}`,
'Prefer': 'resolution=merge-duplicates,return=minimal',
'User-Agent': 'WorkoutLens/1.0 sporty-sync (Azure Functions)',
},
body: JSON.stringify(rows),
}
Expand Down