V2: guarda de envio real (SEND_ENABLED/FORCE_SEND), idempotencia por send_key e Slack nativo
- src/v2/sendGuard.js (novo): canSend() reduz SHADOW_MODE/DEV_MODE/ SEND_ENABLED/FORCE_SEND a uma formula unica testada exaustivamente (secao 15 do ciclo final: matriz completa de guarda de seguranca). computeMessageHash/buildSendKey/isDuplicate implementam a chave logica de idempotencia da secao 13 (report_type+periodo+destino+hash). - src/v2/config.js: SEND_ENABLED, FORCE_SEND, SLACK_DESTINATION. - src/v2/pipeline.js: buildReport agora calcula envio.pode_enviar/ motivo_guarda/duplicata_bloqueada/send_key a partir de triggerType (.mode) e sendLogRows (Data Table de log de envios). - tools/build_v2_workflow*.js: novo Data Table 'Buscar Log de Envios', troca do HTTP Slack com webhook placeholder por node Slack nativo (credencial real lNqwBBrtLfkRqw2X, reaproveitada do projeto irmao Coordenador GLPI), branches de sucesso/falha gravando o log de envio so apos confirmacao real do Slack. - test/sendGuard.test.js, test/workflowStructure.test.js (novos): cobrem a matriz de guarda de seguranca e travam regressao do bug de timeout (executeOnce=true nos 8 nodes de leitura em lote) e do bug de sendGuard.js nao concatenado no Code node gerado. - Corrigido bug real encontrado nesta sessao: sendGuard.js nao estava na lista de modulos concatenados pelo gerador, deixando canSend/ computeMessageHash/buildSendKey undefined em runtime. - Deployado e validado ao vivo no DEV (workflow 3x5kb58TeBCvirm6): n8n_validate_workflow -> valid:true, 0 erros, executeOnce preservado nos 9 nodes de leitura em lote. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
bdfa675d89
commit
64ddfb9b7b
@@ -0,0 +1,77 @@
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Guarda de envio real (Slack) e idempotencia — secoes 12, 13 e 15 da
|
||||
* missao original, usando o MESMO vocabulario (SEND_ENABLED/FORCE_SEND)
|
||||
* ja em uso no projeto irmao "[DEV] Coordenador GLPI - Chamados Abertos
|
||||
* v2 (Shadow)" (workflow y6JFEnp7Z2IJEUe2), para manter consistencia no
|
||||
* ambiente em vez de inventar um padrao novo.
|
||||
*
|
||||
* Regra de decisao (todas as condicoes descritas na missao reduzidas a uma
|
||||
* formula unica, testada exaustivamente em sendGuard.test.js):
|
||||
*
|
||||
* podeEnviar =
|
||||
* SHADOW_MODE === false (absoluto — nada sobrepoe)
|
||||
* && DEV_MODE === false (absoluto — nada sobrepoe; enquanto este
|
||||
* workflow viver em DEV, NUNCA envia)
|
||||
* && SEND_ENABLED === true (absoluto)
|
||||
* && (isScheduled || FORCE_SEND === true) (manual so envia com override explicito)
|
||||
*
|
||||
* FORCE_SEND so tem efeito na execucao MANUAL (destrava o "manual nao
|
||||
* envia por padrao"). Ele nunca sobrepoe SHADOW_MODE/DEV_MODE/SEND_ENABLED.
|
||||
*/
|
||||
|
||||
function canSend({ triggerType, config }) {
|
||||
const isScheduled = triggerType === 'scheduled';
|
||||
|
||||
if (config.SHADOW_MODE === true) {
|
||||
return { podeEnviar: false, motivo: 'SHADOW_MODE=true (absoluto)' };
|
||||
}
|
||||
if (config.DEV_MODE === true) {
|
||||
return { podeEnviar: false, motivo: 'DEV_MODE=true (absoluto, sem override enquanto viver em DEV)' };
|
||||
}
|
||||
if (config.SEND_ENABLED !== true) {
|
||||
return { podeEnviar: false, motivo: 'SEND_ENABLED=false' };
|
||||
}
|
||||
if (!isScheduled && config.FORCE_SEND !== true) {
|
||||
return { podeEnviar: false, motivo: 'execucao manual sem FORCE_SEND=true' };
|
||||
}
|
||||
|
||||
return { podeEnviar: true, motivo: isScheduled ? 'scheduled + guards ok' : 'manual + FORCE_SEND=true' };
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash estavel (nao criptografico — so precisa ser deterministico e
|
||||
* compacto) do texto da mensagem, para a chave de idempotencia detectar
|
||||
* se o CONTEUDO mudou entre duas tentativas do mesmo periodo.
|
||||
*/
|
||||
function computeMessageHash(texto) {
|
||||
const str = String(texto || '');
|
||||
let hash = 5381;
|
||||
for (let i = 0; i < str.length; i += 1) {
|
||||
hash = (hash * 33) ^ str.charCodeAt(i);
|
||||
}
|
||||
// >>> 0 converte para unsigned 32-bit antes de exibir em base36 (compacto)
|
||||
return (hash >>> 0).toString(36);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chave logica de idempotencia (secao 13): duas execucoes com a MESMA
|
||||
* chave representam o MESMO relatorio logico e nao devem ser enviadas
|
||||
* duas vezes, mesmo com retry/execucao manual.
|
||||
*/
|
||||
function buildSendKey({ reportType, periodStart, periodEnd, destination, messageHash }) {
|
||||
return [reportType, periodStart, periodEnd, destination, messageHash].join('|');
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifica se ja existe um envio com status=success para a mesma chave
|
||||
* logica (report_type + period + destination + message_hash).
|
||||
*/
|
||||
function isDuplicate(sendLogRows, key) {
|
||||
return (sendLogRows || []).some(
|
||||
(row) => row && row.send_key === key && row.status === 'success'
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = { canSend, computeMessageHash, buildSendKey, isDuplicate };
|
||||
Reference in New Issue
Block a user