Skip to content

Repository files navigation

FinControl

Aplicativo de controle de orçamento pessoal com rendas, despesas mensais, despesas futuras, rendas futuras e investimentos. Inclui gráficos, indicadores de saúde financeira, projeção de despesas futuras e histórico mensal.

Desenvolvido por Pedro Henrique de Almeida.

🌐 Em produção

A aplicação está rodando em:

https://fincontrol.pythonanywhere.com

Stack: Python 3.11 + Flask + SQLite, deploy em PythonAnywhere. Back e front servidos pelo mesmo processo (mesma origem, sem CORS aberto).

✨ Funcionalidades

  • Autenticação: cadastro e login com usuário/e-mail e senha (hash PBKDF2 + JWT).
  • Rendas ativas e passivas com categoria (Salário, Freelance, Aluguel…).
  • Rendas futuras (13º, PLR, restituição de IR…) com prazo em meses.
  • Despesas mensais com dia de vencimento e categoria.
  • Despesas futuras com prazo em meses (ex.: IPTU daqui a 6 meses).
  • Investimentos com rendimento anual (% a.a.) e cálculo de rendimento mensal.
  • Histórico mensal: salve um snapshot dos totais a cada mês e acompanhe a evolução da renda, despesas e saldo em um gráfico de linha.
  • Gráficos: pizza de distribuição de despesas + barra renda × despesas + linha de evolução temporal.
  • Saúde financeira: taxa de poupança, reserva de emergência (em meses), comprometimento da renda e classificação geral (Excelente → Crítico).
  • Projeção de despesas futuras com sugestão de reserva mensal.
  • Dados isolados por usuário — ninguém vê os dados de outra pessoa.
  • UI responsiva com Tailwind via CDN (sem build step).

🧱 Stack

Camada Tecnologia
Front-end HTML + CSS + JS (vanilla, Tailwind/Chart.js CDN)
Back-end Python 3.9+ / Flask 3 + flask-cors
Banco SQLite (arquivo local)
Auth JWT (PyJWT) + PBKDF2-SHA256 para hash de senha

Nenhuma etapa de build. pip install + python app.py e está rodando.


📂 Estrutura

Fin_Control/
├── index.html             # App principal (protegido)
├── login.html             # Tela de login
├── register.html          # Tela de cadastro
├── style.css              # Estilos (inclui tema das telas de auth)
├── script.js              # Lógica do app (consome a API)
├── api.js                 # Cliente HTTP + storage do token JWT
├── auth.js                # Comportamento das telas de login/registro
├── render.yaml            # Deploy 1-clique no Render.com
├── security_audit.py      # Auditoria automatizada (roda contra a URL em produção)
├── backend/
│   ├── app.py             # Entry point Flask + hardening (CORS, headers)
│   ├── rate_limit.py      # Flask-Limiter compartilhado
│   ├── database.py        # Schema SQLite + helpers de query
│   ├── auth.py            # Hash PBKDF2 + JWT + decorator @auth_required
│   ├── auth_routes.py     # /api/auth/register, /login, /me, /logout
│   ├── data_routes.py     # /api/data + CRUD por recurso + /api/history
│   ├── test_api.py        # Smoke test do backend
│   ├── test_e2e.py        # Fluxo end-to-end
│   ├── test_new_features.py  # future_incomes + monthly_history
│   ├── requirements.txt
│   ├── Dockerfile         # Imagem de produção (gunicorn)
│   ├── Procfile           # Heroku-style start command
│   ├── runtime.txt        # Versão do Python
│   └── .gitignore
└── README.md

🚀 Como rodar

1. Instalar dependências do back-end

cd backend
python3 -m venv .venv
source .venv/bin/activate       # Windows: .venv\Scripts\activate
pip install -r requirements.txt

2. Subir o servidor

python app.py

O servidor escuta em http://127.0.0.1:5000 por padrão. Para mudar: PORT=8080 python app.py. Para expor o backend em outra origem e permitir o front em outro host, defina window.API_BASE antes de api.js (ou ajuste BASE em api.js).

3. Abrir o front-end

4. (Opcional) Configurações de ambiente

Variável Padrão Descrição
PORT 5000 Porta HTTP
FLASK_DEBUG 0 1 para modo debug do Flask
JWT_SECRET dev-secret-change-me-in-production Chave HMAC dos tokens (troque em prod!)
FINCONTROL_DB backend/fincontrol.db Caminho do arquivo SQLite

🔐 API

Todas as rotas de dados exigem o header Authorization: Bearer <token>. Respostas em JSON. Erros vêm como {"error": "mensagem"}.

Autenticação

Método Rota Body Resposta
POST /api/auth/register {username, email, password} 201 {token, user}
POST /api/auth/login {username, password} ou {email, password} 200 {token, user}
GET /api/auth/me 200 {user}
POST /api/auth/logout 200 {ok: true}

Dados do usuário

Método Rota Descrição
GET /api/data Snapshot completo
GET /api/<resource> Lista itens
POST /api/<resource> Cria item
PUT /api/<resource>/<id> Atualiza item
DELETE /api/<resource>/<id> Remove item

Recursos: incomes, passive_incomes, monthly_expenses, future_expenses, future_incomes, investments.

Histórico mensal

Método Rota Descrição
GET /api/history Lista snapshots salvos (mais recente primeiro)
POST /api/history/snapshot Salva/atualiza um snapshot (recalcula totais se omitidos)
PUT /api/history/<id> Atualiza apenas as notas de um snapshot
DELETE /api/history/<id> Remove um snapshot

Exemplos:

// POST /api/monthly_expenses
{
  "name": "Aluguel",
  "value": 2200,
  "category": "Moradia",
  "due_day": 5
}

// POST /api/history/snapshot
{
  "month_year": "2026-07",        // opcional: padrão = mês atual
  "notes": "Mês de férias",        // opcional
  "totals": {                      // opcional: se omitido, o servidor recalcula
    "income": 8000,
    "passive": 500,
    "expenses": 2000,
    "investments": 10000
  }
}

☁️ Deploy

O projeto já vem pronto pra deploy em várias plataformas. Os arquivos de configuração vivem na raiz (render.yaml) e em backend/ (Dockerfile, Procfile, runtime.txt).

🥇 Render.com (recomendado — 1 clique)

Tem plano free com disco persistente de 1 GB (sobrevive a redeploys), e o app inteiro roda num único container.

Opção A — Blueprint (mais fácil):

  1. Crie uma conta em https://render.com
  2. New → Blueprint, aponte para https://github.com/0pedrohalmeida0/Fin_Control
  3. O Render lê o render.yaml e provisiona tudo automaticamente:
    • Cria o web service
    • Cria o disco persistente de 1 GB montado em /var/data
    • Gera um JWT_SECRET aleatório
    • Aponta o health check para /api/health
  4. Após ~3 min, o app está no ar em https://fincontrol.onrender.com

Opção B — Manual (Docker nativo):

  1. New → Web Service → aponte para o repo
  2. Runtime: Docker; Root Directory: backend
  3. Adicione as env vars: JWT_SECRET (gere um valor aleatório), FINCONTROL_DB=/var/data/fincontrol.db
  4. Em Disks, adicione um disco de 1 GB montado em /var/data
  5. Deploy.

⚠️ No plano free o serviço hiberna após 15 min de inatividade; a primeira requisição depois disso pode levar ~30 s para "acordar". Para uso pessoal, dá pra conviver. Pra ficar 24/7, plano starter US$ 7/mês.

🐳 Qualquer host com Docker (Railway, Fly.io, Koyeb, DigitalOcean…)

# build local
docker build -t fincontrol backend/
docker run -p 5000:5000 -e JWT_SECRET=$(openssl rand -hex 32) fincontrol

Ou simplesmente:

  1. Conecte o repo no Railway / Fly.io / Koyeb
  2. Apontar o Dockerfile em backend/
  3. Configurar um volume persistente para /app/data (ou o path que preferir)
  4. Setar JWT_SECRET no ambiente

🟣 Heroku-style (Heroku, Koyeb)

Tem Procfile + runtime.txt. Comando de start: gunicorn -w 1 -b 0.0.0.0:$PORT app:app.

Em Heroku o sistema de arquivos é efêmero — o SQLite zera a cada redeploy. Pra um app pessoal em Heroku, troque o SQLite por um Postgres gratuito do Heroku Postgres (modificações no database.py necessárias).

🔐 Variáveis de ambiente importantes

Variável Obrigatória? Descrição
PORT automática A plataforma define. Default 5000.
JWT_SECRET sim Chave HMAC dos tokens. Gere um valor forte!
FINCONTROL_DB opcional Caminho do SQLite. Default: backend/fincontrol.db.
FLASK_DEBUG opcional 0 em produção.

🧪 Testes

Dois scripts prontos, ambos usam apenas a stdlib (urllib):

cd backend
python3 test_api.py            # ~20 cenários: auth, validações, isolamento
python3 test_e2e.py            # fluxo UI-equivalente completo
python3 test_new_features.py   # future_incomes + monthly_history

🔒 Segurança

O app foi endurecido para uso público. Medidas implementadas:

Criptografia e autenticação

  • Senhas com PBKDF2-HMAC-SHA256, 200.000 iterações, salt aleatório de 16 bytes.
  • JWT com HS256 e expiração de 7 dias.
  • Senha: 6–128 caracteres; usuário: 3-32 chars (a-z A-Z 0-9 _ . -).

Hardening HTTP (em toda resposta)

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY (anti-clickjacking)
  • Strict-Transport-Security: max-age=31536000; includeSubDomains (HSTS)
  • Content-Security-Policy (CSP) restritivo, liberando só os CDNs usados pelo front
  • Referrer-Policy: strict-origin-when-cross-origin
  • Permissions-Policy desabilitando geolocation, câmera, microfone, etc.
  • Server header reescrito para FinControl (esconde stack)

CORS

  • Origens explícitas e restritas (configurável via ALLOWED_ORIGINS).
  • Default: https://fincontrol.pythonanywhere.com.
  • Em dev, defina ALLOWED_ORIGINS=http://localhost:5000,http://127.0.0.1:5000.

Rate limiting

  • /api/auth/login e /api/auth/register: 10 req/min e 30 req/hora por IP via Flask-Limiter.
  • Resposta 429 Too Many Requests quando excede.

Isolamento de dados

  • Cada query SQL filtra por user_id do JWT — um usuário nunca vê dados de outro.
  • Todos os endpoints de dados exigem Authorization: Bearer <token>; sem ele → 401.

Validação de entrada

  • Username regex, email regex, senha com limites.
  • Payloads grandes / strings longas são rejeitados com 400.
  • Queries 100% parametrizadas (sem concatenação) — imune a SQL Injection clássico.
  • Tokens alg=none e inválidos são rejeitados com 401.

Auditoria

  • security_audit.py (na raiz do repo) faz 80+ verificações automatizadas contra uma URL alvo.

Configuração obrigatória em produção

  • JWT_SECRET: gere um valor forte com python -c "import secrets; print(secrets.token_hex(32))". O default dev-secret-change-me-in-production é apenas para dev local.
  • ALLOWED_ORIGINS: defina o(s) domínio(s) do app, separados por vírgula.

About

Aplicação Web com armazenamento em local storage para controle de finanças pessoais.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages