fix: prevent My Apps crash when installing apps + add filebrowser to demo

The My Apps page went blank after installing apps because pkg['static-files'].icon
was accessed without optional chaining on dynamically installed packages that lack
the static-files property.

- Make static-files optional in PackageDataEntry type
- Add defensive ?.icon access with fallback in Apps.vue and AppDetails.vue
- Add filebrowser to mock backend staticDevApps (enables Cloud page in demo)
- Expand portMappings and marketplaceMetadata for all marketplace apps
- installPackage now uses staticApp() format for consistent data shape

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-09 17:09:59 +00:00
parent 9c7ffbb263
commit a2aa9657b1
24 changed files with 1200 additions and 141 deletions

View File

@@ -356,10 +356,12 @@ INSTALLER_ISO="$WORK_DIR/installer-iso"
rm -rf "$INSTALLER_ISO"
mkdir -p "$INSTALLER_ISO"
cd "$INSTALLER_ISO"
(7z x -y "$BASE_ISO" 2>/dev/null || 7za x -y "$BASE_ISO" 2>/dev/null || bsdtar -xf "$BASE_ISO" 2>/dev/null) || {
# 7z returns exit code 2 for warnings (symlinks in ISO) — check for key files instead
7z x -y "$BASE_ISO" >/dev/null 2>&1 || 7za x -y "$BASE_ISO" >/dev/null 2>&1 || bsdtar -xf "$BASE_ISO" 2>/dev/null || true
if [ ! -d "$INSTALLER_ISO/live" ] || [ ! -f "$INSTALLER_ISO/live/vmlinuz" ]; then
echo " ❌ Failed to extract ISO. Install p7zip-full: sudo apt install p7zip-full"
exit 1
}
fi
# =============================================================================
# STEP 3: Add Archipelago components
@@ -501,7 +503,8 @@ mkdir -p "$IMAGES_DIR"
IMAGES_CAPTURED_FROM_SERVER=0
if [ -n "$DEV_SERVER" ] && [ "$DEV_SERVER" != "localhost" ] && [ "$DEV_SERVER" != "127.0.0.1" ]; then
echo " Capturing container images from live server ($DEV_SERVER)..."
CAPTURE_PATTERNS="bitcoin-ui bitcoin-knots lnd lnd-ui electrs-ui filebrowser mempool mempool-electrs tailscale homeassistant btcpayserver nbxplorer postgres nostr-rs-relay strfry alpine-tor fedimintd gatewayd dwn-server"
# Patterns match against `podman images` repository names (not container names)
CAPTURE_PATTERNS="bitcoin-ui bitcoinknots lnd lnd-ui electrs-ui filebrowser mempool backend frontend electrs tailscale homeassistant home-assistant btcpayserver nbxplorer postgres alpine-tor nostr-rs-relay strfry fedimintd gatewayd dwn-server grafana uptime-kuma jellyfin vaultwarden searxng mariadb valkey nginx-alpine portainer"
REMOTE_TMP="/tmp/archipelago-image-capture-$$"
SAVED_LIST=$(ssh "$DEV_SERVER" "mkdir -p $REMOTE_TMP && for p in $CAPTURE_PATTERNS; do img=\$(sudo podman images --format '{{.Repository}}:{{.Tag}}' 2>/dev/null | grep -i \"\$p\" | head -1); [ -n \"\$img\" ] && sudo podman save -o \"$REMOTE_TMP/\$p.tar\" \"\$img\" 2>/dev/null && echo \"\$p\"; done" 2>/dev/null) || true
for p in $SAVED_LIST; do
@@ -518,26 +521,30 @@ fi
# Define images to bundle for fallback (when not from server or missing). Includes filebrowser.
# bitcoin-ui and lnd-ui are custom and normally captured from server or built separately.
# Alpha: core Bitcoin/Lightning stack + essential apps. Others pulled on-demand from Marketplace.
CONTAINER_IMAGES="
bitcoinknots/bitcoin:29 bitcoin-knots.tar
lightninglabs/lnd:v0.18.4-beta lnd.tar
ghcr.io/home-assistant/home-assistant:stable homeassistant.tar
btcpayserver/btcpayserver:latest btcpayserver.tar
docker.io/bitcoinknots/bitcoin:latest bitcoin-knots.tar
docker.io/lightninglabs/lnd:v0.18.4-beta lnd.tar
docker.io/homeassistant/home-assistant:2024.1 homeassistant.tar
docker.io/btcpayserver/btcpayserver:1.13.5 btcpayserver.tar
docker.io/nicolasdorier/nbxplorer:2.6.0 nbxplorer.tar
docker.io/library/postgres:16 postgres-btcpay.tar
mempool/frontend:latest mempool-frontend.tar
mempool/backend:v2.5.0 mempool-backend.tar
mempool/electrs:latest mempool-electrs.tar
docker.io/mariadb:10.11 mariadb-mempool.tar
docker.io/library/postgres:15-alpine postgres-btcpay.tar
docker.io/mempool/frontend:v2.5.0 mempool-frontend.tar
docker.io/mempool/backend:v2.5.0 mempool-backend.tar
docker.io/mempool/electrs:latest mempool-electrs.tar
docker.io/library/mariadb:10.11 mariadb-mempool.tar
docker.io/fedimint/fedimintd:v0.10.0 fedimint.tar
docker.io/fedimint/gatewayd:v0.10.0 fedimint-gateway.tar
docker.io/filebrowser/filebrowser:latest filebrowser.tar
scsibug/nostr-rs-relay:latest nostr-rs-relay.tar
hoytech/strfry:latest strfry.tar
tailscale/tailscale:latest tailscale.tar
docker.io/filebrowser/filebrowser:v2.27.0 filebrowser.tar
docker.io/andrius/alpine-tor:latest alpine-tor.tar
docker.io/library/nginx:alpine nginx-alpine.tar
ghcr.io/tbd54566975/dwn-server:main dwn-server.tar
docker.io/grafana/grafana:10.2.0 grafana.tar
docker.io/louislam/uptime-kuma:1 uptime-kuma.tar
docker.io/vaultwarden/server:1.30.0-alpine vaultwarden.tar
docker.io/searxng/searxng:latest searxng.tar
docker.io/portainer/portainer-ce:2.19.4 portainer.tar
docker.io/tailscale/tailscale:stable tailscale.tar
"
# Pull and save each image (force AMD64 for x86_64 target) only if not already present
@@ -722,6 +729,13 @@ FBCSERVICE
cp "$WORK_DIR/archipelago-first-boot-containers.service" "$ARCH_DIR/scripts/"
fi
# Bundle E2E test script for post-install validation
if [ -f "$SCRIPT_DIR/../scripts/run-e2e-tests.sh" ]; then
cp "$SCRIPT_DIR/../scripts/run-e2e-tests.sh" "$ARCH_DIR/scripts/"
chmod +x "$ARCH_DIR/scripts/run-e2e-tests.sh"
echo " ✅ Bundled E2E test script for post-install validation"
fi
# Bundle docker UI source files for building custom UIs on first boot (fallback if images not captured)
DOCKER_UI_DIR="$SCRIPT_DIR/../docker"
if [ -d "$DOCKER_UI_DIR" ]; then
@@ -992,6 +1006,17 @@ if [ -d "$BOOT_MEDIA/archipelago/container-images" ]; then
echo " ✅ Container images staged for first-boot loading"
fi
# Initialize backend data directories for seamless first boot
mkdir -p /mnt/target/var/lib/archipelago/tor-config
mkdir -p /mnt/target/var/lib/archipelago/identities
mkdir -p /mnt/target/var/lib/archipelago/lnd
# Copy E2E test script for post-install validation
if [ -f "$BOOT_MEDIA/archipelago/scripts/run-e2e-tests.sh" ]; then
cp "$BOOT_MEDIA/archipelago/scripts/run-e2e-tests.sh" /mnt/target/opt/archipelago/scripts/
chmod +x /mnt/target/opt/archipelago/scripts/run-e2e-tests.sh
fi
# Ensure correct ownership (use numeric UID:GID 1000:1000 since we're outside chroot)
chown -R 1000:1000 /mnt/target/opt/archipelago 2>/dev/null || true
chown -R 1000:1000 /mnt/target/var/lib/archipelago 2>/dev/null || true
@@ -1143,6 +1168,8 @@ echo -e "${GREEN}║ Pre-loaded apps (ready to start via Web UI):
echo -e "${GREEN}║ • Bitcoin Knots • LND • Home Assistant ║${NC}"
echo -e "${GREEN}║ • BTCPay Server • Mempool • Nostr Relays ║${NC}"
echo -e "${GREEN}║ ║${NC}"
echo -e "${GREEN}║ Validate: bash /opt/archipelago/scripts/run-e2e-tests.sh ║${NC}"
echo -e "${GREEN}║ ║${NC}"
echo -e "${GREEN}╚═══════════════════════════════════════════════════════════════════╝${NC}"
echo ""
read -p "Press Enter to reboot..."
@@ -1376,37 +1403,88 @@ echo "📦 Step 6: Creating bootable ISO..."
OUTPUT_ISO="$OUTPUT_DIR/archipelago-installer-x86_64.iso"
# Get MBR for hybrid boot
ISOHDPFX=""
for path in \
"/usr/local/share/syslinux/isohdpfx.bin" \
"/usr/share/syslinux/isohdpfx.bin" \
"/opt/homebrew/share/syslinux/isohdpfx.bin" \
"$INSTALLER_ISO/isolinux/isohdpfx.bin"; do
if [ -f "$path" ]; then
ISOHDPFX="$path"
# Extract MBR from original Debian Live ISO (most reliable for hybrid boot)
# This preserves the exact MBR that makes the ISO work as a USB drive in Balena Etcher
echo " Extracting hybrid MBR from original Debian Live ISO..."
ISOHDPFX="$WORK_DIR/isohdpfx.bin"
dd if="$BASE_ISO" bs=1 count=432 of="$ISOHDPFX" 2>/dev/null
# Verify we got a valid MBR (should be 432 bytes)
ISOHDPFX_SIZE=$(stat -c%s "$ISOHDPFX" 2>/dev/null || stat -f%z "$ISOHDPFX" 2>/dev/null || echo 0)
if [ "$ISOHDPFX_SIZE" -ne 432 ]; then
echo " ⚠️ MBR extraction unexpected size ($ISOHDPFX_SIZE), trying syslinux paths..."
for path in \
"/usr/lib/ISOLINUX/isohdpfx.bin" \
"/usr/share/syslinux/isohdpfx.bin" \
"/usr/local/share/syslinux/isohdpfx.bin"; do
if [ -f "$path" ]; then
ISOHDPFX="$path"
echo " Using $path"
break
fi
done
fi
# Find the EFI boot image — 7z may extract it to different locations
EFI_IMG=""
for efi_path in \
"$INSTALLER_ISO/boot/grub/efi.img" \
"$INSTALLER_ISO/EFI/boot/efi.img" \
"$INSTALLER_ISO/efi.img"; do
if [ -f "$efi_path" ]; then
EFI_IMG="$efi_path"
break
fi
done
if [ -z "$ISOHDPFX" ]; then
echo " Extracting MBR from isolinux.bin..."
dd if="$INSTALLER_ISO/isolinux/isolinux.bin" of="$WORK_DIR/isohdpfx.bin" bs=432 count=1 2>/dev/null
ISOHDPFX="$WORK_DIR/isohdpfx.bin"
# If no standalone efi.img, check for [BOOT] directory from 7z extraction
if [ -z "$EFI_IMG" ] && [ -d "$INSTALLER_ISO/[BOOT]" ]; then
# 7z extracts El Torito boot images into [BOOT]/ — the EFI image is usually entry 2
for entry in "$INSTALLER_ISO/[BOOT]/"*; do
# EFI images are typically > 1MB FAT filesystems
if [ -f "$entry" ]; then
entry_size=$(stat -c%s "$entry" 2>/dev/null || stat -f%z "$entry" 2>/dev/null || echo 0)
if [ "$entry_size" -gt 1048576 ]; then
mkdir -p "$INSTALLER_ISO/boot/grub"
cp "$entry" "$INSTALLER_ISO/boot/grub/efi.img"
EFI_IMG="$INSTALLER_ISO/boot/grub/efi.img"
echo " Recovered EFI image from [BOOT] directory"
break
fi
fi
done
fi
xorriso -as mkisofs -o "$OUTPUT_ISO" \
-volid "ARCHIPELAGO" \
-J -R \
-isohybrid-mbr "$ISOHDPFX" \
-c isolinux/boot.cat \
-b isolinux/isolinux.bin \
-no-emul-boot -boot-load-size 4 -boot-info-table \
-eltorito-alt-boot \
-e boot/grub/efi.img \
-no-emul-boot \
-isohybrid-gpt-basdat \
"$INSTALLER_ISO"
if [ -z "$EFI_IMG" ]; then
echo " ⚠️ No EFI boot image found — ISO will only support Legacy BIOS boot"
xorriso -as mkisofs -o "$OUTPUT_ISO" \
-volid "ARCHIPELAGO" \
-iso-level 3 \
-J -joliet-long -R \
-isohybrid-mbr "$ISOHDPFX" \
-c isolinux/boot.cat \
-b isolinux/isolinux.bin \
-no-emul-boot -boot-load-size 4 -boot-info-table \
-partition_offset 16 \
"$INSTALLER_ISO"
else
# Make EFI path relative to INSTALLER_ISO for xorriso
EFI_REL="${EFI_IMG#$INSTALLER_ISO/}"
xorriso -as mkisofs -o "$OUTPUT_ISO" \
-volid "ARCHIPELAGO" \
-iso-level 3 \
-J -joliet-long -R \
-isohybrid-mbr "$ISOHDPFX" \
-c isolinux/boot.cat \
-b isolinux/isolinux.bin \
-no-emul-boot -boot-load-size 4 -boot-info-table \
-eltorito-alt-boot \
-e "$EFI_REL" \
-no-emul-boot \
-isohybrid-gpt-basdat \
-partition_offset 16 \
"$INSTALLER_ISO"
fi
echo ""
echo "╔════════════════════════════════════════════════════════════════╗"