feat: boka push ingest and stale monitoring (todo 04)

Add token- and IP-protected ingest.php, boka status checks in monitor.php,
and document the two-repo push workflow. Todo 04 marked complete.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Frank Schwenk
2026-06-12 14:51:19 +02:00
parent 2440fbf974
commit 9b18b11f32
10 changed files with 240 additions and 31 deletions
+94
View File
@@ -282,6 +282,90 @@ function maybeSendReviewReminder(array $config, array $targets, string $currentH
writeJsonFile($statePath, $newState);
}
function bokaPushStaleMinutes(): int
{
return (int) (env('BOKA_PUSH_STALE_MINUTES') ?: 15);
}
function bokaDiskWarnPercent(): int
{
return (int) (env('BOKA_DISK_WARN_PERCENT') ?: 85);
}
function bokaBackupStaleHours(): int
{
return (int) (env('BOKA_BACKUP_STALE_HOURS') ?: 25);
}
/** @return list<array<string, mixed>> */
function checkBokaPushStatus(): array
{
$ingestToken = env('MONITOR_INGEST_TOKEN') ?? '';
if ($ingestToken === '') {
return [];
}
$statusPath = privateDataDir() . '/boka-status.json';
$status = readJsonFile($statusPath);
if ($status === null) {
return [];
}
$failures = [];
$now = time();
$lastPush = $status['last_push'] ?? null;
if (is_string($lastPush) && $lastPush !== '') {
$lastPushAt = strtotime($lastPush) ?: 0;
$staleMinutes = bokaPushStaleMinutes();
$ageMinutes = (int) floor(($now - $lastPushAt) / 60);
if ($ageMinutes > $staleMinutes) {
$failures[] = [
'name' => 'boka-push',
'url' => 'ingest',
'message' => "boka silent — no push in {$ageMinutes} min",
'retried' => false,
];
}
}
$diskPercent = (int) ($status['disk_percent'] ?? 0);
if ($diskPercent > bokaDiskWarnPercent()) {
$failures[] = [
'name' => 'boka-push',
'url' => 'ingest',
'message' => "disk at {$diskPercent}%",
'retried' => false,
];
}
$unhealthy = (int) ($status['containers_unhealthy'] ?? 0);
if ($unhealthy > 0) {
$failures[] = [
'name' => 'boka-push',
'url' => 'ingest',
'message' => "{$unhealthy} unhealthy container(s)",
'retried' => false,
];
}
$backupLastSuccess = $status['backup_last_success'] ?? null;
if (is_string($backupLastSuccess) && $backupLastSuccess !== '') {
$backupAt = strtotime($backupLastSuccess) ?: 0;
$ageHours = (int) floor(($now - $backupAt) / 3600);
if ($ageHours > bokaBackupStaleHours()) {
$failures[] = [
'name' => 'boka-push',
'url' => 'ingest',
'message' => "backup stale — last success {$ageHours}h ago",
'retried' => false,
];
}
}
return $failures;
}
/** @param list<array<string, mixed>> $failures */
function sendDownAlert(array $failures): void
{
@@ -360,6 +444,16 @@ function runMonitor(bool $verbose = false): int
maybeSendReviewReminder($config, $targets, $currentHash, $verbose);
$failures = array_values(array_filter($checks, static fn (array $check): bool => $check['status'] === 'down'));
$bokaFailures = checkBokaPushStatus();
if ($bokaFailures !== []) {
if ($verbose) {
foreach ($bokaFailures as $failure) {
fwrite(STDERR, sprintf("boka-push: %s\n", $failure['message']));
}
}
$failures = array_merge($failures, $bokaFailures);
}
if ($failures === []) {
return 0;
}