API em Node.js/Express para receber webhooks de diferentes serviços, aplicar filtros de segurança e encaminhar o payload para outro destino (por exemplo, n8n).
telegram, alexa, queryParams).webhooks.yml./api/:serviceName (ou variações compatíveis) no método permitido (POST, GET, PUT, PATCH, DELETE, HEAD, OPTIONS).config/webhooks.yml.200 com mensagem de filtrado.destination configurado usando o mesmo método recebido.POST /api/:serviceNamePOST /api/:serviceName/*subPath (somente se subdomain permitir o caminho)POST /api/:serviceName/webhook/:id/webhookPOST /api/:serviceName/webhook-test/:id/webhookGET /api/:serviceName (somente se methods incluir GET)GET /api/:serviceName/*subPath (somente se methods incluir GET e subdomain permitir)GET /api/:serviceName/webhook/:id/webhook (somente se methods incluir GET)GET /api/:serviceName/webhook-test/:id/webhook (somente se methods incluir GET)PUT/PATCH/DELETE/HEAD/OPTIONS seguem a mesma regra: precisam estar em methods.POST / -> login (form password)GET /dashboard -> dashboard (requer autenticação)GET /api/webhooks -> lista de serviços configurados (requer autenticação)GET /api/logs/:serviceName?limit=10 -> logs por serviço (requer autenticação)No diretório code, copie o arquivo de exemplo:
cp .env.exemple .env
Principais variáveis:
PORT: porta da API (padrão 5124).JSON_BODY_LIMIT: limite do body JSON (padrão 1mb).URLENCODED_BODY_LIMIT: limite para form-urlencoded (padrão 100kb).WEBHOOK_RATE_LIMIT_MAX: default de requisições por IP na janela (padrão 100).WEBHOOK_RATE_LIMIT_WINDOW_MS: default da janela do rate limit em ms (padrão 900000 = 15 min).TRUST_PROXY: nº de proxies confiáveis para derivar o IP do cliente (padrão 1, atrás do app-proxy do Umbrel). Exposição direta, sem proxy: use false. Aceita número, true/false ou lista de subnets/preset. Afeta a chave do rate limit — valor errado permite forjar X-Forwarded-For.LOG_PAYLOADS: false guarda apenas metadados nos logs, sem o payload (padrão true, com redação de segredos).LOGIN_PASSWORD_HASH: hash bcrypt da senha de login.DEBUG: true para logs detalhados.COOKIE_SECURE (recomendado true em produção)COOKIE_SAME_SITE (lax, strict ou none)WEBHOOK_CONFIG_PATH (opcional, caminho absoluto para webhooks.yml; padrão: config/webhooks.yml)helmet):
ENABLE_HELMETENABLE_CSPENABLE_HSTSENABLE_NO_SNIFFENABLE_FRAMEGUARDENABLE_XSS_FILTERN8N_WEBHOOK_URLTELEGRAM_WEBHOOK_URLTELEGRAM_AUTHORIZED_CHAT_IDSTELEGRAM_AUTHORIZED_USERNAMESALEXA_APPLICATION_IDALEXA_WEBHOOK_URL (necessário se usar o serviço alexa em webhooks.yml)Edite config/webhooks.yml para mapear cada serviço:
telegram:
destination: ${TELEGRAM_WEBHOOK_URL}
filter:
telegram:
chatIds: ${TELEGRAM_AUTHORIZED_CHAT_IDS}
usernames: ${TELEGRAM_AUTHORIZED_USERNAMES}
alexa:
destination: ${ALEXA_WEBHOOK_URL}
filter:
alexa:
applicationId: ${ALEXA_APPLICATION_ID}
webhook:
destination: ${N8N_WEBHOOK_URL}
methods:
- POST
filter:
queryParams:
u:
- 1234
response:
default:
forward:
- STATUS
- BODY
- HEADERS
allowedHeaders:
- content-type
methods:
GET:
forward:
STATUS: "*"
BODY: false
HEADERS:
- content-type
subdomain:
- path: rest/ping.view
# sem `filter` => sem filtragem nesta rota
- path: rest/*
filter:
telegram:
chatIds: ${TELEGRAM_AUTHORIZED_CHAT_IDS}
signatureSecret: ${WEBHOOK_SIGNATURE_SECRET}
signatureHeader: x-webhook-signature
signaturePrefix: sha256=
hmacAlgorithm: sha256
Assinatura HMAC (opcional por serviço):
signatureSecret for definido, a requisição precisa conter a assinatura no header configurado.HMAC(rawBody) usando o algoritmo configurado (sha256 por padrão).signaturePrefix estiver definido (ex: sha256=), ele é removido antes da validação.GET não têm corpo para assinar.Token de acesso para GET (opcional por serviço):
GET não tem corpo para assinar via HMAC, use getTokenSecret para exigir um token em header nas requisições GET.getTokenSecret não for definido, o GET segue sem exigência (comportamento padrão).GET precisa enviar o header (default x-webhook-token, customizável via getTokenHeader) com o valor exato; caso contrário retorna 401. A comparação é feita em tempo constante.getTokenSecret: ${SUBSTREAM_GET_TOKEN}) para não versioná-lo.Filtros (filter é um objeto cujas chaves são os tipos de filtro):
filter, todos precisam passar (AND).filter = sem filtragem (aceita tudo).telegram: aceita se chat.id OU from.username estiver em uma allowlist. Config: chatIds e/ou usernames (lista ou CSV). Sem nenhuma allowlist definida, nega (fail-closed).
filter:
telegram:
chatIds: [123456789]
usernames: [joaosilva]
alexa: aceita somente quando applicationId confere. Config: applicationId.
filter:
alexa:
applicationId: ${ALEXA_APPLICATION_ID}
queryParams: aceita conforme a query string. Cada chave é um parâmetro (nome livre) com a lista de valores permitidos. Precisa bater em todos os parâmetros; em cada um, qualquer valor da lista serve. Sem parâmetros definidos, nega (fail-closed).
filter:
queryParams:
u:
- 1234
- 5678
Opção por serviço:
methods: lista de métodos permitidos no serviço (ex.: POST, GET, PATCH, DELETE).
POST.methods: [POST, GET].subdomain: lista opcional de subcaminhos aceitos para o mesmo serviço (* como curinga).
- rest/ping.view- path: rest/ping.view + um bloco filter: (ex.: filter: { queryParams: { u: [1234] } })rest/ping.view aceita somente esse caminho.rest/* aceita qualquer rota abaixo de rest/.destination no encaminhamento.filter global do serviço para aquela rota.response: define como a resposta do destino deve ser repassada.
passStatus (padrão true), passBody (padrão true), passHeaders (padrão true)allowedHeaders, defaultStatus, defaultBodyforward pode ser lista ou objeto:
forward: [STATUS, HEADERS]forward: { STATUS: "*", BODY: false, HEADERS: [content-type] }"*" funciona como atalho para trueHEADERS: "*" repassa todos os headers do destino (exceto hop-by-hop bloqueados)defaultStatus e defaultBody seguem válidos quando STATUS/BODY não estiverem em forwarddefault: política base para todos os métodosmethods.GET, methods.POST, etc: sobrescrevem a política base por métodoPOST e ocultar body no GET usando default + methods.GET.upstream: define como o gateway encaminha a requisição para o destino.
timeoutMs: timeout padrão (em ms) para todos os métodos.timeoutMsByMethod: timeout por método (ex.: GET: 0, POST: 5000).forwardRequest.HEADERS: lista de headers de entrada que devem ser repassados ao destino.
range, accept, user-agent, if-none-match, if-modified-since.forwardRequest.BODY (opcional): reservado para controle explícito de encaminhamento de body por serviço.forwardRequestHeaders: alias legado ainda aceito para compatibilidade, mas recomenda-se migrar para forwardRequest.HEADERS.Exemplo de upstream para streaming:
upstream:
timeoutMsByMethod:
GET: 0
POST: 5000
forwardRequest:
HEADERS:
- range
- accept
- user-agent
- if-none-match
- if-modified-since
Notas de comportamento em GET:
GET com suporte a streaming da resposta do destino.response.methods.GET.forward.HEADERS: "*", os headers de resposta do destino são repassados (exceto hop-by-hop bloqueados).Requisitos:
Comandos (dentro de code):
npm install
npm run dev-backend
Para build de produção:
npm run build
npm run build-frontend
npm start
A aplicação sobe em http://localhost:5124 (ou porta definida em PORT).
No diretório gabriel-store-webhook-gateway:
docker compose up -d
O container expõe a porta 5124 e monta:
${APP_DATA_DIR}/config/webhooks.yml:/app/config/webhooks.ymlAbra:
http://localhost:5124/Envie a senha correspondente ao hash em LOGIN_PASSWORD_HASH.
Exemplo para o serviço telegram:
curl -X POST http://localhost:5124/api/telegram \
-H "Content-Type: application/json" \
-d '{
"message": {
"chat": { "id": 1234 },
"from": { "username": "Batista" },
"text": "Teste"
}
}'
Após autenticar no dashboard:
GET /api/logs/telegram?limit=10100 requisições por 15 minutos). O limite é
configurável por serviço no config/webhooks.yml via bloco rateLimit
(windowMinutes/windowMs e max; use rateLimit: false ou disabled: true
para desligar) e o default global pode ser ajustado pelas envs
WEBHOOK_RATE_LIMIT_MAX e WEBHOOK_RATE_LIMIT_WINDOW_MS.SESSION_EXPIRE_MS).Credenciais inválidas) para evitar vazamento de informação.LOG_PAYLOADS=false para não guardar payload algum.TRUST_PROXY; ajuste conforme sua topologia de proxy para evitar bypass via X-Forwarded-For.webhooks.yml estiver ausente ou inválido, os serviços podem não carregar corretamente.code/src/index.ts: bootstrap do servidor e rotas.code/src/api/processWebhook.ts: processamento/encaminhamento de webhook.code/src/webhook-config.ts: leitura de webhooks.yml e substituição de variáveis.code/src/filters/: filtros de validação.code/public/: páginas de login e dashboard.Content type
Image
Digest
sha256:5418e41bf…
Size
52.5 MB
Last updated
3 months ago
docker pull gabrielsv01/webhook-gateway