env[$key] ?? null; if ($value === false || $value === null || $value === '') { return $default; } return (string) $value; } public function dbPath(): string { return $this->get('MONITOR_DB_PATH', dirname(__DIR__) . '/data/monitor.sqlite'); } public function adminApiKey(): ?string { return $this->get('MONITOR_ADMIN_API_KEY'); } public function statusFilePath(): string { return $this->get('MONITOR_STATUS_FILE', dirname(__DIR__, 2) . '/status.txt'); } public function codebergApiBase(): string { return rtrim((string) $this->get('CODEBERG_API_BASE', 'https://codeberg.org/api/v1'), '/'); } public function codebergToken(): ?string { return $this->get('CODEBERG_TOKEN'); } /** * Secret des Codeberg-Webhooks (Repo-Einstellungen -> Webhooks). Ohne diesen Wert * weist POST /api/v1/webhooks/codeberg jede Anfrage ab, siehe Auth\WebhookAuthenticator. */ public function codebergWebhookSecret(): ?string { return $this->get('CODEBERG_WEBHOOK_SECRET'); } /** * Signaldatei zwischen Webhook-Endpunkt (api) und Sync-Dauerlaeufer (sync), siehe * Sync\SyncTrigger. Liegt per Default neben der Datenbank, weil sich beide Container * genau dieses Verzeichnis teilen. */ public function syncTriggerPath(): string { return $this->get('MONITOR_SYNC_TRIGGER_FILE', dirname(__DIR__) . '/data/sync-requested'); } /** * Abstand zwischen zwei periodischen Codeberg-Syncs (bin/sync-codeberg.php --watch). * Mit funktionierendem Webhook ist das nur noch das Sicherheitsnetz fuer verpasste * Zustellungen, darf also gross sein. */ public function syncIntervalSeconds(): int { return max(1, (int) $this->get('MONITOR_SYNC_INTERVAL', '600')); } public function deskCount(): int { return (int) $this->get('MONITOR_DESK_COUNT', '8'); } /** * Wie viele Polling-Runden der SSE-Endpoint (F13, StreamController) maximal offen bleibt, * bevor er die Verbindung schliesst - EventSource im Frontend verbindet danach automatisch * neu. Mit dem Default (30 * 2s) bleibt eine Verbindung ca. eine Minute offen. */ public function sseMaxIterations(): int { return (int) $this->get('MONITOR_SSE_MAX_ITERATIONS', '30'); } /** Wartezeit zwischen zwei Aenderungs-Checks des SSE-Endpoints in Sekunden. */ public function ssePollIntervalSeconds(): int { return (int) $this->get('MONITOR_SSE_POLL_INTERVAL', '2'); } }