Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

36 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

🔧 Oficina Mecânica

Sistema full-stack para gestão de oficina — API serverless + interface web.
Full-stack mechanic-shop management system — serverless API + web UI.

CI Code coverage Tested with Vitest Vercel deploy MIT License

Dashboard do OFICINA com clientes, veículos e ordens

🌐 Demo ao vivo


💡 Sobre · About

Sistema completo para gestão de oficina mecânica: cadastro de clientes, controle de veículos, abertura e acompanhamento de ordens de serviço, autenticação, upload de foto de veículo e geração de PDF da OS.

Complete mechanic-shop management system: customer registration, vehicle tracking, work-order lifecycle, authentication, vehicle photo upload, and work-order PDF generation.

✨ Funcionalidades · Features

  • 🔐 Autenticação JWT — cadastro, login e sessão do usuário
  • 👤 Clientes — cadastro, listagem e busca por usuário
  • 🚗 Veículos — vinculados a clientes, com tipo, cor e foto
  • 📋 Ordens de serviço — abertura, atualização de status e histórico
  • 🧾 PDF da OS — geração de documento para ordem de serviço
  • 📊 Dashboard — métricas e visão geral da operação
  • 🏢 Multi-tenant — múltiplas oficinas por conta, com isolamento dos dados
  • 👥 Equipe e RBAC — convites e papéis de proprietário, gerente e mecânico
  • 🧪 Testes unitários — Vitest + Testing Library + cobertura

🛠️ Stack

Backend / API

  • Vercel Serverless Functions em Node.js + TypeScript
  • Prisma ORM + PostgreSQL (Neon ou outro Postgres compatível)
  • JWT + bcryptjs para autenticação
  • Vercel Blob para upload de fotos
  • Zod para validação de entrada

Frontend

  • React 18 + TypeScript 5.5
  • Vite 5 + Tailwind CSS 3
  • Componentes UI com Radix + utilitários próprios
  • Lucide React para ícones

Qualidade

  • ESLint
  • Vitest + @testing-library/react
  • GitHub Actions + Codecov

🚀 Como rodar localmente

Pré-requisitos:

  • Node.js 20+
  • Um banco PostgreSQL (Neon, Supabase, Docker local etc.)
  • Vercel CLI se quiser rodar as functions localmente
git clone https://github.com/guuszz/OFICINA.git
cd OFICINA
npm install
cp .env.example .env

Preencha o .env com suas credenciais e rode:

npm run db:generate
npm run db:migrate
npm run db:seed # opcional: cria/restaura o cenário demonstrativo

# recomendado: sobe frontend + API serverless pelo Vercel Dev
npm run dev:full

Acesse a URL exibida pelo Vercel CLI, normalmente http://localhost:3000.

Se quiser rodar somente a interface Vite, sem API serverless:

npm run dev

🔐 Variáveis de ambiente

Veja o arquivo .env.example.

Variável Obrigatória Descrição
DATABASE_URL String de conexão PostgreSQL usada pelo Prisma
JWT_SECRET Segredo longo para assinar tokens JWT
ALLOWED_ORIGINS ⚠️ Origens CORS adicionais, separadas por vírgula
BLOB_READ_WRITE_TOKEN ⚠️ Token do Vercel Blob, necessário para upload/remover fotos
LOG_LEVEL ⚠️ Use silent apenas para desativar logs estruturados em testes
UPSTASH_REDIS_REST_URL ✅ produção URL HTTPS do Redis usado pelo rate limiting
UPSTASH_REDIS_REST_TOKEN ✅ produção Token do Redis REST; nunca exponha no frontend
RATE_LIMIT_SALT ✅ produção Valor aleatório usado para anonimizar o identificador do cliente
RATE_LIMIT_ALLOW_MEMORY ❌ produção Fallback efêmero destinado apenas a testes controlados
BACKUP_ENCRYPTION_KEY ✅ backup Chave Base64 de 32 bytes para AES-256-GCM
BACKUP_DIR ⚠️ Diretório local dos backups; padrão backups
BACKUP_RETENTION_DAYS ⚠️ Retenção local; padrão 30 dias
RESTORE_DATABASE_URL ✅ restauração Banco isolado que receberá a restauração
RESTORE_CONFIRM ✅ restauração Confirmação exata do nome do banco de destino
SEED_DEMO_EMAIL ⚠️ Email opcional do usuário criado pelo seed local
SEED_DEMO_PASSWORD ⚠️ Senha opcional do usuário criado pelo seed local

Dados demonstrativos reproduzíveis

npm run db:seed

O seed restaura um usuário demonstrativo com 2 clientes, 3 veículos e 4 ordens. Ele pode ser executado repetidamente sem duplicar registros. Como o usuário demo é descartável, seus dados anteriores são substituídos a cada execução.

Credenciais locais padrão:

  • email: demo@oficina.local
  • senha: OficinaDemo@2026

Por segurança, a execução é bloqueada quando NODE_ENV=production. Um seed intencional nesse ambiente exige ALLOW_PRODUCTION_SEED=1.

📚 Endpoints principais

O contrato completo da API está em docs/openapi.yaml, no padrão OpenAPI 3.1. Ele pode ser importado no Swagger Editor, Insomnia ou Postman.

Método Rota Descrição
POST /api/auth/register Cria conta e inicia sessão segura
GET /api/health Verifica API e conexão PostgreSQL
POST /api/auth/login Autentica usuário e inicia sessão segura
GET /api/auth/me Retorna usuário autenticado
POST /api/auth/logout Encerra a sessão e remove os cookies
GET /api/clientes Lista clientes do usuário
POST /api/clientes Cadastra cliente
GET /api/veiculos Lista veículos do usuário
POST /api/veiculos Cadastra veículo
POST /api/veiculos/:id/foto Envia foto do veículo
DELETE /api/veiculos/:id/foto Remove foto do veículo
GET /api/ordens Lista ordens de serviço
POST /api/ordens Cria ordem de serviço
PUT /api/ordens/:id/status Atualiza status da ordem

As três rotas de listagem aceitam page e limit (máximo 100). Quando algum deles é enviado, a resposta contém data e metadados em pagination; sem paginação, permanece como array para compatibilidade com o frontend atual.

Rota Filtros disponíveis
/api/clientes q (nome, email ou telefone)
/api/veiculos q, tipo, clienteId
/api/ordens q, status, veiculoId, clienteId

Erros seguem o formato { "message": "...", "error": { "code": "...", "message": "...", "details": {} } }. O campo superior message mantém compatibilidade com clientes antigos.

Observabilidade

Cada requisição recebe um X-Request-Id. Se o cliente enviar um identificador válido, ele é preservado; caso contrário, a API cria um UUID. O mesmo valor aparece no corpo das respostas de erro e no objeto ApiError do frontend.

Os logs são emitidos em JSON, prontos para busca no Vercel ou ingestão por ferramentas externas:

{
  "timestamp": "2026-07-26T15:00:00.000Z",
  "level": "info",
  "service": "oficina-api",
  "event": "request.completed",
  "requestId": "3d1d...",
  "route": "/api/ordens",
  "method": "GET",
  "statusCode": 200,
  "durationMs": 18.42
}

Campos sensíveis — senha, token, cookie, segredo, API key e autorização — são substituídos por [REDACTED], inclusive em objetos aninhados. O health check não expõe credenciais nem detalhes do banco:

curl http://localhost:3000/api/health

Sessão HttpOnly e proteção CSRF

O JWT de sessão é armazenado em cookie HttpOnly, SameSite=Lax e, em produção, Secure com prefixo __Host-. O token não é devolvido no JSON nem salvo em localStorage. Operações autenticadas com efeito colateral (POST, PUT e DELETE) também exigem X-CSRF-Token, validado contra o cookie CSRF e contra o hash incorporado ao JWT.

Organizações, equipe e permissões

Cada conta recebe uma oficina própria e pode também participar de outras organizações por convite. O seletor da barra lateral troca a oficina ativa e renova a sessão; clientes, veículos e ordens são sempre filtrados no backend pelo organizationId, evitando vazamento entre oficinas.

Papel Operação Clientes/veículos Ordens Equipe
Proprietário (OWNER) leitura criar e consultar criar e atualizar convidar, alterar papéis e remover
Gerente (MANAGER) leitura criar e consultar criar e atualizar consultar e convidar mecânicos
Mecânico (MECHANIC) leitura somente consulta criar e atualizar sem acesso

Convites são vinculados ao email, expiram em sete dias e têm somente o hash SHA-256 persistido. A oficina nunca pode ficar sem ao menos um proprietário. Contas e clientes antigos são migrados progressivamente na primeira autenticação, sem exigir perda ou recadastro de dados.

PWA instalável

A aplicação inclui Web App Manifest, ícones 192x192, 512x512, variante maskable, ícone Apple e service worker. Em navegadores compatíveis, a opção Instalar aplicativo aparece na barra lateral e a OFICINA pode abrir em uma janela independente.

O shell da interface funciona offline, mas dados de clientes, veículos, ordens e autenticação continuam dependendo do servidor. Por segurança, o service worker nunca armazena /api/*: nenhuma resposta autenticada ou dado operacional é persistido no cache offline. Navegações usam estratégia network-first e assets versionados usam cache-first. Quando uma nova versão fica pronta, a interface oferece atualização explícita antes de recarregar.

O build valida automaticamente manifest, dimensões dos ícones, referências HTML e exclusão da API:

npm run build
npm run check:pwa

Backup e recuperação

O projeto possui backup PostgreSQL em formato customizado, criptografia AES-256-GCM, checksum SHA-256, retenção e restauração protegida. Um restore drill automatizado prova que o arquivo consegue reconstruir usuários, organizações, membros, convites, clientes, veículos e ordens:

npm run db:backup
npm run test:backup

O procedimento completo, RPO/RTO, automação diária, recuperação e tratamento separado das fotos do Vercel Blob estão em docs/BACKUP_RECOVERY.md.

Proteção contra força bruta

Login e cadastro usam janela deslizante distribuída com Redis REST:

Operação Política
Login 5 tentativas a cada 15 minutos por cliente
Cadastro 3 tentativas a cada hora por cliente

Ao exceder o limite, a API retorna 429 RATE_LIMITED e os headers RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset e Retry-After. O endereço do cliente nunca é salvo diretamente: ele é transformado em SHA-256 com RATE_LIMIT_SALT.

Em desenvolvimento e testes existe um contador em memória. Em produção, a ausência das credenciais Upstash retorna 503 em vez de oferecer uma proteção falsa que seria reiniciada a cada função serverless. Falhas transitórias do provedor são registradas e seguem estratégia fail-open para preservar disponibilidade.

A implementação utiliza @upstash/ratelimit e o cliente REST, adequado a ambientes serverless sem conexões TCP persistentes.

Exemplo rápido

# 1) criar conta e salvar os cookies
curl -X POST http://localhost:3000/api/auth/register \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{"name":"Gustavo","email":"gustavo@email.com","password":"senha-segura-123"}'

# 2) leia oficina_csrf em cookies.txt e envie junto à sessão
curl -X POST http://localhost:3000/api/clientes \
  -H "Content-Type: application/json" \
  -H "X-CSRF-Token: SEU_TOKEN_CSRF" \
  -b cookies.txt \
  -d '{"nome":"João Silva","telefone":"11987654321","email":"joao@email.com"}'

📂 Estrutura

api/                    # roteador serverless único da Vercel
server/                 # handlers, domínio e infraestrutura da API
├── _lib/               # auth, prisma e helpers compartilhados
├── auth/               # register, login, me
├── clientes/           # endpoints de clientes
├── veiculos/           # endpoints de veículos + foto
└── ordens/             # endpoints de ordens de serviço
prisma/
└── schema.prisma       # modelos User, Cliente, Veiculo e Ordem
src/                    # Frontend React
├── components/
├── contexts/
├── lib/
├── App.tsx
└── main.tsx

🧪 Testes

npm test              # roda todos os testes
npm run test:watch    # modo watch durante dev
npm run test:coverage # gera relatório de cobertura + HTML report
npm run test:integration # sobe PostgreSQL temporário e testa a API
npm run test:e2e      # testa o fluxo completo no Chromium

Stack de testes:

  • Vitest — runner rápido, compatível com Jest API
  • @testing-library/react — testa componentes pela perspectiva do usuário
  • jsdom — DOM virtual para hooks e components
  • v8 coverage — relatórios em coverage/index.html

55 testes cobrindo: autenticação, senha, status, observabilidade, rate limiting, cn, hooks (useNumberTicker, useTheme) e componentes (StatusBadge, CarSilhouette, PageHeader).

Testes de integração da API

Os testes de integração utilizam um PostgreSQL 16 descartável:

# requer Docker Desktop funcionando
npm run test:integration

O comando:

  1. inicia docker-compose.test.yml;
  2. aplica o schema Prisma no banco temporário;
  3. testa autenticação, isolamento entre usuários e o fluxo cliente → veículo → ordem → status;
  4. valida seed idempotente, filtros, paginação e erros padronizados;
  5. remove o container e o volume ao final.

No GitHub Actions o mesmo conjunto roda contra um serviço PostgreSQL isolado, sem depender do banco de produção.

Testes E2E no navegador

Na primeira execução, instale o Chromium controlado pelo Playwright:

npx playwright install chromium
npm run test:e2e

O Playwright:

  1. sobe o PostgreSQL 16 temporário e restaura o seed;
  2. gera o build de produção e inicia um servidor full-stack isolado;
  3. valida login inválido, bloqueio de força bruta e o fluxo login → cliente → veículo → ordem → status → download do PDF;
  4. valida health check e correlação por X-Request-Id;
  5. confirma que o chunk pesado do PDF só é baixado quando solicitado;
  6. salva trace, screenshot e vídeo quando ocorre uma falha;
  7. remove o banco temporário ao terminar.

O build também aplica orçamento de tamanho: até 350 kB para o JavaScript principal e 1.550 kB para o chunk isolado do PDF. Assim, o limite maior do PDF não mascara crescimento acidental do bundle carregado na abertura da aplicação.

Para depuração visual:

npm run test:e2e:ui

🗺️ Roadmap

  • Persistência real (PostgreSQL via Neon + Prisma)
  • Autenticação JWT
  • Geração de PDF da OS
  • Testes unitários (Vitest)
  • Documentação OpenAPI
  • Testes E2E (Playwright) do fluxo principal e PDF
  • Testes de integração da API com PostgreSQL efêmero
  • Seed demonstrativo reproduzível
  • Paginação, filtros e erros padronizados na API
  • Carregamento sob demanda do gerador de PDF
  • Rate limiting distribuído em login e cadastro
  • Migração do JWT para cookie HttpOnly com proteção CSRF
  • PWA instalável com shell offline e atualização controlada
  • Backup PostgreSQL criptografado e restore drill automatizado
  • Multi-tenant com organizações, convites, isolamento e papéis por funcionário

🔎 Auditoria técnica

Os achados, correções e próximas fases estão registrados em docs/AUDITORIA_TECNICA.md.

📝 Licença

MIT © Gustavo Avelino Saraiva Oliveira

About

Sistema full-stack de gestão de oficina mecânica — API REST em Node.js/Express + front em React/Vite/Tailwind.

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages