Revise BUILD-GUIDE and enhance ISO build process
- Updated BUILD-GUIDE.md to streamline instructions for building the Archipelago Auto-Installer ISO, including prerequisites and post-installation steps. - Added detailed sections on capturing the live server state and building from source. - Enhanced Docker and Podman integration in build scripts for improved backend and web UI capture. - Introduced new app metadata for "IndeedHub" in the Docker package scanner and updated UI components for better installation progress tracking. - Improved styling and functionality in the Bitcoin UI for a more cohesive user experience.
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 88 KiB |
@@ -0,0 +1,95 @@
|
||||
<svg width="2480" height="2480" viewBox="0 0 2480 2480" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="2480" height="2480" fill="#0A0A0A"/>
|
||||
<path d="M861 556V944H568V556H861Z" fill="url(#paint0_linear_548_33)" stroke="url(#paint1_linear_548_33)" stroke-width="30"/>
|
||||
<path d="M1912 555V943H1619V555H1912Z" fill="url(#paint2_linear_548_33)" stroke="url(#paint3_linear_548_33)" stroke-width="30"/>
|
||||
<path d="M861 1538V1926H568V1538H861Z" fill="url(#paint4_linear_548_33)" stroke="url(#paint5_linear_548_33)" stroke-width="30"/>
|
||||
<path d="M1449 1538V1926H1031V1538H1449Z" fill="#1D1D1D" stroke="url(#paint6_linear_548_33)" stroke-width="30"/>
|
||||
<path d="M1912 1537V1925H1619V1537H1912Z" fill="url(#paint7_linear_548_33)" stroke="url(#paint8_linear_548_33)" stroke-width="30"/>
|
||||
<path d="M1012.25 1112L1160.05 1368H1030.49L882.688 1112H1012.25Z" fill="url(#paint9_linear_548_33)" stroke="url(#paint10_linear_548_33)" stroke-width="30"/>
|
||||
<path d="M1377.71 1112L1525.51 1368H1395.89L1248.08 1112H1377.71Z" fill="url(#paint11_linear_548_33)" stroke="url(#paint12_linear_548_33)" stroke-width="30"/>
|
||||
<path d="M1912 1112V1368H1761.35L1613.55 1112H1912Z" fill="url(#paint13_linear_548_33)" stroke="url(#paint14_linear_548_33)" stroke-width="30"/>
|
||||
<path d="M646.852 1112L794.653 1368H568V1112H646.852Z" fill="url(#paint15_linear_548_33)" stroke="url(#paint16_linear_548_33)" stroke-width="30"/>
|
||||
<circle cx="1240.5" cy="750.5" r="195.5" fill="#1D1D1D" stroke="url(#paint17_linear_548_33)" stroke-width="30"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_548_33" x1="677.804" y1="242.431" x2="1904.77" y2="353.601" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F0003D"/>
|
||||
<stop offset="0.369792" stop-color="#FA4727"/>
|
||||
<stop offset="0.776042" stop-color="#6B90F4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_548_33" x1="553" y1="541" x2="876" y2="959" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#F52532"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_548_33" x1="1728.8" y1="241.431" x2="2955.77" y2="352.601" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F0003D"/>
|
||||
<stop offset="0.369792" stop-color="#FA4727"/>
|
||||
<stop offset="0.776042" stop-color="#6B90F4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_548_33" x1="1604" y1="540" x2="1927" y2="958" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#F52532"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_548_33" x1="677.804" y1="1224.43" x2="1904.77" y2="1335.6" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F0003D"/>
|
||||
<stop offset="0.369792" stop-color="#FA4727"/>
|
||||
<stop offset="0.776042" stop-color="#6B90F4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_548_33" x1="876" y1="1607.2" x2="553" y2="1856.8" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#F52532"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_548_33" x1="1240" y1="1941" x2="1240" y2="1523" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-color="#666666"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint7_linear_548_33" x1="1728.8" y1="1223.43" x2="2955.77" y2="1334.6" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F0003D"/>
|
||||
<stop offset="0.369792" stop-color="#FA4727"/>
|
||||
<stop offset="0.776042" stop-color="#6B90F4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint8_linear_548_33" x1="1927" y1="1606.2" x2="1604" y2="1855.8" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="white"/>
|
||||
<stop offset="1" stop-color="#F52532"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint9_linear_548_33" x1="350.36" y1="1032.44" x2="1729.93" y2="1206.26" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F0003D"/>
|
||||
<stop offset="0.369792" stop-color="#FA4727"/>
|
||||
<stop offset="0.776042" stop-color="#6B90F4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint10_linear_548_33" x1="856.707" y1="1097" x2="1115.61" y2="1436.97" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FAFAFA"/>
|
||||
<stop offset="1" stop-color="#A5729F"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint11_linear_548_33" x1="715.651" y1="1032.44" x2="2095.49" y2="1206.34" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F0003D"/>
|
||||
<stop offset="0.369792" stop-color="#FA4727"/>
|
||||
<stop offset="0.776042" stop-color="#6B90F4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint12_linear_548_33" x1="1222.1" y1="1097" x2="1480.99" y2="1437.02" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FAFAFA"/>
|
||||
<stop offset="1" stop-color="#A5729F"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint13_linear_548_33" x1="1065.68" y1="1032.44" x2="2486.21" y2="1216.92" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F0003D"/>
|
||||
<stop offset="0.369792" stop-color="#FA4727"/>
|
||||
<stop offset="0.776042" stop-color="#6B90F4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint14_linear_548_33" x1="1587.57" y1="1097" x2="1844.29" y2="1444.46" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FAFAFA"/>
|
||||
<stop offset="1" stop-color="#A5729F"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint15_linear_548_33" x1="141.502" y1="1032.44" x2="1268.63" y2="1147.85" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#F0003D"/>
|
||||
<stop offset="0.369792" stop-color="#FA4727"/>
|
||||
<stop offset="0.776042" stop-color="#6B90F4"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint16_linear_548_33" x1="553" y1="1097" x2="821" y2="1383" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#FAFAFA"/>
|
||||
<stop offset="1" stop-color="#A5729F"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint17_linear_548_33" x1="1240.5" y1="540" x2="1240.5" y2="961" gradientUnits="userSpaceOnUse">
|
||||
<stop/>
|
||||
<stop offset="1" stop-color="#666666"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
@@ -473,6 +473,42 @@ export const dummyApps: Record<string, PackageDataEntry> = {
|
||||
},
|
||||
status: ServiceStatus.Running
|
||||
}
|
||||
},
|
||||
'indeedhub': {
|
||||
state: PackageState.Running,
|
||||
'static-files': {
|
||||
license: 'MIT',
|
||||
instructions: 'Decentralized media streaming platform',
|
||||
icon: '/assets/img/app-icons/indeedhub.png'
|
||||
},
|
||||
manifest: {
|
||||
id: 'indeedhub',
|
||||
title: 'IndeedHub',
|
||||
version: '0.1.0',
|
||||
description: {
|
||||
short: 'Decentralized media streaming platform',
|
||||
long: 'IndeedHub is a decentralized media streaming platform built on Nostr. Stream Bitcoin-focused documentaries, educational content, and independent films. Netflix-inspired interface with glassmorphism design, supporting content creators through the decentralized web.'
|
||||
},
|
||||
'release-notes': 'Initial release with Netflix-inspired interface',
|
||||
license: 'MIT',
|
||||
'wrapper-repo': 'https://github.com/indeedhub/indeedhub',
|
||||
'upstream-repo': 'https://github.com/indeedhub/indeedhub',
|
||||
'support-site': 'https://github.com/indeedhub/indeedhub/issues',
|
||||
'marketing-site': 'https://indeedhub.com',
|
||||
'donation-url': null
|
||||
},
|
||||
installed: {
|
||||
'current-dependents': {},
|
||||
'current-dependencies': {},
|
||||
'last-backup': null,
|
||||
'interface-addresses': {
|
||||
main: {
|
||||
'tor-address': 'indeedhub.onion',
|
||||
'lan-address': 'http://localhost:7777'
|
||||
}
|
||||
},
|
||||
status: ServiceStatus.Running
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,21 +2,73 @@
|
||||
<div class="marketplace-container flex flex-col h-full overflow-hidden -mt-4 md:mt-0">
|
||||
<!-- Fixed Header Section -->
|
||||
<div class="flex-shrink-0 -mt-4 md:mt-0">
|
||||
<!-- Installation Progress Banner -->
|
||||
<div v-if="installing" class="mb-6 glass-card p-4 border-l-4 border-blue-500">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-3">
|
||||
<svg class="animate-spin h-5 w-5 text-blue-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
<div>
|
||||
<p class="text-white font-medium">Installing {{ installing }}...</p>
|
||||
<p class="text-white/70 text-sm">Checking for Docker image and starting container</p>
|
||||
<!-- Installation Progress Banner - Multiple Apps -->
|
||||
<div v-if="installingApps.size > 0" class="mb-6 space-y-3">
|
||||
<div
|
||||
v-for="[appId, progress] in installingApps"
|
||||
:key="appId"
|
||||
class="glass-card p-4 border-l-4"
|
||||
:class="{
|
||||
'border-blue-500': progress.status === 'downloading' || progress.status === 'installing',
|
||||
'border-orange-500': progress.status === 'starting',
|
||||
'border-green-500': progress.status === 'complete',
|
||||
'border-red-500': progress.status === 'error'
|
||||
}"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<svg
|
||||
v-if="progress.status !== 'complete' && progress.status !== 'error'"
|
||||
class="animate-spin h-5 w-5 text-blue-400"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
<svg
|
||||
v-else-if="progress.status === 'complete'"
|
||||
class="h-5 w-5 text-green-400"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<svg
|
||||
v-else
|
||||
class="h-5 w-5 text-red-400"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||
</svg>
|
||||
<div>
|
||||
<p class="text-white font-medium">{{ progress.title }}</p>
|
||||
<p class="text-white/70 text-sm">{{ progress.message }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-white/60 text-sm">
|
||||
{{ progress.progress }}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-white/60 text-sm">
|
||||
{{ installAttempt }}/{{ maxAttempts }}s
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<div class="w-full bg-white/10 rounded-full h-2 overflow-hidden">
|
||||
<div
|
||||
class="h-full rounded-full transition-all duration-500"
|
||||
:class="{
|
||||
'bg-gradient-to-r from-blue-500 to-blue-400': progress.status === 'downloading' || progress.status === 'installing',
|
||||
'bg-gradient-to-r from-orange-500 to-orange-400': progress.status === 'starting',
|
||||
'bg-gradient-to-r from-green-500 to-green-400': progress.status === 'complete',
|
||||
'bg-gradient-to-r from-red-500 to-red-400': progress.status === 'error'
|
||||
}"
|
||||
:style="{ width: `${progress.progress}%` }"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -113,15 +165,15 @@
|
||||
<button
|
||||
v-else-if="app.source === 'local' || app.dockerImage"
|
||||
@click.stop="app.source === 'local' ? installApp(app) : installCommunityApp(app)"
|
||||
:disabled="installing === app.id || installing !== null"
|
||||
:disabled="installingApps.has(app.id)"
|
||||
class="flex-1 px-4 py-2 gradient-button rounded-lg text-sm font-medium disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
<span v-if="installing === app.id" class="flex items-center justify-center gap-2">
|
||||
<span v-if="installingApps.has(app.id)" class="flex items-center justify-center gap-2">
|
||||
<svg class="animate-spin h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Installing...
|
||||
{{ installingApps.get(app.id)?.message || 'Installing...' }}
|
||||
</span>
|
||||
<span v-else>Install</span>
|
||||
</button>
|
||||
@@ -340,10 +392,18 @@ const categories = [
|
||||
{ id: 'other', name: 'Other' }
|
||||
]
|
||||
|
||||
// Local apps state
|
||||
const installing = ref<string | null>(null)
|
||||
const installAttempt = ref(0)
|
||||
const maxAttempts = ref(30)
|
||||
// Installation state - support multiple concurrent installations
|
||||
interface InstallProgress {
|
||||
id: string
|
||||
title: string
|
||||
status: 'downloading' | 'installing' | 'starting' | 'complete' | 'error'
|
||||
progress: number // 0-100
|
||||
message: string
|
||||
attempt: number
|
||||
}
|
||||
|
||||
const installingApps = ref<Map<string, InstallProgress>>(new Map())
|
||||
const maxAttempts = ref(60)
|
||||
|
||||
// Sideload modal state
|
||||
const showSideloadModal = ref(false)
|
||||
@@ -529,13 +589,13 @@ async function loadCommunityMarketplace() {
|
||||
function getCuratedAppList() {
|
||||
return [
|
||||
{
|
||||
id: 'bitcoin',
|
||||
id: 'bitcoin-knots',
|
||||
title: 'Bitcoin Knots',
|
||||
version: '27.0.0',
|
||||
version: '28.1.0',
|
||||
description: 'Run a full Bitcoin node. Validate and relay blocks and transactions on the Bitcoin network.',
|
||||
icon: '/assets/img/app-icons/bitcoin-knots.webp',
|
||||
author: 'Bitcoin Knots',
|
||||
dockerImage: 'docker.io/lncm/bitcoind:v27.0',
|
||||
dockerImage: 'docker.io/bitcoinknots/bitcoin:latest',
|
||||
manifestUrl: null,
|
||||
repoUrl: 'https://github.com/bitcoinknots/bitcoin'
|
||||
},
|
||||
@@ -790,14 +850,30 @@ function viewAppDetails(app: any) {
|
||||
}
|
||||
|
||||
async function installApp(app: any) {
|
||||
if (installing.value || isInstalled(app.id)) return
|
||||
if (installingApps.value.has(app.id) || isInstalled(app.id)) return
|
||||
|
||||
installing.value = app.id
|
||||
// Add to installing map
|
||||
installingApps.value.set(app.id, {
|
||||
id: app.id,
|
||||
title: app.title,
|
||||
status: 'downloading',
|
||||
progress: 10,
|
||||
message: 'Preparing installation...',
|
||||
attempt: 0
|
||||
})
|
||||
|
||||
try {
|
||||
const installUrl = app.url || app.manifestUrl || app.s9pkUrl
|
||||
console.log('[Marketplace] Installing local app:', { id: app.id, url: installUrl, version: app.version })
|
||||
|
||||
// Update progress
|
||||
installingApps.value.set(app.id, {
|
||||
...installingApps.value.get(app.id)!,
|
||||
status: 'downloading',
|
||||
progress: 30,
|
||||
message: 'Downloading package...'
|
||||
})
|
||||
|
||||
await rpcClient.call({
|
||||
method: 'package.install',
|
||||
params: {
|
||||
@@ -807,43 +883,93 @@ async function installApp(app: any) {
|
||||
}
|
||||
})
|
||||
|
||||
// Wait for installation to complete (poll for package to appear)
|
||||
installAttempt.value = 0
|
||||
// Update progress
|
||||
installingApps.value.set(app.id, {
|
||||
...installingApps.value.get(app.id)!,
|
||||
status: 'installing',
|
||||
progress: 60,
|
||||
message: 'Installing package...'
|
||||
})
|
||||
|
||||
// Wait for installation to complete (poll for package to appear)
|
||||
const checkInstalled = setInterval(() => {
|
||||
installAttempt.value++
|
||||
const current = installingApps.value.get(app.id)
|
||||
if (!current) {
|
||||
clearInterval(checkInstalled)
|
||||
return
|
||||
}
|
||||
|
||||
const newAttempt = current.attempt + 1
|
||||
installingApps.value.set(app.id, {
|
||||
...current,
|
||||
attempt: newAttempt,
|
||||
progress: Math.min(60 + (newAttempt * 0.5), 95),
|
||||
message: 'Starting application...'
|
||||
})
|
||||
|
||||
if (isInstalled(app.id)) {
|
||||
clearInterval(checkInstalled)
|
||||
installing.value = null
|
||||
installAttempt.value = 0
|
||||
installingApps.value.set(app.id, {
|
||||
...current,
|
||||
status: 'complete',
|
||||
progress: 100,
|
||||
message: 'Installation complete!'
|
||||
})
|
||||
setTimeout(() => {
|
||||
router.push('/dashboard/apps')
|
||||
}, 500)
|
||||
} else if (installAttempt.value >= maxAttempts.value) {
|
||||
installingApps.value.delete(app.id)
|
||||
}, 2000)
|
||||
} else if (newAttempt >= maxAttempts.value) {
|
||||
clearInterval(checkInstalled)
|
||||
installing.value = null
|
||||
installAttempt.value = 0
|
||||
alert('Installation timeout. Please check the backend logs or try refreshing the page.')
|
||||
installingApps.value.set(app.id, {
|
||||
...current,
|
||||
status: 'error',
|
||||
progress: 0,
|
||||
message: 'Installation timeout'
|
||||
})
|
||||
setTimeout(() => {
|
||||
installingApps.value.delete(app.id)
|
||||
}, 5000)
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
} catch (err) {
|
||||
console.error('Installation failed:', err)
|
||||
alert(`Failed to install ${app.title}: ${err}`)
|
||||
installing.value = null
|
||||
installAttempt.value = 0
|
||||
installingApps.value.set(app.id, {
|
||||
...installingApps.value.get(app.id)!,
|
||||
status: 'error',
|
||||
progress: 0,
|
||||
message: `Failed: ${err}`
|
||||
})
|
||||
setTimeout(() => {
|
||||
installingApps.value.delete(app.id)
|
||||
}, 5000)
|
||||
}
|
||||
}
|
||||
|
||||
async function installCommunityApp(app: any) {
|
||||
if (installing.value || isInstalled(app.id) || !app.dockerImage) return
|
||||
if (installingApps.value.has(app.id) || isInstalled(app.id) || !app.dockerImage) return
|
||||
|
||||
installing.value = app.id
|
||||
// Add to installing map
|
||||
installingApps.value.set(app.id, {
|
||||
id: app.id,
|
||||
title: app.title,
|
||||
status: 'downloading',
|
||||
progress: 10,
|
||||
message: 'Pulling Docker image...',
|
||||
attempt: 0
|
||||
})
|
||||
|
||||
try {
|
||||
console.log(`[Marketplace] Installing Docker app ${app.title} using image ${app.dockerImage}`)
|
||||
|
||||
// Update progress
|
||||
installingApps.value.set(app.id, {
|
||||
...installingApps.value.get(app.id)!,
|
||||
status: 'downloading',
|
||||
progress: 20,
|
||||
message: 'Downloading container image...'
|
||||
})
|
||||
|
||||
await rpcClient.call({
|
||||
method: 'package.install',
|
||||
params: {
|
||||
@@ -854,31 +980,66 @@ async function installCommunityApp(app: any) {
|
||||
timeout: 180000 // 3 minutes for large images like Nextcloud
|
||||
})
|
||||
|
||||
// Wait for installation to complete (poll for package to appear)
|
||||
installAttempt.value = 0
|
||||
// Update progress
|
||||
installingApps.value.set(app.id, {
|
||||
...installingApps.value.get(app.id)!,
|
||||
status: 'installing',
|
||||
progress: 60,
|
||||
message: 'Starting container...'
|
||||
})
|
||||
|
||||
// Wait for installation to complete (poll for package to appear)
|
||||
const checkInstalled = setInterval(() => {
|
||||
installAttempt.value++
|
||||
const current = installingApps.value.get(app.id)
|
||||
if (!current) {
|
||||
clearInterval(checkInstalled)
|
||||
return
|
||||
}
|
||||
|
||||
const newAttempt = current.attempt + 1
|
||||
installingApps.value.set(app.id, {
|
||||
...current,
|
||||
attempt: newAttempt,
|
||||
progress: Math.min(60 + (newAttempt * 0.5), 95),
|
||||
message: 'Initializing application...'
|
||||
})
|
||||
|
||||
if (isInstalled(app.id)) {
|
||||
clearInterval(checkInstalled)
|
||||
installing.value = null
|
||||
installAttempt.value = 0
|
||||
installingApps.value.set(app.id, {
|
||||
...current,
|
||||
status: 'complete',
|
||||
progress: 100,
|
||||
message: 'Installation complete!'
|
||||
})
|
||||
setTimeout(() => {
|
||||
router.push('/dashboard/apps')
|
||||
}, 500)
|
||||
} else if (installAttempt.value >= maxAttempts.value) {
|
||||
installingApps.value.delete(app.id)
|
||||
}, 2000)
|
||||
} else if (newAttempt >= maxAttempts.value) {
|
||||
clearInterval(checkInstalled)
|
||||
installing.value = null
|
||||
installAttempt.value = 0
|
||||
console.error('[Marketplace] Installation timeout')
|
||||
installingApps.value.set(app.id, {
|
||||
...current,
|
||||
status: 'error',
|
||||
progress: 0,
|
||||
message: 'Installation timeout'
|
||||
})
|
||||
setTimeout(() => {
|
||||
installingApps.value.delete(app.id)
|
||||
}, 5000)
|
||||
}
|
||||
}, 1000)
|
||||
|
||||
} catch (err) {
|
||||
console.error('[Marketplace] Installation failed:', err)
|
||||
installing.value = null
|
||||
installAttempt.value = 0
|
||||
installingApps.value.set(app.id, {
|
||||
...installingApps.value.get(app.id)!,
|
||||
status: 'error',
|
||||
progress: 0,
|
||||
message: `Failed: ${err}`
|
||||
})
|
||||
setTimeout(() => {
|
||||
installingApps.value.delete(app.id)
|
||||
}, 5000)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user