Files
clube67_newwhats.local/newwhats.clube67.com/newwhats.local/plugins/ext-api/index.ts
T
VPS 4 Deploy Agent 89758a0e6b feat(ext-api): gerenciador de satélites — auth por integration_key + gestão
Torna o registry de satélites (PluginPair) o gatekeeper do ext-api, em vez de
uma única EXT_MASTER_KEY global:
- satellites.ts: serviço + router /api/ext/v1/satellites (listar/provisionar/
  revogar/rotacionar), montado fora do authMiddleware com gate de chave mestra.
- apikey-auth: resolvePairKey aceita a chave do satélite (nwk_) no REST e no WS,
  resolvendo o tenant do par; checa revoke no banco a cada request (revoke
  instantâneo) + throttle de lastSeenAt.
- Chave mestra e ApiKey por-tenant seguem funcionando (não-quebra).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 03:29:43 +02:00

61 lines
3.0 KiB
TypeScript

/**
* Plugin: ext-api
* Entry point — registra rotas REST e WS bridge ao activar.
*/
import type { PluginInstance, PluginContext } from '../../backend/src/core/types'
import { buildApiKeyAuth } from './backend/apikey-auth'
import { buildExtRoutes } from './backend/routes'
import { buildWsBridge } from './backend/ws-bridge'
import { buildWebhookDispatcher } from './backend/webhook-dispatcher'
import { buildSatelliteRoutes } from './backend/satellites'
import { WhatsAppConnectionManager } from '../../backend/src/modules/whatsapp/connection/WhatsAppConnectionManager'
import manifest from './manifest.json'
// O WhatsAppConnectionManager é um singleton instanciado no server.ts.
// O plugin precisa de uma referência a ele para enviar mensagens.
// Injectamos via globalThis como bridge temporária (sem modificar o server.ts).
// Em revisão futura pode ser passado no PluginContext.
declare global {
// eslint-disable-next-line no-var
var __whatsAppManager: WhatsAppConnectionManager | undefined
}
const plugin: PluginInstance = {
manifest: manifest as any,
async activate(ctx: PluginContext): Promise<void> {
const { app, prisma, db, config, hooks, logger, httpServer, io } = ctx
// ── REST ────────────────────────────────────────────────────────────────
const manager = globalThis.__whatsAppManager
if (!manager) {
logger.warn('[ext-api] WhatsAppConnectionManager não disponível via globalThis.__whatsAppManager — endpoints de send desativados')
}
const authMiddleware = buildApiKeyAuth(prisma)
const extRouter = buildExtRoutes(prisma, manager as WhatsAppConnectionManager, db, config, hooks, io)
// Gerenciador de satélites — montado ANTES do authMiddleware (gate próprio
// de chave mestra, sem exigir x-nw-email). Superadmin gerencia os PluginPairs.
app.use('/api/ext/v1/satellites', buildSatelliteRoutes(prisma))
logger.info('[ext-api] Gerenciador de satélites em /api/ext/v1/satellites (chave mestra)')
app.use('/api/ext/v1', authMiddleware, extRouter)
logger.info('[ext-api] Rotas REST registradas em /api/ext/v1')
// ── WS Bridge ───────────────────────────────────────────────────────────
buildWsBridge(httpServer, prisma, hooks)
logger.info('[ext-api] WS bridge ativo em /api/ext/v1/stream (x-nw-key auth)')
// ── Webhook Dispatcher ──────────────────────────────────────────────────
buildWebhookDispatcher(prisma, hooks)
logger.info('[ext-api] Webhook dispatcher ativo')
},
async deactivate(ctx: PluginContext): Promise<void> {
ctx.logger.warn('[ext-api] Desativado. Reinicie o servidor para remover as rotas.')
},
}
export default plugin