Aplicacao interna para emissao, consulta, validacao e administracao de certificados digitais por secretaria.
O projeto esta dividido em duas partes:
- Frontend: exige login, faz a pre-visualizacao do certificado no navegador, monta o PNG final em
canvase envia esse arquivo pronto para a API armazenar. - API: autentica usuarios, controla secretarias, gera o codigo oficial, gera o QR Code em PNG, valida certificados por codigo, guarda o PNG final e registra auditoria.
Resumo importante:
- o QR Code e gerado no backend
- o PNG final do certificado e gerado no frontend
- o backend nao renderiza o certificado inteiro
- a tela inicial do gerador e restrita por login
- a validacao em
/validar/{codigo}continua publica
- HTML, CSS e JavaScript puro
canvaspara renderizacao do certificadoJSZippara geracao do arquivo.zipno loteSheetJS/XLSXpara leitura de.xlsx,.xlse.csv
- Python 3.12
- FastAPI
- SQLAlchemy
- PostgreSQL
- Alembic
- HMAC-SHA256 para integridade dos certificados
- Docker / Docker Compose
- Nginx para servir o frontend estatico
- Portainer para deploy em producao
- Cloudflare Tunnel ou proxy reverso para publicacao no dominio final
- login obrigatorio na pagina inicial
- sessao por cookie HTTP-only
- perfis
admin_globaleoperador - suporte a multiplas secretarias
- selecao de secretaria ativa em sessao
- geracao individual de certificado
- geracao em lote por planilha
- email opcional do participante em emissao individual e lote
- envio automatico do certificado por SMTP quando o PNG e salvo
- reenvio manual de email para certificados ja emitidos
- download do PNG individual
- download do lote em
.zip - pre-visualizacao da planilha antes do lote
- confirmacao antes de gerar lote
- retry automatico no upload de PNG do lote
- certificados ficam pendentes ate o PNG ser salvo
- descarte automatico de certificados pendentes quando o PNG falha
- alerta de possivel duplicidade antes da geracao individual
- filtros rapidos em
CertificadoseAuditoria - reimpressao e visualizacao dos certificados ja emitidos
- edicao administrativa de dados do certificado emitido, com preservacao de auditoria
- lixeira administrativa para certificados excluidos, com restauracao e limpeza controlada
- pagina publica de validacao por QR Code
- cadastro e edicao de usuarios
- vinculo de operador a uma ou mais secretarias
admin_globalacessa todas as secretarias e nao mantem vinculos salvosadmin_globaldeve ser reservado para a equipe de TI e funcoes globais- secretarias inativas deixam de aparecer para vinculo de operadores
- cadastro, edicao, ativacao e desativacao de secretarias
- email de resposta por secretaria para receber replies de certificados enviados
- exclusao administrativa de usuarios
- exclusao administrativa de secretarias sem certificados emitidos
- exclusao administrativa de certificados com confirmacao por codigo e senha do admin
- operadores podem gerenciar moldes, logos, assinaturas e instituicoes apenas das secretarias vinculadas
- operadores nao acessam cadastro de usuarios, cadastro de secretarias nem auditoria
- molde temporario por tela de geracao
- logo temporaria por tela de geracao
- assinatura temporaria por tela de geracao
- assinatura 2 e assinatura 3 opcionais como itens extras da geracao
- layouts salvos por secretaria para reaplicar posicoes, assets selecionados e textos de assinatura
- aba propria de auditoria visivel apenas para
admin_global - auditoria de login, emissao, upload, exclusao e acoes administrativas
- limitacao de tentativas de login
- protecao dos endpoints administrativos
/docsprotegida para admin ou desativavel por ambiente- lote limitado por
CERTIFICADOS_MAX_BATCH_ITEMS - geracao automatica de codigo com reserva atomica por
prefixo + ano - hashes novos em HMAC-SHA256
- compatibilidade com hashes legados em SHA-256
- bootstrap inicial validado no startup
- persistencia de certificados e assets visuais em volumes Docker persistentes
Fluxo da emissao individual:
- o usuario faz login
- o frontend envia os dados basicos para a API
- a API reserva o proximo codigo oficial
- a API devolve
codigo,url_validacaoe os metadados do certificado - o frontend solicita o PNG do QR Code
- o frontend desenha o certificado completo em
canvas - o frontend converte o
canvaspara PNG - o frontend envia o PNG final para a API
- a API marca o certificado como ativo apenas apos salvar o arquivo e registrar auditoria
Fluxo do lote:
- o operador envia a planilha
- o frontend detecta o cabecalho, normaliza os dados, mostra a previa e valida as linhas
- a API registra o lote de certificados como pendentes e reserva codigos oficiais
- o frontend gera um PNG por certificado valido
- o frontend tenta enviar cada PNG com retry
- a API ativa apenas os certificados cujo PNG foi salvo com sucesso
- pendentes que falham no PNG sao descartados automaticamente sempre que possivel
- o frontend monta um
.zipapenas com os certificados concluidos
O sistema ja esta preparado para:
- vincular usuarios a uma ou mais secretarias
- gravar em cada certificado qual secretaria emitiu
- gravar qual usuario emitiu o certificado
- manter moldes por secretaria
- manter logos, assinaturas e instituicoes por secretaria
- manter layouts salvos por secretaria
- permitir que operadores cuidem dos assets visuais das suas proprias secretarias sem permissao global
Secretarias padrao do seed:
SESAUSEMEDSEAFISEAMASEMOPSUGEP
O sistema trabalha com dois tipos de molde:
- molde cadastrado da secretaria: salvo no backend e administrado pelo
admin_globalou por operador vinculado a secretaria - molde temporario: carregado localmente pelo operador apenas para a geracao atual
Regra importante:
- o molde funciona como fundo do certificado
- textos, QR Code, assinatura e logo continuam nas posicoes atuais
- se o arquivo do molde ja trouxer o titulo "Certificado", marque a opcao do molde para ocultar o titulo gerado pelo sistema
- o operador nao move o layout dos campos
Formatos aceitos para moldes:
pngjpgjpegwebp
svg foi removido para evitar risco de XSS em arquivos servidos no mesmo dominio da aplicacao.
O sistema tambem permite catalogos persistidos de:
- logos por secretaria
- assinaturas por secretaria
- instituicoes por secretaria
- selos e marcas extras por secretaria
Funcionamento:
- o
admin_globalpode cadastrar arquivos aprovados de qualquer secretaria - o operador pode cadastrar, editar e excluir arquivos apenas das secretarias vinculadas ao seu usuario
- cada secretaria pode ter um item padrao
- ao selecionar a secretaria ativa, o gerador carrega automaticamente o molde, a logo, a assinatura e a instituicao padrao
- o operador pode trocar para outro item aprovado da mesma secretaria
- uploads manuais na tela continuam valendo como sobrescrita temporaria apenas para aquela geracao
- no gerador, assinatura 2 e assinatura 3 reutilizam o catalogo de assinaturas da secretaria e podem ser posicionadas livremente
- cada assinatura extra e desenhada como bloco completo, com imagem, linha e texto abaixo
- o gerador oferece quatro slots de selos extras para logos de programas, parceiros ou marcas adicionais no rodape
- quando assinatura 2 ou assinatura 3 estiver ativa, a instituicao e ocultada automaticamente para evitar sobreposicao no rodape
Formatos aceitos:
pngjpgjpegwebp
O gerador permite salvar layouts por secretaria para reduzir retrabalho em emissoes recorrentes.
Um layout salvo guarda:
- posicoes e tamanhos de logo, QR Code, assinaturas, instituicao e selos
- molde selecionado
- assets visuais selecionados
- textos das assinaturas
- preferencia de ocultar o titulo quando o molde ja traz essa informacao
Regras:
- layouts ficam vinculados a secretaria ativa
- operadores so listam e salvam layouts das secretarias vinculadas ao proprio usuario
- salvar com o mesmo nome atualiza o layout existente
- certificados emitidos tambem guardam um snapshot do layout usado, permitindo reabrir a edicao com a composicao original
- o backend continua armazenando o PNG final gerado pelo frontend; ele nao renderiza o certificado completo
Formatos suportados:
.xlsx.xls.csv
Colunas reconhecidas:
- obrigatoria:
nome - opcionais:
sobrenome,curso,data,carga_h,linha1,linha2,arquivo,email
Regras:
- o sistema detecta automaticamente a linha de cabecalho nas primeiras linhas da planilha
- se a planilha vier so com
nome, os campos do formulario sao usados como padrao - colunas extras desconhecidas sao ignoradas
- o sistema aceita cabecalhos como
nome,NOME,Nomee aliases conhecidos - para email, tambem reconhece aliases como
e-mail,e_mail,emailaluno,emaildoaluno,emailparticipante,correioecorreioeletronico - linhas totalmente vazias sao ignoradas
- linhas sem
NOMEsao ignoradas no lote - nomes com caracteres de lixo no inicio sao saneados sem perder acentos validos
- datas invalidas geram erro explicito por linha
- o lote respeita
CERTIFICADOS_MAX_BATCH_ITEMS
Eventos auditados incluem:
- login com sucesso
- login com falha
- login bloqueado
- certificado criado
- PNG enviado
- PNG acessado
- certificado excluido
- certificado restaurado
- email enviado ou com falha
- layout salvo ou atualizado
- acoes administrativas de usuarios, secretarias, moldes, logos, assinaturas e instituicoes
Observacoes:
- a auditoria e restrita a
admin_global - por padrao a listagem mostra apenas eventos relevantes; eventos de rotina como login com sucesso, troca de secretaria e acesso a PNG aparecem somente quando filtrados pelo tipo de evento
- o frontend interpreta os horarios vindos da API como UTC e exibe no horario local de
America/Sao_Paulo - a exclusao de certificado preserva o historico anterior de auditoria
- a tela de certificados permite exportar um relatorio CSV respeitando os filtros atuais da listagem
GET /healthGET /api/qrcode?texto=...GET /api/validar/{codigo}GET /validar/{codigo}GET /api/certificados/{codigo}/arquivo
GET /api/auth/mePOST /api/auth/loginPOST /api/auth/logoutPOST /api/auth/select-secretariaGET /api/certificadosGET /api/certificados/possiveis-duplicadosPOST /api/certificadosPOST /api/certificados/lotePOST /api/certificados/{codigo}/arquivoPOST /api/certificados/{codigo}/reenviar-emailDELETE /api/certificados/{codigo}/pendenteGET /api/templatesGET /api/secretaria-assetsGET /api/secretaria-assets/{id}/arquivoGET /api/layout-presetsPOST /api/layout-presetsPATCH /api/layout-presets/{preset_id}
GET /api/admin/templatesPOST /api/admin/templatesPATCH /api/admin/templates/{id}DELETE /api/admin/templates/{id}GET /api/admin/secretaria-assetsPOST /api/admin/secretaria-assetsPATCH /api/admin/secretaria-assets/{id}DELETE /api/admin/secretaria-assets/{id}
Observacao: para operadores, esses endpoints retornam e aceitam somente secretarias vinculadas ao usuario.
GET /api/admin/secretariasPOST /api/admin/secretariasPATCH /api/admin/secretarias/{id}DELETE /api/admin/secretarias/{id}GET /api/admin/usuariosPOST /api/admin/usuariosPATCH /api/admin/usuarios/{id}DELETE /api/admin/usuarios/{id}PATCH /api/admin/certificados/{codigo}DELETE /api/admin/certificados/{codigo}DELETE /api/admin/certificadosPOST /api/admin/certificados/{codigo}/restaurarDELETE /api/admin/certificados/lixeiraGET /api/admin/auditoriaGET /docs(somente admin autenticado, se habilitado)GET /openapi.json(somente admin autenticado, se habilitado)
index.html,frontend/css/*.css,frontend/js/*.js: interface web, login, geracao e administracaoapi/main.py: bootstrap da aplicacao FastAPIapi/common.py: configuracao compartilhada, helpers e dependenciasapi/routes_auth.py: autenticacao e troca de secretariaapi/routes_admin.py: usuarios, secretarias, auditoria e exclusoes administrativasapi/routes_certificates.py: emissao, listagem e arquivos dos certificadosapi/email_delivery.py: envio SMTP e registro de tentativas de emailapi/routes_public.py:health, QR Code e validacao publicaapi/routes_templates.py: moldes por secretariaapi/routes_secretaria_assets.py: logos, assinaturas e instituicoes por secretariaapi/routes_layout_presets.py: layouts salvos por secretariaapi/certificate_sequences.py: reserva atomica de codigosapi/models.py: modelos SQLAlchemyapi/schemas.py: contratos da APIapi/security.py: hashes e senhaapi/manage.py: comandos administrativosapi/migrations.py,api/alembic/: migracoes Alembicapi/templates/validacao.html: pagina publica de validacaoapi/static/style.css: estilo da pagina publicatests/: testes automatizados do backend
Use .env.example como base para o seu .env.
docker compose up -d --buildServicos locais:
- frontend:
http://localhost:28754 - API:
http://localhost:29180 - PostgreSQL:
localhost:25432
Volumes:
postgres_datacertificados_mediatemplates_media
Para producao:
- use
docker-compose.yml - mantenha o
.envreal fora do Git - deixe
AUTO_SEED_SECRETARIAS=true - deixe
AUTO_BOOTSTRAP_ADMIN=true - informe
BOOTSTRAP_ADMIN_USERNAMEeBOOTSTRAP_ADMIN_PASSWORD - configure SMTP por variaveis de ambiente quando o envio automatico estiver pronto para producao
Fluxo recomendado com dominio publico:
/-> frontend/api/*-> API/health-> API/validar/*-> API/static/*-> API
Com isso:
- frontend e API ficam no mesmo dominio
- o frontend usa a mesma origem automaticamente em producao
- o QR Code aponta para
PUBLIC_VALIDATION_BASE_URL
O envio usa uma configuracao SMTP global no .env/Portainer. Cada secretaria
mantem uma lista propria de emails de resposta por setor, e o operador escolhe
no Gerador qual deles sera usado como Reply-To do certificado.
Conta institucional prevista para envio: certificados@amargosa.ba.gov.br.
Variaveis principais:
SMTP_ENABLED=falseSMTP_HOST=smtp.office365.comSMTP_PORT=587SMTP_USERNAME=certificados@amargosa.ba.gov.brSMTP_PASSWORD=...SMTP_FROM_EMAIL=certificados@amargosa.ba.gov.brSMTP_FROM_NAME=Gerador de CertificadosSMTP_STARTTLS=trueSMTP_TIMEOUT_SECONDS=15EMAIL_LOGO_URL=https://certificados.amargosa.ba.gov.br/assets/email/logo-prefeitura.pngEMAIL_INSTITUTION_NAME=Prefeitura Municipal de Amargosa
Regras importantes:
- se
SMTP_ENABLED=false, nenhum envio e tentado - certificado sem email do participante nao dispara SMTP
- falha SMTP nao desfaz a emissao nem remove o PNG
- replies dos participantes vao para o email selecionado em
Responder para - o email HTML usa a logo publica quando
EMAIL_LOGO_URLestiver configurada - a assinatura mostra
EMAIL_INSTITUTION_NAMEe a origem da emissao - a tela
Administracao > Secretariaspermite cadastrar, ativar e definir o email de resposta padrao de cada secretaria - o campo
Setor exibido no e-mailaparece comoEmitido por: Setor exibido - Nome da secretaria - quando o setor for o email principal, o email mostra
Emitido por: SIGLA - Nome da secretaria - o certificado salva um snapshot do Reply-To escolhido para permitir reenvio manual consistente em uma etapa futura
- email e status de envio aparecem apenas nas telas internas, nunca na validacao publica
docker exec certificado-api python manage.py seed-secretarias
docker exec certificado-api python manage.py create-admin --nome "Administrador Local" --username admin --password "troque-esta-senha"Use:
AUTO_SEED_SECRETARIAS=trueAUTO_BOOTSTRAP_ADMIN=trueBOOTSTRAP_ADMIN_NAME=AdministradorBOOTSTRAP_ADMIN_USERNAME=adminBOOTSTRAP_ADMIN_PASSWORD=uma-senha-forte
Com essa opcao:
- as secretarias padrao sao criadas se estiverem ausentes
- o admin inicial e criado automaticamente
- se faltarem
BOOTSTRAP_ADMIN_USERNAMEouBOOTSTRAP_ADMIN_PASSWORD, a API falha no startup para evitar uma stack sem acesso administrativo
Principais:
APP_ENVCODE_PREFIXPUBLIC_VALIDATION_BASE_URLEMAIL_LOGO_URLEMAIL_INSTITUTION_NAMECERTIFICADOS_MAX_UPLOAD_BYTESCERTIFICADOS_MAX_BATCH_ITEMSTEMPLATES_MEDIA_DIRTEMPLATES_MAX_UPLOAD_BYTESSESSION_SECRETCERTIFICATE_HASH_SECRETSESSION_COOKIE_NAMESESSION_SAME_SITESESSION_HTTPS_ONLYSESSION_MAX_AGE_SECONDSENABLE_ADMIN_DOCSTRUST_PROXY_HEADERSLOGIN_MAX_ATTEMPTSLOGIN_WINDOW_SECONDSLOGIN_BLOCK_SECONDSCORS_ALLOW_ORIGINSAUTO_SEED_SECRETARIASAUTO_BOOTSTRAP_ADMINBOOTSTRAP_ADMIN_NAMEBOOTSTRAP_ADMIN_USERNAMEBOOTSTRAP_ADMIN_PASSWORD
Observacoes:
- em ambiente local, o frontend usa
localhost:29180automaticamente - em producao, se frontend e API estiverem no mesmo dominio, o frontend usa a mesma origem automaticamente
SESSION_SECRETeCERTIFICATE_HASH_SECRETdevem ser longos, exclusivos e estaveis- para Cloudflare Tunnel ou proxy confiavel, use
TRUST_PROXY_HEADERS=true ENABLE_ADMIN_DOCS=falsee o padrao recomendado para producao
Para teste local com dominio publico:
- mantenha um
.env.cloudflarefora do Git - aponte
PUBLIC_VALIDATION_BASE_URLpara o dominio do tunnel - inclua o dominio em
CORS_ALLOW_ORIGINS - rebuild a stack com:
docker compose --env-file .env.cloudflare up -d --buildExemplo de rotas do tunnel:
^/api->http://certificado-api:8000^/health$->http://certificado-api:8000^/validar->http://certificado-api:8000^/static->http://certificado-api:8000*->http://certificado-web:80
Cobertura atual:
- autenticacao e autorizacao
- criacao e validacao publica de certificados
- exclusao administrativa
- lixeira, restauracao e limpeza de certificados excluidos
- edicao administrativa e reenvio de email
- duplicidade
- lotes
- auditoria
- templates
- layouts salvos por secretaria
- logos, assinaturas e instituicoes por secretaria
- permissao de operador para gerenciar assets visuais apenas das secretarias vinculadas
- permissao de operador para gerenciar emails de resposta e layouts apenas no proprio escopo
- migracoes Alembic
- compatibilidade entre hash legado e HMAC
Execucao:
python3 -m pip install -r requirements-dev.txt
PYTHONPATH=api pytest -qO projeto usa Alembic para versionar o banco.
Comando manual:
cd api
python3 manage.py migrateCompatibilidade:
- bancos vazios sobem pela migration baseline
- bancos legados sem
alembic_versionsao adotados no startup - novas mudancas de schema devem entrar por revisao Alembic
- o backend nao gera o certificado completo, apenas guarda o PNG final enviado pelo frontend
- a composicao final ainda depende do renderizador em canvas no frontend
- layouts salvos cobrem posicoes de elementos visuais e textos de assinatura, mas nao substituem um editor livre completo de todos os textos do certificado
- reimpressao e segunda via usam o certificado ja salvo; a prevencao de duplicidade trabalha por alerta e confirmacao, nao por bloqueio absoluto
- o gerador foi reorganizado em:
Dados do certificadoTextos customizaveis
- a tela de auditoria saiu da aba
Certificadose ganhou aba propria - a tabela de certificados ficou mais compacta em telas pequenas, com detalhes secundarios embutidos na celula principal
Molde,LogoeAssinaturaforam simplificados em tres blocos mais enxutos, com sobrescrita temporaria apenas quando necessario- ajustes de logo, QR Code, assinaturas, selos e instituicao sao feitos por painel contextual ao clicar nos itens da previa
- layouts salvos permitem reaplicar composicoes por secretaria
Itens extraspermitem usar assinatura 2, assinatura 3 e ate quatro selos com posicionamento independente- modais do sistema substituem confirmacoes nativas do navegador em fluxos sensiveis
- lixeira e restauracao reduzem risco em exclusoes administrativas
- status e reenvio de email dao mais controle sobre comunicacao com participantes
- o foco visual de inputs, botoes e controles foi reforcado para melhorar navegacao por teclado
- relatorios operacionais
- politica de retencao/arquivamento da auditoria
- editor visual mais completo para textos e regras de layout por secretaria
- testes automatizados adicionais no frontend
- maior refinamento de UX mobile conforme uso real
O repositorio agora trabalha com um unico arquivo de stack:
- use
docker-compose.yml
Recomendacao:
- preencha as variaveis de ambiente da producao diretamente no Portainer
- mantenha o
.envde producao fora do Git - para Cloudflare Tunnel, prefira usar o IP privado do host quando houver instabilidade no DNS interno do Docker
Exemplo de rotas por IP:
^/api->http://10.75.2.16:29180^/health$->http://10.75.2.16:29180^/validar->http://10.75.2.16:29180^/static->http://10.75.2.16:29180*->http://10.75.2.16:28754