diff --git a/app/sovran_systemsos_web/server.py b/app/sovran_systemsos_web/server.py index 8233497..aaaf9b9 100644 --- a/app/sovran_systemsos_web/server.py +++ b/app/sovran_systemsos_web/server.py @@ -30,6 +30,8 @@ UPDATE_LOG = "/var/log/sovran-hub-update.log" UPDATE_STATUS = "/var/log/sovran-hub-update.status" UPDATE_UNIT = "sovran-hub-update.service" +INTERNAL_IP_FILE = "/var/lib/secrets/internal-ip" + REBOOT_COMMAND = ["reboot"] CATEGORY_ORDER = [ @@ -154,6 +156,17 @@ def _get_internal_ip() -> str: return "unavailable" +def _save_internal_ip(ip: str): + """Write the internal IP to a file so credentials can reference it.""" + if ip and ip != "unavailable": + try: + os.makedirs(os.path.dirname(INTERNAL_IP_FILE), exist_ok=True) + with open(INTERNAL_IP_FILE, "w") as f: + f.write(ip) + except OSError: + pass + + def _get_external_ip() -> str: MAX_IP_LENGTH = 46 for url in [ @@ -245,7 +258,7 @@ def _resolve_credential(cred: dict) -> dict | None: return {"label": label, "value": value, "multiline": multiline} -# ── Routes ───────────��─────────────────────────────────────────── +# ── Routes ─────────────────────────────────────────────────────── @app.get("/", response_class=HTMLResponse) async def index(request: Request): @@ -392,6 +405,8 @@ async def api_network(): loop.run_in_executor(None, _get_internal_ip), loop.run_in_executor(None, _get_external_ip), ) + # Keep the internal-ip file in sync for credential lookups + _save_internal_ip(internal) return {"internal_ip": internal, "external_ip": external} @@ -461,4 +476,14 @@ async def api_updates_status(offset: int = 0): "result": result, "log": new_log, "offset": new_offset, - } \ No newline at end of file + } + + +# ── Startup: seed the internal IP file immediately ─────────────── + +@app.on_event("startup") +async def _startup_save_ip(): + """Write internal IP to file on server start so credentials work immediately.""" + loop = asyncio.get_event_loop() + ip = await loop.run_in_executor(None, _get_internal_ip) + _save_internal_ip(ip) \ No newline at end of file diff --git a/app/sovran_systemsos_web/static/style.css b/app/sovran_systemsos_web/static/style.css index e78c96c..5db74e6 100644 --- a/app/sovran_systemsos_web/static/style.css +++ b/app/sovran_systemsos_web/static/style.css @@ -793,13 +793,15 @@ button.btn-reboot:hover:not(:disabled) { /* ── Credential links ─────────────────────────────────────── */ .creds-link { - color: #58a6ff; + color: #7ee787; text-decoration: none; word-break: break-all; } .creds-link:hover { text-decoration: underline; - color: #79c0ff; + color: #a5f0b0; +} + } diff --git a/modules/core/sovran-hub.nix b/modules/core/sovran-hub.nix index a0f1d1f..fda649b 100644 --- a/modules/core/sovran-hub.nix +++ b/modules/core/sovran-hub.nix @@ -209,37 +209,6 @@ LAUNCHER in { config = { - # ── Save internal IP for hub credentials ──────────────────── - systemd.services.save-internal-ip = { - description = "Save internal IP address for hub credentials"; - wantedBy = [ "multi-user.target" ]; - after = [ "network-online.target" ]; - wants = [ "network-online.target" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - path = [ pkgs.iproute2 pkgs.coreutils pkgs.hostname ]; - script = '' - mkdir -p /var/lib/secrets - IP=$(hostname -I | awk '{print $1}') - if [ -n "$IP" ]; then - echo "$IP" > /var/lib/secrets/internal-ip - chmod 644 /var/lib/secrets/internal-ip - fi - ''; - }; - - # ── Refresh IP periodically (in case DHCP changes it) ────── - systemd.timers.save-internal-ip = { - wantedBy = [ "timers.target" ]; - timerConfig = { - OnBootSec = "30s"; - OnUnitActiveSec = "15min"; - Unit = "save-internal-ip.service"; - }; - }; - # ── Web server as a systemd service ──────────────────────── systemd.services.sovran-hub-web = { description = "Sovran_SystemsOS Hub Web Interface";