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>
121 lines
3.8 KiB
PHP
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');
|
|
}
|
|
}
|