Files
Simone Cavalli 1faca1f522 docs(01-02): complete Drizzle schema plan — 10 tables live on Postgres
- SUMMARY documenta schema, deviazioni (uuid->text+nanoid, drizzle-kit env),
  threat surface e self-check PASSED
- Piano 03 (Middleware + /c/[token]) può partire
2026-05-13 22:49:47 +02:00

7.7 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions duration completed
01-foundation-client-dashboard 02 database
drizzle-orm
postgres
schema
migrations
nanoid
01-01 (drizzle-kit, postgres-js driver, DATABASE_URL in .env.local)
src/db/schema.ts con 10 tabelle complete
TypeScript types esportati per tutte le entità (Client, Phase, Task, ecc.)
Migration file SQL in src/db/migrations/
Schema live su Postgres 16 (Hetzner/Coolify)
01-03-client-route (usa clients, phases, tasks, deliverables, payments, documents, notes)
01-04-dashboard-ui (usa tutti i types esportati)
01-05-seed-deploy (inserisce dati con i types NewClient, NewPhase, ecc.)
added patterns
ID strategy: text + nanoid() via $defaultFn (non uuid() nativo Postgres) — nanoid genera stringhe 21-char URL-safe, non UUID formato xxxxxxxx-xxxx-xxxx
drizzle-kit push richiede DATABASE_URL passata esplicitamente come env var (non carica .env.local automaticamente)
Relations Drizzle definite per tutti gli FK — usabili in query con with: { ... }
created modified
src/db/schema.ts (245 righe — 10 tabelle + relations + TypeScript types)
src/db/migrations/0000_pretty_typhoid_mary.sql (migration SQL completa)
src/db/migrations/meta/ (drizzle-kit metadata)
src/db/migrations/relations.ts (relazioni per introspect)
src/db/migrations/schema.ts (schema per introspect)
Usato text + $defaultFn(() => nanoid()) invece di uuid().defaultRandom() — nanoid genera ID URL-safe crittograficamente sicuri (21 char, ~126 bit entropia), non UUID formato PostgreSQL
drizzle.config.ts dal Plan 01 già corretto (defineConfig + dialect postgresql + url:) — nessuna modifica necessaria
clients.token: text notNull unique con nanoid — separato dall'id PK, rotabile con single UPDATE
drizzle-kit push richiede DATABASE_URL come env var esplicita (non auto-load .env.local)
15min 2026-05-13

Phase 1 Plan 02: Drizzle Schema + Migration — 10 tabelle live su Postgres

Schema Drizzle ORM completo con 10 tabelle, migration SQL generata e schema live sul database Postgres 16 (Hetzner/Coolify). TypeScript strict compila senza errori.

Performance

  • Duration: ~15 min
  • Started: 2026-05-13T20:21:00Z
  • Completed: 2026-05-13T20:36:00Z
  • Tasks: 3/3
  • Files modified: 6

Accomplishments

  • src/db/schema.ts creato con 10 tabelle complete + relations Drizzle + TypeScript types esportati
  • Vincoli architetturali LOCKED rispettati: clients.token separato dall'id PK (unique, notNull, nanoid), accepted_total denormalizzato, approved_at nullable (audit trail immutabile), quote_items mai esposto al client API
  • Migration SQL (0000_pretty_typhoid_mary.sql) generata con tutti i CREATE TABLE e FK constraints
  • npx drizzle-kit push eseguito con successo — tutte e 10 le tabelle create su postgresql://178.104.27.55:5432/clienthub
  • Verifica via information_schema.tables: clients, comments, deliverables, documents, notes, payments, phases, quote_items, service_catalog, tasks

Task Commits

  1. Task 1: Drizzle schema (src/db/schema.ts) - 1bdbe7a (feat)
  2. Task 2: Migration generation (drizzle-kit generate) - a6ec599 (chore)
  3. Task 3: [BLOCKING] drizzle-kit push → Postgres live - abcbb52 (feat)

Files Created/Modified

  • src/db/schema.ts — 10 tabelle: clients (token separato + accepted_total), phases, tasks, deliverables (approved_at nullable), comments (polimorfici), payments (da_saldare/inviata/saldato), documents, notes, service_catalog, quote_items
  • src/db/migrations/0000_pretty_typhoid_mary.sql — Migration SQL completa con CREATE TABLE + FK + UNIQUE constraint su token
  • src/db/migrations/meta/ — Drizzle-kit metadata (snapshot JSON)
  • src/db/migrations/relations.ts — Relations per introspect
  • src/db/migrations/schema.ts — Schema per introspect

Decisions Made

  • ID strategy: text + $defaultFn(() => nanoid()) invece di uuid().defaultRandom(). La colonna Drizzle uuid() si aspetta il formato PostgreSQL xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, mentre nanoid() genera stringhe 21-char URL-safe. Usare text è corretto e allineato con la decisione architetturale di token crittograficamente sicuro.
  • drizzle.config.ts invariato: La versione dal Plan 01 usa già defineConfig, dialect: "postgresql" e url: (sintassi aggiornata drizzle-kit v0.31) — nessuna modifica necessaria rispetto alla versione suggerita nel piano (che usava l'API obsoleta driver: 'pg').

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] uuid() non compatibile con nanoid() come defaultFn

  • Found during: Task 1
  • Issue: Il piano suggeriva uuid('id').primaryKey().defaultValue(nanoid()) ma Drizzle uuid() si aspetta UUID nel formato xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. nanoid() genera stringhe come Tcyf3muFXVOX9QO9pBUES (21 char, non UUID validi). Usare defaultValue(nanoid()) su una colonna uuid() avrebbe causato errori a runtime al primo INSERT.
  • Fix: Cambiato a text('id').primaryKey().$defaultFn(() => nanoid()) per tutte le PK e per il campo token. Semantica identica (ID crittograficamente sicuro), tipo colonna SQL text invece di uuid.
  • Files modified: src/db/schema.ts
  • Commit: 1bdbe7a

2. [Rule 1 - Bug] drizzle.config.ts dal piano usa API obsoleta

  • Found during: Task 2
  • Issue: Il piano suggeriva driver: 'pg' e dbCredentials: { connectionString: ... } — sintassi drizzle-kit <0.30. Il file esistente usa già defineConfig con dialect: "postgresql" e dbCredentials: { url: ... } — sintassi corretta per drizzle-kit 0.31.
  • Fix: Mantenuto il file esistente senza modifiche (era già corretto).
  • Files modified: nessuno
  • Commit: nessuno necessario

3. [Rule 3 - Blocking] drizzle-kit push non carica .env.local automaticamente

  • Found during: Task 3
  • Issue: npx drizzle-kit push fallisce con "connection url required" perché drizzle-kit non carica .env.local automaticamente (solo .env).
  • Fix: Passato DATABASE_URL esplicitamente come variabile d'ambiente al comando: DATABASE_URL="..." npx drizzle-kit push.
  • Files modified: nessuno (solo comando di esecuzione)
  • Commit: abcbb52

Known Stubs

Nessuno. Il piano è infrastrutturale (schema + DB) — nessun componente UI o dato presentato al cliente. Le tabelle sono vuote, ma questo è intenzionale: il seed script è previsto nel Plan 05.

Threat Surface Scan

Il threat model T-02-001 (unicità token) è mitigato: CONSTRAINT "clients_token_unique" UNIQUE("token") è attivo nel database. T-02-002 e T-02-003 sono accettati come da piano.

Nessuna nuova superficie di sicurezza non prevista dal threat model.

Self-Check

  • src/db/schema.ts esiste (245 righe, 10 tabelle pgTable + relations + types)
  • src/db/migrations/0000_pretty_typhoid_mary.sql esiste con 10 CREATE TABLE
  • Commit 1bdbe7a esiste (schema)
  • Commit a6ec599 esiste (migrations)
  • Commit abcbb52 esiste (push)
  • 10 tabelle verificate live su Postgres via information_schema.tables
  • clients.token è text NOT NULL UNIQUE con nanoid — separato dalla PK
  • approved_at è timestamp with time zone nullable
  • TypeScript strict: npm run build — zero errori TypeScript

Self-Check: PASSED

Next Phase Readiness

  • Plan 03 (Middleware + route /c/[token]) può partire — lo schema è live e i types sono importabili
  • Import pattern: import { clients, phases, tasks, ... } from '@/db/schema'
  • Import types: import type { Client, Phase, Task, ... } from '@/db/schema'

Phase: 01-foundation-client-dashboard Completed: 2026-05-13