Finalizando migração de dados do MongoDB Atlas para Supabase: corrigindo esquemas de campos decimais e garantindo integridade de relacionamento de projetos
This commit is contained in:
@@ -66,15 +66,15 @@ CREATE TABLE IF NOT EXISTS gpi.painting_schemes (
|
|||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
type TEXT,
|
type TEXT,
|
||||||
coat TEXT,
|
coat TEXT,
|
||||||
solids_volume DECIMAL(5,2),
|
solids_volume DECIMAL(12,3),
|
||||||
yield_theoretical DECIMAL(10,2),
|
yield_theoretical DECIMAL(12,3),
|
||||||
eps_min DECIMAL(5,2),
|
eps_min DECIMAL(12,3),
|
||||||
eps_max DECIMAL(5,2),
|
eps_max DECIMAL(12,3),
|
||||||
dilution DECIMAL(5,2),
|
dilution DECIMAL(12,3),
|
||||||
manufacturer TEXT,
|
manufacturer TEXT,
|
||||||
color TEXT,
|
color TEXT,
|
||||||
paint_consumption DECIMAL(10,3),
|
paint_consumption DECIMAL(12,3),
|
||||||
thinner_consumption DECIMAL(10,3),
|
thinner_consumption DECIMAL(12,3),
|
||||||
paint_id TEXT,
|
paint_id TEXT,
|
||||||
thinner_id TEXT,
|
thinner_id TEXT,
|
||||||
color_hex TEXT,
|
color_hex TEXT,
|
||||||
@@ -148,23 +148,23 @@ CREATE TABLE IF NOT EXISTS gpi.technical_data_sheets (
|
|||||||
type TEXT,
|
type TEXT,
|
||||||
file_url TEXT,
|
file_url TEXT,
|
||||||
upload_date DATE,
|
upload_date DATE,
|
||||||
solids_volume DECIMAL(5,2),
|
solids_volume DECIMAL(12,3),
|
||||||
density DECIMAL(6,3),
|
density DECIMAL(12,3),
|
||||||
mixing_ratio TEXT,
|
mixing_ratio TEXT,
|
||||||
yield_theoretical DECIMAL(10,2),
|
yield_theoretical DECIMAL(12,3),
|
||||||
wft_min DECIMAL(6,2),
|
wft_min DECIMAL(12,3),
|
||||||
wft_max DECIMAL(6,2),
|
wft_max DECIMAL(12,3),
|
||||||
dft_min DECIMAL(6,2),
|
dft_min DECIMAL(12,3),
|
||||||
dft_max DECIMAL(6,2),
|
dft_max DECIMAL(12,3),
|
||||||
reducer TEXT,
|
reducer TEXT,
|
||||||
mixing_ratio_weight TEXT,
|
mixing_ratio_weight TEXT,
|
||||||
mixing_ratio_volume TEXT,
|
mixing_ratio_volume TEXT,
|
||||||
dft_reference DECIMAL(6,2),
|
dft_reference DECIMAL(12,3),
|
||||||
yield_factor DECIMAL(5,3),
|
yield_factor DECIMAL(12,3),
|
||||||
dilution DECIMAL(5,2),
|
dilution DECIMAL(12,3),
|
||||||
notes TEXT,
|
notes TEXT,
|
||||||
manufacturer_code TEXT,
|
manufacturer_code TEXT,
|
||||||
min_stock DECIMAL(10,3),
|
min_stock DECIMAL(12,3),
|
||||||
typical_application TEXT,
|
typical_application TEXT,
|
||||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
|||||||
101
migrate.js
Normal file
101
migrate.js
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import { MongoClient } from 'mongodb';
|
||||||
|
import { execSync } from 'child_process';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
const MONGO_URI = "mongodb+srv://admtracksteel:mongodb26@cluster0.a4xiilu.mongodb.net/ts_gpi";
|
||||||
|
const DB_NAME = "ts_gpi";
|
||||||
|
const DEFAULT_ORG_ID = "e47e6210-4879-4e5b-bf21-9285d2713123";
|
||||||
|
|
||||||
|
const client = new MongoClient(MONGO_URI);
|
||||||
|
|
||||||
|
// Map Mongo _id -> Supabase UUID
|
||||||
|
const idMap = new Map();
|
||||||
|
|
||||||
|
function getUUID(mongoId) {
|
||||||
|
if (!mongoId) return null;
|
||||||
|
const mid = mongoId.toString();
|
||||||
|
if (idMap.has(mid)) return idMap.get(mid);
|
||||||
|
const newUuid = crypto.randomUUID();
|
||||||
|
idMap.set(mid, newUuid);
|
||||||
|
return newUuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sqlSafe(val) {
|
||||||
|
if (val === null || val === undefined) return 'NULL';
|
||||||
|
if (val instanceof Date) return `'${val.toISOString()}'`;
|
||||||
|
if (Array.isArray(val)) return `'{"${val.join('","')}"}'`;
|
||||||
|
if (typeof val === 'object' && val.toString) {
|
||||||
|
const s = val.toString();
|
||||||
|
return `'${s.replace(/'/g, "''")}'`;
|
||||||
|
}
|
||||||
|
if (typeof val === 'string') return `'${val.replace(/'/g, "''")}'`;
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
try {
|
||||||
|
await client.connect();
|
||||||
|
console.log("🚀 Conectado ao MongoDB Atlas...");
|
||||||
|
const db = client.db(DB_NAME);
|
||||||
|
|
||||||
|
// 1. Mapear Projetos primeiro
|
||||||
|
const mongoProjects = await db.collection('projects').find().toArray();
|
||||||
|
console.log(`📦 Encontrados ${mongoProjects.length} projetos.`);
|
||||||
|
|
||||||
|
let projectInserts = [];
|
||||||
|
for (const p of mongoProjects) {
|
||||||
|
const uuid = getUUID(p._id);
|
||||||
|
projectInserts.push(`INSERT INTO gpi.projects (id, organization_id, name, client, start_date, end_date, environment, technician, weight_kg, status, created_at, updated_at) VALUES (${sqlSafe(uuid)}, ${sqlSafe(DEFAULT_ORG_ID)}, ${sqlSafe(p.name)}, ${sqlSafe(p.client)}, ${sqlSafe(p.startDate)}, ${sqlSafe(p.endDate)}, ${sqlSafe(p.environment)}, ${sqlSafe(p.technician)}, ${sqlSafe(p.weightKg)}, 'active', ${sqlSafe(p.createdAt)}, ${sqlSafe(p.updatedAt)});`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Mapear Biblioteca de Tintas (Technical Data Sheets)
|
||||||
|
const mongoTDS = await db.collection('technicaldatasheets').find().toArray();
|
||||||
|
console.log(`📚 Encontradas ${mongoTDS.length} fichas técnicas.`);
|
||||||
|
let tdsInserts = [];
|
||||||
|
for (const t of mongoTDS) {
|
||||||
|
const uuid = getUUID(t._id);
|
||||||
|
tdsInserts.push(`INSERT INTO gpi.technical_data_sheets (id, organization_id, name, manufacturer, manufacturer_code, type, min_stock, typical_application, solids_volume, mixing_ratio, yield_theoretical, wft_min, wft_max, dft_min, dft_max, reducer, mixing_ratio_weight, mixing_ratio_volume, dft_reference, yield_factor, dilution, notes, created_at, updated_at) VALUES (${sqlSafe(uuid)}, ${sqlSafe(DEFAULT_ORG_ID)}, ${sqlSafe(t.name)}, ${sqlSafe(t.manufacturer)}, ${sqlSafe(t.manufacturerCode)}, ${sqlSafe(t.type)}, ${sqlSafe(t.minStock)}, ${sqlSafe(t.typicalApplication)}, ${sqlSafe(t.solidsVolume)}, ${sqlSafe(t.mixingRatio)}, ${sqlSafe(t.yieldTheoretical)}, ${sqlSafe(t.wftMin)}, ${sqlSafe(t.wftMax)}, ${sqlSafe(t.dftMin)}, ${sqlSafe(t.dftMax)}, ${sqlSafe(t.reducer)}, ${sqlSafe(t.mixingRatioWeight)}, ${sqlSafe(t.mixingRatioVolume)}, ${sqlSafe(t.dftReference)}, ${sqlSafe(t.yieldFactor)}, ${sqlSafe(t.dilution)}, ${sqlSafe(t.notes)}, ${sqlSafe(t.createdAt)}, ${sqlSafe(t.updatedAt)});`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Mapear Esquemas de Pintura
|
||||||
|
const mongoSchemes = await db.collection('paintingschemes').find().toArray();
|
||||||
|
console.log(`🎨 Encontrados ${mongoSchemes.length} esquemas de pintura.`);
|
||||||
|
let schemeInserts = [];
|
||||||
|
for (const s of mongoSchemes) {
|
||||||
|
const uuid = crypto.randomUUID();
|
||||||
|
const projectId = getUUID(s.projectId);
|
||||||
|
schemeInserts.push(`INSERT INTO gpi.painting_schemes (id, organization_id, project_id, name, type, coat, solids_volume, yield_theoretical, eps_min, eps_max, dilution, manufacturer, color, paint_consumption, thinner_consumption, paint_id, thinner_id, color_hex, thinner_symbol, notes, created_at, updated_at) VALUES (${sqlSafe(uuid)}, ${sqlSafe(DEFAULT_ORG_ID)}, ${sqlSafe(projectId)}, ${sqlSafe(s.name)}, ${sqlSafe(s.type)}, ${sqlSafe(s.coat)}, ${sqlSafe(s.solidsVolume)}, ${sqlSafe(s.yieldTheoretical)}, ${sqlSafe(s.epsMin)}, ${sqlSafe(s.epsMax)}, ${sqlSafe(s.dilution)}, ${sqlSafe(s.manufacturer)}, ${sqlSafe(s.color)}, ${sqlSafe(s.paintConsumption)}, ${sqlSafe(s.thinnerConsumption)}, ${sqlSafe(s.paintId)}, ${sqlSafe(s.thinnerId)}, ${sqlSafe(s.colorHex)}, ${sqlSafe(s.thinnerSymbol)}, ${sqlSafe(s.notes)}, NOW(), NOW());`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Mapear Peças (Parts)
|
||||||
|
const mongoParts = await db.collection('parts').find().toArray();
|
||||||
|
console.log(`🧩 Encontradas ${mongoParts.length} peças.`);
|
||||||
|
let partsInserts = [];
|
||||||
|
for (const pt of mongoParts) {
|
||||||
|
const uuid = getUUID(pt._id);
|
||||||
|
const projectId = getUUID(pt.projectId);
|
||||||
|
partsInserts.push(`INSERT INTO gpi.parts (id, organization_id, project_id, description, dimensions, weight, type, area, quantity, notes, created_at, updated_at) VALUES (${sqlSafe(uuid)}, ${sqlSafe(DEFAULT_ORG_ID)}, ${sqlSafe(projectId)}, ${sqlSafe(pt.description)}, ${sqlSafe(pt.dimensions)}, ${sqlSafe(pt.weight)}, ${sqlSafe(pt.type)}, ${sqlSafe(pt.area)}, ${sqlSafe(pt.quantity)}, ${sqlSafe(pt.notes)}, NOW(), NOW());`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Executar SQL
|
||||||
|
const allSQL = [...projectInserts, ...tdsInserts, ...schemeInserts, ...partsInserts].join('\n');
|
||||||
|
|
||||||
|
console.log("💾 Executando SQL no Supabase...");
|
||||||
|
|
||||||
|
// Escrever para arquivo temporário
|
||||||
|
const fs = await import('fs');
|
||||||
|
fs.writeFileSync('/tmp/bulk_migration.sql', allSQL);
|
||||||
|
|
||||||
|
// Executar via docker
|
||||||
|
execSync(`docker exec -i supabase-db-h0oggskgs0ws0sco8kc4s8ws psql -U supabase_admin -d postgres < /tmp/bulk_migration.sql`);
|
||||||
|
|
||||||
|
console.log("✅ Migração Concluída com Sucesso!");
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error("❌ ERRO DURANTE MIGRAÇÃO:", e.message);
|
||||||
|
} finally {
|
||||||
|
await client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run();
|
||||||
@@ -65,15 +65,15 @@ CREATE TABLE IF NOT EXISTS gpi.painting_schemes (
|
|||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
type TEXT,
|
type TEXT,
|
||||||
coat TEXT,
|
coat TEXT,
|
||||||
solids_volume DECIMAL(5,2),
|
solids_volume DECIMAL(12,3),
|
||||||
yield_theoretical DECIMAL(10,2),
|
yield_theoretical DECIMAL(12,3),
|
||||||
eps_min DECIMAL(5,2),
|
eps_min DECIMAL(12,3),
|
||||||
eps_max DECIMAL(5,2),
|
eps_max DECIMAL(12,3),
|
||||||
dilution DECIMAL(5,2),
|
dilution DECIMAL(12,3),
|
||||||
manufacturer TEXT,
|
manufacturer TEXT,
|
||||||
color TEXT,
|
color TEXT,
|
||||||
paint_consumption DECIMAL(10,3),
|
paint_consumption DECIMAL(12,3),
|
||||||
thinner_consumption DECIMAL(10,3),
|
thinner_consumption DECIMAL(12,3),
|
||||||
paint_id TEXT,
|
paint_id TEXT,
|
||||||
thinner_id TEXT,
|
thinner_id TEXT,
|
||||||
color_hex TEXT,
|
color_hex TEXT,
|
||||||
@@ -147,23 +147,23 @@ CREATE TABLE IF NOT EXISTS gpi.technical_data_sheets (
|
|||||||
type TEXT,
|
type TEXT,
|
||||||
file_url TEXT,
|
file_url TEXT,
|
||||||
upload_date DATE,
|
upload_date DATE,
|
||||||
solids_volume DECIMAL(5,2),
|
solids_volume DECIMAL(12,3),
|
||||||
density DECIMAL(6,3),
|
density DECIMAL(12,3),
|
||||||
mixing_ratio TEXT,
|
mixing_ratio TEXT,
|
||||||
yield_theoretical DECIMAL(10,2),
|
yield_theoretical DECIMAL(12,3),
|
||||||
wft_min DECIMAL(6,2),
|
wft_min DECIMAL(12,3),
|
||||||
wft_max DECIMAL(6,2),
|
wft_max DECIMAL(12,3),
|
||||||
dft_min DECIMAL(6,2),
|
dft_min DECIMAL(12,3),
|
||||||
dft_max DECIMAL(6,2),
|
dft_max DECIMAL(12,3),
|
||||||
reducer TEXT,
|
reducer TEXT,
|
||||||
mixing_ratio_weight TEXT,
|
mixing_ratio_weight TEXT,
|
||||||
mixing_ratio_volume TEXT,
|
mixing_ratio_volume TEXT,
|
||||||
dft_reference DECIMAL(6,2),
|
dft_reference DECIMAL(12,3),
|
||||||
yield_factor DECIMAL(5,3),
|
yield_factor DECIMAL(12,3),
|
||||||
dilution DECIMAL(5,2),
|
dilution DECIMAL(12,3),
|
||||||
notes TEXT,
|
notes TEXT,
|
||||||
manufacturer_code TEXT,
|
manufacturer_code TEXT,
|
||||||
min_stock DECIMAL(10,3),
|
min_stock DECIMAL(12,3),
|
||||||
typical_application TEXT,
|
typical_application TEXT,
|
||||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||||
|
|||||||
Reference in New Issue
Block a user