Backend FastAPI da plataforma Cognix. A API centraliza autenticação com Firebase, leitura da base de questões, registro de tentativas, persistência de sessões de treino, score de perfil com ritmo recente, economia com moedas e avatares, e geração de resumos personalizados com IA.
Fluxo recomendado após clonar o projeto:
- Copie
.env.examplepara.env - Ajuste a
.envpara o seu ambiente - Se usar Firebase, coloque o arquivo de credenciais em
./secrets/firebase-service-account.json - Se a base de questões não existir no banco Docker, coloque o backup em
./backups/cognix.backup - Suba os containers:
docker compose up -d --build- Restaure a base:
.\scripts\restore_db.ps1O script copia ./backups/cognix.backup para o container cognix_db, executa pg_restore --clean --if-exists e valida a presença da tabela questions.
- Confira as tabelas no banco:
docker compose exec db psql -U postgres -d cognix -c "\dt"Saída esperada:
List of tables
Schema | Name | Type | Owner
--------+--------------------------+-------+----------
public | question_attempt_history | table | postgres
public | question_attempts | table | postgres
public | questions | table | postgres
public | training_session_history | table | postgres
public | training_sessions | table | postgres
public | training_summaries | table | postgres
public | training_summaries_user | table | postgres
public | user_avatar_inventory | table | postgres
public | user_coin_ledger | table | postgres
public | users | table | postgres
(10 rows)
- Valide a API:
http://localhost:8000/healthhttp://localhost:8000/docs
Configuração recomendada da .env para Docker:
POSTGRES_DB=cognix
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_PORT=5432
API_PORT=8000
FIREBASE_CREDENTIALS=./secrets/firebase-service-account.json
GEMINI_API_KEY=coloque_sua_chave_aqui
GEMINI_MODEL=gemini-3-flash-previewObservações:
- o
DATABASE_URLé montado automaticamente pelodocker composepara o container da API POSTGRES_PORTcontrola a porta exposta na sua máquina- dentro da rede Docker, a API sempre acessa o banco por
db:5432 - as tabelas internas são criadas no startup, mas a tabela
questionsprecisa vir do backup da base - as pastas
backups/esecrets/podem ir para o repositório vazias com.gitkeep, mas o conteúdo real não deve subir
Na VPS, o fluxo de produção usa estes arquivos:
.env.productiondocker-compose.prod.ymlDockerfile.proddeploy.sh
Comando base da stack em produção:
docker compose --env-file .env.production -f docker-compose.prod.yml ...Esse formato é importante porque o docker compose precisa ler a .env.production antes de montar os serviços.
Subir ou recriar a stack inteira:
docker compose --env-file .env.production -f docker-compose.prod.yml up -d --buildUse esse comando quando você mudar algo da infraestrutura, como:
docker-compose.prod.ymlDockerfile.prod- serviço do banco
- rede, volumes ou estrutura da stack
Subir ou recriar somente a API:
docker compose --env-file .env.production -f docker-compose.prod.yml up -d --build apiEsse é o comando padrão do dia a dia. Use quando você mudar:
- código Python em
app/ - rotas
- serviços
- regras de negócio
- dependências da API
Ver status dos containers:
docker compose --env-file .env.production -f docker-compose.prod.yml psVer logs da API:
docker compose --env-file .env.production -f docker-compose.prod.yml logs -f apiVer logs do banco:
docker compose --env-file .env.production -f docker-compose.prod.yml logs -f dbReiniciar apenas a API:
docker compose --env-file .env.production -f docker-compose.prod.yml restart apiParar a stack sem apagar dados:
docker compose --env-file .env.production -f docker-compose.prod.yml downAtenção:
downpara os containers, mas preserva os volumesdown -vremove volumes; em produção isso pode apagar dados
Fluxo recomendado sempre que você publicar uma nova versão no GitHub:
cd /home/cloudpanel/apps/cognix_api
git pull
docker compose --env-file .env.production -f docker-compose.prod.yml up -d --build api
docker compose --env-file .env.production -f docker-compose.prod.yml ps
curl https://sua-api.com/healthO que cada etapa faz:
git pull: baixa a versão mais nova do repositório.up -d --build api: rebuilda e recria só a API.ps: mostra se os containers ficaram saudáveis.curl: valida se o endpoint/healthcontinua respondendo.
O arquivo deploy.sh existe para você não precisar decorar todos os comandos do deploy.
Uso padrão:
bash deploy.shEsse comando faz cinco coisas:
- entra na pasta do projeto
- roda
git pull --ff-only - atualiza somente a API
- mostra o status dos containers
- testa
http://127.0.0.1:8000/health
Quando você quiser recriar a stack inteira:
bash deploy.sh fullDiferença entre os modos:
bash deploy.sh: atualiza só a API; esse é o modo recomendado para o dia a diabash deploy.sh full: recria a stack inteira; use quando mexer em infraestrutura
Primeiro uso na VPS:
cd /home/cloudpanel/apps/cognix_api
chmod +x deploy.sh
./deploy.shSe preferir, você também pode rodar assim:
bash deploy.shRegra simples para nunca se confundir:
- mudou código da API:
bash deploy.sh - mudou compose, Dockerfile ou algo da stack:
bash deploy.sh full - quer investigar problema: veja
ps,logs -f apie o endpoint/health
- Python 3.10+ no código
- imagem Docker
python:3.13-slim - FastAPI
- SQLAlchemy 2
- PostgreSQL
- Firebase Admin SDK
- Gemini API via HTTP
- validação de
Firebase ID Tokenem rotas protegidas - sincronização automática do usuário Firebase para a tabela interna
users - listagem e filtro de questões por disciplina, subcategoria, ano e busca textual
- remoção automática do campo sensível
gabaritonas respostas de questões - registro de tentativas com snapshot da última resposta e histórico completo append-only
- recompensa em moedas apenas na primeira resposta de cada
usuário + questão - inventário de avatares e seleção de avatar equipado no perfil
- score de perfil com breakdown, nível, consistência e
recent_index - sessões de treino persistidas por
usuário + disciplina + subcategoria - histórico de simulados concluídos para score, overview e reabertura de resultados
- resumos base por subcategoria e resumos personalizados por usuário
- geração opcional de mapas mentais personalizados com Gemini
- persistência e serialização de datas em UTC
No startup, a API garante a criação das tabelas internas abaixo:
usersquestion_attemptsquestion_attempt_historytraining_sessionstraining_session_historytraining_summariestraining_summaries_useruser_coin_ledgeruser_avatar_inventory
Os nomes reais podem ser alterados por variáveis de ambiente.
Tabela de usuários internos sincronizada a partir do token do Firebase.
Campos relevantes:
firebase_uidemaildisplay_nameprovidercoins_half_unitsequipped_avatar_seedcreated_atupdated_at
Tabela snapshot usada para manter a última tentativa conhecida por user_id + question_id.
Campos principais:
user_idfirebase_uidquestion_idselected_letteris_correctdisciplinesubcategoryanswered_at
Existe unicidade por:
user_id + question_id
Tabela append-only usada para registrar todas as respostas do usuário ao longo do tempo.
Campos principais:
user_idfirebase_uidquestion_idselected_letteris_correctdisciplinesubcategoryanswered_at
Essa tabela é a base para:
active_days_last_30- contagem real de tentativas
- precisão histórica
- insights por subcategoria
- estatísticas dos resumos personalizados
- cálculo de
scoreerecent_index
Tabela usada para persistir o estado atual do simulado.
Campos principais:
user_idfirebase_uiddisciplinesubcategorystate_jsoncreated_atupdated_at
Existe unicidade por:
user_id + discipline + subcategory
Isso permite:
- continuar um simulado em andamento
- compartilhar esse estado entre dispositivos com a mesma conta
- manter um snapshot único por subcategoria para cada usuário
Tabela append-only usada para registrar cada simulado concluído.
Campos principais:
user_idfirebase_uiddisciplinesubcategorysession_keytotal_questionsanswered_questionscorrect_answerswrong_answerselapsed_secondscompleted_at
Essa tabela é a base para:
completed_sessions- cards e overview de ritmo do treino
- reabertura consistente da tela de resultados
- métricas do score ligadas a simulados concluídos
Observação:
- um registro em
training_session_historysó é criado quandostate.completed == trueno payload salvo em/sessions
Tabela de resumo base por discipline + subcategory.
Campos principais:
disciplinesubcategorypayload_jsoncreated_atupdated_at
Tabela de resumo personalizado por user_id + discipline + subcategory.
Campos principais:
user_idfirebase_uiddisciplinesubcategorypayload_jsoncreated_atupdated_at
Tabela append-only do extrato de moedas do usuário.
Campos principais:
user_idfirebase_uidreasondelta_half_unitsbalance_after_half_unitsquestion_idavatar_seedcreated_at
Hoje os motivos gravados pelo backend incluem:
question_answer_rewardavatar_purchase
Tabela de inventário de avatares desbloqueados ou comprados.
Campos principais:
user_idfirebase_uidavatar_seedacquired_viacost_half_unitscreated_atupdated_at
Existe unicidade por:
user_id + avatar_seed
O .env.example mantém as variáveis mínimas para subir o ambiente Docker:
POSTGRES_DBPOSTGRES_USERPOSTGRES_PASSWORDPOSTGRES_PORTAPI_PORTFIREBASE_CREDENTIALSGEMINI_API_KEYGEMINI_MODEL
Além disso, o backend expõe opções avançadas em app/core/config.py, caso você queira sobrescrever nomes de tabelas ou outros defaults:
DATABASE_URLQUESTION_TABLEUSERS_TABLEATTEMPTS_TABLEATTEMPT_HISTORY_TABLESESSIONS_TABLESESSION_HISTORY_TABLESUMMARIES_TABLEUSER_SUMMARIES_TABLEUSER_COIN_LEDGER_TABLEUSER_AVATAR_INVENTORY_TABLEALLOWED_ORIGINSFIREBASE_CREDENTIALSGEMINI_API_KEYGEMINI_MODEL
Defaults atuais no código:
- tabela de questões:
questions - usuários internos:
users - snapshot de tentativas:
question_attempts - histórico de tentativas:
question_attempt_history - sessões atuais:
training_sessions - histórico de sessões concluídas:
training_session_history - resumos globais:
training_summaries - resumos por usuário:
training_summaries_user - extrato de moedas:
user_coin_ledger - inventário de avatares:
user_avatar_inventory - CORS:
["*"] GEMINI_MODEL:gemini-3-flash-preview
Exemplo de ALLOWED_ORIGINS:
ALLOWED_ORIGINS=["http://localhost:3000","http://localhost:8080"]Todas as rotas da API exigem autenticação, exceto GET /health.
Rotas protegidas esperam:
Authorization: Bearer <firebase_id_token>Fluxo:
- a API valida o token do Firebase
- extrai
uid,email,nameeprovider - sincroniza o usuário na tabela interna
- usa o
user_idinterno nas tabelas relacionais
Observação:
- hoje não existe checagem adicional de papel/perfil por rota; o controle atual é apenas por token válido
POST /users/sync: valida o token e devolve o usuário interno sincronizadoGET /users/profile: retorna score, breakdowns, progresso geral, moedas, avatar equipado, avatares possuídos e catálogo da lojaPOST /users/avatar/select: equipa um avatar já possuído ou compra/equipa um novo avatar com base emavatar_seed
GET /questions: lista questões comlimit,offset,subject,subcategory,year,searcheinclude_totalGET /questions/disciplines: lista disciplinas distintas da baseGET /questions/subcategories: lista subcategorias e total por subcategoria, com filtro opcional por disciplinaGET /questions/by_ids: busca por ids em ordem preservada viaids=1,2,3GET /questions/{question_id}: retorna uma questão específica
Observação:
- o backend remove o campo
gabaritode todas as respostas de questões
POST /attempts: registra a tentativa no histórico, atualiza o snapshot da última resposta e retornais_correct,correct_lettere estado de moedas
Observação:
- moedas são concedidas apenas quando a questão ainda não possui tentativa anterior para aquele usuário
POST /sessions: salva ou atualiza o estado do treino paradiscipline + subcategoryGET /sessions: recupera o estado salvo de uma sessãoGET /sessions/overview: combina sessões em andamento com histórico de concluídasDELETE /sessions: remove o snapshot atual da sessão
GET /summaries: retorna o resumo base da subcategoria e cria um fallback padrão se ele não existirGET /summaries/personal: retorna resumo personalizado do usuário; se não houver sessão concluída, devolve um payload bloqueadoGET /summaries/progress: retorna progresso da subcategoria com base em tentativas e total de questõesPOST /summaries/auto_generate: força a geração do resumo personalizado via GeminiPOST /summaries: cria ou atualiza manualmente o resumo basePOST /summaries/bootstrap: cria resumos base padrão para todos os pares distintos dediscipline + subcategory
Observações:
GET /summaries/personalsó tenta gerar com IA quandoauto_generate=trueeGEMINI_API_KEYestá configuradaPOST /summaries/auto_generateretorna409se o usuário ainda não concluiu um simulado da subcategoria- se o Gemini não estiver configurado,
GET /summaries/personalfaz fallback para o resumo base, masPOST /summaries/auto_generatefalha com500
A API usa utilitários centralizados em app/core/datetime_utils.py.
Padrão atual:
- persistência em UTC
- colunas internas com
DateTime(timezone=True) - normalização com
ensure_utc(...)antes de comparações sensíveis - serialização consistente para respostas JSON com
isoformat()
Isso evita deslocamentos de horário entre banco, backend e app Flutter.
Para detalhes das rotas, payloads e respostas, use o Swagger da aplicação:
http://localhost:8000/docshttp://localhost:8000/redoc
Se voce estiver atualizando a API em producao, pense assim:
- faz a mudanca no seu computador
- sobe para o GitHub
- entra na VPS
- atualiza o repositorio
- recria a API
- testa o
/health
O comando base da producao e sempre este:
docker compose --env-file .env.production -f docker-compose.prod.yml ...Atualizar so a API:
docker compose --env-file .env.production -f docker-compose.prod.yml up -d --build apiUse esse comando no dia a dia, quando voce mudar:
- codigo Python
- rotas
- servicos
- regras de negocio
- dependencias da API
Atualizar a stack inteira:
docker compose --env-file .env.production -f docker-compose.prod.yml up -d --buildUse esse comando quando voce mudar:
docker-compose.prod.ymlDockerfile.prod- configuracao do banco
- algo da infraestrutura da stack
Ver status:
docker compose --env-file .env.production -f docker-compose.prod.yml psVer logs da API:
docker compose --env-file .env.production -f docker-compose.prod.yml logs -f apiVer logs do banco:
docker compose --env-file .env.production -f docker-compose.prod.yml logs -f dbReiniciar so a API:
docker compose --env-file .env.production -f docker-compose.prod.yml restart apiParar tudo sem apagar dados:
docker compose --env-file .env.production -f docker-compose.prod.yml downAtencao:
downpara os containers, mas mantem os volumesdown -vremove volumes; em producao isso pode apagar dados
Quando voce publicar uma nova versao:
cd /home/cloudpanel/apps/cognix_api
git pull
docker compose --env-file .env.production -f docker-compose.prod.yml up -d --build api
docker compose --env-file .env.production -f docker-compose.prod.yml ps
curl https://sua-api.com/healthO arquivo deploy.sh existe para voce nao precisar decorar tudo.
Uso normal:
bash deploy.shEsse comando:
- entra na pasta do projeto
- roda
git pull --ff-only - atualiza somente a API
- mostra o status dos containers
- espera a API terminar de subir
- testa
http://127.0.0.1:8000/health
Quando voce quiser recriar a stack inteira:
bash deploy.sh fullSe a API estiver demorando mais que o normal para subir, voce pode aumentar a espera:
HEALTHCHECK_RETRIES=30 HEALTHCHECK_INTERVAL_SECONDS=2 bash deploy.shPrimeiro uso na VPS:
cd /home/cloudpanel/apps/cognix_api
chmod +x deploy.sh
./deploy.shRegra simples para nunca se confundir:
- mudou codigo da API:
bash deploy.sh - mudou compose, Dockerfile ou algo da stack:
bash deploy.sh full - o script agora espera a API subir antes de validar o
/health - quer investigar problema: veja
ps,logs -f apie o endpoint/health
Comandos úteis:
docker compose up --build
docker compose logs -f api
docker compose logs -f db
docker compose down
docker compose down -v
python -m unittest discover -s testsCobertura atual de testes unitários incluída no repositório:
- helpers da economia (
coins_from_half_unitse composição da loja de avatares) - cálculo de
recent_index - estabilidade do score quando apenas o índice recente varia