- Rename src/app/c/[token] → src/app/client/[token] - Update proxy.ts, ClientRow, admin client detail with /client/ path - Add output: "standalone" to next.config.ts for Docker build - Add Dockerfile (multi-stage, node:20-alpine) and .dockerignore - Push schema to Coolify Postgres via SSH tunnel (drizzle-kit push ✓) - Update CLAUDE.md constraint 4 to reflect /client/ route - Add Phase 4 planning artifacts (04-00, 04-RESEARCH, 04-PATTERNS) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
11 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 04-progetti-multi-project | 00 | execute | 0 |
|
false |
|
Questo piano esegue PRIMA di 04-01 per garantire che tutto il codice Phase 4 venga scritto e testato sull'infrastruttura finale. Nessun refactor post-esecuzione.
Output: App deployata su hub.iamcavalli.net con DB Postgres su Coolify, route /client/[token] funzionante.
@/Users/simonecavalli/IAMCAVALLI/.planning/ROADMAP.md @/Users/simonecavalli/IAMCAVALLI/CLAUDE.mdproxy.ts riga 32: if (pathname.startsWith("/c/"))
proxy.ts riga 33: pathname.match(/^\/c\/([a-zA-Z0-9_-]+)/)
proxy.ts riga 63: matcher: ["/admin/:path*", "/c/:path*"]
ClientRow.tsx riga 59: href={\/c/${client.token}`}admin/clients/[id]/page.tsx riga 46:href={`/c/${client.token}`}`
Cartella da rinominare: src/app/c/ → src/app/client/
<threat_model> T-00-01: Redirect loop dopo rinomina — mitigazione: aggiornare proxy.ts prima di rinominare la cartella, verificare matcher. T-00-02: DB connection string esposta — mitigazione: .env mai committato, .env.example con placeholder. T-00-03: Dati di test persi durante migrazione DB — accettabile: tutti i dati attuali sono test data (CONTEXT.md). T-00-04: Coolify deploy fallisce silenziosamente — mitigazione: verificare health check dopo deploy. </threat_model>
Task 1: Postgres su Coolify + migrazione DATABASE_URL<read_first>
- .env (connection string Neon attuale — non committare)
- drizzle.config.ts (per confermare che legge DATABASE_URL) </read_first>
Nel pannello Coolify su Hetzner:
- New Resource → Database → PostgreSQL
- Nome:
clienthub-db - Version: 16
- Salva le credenziali generate (host, port, user, password, dbname)
- Abilita "Public port" se necessario per drizzle-kit push da locale
B. Aggiorna .env locale
Sostituisci la riga DATABASE_URL con la connection string Coolify:
DATABASE_URL=postgresql://USER:PASSWORD@HOST:PORT/DBNAME?sslmode=require
Mantieni la vecchia riga commentata come backup:
# DATABASE_URL=postgresql://neon... (backup)
C. Aggiorna .env.example
Sostituisci il placeholder Neon con quello generico:
DATABASE_URL=postgresql://user:password@host:5432/dbname?sslmode=require
D. Push schema al nuovo DB
npx drizzle-kit push
Il DB è fresh — nessuna migrazione soft necessaria, tutti i dati attuali sono test data.
npx drizzle-kit push 2>&1; echo "Exit: $?"<acceptance_criteria>
- drizzle-kit push completes con exit code 0
- .env contiene DATABASE_URL che punta a Coolify (non neon.tech)
- .env.example non contiene credenziali reali
- npx tsc --noEmit non produce errori legati al DB </acceptance_criteria>
<read_first>
- src/proxy.ts (middleware completo — LEGGERE PRIMA di modificare)
- src/components/admin/ClientRow.tsx (link generato riga 59)
- src/app/admin/clients/[id]/page.tsx (link generato riga 46)
- src/app/c/[token]/page.tsx (dashboard cliente da spostare)
- src/app/c/[token]/layout.tsx (layout da spostare) </read_first>
mv src/app/c src/app/client
Questo sposta automaticamente page.tsx e layout.tsx alla nuova route.
B. Aggiorna src/proxy.ts
Tre sostituzioni esatte:
Riga 32 — cambia:
if (pathname.startsWith("/c/")) {
in:
if (pathname.startsWith("/client/")) {
Riga 33 — cambia:
const tokenMatch = pathname.match(/^\/c\/([a-zA-Z0-9_-]+)/);
in:
const tokenMatch = pathname.match(/^\/client\/([a-zA-Z0-9_-]+)/);
Riga 63 — cambia:
matcher: ["/admin/:path*", "/c/:path*"],
in:
matcher: ["/admin/:path*", "/client/:path*"],
C. Aggiorna ClientRow.tsx
Riga 59 — cambia:
href={`/c/${client.token}`}
in:
href={`/client/${client.token}`}
D. Aggiorna admin/clients/[id]/page.tsx
Riga 46 — cambia:
href={`/c/${client.token}`}
in:
href={`/client/${client.token}`}
<acceptance_criteria>
src/app/client/[token]/page.tsxesiste (ls conferma)src/app/c/NON esiste più (ls conferma)grep '"/c/' src/ -rproduce zero risultatigrep "startsWith(\"/client/\")" src/proxy.tsproduce un matchgrep "/client/:path\*" src/proxy.tsproduce un matchnpx tsc --noEmitexit code 0 </acceptance_criteria>
<read_first>
- package.json (script build, versione Node richiesta)
- next.config.ts (per verificare se output: standalone è già presente)
- .gitignore (per confermare che .env non è committato) </read_first>
Next.js standalone mode produce un bundle minimale ottimale per Docker.
In next.config.ts, aggiungi dentro l'oggetto config:
output: "standalone",
B. Crea Dockerfile nella root del progetto
FROM node:20-alpine AS base
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXTAUTH_URL=https://hub.iamcavalli.net
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
C. Crea .dockerignore nella root
.env
.env.local
node_modules
.next
.git
.planning
D. Aggiungi variabili d'ambiente su Coolify
Nel pannello Coolify → app → Environment Variables, aggiungere:
DATABASE_URL=postgresql://... (la stessa del .env locale)
NEXTAUTH_SECRET=...
NEXTAUTH_URL=https://hub.iamcavalli.net
ADMIN_EMAIL=...
ADMIN_PASSWORD=...
E. Configura dominio su Coolify
Nel pannello Coolify → app → Domains:
- Aggiungi:
hub.iamcavalli.net - Abilita SSL automatico (Let's Encrypt)
<acceptance_criteria>
Dockerfileesiste nella root del progetto.dockerignoreesiste e contiene.envnext.config.tscontieneoutput: "standalone"docker buildcompletes con exit code 0 (se Docker disponibile localmente)- In alternativa:
npm run buildexit code 0 (verifica il build Next.js) - Il file
.envNON appare ingit status(confermato da .gitignore) </acceptance_criteria>
<what_was_built>
- Postgres self-hosted su Coolify con schema applicato
- Route /client/[token] funzionante (rinominata da /c/)
- Dockerfile per deploy su Coolify
- Dominio hub.iamcavalli.net configurato </what_was_built>
<how_to_verify>
- Apri https://hub.iamcavalli.net — deve rispondere (anche con pagina 404 standard Next.js)
- Apri https://hub.iamcavalli.net/admin/login — deve mostrare la schermata di login
- Fai login come admin — deve accedere alla lista clienti
- Copia il link di un cliente — deve essere nella forma
hub.iamcavalli.net/client/[token] - Apri il link cliente — la dashboard deve caricare correttamente
- Verifica che il vecchio link
/c/[token]restituisca 404 (non più attivo) </how_to_verify>
<resume_signal> Digita "hub ok" quando tutti i controlli passano. </resume_signal>
1. `src/app/c/` non esiste — `src/app/client/[token]/` esiste 2. `grep -r '"/c/' src/` produce zero risultati 3. `grep "startsWith(\"/client/\")" src/proxy.ts` produce un match 4. `npx drizzle-kit push` exit code 0 sul nuovo DB 5. `npm run build` exit code 0 6. https://hub.iamcavalli.net risponde dopo deploy Coolify 7. https://hub.iamcavalli.net/client/[token] carica la dashboard cliente<summary_template>
Plan 04-00 Complete
Infrastruttura migrata:
- DB: Neon → Postgres self-hosted su Coolify (Hetzner)
- Route: /c/[token] → /client/[token]
- Deploy: Vercel → Coolify via Docker
- Dominio: hub.iamcavalli.net attivo
File modificati: src/proxy.ts, src/components/admin/ClientRow.tsx, src/app/admin/clients/[id]/page.tsx, next.config.ts File creati: Dockerfile, .dockerignore File spostati: src/app/c/ → src/app/client/ </summary_template>