From a34075287d2a732bfbbef3afb27dbe4ac381eda4 Mon Sep 17 00:00:00 2001 From: Dorian Date: Tue, 7 Apr 2026 22:05:08 +0100 Subject: [PATCH] fix: nostr-vpn service crash on reboot, detect activating state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove ReadWritePaths sandbox (causes namespace error when /run/nostr-vpn doesn't exist after reboot — /run is tmpfs) - Detect both 'active' and 'activating' states in VPN status check Co-Authored-By: Claude Opus 4.6 (1M context) --- core/archipelago/src/vpn.rs | 9 +++--- image-recipe/configs/nostr-vpn.service | 4 +-- neode-ui/src/stores/server.ts | 21 ++++++++++++-- neode-ui/src/views/apps/AppCard.vue | 38 ++++++++++++++++---------- 4 files changed, 48 insertions(+), 24 deletions(-) diff --git a/core/archipelago/src/vpn.rs b/core/archipelago/src/vpn.rs index c7d24072..f5fdc5eb 100644 --- a/core/archipelago/src/vpn.rs +++ b/core/archipelago/src/vpn.rs @@ -202,14 +202,15 @@ pub async fn get_status() -> VpnStatus { /// Check if NostrVPN system service is running and get its status. async fn get_nostr_vpn_status() -> Result { - // Check if nostr-vpn service is active - let active = tokio::process::Command::new("systemctl") + // Check if nostr-vpn service is active or activating + let output = tokio::process::Command::new("systemctl") .args(["is-active", "nostr-vpn"]) .output() .await - .map(|o| o.status.success()) - .unwrap_or(false); + .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()) + .unwrap_or_default(); + let active = output == "active" || output == "activating"; if !active { anyhow::bail!("nostr-vpn service not active"); } diff --git a/image-recipe/configs/nostr-vpn.service b/image-recipe/configs/nostr-vpn.service index 297815d6..217dccec 100644 --- a/image-recipe/configs/nostr-vpn.service +++ b/image-recipe/configs/nostr-vpn.service @@ -16,9 +16,7 @@ RestartSec=10 TimeoutStartSec=30 TimeoutStopSec=10 -# Security — runs as root for TUN/WireGuard access -ReadWritePaths=/var/lib/archipelago/nostr-vpn /run/nostr-vpn -RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK +# No sandbox — runs as root for TUN/WireGuard, needs unrestricted filesystem # Resource limits MemoryMax=256M diff --git a/neode-ui/src/stores/server.ts b/neode-ui/src/stores/server.ts index c3851408..080502cd 100644 --- a/neode-ui/src/stores/server.ts +++ b/neode-ui/src/stores/server.ts @@ -35,11 +35,17 @@ export const useServerStore = defineStore('server', () => { const pct = progress.size > 0 ? Math.round((progress.downloaded / progress.size) * 100) : 0 const downloadedMB = (progress.downloaded / (1024 * 1024)).toFixed(1) const totalMB = (progress.size / (1024 * 1024)).toFixed(1) + let message = 'Downloading...' + if (progress.size > 1024 && pct < 100) { + message = `Downloading: ${downloadedMB} / ${totalMB} MB (${pct}%)` + } else if (pct >= 100 || (progress.size > 0 && progress.downloaded >= progress.size)) { + message = 'Installing package...' + } installingApps.value.set(appId, { ...current, - status: 'downloading', + status: pct >= 100 ? 'installing' : 'downloading', progress: Math.min(pct, 95), - message: progress.size > 0 ? `Downloading: ${downloadedMB} / ${totalMB} MB (${pct}%)` : 'Downloading...', + message, }) } } else if (installingApps.value.has(appId)) { @@ -50,6 +56,17 @@ export const useServerStore = defineStore('server', () => { } } } + // Clear installingApps entries for apps that vanished from backend data + // (container was removed, install failed and was cleaned up, etc.) + for (const [appId] of installingApps.value) { + if (packages && !(appId in packages)) { + const entry = installingApps.value.get(appId) + if (entry && entry.attempt > 30) { + // App has been "installing" for 30+ seconds but backend doesn't know about it — failed + installingApps.value.delete(appId) + } + } + } }, { deep: true }) function setInstallProgress(appId: string, progress: Partial & { id: string; title: string }) { diff --git a/neode-ui/src/views/apps/AppCard.vue b/neode-ui/src/views/apps/AppCard.vue index 8dbc237d..6681e934 100644 --- a/neode-ui/src/views/apps/AppCard.vue +++ b/neode-ui/src/views/apps/AppCard.vue @@ -10,19 +10,7 @@ @click="$emit('goToApp', id)" @keydown.enter="handleEnter" > - -
-
- - - - - {{ installProgress?.message || 'Installing...' }} -
-
+
-
+
-
+ +
+
+ + + + + + {{ installProgress?.message || 'Installing...' }} + + {{ Math.round(installProgress?.progress || 0) }}% +
+
+
+
+
+ +