diff --git a/components/BackupRun.vue b/components/BackupRun.vue index e903791..cae972c 100644 --- a/components/BackupRun.vue +++ b/components/BackupRun.vue @@ -1,15 +1,31 @@ @@ -51,6 +84,7 @@ type BackupScript = { key: string label: string icon: string + downloadFolders?: string[] } type BackupScriptListResponse = { @@ -61,27 +95,58 @@ type BackupScriptRunResponse = { ok: boolean key: string label: string + downloadFolders?: string[] output: string } +type ScriptResult = { + key: string | null + label: string + output: string + isError: boolean + downloadFolders: string[] +} + +const emit = defineEmits<{ + result: [payload: ScriptResult] +}>() + const active = ref(null) const loading = ref(true) const runningKey = ref(null) const scripts = ref([]) -const output = ref("") -const message = ref("") +const output = ref("") +const message = ref("") const isError = ref(false) const statusClass = computed(() => (isError.value ? "status-error" : "status-success")) const loadScripts = async () => { loading.value = true + message.value = "" + output.value = "" + isError.value = false + emit("result", { + key: null, + label: "", + output: "", + isError: false, + downloadFolders: [] + }) try { const data = await $fetch("/api/backup-script") scripts.value = data.scripts } catch (error) { + scripts.value = [] isError.value = true message.value = `Erreur chargement scripts: ${error instanceof Error ? error.message : String(error)}` + emit("result", { + key: null, + label: "", + output: "", + isError: true, + downloadFolders: [] + }) } finally { loading.value = false } @@ -99,12 +164,26 @@ const runScript = async (key: string) => { method: "POST", body: { key } }) - message.value = `${data.label} execute` - output.value = data.output + message.value = `${data.label} execute avec succes` + output.value = data.output || "Aucune sortie retournee." + emit("result", { + key: data.key, + label: data.label, + output: output.value, + isError: false, + downloadFolders: data.downloadFolders || [] + }) } catch (error: any) { isError.value = true message.value = error?.data?.statusMessage || "Erreur execution script" output.value = "" + emit("result", { + key, + label: scripts.value.find((item) => item.key === key)?.label || key, + output: "", + isError: true, + downloadFolders: [] + }) } finally { runningKey.value = null } @@ -154,6 +233,11 @@ onMounted(loadScripts) color: rgb(var(--m-text)); } +.backup-btn:focus-visible { + outline: 2px solid rgb(var(--m-accent) / 0.7); + outline-offset: 2px; +} + .backup-btn:disabled { cursor: wait; opacity: 0.7; @@ -188,14 +272,12 @@ onMounted(loadScripts) border: 1px solid rgb(255 99 99 / 0.3); } -.status-title { - margin: 0; -} - -.status-output { - margin: 0.75rem 0 0; - white-space: pre-wrap; - word-break: break-word; +.status-empty { color: rgb(var(--m-muted)); } + +.status-title { + margin: 0; + line-height: 1.5; +} diff --git a/pages/backup.vue b/pages/backup.vue index 0f1fc3f..94cf418 100644 --- a/pages/backup.vue +++ b/pages/backup.vue @@ -23,6 +23,7 @@ @@ -45,6 +46,47 @@ :folder="selectedBackup" /> + +
+
+
+

Execution

+

Resultat du script

+
+ + {{ scriptResult.label || "Pret" }} + +
+ +
+

Aucune sortie disponible

+

+ Lancez un script depuis le panneau de gauche pour afficher son retour ici. +

+
+ +
{{ scriptResult.output }}
+
@@ -57,7 +99,55 @@ import BackupRun from "~/components/BackupRun.vue" definePageMeta({ layout: false }) +type ScriptResult = { + key: string | null + label: string + output: string + isError: boolean + downloadFolders: string[] +} + const selectedBackup = ref(null) +const scriptResult = ref({ + key: null as string | null, + label: "", + output: "", + isError: false, + downloadFolders: [] +}) + +const fetchLatestBackup = async (folder: string) => { + const files = await $fetch(`/api/backups?folder=${encodeURIComponent(folder)}`) + return files[0] || null +} + +const triggerDownload = (folder: string, file: string) => { + const link = document.createElement("a") + link.href = `/api/download?folder=${encodeURIComponent(folder)}&file=${encodeURIComponent(file)}` + link.style.display = "none" + document.body.appendChild(link) + link.click() + link.remove() +} + +const handleScriptResult = async (payload: ScriptResult) => { + scriptResult.value = payload + + if (payload.isError || payload.downloadFolders.length === 0) { + return + } + + for (const folder of payload.downloadFolders) { + try { + const latestFile = await fetchLatestBackup(folder) + if (latestFile) { + triggerDownload(folder, latestFile) + } + } catch (error) { + console.error(`Erreur telechargement automatique pour ${folder}:`, error) + } + } +} diff --git a/server/api/backup-script.get.ts b/server/api/backup-script.get.ts index 39898c1..53ba375 100644 --- a/server/api/backup-script.get.ts +++ b/server/api/backup-script.get.ts @@ -4,15 +4,17 @@ type BackupScript = { key: string label: string icon?: string + downloadFolders?: string[] command: string } export default defineEventHandler(() => { return { - scripts: (scripts as BackupScript[]).map(({ key, label, icon }) => ({ + scripts: (scripts as BackupScript[]).map(({ key, label, icon, downloadFolders }) => ({ key, label, - icon: icon || "mdi:play-circle-outline" + icon: icon || "mdi:play-circle-outline", + downloadFolders: downloadFolders || [] })) } }) diff --git a/server/api/backup-script.post.ts b/server/api/backup-script.post.ts index 59e1b6d..487d786 100644 --- a/server/api/backup-script.post.ts +++ b/server/api/backup-script.post.ts @@ -4,6 +4,7 @@ import scripts from "../config/backup-script.json" type BackupScript = { key: string label: string + downloadFolders?: string[] command: string } @@ -44,6 +45,7 @@ export default defineEventHandler(async (event) => { ok: true, key: script.key, label: script.label, + downloadFolders: script.downloadFolders || [], output: output.trim() } } catch (error) { diff --git a/server/config/backup-script.json b/server/config/backup-script.json index 0a2b248..00b6829 100644 --- a/server/config/backup-script.json +++ b/server/config/backup-script.json @@ -3,18 +3,20 @@ "key": "backup-bdd-recette", "label": "Backup BDD recette", "icon": "mdi:database-export", - "command": "ssh ferme 'cd /home/malio/Scripts-Serveur/RecetteScripts && bash backup-bdd-recette.sh && exit'" + "downloadFolders": ["ferme", "inventory", "sirh", "user"], + "command": "ssh ferme 'cd /home/malio/Malio-ops/RecetteScripts && bash backup-bdd-recette.sh && exit'" }, { "key": "check-statut-recette", "label": "Check statut recette", "icon": "mdi:server-network", - "command": "ssh ferme 'cd /home/malio/Scripts-Serveur/RecetteScripts && bash check-statut-recette.sh && exit'" + "command": "ssh ferme 'cd /home/malio/Malio-ops/RecetteScripts && bash check-statut-recette.sh && exit'" }, { "key": "backup-vaultwarden", "label": "Backup vaultwarden", "icon": "mdi:data", - "command": "ssh ferme 'cd /home/malio/Scripts-Serveur/BackupVaultWarden && bash backup-vaultwarden.sh && exit'" + "downloadFolders": ["bitwarden"], + "command": "ssh bitwarden 'cd /home/matt/vaultwarden/Malio-ops/BackupVaultWarden && bash backup-vaultwarden.sh && exit'" } ]