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-sistemaA 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.
| Escopo | Operações |
|---|---|
configuracoes_sistema:read | GET /, GET /grupos, GET /historico, GET /origem, GET /{chave} |
configuracoes_sistema:write | PUT /{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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
chave | str | query | não | Filtra por chave (máx. 100). * filtra as ações de recarga. Chave fora do catálogo retorna 404. |
page | int | query | não | Página (padrão 1, mínimo 1). |
per_page | int | query | não | Itens 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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
historico_id | int | path | sim | id 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 comvalornão nulo na tabelaconfiguracao_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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
grupo | str | query | sim | Slug 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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
chave | str | path | sim | Nome 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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
chave | str | path | sim | Nome 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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
chave | str | path | sim | Nome 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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
chave | str | path | sim | Chave do tipo arquivo. |
arquivo | UploadFile | form-data | sim | O arquivo .p12/.pfx. |
Request
Content-Type: multipart/form-data
arquivo=@certificado.p12Response 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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
chave | str | path | sim | Chave 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âmetro | Tipo | Local | Obrigatório | Descrição |
|---|---|---|---|---|
chave | str | path | sim | Chave 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
| Campo | Tipo | Descrição |
|---|---|---|
chave | str | Nome da chave (igual ao .env). |
grupo | str | Slug do grupo. |
tipo | str | str, int, float, bool, url, path, csv, enum, secret, arquivo ou cpf. |
descricao | str | Texto para o Hub exibir. |
secreto | bool | Chave cifrada em repouso e write-only. |
perigosa | bool | Exige confirmacao para alterar. |
requer_restart | str | nao, observers, api, workers ou todos. |
testavel | str | "", smtp, smtp_geral, serpro, serpro_cnd, pasta, reinf ou tecnospeed. |
lgpd | bool | Dado pessoal (CPF/nome do responsável REINF) - sai mascarado. |
origem | str | banco, env ou default. |
valor | any | null | Valor efetivo tipado. Sempre null para chave secreta ou lgpd. |
valor_mascarado | str | null | Máscara do segredo ou do dado lgpd. |
definido | bool | Há valor efetivo não vazio. |
definido_no_banco | bool | Existe linha com valor não nulo no banco (mesmo com o kill-switch desligado). |
mascara_indisponivel | bool | O valor cifrado no banco não decifra com a CRIPTO_KEY atual. |
arquivo | ArquivoRead | null | nome, tamanho, sha256, enviado_em (só chave de arquivo com override). |
validacao | ValidacaoRead | minimo, maximo, opcoes, hosts_permitidos, extensoes. |
atualizado_em | datetime | null | Última alteração do override. |
atualizado_por | str | null | Login de quem alterou. |
HistoricoRead
| Campo | Tipo | Descrição |
|---|---|---|
id | int | Id do registro (usado na reversão). |
chave | str | Chave alterada (* para recarga). |
acao | str | Ação registrada (ex.: definir, remover, arquivo, reverter, recarregar). |
valor_anterior | str | null | Resumo do estado anterior - nunca o segredo em claro. |
valor_novo | str | null | Resumo do estado novo - nunca o segredo em claro. |
usuario_id | int | null | Autor. |
usuario_login | str | null | Login do autor. |
ip | str | null | IP da requisição. |
criado_em | datetime | Hora de Brasília. |
Notas
Como os segredos são protegidos
- Em repouso: chaves
secretearquivosão cifradas com Fernet usando aCRIPTO_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é semprenull.valor_mascaradomostra••••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 comoPrimeiro •••. - No histórico: segredos aparecem como a máscara seguida de
hmac:e 16 caracteres hexadecimais (HMAC-SHA256 do ciphertext, com chave derivada daCRIPTO_KEY); arquivos aparecem comonome hmac:.... - Em erro de validação: o campo se chama
valor_secretode propósito - o handler global de422redige campos comsecretno nome, então a senha não volta ecoada na resposta. - No backup do banco: a coluna
valordeconfiguracao_sistemaé exportada comoNULL.
Precedência e recarga
- Precedência por processo: banco >
.env> default. Sem linha no banco (ou com o kill-switchCONFIG_DB_OVERLAY_ENABLED=false), o valor é exatamente o doSettings/.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 dePOST /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.