Files
ecommerce-platform/CLAUDE.md
T
davide 93cfe1ad5e docs: update CLAUDE.md with Italian rule, error workflow, and full data model
Added Italian communication requirement, error-first workflow (report then wait),
and missing Prisma models: ProductVariant, PasswordResetToken, Page/PageSection,
SiteSettings, AuditLog. Added app/coverage/ to .gitignore.
2026-05-19 14:05:34 +02:00

6.2 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

How to work with the user

Before implementing any new feature, always discuss it first: explain whether it makes sense professionally, how it is typically done in production e-commerce platforms, and propose alternatives if there is a better approach. Only proceed with implementation after the user confirms.

Communicate in Italian. The user speaks Italian — all responses, explanations, and questions must be in Italian.

Error handling workflow: When you find an error (in logs, code, or output), report what you found and stop. Do not propose or implement a fix until the user explicitly asks. Present: what the error is, where it occurs, and what likely caused it — then wait for instructions.

Running the project

# Start everything (first run takes a few minutes to build)
docker compose up -d --build

# Check logs
docker compose logs -f app

# Stop
docker compose down

The app is available at http://localhost. Admin panel at http://localhost/admin. Default credentials are in .env (INITIAL_ADMIN_EMAIL / INITIAL_ADMIN_PASSWORD). Test emails are visible at http://localhost:8025 (Mailpit).

Development commands (run inside app/)

npm run dev      # local dev server (port 3000, no Docker)
npm run build    # production build
npx prisma studio            # visual DB browser
npx prisma migrate dev --name <name>   # create a new migration
npx prisma migrate deploy    # apply migrations (done automatically on container start)

Architecture

Stack: Next.js 14 App Router · PostgreSQL 16 · Prisma 5 · Stripe · Nodemailer · TailwindCSS · Zod · Docker + Caddy

Source root: app/src/

Directory Purpose
app/api/ REST API routes — one folder per resource
app/admin/ Admin dashboard pages (role-gated at layout level)
app/(storefront)/ Public-facing pages
components/ Shared React components and UI primitives
lib/ Core utilities (auth, email, storage, Stripe, validation, rate limiting)
context/ Client-side UserContext

Key patterns

API routes follow a consistent shape: validate input with Zod → check auth/role via getCurrentUser() → query Prisma → return JSON. Copy an existing route as a starting point.

Auth is session-based (no NextAuth). lib/auth.ts handles token creation, hashing (SHA256), and the getCurrentUser() helper used in every protected route. Sessions expire after 30 days.

Admin protection is enforced in the admin layout: CUSTOMER role is blocked, ADMIN and OWNER are allowed. A mustChangePassword flag redirects new owners to a password-change page before anything else.

Validation schemas live in lib/validate.ts (Zod). Add new schemas there; never validate inline in routes.

Image uploads go through lib/storage.ts, which validates magic bytes (JPEG/PNG/WebP/ICO) before writing to /app/public/uploads/<productId>/. Uploads are stored in a named Docker volume (uploads) shared between the app and caddy containers.

Emails are sent via lib/email.ts (Nodemailer). Locally they land in Mailpit; in production, configure SMTP env vars.

Rate limiting is IP-based and backed by the LoginAttempt DB table (10 attempts / 15 min). Logic is in lib/rate-limit.ts.

Data model (high level)

  • User → has role (CUSTOMER / ADMIN / OWNER), sessions, orders, reviews
  • Product → belongs to one ProductType (defines the attribute schema), many-to-many with Category, has MediaAsset, ProductVariant, Review
  • ProductType → defines a JSON schema for product attributes (e.g. "clothing" has size/color)
  • ProductVariant → SKU, price, stock per variante (es. taglia/colore di un prodotto)
  • Category → hierarchical (self-referential parentId), many-to-many with Product
  • Order → status state machine (PENDING → PAID → FULFILLED, CANCELLED, REFUNDED), has OrderItem and Payment
  • PasswordResetToken → token hashed con scadenza per il flusso reset password
  • Page / PageSection → CMS minimale per pagine statiche (slug, titolo, sezioni JSON ordinate)
  • SiteSettings — key/value store for shop configuration (branding, etc.)
  • AuditLog — tracks admin actions

Environment variables

Copy .env.example to .env. For production, generate a strong AUTH_SECRET with openssl rand -hex 32 and replace all placeholder values. The DATABASE_URL uses the Docker service name db as host — do not change it for containerised deployments.

Debugging

Check app logs (first thing to do):

docker compose logs -f app          # live
docker compose logs app --tail=100  # last 100 lines

Common error patterns to look for in logs:

  • EACCES: permission denied → volume/filesystem permissions issue
  • PrismaClientKnownRequestError → DB constraint violation or bad query
  • 401 / 403 in API → session expired or role check failing
  • Silent form failures → always check res.ok in fetch calls; UI may show success even on API error

API errors not surfacing in the UI are almost always caused by a missing res.ok check in the frontend fetch call. The pattern to use in every form submit:

const res = await fetch('/api/...', { method: 'POST', ... })
if (!res.ok) {
  const data = await res.json().catch(() => ({}))
  setError(data.error || `Errore (${res.status})`)
  return
}

Database inspection:

# Open Prisma Studio (visual DB browser)
cd app && npx prisma studio

# Or connect directly
docker compose exec db psql -U ecommerce ecommerce

Rebuild after code changes:

docker compose up -d --build app   # rebuild only the app container

TypeScript errors in IDE but not in build: likely a missing node_modules or stale tsconfig in the IDE. Run npm install inside app/ and restart the IDE TypeScript server.

Deployment

  1. Point your domain DNS to the server.
  2. Set APP_URL to https://yourdomain.com in .env.
  3. Edit Caddyfile: replace localhost with your domain.
  4. docker compose up -d --build

Caddy handles TLS automatically via Let's Encrypt. Ports 80 and 443 must be open on the firewall.