147 lines
6.5 KiB
TypeScript
147 lines
6.5 KiB
TypeScript
import * as fs from 'fs/promises';
|
|
import * as path from 'path';
|
|
import * as Minio from 'minio';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
// Ignora o erro de certificado SSL (Self-Signed) durante a migração local
|
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
// ============================================================================
|
|
// CONFIGURAÇÕES DO MINIO
|
|
// Substitua com as credenciais do seu Portainer/MinIO local
|
|
// ============================================================================
|
|
const MINIO_ENDPOINT = process.env.MINIO_ENDPOINT || 'storageedu.microtecinformaticacurso.com.br';
|
|
const MINIO_PORT = parseInt(process.env.MINIO_PORT || '443');
|
|
const MINIO_USE_SSL = process.env.MINIO_USE_SSL !== 'false';
|
|
const MINIO_ACCESS_KEY = process.env.MINIO_ACCESS_KEY || 'minioadmin';
|
|
const MINIO_SECRET_KEY = process.env.MINIO_SECRET_KEY || 'MiniO2026!Seguro';
|
|
const MINIO_PUBLIC_URL = process.env.MINIO_PUBLIC_URL || `https://${MINIO_ENDPOINT}`;
|
|
|
|
const minioClient = new Minio.Client({
|
|
endPoint: MINIO_ENDPOINT,
|
|
port: MINIO_PORT,
|
|
useSSL: MINIO_USE_SSL,
|
|
accessKey: MINIO_ACCESS_KEY,
|
|
secretKey: MINIO_SECRET_KEY,
|
|
});
|
|
|
|
// ============================================================================
|
|
// FUNÇÃO AUXILIAR DE UPLOAD
|
|
// ============================================================================
|
|
async function uploadBase64ToMinio(base64String: string, bucketName: string, fileNamePrefix: string): Promise<string> {
|
|
if (!base64String || !base64String.startsWith('data:image')) {
|
|
return base64String; // Retorna como está se não for Base64 válido
|
|
}
|
|
|
|
// Extrai o MIME Type e os dados puros da string (Ex: data:image/jpeg;base64,...)
|
|
const matches = base64String.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/);
|
|
if (!matches || matches.length !== 3) {
|
|
return base64String;
|
|
}
|
|
|
|
const mimeType = matches[1];
|
|
const base64Data = matches[2];
|
|
const extension = mimeType.split('/')[1] || 'jpeg'; // Pega a extensão (jpeg, png, webp)
|
|
const buffer = Buffer.from(base64Data, 'base64');
|
|
const fileName = `${fileNamePrefix}-${Date.now()}.${extension}`;
|
|
|
|
// Garante que o Bucket existe no MinIO
|
|
const bucketExists = await minioClient.bucketExists(bucketName);
|
|
if (!bucketExists) {
|
|
await minioClient.makeBucket(bucketName, 'us-east-1');
|
|
console.log(`🪣 Bucket criado: ${bucketName}`);
|
|
}
|
|
|
|
// Faz o upload do Buffer
|
|
await minioClient.putObject(bucketName, fileName, buffer, buffer.length, {
|
|
'Content-Type': mimeType,
|
|
});
|
|
|
|
// Retorna a URL pública gerada
|
|
return `${MINIO_PUBLIC_URL}/${bucketName}/${fileName}`;
|
|
}
|
|
|
|
// ============================================================================
|
|
// FUNÇÃO PRINCIPAL DE MIGRAÇÃO
|
|
// ============================================================================
|
|
async function runMigration() {
|
|
console.log('🚀 Iniciando extração e migração de Base64 para MinIO...\n');
|
|
|
|
const inputFilePath = path.join(__dirname, 'backup_supabase_2026-04-19.json');
|
|
const outputFilePath = path.join(__dirname, 'backup_supabase_2026-04-19_migrado.json');
|
|
|
|
try {
|
|
// 1. Ler arquivo JSON
|
|
console.log('📦 Lendo arquivo de backup...');
|
|
const rawData = await fs.readFile(inputFilePath, 'utf-8');
|
|
const db = JSON.parse(rawData);
|
|
|
|
// 2. Migrar Logo da Escola (Tabela 'profile')
|
|
if (db.profile && db.profile.logo && db.profile.logo.startsWith('data:image')) {
|
|
console.log('🏢 Processando Logo da Escola...');
|
|
db.profile.logo = await uploadBase64ToMinio(db.profile.logo, 'escola', 'logo');
|
|
}
|
|
|
|
// 3. Migrar Fotos dos Alunos (Tabela 'students')
|
|
if (db.students && Array.isArray(db.students)) {
|
|
console.log(`🎓 Processando fotos de ${db.students.length} alunos...`);
|
|
for (let i = 0; i < db.students.length; i++) {
|
|
const student = db.students[i];
|
|
if (student.photo && student.photo.startsWith('data:image')) {
|
|
student.photo = await uploadBase64ToMinio(student.photo, 'alunos', `aluno-${student.id}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 4. Migrar Fotos e Atestados da Frequência (Tabela 'attendance')
|
|
if (db.attendance && Array.isArray(db.attendance)) {
|
|
console.log(`📅 Processando ${db.attendance.length} registros de frequência...`);
|
|
for (let i = 0; i < db.attendance.length; i++) {
|
|
const record = db.attendance[i];
|
|
|
|
// 4.1 Foto de biometria/presença
|
|
if (record.photo && record.photo.startsWith('data:image')) {
|
|
record.photo = await uploadBase64ToMinio(record.photo, 'presenca', `presenca-${record.studentId}`);
|
|
}
|
|
|
|
// 4.2 Atestados médicos (Dentro do JSON stringificado em 'justification')
|
|
if (record.justification) {
|
|
try {
|
|
const justObj = JSON.parse(record.justification);
|
|
if (justObj.arquivo_base64 && justObj.arquivo_base64.startsWith('data:image')) {
|
|
// Upload da imagem do atestado
|
|
const newUrl = await uploadBase64ToMinio(
|
|
justObj.arquivo_base64,
|
|
'atestados',
|
|
`atestado-${record.studentId}`
|
|
);
|
|
|
|
// Substitui o Base64 pela URL limpa e recria a string JSON
|
|
justObj.arquivo_base64 = newUrl;
|
|
record.justification = JSON.stringify(justObj);
|
|
console.log(` ✅ Atestado migrado para aluno: ${record.studentId}`);
|
|
}
|
|
} catch (e) {
|
|
// Ignora se 'justification' não for um JSON válido
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 5. Salvar o novo banco de dados limpo
|
|
console.log('\n💾 Salvando novo backup JSON migrado...');
|
|
await fs.writeFile(outputFilePath, JSON.stringify(db, null, 2), 'utf-8');
|
|
|
|
console.log(`\n✅ MIGO CONCLUÍDA COM SUCESSO!`);
|
|
console.log(`✅ Arquivo gerado em: ${outputFilePath}`);
|
|
console.log(`⚠️ IMPORTANTE: Suas senhas e matrículas não foram alteradas.`);
|
|
|
|
} catch (error) {
|
|
console.error('❌ Ocorreu um erro durante a migração:', error);
|
|
}
|
|
}
|
|
|
|
runMigration(); |