OllaHubBackend: API HTTP e WebSocket em Rust para instalar e operar o Ollama, com rotas protegidas por JWT ![Rust 2021](https://img.shields.io/badge/Rust-edi%C3%A7%C3%A3o_2021-dea584?style=flat-square&logo=rust&logoColor=white) ![Actix Web 4.11](https://img.shields.io/badge/Actix_Web-4.11-1447e6?style=flat-square) ![Versão 0.1.0](https://img.shields.io/badge/vers%C3%A3o-0.1.0-555?style=flat-square) ![Licença GPL-3.0](https://img.shields.io/badge/licen%C3%A7a-GPL--3.0-00bc7d?style=flat-square) [Referência da API](docs/API.md) · [Exemplos de cliente](docs/API_EXAMPLES.md) · [Issues](https://git.evandro.dev.br/evandro/OllaHubBackend/issues)
## O que é Um servidor HTTP e WebSocket em Rust ([Actix Web](https://actix.rs)) que cuida do [Ollama](https://ollama.com) por você: verifica se ele está instalado e respondendo, instala quando falta, lista e baixa modelos de um catálogo e conversa com eles por WebSocket. Traz também criptografia AES-256-GCM e um CRUD de usuários, com JWT e Swagger UI. O `.env.example` indica que os tokens vêm de um frontend separado. ## Por que usar - **Setup do Ollama num lugar só.** `GET /v2/api/ollama/check` diz se o Ollama está no `PATH`, qual a versão e se a API responde (`reachable`, `unreachable` ou `degraded`). `POST /v2/api/ollama/install` instala por sistema: no Windows baixa o `OllamaSetup.exe` e roda `/silent`, no Linux usa o script oficial e no macOS extrai o zip em `/Applications`. - **Chat que prepara o modelo.** Em `/v2/ws/chat`, se o modelo pedido não estiver instalado, o servidor o baixa antes de responder e avisa o progresso por mensagens de status. - **Contrato documentado pelo código.** O OpenAPI é gerado com [utoipa](https://github.com/juhaku/utoipa) e servido no Swagger UI em `/docs`. **Quando não usar:** exposto na internet ou em produção. Rotas que baixam e executam instaladores são públicas, os usuários ficam só em memória e o chat não faz streaming incremental. Se você só precisa conversar com um Ollama já instalado, a API dele na porta `11434` basta. > [!IMPORTANT] > **Nem toda rota exige JWT.** O middleware só protege `/api/v1/*`, `/v2/api/crypto*` e `/v2/api/models*`. Ficam **públicas** `/health`, `/v2/health`, `/v2/api/ollama/*` (inclusive `POST /v2/api/ollama/install`, que baixa e executa o instalador do Ollama), `/docs` e os WebSockets `/v2/ws*`. Por padrão o servidor escuta só em `127.0.0.1`; só mude `HOST` sabendo disso. O `.env.example` vem com um `JWT_SECRET` preenchido (sem ele, o padrão é `changeme-dev-secret`): troque pelo seu. ## Como funciona - `src/main.rs`, `src/config.rs` e `src/state.rs`: sobem o servidor, leem a configuração das variáveis de ambiente e montam o `AppContext` (eventos, criptografia, URL do Ollama, catálogo, segredo JWT). - `src/api/`: rotas v2 de saúde, modelos, Ollama e criptografia. - `src/core/models.rs`: lê o `modelList.json` (93 modelos; a API expõe `name` e `description`), consulta `/api/tags` e baixa com `/api/pull` do Ollama. - `src/services/install/`: instalação do Ollama por sistema operacional. - `src/ws/`: WebSockets de eventos (`/v2/ws`) e de chat (`/v2/ws/chat`). - `src/crypto/`: AES-256-GCM com nonce aleatório de 12 bytes antes do ciphertext; a chave fica em hex em `/OllaHubBackend/crypto_v2.key`. - `src/middlewares/`: JWT, CORS com uma única origem, log de requisições e erros no formato `{"error": "..."}`. - `src/routes/`, `src/controllers/` e `src/repositories/`: CRUD de usuários em `/api/v1`, com repositório em memória. ## Início rápido Requisitos: Rust (edição 2021) e um Ollama acessível, ou use `POST /v2/api/ollama/install` depois de subir o servidor. > [!WARNING] > **No Windows, o servidor mexe na configuração da máquina ao iniciar.** Se estiver elevado (administrador), cria regras de entrada no Firewall para as portas `8080` e `11434` (perfis Domain, Private e Public) e adiciona uma exclusão do Windows Defender para o próprio executável (`Add-MpPreference`). Sem privilégios de administrador, só registra avisos e segue. ```bash git clone https://git.evandro.dev.br/evandro/OllaHubBackend.git cd OllaHubBackend cp .env.example .env # troque JWT_SECRET e ajuste ALLOWED_ORIGINS cargo run # sobe em http://127.0.0.1:8080 curl http://127.0.0.1:8080/health curl http://127.0.0.1:8080/v2/api/ollama/check curl -H "Authorization: Bearer " http://127.0.0.1:8080/v2/api/models ``` `cargo build --release` gera o binário em `target/release`; `cargo test` roda o único teste do repositório (ida e volta da criptografia). ## Uso Swagger UI em `/docs`, OpenAPI em `/api-doc/openapi.json`. Referência completa em [`docs/API.md`](docs/API.md). | Método | Rota | JWT | O que faz | |---|---|:-:|---| | GET | `/health` | não | Resposta simples `"ok"` | | GET | `/v2/health` | não | `{"status":"ok"}` | | GET | `/v2/api/ollama/check` | não | Estado do Ollama: `PATH`, versão, API alcançável e problemas encontrados | | POST | `/v2/api/ollama/install` | não | `202` com `operation_id`; instala em background e espera a API responder (até 60 s). Falhas não voltam na resposta | | GET | `/v2/api/models` | sim | Modelos do `modelList.json` | | POST | `/v2/api/models/install` | sim | `202`; baixa o modelo (`{"name": "..."}`) em background via `/api/pull` | | POST | `/v2/api/crypto/set-key` | sim | Define a chave AES-256 (`{"hex_key": "<64 hex>"}`) e a grava em disco | | POST | `/v2/api/crypto/rotate-key` | sim | Gera uma chave nova e a devolve em hex | | POST | `/v2/api/crypto/encrypt` | sim | `{"data": "texto"}` → `{"ciphertext": ""}` | | POST | `/v2/api/crypto/decrypt` | sim | `{"data": ""}` → `{"plaintext": "texto"}` | | GET/POST | `/api/v1/users` | sim | Lista ou cria usuário (nome com 2+ caracteres, e-mail válido) | | GET/PUT/DELETE | `/api/v1/users/{id}` | sim | Lê, atualiza ou remove usuário | | WS | `/v2/ws` | não | Eco de mensagens e pong | | WS | `/v2/ws/chat` | não | Chat com um modelo do Ollama | Autenticação: `Authorization: Bearer `, assinado em HS256 com `JWT_SECRET`, com as claims `sub` e `exp` (e `role` opcional). **A API não emite tokens**: o segredo precisa ser o mesmo do serviço que os emite. No `/v2/ws/chat`, envie um JSON por mensagem de texto: ```json { "model": "llama3.2", "messages": [{ "role": "user", "content": "Olá" }], "options": { "temperature": 0.7 } } ``` O servidor responde com status (`checking`, `ready`, `installing` com `progress`, `installed`) e depois com as linhas JSON do `/api/chat` do Ollama. Falhas chegam como `{"error": "...", "done": true}`. Há ping a cada 5 s, e a conexão fecha após 30 s sem atividade do cliente. ## Configuração O `.env` é carregado com `dotenv` na inicialização; as variáveis são lidas em `src/config.rs` e `src/middlewares/`. | Variável | Padrão | Uso | |---|---|---| | `HOST` | `127.0.0.1` | Endereço de escuta | | `PORT` | `8080` | Porta de escuta | | `OLLAMA_BASE_URL` | `http://localhost:11434` | API do Ollama | | `MODEL_LIST_PATH` | `modelList.json` | Catálogo exposto em `/v2/api/models` | | `JWT_SECRET` | `changeme-dev-secret` | Segredo HS256; deve ser o mesmo de quem emite os tokens | | `ALLOWED_ORIGINS` | `http://localhost:3000` | Única origem aceita pelo CORS (com credenciais) | `SKIP_ANTIVIRUS_WARNINGS` aparece no `.env.example`, mas nenhum código lê essa variável. ## Estado do projeto Versão `0.1.0`, com três commits entre 2025-11-05 e 2025-11-18: estrutura inicial com API v2, WebSocket e instalação do Ollama. Limites conhecidos: - **Usuários só em memória:** o CRUD de `/api/v1/users` perde os dados ao reiniciar. - **`/v2/ws` não envia eventos:** só ecoa mensagens e responde a pings. O envio de eventos (`PushEvent`) existe no código, mas nunca é acionado. - **Chat sem streaming incremental:** a resposta do Ollama é lida por inteiro (timeout de 300 s) antes de as linhas serem repassadas. O `pull_model` faz o mesmo, então downloads também não mostram progresso em tempo real. - **Erros silenciosos:** falhas de instalação do Ollama e de download de modelos em background são descartadas. - **Firewall fixo:** a regra criada no Windows usa a porta `8080` literal, mesmo com outro `PORT`. - **Documentação defasada:** [`docs/OLLAMA.md`](docs/OLLAMA.md) e [`docs/API_EXAMPLES.md`](docs/API_EXAMPLES.md) citam rotas antigas como `/ollama/install-check`; as atuais são `/v2/api/ollama/check` e `/v2/api/ollama/install`. ## Licença [GPL-3.0](LICENSE): você pode usar, estudar, modificar e redistribuir o código, desde que versões distribuídas mantenham o código-fonte aberto sob a mesma licença. Construído com [Actix Web](https://actix.rs), [utoipa](https://github.com/juhaku/utoipa), [jsonwebtoken](https://github.com/Keats/jsonwebtoken), [aes-gcm](https://github.com/RustCrypto/AEADs) e [sysinfo](https://github.com/GuillaumeGomez/sysinfo).