diff --git a/app/sovran_systemsos_web/server.py b/app/sovran_systemsos_web/server.py index 3180f92..a0259bf 100644 --- a/app/sovran_systemsos_web/server.py +++ b/app/sovran_systemsos_web/server.py @@ -57,6 +57,7 @@ ZEUS_CONNECT_FILE = "/var/lib/secrets/zeus-connect-url" REBOOT_COMMAND = ["reboot"] ONBOARDING_FLAG = "/var/lib/sovran/onboarding-complete" +AUTOLAUNCH_DISABLE_FLAG = "/var/lib/sovran/hub-autolaunch-disabled" # ── Tech Support constants ──────────────────────────────────────── @@ -1390,6 +1391,39 @@ async def api_onboarding_complete(): return {"ok": True} +# ── Auto-launch endpoints ───────────────────────────────────────── + +@app.get("/api/autolaunch/status") +async def api_autolaunch_status(): + """Check if Hub auto-launch on login is enabled.""" + disabled = os.path.exists(AUTOLAUNCH_DISABLE_FLAG) + return {"enabled": not disabled} + + +class AutolaunchToggleRequest(BaseModel): + enabled: bool + + +@app.post("/api/autolaunch/toggle") +async def api_autolaunch_toggle(req: AutolaunchToggleRequest): + """Enable or disable Hub auto-launch on login.""" + if req.enabled: + # Remove the disable flag to enable auto-launch + try: + os.remove(AUTOLAUNCH_DISABLE_FLAG) + except FileNotFoundError: + pass + else: + # Create the disable flag to suppress auto-launch + os.makedirs(os.path.dirname(AUTOLAUNCH_DISABLE_FLAG), exist_ok=True) + try: + with open(AUTOLAUNCH_DISABLE_FLAG, "w") as f: + f.write("") + except OSError as exc: + raise HTTPException(status_code=500, detail=f"Could not write flag file: {exc}") + return {"ok": True, "enabled": req.enabled} + + @app.get("/api/config") async def api_config(): cfg = load_config() diff --git a/app/sovran_systemsos_web/static/js/events.js b/app/sovran_systemsos_web/static/js/events.js index 8d5e9ea..a39ef00 100644 --- a/app/sovran_systemsos_web/static/js/events.js +++ b/app/sovran_systemsos_web/static/js/events.js @@ -105,12 +105,14 @@ async function init() { if (cfg.feature_manager) { loadFeatureManager(); } + loadAutolaunchToggle(); } catch (_) { await refreshServices(); loadNetwork(); checkUpdates(); setInterval(refreshServices, POLL_INTERVAL_SERVICES); setInterval(checkUpdates, POLL_INTERVAL_UPDATES); + loadAutolaunchToggle(); } } diff --git a/app/sovran_systemsos_web/static/js/features.js b/app/sovran_systemsos_web/static/js/features.js index 4c92cfb..0b30519 100644 --- a/app/sovran_systemsos_web/static/js/features.js +++ b/app/sovran_systemsos_web/static/js/features.js @@ -579,3 +579,66 @@ function buildFeatureCard(feat) { return card; } + +// ── Auto-launch toggle ──────────────────────────────────────────── + +async function loadAutolaunchToggle() { + try { + var data = await apiFetch("/api/autolaunch/status"); + renderAutolaunchToggle(data.enabled); + } catch (err) { + console.warn("Failed to load autolaunch status:", err); + } +} + +function renderAutolaunchToggle(enabled) { + // Remove existing section if any + var old = $sidebarFeatures.querySelector(".autolaunch-section"); + if (old) old.parentNode.removeChild(old); + + var section = document.createElement("div"); + section.className = "category-section autolaunch-section"; + + section.innerHTML = + '
Preferences
' + + '
' + + '
' + + '
' + + '
' + + '
Auto-launch Hub on Login
' + + '
Automatically open the Sovran Hub dashboard in your browser when you log in to the desktop.
' + + '
' + + '' + + '
' + + '
'; + + $sidebarFeatures.appendChild(section); + + var input = document.getElementById("autolaunch-toggle-input"); + var label = document.getElementById("autolaunch-toggle-label"); + if (!input || !label) return; + + input.addEventListener("change", async function() { + var newEnabled = input.checked; + // Revert visually until confirmed + input.checked = !newEnabled; + if (newEnabled) { label.classList.remove("active"); } else { label.classList.add("active"); } + input.disabled = true; + try { + await apiFetch("/api/autolaunch/toggle", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ enabled: newEnabled }), + }); + input.checked = newEnabled; + if (newEnabled) { label.classList.add("active"); } else { label.classList.remove("active"); } + } catch (err) { + alert("Failed to update auto-launch setting. Please try again."); + } finally { + input.disabled = false; + } + }); +} diff --git a/modules/core/sovran-hub.nix b/modules/core/sovran-hub.nix index c161b50..e0309c9 100644 --- a/modules/core/sovran-hub.nix +++ b/modules/core/sovran-hub.nix @@ -203,6 +203,30 @@ let fi ''; + # ── Hub auto-launch wrapper script ──────────────────────────────── + hub-autolaunch-script = pkgs.writeShellScript "sovran-hub-autolaunch.sh" '' + export PATH="${lib.makeBinPath [ pkgs.curl pkgs.xdg-utils ]}:$PATH" + + DISABLE_FLAG="/var/lib/sovran/hub-autolaunch-disabled" + BOOT_FLAG="/run/sovran-hub-autolaunch-done" + + # User disabled auto-launch via Hub toggle + [ -f "$DISABLE_FLAG" ] && exit 0 + + # Already launched this boot + [ -f "$BOOT_FLAG" ] && exit 0 + + touch "$BOOT_FLAG" + + # Wait for Hub server to become ready (max ~15 seconds) + for i in $(seq 1 15); do + curl -s -o /dev/null http://localhost:8937 && break + sleep 1 + done + + xdg-open http://localhost:8937 + ''; + sovran-hub-web = pkgs.python3Packages.buildPythonApplication { pname = "sovran-systemsos-hub-web"; version = "1.0.0"; @@ -313,5 +337,16 @@ in networking.firewall.allowedTCPPorts = [ 3051 8937 60847 ]; + # ── Auto-launch Hub in browser on login ─────────────────────── + environment.etc."xdg/autostart/sovran-hub-autolaunch.desktop".text = '' +[Desktop Entry] +Type=Application +Name=Sovran Hub Auto-Launch +Exec=${hub-autolaunch-script} +Terminal=false +X-GNOME-Autostart-enabled=true +NoDisplay=true +''; + }; }