Files

306 lines
13 KiB
JavaScript

#!/usr/bin/env node
// pg-convert.js — Converte queries MySQL/adapter → PostgreSQL nativo
// Executa: node pg-convert.js
import { readFileSync, writeFileSync } from 'fs';
let src = readFileSync('./server.js', 'utf8');
// ── 1. Substituir ? por $n seqencial em cada pool.query / client.query ─────────
function replacePlaceholders(sql) {
let n = 0;
return sql.replace(/\?/g, () => `$${++n}`);
}
// Aplica em strings simples: pool.query('...?...', [...])
src = src.replace(/(pool|client)\.query\(('(?:[^'\\]|\\.)*')\s*,/g, (m, obj, str) => {
const raw = str.slice(1, -1);
if (raw.includes('SET ?') || raw.includes('DUPLICATE') || raw.includes('DATE_FORMAT')) return m;
return `${obj}.query('${replacePlaceholders(raw)}',`;
});
// Aplica em template literals: pool.query(`...?...`, [...])
src = src.replace(/(pool|client)\.query\((`(?:[^`\\]|\\.)*`)\s*,/g, (m, obj, tpl) => {
const inner = tpl.slice(1, -1);
if (inner.includes('SET ?') || inner.includes('DUPLICATE') || inner.includes('DATE_FORMAT')) return m;
return `${obj}.query(\`${replacePlaceholders(inner)}\`,`;
});
// ── 2. Corrigir lida = 1 / lida = 0 para booleano PG ──────────────────────────
src = src.replace(/SET lida = 1/g, 'SET lida = TRUE');
src = src.replace(/WHERE lida = 0/g, 'WHERE lida = FALSE');
// ── 3. Corrigir erro MySQL → PG ────────────────────────────────────────────────
src = src.replace(/'ER_DUP_ENTRY'/g, "'23505'");
// ── 4. DATE_FORMAT / CURDATE / DATE_SUB → PG equivalentes ────────────────────
src = src.replace(
/DATE_FORMAT\s*\(\s*"?dataVencimento"?\s*,\s*'%Y-%m'\s*\)/g,
`TO_CHAR("dataVencimento", 'YYYY-MM')`
);
src = src.replace(
/dataVencimento\s*>=\s*DATE_SUB\(CURDATE\(\)\s*,\s*INTERVAL\s*6\s*MONTH\)/g,
`"dataVencimento" >= NOW() - INTERVAL '6 months'`
);
src = src.replace(/GROUP BY mes/g, 'GROUP BY TO_CHAR("dataVencimento", \'YYYY-MM\')');
// ── 5. Remover SET FOREIGN_KEY_CHECKS / SET NAMES (MySQL-only) ─────────────────
src = src.replace(/\s*await pool\.query\('SET FOREIGN_KEY_CHECKS = [01]'\);\r?\n/g, '');
src = src.replace(/\s*await pool\.query\('SET NAMES utf8mb4'\);\r?\n/g, '');
src = src.replace(/\s*await pool\.query\('SET CHARACTER SET utf8mb4'\);\r?\n/g, '');
// ── 6. INSERT INTO table SET ? → buildInsert ───────────────────────────────────
// pacientes
src = src.replace(
"await pool.query('INSERT INTO pacientes SET ?', p);",
"await pool.query(buildInsert('pacientes', p));"
);
// dentistas
src = src.replace(
"await pool.query('INSERT INTO dentistas SET ?', { ...d, ordem });",
"await pool.query(buildInsert('dentistas', { ...d, ordem }));"
);
// especialidades
src = src.replace(
"await pool.query('INSERT INTO especialidades SET ?', { ...req.body, ordem });",
"await pool.query(buildInsert('especialidades', { ...req.body, ordem }));"
);
// planos
src = src.replace(
"await pool.query('INSERT INTO planos SET ?', req.body);",
"await pool.query(buildInsert('planos', req.body));"
);
// financeiro
src = src.replace(
"await pool.query('INSERT INTO financeiro SET ?', req.body);",
"await pool.query(buildInsert('financeiro', req.body));"
);
// leads
src = src.replace(
"await pool.query('INSERT INTO leads SET ?', req.body);",
"await pool.query(buildInsert('leads', req.body));"
);
// gto_items
src = src.replace(
"await pool.query('INSERT INTO gto_items SET ?', item);",
"await pool.query(buildInsert('gto_items', item));"
);
// procedimentos (inside connection)
src = src.replace(
"await connection.query('INSERT INTO procedimentos SET ?', payload);",
"await client.query(buildInsert('procedimentos', payload));"
);
src = src.replace(
/await connection\.query\('INSERT INTO procedimentos_valores_planos SET \?',\s*\{[^}]+\}\);\s*/g,
(m) => m.replace('connection', 'client').replace("SET ?', {", "SET ?', {").replace(
"await client.query('INSERT INTO procedimentos_valores_planos SET ?',",
"await client.query(buildInsert('procedimentos_valores_planos',"
)
);
// ── 7. UPDATE table SET ? WHERE id = ? → buildUpdate ─────────────────────────
src = src.replace(
"await pool.query('UPDATE pacientes SET ? WHERE id = ?', [req.body, req.params.id]);",
"await pool.query(buildUpdate('pacientes', req.body, 'id', req.params.id));"
);
src = src.replace(
"await pool.query('UPDATE dentistas SET ? WHERE id = ?', [req.body, req.params.id]);",
"await pool.query(buildUpdate('dentistas', req.body, 'id', req.params.id));"
);
src = src.replace(
"await pool.query('UPDATE especialidades SET ? WHERE id = ?', [req.body, req.params.id]);",
"await pool.query(buildUpdate('especialidades', req.body, 'id', req.params.id));"
);
src = src.replace(
"await pool.query('UPDATE planos SET ? WHERE id = ?', [req.body, req.params.id]);",
"await pool.query(buildUpdate('planos', req.body, 'id', req.params.id));"
);
src = src.replace(
"await pool.query('UPDATE financeiro SET ? WHERE id = ?', [req.body, req.params.id]);",
"await pool.query(buildUpdate('financeiro', req.body, 'id', req.params.id));"
);
src = src.replace(
"await connection.query('UPDATE procedimentos SET ? WHERE id = ?', [updateData, req.params.id]);",
"await client.query(buildUpdate('procedimentos', updateData, 'id', req.params.id));"
);
// ── 8. Corrigir pool.getConnection() → withTransaction pattern ────────────────
// dentistas/reorder
src = src.replace(
`app.put('/api/dentistas/reorder', async (req, res) => {
const { orders } = req.body; // Array of {id, ordem}
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
for (const item of orders) {
await connection.query('UPDATE dentistas SET ordem = ? WHERE id = ?', [item.ordem, item.id]);
}
await connection.commit();
res.json({ success: true });
} catch (err) {
await connection.rollback();
res.status(500).json({ error: err.message });
} finally { connection.release(); }
});`,
`app.put('/api/dentistas/reorder', async (req, res) => {
const { orders } = req.body;
try {
await withTransaction(async (client) => {
for (const item of orders) {
await client.query('UPDATE dentistas SET ordem = $1 WHERE id = $2', [item.ordem, item.id]);
}
});
res.json({ success: true });
} catch (err) { res.status(500).json({ error: err.message }); }
});`
);
// especialidades/reorder
src = src.replace(
`app.put('/api/especialidades/reorder', async (req, res) => {
const { orders } = req.body; // Array of {id, ordem}
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
for (const item of orders) {
await connection.query('UPDATE especialidades SET ordem = ? WHERE id = ?', [item.ordem, item.id]);
}
await connection.commit();
res.json({ success: true });
} catch (err) {
await connection.rollback();
res.status(500).json({ error: err.message });
} finally { connection.release(); }
});`,
`app.put('/api/especialidades/reorder', async (req, res) => {
const { orders } = req.body;
try {
await withTransaction(async (client) => {
for (const item of orders) {
await client.query('UPDATE especialidades SET ordem = $1 WHERE id = $2', [item.ordem, item.id]);
}
});
res.json({ success: true });
} catch (err) { res.status(500).json({ error: err.message }); }
});`
);
// procedimentos/reorder
src = src.replace(
`app.put('/api/procedimentos/reorder', async (req, res) => {
const { orders } = req.body; // Array of {id, ordem}
const connection = await pool.getConnection();
try {
await connection.beginTransaction();
for (const item of orders) {
await connection.query('UPDATE procedimentos SET ordem = ? WHERE id = ?', [item.ordem, item.id]);
}
await connection.commit();
res.json({ success: true });
} catch (err) {
await connection.rollback();
res.status(500).json({ error: err.message });
} finally { connection.release(); }
});`,
`app.put('/api/procedimentos/reorder', async (req, res) => {
const { orders } = req.body;
try {
await withTransaction(async (client) => {
for (const item of orders) {
await client.query('UPDATE procedimentos SET ordem = $1 WHERE id = $2', [item.ordem, item.id]);
}
});
res.json({ success: true });
} catch (err) { res.status(500).json({ error: err.message }); }
});`
);
// DELETE dentista (transaction)
src = src.replace(
`app.delete('/api/dentistas/:id', async (req, res) => {
const connection = await pool.getConnection();
try {
const { id } = req.params;
await connection.beginTransaction();
// 1. Remove Google Tokens associated with this dentist
await connection.query('DELETE FROM google_tokens WHERE owner_id = ?', [id]);
// 2. Remove clinical links (if any)
await connection.query('DELETE FROM vinculos WHERE usuario_id = ?', [id]);
// 3. Set to NULL in agendamentos if it exists
await connection.query('UPDATE agendamentos SET dentistaId = NULL WHERE dentistaId = ?', [id]);
// 4. Delete from dentistas table
await connection.query('DELETE FROM dentistas WHERE id = ?', [id]);
await connection.commit();
res.json({ success: true });
} catch (err) {
await connection.rollback();
console.error('Error deleting dentist:', err);
res.status(500).json({ error: err.message });
} finally {
connection.release();
}
});`,
`app.delete('/api/dentistas/:id', async (req, res) => {
const { id } = req.params;
try {
await withTransaction(async (client) => {
await client.query('DELETE FROM google_tokens WHERE owner_id = $1', [id]);
await client.query('DELETE FROM vinculos WHERE usuario_id = $1', [id]);
await client.query('UPDATE agendamentos SET "dentistaId" = NULL WHERE "dentistaId" = $1', [id]);
await client.query('DELETE FROM dentistas WHERE id = $1', [id]);
});
res.json({ success: true });
} catch (err) {
console.error('Error deleting dentist:', err);
res.status(500).json({ error: err.message });
}
});`
);
// ── 9. Corrigir google/events pool.query sem placeholders ─────────────────────
src = src.replace(
"const [tokenRows] = await pool.query('SELECT * FROM google_tokens');",
"const { rows: tokenRows } = await pool.query('SELECT * FROM google_tokens');"
);
src = src.replace(
"const [dent] = await pool.query('SELECT nome FROM dentistas WHERE id = ?', [tokenData.owner_id]);",
"const { rows: dent } = await pool.query('SELECT nome FROM dentistas WHERE id = $1', [tokenData.owner_id]);"
);
src = src.replace(
"if (dent[0]) ownerName = dent[0].nome;",
"if (dent[0]) ownerName = dent[0].nome;"
);
src = src.replace(
"const [rows] = await pool.query('SELECT data FROM settings WHERE id = \"main\"');",
"const { rows } = await pool.query(\"SELECT data FROM settings WHERE id = 'main'\");"
);
src = src.replace(
"const [dName] = await pool.query('SELECT nome FROM dentistas WHERE id = ?', [dentistId]);",
"const { rows: dName } = await pool.query('SELECT nome FROM dentistas WHERE id = $1', [dentistId]);"
);
// ── 10. Corrigir destructuring [rows] → { rows } para pool.query simples ──────
// Algumas queries sem parâmetro ainda usam o padrão antigo [rows]
src = src.replace(
/const \[rows\] = await pool\.query\('SELECT \* FROM (\w+)'\);/g,
"const { rows } = await pool.query('SELECT * FROM $1');"
);
src = src.replace(
/const \[rows\] = await pool\.query\('SELECT \* FROM (\w+) ORDER BY ([^']+)'\);/g,
"const { rows } = await pool.query('SELECT * FROM $1 ORDER BY $2');"
);
// ── 11. Remover initTables (função morta MySQL DDL) ───────────────────────────
const initStart = src.indexOf('// Initialization: Create tables if not exist');
const initEnd = src.indexOf('\n// generateIntelligentNotifications');
if (initStart > 0 && initEnd > initStart) {
src = src.slice(0, initStart) + src.slice(initEnd + 1);
}
writeFileSync('./server.js', src, 'utf8');
console.log('[pg-convert] Done! server.js updated in place.');