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 }); } }