Every saved config, and every earlier baseline, is listed per app. You can view it, diff it against a server's current config, and roll it back through preview and apply. Earlier baselines can be restored. A drift check (HUB_DRIFT_MINUTES, default 15) compares each server with its effective config. Drift shows in the header and the matrix, with Accept as baseline and Revert to baseline. All baselines export to one file. Importing that file shows a dry run before it writes. Rolling back preselects, and diffs against, the server the version came from. The line-diff op "del" is renamed to "remove" for the language lint. Co-Authored-By: Claude Opus 5.5 <[email protected]>
73 lines
3.0 KiB
TypeScript
73 lines
3.0 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. The app writes need `apps.write`.
|
|
|
|
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 })
|
|
const writeOne = (capability: Capability): Route => ({ kind: 'write-one', capability })
|
|
const writeAll = (capability: Capability): Route => ({ kind: 'write-all', 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'),
|
|
'hub/apps/matrix': read(),
|
|
'apps/listStoreApps': read(),
|
|
// Writes. They go to a server's ADMIN token only; see apps-write.ts.
|
|
'apps/downloadAndInstall': writeOne('apps.write'),
|
|
'apps/downloadAndUpdate': writeOne('apps.write'),
|
|
'apps/uninstall': writeOne('apps.write'),
|
|
'apps/install': writeOne('apps.write'),
|
|
'apps/update': writeOne('apps.write'),
|
|
// Config sync (apps-config.ts). Compare shows live configs to anyone who can read; the rest needs apps.write.
|
|
'apps/config/get': read(),
|
|
'apps/config/set': writeOne('apps.write'),
|
|
'hub/apps/config/compare': read(),
|
|
'hub/apps/config/preview': read('apps.write'),
|
|
'hub/apps/overrides/current': read('apps.write'),
|
|
'hub/apps/baseline/import': writeAll('apps.write'),
|
|
'hub/apps/baseline/save': writeAll('apps.write'),
|
|
'hub/apps/overrides/save': writeAll('apps.write'),
|
|
'hub/apps/config/apply': writeAll('apps.write'),
|
|
// History, drift, and moving baselines between hubs (apps-config.ts, drift.ts).
|
|
'hub/apps/history/list': read(),
|
|
'hub/apps/history/get': read(),
|
|
'hub/apps/history/diff': read(),
|
|
'hub/apps/history/rollback': writeAll('apps.write'),
|
|
'hub/apps/baseline/restore': writeAll('apps.write'),
|
|
'hub/apps/drift/accept': writeAll('apps.write'),
|
|
'hub/drift/get': read(),
|
|
'hub/drift/check': writeAll('apps.write'),
|
|
// Configs can hold secrets, so the file of all baselines needs apps.write even to read.
|
|
'hub/apps/export': read('apps.write'),
|
|
'hub/apps/import': writeAll('apps.write'),
|
|
'hub/apps/run': writeAll('apps.write'),
|
|
'hub/apps/upload': writeAll('apps.write'),
|
|
}
|
|
|
|
export function routeFor(apiPath: string): Route | undefined {
|
|
return Object.hasOwn(ROUTES, apiPath) ? ROUTES[apiPath] : undefined
|
|
}
|
|
|
|
export const isHubPath = (apiPath: string): boolean => apiPath.startsWith('hub/')
|