Point monitoring help at the current platform navigation

Replace retired task-page advice with registered platform routes, TrueNAS tabs and Proxmox backups. Preserve saved connections when troubleshooting missing navigation and distinguish proxy reload failures from missing inventory.

Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-10-03 02:32:05 +01:00
parent a9533eb82a
commit 1a1b59072b
8 changed files with 338 additions and 50 deletions
+3 -2
View File
@@ -152,7 +152,7 @@ It does **not** allow:
If you have multiple PBS servers, add each one separately in Settings. Pulse will:
- Monitor each server independently
- Show backups from all servers in the unified Recovery view
- Show backups from all servers under **Proxmox → Backups** (`/proxmox/backups`)
- Deduplicate if the same backup appears via both PVE passthrough and direct PBS
---
@@ -301,7 +301,8 @@ or change retention to hide a display problem.
## Data Source Indicator
In the Recovery view, PBS backups show a data source indicator:
Under **Proxmox → Backups** (`/proxmox/backups`), PBS backups show a data source
indicator. There is no current top-level Recovery page:
- **"PBS"** badge alone = Direct PBS connection (full data)
- **"PBS via PVE"** = Passthrough via PVE storage (limited data)
+37 -4
View File
@@ -413,7 +413,7 @@ consequential, manually redacted error.
### TrueNAS
#### "TrueNAS service unavailable"
- Ensure TrueNAS was added in **Settings → TrueNAS** with a valid HTTPS URL,
- Ensure TrueNAS was added in **Settings → Infrastructure → Platform connections** with a valid HTTPS URL,
API key, and the username that owns the key.
- Check that the TrueNAS system is reachable from the Pulse server (default
HTTPS port).
@@ -424,7 +424,10 @@ consequential, manually redacted error.
#### TrueNAS pools/datasets not appearing
- TrueNAS data appears in the unified resource model and may take one configured
polling cycle (60 seconds by default) to appear.
- Check **Infrastructure** (TrueNAS host), **Storage** (pools/datasets), and **Recovery** (snapshots/replication).
- Open **TrueNAS → Overview** for the appliance, **TrueNAS → Storage**
(`/truenas/storage`) for pools, datasets and disks, and **TrueNAS → Protection**
(`/truenas/protection`) for snapshots and replication. These are tabs within
TrueNAS, not separate top-level Storage or Recovery pages.
- For data that stops refreshing, use the [TrueNAS polling checks](TRUENAS.md#stale-truenas-data)
before testing or restarting. A stale badge is not proof of an invalid key,
and a successful connection test is not proof that collection has recovered.
@@ -432,8 +435,38 @@ consequential, manually redacted error.
### Navigation (v6)
#### Old bookmarks don't work
- Legacy URLs (`/proxmox`, `/docker`, `/kubernetes`, `/hosts`, `/services`) are not supported in v6.
- Update bookmarks to canonical routes. See [Migration Guide](MIGRATION_UNIFIED_NAV.md).
Current Pulse uses platform navigation. `/proxmox`, `/docker` and `/kubernetes`
are supported; do not replace them with the retired task-based routes.
Open Pulse at its base URL and use the menu to find the relevant page:
| Menu | Entry route |
| --- | --- |
| Proxmox | `/proxmox/overview` |
| Docker | `/docker/overview` |
| Kubernetes | `/kubernetes/overview` |
| TrueNAS | `/truenas/overview` |
| vSphere | `/vmware/overview` |
| Machines | `/standalone/machines` |
The short-lived top-level `/workloads`, `/storage` and `/recovery` layout is
retired. `/infrastructure` now opens the default workspace, not the former
unified host page. For old `/hosts` or `/services` bookmarks, select the current
platform or Machines page instead of assuming an automatic redirect. PBS
backups are under **Proxmox → Backups**, and TrueNAS snapshots and replication
are under **TrueNAS → Protection**.
Platform-connected hosts can appear under their platform rather than Machines.
If a menu or expected resource is missing, check its saved connection and last
successful collection in **Settings → Infrastructure**; a missing page alone
does not prove a host was deleted. Do not delete connections or re-enrol agents
just to repair a bookmark.
If menu navigation works but reloading the same URL returns a proxy 404, check
the proxy's route handling using [Reverse Proxy Configuration](REVERSE_PROXY.md).
For the current layout, see [FAQ](FAQ.md#how-is-navigation-organised-in-pulse-v6).
The [unified-navigation migration](MIGRATION_UNIFIED_NAV.md) is historical,
not a guide to the current menu.
### Relay / Mobile
+24 -19
View File
@@ -4,8 +4,9 @@ Pulse v6 includes first-class monitoring for **TrueNAS SCALE** and **TrueNAS COR
## Quick Start
1. Go to **Settings → TrueNAS**.
2. Click **Add Connection**.
1. Go to **Settings → Infrastructure**.
2. Click **Add infrastructure** and choose **TrueNAS**. Existing connections
are listed under **Platform connections**.
3. Enter the TrueNAS URL (e.g., `https://truenas.local`), the API key, and the
username that owns the key.
4. Click **Test Connection** → **Save**.
@@ -28,27 +29,27 @@ On your TrueNAS system:
## What Gets Monitored
| Data | Unified Page | Details |
| Data | Pulse page / tab | Details |
|---|---|---|
| System info (hostname, version, uptime) | Infrastructure | CPU, memory, health status |
| Virtual machines | TrueNAS Overview | State, CPU, memory, boot mode, devices, and security flags from the TrueNAS VM API |
| Apps | TrueNAS Overview | Native app state, image/version, ports, volumes, networks, and runtime container details |
| ZFS Pools | Storage | Total/used/free capacity, pool status (ONLINE/DEGRADED/FAULTED) |
| ZFS Datasets | Storage | Used/available space, mount status, read-only flag |
| Physical Disks | Storage | Model, serial, size, transport type, rotational flag, temperature, and native SMART failure/counter evidence when TrueNAS reports it |
| ZFS Snapshots | Recovery | Dataset, creation time, size, referenced data |
| Replication Tasks | Recovery | Source/target datasets, direction, last run status |
| System info (hostname, version, uptime) | TrueNAS → Overview | CPU, memory, health status |
| Virtual machines | TrueNAS → VMs | State, CPU, memory, boot mode, devices, and security flags from the TrueNAS VM API |
| Apps | TrueNAS → Apps | Native app state, image/version, ports, volumes, networks, and runtime container details |
| ZFS Pools | TrueNAS → Storage | Total/used/free capacity, pool status (ONLINE/DEGRADED/FAULTED) |
| ZFS Datasets | TrueNAS → Storage | Used/available space, mount status, read-only flag |
| Physical Disks | TrueNAS → Storage | Model, serial, size, transport type, rotational flag, temperature, and native SMART failure/counter evidence when TrueNAS reports it |
| ZFS Snapshots | TrueNAS → Protection | Dataset, creation time, size, referenced data |
| Replication Tasks | TrueNAS → Protection | Source/target datasets, direction, last run status |
| TrueNAS Alerts | Alerts | Native TrueNAS alert messages and severity levels |
## Unified Resource Mapping
TrueNAS resources are mapped into the unified resource model:
- **TrueNAS host** → appears as a resource with `source: truenas` on the **Infrastructure** page.
- **TrueNAS VMs** → appear as canonical `vm` workloads on the **TrueNAS** page.
- **TrueNAS apps** → appear as canonical `app-container` workloads on the **TrueNAS** page.
- **ZFS pools and datasets** → appear on the **Storage** page.
- **ZFS snapshots and replication** → appear on the **Recovery** page as recovery points.
- **TrueNAS host** → appears as a resource with `source: truenas` under **TrueNAS → Overview** (`/truenas/overview`).
- **TrueNAS VMs** → appear as canonical `vm` workloads under **TrueNAS → VMs** (`/truenas/vms`).
- **TrueNAS apps** → appear as canonical `app-container` workloads under **TrueNAS → Apps** (`/truenas/apps`).
- **ZFS pools, datasets and physical disks** → appear under **TrueNAS → Storage** (`/truenas/storage`).
- **ZFS snapshots and replication** → appear under **TrueNAS → Protection** (`/truenas/protection`) as recovery points.
- **TrueNAS alerts** → surfaced on the **Alerts** page alongside Proxmox and other platform alerts.
TrueNAS drive-health alerts that identify a specific disk remain disk-health
@@ -60,7 +61,11 @@ typed uncorrectable-error or spare-reserve argument, Pulse also projects that
value into the disk's SMART details. Pulse does not infer counters from alert
text, and current TrueNAS APIs do not expose every raw SMART attribute.
Resources from TrueNAS can be filtered using the **source** filter on any page.
The unified resource model is a backend contract, not a top-level
Infrastructure, Storage or Recovery menu. TrueNAS tabs follow the inventory
Pulse has collected; an absent tab is not proof of successful collection.
For missing or stale data, use the [polling checks](#stale-truenas-data) before
testing the connection or restarting.
## Multiple TrueNAS Systems
@@ -91,7 +96,7 @@ All endpoints require admin authentication.
### Testing and adding a connection (API)
The **Settings → TrueNAS** flow is the simplest option. For an API client,
The **Settings → Infrastructure → Platform connections** flow is the simplest option. For an API client,
prepare the private Pulse header file as described in the
[API authentication guide](API.md). The Pulse API token authenticates to
Pulse; the separate TrueNAS API key belongs in the connection's JSON file.
@@ -260,4 +265,4 @@ Set `PULSE_ENABLE_TRUENAS=false` and restart Pulse. Existing connection data is
JSON-RPC transport, API-key, and TLS requirements
- [Configuration Guide](CONFIGURATION.md#truenas) — environment variables and setup
- [ZFS Monitoring](ZFS_MONITORING.md) — Proxmox-native ZFS pool monitoring
- [Recovery](RECOVERY.md) — TrueNAS snapshots in the recovery view
- [Recovery data](RECOVERY.md) — the snapshot and replication data shown under **TrueNAS → Protection**
@@ -0,0 +1,110 @@
// Production Docs/router/styles with the actual shipped Markdown; no backend.
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const { chromium, webkit } = require('playwright');
(async () => {
const root = '/workspace/frontend-modern';
const artifacts = path.join(root, 'node_modules', 'navigation-help-browser');
fs.mkdirSync(artifacts, { recursive: true });
const { createServer } = await import(path.join(root, 'node_modules/vite/dist/node/index.js'));
const server = await createServer({
root,
configFile: path.join(root, 'vite.config.ts'),
cacheDir: path.join(root, 'node_modules', '.vite-navigation-help'),
server: { host: '127.0.0.1', port: 5247, strictPort: true },
optimizeDeps: { noDiscovery: true, include: [] },
});
const results = [];
let browser;
try {
await server.listen();
for (const [engine, width, tone] of [['chromium', 1280, 'light'], ['webkit', 390, 'dark']]) {
browser = await { chromium, webkit }[engine].launch(
engine === 'chromium'
? { headless: true, channel: 'chromium', args: ['--no-sandbox'] }
: { headless: true },
);
const page = await browser.newPage({
viewport: { width, height: 900 },
...(engine === 'webkit' ? { isMobile: true, hasTouch: true } : {}),
});
const errors = [];
page.on('pageerror', (e) => errors.push(e.message));
page.on('console', (m) => { if (m.type() === 'error') errors.push(m.text()); });
await page.goto('http://127.0.0.1:5247/browser-tests/docs-fragment-navigation.html?scenario=troubleshooting',
{ waitUntil: 'domcontentloaded', timeout: 60_000 });
await page.getByRole('heading', { name: "Old bookmarks don't work", exact: true }).waitFor();
if (tone === 'dark') await page.evaluate(() => document.documentElement.classList.add('dark'));
const routes = page.locator('article table').filter({ hasText: '/standalone/machines' });
assert.equal(await routes.count(), 1);
assert.deepEqual(await routes.locator('tbody tr').allTextContents(), [
'Proxmox/proxmox/overview', 'Docker/docker/overview', 'Kubernetes/kubernetes/overview',
'TrueNAS/truenas/overview', 'vSphere/vmware/overview', 'Machines/standalone/machines',
]);
const article = await page.locator('article').innerText();
assert.ok(article.includes('are supported; do not replace them with the retired task-based routes.'));
assert.ok(article.includes('Do not delete connections or re-enrol agents'));
await routes.scrollIntoViewIfNeeded();
assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth + 1));
await page.screenshot({ path: path.join(artifacts, `${engine}-routes.png`) });
await page.getByRole('link', { name: 'FAQ', exact: true }).click();
const faq = page.getByRole('heading', { name: 'How is navigation organised in Pulse v6?', exact: true });
await faq.waitFor();
await page.waitForFunction(() => {
const heading = document.getElementById('how-is-navigation-organised-in-pulse-v6');
return heading && heading.getBoundingClientRect().top >= 0 && heading.getBoundingClientRect().top < innerHeight;
});
assert.ok(page.url().endsWith('/docs/FAQ#how-is-navigation-organised-in-pulse-v6'));
await page.getByRole('link', { name: '← All documentation' }).click();
await page.getByRole('link', { name: 'TrueNAS SCALE and CORE', exact: true }).click();
const inventory = page.locator('article table').filter({ hasText: 'Pulse page / tab' });
await inventory.waitFor();
const destinations = await inventory.locator('tbody tr td:nth-child(2)').allTextContents();
assert.deepEqual(destinations, ['TrueNAS → Overview', 'TrueNAS → VMs', 'TrueNAS → Apps',
'TrueNAS → Storage', 'TrueNAS → Storage', 'TrueNAS → Storage',
'TrueNAS → Protection', 'TrueNAS → Protection', 'Alerts']);
const mapping = await page.locator('article').innerText();
for (const route of ['/truenas/overview', '/truenas/vms', '/truenas/apps', '/truenas/storage', '/truenas/protection'])
assert.ok(mapping.includes(route));
await inventory.scrollIntoViewIfNeeded();
const geometry = await inventory.evaluate((table) => ({
localScroll: table.parentElement.dataset.docTableScroll !== undefined,
viewport: innerWidth,
outer: document.documentElement.scrollWidth,
table: table.parentElement.scrollWidth,
wrapper: table.parentElement.clientWidth,
}));
assert.ok(geometry.localScroll);
assert.ok(geometry.outer <= geometry.viewport + 1);
if (width === 390) assert.ok(geometry.table > geometry.wrapper);
await page.screenshot({ path: path.join(artifacts, `${engine}-truenas.png`) });
await page.getByRole('link', { name: '← All documentation' }).click();
await page.getByRole('link', { name: 'Proxmox Backup Server', exact: true }).click();
const pbs = page.getByRole('heading', { name: 'Data Source Indicator', exact: true });
await pbs.waitFor();
const pbsText = await page.locator('article').innerText();
assert.ok(pbsText.includes('Proxmox → Backups (/proxmox/backups)'));
assert.ok(pbsText.includes('There is no current top-level Recovery page'));
await pbs.scrollIntoViewIfNeeded();
assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1));
await page.screenshot({ path: path.join(artifacts, `${engine}-pbs.png`) });
assert.deepEqual(errors, []);
results.push({ engine, version: browser.version(), width, tone, routes: 6, destinations,
geometry, faqFragment: true, pbsPlacement: true, errors });
await browser.close();
browser = undefined;
}
const result = { result: 'passed', playwright: require('playwright/package.json').version,
scope: 'shipped help rendering and navigation, not appliance collection', results };
fs.writeFileSync(path.join(artifacts, 'result.json'), JSON.stringify(result, null, 2) + '\n');
console.log(JSON.stringify(result));
} finally {
if (browser) await browser.close();
await server.close();
}
})().catch((error) => { console.error(error); process.exitCode = 1; });
+3 -2
View File
@@ -152,7 +152,7 @@ It does **not** allow:
If you have multiple PBS servers, add each one separately in Settings. Pulse will:
- Monitor each server independently
- Show backups from all servers in the unified Recovery view
- Show backups from all servers under **Proxmox → Backups** (`/proxmox/backups`)
- Deduplicate if the same backup appears via both PVE passthrough and direct PBS
---
@@ -301,7 +301,8 @@ or change retention to hide a display problem.
## Data Source Indicator
In the Recovery view, PBS backups show a data source indicator:
Under **Proxmox → Backups** (`/proxmox/backups`), PBS backups show a data source
indicator. There is no current top-level Recovery page:
- **"PBS"** badge alone = Direct PBS connection (full data)
- **"PBS via PVE"** = Passthrough via PVE storage (limited data)
+37 -4
View File
@@ -413,7 +413,7 @@ consequential, manually redacted error.
### TrueNAS
#### "TrueNAS service unavailable"
- Ensure TrueNAS was added in **Settings → TrueNAS** with a valid HTTPS URL,
- Ensure TrueNAS was added in **Settings → Infrastructure → Platform connections** with a valid HTTPS URL,
API key, and the username that owns the key.
- Check that the TrueNAS system is reachable from the Pulse server (default
HTTPS port).
@@ -424,7 +424,10 @@ consequential, manually redacted error.
#### TrueNAS pools/datasets not appearing
- TrueNAS data appears in the unified resource model and may take one configured
polling cycle (60 seconds by default) to appear.
- Check **Infrastructure** (TrueNAS host), **Storage** (pools/datasets), and **Recovery** (snapshots/replication).
- Open **TrueNAS → Overview** for the appliance, **TrueNAS → Storage**
(`/truenas/storage`) for pools, datasets and disks, and **TrueNAS → Protection**
(`/truenas/protection`) for snapshots and replication. These are tabs within
TrueNAS, not separate top-level Storage or Recovery pages.
- For data that stops refreshing, use the [TrueNAS polling checks](TRUENAS.md#stale-truenas-data)
before testing or restarting. A stale badge is not proof of an invalid key,
and a successful connection test is not proof that collection has recovered.
@@ -432,8 +435,38 @@ consequential, manually redacted error.
### Navigation (v6)
#### Old bookmarks don't work
- Legacy URLs (`/proxmox`, `/docker`, `/kubernetes`, `/hosts`, `/services`) are not supported in v6.
- Update bookmarks to canonical routes. See [Migration Guide](MIGRATION_UNIFIED_NAV.md).
Current Pulse uses platform navigation. `/proxmox`, `/docker` and `/kubernetes`
are supported; do not replace them with the retired task-based routes.
Open Pulse at its base URL and use the menu to find the relevant page:
| Menu | Entry route |
| --- | --- |
| Proxmox | `/proxmox/overview` |
| Docker | `/docker/overview` |
| Kubernetes | `/kubernetes/overview` |
| TrueNAS | `/truenas/overview` |
| vSphere | `/vmware/overview` |
| Machines | `/standalone/machines` |
The short-lived top-level `/workloads`, `/storage` and `/recovery` layout is
retired. `/infrastructure` now opens the default workspace, not the former
unified host page. For old `/hosts` or `/services` bookmarks, select the current
platform or Machines page instead of assuming an automatic redirect. PBS
backups are under **Proxmox → Backups**, and TrueNAS snapshots and replication
are under **TrueNAS → Protection**.
Platform-connected hosts can appear under their platform rather than Machines.
If a menu or expected resource is missing, check its saved connection and last
successful collection in **Settings → Infrastructure**; a missing page alone
does not prove a host was deleted. Do not delete connections or re-enrol agents
just to repair a bookmark.
If menu navigation works but reloading the same URL returns a proxy 404, check
the proxy's route handling using [Reverse Proxy Configuration](REVERSE_PROXY.md).
For the current layout, see [FAQ](FAQ.md#how-is-navigation-organised-in-pulse-v6).
The [unified-navigation migration](MIGRATION_UNIFIED_NAV.md) is historical,
not a guide to the current menu.
### Relay / Mobile
+24 -19
View File
@@ -4,8 +4,9 @@ Pulse v6 includes first-class monitoring for **TrueNAS SCALE** and **TrueNAS COR
## Quick Start
1. Go to **Settings → TrueNAS**.
2. Click **Add Connection**.
1. Go to **Settings → Infrastructure**.
2. Click **Add infrastructure** and choose **TrueNAS**. Existing connections
are listed under **Platform connections**.
3. Enter the TrueNAS URL (e.g., `https://truenas.local`), the API key, and the
username that owns the key.
4. Click **Test Connection** → **Save**.
@@ -28,27 +29,27 @@ On your TrueNAS system:
## What Gets Monitored
| Data | Unified Page | Details |
| Data | Pulse page / tab | Details |
|---|---|---|
| System info (hostname, version, uptime) | Infrastructure | CPU, memory, health status |
| Virtual machines | TrueNAS Overview | State, CPU, memory, boot mode, devices, and security flags from the TrueNAS VM API |
| Apps | TrueNAS Overview | Native app state, image/version, ports, volumes, networks, and runtime container details |
| ZFS Pools | Storage | Total/used/free capacity, pool status (ONLINE/DEGRADED/FAULTED) |
| ZFS Datasets | Storage | Used/available space, mount status, read-only flag |
| Physical Disks | Storage | Model, serial, size, transport type, rotational flag, temperature, and native SMART failure/counter evidence when TrueNAS reports it |
| ZFS Snapshots | Recovery | Dataset, creation time, size, referenced data |
| Replication Tasks | Recovery | Source/target datasets, direction, last run status |
| System info (hostname, version, uptime) | TrueNAS → Overview | CPU, memory, health status |
| Virtual machines | TrueNAS → VMs | State, CPU, memory, boot mode, devices, and security flags from the TrueNAS VM API |
| Apps | TrueNAS → Apps | Native app state, image/version, ports, volumes, networks, and runtime container details |
| ZFS Pools | TrueNAS → Storage | Total/used/free capacity, pool status (ONLINE/DEGRADED/FAULTED) |
| ZFS Datasets | TrueNAS → Storage | Used/available space, mount status, read-only flag |
| Physical Disks | TrueNAS → Storage | Model, serial, size, transport type, rotational flag, temperature, and native SMART failure/counter evidence when TrueNAS reports it |
| ZFS Snapshots | TrueNAS → Protection | Dataset, creation time, size, referenced data |
| Replication Tasks | TrueNAS → Protection | Source/target datasets, direction, last run status |
| TrueNAS Alerts | Alerts | Native TrueNAS alert messages and severity levels |
## Unified Resource Mapping
TrueNAS resources are mapped into the unified resource model:
- **TrueNAS host** → appears as a resource with `source: truenas` on the **Infrastructure** page.
- **TrueNAS VMs** → appear as canonical `vm` workloads on the **TrueNAS** page.
- **TrueNAS apps** → appear as canonical `app-container` workloads on the **TrueNAS** page.
- **ZFS pools and datasets** → appear on the **Storage** page.
- **ZFS snapshots and replication** → appear on the **Recovery** page as recovery points.
- **TrueNAS host** → appears as a resource with `source: truenas` under **TrueNAS → Overview** (`/truenas/overview`).
- **TrueNAS VMs** → appear as canonical `vm` workloads under **TrueNAS → VMs** (`/truenas/vms`).
- **TrueNAS apps** → appear as canonical `app-container` workloads under **TrueNAS → Apps** (`/truenas/apps`).
- **ZFS pools, datasets and physical disks** → appear under **TrueNAS → Storage** (`/truenas/storage`).
- **ZFS snapshots and replication** → appear under **TrueNAS → Protection** (`/truenas/protection`) as recovery points.
- **TrueNAS alerts** → surfaced on the **Alerts** page alongside Proxmox and other platform alerts.
TrueNAS drive-health alerts that identify a specific disk remain disk-health
@@ -60,7 +61,11 @@ typed uncorrectable-error or spare-reserve argument, Pulse also projects that
value into the disk's SMART details. Pulse does not infer counters from alert
text, and current TrueNAS APIs do not expose every raw SMART attribute.
Resources from TrueNAS can be filtered using the **source** filter on any page.
The unified resource model is a backend contract, not a top-level
Infrastructure, Storage or Recovery menu. TrueNAS tabs follow the inventory
Pulse has collected; an absent tab is not proof of successful collection.
For missing or stale data, use the [polling checks](#stale-truenas-data) before
testing the connection or restarting.
## Multiple TrueNAS Systems
@@ -91,7 +96,7 @@ All endpoints require admin authentication.
### Testing and adding a connection (API)
The **Settings → TrueNAS** flow is the simplest option. For an API client,
The **Settings → Infrastructure → Platform connections** flow is the simplest option. For an API client,
prepare the private Pulse header file as described in the
[API authentication guide](API.md). The Pulse API token authenticates to
Pulse; the separate TrueNAS API key belongs in the connection's JSON file.
@@ -260,4 +265,4 @@ Set `PULSE_ENABLE_TRUENAS=false` and restart Pulse. Existing connection data is
JSON-RPC transport, API-key, and TLS requirements
- [Configuration Guide](CONFIGURATION.md#truenas) — environment variables and setup
- [ZFS Monitoring](ZFS_MONITORING.md) — Proxmox-native ZFS pool monitoring
- [Recovery](RECOVERY.md) — TrueNAS snapshots in the recovery view
- [Recovery data](RECOVERY.md) — the snapshot and replication data shown under **TrueNAS → Protection**
+100
View File
@@ -0,0 +1,100 @@
#!/usr/bin/env python3
"""Keep operator navigation guidance tied to the shipped routes and tab labels.
These checks cover documentation accuracy, not collection or appliance health.
"""
from pathlib import Path
import re
import unittest
ROOT = Path(__file__).resolve().parents[2]
def doc_text(name: str) -> str:
return (ROOT / "docs" / name).read_text(encoding="utf-8")
def source(name: str) -> str:
return (ROOT / "frontend-modern/src" / name).read_text(encoding="utf-8")
class NavigationHelpDocsTest(unittest.TestCase):
def test_entry_routes_match_the_registered_platform_routes(self):
guide = doc_text("TROUBLESHOOTING.md").split("#### Old bookmarks don't work", 1)[1]
guide = guide.split("### Relay / Mobile", 1)[0]
links = source("routing/resourceLinks.ts")
app = source("App.tsx")
for label, prefix in (("Proxmox", "PROXMOX"), ("Docker", "DOCKER"),
("Kubernetes", "KUBERNETES"), ("TrueNAS", "TRUENAS"),
("vSphere", "VMWARE"), ("Machines", "STANDALONE")):
with self.subTest(platform=label):
path = re.search(rf"export const {prefix}_PATH = '([^']+)';", links).group(1)
tab = re.search(rf"export const {prefix}_DEFAULT_TAB = '([^']+)';", links).group(1)
self.assertIn(f"| {label} | `{path}/{tab}` |", guide)
self.assertIn(f'<Route path={{{prefix}_PATH}}', app)
self.assertNotIn("Legacy URLs", guide)
self.assertIn("are supported", guide)
def test_navigation_guidance_preserves_connections_and_separates_proxy_failures(self):
guide = doc_text("TROUBLESHOOTING.md")
for phrase in ("rather than Machines", "last\nsuccessful collection",
"Do not delete connections or re-enrol agents",
"reloading the same URL returns a proxy 404",
"REVERSE_PROXY.md", "FAQ.md#how-is-navigation-organised-in-pulse-v6",
"is historical,\nnot a guide to the current menu"):
with self.subTest(phrase=phrase):
self.assertIn(phrase, guide)
# /infrastructure is still an entry route, not an invented missing page.
self.assertIn('<Route path="/infrastructure" component={RuntimeHomePage}', source("App.tsx"))
self.assertIn("`/infrastructure` now opens the default workspace", guide)
def test_truenas_locations_match_the_actual_tab_labels_and_paths(self):
model = source("features/truenas/truenasPageModel.ts")
tabs = dict(re.findall(r"label: '([^']+)', path: '(/truenas/[^']+)'", model))
guide = doc_text("TRUENAS.md")
for label in ("Overview", "Storage", "Protection", "Apps", "VMs"):
with self.subTest(tab=label):
self.assertIn(f"**TrueNAS → {label}** (`{tabs[label]}`)", guide)
self.assertIn(f"| TrueNAS → {label} |", guide)
self.assertNotIn("| Unified Page |", guide)
self.assertNotIn("on the **Infrastructure** page", guide)
self.assertNotIn("on the **Recovery** page", guide)
self.assertNotIn("on the **Storage** page", guide)
def test_general_truenas_help_uses_platform_tabs(self):
guide = doc_text("TROUBLESHOOTING.md").split("### TrueNAS", 1)[1].split("### Navigation", 1)[0]
for label in ("Overview", "Storage", "Protection"):
self.assertIn(f"**TrueNAS → {label}**", guide)
for path in ("/truenas/storage", "/truenas/protection"):
self.assertIn(f"`{path}`", guide)
self.assertIn("TRUENAS.md#stale-truenas-data", guide)
def test_setup_uses_the_existing_infrastructure_editor(self):
guide = doc_text("TRUENAS.md")
self.assertIn("**Settings → Infrastructure**", guide)
self.assertIn("**Add infrastructure** and choose **TrueNAS**", guide)
self.assertNotIn("**Settings → TrueNAS**", guide)
editor = source("components/Settings/InfrastructureWorkspace.tsx")
self.assertIn("return 'Add infrastructure'", editor)
self.assertIn("case 'truenas':", editor)
self.assertIn("<TrueNASCredentialSlot", editor)
def test_pbs_backups_live_under_proxmox_not_top_level_recovery(self):
guide = doc_text("PBS.md")
self.assertGreaterEqual(guide.count("**Proxmox → Backups** (`/proxmox/backups`)"), 2)
self.assertNotIn("unified Recovery view", guide)
self.assertNotIn("In the Recovery view", guide)
self.assertIn("{ id: 'backups', label: 'Backups'", source("features/proxmox/proxmoxPageModel.ts"))
self.assertIn("PROXMOX_BACKUPS_PATH = `${PROXMOX_PATH}/backups`", source("routing/resourceLinks.ts"))
def test_all_changed_guides_match_their_shipped_copies(self):
for name in ("TROUBLESHOOTING.md", "TRUENAS.md", "PBS.md"):
with self.subTest(guide=name):
self.assertEqual(doc_text(name),
(ROOT / "frontend-modern/public/docs" / name).read_text(encoding="utf-8"))
if __name__ == "__main__":
unittest.main()