hub/apps/matrix reads apps/list and each app's config on every server (the read token first, the admin token once if a server refuses) and returns per app and server the version, available update and a config hash, plus whether versions and configs match and how many JSON leaf paths differ. Config texts never leave the hub. In hub mode the Apps section has the server selector: All servers shows the matrix with filters for differing versions, differing configs and updates; one server shows its list without write actions. The dev mock takes --variant a|b with differing apps. Co-Authored-By: Claude Opus 5.5 <[email protected]>
40 lines
1.3 KiB
TypeScript
40 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'),
|
|
'hub/apps/matrix': 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/')
|