aorus
This commit is contained in:
@@ -47,4 +47,21 @@ export const settingsApi = {
|
||||
}),
|
||||
backupSamba: () =>
|
||||
client.post<{ ok: boolean; fichier: string; chemin: string }>('/api/settings/backup/samba').then(r => r.data),
|
||||
resizeAllImages: () =>
|
||||
client.post<{ ok: boolean; redimensionnees: number; ignorees: number; erreurs: number; message?: string }>(
|
||||
'/api/settings/images/resize-all'
|
||||
).then(r => r.data),
|
||||
restoreBackup: (file: File, overwrite: boolean) => {
|
||||
const form = new FormData()
|
||||
form.append('file', file)
|
||||
form.append('overwrite', String(overwrite))
|
||||
return client.post<{
|
||||
ok: boolean
|
||||
uploads_copies: number
|
||||
uploads_ignores: number
|
||||
db_restauree: boolean
|
||||
db_lignes_ajoutees: number
|
||||
erreurs: number
|
||||
}>('/api/settings/backup/restore', form).then(r => r.data)
|
||||
},
|
||||
}
|
||||
|
||||
@@ -138,6 +138,52 @@
|
||||
|
||||
<div v-if="backupMsg" class="text-[10px] text-center font-bold text-aqua animate-bounce">{{ backupMsg }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Restauration -->
|
||||
<div class="mt-6 pt-5 border-t border-bg-hard space-y-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-base">🔁</span>
|
||||
<span class="text-[10px] font-black uppercase tracking-widest text-text-muted">Restaurer une sauvegarde</span>
|
||||
</div>
|
||||
|
||||
<label class="flex items-center gap-3 cursor-pointer group">
|
||||
<div class="relative">
|
||||
<input v-model="restoreOverwrite" type="checkbox" class="sr-only peer" />
|
||||
<div class="w-8 h-4 bg-bg-hard rounded-full peer peer-checked:bg-red/70 transition-colors"></div>
|
||||
<div class="absolute left-1 top-1 w-2 h-2 bg-text-muted peer-checked:bg-bg peer-checked:translate-x-4 rounded-full transition-all"></div>
|
||||
</div>
|
||||
<span class="text-[10px] text-text-muted group-hover:text-text transition-colors leading-tight">
|
||||
Écraser les données existantes<br>
|
||||
<span class="text-[9px] italic">Si décoché : ajoute uniquement les nouveaux éléments</span>
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<div class="flex gap-2">
|
||||
<label class="flex-1 cursor-pointer">
|
||||
<div :class="[
|
||||
'py-2 px-3 rounded-xl border text-[10px] font-bold text-center truncate transition-all',
|
||||
restoreFile ? 'border-yellow/50 text-yellow bg-yellow/10' : 'border-bg-soft text-text-muted hover:border-yellow/30'
|
||||
]">
|
||||
{{ restoreFile ? restoreFile.name : 'Choisir un fichier .zip' }}
|
||||
</div>
|
||||
<input type="file" accept=".zip" class="hidden" @change="onRestoreFileSelected" />
|
||||
</label>
|
||||
<button
|
||||
:disabled="!restoreFile || restoringBackup"
|
||||
@click="confirmAndRestore"
|
||||
:class="[
|
||||
'btn-primary !py-2 !px-4 text-xs shrink-0 transition-all',
|
||||
restoreOverwrite ? '!bg-red !text-bg' : '!bg-yellow !text-bg'
|
||||
]"
|
||||
>
|
||||
{{ restoringBackup ? '⏳' : '♻️' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="restoreMsg" :class="['text-[10px] font-bold p-2 rounded-lg text-center', restoreError ? 'text-red bg-red/10' : 'text-green bg-green/10']">
|
||||
{{ restoreMsg }}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Section Images -->
|
||||
@@ -175,7 +221,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-8 pt-4 border-t border-bg-hard flex items-center justify-end gap-3">
|
||||
<div class="mt-6 space-y-3">
|
||||
<button
|
||||
class="btn-outline w-full py-3 flex items-center justify-center gap-2 border-orange/30 text-orange hover:bg-orange/10 text-xs"
|
||||
:disabled="resizingAll || imageMaxWidth === 0"
|
||||
@click="resizeAllImages"
|
||||
>
|
||||
<span>{{ resizingAll ? '⏳' : '🔄' }}</span>
|
||||
<span class="font-black uppercase tracking-widest text-[10px]">{{ resizingAll ? 'Traitement...' : 'Appliquer à la bibliothèque existante' }}</span>
|
||||
</button>
|
||||
<div v-if="resizeAllMsg" :class="['text-[10px] text-center font-bold p-2 rounded-lg', resizeAllError ? 'text-red bg-red/10' : 'text-green bg-green/10']">
|
||||
{{ resizeAllMsg }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 pt-4 border-t border-bg-hard flex items-center justify-end gap-3">
|
||||
<span v-if="imageSavedMsg" class="text-[10px] font-bold text-aqua">{{ imageSavedMsg }}</span>
|
||||
<button
|
||||
class="btn-primary !bg-orange !text-bg !py-2 !px-6 text-xs"
|
||||
@@ -313,6 +373,45 @@ const imageMaxWidthLabel = computed(() => {
|
||||
})
|
||||
const savingImage = ref(false)
|
||||
const imageSavedMsg = ref('')
|
||||
const resizingAll = ref(false)
|
||||
const resizeAllMsg = ref('')
|
||||
const resizeAllError = ref(false)
|
||||
|
||||
// --- Restauration ---
|
||||
const restoreFile = ref<File | null>(null)
|
||||
const restoreOverwrite = ref(true)
|
||||
const restoringBackup = ref(false)
|
||||
const restoreMsg = ref('')
|
||||
const restoreError = ref(false)
|
||||
|
||||
function onRestoreFileSelected(e: Event) {
|
||||
const input = e.target as HTMLInputElement
|
||||
restoreFile.value = input.files?.[0] ?? null
|
||||
}
|
||||
|
||||
async function confirmAndRestore() {
|
||||
if (!restoreFile.value) return
|
||||
const mode = restoreOverwrite.value ? 'ÉCRASER les données existantes' : 'ajouter uniquement les nouveaux éléments'
|
||||
if (!window.confirm(`Restaurer "${restoreFile.value.name}" ?\n\nMode : ${mode}.\n\nCette opération est irréversible.`)) return
|
||||
restoringBackup.value = true
|
||||
restoreMsg.value = ''
|
||||
restoreError.value = false
|
||||
try {
|
||||
const res = await settingsApi.restoreBackup(restoreFile.value, restoreOverwrite.value)
|
||||
const parts = [`${res.uploads_copies} fichier(s) restauré(s)`]
|
||||
if (res.uploads_ignores) parts.push(`${res.uploads_ignores} ignoré(s)`)
|
||||
if (res.db_restauree) parts.push(restoreOverwrite.value ? 'BDD remplacée' : `${res.db_lignes_ajoutees} ligne(s) BDD ajoutée(s)`)
|
||||
if (res.erreurs) parts.push(`${res.erreurs} erreur(s)`)
|
||||
restoreMsg.value = parts.join(' · ')
|
||||
restoreFile.value = null
|
||||
} catch (err: any) {
|
||||
restoreError.value = true
|
||||
restoreMsg.value = err?.response?.data?.detail || 'Erreur lors de la restauration.'
|
||||
} finally {
|
||||
restoringBackup.value = false
|
||||
setTimeout(() => { restoreMsg.value = '' }, 6000)
|
||||
}
|
||||
}
|
||||
|
||||
// --- Samba ---
|
||||
const samba = ref({ serveur: '', partage: '', sous_dossier: '', utilisateur: '', motdepasse: '' })
|
||||
@@ -421,6 +520,26 @@ async function loadSettings() {
|
||||
}
|
||||
}
|
||||
|
||||
async function resizeAllImages() {
|
||||
resizingAll.value = true
|
||||
resizeAllMsg.value = ''
|
||||
resizeAllError.value = false
|
||||
try {
|
||||
const res = await settingsApi.resizeAllImages()
|
||||
if (res.message) {
|
||||
resizeAllMsg.value = res.message
|
||||
} else {
|
||||
resizeAllMsg.value = `${res.redimensionnees} redimensionnée(s), ${res.ignorees} ignorée(s)${res.erreurs ? `, ${res.erreurs} erreur(s)` : ''}`
|
||||
}
|
||||
} catch (err: any) {
|
||||
resizeAllError.value = true
|
||||
resizeAllMsg.value = err?.response?.data?.detail || 'Erreur lors du redimensionnement.'
|
||||
} finally {
|
||||
resizingAll.value = false
|
||||
setTimeout(() => { resizeAllMsg.value = '' }, 5000)
|
||||
}
|
||||
}
|
||||
|
||||
async function saveImageSettings() {
|
||||
savingImage.value = true
|
||||
imageSavedMsg.value = ''
|
||||
|
||||
Reference in New Issue
Block a user