From a4397c51785bd312df8b7eede98cbc7ec727245a Mon Sep 17 00:00:00 2001 From: jwed Date: Sat, 23 May 2026 16:35:32 -0400 Subject: [PATCH] chore: remove AP Groups surfaces (legacy API auth incompatible) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UniFi's /rest/apgroup endpoints (and per-SSID ap_group_ids writes via /rest/wlanconf) require session-cookie auth — they don't accept the X-API-Key header. The Integration API doesn't expose AP groups at all. So with the current deployment running on API-key auth, every AP-group operation returned 400 api.err.InvalidObject. Removing the dead code rather than carrying a feature that can't function. * Deleted ApGroupController, ApGroups.vue, the /ap-groups/* routes, and getApGroups/createApGroup/updateApGroup/deleteApGroup from UnifiApiClient. * Removed the per-SSID AP-group assignment from Wifi.vue + the updateApGroups action + /wifi/{wlanId}/ap-groups route + the ap_group_ids field from the mapWlan output. * Removed the AP Groups nav entry from composer.json. If a future deploy adds local-admin username+password auth, AP groups can be reintroduced — the UnifiApiClient::buildRequest() session-cookie path is intact. v1.3.1. Co-Authored-By: Claude Opus 4.7 (1M context) --- composer.json | 7 +- src/Http/Controllers/ApGroupController.php | 95 ---------------------- src/Http/Controllers/WifiController.php | 35 +------- src/Services/UnifiApiClient.php | 22 ----- src/routes/unifi.php | 8 -- 5 files changed, 4 insertions(+), 163 deletions(-) delete mode 100644 src/Http/Controllers/ApGroupController.php diff --git a/composer.json b/composer.json index d733a49..7fe11c2 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "dashboard/unifi", "description": "UniFi network management, WiFi stats, and captive portal authentication for the Dashboard platform", - "version": "1.3.0", + "version": "1.3.1", "type": "library", "license": "MIT", "autoload": { @@ -27,9 +27,8 @@ { "label": "Devices", "route_name": "unifi.devices", "icon": "cpu-chip", "permission": "unifi.stats", "sort_order": 3 }, { "label": "Clients", "route_name": "unifi.clients", "icon": "users", "permission": "unifi.stats", "sort_order": 4 }, { "label": "WiFi Networks", "route_name": "unifi.wifi", "icon": "wifi", "permission": "unifi.manage", "sort_order": 5 }, - { "label": "AP Groups", "route_name": "unifi.ap-groups.index", "icon": "rectangle-stack", "permission": "unifi.manage", "sort_order": 6 }, - { "label": "Portal", "route_name": "unifi.portal.settings", "icon": "shield-check", "permission": "unifi.auth", "sort_order": 7 }, - { "label": "Webhooks", "route_name": "unifi.webhooks.index", "icon": "bell-alert", "permission": "unifi.settings", "sort_order": 8 }, + { "label": "Portal", "route_name": "unifi.portal.settings", "icon": "shield-check", "permission": "unifi.auth", "sort_order": 6 }, + { "label": "Webhooks", "route_name": "unifi.webhooks.index", "icon": "bell-alert", "permission": "unifi.settings", "sort_order": 7 }, { "label": "Settings", "route_name": "unifi.settings", "icon": "cog-6-tooth", "permission": "unifi.settings", "sort_order": 99 } ], "permissions": [ diff --git a/src/Http/Controllers/ApGroupController.php b/src/Http/Controllers/ApGroupController.php deleted file mode 100644 index 2ead2e9..0000000 --- a/src/Http/Controllers/ApGroupController.php +++ /dev/null @@ -1,95 +0,0 @@ -getApGroups())->map(fn ($g) => [ - 'id' => $g['_id'], - 'name' => $g['name'] ?? 'Unnamed', - 'device_macs' => $g['device_macs'] ?? [], - 'is_default' => $g['attr_no_delete'] ?? false, - ])->values(); - - $devices = collect($unifi->getAccessPoints())->map(fn ($d) => [ - 'mac' => strtolower($d['mac']), - 'name' => $d['name'] ?? $d['model'] ?? $d['mac'], - 'model' => $d['model'] ?? '', - 'state' => $d['state'] ?? 0, - ])->values(); - - return Inertia::render('Unifi/ApGroups', [ - 'groups' => $groups, - 'devices' => $devices, - ]); - } catch (\Throwable $e) { - return Inertia::render('Unifi/ApGroups', [ - 'groups' => [], 'devices' => [], 'error' => $e->getMessage(), - ]); - } - } - - public function store(Request $request, UnifiApiClient $unifi) - { - $data = $request->validate([ - 'name' => 'required|string|max:100', - 'device_macs' => 'present|array', - 'device_macs.*' => 'string', - ]); - - try { - $result = $unifi->createApGroup([ - 'name' => $data['name'], - 'device_macs' => array_values(array_map('strtolower', $data['device_macs'])), - ]); - return back()->with('success', 'AP group created.'); - } catch (\Throwable $e) { - return back()->withErrors(['error' => $e->getMessage()]); - } - } - - public function update(Request $request, string $groupId, UnifiApiClient $unifi) - { - $data = $request->validate([ - 'name' => 'sometimes|string|max:100', - 'device_macs' => 'sometimes|array', - 'device_macs.*' => 'string', - ]); - - if (isset($data['device_macs'])) { - $data['device_macs'] = array_values(array_map('strtolower', $data['device_macs'])); - } - - try { - $unifi->updateApGroup($groupId, $data); - return back()->with('success', 'AP group updated.'); - } catch (\Throwable $e) { - return back()->withErrors(['error' => $e->getMessage()]); - } - } - - public function destroy(string $groupId, UnifiApiClient $unifi) - { - try { - $unifi->deleteApGroup($groupId); - return back()->with('success', 'AP group deleted.'); - } catch (\Throwable $e) { - return back()->withErrors(['error' => $e->getMessage()]); - } - } -} diff --git a/src/Http/Controllers/WifiController.php b/src/Http/Controllers/WifiController.php index f8019fa..2ee005e 100644 --- a/src/Http/Controllers/WifiController.php +++ b/src/Http/Controllers/WifiController.php @@ -13,25 +13,9 @@ class WifiController extends Controller { public function index(UnifiApiClient $unifi) { - // Always pull fresh device data on this page so AP-group / SSID - // edits never go out against a stale snapshot. getWlans() and - // getApGroups() aren't cached, but getDevices() is. - \Illuminate\Support\Facades\Cache::forget('unifi:devices'); - try { $wlans = collect($unifi->getWlans())->map(fn ($w) => $this->mapWlan($w))->values(); - try { - $apGroups = collect($unifi->getApGroups())->map(fn ($g) => [ - 'id' => $g['_id'], - 'name' => $g['attr_no_delete'] ?? false ? 'Default' : ($g['name'] ?? 'Unnamed'), - 'device_macs' => $g['device_macs'] ?? [], - 'is_default' => $g['attr_no_delete'] ?? false, - ])->values(); - } catch (\Throwable $e) { - $apGroups = collect(); // AP groups not supported by this controller - } - $raw = Setting::get('unifi.ssid_groups', '{}'); $groups = json_decode($raw, true); if (! is_array($groups) || array_is_list($groups)) $groups = []; @@ -42,13 +26,12 @@ class WifiController extends Controller return Inertia::render('Unifi/Wifi', [ 'wlans' => $wlans, 'groups' => $groups, - 'apGroups' => $apGroups, 'rotateWlanIds' => $rotateWlanIds, 'ppskSchedulingEnabled' => (bool) Setting::get('unifi.ppsk_scheduling.enabled', false), ]); } catch (\Throwable $e) { return Inertia::render('Unifi/Wifi', [ - 'wlans' => [], 'groups' => [], 'apGroups' => [], 'rotateWlanIds' => [], 'error' => $e->getMessage(), + 'wlans' => [], 'groups' => [], 'rotateWlanIds' => [], 'error' => $e->getMessage(), ]); } } @@ -103,21 +86,6 @@ class WifiController extends Controller } } - /** - * Update AP group assignments for a single WLAN (not synced to group siblings). - */ - public function updateApGroups(Request $request, string $wlanId, UnifiApiClient $unifi) - { - $request->validate(['ap_group_ids' => 'required|array']); - - try { - $unifi->updateWlan($wlanId, ['ap_group_ids' => $request->ap_group_ids]); - return back()->with('success', 'AP groups updated.'); - } catch (\Throwable $e) { - return back()->withErrors(['error' => $e->getMessage()]); - } - } - public function toggle(Request $request, string $wlanId, UnifiApiClient $unifi) { $request->validate(['enabled' => 'required|boolean']); @@ -419,7 +387,6 @@ class WifiController extends Controller 'hide_ssid' => $w['hide_ssid'] ?? false, 'passphrase' => $w['x_passphrase'] ?? '', 'band' => $this->detectBand($w), - 'ap_group_ids' => $w['ap_group_ids'] ?? [], 'mac_filter_enabled' => $w['mac_filter_enabled'] ?? false, 'mac_filter_policy' => $w['mac_filter_policy'] ?? 'deny', 'ppsk_enabled' => ($w['wpa3_ppsk'] ?? false) diff --git a/src/Services/UnifiApiClient.php b/src/Services/UnifiApiClient.php index d997136..766e42f 100644 --- a/src/Services/UnifiApiClient.php +++ b/src/Services/UnifiApiClient.php @@ -312,28 +312,6 @@ class UnifiApiClient return $this->put("/rest/wlanconf/{$wlanId}", $data); } - // ── AP Groups ───────────────────────────────────────────────────────────── - - public function getApGroups(): array - { - return $this->get('/rest/apgroups'); - } - - public function createApGroup(array $data): array - { - return $this->post('/rest/apgroups', $data); - } - - public function updateApGroup(string $groupId, array $data): array - { - return $this->put("/rest/apgroups/{$groupId}", $data); - } - - public function deleteApGroup(string $groupId): void - { - $this->delete("/rest/apgroups/{$groupId}"); - } - // ── PPSK ───────────────────────────────────────────────────────────────── /** diff --git a/src/routes/unifi.php b/src/routes/unifi.php index e9c91fe..079954b 100644 --- a/src/routes/unifi.php +++ b/src/routes/unifi.php @@ -1,6 +1,5 @@ name('wifi'); Route::put('/wifi/{wlanId}', [WifiController::class, 'update']) ->name('wifi.update'); - Route::put('/wifi/{wlanId}/ap-groups', [WifiController::class, 'updateApGroups']) ->name('wifi.ap-groups'); Route::post('/wifi/{wlanId}/toggle', [WifiController::class, 'toggle']) ->name('wifi.toggle'); Route::post('/wifi/groups', [WifiController::class, 'saveGroups']) ->name('wifi.groups'); @@ -44,12 +42,6 @@ Route::middleware(['web', 'auth', 'app.access:unifi']) Route::put('/wifi/{wlanId}/ppsk/{ppskId}/schedule', [WifiController::class, 'ppskSchedule']) ->name('wifi.ppsk.schedule'); Route::patch('/wifi/{wlanId}/ppsk/{ppskId}/rotation',[WifiController::class, 'ppskToggleRotation'])->name('wifi.ppsk.rotation'); - // AP Groups - Route::get('/ap-groups', [ApGroupController::class, 'index']) ->name('ap-groups.index'); - Route::post('/ap-groups', [ApGroupController::class, 'store']) ->name('ap-groups.store'); - Route::put('/ap-groups/{groupId}', [ApGroupController::class, 'update']) ->name('ap-groups.update'); - Route::delete('/ap-groups/{groupId}', [ApGroupController::class, 'destroy']) ->name('ap-groups.destroy'); - // Devices Route::post('/devices/reboot', [DeviceController::class, 'reboot']) ->name('devices.reboot'); Route::post('/clients/kick', [ClientController::class, 'kick']) ->name('clients.kick');