admin console: read-only /admin screens served by distd (P6.1)
Subplan 06, first slice. src/server/admin.sx server-renders the
operator console the same way the index and install pages are built —
sx string rendering, no client framework, no build step:
/admin apps overview (platform coverage, channel and
release counts, latest release)
/admin/apps/<slug> metadata + iOS install-mode chip, channels with
policy/rollout/retention, releases newest-first
/admin/releases/<id> artifacts with validation chips and download
links, channels serving the release, audit
timeline (by target, by metadata, by artifact
id prefix)
/admin/tokens lifecycle status via token_status; never a
secret or hash
/admin/audit the full log, newest first
Reads are public like every distd GET in v0; mutations stay on the
token-gated POST endpoints (UI actions are a later slice). Unknown
slug/release/route answer 404 JSON errors with stable codes
(admin.unknown_app / admin.unknown_release / http.not_found). The
module is self-contained (adm_-prefixed helpers) so distd can import it
without a cycle; timestamps render through a local civil-from-days
formatter. The public index footer links the console.
tests/server_admin.sx drives the built binary over curl and pins every
screen, the no-secret-material guarantee, and the 404 codes.
make test 24/24 green.
This commit is contained in:
@@ -70,6 +70,7 @@ jout :: #import "../json_out.sx";
|
||||
// call sites that mirror dist.sx's.
|
||||
pl :: #import "../publish/publish.sx";
|
||||
ops :: #import "../release/ops.sx";
|
||||
adm :: #import "admin.sx";
|
||||
// Aliased so the json reader is called as `jsrv.parse` — a bare `parse`
|
||||
// would bind to `std.cli`'s once both modules share the `dist` program.
|
||||
jsrv :: #import "modules/std/json.sx";
|
||||
@@ -245,7 +246,7 @@ short_sha :: (sha: string) -> string {
|
||||
|
||||
INDEX_HEAD :: "<!doctype html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"><title>dist</title><style>body{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;background:#101216;color:#d6dae1;margin:2rem auto;max-width:60rem;padding:0 1rem}h1{font-size:1.1rem;letter-spacing:.04em}h2{font-size:1rem;margin-top:2rem;border-bottom:1px solid #2a2f3a;padding-bottom:.3rem}table{border-collapse:collapse;width:100%;font-size:.85rem}td,th{text-align:left;padding:.3rem .8rem .3rem 0;border-bottom:1px solid #1d212a;vertical-align:top}th{color:#8b93a3;font-weight:normal}a{color:#7ab7ff;text-decoration:none}a:hover{text-decoration:underline}small,.dim{color:#737b8c}</style></head><body><h1>dist — release console</h1>";
|
||||
|
||||
INDEX_FOOT :: "<p class=\"dim\"><small>API: <a href=\"/api/apps\">/api/apps</a> · <a href=\"/healthz\">/healthz</a></small></p></body></html>";
|
||||
INDEX_FOOT :: "<p class=\"dim\"><small><a href=\"/admin\">admin console</a> · API: <a href=\"/api/apps\">/api/apps</a> · <a href=\"/healthz\">/healthz</a></small></p></body></html>";
|
||||
|
||||
// Server-rendered index: every app with its channels and its releases'
|
||||
// artifacts as direct /download links. Dense and read-only, per the
|
||||
@@ -978,6 +979,10 @@ route :: (client: i32, store_dir: string, req: *http.Request) -> i64 {
|
||||
handle_install_route(client, store_dir, req, tail_after(path, "/install/"));
|
||||
return 200;
|
||||
}
|
||||
if path == "/admin" or starts_with(path, "/admin/") {
|
||||
adm.handle_admin(client, store_dir, path);
|
||||
return 200;
|
||||
}
|
||||
respond_error(client, 404, "http.not_found",
|
||||
concat("no route for ", path));
|
||||
return 404;
|
||||
|
||||
Reference in New Issue
Block a user