# 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: "` 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