# SeConectai Vendas CRM/controle de vendas multi-tenant. Um painel **master** (dono da plataforma) administra **tenants** (clientes), que por sua vez cadastram seus próprios clientes ("customers"), produtos/serviços e vendas. Stack: Node.js 20 + TypeScript, NestJS, PostgreSQL 16 + Prisma, Redis, Docker/Docker Compose. Multi-tenancy via banco único, isolado por `tenant_id` + Row Level Security (RLS) do Postgres. ## Subindo o ambiente do zero 1. Copie o arquivo de variáveis de ambiente e ajuste se necessário: ```bash cp .env.example .env ``` 2. Suba os containers (Postgres, Redis e a API com hot-reload em modo dev): ```bash docker compose up ``` Isso builda a imagem `app` (stage `dev`), sobe `postgres` e `redis` com healthcheck, e só inicia a API depois que as duas dependências estiverem saudáveis. `docker-compose.override.yml` é aplicado automaticamente e monta o código-fonte local no container, então alterações em `src/` reiniciam a API sozinhas (`npm run dev` / `nest start --watch`). O mesmo `docker compose up` já sobe o serviço `worker` (motor de alertas — ver seção própria abaixo) lado a lado com a API. 3. Rode as migrations do Prisma (tabelas, enums, índices) e a migration manual de RLS/views/checks: ```bash docker compose exec app npx prisma migrate deploy ``` Em desenvolvimento, para criar uma nova migration a partir de mudanças no `schema.prisma`: ```bash docker compose exec app npx prisma migrate dev ``` 4. Confirme que a API está de pé: ```bash curl http://localhost:3000/health ``` Deve retornar `200` com `{"status":"ok","database":"up",...}`. ## Rodando sem Docker (opcional) Requer Node.js 20+, um Postgres 16 e um Redis acessíveis localmente. ```bash npm install npm run prisma:generate npm run prisma:migrate npm run dev ``` ## Variáveis de ambiente Veja [.env.example](.env.example) — todas as chaves usadas pela aplicação e pelo `docker-compose.yml` (credenciais do Postgres, `DATABASE_URL` e variantes, Redis, `JWT_SECRET` e demais parâmetros de autenticação) estão documentadas lá. ## Autenticação `src/modules/auth/` implementa login/refresh/logout/reset de senha para os dois tipos de usuário: - `POST /auth/master/login` e `POST /auth/tenant/login` — e-mail+senha → access token (JWT, 15 min por padrão) + refresh token opaco (rotacionado a cada uso, guardado só como hash sha256 em `auth_refresh_tokens`). - `POST /auth/refresh` — rotaciona o refresh token; reuso de um token já rotacionado é tratado como indício de roubo e revoga toda a família de tokens do usuário. - `POST /auth/logout` — revoga o refresh token informado. - `POST /auth/password/forgot` / `POST /auth/password/reset` — gera token de reset (hash, expiração curta, uso único); por ora o token é só logado no console (envio de e-mail real é da Etapa 4). - Senhas com `argon2id` (parâmetros OWASP 2024). Bloqueio de força bruta por e-mail após `LOGIN_MAX_ATTEMPTS` falhas em `LOGIN_LOCKOUT_WINDOW_MINUTES` (tabela `login_attempts`), mais um rate limit por IP nas rotas de login (`@nestjs/throttler`). - `JwtAuthGuard`, `MasterGuard`, `TenantGuard`, `RolesGuard` (+ `@Roles(...)`) e o decorator `@CurrentUser()` — ver `GET /auth/me`, `GET /auth/master/admins` e `GET /auth/tenant/users` em `auth.controller.ts` para exemplos de uso de cada guard. ## Migrations e RLS O schema em `prisma/schema.prisma` é a tradução 1:1 de `Etapas/seconectai-vendas-schema.sql` (fonte de verdade). Construtos que o Prisma DSL não expressa nativamente — views de dashboard, `CHECK` constraints e as `CREATE POLICY` de Row Level Security — vivem numa migration manual em `prisma/migrations/_rls_views_constraints/`, aplicada junto com as migrations geradas automaticamente pelo Prisma (`prisma migrate deploy` roda todas em ordem). Uma segunda migration manual (`prisma/migrations/20260813120000_auth_app_roles/`) cria duas roles de banco usadas em runtime: - **`app_master`** (`LOGIN` + `BYPASSRLS`, conectada via `DATABASE_URL_MASTER`) — usada pelo painel master, que precisa enxergar dados de todos os tenants. - **`app_tenant`** (`LOGIN` comum, sem `BYPASSRLS`, conectada via `DATABASE_URL_TENANT`) — usada por toda query autenticada como `tenant_user`. Isso é necessário porque a role de `DATABASE_URL` (`seconectai`, dona das tabelas) é a superuser bootstrap do Postgres criada pelo `POSTGRES_USER` do docker-compose — e superusers **sempre** ignoram Row Level Security, mesmo com as policies criadas. Sem uma role não-superuser dedicada, o isolamento por tenant seria só decorativo. Por request, o `TenantRlsInterceptor` (registrado globalmente) identifica requests autenticadas como `tenant_user` e, via `TenantContextService`, abre uma transação na conexão `app_tenant` executando: ```sql SELECT set_config('app.current_tenant_id', '', true); -- equivalente a SET LOCAL ``` sempre usando o `tenant_id` do JWT da sessão — nunca um valor vindo de input do usuário (body/query). Serviços que precisam rodar dentro desse contexto usam `TenantContextService.getTx()` (ver `AuthController.listTenantUsers` como exemplo). Requests master usam `PrismaMasterService` (conexão `app_master`, sem passar pelas policies). ## Motor de alertas (worker) O disparo automatizado de alertas (mensalidade do tenant, cobrança de parcelas do customer) roda num **processo separado do processo web**: - `src/main.ts` (processo `app`) — só HTTP. Não importa `ScheduleModule` nem processa nenhuma fila. - `src/worker.ts` (processo `worker`) — sem HTTP. Sobe via `NestFactory.createApplicationContext(WorkerModule)`, que inicializa os dois crons diários (`AlertSchedulerService`, `@nestjs/schedule`) e os dois processors do BullMQ (`MensalidadeProcessor`/`CobrancaProcessor`, filas `alertas-mensalidade`/`alertas-cobranca` no Redis já usado pelo `docker-compose`). Essa separação existe para poder escalar os dois processos de forma independente sem duplicar o disparo do cron: se o `app` rodar em várias réplicas (comum atrás de um load balancer), um `@Cron()` registrado nele dispararia o mesmo job uma vez por réplica. Como os crons vivem só no `worker`, escalar `worker` (`docker compose up --scale worker=3`) paraleliza o *processamento* das filas, não a geração dos jobs. `docker compose up` já sobe os dois processos (serviço `worker` no `docker-compose.yml`/`docker-compose.override.yml`, mesma imagem do `app`, `command` diferente: `node dist/worker` em produção, `npm run worker:dev` em desenvolvimento). Para rodar só o worker manualmente: ```bash docker compose up worker ``` Horário dos crons, antecedência do alerta e política de retry são configuráveis via `ALERT_CRON_MENSALIDADE`/`ALERT_CRON_COBRANCA`, `ALERT_DIAS_ANTECEDENCIA_MENSALIDADE`/`ALERT_DIAS_ANTECEDENCIA_COBRANCA` e `ALERT_JOB_MAX_ATTEMPTS`/`ALERT_JOB_BACKOFF_MS` (ver `.env.example`). ## Scripts | Script | Descrição | |----------------------------|-----------------------------------------------------| | `npm run dev` | Inicia a API em modo watch | | `npm run build` | Compila TypeScript para `dist/` | | `npm run start:prod` | Roda a build de produção (`dist/main.js`) | | `npm run worker:dev` | Inicia o worker de alertas em modo watch | | `npm run worker:prod` | Roda a build de produção do worker (`dist/worker.js`) | | `npm run lint` | ESLint (com `--fix`) | | `npm run test` | Testes unitários (Jest) | | `npm run test:e2e` | Testes end-to-end (Jest + Supertest) | | `npm run prisma:generate` | Gera o Prisma Client | | `npm run prisma:migrate` | Cria/aplica migration em desenvolvimento | | `npm run prisma:migrate:deploy` | Aplica migrations pendentes (produção/CI) | | `npm run prisma:studio` | Abre o Prisma Studio | ## Estrutura do projeto ``` src/ ├── main.ts # processo web (HTTP) ├── worker.ts # processo worker (crons + filas BullMQ, sem HTTP) ├── app.module.ts # módulos do processo web ├── config/ # ConfigModule, validação de env, PrismaService/PrismaMasterService/ │ # PrismaTenantService, TenantContextService (mecanismo de RLS por request) ├── worker/ # WorkerModule, AlertSchedulerService (crons), processors do BullMQ └── modules/ ├── health/ # GET /health ├── auth/ # login/refresh/logout/reset, guards, decorators, JwtStrategy └── alerts/ # CRUD de alert_templates, GET .../alertas/logs, resolver de fallback # e placeholders (usado pelo módulo web e pelo worker) prisma/ ├── schema.prisma └── migrations/ Etapas/ # Prompts e controle de progresso do build faseado ``` ## Progresso do build Este projeto é construído em etapas. O estado oficial de cada etapa (o que já foi concluído, o que falta, decisões tomadas) fica em [`Etapas/09-controle-de-progresso.md`](Etapas/09-controle-de-progresso.md).