# Gertel Talentos Backend — Documentação Técnica

## Visão Geral

**Nome do pacote**: `appgertel_api`
**Versão**: 1.0.0
**Framework**: Fastify v5
**Linguagem**: TypeScript
**Ambiente de execução**: Node.js
**Fuso horário padrão**: America/Sao_Paulo

Backend da aplicação Gertel Talentos, responsável por autenticação de usuários, integração com a API Gertel e processamento de formulários do site.

---

## Estrutura de Diretórios

```
gertel-talentos-backend/
├── src/
│   ├── server.ts                          # Entry point — inicia o servidor
│   ├── app.ts                             # Configuração do app Fastify
│   ├── env/
│   │   └── index.ts                       # Validação de variáveis de ambiente (Zod)
│   ├── @types/
│   │   ├── fastify.d.ts                   # Extensão do FastifyRequest
│   │   ├── fastify-jwt.d.ts               # Extensão do payload JWT
│   │   └── serializes-error.d.ts          # Tipo SerializedError
│   ├── schemas/                           # Schemas Mongoose (MongoDB)
│   │   ├── requests.ts                    # Log de requisições HTTP
│   │   ├── external_requests.ts           # Log de chamadas externas (API Gertel)
│   │   ├── external_access.ts             # Credenciais e tokens externos
│   │   ├── tokens.ts                      # Tokens JWT emitidos
│   ├── http/
│   │   ├── middlewares/
│   │   │   ├── jwt.ts                     # Verificação de autenticação JWT
│   │   │   └── sniffer.ts                 # Logging de request/response
│   │   └── controllers/
│   │       ├── auth/
│   │       │   ├── routes.ts              # POST /auth, PATCH /auth
│   │       │   ├── user.ts                # Handler de login
│   │       │   └── refresh.ts             # Handler de refresh token
│   │       ├── me/
│   │       │   ├── routes.ts              # GET /me
│   │       │   └── me.ts                  # Handler — retorna usuário atual
│   │       └── test/
│   │           └── routes.ts              # GET /, POST / (rotas de teste)
│   ├── use-cases/
│   │   ├── control-access.ts              # Caso de uso: autenticação
│   │   ├── control-user.ts                # Caso de uso: busca de usuário
│   │   └── factories/
│   │       ├── make-control-access.ts     # Factory para ControlAccessUseCase
│   │       └── make-control-user.ts       # Factory para ControlUserUseCase
│   ├── services/
│   │   ├── send-contact.ts                # Envio de e-mail e SMS via API Gertel
│   │   └── reCaptcha.ts                   # Validação de reCAPTCHA Google
│   ├── lib/
│   │   ├── api-gertel.ts                  # Instância Axios para a API Gertel
│   │   └── mongoose.ts                    # Conexão com MongoDB
│   ├── plugins/
│   │   └── get-token-api-gertel.ts        # Gerenciamento de tokens da API Gertel
│   └── error/
│       └── normalize.ts                   # Normalização de erros para serialização
├── .env.example                           # Modelo de variáveis de ambiente
├── .eslintrc.cjs                          # Configuração do ESLint
├── .eslintignore
├── .gitignore
├── tsconfig.json
├── package.json
└── build/                                 # Output do build (tsup)
```

---

## Stack Tecnológica

| Categoria | Tecnologia |
|-----------|------------|
| Web Framework | Fastify v5 |
| Linguagem | TypeScript 5 |
| ORM / ODM | Prisma (PostgreSQL) + Mongoose (MongoDB) |
| Autenticação | JWT (`@fastify/jwt`) |
| Validação | Zod v4 |
| HTTP Client | Axios |
| Data/Hora | Day.js com timezone |
| Build | tsup |
| Dev server | tsx watch |
| Linting | ESLint + @typescript-eslint |
| Testes | Vitest |

---

## Variáveis de Ambiente

Definidas em `src/env/index.ts` e validadas via Zod. O arquivo `.env.example` documenta todas as chaves:

| Variável | Descrição |
|----------|-----------|
| `NODE_ENV` | Ambiente: `dev`, `homologation`, `production` |
| `HOST_DEV` / `PORT_DEV` | Host e porta para desenvolvimento |
| `HOST_HOMOLOGATION` / `PORT_HOMOLOGATION` | Host e porta para homologação |
| `HOST_PRODUCTION` / `PORT_PRODUCTION` | Host e porta para produção |
| `API_GERTEL_PRODUCTION` | URL base da API Gertel (produção) |
| `API_GERTEL_HOMOLOGATION` | URL base da API Gertel (homologação) |
| `JWT_SECRET` | Segredo para assinatura dos JWTs |
| `RECAPTCHA_SECRET` | Chave secreta do Google reCAPTCHA |
| `DATABASE_URL` | Connection string PostgreSQL (Prisma) |
| `SHADOW_DATABASE_URL` | Shadow DB do Prisma |
| `COLLECTION_URL` | Connection string MongoDB (Mongoose) |

---

## Configuração da Aplicação

### `src/server.ts`
- Configura o fuso horário global via `dayjs.extend(timezone)` para `America/Sao_Paulo`
- Determina HOST e PORT com base em `NODE_ENV`
- Chama `app.listen()`

### `src/app.ts`
Registra os seguintes plugins e rotas no Fastify:

| Plugin / Recurso | Configuração |
|-----------------|--------------|
| `@fastify/cors` | Todas as origens habilitadas |
| `@fastify/jwt` | Secret via env, expiração padrão de 30m |
| `@fastify/websocket` | Habilitado |
| `@fastify/cookie` | Habilitado |
| `@fastify/multipart` | Limite de 10MB por arquivo |
| Middleware Sniffer | Logging de todas as requests |
| Error handler | Trata erros Zod e erros genéricos |

---

## Endpoints da API

| Método | Rota | Auth | Descrição |
|--------|------|:----:|-----------|
| `GET` | `/` | Não | Rota de teste |
| `POST` | `/` | Não | Rota de teste |
| `POST` | `/auth` | Não | Login — retorna access token |
| `PATCH` | `/auth` | Não | Refresh — renova tokens via cookie |
| `GET` | `/me` | Sim | Retorna dados do usuário autenticado |

---

## Fluxo de Autenticação

```
1. POST /auth  {login, password}
       │
       ▼
2. ControlAccessUseCase.auth()  →  valida credenciais
       │
       ▼
3. Gera access token  (exp: 30m)  +  refresh token  (exp: 30d)
       │
       ▼
4. Tokens persistidos no MongoDB (schema Tokens)
       │
       ├─ Access token  →  response body
       └─ Refresh token →  cookie HTTP-only

5. PATCH /auth  (cookie com refresh token)
       │
       ▼
6. Valida refresh token (assinatura + existência no MongoDB)
       │
       ▼
7. Gera novos tokens  →  atualiza cookie  →  retorna novo access token
```

### Payload do JWT

```typescript
{
  sub: string   // ID do usuário
  role: 'ADMINISTRATOR' | 'CANDIDATE'
  type: 'access' | 'refresh'
}
```

---

## Middlewares

### `src/http/middlewares/jwt.ts` — Verificação JWT
Aplicado nas rotas protegidas. Validações:
1. Presença e formato do header `Authorization: Bearer <token>`
2. Assinatura do JWT
3. Campo `type === 'access'` (rejeita refresh tokens)
4. Existência e não-expiração do token no MongoDB

### `src/http/middlewares/sniffer.ts` — Request Logger
Captura e persiste no MongoDB todo o ciclo de vida de cada requisição:
- **onRequest**: IP, URL, método, headers, body, query, arquivos (base64)
- **onSend**: status da resposta, payload

---

## Schemas MongoDB

### `requests` — Log de Requisições HTTP
Registra todas as requisições recebidas: IP, URL, método, headers, body, query, dados de usuário, status da resposta, payload e erros.

### `external_requests` — Log de Chamadas Externas
Registra chamadas feitas à API Gertel: URL, método, headers, payload enviado, status e resposta recebida, timestamps.

### `external_access` — Credenciais e Tokens Externos
Armazena credenciais e tokens em cache para APIs externas:
- `ref`: identificador da credencial
- `login`, `password`: credenciais
- `token`, `expires_at`: token atual e expiração
- `method`, `path`, `baseURL`: configuração do endpoint de autenticação

### `tokens` — Tokens JWT Emitidos
Controle dos tokens ativos:
- `token`: string JWT (único)
- `type`: `'access'` | `'refresh'`
- `expires_at`: expiração com TTL index (auto-delete)
- `sub`: ID do usuário
- `ip`: IP do cliente

---

## Casos de Uso

### `ControlAccessUseCase` (`src/use-cases/control-access.ts`)
- `auth(login, password)`: Valida credenciais e retorna dados do usuário
- `findById(id)`: Retorna usuário pelo ID

> **Nota**: Atualmente utiliza dados hardcoded para testes. Não está pronto para produção.

### `ControlUserUseCase` (`src/use-cases/control-user.ts`)
- `findById(id)`: Retorna `{ id, name }` do usuário ou lança erro 404

---

## Serviços

### `src/services/send-contact.ts` — Envio de E-mail e SMS

**`sendEMAIL(data)`**
- Obtém token da API Gertel via `getToken()`
- Codifica o corpo da mensagem em base64
- POST para `/gertel/email`
- Loga a chamada no schema `external_requests`

**`sendSMS(data)`**
- Obtém token da API Gertel
- POST para `/gertel/sms` com `{ nome, fone, mensagem }`

### `src/services/reCaptcha.ts` — Validação reCAPTCHA
- POST para a API do Google reCAPTCHA
- Retorna `{ success: boolean }`
- Loga a chamada externa

---

## Plugins

### `src/plugins/get-token-api-gertel.ts` — Gerenciamento de Token

`getToken(ref)`:
1. Busca credencial por `ref` no MongoDB
2. Verifica se o token ainda é válido para o próximo minuto
3. Se expirado, chama `renerateToken()` e persiste o novo token
4. Retorna o token ativo

---

## Path Aliases (tsconfig)

| Alias | Caminho |
|-------|---------|
| `@env/*` | `src/env/*` |
| `@lib/*` | `src/lib/*` |
| `@plugins/*` | `src/plugins/*` |
| `@schemas/*` | `src/schemas/*` |
| `@services/*` | `src/services/*` |
| `@use-cases/*` | `src/use-cases/*` |
| `@controllers/*` | `src/http/controllers/*` |
| `@middlewares/*` | `src/http/middlewares/*` |
| `@factories/*` | `src/use-cases/factories/*` |
| `@erro/*` | `src/error/*` |

---

## Scripts NPM

| Comando | Descrição |
|---------|-----------|
| `npm run start:dev` | Inicia servidor em modo desenvolvimento com hot reload (`tsx watch`) |
| `npm run start` | Executa o build de produção (`node build/server.js`) |
| `npm run build` | Compila TypeScript → JavaScript com `tsup` |
| `npm run test` | Executa testes com Vitest |

---

## Arquitetura e Padrões de Projeto

### Arquitetura Hexagonal (Ports & Adapters)

O projeto adota a **Arquitetura Hexagonal**, cujo objetivo é isolar a lógica de negócio (núcleo) de detalhes de infraestrutura como banco de dados, HTTP e serviços externos.

```
┌─────────────────────────────────────────────────────────┐
│                    LADO EXTERNO                         │
│                                                         │
│   [ HTTP / Fastify ]        [ Banco de Dados / Prisma ] │
│         ↓                              ↑                │
│   Controllers               PrismaUserRepository        │
│   (Adaptadores de entrada)  (Adaptadores de saída)      │
│         ↓                              ↑                │
│─────────────────────────────────────────────────────────│
│                    NÚCLEO                               │
│                                                         │
│      ControlUserUseCase  ←→  UserRepository (interface) │
│      ControlAccessUseCase                               │
│                                                         │
└─────────────────────────────────────────────────────────┘
```

#### As três camadas do projeto

| Camada                   | Onde fica               | Responsabilidade                                      |
|--------------------------|-------------------------|-------------------------------------------------------|
| **HTTP (entrada)**       | `src/http/controllers/` | Receber requisições, validar input, retornar resposta |
| **Núcleo (negócio)**     | `src/use-cases/`        | Regras de negócio, independente de framework ou banco |
| **Repositórios (saída)** | `src/repository/`       | Acesso ao banco de dados                              |

---

### Padrões de Projeto Utilizados

#### 1. Repository Pattern

Abstrai o acesso ao banco de dados atrás de uma interface. O Use Case não sabe se os dados vêm do Prisma, de um array em memória ou de qualquer outra fonte.

```
src/repository/
├── user-repository.ts              ← Interface (contrato/porta)
├── prisma/
│   └── prisma-user-repository.ts   ← Adaptador de produção (Prisma + SQLite)
└── in-memory/
    └── in-memory-user-repository.ts ← Adaptador de testes (array em memória)
```

**Interface (`user-repository.ts`)** — define o contrato:
```typescript
export interface UserRepository {
    findById(id: string): Promise<User | null>
    findByLogin(login: string): Promise<User | null>
    findMany(): Promise<User[]>
    create(data: UserData): Promise<{ id: string }>
    update(data: { id: string; newPassword: string }): Promise<Omit<User, 'password'>>
}
```

**Adaptador de produção (`prisma-user-repository.ts`)** — implementa usando Prisma:
```typescript
export class PrismaUserRepository implements UserRepository {
    async findByLogin(login: string) {
        return prisma.user.findUnique({ where: { login } })
    }
    // ...
}
```

**Adaptador de testes (`in-memory-user-repository.ts`)** — implementa usando array:
```typescript
export class InMemoryUserRepository implements UserRepository {
    public items: User[] = []
    async findByLogin(login: string) {
        return this.items.find(i => i.login === login) ?? null
    }
    // ...
}
```

---

#### 2. Use Case Pattern

Cada funcionalidade do sistema é encapsulada em um caso de uso. A lógica de negócio vive aqui — sem dependência de Fastify, Prisma ou qualquer detalhe externo.

```typescript
// src/use-cases/control-user.ts
export class ControlUserUseCase {
    constructor(private userRepository: UserRepository) { }

    async create(data: CreateUserInput) {
        const existing = await this.userRepository.findByLogin(data.login)
        if (existing) throw new Error('Não foi possivel criar o usuário.')

        const passwordHash = hash('sha256', data.password)
        return this.userRepository.create({ ...data, password: passwordHash })
    }
}
```

O Use Case recebe a interface `UserRepository` no construtor — não sabe qual implementação será usada.

---

#### 3. Factory Pattern

As factories constroem e conectam as dependências. São o único lugar do código onde a implementação concreta (ex: `PrismaUserRepository`) é acoplada ao Use Case.

```typescript
// src/use-cases/factories/make-control-user.ts
export function makeControlUserUseCase() {
    const userRepository = new PrismaUserRepository()
    return new ControlUserUseCase(userRepository)
}
```

Os controllers chamam a factory, nunca instanciam o Use Case diretamente:
```typescript
// src/http/controllers/users/create-user.ts
const useCase = makeControlUserUseCase()
await useCase.create(data)
```

---

### Fluxo Completo de uma Requisição

```
POST /users
    │
    ▼
Controller (create-user.ts)
    │  valida o body com Zod
    │  chama makeControlUserUseCase()
    ▼
Factory (make-control-user.ts)
    │  instancia PrismaUserRepository
    │  injeta no ControlUserUseCase
    ▼
ControlUserUseCase.create()
    │  verifica se login já existe
    │  faz hash da senha
    │  chama userRepository.create()
    ▼
PrismaUserRepository.create()
    │  executa prisma.user.create()
    ▼
SQLite (banco de dados)
```

---

### Benefícios desta Arquitetura no Projeto

| Benefício                          | Como se aplica aqui                                                       |
|------------------------------------|---------------------------------------------------------------------------|
| **Testabilidade**                  | `InMemoryUserRepository` permite testar Use Cases sem banco real          |
| **Independência de framework**     | Use Cases não importam nada do Fastify                                    |
| **Troca de banco**                 | Substituir SQLite por PostgreSQL só exige nova implementação da interface |
| **Separação de responsabilidades** | Controller não contém lógica; Use Case não conhece HTTP                   |

---

## Observações Importantes

- **Autenticação hardcoded**: `control-access.ts` usa usuários estáticos — deve ser substituído por integração com banco de dados em produção.
- **Dual database**: PostgreSQL (Prisma) para dados da aplicação; MongoDB (Mongoose) para logs de auditoria e formulários.
- **Audit trail completo**: Toda requisição HTTP e chamada externa é registrada no MongoDB via middleware Sniffer e serviços de contato.
- **Token rotation**: Refresh tokens são rotacionados a cada renovação.
