Files
technitium-console/hub/routes.ts
T
DenozordecandClaude Opus 5.5 0ef2d53a65 Hub stage 0: own users with roles, login limit, audit log, permission table
Users in HUB_DATA/users.json with scrypt hashes and roles (viewer, operator,
apps-admin, admin), managed by hub/users-cli.ts; HUB_USER/HUB_PASSWORD remain a
bootstrap admin while the file does not exist. Five failed logins in 15 minutes lock
the address or the name for 15 minutes. Logins, logouts and every refused request go
to a JSON Lines audit log that admins read through hub/audit/list. Endpoints are
listed in routes.ts with their kind and capability; writes stay refused for now.
Optional <ID>_ADMIN_TOKEN is read for the coming write stages. The management plan
is in docs/hub-management-plan.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-31 15:25:38 +07:00

39 lines
1.3 KiB
TypeScript

/*
What each `/api/<path>` does and who may call it. `kind` says what it touches:
`read` (no change), `write-one` (changes one server) and `write-all` (changes every server).
A path that is not listed is refused. Today every entry is a read.
Not in the table: `status`, `user/login`, `user/session/get` and `user/logout`, which the
server answers itself before the table is consulted.
*/
import type { Capability } from './users.ts'
export type RouteKind = 'read' | 'write-one' | 'write-all'
export interface Route {
kind: RouteKind
capability: Capability
}
const read = (capability: Capability = 'read'): Route => ({ kind: 'read', capability })
export const ROUTES: Record<string, Route> = {
'dashboard/stats/get': read(),
'dashboard/stats/getTop': read(),
'dashboard/metrics/json': read(),
'apps/list': read(),
'logs/query': read(),
'logs/list': read(),
'settings/get': read(),
'user/checkForUpdate': read(),
'admin/cluster/state': read(),
// The hub's own namespace; never forwarded to a server.
'hub/audit/list': read('audit.read'),
}
export function routeFor(apiPath: string): Route | undefined {
return Object.hasOwn(ROUTES, apiPath) ? ROUTES[apiPath] : undefined
}
export const isHubPath = (apiPath: string): boolean => apiPath.startsWith('hub/')