From 21bebd3256e1aa76c45b9b311281fc07c8c3bad8 Mon Sep 17 00:00:00 2001 From: VPS 4 Builder Date: Thu, 2 Jul 2026 23:26:31 +0200 Subject: [PATCH] =?UTF-8?q?feat(newwhats):=20ownership=20de=20sess=C3=B5es?= =?UTF-8?q?,=20auto-provisionamento,=20assinatura=20e=20drawer=20na=20agen?= =?UTF-8?q?da?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Drawer de WhatsApp focado (WhatsChatDrawer) aberto do slot do agendamento, com dropdown de números do paciente + grupo familiar e fluxo de iniciar conversa - Auto-provisionamento de conta no motor: eager no cadastro/convite + self-heal no proxy (404 "conta não encontrada" → provisiona e refaz o request) - Ownership por sessão (wa_session_authz): dono do workspace (owner_id ou vínculo donoclinica/donoconsultorio) tem poder total; delega re-scan, excluir sessão, apagar conversa/mensagem, acesso ao Inbox e à Secretária — por conta - Enforcement no proxy: guardSessionAction (scan/rescan/delete sessão), guardAreaAccess (inbox/secretaria), guardInboxDestructive (apagar conversa/msg) - Endpoints /api/nw/authz (dono gerencia) e /api/nw/access (acesso agregado) - Assinatura do operador nas mensagens (*Nome*, desambiguação de homônimos → *Rui C.*) - UI: painel "Gerenciar acesso" com 5 toggles; menu e botões de apagar condicionados - Proxy de mídia /api/nw/media + re-download sob demanda Co-Authored-By: Claude Opus 4.8 --- backend/newwhats/index.js | 43 +- backend/newwhats/proxy.js | 197 +++++++- backend/server.js | 169 ++++++- frontend/components/AgendaDetailModal.tsx | 10 +- frontend/components/Sidebar.tsx | 22 +- frontend/services/backend.ts | 55 +++ frontend/views/AgendaView.tsx | 12 + frontend/views/newwhats/InboxView.tsx | 11 + frontend/views/newwhats/SessionsView.tsx | 305 ++++++++++-- frontend/views/newwhats/WhatsChatDrawer.tsx | 449 ++++++++++++++++++ .../views/newwhats/components/ChatList.tsx | 3 + .../newwhats/components/ChatListItem.tsx | 8 +- .../views/newwhats/components/ChatSidebar.tsx | 3 + .../views/newwhats/components/MessageArea.tsx | 13 +- .../newwhats/components/MessageBubble.tsx | 4 +- .../views/newwhats/services/chatApiService.ts | 7 +- frontend/views/newwhats/utils/inboxUtils.ts | 11 +- 17 files changed, 1265 insertions(+), 57 deletions(-) create mode 100644 frontend/views/newwhats/WhatsChatDrawer.tsx diff --git a/backend/newwhats/index.js b/backend/newwhats/index.js index f8961bd..9eb197c 100644 --- a/backend/newwhats/index.js +++ b/backend/newwhats/index.js @@ -6,11 +6,23 @@ // registerNewwhats(app, pool, { superadminGuard }) import { createRoutes } from './routes.js' import { createWebhookReceiver } from './webhook-receiver.js' -import { createRestProxy } from './proxy.js' +import { createRestProxy, provisionNewwhatsAccount } from './proxy.js' import { getConfig, isConfigured, loadConfigFromDb, saveConfigToDb } from './config.js' +import { Readable } from 'node:stream' export { syncKnowledge, buildKnowledgeText } from './sync-knowledge.js' export { createWsTunnel } from './proxy.js' + +// Provisiona (eager) a conta do email no motor NewWhats, best-effort e idempotente. +// Chamado no cadastro de usuário/clínica do scoreodonto: "assim que a conta nasce +// aqui, já nasce lá — pronta esperando quando a pessoa for mexer com WhatsApp". +export async function provisionNewwhatsAccountEager(pool, email) { + try { + const cfg = getConfig() + if (!cfg.motorUrl || !cfg.integrationKey || !email) return false + return await provisionNewwhatsAccount(pool, cfg, email) + } catch { return false } +} import { createAgendaBridge } from './agenda-bridge.js' // Valida uma chave contra o motor. Envia um email-sonda (a chave mestra exige @@ -36,6 +48,33 @@ export function registerNewwhats(app, pool, opts = {}) { // Auth própria por segredo compartilhado (x-nw-agenda-secret). Server-to-server. app.use('/api/nw/agenda', createAgendaBridge(pool)); + // ── Proxy de MÍDIA: /api/nw/media/* → motor /media/* ─────────────────────── + // A mídia do WhatsApp vive no motor (servida do Wasabi/local, pública). O + // satélite a repassa mantendo same-origin no /