fix: container DNS, nginx chown, onboarding guard, seed UX, install flow
Backend: - Add --add-host host.containers.internal:host-gateway to LND and Bitcoin Knots containers (fixes DNS resolution failure in rootless podman) - Add --user 0:0 and DAC_OVERRIDE to nginx UI sidecar containers (fixes chown crash in rootless podman for bitcoin-ui, electrs-ui, lnd-ui) - Add hostadd to Rust Podman API client for web UI container installs - Add Chromium privacy flags to kiosk launcher (disable telemetry) Frontend: - Fix onboarding reset on raw IP visits (trust localStorage as first-class signal, skip boot screen when server is up but not onboarded) - Fix seed regression: persist challenge indices in sessionStorage so going back from Verify doesn't change which words are asked - Remove glass container from seed Generate/Verify/Restore screens - Add Back button to Restore from Seed screen - Replace Network card: Tor (purple), VPN status (orange), Bitcoin sync (orange) - Add ElectrumX to curated app list with correct .webp icon - Install flow: navigate to My Apps immediately with toast, hide installed/installing apps from marketplace and discover views Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b4a57e83d0
commit
6d3704fff5
@ -310,6 +310,7 @@ impl PodmanClient {
|
||||
"portmappings": port_mappings,
|
||||
"mounts": mounts,
|
||||
"env": env_map,
|
||||
"hostadd": ["host.containers.internal:host-gateway"],
|
||||
"devices": manifest.app.devices.iter().map(|d| {
|
||||
serde_json::json!({"path": d})
|
||||
}).collect::<Vec<_>>(),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -47,6 +47,11 @@ while true; do
|
||||
--disable-background-networking \
|
||||
--disable-background-timer-throttling \
|
||||
--disable-backgrounding-occluded-windows \
|
||||
--disable-breakpad \
|
||||
--disable-metrics \
|
||||
--disable-metrics-reporting \
|
||||
--metrics-recording-only \
|
||||
--disable-domain-reliability \
|
||||
--js-flags="--max-old-space-size=128" \
|
||||
--user-data-dir=/home/archipelago/.config/chromium-kiosk
|
||||
sleep 3
|
||||
|
||||
@ -19,9 +19,11 @@ async function callWithRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T
|
||||
}
|
||||
|
||||
export async function isOnboardingComplete(): Promise<boolean> {
|
||||
// localStorage is set on completion and survives backend restarts/resets
|
||||
if (localStorage.getItem('neode_onboarding_complete') === '1') return true
|
||||
const result = await callWithRetry(() => rpcClient.isOnboardingComplete(), 2)
|
||||
if (result !== null) return result
|
||||
return localStorage.getItem('neode_onboarding_complete') === '1'
|
||||
return false
|
||||
}
|
||||
|
||||
export async function completeOnboarding(): Promise<void> {
|
||||
|
||||
@ -144,6 +144,7 @@ import { useServerStore } from '@/stores/server'
|
||||
import { rpcClient } from '@/api/rpc-client'
|
||||
import { useMarketplaceApp } from '@/composables/useMarketplaceApp'
|
||||
import { useAppLauncherStore } from '@/stores/appLauncher'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import DiscoverHero from './discover/DiscoverHero.vue'
|
||||
import FeaturedApps from './discover/FeaturedApps.vue'
|
||||
import AppGrid from './discover/AppGrid.vue'
|
||||
@ -427,9 +428,13 @@ function startInstallPolling(appId: string, statusMessage: string) {
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const toast = useToast()
|
||||
|
||||
async function installApp(app: MarketplaceApp) {
|
||||
if (installingApps.has(app.id) || isInstalled(app.id)) return
|
||||
installingApps.set(app.id, { id: app.id, title: app.title ?? app.id, status: 'downloading', progress: 10, message: 'Preparing installation...', attempt: 0 })
|
||||
toast.info(`Installing ${app.title ?? app.id} — check My Apps`)
|
||||
router.push('/dashboard/apps').catch(() => {})
|
||||
try {
|
||||
const installUrl = app.url || app.manifestUrl || app.s9pkUrl
|
||||
installingApps.set(app.id, { ...installingApps.get(app.id)!, status: 'downloading', progress: 30, message: 'Downloading package...' })
|
||||
@ -446,6 +451,8 @@ async function installApp(app: MarketplaceApp) {
|
||||
async function installCommunityApp(app: MarketplaceApp) {
|
||||
if (installingApps.has(app.id) || isInstalled(app.id) || !app.dockerImage) return
|
||||
installingApps.set(app.id, { id: app.id, title: app.title ?? app.id, status: 'downloading', progress: 10, message: 'Pulling Docker image...', attempt: 0 })
|
||||
toast.info(`Installing ${app.title ?? app.id} — check My Apps`)
|
||||
router.push('/dashboard/apps').catch(() => {})
|
||||
try {
|
||||
installingApps.set(app.id, { ...installingApps.get(app.id)!, status: 'downloading', progress: 20, message: 'Downloading container image...' })
|
||||
await rpcClient.call({ method: 'package.install', params: { id: app.id, dockerImage: app.dockerImage, version: app.version }, timeout: 180000 })
|
||||
|
||||
@ -140,16 +140,16 @@
|
||||
</div>
|
||||
<div class="home-card-stats space-y-3 mb-4 flex-1 min-h-0">
|
||||
<div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
|
||||
<div class="flex items-center gap-3"><div class="w-2 h-2 rounded-full" :class="servicesDotColor"></div><span class="text-sm text-white/80">{{ t('home.servicesStatus') }}</span></div>
|
||||
<span class="text-sm font-medium" :class="servicesStatusColor">{{ servicesStatusText }}</span>
|
||||
<div class="flex items-center gap-3"><div class="w-2 h-2 rounded-full" :class="torConnected ? 'bg-purple-400' : 'bg-white/40'"></div><span class="text-sm text-white/80">Tor</span></div>
|
||||
<span class="text-sm font-medium" :class="torConnected ? 'text-purple-400' : 'text-white/40'">{{ torConnected ? 'Connected' : 'Offline' }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
|
||||
<div class="flex items-center gap-3"><div class="w-2 h-2 rounded-full" :class="connectivityDotColor"></div><span class="text-sm text-white/80">{{ t('home.connectivity') }}</span></div>
|
||||
<span class="text-sm font-medium" :class="connectivityColor">{{ connectivityText }}</span>
|
||||
<div class="flex items-center gap-3"><div class="w-2 h-2 rounded-full" :class="vpnConnected ? 'bg-orange-400' : 'bg-white/40'"></div><span class="text-sm text-white/80">VPN</span></div>
|
||||
<span class="text-sm font-medium" :class="vpnConnected ? 'text-orange-400' : 'text-white/40'">{{ vpnConnected ? 'Connected' : 'Not configured' }}</span>
|
||||
</div>
|
||||
<div class="flex items-center justify-between p-3 bg-white/5 rounded-lg">
|
||||
<div class="flex items-center gap-3"><div class="w-2 h-2 rounded-full bg-blue-400"></div><span class="text-sm text-white/80">{{ t('home.runningApps') }}</span></div>
|
||||
<span class="text-sm text-white/80 font-medium">{{ runningCount }}</span>
|
||||
<div class="flex items-center gap-3"><div class="w-2 h-2 rounded-full" :class="systemStats.bitcoinAvailable ? 'bg-orange-400' : 'bg-white/40'"></div><span class="text-sm text-white/80">Bitcoin</span></div>
|
||||
<span class="text-sm font-medium" :class="systemStats.bitcoinAvailable ? 'text-orange-400' : 'text-white/40'">{{ bitcoinSyncDisplay }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-card-buttons flex gap-2 mt-auto pt-4 shrink-0">
|
||||
@ -306,13 +306,20 @@ const quickLaunchApps = [
|
||||
{ id: '484-kitchen', name: '484 Kitchen', icon: '/assets/img/app-icons/484-kitchen.png', bg: '', padded: false },
|
||||
]
|
||||
|
||||
const servicesAllRunning = computed(() => appCount.value > 0 && runningCount.value === appCount.value)
|
||||
const servicesStatusText = computed(() => appCount.value === 0 ? t('home.noApps') : servicesAllRunning.value ? t('home.allRunning') : `${runningCount.value}/${appCount.value} ${t('home.runningLabel')}`)
|
||||
const servicesStatusColor = computed(() => appCount.value === 0 ? 'text-white/60' : servicesAllRunning.value ? 'text-green-400' : 'text-yellow-400')
|
||||
const servicesDotColor = computed(() => appCount.value === 0 ? 'bg-white/40' : servicesAllRunning.value ? 'bg-green-400' : 'bg-yellow-400')
|
||||
const connectivityText = computed(() => store.isConnected ? t('common.connected') : t('common.disconnected'))
|
||||
const connectivityColor = computed(() => store.isConnected ? 'text-green-400' : 'text-red-400')
|
||||
const connectivityDotColor = computed(() => store.isConnected ? 'bg-green-400' : 'bg-red-400')
|
||||
// Network card data
|
||||
const torConnected = computed(() => {
|
||||
const torAddr = store.data?.['server-info']?.['tor-address']
|
||||
return !!torAddr && torAddr.length > 0
|
||||
})
|
||||
const vpnConnected = computed(() => {
|
||||
const pkg = packages.value['tailscale']
|
||||
return !!pkg && pkg.state === PackageState.Running
|
||||
})
|
||||
const bitcoinSyncDisplay = computed(() => {
|
||||
if (!systemStats.bitcoinAvailable) return 'Not running'
|
||||
if (systemStats.bitcoinSyncPercent >= 99.9) return 'Synced'
|
||||
return `${systemStats.bitcoinSyncPercent.toFixed(1)}%`
|
||||
})
|
||||
|
||||
// Quick Start
|
||||
const quickStartDismissed = ref(false)
|
||||
|
||||
@ -116,6 +116,7 @@ import { useServerStore } from '@/stores/server'
|
||||
import { rpcClient } from '@/api/rpc-client'
|
||||
import { useMarketplaceApp } from '@/composables/useMarketplaceApp'
|
||||
import { useAppLauncherStore } from '@/stores/appLauncher'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import MarketplaceAppCard from './marketplace/MarketplaceAppCard.vue'
|
||||
import MarketplaceFilterModal from './marketplace/MarketplaceFilterModal.vue'
|
||||
import {
|
||||
@ -135,6 +136,7 @@ const { t } = useI18n()
|
||||
const showStagger = !marketplaceAnimationDone
|
||||
const { setCurrentApp } = useMarketplaceApp()
|
||||
const appLauncher = useAppLauncherStore()
|
||||
const toast = useToast()
|
||||
|
||||
// Category state — read initial value from query param (set by Discover page navigation)
|
||||
const selectedCategory = ref((route.query.category as string) || 'all')
|
||||
@ -300,11 +302,8 @@ const filteredApps = computed(() => {
|
||||
)
|
||||
}
|
||||
|
||||
apps.sort((a, b) => {
|
||||
const aInstalled = isInstalled(a.id) ? 1 : 0
|
||||
const bInstalled = isInstalled(b.id) ? 1 : 0
|
||||
return aInstalled - bInstalled
|
||||
})
|
||||
// Hide installed and installing apps from marketplace — they belong in My Apps
|
||||
apps = apps.filter(app => !isInstalled(app.id) && !installingApps.has(app.id))
|
||||
|
||||
return apps
|
||||
})
|
||||
@ -433,6 +432,10 @@ async function installApp(app: MarketplaceApp) {
|
||||
id: app.id, title: app.title ?? app.id, status: 'downloading', progress: 10, message: 'Preparing installation...', attempt: 0
|
||||
})
|
||||
|
||||
// Navigate to My Apps immediately and show toast
|
||||
toast.info(`Installing ${app.title ?? app.id} — check My Apps`)
|
||||
router.push('/dashboard/apps').catch(() => {})
|
||||
|
||||
try {
|
||||
const installUrl = app.url || app.manifestUrl || app.s9pkUrl
|
||||
|
||||
@ -457,6 +460,10 @@ async function installCommunityApp(app: MarketplaceApp) {
|
||||
id: app.id, title: app.title ?? app.id, status: 'downloading', progress: 10, message: 'Pulling Docker image...', attempt: 0
|
||||
})
|
||||
|
||||
// Navigate to My Apps immediately and show toast
|
||||
toast.info(`Installing ${app.title ?? app.id} — check My Apps`)
|
||||
router.push('/dashboard/apps').catch(() => {})
|
||||
|
||||
try {
|
||||
installingApps.set(app.id, { ...installingApps.get(app.id)!, status: 'downloading', progress: 20, message: 'Downloading container image...' })
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="h-[100dvh] flex items-center justify-center p-3 sm:p-4 md:p-6">
|
||||
<div class="max-w-[800px] w-full max-h-full relative z-10 path-glass-container onb-scroll-container flex flex-col">
|
||||
<div class="max-w-[800px] w-full max-h-full relative z-10 onb-scroll-container flex flex-col">
|
||||
<!-- Header -->
|
||||
<div class="text-center flex-shrink-0 px-3 sm:px-4 pt-4 sm:pt-6 pb-2 sm:pb-3">
|
||||
<h1 class="text-xl sm:text-2xl md:text-[26px] font-semibold text-white/96 mb-1.5 drop-shadow-[0_2px_6px_rgba(0,0,0,0.4)]">
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="h-[100dvh] flex items-center justify-center p-3 sm:p-4 md:p-6">
|
||||
<div class="max-w-[800px] w-full max-h-full relative z-10 path-glass-container onb-scroll-container flex flex-col">
|
||||
<div class="max-w-[800px] w-full max-h-full relative z-10 onb-scroll-container flex flex-col">
|
||||
<!-- Header -->
|
||||
<div class="text-center flex-shrink-0 px-3 sm:px-4 pt-4 sm:pt-6 pb-2 sm:pb-3">
|
||||
<h1 class="text-xl sm:text-2xl md:text-[26px] font-semibold text-white/96 mb-1.5 drop-shadow-[0_2px_6px_rgba(0,0,0,0.4)]">
|
||||
@ -69,7 +69,14 @@
|
||||
</div>
|
||||
|
||||
<!-- Fixed Footer -->
|
||||
<div class="flex-shrink-0 flex justify-center px-3 sm:px-4 pt-3 pb-4 sm:pb-6">
|
||||
<div class="flex-shrink-0 flex items-center justify-center gap-4 max-w-[600px] mx-auto w-full px-6 sm:px-8 pt-3 pb-4 sm:pb-6">
|
||||
<span
|
||||
v-if="!restored"
|
||||
@click="goBack"
|
||||
class="path-action-button path-action-button--continue cursor-pointer select-none inline-flex items-center justify-center"
|
||||
>
|
||||
Back
|
||||
</span>
|
||||
<button
|
||||
v-if="!restored"
|
||||
@click="restore"
|
||||
@ -116,6 +123,11 @@ const restoredDid = ref('')
|
||||
|
||||
const allFilled = computed(() => seedWords.value.every(w => w.trim().length > 0))
|
||||
|
||||
function goBack() {
|
||||
playNavSound('action')
|
||||
router.push('/onboarding/path').catch(() => {})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
setTimeout(() => wordInputs.value[0]?.focus({ preventScroll: true }), 300)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="h-[100dvh] flex items-center justify-center p-3 sm:p-4 md:p-6">
|
||||
<div class="max-w-[800px] w-full max-h-full relative z-10 path-glass-container onb-scroll-container flex flex-col">
|
||||
<div class="max-w-[800px] w-full max-h-full relative z-10 onb-scroll-container flex flex-col">
|
||||
<!-- Header (hidden after verification) -->
|
||||
<div v-if="!verified" class="text-center flex-shrink-0 px-3 sm:px-4 pt-4 sm:pt-6 pb-2 sm:pb-3">
|
||||
<h1 class="text-xl sm:text-2xl md:text-[26px] font-semibold text-white/96 mb-1.5 drop-shadow-[0_2px_6px_rgba(0,0,0,0.4)]">
|
||||
@ -168,7 +168,24 @@ onMounted(() => {
|
||||
return
|
||||
}
|
||||
words.value = JSON.parse(stored)
|
||||
challengeIndices.value = pickRandomIndices(4, 24)
|
||||
|
||||
// Restore challenge indices so going back and returning asks the same words
|
||||
const savedIndices = sessionStorage.getItem('_seed_challenge_indices')
|
||||
if (savedIndices) {
|
||||
try {
|
||||
const parsed = JSON.parse(savedIndices)
|
||||
if (Array.isArray(parsed) && parsed.length === 4) {
|
||||
challengeIndices.value = parsed
|
||||
} else {
|
||||
challengeIndices.value = pickRandomIndices(4, 24)
|
||||
}
|
||||
} catch {
|
||||
challengeIndices.value = pickRandomIndices(4, 24)
|
||||
}
|
||||
} else {
|
||||
challengeIndices.value = pickRandomIndices(4, 24)
|
||||
}
|
||||
sessionStorage.setItem('_seed_challenge_indices', JSON.stringify(challengeIndices.value))
|
||||
|
||||
nextTick(() => {
|
||||
setTimeout(() => inputRefs.value[0]?.focus({ preventScroll: true }), 300)
|
||||
@ -232,6 +249,7 @@ async function verify() {
|
||||
localStorage.setItem('neode_did', res.did)
|
||||
if (res.nostr_npub) localStorage.setItem('neode_nostr_npub', res.nostr_npub)
|
||||
sessionStorage.removeItem('_seed_words')
|
||||
sessionStorage.removeItem('_seed_challenge_indices')
|
||||
|
||||
nextTick(() => {
|
||||
setTimeout(() => continueButton.value?.focus({ preventScroll: true }), 100)
|
||||
|
||||
@ -124,10 +124,12 @@ onMounted(async () => {
|
||||
proceedToApp()
|
||||
return
|
||||
}
|
||||
log('server up + NOT onboarded → boot screen')
|
||||
log('server up + NOT onboarded → onboarding intro')
|
||||
router.replace('/onboarding/intro').catch(() => {})
|
||||
return
|
||||
}
|
||||
|
||||
// Server not ready OR first boot — show boot screen
|
||||
// Server not ready — show boot screen (waiting for backend)
|
||||
showBootScreen.value = true
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -24,7 +24,7 @@ export function getCuratedAppList(): MarketplaceApp[] {
|
||||
{ id: 'portainer', title: 'Portainer', version: '2.19.4', description: 'Container management UI. Manage your containerized services through the web.', icon: '/assets/img/app-icons/portainer.webp', author: 'Portainer', dockerImage: `${R}/portainer:latest`, repoUrl: 'https://github.com/portainer/portainer' },
|
||||
{ id: 'uptime-kuma', title: 'Uptime Kuma', version: '1.23.0', description: 'Self-hosted uptime monitoring. Track HTTP, TCP, DNS, and more.', icon: '/assets/img/app-icons/uptime-kuma.webp', author: 'Uptime Kuma', dockerImage: `${R}/uptime-kuma:1`, repoUrl: 'https://github.com/louislam/uptime-kuma' },
|
||||
{ id: 'tailscale', title: 'Tailscale', version: '1.78.0', description: 'Zero-config VPN. Secure remote access with WireGuard mesh networking.', icon: '/assets/img/app-icons/tailscale.webp', author: 'Tailscale', dockerImage: `${R}/tailscale:stable`, repoUrl: 'https://github.com/tailscale/tailscale' },
|
||||
{ id: 'electrumx', title: 'ElectrumX', version: '1.18.0', description: 'Electrum protocol server. Index the blockchain for fast wallet lookups, privately.', icon: '/assets/img/app-icons/electrumx.png', author: 'Luke Childs', dockerImage: `${R}/electrumx:v1.18.0`, repoUrl: 'https://github.com/spesmilo/electrumx' },
|
||||
{ id: 'electrumx', title: 'ElectrumX', version: '1.18.0', description: 'Electrum protocol server. Index the blockchain for fast wallet lookups, privately.', icon: '/assets/img/app-icons/electrumx.webp', author: 'Luke Childs', dockerImage: `${R}/electrumx:v1.18.0`, repoUrl: 'https://github.com/spesmilo/electrumx' },
|
||||
{ id: 'fedimint', title: 'Fedimint', version: '0.10.0', description: 'Federated Bitcoin mint. Private, scalable Bitcoin through federated guardians.', icon: '/assets/img/app-icons/fedimint.png', author: 'Fedimint', dockerImage: `${R}/fedimintd:v0.10.0`, repoUrl: 'https://github.com/fedimint/fedimint' },
|
||||
{ id: 'nostr-rs-relay', title: 'Nostr Relay', version: '0.9.0', category: 'nostr', description: 'Your own Nostr relay. Store events locally, relay for friends, publish over Tor.', icon: '/assets/img/app-icons/nostr-rs-relay.svg', author: 'scsiblade', dockerImage: `${R}/nostr-rs-relay:0.9.0`, repoUrl: 'https://sr.ht/~gheartsfield/nostr-rs-relay/' },
|
||||
{ id: 'indeedhub', title: 'Indeehub', version: '0.1.0', description: 'Bitcoin documentary streaming with Nostr identity. Stream sovereignty content.', icon: '/assets/img/app-icons/indeedhub.png', author: 'Indeehub Team', dockerImage: 'localhost/indeedhub:latest', repoUrl: 'https://github.com/indeedhub/indeedhub' },
|
||||
|
||||
@ -303,6 +303,17 @@ export function getCuratedAppList(): MarketplaceApp[] {
|
||||
manifestUrl: undefined,
|
||||
repoUrl: 'https://github.com/immich-app/immich'
|
||||
},
|
||||
{
|
||||
id: 'electrumx',
|
||||
title: 'ElectrumX',
|
||||
version: '1.18.0',
|
||||
description: 'Electrum protocol server. Index the blockchain for fast wallet lookups, privately.',
|
||||
icon: '/assets/img/app-icons/electrumx.webp',
|
||||
author: 'Luke Childs',
|
||||
dockerImage: `${REGISTRY}/electrumx:v1.18.0`,
|
||||
manifestUrl: undefined,
|
||||
repoUrl: 'https://github.com/spesmilo/electrumx'
|
||||
},
|
||||
{
|
||||
id: 'filebrowser',
|
||||
title: 'File Browser',
|
||||
|
||||
@ -368,6 +368,7 @@ if ! $DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -qE 'bitcoin-knots|arch
|
||||
if $DOCKER run -d --name bitcoin-knots --restart unless-stopped \
|
||||
--health-cmd="bitcoin-cli -rpcuser=\$BITCOIN_RPC_USER -rpcpassword=\$BITCOIN_RPC_PASS getblockchaininfo || exit 1" --health-interval=120s --health-timeout=5s --health-retries=3 \
|
||||
--memory=$(mem_limit bitcoin-knots) --network archy-net \
|
||||
--add-host host.containers.internal:host-gateway \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add FOWNER --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
|
||||
--security-opt no-new-privileges:true \
|
||||
-p 8332:8332 -p 8333:8333 -p 28332:28332 -p 28333:28333 \
|
||||
@ -483,21 +484,21 @@ if ! $DOCKER ps --format '{{.Names}}' 2>/dev/null | grep -q electrs-ui; then
|
||||
log "Starting ElectrumX UI from pre-built image..."
|
||||
$DOCKER run -d --name archy-electrs-ui --network host --restart unless-stopped \
|
||||
--health-cmd="curl -sf http://localhost:80/ || exit 1" --health-interval=120s --health-timeout=5s --health-retries=3 \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID \
|
||||
--security-opt no-new-privileges:true \
|
||||
--user 0:0 \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
|
||||
localhost/electrs-ui:local 2>>"$LOG" || \
|
||||
$DOCKER run -d --name archy-electrs-ui --network host --restart unless-stopped \
|
||||
--health-cmd="curl -sf http://localhost:80/ || exit 1" --health-interval=120s --health-timeout=5s --health-retries=3 \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID \
|
||||
--security-opt no-new-privileges:true \
|
||||
--user 0:0 \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
|
||||
electrs-ui:local 2>>"$LOG" || true
|
||||
elif [ -d /opt/archipelago/docker/electrs-ui ]; then
|
||||
log "Building and starting ElectrumX UI from source..."
|
||||
$DOCKER build -t electrs-ui:local /opt/archipelago/docker/electrs-ui 2>>"$LOG" && \
|
||||
$DOCKER run -d --name archy-electrs-ui --network host --restart unless-stopped \
|
||||
--health-cmd="curl -sf http://localhost:80/ || exit 1" --health-interval=120s --health-timeout=5s --health-retries=3 \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID \
|
||||
--security-opt no-new-privileges:true \
|
||||
--user 0:0 \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
|
||||
electrs-ui:local 2>>"$LOG" || true
|
||||
else
|
||||
log "ElectrumX UI: no image or source found, skipping"
|
||||
@ -607,6 +608,7 @@ LNDCONF
|
||||
$DOCKER run -d --name lnd --restart unless-stopped \
|
||||
--health-cmd="curl -sf --insecure https://localhost:8080/v1/getinfo || exit 1" --health-interval=120s --health-timeout=5s --health-retries=3 \
|
||||
--memory=$(mem_limit lnd) --network archy-net \
|
||||
--add-host host.containers.internal:host-gateway \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add FOWNER --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE --cap-add NET_RAW \
|
||||
--security-opt no-new-privileges:true \
|
||||
-p 9735:9735 -p 10009:10009 -p 8080:8080 \
|
||||
@ -994,23 +996,23 @@ for ui in bitcoin-ui lnd-ui; do
|
||||
log "Starting $ui from pre-built image..."
|
||||
IMG=$($DOCKER images --format '{{.Repository}}:{{.Tag}}' 2>/dev/null | grep "$ui" | head -1)
|
||||
$DOCKER run -d --name "$CONTAINER_NAME" $PORT_ARG --restart unless-stopped --memory=$(mem_limit "$CONTAINER_NAME") $NET_ARG \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID \
|
||||
--security-opt no-new-privileges:true \
|
||||
--user 0:0 \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
|
||||
"$IMG" 2>>"$LOG" || true
|
||||
elif [ -d "/opt/archipelago/docker/$ui" ]; then
|
||||
log "Building $ui from source (/opt/archipelago/docker/$ui)..."
|
||||
if $DOCKER build -t "$ui:local" "/opt/archipelago/docker/$ui" 2>>"$LOG"; then
|
||||
$DOCKER run -d --name "$CONTAINER_NAME" $PORT_ARG --restart unless-stopped --memory=$(mem_limit "$CONTAINER_NAME") $NET_ARG \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID \
|
||||
--security-opt no-new-privileges:true \
|
||||
--user 0:0 \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
|
||||
"$ui:local" 2>>"$LOG" || true
|
||||
fi
|
||||
elif [ -d "/home/archipelago/archy/docker/$ui" ]; then
|
||||
log "Building $ui from source (/home/archipelago/archy/docker/$ui)..."
|
||||
if $DOCKER build -t "$ui:local" "/home/archipelago/archy/docker/$ui" 2>>"$LOG"; then
|
||||
$DOCKER run -d --name "$CONTAINER_NAME" $PORT_ARG --restart unless-stopped --memory=$(mem_limit "$CONTAINER_NAME") $NET_ARG \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID \
|
||||
--security-opt no-new-privileges:true \
|
||||
--user 0:0 \
|
||||
--cap-drop ALL --cap-add CHOWN --cap-add SETUID --cap-add SETGID --cap-add DAC_OVERRIDE \
|
||||
"$ui:local" 2>>"$LOG" || true
|
||||
fi
|
||||
else
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user