fix(ui): shorten install/uninstall/update timeouts for async RPCs
With the backend flipped to async-spawn, install/uninstall/update return
immediately with a { status, package_id } envelope. Client timeouts of
45m/11m were a leftover from synchronous handlers and masked real RPC
failures.
Drop all install/uninstall/update RPC timeouts to 15s. Progress and
terminal state still arrive through the live state stream — the RPC
only needs to confirm the spawn was accepted.
Return-type annotations updated in rpc-client.ts and stores/server.ts.
Five direct rpcClient.call sites across Marketplace.vue, Discover.vue,
and MarketplaceAppDetails.vue updated with the shorter timeout.
This commit is contained in:
parent
1ad889608f
commit
702b5d64d3
@ -521,23 +521,30 @@ class RPCClient {
|
||||
})
|
||||
}
|
||||
|
||||
async installPackage(id: string, marketplaceUrl: string, version: string): Promise<string> {
|
||||
async installPackage(
|
||||
id: string,
|
||||
marketplaceUrl: string,
|
||||
version: string,
|
||||
): Promise<{ status: string; package_id: string }> {
|
||||
// Backend is async — returns { status: 'installing' } in <1s after
|
||||
// flipping state and spawning the pull/install pipeline. Progress is
|
||||
// streamed via WebSocket (install_progress field on the package entry).
|
||||
return this.call({
|
||||
method: 'package.install',
|
||||
params: { id, 'marketplace-url': marketplaceUrl, version },
|
||||
// 45 min — IndeedHub is 6 images and gitea raw-file throughput is
|
||||
// ~70 KB/s per image; 15 min was short enough to kill the install
|
||||
// mid-pull and land the user on a "didn't work" screen while the
|
||||
// backend kept working in the background.
|
||||
timeout: 2700000,
|
||||
timeout: 15000,
|
||||
})
|
||||
}
|
||||
|
||||
async uninstallPackage(id: string): Promise<void> {
|
||||
async uninstallPackage(id: string): Promise<{ status: string; package_id: string }> {
|
||||
// Backend is async — returns { status: 'removing' } immediately after
|
||||
// flipping state. Graceful stop (up to 600s for bitcoin) and data wipe
|
||||
// (up to minutes for large chainstate) run in a background task.
|
||||
// Progress shown via uninstall_stage field on the package entry.
|
||||
return this.call({
|
||||
method: 'package.uninstall',
|
||||
params: { id },
|
||||
timeout: 660000, // Bitcoin Knots needs up to 600s for UTXO flush
|
||||
timeout: 15000,
|
||||
})
|
||||
}
|
||||
|
||||
@ -545,7 +552,7 @@ class RPCClient {
|
||||
return this.call({
|
||||
method: 'package.start',
|
||||
params: { id },
|
||||
timeout: 60000,
|
||||
timeout: 15000,
|
||||
})
|
||||
}
|
||||
|
||||
@ -553,7 +560,7 @@ class RPCClient {
|
||||
return this.call({
|
||||
method: 'package.stop',
|
||||
params: { id },
|
||||
timeout: 120000,
|
||||
timeout: 15000,
|
||||
})
|
||||
}
|
||||
|
||||
@ -561,15 +568,18 @@ class RPCClient {
|
||||
return this.call({
|
||||
method: 'package.restart',
|
||||
params: { id },
|
||||
timeout: 120000,
|
||||
timeout: 15000,
|
||||
})
|
||||
}
|
||||
|
||||
async updatePackage(id: string): Promise<{ status: string }> {
|
||||
async updatePackage(id: string): Promise<{ status: string; package_id: string }> {
|
||||
// Backend is async — returns { status: 'updating' } immediately after
|
||||
// flipping state. Pull / stop / recreate / verify runs in background,
|
||||
// with rollback-on-failure.
|
||||
return this.call({
|
||||
method: 'package.update',
|
||||
params: { id },
|
||||
timeout: 660000, // Bitcoin Knots needs up to 600s for graceful shutdown
|
||||
timeout: 15000,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -100,12 +100,19 @@ export const useServerStore = defineStore('server', () => {
|
||||
const isShuttingDown = computed(() => sync.serverInfo?.['status-info']?.['shutting-down'] || false)
|
||||
const isOffline = computed(() => !sync.isConnected || isRestarting.value || isShuttingDown.value)
|
||||
|
||||
// Package actions
|
||||
async function installPackage(id: string, marketplaceUrl: string, version: string): Promise<string> {
|
||||
// Package actions. install/uninstall/update are async on the backend:
|
||||
// the RPC returns immediately with { status: 'installing'|'removing'|'updating',
|
||||
// package_id } after flipping state, and the real work runs in a spawn.
|
||||
// Progress is streamed via the WebSocket state push, not the RPC response.
|
||||
async function installPackage(
|
||||
id: string,
|
||||
marketplaceUrl: string,
|
||||
version: string,
|
||||
): Promise<{ status: string; package_id: string }> {
|
||||
return rpcClient.installPackage(id, marketplaceUrl, version)
|
||||
}
|
||||
|
||||
async function uninstallPackage(id: string): Promise<void> {
|
||||
async function uninstallPackage(id: string): Promise<{ status: string; package_id: string }> {
|
||||
return rpcClient.uninstallPackage(id)
|
||||
}
|
||||
|
||||
@ -121,7 +128,7 @@ export const useServerStore = defineStore('server', () => {
|
||||
return rpcClient.restartPackage(id)
|
||||
}
|
||||
|
||||
async function updatePackage(id: string): Promise<{ status: string }> {
|
||||
async function updatePackage(id: string): Promise<{ status: string; package_id: string }> {
|
||||
return rpcClient.updatePackage(id)
|
||||
}
|
||||
|
||||
|
||||
@ -489,7 +489,7 @@ async function installApp(app: MarketplaceApp) {
|
||||
try {
|
||||
const installUrl = app.url || app.manifestUrl || app.s9pkUrl
|
||||
installingApps.set(app.id, { ...installingApps.get(app.id)!, status: 'downloading', progress: 30, message: 'Downloading package...' })
|
||||
await rpcClient.call({ method: 'package.install', params: { id: app.id, url: installUrl, version: app.version } })
|
||||
await rpcClient.call({ method: 'package.install', params: { id: app.id, url: installUrl, version: app.version }, timeout: 15000 })
|
||||
installingApps.set(app.id, { ...installingApps.get(app.id)!, status: 'installing', progress: 60, message: 'Installing package...' })
|
||||
startInstallPolling(app.id, 'Starting application...')
|
||||
} catch (err) {
|
||||
@ -511,7 +511,7 @@ async function installCommunityApp(app: MarketplaceApp) {
|
||||
if ((app as Record<string, unknown>).containerConfig) {
|
||||
installParams.containerConfig = (app as Record<string, unknown>).containerConfig
|
||||
}
|
||||
await rpcClient.call({ method: 'package.install', params: installParams, timeout: 180000 })
|
||||
await rpcClient.call({ method: 'package.install', params: installParams, timeout: 15000 })
|
||||
installingApps.set(app.id, { ...installingApps.get(app.id)!, status: 'installing', progress: 60, message: 'Starting container...' })
|
||||
startInstallPolling(app.id, 'Initializing application...')
|
||||
} catch (err) {
|
||||
|
||||
@ -418,7 +418,7 @@ async function installApp(app: MarketplaceApp) {
|
||||
|
||||
installingApps.set(app.id, { ...installingApps.get(app.id)!, status: 'downloading', progress: 30, message: 'Downloading package...' })
|
||||
|
||||
await rpcClient.call({ method: 'package.install', params: { id: app.id, url: installUrl, version: app.version } })
|
||||
await rpcClient.call({ method: 'package.install', params: { id: app.id, url: installUrl, version: app.version }, timeout: 15000 })
|
||||
|
||||
installingApps.set(app.id, { ...installingApps.get(app.id)!, status: 'installing', progress: 60, message: 'Installing package...' })
|
||||
|
||||
@ -447,7 +447,7 @@ async function installCommunityApp(app: MarketplaceApp) {
|
||||
await rpcClient.call({
|
||||
method: 'package.install',
|
||||
params: { id: app.id, dockerImage: app.dockerImage, version: app.version },
|
||||
timeout: 180000
|
||||
timeout: 15000
|
||||
})
|
||||
|
||||
installingApps.set(app.id, { ...installingApps.get(app.id)!, status: 'installing', progress: 60, message: 'Starting container...' })
|
||||
|
||||
@ -546,7 +546,7 @@ async function installDependencies() {
|
||||
id: dep.id,
|
||||
dockerImage: dep.dockerImage,
|
||||
},
|
||||
timeout: 180000,
|
||||
timeout: 15000,
|
||||
})
|
||||
// Wait for package to register before installing next
|
||||
await new Promise(resolve => setTimeout(resolve, 2000))
|
||||
@ -579,7 +579,7 @@ async function installApp() {
|
||||
dockerImage: app.value.dockerImage,
|
||||
version: app.value.version,
|
||||
},
|
||||
timeout: 180000,
|
||||
timeout: 15000,
|
||||
})
|
||||
} else {
|
||||
// Package-based installation
|
||||
@ -591,6 +591,7 @@ async function installApp() {
|
||||
url: installUrl,
|
||||
version: app.value.version,
|
||||
},
|
||||
timeout: 15000,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user