7.4 KiB
7.4 KiB
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 inpublic/ - Deployment: Docker Compose (app + db + tailscale), bind-mounted uploads, named volume for Postgres data
- Auth: Admin-only via
X-Admin-Tokenheader (value =ADMIN_PASSWORDenv 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.sqlfor existing DBs — update both when changing schema - Connection: Single
Poolfrompg,DATABASE_URLfrom env - Queries: Parameterized only (
$1,$2...), never string interpolation - Transactions: Use
client.query('BEGIN')/COMMIT/ROLLBACKfor 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 outsideuploads/
4. API Patterns
- Admin routes:
requireAdminmiddleware (checksX-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/offsetquery 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
fetchwithX-Admin-TokenfromlocalStorage - 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 mountedapp: builds from Dockerfile, exposes 3003/3443, mounts uploadstailscale: host network, NET_ADMIN, runsstart-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
- Add route in
server.js(group with related routes) - Use
requireAdminfor admin,apiLimiter/fileLimiterfor public - Validate input with helper functions
- Use parameterized queries, transactions if multi-table
- Call
logAudit(req, 'action.name', { ... })for mutations - Return JSON, handle errors with appropriate status codes
Add a database column/table
- Update
db/init.sql(CREATE TABLE / ALTER TABLE) - Update
db/migration.sql(idempotent ALTERs) - Update
server.jsqueries that SELECT/INSERT the table - Test:
docker compose down && docker compose up -d --build
Add a frontend page
- Create
public/newpage.html(copy structure from existing) - Link in
public/admin.htmlnavigation if admin page - Use
admin.jsutilities:api(),requireAuth(),formatDate(), etc. - No build step — just refresh browser
Modify file upload rules
- Edit
BLOCKED_EXT,ALLOWED_IMAGE_EXT,ADMIN_ALLOWED_EXTconstants - Update Multer
fileFilterfunctions - Keep
MAX_TOTAL_UPLOAD_BYTESand per-file limit in sync
Testing & Verification
No automated test suite exists. Verify manually:
# 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
corsmiddleware)
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 |
scripts/deploy.sh |
Deploy script (pull master, build image with commit version, restart app) |
start-tailscale.sh |
Tailscale container entrypoint |
.env.example |
Env var template |
Do Not
- ❌ Add dependencies without updating
package.jsonand rebuilding - ❌ Write files outside
uploads/orcerts/ - ❌ Commit
.env,certs/,uploads/,backups/,node_modules/ - ❌ Expose DB port (5432) outside docker network
- ❌ Use
eval,Functionconstructor, or dynamic code execution - ❌ Add comments to code (this file excepted)
Quick Commands
# Full rebuild
docker compose down && docker compose up -d --build
# Обновление на сервере (pull master + сборка образа с версией коммита + перезапуск app)
./scripts/deploy.sh
# 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