saveinmed/saveinmed-bff/README.md
Tiago Yamamoto b39caf0fd0 first commit
2025-12-17 13:58:26 -03:00

239 lines
9.8 KiB
Markdown

# Save in Med BFF
BFF para a plataforma Save in Med, integrando serviços do Appwrite (Databases) e expondo rotas de categorias, subcategorias, usuários, empresas, endereços, produtos de catálogo, carrinhos, pedidos e pagamentos.
## Pré-requisitos
- Python 3.12+
- Docker (opcional)
## Configuração & Segurança
A aplicação utiliza variáveis de ambiente carregadas via arquivo `.env`. Utilize o arquivo `.env.example` como base.
Variáveis obrigatórias:
- `APPWRITE_ENDPOINT`: URL do serviço Appwrite.
- `APPWRITE_PROJECT_ID`: identificador do projeto Appwrite.
- `APPWRITE_API_KEY`: chave da API Appwrite com permissões para Databases.
- `APPWRITE_DATABASE_ID`: identificador do banco de dados utilizado.
- `APPWRITE_COLLECTION_PRODUTOS_CATALOGO_ID`: identificador da coleção de produtos de catálogo.
- `APPWRITE_COLLECTION_LABORATORIOS_ID`: identificador da coleção de laboratórios.
- `APPWRITE_COLLECTION_CATEGORIAS_ID`: identificador da coleção de categorias.
- `APPWRITE_COLLECTION_SUBCATEGORIAS_ID`: identificador da coleção de subcategorias de produtos.
- `APPWRITE_COLLECTION_CARRINHOS_ID`: identificador da coleção de carrinhos.
- `APPWRITE_COLLECTION_PEDIDOS_ID`: identificador da coleção de pedidos.
- `APPWRITE_COLLECTION_USUARIOS_ID`: identificador da coleção de usuários.
- `APPWRITE_COLLECTION_EMPRESAS_ID`: identificador da coleção de empresas.
- `APPWRITE_COLLECTION_EMPRESAS_DADOS_ID`: identificador da coleção com os dados enriquecidos de empresas (ReceitaWS).
- `APPWRITE_COLLECTION_ENDERECOS_ID`: identificador da coleção de endereços.
- `APPWRITE_COLLECTION_ENTREGAS_ID`: identificador da coleção de entregas.
- `MAPBOX_ACCESS_TOKEN`: token de acesso para geocodificação automática dos endereços das empresas.
Variáveis opcionais:
- `SECURITY_API_KEY`: chave para proteger as rotas com o header `x-api-key`.
- `CORS_ALLOW_ORIGINS`: lista separada por vírgula de origens autorizadas no CORS. Se vazia, `*` é utilizado.
### Campos adicionais na coleção `empresas-dados` (Appwrite)
Crie os atributos abaixo na coleção `empresas-dados` para suportar frete e geocodificação automática:
- `tipo-frete` (string, ex.: `gratis`, `fixo`, `por_km`)
- `taxa-entrega` (double/decimal)
- `valor-frete-km` (double/decimal)
- `raio-entrega-km` (double/decimal)
- `latitude` (double/decimal)
- `longitude` (double/decimal)
Os novos campos serão preenchidos automaticamente a partir do endereço relacionado (`enderecos`).
Variáveis de autenticação e sessão:
- `JWT_SECRET`: segredo para assinar/verificar JWT.
- `JWT_ALGORITHM`: algoritmo (ex.: `HS256`).
- `JWT_ACCESS_TTL_SECONDS`: validade do token (segundos).
- `SESSION_COOKIE_NAME`: nome do cookie httpOnly.
- `COOKIE_SECURE`: `true` para setar cookie com `Secure` (produção HTTPS).
- `COOKIE_DOMAIN`: domínio do cookie (opcional).
- `SUPERADMIN_IDENTIFICADOR` e `SUPERADMIN_SENHA`: credenciais para criar usuário superadmin no startup. A senha é armazenada com `bcrypt_sha256`, permitindo valores longos (recomendado usar frases secretas robustas). Não utilize chaves de API como senha.
Quando `SECURITY_API_KEY` está definido com um valor não vazio, todas as rotas passam a exigir o header `x-api-key` correspondente. Essa validação é feita via middleware, portanto o header **não** aparece na documentação do Swagger. Caso a variável esteja vazia ou ausente, nenhuma checagem é feita — ideal para ambientes de desenvolvimento.
## Endpoints
- `GET /``{ "message": "Hello, World!" }`
- `GET /healthz``{ "status": "ok" }`
- `GET /api/v1/ping``{ "pong": true }`
- `POST /api/v1/auth/register`
- `POST /api/v1/auth/login`
- `POST /api/v1/auth/logout`
- `GET /api/v1/auth/me`
- `GET /api/v1/categorias`
- `GET /api/v1/subcategorias`
- `GET /api/v1/usuarios`
- `GET /api/v1/empresas`
- `GET /api/v1/enderecos`
- `GET /api/v1/produtos-catalogo`
- `GET /api/v1/carrinhos`
- `GET /api/v1/pedidos`
## Como rodar em dev
```bash
pip install -r requirements.txt
uvicorn src.main:app --reload
# Docker
docker build -t saveinmed-bff .
docker run -p 8000:8000 --env-file .env saveinmed-bff
```
## Testes rápidos com curl
```bash
curl http://localhost:8000/
curl http://localhost:8000/healthz
curl http://localhost:8000/api/v1/ping
curl http://localhost:8000/api/v1/carrinhos
curl http://localhost:8000/api/v1/pedidos
```
> Adicione o header `-H "x-api-key: <token>"` nos exemplos abaixo quando a variável `SECURITY_API_KEY` estiver configurada.
### Auth
```bash
# Registro de usuário (senha é armazenada com hash no Appwrite)
curl -X POST "http://localhost:8000/api/v1/auth/register" \
-H "Content-Type: application/json" \
-d '{"identificador": "user@example.com", "nome": "Nome do Usuário", "senha": "senha123"}'
# Login retorna o JWT e seta o cookie httpOnly
curl -i -X POST "http://localhost:8000/api/v1/auth/login" \
-H "Content-Type: application/json" \
-d '{"identificador": "user@example.com", "senha": "senha123"}'
# Após o login, o cookie é enviado automaticamente pelo navegador/Swagger
curl -b cookiejar.txt -c cookiejar.txt http://localhost:8000/api/v1/auth/me
# Logout remove o cookie httpOnly
curl -X POST http://localhost:8000/api/v1/auth/logout
```
> Rotas públicas: `/`, `/healthz`, `/api/v1/ping` e todas as rotas sob `/api/v1/auth/*`. As demais rotas exigem autenticação via JWT (Bearer ou cookie httpOnly).
Durante o startup, caso nenhum usuário superadmin exista na coleção, o BFF cria automaticamente um registro utilizando as credenciais definidas em `SUPERADMIN_IDENTIFICADOR` e `SUPERADMIN_SENHA`. Se a senha estiver vazia ou muito longa (> 1024 caracteres), o sistema registra um aviso no log e gera uma senha temporária aleatória.
### Categorias
```bash
curl -X GET "http://localhost:8000/api/v1/categorias?page=1&per_page=20"
curl -X POST "http://localhost:8000/api/v1/categorias" \
-H "Content-Type: application/json" \
-d '{"nome": "Dermocosméticos"}'
curl -X GET "http://localhost:8000/api/v1/categorias/cat_1"
curl -X PATCH "http://localhost:8000/api/v1/categorias/cat_1" \
-H "Content-Type: application/json" \
-d '{"nome": "Dermocosméticos premium"}'
curl -X DELETE "http://localhost:8000/api/v1/categorias/cat_1"
```
### Subcategorias
```bash
curl -X GET "http://localhost:8000/api/v1/subcategorias?page=1&per_page=20"
curl -X POST "http://localhost:8000/api/v1/subcategorias" \
-H "Content-Type: application/json" \
-d '{"nome": "Suplementos", "categoriaId": "cat_1"}'
curl -X GET "http://localhost:8000/api/v1/subcategorias/sub_1"
curl -X PATCH "http://localhost:8000/api/v1/subcategorias/sub_1" \
-H "Content-Type: application/json" \
-d '{"nome": "Suplementos esportivos"}'
curl -X DELETE "http://localhost:8000/api/v1/subcategorias/sub_1"
```
### Usuarios
```bash
curl -X GET "http://localhost:8000/api/v1/usuarios?page=1&per_page=20"
curl -X POST "http://localhost:8000/api/v1/usuarios" \
-H "Content-Type: application/json" \
-d '{"identificador": "admin@saveinmed.com", "senha": "senha123", "ativo": true, "superadmin": true, "telefone": "11987654321"}'
curl -X GET "http://localhost:8000/api/v1/usuarios/user_1"
curl -X PATCH "http://localhost:8000/api/v1/usuarios/user_1" \
-H "Content-Type: application/json" \
-d '{"ativo": false}'
curl -X DELETE "http://localhost:8000/api/v1/usuarios/user_1"
```
### Empresas
```bash
curl -X GET "http://localhost:8000/api/v1/empresas?page=1&per_page=20"
curl -X POST "http://localhost:8000/api/v1/empresas" \
-H "Content-Type: application/json" \
-d '{"cnpj": "12.345.678/0001-00", "razaoSocial": "Clínica Alfa LTDA", "nomeFantasia": "Clínica Alfa"}'
curl -X GET "http://localhost:8000/api/v1/empresas/emp_1"
curl -X PATCH "http://localhost:8000/api/v1/empresas/emp_1" \
-H "Content-Type: application/json" \
-d '{
"nome-fantasia": "Clínica Alfa Centro",
"telefone": "62999999999",
"email": "contato@clinicaalfa.com"
}'
curl -X DELETE "http://localhost:8000/api/v1/empresas/emp_1"
```
### Enderecos
```bash
curl -X GET "http://localhost:8000/api/v1/enderecos?page=1&per_page=20"
curl -X POST "http://localhost:8000/api/v1/enderecos" \
-H "Content-Type: application/json" \
-d '{"titulo": "Matriz", "cep": "12345-678", "cidade": "São Paulo", "estado": "SP"}'
curl -X GET "http://localhost:8000/api/v1/enderecos/end_1"
curl -X PATCH "http://localhost:8000/api/v1/enderecos/end_1" \
-H "Content-Type: application/json" \
-d '{"numero": "456"}'
curl -X DELETE "http://localhost:8000/api/v1/enderecos/end_1"
```
## Estrutura do projeto
```
.
├─ src/
│ ├─ __init__.py
│ ├─ main.py
│ ├─ app/
│ │ ├─ __init__.py
│ │ ├─ main.py
│ │ └─ api/
│ │ ├─ __init__.py
│ │ └─ v1/
│ │ ├─ __init__.py
│ │ └─ routes.py
│ ├─ core/
│ │ ├─ __init__.py
│ │ ├─ appwrite_client.py
│ │ ├─ config.py
│ │ ├─ deps.py
│ │ └─ security.py
│ └─ modules/
│ ├─ __init__.py
│ ├─ categorias/
│ │ ├─ __init__.py
│ │ ├─ router.py
│ │ ├─ schemas.py
│ │ └─ service.py
│ ├─ carrinhos/
│ │ ├─ __init__.py
│ │ ├─ router.py
│ │ ├─ schemas.py
│ │ └─ service.py
│ ├─ pedidos/
│ │ ├─ __init__.py
│ │ ├─ router.py
│ │ ├─ schemas.py
│ │ └─ service.py
│ └─ produtos_catalogo/
│ ├─ __init__.py
│ ├─ router.py
│ ├─ schemas.py
│ └─ service.py
├─ requirements.txt
├─ Dockerfile
├─ README.md
└─ .gitignore
```
## Próximos passos
- Criar as relações no Appwrite para os campos `usuarios`, `pedidos`, `comprador`, `vendedor` e `pagamentos` das coleções de carrinhos e pedidos. Quando as relações estiverem disponíveis, ajustar o BFF para enviar/receber os objetos de relação em vez de strings.
## Licença
MIT