remove users, chats, etc

This commit is contained in:
Lucas Tettamanti
2026-01-25 21:04:54 -03:00
parent a489ec66a2
commit bb947ea75e
3 changed files with 244 additions and 6 deletions

View File

@@ -0,0 +1,244 @@
/**
* 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();
});

View File

@@ -100,11 +100,7 @@ export async function upsertSettings({ tenantId, settings }) {
pickup_hours_end || null,
];
console.log("[settingsRepo] upsertSettings params:", params);
const { rows } = await pool.query(sql, params);
console.log("[settingsRepo] upsertSettings result:", rows[0]);
return rows[0];
}

View File

@@ -44,8 +44,6 @@ export async function handleGetSettings({ tenantId }) {
* Guarda la configuración del tenant
*/
export async function handleSaveSettings({ tenantId, settings }) {
console.log("[settings] handleSaveSettings", { tenantId, settings });
// Validaciones básicas
if (!settings.store_name?.trim()) {
throw new Error("store_name is required");