11import {
2- readAdminSystemHealthStatus
2+ readAdminSystemHealthStatus ,
3+ runAdminSystemHealthStorageConnectivityAction
34} from "../../../src/engine/api/admin-system-health-api-client.js" ;
45
6+ const STORAGE_CONNECTIVITY_ACTIONS = Object . freeze ( [
7+ "storage-list" ,
8+ "storage-write-test-object" ,
9+ "storage-read-test-object" ,
10+ "storage-delete-test-object" ,
11+ ] ) ;
12+
513class AdminSystemHealthController {
614 constructor ( root ) {
715 this . root = root ;
16+ this . connectionSummary = root . querySelector ( "[data-admin-system-health-connection-summary]" ) ;
17+ this . databaseStatusRows = root . querySelector ( "[data-admin-system-health-database-status-rows]" ) ;
818 this . details = root . querySelector ( "[data-admin-system-health-detail-rows]" ) ;
919 this . limits = root . querySelector ( "[data-admin-system-health-limit-rows]" ) ;
1020 this . overview = root . querySelector ( "[data-admin-system-health-overview-rows]" ) ;
1121 this . r2Readiness = root . querySelector ( "[data-admin-system-health-r2-readiness-rows]" ) ;
1222 this . status = root . querySelector ( "[data-admin-system-health-status]" ) ;
23+ this . storageActionButtons = Array . from ( root . querySelectorAll ( "[data-admin-system-health-storage-action]" ) ) ;
24+ this . storageConnectivityRows = root . querySelector ( "[data-admin-system-health-storage-connectivity-result-rows]" ) ;
25+ this . storageConnectivityStatus = root . querySelector ( "[data-admin-system-health-storage-connectivity-status]" ) ;
26+ this . storageStatusRows = root . querySelector ( "[data-admin-system-health-storage-status-rows]" ) ;
1327 this . summary = root . querySelector ( "[data-admin-system-health-summary-rows]" ) ;
1428 }
1529
1630 init ( ) {
17- if ( ! this . details || ! this . limits || ! this . overview || ! this . r2Readiness || ! this . status || ! this . summary ) {
31+ if ( ! this . connectionSummary || ! this . databaseStatusRows || ! this . details || ! this . limits || ! this . overview || ! this . r2Readiness || ! this . status || ! this . storageConnectivityRows || ! this . storageConnectivityStatus || ! this . storageStatusRows || ! this . summary ) {
1832 return ;
1933 }
34+ this . storageActionButtons . forEach ( ( button ) => {
35+ button . addEventListener ( "click" , ( ) => this . runStorageConnectivityAction ( button . dataset . adminSystemHealthStorageAction ) ) ;
36+ } ) ;
2037 this . load ( ) ;
2138 }
2239
2340 setStatus ( status , message ) {
2441 this . status . textContent = `${ status } : ${ message } ` ;
2542 }
2643
44+ setStorageConnectivityStatus ( status , message ) {
45+ this . storageConnectivityStatus . textContent = `${ status } : ${ message } ` ;
46+ }
47+
2748 createRow ( values ) {
2849 const row = document . createElement ( "tr" ) ;
2950 values . forEach ( ( value ) => {
@@ -57,6 +78,36 @@ class AdminSystemHealthController {
5778 ] ) ) ;
5879 }
5980
81+ renderConnectionSummary ( summary = { } ) {
82+ this . connectionSummary . replaceChildren ( ) ;
83+ [
84+ [ "Account" , summary . account ?. status || "unknown" , summary . account ?. configured === true ? "configured" : "not configured" ] ,
85+ [ "Product Data / Local DB" , summary . productData ?. status || "unknown" , summary . productData ?. configured === true ? "configured" : "not configured" ] ,
86+ [ "Project Asset Storage / R2" , summary . projectAssetStorage ?. status || "unknown" , summary . projectAssetStorage ?. configured === true ? "configured" : "not configured" ] ,
87+ [ "Environment Switching" , summary . environmentSwitching || "manual-env-change-and-server-restart" , "manual" ] ,
88+ [ "Secrets" , summary . secretsExposed === true ? "exposed" : "not exposed" , "read-only summary" ] ,
89+ ] . forEach ( ( row ) => {
90+ this . connectionSummary . append ( this . createRow ( row ) ) ;
91+ } ) ;
92+ }
93+
94+ renderDatabaseStatus ( databaseStatus = { } ) {
95+ const migrationCounts = databaseStatus . migrationCounts || { } ;
96+ const lastMigration = databaseStatus . lastMigration || { } ;
97+ this . databaseStatusRows . replaceChildren ( ) ;
98+ [
99+ [ "Connection Configured" , databaseStatus . configured === true ? "PASS" : "WARN" , databaseStatus . configured === true ? "yes" : "no" ] ,
100+ [ "Database Host" , databaseStatus . hostStatus || "WARN" , databaseStatus . host || "not configured" ] ,
101+ [ "Database Port" , databaseStatus . portStatus || "WARN" , databaseStatus . port ? String ( databaseStatus . port ) : "not configured" ] ,
102+ [ "Database Name" , databaseStatus . databaseNameStatus || "WARN" , databaseStatus . databaseName || "not configured" ] ,
103+ [ "SSL Mode" , databaseStatus . sslModeStatus || "WARN" , databaseStatus . sslMode || "not configured" ] ,
104+ [ "Migration Counts" , databaseStatus . migrationStatus || "WARN" , `DDL=${ migrationCounts . DDL || 0 } ; DML=${ migrationCounts . DML || 0 } ` ] ,
105+ [ "Last Migration" , databaseStatus . lastMigrationStatus || "WARN" , lastMigration . name && lastMigration . appliedAt ? `${ lastMigration . type || "unknown" } ${ lastMigration . name } at ${ lastMigration . appliedAt } ` : "not recorded" ] ,
106+ ] . forEach ( ( row ) => {
107+ this . databaseStatusRows . append ( this . createRow ( row ) ) ;
108+ } ) ;
109+ }
110+
60111 renderDetails ( rows ) {
61112 this . details . replaceChildren ( ) ;
62113 rows . forEach ( ( row ) => {
@@ -69,6 +120,20 @@ class AdminSystemHealthController {
69120 } ) ;
70121 }
71122
123+ renderStorageStatus ( storageStatus = { } ) {
124+ this . storageStatusRows . replaceChildren ( ) ;
125+ [
126+ [ "Storage Configured" , storageStatus . configured === true ? "PASS" : "WARN" , storageStatus . configured === true ? "yes" : "no" ] ,
127+ [ "Storage Endpoint" , storageStatus . endpointStatus || "WARN" , storageStatus . endpoint || "not configured" ] ,
128+ [ "Storage Bucket" , storageStatus . bucketStatus || "WARN" , storageStatus . bucket || "not configured" ] ,
129+ [ "Projects Prefix" , storageStatus . projectsPrefixStatus || "WARN" , storageStatus . projectsPrefix || "not configured" ] ,
130+ [ "Access Key" , storageStatus . accessKeyStatus || "WARN" , storageStatus . accessKeyConfigured === true ? "configured; value hidden" : "not configured" ] ,
131+ [ "Secret Key" , storageStatus . secretKeyStatus || "WARN" , storageStatus . secretKeyConfigured === true ? "configured; value hidden" : "not configured" ] ,
132+ ] . forEach ( ( row ) => {
133+ this . storageStatusRows . append ( this . createRow ( row ) ) ;
134+ } ) ;
135+ }
136+
72137 renderLimits ( rows ) {
73138 this . limits . replaceChildren ( ) ;
74139 rows . forEach ( ( row ) => {
@@ -96,28 +161,87 @@ class AdminSystemHealthController {
96161 } ) ;
97162 }
98163
164+ appendStorageConnectivityResult ( result = { } ) {
165+ this . storageConnectivityRows . prepend ( this . createRow ( [
166+ result . actionId || "storage-connectivity" ,
167+ result . status || "WARN" ,
168+ result . executed === true ? "yes" : "no" ,
169+ result . message || "No storage connectivity message returned." ,
170+ ] ) ) ;
171+ }
172+
173+ clearStorageConnectivityResults ( ) {
174+ this . storageConnectivityRows . replaceChildren ( ) ;
175+ }
176+
177+ runStorageConnectivityAction ( actionId ) {
178+ try {
179+ const result = runAdminSystemHealthStorageConnectivityAction ( actionId ) ;
180+ this . appendStorageConnectivityResult ( result ) ;
181+ this . setStorageConnectivityStatus ( result . status || "WARN" , result . message || "Storage connectivity action returned no message." ) ;
182+ return result ;
183+ } catch ( error ) {
184+ const result = {
185+ actionId : actionId || "storage-connectivity" ,
186+ executed : false ,
187+ message : error instanceof Error ? error . message : "Storage connectivity action failed." ,
188+ status : "FAIL" ,
189+ } ;
190+ this . appendStorageConnectivityResult ( result ) ;
191+ this . setStorageConnectivityStatus ( "FAIL" , result . message ) ;
192+ return result ;
193+ }
194+ }
195+
196+ runStartupStorageConnectivity ( ) {
197+ this . clearStorageConnectivityResults ( ) ;
198+ this . setStorageConnectivityStatus ( "SKIP" , "Storage connectivity startup running." ) ;
199+ const results = STORAGE_CONNECTIVITY_ACTIONS . map ( ( actionId ) => this . runStorageConnectivityAction ( actionId ) ) ;
200+ const failed = results . find ( ( result ) => result . status === "FAIL" ) ;
201+ const warned = results . find ( ( result ) => result . status === "WARN" ) ;
202+ const skipped = results . find ( ( result ) => result . status === "SKIP" ) ;
203+ if ( failed ) {
204+ this . setStorageConnectivityStatus ( "FAIL" , "Storage connectivity startup found one or more failures. Review result diagnostics." ) ;
205+ } else if ( warned ) {
206+ this . setStorageConnectivityStatus ( "WARN" , "Storage connectivity startup found one or more warnings. Review result diagnostics." ) ;
207+ } else if ( skipped ) {
208+ this . setStorageConnectivityStatus ( "SKIP" , "Storage connectivity startup skipped one or more actions. Review result diagnostics." ) ;
209+ } else {
210+ this . setStorageConnectivityStatus ( "PASS" , "Storage connectivity startup completed List, Write Test Object, Read Test Object, and Delete Test Object." ) ;
211+ }
212+ }
213+
99214 load ( ) {
100215 try {
101216 const payload = readAdminSystemHealthStatus ( ) ;
102217 this . renderSummary ( payload . summary || { } ) ;
103218 this . renderOverview ( Array . isArray ( payload . overview ) ? payload . overview : [ ] ) ;
219+ this . renderConnectionSummary ( payload . connectionSummary || { } ) ;
220+ this . renderDatabaseStatus ( payload . databaseStatus || { } ) ;
221+ this . renderStorageStatus ( payload . storageStatus || { } ) ;
104222 this . renderDetails ( Array . isArray ( payload . details ) ? payload . details : [ ] ) ;
105223 this . renderLimits ( Array . isArray ( payload . limits ) ? payload . limits : [ ] ) ;
106224 this . renderR2Readiness ( payload . r2Readiness || { } ) ;
107225 this . setStatus ( payload . status || "WARN" , payload . message || "Admin System Health loaded." ) ;
226+ this . runStartupStorageConnectivity ( ) ;
108227 } catch ( error ) {
109228 this . renderSummary ( { counts : { FAIL : 1 , PASS : 0 , WARN : 0 } , lastRefreshAt : "not available" , score : 0 , status : "FAIL" } ) ;
229+ this . renderConnectionSummary ( { } ) ;
230+ this . renderDatabaseStatus ( { } ) ;
110231 this . renderOverview ( [
111232 {
112233 area : "System Health" ,
113234 status : "FAIL" ,
114235 summary : error instanceof Error ? error . message : "Admin System Health unavailable." ,
115236 } ,
116237 ] ) ;
238+ this . renderStorageStatus ( { } ) ;
117239 this . renderDetails ( [ ] ) ;
118240 this . renderLimits ( [ ] ) ;
119241 this . renderR2Readiness ( { rows : [ ] } ) ;
242+ this . clearStorageConnectivityResults ( ) ;
120243 this . setStatus ( "FAIL" , error instanceof Error ? error . message : "Admin System Health unavailable." ) ;
244+ this . setStorageConnectivityStatus ( "SKIP" , "Storage connectivity startup skipped because Admin System Health failed to load." ) ;
121245 }
122246 }
123247}
0 commit comments