Implement multi-container app installation for Immich and Penpot, enhance Docker package scanning, and update Nginx configuration for iframe support

- Added support for installing Immich and Penpot stacks, including necessary Docker images and network configurations.
- Updated DockerPackageScanner to exclude Immich and Penpot related containers from app listings.
- Enhanced Nginx configuration to support iframe embedding for Immich and Penpot applications, improving user experience.
- Modified deployment scripts to ensure proper setup of first-boot container creation services.
This commit is contained in:
Dorian
2026-02-25 18:04:41 +00:00
parent f0ef84e4a5
commit 4cb9ac1faa
12 changed files with 876 additions and 30 deletions

View File

@@ -1,16 +1,35 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
/** BTCPay and Home Assistant set X-Frame-Options and don't support subpath proxy - open in new tab */
/** Apps that set X-Frame-Options and/or don't support subpath proxy - open in new tab for correct display */
function mustOpenInNewTab(url: string): boolean {
try {
const u = new URL(url)
return u.port === '23000' || u.port === '8123'
return (
u.port === '23000' || // BTCPay
u.port === '8123' || // Home Assistant
u.port === '8085' || // Nextcloud (subpath breaks CSS/assets)
u.port === '2283' // Immich (subpath breaks SPA)
)
} catch {
return false
}
}
/** Rewrite to same-origin proxy so iframe can embed (nginx strips X-Frame-Options) */
function toEmbeddableUrl(url: string): string {
try {
const u = new URL(url)
const origin = window.location.origin
// Only Vaultwarden and Penpot support subpath proxy; Nextcloud/Immich open in new tab
if (u.port === '8082') return `${origin}/app/vaultwarden/`
if (u.port === '9001') return `${origin}/app/penpot/`
} catch {
/* ignore */
}
return url
}
export const useAppLauncherStore = defineStore('appLauncher', () => {
const isOpen = ref(false)
const url = ref('')
@@ -23,7 +42,7 @@ export const useAppLauncherStore = defineStore('appLauncher', () => {
return
}
previousActiveElement = (document.activeElement as HTMLElement) || null
url.value = payload.url
url.value = toEmbeddableUrl(payload.url)
title.value = payload.title
isOpen.value = true
}

View File

@@ -456,6 +456,23 @@ const ROUTE_TO_PACKAGE_KEY: Record<string, string> = {
'lnd-ui': 'lnd',
bitcoin: 'bitcoin-knots',
'bitcoin-knots': 'bitcoin-knots',
homeassistant: 'homeassistant',
'home-assistant': 'homeassistant',
grafana: 'grafana',
searxng: 'searxng',
ollama: 'ollama',
onlyoffice: 'onlyoffice',
penpot: 'penpot',
nextcloud: 'nextcloud',
vaultwarden: 'vaultwarden',
jellyfin: 'jellyfin',
photoprism: 'photoprism',
immich: 'immich',
filebrowser: 'filebrowser',
'nginx-proxy-manager': 'nginx-proxy-manager',
portainer: 'portainer',
'uptime-kuma': 'uptime-kuma',
tailscale: 'tailscale',
}
function resolvePackageKey(routeId: string): string {
@@ -671,17 +688,29 @@ function launchApp() {
prod: 'http://localhost:11434'
},
'searxng': {
dev: 'http://localhost:8082',
prod: 'http://localhost:8082'
dev: 'http://localhost:8888',
prod: 'http://localhost:8888'
},
'onlyoffice': {
dev: 'http://localhost:8083',
prod: 'http://localhost:8083'
dev: 'http://localhost:9980',
prod: 'http://localhost:9980'
},
'penpot': {
dev: 'http://localhost:9001',
prod: 'http://localhost:9001'
}
},
'nextcloud': { dev: 'http://localhost:8085', prod: 'http://localhost:8085' },
'vaultwarden': { dev: 'http://localhost:8082', prod: 'http://localhost:8082' },
'jellyfin': { dev: 'http://localhost:8096', prod: 'http://localhost:8096' },
'photoprism': { dev: 'http://localhost:2342', prod: 'http://localhost:2342' },
'immich': { dev: 'http://localhost:2283', prod: 'http://localhost:2283' },
'filebrowser': { dev: 'http://localhost:8083', prod: 'http://localhost:8083' },
'nginx-proxy-manager': { dev: 'http://localhost:81', prod: 'http://localhost:81' },
'portainer': { dev: 'http://localhost:9000', prod: 'http://localhost:9000' },
'uptime-kuma': { dev: 'http://localhost:3001', prod: 'http://localhost:3001' },
'tailscale': { dev: 'http://localhost:8240', prod: 'http://localhost:8240' },
'lnd': { dev: 'http://localhost:8081', prod: 'http://localhost:8081' },
'bitcoin-knots': { dev: 'http://localhost:8334', prod: 'http://localhost:8334' }
}
if (appUrls[id]) {

View File

@@ -587,8 +587,16 @@ const filteredCommunityApps = computed(() => {
return communityApps.value
})
/** Marketplace app ID -> backend package keys (for "Already Installed" when first-boot/deploy created them) */
const INSTALLED_ALIASES: Record<string, string[]> = {
mempool: ['mempool-web'],
bitcoin: ['bitcoin-knots'],
btcpay: ['btcpay-server'],
}
function isInstalled(appId: string): boolean {
return appId in installedPackages.value
if (appId in installedPackages.value) return true
const aliases = INSTALLED_ALIASES[appId]
return aliases ? aliases.some((a) => a in installedPackages.value) : false
}
// Load community marketplace on mount
@@ -818,7 +826,7 @@ function getCuratedAppList() {
description: 'Self-hosted monitoring tool. Monitor uptime for HTTP(s), TCP, DNS, and more.',
icon: '/assets/img/app-icons/uptime-kuma.webp',
author: 'Uptime Kuma',
dockerImage: 'docker.io/louislam/uptime-kuma:1.23.11',
dockerImage: 'docker.io/louislam/uptime-kuma:1',
manifestUrl: null,
repoUrl: 'https://github.com/louislam/uptime-kuma'
},