fix: ISO build freshness, WireGuard startup, VPN status, kiosk remote doubling
- ISO builder: run npm ci before npm run build to prevent stale UI artifacts - Unbundled ISO: clean container-images dir to prevent bundled tars leaking - WireGuard: use After=network.target instead of network-online.target for faster wg0 startup on install - VPN status: check actual nvpn0 interface instead of config tunnel_ip to prevent NostrVPN from showing standalone WireGuard IP - ContainerApps: filter out not-installed bundled apps (fixes Bitcoin Knots appearing on clean unbundled installs) - Kiosk: persist kiosk mode to localStorage before /kiosk redirect so App.vue can skip remote relay (fixes input doubling with companion app) - IndeedHub: fix port mapping and X-Forwarded-Prefix passthrough Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
401a44b40a
commit
8ffb10d7e0
@ -46,14 +46,37 @@ impl RpcHandler {
|
|||||||
let wg_pubkey = tokio::fs::read_to_string("/var/lib/archipelago/wireguard/public.key")
|
let wg_pubkey = tokio::fs::read_to_string("/var/lib/archipelago/wireguard/public.key")
|
||||||
.await.ok().map(|s| s.trim().to_string());
|
.await.ok().map(|s| s.trim().to_string());
|
||||||
|
|
||||||
// Don't report NostrVPN ip_address if it's the same as WireGuard (means tunnel not up)
|
// Check if nvpn0 tunnel interface actually exists and has an IP
|
||||||
let nvpn_ip = status.ip_address.as_ref().and_then(|ip| {
|
let nvpn0_ip = tokio::process::Command::new("ip")
|
||||||
let clean = ip.split('/').next().unwrap_or(ip);
|
.args(["-4", "addr", "show", "nvpn0"])
|
||||||
if wg_ip.as_deref() == Some(clean) { None } else { Some(ip.clone()) }
|
.output().await
|
||||||
|
.ok()
|
||||||
|
.and_then(|o| {
|
||||||
|
let out = String::from_utf8_lossy(&o.stdout).to_string();
|
||||||
|
out.lines()
|
||||||
|
.find(|l| l.contains("inet "))
|
||||||
|
.and_then(|l| l.split_whitespace().nth(1))
|
||||||
|
.map(|s| s.split('/').next().unwrap_or(s).to_string())
|
||||||
|
});
|
||||||
|
|
||||||
|
// NostrVPN IP: only report if nvpn0 tunnel is actually up with its own IP,
|
||||||
|
// and that IP is distinct from the standalone WireGuard IP
|
||||||
|
let nvpn_ip = nvpn0_ip.as_ref().and_then(|ip| {
|
||||||
|
if wg_ip.as_deref() == Some(ip.as_str()) { None } else { Some(ip.clone()) }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// NostrVPN is connected only if its dedicated tunnel (nvpn0) has a distinct IP
|
||||||
|
let nvpn_connected = status.provider.as_deref() == Some("nostr-vpn") && nvpn_ip.is_some();
|
||||||
|
|
||||||
|
// connected = NostrVPN tunnel is up OR another VPN provider is active OR standalone WireGuard is up
|
||||||
|
let is_connected = if status.provider.as_deref() == Some("nostr-vpn") {
|
||||||
|
nvpn_connected || wg_ip.is_some()
|
||||||
|
} else {
|
||||||
|
status.connected || wg_ip.is_some()
|
||||||
|
};
|
||||||
|
|
||||||
Ok(serde_json::json!({
|
Ok(serde_json::json!({
|
||||||
"connected": status.connected || wg_ip.is_some(),
|
"connected": is_connected,
|
||||||
"provider": status.provider,
|
"provider": status.provider,
|
||||||
"interface": status.interface,
|
"interface": status.interface,
|
||||||
"ip_address": nvpn_ip,
|
"ip_address": nvpn_ip,
|
||||||
|
|||||||
@ -1076,6 +1076,8 @@ if [ "$WEBUI_CAPTURED" = "0" ]; then
|
|||||||
echo " ⚠️ Could not capture from live server, building from source..."
|
echo " ⚠️ Could not capture from live server, building from source..."
|
||||||
fi
|
fi
|
||||||
cd "$SCRIPT_DIR/../neode-ui"
|
cd "$SCRIPT_DIR/../neode-ui"
|
||||||
|
echo " Installing frontend dependencies..."
|
||||||
|
npm ci --prefer-offline 2>&1 | tail -3
|
||||||
if npm run build 2>&1 | tail -5; then
|
if npm run build 2>&1 | tail -5; then
|
||||||
if [ -d "$SCRIPT_DIR/../web/dist/neode-ui" ]; then
|
if [ -d "$SCRIPT_DIR/../web/dist/neode-ui" ]; then
|
||||||
echo " Including web UI from web/dist/neode-ui..."
|
echo " Including web UI from web/dist/neode-ui..."
|
||||||
@ -1153,6 +1155,8 @@ if [ "$UNBUNDLED" = "1" ]; then
|
|||||||
# Marker file: first-boot-containers.sh checks this to skip app creation
|
# Marker file: first-boot-containers.sh checks this to skip app creation
|
||||||
touch "$ARCH_DIR/.unbundled"
|
touch "$ARCH_DIR/.unbundled"
|
||||||
IMAGES_DIR="$ARCH_DIR/container-images"
|
IMAGES_DIR="$ARCH_DIR/container-images"
|
||||||
|
# Clean stale images from previous builds (e.g. bundled build tars leaking into unbundled)
|
||||||
|
rm -rf "$IMAGES_DIR"
|
||||||
mkdir -p "$IMAGES_DIR"
|
mkdir -p "$IMAGES_DIR"
|
||||||
# FileBrowser is a core dependency (powers the Cloud file manager) — always bundle it
|
# FileBrowser is a core dependency (powers the Cloud file manager) — always bundle it
|
||||||
CORE_IMAGE="${FILEBROWSER_IMAGE}"
|
CORE_IMAGE="${FILEBROWSER_IMAGE}"
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Archipelago Standalone WireGuard (wg0)
|
Description=Archipelago Standalone WireGuard (wg0)
|
||||||
After=network-online.target
|
After=network.target
|
||||||
Wants=network-online.target
|
|
||||||
ConditionPathExists=/var/lib/archipelago/wireguard/private.key
|
ConditionPathExists=/var/lib/archipelago/wireguard/private.key
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
|
|||||||
@ -104,7 +104,8 @@ watch(() => appStore.isAuthenticated, (authenticated) => {
|
|||||||
screensaverStore.resetInactivityTimer()
|
screensaverStore.resetInactivityTimer()
|
||||||
// Don't start relay on kiosk — kiosk gets input via xdotool (system-level),
|
// Don't start relay on kiosk — kiosk gets input via xdotool (system-level),
|
||||||
// relay would duplicate every keystroke/click as DOM events
|
// relay would duplicate every keystroke/click as DOM events
|
||||||
const isKiosk = window.location.pathname.startsWith('/kiosk')
|
const isKiosk = localStorage.getItem('kiosk') === 'true'
|
||||||
|
|| new URLSearchParams(window.location.search).has('kiosk')
|
||||||
if (!isKiosk) {
|
if (!isKiosk) {
|
||||||
startRemoteRelay()
|
startRemoteRelay()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,7 +44,7 @@ const DISMISS_KEY = 'archipelago_pwa_install_dismissed'
|
|||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
// Don't show in kiosk mode, if already dismissed, or if already installed
|
// Don't show in kiosk mode, if already dismissed, or if already installed
|
||||||
if (window.location.pathname.startsWith('/kiosk')) return
|
if (localStorage.getItem('kiosk') === 'true') return
|
||||||
if (sessionStorage.getItem(DISMISS_KEY) === '1') return
|
if (sessionStorage.getItem(DISMISS_KEY) === '1') return
|
||||||
if (window.matchMedia('(display-mode: standalone)').matches) return
|
if (window.matchMedia('(display-mode: standalone)').matches) return
|
||||||
if ((window.navigator as Navigator & { standalone?: boolean }).standalone) return
|
if ((window.navigator as Navigator & { standalone?: boolean }).standalone) return
|
||||||
|
|||||||
@ -87,6 +87,11 @@ const router = createRouter({
|
|||||||
path: '/kiosk',
|
path: '/kiosk',
|
||||||
name: 'kiosk',
|
name: 'kiosk',
|
||||||
redirect: '/',
|
redirect: '/',
|
||||||
|
beforeEnter: () => {
|
||||||
|
// Persist kiosk mode before redirect so App.vue can skip the remote relay
|
||||||
|
// (relay duplicates xdotool input on the kiosk display)
|
||||||
|
localStorage.setItem('kiosk', 'true')
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/dashboard',
|
path: '/dashboard',
|
||||||
|
|||||||
@ -210,7 +210,10 @@ const store = useContainerStore()
|
|||||||
const appLauncherStore = useAppLauncherStore()
|
const appLauncherStore = useAppLauncherStore()
|
||||||
|
|
||||||
// Use enriched bundled apps with runtime data (like lan_address)
|
// Use enriched bundled apps with runtime data (like lan_address)
|
||||||
const bundledApps = computed(() => store.enrichedBundledApps)
|
// Only show apps that actually have a container (hides pre-defined apps on unbundled installs)
|
||||||
|
const bundledApps = computed(() => store.enrichedBundledApps.filter(
|
||||||
|
app => store.getAppState(app.id) !== 'not-installed'
|
||||||
|
))
|
||||||
|
|
||||||
// Get current host for launch URLs
|
// Get current host for launch URLs
|
||||||
const currentHost = computed(() => window.location.hostname)
|
const currentHost = computed(() => window.location.hostname)
|
||||||
|
|||||||
@ -173,7 +173,7 @@ echo "Creating indeedhub frontend..."
|
|||||||
podman run -d --name indeedhub \
|
podman run -d --name indeedhub \
|
||||||
--restart unless-stopped \
|
--restart unless-stopped \
|
||||||
--network "$NETWORK" \
|
--network "$NETWORK" \
|
||||||
-p 7777:7777 \
|
-p 7778:7777 \
|
||||||
--label "com.archipelago.app=indeedhub" \
|
--label "com.archipelago.app=indeedhub" \
|
||||||
--label "com.archipelago.title=IndeedHub" \
|
--label "com.archipelago.title=IndeedHub" \
|
||||||
--label "com.archipelago.version=0.1.0" \
|
--label "com.archipelago.version=0.1.0" \
|
||||||
@ -200,6 +200,11 @@ if podman ps --format '{{.Names}}' 2>/dev/null | grep -q "^indeedhub$"; then
|
|||||||
rm -f /tmp/ih-nginx.conf
|
rm -f /tmp/ih-nginx.conf
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Fix X-Forwarded-Prefix for NIP-98 URL reconstruction in iframe context
|
||||||
|
# The outer Archipelago nginx sets X-Forwarded-Prefix to /app/indeedhub;
|
||||||
|
# the inner nginx must pass it through (appending /api) instead of hardcoding /api
|
||||||
|
podman exec indeedhub sed -i 's|proxy_set_header X-Forwarded-Prefix /api;|proxy_set_header X-Forwarded-Prefix $http_x_forwarded_prefix/api;|' /etc/nginx/conf.d/default.conf 2>/dev/null || true
|
||||||
|
|
||||||
# Replace DNS-based upstream resolution with hardcoded container IPs
|
# Replace DNS-based upstream resolution with hardcoded container IPs
|
||||||
# (podman DNS resolver 127.0.0.11 is unreliable, causing 502 errors)
|
# (podman DNS resolver 127.0.0.11 is unreliable, causing 502 errors)
|
||||||
API_IP=$(podman inspect indeedhub-build_api_1 --format "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" 2>/dev/null)
|
API_IP=$(podman inspect indeedhub-build_api_1 --format "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" 2>/dev/null)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user