Aplicação fullstack de reservas de salas de reunião com visualização em calendário e notificações assíncronas por e-mail.
| Camada | Tecnologia |
|---|---|
| Backend | Python 3.12, FastAPI, SQLAlchemy 2 async, asyncpg |
| Banco | PostgreSQL 16 (btree_gist + EXCLUDE USING gist) |
| Worker | Celery 5 + Redis |
| E-mail (dev) | Mailpit |
| Frontend | React 18 + TypeScript + Vite + Tailwind CSS + FullCalendar |
| Package manager | uv (backend) · npm (frontend) |
| Containers | Docker Compose |
git clone https://github.com/JuanLadeira/webchalleng.git
cd webchalleng
cp .env.example .env # ajuste as variáveis se necessário
make up # sobe todos os serviçosAguarde os containers ficarem healthy e acesse:
| Serviço | URL |
|---|---|
| Frontend | http://localhost:5173 |
| API | http://localhost:8000 |
| Swagger UI | http://localhost:8000/docs |
| Mailpit (e-mails dev) | http://localhost:8025 |
Na primeira inicialização o entrypoint.sh cria automaticamente um usuário OWNER com as credenciais definidas no .env:
SEED_ADMIN_NAME=Admin
SEED_ADMIN_EMAIL=admin@example.com
SEED_ADMIN_PASSWORD=admin123make up # sobe todos os serviços em background
make down # derruba todos os serviços
make rebuild # rebuilda as imagens e sobe
make logs # acompanha logs do backend em tempo realmake migrate # aplica todas as migrations (alembic upgrade head)
make rollback # reverte a última migration (alembic downgrade -1)make test # roda todos os testes do backend
make test-cov # testes com relatório de cobertura
make test-unit # só testes unitários (sem banco)
make test-int # só testes de integração
make test-fe # roda testes do frontend (vitest)make lint # verifica erros de estilo com ruff
make fmt # formata o código automaticamente
make fmt-check # checa formatação sem alterar arquivosCopie .env.example para .env e ajuste conforme necessário.
| Variável | Padrão | Descrição |
|---|---|---|
POSTGRES_USER |
user |
Usuário do banco |
POSTGRES_PASSWORD |
pass |
Senha do banco |
POSTGRES_DB |
meetings |
Nome do banco |
POSTGRES_HOST |
db |
Host do banco (localhost fora do Docker) |
POSTGRES_PORT |
5432 |
Porta do banco |
REDIS_HOST |
redis |
Host do Redis |
REDIS_PORT |
6379 |
Porta do Redis |
JWT_SECRET_KEY |
change-me |
Chave secreta para tokens JWT |
JWT_ALGORITHM |
HS256 |
Algoritmo JWT |
JWT_EXPIRATION_MINUTES |
60 |
Validade do token em minutos |
SMTP_HOST |
mailpit |
Host SMTP |
SMTP_PORT |
1025 |
Porta SMTP |
SMTP_FROM |
noreply@meetingrooms.local |
Remetente dos e-mails |
SMTP_TLS |
false |
TLS no SMTP (true em produção) |
OUTBOX_POLL_INTERVAL_SECONDS |
10 |
Intervalo do worker de outbox |
SEED_ADMIN_NAME |
— | Nome do usuário admin criado no seed |
SEED_ADMIN_EMAIL |
— | E-mail do admin (seed idempotente) |
SEED_ADMIN_PASSWORD |
— | Senha do admin |
- Visualização semanal, mensal e diária (estilo Google Calendar)
- Clicar em um horário vazio → cria reserva com datas pré-preenchidas
- Clicar em um evento → modal com detalhes e opção de cancelamento
- Drag & Drop e resize de eventos diretamente no calendário
- Navegação direta do "Minhas Reservas" para o calendário destacando a reserva
- Painel ⚙️ de configurações (range de horas, tamanho de slot, horário comercial, finais de semana)
- Criação com título, horário e participantes (e-mail)
- Sala criada automaticamente — não é necessário gerenciar salas manualmente
- Recorrência: diária ou semanal, com número de ocorrências configurável
- Cor customizável por reserva (10 swatches + opção automática por hash do título)
- Cancelamento com soft delete
- Proteção contra conflito de horário em três camadas (ver Arquitetura)
- Dark mode completo com toggle ☀️/🌙 na sidebar
- Tema persiste por sessão via
sessionStorage
- Gerenciamento de salas: ativar/desativar
- Gerenciamento de usuários: alternar role MEMBER ↔ OWNER
Documentação completa em http://localhost:8000/docs (Swagger UI).
| Método | Rota | Descrição |
|---|---|---|
POST |
/api/auth/register |
Cria novo usuário |
POST |
/api/auth/login |
Login — retorna JWT |
GET |
/api/auth/me |
Dados do usuário autenticado |
| Método | Rota | Descrição |
|---|---|---|
GET |
/api/rooms |
Lista salas (param: active_only) |
GET |
/api/rooms/{id} |
Detalhe da sala |
POST |
/api/rooms |
Cria sala |
PATCH |
/api/rooms/{id} |
Atualiza sala |
DELETE |
/api/rooms/{id} |
Desativa sala (soft delete) |
GET |
/api/rooms/{id}/bookings |
Reservas ativas de uma sala |
| Método | Rota | Descrição |
|---|---|---|
POST |
/api/bookings |
Cria reserva (sala criada automaticamente; suporta recorrência) |
GET |
/api/bookings |
Lista reservas do usuário autenticado |
GET |
/api/bookings/{id} |
Detalhe da reserva |
PATCH |
/api/bookings/{id} |
Atualiza reserva |
DELETE |
/api/bookings/{id} |
Cancela reserva |
| Método | Rota | Descrição |
|---|---|---|
GET |
/api/admin/users |
Lista todos os usuários |
PATCH |
/api/admin/users/{id}/role |
Alterna role MEMBER ↔ OWNER |
O backend segue Clean Architecture com 4 camadas:
domain/ ← regras de negócio puras (sem framework)
application/ ← casos de uso + interfaces dos repositórios
infrastructure/ ← SQLAlchemy, bcrypt, SMTP, Celery
api/ ← routers FastAPI + dependencies
A regra central: dependências sempre apontam para dentro. O domain/ não importa nada externo.
Documentação detalhada:
docs/architecture.md— visão geral, fluxos e topologia de serviçosdocs/decisions.md— decisões técnicas relevantesdocs/roadmap.md— fases de desenvolvimento
Três camadas de proteção:
- Application layer — query de overlap antes de inserir, retorna HTTP 409 amigável
- EXCLUDE USING gist no PostgreSQL — garante atomicidade mesmo sob requisições concorrentes
- Transação atômica —
booking+outbox_eventinseridos juntos; qualquer falha reverte tudo
EXCLUDE USING gist (
room_id WITH =,
tstzrange(start_at, end_at, '[)') WITH &&
) WHERE (status = 'active')Fluxo completo e em produção:
- Reserva criada/alterada/cancelada → evento persistido em
outbox_eventsna mesma transação - Celery Beat dispara
process_pending_eventsa cada 10 segundos - Worker busca eventos com
SELECT FOR UPDATE SKIP LOCKED - Cada evento tem
idempotency_keyUUID único — reprocessamentos são ignorados - E-mail enviado e evento marcado como
processed
Em desenvolvimento, os e-mails são capturados pelo Mailpit em http://localhost:8025.
backend/tests/
├── conftest.py ← fixtures: engine, db_session, async_client
├── test_health.py ← smoke test
├── unit/
│ ├── test_booking_validators.py
│ └── test_domain_rules.py
└── integration/
├── test_auth_api.py
├── test_rooms_api.py
├── test_bookings_api.py
├── test_booking_overlap.py
└── test_outbox_creation.py
frontend/src/test/
├── setup.ts
├── LoginPage.test.tsx
├── BookingForm.test.tsx
└── RoomsPage.test.tsx
Cobertura frontend via @vitest/coverage-istanbul:
cd frontend && npm run test -- --coverage| Fase | Descrição | Status |
|---|---|---|
| 1 — Infraestrutura + CI | Docker Compose, Dockerfiles, GitHub Actions | ✅ Concluída |
| 2 — Domain + Models | Entidades, migrations, validators | ✅ Concluída |
| 3 — Autenticação | register, login, /me, JWT | ✅ Concluída |
| 4 — Salas | CRUD de salas | ✅ Concluída |
| 5 — Reservas | Overlap, outbox transacional, concorrência | ✅ Concluída |
| 6 — Outbox + Worker | Celery worker + SMTP (Mailpit) + retry + idempotência | ✅ Concluída |
| 7 — Frontend | React + FullCalendar + DnD + admin | ✅ Concluída |
| 8 — UX Polish | Booking modal DnD, cor de reserva, validação user-overlap | ✅ Concluída |
| 9 — Dark Mode | ThemeContext, toggle ☀️/🌙, suporte FullCalendar, coverage frontend | ✅ Concluída |
| 10 — Live Meetings | Videoconferência integrada (Daily.co) | 🔲 Planejada |