Files
compass_teams_nhs/internal/store/migrations/001_init.sql
Sam Rolfe 7b98e487c0 Phase 1: Compass/NHS skeleton (Go, SQLite, session login, Docker, Caddy)
Phase 1 of AGENTS.md §11: the app runs on .13 in its own container on port
8792, behind Caddy at school.lab.audasmedia.com.au, with its own session login
and GET /health.

- Go service (Go 1.25, modernc SQLite, no cgo) with its own image.
- SQLite schema per AGENTS.md §7: children, items, scans, notifications,
  session (internal/store/migrations/001_init.sql).
- Separate session login (signed cookie, CSRF double-submit), not SSO.
- HTMX server-rendered shell using the Family Home Lab tokens.css: canvas
  #f6f5f4, action blue #0075de, Inter, 8px grid, hairline borders, pill CTAs,
  dark mode, phone-friendly.
- Fail loudly (FR-12): a missing or failed scan shows as an explicit warning or
  error banner, never as an empty day.
- Dockerfile + docker-compose.yml (own container, own port 8792; uses
  network_mode bridge because .13 has no free default Docker address pool).
- Caddy snippet for .35 in deploy/caddy/, deployment notes, and
  deploy/verify-phase1.sh.

No school scan in this phase; the Compass login and fetch are Phase 2.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-15 13:21:20 +11:00

68 lines
2.5 KiB
SQL

-- Migration 001 — Phase 1 foundation (AGENTS.md §7).
--
-- Every item is stored with its child, source page, type, subject, due date and
-- link (FR-5). Scans are append-only and record success or an explicit error,
-- so a failed scan can never be mistaken for "nothing due" (FR-12).
CREATE TABLE IF NOT EXISTS children (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL UNIQUE,
year TEXT,
compass_id TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
-- One row per message, task or calendar event read from Compass or Teams.
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY,
child_id INTEGER NOT NULL REFERENCES children(id),
source TEXT NOT NULL DEFAULT 'compass' CHECK (source IN ('compass', 'teams')),
type TEXT NOT NULL CHECK (type IN ('message', 'task', 'event')),
subject TEXT,
teacher TEXT,
title TEXT NOT NULL,
body TEXT,
due_at TEXT,
url TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_items_child ON items(child_id);
CREATE INDEX IF NOT EXISTS idx_items_due ON items(child_id, due_at);
CREATE INDEX IF NOT EXISTS idx_items_type ON items(child_id, type);
-- Append-only scan log (FR-13). ok = 0 with a message is a loud failure.
CREATE TABLE IF NOT EXISTS scans (
id INTEGER PRIMARY KEY,
child_id INTEGER REFERENCES children(id),
started_at TEXT NOT NULL DEFAULT (datetime('now')),
finished_at TEXT,
pages_fetched INTEGER NOT NULL DEFAULT 0,
items_found INTEGER NOT NULL DEFAULT 0,
ok INTEGER NOT NULL DEFAULT 0,
error TEXT
);
CREATE INDEX IF NOT EXISTS idx_scans_started ON scans(started_at);
-- One row per delivered brief (FR-9, FR-14).
CREATE TABLE IF NOT EXISTS notifications (
id INTEGER PRIMARY KEY,
recipient TEXT NOT NULL,
summary TEXT NOT NULL,
sent_at TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_notifications_sent ON notifications(sent_at);
-- Harness session state per child. SECRET: session cookies and tokens live
-- here, in the database volume only. Never exported, never logged (AGENTS.md
-- §10 rule 3).
CREATE TABLE IF NOT EXISTS session (
child_id INTEGER PRIMARY KEY REFERENCES children(id),
state TEXT NOT NULL,
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);