Configurações do Sistema

Endpoints que alimentam a tela Hub > Configurações. Cada chave do catálogo (ex.: EMAIL_HOST, EMAIL_PASSWORD, API_PUBLIC_URL) pode receber um override gravado no banco (tabela configuracao_sistema), que passa a valer no lugar do .env. A API lista as chaves por grupo, mostra a origem efetiva de cada uma, grava/remove overrides, recebe arquivos de certificado (.p12/.pfx), testa conexões, mantém o histórico de alterações (com reversão) e força a recarga da configuração.

Todas as rotas exigem perfil administrador com JWT de pessoa (guard somente_admin_humano). API Key recebe 403 mesmo com escopo *:*. Veja Autenticação.

Valor salvo no Hub sobrepõe o .env - inclusive valor em branco

A precedência é banco > .env > default (resolver.py). Qualquer linha com valor não nulo na tabela configuracao_sistema vence o .env, sem diff no código e sem restart. Isso vale também para texto vazio: em chaves do tipo str e csv o PUT aceita "", grava "" (que não é NULL) e o resolver passa a devolver "" no lugar do valor do .env. Nesses casos o GET mostra definido: false, mas origem: "banco" e definido_no_banco: true - e o GET /configuracoes-sistema/origem também aponta banco. Para voltar a valer o .env, use DELETE /configuracoes-sistema/{chave} (remove o override). Chaves secret (Segredo vazio.), url, path, enum e bool recusam valor vazio com 422.

Base path

Todos os endpoints abaixo são montados sob o prefixo:

/api/v1/configuracoes-sistema

A URL completa em produção é https://api.contabilidadealcance.com.br/api/v1/configuracoes-sistema.

Escopos necessários

O router é montado com _SCOPE_DEP, então o escopo é derivado da rota: o prefixo /configuracoes-sistema vira o recurso configuracoes_sistema (hífen → underscore) e o método define a ação.

EscopoOperações
configuracoes_sistema:readGET /, GET /grupos, GET /historico, GET /origem, GET /{chave}
configuracoes_sistema:writePUT /{chave}, DELETE /{chave}, POST e DELETE /{chave}/arquivo, POST /{chave}/testar, POST /historico/{historico_id}/reverter, POST /recarregar

Na prática o escopo não basta: o guard somente_admin_humano exige perfil = administrador (diretoria não entra) e token_type == "user". Credencial de máquina não altera configuração do sistema. As mutações e o testar têm rate limit de 30 requisições por 60 s (bucket config-sistema-write).

Endpoints

GET /configuracoes-sistema/grupos

Lista os grupos do catálogo (as páginas da tela de configurações), com o total de chaves e quantas estão definidas no banco.

Parâmetros

Este endpoint não recebe parâmetros.

Request

Sem corpo de requisição.

Response 200 OK

{
  "status": "success",
  "message": "Grupos recuperados com sucesso",
  "data": [
    {
      "slug": "email",
      "titulo": "E-mail (SMTP)",
      "pill": "Geral",
      "total": 6,
      "definidas_no_banco": 1,
      "perigoso": false
    }
  ]
}

Grupos existentes no catálogo: email, email_geral, portal, hub, identidade, serpro, serpro_cnd, reinf, cora, tecnospeed, chaves_api, acessorias, ekontrol, pastas, uploads, portais, radar, seguranca.

Escopo: configuracoes_sistema:read + administrador com JWT de pessoa

GET /configuracoes-sistema/historico

Histórico paginado das alterações de configuração (lido da trilha de auditoria unificada). O id de cada item é o que se passa para o endpoint de reversão. criado_em sai em hora de Brasília.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
chavestrquerynãoFiltra por chave (máx. 100). * filtra as ações de recarga. Chave fora do catálogo retorna 404.
pageintquerynãoPágina (padrão 1, mínimo 1).
per_pageintquerynãoItens por página (padrão 50, entre 1 e 200).

Request

Sem corpo de requisição.

Response 200 OK

{
  "status": "success",
  "message": "Histórico recuperado com sucesso",
  "data": {
    "itens": [
      {
        "id": 98123,
        "chave": "EMAIL_PASSWORD",
        "acao": "definir",
        "valor_anterior": "••••a1b2 hmac:3f9c0d1e2a4b5c6d",
        "valor_novo": "••••ef9a hmac:7a8b9c0d1e2f3a4b",
        "usuario_id": 7,
        "usuario_login": "admin.hub",
        "ip": "10.0.0.15",
        "criado_em": "2026-10-14T16:02:11"
      }
    ],
    "total": 1,
    "page": 1,
    "per_page": 50
  }
}

Escopo: configuracoes_sistema:read + administrador com JWT de pessoa

POST /configuracoes-sistema/historico/{historico_id}/reverter

Restaura o estado da chave anterior à alteração indicada. Se antes não havia override, a reversão remove o override atual. A ação de recarga (chave = "*") não é reversível (422).

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
historico_idintpathsimid do item retornado por GET /historico.

Corpo opcional (ConfirmacaoBody): confirmacao - o nome da chave repetido. É obrigatório quando a chave é perigosa e também sempre que a chave é secreta ou de arquivo (a reversão troca uma credencial viva por uma antiga). Sem ele, 422.

Request

{
  "confirmacao": "EMAIL_PASSWORD"
}

Response 200 OK

Retorna a EntradaRead da chave já revertida, com a mensagem "Alteração revertida com sucesso".

Erros: 404 (histórico ou chave não encontrados), 422 (confirmação ausente/errada, recarga, URL que não passa mais nas regras de host), 503 (auditoria desligada). Escopo: configuracoes_sistema:write + administrador com JWT de pessoa

GET /configuracoes-sistema/origem

Origem efetiva de todas as chaves do catálogo, sem valores. Útil para conferir de onde cada configuração está vindo.

  • banco - existe linha com valor não nulo na tabela configuracao_sistema (inclusive texto vazio) e o overlay está habilitado;
  • env - a variável existe no ambiente do processo (os.environ);
  • default - nenhum dos dois.

Com o kill-switch CONFIG_DB_OVERLAY_ENABLED=false, nenhuma chave aparece como banco. geracao é um contador do processo que sobe a cada recarga em que o overlay mudou.

Parâmetros

Este endpoint não recebe parâmetros.

Request

Sem corpo de requisição.

Response 200 OK

{
  "status": "success",
  "message": "Origens recuperadas com sucesso",
  "data": [
    { "chave": "EMAIL_HOST", "origem": "banco", "requer_restart": "nao", "geracao": 4 },
    { "chave": "EMAIL_PASSWORD", "origem": "env", "requer_restart": "nao", "geracao": 4 }
  ]
}

Escopo: configuracoes_sistema:read + administrador com JWT de pessoa

POST /configuracoes-sistema/recarregar

Recarrega a configuração na hora: refaz o overlay da API, envia um broadcast para os workers Celery e reinicia os observers de pasta se alguma pasta vigiada mudou. A recarga é registrada na trilha em modo best-effort (não retorna 503 com auditoria desligada).

Parâmetros

Este endpoint não recebe parâmetros nem corpo.

Request

Sem corpo de requisição.

Response 200 OK

{
  "status": "success",
  "message": "Configuração recarregada",
  "data": {
    "geracao": 5,
    "api": "ok",
    "workers": "broadcast enviado",
    "observers": "nao_aplicavel"
  }
}

Valores possíveis: api = ok | erro; workers = broadcast enviado | indisponivel; observers = reiniciados | nao_aplicavel | erro. Escopo: configuracoes_sistema:write + administrador com JWT de pessoa

GET /configuracoes-sistema/

Lista as chaves de um grupo com o estado efetivo de cada uma (EntradaRead).

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
grupostrquerysimSlug do grupo (1 a 50 caracteres). Grupo desconhecido → 404.

Request

Sem corpo de requisição.

Response 200 OK

{
  "status": "success",
  "message": "Configurações recuperadas com sucesso",
  "data": [
    {
      "chave": "EMAIL_HOST",
      "grupo": "email",
      "tipo": "str",
      "descricao": "Servidor SMTP de saída (ex.: smtp.gmail.com).",
      "secreto": false,
      "perigosa": false,
      "requer_restart": "nao",
      "testavel": "smtp",
      "lgpd": false,
      "origem": "banco",
      "valor": "smtp.exemplo.com.br",
      "valor_mascarado": null,
      "definido": true,
      "definido_no_banco": true,
      "mascara_indisponivel": false,
      "arquivo": null,
      "validacao": { "minimo": null, "maximo": null, "opcoes": [], "hosts_permitidos": [], "extensoes": [] },
      "atualizado_em": "2026-10-14T16:02:11",
      "atualizado_por": "admin.hub"
    },
    {
      "chave": "EMAIL_PASSWORD",
      "grupo": "email",
      "tipo": "secret",
      "descricao": "Senha do usuário SMTP.",
      "secreto": true,
      "perigosa": false,
      "requer_restart": "nao",
      "testavel": "",
      "lgpd": false,
      "origem": "env",
      "valor": null,
      "valor_mascarado": "••••",
      "definido": true,
      "definido_no_banco": false,
      "mascara_indisponivel": false,
      "arquivo": null,
      "validacao": { "minimo": null, "maximo": null, "opcoes": [], "hosts_permitidos": [], "extensoes": [] },
      "atualizado_em": null,
      "atualizado_por": null
    }
  ]
}

Escopo: configuracoes_sistema:read + administrador com JWT de pessoa

GET /configuracoes-sistema/{chave}

Retorna uma chave do catálogo com o estado efetivo (EntradaRead).

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
chavestrpathsimNome da chave (igual ao do .env). Fora do catálogo → 404.

Request

Sem corpo de requisição.

Response 200 OK

Envelope com uma EntradaRead (mesmo formato dos itens de GET /configuracoes-sistema/) e a mensagem "Configuração recuperada com sucesso".

Escopo: configuracoes_sistema:read + administrador com JWT de pessoa

PUT /configuracoes-sistema/{chave}

Grava o override da chave no banco. Depois da gravação, o overlay da API é recarregado na hora; os workers pegam a mudança pelo TTL do cache (CONFIG_CACHE_TTL_SECONDS, padrão 60 s) ou por POST /recarregar.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
chavestrpathsimNome da chave.

Corpo EntradaUpdate (campos extras são recusados):

  • valor - para chave não secreta (texto, número ou booleano);
  • valor_secreto - para chave secreta (máx. 8192 caracteres; write-only, nunca volta em leitura);
  • confirmacao - o nome da chave repetido, obrigatório quando a chave é perigosa.

Informar os dois (valor e valor_secreto), ou o campo errado para o tipo da chave, retorna 422. Chave de arquivo não aceita PUT (use POST /{chave}/arquivo).

Request

{
  "valor_secreto": "nova-senha-do-smtp-2026"
}

Response 200 OK

Envelope com a EntradaRead atualizada e a mensagem "Configuração salva com sucesso".

Erros: 404 (chave desconhecida), 422 (valor inválido para o tipo, confirmação ausente/errada - a mensagem nunca ecoa o valor), 503 (auditoria desligada: AUDIT_ENABLED=false bloqueia toda mutação). Escopo: configuracoes_sistema:write + administrador com JWT de pessoa

DELETE /configuracoes-sistema/{chave}

Remove o override do banco: a chave volta a valer o .env (ou o default). Idempotente - sem override, apenas devolve o estado atual.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
chavestrpathsimNome da chave.

Request

Sem corpo de requisição.

Response 200 OK

Envelope com a EntradaRead já sem override e a mensagem "Override removido; vale o .env/default".

Erros: 404, 503 (auditoria desligada). Escopo: configuracoes_sistema:write + administrador com JWT de pessoa

POST /configuracoes-sistema/{chave}/arquivo

Envia o arquivo de uma chave do tipo arquivo (certificados .p12/.pfx). Requisição multipart/form-data com o campo arquivo. A extensão precisa estar entre as aceitas pela chave, o arquivo não pode ser vazio nem passar do tamanho máximo, e - quando a chave tem senha associada - o certificado é aberto com a senha efetiva para validar. O conteúdo é gravado cifrado; a API materializa o arquivo em <STORAGE_ROOT>/configuracao_sistema/ quando precisa usá-lo.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
chavestrpathsimChave do tipo arquivo.
arquivoUploadFileform-datasimO arquivo .p12/.pfx.

Request

Content-Type: multipart/form-data
arquivo=@certificado.p12

Response 200 OK

Envelope com a EntradaRead (campo arquivo preenchido com nome, tamanho, sha256, enviado_em) e a mensagem "Arquivo salvo com sucesso".

Erros: 404, 422 (extensão não permitida, arquivo vazio/grande demais, Arquivo ou senha inválidos., chave que não recebe arquivo), 503. Escopo: configuracoes_sistema:write + administrador com JWT de pessoa

DELETE /configuracoes-sistema/{chave}/arquivo

Remove o arquivo enviado: volta a valer o caminho configurado no .env. O arquivo materializado em disco também é apagado.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
chavestrpathsimChave do tipo arquivo.

Request

Sem corpo de requisição.

Response 200 OK

Envelope com a EntradaRead e a mensagem "Arquivo removido".

Erros: 404, 422 (chave que não é de arquivo), 503. Escopo: configuracoes_sistema:write + administrador com JWT de pessoa

POST /configuracoes-sistema/{chave}/testar

Testa a chave (conexão SMTP, SERPRO, REINF, TecnoSpeed ou acesso a pasta, conforme testavel). Sem corpo, testa o valor efetivo; com corpo (TestarBody: valor ou valor_secreto), testa o valor informado sem salvar. Nos testes smtp e smtp_geral o corpo é ignorado (a senha real nunca é enviada a um host vindo do request) e o resultado traz detalhes.aviso. Timeout de 15 s.

Parâmetros

ParâmetroTipoLocalObrigatórioDescrição
chavestrpathsimChave testável. Chave não testável → 422.

Request

{
  "valor": "\\\\servidor\\compartilhamento\\entrada"
}

Response 200 OK

{
  "status": "success",
  "message": "Tempo esgotado (15 s).",
  "data": {
    "ok": false,
    "mensagem": "Tempo esgotado (15 s).",
    "detalhes": {}
  }
}

A message do envelope é a própria mensagem do teste. Falha do teste não vira erro HTTP: retorna 200 com ok: false (exceções viram Falha: <Tipo> com o erro redigido em detalhes.erro). Escopo: configuracoes_sistema:write + administrador com JWT de pessoa

Schemas

EntradaRead

CampoTipoDescrição
chavestrNome da chave (igual ao .env).
grupostrSlug do grupo.
tipostrstr, int, float, bool, url, path, csv, enum, secret, arquivo ou cpf.
descricaostrTexto para o Hub exibir.
secretoboolChave cifrada em repouso e write-only.
perigosaboolExige confirmacao para alterar.
requer_restartstrnao, observers, api, workers ou todos.
testavelstr"", smtp, smtp_geral, serpro, serpro_cnd, pasta, reinf ou tecnospeed.
lgpdboolDado pessoal (CPF/nome do responsável REINF) - sai mascarado.
origemstrbanco, env ou default.
valorany | nullValor efetivo tipado. Sempre null para chave secreta ou lgpd.
valor_mascaradostr | nullMáscara do segredo ou do dado lgpd.
definidoboolHá valor efetivo não vazio.
definido_no_bancoboolExiste linha com valor não nulo no banco (mesmo com o kill-switch desligado).
mascara_indisponivelboolO valor cifrado no banco não decifra com a CRIPTO_KEY atual.
arquivoArquivoRead | nullnome, tamanho, sha256, enviado_em (só chave de arquivo com override).
validacaoValidacaoReadminimo, maximo, opcoes, hosts_permitidos, extensoes.
atualizado_emdatetime | nullÚltima alteração do override.
atualizado_porstr | nullLogin de quem alterou.

HistoricoRead

CampoTipoDescrição
idintId do registro (usado na reversão).
chavestrChave alterada (* para recarga).
acaostrAção registrada (ex.: definir, remover, arquivo, reverter, recarregar).
valor_anteriorstr | nullResumo do estado anterior - nunca o segredo em claro.
valor_novostr | nullResumo do estado novo - nunca o segredo em claro.
usuario_idint | nullAutor.
usuario_loginstr | nullLogin do autor.
ipstr | nullIP da requisição.
criado_emdatetimeHora de Brasília.

Notas

Como os segredos são protegidos

  • Em repouso: chaves secret e arquivo são cifradas com Fernet usando a CRIPTO_KEY. Se a chave de criptografia estiver ausente ou trocada, a gravação falha (sem gravar nada) e o resolver deixa a chave fora do overlay - vale o .env, nunca o ciphertext.
  • Na leitura: valor é sempre null. valor_mascarado mostra •••• seguido dos 4 últimos caracteres apenas quando o segredo tem 12 ou mais caracteres; abaixo disso, só ••••. Sem valor, null. Se o ciphertext não decifra, mascara_indisponivel: true.
  • Dados lgpd: CPF sai como ***.***.***-NN (2 últimos dígitos) e nome como Primeiro •••.
  • No histórico: segredos aparecem como a máscara seguida de hmac: e 16 caracteres hexadecimais (HMAC-SHA256 do ciphertext, com chave derivada da CRIPTO_KEY); arquivos aparecem como nome hmac:....
  • Em erro de validação: o campo se chama valor_secreto de propósito - o handler global de 422 redige campos com secret no nome, então a senha não volta ecoada na resposta.
  • No backup do banco: a coluna valor de configuracao_sistema é exportada como NULL.

Precedência e recarga

  • Precedência por processo: banco > .env > default. Sem linha no banco (ou com o kill-switch CONFIG_DB_OVERLAY_ENABLED=false), o valor é exatamente o do Settings/.env.
  • A API recarrega o overlay em background a cada CONFIG_CACHE_TTL_SECONDS (padrão 60 s) e imediatamente após cada escrita feita por estas rotas. Workers recarregam pelo TTL ou pelo broadcast de POST /recarregar.
  • Banco indisponível na recarga mantém o overlay anterior e loga um aviso; a API sobe mesmo com o banco fora.
  • Valor gravado que não tipa (ex.: texto em chave int) fica fora do overlay - vale o .env.

Auditoria obrigatória

Toda mutação (PUT, DELETE, arquivo, reversão) exige a trilha de auditoria ligada. Com AUDIT_ENABLED=false (ou sem a tabela audit_log), essas rotas retornam 503 e a consulta continua disponível. Chaves perigosas geram log de aviso com login e IP do autor.