7.3 KiB
AGENTS.md — Africa Alert PWA
Compact orientation for AI coding agents. Update this file when commands, structure, or load-bearing conventions change.
What this repo is
Offline-first PWA school-management system. React 18 + Vite + TypeScript client, Node 20 + Express + SQLite server, bidirectional sync to Supabase, Paynow (Zimbabwe) for fee payments. Deployed as a single Docker container.
Quickstart (local dev, two terminals)
# Terminal 1 — API on :3001
cd server
npm install
npm run db:init # one-off; creates server/data/school.db
npm run dev
# Terminal 2 — client on :3000 (proxies /api → :3001)
cd client
npm install
npm run dev
Login with admin@school.com / admin123 (other demo accounts in server/src/database/init.js).
For production: docker-compose up --build (ports 3000 + 3001). The Dockerfile in the root is a separate, currently unused multi-stage Nginx build — docker-compose.yml builds and serves from node:20-alpine directly. Reconcile or pick one.
Repository layout (don't deviate)
client/src/
App.tsx router, ProtectedRoute, role-based getRoutes()
main.tsx
components/ shared UI (Nav, PaynowPayment)
pages/<role>/ role-specific pages
pages/dashboard/ role landing pages
store/ Zustand slices — use client/src/store/api.ts as the axios instance
server/src/
index.js Express bootstrap, registers controllers under /api/<x>
controllers/ one file per resource, inline auth middleware
services/ SyncEngine.js, AuditService.js
database/init.js schema source of truth (idempotent CREATE TABLE IF NOT EXISTS)
data/ SQLite file (gitignored, WAL mode → .db-shm + .db-wal alongside)
uploads/ multer uploads (gitignored)
.harness/ team memory (AGENTS.md, docs/conventions.md, reins/, changelogs/)
There is no root package.json. Run npm install and scripts inside client/ and server/.
Hard rules
- All code changes go in a worktree at
.worktrees/<name>/, branchfeature/<kebab>orfix/<kebab>, branched fromdev. Never edit the main checkout. - Branch model is Git Flow. Work and merge on
dev. Promote tomainonly whendevis green and deployable. No direct commits tomain. - Single axios instance lives at
client/src/store/api.ts. Don't create a second one in a store or component. - All tables must have
uid TEXT UNIQUE,last_synced_at,sync_statusCHECK in ('synced','pending','conflict') DEFAULT 'pending',is_deleted INTEGER DEFAULT 0. Add toinit.jsand append the table name totablesToSyncinSyncEngine.js(in FK dependency order — parents first). - Route gating is done in
App.tsxvia<ProtectedRoute allowedRoles={…}>— do not gate inside page components. - Demo accounts are seeded in
server/src/database/init.js. Don't add new ones in a migration; updateinit.js. .envis gitignored. Never commit secrets, never paste.envcontents into chat. The repo's actual remote is Gitea (git.techarvest.co.zw), not GitHub.
Commands cheat sheet
| What | Command | Where |
|---|---|---|
| Install deps | npm install |
client/ and server/ separately |
| Dev server (client) | npm run dev |
client/ — Vite, port 3000, proxies /api → :3001 |
| Dev server (server) | npm run dev |
server/ — nodemon, port 3001 |
| Init / migrate DB | npm run db:init |
server/ — runs src/database/init.js |
| Production build | npm run build |
client/ — outputs client/dist/, generates the PWA service worker |
| Preview build | npm run preview |
client/ |
| Full stack (prod-like) | docker-compose up --build |
repo root |
| Stop stack | docker-compose down |
repo root |
| Lint / typecheck | not configured | n/a — see Open follow-ups |
| E2E tests (Playwright) | npm run test:e2e |
client/ — Playwright config + e2e/*.spec.ts; needs both client (3000) and server (3001) running; first run also: npx playwright install chromium |
npm run build (not npm run dev) is required to exercise the PWA service worker — vite-plugin-pwa only ships the SW in production builds.
Canonical reference for new features
The exams module is the vertical slice to mimic: server/src/controllers/exams.controller.js, client/src/store/exams.ts, client/src/pages/exams/, route + nav entry in App.tsx. See IMPLEMENTATION_SUMMARY.md for the design notes.
Things that will trip you up
- Hardcoded JWT secret
'africa-alert-secret-key-2024'is the fallback inserver/src/index.jsand duplicated across controllers. SetJWT_SECRETenv var in any non-dev environment. - CORS is wide open (
app.use(cors())inserver/src/index.js). Don't add more routes without a CORS review. outputs/andlogo/at the repo root are unused build artifacts — don't import from them. Real brand assets live inclient/public/.- Sync engine uses raw
http/https, notfetchoraxios. 10s timeout, 100 rows/table push, 50/table delete, server-wins conflict, 30s default interval.SUPABASE_KEYempty → sync no-ops, app keeps working. - Paynow integration in
server/src/controllers/payments.controller.js. Webhook is idempotent. Payment +student_fees.statusupdate wrapped indb.transaction(). - Auth is JWT only (7-day expiry). No session cookies, no refresh tokens. Logout is client-side only (clears Zustand store).
- Roles in active UI:
school_admin,systems_admin,principal,hr,bursar,teacher,student,parent,librarian,clubs_head. Schema also definesaccountantandnursebut those have no UI. index.jsexposesmodule.exports = appAND starts the server withapp.listen. Importing it in tests will bind a port — use a separate bootstrap or guard the listen call.
Where to read more
.harness/AGENTS.md— project-level memory (demo accounts, branch model, "exams" as canonical reference)..harness/docs/conventions.md— SQL contract, controller/store templates, auth+RBAC, sync contract, Paynow flow, env vars..harness/docs/testing.md— test stack and manual recipes (when set up)..harness/reins/— agent role definitions (backend-expert, frontend-expert, sync-expert, tester, etc.).IMPLEMENTATION_SUMMARY.md— modules and their tables; rationale for sync engine.DATABASE_SCHEMA.md— full schema reference.SCREENS.md— UI map and role/permission matrix (canonical for screen-level access).GEMINI.md— high-level overview (older, points here for details).
Open follow-ups (do not silently take these on — ask first)
- No test framework is installed or configured anywhere. The
testerrein owns adding Vitest/Jest/Playwright. - No CI — no GitHub Actions, no Gitea Actions workflow. Add when the user asks.
- Schema is
CREATE TABLE IF NOT EXISTSonly ininit.js— no migration framework. Schema changes that require ALTER are blocked until a migration tool lands. - No
lintortypecheckscript in eitherpackage.json.tsconfig.jsonhas"strict": false— the client typechecks leniently. Dockerfileanddocker-compose.ymlare divergent. Pick one deploy story.- JWT secret, CORS, request-size limit (
express.json({ limit: '50mb' })) are all loose. Don't relax further; tighten when touching the area.