Um mini SaaS de agendamento + execução + histórico de rotinas HTTP — pensado como projeto de portfólio para demonstrar, na prática, competências de back-end, dados/SQL, automação/scheduler, cloud/deploy e integrações
-
App: https://opspulse-self.vercel.app/ Demo account
-
Email: demo@opspulse.app
-
Senha: opspulse
Objetivo do projeto: resolver um problema real de operação — ter um lugar simples para agendar, executar e acompanhar rotinas HTTP (pings, webhooks e checagens), com histórico e visibilidade do que deu certo/errado.
Ao mesmo tempo, o OpsPulse serve como um “laboratório prático” para eu aplicar na prática os conceitos da trilha AZ-900, principalmente:
- computação em nuvem e modelo de responsabilidade compartilhada
- serviços gerenciados e configuração por variáveis de ambiente
- observabilidade por logs e rastreabilidade de execuções
- autenticação/autorização em aplicações modernas
- deploy e separação clara entre front-end e back-end
A prioridade do projeto é ser simples, funcional e barato de manter, usando free tiers sempre que possível.
Pense nele como um “cron + monitor + histórico” minimalista.
Você consegue:
- Criar conta e logar (Supabase Auth)
- Cadastrar rotinas HTTP (URL + método + headers seguros)
- Definir frequência por
interval_minutes(MVP sem cron string) - Rodar manualmente (teste rápido)
- Rodar automaticamente (scheduler via Azure Timer Trigger)
- Ver histórico de execuções (status, HTTP status, duração, timestamps)
- Ver “saúde” do backend via
/health
Back-end
- Azure Functions (Python)
- Endpoints REST
- Timer Trigger como scheduler
- Configuração via App Settings (env vars)
- Logs como fonte de verdade de produção
Banco + Auth
- Supabase (PostgreSQL + Auth)
- Front autentica direto no Supabase
- Backend valida o usuário com token do Supabase
- Banco “de verdade”: constraints, índices e consistência
Front-end
- React + Vite + TypeScript
- Tailwind + shadcn/ui (UI moderna, tema claro)
- Deploy no Vercel
- SPA routing configurado (regras de rewrite)
- Agendamento por
interval_minutes(>= 5) no MVP - Busca rotinas “devidas” por
next_run_at - Proteção de concorrência com lock (
lock_until,locked_by) MAX_CONCURRENCYpara limitar execuções em paralelo- Timeout controlado na execução HTTP
O scheduler roda a cada 5 minutos (Timer Trigger). Na prática, o trigger pode acordar alguns segundos “antes do minuto cravado”. Para não perder o slot:
- query considera um pequeno slack (
DUE_SLACK_SECONDS, default 3s) - cálculo do próximo
next_run_até ancorado no slot devido, evitando drift - timestamps padronizados com truncagem para minuto (segundos/micros = 0)
- Segredos não vão para o banco
auth_mode = NONE | SECRET_REFsecret_refaponta para env var no backend (Azure App Settings)- Backend bloqueia headers sensíveis em
headers_json(ex.:Authorization,Cookie,X-API-Key) - Backend usa
SERVICE_ROLEapenas no servidor (nunca no front)
O projeto registra e incorpora problemas reais que surgem no caminho:
- deploy separado (Vercel vs Azure)
- SPA rewrite (refresh em rota)
- diferenças de tipos/serialização (Pydantic/URL)
- variáveis de ambiente e tokens (CRLF, etc.)
- consistência de enums/constraints no banco
Tabelas:
workspaces:id,owner_id,name,created_atroutines:workspace_id,name,kind,interval_minutes,next_run_at,last_run_at,is_active,endpoint_url,http_method,headers_json,auth_mode,secret_ref,lock_until,locked_by,created_at,updated_atroutine_runs:routine_id,triggered_by (MANUAL|SCHEDULE),status (SUCCESS|FAIL),http_status,duration_ms,error_message,started_at,finished_at,created_at- campos futuros previstos:
ai_summary,ai_confidence
- campos futuros previstos:
Índices principais (foco scheduler e histórico):
idx_routines_scheduler (is_active, next_run_at)idx_runs_routine_created (routine_id, created_at desc)
Mais detalhes: docs/DB_OVERVIEW.md
api/→ Azure Functions (Python)web/→ React + Vite + TSdocs/→ documentação técnica (DB, decisões, etc.)tools/→ scripts utilitários (ex.: token/automação local)
- Node 18+ (recomendado 20+)
- pnpm
- Python 3.11+ (ou 3.10+)
- Azure Functions Core Tools v4
Crie um projeto no Supabase e obtenha:
SUPABASE_URLSUPABASE_ANON_KEYSUPABASE_SERVICE_ROLE_KEY
Crie as tabelas conforme docs/DB_OVERVIEW.md (ou via SQL equivalente).
Dentro de api/, crie local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "python",
"SUPABASE_URL": "https://xxxx.supabase.co",
"SUPABASE_ANON_KEY": "xxxxx",
"SUPABASE_SERVICE_ROLE_KEY": "xxxxx",
"HTTP_TIMEOUT_SECONDS": "8",
"MAX_CONCURRENCY": "5",
"LOCK_LEASE_SECONDS": "45",
"SCHEDULER_BATCH_LIMIT": "20",
"DUE_SLACK_SECONDS": "3"
}
}Instalar deps e rodar:
cd api
python -m venv .venv
# ativar venv
pip install -r requirements.txt
func startEndpoints principais:
GET /api/healthPOST /api/routinesGET /api/routinesGET /api/routines/{id}PATCH /api/routines/{id}DELETE /api/routines/{id}POST /api/routines/{id}/runGET /api/routines/{id}/runs
Dentro de web/, crie .env.local:
VITE_SUPABASE_URL=https://xxxx.supabase.co
VITE_SUPABASE_ANON_KEY=xxxxx
VITE_API_BASE_URL=http://localhost:7071/apiRodar:
cd web
pnpm install
pnpm dev- configurar App Settings com as mesmas env vars do
local.settings.json(sem commitar secrets) SUPABASE_SERVICE_ROLE_KEYfica somente no backend
- setar
VITE_SUPABASE_URL,VITE_SUPABASE_ANON_KEY,VITE_API_BASE_URL - manter
vercel.jsoncom rewrite para SPA routing
- Supabase: free tier (DB + Auth)
- Vercel: free tier para front
- Azure Functions: free grant/consumo
- Cron string (opcional) + timezone handling
- RLS no Supabase (polimento de segurança real)
- Página de observabilidade (filtros, agregações, alertas)
- Integrações (webhook, Slack/Email) para falhas
- “AI Run Summary” para falhas (enviando contexto mínimo sanitizado)
MIT (ver LICENSE)