feat: add cloud backup upload and universal restore script

This commit is contained in:
Erik
2025-11-29 12:44:47 -03:00
parent 1600cc8267
commit 932caf1b6c
4 changed files with 587 additions and 0 deletions

View File

@@ -0,0 +1,131 @@
import { NextRequest, NextResponse } from 'next/server';
import * as fs from 'fs';
import * as path from 'path';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
// Configuração MinIO/S3
const MINIO_ENDPOINT = process.env.MINIO_ENDPOINT || 'minio';
const MINIO_PORT = process.env.MINIO_PORT || '9000';
const MINIO_ACCESS_KEY = process.env.MINIO_ACCESS_KEY || 'admin';
const MINIO_SECRET_KEY = process.env.MINIO_SECRET_KEY || 'adminpassword';
const MINIO_USE_SSL = process.env.MINIO_USE_SSL === 'true';
const MINIO_BUCKET_NAME = process.env.MINIO_BUCKET_NAME || 'backups';
// Diretório de backups locais
const BACKUP_DIR = path.join(process.cwd(), '.backups');
// Inicializar cliente S3 (MinIO é compatível com S3)
const s3Client = new S3Client({
region: 'us-east-1',
endpoint: `http${MINIO_USE_SSL ? 's' : ''}://${MINIO_ENDPOINT}:${MINIO_PORT}`,
credentials: {
accessKeyId: MINIO_ACCESS_KEY,
secretAccessKey: MINIO_SECRET_KEY,
},
forcePathStyle: true,
});
/**
* POST /api/backup/upload
* Faz upload de um backup local para MinIO/S3
*/
export async function POST(request: NextRequest) {
try {
const { filename } = await request.json();
if (!filename) {
return NextResponse.json(
{ success: false, error: 'Filename é obrigatório' },
{ status: 400 }
);
}
const backupPath = path.join(BACKUP_DIR, filename);
// Validar se arquivo existe
if (!fs.existsSync(backupPath)) {
return NextResponse.json(
{ success: false, error: 'Arquivo de backup não encontrado' },
{ status: 404 }
);
}
// Ler arquivo
const fileContent = fs.readFileSync(backupPath);
const fileSize = fs.statSync(backupPath).size;
console.log(`[BACKUP UPLOAD] Iniciando upload de ${filename} (${fileSize} bytes)...`);
// Upload para MinIO
const command = new PutObjectCommand({
Bucket: MINIO_BUCKET_NAME,
Key: `backups/${filename}`,
Body: fileContent,
ContentType: 'application/gzip',
ContentLength: fileSize,
});
await s3Client.send(command);
console.log(`[BACKUP UPLOAD] Upload concluído: ${filename}`);
return NextResponse.json({
success: true,
message: 'Backup enviado para cloud com sucesso',
filename,
size: fileSize,
url: `${MINIO_USE_SSL ? 'https' : 'http'}://${MINIO_ENDPOINT}:${MINIO_PORT}/${MINIO_BUCKET_NAME}/backups/${filename}`,
});
} catch (error) {
console.error('[BACKUP UPLOAD] Erro:', error);
return NextResponse.json(
{
success: false,
error: (error as Error).message,
},
{ status: 500 }
);
}
}
/**
* GET /api/backup/upload/list
* Lista backups na cloud
*/
export async function GET(request: NextRequest) {
try {
const { ListObjectsV2Command } = await import('@aws-sdk/client-s3');
const command = new ListObjectsV2Command({
Bucket: MINIO_BUCKET_NAME,
Prefix: 'backups/',
});
const response = await s3Client.send(command);
const backups = (response.Contents || [])
.map((obj) => ({
key: obj.Key,
filename: obj.Key?.replace('backups/', '') || '',
size: obj.Size || 0,
lastModified: obj.LastModified?.toISOString() || '',
}))
.filter((b) => b.filename); // Remover pasta vazia
return NextResponse.json({
success: true,
backups,
count: backups.length,
});
} catch (error) {
console.error('[BACKUP LIST] Erro:', error);
return NextResponse.json(
{
success: false,
error: (error as Error).message,
backups: [],
count: 0,
},
{ status: 500 }
);
}
}