Migrate infrastructure to AWS (SST + Fargate + RDS + Clerk + S3 + SES) (#1)
* docs: add CLAUDE.md for repo navigation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* infra: scaffold SST + Fargate Dockerfile
* db: add Drizzle schema and migration, swap supabase-js dep
Translates the Supabase schema in backend/schema.sql to a Drizzle ORM
definition targeting AWS RDS/Aurora Postgres. Lays the foundation for
Stages D/E, which rewrite the routes and middleware off supabase-js.
- backend/src/db/schema.ts: 16 tables translated 1:1 with schema.sql.
user_id columns that previously referenced auth.users(id) are now
plain text (Clerk owns identity). RLS policies, the handle_new_user
trigger, and Supabase grants are not modelled - access control moves
to the Express layer.
- backend/drizzle.config.ts: drizzle-kit config with a dummy default
DATABASE_URL so `generate` works without env setup.
- backend/drizzle/0000_init.sql: initial migration with all tables,
indexes (including GIN on shared_with), check constraints, and
foreign keys. Prepended CREATE EXTENSION pgcrypto. Manually appended
the two circular FKs (documents.current_version_id and
document_edits.chat_message_id) that Drizzle could not emit inline.
- backend/package.json: dropped @supabase/supabase-js; added
drizzle-orm, drizzle-kit, pg, @types/pg, @clerk/backend. Added
db:generate / db:migrate / db:push / db:studio scripts.
The legacy backend/schema.sql is kept in place until upstream merges
clear; Stage D will start removing supabase.ts and the route consumers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* auth: swap Supabase verification for Clerk JWT + add Drizzle client
- Drop @supabase/supabase-js from middleware/auth.ts; verify Clerk JWTs
with @clerk/backend (verifyToken), supporting both JWKS and offline
jwtKey verification.
- First-request profile bootstrap (in-memory cache) replaces the old
Supabase handle_new_user trigger.
- Add backend/src/lib/db.ts exporting Drizzle client + pg.Pool +
withTransaction helper.
- Delete backend/src/lib/supabase.ts. Routes and several libs still
import it; Stages E1/E2 will rewrite them.
tsc --noEmit fails (42 errors), all confined to files Stage E1/E2 will
rewrite. db.ts and middleware/auth.ts typecheck cleanly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* routes: migrate simpler routes to Drizzle (Stage E1)
Rewrite user, downloads, workflows, and projectChat routes - plus the
access, userSettings, and userApiKeys libs - to query Postgres directly
via Drizzle instead of Supabase. Brings the backend tsc error count from
40 to 24; remaining errors are all in Stage E2 files (chat, projects,
documents, tabular, chatTools, documentVersions).
- Lib helper signatures keep a trailing optional `_db` arg so out-of-scope
E2 callers don't all immediately break on extra-argument errors before
Stage E2 lands.
- Workflows route now backfills sharer display names via the Clerk
Backend API instead of the Supabase admin listUsers call.
- `/user/account` no longer calls supabase.auth.admin.deleteUser; it
removes the local profile row and leaves Clerk-side deletion to a
separate path.
- userApiKeys keeps the AES-GCM crypto unchanged; only the DB plumbing
swapped to Drizzle's onConflictDoUpdate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* routes: migrate heavy routes + chatTools to Drizzle (Stage E2)
Rewrites projects, documents, tabular, chat, chatTools, and
documentVersions to use Drizzle queries against the shared db
singleton. Drops the trailing _db?: unknown bridge parameter that
Stage E1 added to access/userSettings/userApiKeys helpers.
Behavioral notes:
- Member email->id lookups in projects.ts and tabular.ts now use
Clerk's users.getUserList({ emailAddress: [...] }) and
users.getUser(id) instead of Supabase's auth.admin.listUsers.
- chat.ts /chat OR-filter converted from Supabase's PostgREST .or()
string syntax to Drizzle's or(eq, inArray).
- Dead buildTabularContext helper removed from tabular.ts (no
callers anywhere).
- jsonb inserts retain as any casts to match Stage E1 pattern.
tsc --noEmit -p backend: 0 errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* storage+email: swap R2 for S3, Resend for SES (Stage F)
- Rewrite backend/src/lib/storage.ts to use native AWS S3 via the default credential chain (Fargate task role). Drop R2 endpoint, forcePathStyle, and explicit access-key env vars. BUCKET reads S3_BUCKET_NAME with a transitional R2_BUCKET_NAME fallback.
- Add backend/src/lib/email.ts wrapping @aws-sdk/client-sesv2 with a sendEmail helper that no-ops when SES_FROM_ADDRESS is unset.
- Swap deps in backend/package.json: drop resend, add @aws-sdk/client-sesv2. Refresh package-lock.json.
- No existing Resend call sites in backend/src - helper added for future use.
tsc --noEmit -p backend: 0 errors.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* frontend: swap Supabase auth for Clerk (Stage G)
Replaces every frontend Supabase auth touchpoint with Clerk equivalents. ClerkProvider wraps the app, clerkMiddleware protects routes, and Clerk's hosted SignIn/SignUp components render at /login and /signup via catch-all segments. mikeApi reads the bearer token from window.Clerk.session, and hooks/components that previously called supabase.auth.getSession() now use useAuth().getToken() from @clerk/nextjs. AuthContext is removed; consumers read user/userId from Clerk's useAuth/useUser hooks directly.
Notes:
- @clerk/nextjs 7.3.3 requires react ~19.2.3; project pins 19.2.0, so npm install was run with --legacy-peer-deps. Runtime works correctly.
- tagWIdsOnRenderedDom in DocxView refactored to accept a token argument since it runs outside React hooks.
- tsc --noEmit -p frontend: clean. Lint: 104 problems (-1 vs baseline).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* frontend: swap OpenNext Cloudflare adapter for AWS (Stage H)
* docs: update README and CLAUDE.md for AWS stack (Stage I)
* docs: warn about gh pr create defaulting to upstream on forks
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* backend+frontend: delete Clerk user on account close; drop --legacy-peer-deps
- DELETE /user/account now removes the Clerk identity before nuking the
local user_profiles row. Without this the frontend's "Delete account"
flow only cleared the profile, leaving the Clerk user able to sign
back in. Exports getClerkClient + new forgetUser helper from the auth
middleware so the route can reuse the same cached Clerk client and
evict the just-deleted user from the in-process bootstrap caches.
- Bump react/react-dom to ^19.2.6 so npm install no longer needs
--legacy-peer-deps for @clerk/nextjs@7.3.3 (which peer-deps ~19.2.3).
CLAUDE.md and README.md updated to drop the workaround note.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> | Repository | LevelFive-Studio/helix-tribune |
|---|---|
| Author | Sarat Pediredla <sarat.pediredla@level5.ventures> |
| Authored | |
| Parents | 56c6051f |
| Stats | 73 files changed , +8560 , -6343 |
| Part of | AWS migration: SST + Fargate + RDS + Clerk + S3 + SES |
Capture this commit into my fork
Download a Markdown prompt that tells Claude how to port this
exact commit into your working tree. Run it via
claude -p < capture-commit-03011e63.md
from inside the repo you want the change in.