Aplicacao fullstack com backend FastAPI e frontend React + Vite + Tailwind, servida em same-origin em producao.
- Backend: FastAPI + SQLModel + Alembic
- Frontend: React 18 + TypeScript + Vite 5 + Tailwind v3
- IA: Google Gemini (
google-genaiSDK) - Banco: Postgres (Docker) / SQLite (local)
- Cache/rate limiting compartilhado: Redis
- E2E: Playwright
- CI: GitHub Actions (Branch protection estrita, pnpm lint, typecheck, pytest 70+, pip-audit para CVEs)
/hub/combate/revisoes/arquivos/evolucao/ia/config/redireciona para/hub- Rotas desconhecidas exibem pagina 404 tematica
pnpm install
python -m pip install -r backend/requirements.txt -r backend/requirements-dev.txt
pnpm dev:all- Frontend:
http://127.0.0.1:3000/hub - API:
http://localhost:8000 - Docs:
http://localhost:8000/docs
docker compose up -d --build redis db api webhook_worker- App:
http://localhost:8000/hub - Health:
http://localhost:8000/api/v1/health - Docs:
http://localhost:8000/docs
pnpm buildOutput: dist/public.
O frontend usa POST /api/v1/ai/hunter (sem chave no cliente). A chave Gemini fica apenas no backend (GEMINI_API_KEY).
- SDK:
google-genai(Client-based API) - Chave do usuario encriptada com Fernet no DB (mesma infra dos webhook secrets)
- Response mascara a chave (ex:
AIza****abcd) - Rate limit de burst aplicado por IP (camada compartilhada) e por usuario autenticado
- Quota diaria configuravel separada para guest e usuario autenticado
- Logs/metricas de IA evitam persistir prompt bruto por padrao
- A tela
Arquivoscarrega videos locais via seletor do navegador (input file). - Tambem e possivel carregar uma pasta inteira; o app importa apenas os arquivos de video encontrados.
- Para bibliotecas grandes, use
Conectar pasta (alto volume)(File System Access API) para catalogar por handle sem copiar todo blob para o IndexedDB. - O layout foi totalmente refatorado sob o sistema Glassmorphism, abandonando a antiga trilha lateral. Agora, os itens dividem-se magicamente em abas ("Conteúdo" e "Uploads") com exibições em grade (Cards translúcidos limpos).
- Controles disponiveis via Header elegante:
Selecionar Arquivos/Pastas, ordenação flexível e botões deLimpar biblioteca. - Em mobile, o grid e todo AppShell repensa o espaço em bottom-tabs nativos pra máxima legibilidade on-the-go.
- Os videos ficam salvos no IndexedDB local (
cmd8_local_media/videos). - Itens podem ficar em dois modos:
Local (Blob)ouConectado (Handle). - Meta operacional atual do app: ate
5000videos catalogados por navegador/perfil. - Em falta de espaco de quota para blobs, a importacao continua e os excedentes entram como
sem espacono resumo. - A grade de arquivos utiliza barra de paginação fixada no
Bottompara manter altíssima responsividade com colecoes grandes de imagens/vídeos. - Nao ha upload de midia para o backend nesta fase.
- Por seguranca do navegador, o app nao acessa caminho absoluto do sistema operacional.
- O modo
Conectar pastaexige navegador Chromium; mover/deletar arquivos no disco pode invalidar handles. - Upload avulso (arquivo unico) entra no grupo
Arquivos avulsos. - Para contabilizar RPG (XP/nivel/rank), ha mini login na propria pagina usando API cookie-auth.
- O ganho de XP por aula concluida e calculado pela API por duracao (
minutes = ceil(duracao/60)). - A conclusao e manual (botao
Concluir aula (+XP)), com dedupe por video (notes=video_completion::<videoRef>). - O
videoRefde dedupe agora usa identificador estavelv2(hash parcial SHA-256 quando possivel, com fallback por metadados). - O backend aplica dedupe server-side para
video_lesson, retornando200comxpEarned=0/goldEarned=0em repeticao. - Sem login, a biblioteca local funciona normalmente, mas sem contabilizar progressao RPG.
- O progresso RPG sincroniza com
/api/v1/me/statee atualiza HUD global. - O dedupe de aula concluida persiste por conta (via sessoes
mode=video_lesson). - Dados de blobs locais nao sincronizam entre dispositivos/navegadores.
- Se limpar os dados do navegador, a biblioteca local sera perdida.
SERVE_FRONTENDhabilita serving web no FastAPIFRONTEND_DIST_PATHcaminho do build (dist/publicpor padrao)CORS_ORIGINSlista explicita de origens permitidasWEBHOOK_OUTBOX_ENABLEDhabilita persistencia de eventos de webhook em outboxWEBHOOK_OUTBOX_SEND_ENABLEDativa processamento por worker (desativa envio legacy in-process)WEBHOOK_WORKER_*controla batch/poll/locks/retries/backoff do workerWEBHOOK_WORKER_HEARTBEAT_FILEeWEBHOOK_WORKER_HEARTBEAT_MAX_AGE_SECcontrolam healthcheck do workerWEBHOOK_DELIVERY_TIMEOUT_SECtimeout de entrega HTTP por webhookGEMINI_API_KEYchave de API do Gemini (fallback do sistema)AI_GUEST_DAILY_MAXeAI_GUEST_DAILY_WINDOW_SECquota diaria para guestsAI_USER_DAILY_MAXeAI_USER_DAILY_WINDOW_SECquota diaria para usuarios autenticadosAI_RATE_LIMIT_MAXeAI_RATE_LIMIT_WINDOW_SECburst de IA (IP compartilhado + usuario)WEBHOOK_SECRET_ENC_KEY_PREVlista (csv) de chaves antigas aceitas temporariamente durante rotacao
AUTO_CREATE_DB=falseSEED_DEV_DATA=falsePERSIST_REFRESH_TOKENS=trueJWT_SECRETforte (>=32)WEBHOOK_SECRET_ENC_KEYdefinidoCORS_ORIGINSvazio para same-origin (ou lista explicita para cross-origin)
- Auth por cookie com
HttpOnly,Secure(prod/staging),SameSiteconfiguravel (lax|strict|none) ePathcontrolado por config. SameSite=Noneso e aceito com cookieSecure=true(validacao de startup em prod/staging).- Protecao CSRF ativa para metodos mutaveis via double-submit cookie:
- endpoint
GET /api/v1/auth/csrfemite token - cliente envia
X-CSRF-Tokencom o mesmo valor do cookie - token e rotacionado em
signup/login/refresh/logout
- endpoint
- Em producao (
Secure=true), o cookie CSRF usa prefixo__Host-para mitigar injecao por subdominio. - CSRF e aplicado apenas quando ha cookies de sessao (nao bloqueia requests publicos/bearer sem cookie).
- Headers de seguranca aplicados por padrao:
Content-Security-Policy,X-Frame-Options,Referrer-Policy,Permissions-Policy,HSTS(HTTPS em prod). - CSP same-origin padrao:
default-src/script-src/connect-src 'self'script-src-attr 'none'(bloqueia handlers inline)style-src-attr 'none'(sem inline style em atributos)object-src 'none',frame-src 'none',frame-ancestors 'none'
robots.txtesitemap.xmlemclient/public/manifest.webmanifestpara PWA (installable)- Open Graph e Twitter Card em
index.html - Schema.org JSON-LD injetado em runtime por
client/src/lib/structuredData.ts(sem inline script no HTML) - Google Fonts com
display=swap - Web Vitals (CLS/INP/LCP) reportados ao backend
- Skip-to-content link
- ARIA attributes nos toggles (
role="switch",aria-checked) - Focus-visible ring global
- Font-size minimo 11px
- Logs JSON com
request_ide camposextraestruturados (incluiworker_id,outbox_id,correlation_idquando presentes). - Metricas Prometheus para IA (
ai_request_duration_seconds,ai_request_errors_total,ai_rate_limited_total). - Metricas Prometheus para webhook outbox (
webhook_outbox_sent_total,webhook_outbox_retry_total,webhook_outbox_dead_total,webhook_outbox_queue_depth). - Healthcheck da API:
GET /api/v1/health(DB + Redis). - Healthcheck dedicado do worker:
python backend/scripts/webhook_worker_healthcheck.py.
pnpm dev:clientpnpm dev:apipnpm dev:allpnpm buildpnpm e2epnpm check$env:ANALYZE="true"; pnpm build:client(gerastats.htmlcom mapa de bundle)PYTHONPATH=. python backend/scripts/run_webhook_worker.py --oncePYTHONPATH=. python backend/scripts/webhook_worker_healthcheck.pyPYTHONPATH=. python backend/scripts/requeue_webhook_outbox.py --all-dead --limit 100PYTHONPATH=. python backend/scripts/cleanup_webhook_outbox.py --sent-days 14 --shadow-days 7 --dead-days 30PYTHONPATH=. python backend/scripts/reencrypt_secrets.py(dry-run)PYTHONPATH=. python backend/scripts/reencrypt_secrets.py --apply
- Rollout gradual:
docs/webhook_outbox_rollout.md - Sunset do caminho legado (release futura):
docs/webhook_outbox_legacy_sunset.md - Dashboard SQL (Postgres):
backend/scripts/sql/webhook_outbox_dashboard.sql - Healthcheck dedicado do worker:
python backend/scripts/webhook_worker_healthcheck.py - Plano de execucao QA + Arquitetura (2 sprints):
docs/sprint-plan-qa-architecture.md
- Guia operacional:
docs/key_rotation.md - Re-encriptacao assistida:
python backend/scripts/reencrypt_secrets.py --apply - Politica recomendada: rotacao periodica com janela curta de keyring (
WEBHOOK_SECRET_ENC_KEY_PREV) + remocao da chave antiga apos re-encriptacao validada.
Executar dashboard SQL no compose local:
Get-Content backend/scripts/sql/webhook_outbox_dashboard.sql | docker compose exec -T db psql -U postgres -d study_leveling -f -dev_local.bat start|stop|status|open(Vite + API local)cmd8.bat start|stop|logs|resetdb|open|docs(Docker fullstack)
Dockerfileda raiz (multi-stage fullstack)render.yamlaponta para oDockerfileda raiz (servico web + worker dedicado)
Mais detalhes: DEPLOYMENT.md e CHECKLIST_RELEASE.md.
Attribution dos assets: docs/assets-attribution.md.