diff --git a/frontend/src/app/api/admin/backup/full/route.ts b/frontend/src/app/api/admin/backup/full/route.ts index d934b72..e704997 100644 --- a/frontend/src/app/api/admin/backup/full/route.ts +++ b/frontend/src/app/api/admin/backup/full/route.ts @@ -1,9 +1,10 @@ import { NextRequest, NextResponse } from 'next/server'; import prisma from '@/lib/prisma'; -import { minioClient, bucketName, ensureBucketExists } from '@/lib/minio'; +import { minioClient, bucketName, ensureBucketExists, s3Client } from '@/lib/minio'; import { cookies } from 'next/headers'; import jwt from 'jsonwebtoken'; import JSZip from 'jszip'; +import { PutObjectCommand } from '@aws-sdk/client-s3'; async function authenticate() { const cookieStore = await cookies(); @@ -94,3 +95,105 @@ export async function GET() { return NextResponse.json({ error: 'Erro ao processar backup completo' }, { status: 500 }); } } + +// POST /api/admin/backup/full - Restaurar TUDO (Dados + Mídias) a partir de um ZIP +export async function POST(req: NextRequest) { + try { + // Verificar se é uma restauração de emergência (banco vazio) ou se está logado + const userCount = await prisma.user.count(); + const user = await authenticate(); + + // Se houver usuários e não estiver logado, bloqueia + if (userCount > 0 && !user) { + return NextResponse.json({ error: 'Não autorizado. Use a página de emergência apenas em instalações novas.' }, { status: 401 }); + } + + const formData = await req.formData(); + const file = formData.get('file') as File; + + if (!file) { + return NextResponse.json({ error: 'Arquivo não enviado' }, { status: 400 }); + } + + const buffer = Buffer.from(await file.arrayBuffer()); + const zip = await JSZip.loadAsync(buffer); + + // 1. Restaurar Dados do Banco (data.json) + const dataFile = zip.file('data.json'); + if (!dataFile) { + return NextResponse.json({ error: 'Arquivo data.json não encontrado no ZIP' }, { status: 400 }); + } + + const dataContent = await dataFile.async('string'); + const data = JSON.parse(dataContent); + + // Importação atômica + await prisma.$transaction(async (tx) => { + // Limpar dados atuais + await tx.message.deleteMany(); + await tx.project.deleteMany(); + await tx.service.deleteMany(); + await tx.pageContent.deleteMany(); + await tx.user.deleteMany(); + + // Restaurar Usuários + if (data.users && data.users.length > 0) { + await tx.user.createMany({ data: data.users }); + } + + // Restaurar Projetos + if (data.projects && data.projects.length > 0) { + await tx.project.createMany({ data: data.projects }); + } + + // Restaurar Serviços + if (data.services && data.services.length > 0) { + await tx.service.createMany({ data: data.services }); + } + + // Restaurar Conteúdo de Páginas + if (data.pageContents && data.pageContents.length > 0) { + await tx.pageContent.createMany({ data: data.pageContents }); + } + + // Restaurar Mensagens + if (data.messages && data.messages.length > 0) { + await tx.message.createMany({ data: data.messages }); + } + + // Restaurar Configurações + if (data.settings) { + await tx.settings.deleteMany(); + await tx.settings.create({ data: data.settings }); + } + }); + + // 2. Restaurar Mídias no S3/MinIO + await ensureBucketExists(); + const mediaFolder = zip.folder('media'); + if (mediaFolder) { + const files = Object.keys(mediaFolder.files); + for (const fileName of files) { + const mediaFile = mediaFolder.file(fileName); + if (mediaFile && !mediaFile.dir) { + const content = await mediaFile.async('nodebuffer'); + const pureFileName = fileName.replace('media/', ''); + + await s3Client.send(new PutObjectCommand({ + Bucket: bucketName, + Key: pureFileName, + Body: content + })); + } + } + } + + return NextResponse.json({ message: 'Restauração completa concluída com sucesso' }); + } catch (error) { + console.error('Erro na restauração completa:', error); + return NextResponse.json({ + error: 'Erro ao processar restauração', + details: error instanceof Error ? error.message : String(error) + }, { status: 500 }); + } +}