Files
Simone Cavalli 4707ab5d6f docs(01-03): complete token middleware + client portal data layer plan
- 01-03-SUMMARY.md: 3/3 tasks complete, npm run build passes
- Edge proxy pattern (proxy.ts), validate-token API route, ClientView type system
- /c/[token] Server Component operational, ready for Plan 04 dashboard UI
2026-05-14 21:25:56 +02:00

9.4 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 03 client-portal
nextjs
middleware
drizzle-orm
client-view
server-component
edge-runtime
01-01 (Next.js bootstrap, DATABASE_URL, Drizzle client in src/db/index.ts)
01-02 (src/db/schema.ts — clients, phases, tasks, deliverables, payments, documents, notes)
src/proxy.ts (Edge proxy per validazione token su /c/:path*)
src/app/api/internal/validate-token/route.ts (Node.js route che interroga clients.token)
src/lib/client-view.ts (ClientView interface + getClientView() function)
src/app/c/[token]/page.tsx (Server Component placeholder per dashboard cliente)
src/app/c/[token]/layout.tsx (Layout con metadata per rotte token-auth)
01-04-dashboard-ui (consuma ClientView interface e getClientView())
01-05-seed-deploy (genera token, verifica accesso /c/[token])
added patterns
Edge proxy pattern: proxy.ts usa fetch() verso /api/internal/validate-token — nessun import Drizzle/postgres-js in Edge runtime
Next.js 16 breaking change: file convention rinominata da 'middleware' a 'proxy'; export function rinominata da 'middleware' a 'proxy'
Next.js 15+ breaking change: params in Server Component è Promise<{ token: string }> — await params prima dell'uso
ClientView enforced server-side: interface TypeScript + query function che non tocca mai quote_items o service_catalog
inArray() per scoping tasks/deliverables: previene full table scan su clienti che non appartengono alla sessione
created modified
src/proxy.ts (proxy Edge-compatible — rinominato da middleware.ts per Next.js 16)
src/app/api/internal/validate-token/route.ts (Node.js route, query clients.token via Drizzle)
src/lib/client-view.ts (ClientView interface + getClientView() — 209 righe)
src/app/c/[token]/page.tsx (Server Component placeholder — 28 righe)
src/app/c/[token]/layout.tsx (Layout con metadata — 14 righe)
Next.js 16 richiede file 'proxy.ts' (non 'middleware.ts') ed export function 'proxy' (non 'middleware') — auto-corretto da Rule 1
params nelle Server Component Next.js 15+ è Promise<{ token: string }> — await obbligatorio (breaking change)
ClientView.payments non espone 'amount' — solo label e status — vincolo architetturale LOCKED rispettato
getClientView() usa inArray() per scopare tasks e deliverables ai soli phase_id del cliente, evitando leak cross-client
25min 2026-05-14

Phase 1 Plan 03: Token Middleware + Client Portal Data Layer — Route /c/[token] operativa

Edge proxy con validazione token, ClientView type system che esclude quote_items, e Server Component che fetcha tutti i dati cliente senza esporre segreti admin. Build Next.js 16 senza errori TypeScript.

Performance

  • Duration: ~25 min
  • Started: 2026-05-13T22:50:00Z
  • Completed: 2026-05-14T00:15:00Z
  • Tasks: 3/3
  • Files created: 5

Accomplishments

  • src/proxy.ts: proxy Edge-compatible per Next.js 16 — valida token via fetch('/api/internal/validate-token') senza import Drizzle/postgres-js. Matcher configurato su /c/:path*. Token non trovato → rewrite su /not-found.
  • src/app/api/internal/validate-token/route.ts: route Node.js che interroga clients.token via Drizzle ORM. Ritorna { valid: true } (200) o { valid: false } (404/400/500). Drizzle funziona correttamente qui perché non è nel runtime Edge.
  • src/lib/client-view.ts: ClientView interface che esclude esplicitamente quote_items, service_catalog e prezzi singoli. getClientView() esegue 6 query scoped (client by token → phases → tasks via inArray → deliverables via inArray → payments → documents → notes). Progress % calcolata server-side. accepted_total: client.accepted_total ?? '0'.
  • src/app/c/[token]/page.tsx: Server Component con await params, chiama getClientView(token), ritorna notFound() se null. ISR a 60s.
  • src/app/c/[token]/layout.tsx: layout minimo con metadata.
  • npm run build completato con successo — zero errori TypeScript, tutte le route presenti nel build output.

Task Commits

  1. Task 1: Edge proxy + validate-token API routeef34817 (feat)
  2. Task 2: ClientView type system + getClientView()14787ba (feat)
  3. Task 3: /c/[token] Server Component + layout8b5e723 (feat, include deviazione Rule 1)

Files Created/Modified

  • src/proxy.ts — Edge proxy per /c/:path* (rinominato da middleware.ts — vedi Deviazioni)
  • src/app/api/internal/validate-token/route.ts — Node.js route, query eq(clients.token, token), risposta 200/404
  • src/lib/client-view.ts — ClientView interface (quote_items mai inclusi) + getClientView() con inArray scoping
  • src/app/c/[token]/page.tsx — Server Component placeholder, notFound() su token invalido
  • src/app/c/[token]/layout.tsx — Layout con metadata

Decisions Made

  • Next.js 16 proxy convention: La nuova convenzione rinomina middleware.tsproxy.ts ed esige export function proxy (non middleware). La build ha segnalato il deprecation warning al primo tentativo. Risolto con rename + export update (Rule 1).
  • params come Promise: Next.js 15+ tratta params come Promise<{ token: string }> nelle Server Component. Il piano usava la sintassi Next.js 14 sincrona — aggiornato ad await params per conformità (Rule 1).
  • ClientView.payments senza amount: Il campo amount di payments è intenzionalmente omesso dalla ClientView. Il cliente vede solo label e status. Vincolo architetturale LOCKED rispettato a livello di query.

Deviations from Plan

Auto-fixed Issues

1. [Rule 1 - Bug] Next.js 16 depreca la convenzione 'middleware' in favore di 'proxy'

  • Found during: Task 3 — npm run build ha emesso warning e poi errore: "Proxy is missing expected function export name"
  • Issue: Next.js 16 ha rinominato la file convention da middleware.ts a proxy.ts e richiede che la funzione esportata si chiami proxy (o default), non middleware.
  • Fix: Rinominato src/middleware.tssrc/proxy.ts; rinominato export async function middlewareexport async function proxy. Il commit ef34817 conteneva middleware.ts — il Task 3 commit 8b5e723 include la modifica.
  • Files modified: src/proxy.ts (rinominato da src/middleware.ts)
  • Commit: 8b5e723

2. [Rule 1 - Bug] params come Promise in Next.js 15+ Server Component

  • Found during: Task 3 — il piano usava la sintassi params: { token: string } di Next.js 14
  • Issue: In Next.js 15+, i params nelle Server Component sono Promise<{ token: string }>. Usare la sintassi sincrona avrebbe causato TypeScript error e comportamento errato a runtime.
  • Fix: Tipizzato params: Promise<{ token: string }> e aggiunto const { token } = await params; prima dell'uso.
  • Files modified: src/app/c/[token]/page.tsx
  • Commit: 8b5e723

Known Stubs

La page.tsx mostra un placeholder minimo (brand_name, brief, token). Questo è intenzionale e documentato nel piano: "Placeholder content — full UI in Plan 04". Il Plan 04 (Dashboard UI) sostituirà questo placeholder con la UI completa. Il goal del piano (route operativa + data fetching corretto) è raggiunto pienamente.

Threat Surface Scan

Nessuna nuova superficie di sicurezza non prevista dal threat model 01-03:

  • T-03-001 (ClientView shape): mitigato — interface TypeScript + getClientView() non tocca mai quote_items/service_catalog
  • T-03-002 (Token parameter): mitigato — proxy valida il token prima che qualsiasi pagina venga renderizzata; token invalidi → /not-found
  • T-03-003 (DoS su getClientView): accettato — query indicizzate su client_id/token

L'API route /api/internal/validate-token è accessibile pubblicamente (nessun secret header). Questo è intenzionale: ritorna solo { valid: boolean } — nessun dato cliente esposto. Un attaccante può enumerare token validi con brute force ma: (1) nanoid 21 chars offre ~126 bit di entropia, rendendo il brute force computazionalmente impossibile; (2) nessun dato sensibile è esposto dalla route.

Self-Check

  • src/proxy.ts esiste (edge proxy — rinominato da middleware.ts)
  • src/app/api/internal/validate-token/route.ts esiste (query clients.token via Drizzle)
  • src/lib/client-view.ts esiste (ClientView interface + getClientView())
  • src/app/c/[token]/page.tsx esiste (Server Component con await params)
  • src/app/c/[token]/layout.tsx esiste
  • Commit ef34817 esiste (Task 1)
  • Commit 14787ba esiste (Task 2)
  • Commit 8b5e723 esiste (Task 3)
  • npm run build completato senza errori TypeScript
  • Build output mostra /api/internal/validate-token (Dynamic) e /c/[token] (Dynamic)
  • proxy.ts NON importa @/db o drizzle-orm (Edge safe)
  • client-view.ts non contiene query su quote_items o service_catalog
  • accepted_total: client.accepted_total ?? '0' presente

Self-Check: PASSED

Next Phase Readiness

  • Plan 04 (Dashboard UI) può partire — getClientView() è disponibile e testa TypeScript OK
  • Import pattern: import { getClientView, type ClientView } from '@/lib/client-view'
  • La route /c/[token] è operativa — con un cliente seedato da Plan 05 sarà accessibile

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