CI / test (push) Successful in 30s
The hub becomes a Bun monorepo: apps/api (Elysia, Better Auth for accounts, roles, bans, proxy device sign-in and JWT keys; tagline linking, presence and the console as before), packages/db (Drizzle; Postgres in production, PGlite in development and tests), and the Next.js website from apps/web. Self-hostable with docker compose; the website deploys to Vercel-style hosts, the API to any that runs Bun or containers. The first version, in Go, is removed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
3.3 KiB
3.3 KiB
The hub's API
apps/api (Elysia on Bun). The route definitions in apps/api/src/app.ts are the reference: their schemas validate
requests and type the website's client (Eden). This is the overview.
/api/auth/*: Better Auth (accounts, sessions, device sign-in, JWT keys)./api/v1/*: the hub's own routes.
The website reaches the API through its own origin (Next.js rewrites /api to it), so sessions are first-party
cookies. T5DE's proxy calls it directly with Authorization: Bearer <session token>.
Errors are {"error": "<code>", "message": "<text for people>"} with a 4xx/5xx status; message can be shown as
is. Codes: invalid, signed_out, forbidden, not_found, no_such_imvu_account, already_linked,
code_missing, expired, link_cap, rate_limited, imvu_unavailable.
Accounts (Better Auth)
POST /api/auth/sign-up/email |
{email, password, name, username}. Usernames 3-32 of A-Za-z0-9_; passwords 10-256 characters. The first account is an admin. |
POST /api/auth/sign-in/username |
{username, password} |
POST /api/auth/sign-out |
|
GET /api/v1/me |
{person} |
Proxies
POST /api/auth/device/code |
{client_id: "t5de-proxy"} → {device_code, user_code, verification_uri, verification_uri_complete, interval, expires_in} (RFC 8628). verification_uri is the website's /device. |
POST /api/auth/device/token |
{grant_type: "urn:ietf:params:oauth:grant-type:device_code", device_code, client_id} → {access_token} (a session token, used as a bearer token) or {error: authorization_pending | slow_down | expired_token | access_denied} |
POST /api/v1/device/approve |
(website, signed in) {code}: signs the waiting proxy in as this person |
GET /api/v1/realtime |
WebSocket, bearer token. Protocol in apps/api/src/realtime.ts. |
GET /.well-known/jwks.json |
the Ed25519 keys JWTs are signed with |
Website routes (signed in unless noted)
GET /api/v1/home |
(anyone) {onlineCount, links?} |
GET /api/v1/online |
(anyone) {online}: accounts showing they're online (moderators see all, with hidden) |
GET /api/v1/online/events |
(anyone) server-sent events: online with {online} now and on each change |
GET /api/v1/account |
{person, links, challenges, computers, canLink} |
POST /api/v1/account/links |
{username} → 201 {challenge} (the tagline code) |
POST /api/v1/account/links/:imvu/check |
{link, moved} |
DELETE /api/v1/account/challenges/:imvu |
cancel linking |
PATCH /api/v1/account/links/:imvu |
{showBadge?, showPresence?} → {link} |
POST /api/v1/account/links/:imvu/recheck |
{link} (age checked with IMVU again) |
DELETE /api/v1/account/links/:imvu |
unlink |
POST /api/v1/account/signout-computers |
signs T5DE out on all their computers |
Console (moderators; roles: admins)
GET /api/v1/console |
{counts, connections, online, audit} |
GET /api/v1/console/people?q= |
{people} |
GET /api/v1/console/people/:id |
{person, links, computers} |
POST /api/v1/console/people/:id/link-cap |
{linkCap} (0-1000) |
POST /api/v1/console/people/:id/ban |
{reason}; only admins ban moderators |
POST /api/v1/console/people/:id/unban |
|
POST /api/v1/console/people/:id/role |
(admins) {role} |