fix(api): try UniFi OS Integration API first for X-API-Key auth
The /api/self/sites and /proxy/network/api/self/sites endpoints belong
to the legacy session-cookie API — they don't accept X-API-Key auth and
return 401 for keys generated in UniFi OS → Control Plane → Integrations.
Adds /proxy/network/integration/v1/sites as the first endpoint tried,
which is the actual home of API keys. Integration response rows look
like { id, internalReference, name }; getSites normalizes them to the
legacy { name, desc } shape using internalReference as the slug so
downstream URLs (which build paths from $this->site) keep working.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -625,8 +625,14 @@ class UnifiApiClient
|
|||||||
$http = $this->buildRequest();
|
$http = $this->buildRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try multiple URL patterns
|
// Endpoints, in preferred order:
|
||||||
|
// 1. UniFi OS Integration API — the one X-API-Key keys are issued for
|
||||||
|
// (response shape: [{ id, internalReference, name }])
|
||||||
|
// 2. Legacy /proxy/network/api/self/sites — session-cookie API on
|
||||||
|
// UniFi OS consoles (response: [{ name, desc, ... }])
|
||||||
|
// 3. Legacy /api/self/sites — standalone controller (no /proxy prefix)
|
||||||
$paths = [
|
$paths = [
|
||||||
|
'/proxy/network/integration/v1/sites',
|
||||||
'/proxy/network/api/self/sites',
|
'/proxy/network/api/self/sites',
|
||||||
'/api/self/sites',
|
'/api/self/sites',
|
||||||
];
|
];
|
||||||
@@ -645,11 +651,28 @@ class UnifiApiClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
$data = $response->json('data', $response->json());
|
$data = $response->json('data', $response->json());
|
||||||
if (is_array($data) && ! empty($data) && isset($data[0]['name'])) {
|
if (! is_array($data) || empty($data)) {
|
||||||
|
$lastError = "No sites in response from {$path}";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integration API rows have `internalReference` (== legacy
|
||||||
|
// site slug) and `name` (human-readable). Normalize to
|
||||||
|
// the legacy {name, desc} shape so downstream code that
|
||||||
|
// builds URLs with the site slug keeps working.
|
||||||
|
if (isset($data[0]['internalReference'])) {
|
||||||
|
$data = array_map(fn ($s) => [
|
||||||
|
'name' => $s['internalReference'] ?? 'default',
|
||||||
|
'desc' => $s['name'] ?? $s['internalReference'] ?? 'Default',
|
||||||
|
], $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data[0]['name'])) {
|
||||||
Log::debug('unifi.sites_found', ['path' => $path, 'count' => count($data)]);
|
Log::debug('unifi.sites_found', ['path' => $path, 'count' => count($data)]);
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
$lastError = "No sites in response from {$path}";
|
|
||||||
|
$lastError = "Unexpected response shape from {$path}";
|
||||||
} else {
|
} else {
|
||||||
$lastError = "HTTP {$response->status()} on {$path}";
|
$lastError = "HTTP {$response->status()} on {$path}";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user