feat: password rotation, PPSK management, VLAN/AP groups

- Add password rotation: RotatePasswords console command + migration + service updates
- Add PPSK management: UnifiPpsk model, migration, SyncPpskSchedules console
- Add VLAN groups and AP groups: VlanGroupController, ApGroupController, model, migration
- Add RebootAllAps console command
- Add in_alert column to device states
- Wire new features through service provider, routes, and existing controllers/services

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-19 17:54:24 -04:00
parent ce3217d8f4
commit 0802ef35f3
22 changed files with 1771 additions and 305 deletions

View File

@@ -0,0 +1,62 @@
<?php
namespace Dashboard\Unifi\Console;
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}';
protected $description = 'Planned reboot of all access points — suppresses webhook offline/online alerts';
public function handle(UnifiApiClient $unifi): int
{
try {
$aps = $unifi->getAccessPoints();
} catch (\Throwable $e) {
$this->error('Failed to fetch APs: ' . $e->getMessage());
return self::FAILURE;
}
if (empty($aps)) {
$this->warn('No access points found.');
return self::SUCCESS;
}
$delay = max(0, (int) $this->option('delay'));
// Pre-mark all APs as planned reboots before sending any commands
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();
$ok = 0;
$fail = 0;
foreach ($aps as $ap) {
$mac = strtolower($ap['mac']);
$name = $ap['name'] ?? $mac;
try {
$unifi->rebootDevice($mac);
$this->info("Rebooted: {$name} ({$mac})");
$ok++;
} catch (\Throwable $e) {
$this->error("Failed to reboot {$name}: {$e->getMessage()}");
$fail++;
}
if ($delay > 0 && $ok + $fail < count($aps)) {
sleep($delay);
}
}
$this->newLine();
$this->info("Done. {$ok} rebooted, {$fail} failed.");
return $fail > 0 ? self::FAILURE : self::SUCCESS;
}
}