Files
dashboard-unifi/src/Console/RebootAllAps.php
jwed 75943fbe2b feat(logs): structured cron run history + read endpoint
Adds unifi_cron_runs table (one row per scheduled-task execution) and
UnifiCronRun::record() wrapper that captures start/finish/status and
exceptions. The three scheduled commands now write through it:

  - reboot-all-aps    → rebooted/failed AP names per run
  - rotate-passwords  → rotated SSIDs + PPSKs, failures (when actually
                        rotating; the "is it due" early-return is silent
                        so we don't flood the log with no-op rows every
                        minute)
  - sync-ppsk-schedules → enabled/disabled PPSKs (silent when there's
                          no work)

UnifiCronLogsController returns the most-recent 200 runs as JSON,
filterable by command + status. Behind permission:unifi.settings; no
super-admin required — read-only history is fine for any operator
who can see settings.

v1.5.0.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 16:05:36 -04:00

70 lines
2.6 KiB
PHP

<?php
namespace Dashboard\Unifi\Console;
use Dashboard\Unifi\Models\UnifiCronRun;
use Dashboard\Unifi\Services\UnifiApiClient;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
class RebootAllAps extends Command
{
protected $signature = 'unifi:reboot-all-aps {--delay=5 : Seconds to wait between each reboot} {--triggered-by=schedule}';
protected $description = 'Planned reboot of all access points — suppresses webhook offline/online alerts';
public function handle(UnifiApiClient $unifi): int
{
$run = UnifiCronRun::record(
'reboot-all-aps',
$this->option('triggered-by') ?: 'schedule',
null,
function () use ($unifi) {
$aps = $unifi->getAccessPoints();
if (empty($aps)) {
$this->warn('No access points found.');
return ['status' => 'skipped', 'reason' => 'no APs found'];
}
$delay = max(0, (int) $this->option('delay'));
$rebooted = [];
$failed = [];
foreach ($aps as $ap) {
$mac = strtolower($ap['mac']);
Cache::put("unifi:planned_reboot:{$mac}", true, now()->addMinutes(20));
$this->line("Marked planned reboot: {$ap['name']} ({$mac})");
}
$this->newLine();
foreach ($aps as $ap) {
$mac = strtolower($ap['mac']);
$name = $ap['name'] ?? $mac;
try {
$unifi->rebootDevice($mac);
$this->info("Rebooted: {$name} ({$mac})");
$rebooted[] = $name;
} catch (\Throwable $e) {
$this->error("Failed to reboot {$name}: {$e->getMessage()}");
$failed[] = ['name' => $name, 'error' => $e->getMessage()];
}
if ($delay > 0 && count($rebooted) + count($failed) < count($aps)) {
sleep($delay);
}
}
return [
'status' => count($failed) === 0 ? 'succeeded' : (count($rebooted) > 0 ? 'partial' : 'failed'),
'rebooted' => $rebooted,
'failed' => $failed,
'total' => count($aps),
];
}
);
$this->info("Done. Status: {$run->status}.");
return $run->status === 'failed' ? self::FAILURE : self::SUCCESS;
}
}