备份与恢复
This commit is contained in:
1 parent
5d5fecde1b
commit
b4535d6aea
6 files changed
+252
-27
No files matched your search
@@ -11,8 +11,9 @@ import {
|
||||
getBackupStagingDir,
|
||||
isSafeBackupFilename
|
||||
} from '../../../utils/backup/paths'
|
||||
import { basename, join } from 'node:path'
|
||||
import { promises as fs } from 'node:fs'
|
||||
import { createWriteStream, promises as fs } from 'node:fs'
|
||||
import { basename, dirname, join } from 'node:path'
|
||||
import { pipeline } from 'node:stream/promises'
|
||||
|
||||
interface RestoreBody {
|
||||
archiveName?: string
|
||||
@@ -20,6 +21,20 @@ interface RestoreBody {
|
||||
dryRun?: boolean
|
||||
}
|
||||
|
||||
async function streamUploadToFile(event: H3Event, archivePath: string): Promise<void> {
|
||||
await fs.mkdir(dirname(archivePath), { recursive: true })
|
||||
try {
|
||||
await pipeline(event.node.req, createWriteStream(archivePath))
|
||||
const stat = await fs.stat(archivePath)
|
||||
if (stat.size === 0) {
|
||||
throw new Error('备份文件为空')
|
||||
}
|
||||
} catch (error) {
|
||||
await fs.rm(archivePath, { force: true }).catch(() => {})
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveRestoreInput(event: H3Event, userId: number): Promise<{
|
||||
archivePath: string
|
||||
body: RestoreBody
|
||||
@@ -27,6 +42,38 @@ async function resolveRestoreInput(event: H3Event, userId: number): Promise<{
|
||||
}> {
|
||||
const contentType = getHeader(event, 'content-type') ?? ''
|
||||
|
||||
if (contentType.includes('application/octet-stream')) {
|
||||
const dryRun = getHeader(event, 'x-pichost-dry-run') === 'true'
|
||||
const conflictMode = getHeader(event, 'x-pichost-conflict-mode') === 'skip'
|
||||
? 'skip'
|
||||
: 'overwrite'
|
||||
|
||||
if (dryRun) {
|
||||
const tempDir = getBackupStagingDir(`upload-preview-${Date.now()}`)
|
||||
await fs.mkdir(tempDir, { recursive: true })
|
||||
const archivePath = join(tempDir, 'package.phost.tar.gz')
|
||||
await streamUploadToFile(event, archivePath)
|
||||
return {
|
||||
archivePath,
|
||||
body: { conflictMode, dryRun: true },
|
||||
cleanup: async () => {
|
||||
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await fs.mkdir(getBackupsDir(), { recursive: true })
|
||||
const archivePath = join(
|
||||
getBackupsDir(),
|
||||
`upload-${Date.now()}${BACKUP_ARCHIVE_EXT}`
|
||||
)
|
||||
await streamUploadToFile(event, archivePath)
|
||||
return {
|
||||
archivePath,
|
||||
body: { conflictMode, dryRun: false }
|
||||
}
|
||||
}
|
||||
|
||||
if (contentType.includes('multipart/form-data')) {
|
||||
const form = await readMultipartFormData(event)
|
||||
const filePart = form?.find(part => part.name === 'file' && part.data?.length)
|
||||
|
||||
Reference in new issue
Block a user