@@ -904,6 +904,13 @@ function localApiStartupUrlDisplay(value, fallback = "not configured") {
904904 }
905905}
906906
907+ function localApiStartupPortStatus ( port ) {
908+ if ( port === "invalid URL" ) {
909+ return "FAIL" ;
910+ }
911+ return port === "not configured" ? "WARN" : "PASS" ;
912+ }
913+
907914function localApiStartupBindTarget ( env = process . env ) {
908915 const host = String ( env . GAMEFOUNDRY_LOCAL_API_HOST || LOCAL_API_STARTUP_DEFAULT_HOST ) . trim ( ) || LOCAL_API_STARTUP_DEFAULT_HOST ;
909916 const port = String ( env . GAMEFOUNDRY_LOCAL_API_PORT || LOCAL_API_STARTUP_DEFAULT_PORT ) . trim ( ) || LOCAL_API_STARTUP_DEFAULT_PORT ;
@@ -916,11 +923,57 @@ function localApiStartupBindTarget(env = process.env) {
916923 } ;
917924}
918925
926+ function localApiStartupDatabaseMode ( env = process . env ) {
927+ const databaseStatus = databaseConfigStatus ( env ) ;
928+ if ( databaseStatus . hostStatus === "FAIL" || databaseStatus . databaseNameStatus === "FAIL" ) {
929+ return {
930+ reason : "GAMEFOUNDRY_DATABASE_URL is present but is not a valid Postgres URL." ,
931+ status : "FAIL" ,
932+ value : "invalid database URL" ,
933+ } ;
934+ }
935+ if ( ! databaseStatus . configured ) {
936+ return {
937+ reason : "GAMEFOUNDRY_DATABASE_URL is not configured for the Local API startup report." ,
938+ status : "WARN" ,
939+ value : "not configured" ,
940+ } ;
941+ }
942+ return {
943+ reason : "GAMEFOUNDRY_DATABASE_URL is configured with a Postgres protocol; credentials remain hidden." ,
944+ status : "PASS" ,
945+ value : "Postgres" ,
946+ } ;
947+ }
948+
949+ function localApiStartupStorageStatus ( env = process . env ) {
950+ const storageConfig = loadStorageConfig ( env ) ;
951+ if ( storageConfig . configured ) {
952+ return {
953+ reason : `Cloudflare R2 configuration is present for bucket ${ storageConfig . safe . bucket } and prefix ${ storageConfig . safe . projectsPrefix } ; credential values remain hidden.` ,
954+ status : "PASS" ,
955+ value : "configured" ,
956+ } ;
957+ }
958+ const issue = storageConfig . validationError
959+ || `missing ${ storageConfig . missingKeys ?. join ( ", " ) || "storage configuration" } ` ;
960+ return {
961+ reason : `Cloudflare R2 storage is not fully configured: ${ issue } .` ,
962+ status : "WARN" ,
963+ value : "not configured" ,
964+ } ;
965+ }
966+
919967function systemHealthLocalApiStartupDiagnostics ( env = process . env ) {
920968 const bindTarget = localApiStartupBindTarget ( env ) ;
921969 const configuredApiUrl = String ( env . GAMEFOUNDRY_API_URL || "" ) . trim ( ) ;
922970 const derivedApiUrl = `http://${ bindTarget . value } /api` ;
923971 const siteUrl = String ( env . GAMEFOUNDRY_SITE_URL || "" ) . trim ( ) ;
972+ const apiUrlDisplay = localApiStartupUrlDisplay ( configuredApiUrl || derivedApiUrl ) ;
973+ const siteUrlDisplay = localApiStartupUrlDisplay ( siteUrl ) ;
974+ const siteUrlPort = localApiStartupPortFromUrl ( siteUrl ) ;
975+ const databaseMode = localApiStartupDatabaseMode ( env ) ;
976+ const storageStatus = localApiStartupStorageStatus ( env ) ;
924977 const rows = [
925978 {
926979 field : "Approved diagnostics format" ,
@@ -934,6 +987,18 @@ function systemHealthLocalApiStartupDiagnostics(env = process.env) {
934987 status : "PASS" ,
935988 value : "masked and redacted" ,
936989 } ,
990+ {
991+ field : "Environment variable order" ,
992+ reason : "Runtime .env keys are sorted alphabetically before startup diagnostics are printed." ,
993+ status : "PASS" ,
994+ value : "alphabetical" ,
995+ } ,
996+ {
997+ field : "Secret masking markers" ,
998+ reason : "Startup diagnostics mask variables whose keys contain PASSWORD, SECRET, TOKEN, KEY, SERVICE_ROLE, or JWT." ,
999+ status : "PASS" ,
1000+ value : "PASSWORD, SECRET, TOKEN, KEY, SERVICE_ROLE, JWT" ,
1001+ } ,
9371002 {
9381003 field : "Configured startup bind target" ,
9391004 reason : bindTarget . status === "PASS"
@@ -942,6 +1007,28 @@ function systemHealthLocalApiStartupDiagnostics(env = process.env) {
9421007 status : bindTarget . status ,
9431008 value : bindTarget . value ,
9441009 } ,
1010+ {
1011+ field : "Local API URL" ,
1012+ reason : configuredApiUrl
1013+ ? "GAMEFOUNDRY_API_URL is configured and displayed without URL credentials."
1014+ : "GAMEFOUNDRY_API_URL is not configured; startup diagnostics derive /api from the bind target." ,
1015+ status : apiUrlDisplay === "invalid URL" ? "FAIL" : "PASS" ,
1016+ value : apiUrlDisplay ,
1017+ } ,
1018+ {
1019+ field : "Local site URL" ,
1020+ reason : siteUrl
1021+ ? "GAMEFOUNDRY_SITE_URL is available for startup diagnostics."
1022+ : "GAMEFOUNDRY_SITE_URL is not configured for the Local API startup report." ,
1023+ status : siteUrl ? ( siteUrlDisplay === "invalid URL" ? "FAIL" : "PASS" ) : "WARN" ,
1024+ value : siteUrlDisplay ,
1025+ } ,
1026+ {
1027+ field : "Local site URL port" ,
1028+ reason : "Port is derived from GAMEFOUNDRY_SITE_URL for display only." ,
1029+ status : localApiStartupPortStatus ( siteUrlPort ) ,
1030+ value : siteUrlPort ,
1031+ } ,
9451032 {
9461033 field : "Configured site URL" ,
9471034 reason : siteUrl
@@ -956,14 +1043,26 @@ function systemHealthLocalApiStartupDiagnostics(env = process.env) {
9561043 ? "GAMEFOUNDRY_API_URL is configured and displayed without URL credentials."
9571044 : "GAMEFOUNDRY_API_URL is not configured; startup diagnostics derive /api from the bind target." ,
9581045 status : "PASS" ,
959- value : localApiStartupUrlDisplay ( configuredApiUrl || derivedApiUrl ) ,
1046+ value : apiUrlDisplay ,
9601047 } ,
9611048 {
9621049 field : "Configured API URL port" ,
9631050 reason : "Port is derived from the configured or startup-derived API URL for display only." ,
9641051 status : "PASS" ,
9651052 value : localApiStartupPortFromUrl ( configuredApiUrl || derivedApiUrl ) ,
9661053 } ,
1054+ {
1055+ field : "Database mode" ,
1056+ reason : databaseMode . reason ,
1057+ status : databaseMode . status ,
1058+ value : databaseMode . value ,
1059+ } ,
1060+ {
1061+ field : "Storage status" ,
1062+ reason : storageStatus . reason ,
1063+ status : storageStatus . status ,
1064+ value : storageStatus . value ,
1065+ } ,
9671066 {
9681067 field : "Configurable multiple runtime ports" ,
9691068 reason : "Configurable multiple runtime ports are explicitly deferred/cancelled for this PR." ,
0 commit comments