monitor/api/src/Config.php
fds2610-bot d72c945a61 Codeberg-Projekte des Sync ueber ein Modal am PO konfigurierbar machen
Klick auf den PO oeffnet ein Modal, in dem sich die Projekte (Owner/Repo +
Farbe), aus denen der Codeberg-Sync liest, hinzufuegen, bearbeiten und
loeschen lassen - Icon-Schaltflaechen mit Tooltip statt Textbuttons. Die
Projektliste war bisher eine Umgebungsvariable (CODEBERG_REPOS, Issue #26)
und wandert dafuer in eine neue Tabelle repo_configs (Migration
003_repo_configs.sql, geseedet mit den bisherigen drei Default-Projekten);
CodebergSyncService liest sie ueber RepoConfigRepository bei jedem Lauf
frisch ein, damit eine Aenderung im laufenden --watch-Prozess ohne Neustart
wirkt. Neue Endpunkte GET/POST/PATCH/DELETE /api/v1/repo-configs, Schreiben
hinter X-Admin-Key wie die Agenten-Registrierung; das Frontend fragt den Key
bei der ersten Aenderung per Prompt ab und merkt ihn sich im lokalen
Speicher. office-modal.js bekommt dafuer einen Oeffnen-Token
(isCurrentModal()), damit ein verspaetet aufloesender Request nach dem
Oeffnen eines anderen Modals nicht dessen Inhalt ueberschreibt.

Schliesst #27
2026-07-30 17:53:39 +02:00

111 lines
3.4 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');
}
}