Opa, olha só eu uso já em dois clientes a WhatsMeow, mas facilmente usariamos a ApiMe com algumas adaptações, possivelmente vamos usar nas próximas.
O que achei muito bom, bem estruturado, camadas separadas (handlers → services → repositories), factory pattern para storage, já suporta troca entre postgres e memory via config, interfaces bem definidas.
Segurança, autenticação JWT (usuário), API Token (programático), Instance Token (por instância), RBAC implementado, Admin vs User roles, webhooks assinados com HMAC-SHA256 para verificação. Graceful shutdown e restauração de sessões, reconecta instâncias automaticamente no restart.
Questões a observar, eu acho o Redis desnecessário para casos simples:
No factory.go:30-38:
// Inicializar Redis (sempre necessário para webhooks)
redisClient, err := redis.New(cfg.Redis, log)
O Redis é usado apenas como fila de webhooks com operações simples, LPush para enfileirar, BRPop para desenfileirar (bloqueante). Isso pode ser feito in-memory com um channel Go.
type InMemoryQueue struct {
events chan Event
}
O uso atual do Redis é overkill para instalações single-node, desenvolvimento local, casos com poucas instâncias.
Quando Redis faz sentido é em casos de múltiplas réplicas do API (horizontal scaling), persistência de eventos durante restart, monitoramento externo da fila
PostgreSQL vs In-Memory, o driver memory já existe e funciona, mas não há persistência, ao reiniciar, perde-se tudo (usuários, instâncias, tokens).
SQLite seria melhor que PostgreSQL para, single-node deployments, desenvolvimento local, ambientes com poucos recursos, já está sendo usado para sessões WhatsMeow.
PostgreSQL faz sentido em multi-tenant com muitos usuários, backup e replicação necessários, queries complexas (que não existem no código atual), compliance/auditoria.
O projeto já usa SQLite para sessões WhatsMeow (por instância), postgreSQL para metadados da instância. Isso cria complexidade desnecessária.
Outra coisa, o webhook trabalha single-threaded.
Em worker.go:38-50:
for {
select {
case <-ctx.Done():
return
default:
w.processNext(ctx) // Um evento por vez
}
}
Processa eventos sequencialmente, para alto throughput, deveria ter worker pool.
Vi também que está sem rate limiting, não há proteção contra abuse na API. Qualquer token válido pode fazer requests ilimitados.
Para a maioria dos casos de uso (desenvolvimento, single-node, poucas instâncias), PostgreSQL e Redis são overhead desnecessário. O projeto já tem 90% da implementação in-memory pronta, falta apenas:
- Criar QueueDriver in-memory (channel Go)
- Tornar Redis opcional no factory.go
- Considerar SQLite como opção de persistência leve
A arquitetura atual é preparada para enterprise mas adiciona complexidade de infra que muitos usuários não precisam.