# AGENTS.md - Development Guidelines Essential information for AI agents and developers working on this codebase. ## Project Overview Web application for "Harmonie de Montpellier-Jacou" (music band) to manage and share sheet music (partitions). - **Frontend**: SvelteKit (Svelte 5, runes) + Tailwind CSS in `partitions/` - **Backend**: PHP 8.5 REST API in `api/` (no framework) - **Storage**: flat files under `legacy/Scores/` (no database) - **Auth**: JWT (HS256), see `api/lib/Auth.php` - **Production**: `https://partitions.c.nadal-fr.com` (front) + `https://ohmj-api.c.nadal-fr.com` (API) There is **no MySQL** and **no Vue.js** in the active codebase. The old Vue.js 2 / MySQL site is archived in `legacy/` and must not be modified. ## Stack & Versions - Node.js 26 / npm 11 - Svelte 5, SvelteKit 2, `@sveltejs/adapter-static`, Vite 5, Tailwind 3 - PHP 8.5 (CLI built-in server in development) - Dependencies: `axios`, `pdfjs-dist`, `lucide-svelte` (front); `setasign/fpdi`, `setasign/fpdf`, `tecnickcom/tcpdf` (back) > PHP 8.5 note: nullable parameters MUST use explicit `?type` syntax > (e.g. `?string $x = null`). Implicit nullable is deprecated and pollutes API output. ## Build & Run Commands ### Backend (PHP) ```bash cd api php -c php-upload.ini -S localhost:8000 router.php ``` - `-c php-upload.ini` is **required** so PDF uploads work (`upload_max_filesize`/`post_max_size` = 64M). `ini_set()` cannot change those directives at runtime. - API base URL: `http://localhost:8000` (paths have **no** `/api` prefix in the client). - Copy `api/.env.example` to `api/.env` and set a strong `JWT_SECRET`. ### Frontend (SvelteKit) ```bash cd partitions npm install npm run dev # http://localhost:5173 npm run build # outputs to partitions/build npm run preview ``` - `frontend` is a symlink to `partitions/build` (the deployed static site). - No ESLint / test framework is configured for the frontend. - `VITE_API_URL` is defined but currently unused (base URL is chosen from `import.meta.env.DEV`). ### Deploy ```bash ./deploy/deploy.sh ``` - Do NOT include `legacy/` or `api/tests.php` in the deploy archive. - `api/.env` (with `JWT_SECRET`) is part of the deployment. - The frontend build must be present in `frontend/`. ## Testing ### PHP API tests ```bash cd api php -c php-upload.ini -S localhost:8000 router.php & # if not already running rm -f /tmp/rate_* 2>/dev/null # clear rate limiting first php tests.php ``` - Expected: **53/53 tests passed (100%)**. - Covers: auth (login, bad credentials, missing/invalid token), scores CRUD, create-score-with-pieces, files tree, path traversal, rate limiting, upload validation, INI injection, security headers. - `tests.php` is kept for development only and MUST NOT be shipped in the deploy zip. ## Authentication - Login: `POST /login` with `{"username":"admin","password":"password"}` (development). - Token: JWT HS256 signed with `JWT_SECRET`, 1 hour expiry. - API calls: send `Authorization: Bearer `. - Token is stored in `localStorage` on the frontend and attached via an axios request interceptor. - Roles: `admin` (full access) and `user` (read-only). Admin routes live under `/admin/...`. - Frontend dev login page: http://localhost:5173 ## API Endpoints Public / all authenticated users: - `POST /login` - `GET /scores` - `GET /scores/:id` - `GET /scores/:id/instruments[?piece=N]` - `GET /pieces/:scoreId` - `GET /download/:path` (token via `Authorization` header or `?token=`) Admin only (`role === 'admin'`): - `POST /admin/scores` - `PUT /admin/scores/:id` - `PUT /admin/scores/:id/pieces` - `DELETE /admin/scores/:id` - `POST /admin/scores/:id/upload` - `GET /admin/scores/:id/files` - `DELETE /admin/scores/:id/files?path=...` ## File Organization ``` / ├── api/ # PHP REST API (active) │ ├── index.php # Router / entry point │ ├── router.php # Dev server router (+ CORS, .env loading) │ ├── lib/Auth.php # JWT auth │ ├── lib/ScoreScanner.php# File-based score index │ ├── config/users.json # Users (bcrypt hashes) │ ├── tests.php # Functional tests (dev only) │ └── .env # JWT_SECRET, SCORES_PATH ├── partitions/ # SvelteKit app (active) │ ├── src/lib/api.ts # Axios client + apiService │ ├── src/lib/stores/ # auth store │ ├── src/lib/components/ # PdfViewer │ ├── src/routes/ # pages (login, scores, admin) │ └── build/ # static output (gitignored) ├── frontend -> partitions/build # symlink used for deployment ├── deploy/deploy.sh # deploy archive builder ├── scripts/convert_final_v2.js # one-off filename converter ├── legacy/ # archived old site + Scores/ (do not modify) └── _builds/ # temporary work files (gitignored) ``` ## Data Model (files) ``` legacy/Scores/////.pdf legacy/Scores//score.ini ``` `score.ini`: ```ini [info] name = Nom du morceau compositor = Compositeur ressource = https://... # optional external link [pieces] count = 1 ``` - `NUM` = score id (zero-padded, e.g. `102`). - `PIECE` = movement/piece number (1 for single-piece scores). - `INSTRUMENT` = trigram code (see below). - `VERSION` = version number. - `PARTIE` = part number. Instrument codes: `dir`, `pic`, `flu`, `cla`, `clb`, `sax`, `sat`, `sab`, `coa`, `cba`, `cor`, `trp`, `trb`, `tub`, `htb`, `bas`, `per`, `crn`, `eup`, `har`, `pia`, plus `sup`/`par` for combined parts. ## Code Style ### PHP - `" # Security headers on any response curl -s -I http://localhost:8000/login -X POST \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"password"}' ``` > Known security findings from the latest review (not yet fixed): static file > passthrough in `api/router.php` exposes `.env`/`users.json`; `deleteScoreFile` > lacks path containment; `api/.env` is tracked by git; JWT is standard base64 > (not base64url) and expiry is optional; stored XSS via the `ressource` link. > See the project issue tracker / recent review notes. ## Temporary Files - Put temporary scripts and outputs in `_builds/` (gitignored). - `scripts/convert_final_v2.js` is the only committed script in `scripts/`. ## Git - Default branch: `master`. - No CI/CD configured. - Only commit when explicitly requested.