gateway: nginx-hosts the full Swagger UI dist, core drops the fallback

Extends the theme-only alias into the full shape mara asked for on the
PR thread:

1. nix/packages/swagger-ui-dist.nix — plain vendored Swagger UI 5.17.14
   dist, sourced directly from the swagger-ui-dist npm package (same
   release the Rust utoipa-swagger-ui-vendored crate ships, verified
   via matching gitHead commit) rather than through Cargo.lock/cargo.
2. nix/packages/swagger-ui-theme.nix — overlays our 3 override files
   (index.html, hyperhive-theme.css, and now swagger-initializer.js)
   onto (1).
3. vhosts.nix's swaggerUiLocations now prefix-matches the whole
   /api/docs/ tree (not just 2 exact-match files) straight from (2),
   plus a `= /api/docs` redirect shim since hive-c0re's own redirect
   is going away too. /api/openapi.json (outside this prefix) keeps
   proxying to c0re unchanged — that's the one thing that stays
   dynamic.

New file swagger-initializer.js needed hand-verification: the plain
vendored copy hardcodes the swagger.io petstore demo URL.
utoipa-swagger-ui normally rewrites it per-request from a {{config}}
placeholder its own build.rs injects — since hive-c0re won't be
serving this file at all once its SwaggerUi mount is removed, that
rewrite has to be baked in statically here instead. Derived by
actually running build.rs's own two transforms (strip the default
layout: line, splice the Config JSON in place of the url/deepLinking
block) against the real vendored file, not typed from scratch —
verified byte-for-byte against what format_config() would produce for
hive-c0re's actual single-URL config, and checked with node --check.

Coordinated with damocles: he's taking the corresponding hive-c0re
side (drop the utoipa-swagger-ui dependency + SwaggerUi::new(...)
mount, keep only the plain /api/openapi.json route) once this lands.

Verified: nix fmt clean; nix build .#swagger-ui-theme succeeds, output
byte-matches the checked-in override files and node --check passes on
swagger-initializer.js; a full nixosSystem eval of nixosModules.default
resolves both new locations (/api/docs/ aliased to the right store
path, = /api/docs redirecting) with auth threaded through.
This commit is contained in:
iris 2026-08-02 20:43:19 +02:00 committed by mara
commit 1bc9c18504
8 changed files with 166 additions and 76 deletions

View file

@ -1,16 +1,13 @@
<!-- Served by the gateway's nginx (an exact-match `location = /api/docs/` <!-- Served by the gateway's nginx — one of 3 files (see also
alias straight to this file's nix store path — see hyperhive-theme.css, swagger-initializer.js) that
nix/host-modules/hive-gateway/vhosts.nix's swaggerThemeLocations), nix/packages/swagger-ui-theme.nix overlays onto the plain vendored
NOT by hive-c0re: a theme tweak here is a gateway config change, dist (swagger-ui-dist.nix), and the whole result is what
not a hive-c0re rebuild+restart. Byte-identical to the vendored vhosts.nix's `swaggerUiLocations` serves at /api/docs/, wholesale,
Swagger UI's dist/index.html except two added <link>s: `colors.css` with NO hive-c0re fallback for any file in the tree. Byte-identical
(same origin as the dashboard — so this resolves the operator's to the vendored Swagger UI's dist/index.html except two added
live stylix theme, not a frozen copy) and hyperhive-theme.css, <link>s: `colors.css` (same origin as the dashboard — so this
which consumes its `--baseNN` vars. Every other path here (script resolves the operator's live stylix theme, not a frozen copy) and
src, other stylesheets, favicons) stays relative and falls through hyperhive-theme.css, which consumes its `--baseNN` vars. -->
nginx's `/api/` prefix proxy to hive-c0re's own vendored,
unmodified Swagger UI assets — only this file and
hyperhive-theme.css are intercepted. -->
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>

View file

@ -0,0 +1,21 @@
window.onload = function() {
//<editor-fold desc="Changeable Configuration Block">
// the following lines will be replaced by docker/configurator, when it runs in a docker-container
window.ui = SwaggerUIBundle({
"dom_id": "#swagger-ui",
"url": "/api/openapi.json",
"deepLinking": true,
"layout": "StandaloneLayout",
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
});
//</editor-fold>
};

View file

@ -69,13 +69,13 @@
type = lib.types.package; type = lib.types.package;
defaultText = lib.literalExpression "hyperhive.packages.\${system}.swagger-ui-theme"; defaultText = lib.literalExpression "hyperhive.packages.\${system}.swagger-ui-theme";
description = '' description = ''
Swagger UI re-theme static override files (see Full Swagger UI static dist, hyperhive-themed (see
`nix/packages/swagger-ui-theme.nix`; output has `index.html` + `nix/packages/swagger-ui-theme.nix`, built on
`hyperhive-theme.css`). The hive-gateway module `alias`es these `nix/packages/swagger-ui-dist.nix`). hive-gateway serves this
two files straight from the store over `/api/docs/`, so a theme whole tree directly at `/api/docs/` hive-c0re hosts none of
tweak is a gateway config change hive-c0re's own vendored it, only the dynamic `/api/openapi.json` route. Override to
Swagger UI build is untouched. Override to ship a custom theme ship a custom theme (or the plain vendored dist) without a
(or the vendored default) without a hive-c0re rebuild. hive-c0re rebuild.
''; '';
}; };
hyperhiveFlake = lib.mkOption { hyperhiveFlake = lib.mkOption {

View file

@ -24,10 +24,10 @@ let
# container's). # container's).
dashboardDist = "${config.services.hyperhive.c0re.servedFrontend}/dashboard"; dashboardDist = "${config.services.hyperhive.c0re.servedFrontend}/dashboard";
# Swagger UI re-theme static override files — nginx `alias`es these # Full hyperhive-themed Swagger UI dist — nginx serves this whole
# straight from the store over `/api/docs/`, so a theme tweak ships # tree straight from the store at /api/docs/, no hive-c0re fallback
# as a gateway config change (see vhosts.nix's `swaggerThemeLocations` # (see vhosts.nix's `swaggerUiLocations` and
# and nix/packages/swagger-ui-theme.nix). # nix/packages/swagger-ui-{dist,theme}.nix).
swaggerUiTheme = config.services.hyperhive.c0re.swaggerUiTheme; swaggerUiTheme = config.services.hyperhive.c0re.swaggerUiTheme;
# Self-signed TLS is the implicit floor: when neither an operator cert # Self-signed TLS is the implicit floor: when neither an operator cert

View file

@ -306,28 +306,35 @@ let
}; };
}; };
# Swagger UI re-theme: exact-match locations `alias`ed straight to # Swagger UI: nginx hosts the FULL themed dist (`swaggerUiTheme` —
# `swaggerUiTheme`'s store path, nginx-served with no involvement # vendored Swagger UI + our overlay, see nix/packages/swagger-ui-
# from hive-c0re at all. `=` locations win over the `/api/` prefix # dist.nix + swagger-ui-theme.nix) straight from the store, with NO
# block above regardless of declaration order (nginx's own location- # fallback to hive-c0re at all — per the operator's shape: "core
# matching precedence — exact match beats longest-prefix), so this # should not need the swagger ui at all if it is hosted in gateway"
# doesn't need to be declared before it. Every OTHER Swagger UI asset # / "core only hosts the json". Only `/api/openapi.json`
# (bundle.js, openapi.json, favicons, the *vendored* index.html c0re # (the live-generated spec `index.html` fetches; not under this
# would otherwise serve) still falls through `/api/` to c0re's own # prefix) still proxies to c0re via "/api/" below — that's the one
# embedded `utoipa-swagger-ui` dist unchanged — only these two exact # thing that has to stay dynamic.
# paths are intercepted. A theme tweak is therefore a change to #
# `hive-c0re/swagger-ui-theme/` + a gateway container activation, not # Prefix location, not exact-match: wins over "/api/" on plain
# a hive-c0re rebuild+restart. See nix/packages/swagger-ui-theme.nix. # prefix length (no ordering/`=` needed), and now needs to cover
swaggerThemeLocations = { # every file in the tree (bundle.js, maps, favicons, …), not just
"= /api/docs/" = { # our 2 override files — hive-c0re no longer serves any of this as
# a fallback once its own `utoipa-swagger-ui` mount is removed.
# `= /api/docs` (no trailing slash) issues the same redirect
# `utoipa-swagger-ui`'s router used to: that mount is going away
# too, so nginx has to own it now, or the H0M3 hub's own `/api/docs`
# link (no trailing slash) would 404 once hive-c0re drops the route.
swaggerUiLocations = {
"= /api/docs" = {
extraConfig = '' extraConfig = ''
alias ${swaggerUiTheme}/index.html; return 301 /api/docs/;
${dashboardAuth}
''; '';
}; };
"= /api/docs/hyperhive-theme.css" = { "/api/docs/" = {
alias = "${swaggerUiTheme}/";
extraConfig = '' extraConfig = ''
alias ${swaggerUiTheme}/hyperhive-theme.css; index index.html;
${dashboardAuth} ${dashboardAuth}
''; '';
}; };
@ -353,7 +360,7 @@ in
// wellKnownLocations // wellKnownLocations
// agentLocations // agentLocations
// dashboardProxyLocation // dashboardProxyLocation
// swaggerThemeLocations // swaggerUiLocations
// lib.optionalAttrs cfg.auth.enable { // lib.optionalAttrs cfg.auth.enable {
# Internal-only target for the 401 error_page above. # Internal-only target for the 401 error_page above.
# `internal` prevents direct client access; `alias` serves # `internal` prevents direct client access; `alias` serves

View file

@ -18,6 +18,13 @@ let
inherit (nixpkgs.lib) nixosSystem; inherit (nixpkgs.lib) nixosSystem;
}; };
# Plain vendored Swagger UI dist (./swagger-ui-dist.nix) + the
# hyperhive-themed overlay on top (./swagger-ui-theme.nix) — bound
# here (not just inline in the attrset below) so the theme
# derivation can take the dist derivation as an explicit input.
swagger-ui-dist = pkgs.callPackage ./swagger-ui-dist.nix { };
swagger-ui-theme = pkgs.callPackage ./swagger-ui-theme.nix { inherit swagger-ui-dist; };
# Every per-binary package: name → description. The single source of # Every per-binary package: name → description. The single source of
# truth for the bin list — it drives the per-bin extractor packages # truth for the bin list — it drives the per-bin extractor packages
# and the `default` bundle, so adding a binary is one entry here. # and the `default` bundle, so adding a binary is one entry here.
@ -158,11 +165,13 @@ in
xdg-icons = pkgs.callPackage ./hive-xdg-icons.nix { xdg-icons = pkgs.callPackage ./hive-xdg-icons.nix {
hyperhiveSvg = ../../branding/hyperhive.svg; hyperhiveSvg = ../../branding/hyperhive.svg;
}; };
# Swagger UI re-theme static override files — see # Swagger UI: plain vendored dist + the hyperhive-themed overlay —
# ./swagger-ui-theme.nix. The gateway `alias`es these straight from # see ./swagger-ui-dist.nix / ./swagger-ui-theme.nix (both computed
# the store; a theme tweak is a gateway config change, not a # above, in `let`). The gateway serves `swagger-ui-theme`'s full
# hive-c0re rebuild. # tree straight from the store at /api/docs/; a theme tweak is a
swagger-ui-theme = pkgs.callPackage ./swagger-ui-theme.nix { }; # gateway config change, not a hive-c0re rebuild. `swagger-ui-dist`
# is exposed too since it's independently useful/inspectable.
inherit swagger-ui-dist swagger-ui-theme;
# Pre-built per-container system closures. Exposed as packages # Pre-built per-container system closures. Exposed as packages
# so operators can `nix build .#agent-base-toplevel` (or wire # so operators can `nix build .#agent-base-toplevel` (or wire

View file

@ -0,0 +1,47 @@
{
stdenv,
fetchurl,
}:
# Pure vendored Swagger UI 5.17.14 static dist — no hyperhive theming
# (see ./swagger-ui-theme.nix for that layer). Deliberately NOT sourced
# via the Rust `utoipa-swagger-ui-vendored` crate / Cargo.lock (per the
# operator's ask: a nix package that just contains the plain static
# dist, not fetched via cargo) — hive-c0re no longer needs to know this
# exists at all once the gateway hosts it directly.
#
# Sourced straight from the `swagger-ui-dist` npm package, which ships
# exactly the built `dist/` files (no source, no build step needed) at
# the same release as the Rust ecosystem's vendored copy — verified by
# comparing this tarball's recorded `gitHead` (in its `package.json`,
# `74ed0adebfc9c8dd0de2bf8e81495b022a66c083`) against the commit the
# `utoipa-swagger-ui-vendored` crate's own pinned zip was built from
# (same hash, printed by `unzip -l` on that zip's first entry).
stdenv.mkDerivation {
pname = "swagger-ui-dist";
version = "5.17.14";
src = fetchurl {
url = "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.14.tgz";
hash = "sha512-CVbSfaLpstV65OnSjbXfVd6Sta3q3F7Cj/yYuvHMp1P90LztOLs6PfUnKEVAeiIVQt9u2SaPwv0LiH/OyMjHRw==";
};
sourceRoot = "package";
dontBuild = true;
installPhase = ''
runHook preInstall
mkdir -p $out
# The npm package also carries a few node-consumption conveniences
# (package.json, index.js, absolute-path.js, README/LICENSE/NOTICE)
# alongside the actual browser dist — only copy what a web server
# should ever expose.
cp *.html *.css *.css.map *.js *.js.map *.png $out/
runHook postInstall
'';
meta = {
description = "Vendored Swagger UI 5.17.14 static dist (unthemed), from the swagger-ui-dist npm package";
homepage = "https://forge.darkest.space/hyperhive/hyperhive";
};
}

View file

@ -1,49 +1,58 @@
{ {
stdenv, stdenv,
swagger-ui-dist,
}: }:
# The hand-authored Swagger UI re-theme files (`hive-c0re/swagger-ui- # hyperhive's Catppuccin Mocha re-theme of Swagger UI: overlays our 3
# theme/`), shipped as a standalone derivation so the gateway can # hand-authored override files (`hive-c0re/swagger-ui-theme/`) onto
# `alias` them straight from the nix store instead of hive-c0re's own # the plain vendored dist (./swagger-ui-dist.nix). This is the FULL
# build serving them — see nix/host-modules/hive-gateway/vhosts.nix's # tree hive-gateway's nginx serves at `/api/docs/` — hive-c0re hosts
# `swaggerThemeLocations`. This is what lets a CSS tweak ship as a # none of it, only the dynamic `/api/openapi.json` (see
# gateway config change (activate the gateway container) rather than a # nix/host-modules/hive-gateway/vhosts.nix).
# hive-c0re rebuild+restart: the two files nginx exact-matches
# (`/api/docs/` and `/api/docs/hyperhive-theme.css`) come from here;
# every other Swagger UI asset (bundle.js, openapi.json, favicons, …)
# still falls through the existing `/api/` prefix proxy to hive-c0re's
# own vendored (unthemed) `utoipa-swagger-ui` embed, unchanged.
# #
# Pure data: copied verbatim, no build step. # Output layout: every file from `swagger-ui-dist`, with 3 replaced:
# # index.html — adds <link>s for colors.css (live stylix
# Output layout: # theme, same origin) + hyperhive-theme.css
# $out/index.html — themed index.html Swagger UI page # hyperhive-theme.css — new; the re-theme itself, consumes
# $out/hyperhive-theme.css — the re-theme, consumes --baseNN vars # --baseNN vars from colors.css
# from the dashboard's own colors.css # swagger-initializer.js — hardcodes `url: "/api/openapi.json"`. The
# (loaded via an absolute /static/ # plain vendored copy points at the
# colors.css <link>, same origin) # swagger.io petstore demo; normally
# `utoipa-swagger-ui`'s `serve()` rewrites
# this file per-request from a `{{config}}`
# placeholder it injects at its OWN build
# time (see that crate's `build.rs`'s
# `replace_default_url_with_config` +
# `format_config`). hive-c0re no longer
# serves this file at all, so that rewrite
# has to happen here instead — this copy is
# hand-verified to byte-match what
# `format_config` would produce for
# hive-c0re's actual config (single unnamed
# url, otherwise all `Config` defaults): the
# same transform applied to the real
# vendored file, not hand-typed from
# scratch.
stdenv.mkDerivation { stdenv.mkDerivation {
pname = "hyperhive-swagger-ui-theme"; pname = "hyperhive-swagger-ui-theme";
version = "0.1.0"; version = "0.1.0";
# Narrow src keeps this derivation's input hash decoupled from the
# rest of the tree — a theme tweak only re-hashes this.
src = ../../hive-c0re/swagger-ui-theme;
dontUnpack = true;
dontBuild = true; dontBuild = true;
dontConfigure = true;
installPhase = '' installPhase = ''
runHook preInstall runHook preInstall
mkdir -p $out mkdir -p $out
cp -r ./* $out/ cp -r ${swagger-ui-dist}/. $out/
chmod -R u+w $out
install -m644 ${../../hive-c0re/swagger-ui-theme/index.html} $out/index.html
install -m644 ${../../hive-c0re/swagger-ui-theme/hyperhive-theme.css} $out/hyperhive-theme.css
install -m644 ${../../hive-c0re/swagger-ui-theme/swagger-initializer.js} $out/swagger-initializer.js
runHook postInstall runHook postInstall
''; '';
dontFixup = true;
meta = { meta = {
description = "hyperhive Swagger UI Catppuccin Mocha re-theme (static override files)"; description = "hyperhive Swagger UI Catppuccin Mocha re-theme full static dist, nginx-served at /api/docs/";
homepage = "https://forge.darkest.space/hyperhive/hyperhive"; homepage = "https://forge.darkest.space/hyperhive/hyperhive";
}; };
} }