245 lines
7.8 KiB
JavaScript
245 lines
7.8 KiB
JavaScript
/**
|
|
* Script para resetear datos de conversación de un tenant
|
|
*
|
|
* Uso:
|
|
* node scripts/reset-tenant-data.mjs --tenant-key piaf
|
|
* node scripts/reset-tenant-data.mjs --tenant-key piaf --keep-audit
|
|
* node scripts/reset-tenant-data.mjs --tenant-key piaf --yes # Sin confirmación
|
|
*
|
|
* Tablas que se limpian (en orden):
|
|
* 1. human_takeovers
|
|
* 2. wa_messages
|
|
* 3. conversation_runs
|
|
* 4. wa_conversation_state
|
|
* 5. wa_identity_map
|
|
* 6. audit_log (opcional, se borra por defecto)
|
|
*/
|
|
|
|
import "dotenv/config";
|
|
import readline from "readline";
|
|
import { pool } from "../src/modules/shared/db/pool.js";
|
|
|
|
// ─────────────────────────────────────────────────────────────
|
|
// Args parsing
|
|
// ─────────────────────────────────────────────────────────────
|
|
|
|
function parseArgs() {
|
|
const args = process.argv.slice(2);
|
|
const out = {
|
|
tenantKey: null,
|
|
keepAudit: false,
|
|
yes: false,
|
|
};
|
|
|
|
for (let i = 0; i < args.length; i++) {
|
|
const a = args[i];
|
|
if (a === "--tenant-key") out.tenantKey = args[++i];
|
|
else if (a === "--keep-audit") out.keepAudit = true;
|
|
else if (a === "--yes" || a === "-y") out.yes = true;
|
|
else if (a === "--help" || a === "-h") {
|
|
printHelp();
|
|
process.exit(0);
|
|
}
|
|
}
|
|
|
|
if (!out.tenantKey) {
|
|
console.error("Error: --tenant-key es requerido\n");
|
|
printHelp();
|
|
process.exit(1);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
function printHelp() {
|
|
console.log(`
|
|
Uso: node scripts/reset-tenant-data.mjs [opciones]
|
|
|
|
Opciones:
|
|
--tenant-key <key> Clave del tenant (requerido)
|
|
--keep-audit No borrar audit_log
|
|
--yes, -y Confirmar automáticamente (sin prompt)
|
|
--help, -h Mostrar esta ayuda
|
|
|
|
Ejemplos:
|
|
node scripts/reset-tenant-data.mjs --tenant-key piaf
|
|
node scripts/reset-tenant-data.mjs --tenant-key piaf --keep-audit
|
|
node scripts/reset-tenant-data.mjs --tenant-key piaf --yes
|
|
`);
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────
|
|
// Confirmación interactiva
|
|
// ─────────────────────────────────────────────────────────────
|
|
|
|
async function askConfirmation(message) {
|
|
const rl = readline.createInterface({
|
|
input: process.stdin,
|
|
output: process.stdout,
|
|
});
|
|
|
|
return new Promise((resolve) => {
|
|
rl.question(message, (answer) => {
|
|
rl.close();
|
|
resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes" || answer.toLowerCase() === "si");
|
|
});
|
|
});
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────
|
|
// Reset de datos
|
|
// ─────────────────────────────────────────────────────────────
|
|
|
|
async function getTenantByKey(tenantKey) {
|
|
const { rows } = await pool.query(
|
|
`SELECT id, key, name FROM tenants WHERE key = $1`,
|
|
[tenantKey]
|
|
);
|
|
return rows[0] || null;
|
|
}
|
|
|
|
async function getStats(tenantId) {
|
|
const queries = [
|
|
{ name: "human_takeovers", sql: `SELECT COUNT(*) as count FROM human_takeovers WHERE tenant_id = $1` },
|
|
{ name: "wa_messages", sql: `SELECT COUNT(*) as count FROM wa_messages WHERE tenant_id = $1` },
|
|
{ name: "conversation_runs", sql: `SELECT COUNT(*) as count FROM conversation_runs WHERE tenant_id = $1` },
|
|
{ name: "wa_conversation_state", sql: `SELECT COUNT(*) as count FROM wa_conversation_state WHERE tenant_id = $1` },
|
|
{ name: "wa_identity_map", sql: `SELECT COUNT(*) as count FROM wa_identity_map WHERE tenant_id = $1` },
|
|
{ name: "audit_log", sql: `SELECT COUNT(*) as count FROM audit_log WHERE tenant_id = $1` },
|
|
];
|
|
|
|
const stats = {};
|
|
for (const q of queries) {
|
|
const { rows } = await pool.query(q.sql, [tenantId]);
|
|
stats[q.name] = parseInt(rows[0].count, 10);
|
|
}
|
|
return stats;
|
|
}
|
|
|
|
async function resetTenantData({ tenantId, keepAudit = false }) {
|
|
const client = await pool.connect();
|
|
|
|
try {
|
|
await client.query("BEGIN");
|
|
|
|
// 1. human_takeovers
|
|
const r1 = await client.query(
|
|
`DELETE FROM human_takeovers WHERE tenant_id = $1`,
|
|
[tenantId]
|
|
);
|
|
console.log(` - human_takeovers: ${r1.rowCount} registros eliminados`);
|
|
|
|
// 2. wa_messages
|
|
const r2 = await client.query(
|
|
`DELETE FROM wa_messages WHERE tenant_id = $1`,
|
|
[tenantId]
|
|
);
|
|
console.log(` - wa_messages: ${r2.rowCount} registros eliminados`);
|
|
|
|
// 3. conversation_runs
|
|
const r3 = await client.query(
|
|
`DELETE FROM conversation_runs WHERE tenant_id = $1`,
|
|
[tenantId]
|
|
);
|
|
console.log(` - conversation_runs: ${r3.rowCount} registros eliminados`);
|
|
|
|
// 4. wa_conversation_state
|
|
const r4 = await client.query(
|
|
`DELETE FROM wa_conversation_state WHERE tenant_id = $1`,
|
|
[tenantId]
|
|
);
|
|
console.log(` - wa_conversation_state: ${r4.rowCount} registros eliminados`);
|
|
|
|
// 5. wa_identity_map
|
|
const r5 = await client.query(
|
|
`DELETE FROM wa_identity_map WHERE tenant_id = $1`,
|
|
[tenantId]
|
|
);
|
|
console.log(` - wa_identity_map: ${r5.rowCount} registros eliminados`);
|
|
|
|
// 6. audit_log (opcional)
|
|
if (!keepAudit) {
|
|
const r6 = await client.query(
|
|
`DELETE FROM audit_log WHERE tenant_id = $1`,
|
|
[tenantId]
|
|
);
|
|
console.log(` - audit_log: ${r6.rowCount} registros eliminados`);
|
|
} else {
|
|
console.log(` - audit_log: conservado (--keep-audit)`);
|
|
}
|
|
|
|
await client.query("COMMIT");
|
|
|
|
return { ok: true };
|
|
} catch (err) {
|
|
await client.query("ROLLBACK");
|
|
throw err;
|
|
} finally {
|
|
client.release();
|
|
}
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────
|
|
// Main
|
|
// ─────────────────────────────────────────────────────────────
|
|
|
|
async function main() {
|
|
const args = parseArgs();
|
|
|
|
console.log("\n🔄 Reset de datos de tenant\n");
|
|
|
|
// Buscar tenant
|
|
const tenant = await getTenantByKey(args.tenantKey);
|
|
if (!tenant) {
|
|
console.error(`Error: No se encontró tenant con key "${args.tenantKey}"`);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(`Tenant: ${tenant.name} (${tenant.key})`);
|
|
console.log(`ID: ${tenant.id}\n`);
|
|
|
|
// Mostrar estadísticas
|
|
console.log("Registros actuales:");
|
|
const stats = await getStats(tenant.id);
|
|
for (const [table, count] of Object.entries(stats)) {
|
|
console.log(` - ${table}: ${count}`);
|
|
}
|
|
console.log("");
|
|
|
|
const total = Object.values(stats).reduce((a, b) => a + b, 0);
|
|
if (total === 0) {
|
|
console.log("No hay datos para eliminar.");
|
|
process.exit(0);
|
|
}
|
|
|
|
// Confirmación
|
|
if (!args.yes) {
|
|
const confirm = await askConfirmation(
|
|
`⚠️ Se eliminarán ${total} registros. ¿Continuar? (y/N): `
|
|
);
|
|
if (!confirm) {
|
|
console.log("Operación cancelada.");
|
|
process.exit(0);
|
|
}
|
|
}
|
|
|
|
console.log("\nEliminando datos...\n");
|
|
|
|
// Ejecutar reset
|
|
await resetTenantData({
|
|
tenantId: tenant.id,
|
|
keepAudit: args.keepAudit
|
|
});
|
|
|
|
console.log("\n✅ Reset completado exitosamente.\n");
|
|
}
|
|
|
|
main()
|
|
.catch((err) => {
|
|
console.error("\n❌ Error:", err.message);
|
|
process.exit(1);
|
|
})
|
|
.finally(() => {
|
|
pool.end();
|
|
});
|