93cfe1ad5e
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.
136 lines
6.2 KiB
Markdown
136 lines
6.2 KiB
Markdown
# 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 <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):**
|
|
```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.
|