Logosulfite.app
rafagazani/sulfite 999999

Conexões

Gerenciamento de conexões de banco de dados e HTTP do sulfite_server

Conexões#

O servidor gerencia conexões com bancos de dados e serviços HTTP usados pelos datasources dos relatórios. Conexões são armazenadas com criptografia (quando ENCRYPTION_KEY está configurado) e testadas automaticamente na criação.

Todos os endpoints de conexão requerem a permissão canSettings.

Em deploys onde outro sistema já gerencia credenciais de banco por tenant, use CONNECTION_REGISTRY_BACKEND=remote. Nesse modo o Sulfite não armazena senhas localmente — cada connectionRef é resolvido sob demanda no hub da sua infra.

Tipos de conexão#

Database#

Para PostgreSQL e outros bancos via postgres ou postgrest driver:

{
  "name": "my-postgres",
  "type": "database",
  "host": "db.example.com",
  "port": 5432,
  "database": "mydb",
  "username": "user",
  "password": "secret",
  "ssl": true,
  "maxConnections": 5,
  "extra": {
    "schema": "public",
    "authSchema": "sulfite_meta"
  }
}
CampoObrigatórioDefaultDescrição
nameSimIdentificador único da conexão
typeSimdatabase
hostSimHost do banco
portNão5432Porta
databaseSimNome do banco
usernameSimUsuário
passwordSimSenha
sslNãotrueUsar SSL
maxConnections Não 5 Tamanho máximo do pool para essa conexão
extraNãoConfigurações extras (driver-specific)

Campos comuns em extra:

CampoUso
schema Schema padrão usado por datasources SQL e como fallback para autorização por tenant
authSchema Schema específico onde fica sulfite_authorized_users

Veja Autorização por tenant para o contrato da view sulfite_authorized_users.

HTTP#

Para APIs REST e PostgREST:

{
  "name": "my-api",
  "type": "http",
  "baseUrl": "https://api.example.com",
  "defaultHeaders": { "X-Token": "abc123" },
  "extra": { "timeout": 30 }
}
CampoObrigatórioDefaultDescrição
nameSimIdentificador único da conexão
typeSimhttp
baseUrlSimURL base
defaultHeaders Não Headers padrão para todas as requests
extraNãoConfigurações extras

Sanitização de respostas#

As respostas de GET omitem dados sensíveis:

TipoCampos retornadosCampos omitidos
database name , type , host , port , database , ssl username, password, extra
http name, type, baseUrl defaultHeaders, extra

Criptografia at-rest#

Quando ENCRYPTION_KEY está configurado, os campos sensíveis são criptografados usando AES-256-GCM (via EnvelopeEncryptor):

TipoCampos criptografados
database password, username, extra
httpdefaultHeaders, extra

Os dados criptografados são armazenados em um campo _encrypted. Suporta rotação de chave via ENCRYPTION_KEY_PREV — entradas legadas em texto plano são migradas automaticamente no próximo upsert.

Registry remoto#

Com o backend remoto, o CRUD de conexões pode ficar desligado e o Sulfite apenas resolve connectionRef no momento da geração:

CONNECTION_REGISTRY_BACKEND=remote
CONNECTION_HUB_URL=https://hub.your-infra.internal
CONNECTION_HUB_TOKEN=<credencial-de-servico>
CONNECTION_HUB_CACHE_TTL_SECONDS=60
CONNECTION_ROUTES_ENABLED=false

O servidor chama GET /internal/tenants/{tenantId}/connections/{connectionRef} no hub e mantém cache curto por tenant/conexão. Use esse modo quando outro sistema é a fonte de verdade para credenciais.

O hub deve retornar um JSON no mesmo formato de ConnectionEntry:

{ "type": "database", "name": "erp", "host": "db.internal", "port": 5432,
  "database": "erp", "username": "reader", "password": "secret", "ssl": true }
Status do hubResultado
200Conexão cacheada e usada
404resolveAsync retorna null
403Geração retorna 403 connection_forbidden
5xx / timeoutGeração retorna 503 connection_unavailable

Endpoints#

Listar — GET /api/v1/connections#

Query params: ?page=1&limit=20 (paginação opcional)

Resposta 200: Array de conexões sanitizadas.


Detalhes — GET /api/v1/connections/:name#

Resposta 200: Conexão sanitizada.

StatusErroQuando
404not_foundNome não encontrado

Criar — POST /api/v1/connections#

Body: JSON completo da conexão (veja tipos acima).

Resposta 201: Conexão sanitizada.

Após armazenar, o servidor **testa automaticamente** a conexão. Se o teste falha, a conexão é removida e retorna `422 connection_failed`.
StatusErroQuando
400missing_namename ausente
400invalid_typetype inválido
409 already_exists Já existe uma conexão com esse nome
422 connection_failed Teste de conectividade falhou

Atualizar — PUT /api/v1/connections/:name#

Body: JSON completo da conexão (o name da URL é usado).

Resposta 200: Conexão sanitizada.

Se o teste da conexão atualizada falha, a entrada anterior é restaurada.
StatusErroQuando
400invalid_typetype inválido
404not_foundNome não encontrado
422 type_change_forbidden Tentativa de mudar databasehttp
422 connection_failed Teste de conectividade falhou

Excluir — DELETE /api/v1/connections/:name#

Resposta 200:

{ "deleted": "my-postgres" }
StatusErroQuando
404not_foundNome não encontrado

Testar — POST /api/v1/connections/:name/test#

Testa a conectividade sem modificar a conexão armazenada.

Resposta 200:

{ "status": "ok", "latency_ms": 42 }
StatusErroQuando
404not_foundNome não encontrado
422 connection_failed Teste falhou (inclui latency_ms)

Mecanismo de teste:

  • Database: executa SELECT 1 AS ping via postgres resolver
  • HTTP: faz GET na URL base via REST resolver

Exemplo#

# Criar conexão
curl -X POST http://localhost:8080/api/v1/connections \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "producao",
    "type": "database",
    "host": "db.example.com",
    "port": 5432,
    "database": "mydb",
    "username": "reader",
    "password": "s3cret",
    "ssl": true
  }'

# Listar (sem dados sensíveis)
curl http://localhost:8080/api/v1/connections \
  -H "Authorization: Bearer $TOKEN"

# Testar conectividade
curl -X POST http://localhost:8080/api/v1/connections/producao/test \
  -H "Authorization: Bearer $TOKEN"