2 KiB
2 KiB
| type | parent | status | tags | |||
|---|---|---|---|---|---|---|
| project-doc | Tia | verified |
|
🏗️ Tia — Architecture
Verified from CLAUDE.md + memory files.
Stack
Next.js 16 (App Router, Turbopack) · PostgreSQL 16 + pgvector (pgvector/pgvector:pg18 on prod) · Drizzle ORM · LiteLLM gateway → MiniMax (minimax-2.7) · Cloudflare R2 · Resend · Tailwind v4 · Dokploy (auto-migrate on deploy).
Route groups (Decision Log#TD-005)
(marketing)/→/,/pricing,/privacy,/terms— static, no DB/auth imports(app)/→ authenticated app with ThemeProvider + FamilyProvider + BottomNav; dashboard at/home
Auth
- User:
/login→POST /api/auth/signin(bcrypt) → session row → httpOnlytia_session; routes userequireFamily() - Admin:
/admin-login→tia_admin_session; admin layout is a server component callingverifyAdminSession()
Data & context
FamilyProviderresolves family/child from session:{ familyId, child, children, tier, memberCount, updateChildImage }- Quota logic in
src/lib/quota.ts(pure fns + queries); usage always derived viaSUM(Decision Log#TD-001)
Migrations (the big gotcha) (Decision Log#TD-003)
migrate.mjsruns beforeserver.js; reads all SQL indrizzle/, applies any not in__drizzle_migrationswhenmust beDate.now()(> 1779539431897) or Drizzle silently skips it- Hot-apply via
POST /api/debug-migration(headerx-run-migration: yes), idempotent steps
Media (R2) (Decision Log#TD-004)
- 3-step upload: init → server-proxied
PUT /api/upload→ save URL - Never direct cross-origin PUT; never raw
*.r2.devin<img>— use/api/img?key=proxy
Observability (admin)
error_events table + logError() + POST /api/errors; /admin/{health,errors,audit,ai} over audit_log, ai_usage (p95 via percentile_cont).