Skip to content

Commit d69a02c

Browse files
committed
Wire admin R2 diagnostics runtime
1 parent 297108b commit d69a02c

10 files changed

Lines changed: 638 additions & 115 deletions

admin/system-health.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,11 @@ <h2 id="admin-system-health-title">System Health Tables</h2>
105105
</tr>
106106
</thead>
107107
<tbody>
108-
<tr><td>Bucket</td><td>Configured bucket placeholder</td><td data-health-status="PENDING" 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>
109-
<tr><td>List</td><td>Objects prefix</td><td data-health-status="PENDING" 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>
110-
<tr><td>Read</td><td>Health object</td><td data-health-status="PENDING" 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>
111-
<tr><td>Write</td><td>Health object</td><td data-health-status="PENDING" 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>
112-
<tr><td>Delete</td><td>Health object</td><td data-health-status="PENDING" 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>
108+
<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>
109+
<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>
110+
<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>
111+
<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>
112+
<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>
113113
</tbody>
114114
</table>
115115
</div>

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

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1-
import { readAdminSystemHealthStatus } from "../../../src/api/admin-system-health-api-client.js";
1+
import {
2+
readAdminSystemHealthStatus,
3+
runAdminSystemHealthStorageConnectivityAction,
4+
} from "../../../src/api/admin-system-health-api-client.js";
25

36
const STATUS_VALUES = Object.freeze(["PASS", "WARN", "FAIL", "PENDING", "INFO", "SKIP"]);
7+
const STORAGE_DIAGNOSTIC_ACTIONS = Object.freeze([
8+
Object.freeze({ actionId: "storage-list", key: "list" }),
9+
Object.freeze({ actionId: "storage-write-test-object", key: "write" }),
10+
Object.freeze({ actionId: "storage-read-test-object", key: "read" }),
11+
Object.freeze({ actionId: "storage-delete-test-object", key: "delete" }),
12+
]);
413

514
function asText(value, fallback = "not available") {
615
const text = String(value ?? "").trim();
@@ -28,10 +37,18 @@ class AdminSystemHealthController {
2837
node.dataset.adminSystemHealthDbStatus,
2938
node,
3039
]));
40+
this.storageValues = new Map(Array.from(root.querySelectorAll("[data-admin-system-health-storage-value]")).map((node) => [
41+
node.dataset.adminSystemHealthStorageValue,
42+
node,
43+
]));
44+
this.storageStatuses = new Map(Array.from(root.querySelectorAll("[data-admin-system-health-storage-status]")).map((node) => [
45+
node.dataset.adminSystemHealthStorageStatus,
46+
node,
47+
]));
3148
}
3249

3350
init() {
34-
if (!this.dbValues.size || !this.dbStatuses.size || document.querySelector("[data-session-access-blocked='admin']") || window.GameFoundrySessionGuard?.blocked === true) {
51+
if ((!this.dbValues.size && !this.storageValues.size) || document.querySelector("[data-session-access-blocked='admin']") || window.GameFoundrySessionGuard?.blocked === true) {
3552
return;
3653
}
3754
this.load();
@@ -44,8 +61,24 @@ class AdminSystemHealthController {
4461
}
4562
}
4663

64+
setStorageValue(key, value, fallback) {
65+
const node = this.storageValues.get(key);
66+
if (node) {
67+
node.textContent = asText(value, fallback);
68+
}
69+
}
70+
4771
setStatus(key, status, reason = "") {
4872
const node = this.dbStatuses.get(key);
73+
this.setStatusNode(node, status, reason);
74+
}
75+
76+
setStorageStatus(key, status, reason = "") {
77+
const node = this.storageStatuses.get(key);
78+
this.setStatusNode(node, status, reason);
79+
}
80+
81+
setStatusNode(node, status, reason = "") {
4982
if (!node) {
5083
return;
5184
}
@@ -66,6 +99,13 @@ class AdminSystemHealthController {
6699
["host", "database", "migration", "connection"].forEach((key) => {
67100
this.setStatus(key, "PENDING", reason);
68101
});
102+
this.renderStoragePending(reason);
103+
}
104+
105+
renderStoragePending(reason) {
106+
["bucket", "list", "read", "write", "delete"].forEach((key) => {
107+
this.setStorageStatus(key, "PENDING", reason);
108+
});
69109
}
70110

71111
renderPostgresStatus(databaseStatus = {}) {
@@ -90,6 +130,42 @@ class AdminSystemHealthController {
90130
this.setStatus("connection", databaseStatus.status, connectionReason);
91131
}
92132

133+
renderStorageStatus(storageStatus = {}) {
134+
const reason = storageStatus.message || "Cloudflare R2 configuration status returned by the safe Admin System Health API.";
135+
this.setStorageValue("bucket", storageStatus.bucket, "not configured");
136+
this.setStorageStatus("bucket", storageStatus.bucketStatus || storageStatus.status, reason);
137+
}
138+
139+
storageResultTarget(result = {}) {
140+
if (typeof result.keysListed === "number" && result.actionId === "storage-list") {
141+
return `${result.keysListed} object(s) under ${asText(result.projectsPrefix, "configured prefix")}`;
142+
}
143+
return result.testObjectKey || result.projectsPrefix || result.message || "diagnostic target unavailable";
144+
}
145+
146+
renderStorageResult(key, result = {}) {
147+
if (result?.secretsExposed === true || result?.secretEditingAllowed === true) {
148+
this.setStorageStatus(key, "PENDING", "Safe R2 diagnostic response was blocked because it exposed secret controls.");
149+
return;
150+
}
151+
this.setStorageValue(key, this.storageResultTarget(result));
152+
this.setStorageStatus(key, result.status, result.message || "R2 diagnostic returned without a message.");
153+
}
154+
155+
runStorageDiagnostics() {
156+
STORAGE_DIAGNOSTIC_ACTIONS.forEach(({ key }) => {
157+
this.setStorageStatus(key, "PENDING", "R2 diagnostic is running through the safe Admin System Health API.");
158+
});
159+
STORAGE_DIAGNOSTIC_ACTIONS.forEach(({ actionId, key }) => {
160+
try {
161+
this.renderStorageResult(key, runAdminSystemHealthStorageConnectivityAction(actionId));
162+
} catch (error) {
163+
const message = error instanceof Error ? error.message : "Safe R2 diagnostic API is unavailable.";
164+
this.setStorageStatus(key, "PENDING", message);
165+
}
166+
});
167+
}
168+
93169
load() {
94170
try {
95171
const data = readAdminSystemHealthStatus();
@@ -98,6 +174,8 @@ class AdminSystemHealthController {
98174
return;
99175
}
100176
this.renderPostgresStatus(data?.databaseStatus || {});
177+
this.renderStorageStatus(data?.storageStatus || {});
178+
this.runStorageDiagnostics();
101179
} catch (error) {
102180
const message = error instanceof Error ? error.message : "Safe Admin System Health API is unavailable.";
103181
this.renderPending(message);
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# PR_26171_GAMMA_015 Instruction Compliance Checklist
2+
3+
## Required Reads
4+
5+
- PASS: Read `docs_build/dev/PROJECT_INSTRUCTIONS.md`.
6+
- PASS: Read `docs_build/dev/PROJECT_MULTI_PC.txt`.
7+
- PASS: Read exact target file `admin/system-health.html`.
8+
- PASS: Read existing runtime target `assets/theme-v2/js/admin-system-health.js`.
9+
- PASS: Read existing API client `src/api/admin-system-health-api-client.js`.
10+
- PASS: Read existing safe server R2 contracts in `src/dev-runtime/server/local-api-router.mjs`.
11+
- PASS: Read existing target route test `tests/playwright/tools/AdminHealthOperationsPage.spec.mjs`.
12+
13+
## Ownership And Branch
14+
15+
- PASS: Queued PR name includes TEAM token `GAMMA`.
16+
- PASS: TEAM ownership was verified against `PROJECT_MULTI_PC.txt`.
17+
- PASS: User explicitly directed continuation on `team/GAMMA/admin`.
18+
- PASS: Verified clean status and local/origin sync `0 0` after PR014 before PR015 edits.
19+
- PASS: User explicitly directed updating existing draft PR #36 and not creating a separate PR015 GitHub PR.
20+
- PASS: Work remained within the Admin diagnostics/runtime scope.
21+
22+
## Scope Compliance
23+
24+
- PASS: Wired Admin System Health Cloudflare R2 diagnostics.
25+
- PASS: Used existing safe R2 health/check logic.
26+
- PASS: Showed bucket configured state.
27+
- PASS: Showed list check.
28+
- PASS: Showed read check.
29+
- PASS: Showed write check.
30+
- PASS: Showed delete check.
31+
- PASS: Did not expose credentials.
32+
- PASS: Did not add new persistence code.
33+
- PASS: Did not introduce SQLite.
34+
- PASS: Preserved Theme V2 only.
35+
- PASS: Preserved R2-only storage direction.
36+
- PASS: Added reason text for every non-`PASS` status rendered by PR015 code.
37+
38+
## Validation Compliance
39+
40+
- PASS: Ran `git diff --check`.
41+
- PASS: Ran targeted Admin System Health source validation.
42+
- PASS: Ran targeted Admin System Health Playwright route/behavior validation.
43+
- PASS: Verified no SQLite was introduced in the changed page/runtime module.
44+
- PASS: Verified no secret values are exposed by the page behavior.
45+
- PASS: Verified every non-`PASS` status has reason text.
46+
- PASS: Produced Playwright V8 coverage notes for changed runtime JavaScript.
47+
- PASS: Did not run samples.
48+
49+
## Reports And Packaging
50+
51+
- PASS: Created queued-scope PR report.
52+
- PASS: Created manual validation notes.
53+
- PASS: Created instruction compliance checklist.
54+
- PASS: Generate `codex_review.diff` after scoped changes.
55+
- PASS: Generate `codex_changed_files.txt` after scoped changes.
56+
- PASS: Create repo-structured delta ZIP under `tmp/PR_26171_GAMMA_015-admin-r2-diagnostics-runtime_delta.zip`.
57+
- PASS: Verify no report remains modified after ZIP creation.
58+
59+
## Merge Control
60+
61+
- PASS: No merge performed.
62+
- PASS: Owner-controlled EOD merge approval remains required.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# PR_26171_GAMMA_015 Manual Validation Notes
2+
3+
## Manual Review Notes
4+
5+
- Reviewed `admin/system-health.html` after adding R2 diagnostics hooks.
6+
- Confirmed Storage Health remains labeled Cloudflare R2.
7+
- Confirmed the page shows bucket configured, list, read, write, and delete check rows.
8+
- Confirmed the runtime module calls existing safe R2 connectivity actions.
9+
- Confirmed no storage action buttons were added.
10+
- Confirmed no client-side storage credentials, access keys, secret keys, tokens, or secret values are rendered.
11+
- Confirmed non-`PASS` status cells include hover/accessibility reason text.
12+
- Confirmed no SQLite runtime or persistence code was introduced.
13+
14+
## Validation Notes
15+
16+
- `git diff --check` passed.
17+
- Targeted Admin System Health source validation passed.
18+
- Targeted Admin System Health Playwright route/behavior spec passed with 3 tests.
19+
- Playwright V8 coverage report includes `assets/theme-v2/js/admin-system-health.js`.
20+
- Samples were not run because samples are outside this queued Admin diagnostics scope.
21+
22+
## User Review Focus
23+
24+
- Review whether automatic R2 list/write/read/delete diagnostics on Admin page load are the desired operational behavior.
25+
- Review R2 status wording when storage is not configured in local `.env`.
26+
- Confirm owner approval before any EOD merge.
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# PR_26171_GAMMA_015-admin-r2-diagnostics-runtime
2+
3+
## Summary
4+
5+
Queued scope 015 was applied to the existing draft PR #36 workstream branch:
6+
7+
- PR #36: `PR_26171_GAMMA_011-admin-system-health-foundation`
8+
- Branch: `team/GAMMA/admin`
9+
10+
This queued scope wires Admin System Health Cloudflare R2 diagnostics to existing safe status and storage connectivity contracts without creating a separate GitHub PR.
11+
12+
## Scope Evidence
13+
14+
- Added stable R2 table hooks in `admin/system-health.html`.
15+
- Extended `assets/theme-v2/js/admin-system-health.js` to render safe R2 bucket configuration.
16+
- Used existing `runAdminSystemHealthStorageConnectivityAction()` for R2 list/write/read/delete diagnostics.
17+
- Displayed bucket configured state, list check, read check, write check, and delete check.
18+
- Kept the table-first Admin System Health presentation.
19+
- Did not add storage action buttons.
20+
- Did not expose endpoint credentials, access keys, secret keys, tokens, or secret values.
21+
- Did not introduce SQLite, new persistence code, new storage services, inline CSS, or inline JavaScript.
22+
23+
## Safe Backend Contract
24+
25+
- Existing safe status contract found: `GET /api/admin/system-health/status`.
26+
- Existing safe storage action contract found: `POST /api/admin/system-health/storage-connectivity-action`.
27+
- Existing action IDs used:
28+
- `storage-list`
29+
- `storage-write-test-object`
30+
- `storage-read-test-object`
31+
- `storage-delete-test-object`
32+
- The existing backend returns `secretEditingAllowed: false` and `secretsExposed: false`; the page keeps any unsafe response as `PENDING` instead of rendering it.
33+
- The write/read/delete checks use the existing temporary storage connectivity object contract and do not add new persistence code.
34+
35+
## Instruction Start Gate
36+
37+
- Instructions read: PASS
38+
- `docs_build/dev/PROJECT_INSTRUCTIONS.md`: read before queued edits
39+
- `docs_build/dev/PROJECT_MULTI_PC.txt`: read before queued edits
40+
- Current branch: `team/GAMMA/admin`
41+
- Clean status before PR015 edits: PASS
42+
- Local/origin sync before PR015 edits: PASS (`0 0`)
43+
- TEAM token: `GAMMA`
44+
- TEAM ownership: PASS by explicit Master Control/user assignment for diagnostics/admin workstream
45+
- Implementation path: `admin/system-health.html`, `assets/theme-v2/js/admin-system-health.js`, `tests/playwright/tools/AdminHealthOperationsPage.spec.mjs`
46+
- Existing draft PR target: PR #36
47+
- Separate PR creation: SKIP by explicit user instruction
48+
- Merge: SKIP, owner-controlled EOD approval remains required
49+
50+
## Validation
51+
52+
- PASS: `git diff --check`
53+
- PASS: targeted source check found no SQLite references in `admin/system-health.html` or `assets/theme-v2/js/admin-system-health.js`.
54+
- PASS: targeted source check verified every static non-`PASS` status in `admin/system-health.html` has `title` or `aria-label` reason text.
55+
- PASS: targeted source check verified R2 diagnostics use the existing safe API client action.
56+
- PASS: targeted source check found no direct secret value fields in the Admin System Health runtime module.
57+
- PASS: Playwright verified the admin page calls `/api/admin/system-health/status`.
58+
- PASS: Playwright verified the admin page calls `/api/admin/system-health/storage-connectivity-action` four times for Admin sessions.
59+
- PASS: Playwright verified non-admin sessions do not call status or storage connectivity endpoints.
60+
- PASS: Playwright verified no full Postgres URL or configured secret values are exposed.
61+
- PASS: `npx playwright test tests/playwright/tools/AdminHealthOperationsPage.spec.mjs --config=codex_playwright_system_chrome.config.cjs --project=playwright` (3 passed)
62+
- PASS: Playwright V8 coverage report lists changed runtime JS file `assets/theme-v2/js/admin-system-health.js`.
63+
64+
## Playwright Coverage Notes
65+
66+
- `docs_build/dev/reports/playwright_v8_coverage_report.txt`
67+
- `docs_build/dev/reports/coverage_changed_js_guardrail.txt`
68+
- Changed runtime JS coverage: `(92%) assets/theme-v2/js/admin-system-health.js - executed lines 174/174; executed functions 24/26`
69+
- Coverage is advisory only; no threshold is enforced.
70+
71+
## Skipped Lanes
72+
73+
- Full samples smoke: skipped by request because this Admin diagnostics runtime PR does not touch samples.
74+
- Full Playwright suite: skipped because the targeted Admin System Health route/behavior spec covers the changed page and runtime module.
75+
- Runtime environment dynamic visibility validation: skipped because PR016 owns runtime environment wiring.
76+
- Database-only diagnostics validation beyond the existing targeted Admin page spec: skipped because PR014 already covered Postgres wiring and PR015 touched only R2 diagnostics.
77+
78+
## Required Reports
79+
80+
- `docs_build/dev/reports/codex_review.diff`
81+
- `docs_build/dev/reports/codex_changed_files.txt`
82+
- `docs_build/dev/reports/PR_26171_GAMMA_015-admin-r2-diagnostics-runtime.md`
83+
- `docs_build/dev/reports/PR_26171_GAMMA_015-admin-r2-diagnostics-runtime-manual-validation-notes.md`
84+
- `docs_build/dev/reports/PR_26171_GAMMA_015-admin-r2-diagnostics-runtime-instruction-compliance-checklist.md`
85+
- `docs_build/dev/reports/playwright_v8_coverage_report.txt`
86+
- `docs_build/dev/reports/coverage_changed_js_guardrail.txt`
87+
88+
## ZIP Artifact
89+
90+
- `tmp/PR_26171_GAMMA_015-admin-r2-diagnostics-runtime_delta.zip`
91+
- Generated from the current `team/GAMMA/admin` branch delta against the branch merge-base with `origin/main`, preserving the existing PR #36 workstream context.
92+
93+
## EOD Approval
94+
95+
No merge was performed. EOD merge remains owner-controlled and requires explicit approval.

0 commit comments

Comments
 (0)