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