备份与恢复

This commit is contained in:
Yeomai committed 2026-10-18 20:28:46 +08:00
1 parent 5d5fecde1b
commit b4535d6aea
6 files changed
+252 -27

No files matched your search

+35 -13
View File
@@ -1,6 +1,7 @@
import { createReadStream, promises as fs } from 'node:fs'
import { join, relative } from 'node:path'
import { pipeline } from 'node:stream/promises'
import { DatabaseSync } from 'node:sqlite'
import * as tar from 'tar'
import { getImageIndexRow } from '../image-index'
import { getActiveBackend } from '../storage/resolver'
@@ -48,32 +49,53 @@ async function readManifest(stagingRoot: string): Promise<BackupManifest> {
return manifest
}
interface RestoreImageRow {
key: string
thumb_key: string | null
hls_key: string | null
source_deleted: number
}
function readRestoreImageRows(stagingRoot: string): RestoreImageRow[] {
const db = new DatabaseSync(
join(stagingRoot, 'database', 'pichost.sqlite'),
{ readOnly: true }
)
try {
return db.prepare(`
SELECT key, thumb_key, hls_key, source_deleted
FROM images
`).all() as unknown as RestoreImageRow[]
} finally {
db.close()
}
}
export async function previewFullRestore(archivePath: string): Promise<BackupRestorePreview> {
const stagingRoot = getBackupStagingDir(`preview-${Date.now()}`)
try {
await extractArchive(archivePath, stagingRoot)
const manifest = await readManifest(stagingRoot)
const blobsRoot = join(stagingRoot, 'blobs')
let blobFiles: string[] = []
try {
blobFiles = await walkFiles(blobsRoot)
} catch {
blobFiles = []
}
const backend = await getActiveBackend()
const imageRows = readRestoreImageRows(stagingRoot)
let conflictingImages = 0
for (const file of blobFiles) {
const rel = relative(blobsRoot, file).replace(/\\/g, '/')
if (await backend.head(rel)) {
for (const row of imageRows) {
const keys = [
...(Number(row.source_deleted) === 1 ? [] : [row.key]),
...(row.thumb_key ? [row.thumb_key] : []),
...(row.hls_key ? [row.hls_key] : [])
]
for (const key of keys) {
if (!await backend.head(key)) continue
conflictingImages += 1
break
}
}
return {
imageCount: manifest.imageCount,
imageCount: imageRows.length,
imageBytes: manifest.imageBytes,
newImages: Math.max(0, blobFiles.length - conflictingImages),
newImages: imageRows.length - conflictingImages,
conflictingImages,
formatVersion: manifest.formatVersion,
appVersion: manifest.appVersion,