1faca1f522
- SUMMARY documenta schema, deviazioni (uuid->text+nanoid, drizzle-kit env), threat surface e self-check PASSED - Piano 03 (Middleware + /c/[token]) può partire
144 lines
7.7 KiB
Markdown
144 lines
7.7 KiB
Markdown
---
|
|
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* |