diff --git a/app/sovran_systemsos_web/server.py b/app/sovran_systemsos_web/server.py index 793d508..83f34ac 100644 --- a/app/sovran_systemsos_web/server.py +++ b/app/sovran_systemsos_web/server.py @@ -2576,6 +2576,15 @@ def _get_service_version(unit: str) -> str | None: ) if result.returncode == 0 and result.stdout.strip(): m = _NIX_STORE_VERSION_RE.search(result.stdout) + # Some wrapped systemd commands contain an escaped or nested + # store path that the strict package-path expression misses. Fall + # back to scanning each store path segment for the version token. + if not m: + for store_path in re.findall(r"/nix/store/[a-z0-9]{32}-[^\\s/\\\"]+", result.stdout): + candidates = re.findall(r"(?:^|-)(\\d+\\.\\d+(?:\\.\\d+)*(?:[+-][a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*)?)$", store_path) + if candidates: + m = type("_VersionMatch", (), {"group": lambda self, n: candidates[-1]})() + break if m: ver = m.group(1) # Strip a single trailing period (defensive; shouldn't appear in store paths) diff --git a/app/sovran_systemsos_web/static/css/tiles.css b/app/sovran_systemsos_web/static/css/tiles.css index 94e9f7f..d918c64 100644 --- a/app/sovran_systemsos_web/static/css/tiles.css +++ b/app/sovran_systemsos_web/static/css/tiles.css @@ -256,6 +256,80 @@ color: var(--text-primary); } +/* Version lives in the detail modal so compact tiles stay visually stable. */ +.svc-detail-version-card { + display: flex; + align-items: center; + gap: 12px; + margin: -6px 0 20px; + padding: 12px 14px; + border: 1px solid rgba(109, 191, 139, 0.28); + border-radius: 10px; + background: linear-gradient(135deg, rgba(109, 191, 139, 0.12), rgba(109, 191, 139, 0.04)); +} + +.svc-detail-version-icon { + display: grid; + place-items: center; + width: 30px; + height: 30px; + border-radius: 8px; + background: rgba(109, 191, 139, 0.18); + color: var(--accent-color); + font-size: 1.35rem; + font-weight: 700; +} + +.svc-detail-version-label { + color: var(--text-dim); + font-size: 0.7rem; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.svc-detail-version-copy { + min-width: 0; +} + +.svc-detail-version-state { + display: inline-block; + margin-left: 6px; + padding: 2px 6px; + border-radius: 999px; + color: var(--accent-color); + background: rgba(109, 191, 139, 0.14); + font-size: 0.62rem; + letter-spacing: 0.04em; +} + +.svc-detail-version-card--unavailable, +.svc-detail-version-card--not-applicable { + border-color: rgba(229, 165, 10, 0.3); + background: rgba(229, 165, 10, 0.07); +} + +.svc-detail-version-card--unavailable .svc-detail-version-icon, +.svc-detail-version-card--not-applicable .svc-detail-version-icon { + color: var(--yellow); + background: rgba(229, 165, 10, 0.14); +} + +.svc-detail-version-card--unavailable .svc-detail-version-state, +.svc-detail-version-card--not-applicable .svc-detail-version-state { + color: var(--yellow); + background: rgba(229, 165, 10, 0.14); +} + +.svc-detail-version-value { + margin-top: 2px; + color: var(--text-primary); + font-family: 'JetBrains Mono', 'Fira Code', monospace; + font-size: 0.95rem; + font-weight: 700; + word-break: break-word; +} + /* ── Service detail: Domain ──────────────────────────────────────── */ .svc-detail-domain-value { diff --git a/app/sovran_systemsos_web/static/js/service-detail.js b/app/sovran_systemsos_web/static/js/service-detail.js index 060574a..2d077e8 100644 --- a/app/sovran_systemsos_web/static/js/service-detail.js +++ b/app/sovran_systemsos_web/static/js/service-detail.js @@ -659,6 +659,29 @@ async function openServiceDetailModal(unit, name, icon) { '' + ''); + // Keep package versions in the detail modal rather than the compact tile. + // The API uses `version` for regular services and `bitcoin_version` for + // older Bitcoin payloads, so accept both while the backend is upgraded. + var serviceVersion = data.version || data.bitcoin_version || ''; + // These services are wrappers, setup helpers, or application stacks rather + // than a single versioned daemon represented by the tile's systemd unit. + var versionNotApplicable = [ + 'phpfpm-wordpress.service', 'phpfpm-nextcloud.service', + 'albyhub.service', 'nwc-wallets.service', + 'zeus-connect-setup.service', 'sparrow-autoconnect.service' + ].indexOf(unit) !== -1; + var versionState = serviceVersion ? 'detected' : (versionNotApplicable ? 'not-applicable' : 'unavailable'); + var versionText = serviceVersion || (versionNotApplicable + ? 'This service does not expose a single package version' + : (effectiveEnabled ? 'Version could not be detected' : 'Start the service to detect its version')); + var versionBadge = serviceVersion ? 'Detected' : (versionNotApplicable ? 'Not applicable' : 'Not detected'); + addSetup('