feat(service-credential-tester): slice 1 — backend test endpoint + per-type routines
TestResult dataclass + translate_connection_error shared helper in base.py. test_callable field on ServiceDefinition (default None). 7 per-type test_connection routines (qbittorrent, prometheus via Grafana gateway, alertmanager, jellyfin, authentik, ssh_tasks via build_ssh_client, nextcloud). POST /api/services/test endpoint: validation-first (422 on malformed config), dispatch, no-persistence, no-secret-logs. backups has test_callable=None. qBit 'Fails.' → specific auth message (resolves #3 at API layer). Backend: 362 pytest pass (+31 new), ruff clean. Frontend: build green.
This commit is contained in:
@@ -6,13 +6,39 @@ dashboard widgets yet; its service page holds connection config only.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import requests
|
||||
|
||||
from media_library_viewer_api.integrations.base import (
|
||||
SecretField,
|
||||
ServiceBaseUrl,
|
||||
ServiceConfigBase,
|
||||
ServiceDefinition,
|
||||
TestResult,
|
||||
translate_connection_error,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from media_library_viewer_api.services.settings_store import SettingsStore
|
||||
|
||||
|
||||
def test_connection(
|
||||
config: dict[str, Any],
|
||||
secrets: dict[str, str],
|
||||
store: SettingsStore,
|
||||
) -> TestResult:
|
||||
"""GET {base_url}/status.php (unauthenticated server probe)."""
|
||||
try:
|
||||
base_url = str(config.get("base_url") or "").rstrip("/")
|
||||
resp = requests.get(f"{base_url}/status.php", timeout=10)
|
||||
resp.raise_for_status()
|
||||
payload = resp.json()
|
||||
version = str(payload.get("version", "") or "connected")
|
||||
return TestResult(ok=True, detail="Connected to Nextcloud.", evidence=version)
|
||||
except Exception as exc:
|
||||
return translate_connection_error(exc, context="Nextcloud")
|
||||
|
||||
|
||||
class NextcloudConfig(ServiceConfigBase):
|
||||
"""Non-secret Nextcloud connection config."""
|
||||
@@ -30,4 +56,5 @@ DEFINITION = ServiceDefinition(
|
||||
SecretField(key="app_password", label="App password", required=True),
|
||||
],
|
||||
widget_kinds=[],
|
||||
test_callable=test_connection,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user