release/v1.26.0: complete group model, enforce naming, fix docs + status dots (#166)

* feat: complete the group model (god_admin, site groups, aggregates), enforce naming, fix docs 500s + status dots (v1.26.0)

- seed god_admin + nest into app_super_admin; auto-provision site groups (S_super_admin, S_hosts_*/S_apps_* aggregates, S_everyone) on site create + self-heal on Directory load
- map service resources to the app kind (site_local_app_<slug>_*); nest per-resource groups into site aggregates (physical inheritance lattice)
- enforce the group naming convention server-side on POST /groups; surface god_admin + site groups on the site resource modal
- fix in-app /docs/<slug> 500s (Dockerfile never copied docs/); serve doc images at /docs/images
- fix Directory status dots (neutral grey when agent endpoint unreachable); align Profile/API cards full-width
- group resolver: keep the site slug verbatim (site_local not re-slugified)
- bump to 1.26.0

* fix: use verbatim resource slugs in group names (matches access-request tests + live convention)

The group naming inserts a kind segment (resourceGroupCns(site, kind, slug, level)),
but the access-request tests + the live directory convention are verbatim
({site}_{slug}_{level} -- the kind is carried in the resource slug, e.g. host_theta-env).
For bare test slugs this produced site_x_host_artest-host_x_access instead of the
expected site_x_artest-host_x_access, so the requester was never removed from the
auto-provisioned access group and every request 409'd. resourceGroupCns is now
(site, slug, level) with the verbatim slug; the kind is used only to pick the
aggregate the group nests into.
This commit is contained in:
2026-08-04 19:07:51 -04:00
committed by GitHub
parent 512a28d1f5
commit 8a9de94d24
12 changed files with 322 additions and 82 deletions
+22 -4
View File
@@ -530,6 +530,11 @@
// tab read from these. Agent data comes from /api/agent/nodes (admin-gated).
var agentsByHost = {};
var agentsByToken = {};
// True when the agent/nodes endpoint itself was unreachable (network, or an
// older app without the agent route). When set we cannot tell "this host has
// no agent" apart from "the agent service is down", so we must NOT paint every
// host red as if it lacked an agent.
var agentsUnavailable = false;
async function loadResources() {
try {
@@ -540,8 +545,12 @@
// Access counts are a nicety, not load-bearing: if the LDAP join fails
// the table still renders, just without the Access column populated.
app.api.get('directory-admin/access-summary').catch(function(){ return {results: {}}; }),
// Agents are a nicety too: never block the directory on them.
app.api.get('agent/nodes').catch(function(){ return {agents: []}; })
// Agents are a nicety too: never block the directory on them. Track
// whether the endpoint itself is reachable so host rows can tell "no
// agent on this host" from "agent service is down" (see attachAgentStatus).
app.api.get('agent/nodes')
.then(function(res){ agentsUnavailable = false; return res; })
.catch(function(){ agentsUnavailable = true; return {agents: []}; })
]);
accessSummary = (resAccess && resAccess.results) || {};
@@ -610,7 +619,12 @@
const a = agentsByHost[name] || (slug && agentsByHost[slug]);
n.agent = a || null;
if (resourcesById[n.id]) resourcesById[n.id].agent = a || null;
if (!a) { n.agentColor = '#dc3545'; n.agentStatusTitle = 'No theta-agent connected'; return; }
if (!a) {
// Endpoint unreachable: we genuinely don't know -- neutral grey, not a
// false red alarm across every host.
if (agentsUnavailable) { n.agentColor = '#adb5bd'; n.agentStatusTitle = 'Agent service unreachable'; return; }
n.agentColor = '#dc3545'; n.agentStatusTitle = 'No theta-agent connected'; return;
}
if (!a.isOnline) { n.agentColor = '#dc3545'; n.agentStatusTitle = 'Agent offline (' + (a.hostname || 'unknown') + ')'; return; }
const t = a.telemetry || {};
const high = (t.cpu_usage_percent > 80) || (t.ram_usage_percent > 80) || (t.disk_usage_percent > 90);
@@ -655,8 +669,12 @@
try {
const res = await app.api.get('agent/nodes');
indexAgents((res && res.agents) || []);
agentsUnavailable = false;
renderTable();
} catch (e) { /* non-fatal */ }
} catch (e) {
agentsUnavailable = true;
renderTable(); // re-render so dots flip to neutral, not stale green
}
}
// "Who can reach this?" at a glance. A resource with no linked group is not a
+2 -2
View File
@@ -657,8 +657,8 @@
</script>
<div id="own-api-tokens-section" style="display:none">
<div class="row mt-3 justify-content-center">
<div class="col-md-8">
<div class="row mt-3">
<div class="col-12">
<div class="card shadow-lg">
<div class="card-header d-flex justify-content-between align-items-center">
<span><i class="fa-solid fa-key me-1"></i> API Tokens</span>