Este projeto faz parte do Módulo de Agendamento e Gestão de Tarefas, desenvolvido em uma estrutura monorepositório (monorepo) que integra o backend (Express.js) e o frontend (Next.js) dentro de um único repositório.
foco-total/
└── src/
├── backend/ → API com Express.js
└── frontend/ → Interface com Next.js
Um monorepo é um único repositório que contém múltiplos projetos relacionados.
No nosso caso:
- O backend fornece a API e a lógica de negócios.
- O frontend consome a API e exibe a interface ao usuário.
Ambos compartilham configurações, dependências e scripts comuns, o que facilita o desenvolvimento e a manutenção.
- Node.js (versão 18 ou superior)
- npm (versão 8 ou superior)
# Clone este repositório
git clone https://github.com/<seu-usuario>/<nome-do-repositorio>.git
# Acesse a pasta do projeto
cd foco-total
# Instale as dependências de todos os módulos (frontend + backend)
npm install
# Execute frontend e backend juntos
npm run devSe quiser rodar separadamente:
npm run dev:back # Inicia o backend
npm run dev:front # Inicia o frontendDentro do monorepo, cada módulo tem seu próprio package.json.
-
Para instalar dependências no backend:
npm install <pacote> --workspace src/backend
-
Para instalar dependências no frontend:
npm install <pacote> --workspace src/frontend
Isso garante que cada pacote fique isolado no local certo, sem bagunçar o projeto todo.
As variáveis de ambiente devem ficar em um arquivo .env na raiz do projeto.
Para referência, existe um modelo chamado .env.example.
Exemplo:
PORT=3000 DB_URL=postgres://user:senha@localhost:5432/focototal
NEXT_PUBLIC_API_URL=http://localhost:3000
| Comando | Descrição |
|---|---|
npm run dev:back |
Inicia o backend com Nodemon |
npm run dev:front |
Inicia o frontend com Next.js |
npm run dev |
Inicia ambos simultaneamente (logs coloridos) |
- Node.js + Express.js → API e rotas do backend
- Next.js + React → Interface moderna do frontend
- Nodemon → Atualização automática durante o desenvolvimento
- Concurrently → Execução paralela do front e do back
💡 Dica: Caso ocorra algum erro de porta em uso, verifique se nenhuma outra aplicação está rodando em
localhost:3000. O backend e o frontend podem usar portas diferentes conforme configurado no.env.
Observações importantes
Este projeto segue um padrão de branches e commits para manter a organização e facilitar o trabalho em equipe.
Antes de contribuir, leia atentamente as instruções abaixo.
Sempre crie uma branch nova a partir da main.
O formato deve ser:
-
Nova funcionalidade →
feat/nome-da-funcionalidade
Exemplo:feat/adicionar-tarefas -
Correção de bug →
fix/nome-do-bug
Exemplo:fix/contador-incorreto -
Refatoração (melhoria sem mudar regra de negócio) →
refactor/nome-da-refatoracao
Exemplo:refactor/estrutura-componentes -
Estilo/ajuste visual (CSS, Tailwind, layout) →
style/nome-do-ajuste
Exemplo:style/responsividade-lista -
Configuração (dependências, vite, eslint, etc.) →
chore/nome-da-config
Exemplo:chore/configurar-tailwind -
Documentação →
docs/nome-do-doc
Exemplo:docs/atualizar-readme
Os commits devem seguir o padrão:
-
feat:→ nova funcionalidade
Ex:feat: adicionar input de nova tarefa -
fix:→ correção de bug
Ex:fix: corrigir erro ao remover tarefa -
refactor:→ refatoração de código (sem mudar regra de negócio)
Ex:refactor: melhorar performance da lista -
style:→ mudanças visuais/estilo (não altera lógica)
Ex:style: ajustar espaçamento no header -
chore:→ alterações de configuração, build, dependências
Ex:chore: instalar react-icons -
docs:→ alterações na documentação
Ex:docs: adicionar instruções de instalação no readme
- Nunca commitar diretamente na
main. - Use nomes de branch curtos, descritivos e em inglês.
- Faça commits pequenos e frequentes (não deixe tudo em um único commit).
- Ao abrir um Pull Request, escreva um título claro e uma descrição objetiva.
📄 Licença Este projeto é de uso interno e faz parte do módulo de desenvolvimento do sistema Foco Total.
Esta seção foi atualizada para refletir as melhorias recentes: testes automatizados com Jest, sanitização de variáveis, validações centralizadas, logout, rate limiting condicionado e relacionamento Task ↔ Goal.
- Porta padrão atual:
3001(configurável viaPORT). - Health check:
GET /health→ retorna status da API. - Base da API (desenvolvimento):
http://localhost:3001/api.
Arquivo de referência: .env.example na raiz. Crie um .env na raiz (carregado pelo backend e pelo frontend quando necessário). Principais variáveis usadas pelo backend:
PORT=3001
DATABASE_URL=postgres://usuario:senha@localhost:5432/focototal
JWT_SECRET=changeme-dev
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX_REQUESTS=100
CORS_ORIGIN=http://localhost:3000
SWAGGER_ENABLED=true
SWAGGER_ROUTE=/docs
NODE_ENV=developmentVariáveis consumidas pelo frontend (Next.js):
NEXT_PUBLIC_API_URL=http://localhost:3001/api
NEXT_PUBLIC_DEV_MODE=trueEm ambiente de teste (NODE_ENV=test) o rate limiter é automaticamente desativado para evitar falsos negativos nos testes.
Os testes de API (controllers, middlewares, validações) rodam com Jest + Supertest.
Arquivos principais:
src/backend/jest.config.js→ Configuração geral (match dos testes, setup).src/backend/jest.setup.js→ Carrega variáveis de ambiente (.env.test → .env → fallback) e evita hard-code de segredos.- Diretório de testes:
src/backend/__tests__/(ex.:controllers/authController.test.js).
Execução:
cd src/backend
npm testPara depurar: defina JEST_DEBUG=1 no ambiente para logs adicionais (segredos são redigidos).
cd src/backend
npm install # caso não tenha feito na raiz
npm run dev # nodemon + reload
npm start # execução simplesSe o frontend chamar a API, assegure-se que NEXT_PUBLIC_API_URL aponta para a URL correta.
Scripts (executar dentro de src/backend):
npm run prisma:generate # gera cliente
npm run prisma:migrate # aplica migrations
npm run prisma:studio # visualização
npm run prisma:seed # (opcional) popular dadosRelacionamento atual Task ↔ Goal (trecho relevante):
model Task {
id String @id @default(uuid())
title String
status Status @default(PENDING)
userId String
goalId String? // opcional
goal Goal? @relation(fields: [goalId], references: [id], onDelete: SetNull)
}
model Goal {
id String @id @default(uuid())
title String
target Int
current Int @default(0)
userId String
tasks Task[]
}Pontos-chave:
- Task pode existir sem meta (
goalId = null). - Vincular: enviar
goalIdno POST/PATCH de Task. - Desvincular: PATCH com
goalId: null. - Exclusão de Goal mantém Tasks (SetNull).
- Filtros suportados em
/api/tasks:goalId,status,userId. - Listar tarefas de uma meta:
GET /api/goals/:goalId/tasks.
Fluxo principal:
- Registro →
POST /api/auth/register(cria usuário, valida campos). - Login →
POST /api/auth/login(retorna token JWT). - Rotas protegidas exigem header
Authorization: Bearer <token>. - Logout →
POST /api/auth/logout(invalidação lógica / limpeza lado cliente; backend pode opcionalmente manter lista de tokens revogados se necessário em evolução futura).
Validações aplicadas (exemplos):
- Email obrigatório e formato válido.
- Senha com comprimento mínimo definido.
- Impede registro duplicado (retorna status adequado).
- Middleware de autenticação rejeita token inválido ou expirado.
authMiddleware→ verifica e decodifica JWT, anexa usuário ao request.rateLimiter→ aplicado em produção/desenvolvimento; ignorado emNODE_ENV=test.validate→ esquemas centralizados emsrc/backend/validations/para entradas (ex.: login, registro, tarefas).errorHandler(dentro deapp.js) → garante resposta JSON consistente usandoerr.statusCode || 500.
Registro:
POST /api/auth/register
Content-Type: application/json
{
"name": "João Silva",
"email": "joao@test.com",
"password": "senha123"
}Login:
POST /api/auth/login
Content-Type: application/json
{
"email": "joao@test.com",
"password": "senha123"
}Logout:
POST /api/auth/logout
Authorization: Bearer <token>$response = Invoke-RestMethod -Uri 'http://localhost:3001/api/auth/register' -Method POST -ContentType 'application/json' -Body (@{name='João Silva';email='joao@test.com';password='senha123'} | ConvertTo-Json)
$response | ConvertTo-Json -Depth 5Configuração básica controlada por:
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX_REQUESTS=100Nos testes (NODE_ENV=test) o middleware retorna imediatamente (sem bloquear) para evitar falhas artificiais.
Cada teste utiliza Supertest para simular chamadas HTTP sem subir um servidor externo persistente. Cuidados aplicados:
- Limpeza de dados entre cenários (quando necessário).
- Uso de variáveis de ambiente carregadas dinamicamente.
- Evita dependência de logs ou timeouts artificiais.
- Nenhum segredo sensível hard-coded nos arquivos de teste.
- Fallback seguro para variáveis ausentes (placeholders não produtivos).
- Respostas de erro padronizadas (
{ message, statusCode }). - Validações centralizadas para reduzir duplicação em controllers.
- Adicionar seção mais detalhada de Swagger quando documentação estiver estável.
- Introduzir lista de tokens revogados para logout definitivo (se requisito surgir).
- Criar
.env.testdedicado para isolamento completo do banco de testes.