feat: added HMAC sheet sync endpoint #2

Merged
kritdass merged 1 commit from feat/sheet-sync into main 2026-10-15 18:57:57 +00:00
Member

Adds the Sheet → SQLite sync. An Apps Script on the submission Sheet sends all rows to POST /api/sync every 2 minutes, signed with HMAC-SHA256 using a shared secret.

Changes

  • src/lib/server/parse.ts: reads rows by header name; trims names, splits members and emails, lowercases emails
  • src/lib/server/sync.ts: updates teams and members in one transaction; teams removed from the Sheet are marked inactive instead of deleted
  • src/lib/server/hmac.ts: checks the signature over timestamp.body and rejects requests older than 5 minutes
  • src/routes/api/sync/+server.ts: the endpoint; checks the signature before reading the data
  • scripts/apps-script.gs: the script to add to the Sheet (setup steps in the file)
  • scripts/send-test-sync.ts: local stand-in for the Apps Script, for testing
  • secretspec.toml: adds SYNC_HMAC_SECRET

Tested locally

  • Verified with the real dev secret from OpenBao
  • 3 teams synced; names trimmed, members split, emails lowercased
  • Two teams with the same timestamp don't overwrite each other (key is timestamp + team name)
  • Rerunning keeps the same team IDs
  • Removed team → active = 0, not deleted
  • Wrong secret → 401
  • Request older than 5 minutes → 401
  • Missing "Team Name" column → 500, and no teams are hidden

Needs before merge

  • SYNC_HMAC_SECRET set in OpenBao for preview and prod (I only have dev set up locally). The preview deploy will fail until then.
Adds the Sheet → SQLite sync. An Apps Script on the submission Sheet sends all rows to `POST /api/sync` every 2 minutes, signed with HMAC-SHA256 using a shared secret. ## Changes - `src/lib/server/parse.ts`: reads rows by header name; trims names, splits members and emails, lowercases emails - `src/lib/server/sync.ts`: updates teams and members in one transaction; teams removed from the Sheet are marked inactive instead of deleted - `src/lib/server/hmac.ts`: checks the signature over `timestamp.body` and rejects requests older than 5 minutes - `src/routes/api/sync/+server.ts`: the endpoint; checks the signature before reading the data - `scripts/apps-script.gs`: the script to add to the Sheet (setup steps in the file) - `scripts/send-test-sync.ts`: local stand-in for the Apps Script, for testing - `secretspec.toml`: adds `SYNC_HMAC_SECRET` ## Tested locally - Verified with the real dev secret from OpenBao - 3 teams synced; names trimmed, members split, emails lowercased - Two teams with the same timestamp don't overwrite each other (key is timestamp + team name) - Rerunning keeps the same team IDs - Removed team → `active = 0`, not deleted - Wrong secret → 401 - Request older than 5 minutes → 401 - Missing "Team Name" column → 500, and no teams are hidden ## Needs before merge - `SYNC_HMAC_SECRET` set in OpenBao for **preview** and **prod** (I only have dev set up locally). The preview deploy will fail until then.

Kennel Deployments

Torn down at 2026-10-15 18:57:59 UTC.

<!-- kennel-deployment --> ### Kennel Deployments Torn down at 2026-10-15 18:57:59 UTC.
feat: add HMAC sheet sync endpoint
All checks were successful
CI / check-1 (pull_request) Successful in 1m34s
CI / build (pull_request) Successful in 1m20s
CI / check (pull_request) Successful in 0s
kennel/build build succeeded
kennel/deploy deployment healthy
CI / check-1 (push) Successful in 1m37s
CI / build (push) Successful in 1m3s
CI / check (push) Successful in 0s
03d3decaae
kritdass changed title from WIP: feat: added HMAC sheet sync endpoint to feat: added HMAC sheet sync endpoint 2026-10-15 18:57:40 +00:00
kritdass deleted branch feat/sheet-sync 2026-10-15 18:57:57 +00:00
Sign in to join this conversation.
No description provided.