# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview FurryPlace is an unofficial open-source backend for wplace (a collaborative pixel art canvas), built with TypeScript, tinyhttp, Prisma, and MySQL. The system manages user authentication, pixel painting with charge-based rate limiting, alliances, leaderboards, and moderation features. ## Development Commands ### Essential Commands - `pnpm dev` - Run development server with hot reload (watches src/ directory) - `pnpm build` - Compile TypeScript to dist/ - `pnpm start` - Run production build from dist/ - `pnpm test` - Run tests once - `pnpm test:watch` - Run tests in watch mode - `pnpm lint` - Check for linting issues - `pnpm lint:fix` - Auto-fix linting issues ### Database Commands - `pnpm db:push` - Push schema changes to database and regenerate Prisma client - `pnpm db:generate` - Regenerate Prisma client only - `pnpm db:migrate` - Create and run a new migration - `pnpm seed` - Seed database with initial data ### Requirements - Node.js >= 24 - pnpm >= 10 - MySQL/MariaDB ## Architecture ### Backend Structure **Entry Point**: [src/index.ts](src/index.ts) - Sets up tinyhttp app with middleware (CORS, cookie parser, JSON body parsing, logging) and registers all route modules. **Authentication Flow**: JWT tokens stored in `j` cookie → validated by [src/middleware/auth.ts](src/middleware/auth.ts) → checks session validity in database → attaches `req.user` with `{ id, sessionId }` **Database**: Prisma ORM with MySQL. Global instance exported from [src/config/database.ts](src/config/database.ts) and injected into request via middleware. **Route Organization**: Each feature module exports a function that registers routes on the tinyhttp app: - [src/routes/auth.ts](src/routes/auth.ts) - Registration, login, logout - [src/routes/pixel.ts](src/routes/pixel.ts) - Pixel painting, tile generation, pixel info - [src/routes/alliance.ts](src/routes/alliance.ts) - Alliance CRUD, invites, bans, leaderboards - [src/routes/me.ts](src/routes/me.ts) - User profile, favorite locations, settings - [src/routes/admin.ts](src/routes/admin.ts) - User management, bans, timeouts, tickets - [src/routes/moderator.ts](src/routes/moderator.ts) - Moderation actions - [src/routes/leaderboard.ts](src/routes/leaderboard.ts) - Global leaderboards - [src/routes/store.ts](src/routes/store.ts) - Purchase colors and flags **Service Layer**: Business logic isolated in service classes: - [src/services/pixel.ts](src/services/pixel.ts) - `PixelService` handles pixel painting with charge validation, color unlocking checks, tile image generation using @napi-rs/canvas, and level calculation - [src/services/alliance.ts](src/services/alliance.ts) - `AllianceService` handles alliance creation, member management, bans - [src/services/user.ts](src/services/user.ts) - `UserService` handles user profile updates, favorites **Core Systems**: 1. **Charge System** ([src/utils/charges.ts](src/utils/charges.ts)): Rate-limiting mechanism where users have `maxCharges` (default 20) that regenerate every `chargesCooldownMs` (default 30s). Painting consumes charges. Function `calculateChargeRecharge()` computes current charge based on time elapsed. 2. **Bitmap System** ([src/utils/bitmap.ts](src/utils/bitmap.ts)): `WplaceBitMap` class stores boolean flags as packed bytes for efficient storage (used for unlocked colors, flags). Stored in database as Bytes, converted to base64 for API responses. 3. **Color Palette** ([src/utils/colors.ts](src/utils/colors.ts)): Defines available colors with RGB values and whether they're paid. `checkColorUnlocked()` validates if user has purchased a color by checking bitmap. 4. **Tile System**: Canvas divided into 1000x1000 tiles. Each pixel has coordinates `(tileX, tileY, x, y)`. Tile images dynamically generated on-demand from pixel data. 5. **Regions** ([src/config/regions.ts](src/config/regions.ts)): Maps coordinates to geographic regions/countries. Users get 10% charge discount when painting in their equipped flag's region. **Currently returns placeholder data - implementation needed.** ### Database Schema Key models in [prisma/schema.prisma](prisma/schema.prisma): - **User**: Core user data, charge state, pixels painted, level, alliance membership, equipped flag, unlocked colors bitmap - **Pixel**: Individual pixel with coordinates (tileX, tileY, x, y), colorId, paintedBy userId, timestamp - **Tile**: Metadata for 1000x1000 tile regions, has many Pixels - **Alliance**: Groups with members, bans, invites, HQ coordinates, total pixels painted - **Session**: JWT session tracking with expiration - **Ticket**: Moderation reports with evidence - **UserNote**: Moderator notes on users ### Frontend Pre-built SvelteKit frontend in [frontend/](frontend/) directory (served as static files, not part of development workflow). Backend serves 404.html for unmatched routes. ## Key Implementation Patterns 1. **Route Pattern**: Routes validate input → call service method → return JSON or handle service errors via `handleServiceError()` 2. **Service Pattern**: Services receive Prisma client in constructor, contain business logic, throw descriptive errors that are caught by error handler middleware 3. **Bulk Pixel Insert**: Painting uses raw SQL `INSERT ... ON DUPLICATE KEY UPDATE` for performance when updating multiple pixels 4. **Level Calculation**: `Math.floor(Math.sqrt(pixelsPainted / 100)) + 1` 5. **Validation**: Separate validator functions in [src/validators/](src/validators/) for common input patterns (seasons, coordinates, pagination) 6. **Error Responses**: Standardized via `createErrorResponse()` and HTTP_STATUS constants in [src/utils/response.ts](src/utils/response.ts) ## Environment Setup Copy `.env.example` to `.env` and configure: - `DATABASE_URL` - MySQL connection string (format: `mysql://user:password@host/database`) - `JWT_SECRET` - Secret key for JWT signing - `PORT` - Server port (default 3000) ## Important Notes - The project is a work-in-progress with incomplete features (see README.md warnings) - Region lookup system is stubbed and returns placeholder data - needs implementation - Authentication uses JWT cookies named `j` - All API responses use JSON format - The backend is designed to work with the wplace.live frontend protocol - Production deployment requires SSL/HTTPS (enforced by design) - Use `pnpm` as package manager (not npm)