- 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
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 |
|
|
|
|
|
|
|
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 viafetch('/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 interrogaclients.tokenvia 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:ClientViewinterface che esclude esplicitamentequote_items,service_cataloge 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 conawait params, chiamagetClientView(token), ritornanotFound()se null. ISR a 60s.src/app/c/[token]/layout.tsx: layout minimo con metadata.npm run buildcompletato con successo — zero errori TypeScript, tutte le route presenti nel build output.
Task Commits
- Task 1: Edge proxy + validate-token API route —
ef34817(feat) - Task 2: ClientView type system + getClientView() —
14787ba(feat) - Task 3: /c/[token] Server Component + layout —
8b5e723(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, queryeq(clients.token, token), risposta 200/404src/lib/client-view.ts— ClientView interface (quote_items mai inclusi) + getClientView() con inArray scopingsrc/app/c/[token]/page.tsx— Server Component placeholder, notFound() su token invalidosrc/app/c/[token]/layout.tsx— Layout con metadata
Decisions Made
- Next.js 16 proxy convention: La nuova convenzione rinomina
middleware.ts→proxy.tsed esigeexport function proxy(nonmiddleware). La build ha segnalato il deprecation warning al primo tentativo. Risolto con rename + export update (Rule 1). - params come Promise: Next.js 15+ tratta
paramscomePromise<{ token: string }>nelle Server Component. Il piano usava la sintassi Next.js 14 sincrona — aggiornato adawait paramsper conformità (Rule 1). - ClientView.payments senza amount: Il campo
amountdipaymentsè intenzionalmente omesso dallaClientView. Il cliente vede sololabelestatus. 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 buildha emesso warning e poi errore: "Proxy is missing expected function export name" - Issue: Next.js 16 ha rinominato la file convention da
middleware.tsaproxy.tse richiede che la funzione esportata si chiamiproxy(odefault), nonmiddleware. - Fix: Rinominato
src/middleware.ts→src/proxy.ts; rinominatoexport async function middleware→export async function proxy. Il commitef34817conteneva middleware.ts — il Task 3 commit8b5e723include 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
paramsnelle Server Component sonoPromise<{ token: string }>. Usare la sintassi sincrona avrebbe causato TypeScript error e comportamento errato a runtime. - Fix: Tipizzato
params: Promise<{ token: string }>e aggiuntoconst { 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.tsesiste (edge proxy — rinominato da middleware.ts)src/app/api/internal/validate-token/route.tsesiste (query clients.token via Drizzle)src/lib/client-view.tsesiste (ClientView interface + getClientView())src/app/c/[token]/page.tsxesiste (Server Component con await params)src/app/c/[token]/layout.tsxesiste- Commit
ef34817esiste (Task 1) - Commit
14787baesiste (Task 2) - Commit
8b5e723esiste (Task 3) npm run buildcompletato senza errori TypeScript- Build output mostra
/api/internal/validate-token(Dynamic) e/c/[token](Dynamic) proxy.tsNON importa@/dbodrizzle-orm(Edge safe)client-view.tsnon contiene query suquote_itemsoservice_catalogaccepted_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