# AGENTS.md

## Scope
This file applies to the entire repository. Follow any more specific AGENTS.md files if they appear in subdirectories.

## Project Overview
- Repo contains two Node.js apps: `backend/` (Express) and `hls-player/` (Vite + React + TypeScript). (backend/index.js:1, hls-player/src/main.tsx:1)
- Dev + prod orchestration lives in `dev.sh` and `prod.sh` at repo root. (dev.sh:1, prod.sh:1)

## Command Quick Reference
> All commands are verified from `package.json`, scripts, and shell files. Run from repo root unless noted.

### Install
- Frontend: `cd hls-player && npm install` (hls-player/package.json:6)
- Backend: `cd backend && npm install` (backend/package.json:6)
- Combined (dev script installs missing deps): `./dev.sh` (dev.sh:21)

### Dev
- Full stack (frontend + backend): `./dev.sh` (dev.sh:29)
- Backend only: `cd backend && npm run dev` (backend/package.json:7)
- Frontend only: `cd hls-player && npm run dev -- --host 0.0.0.0 --port 7594 --strictPort` (dev.sh:38-42)

### Build
- Frontend build: `cd hls-player && npm run build` (hls-player/package.json:8)
- Full stack production startup: `./prod.sh` (prod.sh:78)

### Lint (frontend)
- `cd hls-player && npm run lint` (hls-player/package.json:9)

### Tests
#### Backend (Node test runner)
- All tests: `cd backend && npm test` (backend/package.json:9)
- Single file: `cd backend && node --test tests/proxy.test.js` (backend/tests/proxy.test.js:1)
- Single test name: `cd backend && node --test --test-name-pattern "rewrites playlist urls" tests/proxy.test.js` (backend/tests/proxy.test.js:27)

#### Frontend (Vitest)
- All tests: `cd hls-player && npm test` (hls-player/package.json:11)
- Single file: `cd hls-player && npx vitest src/components/HlsPlayer.test.tsx` (hls-player/src/components/HlsPlayer.test.tsx:1)
- Single test name: `cd hls-player && npx vitest -t "renders a video element" src/components/HlsPlayer.test.tsx` (hls-player/src/components/HlsPlayer.test.tsx:30)

## Repository-Specific Rules (Cursor/Copilot)
- No `.cursor/rules/`, `.cursorrules`, or `.github/copilot-instructions.md` files exist in this repo. (repo root listing)
- If they are added later, update this file and follow them.

## Environment & Runtime Config
| Variable | Source | Default | Purpose |
|----------|---------|---------|---------|
| `.env` path | Loaded by `dev.sh` and `prod.sh` from `hls-player/.env` | - | Main env file location |
| `.env.example` | Template in `hls-player/.env.example` | - | Example configuration |
| `BACKEND_PORT` | `backend/index.js:33-34` | `2738` | Express server port |
| `BACKEND_HOST` | `backend/index.js:33-34` | `0.0.0.0` | Express bind address |
| `FRONTEND_PORT` | `dev.sh:7-10`, `vite.config.ts:4-5` | `7594` | Vite dev server port |
| `FRONTEND_HOST` | `dev.sh:7-10` | `0.0.0.0` | Vite bind address |
| `FRONTEND_URL` / `FRONTEND_PUBLIC_URL` | `dev.sh:5-15` | `http://127.0.0.1:7594` | Frontend CORS origins |
| `FRONTEND_ALLOWED_HOSTS` | `hls-player/vite.config.ts:6-20` | `''` | Vite dev/preview host allowlist |
| `VITE_BACKEND_URL` | Baked into frontend build | - | Backend origin for build-time resolution |
| `window.__HLS_PLAYER_ENV__` | `public/runtime-config.js:1` | - | Runtime backend URL override |
| `ADMIN_USERNAME` / `ADMIN_PASSWORD` | `backend/index.js:126-127` | *required for admin* | Admin credentials |
| `ENABLE_LOCAL_MEDIA` | `backend/index.js:98-99` | `'0'` | Set to `1` to enable `/local/*` APIs |
| `LOCAL_MEDIA_ROOT` | `backend/index.js:98-99` | `''` | Path to media library |
| `ADMIN_SESSION_TTL_SECONDS` | `backend/index.js:103-104` | `0` (no expiry) | Admin session lifetime |
| `LOCAL_SHARE_TTL_SECONDS` | `backend/index.js:103-104` | `86400` (24h) | Default share TTL |
| `LOCAL_SHARE_MAX_TTL_SECONDS` | `backend/index.js:128` | `2592000` (30d) | Max share TTL |

## Backend Notes
- Backend uses Express with Helmet, rate limiting, and trust-proxy settings. (backend/index.js:1-152)
- CORS is permissive by default; tighten via FRONTEND_ALLOWED_HOSTS. (backend/index.js:157-163)
- Security headers via Helmet; CSP disabled for HLS compatibility. (backend/index.js:140-144)
- Global rate limit: 100 req/15 min per IP. (backend/index.js:147-152)

**Admin Authentication:**
- Login/logout at `POST /admin/login`, `POST /admin/logout`, `GET /admin/me`. (backend/adminAuth.js:218-254)
- Uses session cookies (HttpOnly, SameSite-aware, timing-safe credential check). (backend/adminAuth.js:1-278)
- Sessions stored in JSON file (default: `backend/data/admin-sessions.json`). (backend/adminSessionStore.js:1-127)
- Configure via `ADMIN_USERNAME`, `ADMIN_PASSWORD`, `ADMIN_SESSION_TTL_SECONDS`. (backend/index.js:125-133)

**Local Media & Sharing:**
- Enabled via `ENABLE_LOCAL_MEDIA=1` + `LOCAL_MEDIA_ROOT=<path>`. (backend/index.js:98-123)
- Browse files: `GET /local/browse?dir=...` (admin-only). (backend/localMedia.js:various)
- Create shares: `POST /local/shares` with `{ path, ttlSeconds }`. (backend/localMediaStore.js:various)
- List/revoke shares: `GET /local/shares`, `DELETE /local/shares/:id`. (backend/index.js:various)
- Serve shared files: `GET /local/share/file/:id` (Range-aware), `GET /local/share/hls/:id/*`. (backend/index.js:various)

**File Upload:**
- Image upload: `POST /upload` with `multipart/form-data` key `file`. (backend/index.js:165-210)
- Max size: 5MB. Allowed MIME: image/jpeg, image/png, image/gif, image/webp, image/tiff, image/bmp. (backend/index.js:167-184)
- Uploaded files stored in `backend/uploads` and served at `GET /uploads/:filename`. (backend/index.js:166, 212-217)

**HLS Packaging:**
- On-demand packaging for `.ts` files via ffmpeg. (backend/hlsPackager.js:various)
- Caches output in `backend/data/hls-cache/<shareId>/`. (backend/index.js:102)
- Configure via `FFMPEG_PATH`, `FFMPEG_MAX_JOBS`, `LOCAL_TRANSCODE_TARGET`. (backend/index.js:109-121)

**Proxy Endpoint:**
- `GET /proxy?url=...` with playlist rewriting. (backend/proxy.js, backend/index.js:various)
- Blocks non-http(s) URLs and private/localhost targets. (backend/proxy.js:19-31)
- Exposes Content-Range header for range requests. (backend/index.js:161-162)

**Health Check:**
- `GET /health` returns `{ status: 'ok' }`. (backend/index.js:various)

## Frontend Notes
- React entrypoint mounts `App` under `BrowserRouter`. (hls-player/src/main.tsx:1-9)
- Routes include `/` (Toolbox), `/embed`, and `/admin` (redirects to `/`). (hls-player/src/App.tsx:1-18)
- Player config defaults live in `types/playerConfig.ts`. (hls-player/src/types/playerConfig.ts:1-23)
- Querystring config encoding/decoding is in `lib/configCodec.ts`. (hls-player/src/lib/configCodec.ts:1-75)
- HLS playback uses `hls.js` with retry logic + status callbacks. (hls-player/src/lib/hlsController.ts:1-104)
- `HlsPlayer` integrates Plyr and builds proxy URLs via backend base. (hls-player/src/components/HlsPlayer.tsx:1-65)
- Admin file browsing via `FileBrowserDialog` component (dialog for /local/browse). (hls-player/src/components/toolbox/FileBrowserDialog.tsx)
- Tests use `@testing-library` + Vitest with global cleanup. (hls-player/src/test/setup.ts:1-10)

## Design & Color Scheme (Critical)
- Primary palette must match the Vietnam national flag: dominant red surfaces with yellow accents only. No additional hues are permitted for UI elements, states, or typography.
- When introducing new components or themes, ensure background/base tones stay red and interactive/accent elements stay yellow; avoid gradients or tertiary colors that diverge from this pairing.
- If accessibility adjustments are needed (contrast), adjust luminance within the red/yellow pairing rather than introducing new colors; document any contrast tweaks.

## Code Style & Conventions
> Derived from current code patterns and configuration.

### Language & Tooling
- Backend: ESM Node (`"type": "module"` in `backend/package.json`). (backend/package.json:5)
- Frontend: React + TypeScript, Vite, ESLint flat config, Vitest. (hls-player/package.json:7-40)
- TypeScript strict mode is enabled. (hls-player/tsconfig.app.json:16)
- ESLint ignores `dist/`. (hls-player/eslint.config.js:8-10)

### Formatting
- JS/TS uses single quotes and no semicolons. (e.g., hls-player/src/main.tsx:1-9, backend/index.js:1-12)
- Indentation is 2 spaces. (repo-wide)
- Keep lines readable; favor early returns over nested blocks. (backend/index.js:106-115, configCodec.ts:26-38)

### Imports
- Use ESM `import` / `export` everywhere (backend is ESM, frontend uses ESM). (backend/index.js:1-12, hls-player/src/main.tsx:1-4)
- Group built-ins, third-party, then local imports. (backend/index.js:1-12)
- Use `type` imports for types in TS/TSX. (hls-player/src/components/HlsPlayer.tsx:5-8)
- Avoid default re-exports or barrel files unless already present. (pattern observed)

### Naming
- React components: `PascalCase` file + component name. (hls-player/src/components/HlsPlayer.tsx:45)
- Hooks: `useX` naming, return tuples or named values. (hls-player/src/hooks/useDebounce.ts:3, useUrlDraft.ts:3)
- Constants: `UPPER_SNAKE_CASE` for module-level constants. (hls-player/src/lib/configCodec.ts:7)
- Utility functions: `camelCase`. (backend/proxy.js:9, hls-player/src/lib/backendUrl.ts:10)
- Types/interfaces: `PascalCase` with clear domain names. (hls-player/src/types/playerConfig.ts:3)

### Types & State
- Prefer explicit types for public APIs and complex objects. (hls-player/src/lib/hlsController.ts:19-23)
- Use `as const` only for literal unions. (hls-player/src/lib/configCodec.test.ts:19)
- Keep React state scoped to component responsibilities. (hls-player/src/components/HlsPlayer.tsx:45-55)

### React Patterns
- Favor functional components + hooks. (hls-player/src/components/HlsPlayer.tsx:45)
- Use `useMemo`/`useCallback` for derived values or callbacks passed to children. (HlsPlayer.tsx:52-65)
- Avoid side-effects in render; use `useEffect`/`useLayoutEffect`. (HlsPlayer.tsx:67-114)
- Clean up event listeners and subscriptions in effects. (HlsPlayer.tsx:103-113)

### Error Handling
- Backend: return JSON with `{ error: ... }` and HTTP status codes. (backend/index.js:77-80, 105-115, 175-177)
- Frontend: surface user-facing errors with state, and log diagnostics to console. (hls-player/src/components/EmbedSnippet.tsx:62-87)
- Prefer guarding against invalid input early. (backend/proxy.js:19-31, configCodec.ts:26-38)

### Networking & Security
- Backend uses validation for proxy URLs and rate limiting. (backend/index.js:27-35, backend/proxy.js:19-31)
- Reject non-http(s) URLs and private/localhost targets in proxy. (backend/proxy.js:22-27)
- When adding new endpoints, apply similar validation + CORS behavior. (backend/index.js:39-43, 93-97)

### Tests
- Backend uses Node’s built-in test runner (`node:test`). (backend/tests/proxy.test.js:1)
- Frontend uses Vitest + Testing Library + `setup.ts` cleanup. (hls-player/src/test/setup.ts:1-10)
- Keep tests deterministic, avoid timers unless necessary. (general pattern)

## Key Files
- Backend entry: `backend/index.js`
- Backend proxy logic: `backend/proxy.js`
- Frontend app entry: `hls-player/src/main.tsx`
- Frontend router: `hls-player/src/App.tsx`
- Frontend player logic: `hls-player/src/lib/hlsController.ts`
- Config encoding/decoding: `hls-player/src/lib/configCodec.ts`
- Runtime backend resolution: `hls-player/src/lib/backendUrl.ts`

## Recent Update
- Updated: 2026-01-22 (aligned README.md and CLAUDE.md for consistency)
- Previous: Admin auth modules (`adminAuth.js`, `adminSessionStore.js`) added for timing-safe login and persistent sessions.
