From 1faca1f522a30c27e91da8844862683733ab3487 Mon Sep 17 00:00:00 2001 From: Simone Cavalli Date: Wed, 13 May 2026 22:49:47 +0200 Subject: [PATCH] =?UTF-8?q?docs(01-02):=20complete=20Drizzle=20schema=20pl?= =?UTF-8?q?an=20=E2=80=94=2010=20tables=20live=20on=20Postgres?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SUMMARY documenta schema, deviazioni (uuid->text+nanoid, drizzle-kit env), threat surface e self-check PASSED - Piano 03 (Middleware + /c/[token]) può partire --- .../01-02-SUMMARY.md | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 .planning/phases/01-foundation-client-dashboard/01-02-SUMMARY.md diff --git a/.planning/phases/01-foundation-client-dashboard/01-02-SUMMARY.md b/.planning/phases/01-foundation-client-dashboard/01-02-SUMMARY.md new file mode 100644 index 0000000..8cba053 --- /dev/null +++ b/.planning/phases/01-foundation-client-dashboard/01-02-SUMMARY.md @@ -0,0 +1,144 @@ +--- +phase: 01-foundation-client-dashboard +plan: 02 +subsystem: database +tags: [drizzle-orm, postgres, schema, migrations, nanoid] + +# Dependency graph +requires: + - 01-01 (drizzle-kit, postgres-js driver, DATABASE_URL in .env.local) +provides: + - 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) +affects: + - 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.) + +# Tech tracking +tech-stack: + 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: { ... }" + +key-files: + created: + - 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) + modified: [] + +key-decisions: + - "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)" + +# Metrics +duration: 15min +completed: 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 + +- [x] `src/db/schema.ts` esiste (245 righe, 10 tabelle pgTable + relations + types) +- [x] `src/db/migrations/0000_pretty_typhoid_mary.sql` esiste con 10 CREATE TABLE +- [x] Commit `1bdbe7a` esiste (schema) +- [x] Commit `a6ec599` esiste (migrations) +- [x] Commit `abcbb52` esiste (push) +- [x] 10 tabelle verificate live su Postgres via `information_schema.tables` +- [x] `clients.token` è `text NOT NULL UNIQUE` con nanoid — separato dalla PK +- [x] `approved_at` è `timestamp with time zone` nullable +- [x] 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* \ No newline at end of file