12 KiB
TechXCar — Design Spec
Data: 2026-06-16 Versão: 1.0
1. Visão Geral
TechXCar é um SaaS multi-tenant de gestão de oficina automóvel. Cada oficina tem o seu espaço isolado com clientes, veículos, ordens de trabalho, faturação, técnicos e relatórios. A plataforma é gerida por um super-admin que cria e convida oficinas manualmente.
Inspiração: LubeLogger, mas focado na gestão operacional da oficina (não no diário do proprietário do veículo).
2. Stack Técnica
| Camada | Tecnologia |
|---|---|
| Frontend | React 19 + TypeScript + Vite |
| Backend | Go + Fiber (framework HTTP) |
| Base de dados | PostgreSQL 16 |
| Cache / Filas | Redis 7 |
| Auth | JWT (access 15min + refresh 30d, httpOnly cookie) |
| Passwords | bcrypt cost 12 |
| Geração server-side em Go (html/template → PDF) | |
| Notificações | Telegram Bot API + SMTP |
| Frontend UI | Tailwind CSS 4 + shadcn/ui |
| Estado cliente | TanStack Query + Zustand |
| Formulários | React Hook Form + Zod |
| Roteamento | React Router v7 |
| Migrations | golang-migrate |
| Deploy | Docker Compose → Coolify |
3. Arquitectura de Deploy
Serviços Docker
techxcar/
├── frontend/ # React 19 + TypeScript + Vite (servido via Nginx)
├── backend/ # Go + Fiber
├── docker-compose.yml # Desenvolvimento
└── docker-compose.prod.yml # Produção (Coolify)
Quatro serviços:
frontend— Nginx a servir o build Vite na porta 80backend— Binário Go na porta 8080postgres— PostgreSQL 16redis— Redis 7
Configuração em dois níveis
.env— valores de arranque imutáveis em runtime:DATABASE_URL,REDIS_URL,JWT_SECRET,PORT,APP_ENV. Requerem reinício do container para alterar.platform_settings/tenant_settings(DB) — configuração dinâmica gerida pela Web UI sem redeploy: SMTP, Telegram Bot Token, templates de notificação, limites, dados de faturação. Cached em Redis por 5 minutos.
Coolify
- Secrets geridos pelo Coolify (injectados como variáveis de ambiente)
- HTTPS automático via Coolify (Let's Encrypt)
- Health checks em
/api/v1/health
4. Multi-tenancy
Estratégia: Schema-per-tenant no PostgreSQL
- Schema
public: tabelas globais (tenants,invites,super_admins,platform_settings) - Schema
tenant_<uuid>: dados isolados de cada oficina - Middleware no backend define
SET search_path = tenant_<uuid>por request, com base no JWT
Onboarding de oficinas
Via convite: Super-admin gera token assinado com expiração configurável. Oficina clica no link, preenche nome, NIF, email, password → schema PostgreSQL provisionado automaticamente via migrations.
Via criação manual: Super-admin cria a oficina no painel /admin, define credenciais iniciais, envia email de boas-vindas.
5. Autenticação & Autorização
Níveis de acesso
super_admin — gestão da plataforma, todos os tenants
tenant_admin — dono/gestor principal da oficina
manager — gestor operacional, sem acesso a settings financeiras
technician — técnico interno, acesso a OTs atribuídas
external_technician — sem login; apenas para atribuição e registo de custos
Fluxo JWT
POST /api/v1/auth/login→ devolveaccess_token(body) +refresh_token(httpOnly cookie)POST /api/v1/auth/refresh→ renova access token usando refresh token do cookiePOST /api/v1/auth/logout→ invalida refresh token- Rate limiting: 10 tentativas/min por IP nas rotas de auth (Redis)
6. Módulos de Negócio
6.1 Clientes & Veículos
- Cliente: nome, NIF, telefone, email, morada, notas
- Veículo: pertence a um cliente (opcional), matrícula, marca, modelo, ano, VIN, km actual
- Um cliente pode ter N veículos
- OT pode ser criada sem cliente nem veículo (cliente temporário)
6.2 Ordens de Trabalho (OT)
Estados e transições:
Aberta → Em Progresso → Concluída → Faturada
↓
Cancelada (de qualquer estado excepto Faturada)
Conteúdo de uma OT:
- Cliente + Veículo (ambos opcionais)
- Items: serviços/peças do catálogo (qty, preço unitário, desconto por item)
- Técnico(s) atribuído(s) + registo de horas (horas × custo/hora)
- Notas internas (não visíveis ao cliente)
- Notas para o cliente (aparecem no PDF)
- Log de estados: timestamp + utilizador responsável por cada transição
- Total calculado: soma de items + mão de obra
6.3 Catálogo de Serviços & Peças
- Item: código, nome, categoria, unidade (un/hora/litro/kg), preço base, activo/inactivo
- Categorias livres (criadas pela oficina)
- Ao adicionar a OT: preço copiado do catálogo mas editável por OT
- Pesquisa por código ou nome no momento de adição à OT
6.4 Faturação & Orçamentos
- Orçamento: gerado a partir de OT em estado "Aberta", PDF com logo e dados da oficina
- Fatura: gerada quando OT passa a "Faturada", numeração sequencial por oficina
- PDFs gerados server-side em Go com
chromedp(render HTML → PDF via Chrome headless); armazenados localmente (volume Docker) ou S3-compatible (configurável nas Settings) - Dados da oficina para PDF: logo, nome, NIF, morada, IBAN — configuráveis nas Settings da Web UI
6.5 Técnicos (Staff)
- Interno: tem conta no sistema (role
technician), aparece na atribuição de OTs - Externo: registo simplificado (nome, contacto, custo/hora), sem login
- Registo de horas por OT: horas trabalhadas × custo/hora = custo de mão de obra
- Listagem com estado activo/inactivo
6.6 Combustíveis & Despesas da Oficina
- Registo: data, tipo (combustível / peças / ferramentas / outros), valor, descrição, veículo (opcional)
- Filtros por período e tipo
- Totais agregados no dashboard e relatórios
6.7 Notificações ao Cliente
- Telegram: token do bot configurável nas Settings; envia mensagem quando OT muda para "Concluída" ou "Faturada"
- Email: SMTP configurável nas Settings; template HTML editável na Web UI
- Configurável por oficina: quais eventos disparam notificação e qual canal
- Envio assíncrono: o handler HTTP publica um job numa lista Redis; uma goroutine dedicada consome a lista e envia as notificações (não bloqueia o request HTTP)
6.8 Dashboard & Relatórios
Dashboard (tempo real):
- Receita do mês actual vs mês anterior
- Nº de OTs por estado (cards)
- OTs abertas recentes (tabela)
- Serviços mais realizados (top 5)
Relatórios (com filtro de período):
- Receita por período (dia/semana/mês)
- OTs por estado e por técnico
- Despesas vs Receita
- Exportação em PDF e CSV
7. Estrutura do Backend Go
backend/
├── cmd/server/main.go
├── internal/
│ ├── auth/ # JWT, middleware, bcrypt
│ ├── tenant/ # Gestão tenants, schema provisioning
│ ├── workorder/ # Ordens de trabalho
│ ├── client/ # Clientes & veículos
│ ├── catalog/ # Serviços & peças
│ ├── invoice/ # Faturação, geração PDF
│ ├── staff/ # Técnicos
│ ├── expense/ # Combustíveis & despesas
│ ├── notification/ # Telegram + Email (worker assíncrono)
│ ├── report/ # Relatórios & exportações
│ └── settings/ # Settings dinâmicas (DB + cache Redis)
├── pkg/
│ ├── database/ # Pool PostgreSQL, migrations
│ ├── redis/ # Cliente Redis
│ └── pdf/ # Geração PDF
└── migrations/
├── public/ # Schema público
└── tenant/ # Schema tenant (aplicado no provisionamento)
API
- Base:
/api/v1/ - Autenticação: Bearer token no header
Authorization - Tenant resolvido via claim
tenant_idno JWT - Respostas:
{ "data": ..., "error": null, "meta": { "page": ..., "total": ... } } - Paginação cursor-based
8. Estrutura do Frontend React
frontend/
├── src/
│ ├── pages/
│ │ ├── auth/ # Login
│ │ ├── admin/ # Super-admin (dashboard, tenants, settings)
│ │ └── app/ # Área da oficina
│ │ ├── dashboard/
│ │ ├── work-orders/
│ │ ├── clients/
│ │ ├── vehicles/
│ │ ├── catalog/
│ │ ├── staff/
│ │ ├── expenses/
│ │ ├── reports/
│ │ └── settings/
│ ├── components/
│ │ ├── ui/ # shadcn/ui base components
│ │ └── shared/ # Componentes reutilizáveis da app
│ ├── hooks/ # Custom hooks (useAuth, useTenant, etc.)
│ ├── lib/
│ │ ├── api.ts # Cliente HTTP (fetch + interceptors)
│ │ └── queryClient.ts # TanStack Query config
│ ├── store/ # Zustand stores
│ └── i18n/ # Estrutura i18n (PT por defeito, EN preparado)
Rotas protegidas
/login— público/admin/*— requer rolesuper_admin/app/*— requer roletenant_admin,manageroutechnician- Guards no React Router v7 redireccionam para
/loginse JWT expirado
9. Base de Dados — Schemas
Schema public
tenants (id uuid PK, slug text UNIQUE, name text, status, plan, created_at)
invites (id uuid PK, tenant_id uuid FK, token text UNIQUE, expires_at, used_at nullable)
super_admins (id uuid PK, email text UNIQUE, password_hash text, created_at)
platform_settings (key text PK, value text, updated_at)
Schema tenant_<uuid> (por oficina)
users (id uuid PK, email, password_hash, role, name, active)
clients (id uuid PK, name, nif, phone, email, address, notes, created_at)
vehicles (id uuid PK, client_id uuid nullable FK, plate, brand, model, year, vin, mileage, notes)
work_orders (id uuid PK, number serial, client_id nullable, vehicle_id nullable, status, internal_notes, client_notes, created_by, created_at, updated_at)
wo_items (id uuid PK, work_order_id FK, catalog_item_id nullable FK, description, qty, unit_price, discount_pct, total)
wo_staff_hours (id uuid PK, work_order_id FK, staff_id FK, hours, cost_per_hour, total)
wo_status_log (id uuid PK, work_order_id FK, from_status, to_status, changed_by FK, changed_at)
catalog_items (id uuid PK, code, name, category, unit, base_price, active)
invoices (id uuid PK, work_order_id FK, type: quote|invoice, number serial, pdf_path, issued_at)
staff (id uuid PK, user_id nullable FK, name, email, phone, type: internal|external, hourly_rate, active)
expenses (id uuid PK, vehicle_id nullable FK, type, amount, description, date)
tenant_settings (key text PK, value text, updated_at)
10. Internacionalização (i18n)
- Estrutura i18n no frontend desde o início (ficheiros JSON por locale em
src/i18n/) - Idioma padrão e único no MVP: Português de Portugal (pt-PT)
- Inglês (en) preparado na estrutura mas não traduzido no MVP
- Datas, moeda e números formatados com
IntlAPI (localept-PT, moedaEUR)
11. Segurança
- HTTPS obrigatório em produção (Coolify / Let's Encrypt)
- JWT com expiração curta + refresh token rotativo em httpOnly cookie
- Passwords com bcrypt cost 12
- Rate limiting em todas as rotas de auth (Redis)
- Row-level isolation via PostgreSQL
search_pathpor tenant - Headers de segurança: CSP, HSTS, X-Frame-Options (middleware Fiber)
- Inputs validados com Zod (frontend) e struct validation em Go (backend)
- Ficheiros PDF armazenados fora do webroot, servidos via endpoint autenticado
12. Fora de Âmbito (MVP)
- Billing automático / Stripe
- App mobile nativa
- Integração com sistemas de diagnóstico OBD
- Portal self-service para clientes da oficina
- Multi-idioma completo (EN traduzido)
- Backups automáticos (responsabilidade do Coolify/infra)