diff --git a/.gitignore b/.gitignore index 7a64e66..379810a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,11 +6,15 @@ # Node app/node_modules/ node_modules/ +test/node_modules # Next.js build output app/.next/ app/out/ +# Test coverage report +app/coverage/ + # Prisma generated client (rebuilt on npm install) app/node_modules/.prisma/ diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..38f1273 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,135 @@ +# 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 + +```bash +# 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/`) + +```bash +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 # 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//`. 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):** +```bash +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: +```ts +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:** +```bash +# 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:** +```bash +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.