Files
WhatIDo/PRD.md
T

12 KiB

PRD.md — Product Requirements Document: WhatIDo

Version: 1.0
Status: Active
Last Updated: 2026-09-09


1. Product Summary

WhatIDo is a self-hosted accounting system for an educational center (youth club, coding school, art studio). It tracks attendance, student project works, group photo chronicles, and publishes public showcase pages via share links. Designed for zero-public-IP deployment using Tailscale Funnel.

Target Users:

  • Administrators/Teachers — manage groups, students, entries, files, settings
  • Parents/Students — view public showcase pages (read-only, no auth)

Core Value: Simple, secure, zero-infrastructure publishing. Runs on any Linux box with Docker.


2. Functional Requirements

2.1 Groups Management

ID Requirement Priority
GRP-1 CRUD groups: name, schedule (day of week, start/end time), branch assignment Must
GRP-2 Group cover photo (auto-set from latest group photo or manual) Must
GRP-3 Photo chronicle per group: upload, caption, date taken, pagination Must
GRP-4 Active groups endpoint (filters by current day/time in Europe/Moscow) Must
GRP-5 Branches (locations): CRUD with address, phone; groups link to branch Should

2.2 Students Management

ID Requirement Priority
STU-1 CRUD students: name, group assignment Must
STU-2 Batch assign students to group Should
STU-3 Unique name constraint per student Must

2.3 Journal Entries (Attendance + Project Works)

ID Requirement Priority
ENT-1 Create entry: student name (free text), group, description, photo, multiple files Must
ENT-2 List entries with filters: group, date range, student name, search (name/description), deleted flag Must
ENT-3 Update entry: description, photo, files Must
ENT-4 Soft delete / restore (deleted_at timestamp) Must
ENT-5 Trash view: list deleted entries, restore, permanent delete Must
ENT-6 Pagination (limit/offset) + total count Must
ENT-7 Anti-spam: min interval between entries per student (configurable, default 30 min) Must
ENT-8 Files attached to entry: upload (max 10 files, 10 MB each, 30 MB total), download by token Must

2.4 Files Management (Centralized)

ID Requirement Priority
FIL-1 List all files with filters: search, student, group, date range Must
FIL-2 Detached files tab: files with entry_id = NULL Must
FIL-3 Detach file from entry (sets detached_at) Must
FIL-4 Delete file (removes from disk + DB) Must
FIL-5 Public file access by token (image inline, others download) Must
FIL-6 File size display in list Should
ID Requirement Priority
SHR-1 Create share link: name, optional group, student, date range, anonymize names, expiry (default 7 days), optional password Must
SHR-2 List/Edit/Delete share links (admin) Must
SHR-3 Public page (/s/:token): shows filtered entries + group photos Must
SHR-4 Password protection on share link (bcrypt) Must
SHR-5 Expiry enforcement (410 Gone after expires_at) Must
SHR-6 Anonymize student names on public page (Student 1, Student 2...) Should
SHR-7 Group photos on public page (latest 12) Should
SHR-8 File download from share page (validates link + password + filters) Must

2.6 Dashboard & Statistics

ID Requirement Priority
DSH-1 Stats cards: total entries, trash count, groups, unique students Must
DSH-2 Active groups right now (schedule match) Must
DSH-3 Activity chart: entries per day (last 14 days) Should
DSH-4 Top students by entry count Should
DSH-5 Recent entries list (last 10) Should

2.7 Settings

ID Requirement Priority
SET-1 Footer left/right text (displayed on public pages) Must
SET-2 Anti-spam interval (minutes) Must
SET-3 Admin-only access Must

2.8 Backup & Restore

ID Requirement Priority
BAK-1 Download full backup: tar.gz with data.json (all tables) + uploads/ Must
BAK-2 Restore from backup file: validates format, replaces all data, resets sequences Must
BAK-3 Host-level scripts: backup.sh, restore.sh Should
BAK-4 Audit log entry for backup download/restore Must

2.9 Audit Log

ID Requirement Priority
AUD-1 Log all mutating actions: action name, target JSON, IP, timestamp Must
AUD-2 Admin view: paginated list (default 100, max 1000) Must

2.10 Security & Infrastructure

ID Requirement Priority
SEC-1 Admin auth via X-Admin-Token header (env ADMIN_PASSWORD, no default) Must
SEC-2 Rate limiting: entries 10/15min, files/share 300/15min Must
SEC-3 Upload validation: block dangerous extensions, MIME check, size limits Must
SEC-4 Path traversal protection on file delete/serve Must
SEC-5 Helmet headers (X-Frame-Options, nosniff, HSTS, Referrer-Policy) Must
SEC-6 No CORS (cross-origin blocked) Must
SEC-7 DB port not exposed publicly Must
SEC-8 TLS termination by Tailscale (Let's Encrypt), app uses self-signed cert internally Must
SEC-9 Tailscale Funnel publication (no public IP, no port forward) Must

3. Non-Functional Requirements

Category Requirement
Performance API responses < 500ms for typical queries; pagination for large lists
Reliability DB healthcheck; app restarts on crash; uploads persisted on host
Scalability Single-instance design; PostgreSQL connection pooling via pg.Pool
Maintainability Single server.js file; vanilla frontend; no build step
Portability Docker Compose; runs on any Linux/ARM64/AMD64 with Docker
Backup/Recovery Full restore < 5 min for typical dataset (< 1 GB)
Security No secrets in image; env vars only; regular dependency updates

4. Data Model

groups
  id PK, name UK, created_at, day_of_week (0-6), time_start, time_end, branch_id FK, cover_path

students
  id PK, name UK, group_id FK, created_at

entries
  id PK, student_name, group_id FK, description, photo_path, deleted_at, created_at

project_files
  id PK, entry_id FK (nullable), token UK, path, name, created_at, detached_at

group_photos
  id PK, group_id FK, photo_path, caption, taken_at, created_at

share_links
  id PK, token UK, name, group_id FK, student_name, date_from, date_to,
  anonymize_names, expires_at, access_password_hash, created_at

settings
  key PK, value

audit_log
  id PK, action, target JSONB, ip, created_at

branches
  id PK, name UK, address, phone, created_at

5. API Surface (Key Endpoints)

Method Path Auth Description
GET /api/entries Admin List entries (filters, pagination)
POST /api/entries Admin Create entry (photo + files)
PUT /api/entries/:id Admin Update entry
DELETE /api/entries/:id Admin Soft delete
POST /api/entries/:id/restore Admin Restore from trash
GET /api/files Admin All files (filters)
GET /api/files/detached Admin Detached files
POST /api/files/:id/detach Admin Detach file
GET /api/files/:token Public Download file by token
GET /api/groups Public List groups
POST/PUT/DELETE /api/groups Admin CRUD groups
GET/POST /api/groups/:id/photos Admin Group photo chronicle
GET /api/share/:token Public Share page data
GET /api/links Admin List share links
POST/PUT/DELETE /api/links Admin CRUD share links
GET /api/backup Admin Download backup
POST /api/restore Admin Upload & restore backup
GET /api/dashboard Admin Dashboard data
GET /api/stats Admin Stats cards
GET /api/audit Admin Audit log

6. User Flows

6.1 Teacher Creates Attendance Entry

  1. Opens /journal.html
  2. Fills form: student name (typeahead from existing), group, description
  3. Adds photo (optional) + project files (optional)
  4. Submits → entry appears in list, files accessible by token

6.2 Admin Publishes Showcase for Parents

  1. Opens /links.html
  2. Creates share link: selects group, date range, sets password
  3. Copies link https://whatido.tailnet.ts.net/s/abc123
  4. Sends to parents → they open, enter password, view entries + photos

6.3 Admin Restores from Backup

  1. Opens /settings.html → Backups tab
  2. Uploads .tar.gz backup file
  3. Confirms → all data replaced, sequences reset, orphans cleaned

7. Deployment Architecture

Internet / Tailnet
       │
       ▼
┌──────────────────┐
│  Tailscale       │  (network_mode: host, port 443)
│  Funnel/Serve    │  TLS: Let's Encrypt (*.ts.net)
└────────┬─────────┘
         │ HTTPS (trusts app self-signed cert)
         ▼
┌──────────────────┐
│  App (Node.js)   │  127.0.0.1:3443 (HTTPS), :3003 (HTTP→HTTPS redirect)
│  Express + pg    │
└────────┬─────────┘
         │
         ▼
┌──────────────────┐
│  PostgreSQL 16   │  Internal docker network only
│  (named volume)  │
└──────────────────┘

Host filesystem:
  ./uploads  ──────► /app/uploads (bind mount)
  ./certs    ──────► /etc/tailscale/app-certs (ro)

8. Configuration

Variable Required Description
ADMIN_PASSWORD Yes Admin token value (no default, server refuses start)
DB_PASSWORD Yes Postgres app user password

9. Release Criteria

  • All Must-have requirements implemented and tested
  • Docker Compose starts cleanly on fresh host (docker compose up -d --build)
  • Tailscale Funnel publishes successfully (manual verification)
  • Backup/restore roundtrip works (data + files intact)
  • No critical security findings (rate limits, upload validation, auth)
  • README.md updated with accurate setup instructions

10. Future Considerations (Not in Scope v1)

  • Multi-user auth (teachers with own logins)
  • Email/push notifications
  • Mobile app / PWA
  • Rich text editor for descriptions
  • Bulk import students (CSV)
  • Webhooks for external integrations
  • Automated scheduled backups to S3/remote
  • Role-based access (read-only vs admin)

11. Acceptance Test Scenarios

Scenario Steps Expected
Fresh deploy cp .env.example .env → edit → docker compose up -d --build App healthy, DB migrated, HTTPS on 3443
Create entry POST /api/entries with photo + 2 files Entry created, files downloadable by token
Share link Create link with password → open /s/token → enter password Entries filtered, photos shown, files download
Backup/restore Download backup → delete entry → restore → verify entry back Full state restored, sequences correct
Tailscale publish tailscale up → enable Funnel → tailscale funnel Public URL accessible via HTTPS
Upload rejection POST .html file → 400 error Dangerous extensions blocked
Rate limit 11 rapid POST /api/entries → 429 on 11th Entry limiter enforced