Mono-tenant: resolver id una vez al boot, eliminar lookups por turno
El sistema nunca fue realmente multi-tenant en la práctica. El esquema
DB conserva las columnas tenant_id (queda lista para escalar más adelante
sin migración), pero la app ahora resuelve el tenant una sola vez al
arranque y todas las capas leen de un único punto.
- src/modules/shared/tenant.js: nuevo módulo. setTenant() en boot,
getTenantId() lo lee desde cualquier lado.
- index.js: ensureTenant() → setTenant({ id, key }). Sin cambios externos.
- pipeline.resolveTenantId(): pasa de hacer 1-2 queries a DB por turno
a un return sincrónico del id cacheado. Mantiene firma async para no
romper callers.
- intake handlers (sim.js, evolution.js): usan getTenantId() directo,
sin parsing de tenant_key del chat_id ni lookup por canal.
- wooWebhooks: ya no requiere ?tenant_key=... en la query string.
El webhook va al único tenant configurado.
- repo.js: eliminados getTenantByKey() y getTenantIdByChannel() (no más
callers).
Plumbing del parámetro tenantId en signatures de handlers/repos/machine
queda intacto — bajar eso es ruido de alto riesgo y no aporta hoy.
188 tests pasando.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { refreshProductByWooId } from "../../shared/wooSnapshot.js";
|
||||
import { getTenantByKey } from "../db/repo.js";
|
||||
import { getTenantId } from "../../shared/tenant.js";
|
||||
import { insertAuditLog } from "../../0-ui/db/repo.js";
|
||||
|
||||
function unauthorized(res) {
|
||||
@@ -48,11 +48,8 @@ export function makeWooProductWebhook() {
|
||||
const { id, parentId, resource, action, changes } = parseWooPayload(req.body || {});
|
||||
if (!id) return res.status(400).json({ ok: false, error: "missing_id" });
|
||||
|
||||
// Determinar tenant por query ?tenant_key=...
|
||||
const tenantKey = req.query?.tenant_key || process.env.TENANT_KEY || null;
|
||||
if (!tenantKey) return res.status(400).json({ ok: false, error: "missing_tenant_key" });
|
||||
const tenant = await getTenantByKey(String(tenantKey).toLowerCase());
|
||||
if (!tenant?.id) return res.status(404).json({ ok: false, error: "tenant_not_found" });
|
||||
// Mono-tenant: el tenant es el único cargado al boot.
|
||||
const tenant = { id: getTenantId() };
|
||||
|
||||
const parentForVariation =
|
||||
resource && String(resource).includes("variation") ? parentId || null : null;
|
||||
|
||||
@@ -461,21 +461,6 @@ export async function deleteIdentityMapByChat({ tenant_id, wa_chat_id, provider
|
||||
return rowCount || 0;
|
||||
}
|
||||
|
||||
export async function getTenantByKey(key) {
|
||||
const { rows } = await pool.query(`select id, key, name from tenants where key=$1`, [key]);
|
||||
return rows[0] || null;
|
||||
}
|
||||
|
||||
export async function getTenantIdByChannel({ channel_type, channel_key }) {
|
||||
const q = `
|
||||
select tenant_id
|
||||
from tenant_channels
|
||||
where channel_type=$1 and channel_key=$2
|
||||
`;
|
||||
const { rows } = await pool.query(q, [channel_type, channel_key]);
|
||||
return rows[0]?.tenant_id || null;
|
||||
}
|
||||
|
||||
export async function getExternalCustomerIdByChat({ tenant_id, wa_chat_id, provider = "woo" }) {
|
||||
const q = `
|
||||
select external_customer_id
|
||||
|
||||
@@ -8,9 +8,8 @@ import {
|
||||
getExternalCustomerIdByChat,
|
||||
upsertExternalCustomerMap,
|
||||
updateRunLatency,
|
||||
getTenantByKey,
|
||||
getTenantIdByChannel,
|
||||
} from "../db/repo.js";
|
||||
import { getTenantId } from "../../shared/tenant.js";
|
||||
import { sseSend } from "../../shared/sse.js";
|
||||
import { createWooCustomer, getWooCustomerById } from "./woo.js";
|
||||
import { debug as dbg } from "../../shared/debug.js";
|
||||
@@ -469,28 +468,11 @@ const runStatus = llmMeta?.error ? "warn" : "ok";
|
||||
return { run_id, reply: plan.reply };
|
||||
}
|
||||
|
||||
function parseTenantFromChatId(chat_id) {
|
||||
const m = /^([a-z0-9_-]+):/.exec(chat_id);
|
||||
return m?.[1]?.toLowerCase() || null;
|
||||
}
|
||||
|
||||
export async function resolveTenantId({ chat_id, to_phone = null, tenant_key = null }) {
|
||||
const explicit = (tenant_key || parseTenantFromChatId(chat_id) || "").toLowerCase();
|
||||
|
||||
if (explicit) {
|
||||
const t = await getTenantByKey(explicit);
|
||||
if (t) return t.id;
|
||||
throw new Error(`tenant_not_found: ${explicit}`);
|
||||
}
|
||||
|
||||
if (to_phone) {
|
||||
const id = await getTenantIdByChannel({ channel_type: "whatsapp", channel_key: to_phone });
|
||||
if (id) return id;
|
||||
}
|
||||
|
||||
const fallbackKey = (process.env.TENANT_KEY || "piaf").toLowerCase();
|
||||
const t = await getTenantByKey(fallbackKey);
|
||||
if (t) return t.id;
|
||||
throw new Error(`tenant_not_found: ${fallbackKey}`);
|
||||
/**
|
||||
* Mono-tenant: devuelve el id resuelto al boot. No hace queries por turno.
|
||||
* Se mantiene como async para no romper callers existentes.
|
||||
*/
|
||||
export async function resolveTenantId() {
|
||||
return getTenantId();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user