# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Openplace 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)
