tia/drizzle
Mannu 6bdaade777 feat: email verification + Google OAuth
- Signup now creates unverified users and sends a verification email
  (Resend); dev falls back to [VERIFY-LINK] console log
- /api/auth/verify-email: single-use token handler, mints tia_session
  on success, redirects to /onboarding
- /api/auth/resend-verification: rate-limited (3/hr), enumeration-safe
- Sign-in gated on email_verified — unverified accounts get 403 with
  needsVerification flag so the UI can show the resend button
- Google OAuth via arctic v3: PKCE + state anti-CSRF, find-or-create
  user, writes accounts row, mints tia_session
- Login page: Google button, check-email screen, resend link on 403
- drizzle/0005_email_verification.sql: creates email_verifications
  table + backfills all existing users as verified (runs automatically
  on container start before app boots)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 12:56:02 +05:30
..
manual feat(wardrobe): add complete wardrobe feature (W0–W9) 2026-05-23 18:09:22 +05:30
meta feat: email verification + Google OAuth 2026-05-24 12:56:02 +05:30
0000_baseline_prod_2026_05_19.sql chore(db): regenerate baseline migration from corrected schema 2026-05-23 12:25:20 +05:30
0001_wardrobe_tables.sql feat(wardrobe): add complete wardrobe feature (W0–W9) 2026-05-23 18:09:22 +05:30
0002_outfits_table.sql feat(wardrobe): add complete wardrobe feature (W0–W9) 2026-05-23 18:09:22 +05:30
0003_circles.sql Fix migration: add statement-breakpoints + use superuser URL 2026-05-24 01:23:06 +05:30
0004_circle_invite_email.sql feat: email-based circle invites with in-app notifications 2026-05-24 02:05:10 +05:30
0005_email_verification.sql feat: email verification + Google OAuth 2026-05-24 12:56:02 +05:30
README.md chore(db): regenerate baseline migration from corrected schema 2026-05-23 12:25:20 +05:30

Tia — Database Migrations

This folder is source code and is committed to git. It is consumed by the deploy pipeline (pnpm db:migrate, run on container start — see Dockerfile).

Baseline reset — 2026-05-19

The project's first 16 migrations (00000015) plus a manual/ folder were hand-rolled SQL applied directly via the Dokploy database terminal. They were never run through Drizzle's migrator, so:

  • prod had no __drizzle_migrations tracking table;
  • the drizzle/ folder was gitignored, so migration SQL never reached the server;
  • schema.ts had drifted well behind the real production schema.

To fix this we performed a Path A baseline reset:

  1. pg_dump backup of prod taken and stored off-server.
  2. drizzle-kit pull introspected the live prod schema (35 tables).
  3. src/db/schema/*.ts was rewritten to match prod exactly.
  4. Legacy migrations were archived to _archived_pre_baseline_2026-05-19/ (also retained in git history).
  5. A single fresh baseline — 0000_baseline_prod_2026_05_19.sql — was generated and verified column-for-column against the introspected prod schema.
  6. Prod's drizzle.__drizzle_migrations table was created and seeded with one row marking 0000_baseline_prod_2026_05_19 as already applied, so the migrator treats prod as up-to-date and runs nothing on the next deploy.

Normal workflow from here

# 1. Edit src/db/schema/*.ts
# 2. Generate a migration from the diff:
pnpm db:generate            # writes drizzle/000N_<name>.sql
# 3. Review the generated SQL by eye.
# 4. Apply locally against the dev DB:
pnpm db:migrate
# 5. Commit schema + migration together, then push.
#    Dokploy redeploys; the migrator applies it in prod on container start.

Hard rules

  • Never edit a migration file after it has been pushed. Fix-forward with a new migration instead.
  • Never run schema-changing SQL directly against prod. It becomes drift.
  • The drizzle/ folder must stay out of .gitignore.

RLS policies

Five log tables (feeds, diapers_logs, sleeps, vaccinations, growth) plus children / family_members carry row-level-security policies in prod. These are not modelled in the pgTable definitions and are managed separately in the database. Drizzle migrations will not recreate them — keep that in mind if you ever rebuild the DB from scratch.