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

@@ -4,6 +4,7 @@ namespace Dashboard\Unifi\Http\Controllers;
use Dashboard\Unifi\Models\KnownMac;
use Dashboard\Unifi\Models\PortalSession;
use Dashboard\Unifi\Models\VlanGroup;
use Dashboard\Unifi\Services\UnifiApiClient;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
@@ -11,7 +12,7 @@ use Inertia\Inertia;
class ClientController extends Controller
{
public function index(UnifiApiClient $unifi)
public function index(Request $request, UnifiApiClient $unifi)
{
try {
$clients = collect($unifi->getActiveClients())->map(fn ($c) => [
@@ -26,7 +27,10 @@ class ClientController extends Controller
'is_wired' => $c['is_wired'] ?? false,
'is_guest' => $c['is_guest'] ?? false,
'ssid' => $c['essid'] ?? null,
'network' => $c['network'] ?? null,
'ap_mac' => $c['ap_mac'] ?? null,
'sw_mac' => $c['sw_mac'] ?? null,
'sw_port' => $c['sw_port'] ?? null,
'rssi' => $c['rssi'] ?? null,
'signal' => $c['signal'] ?? null,
'channel' => $c['channel'] ?? null,
@@ -34,12 +38,32 @@ class ClientController extends Controller
'rx_bytes' => $c['rx_bytes'] ?? 0,
'tx_rate' => $c['tx_rate'] ?? 0,
'rx_rate' => $c['rx_rate'] ?? 0,
'tx_rate_r' => $c['tx_bytes-r'] ?? 0,
'rx_rate_r' => $c['rx_bytes-r'] ?? 0,
'uptime' => $c['uptime'] ?? 0,
'satisfaction' => $c['satisfaction'] ?? null,
'vlan_id' => ($c['vlan_id'] ?? 0) ?: null,
'dot1x' => $c['1x_identity'] ?? $c['dot1x_identity'] ?? null,
'is_known' => KnownMac::where('mac_address', strtolower($c['mac']))->exists(),
])->values();
return Inertia::render('Unifi/Clients', ['clients' => $clients]);
// APs and switches for the device filter dropdown
$devices = collect($unifi->getDevices())
->filter(fn ($d) => in_array($d['type'] ?? '', ['uap', 'usw']))
->map(fn ($d) => [
'mac' => $d['mac'],
'name' => $d['name'] ?? $d['model'] ?? $d['mac'],
'type' => $d['type'],
])
->sortBy('name')
->values();
return Inertia::render('Unifi/Clients', [
'clients' => $clients,
'vlanGroups' => VlanGroup::orderBy('sort_order')->get(),
'devices' => $devices,
'selectedDevice' => $request->query('device'),
]);
} catch (\Throwable $e) {
return Inertia::render('Unifi/Clients', ['clients' => [], 'error' => $e->getMessage()]);
}