diff --git a/loop/plan.md b/loop/plan.md index f98645d7..e0bedbd5 100644 --- a/loop/plan.md +++ b/loop/plan.md @@ -13,7 +13,7 @@ - [x] **APP-101** — fix(marketplace): audit and fix all 24 marketplace app install flows. For each app in `getCuratedAppList()` in `neode-ui/src/views/Marketplace.vue` (bitcoin-knots, electrs, btcpay-server, lnd, mempool, homeassistant, grafana, searxng, ollama, onlyoffice, penpot, nextcloud, vaultwarden, jellyfin, photoprism, immich, filebrowser, nginx-proxy-manager, portainer, uptime-kuma, tailscale, fedimint, indeedhub), verify each one: (1) marketplace card renders correctly with icon, (2) clicking Install triggers `package.install` RPC, (3) container pulls and creates successfully, (4) container starts on the correct ports per `apps/PORTS.md`, (5) status shows "Running" in My Apps. Fix any broken apps. Deploy with `./scripts/deploy-to-target.sh --live`. Test each app at http://192.168.1.228. -- [ ] **APP-102** — fix(apps): ensure iframe vs new-tab behavior is correct for all apps. In `neode-ui/src/stores/appLauncher.ts`, verify `mustOpenInNewTab()` includes all apps that set `X-Frame-Options: DENY/SAMEORIGIN`. Currently covers BTCPay (23000), Home Assistant (8123), Nextcloud (8085), Immich (2283). Test each running app by clicking "Open" in AppDetails.vue — iframe apps must load inside the overlay, new-tab apps must open in a fresh browser tab. If any app fails to load in iframe, either fix the nginx proxy to strip X-Frame-Options or add it to `mustOpenInNewTab()`. Deploy and verify each app. +- [x] **APP-102** — fix(apps): ensure iframe vs new-tab behavior is correct for all apps. In `neode-ui/src/stores/appLauncher.ts`, verify `mustOpenInNewTab()` includes all apps that set `X-Frame-Options: DENY/SAMEORIGIN`. Currently covers BTCPay (23000), Home Assistant (8123), Nextcloud (8085), Immich (2283). Test each running app by clicking "Open" in AppDetails.vue — iframe apps must load inside the overlay, new-tab apps must open in a fresh browser tab. If any app fails to load in iframe, either fix the nginx proxy to strip X-Frame-Options or add it to `mustOpenInNewTab()`. Deploy and verify each app. - [ ] **APP-103** — fix(apps): verify all PORT_TO_PROXY mappings in appLauncher.ts match nginx config. Cross-reference every entry in `PORT_TO_PROXY` in `neode-ui/src/stores/appLauncher.ts` with the actual nginx location blocks in `image-recipe/configs/nginx-archipelago.conf` and `image-recipe/configs/snippets/archipelago-https-app-proxies.conf`. Any missing nginx proxy blocks must be added. Any port mismatches must be corrected. Deploy nginx config and verify each app loads via its proxy path. diff --git a/neode-ui/src/stores/appLauncher.ts b/neode-ui/src/stores/appLauncher.ts index 91c7483a..8c623021 100644 --- a/neode-ui/src/stores/appLauncher.ts +++ b/neode-ui/src/stores/appLauncher.ts @@ -1,16 +1,35 @@ import { defineStore } from 'pinia' import { ref } from 'vue' -/** Apps that set X-Frame-Options and/or don't support subpath proxy - open in new tab for correct display */ +/** Apps that must open in new tab instead of iframe. + * - DENY apps: always new tab (X-Frame-Options: DENY) + * - Redirect apps: new tab on HTTPS (absolute redirects break subpath proxy in iframe) + * On HTTP, these load via direct port URL so iframe works fine. + */ function mustOpenInNewTab(url: string): boolean { try { const u = new URL(url) - return ( - u.port === '23000' || // BTCPay - u.port === '8123' || // Home Assistant + // Always new tab: X-Frame-Options DENY or subpath fundamentally breaks the app + if ( + u.port === '23000' || // BTCPay (X-Frame-Options: DENY) + u.port === '8123' || // Home Assistant (subpath breaks routing) u.port === '8085' || // Nextcloud (subpath breaks CSS/assets) u.port === '2283' // Immich (subpath breaks SPA) - ) + ) { + return true + } + // On HTTPS, apps with absolute-path redirects break in iframe via proxy + if (window.location.protocol === 'https:') { + return ( + u.port === '8096' || // Jellyfin (redirects to /web/index.html) + u.port === '9000' || // Portainer (redirects to /timeout.html) + u.port === '2342' || // PhotoPrism (redirects to /library/login) + u.port === '9980' || // OnlyOffice (redirects to /welcome/) + u.port === '3001' || // Uptime Kuma (redirects to /dashboard) + u.port === '8175' // Fedimint (redirects to /login) + ) + } + return false } catch { return false }