- Create packages/types with shared TypeScript domain model interfaces (User, Video, Model, Article, Comment, Recording, etc.) - Wire both frontend and backend packages to use @sexy.pivoine.art/types via workspace:* - Update backend Pothos objectRef types to use shared interfaces instead of inline types - Update frontend $lib/types.ts to re-export from shared package - Fix all type errors introduced by more accurate nullable types (avatar/banner as string|null UUIDs, author nullable, events/device_info as object[]) - Add artist_name to comment user select in backend resolver - Widen utility function signatures (getAssetUrl, getUserInitials, calcReadingTime) to accept null/undefined Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.8 KiB
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.
# 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/:idgraphql/builder.ts— Pothos schema builder (code-first GraphQL)graphql/context.ts— InjectscurrentUserfrom Redis session into every requestlib/auth.ts— Session management:nanoid(32)token stored in Redis with 24h TTL, set as httpOnly cookiedb/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 typeshooks.server.ts— Auth guard: reads session cookie, fetchesmequery, redirects if neededroutes/— 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.jsat root) —anyis 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
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.