186 lines
7.1 KiB
Markdown
186 lines
7.1 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) |
|
|
| `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
|
|
```
|