diff --git a/src/hub/Server.py b/src/hub/Server.py index 6a4b4a5..bb2b515 100644 --- a/src/hub/Server.py +++ b/src/hub/Server.py @@ -1497,12 +1497,6 @@ def sync_upstream_packages_api(auth_user: str = Depends(require_admin_auth)): """ Downloads latest client binary packages from upstream release assets to local Hub storage. """ - db_path = SERVER_STATE.get("config", {}).get("db_path", DEFAULT_DB_FILE) - if not is_feature_licensed("auto_update", db_path): - raise HTTPException( - status_code=403, - detail="Fleet package synchronization requires an active license with the 'auto_update' capability." - ) mgr: ServerUpdateManager = SERVER_STATE.get("update_manager") or UPDATE_MANAGER results = mgr.sync_upstream_packages(force=True) return { @@ -1519,16 +1513,9 @@ def update_server_update_config(req: ServerUpdateConfigRequest, auth_user: str = """ Updates fleet auto-update settings (on/off switch and automatic vs manual acquisition mode). """ - db_path = SERVER_STATE.get("config", {}).get("db_path", DEFAULT_DB_FILE) config = SERVER_STATE.get("config", {}) mgr: ServerUpdateManager = SERVER_STATE.get("update_manager") or UPDATE_MANAGER - if req.auto_update_enabled and not is_feature_licensed("auto_update", db_path): - raise HTTPException( - status_code=403, - detail="Fleet auto-update requires an active license with the 'auto_update' capability." - ) - mode = (req.update_mode or "auto").lower() if mode not in ("auto", "manual"): raise HTTPException(status_code=400, detail="Invalid update_mode. Must be 'auto' (fetch from upstream) or 'manual' (provided only).") @@ -1550,7 +1537,7 @@ def update_server_update_config(req: ServerUpdateConfigRequest, auth_user: str = except Exception as e: print(f"[!] Warning: Could not save config file: {e}") - # If enabled and switched to auto mode, optionally trigger sync in background + # If enabled and in auto mode, trigger upstream check if mgr.auto_update_enabled and mgr.update_mode == "auto": try: mgr.check_upstream_updates(force=False, auto_download_clients=True) @@ -1594,14 +1581,6 @@ def check_client_update( except Exception: pass - if not is_feature_licensed("auto_update", db_path): - return { - "update_available": False, - "current_version": current_version or "0.0.0", - "target_version": current_version or "0.0.0", - "reason": "Fleet auto-update is not authorized by the server license." - } - return mgr.check_client_update(client_id=client_id, os_type=target_os, current_version=current_version, arch=arch) @@ -2363,13 +2342,10 @@ async def update_checker_loop(interval_hours: int = 1): await asyncio.sleep(initial_delay) while True: try: - db_path = SERVER_STATE.get("config", {}).get("db_path", DEFAULT_DB_FILE) - if is_feature_licensed("auto_update", db_path): - mgr: ServerUpdateManager = SERVER_STATE.get("update_manager") or UPDATE_MANAGER - if mgr.auto_update_enabled: - loop = asyncio.get_running_loop() - should_dl = (mgr.update_mode == "auto") - await loop.run_in_executor(None, lambda: mgr.check_upstream_updates(force=False, auto_download_clients=should_dl)) + mgr: ServerUpdateManager = SERVER_STATE.get("update_manager") or UPDATE_MANAGER + loop = asyncio.get_running_loop() + should_dl = bool(mgr.auto_update_enabled and mgr.update_mode == "auto") + await loop.run_in_executor(None, lambda: mgr.check_upstream_updates(force=False, auto_download_clients=should_dl)) except Exception as e: print(f"[!] Warning in update checker background loop: {e}") await asyncio.sleep(interval_hours * 3600) diff --git a/src/hub/web_ui.py b/src/hub/web_ui.py index 2c725aa..af8059a 100644 --- a/src/hub/web_ui.py +++ b/src/hub/web_ui.py @@ -651,8 +651,6 @@ class WebUIRenderer:
- {auto_update_lock_banner} -
@@ -683,14 +681,14 @@ class WebUIRenderer:
-
+
⚙️ Fleet Distribution & Auto-Update Policy
Configure hot-upgrade execution and choose how client binary images are acquired & hosted
- {('🔄 FLEET AUTO-UPDATE LICENSED' if is_auto_update_unlocked else '🔒 UNLICENSED')} + {('🔄 FLEET AUTO-UPDATE LICENSED' if is_auto_update_unlocked else 'COMMUNITY / UNRESTRICTED')}
@@ -702,11 +700,11 @@ class WebUIRenderer:
@@ -719,14 +717,14 @@ class WebUIRenderer:
- - + +
diff --git a/tests/test_license_authority.py b/tests/test_license_authority.py index 2cf2936..4477e84 100644 --- a/tests/test_license_authority.py +++ b/tests/test_license_authority.py @@ -323,11 +323,12 @@ class TestServerLicensingAndWebUI(unittest.TestCase): conn.commit() conn.close() - # WebUI should render locked banner and disabled upload controls + # WebUI should render update controls and community status resp = Server.serve_webui() html = resp.body.decode("utf-8") - self.assertIn("Fleet Auto-Update is Inactive & Locked", html) - self.assertIn("disabled", html) + self.assertIn("Fleet Distribution & Auto-Update Policy", html) + self.assertIn("COMMUNITY / UNRESTRICTED", html) + self.assertIn("autoUpdateEnabledRadio", html) def test_json_lic_package_import(self): """Verifies importing an activated .lic JSON package via /api/license/import.""" diff --git a/tests/test_updates.py b/tests/test_updates.py index b806bd1..e137749 100644 --- a/tests/test_updates.py +++ b/tests/test_updates.py @@ -130,14 +130,14 @@ class TestUpdateSystem(unittest.TestCase): f.write(b"WIN_CLIENT_V0360") self.update_mgr.register_client_package("windows", dummy_pkg, version="0.3.6.0") - # 3. Client update check endpoint (Unlicensed should block auto_update) - chk_blocked = Server.check_client_update( + # 3. Client update check endpoint (Allowed regardless of license) + chk_unlicensed = Server.check_client_update( client_id="win-client-01", os="windows", current_version="0.3.5.1" ) - self.assertFalse(chk_blocked["update_available"]) - self.assertIn("not authorized", chk_blocked.get("reason", "")) + self.assertTrue(chk_unlicensed["update_available"]) + self.assertEqual(chk_unlicensed["latest_version"], "0.3.6.0") # 4. Enable auto_update feature flag in license config import sqlite3