npm package: @marcos_feitoza/personal-finance-backend-shared
Description: Este projeto contém código e utilitários compartilhados usados por todos os microsserviços do backend de finanças pessoais.
Latest version: 1.5.4

# Biblioteca Compartilhada - Personal Finance Backend

Este projeto contém código e utilitários compartilhados usados por todos os microsserviços do backend de finanças pessoais.

## Propósito

O objetivo desta biblioteca é centralizar a lógica de negócios, as definições de dados principais e as funcionalidades comuns para evitar a duplicação de código e garantir a consistência, manutenibilidade e observabilidade entre os serviços.

## Conteúdo Principal

- **`database.py`**: Gerenciamento de sessão e configuração de conexão com o banco de dados PostgreSQL.
- **`models.py`**: Definições das tabelas do banco de dados usando SQLAlchemy ORM.
- **`schemas.py`**: Modelos de dados Pydantic para validação de dados em requisições e respostas de API.
- **`crud.py`**: Funções comuns para operações de Create, Read, Update, Delete (CRUD) e lógica de cálculo de portfólio.
- **`auth_utils.py`**: Utilitários para hashing de senhas e manipulação de JWT (JSON Web Tokens).
- **`dependencies.py`**: Dependências do FastAPI para autenticação de usuário (extração de `current_user` e `token`).
- **`logging_utils.py`**: Configuração centralizada para logging estruturado (JSON) com suporte a `Correlation ID`.

Modelos de monetização AI adicionados:

- `users.plan` (`free`, `plus`, `pro`)
- `ai_usage_events` (metering por request AI)
- `ai_usage_credits` (saldo de créditos de overage por usuário)
- `ai_usage_credit_ledger` (movimentações de crédito append-only)
- `ai_notification_deliveries` (estado de entrega de notificações AI por canal)
- `admin_audit_log` (trilha de ações administrativas com before/after, ip e request_id)

Campos de segurança/gestão em `users`:

- `account_status` (`active`/`suspended`)
- `suspended_reason` / `suspended_at`
- `force_password_change`
- `session_revoked_at`

---

## Como Usar (Instalação como Dependência)

Este projeto não é um serviço executável por si só. Ele é projetado para ser instalado como uma dependência local por outros serviços do backend (ex: `personal-finance-backend-core`, `personal-finance-backend-market-data`).

Para incluir esta biblioteca como uma dependência editável em outro serviço (por exemplo, no `requirements.txt` do `personal-finance-backend-core`), adicione a seguinte linha no `requirements.txt` do serviço consumidor:

```
-e ./personal-finance-backend-shared
```

**Importante:** Se o serviço consumidor for construído via `dockerbuild.sh` (como `market-data`), que copia a biblioteca compartilhada para um diretório local temporário, o `requirements.txt` do serviço consumidor deverá ter a linha:

```
-e ./shared-library
```
Nesses casos, certifique-se de que o `dockerbuild.sh` do serviço está copiando a biblioteca corretamente para o contexto de build do Docker.

## Logging

Esta biblioteca contém a implementação centralizada do sistema de **logging estruturado (JSON)**.

- **`logging_utils.py`**: Este módulo fornece a função `setup_logging()` e o `REQUEST_ID_CTX` (para `Correlation ID`).
- **Como Funciona**: Todos os microsserviços do backend que utilizam esta biblioteca podem chamar `setup_logging()` em seu `main.py` para configurar seus loggers para emitir logs em JSON com o `Correlation ID` automaticamente incluído, permitindo rastreabilidade ponta a ponta.
- **Exemplo de Uso**:
  ```python
  # No main.py de um serviço
  from personal_finance_shared.logging_utils import setup_logging
  setup_logging()
  ```

---

## Autenticação (JWT)

Esta biblioteca contém utilitários compartilhados para autenticação via JWT:

- `personal_finance_shared/auth_utils.py`: criação e decode de tokens (atualmente HS256 via `JWT_SECRET_KEY`).
- `personal_finance_shared/dependencies.py`: dependências FastAPI para extrair e validar `Authorization: Bearer <token>` e obter `current_user`.

### Refresh token (Fase 3)

Além do access token, o projeto suporta **refresh token** (JWT assinado com um segredo diferente) para permitir renovar o access token sem exigir relogin.

- `create_refresh_token(...)` / `decode_refresh_token(...)` em `auth_utils.py`
- Persistência **stateful** no Postgres via tabela `refresh_tokens` (somente hash do token + `jti` + expiração + revogação)
- `hash_token(token)` (SHA-256) para nunca armazenar o token bruto no banco

### Variáveis de ambiente

- `JWT_SECRET_KEY` (**obrigatória**): usada para assinar e validar access tokens.

Variáveis reservadas para hardening futuro (documentadas na Fase 0 do plano):

- `JWT_ISSUER`: issuer dos tokens (ex.: `personal-finance-backend-core`).
- `JWT_AUDIENCE`: audience dos tokens (ex.: `personal-finance-frontend`).
- `JWT_REFRESH_SECRET_KEY`: reservado para refresh token.

Variáveis do refresh token:

- `JWT_REFRESH_SECRET_KEY` (**obrigatória para refresh token**) — segredo separado do access token.
- `REFRESH_TOKEN_EXPIRE_DAYS` (opcional, default `30`) — expiração do refresh token.

Opcional (recomendado em produção):

- `JWT_STRICT_ISS_AUD`: se `true`, rejeita tokens sem `iss/aud` (tokens legados)

---

## Autorização (RBAC leve por role)

Além de autenticação, esta biblioteca fornece um helper simples para autorização baseada em `role`.

### `require_roles(*roles)`

Arquivo: `personal_finance_shared/dependencies.py`

Exemplo (proteger endpoint):

```py
from fastapi import Depends
from personal_finance_shared.dependencies import require_roles

@router.get("/admin-only", dependencies=[Depends(require_roles("admin", "support_admin"))])
def admin_only_endpoint():
    return {"ok": True}
```

Ou retornando o usuário já validado:

```py
@router.get("/admin-only")
def admin_only_endpoint(current_user = Depends(require_roles("admin", "support_admin"))):
    return {"user_id": current_user.id}
```

### Observação

Por padrão, o helper valida permissões usando `current_user.role` (fonte de verdade no banco), mesmo que o JWT também carregue a claim `role`.

---

## Autorização por Plano (Monetização)

Além de RBAC por `role`, a biblioteca agora suporta controle por plano:

- campo `users.plan` no modelo de usuário (`free`, `plus`, `pro`)
- helper `require_min_plan("plus" | "pro")`
- normalização de aliases:
  - `bronze -> free`
  - `prata/silver -> plus`
  - `gold -> pro`

Exemplo:

```py
from fastapi import Depends
from personal_finance_shared.dependencies import require_min_plan

@router.get("/investments", dependencies=[Depends(require_min_plan("plus"))])
def investments_only_plus():
    return {"ok": True}
```

---

## Serviços internos e autenticação sem lookup de usuário

Alguns microsserviços internos (ex.: `balance-service`) não precisam carregar o usuário completo do banco (`users`) para funcionar.

Para esses casos, esta biblioteca fornece:

### `get_current_user_id`

Arquivo: `personal_finance_shared/dependencies.py`

Ele valida o JWT e retorna apenas o `user_id` (int), **sem consultar** a tabela `users`.

Isso evita falhas de 401 em serviços que não têm o mesmo contexto de usuário (por exemplo, quando o token mudou e o serviço só precisa filtrar por `user_id`).