Files

187 lines
7.2 KiB
Markdown

# AGENT.md — Developer Agent Guidelines for WhatIDo
This document defines how AI agents should work with the WhatIDo codebase. Follow these rules strictly.
---
## Project Overview
**WhatIDo** — Accounting system for an educational center: attendance journal, student project works, group gallery, detached files, and public showcase pages (share links).
- **Stack**: Node.js 20 + Express, PostgreSQL 16, Docker Compose, Tailscale (Serve/Funnel)
- **Architecture**: Single Express server (`server.js`) + static frontend in `public/`
- **Deployment**: Docker Compose (app + db + tailscale), bind-mounted uploads, named volume for Postgres data
- **Auth**: Admin-only via `X-Admin-Token` header (value = `ADMIN_PASSWORD` env var). No user sessions.
---
## Development Rules
### 1. Code Style
- **No comments** unless explicitly requested
- **ES modules not used** — CommonJS (`require`) throughout
- **Error handling**: try/catch with explicit status codes, no global error handler
- **Validation**: Inline helper functions (`reqInt`, `reqStr`, `optInt`, etc.) — use them
- **Security first**: All uploads validated, path traversal blocked, rate limits on public routes
### 2. Database
- **Schema**: Defined in `db/init.sql` (runs on first container start)
- **Migrations**: `db/migration.sql` for existing DBs — update both when changing schema
- **Connection**: Single `Pool` from `pg`, `DATABASE_URL` from env
- **Queries**: Parameterized only (`$1`, `$2`...), never string interpolation
- **Transactions**: Use `client.query('BEGIN')` / `COMMIT` / `ROLLBACK` for multi-statement ops
### 3. File Uploads
- **Multer configs**: `upload` (images only), `adminUpload` (wider allowed ext), `uploadBackup` (restore)
- **Limits**: 10 MB/file, 30 MB total per entry
- **Storage**: `uploads/` bind-mounted to host, filenames = `timestamp-random.ext`
- **HEIC**: Auto-converted to JPEG via `heic-convert`
- **Cleanup**: `safeUnlink` / `sweepOrphanedUploads` — never delete outside `uploads/`
### 4. API Patterns
- **Admin routes**: `requireAdmin` middleware (checks `X-Admin-Token`)
- **Public routes**: `apiLimiter` (300/15min), `entryLimiter` (10/15min), `fileLimiter` (300/15min)
- **Responses**: JSON, `{ error: 'message' }` on failure, data directly on success
- **Pagination**: `limit` / `offset` query params, return `{ items, total }` or `{ entries, total }`
- **Filters**: `group_id`, `date_from`, `date_to`, `student_name`, `search`, `deleted`
### 5. Frontend (public/)
- Vanilla HTML/CSS/JS, no build step
- Each page = single HTML file + shared `admin.js` / `admin.css`
- API calls via `fetch` with `X-Admin-Token` from `localStorage`
- Share pages (`share.html`, `links.html`) work without auth
### 6. Docker / Compose
- **Dockerfile**: Node 20 Alpine, installs deps, generates self-signed TLS cert
- **docker-compose.yml**: 3 services (db, app, tailscale)
- `db`: postgres:16-alpine, healthcheck, init.sql mounted
- `app`: builds from Dockerfile, exposes 3003/3443, mounts uploads
- `tailscale`: host network, NET_ADMIN, runs `start-tailscale.sh` (funnel to 127.0.0.1:3443)
- **Env vars** (required): `ADMIN_PASSWORD`, `DB_PASSWORD`
- **Port 443 on host** must be free (tailscale listens directly)
### 7. Tailscale Publication
- No external IP / port forwarding needed
- Access: `https://whatido.<tailnet>.ts.net` (inside tailnet + internet via Funnel)
- First run: `docker exec -it whatido-tailscale-1 tailscale up --hostname=whatido` → authorize in browser
- Enable Serve/Funnel in Tailscale admin console for the node
- Cert: app generates self-signed cert at build (`certs/cert.pem`), mounted into tailscale container
### 8. Backup / Restore
- **Admin UI**: `/api/backup` (download tar.gz), `/api/restore` (upload tar.gz)
- **Scripts**: `scripts/backup.sh`, `scripts/restore.sh` (host-level)
- Backup format: `data.json` (all tables) + `uploads/` directory
- Restore validates all data, resets sequences, sweeps orphans
---
## Common Tasks
### Add a new API endpoint
1. Add route in `server.js` (group with related routes)
2. Use `requireAdmin` for admin, `apiLimiter`/`fileLimiter` for public
3. Validate input with helper functions
4. Use parameterized queries, transactions if multi-table
5. Call `logAudit(req, 'action.name', { ... })` for mutations
6. Return JSON, handle errors with appropriate status codes
### Add a database column/table
1. Update `db/init.sql` (CREATE TABLE / ALTER TABLE)
2. Update `db/migration.sql` (idempotent ALTERs)
3. Update `server.js` queries that SELECT/INSERT the table
4. Test: `docker compose down && docker compose up -d --build`
### Add a frontend page
1. Create `public/newpage.html` (copy structure from existing)
2. Link in `public/admin.html` navigation if admin page
3. Use `admin.js` utilities: `api()`, `requireAuth()`, `formatDate()`, etc.
4. No build step — just refresh browser
### Modify file upload rules
- Edit `BLOCKED_EXT`, `ALLOWED_IMAGE_EXT`, `ADMIN_ALLOWED_EXT` constants
- Update Multer `fileFilter` functions
- Keep `MAX_TOTAL_UPLOAD_BYTES` and per-file limit in sync
---
## Testing & Verification
No automated test suite exists. Verify manually:
```bash
# Start stack
docker compose up -d --build
# Check logs
docker compose logs -f app
# Test API (replace TOKEN)
curl -H "X-Admin-Token: $ADMIN_PASSWORD" http://localhost:3003/api/groups
# Run backup/restore scripts
./scripts/backup.sh
./scripts/restore.sh backups/whatido-backup-<date>.tar.gz
```
---
## Security Checklist (before any change)
- [ ] No SQL interpolation — only `$1`, `$2`...
- [ ] Upload path validation via `isSafeUploadPath` / `safeUnlink`
- [ ] Rate limiter on new public routes
- [ ] Admin routes behind `requireAdmin`
- [ ] No secrets in code — only via env vars
- [ ] Helmet headers present (already global)
- [ ] CORS disabled (no `cors` middleware)
---
## File Map (key files)
| File | Purpose |
|------|---------|
| `server.js` | Entire backend (Express, routes, DB, uploads, backup) |
| `worker.js` | Background AI auto-check worker for entry messages |
| `db/init.sql` | Initial schema (runs on fresh DB) |
| `db/migration.sql` | Idempotent migrations for existing DBs |
| `docker-compose.yml` | Service definitions (app, db, tailscale) |
| `Dockerfile` | App image build |
| `public/*.html` | Frontend pages |
| `public/admin.js` | Shared frontend logic |
| `scripts/backup.sh` | Host-level backup script |
| `scripts/restore.sh` | Host-level restore script |
| `start-tailscale.sh` | Tailscale container entrypoint |
| `.env.example` | Env var template |
---
## Do Not
- ❌ Add dependencies without updating `package.json` and rebuilding
- ❌ Write files outside `uploads/` or `certs/`
- ❌ Commit `.env`, `certs/`, `uploads/`, `backups/`, `node_modules/`
- ❌ Expose DB port (5432) outside docker network
- ❌ Use `eval`, `Function` constructor, or dynamic code execution
- ❌ Add comments to code (this file excepted)
---
## Quick Commands
```bash
# Full rebuild
docker compose down && docker compose up -d --build
# App logs
docker compose logs -f app
# DB shell
docker compose exec db psql -U app -d whereldo
# Tailscale status
docker exec -it whatido-tailscale-1 tailscale status
# Manual funnel restart
docker exec whatido-tailscale-1 tailscale funnel --bg --yes https://127.0.0.1:3443
```