Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb74edf4c1 | |||
| e5cc075938 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "dashboard/unifi",
|
"name": "dashboard/unifi",
|
||||||
"description": "UniFi network management, WiFi stats, and captive portal authentication for the Dashboard platform",
|
"description": "UniFi network management, WiFi stats, and captive portal authentication for the Dashboard platform",
|
||||||
"version": "1.10.0",
|
"version": "1.10.2",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
|||||||
@@ -104,6 +104,18 @@ class RotatePasswords extends Command
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
|
// "Not found" in a sibling just means the
|
||||||
|
// PPSK isn't mirrored on that band — totally
|
||||||
|
// normal if GUEST was only configured on one
|
||||||
|
// band. Skip quietly; don't poison the
|
||||||
|
// run status.
|
||||||
|
if (str_contains($e->getMessage(), 'not found')) {
|
||||||
|
\Illuminate\Support\Facades\Log::info('unifi.ppsk_sibling_skipped', [
|
||||||
|
'sibling_wlan' => $siblingWlanId,
|
||||||
|
'ppsk_name' => $ppsk->name,
|
||||||
|
]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
$this->error("Sibling rotate failed for wlan {$siblingWlanId}: {$e->getMessage()}");
|
$this->error("Sibling rotate failed for wlan {$siblingWlanId}: {$e->getMessage()}");
|
||||||
$failedPpsks[] = ['name' => $ppsk->name . ' (sibling wlan ' . $siblingWlanId . ')', 'error' => $e->getMessage()];
|
$failedPpsks[] = ['name' => $ppsk->name . ' (sibling wlan ' . $siblingWlanId . ')', 'error' => $e->getMessage()];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -149,8 +149,18 @@ class WifiController extends Controller
|
|||||||
$name = $networksById[$nconfId]['name'] ?? null;
|
$name = $networksById[$nconfId]['name'] ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Match by unifi_id, or by passphrase for a held embedded record re-appearing
|
// Match in priority order:
|
||||||
|
// 1. by current unifi_id (already-synced row)
|
||||||
|
// 2. by name within this wlan (catches rotation: passphrase
|
||||||
|
// changed → synthetic id changed → row identity unchanged)
|
||||||
|
// 3. by passphrase among held rows (legacy fallback for
|
||||||
|
// cases where name wasn't ingested)
|
||||||
$record = UnifiPpsk::where('unifi_id', $uid)->first()
|
$record = UnifiPpsk::where('unifi_id', $uid)->first()
|
||||||
|
?? ($name
|
||||||
|
? UnifiPpsk::where('wlan_id', $wlanId)->where('name', $name)
|
||||||
|
->orderByRaw("FIELD(state, 'active', 'held')")
|
||||||
|
->first()
|
||||||
|
: null)
|
||||||
?? UnifiPpsk::where('wlan_id', $wlanId)
|
?? UnifiPpsk::where('wlan_id', $wlanId)
|
||||||
->where('x_passphrase', $pass)
|
->where('x_passphrase', $pass)
|
||||||
->where('state', 'held')
|
->where('state', 'held')
|
||||||
@@ -174,8 +184,8 @@ class WifiController extends Controller
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only mark as held when we have confirmed live IDs —
|
// Mark non-matching active rows as held — but ONLY if there's no
|
||||||
// never wipe on an empty API response (prevents false-holds on API failures)
|
// other active row with the same name we just reconnected.
|
||||||
if (! empty($liveIds)) {
|
if (! empty($liveIds)) {
|
||||||
UnifiPpsk::where('wlan_id', $wlanId)
|
UnifiPpsk::where('wlan_id', $wlanId)
|
||||||
->where('state', 'active')
|
->where('state', 'active')
|
||||||
@@ -184,6 +194,47 @@ class WifiController extends Controller
|
|||||||
->update(['state' => 'held', 'unifi_id' => null]);
|
->update(['state' => 'held', 'unifi_id' => null]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For each active row, salvage any rotate_password / schedule
|
||||||
|
// settings from the held tombstones with the same name BEFORE
|
||||||
|
// we prune them. Otherwise a row that had rotate=on loses the
|
||||||
|
// flag every time a rotation changes its synthetic id.
|
||||||
|
$activeRows = UnifiPpsk::where('wlan_id', $wlanId)
|
||||||
|
->where('state', 'active')
|
||||||
|
->whereNotNull('name')
|
||||||
|
->get();
|
||||||
|
foreach ($activeRows as $active) {
|
||||||
|
$heldWithSettings = UnifiPpsk::where('wlan_id', $wlanId)
|
||||||
|
->where('state', 'held')
|
||||||
|
->where('name', $active->name)
|
||||||
|
->where(fn ($q) => $q
|
||||||
|
->where('rotate_password', true)
|
||||||
|
->orWhereNotNull('schedule'))
|
||||||
|
->orderByDesc('updated_at')
|
||||||
|
->first();
|
||||||
|
if (! $heldWithSettings) continue;
|
||||||
|
|
||||||
|
$patch = [];
|
||||||
|
if ($heldWithSettings->rotate_password && ! $active->rotate_password) {
|
||||||
|
$patch['rotate_password'] = true;
|
||||||
|
}
|
||||||
|
if ($heldWithSettings->schedule && ! $active->schedule) {
|
||||||
|
$patch['schedule'] = $heldWithSettings->schedule;
|
||||||
|
}
|
||||||
|
if ($patch) $active->update($patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prune obsolete held rows: any held row whose name matches an
|
||||||
|
// active row in the same wlan is a stale tombstone — its
|
||||||
|
// settings have been salvaged above, and its data has been
|
||||||
|
// superseded by the active one.
|
||||||
|
$activeNames = $activeRows->pluck('name')->filter()->unique();
|
||||||
|
if ($activeNames->isNotEmpty()) {
|
||||||
|
UnifiPpsk::where('wlan_id', $wlanId)
|
||||||
|
->where('state', 'held')
|
||||||
|
->whereIn('name', $activeNames)
|
||||||
|
->delete();
|
||||||
|
}
|
||||||
|
|
||||||
$dbRecords = UnifiPpsk::where('wlan_id', $wlanId)
|
$dbRecords = UnifiPpsk::where('wlan_id', $wlanId)
|
||||||
->orderByRaw("FIELD(state, 'active', 'held')")
|
->orderByRaw("FIELD(state, 'active', 'held')")
|
||||||
->orderBy('name')
|
->orderBy('name')
|
||||||
@@ -314,7 +365,11 @@ class WifiController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
\Illuminate\Support\Facades\Log::warning('unifi.ppsk_sibling_update_failed', [
|
// PPSK absent on this band is fine — just
|
||||||
|
// means it isn't mirrored. Anything else
|
||||||
|
// gets warning-logged.
|
||||||
|
$level = str_contains($e->getMessage(), 'not found') ? 'info' : 'warning';
|
||||||
|
\Illuminate\Support\Facades\Log::log($level, 'unifi.ppsk_sibling_update', [
|
||||||
'sibling_wlan' => $siblingWlanId,
|
'sibling_wlan' => $siblingWlanId,
|
||||||
'error' => $e->getMessage(),
|
'error' => $e->getMessage(),
|
||||||
]);
|
]);
|
||||||
|
|||||||
Reference in New Issue
Block a user