- auth.rs now infers onboarding-complete from setup_complete + password_hash so nodes stop bouncing users through the intro wizard after browser clear / update / reboot; the flag self-heals to disk on next check - frontend: "backend uncertain" no longer defaults to /onboarding/intro — useOnboarding returns null + callers poll / retry instead of flashing the wizard - login sounds (synthwave, welcome voice, pop, whoosh, oomph) gated by isFirstInstallPhase(); typing sounds unaffected - removed FIPS app, Nostr Relay, Nostr VPN, Routstr, Penpot from catalog, frontend config, Rust AppMetadata + install dispatch + install_penpot_stack; docker/fips-ui + docker/nostr-vpn-ui + apps/penpot dirs and 5 icons deleted; 15 image versions deleted from tx1138, .168, gitea-local registries (.160 Gitea was 502 at release time — follow-up) - AIUI baked into frontend release tarball via demo/aiui/; deploy-to-target falls back to demo/aiui/ when the AIUI sibling checkout is missing - prebuild hook syncs app-catalog/catalog.json → public/catalog.json so the two copies can no longer drift (was the source of the "apps still visible" bug — public/ had stale data) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
169 lines
8.1 KiB
TypeScript
169 lines
8.1 KiB
TypeScript
/**
|
|
* AppDetails data: URL maps, route-to-package mappings, aliases, and status helpers.
|
|
* Extracted from AppDetails.vue to keep the view under 500 lines.
|
|
*/
|
|
import { PackageState } from '@/types/api'
|
|
|
|
/** Web-only app detection (no container -- external websites) */
|
|
export const WEB_ONLY_APP_URLS: Record<string, string> = {
|
|
'indeedhub': `${window.location.protocol}//${window.location.hostname}:7777`,
|
|
'nwnn': 'https://nwnn.l484.com',
|
|
'484-kitchen': 'https://484.kitchen',
|
|
'call-the-operator': 'https://cta.tx1138.com',
|
|
'arch-presentation': 'https://present.l484.com',
|
|
'syntropy-institute': 'https://syntropy.institute',
|
|
't-zero': 'https://teeminuszero.net',
|
|
}
|
|
|
|
/** Map route/marketplace app IDs to backend package keys (container names). */
|
|
export const ROUTE_TO_PACKAGE_KEY: Record<string, string> = {
|
|
mempool: 'mempool-web',
|
|
'mempool-electrs': 'mempool-electrs',
|
|
electrs: 'mempool-electrs',
|
|
btcpay: 'btcpay-server',
|
|
'btcpay-server': 'btcpay-server',
|
|
fedimint: 'fedimint',
|
|
'fedimint-gateway': 'fedimint-gateway',
|
|
lnd: 'lnd',
|
|
'lnd-ui': 'lnd',
|
|
bitcoin: 'bitcoin-knots',
|
|
'bitcoin-knots': 'bitcoin-knots',
|
|
homeassistant: 'homeassistant',
|
|
'home-assistant': 'homeassistant',
|
|
grafana: 'grafana',
|
|
searxng: 'searxng',
|
|
ollama: 'ollama',
|
|
onlyoffice: 'onlyoffice',
|
|
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',
|
|
}
|
|
|
|
/** Backend may register under variant container names */
|
|
export const PACKAGE_ALIASES: Record<string, string[]> = {
|
|
immich: ['immich_server', 'immich-server'],
|
|
nextcloud: ['nextcloud-aio', 'nextcloud-server'],
|
|
}
|
|
|
|
export function resolvePackageKey(routeId: string): string {
|
|
return ROUTE_TO_PACKAGE_KEY[routeId] ?? routeId
|
|
}
|
|
|
|
/** Apps that depend on Bitcoin being synced */
|
|
export const BITCOIN_DEPENDENT_APPS = ['lnd', 'electrumx', 'electrs', 'mempool-electrs', 'btcpay-server', 'btcpayserver']
|
|
|
|
/** App launch URLs for dev and prod environments */
|
|
export const APP_URLS: Record<string, { dev: string; prod: string }> = {
|
|
'lorabell': { dev: 'http://192.168.1.166', prod: 'http://192.168.1.166' },
|
|
'atob': { dev: 'http://localhost:8102', prod: 'https://app.atobitcoin.io' },
|
|
'k484': { dev: 'http://localhost:8103', prod: 'http://localhost:8103' },
|
|
'indeedhub': { dev: 'https://archipelago.indeehub.studio', prod: 'https://archipelago.indeehub.studio' },
|
|
'bitcoin': { dev: 'http://localhost:8332', prod: 'http://localhost:8332' },
|
|
'btcpay-server': { dev: 'http://localhost:23000', prod: 'http://localhost:23000' },
|
|
'homeassistant': { dev: 'http://localhost:8123', prod: 'http://localhost:8123' },
|
|
'grafana': { dev: 'http://localhost:3000', prod: 'http://localhost:3000' },
|
|
'endurain': { dev: 'http://localhost:8080', prod: 'http://localhost:8080' },
|
|
'fedimint': { dev: 'http://localhost:8175', prod: 'http://192.168.1.228:8175' },
|
|
'fedimint-gateway': { dev: 'http://localhost:8176', prod: 'http://192.168.1.228:8176' },
|
|
'morphos-server': { dev: 'http://localhost:8081', prod: 'http://localhost:8081' },
|
|
'lightning-stack': { dev: 'http://localhost:9735', prod: 'http://localhost:9735' },
|
|
'mempool': { dev: 'http://localhost:4080', prod: 'http://localhost:4080' },
|
|
'ollama': { dev: 'http://localhost:11434', prod: 'http://localhost:11434' },
|
|
'searxng': { dev: 'http://localhost:8888', prod: 'http://localhost:8888' },
|
|
'onlyoffice': { dev: 'http://localhost:9980', prod: 'http://localhost:9980' },
|
|
'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' },
|
|
'botfights': { dev: 'http://localhost:9100', prod: 'http://localhost:9100' },
|
|
'nwnn': { dev: 'https://nwnn.l484.com', prod: 'https://nwnn.l484.com' },
|
|
'484-kitchen': { dev: 'https://484.kitchen', prod: 'https://484.kitchen' },
|
|
'call-the-operator': { dev: 'https://cta.tx1138.com', prod: 'https://cta.tx1138.com' },
|
|
'arch-presentation': { dev: 'https://present.l484.com', prod: 'https://present.l484.com' },
|
|
'syntropy-institute': { dev: 'https://syntropy.institute', prod: 'https://syntropy.institute' },
|
|
't-zero': { dev: 'https://teeminuszero.net', prod: 'https://teeminuszero.net' },
|
|
}
|
|
|
|
/** V3 onion addresses are 56+ chars + .onion. Placeholders like "btcpay.onion" are not real. */
|
|
export function isRealOnionAddress(addr: string | undefined): boolean {
|
|
return !!(addr && addr.endsWith('.onion') && addr.length >= 60 && addr.length <= 70)
|
|
}
|
|
|
|
export function getStatusClass(state: PackageState, health?: string | null, exitCode?: number | null): string {
|
|
if (state === PackageState.Running && health === 'starting') return 'bg-yellow-500/20 text-yellow-200 border border-yellow-500/30'
|
|
if (state === PackageState.Running && health === 'unhealthy') return 'bg-orange-500/20 text-orange-200 border border-orange-500/30'
|
|
switch (state) {
|
|
case PackageState.Running:
|
|
return 'bg-green-500/20 text-green-200 border border-green-500/30'
|
|
case PackageState.Stopped:
|
|
return 'bg-gray-500/20 text-gray-200 border border-gray-500/30'
|
|
case PackageState.Exited:
|
|
return exitCode != null && exitCode !== 0
|
|
? 'bg-red-500/20 text-red-200 border border-red-500/30'
|
|
: 'bg-gray-500/20 text-gray-200 border border-gray-500/30'
|
|
case PackageState.Starting:
|
|
case PackageState.Stopping:
|
|
case PackageState.Restarting:
|
|
return 'bg-yellow-500/20 text-yellow-200 border border-yellow-500/30'
|
|
case PackageState.Installing:
|
|
return 'bg-blue-500/20 text-blue-200 border border-blue-500/30'
|
|
case PackageState.Updating:
|
|
return 'bg-orange-500/20 text-orange-200 border border-orange-500/30'
|
|
default:
|
|
return 'bg-gray-500/20 text-gray-200 border border-gray-500/30'
|
|
}
|
|
}
|
|
|
|
export function getStatusDotClass(state: PackageState, health?: string | null, exitCode?: number | null): string {
|
|
if (state === PackageState.Running && health === 'starting') return 'bg-yellow-400 animate-pulse'
|
|
if (state === PackageState.Running && health === 'unhealthy') return 'bg-orange-400 animate-pulse'
|
|
switch (state) {
|
|
case PackageState.Running:
|
|
return 'bg-green-400'
|
|
case PackageState.Stopped:
|
|
return 'bg-gray-400'
|
|
case PackageState.Exited:
|
|
return exitCode != null && exitCode !== 0
|
|
? 'bg-red-400 animate-pulse'
|
|
: 'bg-gray-400'
|
|
case PackageState.Starting:
|
|
case PackageState.Stopping:
|
|
case PackageState.Restarting:
|
|
return 'bg-yellow-400 animate-pulse'
|
|
case PackageState.Installing:
|
|
return 'bg-blue-400 animate-pulse'
|
|
case PackageState.Updating:
|
|
return 'bg-orange-400 animate-pulse'
|
|
default:
|
|
return 'bg-gray-400'
|
|
}
|
|
}
|
|
|
|
export function getStatusLabel(state: PackageState, health?: string | null, exitCode?: number | null): string {
|
|
if (state === PackageState.Updating) return 'updating...'
|
|
if (state === PackageState.Running && health === 'starting') return 'starting up'
|
|
if (state === PackageState.Running && health === 'unhealthy') return 'unhealthy'
|
|
if (state === PackageState.Running && health === 'healthy') return 'healthy'
|
|
if (state === PackageState.Exited) {
|
|
if (exitCode === 137) return 'killed (OOM)'
|
|
if (exitCode != null && exitCode !== 0) return 'crashed'
|
|
return 'stopped'
|
|
}
|
|
return state
|
|
}
|