Add error handling and timeout to system operations
This commit is contained in:
parent
f54cc401dc
commit
f1fc4fcde8
|
|
@ -14,15 +14,15 @@ export const unshareApp = (name: string) =>
|
|||
fetch(`/api/apps/${name}/tunnel`, { method: 'DELETE' })
|
||||
|
||||
export const applyUpdate = () =>
|
||||
fetch('/api/system/update', { method: 'POST' }).then(r => r.json())
|
||||
fetch('/api/system/update', { method: 'POST' }).then(r => { if (!r.ok) throw new Error('update failed'); return r.json() })
|
||||
|
||||
export const checkForUpdate = (): Promise<{ available: boolean, current: string, latest: string, commits: string[] }> =>
|
||||
fetch('/api/system/update').then(r => r.json())
|
||||
fetch('/api/system/update').then(r => { if (!r.ok) throw new Error('check failed'); return r.json() })
|
||||
|
||||
export const restartApp = (name: string) => fetch(`/api/apps/${name}/restart`, { method: 'POST' })
|
||||
|
||||
export const restartServer = () =>
|
||||
fetch('/api/system/restart', { method: 'POST' }).then(r => r.json())
|
||||
fetch('/api/system/restart', { method: 'POST' }).then(r => { if (!r.ok) throw new Error('restart failed'); return r.json() })
|
||||
|
||||
export const startApp = (name: string) => fetch(`/api/apps/${name}/start`, { method: 'POST' })
|
||||
|
||||
|
|
|
|||
|
|
@ -19,8 +19,15 @@ import {
|
|||
|
||||
type UpdateInfo = { available: boolean, current: string, latest: string, commits: string[] }
|
||||
|
||||
function pollUntilBack(onBack: () => void) {
|
||||
function pollUntilBack(onBack: () => void, onTimeout?: () => void) {
|
||||
let elapsed = 0
|
||||
const poll = setInterval(async () => {
|
||||
elapsed += 2000
|
||||
if (elapsed > 60000) {
|
||||
clearInterval(poll)
|
||||
onTimeout?.()
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await fetch('/api/system/info')
|
||||
if (res.ok) {
|
||||
|
|
@ -62,17 +69,21 @@ export function SettingsPage({ render }: { render: () => void }) {
|
|||
setTheme()
|
||||
}
|
||||
|
||||
const handleRestart = () => {
|
||||
if (!confirm('Are you sure you want to restart the server?')) return
|
||||
setRestarting(true)
|
||||
restartServer().catch(() => {})
|
||||
pollUntilBack(() => {
|
||||
setRestarting(false)
|
||||
const refreshSystemInfo = () => {
|
||||
getSystemInfo().then(info => {
|
||||
setVersion(info.version)
|
||||
setSha(info.sha)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const handleRestart = () => {
|
||||
if (!confirm('Are you sure you want to restart the server?')) return
|
||||
setRestarting(true)
|
||||
restartServer().catch(() => {})
|
||||
pollUntilBack(
|
||||
() => { setRestarting(false); refreshSystemInfo() },
|
||||
() => { setRestarting(false) },
|
||||
)
|
||||
}
|
||||
|
||||
const handleCheckUpdate = async () => {
|
||||
|
|
@ -91,15 +102,13 @@ export function SettingsPage({ render }: { render: () => void }) {
|
|||
setUpdating(true)
|
||||
try {
|
||||
await applyUpdate()
|
||||
} catch {}
|
||||
pollUntilBack(() => {
|
||||
pollUntilBack(
|
||||
() => { setUpdating(false); refreshSystemInfo(); setUpdateInfo(null) },
|
||||
() => { setUpdating(false) },
|
||||
)
|
||||
} catch {
|
||||
setUpdating(false)
|
||||
getSystemInfo().then(info => {
|
||||
setVersion(info.version)
|
||||
setSha(info.sha)
|
||||
})
|
||||
setUpdateInfo(null)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -186,8 +186,10 @@ router.sse('/metrics/stream', (send) => {
|
|||
})
|
||||
|
||||
// System info
|
||||
const pkg = JSON.parse(readFileSync(join(import.meta.dir, '../../../package.json'), 'utf-8'))
|
||||
const sha = Bun.spawnSync(['git', 'rev-parse', '--short', 'HEAD'], { cwd: join(import.meta.dir, '../../..') }).stdout.toString().trim() || 'unknown'
|
||||
const projectRoot = join(import.meta.dir, '../../..')
|
||||
const pkg = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf-8'))
|
||||
const sha = Bun.spawnSync(['git', 'rev-parse', '--short', 'HEAD'], { cwd: projectRoot }).stdout.toString().trim() || 'unknown'
|
||||
let isUpdating = false
|
||||
|
||||
router.get('/info', c => {
|
||||
return c.json({ version: pkg.version, sha })
|
||||
|
|
@ -274,16 +276,15 @@ router.post('/restart', c => {
|
|||
|
||||
// Check for updates
|
||||
router.get('/update', async c => {
|
||||
const cwd = join(import.meta.dir, '../../..')
|
||||
try {
|
||||
const fetch = Bun.spawnSync(['git', 'fetch', 'origin', 'main'], { cwd })
|
||||
if (fetch.exitCode !== 0) return c.json({ available: false })
|
||||
const gitFetch = await Bun.spawn(['git', 'fetch', 'origin', 'main'], { cwd: projectRoot }).exited
|
||||
if (gitFetch !== 0) return c.json({ available: false })
|
||||
|
||||
const log = Bun.spawnSync(['git', 'log', 'HEAD..origin/main', '--oneline'], { cwd })
|
||||
const log = Bun.spawnSync(['git', 'log', 'HEAD..origin/main', '--oneline'], { cwd: projectRoot })
|
||||
const output = log.stdout.toString().trim()
|
||||
const commits = output ? output.split('\n') : []
|
||||
|
||||
const latest = Bun.spawnSync(['git', 'rev-parse', '--short', 'origin/main'], { cwd })
|
||||
const latest = Bun.spawnSync(['git', 'rev-parse', '--short', 'origin/main'], { cwd: projectRoot })
|
||||
.stdout.toString().trim()
|
||||
|
||||
return c.json({
|
||||
|
|
@ -299,21 +300,24 @@ router.get('/update', async c => {
|
|||
|
||||
// Apply update and restart
|
||||
router.post('/update', async c => {
|
||||
const cwd = join(import.meta.dir, '../../..')
|
||||
if (isUpdating) return c.json({ ok: false, error: 'update already in progress' }, 409)
|
||||
isUpdating = true
|
||||
try {
|
||||
const pull = Bun.spawnSync(['git', 'pull', 'origin', 'main'], { cwd })
|
||||
if (pull.exitCode !== 0) return c.json({ ok: false, error: 'git pull failed' }, 500)
|
||||
const pull = await Bun.spawn(['git', 'pull', 'origin', 'main'], { cwd: projectRoot }).exited
|
||||
if (pull !== 0) return c.json({ ok: false, error: 'git pull failed' }, 500)
|
||||
|
||||
const install = Bun.spawnSync(['bun', 'install'], { cwd })
|
||||
if (install.exitCode !== 0) return c.json({ ok: false, error: 'bun install failed' }, 500)
|
||||
const install = await Bun.spawn(['bun', 'install'], { cwd: projectRoot }).exited
|
||||
if (install !== 0) return c.json({ ok: false, error: 'bun install failed' }, 500)
|
||||
|
||||
const build = Bun.spawnSync(['bun', 'run', 'build'], { cwd })
|
||||
if (build.exitCode !== 0) return c.json({ ok: false, error: 'build failed' }, 500)
|
||||
const build = await Bun.spawn(['bun', 'run', 'build'], { cwd: projectRoot }).exited
|
||||
if (build !== 0) return c.json({ ok: false, error: 'build failed' }, 500)
|
||||
|
||||
setTimeout(() => process.exit(0), 100)
|
||||
return c.json({ ok: true })
|
||||
} catch {
|
||||
return c.json({ ok: false, error: 'update failed' }, 500)
|
||||
} finally {
|
||||
isUpdating = false
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user