first commit
This commit is contained in:
@@ -0,0 +1,172 @@
|
||||
# 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`).
|
||||
|
||||
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/<timestamp>_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', '<uuid-do-tenant-logado>', 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).
|
||||
|
||||
## 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 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
|
||||
├── app.module.ts
|
||||
├── config/ # ConfigModule, validação de env, PrismaService/PrismaMasterService/
|
||||
│ # PrismaTenantService, TenantContextService (mecanismo de RLS por request)
|
||||
└── modules/
|
||||
├── health/ # GET /health
|
||||
└── auth/ # login/refresh/logout/reset, guards, decorators, JwtStrategy
|
||||
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).
|
||||
Reference in New Issue
Block a user