$settings Effective settings array (the same array that built the container). */ public function __construct(private readonly array $settings) { } public function show(ServerRequestInterface $request, ResponseInterface $response): ResponseInterface { return self::json($response, 200, [ 'sections' => $this->sections(), ]); } /** * @return array> */ private function sections(): array { $db = $this->settings['db'] ?? []; $geoip = $this->settings['geoip'] ?? []; return [ 'app' => [ 'APP_ENV' => $this->settings['app_env'] ?? null, 'LOG_LEVEL' => $this->levelName(), 'APP_SECRET' => self::mask((string) ($this->settings['app_secret'] ?? '')), 'UI_ORIGIN' => $this->settings['ui_origin'] ?? null, ], 'database' => [ 'DB_DRIVER' => $db['driver'] ?? null, 'DB_SQLITE_PATH' => $db['sqlite_path'] ?? null, 'DB_MYSQL_HOST' => $db['mysql_host'] ?? null, 'DB_MYSQL_PORT' => $db['mysql_port'] ?? null, 'DB_MYSQL_DATABASE' => $db['mysql_database'] ?? null, 'DB_MYSQL_USERNAME' => $db['mysql_username'] ?? null, 'DB_MYSQL_PASSWORD' => self::mask((string) ($db['mysql_password'] ?? '')), ], 'auth' => [ 'UI_SERVICE_TOKEN' => self::previewToken((string) ($this->settings['ui_service_token'] ?? '')), 'INTERNAL_JOB_TOKEN' => self::mask((string) ($this->settings['internal_job_token'] ?? '')), 'OIDC_DEFAULT_ROLE' => $this->oidcDefaultRoleName(), ], 'reputation' => [ 'SCORE_REPORT_HARD_CUTOFF_DAYS' => $this->settings['score_hard_cutoff_days'] ?? null, 'SCORE_RECOMPUTE_INTERVAL_SECONDS' => $this->settings['score_recompute_interval_seconds'] ?? null, 'API_RATE_LIMIT_PER_SECOND' => $this->settings['rate_limit_per_second'] ?? null, 'CIDR_EVALUATOR_TTL_SECONDS' => $this->settings['cidr_evaluator_ttl_seconds'] ?? null, 'BLOCKLIST_CACHE_TTL_SECONDS' => $this->settings['blocklist_cache_ttl_seconds'] ?? null, ], 'jobs' => [ 'JOB_RECOMPUTE_MAX_RUNTIME_SECONDS' => $this->settings['job_recompute_max_runtime_seconds'] ?? null, 'JOB_RECOMPUTE_MAX_ROWS_PER_TICK' => $this->settings['job_recompute_max_rows_per_tick'] ?? null, 'JOB_AUDIT_RETENTION_DAYS' => $this->settings['job_audit_retention_days'] ?? null, 'JOB_GEOIP_REFRESH_INTERVAL_DAYS' => $geoip['refresh_interval_days'] ?? null, ], 'geoip' => [ 'GEOIP_ENABLED' => $geoip['enabled'] ?? null, 'GEOIP_PROVIDER' => $geoip['provider'] ?? null, 'GEOIP_COUNTRY_DB' => $geoip['country_db'] ?? null, 'GEOIP_ASN_DB' => $geoip['asn_db'] ?? null, 'MAXMIND_LICENSE_KEY' => self::mask((string) ($geoip['maxmind_license_key'] ?? '')), 'IPINFO_TOKEN' => self::mask((string) ($geoip['ipinfo_token'] ?? '')), ], ]; } private static function mask(string $value): string { return $value === '' ? '' : '***'; } /** * Token preview: empty stays empty so misconfiguration is visible; * present values show first 8 + ellipsis. */ private static function previewToken(string $value): string { if ($value === '') { return ''; } return substr($value, 0, 8) . '...'; } private function levelName(): ?string { $level = $this->settings['log_level'] ?? null; if ($level instanceof \Monolog\Level) { return $level->getName(); } return is_string($level) ? $level : null; } private function oidcDefaultRoleName(): ?string { $role = $this->settings['oidc_default_role'] ?? null; if ($role === null) { return 'none'; } if ($role instanceof \App\Domain\Auth\Role) { return $role->value; } return is_string($role) ? $role : null; } }