StudyCalendar backend: Aplicação web de calendário e planejamento de estudos
Find a file
Murilo Scolari 573152a3f0
Some checks are pending
CI / test (push) Waiting to run
Initial commit of StudyCalendar
2026-09-16 20:00:04 -03:00
.github/workflows Initial commit of StudyCalendar 2026-09-16 20:00:04 -03:00
static primeiro commit 2026-09-16 19:58:18 -03:00
templates primeiro commit 2026-09-16 19:58:18 -03:00
tests Initial commit of StudyCalendar 2026-09-16 20:00:04 -03:00
.env.example Initial commit of StudyCalendar 2026-09-16 20:00:04 -03:00
.gitignore Initial commit of StudyCalendar 2026-09-16 20:00:04 -03:00
app.py primeiro commit 2026-09-16 19:58:18 -03:00
database.py primeiro commit 2026-09-16 19:58:18 -03:00
ics_sync.py primeiro commit 2026-09-16 19:58:18 -03:00
README.md Initial commit of StudyCalendar 2026-09-16 20:00:04 -03:00
requirements-dev.in Initial commit of StudyCalendar 2026-09-16 20:00:04 -03:00
requirements-dev.txt Initial commit of StudyCalendar 2026-09-16 20:00:04 -03:00
requirements.in Initial commit of StudyCalendar 2026-09-16 20:00:04 -03:00
requirements.txt primeiro commit 2026-09-16 19:58:18 -03:00
run_dev.sh Initial commit of StudyCalendar 2026-09-16 20:00:04 -03:00
sample_feed.ics Initial commit of StudyCalendar 2026-09-16 20:00:04 -03:00
scheduler_process.py Initial commit of StudyCalendar 2026-09-16 20:00:04 -03:00
security.py Initial commit of StudyCalendar 2026-09-16 20:00:04 -03:00
test_feed.py Initial commit of StudyCalendar 2026-09-16 20:00:04 -03:00

StudyCalendar backend

Desenvolvimento

O modo de desenvolvimento é explícito e o servidor embutido escuta apenas loopback:

python -m venv .venv
.venv/bin/pip install -r requirements-dev.txt
APP_ENV=development DATABASE_PATH=/tmp/studycalendar-dev.db .venv/bin/python app.py

A chave aleatória gerada no modo development muda a cada reinício. Defina FLASK_SECRET_KEY se quiser preservar sessões.

Deploy seguro

  1. Copie as variáveis de .env.example para o gerenciador de segredos do serviço. O arquivo .env é ignorado; nunca grave a chave no repositório.
  2. Gere uma chave exclusiva com python -c "import secrets; print(secrets.token_urlsafe(48))".
  3. Defina TRUSTED_HOSTS com os nomes DNS públicos exatos, separados por vírgula. Não use curingas amplos.
  4. Termine TLS em um proxy reverso, mantenha SESSION_COOKIE_SECURE=true, ENABLE_HSTS=true e exponha somente HTTPS.
  5. Mantenha ALLOW_REGISTRATION=false e provisione usuários por um fluxo administrativo controlado, ou habilite cadastro somente quando houver uma decisão explícita de produto e proteção adicional no proxy/WAF.
  6. Execute o web app com gunicorn --workers 2 --bind 127.0.0.1:8000 app:app.
  7. Execute exatamente uma instância separada de python scheduler_process.py. Configure SCHEDULER_LOCK_PATH em diretório gravável apenas pelo serviço. O lock impede duplicação acidental no mesmo host; em múltiplos hosts, use somente uma unidade/replica do scheduler.
  8. Dê ao usuário do serviço acesso mínimo ao diretório de DATABASE_PATH e faça backups consistentes do SQLite.

Crie o primeiro usuário de forma interativa, sem expor a senha na linha de comando:

APP_ENV=production FLASK_SECRET_KEY='...' TRUSTED_HOSTS=calendario.seudominio.com DATABASE_PATH=/var/lib/studycalendar/calendario.db \
  .venv/bin/flask --app app create-user

Não inicie o scheduler em hooks do Gunicorn nem em cada worker. O processo web nunca inicia tarefas em importação. As rotas web apenas enfileiram sincronizações no SQLite; downloads e parsing são executados pelo scheduler dedicado para não ocupar workers HTTP.

Proxy reverso

TRUSTED_PROXY_COUNT=0 é o padrão seguro e ignora X-Forwarded-*. Altere para o número exato de proxies sob seu controle entre o cliente e Gunicorn. Nunca aceite tráfego direto no Gunicorn quando esse valor for maior que zero. O endereço resultante é usado pelo rate limit de autenticação.

O limitador é local e em memória: não precisa de Redis e é suficiente para uma implantação pequena, mas o limite é aplicado por worker/processo. Para escala horizontal, complemente-o no proxy/WAF sem remover os limites da aplicação.

CSRF e same-origin

O backend não habilita CORS. Navegadores same-origin existentes são validados por Origin/Referer. Clientes de API podem fazer GET /api/auth/csrf, manter o cookie de sessão e enviar o valor retornado no header X-CSRF-Token em POST, PUT, PATCH e DELETE.

Feeds ICS

HTTPS e porta 443 são obrigatórios por padrão. Cada destino e redirect tem DNS validado; endereços não globais IPv4/IPv6 são bloqueados. TLS sempre é verificado, redirects e corpo são limitados, proxies de ambiente são ignorados e o peer conectado é comparado obrigatoriamente ao DNS validado. URLs não são registradas em log.

Para permitir HTTP público em ambiente legado, defina ICS_HTTPS_ONLY=false, reconhecendo a perda de confidencialidade e integridade. Não há opção para desativar verificação TLS.

Banco legado

A antiga migração que atribuía registros sem proprietário ao usuário 1 foi removida. Bancos anteriores ao schema multiusuário devem ser migrados offline com uma associação de proprietário definida pelo operador; a aplicação não inventa ownership.

Testes

.venv/bin/pytest -q
.venv/bin/pip-audit --requirement requirements.txt

Os arquivos requirements*.txt são locks com hashes. Para atualizá-los intencionalmente:

.venv/bin/pip-compile --generate-hashes --output-file=requirements.txt requirements.in
.venv/bin/pip-compile --generate-hashes --output-file=requirements-dev.txt requirements-dev.in