diff --git a/backend/src/media_library_viewer_api/routers/dashboard.py b/backend/src/media_library_viewer_api/routers/dashboard.py index 256c462..2c6d8a9 100644 --- a/backend/src/media_library_viewer_api/routers/dashboard.py +++ b/backend/src/media_library_viewer_api/routers/dashboard.py @@ -43,7 +43,7 @@ def get_monitoring_overview( store=Depends(get_settings_store), ) -> dict[str, Any]: """Return one lightweight monitoring row per configured machine.""" - machines = store.list_machines() + machines = [m for m in store.list_machines() if m.get("enabled")] rows: list[dict[str, Any]] = [] for machine in machines: try: diff --git a/backend/src/media_library_viewer_api/routers/monitoring.py b/backend/src/media_library_viewer_api/routers/monitoring.py index 84e4ffe..7a1a90e 100644 --- a/backend/src/media_library_viewer_api/routers/monitoring.py +++ b/backend/src/media_library_viewer_api/routers/monitoring.py @@ -53,8 +53,8 @@ def _resolve_machine(store: SettingsStore, machine_id: str | None) -> dict[str, @router.get("/machines") def get_machines(store: SettingsStore = Depends(get_settings_store)) -> list[dict[str, Any]]: - """Return monitoring machines for the UI.""" - return store.list_machines() + """Return enabled monitoring machines for the UI.""" + return [m for m in store.list_machines() if m.get("enabled")] @router.get("/poller") diff --git a/backend/src/media_library_viewer_api/routers/settings.py b/backend/src/media_library_viewer_api/routers/settings.py index d6c25cf..b4f27e0 100644 --- a/backend/src/media_library_viewer_api/routers/settings.py +++ b/backend/src/media_library_viewer_api/routers/settings.py @@ -318,9 +318,6 @@ def reset_local_database( media_index = MediaIndex() media_removed = remove_sqlite_database(media_index.db_path) - # Recreate the default local machine immediately so the UI remains usable. - store.ensure_defaults() - return { "status": "reset", "settings_db_removed": bool(settings_removed), diff --git a/backend/src/media_library_viewer_api/services/settings_store.py b/backend/src/media_library_viewer_api/services/settings_store.py index aa4d558..3e926a6 100644 --- a/backend/src/media_library_viewer_api/services/settings_store.py +++ b/backend/src/media_library_viewer_api/services/settings_store.py @@ -355,7 +355,7 @@ class SettingsStore: ) def list_machines(self) -> list[dict[str, Any]]: - self.ensure_defaults() + self.init_schema() with self.connect() as conn: rows = conn.execute( "SELECT * FROM monitoring_machines ORDER BY CASE WHEN id = ? THEN 0 ELSE 1 END, name COLLATE NOCASE", @@ -366,7 +366,7 @@ class SettingsStore: def get_machine(self, machine_id: str | None) -> dict[str, Any] | None: if not machine_id: return None - self.ensure_defaults() + self.init_schema() with self.connect() as conn: row = conn.execute("SELECT * FROM monitoring_machines WHERE id = ?", (machine_id,)).fetchone() return self._row_to_machine(row) if row else None @@ -375,7 +375,7 @@ class SettingsStore: """Return the full machine config including secrets.""" if not machine_id: return None - self.ensure_defaults() + self.init_schema() with self.connect() as conn: row = conn.execute("SELECT * FROM monitoring_machines WHERE id = ?", (machine_id,)).fetchone() if not row: diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index ff5e713..d850172 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -128,12 +128,14 @@ def mock_ssh(): @pytest.fixture -def test_client(mock_jellyfin, mock_jellyseerr, mock_ssh): +def test_client(mock_jellyfin, mock_jellyseerr, mock_ssh, tmp_path): """FastAPI test client with mocked dependencies.""" app.dependency_overrides[get_jellyfin_client] = lambda: mock_jellyfin app.dependency_overrides[get_jellyseerr_client] = lambda: mock_jellyseerr app.dependency_overrides[get_ssh_client] = lambda: mock_ssh app.dependency_overrides[get_user_id] = lambda: "user123" + store = SettingsStore(tmp_path / "settings.sqlite") + app.dependency_overrides[get_settings_store] = lambda: store auth_settings = SimpleNamespace(auth_enabled=False) with patch("media_library_viewer_api.auth.get_settings", return_value=auth_settings): client = TestClient(app) @@ -294,7 +296,7 @@ class TestSettingsReset: settings_module.MediaIndex = original_media_index assert response.status_code == 400 - def test_reset_local_database_wipes_state_and_reseeds_local_machine(self, test_client, tmp_path): + def test_reset_local_database_wipes_state_and_leaves_no_machines(self, test_client, tmp_path): store = SettingsStore(tmp_path / "settings.sqlite") store.ensure_defaults() media_db = tmp_path / "media.sqlite" @@ -324,8 +326,8 @@ class TestSettingsReset: assert payload["status"] == "reset" assert not media_db.exists() assert not media_wal.exists() - assert store.get_machine("local") is not None - assert len(store.list_machines()) == 1 + assert store.get_machine("local") is None + assert len(store.list_machines()) == 0 # --- Users --- @@ -644,7 +646,20 @@ class TestJobs: # --- Monitoring --- class TestMonitoring: + def _ensure_machine(self): + store = app.dependency_overrides[get_settings_store]() + if not store.list_machines(): + store.upsert_machine({ + "name": "Test Machine", + "mode": "ssh", + "enabled": True, + "services": ["monitoring", "files", "jellyfin"], + "host": "test-host", + "username": "test-user", + }) + def test_status(self, test_client, mock_ssh): + self._ensure_machine() mock_ssh.run.return_value = CommandResult( command="...", exit_status=0, stdout="running pid=1234\n", stderr="" ) @@ -653,6 +668,7 @@ class TestMonitoring: assert "running" in response.json()["status"] def test_metrics_empty(self, test_client, mock_ssh): + self._ensure_machine() mock_ssh.run.return_value = CommandResult( command="...", exit_status=0, stdout="", stderr="" ) @@ -662,6 +678,7 @@ class TestMonitoring: assert data["samples"] == [] def test_disk(self, test_client, mock_ssh): + self._ensure_machine() mock_ssh.run.return_value = CommandResult( command="df ...", exit_status=0, @@ -674,6 +691,7 @@ class TestMonitoring: assert data["used_pct"] == "50%" def test_start(self, test_client, mock_ssh): + self._ensure_machine() mock_ssh.run.return_value = CommandResult( command="...", exit_status=0, stdout="started pid=5678\n", stderr="" ) @@ -682,6 +700,7 @@ class TestMonitoring: assert "started" in response.json()["message"] def test_stop(self, test_client, mock_ssh): + self._ensure_machine() mock_ssh.run.return_value = CommandResult( command="...", exit_status=0, stdout="stopped pid=5678\n", stderr="" ) @@ -689,6 +708,7 @@ class TestMonitoring: assert response.status_code == 200 def test_restart(self, test_client, mock_ssh): + self._ensure_machine() mock_ssh.run.return_value = CommandResult( command="...", exit_status=0, stdout="stopped pid=5678\nstarted pid=9999\n", stderr="" ) diff --git a/frontend/src/pages/Settings.tsx b/frontend/src/pages/Settings.tsx index d1a5880..64f45ab 100644 --- a/frontend/src/pages/Settings.tsx +++ b/frontend/src/pages/Settings.tsx @@ -1378,17 +1378,15 @@ export function Settings() { > Edit - {selectedMachine.id !== "local" && ( - - )} + ) : null} @@ -1443,7 +1441,7 @@ export function Settings() { (machineDraft.mode === "ssh" && !machineDraft.host.trim()) } secondaryAction={ - machineDraft.id && machineDraft.id !== "local" ? ( + machineDraft.id ? (