mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-14 13:15:07 +08:00
feat(webui): support backup file export and import
This commit is contained in:
@@ -6,7 +6,9 @@ export default function BackupSection({
|
||||
setImportMode,
|
||||
importing,
|
||||
onLoadExportData,
|
||||
onDownloadExportFile,
|
||||
onImport,
|
||||
onImportFileChange,
|
||||
importText,
|
||||
setImportText,
|
||||
exportData,
|
||||
@@ -23,6 +25,27 @@ export default function BackupSection({
|
||||
<Download className="w-4 h-4" />
|
||||
{t('settings.loadExport')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onDownloadExportFile}
|
||||
className="px-3 py-2 rounded-lg bg-secondary border border-border hover:bg-secondary/80 text-sm flex items-center gap-2"
|
||||
>
|
||||
<Download className="w-4 h-4" />
|
||||
{t('settings.downloadExport')}
|
||||
</button>
|
||||
<label className="px-3 py-2 rounded-lg bg-secondary border border-border hover:bg-secondary/80 text-sm flex items-center gap-2 cursor-pointer">
|
||||
<Upload className="w-4 h-4" />
|
||||
{t('settings.chooseImportFile')}
|
||||
<input
|
||||
type="file"
|
||||
accept=".json,application/json"
|
||||
className="hidden"
|
||||
onChange={(e) => {
|
||||
onImportFileChange(e.target.files?.[0] || null)
|
||||
e.target.value = ''
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
<select
|
||||
value={importMode}
|
||||
onChange={(e) => setImportMode(e.target.value)}
|
||||
|
||||
@@ -36,6 +36,8 @@ export default function SettingsContainer({ onRefresh, onMessage, authFetch, onF
|
||||
saveSettings,
|
||||
updatePassword,
|
||||
loadExportData,
|
||||
downloadExportFile,
|
||||
loadImportFile,
|
||||
doImport,
|
||||
} = useSettingsForm({
|
||||
apiFetch,
|
||||
@@ -102,7 +104,9 @@ export default function SettingsContainer({ onRefresh, onMessage, authFetch, onF
|
||||
setImportMode={setImportMode}
|
||||
importing={importing}
|
||||
onLoadExportData={loadExportData}
|
||||
onDownloadExportFile={downloadExportFile}
|
||||
onImport={doImport}
|
||||
onImportFileChange={loadImportFile}
|
||||
importText={importText}
|
||||
setImportText={setImportText}
|
||||
exportData={exportData}
|
||||
|
||||
@@ -222,15 +222,60 @@ export function useSettingsForm({ apiFetch, t, onMessage, onRefresh, onForceLogo
|
||||
const { res, data } = await getExportData(apiFetch)
|
||||
if (!res.ok) {
|
||||
onMessage('error', data.detail || t('settings.exportFailed'))
|
||||
return
|
||||
return null
|
||||
}
|
||||
setExportData(data)
|
||||
onMessage('success', t('settings.exportLoaded'))
|
||||
return data
|
||||
} catch (_e) {
|
||||
onMessage('error', t('settings.exportFailed'))
|
||||
return null
|
||||
}
|
||||
}, [apiFetch, onMessage, t])
|
||||
|
||||
const downloadExportFile = useCallback(async () => {
|
||||
let latest = exportData
|
||||
if (!latest?.json) {
|
||||
const loaded = await loadExportData()
|
||||
if (!loaded) {
|
||||
return
|
||||
}
|
||||
latest = loaded
|
||||
}
|
||||
const jsonText = String(latest?.json || '').trim()
|
||||
if (!jsonText) {
|
||||
onMessage('error', t('settings.exportFailed'))
|
||||
return
|
||||
}
|
||||
const blob = new Blob([jsonText], { type: 'application/json;charset=utf-8' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const now = new Date()
|
||||
const pad = (n) => String(n).padStart(2, '0')
|
||||
const filename = `ds2api-config-backup-${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}.json`
|
||||
const link = document.createElement('a')
|
||||
link.href = url
|
||||
link.download = filename
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
URL.revokeObjectURL(url)
|
||||
onMessage('success', t('settings.exportDownloaded'))
|
||||
}, [exportData, loadExportData, onMessage, t])
|
||||
|
||||
const loadImportFile = useCallback((file) => {
|
||||
if (!file) return
|
||||
const reader = new FileReader()
|
||||
reader.onload = () => {
|
||||
const text = String(reader.result || '')
|
||||
setImportText(text)
|
||||
onMessage('success', t('settings.importFileLoaded'))
|
||||
}
|
||||
reader.onerror = () => {
|
||||
onMessage('error', t('settings.importFileReadFailed'))
|
||||
}
|
||||
reader.readAsText(file, 'utf-8')
|
||||
}, [onMessage, t])
|
||||
|
||||
const doImport = useCallback(async () => {
|
||||
if (!String(importText || '').trim()) {
|
||||
onMessage('error', t('settings.importEmpty'))
|
||||
@@ -290,6 +335,8 @@ export function useSettingsForm({ apiFetch, t, onMessage, onRefresh, onForceLogo
|
||||
saveSettings,
|
||||
updatePassword,
|
||||
loadExportData,
|
||||
downloadExportFile,
|
||||
loadImportFile,
|
||||
doImport,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user