# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Overview `sexy.pivoine.art` is a self-hosted adult content platform (18+) built as a pnpm monorepo with three packages: `frontend` (SvelteKit 5), `backend` (Fastify + GraphQL), and `buttplug` (hardware integration via WebBluetooth/WASM). ## Common Commands Run from the repo root unless otherwise noted. ```bash # Development pnpm dev:data # Start postgres & redis via Docker pnpm dev:backend # Start backend on http://localhost:4000 pnpm dev # Start backend + frontend (frontend on :3000) # Linting & Formatting pnpm lint # ESLint across all packages pnpm lint:fix # Auto-fix ESLint issues pnpm format # Prettier format all files pnpm format:check # Check formatting without changes # Build pnpm build:frontend # SvelteKit production build pnpm build:backend # Compile backend TypeScript to dist/ # Database migrations (from packages/backend/) pnpm migrate # Run pending Drizzle migrations ``` ## Architecture ### Monorepo Layout ``` packages/ frontend/ # SvelteKit 2 + Svelte 5 + Tailwind CSS 4 backend/ # Fastify v5 + GraphQL Yoga v5 + Drizzle ORM buttplug/ # TypeScript/Rust hybrid, compiles to WASM ``` ### Backend (`packages/backend/src/`) - **`index.ts`** — Fastify server entry: registers plugins (CORS, multipart, static), mounts GraphQL at `/graphql`, serves transformed assets at `/assets/:id` - **`graphql/builder.ts`** — Pothos schema builder (code-first GraphQL) - **`graphql/context.ts`** — Injects `currentUser` from Redis session into every request - **`lib/auth.ts`** — Session management: `nanoid(32)` token stored in Redis with 24h TTL, set as httpOnly cookie - **`db/schema/`** — Drizzle ORM table definitions (users, videos, files, comments, gamification, etc.) - **`migrations/`** — SQL migration files managed by Drizzle Kit ### Frontend (`packages/frontend/src/`) - **`lib/api.ts`** — GraphQL client (graphql-request) - **`lib/services.ts`** — All API calls (login, videos, comments, models, etc.) - **`lib/types.ts`** — Shared TypeScript types - **`hooks.server.ts`** — Auth guard: reads session cookie, fetches `me` query, redirects if needed - **`routes/`** — SvelteKit file-based routing: `/`, `/login`, `/signup`, `/me`, `/models`, `/models/[slug]`, `/videos`, `/play/[slug]`, `/magazine`, `/leaderboard` ### Asset Pipeline Backend serves images with server-side Sharp transforms, cached to disk as WebP. Presets: `mini` (80×80), `thumbnail` (300×300), `preview` (800px wide), `medium` (1400px wide), `banner` (1600×480 cropped). ### Gamification Points + achievements system tracked in `user_points` and `user_stats` tables. Logic in `packages/backend/src/lib/gamification.ts` and the `gamification` resolver. ## Code Style - **TypeScript strict mode** in all packages - **ESLint flat config** (`eslint.config.js` at root) — `any` is allowed but discouraged; enforces consistent type imports - **Prettier**: 2-space indent, trailing commas, 100-char line width, Svelte plugin - Migrations folder (`packages/backend/src/migrations/`) is excluded from lint ## Environment Variables (Backend) | Variable | Purpose | |----------|---------| | `DATABASE_URL` | PostgreSQL connection string | | `REDIS_URL` | Redis connection string | | `COOKIE_SECRET` | Session cookie signing | | `CORS_ORIGIN` | Frontend origin URL | | `UPLOAD_DIR` | File storage path | | `SMTP_HOST/PORT/EMAIL_FROM` | Email (Nodemailer) | ## Docker ```bash docker compose up -d # Start all services (postgres, redis, backend, frontend) arty up -d # Preferred way to manage containers in this project ``` Production images are built and pushed to `dev.pivoine.art` via Gitea Actions on push to `main`.