# tachyon-api Minimal Express + SQLite API to back the TACHYON site's dynamic sections (News, Media, and the Overview/Statistics panel — current users, recent activity, general stats). ## Stack - **Express** — HTTP layer - **better-sqlite3** — synchronous SQLite driver, no separate DB process to run - Plain SQL (no ORM) — the schema is small enough that raw `.prepare()` calls stay readable; swap in Drizzle/Prisma later if it grows ## Setup ```bash npm install npm run db:init # creates db/tachyon.db and applies schema.sql npm run db:seed # (optional) fills it with placeholder rows matching the mockup npm run dev # starts on http://localhost:3000, restarts on file changes ``` ## Endpoints | Method | Path | Description | |--------|---------------------------|-------------------------------------------------| | GET | `/api/health` | Liveness check | | GET | `/api/news?limit=20` | List news posts, newest first | | POST | `/api/news` | Create a post — `{ title, content }` | | DELETE | `/api/news/:id` | Delete a post | | GET | `/api/media?limit=24` | List media items, newest first | | POST | `/api/media` | Add media — `{ title, media_type, url }` | | DELETE | `/api/media/:id` | Delete a media item | | GET | `/api/stats/overview` | Bundled currentUsers + recentActivity + general | | GET | `/api/stats/users` | Just the current-users list | | POST | `/api/stats/session/start`| `{ username }` — opens a session (marks online) | | POST | `/api/stats/session/end` | `{ sessionId }` — closes a session | `/api/stats/overview` is the one the frontend's Overview panel would call — it returns everything needed for the three stat columns in one round trip: ```json { "currentUsers": [{ "id": 1, "username": "...", "started_at": "..." }], "recentActivity": [{ "id": 1, "description": "...", "created_at": "..." }], "general": { "users": { "total": 3, "mostRecent": { "username": "...", "id": 3 } }, "reports": { "named": "00", "associated": "00", "logged": "00" }, "build": { "pipeline": "Completed", "buildNumber": "00000000", "version": "0.0.0" } } } ``` ## Schema notes - **"Current users"** isn't a boolean flag on `users` — it's derived from `sessions` rows with `ended_at IS NULL`. That way "who's online right now" falls naturally out of a query instead of needing a background job to flip a flag, and you get session history for free. - **`site_stats`** is a plain key/value table for the "General Statistics" cards (build number, version, report counts) since those are free-form and don't need their own table each. - Timestamps are ISO-8601 `TEXT`, so `ORDER BY created_at DESC` sorts correctly without needing SQLite's more limited native datetime type.