Skip to content

Commit 939c2ab

Browse files
authored
Merge pull request #153 from ToolboxAid/pr/26175-CHARLIE-009-system-health-current-r2-health
PR_26175_CHARLIE_009-system-health-current-r2-health
2 parents 9701da2 + f077e85 commit 939c2ab

21 files changed

Lines changed: 1616 additions & 583 deletions

admin/system-health.html

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ <h2>Admin</h2>
4343
<p>Local API Startup</p>
4444
<p>Database Health</p>
4545
<p>Storage Health</p>
46+
<p>Health Check History</p>
4647
<p>Runtime Environment</p>
4748
<p>Limits &amp; Capacity</p>
4849
<p>Diagnostics Plan</p>
@@ -143,11 +144,29 @@ <h2 id="admin-system-health-title">System Health Tables</h2>
143144
</tr>
144145
</thead>
145146
<tbody>
146-
<tr><td>Bucket</td><td data-admin-system-health-storage-value="bucket">Configured bucket placeholder</td><td data-health-status="PENDING" data-admin-system-health-storage-status="bucket" title="Reason: R2 bucket reader is intentionally not wired in this foundation PR." aria-label="PENDING: R2 bucket reader is intentionally not wired in this foundation PR.">PENDING</td></tr>
147-
<tr><td>List</td><td data-admin-system-health-storage-value="list">Objects prefix</td><td data-health-status="PENDING" data-admin-system-health-storage-status="list" title="Reason: R2 list check is intentionally not wired in this foundation PR." aria-label="PENDING: R2 list check is intentionally not wired in this foundation PR.">PENDING</td></tr>
148-
<tr><td>Read</td><td data-admin-system-health-storage-value="read">Health object</td><td data-health-status="PENDING" data-admin-system-health-storage-status="read" title="Reason: R2 read check is intentionally not wired in this foundation PR." aria-label="PENDING: R2 read check is intentionally not wired in this foundation PR.">PENDING</td></tr>
149-
<tr><td>Write</td><td data-admin-system-health-storage-value="write">Health object</td><td data-health-status="PENDING" data-admin-system-health-storage-status="write" title="Reason: R2 write check is intentionally not wired in this foundation PR." aria-label="PENDING: R2 write check is intentionally not wired in this foundation PR.">PENDING</td></tr>
150-
<tr><td>Delete</td><td data-admin-system-health-storage-value="delete">Health object</td><td data-health-status="PENDING" data-admin-system-health-storage-status="delete" title="Reason: R2 delete check is intentionally not wired in this foundation PR." aria-label="PENDING: R2 delete check is intentionally not wired in this foundation PR.">PENDING</td></tr>
147+
<tr><td>Bucket connectivity</td><td data-admin-system-health-storage-value="bucket">Loading</td><td data-health-status="PENDING" data-admin-system-health-storage-status="bucket" title="Reason: R2 bucket connectivity has not loaded yet." aria-label="PENDING: R2 bucket connectivity has not loaded yet.">PENDING</td></tr>
148+
<tr><td>List</td><td data-admin-system-health-storage-value="list">Loading</td><td data-health-status="PENDING" data-admin-system-health-storage-status="list" title="Reason: R2 list check has not loaded yet." aria-label="PENDING: R2 list check has not loaded yet.">PENDING</td></tr>
149+
<tr><td>Upload</td><td data-admin-system-health-storage-value="upload">Loading</td><td data-health-status="PENDING" data-admin-system-health-storage-status="upload" title="Reason: R2 upload check has not loaded yet." aria-label="PENDING: R2 upload check has not loaded yet.">PENDING</td></tr>
150+
<tr><td>Read</td><td data-admin-system-health-storage-value="read">Loading</td><td data-health-status="PENDING" data-admin-system-health-storage-status="read" title="Reason: R2 read check has not loaded yet." aria-label="PENDING: R2 read check has not loaded yet.">PENDING</td></tr>
151+
<tr><td>Delete</td><td data-admin-system-health-storage-value="delete">Loading</td><td data-health-status="PENDING" data-admin-system-health-storage-status="delete" title="Reason: R2 delete check has not loaded yet." aria-label="PENDING: R2 delete check has not loaded yet.">PENDING</td></tr>
152+
<tr><td>Last checked</td><td data-admin-system-health-storage-value="lastChecked">Loading</td><td data-health-status="PENDING" data-admin-system-health-storage-status="lastChecked" title="Reason: R2 health has not loaded yet." aria-label="PENDING: R2 health has not loaded yet.">PENDING</td></tr>
153+
</tbody>
154+
</table>
155+
</div>
156+
<div class="table-wrapper">
157+
<table class="data-table" aria-label="Health check history">
158+
<caption>Health Check History</caption>
159+
<thead>
160+
<tr>
161+
<th scope="col">Time</th>
162+
<th scope="col">Environment</th>
163+
<th scope="col">Area</th>
164+
<th scope="col">Result</th>
165+
<th scope="col">Summary</th>
166+
</tr>
167+
</thead>
168+
<tbody data-admin-system-health-history-rows>
169+
<tr><td>Loading</td><td>Loading</td><td>Current environment</td><td data-health-status="PENDING" title="Reason: health check history has not loaded yet." aria-label="PENDING: health check history has not loaded yet.">PENDING</td><td>Waiting for safe API status.</td></tr>
151170
</tbody>
152171
</table>
153172
</div>

assets/theme-v2/js/admin-system-health.js

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import {
99
} from "../../js/shared/status.js";
1010

1111
const STORAGE_DIAGNOSTIC_ACTIONS = Object.freeze([
12+
Object.freeze({ actionId: "storage-bucket-connectivity", key: "bucket" }),
1213
Object.freeze({ actionId: "storage-list", key: "list" }),
13-
Object.freeze({ actionId: "storage-write-test-object", key: "write" }),
14+
Object.freeze({ actionId: "storage-upload-test-object", key: "upload" }),
1415
Object.freeze({ actionId: "storage-read-test-object", key: "read" }),
1516
Object.freeze({ actionId: "storage-delete-test-object", key: "delete" }),
1617
]);
@@ -46,6 +47,7 @@ class AdminSystemHealthController {
4647
node.dataset.adminSystemHealthStorageStatus,
4748
node,
4849
]));
50+
this.historyRows = root.querySelector("[data-admin-system-health-history-rows]");
4951
this.startupRows = root.querySelector("[data-admin-system-health-startup-rows]");
5052
this.runtimeRows = root.querySelector("[data-admin-system-health-runtime-rows]");
5153
}
@@ -106,6 +108,7 @@ class AdminSystemHealthController {
106108
});
107109
this.renderStartupPending(reason);
108110
this.renderStoragePending(reason);
111+
this.renderHistoryPending(reason);
109112
}
110113

111114
renderEnvironmentIdentity(environmentIdentity = {}) {
@@ -127,7 +130,7 @@ class AdminSystemHealthController {
127130
}
128131

129132
renderStoragePending(reason) {
130-
["bucket", "list", "read", "write", "delete"].forEach((key) => {
133+
["bucket", "list", "upload", "read", "delete", "lastChecked"].forEach((key) => {
131134
this.setStorageStatus(key, "PENDING", reason);
132135
});
133136
this.renderRuntimePending(reason);
@@ -153,8 +156,10 @@ class AdminSystemHealthController {
153156

154157
renderStorageStatus(storageStatus = {}) {
155158
const reason = storageStatus.message || "Cloudflare R2 configuration status returned by the safe Admin System Health API.";
156-
this.setStorageValue("bucket", storageStatus.bucket, "not configured");
159+
this.setStorageValue("bucket", storageStatus.environmentFolder ? `${storageStatus.bucket || "not configured"} ${storageStatus.environmentFolder}` : storageStatus.bucket, "not configured");
157160
this.setStorageStatus("bucket", storageStatus.bucketStatus || storageStatus.status, reason);
161+
this.setStorageValue("lastChecked", storageStatus.lastChecked, "not available");
162+
this.setStorageStatus("lastChecked", storageStatus.lastChecked ? "PASS" : "WARN", reason);
158163
}
159164

160165
renderStartupPending(reason) {
@@ -198,9 +203,12 @@ class AdminSystemHealthController {
198203

199204
storageResultTarget(result = {}) {
200205
if (typeof result.keysListed === "number" && result.actionId === "storage-list") {
201-
return `${result.keysListed} object(s) under ${asText(result.projectsPrefix, "configured prefix")}`;
206+
return `${result.keysListed} object(s) under ${asText(result.environmentFolder, "current environment folder")}`;
202207
}
203-
return result.testObjectKey || result.projectsPrefix || result.message || "diagnostic target unavailable";
208+
if (result.actionId === "storage-bucket-connectivity") {
209+
return result.environmentFolder || result.message || "bucket connectivity target unavailable";
210+
}
211+
return result.testObjectKey || result.environmentFolder || result.message || "diagnostic target unavailable";
204212
}
205213

206214
renderStorageResult(key, result = {}) {
@@ -210,6 +218,49 @@ class AdminSystemHealthController {
210218
}
211219
this.setStorageValue(key, this.storageResultTarget(result));
212220
this.setStorageStatus(key, result.status, result.message || "R2 diagnostic returned without a message.");
221+
if (result.lastChecked) {
222+
this.setStorageValue("lastChecked", result.lastChecked);
223+
this.setStorageStatus("lastChecked", "PASS", "Most recent current-environment R2 health check timestamp.");
224+
}
225+
}
226+
227+
renderHistoryPending(reason) {
228+
if (!this.historyRows) {
229+
return;
230+
}
231+
const row = document.createElement("tr");
232+
row.append(
233+
this.createCell("not available"),
234+
this.createCell("current environment"),
235+
this.createCell("Health Check History"),
236+
this.createStatusCell("PENDING", reason),
237+
this.createCell("Safe health check history is not available."),
238+
);
239+
this.historyRows.replaceChildren(row);
240+
}
241+
242+
renderHealthCheckHistory(historyRows = []) {
243+
if (!this.historyRows) {
244+
return;
245+
}
246+
const rows = Array.isArray(historyRows) ? historyRows : [];
247+
if (!rows.length) {
248+
this.renderHistoryPending("Safe Admin System Health API returned no current-environment health check history rows.");
249+
return;
250+
}
251+
const fragment = document.createDocumentFragment();
252+
rows.forEach((historyRow) => {
253+
const row = document.createElement("tr");
254+
row.append(
255+
this.createCell(historyRow.checkedAt),
256+
this.createCell(historyRow.environmentName),
257+
this.createCell(historyRow.area),
258+
this.createStatusCell(historyRow.result || historyRow.status, historyRow.summary),
259+
this.createCell(historyRow.summary),
260+
);
261+
fragment.append(row);
262+
});
263+
this.historyRows.replaceChildren(fragment);
213264
}
214265

215266
runStorageDiagnostics() {
@@ -292,6 +343,7 @@ class AdminSystemHealthController {
292343
this.renderStartupDiagnostics(data?.localApiStartup || {});
293344
this.renderStorageStatus(data?.storageStatus || {});
294345
this.runStorageDiagnostics();
346+
this.renderHealthCheckHistory(data?.healthCheckHistory || []);
295347
this.renderRuntimeEnvironment(data?.runtimeEnvironment || {});
296348
} catch (error) {
297349
const message = error instanceof Error ? error.message : "Safe Admin System Health API is unavailable.";
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# PR_26175_CHARLIE_009 System Health Current R2 Health
2+
3+
## Scope
4+
5+
Team: Charlie
6+
7+
Purpose: Add current-environment Cloudflare R2 folder health only to Admin System Health.
8+
9+
## Changes
10+
11+
- Replaced the System Health Storage Health rows with current-environment R2 checks:
12+
- bucket connectivity
13+
- list
14+
- upload
15+
- read
16+
- delete
17+
- last checked
18+
- System Health storage diagnostics run against the current environment folder only:
19+
- Local: `/local`
20+
- DEV: `/dev`
21+
- IST: `/ist`
22+
- UAT: `/uat`
23+
- PRD: `/prd`
24+
- Preserved the separate Admin Infrastructure storage diagnostic path that uses the configured project prefix.
25+
- Updated focused API and Playwright tests.
26+
27+
## Architecture Constraint
28+
29+
PASS. R2 health checks target only the current deployment environment folder. No peer environment R2 folders are actively checked by System Health.
30+
31+
## Validation
32+
33+
- PASS: `node --check src/dev-runtime/server/local-api-router.mjs`
34+
- PASS: `node --check assets/theme-v2/js/admin-system-health.js`
35+
- PASS: `git diff --check`
36+
- PASS: `node --test tests/dev-runtime/AdminHealthOperations.test.mjs`
37+
- PASS: `npx playwright test tests/playwright/tools/AdminHealthOperationsPage.spec.mjs --workers=1 --reporter=line`
38+
39+
## Artifact
40+
41+
- `tmp/PR_26175_CHARLIE_009-system-health-current-r2-health_delta.zip`
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# PR_26175_CHARLIE_010 System Health History and Closeout
2+
3+
## Scope
4+
5+
Team: Charlie
6+
7+
Purpose: Add current-environment Health Check History and close out the System Health stacked expansion chain.
8+
9+
## Chain
10+
11+
- `PR_26175_CHARLIE_006-project-instructions-system-health-infrastructure`
12+
- Branch: `PR_26175_CHARLIE_006-project-instructions-system-health-infrastructure`
13+
- Commit: `c870b812faded383094fb13fc60ae9cfc7f14889`
14+
- Result: pushed to origin, not merged.
15+
- `PR_26175_CHARLIE_007-system-health-environment-identity`
16+
- Branch: `pr/26175-CHARLIE-007-system-health-environment-identity`
17+
- Commit: `6556e73efcd8ae13b51b56288416c88688e67634`
18+
- Draft PR: https://github.com/ToolboxAid/HTML-JavaScript-Gaming/pull/151
19+
- `PR_26175_CHARLIE_008-system-health-current-database-health`
20+
- Branch: `pr/26175-CHARLIE-008-system-health-current-database-health`
21+
- Commit: `2603aeb6e5a7ccca516051953cbd70a5a6c94c4b`
22+
- Draft PR: https://github.com/ToolboxAid/HTML-JavaScript-Gaming/pull/152
23+
- `PR_26175_CHARLIE_009-system-health-current-r2-health`
24+
- Branch: `pr/26175-CHARLIE-009-system-health-current-r2-health`
25+
- Commit: `9740705fbe73dffd43744ab66338f8e4a925eed4`
26+
- Draft PR: https://github.com/ToolboxAid/HTML-JavaScript-Gaming/pull/153
27+
- `PR_26175_CHARLIE_010-system-health-history-and-closeout`
28+
- Branch: `pr/26175-CHARLIE-010-system-health-history-and-closeout`
29+
- Commit and draft PR URL are assigned after this report is committed and pushed.
30+
31+
## Changes
32+
33+
- Added Health Check History to the Admin System Health page.
34+
- Health Check History is derived from the current deployment only:
35+
- Environment Summary
36+
- Database Health
37+
- Storage Health
38+
- Runtime Health
39+
- Warning and failure rows are generated only from current-environment health rows.
40+
- Creator sessions remain blocked from Admin System Health and do not trigger status or storage health requests.
41+
42+
## Governance
43+
44+
- PASS: System Health is one page per deployed environment.
45+
- PASS: Each deployment actively checks only itself.
46+
- PASS: The Environment Map remains a static reference for Local, DEV, IST, UAT, and PRD.
47+
- PASS: System Health does not actively check peer databases or peer R2 folders.
48+
- PASS: Cancelled initiatives remain not doing:
49+
- Environment Isolation & Developer Experience
50+
- multi-port workspace framework
51+
- Alpha/Beta/User runtime separation
52+
- runtime port management initiative
53+
54+
## Validation
55+
56+
- PASS: `node --check src/dev-runtime/server/local-api-router.mjs`
57+
- PASS: `node --check assets/theme-v2/js/admin-system-health.js`
58+
- PASS: `git diff --check`
59+
- PASS: `node --test tests/dev-runtime/AdminHealthOperations.test.mjs`
60+
- PASS: `node --test tests/dev-runtime/PublicEnvironmentConfig.test.mjs`
61+
- PASS: `npx playwright test tests/playwright/tools/AdminHealthOperationsPage.spec.mjs --workers=1 --reporter=line`
62+
63+
## Artifacts
64+
65+
- `tmp/PR_26175_CHARLIE_007-system-health-environment-identity_delta.zip`
66+
- `tmp/PR_26175_CHARLIE_008-system-health-current-database-health_delta.zip`
67+
- `tmp/PR_26175_CHARLIE_009-system-health-current-r2-health_delta.zip`
68+
- `tmp/PR_26175_CHARLIE_010-system-health-history-and-closeout_delta.zip`
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# PR_26175_CHARLIE_011 Branch Validation
2+
3+
## Start Gate
4+
5+
- PASS: Current branch was `pr/26175-CHARLIE-010-system-health-history-and-closeout`.
6+
- PASS: Worktree was clean before implementation.
7+
- PASS: Build remained on the current Charlie continuation branch.
8+
- PASS: No rebase was performed.
9+
- PASS: No new root branch was created.
10+
11+
## Final Branch Target
12+
13+
- Branch to push: `pr/26175-CHARLIE-010-system-health-history-and-closeout`
14+
- Expected draft PR to update: https://github.com/ToolboxAid/HTML-JavaScript-Gaming/pull/155
15+
- Merge status: no merge requested or performed.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# PR_26175_CHARLIE_011 Manual Validation Notes
2+
3+
## Admin Submenu Order
4+
5+
Visible labels after the change:
6+
7+
`Admin Tools`, `Analytics`, `Controls`, `Creators`, `DB Viewer`, `Environments`, `Game Migration`, `Infrastructure`, `Invites`, `Moderation`, `Operations`, `Platform Settings`, `Ratings`, `Responsibilities`, `Site Setup`, `System Health`, `Tool Votes`
8+
9+
## Route Preservation
10+
11+
The only source navigation reorder moved this existing item:
12+
13+
- `Creators`
14+
- path remained `admin/users.html`
15+
- route remained `admin-users`
16+
- label remained `Creators`
17+
18+
All other Admin navigation objects remained unchanged.
19+
20+
## Open-Page Check
21+
22+
The targeted Playwright navigation test collected every active Admin submenu href from the rendered submenu and requested each page through the local test server. Every response returned a status below HTTP 400.
23+
24+
## Duplicate Check
25+
26+
The targeted Playwright navigation test checks for duplicate rendered labels and duplicate active hrefs. The targeted unit script also checks duplicate labels and existing active paths.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# PR_26175_CHARLIE_011 Requirement Checklist
2+
3+
## Branch Rules
4+
5+
- PASS: Continued on `pr/26175-CHARLIE-010-system-health-history-and-closeout`.
6+
- PASS: Did not return to `main`.
7+
- PASS: Did not rebase.
8+
- PASS: Did not create a new root branch.
9+
- PASS: Hard stop conditions were checked before implementation.
10+
11+
## Objective
12+
13+
- PASS: Admin submenu entries are alphabetized by displayed text.
14+
15+
## Scope
16+
17+
- PASS: Located Admin submenu definition in `src/api/admin-owner-navigation.js`.
18+
- PASS: Sorted submenu entries by displayed text.
19+
- PASS: Preserved routes, URLs, IDs, CSS classes, Theme V2 styling, and existing renderer behavior.
20+
- PASS: Did not rename pages.
21+
- PASS: Did not rename folders.
22+
- PASS: Did not modify page content.
23+
- PASS: No unrelated cleanup.
24+
25+
## Validation
26+
27+
- PASS: Verified submenu labels are alphabetical.
28+
- PASS: Verified every Admin page path exists and every Admin submenu href opens below HTTP 400 in targeted Playwright.
29+
- PASS: Verified no duplicate labels or hrefs.
30+
- PASS: Ran affected Playwright navigation tests.
31+
- PASS: Ran affected targeted unit tests.
32+
33+
## Artifacts
34+
35+
- PASS: `docs_build/dev/reports/codex_review.diff`
36+
- PASS: `docs_build/dev/reports/codex_changed_files.txt`
37+
- PASS: `docs_build/dev/reports/PR_26175_CHARLIE_011-admin-submenu-alphabetical-order.md`
38+
- PASS: validation report
39+
- PASS: branch validation report
40+
- PASS: requirement checklist
41+
- PASS: manual validation notes
42+
- PASS: repository ZIP under `tmp/`

0 commit comments

Comments
 (0)