mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 01:35:39 -04:00
- Backup service using VACUUM INTO for WAL-safe atomic snapshots - Server-side storage in DATA_DIR/backups with download/upload API routes - Restore with integrity validation and automatic pre-restore safety backup - Scheduled daily backups via cron with configurable retention - Admin-only settings UI consolidated under single Server section - Clean up account section sign-out button, fix switch sub-pixel rendering Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
37 lines
1.1 KiB
TypeScript
37 lines
1.1 KiB
TypeScript
import { headers } from "next/headers";
|
|
import { NextResponse } from "next/server";
|
|
import { auth } from "@/lib/auth/server";
|
|
import { restoreFromBackup } from "@/lib/services/backup";
|
|
|
|
const MAX_SIZE = 500 * 1024 * 1024; // 500MB
|
|
|
|
export async function POST(req: Request) {
|
|
const session = await auth.api.getSession({ headers: await headers() });
|
|
if (!session) {
|
|
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
if (session.user.role !== "admin") {
|
|
return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
|
}
|
|
|
|
const formData = await req.formData();
|
|
const file = formData.get("file") as File | null;
|
|
if (!file) {
|
|
return NextResponse.json({ error: "No file provided" }, { status: 400 });
|
|
}
|
|
|
|
if (file.size > MAX_SIZE) {
|
|
return NextResponse.json({ error: "File too large" }, { status: 413 });
|
|
}
|
|
|
|
const buffer = Buffer.from(await file.arrayBuffer());
|
|
|
|
try {
|
|
restoreFromBackup(buffer);
|
|
return NextResponse.json({ success: true });
|
|
} catch (err) {
|
|
const message = err instanceof Error ? err.message : "Restore failed";
|
|
return NextResponse.json({ error: message }, { status: 400 });
|
|
}
|
|
}
|