88 lines
6.2 KiB
Vue
88 lines
6.2 KiB
Vue
<script setup lang="ts">
|
|
const { overview, job, pending, connected, submitting, refreshing, message, accessDenied, recovery, busy, available, canApply,
|
|
check, apply, refresh, sendPending } = useSelfhostUpdates()
|
|
const build = useRuntimeConfig().public
|
|
const confirmOpen = ref(false)
|
|
const confirmed = ref(false)
|
|
const phaseLabels: Record<string, string> = {
|
|
preparing: 'Update vorbereiten', validating: 'Release und Konfiguration prüfen', checked: 'Prüfung abgeschlossen',
|
|
pulling: 'Images herunterladen', maintenance: 'Dienste anhalten und Backup erstellen', backed_up: 'Backup abgeschlossen',
|
|
applying: 'Compose und Konfiguration aktualisieren', retiring_matrix: 'Alte Matrix-Dienste stilllegen',
|
|
migrating: 'Datenbank migrieren', starting: 'Dienste starten', verifying: 'Version und Erreichbarkeit prüfen',
|
|
complete: 'Abgeschlossen', resolved: 'Wiederherstellung bestätigt',
|
|
}
|
|
const statusLabels: Record<string, string> = {
|
|
queued: 'Auftrag angenommen', running: 'Auftrag läuft', checked: 'Update verfügbar', up_to_date: 'FEDEO ist aktuell',
|
|
succeeded: 'Update erfolgreich abgeschlossen', failed: 'Auftrag fehlgeschlagen', interrupted: 'Auftrag unterbrochen',
|
|
idle: 'Bereit', recovered: 'Wiederherstellung bestätigt', unknown: 'Auftragsstatus wird geladen',
|
|
}
|
|
const progress = computed(() => job.value?.progress || (busy.value ? overview.value?.engine : null))
|
|
const statusLabel = computed(() => phaseLabels[progress.value?.phase || ''] || statusLabels[job.value?.status || 'idle'])
|
|
const backupSize = computed(() => {
|
|
const bytes = job.value?.plan?.estimatedBackupBytes
|
|
return bytes === undefined ? 'Wird vor dem Start geprüft' : `${(bytes / 1024 ** 3).toLocaleString('de-DE', { maximumFractionDigits: 1 })} GiB`
|
|
})
|
|
const openConfirmation = () => { confirmed.value = false; confirmOpen.value = true }
|
|
const start = async () => {
|
|
if (!confirmed.value || !canApply.value) return
|
|
confirmOpen.value = false
|
|
await apply()
|
|
}
|
|
const reload = () => window.location.reload()
|
|
</script>
|
|
|
|
<template>
|
|
<UCard :ui="{ root: 'rounded-lg' }">
|
|
<template #header>
|
|
<div class="flex flex-wrap items-center justify-between gap-3">
|
|
<div class="flex items-center gap-2">
|
|
<UIcon name="i-heroicons-arrow-down-tray" class="size-5 text-primary" />
|
|
<h2 class="text-base font-semibold text-highlighted">FEDEO-Updates</h2>
|
|
</div>
|
|
<UBadge color="neutral" variant="soft">Installiert: {{ overview?.backend.version || build.fedeoVersion }}</UBadge>
|
|
</div>
|
|
</template>
|
|
<div class="space-y-4">
|
|
<p class="text-sm text-muted">Aktualisiert die gesamte Selfhost-Instanz einschließlich Docker Compose und Datenbank. Vor Änderungen wird ein Backup erstellt.</p>
|
|
<div role="status" aria-live="polite" class="space-y-3">
|
|
<UAlert v-if="message" :color="busy ? 'warning' : 'neutral'" variant="soft" :title="message" />
|
|
<UAlert v-if="recovery" color="error" title="Wiederherstellung auf dem Host erforderlich" description="Weitere Updates sind gesperrt. Den letzten Auftrag und die Backups auf dem Host prüfen." />
|
|
<UAlert v-if="job?.error" color="error" :title="job.error.message" />
|
|
<UAlert v-if="job?.plan?.requiresComposeAdoption" color="warning" title="Compose-Datei zuerst auf dem Host übernehmen" description="Die bestehende Konfiguration benötigt eine manuelle Prüfung mit dem CLI-Updater. Anschließend hier erneut nach Updates suchen." />
|
|
<p v-if="job || busy" class="text-sm font-medium">{{ statusLabel }}</p>
|
|
<UProgress v-if="busy && !pending" aria-label="Update-Fortschritt" />
|
|
</div>
|
|
<dl v-if="job?.plan" class="grid gap-3 text-sm sm:grid-cols-3">
|
|
<div><dt class="text-muted">Geprüfte Version</dt><dd class="font-medium">{{ job.plan.version }}</dd></div>
|
|
<div><dt class="text-muted">Commit</dt><dd class="font-mono" :title="job.plan.revision">{{ job.plan.revision?.slice(0, 8) }}</dd></div>
|
|
<div><dt class="text-muted">Geschätztes Backup</dt><dd>{{ backupSize }}</dd></div>
|
|
</dl>
|
|
<p v-if="job?.plan?.retiredServices.length" class="text-sm text-muted">Alte Matrix-Dienste werden stillgelegt; Daten und Container bleiben erhalten: {{ job.plan.retiredServices.join(', ') }}.</p>
|
|
<p v-if="job?.requestId" class="break-all text-xs text-muted">Auftrag: {{ job.requestId }}</p>
|
|
<div class="flex flex-wrap gap-2">
|
|
<UButton icon="i-heroicons-magnifying-glass" :disabled="!available" :loading="submitting && pending?.command === 'check'" @click="check">Nach Updates suchen</UButton>
|
|
<UButton v-if="canApply" icon="i-heroicons-arrow-down-tray" @click="openConfirmation">Update auf {{ job?.plan?.version }} installieren</UButton>
|
|
<UButton v-if="pending && connected && !accessDenied" color="warning" :loading="submitting" @click="sendPending">Auftrag erneut senden</UButton>
|
|
<UButton v-if="!connected && !accessDenied" color="neutral" variant="outline" :loading="refreshing" @click="refresh">Verbindung prüfen</UButton>
|
|
<UButton v-if="job?.command === 'apply' && job.status === 'succeeded'" color="success" @click="reload">Neue Version laden</UButton>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
<UModal v-model:open="confirmOpen" title="FEDEO-Update installieren" description="Dieses Update betrifft die gesamte Instanz und alle Mandanten.">
|
|
<template #body>
|
|
<div class="space-y-4 text-sm">
|
|
<p>Version <strong>{{ job?.plan?.version }}</strong> wird installiert. Dabei werden die Dienste vorübergehend angehalten, Daten gesichert und Datenbankmigrationen ausgeführt.</p>
|
|
<p>Die Verbindung kann währenddessen abbrechen. Der Host setzt den Auftrag fort; diese Seite fragt den Status automatisch wieder ab.</p>
|
|
<p>Falls die Migration fehlschlägt, kann eine manuelle Wiederherstellung auf dem Host erforderlich sein.</p>
|
|
<UCheckbox v-model="confirmed" label="Die Wartungsunterbrechung für alle Mandanten ist abgestimmt." />
|
|
</div>
|
|
</template>
|
|
<template #footer>
|
|
<div class="flex flex-wrap justify-end gap-2">
|
|
<UButton color="neutral" variant="outline" @click="confirmOpen = false">Abbrechen</UButton>
|
|
<UButton :disabled="!confirmed || !canApply" :loading="submitting" @click="start">Jetzt installieren</UButton>
|
|
</div>
|
|
</template>
|
|
</UModal>
|
|
</template>
|