monitor/api/src/Config.php
fds2610-bot 79ccef099d Hooks-KPI im KPI-Monitor ergaenzen (Issue #38)
Die im Issue beschriebene Event-Anzeige (neuestes Event oben, aeltere
schieben nach, Ueberschuss wird abgeschnitten) ist bereits seit Phase 4
umgesetzt (buildEventLines()/MAX_EVENT_LINES, F5) und braucht keine
Aenderung.

Neu ist die vierte, immer sichtbare KPI-Kachel ganz rechts im
KPI-Monitor: "Hooks: n" zaehlt die seit dem letzten Start des
api-Containers empfangenen Webhooks. Der Zaehler ist dateibasiert
(Metrics\HookCounter), weil der eingebaute PHP-Server jede Anfrage als
eigene Skriptausfuehrung behandelt; docker/entrypoint.sh setzt die Datei
beim Start des api-Containers (nicht des sync-Containers) zurueck. Neuer
oeffentlicher Endpunkt GET /api/v1/webhooks/stats liefert den Stand,
das Frontend haengt ihn als eigene Kachel an (buildHooksKpi()) und
reserviert ihr dafuer eine feste Kachel statt sie der MAX_KPI_TILES-
Kappung der uebrigen KPIs zu unterwerfen. Der Wert fliesst zusaetzlich
in den SSE-Fingerabdruck (ChangeFeed) ein.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-04 13:18:05 +02:00

121 lines
3.8 KiB
PHP

<?php
declare(strict_types=1);
namespace Monitor\Api;
/**
* Liest die Konfiguration ausschliesslich aus Umgebungsvariablen (12-factor-Stil),
* damit die Docker-Compose-Umgebung ohne zusaetzliche Config-Dateien auskommt.
*
* Die Umgebung wird ausschliesslich in fromEnvironment() eingesammelt; get() greift nie
* selbst auf getenv() zu. Damit ist eine mit explizitem Array gebaute Config in Tests
* vollstaendig gekapselt und uebernimmt keine Werte (z.B. CODEBERG_TOKEN) aus der
* Umgebung des laufenden Prozesses.
*/
final class Config
{
public function __construct(private readonly array $env = [])
{
}
public static function fromEnvironment(): self
{
return new self($_ENV + $_SERVER + (getenv() ?: []));
}
private function get(string $key, ?string $default = null): ?string
{
$value = $this->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');
}
/**
* Zaehldatei fuer den Hooks-KPI (Issue #38, Metrics\HookCounter). Liegt per Default wie
* syncTriggerPath() im Datenverzeichnis, wird aber ausschliesslich vom api-Container
* geschrieben/zurueckgesetzt (siehe docker/entrypoint.sh).
*/
public function hooksCounterPath(): string
{
return $this->get('MONITOR_HOOKS_COUNTER_FILE', dirname(__DIR__) . '/data/hooks-count');
}
}