# Biometria Rekognition Embed

Embed funcional de biometria para uso como biblioteca NPM.

Essa versão usa apenas soluções gratuitas e open-source:

- frontend em TypeScript + Vite
- backend em Python + FastAPI
- detecção facial inicial no backend com OpenCV + Haar Cascade
- sem MediaPipe, sem modelo remoto, sem CDN de visão computacional

## O que este pacote entrega

- `BiometriaEmbed.init(...)` para montar o widget em qualquer aplicação web
- módulos modulares:
  - prova de vida
  - identificação de idade
  - base visual para biometria facial
- styles empacotados junto com o bundle
- build em formato `ES` e `UMD`

## Estrutura

```txt
/frontend
/backend
README.md
```

## Requisitos

- Node.js 18+
- Python 3.10+
- Navegador com acesso à câmera

## Como rodar localmente

### Backend

```bash
cd backend
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000
```

### Frontend

```bash
cd frontend
npm install
npm run dev
```

Abra:

- Frontend: `http://localhost:5173`
- Backend: `http://localhost:8000`
- Docs da API: `http://localhost:8000/docs`

## Como gerar a lib para NPM

```bash
cd frontend
npm install
npm run build
```

O build gera primeiro os bundles JS/CSS e depois os `.d.ts`, para manter tudo no `dist`.

Saída esperada em `frontend/dist`:

- `biometria-embed.js`
- `biometria-embed.umd.cjs`
- `rekognition-embed.css`
- `index.d.ts`

## Como publicar

Depois do build, você pode publicar a pasta `dist` no registry que usa no projeto Angular.

Exemplo com NPM:

```bash
npm publish --access public
```

Se o pacote for privado, publique em um registry interno ou use scope privado.

## Como validar antes de publicar

```bash
npm pack --dry-run
```

Esse comando deve listar os arquivos em `dist`, incluindo:

- `dist/index.d.ts`
- `dist/biometria-embed.js`
- `dist/biometria-embed.umd.cjs`
- `dist/rekognition-embed.css`

## Como consumir no Angular

```ts
import { BiometriaEmbed } from '@tiagohsf_gf/rekognition-embed';

ngAfterViewInit(): void {
  this.instance = BiometriaEmbed.init({
    container: this.container.nativeElement,
    sessionId: this.sessionId,
    apiBaseUrl: environment.apiUrl,
    entryMode: 'selection',
    modules: {
      provaDeVida: true,
      ageEstimation: true,
      facialBiometrics: true,
    },
    callbacks: {
      onSuccess: (result) => console.log(result),
      onAgeEstimated: (result) => console.log(result),
      onError: (error) => console.error(error),
    },
  });
}

ngOnDestroy(): void {
  this.instance?.destroy();
}
```

Se preferir carregar o CSS separadamente, importe:

```ts
import '@tiagohsf_gf/rekognition-embed/style.css';
```

## White-label

O embed aceita tema e branding por configuração, para você adaptar a aparência ao projeto que estiver integrando.

Exemplo:

```ts
this.instance = BiometriaEmbed.init({
  container: this.container.nativeElement,
  sessionId: this.sessionId,
  apiBaseUrl: environment.apiUrl,
  entryMode: 'auto',
  modules: {
    provaDeVida: true,
    ageEstimation: true,
    facialBiometrics: true,
  },
  theme: {
    backgroundColor: '#07111a',
    surfaceColor: '#091321',
    cardColor: '#0b1220',
    primaryColor: '#6ee56a',
    secondaryColor: '#46b84b',
    textPrimaryColor: '#f8fbff',
    textSecondaryColor: '#d6deea',
    borderColor: 'rgba(255, 255, 255, 0.1)',
    outlineColor: '#2563eb',
    helperBackgroundColor: '#1d4ed8',
    helperTextColor: '#ffffff',
    selectionAccentColor: '#6ee56a',
  },
  branding: {
    logoUrl: 'https://meu-dominio.com/minha-logo.svg',
    logoAlt: 'Minha marca',
  },
});
```

Se `branding.logoUrl` não for informado, o embed usa automaticamente a logo do DealSafe como fallback.

Comportamento de entrada:

- `entryMode: 'selection'` força a tela inicial de seleção, útil para demo local.
- `entryMode` ausente usa o modo automático da lib.
- No modo automático, o widget abre direto no primeiro módulo habilitado seguindo a ordem:
  - `ageEstimation`
  - `provaDeVida`
  - `facialBiometrics`
- O topo do módulo mostra um stepper interno com a etapa atual e o nome do módulo.
- Se um módulo estiver desabilitado, ele simplesmente é pulado na sequência.

## Como testar

1. Suba o backend.
2. Suba o frontend.
3. Abra o frontend no navegador.
4. Permita a câmera.
5. Clique em `Iniciar prova de vida`.
6. Mova o rosto até o backend responder com `Tudo certo, pode tirar a selfie`.
7. Clique em `Tirar selfie`.

O frontend envia frames periodicamente para o backend por um websocket persistente, em média de 2 a 4 frames por segundo, sem chamadas simultâneas de HTTP.

## Fluxo atual do embed

1. O navegador abre a câmera com `navigator.mediaDevices.getUserMedia`.
2. O frontend captura um frame em `canvas`.
3. O frame é enviado por websocket para `/api/v1/liveness/analyze-frame/ws`.
4. O backend analisa o rosto com OpenCV/Haar Cascade.
5. O backend devolve `instruction`, `checks`, `metrics` e `canCapture`.
6. O widget usa essa resposta para liberar ou bloquear a selfie final.
7. A selfie final também é validada no backend antes de ser salva em `POST /api/v1/liveness/sessions/{session_id}/capture`.

## O que foi implementado

### Frontend

- SDK global `BiometriaEmbed`
- Widget em TypeScript sem React
- Camera preview com `getUserMedia`
- Captura de frame via `canvas`
- Análise delegada ao backend Python
- Overlay visual alinhado ao vídeo
- Feedback por instrução:
  - `Nenhum rosto detectado`
  - `Aproxime o rosto`
  - `Afaste um pouco`
  - `Centralize seu rosto`
  - `Tudo certo, pode tirar a selfie`
- Botão `Tirar selfie` só habilita quando `canCapture` vem verdadeiro do backend
- Limite de envio de frames para evitar spam
- Logs de debug opcionais
- exportação como biblioteca consumível por outros projetos

### Backend

- FastAPI com CORS liberado para localhost
- Criação de sessão
- Endpoint de análise:
  - `POST /api/liveness/analyze-frame`
- Endpoint de captura final:
  - `POST /api/v1/liveness/sessions/{session_id}/capture`
- Suporte a base64 e multipart/form-data
- Detecção facial com OpenCV + Haar Cascade
- Resposta padronizada em JSON

### Resposta da análise

```json
{
  "success": true,
  "module": "liveness",
  "status": "valid",
  "instruction": "Tudo certo, pode tirar a selfie",
  "canCapture": true,
  "checks": {
    "faceDetected": true,
    "centered": true,
    "distanceOk": true,
    "tooClose": false,
    "tooFar": false
  },
  "metrics": {
    "frameWidth": 640,
    "frameHeight": 480,
    "faceX": 210,
    "faceY": 120,
    "faceWidth": 180,
    "faceHeight": 210,
    "faceCenterX": 300,
    "faceCenterY": 225,
    "frameCenterX": 320,
    "frameCenterY": 240,
    "faceWidthRatio": 0.2812,
    "faceHeightRatio": 0.4375,
    "centerOffsetX": 0.0312,
    "centerOffsetY": 0.0312
  }
}
```

### Resposta da captura final

```json
{
  "success": true,
  "module": "liveness",
  "status": "approved",
  "reason": "face_centered_and_stable",
  "metadata": {}
}
```

## Regras de validação

As tolerâncias atuais ficam em:

- [`frontend/src/components/BiometriaWidget.ts`](/home/tiago/Desktop/projects/gf-rekognition/frontend/src/components/BiometriaWidget.ts)
- [`backend/app/services/liveness_service.py`](/home/tiago/Desktop/projects/gf-rekognition/backend/app/services/liveness_service.py)

No backend, os thresholds principais são:

- `ANALYSIS.CENTER_TOLERANCE_X`
- `ANALYSIS.CENTER_TOLERANCE_Y`
- `ANALYSIS.MIN_WIDTH_RATIO`
- `ANALYSIS.MIN_HEIGHT_RATIO`
- `ANALYSIS.MAX_WIDTH_RATIO`
- `ANALYSIS.MAX_HEIGHT_RATIO`

Ordem prática da validação:

1. detectar rosto;
2. se não houver rosto, bloquear captura;
3. se o rosto estiver pequeno demais, pedir para aproximar;
4. se estiver grande demais, pedir para afastar;
5. se o rosto estiver fora do centro, pedir para centralizar;
6. se tudo estiver ok, liberar a selfie.

## Como ativar debug

Você pode ligar debug de duas formas:

```ts
BiometriaEmbed.init({
  container: "#biometria-container",
  sessionId: "test-session",
  apiBaseUrl: "http://localhost:8000",
  modules: { provaDeVida: true },
  debug: true
});
```

Ou no console:

```js
localStorage.setItem("biometria-debug", "1")
```

Em debug o frontend imprime:

- `frameWidth`
- `frameHeight`
- `faceCenterX`
- `faceCenterY`
- `frameCenterX`
- `frameCenterY`
- `faceWidthRatio`
- `faceHeightRatio`
- `centerOffsetX`
- `centerOffsetY`
- `status`
- `instruction`
- `canCapture`

## Dependências

### Backend

Instaladas com:

```bash
pip install -r requirements.txt
```

Pacotes principais:

- `fastapi`
- `uvicorn`
- `opencv-python`
- `numpy`
- `python-multipart`
- `pydantic`

### Frontend

Instaladas com:

```bash
npm install
```

O frontend não depende mais de MediaPipe nem de modelo remoto.

### Build da biblioteca

O build de publicação usa:

- `npm run build:types`
- `vite build --mode library`

Isso gera os artefatos para empacotamento.

## Endpoints

- `POST /api/v1/liveness/sessions`
- `POST /api/liveness/analyze-frame`
- `POST /api/v1/liveness/sessions/{session_id}/capture`
- `POST /api/v1/liveness/sessions/{session_id}/capture/upload`
- `GET /api/v1/liveness/sessions/{session_id}`

## Limitações

Esse MVP faz apenas validação inicial de rosto e enquadramento.

O Haar Cascade do OpenCV:

- é gratuito e simples de manter
- funciona bem para MVP
- não é anti-fraude real
- não substitui liveness avançado

## Próximas melhorias para liveness real

1. Detectar piscada e movimento ocular.
2. Adicionar desafio dinâmico, como virar o rosto.
3. Validar micro-movimentos e respiração.
4. Incluir anti-spoofing dedicado.
5. Adicionar detecção de deepfake.
6. Persistir sessões em banco.
7. Assinar sessões com JWT.
8. Publicar o SDK como pacote versionado.
