Files
simone 5bf5dfce71 infra(04-00): route /c/ → /client/, Dockerfile, Gitea deploy
- 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>
2026-05-21 16:12:05 +02:00

7.4 KiB

Pitfalls — ClientHub Freelancer Client Portal

Domain: Freelancer client portal with secret-link access, solo developer, Vercel deploy Researched: 2026-05-09 Confidence: HIGH


Critical Pitfalls

What goes wrong: If client tokens are generated from names, sequential IDs, or short strings, they can be enumerated. /client/mario-rossi or /client/3 are not secrets.

Prevention:

  • Generate tokens as cryptographically random UUIDs (v4) or nanoid (21 chars / ~126 bits of entropy)
  • Never derive from name, company, or sequential ID
  • Never log or display full tokens in admin analytics

Warning signs: Client slug contains the client's name. Token under 20 characters. Link can be reconstructed from info the client already knows.

Phase mapping: Phase 1 — data model + routing. Cannot be retrofitted after links are distributed.


2. Client API Exposes Admin Data (Hidden in UI Only)

What goes wrong: Developers fetch all client data and conditionally render fields. A technical client opens DevTools and sees the full quote breakdown — the exact thing the product prevents.

Prevention:

  • Define two distinct data shapes: ClientView and AdminView
  • Client API routes (/api/c/[token]/) return ClientView only — enforced server-side, not in the UI
  • accepted_total goes on the clients row. Client API never queries quote_items

Warning signs: Client API response includes lineItems filtered in the frontend. "Client sees only total" enforced with display: none.

Phase mapping: Data model and API shape — Phase 1.


3. Data Loss from Vercel Filesystem / In-Memory Storage

What goes wrong: Vercel serverless functions are stateless. File writes to local filesystem and in-memory state are lost between invocations. SQLite on disk silently vanishes after cold start. Bug only manifests in production.

Prevention:

  • External persistent DB from day one: Neon (Postgres free tier)
  • Never write to fs for persistent data on Vercel
  • Validate persistence in the first production deploy

Warning signs: Project stores data in a data/ JSON directory. SQLite without a remote adapter. Data changes in one request not visible in the next.

Phase mapping: Day-one infrastructure decision — Phase 1, before any real data is entered.


4. Over-Engineering Before the First Client Uses It

What goes wrong: Building the Claude onboarding flow and quote generator before a single client has opened their dashboard. Real clients still managed via email while the portal is "almost ready."

Prevention:

  • Hard success criterion for Phase 1: one client link is shareable and works
  • Phase 1 ships read-only client dashboard only
  • Collect direct feedback from one real client before adding features

Warning signs: >2 weeks pass without a shareable client link. Claude integration started before admin edit UI exists.

Phase mapping: Enforced by roadmap phase ordering.


5. Token Is the Primary Key (Unrotatable)

What goes wrong: A client forwards their link. The link appears in a screenshot. There is no mechanism to rotate without losing data or breaking bookmarks. Worse if token = primary key: rotation requires a migration.

Prevention:

  • Data model: stable internal UUID as PK; secret token is a separate, independently updatable field
  • Build "Regenerate link" in admin area during Phase 2 — it's a single field UPDATE
  • Overwriting the token field is sufficient to invalidate the old link

Warning signs: Token is used as PK of the client record. No admin affordance to regenerate a link.

Phase mapping: Data model separation — Phase 1. Admin rotation UI — Phase 2.


6. Client Approval Has No Immutable Record

What goes wrong: Client clicks "Approve." Later: "I never approved that." If approval is a boolean with no timestamp, there is no evidence. Weakens your position in commercial disputes.

Prevention:

  • Store approved_at timestamp alongside the approval boolean — from day one
  • Display approval timestamp in admin view
  • Approvals are immutable for clients — no "undo" button

Warning signs: Approval is a boolean column with no timestamp. Client-visible "undo approval" button exists.

Phase mapping: Schema — Phase 1. Display in admin — Phase 2.


Moderate Pitfalls

7. Payment Status Without a Valid State Machine

Payment has three states (da_saldare / inviata / saldato) for two payments. If transitions are not enforced, the dashboard shows contradictory states.

Prevention: Enforce valid transitions: da_saldare → inviata → saldato. Admin UI offers only valid next states.

Phase mapping: Admin UI — Phase 2.


Document links break when sharing settings change or the Drive account changes. Client sees a broken link to their own deliverable.

Prevention: Store a display name alongside the URL so broken links are visible in admin. Build a simple "update link" affordance in Phase 2.


9. Admin Area With No Real Access Control

/admin is a secret route with no authentication. A client who guesses the URL accesses all client data.

Prevention: Add Next.js middleware check against ADMIN_PASSWORD env variable before Phase 2 ships. Never rely on security through obscurity for a route that contains all client data.

Phase mapping: Phase 2, before admin area contains real data.


10. Comments Scope Creeping Into Threading

"Clients can leave comments" becomes complex when replies, read/unread state, and notifications are added. Can double Phase 1 scope.

Prevention: Phase 1 ships comments as a flat append-only list per task. No threading, no replies, no email notifications.


Minor Pitfalls

11. DNS Configuration as a Last-Minute Task

welcomeclient.iamcavalli.net requires a CNAME to Vercel's DNS. Propagation takes minutes to hours. Doing this the day of a client demo misses the deadline.

Prevention: Configure DNS in Phase 1 before the UI is complete. Verify propagation independently.


12. Mobile Responsiveness as an Afterthought

Clients open the link on their phone from a shared message. A broken mobile layout is the first impression.

Prevention: Use Tailwind mobile-first defaults from the start. Test on a real phone before any link is sent.


13. No Empty States

A new client record with no tasks shows a blank page. The client assumes something is broken.

Prevention: Design minimal empty states for no-tasks, no-documents, no-comments.


Phase-Specific Warnings Summary

Phase Topic Likely Pitfall Mitigation
Client token generation Guessable slug from name Crypto-random UUID/nanoid, never name-derived
Client-facing API Admin data in JSON response ClientView type enforced server-side
Storage choice Vercel filesystem not persistent External DB (Neon) before first data write
Admin area access No real auth Middleware check before Phase 2 ships
Approval recording Boolean-only, no audit trail Store approved_at from day one
Token in data model Token = PK, unrotatable Separate stable ID from rotatable token field
Phase ordering Claude flow before dashboard Enforce: client view → admin edit → Claude
Comments Threading scope creep Flat list in Phase 1
DNS Last-minute failure Configure and verify in Phase 1