# 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 <service>          # 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`.
