Files
api/README.md
T
2026-07-08 13:13:44 -05:00

3.0 KiB

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

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:

{
  "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.