b0439e39c4
Replace the JSON-based import/export with a proper backup system that copies the SQLite database file directly. Supports manual on-demand backups, periodic scheduled backups via node-cron, configurable retention, file download, and full database restore. - Add backupService with VACUUM INTO for safe DB copies - Add backupScheduler following healthcheckScheduler pattern - Add 6 admin API endpoints (create, list, download, restore, delete, schedule) - Add BackupPanel UI with backup table, confirmation dialogs, schedule config - Add backup fields to SystemSettings schema - Remove old ImportExportPanel, exportService, importService, and related code
34 lines
1.0 KiB
TypeScript
34 lines
1.0 KiB
TypeScript
import { json } from '@sveltejs/kit';
|
|
import type { RequestHandler } from './$types';
|
|
import { requireAdmin } from '$lib/server/middleware/authorize.js';
|
|
import { getBackupFilePath } from '$lib/server/services/backupService.js';
|
|
import { error } from '$lib/server/utils/response.js';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { Readable } from 'node:stream';
|
|
|
|
/**
|
|
* GET /api/admin/backups/:filename/download — Download a backup file (streamed).
|
|
*/
|
|
export const GET: RequestHandler = async (event) => {
|
|
requireAdmin(event);
|
|
const { filename } = event.params;
|
|
|
|
const filePath = getBackupFilePath(filename);
|
|
if (!filePath) {
|
|
return json(error('Backup not found'), { status: 404 });
|
|
}
|
|
|
|
const stats = fs.statSync(filePath);
|
|
const stream = fs.createReadStream(filePath);
|
|
|
|
return new Response(Readable.toWeb(stream) as ReadableStream, {
|
|
status: 200,
|
|
headers: {
|
|
'Content-Type': 'application/octet-stream',
|
|
'Content-Disposition': `attachment; filename="${path.basename(filePath)}"`,
|
|
'Content-Length': String(stats.size)
|
|
}
|
|
});
|
|
};
|