73 lines
3.0 KiB
TypeScript
73 lines
3.0 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import serverConfig from '@lifetracker/shared/config';
|
|
import { pgDump, pgRestore } from 'pg-dump-restore';
|
|
import { db, getConnectionDetails } from '@lifetracker/db/drizzle';
|
|
import { sql } from 'drizzle-orm';
|
|
|
|
// Define paths for uploaded files and the production DB
|
|
const UPLOAD_DIR = path.join(process.cwd(), 'uploads');
|
|
const DB_PATH = path.join(serverConfig.dataDir, `lifetracker-${new Date().getTime()}.sql`);
|
|
|
|
// Ensure upload directory exists
|
|
if (!fs.existsSync(UPLOAD_DIR)) {
|
|
fs.mkdirSync(UPLOAD_DIR, { recursive: true });
|
|
}
|
|
|
|
export async function POST(req: NextRequest) {
|
|
try {
|
|
const contentType = req.headers.get('content-type') || '';
|
|
if (!contentType.includes('multipart/form-data')) {
|
|
return NextResponse.json({ message: 'Invalid content type. Please upload a file.' }, { status: 400 });
|
|
}
|
|
|
|
// Create a writable stream for saving the uploaded file
|
|
const formBoundary = contentType.split('boundary=')[1];
|
|
const formData = req.body;
|
|
|
|
// Parse and save the uploaded file
|
|
const chunks: Buffer[] = [];
|
|
for await (const chunk of formData) {
|
|
chunks.push(chunk);
|
|
}
|
|
|
|
const bodyBuffer = Buffer.concat(chunks);
|
|
const start = bodyBuffer.indexOf(`\r\n\r\n`) + 4;
|
|
const end = bodyBuffer.indexOf(`\r\n--${formBoundary}`, start);
|
|
const fileContent = bodyBuffer.slice(start, end);
|
|
|
|
// Save the uploaded file to the upload directory
|
|
const uploadedFilePath = path.join(UPLOAD_DIR, 'uploaded.sql');
|
|
fs.writeFileSync(uploadedFilePath, fileContent);
|
|
|
|
// Back up the existing production database
|
|
const backupPath = `${serverConfig.dataDir}/pg-backup-${new Date().getTime()}.sql`;
|
|
const { stdout, stderr } = await pgDump(getConnectionDetails(), {
|
|
filePath: `${backupPath}`,
|
|
});
|
|
|
|
console.log("Backed up existing data to ", backupPath);
|
|
console.log(stdout);
|
|
|
|
// If it's a SQL file, restore the production database from the uploaded file
|
|
if (uploadedFilePath.endsWith('.sql')) {
|
|
const { stdout: restoreStdout, stderr: restoreStderr } = await pgRestore(getConnectionDetails(), {
|
|
clean: true,
|
|
filePath: uploadedFilePath,
|
|
});
|
|
}
|
|
// If it ends in .db, assume it's a sqlite
|
|
else if (uploadedFilePath.endsWith('.db')) {
|
|
// TODO, if ever
|
|
return NextResponse.json({ message: 'Invalid file type. Please upload a .sql file.' }, { status: 400 });
|
|
}
|
|
else { return NextResponse.json({ message: 'Invalid file type. Please upload a .sql file.' }, { status: 400 }); }
|
|
|
|
return NextResponse.json({ message: 'Database uploaded and replaced successfully!' }, { status: 200 });
|
|
} catch (error) {
|
|
console.error('Error handling the uploaded file:', error);
|
|
return NextResponse.json({ message: 'Error processing upload.' }, { status: 500 });
|
|
}
|
|
}
|