diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8bfdfe6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,299 @@ +# Library Project - Claude Instructions + +This is a personal media library tracking application built with an Nx monorepo structure. It tracks games, books, music, art, shows, and manga with user profiles, comments, achievements, and social features. + +## Git Commit Guidelines + +**CRITICAL**: When working on this project: +- **Always commit as Hikari** using: `--author="Hikari " --no-gpg-sign` +- **Always ask permission before creating commits** - Never commit without explicit user approval +- **Never modify git config** - Always use CLI flags for author information + +## User Permissions + +This is **Naomi's personal library**: +- **Admin (Naomi only)** - Can create, update, and delete all media items (games, books, music, art, shows, manga) +- **Regular users** - Can only: + - Like items + - Comment on items (with markdown support) + - Submit suggestions for new items (requires admin approval) + - Customise their own profile + +All CRUD operations on media items are **admin-only**. Regular users interact through the social features (likes, comments, suggestions). + +## Project Structure + +This is an **Nx monorepo** containing: +- **`api/`** - Fastify backend API with Prisma ORM +- **`apps/frontend/`** - Angular frontend application +- **`shared-types/`** - Shared TypeScript types between frontend and backend +- **`libs/`** - Shared libraries + +## Technology Stack + +### Backend (api/) +- **Runtime**: Node.js v24 +- **Framework**: Fastify 5.x with plugins: + - `@fastify/autoload` - Auto-loads routes and plugins + - `@fastify/jwt` - JWT authentication + - `@fastify/oauth2` - Discord OAuth integration + - `@fastify/helmet` - Security headers + - `@fastify/cors` - CORS configuration + - `@fastify/csrf-protection` - CSRF protection + - `@fastify/rate-limit` - Rate limiting + - `@fastify/cookie` - Cookie handling + - `@fastify/static` - Static file serving +- **Database**: MongoDB with Prisma ORM +- **Logging**: `@nhcarrigan/logger` (custom logger package) +- **Markdown**: `marked` for parsing, `dompurify` + `jsdom` for sanitisation + +### Frontend (apps/frontend/) +- **Framework**: Angular 21.x +- **Icons**: FontAwesome (solid + brands) +- **Styling**: Angular's built-in styling system + +### Development Tools +- **Package Manager**: pnpm v10 +- **Monorepo**: Nx 22.x +- **Linting**: ESLint 9 (flat config) with `@nhcarrigan/eslint-config` +- **Testing**: Jest 30.x +- **Build**: esbuild (backend), Angular CLI (frontend) +- **Secrets**: 1Password CLI (`op run`) + +## Database Schema + +The Prisma schema (`api/prisma/schema.prisma`) defines these models: +- **Game** - Video game tracking with platform, status, rating, cover image, tags, series +- **Book** - Book tracking with author, ISBN, status, rating, cover image, tags, series +- **Music** - Music tracking (album/single/EP) with artist, status, rating, cover art, tags +- **Art** - Art collection with artist, description, image, tags +- **Show** - TV/anime/film tracking with type, status, rating, cover image, tags +- **Manga** - Manga tracking with author, status, rating, cover image, tags +- **User** - User profiles with Discord auth, slug, bio, badges, achievements, social links +- **Comment** - Comments on any media type with markdown support +- **AuditLog** - Security and action logging +- **Suggestion** - User-submitted content suggestions +- **Like** - User likes on media items +- **RefreshToken** - JWT refresh token storage +- **ProfileReport** - User profile reporting system +- **CommentReport** - Comment reporting system +- **UserAchievement** - Achievement tracking with progress + +All models use MongoDB ObjectIds and include timestamps (`createdAt`, `updatedAt`). + +## Authentication Flow + +The application uses **Discord OAuth** for authentication: +1. User clicks "Login with Discord" +2. OAuth flow redirects to Discord for authorisation +3. Discord redirects back with authorisation code +4. Backend exchanges code for Discord user info +5. Backend creates/updates User record +6. Backend issues JWT access token (15m expiry) and refresh token (7d expiry) +7. Frontend stores tokens in cookies +8. Access token in `Authorization` header for API requests +9. Refresh token used to obtain new access tokens + +See `api/AUTH_FLOW.md` for detailed authentication implementation. + +## Image Upload Handling + +The application supports **two methods** for cover images: +1. **Regular URLs** - Standard image URLs (max 2048 characters) +2. **Base64 Data URLs** - Inline base64-encoded images (max 5MB decoded size) + +### Base64 Upload Constraints +- **Fastify body limit**: 10MB (accommodates base64 overhead) +- **Validation**: Data URLs must match format `data:image/(jpeg|png|gif|webp|svg+xml);base64,[base64data]` +- **Size calculation**: Base64 data is extracted and decoded size calculated as `base64Data.length * 0.75` +- **Size limit**: Decoded image must be under 5MB + +### Implementation Notes +- Base64 size check happens AFTER format validation +- Regular URL length check (2048 chars) does NOT apply to data URLs +- Validation logic is in `api/src/app/services/*.service.ts` files +- Base64 validation helpers in `api/src/app/utils/validation.ts` + +## Development Workflow + +### Local Development +```bash +pnpm dev # Builds all projects and starts API with dev.env secrets +``` + +The `dev` script: +1. Runs `nx run-many --target=build --all` to build all projects +2. Uses `op run --env-file=dev.env` to inject secrets from 1Password +3. Starts the API with `NODE_ENV=production node dist/api/main.js` +4. API serves the frontend as static files from `dist/apps/frontend/browser` + +### Separate Frontend/Backend Development +```bash +pnpm start:frontend:dev # nx serve frontend (port 4200) +pnpm start:api:dev # nx serve api (port 3000, watch mode) +``` + +### Building for Production +```bash +pnpm build # Generates Prisma client, then builds all projects +``` + +### Database Management +```bash +pnpm db:gen # Generate Prisma client +pnpm db:push # Push schema changes to MongoDB (uses prod.env secrets) +``` + +### Linting & Testing +```bash +pnpm lint # Lints all projects with ESLint +pnpm test # Runs all tests with Jest +``` + +## CI/CD Pipeline + +The project uses **Gitea Actions** (`.gitea/workflows/ci.yml`): + +1. **Dependency Pin Check** - Ensures all dependencies are pinned (no `^` or `~`) +2. **Install Dependencies** - `pnpm install` +3. **Lint** - `pnpm run lint` +4. **Build** - `pnpm run build` +5. **Test** - `pnpm run test` + +CI runs on: +- Pushes to `main` branch +- Pull requests to `main` branch + +## Configuration Standards + +### TypeScript +- Uses `@nhcarrigan/typescript-config` as base +- Separate configs for app code (`tsconfig.app.json`) and tests (`tsconfig.spec.json`) +- Output directory: `dist/` for builds + +### ESLint +- Uses `@nhcarrigan/eslint-config` (ESLint 9 flat config) +- **No Prettier** - all style rules handled by ESLint +- Configured in `eslint.config.mjs` +- Angular-specific rules enabled for frontend + +### Testing +- Jest 30.x with `ts-jest` for TypeScript support +- Separate jest configs per project (e.g., `api/jest.config.cts`) +- Cypress for e2e testing (frontend-e2e project) + +## Secrets Management + +### Environment Files +- **`dev.env`** - Development secrets (1Password vault references) +- **`prod.env`** - Production secrets (1Password vault references) + +These files contain **ONLY** 1Password references (e.g., `op://vault/item/field`), NOT actual secrets. They are safe to commit. + +### Non-Secret Configuration +Configuration that isn't sensitive (URLs, intervals, feature flags) should be in code (e.g., constants files), NOT in `.env` files. + +### Running with Secrets +Always use 1Password CLI: +```bash +op run --env-file=dev.env -- +op run --env-file=prod.env -- +``` + +## Security Features + +The application implements multiple security layers: +- **Helmet** - Security headers (CSP, HSTS, etc.) +- **CORS** - Configured origin restrictions +- **CSRF Protection** - Token-based CSRF validation +- **Rate Limiting** - Request rate limits per IP +- **JWT** - Short-lived access tokens with refresh token rotation +- **Audit Logging** - All security events logged to AuditLog model +- **Content Sanitisation** - Markdown comments sanitised with DOMPurify +- **Input Validation** - All inputs validated before database operations + +## API Routes Structure + +Routes are auto-loaded via `@fastify/autoload` from `api/src/app/routes/`: +- Each route file exports Fastify route handlers +- Routes follow REST conventions (GET, POST, PUT, DELETE) +- All routes require JWT authentication (except auth endpoints) +- Route handlers call service functions for business logic + +Example structure: +- `api/src/app/routes/games/index.ts` - Game CRUD endpoints +- `api/src/app/services/game.service.ts` - Game business logic + +## Code Style Conventions + +### General +- Use **clear, descriptive variable/function names** +- Prefer **self-documenting code** over comments +- Only use comments to explain **reasoning** when intent isn't clear +- Use **British English** spelling (colour, organise, whilst) + +### TypeScript +- Prefer `interface` over `type` for object shapes +- Use `const` assertions where appropriate +- Enable strict mode (inherited from `@nhcarrigan/typescript-config`) + +### Error Handling +- Service functions throw `Error` instances with descriptive messages +- Route handlers wrap service calls in try-catch blocks +- Return 400 for validation errors with error message +- Return 500 for unexpected errors (message hidden in production) +- All errors logged to AuditLog for security events + +### Validation +- Validation utilities in `api/src/app/utils/validation.ts` +- Constants for max lengths in `shared-types/src/lib/constants.ts` +- Validate early in service functions before database operations + +## Common Gotchas + +### Base64 Image Uploads +- **Body limit must be 10MB** (`api/src/main.ts`) to accommodate base64 overhead +- Validate data URL format BEFORE size check +- Extract base64 portion (after comma) for size calculation +- Don't apply regular URL length validation to data URLs + +### Prisma Client Generation +- Must run `pnpm db:gen` before building if schema changed +- The build script automatically runs this +- Prisma client is generated to `api/generated/` + +### Nx Cache +- Nx caches build outputs for faster rebuilds +- Clear cache with `nx reset` if experiencing issues +- Cache stored in `.nx/cache/` + +### MongoDB ObjectIds +- All IDs are MongoDB ObjectIds (not UUIDs or auto-increment integers) +- Use `@db.ObjectId` in Prisma schema +- Use `String` type in TypeScript for ObjectIds + +## Deployment + +The application is deployed in production mode: +```bash +pnpm build # Build all projects +pnpm start # Start with prod.env secrets +``` + +Production start command: +```bash +NODE_ENV=production op run --env-file=prod.env -- node dist/api/main.js +``` + +The API serves the built frontend as static files, so only the API process needs to run in production. + +## Important Notes + +- This is a **personal project** for Naomi's media tracking +- Uses **Discord OAuth** for authentication (no other auth methods) +- **MongoDB** is the only supported database (Prisma schema is MongoDB-specific) +- All **timestamps** are stored as UTC in the database +- **Achievements system** tracks user activity and unlocks (see UserAchievement model) +- **Comments support markdown** with sanitisation +- **User profiles** support custom slugs, bios, and social links +- **Reporting system** for profiles and comments with moderation workflow