Public Markdown Document
π£ Sistema de Comunicados β Manual Tecnico
Painel de configuracao de notificacoes/alertas baseado em eventos. E mail nesta primeira fase, WhatsApp (via WAHA) na proxima. Segue o padrao do Fastapp Engine: o painel e um Object em app.objects[].
π£ Sistema de Comunicados β Manual Tecnico
Painel de configuracao de notificacoes/alertas baseado em eventos. E-mail nesta primeira fase, WhatsApp (via WAHA) na proxima.
Segue o padrao do Fastapp Engine: o painel e um Object emapp.objects[].
1. Conceito
"Quero receber um e-mail toda vez que alguem criar uma task nova no MEU app."
O usuario configura regras de comunicado no painel. Cada regra descreve:
- Quando: qual objeto monitorar + qual evento (create/update/delete) + filtro opcional
- Para onde: canal (email/whatsapp) + destinatario (email ou telefone)
- O que: assunto + mensagem com placeholders
{{record.<campo>}}
Quando um registro do objeto monitorado e criado/atualizado/excluido, o motor le as regras ativas e dispara as notificacoes.
2. Posicionamento Arquitetural
graph TB
subgraph "Metadata Layer (App JSON)"
OBJ_CFG["app.objects.comunicado_config<br/>(regras configuradas)"]
OBJ_TASK["app.objects.task_item<br/>(registros monitorados)"]
end
subgraph "Engine Layer"
POST["POST /api/v1/data/:app/:object<br/>(index.post.ts)"]
DISPATCHER["dispatchComunicados()<br/>(2a fase)"]
end
subgraph "Infrastructure Layer"
EMAIL["server/utils/email.ts<br/>(2a fase)"]
WAHA["server/utils/waha.ts<br/>(ja existe)"]
end
POST -->|ao criar task| DISPATCHER
DISPATCHER -->|le regras| OBJ_CFG
DISPATCHER -->|canal=email| EMAIL
DISPATCHER -->|canal=whatsapp| WAHA
style OBJ_CFG fill:#0891b2,color:#fff
style DISPATCHER fill:#7c3aed,color:#fff
style WAHA fill:#25D366,color:#000
Decisao chave: o painel de configuracao e um Object normal, nao um sistema separado. Isso significa:
- Persistencia: tabela
Record(storage:generic) β zero DDL - UI: gerada automaticamente pelo
CoreDynamicCRUD - API:
/api/v1/data/:app/comunicado_config(CRUD ja pronto pelo motor) - Permissoes: passa pelo RBAC padrao
- Snapshot: incluido no
static-manifest.ts
3. Definicao do Object comunicado_config
Localizada em schemas/comunicado-config-object.json.
| Atributo | Valor |
|---|---|
name |
comunicado_config |
category |
configuration |
nature |
physical |
storage |
generic (tabela Record) |
crud_mode |
modal |
ui_mode |
list |
key_property |
id |
label_property |
name |
Properties
| Propriedade | Tipo | Funcao |
|---|---|---|
name |
string (required) | Nome amigavel da regra |
is_active |
boolean | Liga/desliga sem precisar excluir |
trigger_object |
foreign_key -> object (required) | Dropdown com todos os objects de todos os apps (alimentado por systemStore.getLookupData('object')). Valor salvo = o.name. |
trigger_event |
enum: create/update/delete | Quando disparar |
trigger_filter |
json | Filtro adicional. Ex: {"status":"TODO","prioridade":"ALTA"} |
scope_app_id |
FK app | Restringe a registros cujo app_origem_id coincide |
channel |
enum: email/whatsapp/both | Canal de envio |
recipient_email |
string (semantic: email) | Destinatario quando canal = email |
recipient_phone |
string (semantic: phone) | Destinatario quando canal = whatsapp ou both |
waha_session |
string | Session WAHA usada para enviar |
subject |
string | Assunto (suporta placeholders) |
message_template |
textarea (required) | Corpo da mensagem (suporta placeholders) |
last_dispatched_at |
datetime | Telemetria β preenchida pelo dispatcher |
dispatch_count |
number | Telemetria β contador |
Placeholders
{{record.<campo>}} β substituido pelos campos do registro que disparou o evento. Ex: numa regra para task_item:
Subject: Nova task: {{record.titulo}}
Body:
Foi criada a task "{{record.titulo}}" com prioridade {{record.prioridade}}.
Status atual: {{record.status}}
4. Como instalar em um App
Opcao A β Script (recomendado)
# Apenas no app task-board:
node scripts/add-comunicado-config-to-app.mjs task-board
# Em todos os apps:
node scripts/add-comunicado-config-to-app.mjs --all
O script (scripts/add-comunicado-config-to-app.mjs) faz, de forma idempotente:
- Carrega o schema de
schemas/comunicado-config-object.json - Adiciona/atualiza em
app.objects[] - Garante que existe um module
Configuracoesemapp.modules[]referenciando o object (e o motor gera o item de menu automaticamente em/<app-slug>/configuracoes/comunicado_config) - Limpa entradas legadas em
app.pages[]deixadas por versoes anteriores do script (que causavam o erro "App comunicados nao encontrada no manifesto") - Persiste via
prisma.app.update
Por que nao adicionar em
app.pages[]?
O item de sidebar vem domodules[].objects[]β adicionar empages[]cria duplicacao e, se opathfor absoluto, conflita com a rota dinamica[app]/index.vue.
Apos rodar, recarregue o app β o item "Comunicados" aparece no menu lateral em Configuracoes.
Opcao B β Manual via Schema Manager
- Abra
/fastapp/schema-v3?app=<id> - Importe o JSON de
schemas/comunicado-config-object.json - Adicione module/page conforme padrao
5. Roadmap β Fases
Fase 1 β Painel (β entregue)
- Object schema
comunicado_config - Script de migracao (Add to App)
- Documentacao
Fase 2 β Dispatcher + Email (β entregue)
-
server/utils/email.tsβ wrapper SMTP (Nodemailer, Gmail por padrao, SSL/TLS) -
server/utils/comunicado-dispatcher.tsβ le regras, filtra (trigger_object/event, trigger_filter, scope_app_id), renderiza template, dispara - Hook fire-and-forget em
server/api/v1/data/[app]/[object]/index.post.ts(4 caminhos: external, native delegate, native table, generic Record) - Hook em
[id].patch.tspara eventoupdate(mesmos 4 caminhos) - Hook em
[id].delete.tspara eventodelete(mesmos 4 caminhos; passa oexistinglido antes do delete para o template ter acesso aos campos) -
GET /api/email/verifyβ health check do SMTP sem enviar -
POST /api/email/testβ envia e-mail de teste para um destinatario - Telemetria β
last_dispatched_atedispatch_countatualizados na propria regra
Configuracao de SMTP (Gmail)
- Ativar Verificacao em 2 etapas: https://myaccount.google.com/security
- Gerar Senha de app: https://myaccount.google.com/apppasswords (cole sem espacos)
.env:SMTP_HOST=smtp.gmail.com SMTP_PORT=465 [email protected] SMTP_PASS=abcdefghijklmnop # 16 chars da senha de app [email protected]- Validar:
curl http://localhost:3000/api/email/verifyβ{ ok: true, configured: true } - Enviar teste:
curl -X POST http://localhost:3000/api/email/test -H "Content-Type: application/json" -d '{"to":"[email protected]"}'
Fase 3 β WhatsApp (β entregue)
- Reaproveita
server/utils/waha.ts - O dispatcher decide canal pelo campo
channel - Opcoes de canal:
email,whatsappeboth(envia pelos dois canais) recipient_phoneaceita multiplos numeros separados por;,,ou quebra de linha, sem mascara de telefone- Se uma regra antiga estiver como
email, mas tiverrecipient_phonepreenchido, o dispatcher tambem envia WhatsApp waha_sessionusadefaultquando vazio
Fase 4 β UI Refinada
- View kanban-like agrupando por canal/objeto
- Preview de mensagem com placeholders renderizados
- Teste de envio manual ("Enviar teste agora")
- Painel de telemetria (taxa de entrega, ultimos disparos)
6. Como funciona o gatilho (vista geral, fase 2)
sequenceDiagram
participant User
participant API as POST /api/v1/data/<br/>:app/task_item
participant DB as createRecord()
participant Dispatcher as dispatchComunicados()
participant Rules as app.objects.<br/>comunicado_config
participant Email as email.ts
participant WAHA as waha.ts
User->>API: cria task
API->>DB: insert Record
DB-->>API: novo registro
API->>Dispatcher: (objectName="task_item", event="create", record)
Dispatcher->>Rules: filtra is_active + trigger_object + trigger_event
Dispatcher->>Dispatcher: aplica trigger_filter
Dispatcher->>Dispatcher: aplica scope_app_id (se setado)
par para cada regra ativa
alt channel = email
Dispatcher->>Email: send(to, subject, body)
else channel = whatsapp/both
Dispatcher->>WAHA: sendText(session, chatId, text)
end
end
Dispatcher->>Rules: incrementa dispatch_count + last_dispatched_at
API-->>User: registro criado
PrincΓpio: o dispatcher e fire-and-forget (nunca bloqueia o response da API). Falhas viram log + telemetria, nao quebram a criacao do registro.
7. Exemplo Completo (caso do usuario)
"Quero receber alerta no email [email protected] toda vez que alguem criar uma task nova para o app task-board."
Apos rodar node scripts/add-comunicado-config-to-app.mjs task-board, navegar ate /task-board/configuracoes/comunicado_config e criar:
{
"name": "Alerta de nova task β gestor",
"is_active": true,
"trigger_object": "task_item",
"trigger_event": "create",
"trigger_filter": null,
"scope_app_id": 25,
"channel": "email",
"recipient_email": "[email protected]",
"subject": "Nova task: {{record.titulo}}",
"message_template": "Uma nova task foi criada:\n\nTitulo: {{record.titulo}}\nPrioridade: {{record.prioridade}}\nStatus: {{record.status}}"
}
Quando a fase 2 estiver implementada, isso bastara β sem codigo customizado no app.
Status: Fase 1, Fase 2 e Fase 3 entregues. WhatsApp via WAHA ativo no dispatcher.
Branch: feat/Panel-Comunication