v2.1.2: list_app_catalog использует GET и расширенный список путей
This commit is contained in:
+65
-43
@@ -1,48 +1,70 @@
|
||||
def _rest_request(self, method: str, endpoint: str, payload: Optional[dict] = None, timeout: Optional[int] = None) -> Optional[Any]:
|
||||
method = method.upper()
|
||||
if method not in ("POST", "PUT", "DELETE", "PATCH", "GET"):
|
||||
self._last_error = f"_rest_request: неподдерживаемый метод {method}"
|
||||
return None
|
||||
url = f"{self._base}/api/v2.0/{endpoint}"
|
||||
t = timeout if timeout is not None else self.valves.request_timeout
|
||||
payload: Dict[str, Any] = {"catalog_name": catalog, "retrieve_all_trains": True}
|
||||
if category:
|
||||
payload["categories"] = [category]
|
||||
# v2.1.2: TrueNAS 25.10 исторически делал POST /app/available, но в новых билдах
|
||||
# многие эндпоинты переехали на GET. _rest_request сам делает POST→GET fallback
|
||||
# при 405, поэтому здесь используем _post — он сам разберётся.
|
||||
# Дополнительно пробуем GET напрямую для надёжности.
|
||||
endpoints = [
|
||||
"app/available", # новый (Electric Eel+)
|
||||
"app.available",
|
||||
"chart/release/names",
|
||||
"chart.release.names",
|
||||
"chart_release/names",
|
||||
"catalog/items", # fallback
|
||||
"catalog.items",
|
||||
"catalog_items",
|
||||
"app/available_apps",
|
||||
"app/list_apps",
|
||||
]
|
||||
apps: List[Dict[str, Any]] = []
|
||||
used_ep = None
|
||||
used_method = None
|
||||
for ep in endpoints:
|
||||
# Сначала GET (часто работает на 25.10)
|
||||
try:
|
||||
r = requests.request(method, url, headers=self._headers, json=payload if payload is not None else {}, timeout=t, verify=self.valves.verify_ssl)
|
||||
# v2.1.2: автоматический fallback POST → GET при 405 (Method Not Allowed).
|
||||
# Это часто встречается в TrueNAS 25.10: некоторые эндпоинты каталога
|
||||
# исторически были POST, но в новых версиях стали GET.
|
||||
if r.status_code == 405 and method == "POST":
|
||||
r = requests.get(
|
||||
f"{self._base}/api/v2.0/{ep}",
|
||||
headers=self._headers,
|
||||
params=payload if payload else None,
|
||||
timeout=self.valves.request_timeout,
|
||||
verify=self.valves.verify_ssl,
|
||||
)
|
||||
if r.status_code == 200:
|
||||
try:
|
||||
r2 = requests.get(url, headers=self._headers, params=payload or {}, timeout=t, verify=self.valves.verify_ssl)
|
||||
if r2.status_code in (200, 201, 202, 204):
|
||||
if not r2.text:
|
||||
return {"status": "ok", "http_code": r2.status_code, "via": "GET-fallback"}
|
||||
try:
|
||||
data = r2.json()
|
||||
data = r.json()
|
||||
if isinstance(data, list):
|
||||
apps = data
|
||||
used_ep = ep
|
||||
used_method = "GET"
|
||||
break
|
||||
if isinstance(data, dict):
|
||||
data["_via"] = "GET-fallback"
|
||||
return data
|
||||
for key in ("items", "applications", "releases", "apps"):
|
||||
if key in data and isinstance(data[key], list):
|
||||
apps = data[key]
|
||||
used_ep = ep
|
||||
used_method = "GET"
|
||||
break
|
||||
if apps:
|
||||
break
|
||||
except Exception:
|
||||
return {"status": "ok", "text": r2.text, "http_code": r2.status_code, "_via": "GET-fallback"}
|
||||
pass
|
||||
except Exception:
|
||||
pass # вернёмся к исходной ошибке ниже
|
||||
if r.status_code in (200, 201, 202, 204):
|
||||
if not r.text:
|
||||
return {"status": "ok", "http_code": r.status_code}
|
||||
try:
|
||||
return r.json()
|
||||
except Exception:
|
||||
return {"status": "ok", "text": r.text, "http_code": r.status_code}
|
||||
self._last_error = f"{method} {endpoint} → HTTP {r.status_code}: {r.text[:400]}"
|
||||
return None
|
||||
except requests.exceptions.SSLError as e:
|
||||
self._last_error = f"SSL-ошибка {method} {endpoint}: {e}"
|
||||
return None
|
||||
except requests.exceptions.ConnectionError as e:
|
||||
self._last_error = f"Соединение {method} {endpoint}: {e}"
|
||||
return None
|
||||
except requests.exceptions.Timeout:
|
||||
self._last_error = f"Таймаут {method} {endpoint} (> {t}с)"
|
||||
return None
|
||||
except Exception as e:
|
||||
self._last_error = f"{method} {endpoint}: {type(e).__name__}: {e}"
|
||||
return None
|
||||
pass
|
||||
# Затем POST (через _rest_request — у него есть fallback на GET при 405)
|
||||
res = self._post(ep, payload, timeout=self.valves.request_timeout)
|
||||
if res is not None:
|
||||
if isinstance(res, list):
|
||||
apps = res
|
||||
used_ep = ep
|
||||
used_method = "POST"
|
||||
break
|
||||
if isinstance(res, dict):
|
||||
for key in ("items", "applications", "releases", "apps"):
|
||||
if key in res and isinstance(res[key], list):
|
||||
apps = res[key]
|
||||
used_ep = ep
|
||||
used_method = res.get("_via", "POST")
|
||||
break
|
||||
if apps:
|
||||
break
|
||||
Reference in New Issue
Block a user