Files
LTX-DeskWEB/frontend/components/WebProjectBackup.tsx
T

33 lines
2.2 KiB
TypeScript

import { useState } from 'react'
import { migrateProjectData } from '../types/project-model'
import { readProjectIds, writeProject, writeProjectIds, getProjectStorageKey } from '../lib/project-storage'
export function WebProjectBackup() {
const [error, setError] = useState('')
return <div className="flex gap-4 items-center flex-wrap">
<button className="underline" onClick={() => {
const data: Record<string, string> = { 'ltx-project-ids': JSON.stringify(readProjectIds()) }
for (const id of readProjectIds()) { const key = getProjectStorageKey(id), item = localStorage.getItem(key); if (item) data[key] = item }
void window.electronAPI.saveFile({ filePath: 'ltx-projects-backup.json', data: JSON.stringify(data, null, 2) })
}}>Back up projects (JSON)</button>
<label className="underline cursor-pointer">Restore project backup
<input type="file" accept=".json" className="hidden" onChange={async event => {
const file = event.target.files?.[0]; if (!file) return
setError('')
try {
if (file.size > 20 * 1024 * 1024) throw Error('Backup is too large (maximum 20 MiB)')
const data = JSON.parse(await file.text()) as Record<string, string>
const ids: unknown = JSON.parse(data['ltx-project-ids'])
if (!Array.isArray(ids) || ids.some(id => typeof id !== 'string' || !/^project-[a-zA-Z0-9-]+$/.test(id))) throw Error('Invalid project index')
const projects = ids.map(id => { const result = migrateProjectData(JSON.parse(data[getProjectStorageKey(id)])).project; if (result.id !== id) throw Error('Project ID mismatch'); return result })
if (!confirm(`Restore ${projects.length} project(s)? Projects with matching IDs in this browser will be replaced. Media files are not included in the backup.`)) return
for (const project of projects) writeProject(project.id, project)
writeProjectIds([...new Set([...ids as string[], ...readProjectIds()])]); location.reload()
} catch (e) { setError(e instanceof Error ? e.message : 'Cannot restore backup') }
event.target.value = ''
}} />
</label>
{error && <p role="alert" className="text-red-300">{error}</p>}
</div>
}