Skip to content

Commit 1921be8

Browse files
committed
feat(updates): enhance backup functionality and improve update checks with error handling
1 parent 3a5d96e commit 1921be8

5 files changed

Lines changed: 26 additions & 5 deletions

File tree

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ WORKDIR /app
2929

3030
ENV NODE_ENV=production
3131

32+
# PostgreSQL client tools for database backup via /api/updates/backup
33+
RUN apk add --no-cache postgresql16-client
34+
3235
RUN addgroup -S reqcore && adduser -S reqcore -G reqcore
3336

3437
# .output is fully self-contained (server, public assets)

app/components/AppTopBar.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ onUnmounted(() => document.removeEventListener('click', onClickOutsideUser))
204204
? 'text-brand-600 dark:text-brand-400 bg-brand-50 dark:bg-brand-950/40'
205205
: 'text-surface-500 dark:text-surface-400 hover:text-surface-700 dark:hover:text-surface-200 hover:bg-surface-100 dark:hover:bg-surface-800'"
206206
title="Updates & changelog"
207+
aria-label="Updates & changelog"
207208
>
208209
<ArrowUpCircle class="size-4" />
209210
</NuxtLink>

app/pages/dashboard/updates.vue

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -233,24 +233,30 @@ function formatDate(dateString: string | null | undefined): string {
233233
class="flex items-center justify-center size-10 rounded-lg"
234234
:class="versionInfo?.updateAvailable
235235
? 'bg-warning-50 dark:bg-warning-950 text-warning-600 dark:text-warning-400'
236-
: 'bg-success-50 dark:bg-success-950 text-success-600 dark:text-success-400'"
236+
: !versionInfo && !versionLoading
237+
? 'bg-surface-100 dark:bg-surface-800 text-surface-500 dark:text-surface-400'
238+
: 'bg-success-50 dark:bg-success-950 text-success-600 dark:text-success-400'"
237239
>
238240
<ArrowUpCircle v-if="versionInfo?.updateAvailable" class="size-5" />
241+
<AlertTriangle v-else-if="!versionInfo && !versionLoading" class="size-5" />
239242
<CheckCircle2 v-else class="size-5" />
240243
</div>
241244
<div>
242245
<h2 class="text-base font-semibold text-surface-900 dark:text-surface-100">
243-
{{ versionInfo?.updateAvailable ? 'Update available' : 'Up to date' }}
246+
{{ versionInfo?.updateAvailable ? 'Update available' : !versionInfo && !versionLoading ? 'Unable to check' : 'Up to date' }}
244247
</h2>
245248
<p class="text-sm text-surface-500 dark:text-surface-400">
246249
<template v-if="versionLoading">
247250
Checking for updates…
248251
</template>
249-
<template v-else-if="versionInfo?.updateAvailable">
252+
<template v-else-if="!versionInfo">
253+
Could not check for updates. Verify your network connection and try again.
254+
</template>
255+
<template v-else-if="versionInfo.updateAvailable">
250256
Version {{ versionInfo.latestVersion }} is available (you're on {{ versionInfo.currentVersion }})
251257
</template>
252258
<template v-else>
253-
You're running the latest version ({{ versionInfo?.currentVersion }})
259+
You're running the latest version ({{ versionInfo.currentVersion }})
254260
</template>
255261
</p>
256262
</div>
@@ -550,6 +556,11 @@ function formatDate(dateString: string | null | undefined): string {
550556
Node.js {{ systemInfo.nodeVersion }}
551557
</div>
552558
</div>
559+
560+
<div v-else class="px-6 py-8 text-center">
561+
<AlertTriangle class="size-5 mx-auto text-surface-400 mb-2" />
562+
<p class="text-sm text-surface-500 dark:text-surface-400">Failed to load system information.</p>
563+
</div>
553564
</section>
554565

555566
<!-- Expand / Collapse controls -->

server/api/updates/backup.post.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,16 @@ interface BackupResult {
2222
export default defineEventHandler(async (event) => {
2323
await requirePermission(event, { organization: ['delete'] })
2424

25-
const { mkdir } = await import('node:fs/promises')
25+
const { mkdir, writeFile, unlink } = await import('node:fs/promises')
26+
const { join } = await import('node:path')
2627
const backupDir = '/data/backups'
2728
let effectiveDir = backupDir
2829
try {
2930
await mkdir(backupDir, { recursive: true })
31+
// Probe actual write access — mkdir succeeds even on read-only mounts
32+
const probe = join(backupDir, `.probe-${Date.now()}`)
33+
await writeFile(probe, '')
34+
await unlink(probe)
3035
}
3136
catch {
3237
// Fall back to /tmp if /data/backups is not writable (e.g. non-Docker)

server/api/updates/version.get.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export default defineEventHandler(async (event) => {
2727
'Accept': 'application/vnd.github.v3+json',
2828
'User-Agent': `Reqcore/${currentVersion}`,
2929
},
30+
signal: AbortSignal.timeout(10_000),
3031
},
3132
)
3233

0 commit comments

Comments
 (0)