797 lines
19 KiB
Markdown
797 lines
19 KiB
Markdown
# Exemplos Práticos de Uso da API - OllaHubBackend
|
|
|
|
Este documento contém exemplos práticos e casos de uso comuns para integração com a API do OllaHubBackend.
|
|
|
|
---
|
|
|
|
## Configuração Inicial
|
|
|
|
### TypeScript/JavaScript - Cliente Base
|
|
|
|
```typescript
|
|
// api-client.ts
|
|
const API_BASE_URL = 'http://127.0.0.1:8080';
|
|
const WS_BASE_URL = 'ws://127.0.0.1:8080';
|
|
|
|
class OllaHubClient {
|
|
private token: string | null = null;
|
|
|
|
constructor(token?: string) {
|
|
this.token = token || null;
|
|
}
|
|
|
|
setToken(token: string) {
|
|
this.token = token;
|
|
}
|
|
|
|
private async request<T>(
|
|
endpoint: string,
|
|
options: RequestInit = {}
|
|
): Promise<T> {
|
|
const headers: HeadersInit = {
|
|
'Content-Type': 'application/json',
|
|
...options.headers,
|
|
};
|
|
|
|
if (this.token) {
|
|
headers['Authorization'] = `Bearer ${this.token}`;
|
|
}
|
|
|
|
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
|
|
...options,
|
|
headers,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json();
|
|
throw new Error(error.error || `HTTP ${response.status}`);
|
|
}
|
|
|
|
if (response.status === 204) {
|
|
return null as T;
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
// Health
|
|
async health(): Promise<{ status: string }> {
|
|
return this.request('/v2/health');
|
|
}
|
|
|
|
// Modelos
|
|
async listModels(): Promise<Array<{ name: string; description?: string }>> {
|
|
return this.request('/v2/api/models');
|
|
}
|
|
|
|
async installModel(name: string): Promise<{ status: string; model: string }> {
|
|
return this.request('/v2/api/models/install', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ name }),
|
|
});
|
|
}
|
|
|
|
// Criptografia
|
|
async setCryptoKey(hexKey: string): Promise<{ ok: boolean }> {
|
|
return this.request('/v2/api/crypto/set-key', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ hex_key: hexKey }),
|
|
});
|
|
}
|
|
|
|
async rotateCryptoKey(): Promise<{ key: string }> {
|
|
return this.request('/v2/api/crypto/rotate-key', {
|
|
method: 'POST',
|
|
});
|
|
}
|
|
|
|
async encrypt(data: string): Promise<{ ciphertext: string }> {
|
|
return this.request('/v2/api/crypto/encrypt', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ data }),
|
|
});
|
|
}
|
|
|
|
async decrypt(ciphertext: string): Promise<{ plaintext: string }> {
|
|
return this.request('/v2/api/crypto/decrypt', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ data: ciphertext }),
|
|
});
|
|
}
|
|
|
|
// Usuários
|
|
async listUsers(): Promise<Array<{ id: string; name: string; email: string }>> {
|
|
return this.request('/api/v1/users');
|
|
}
|
|
|
|
async getUser(id: string): Promise<{ id: string; name: string; email: string }> {
|
|
return this.request(`/api/v1/users/${id}`);
|
|
}
|
|
|
|
async createUser(data: { name: string; email: string }): Promise<{ id: string; name: string; email: string }> {
|
|
return this.request('/api/v1/users', {
|
|
method: 'POST',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async updateUser(id: string, data: { name?: string; email?: string }): Promise<{ id: string; name: string; email: string }> {
|
|
return this.request(`/api/v1/users/${id}`, {
|
|
method: 'PUT',
|
|
body: JSON.stringify(data),
|
|
});
|
|
}
|
|
|
|
async deleteUser(id: string): Promise<void> {
|
|
return this.request(`/api/v1/users/${id}`, {
|
|
method: 'DELETE',
|
|
});
|
|
}
|
|
|
|
// WebSocket
|
|
createWebSocket(): WebSocket {
|
|
return new WebSocket(`${WS_BASE_URL}/v2/ws`);
|
|
}
|
|
}
|
|
|
|
export default OllaHubClient;
|
|
```
|
|
|
|
---
|
|
|
|
## Casos de Uso
|
|
|
|
### 1. Verificar Status do Sistema
|
|
|
|
```typescript
|
|
import OllaHubClient from './api-client';
|
|
|
|
const client = new OllaHubClient();
|
|
|
|
async function checkSystem() {
|
|
try {
|
|
const health = await client.health();
|
|
console.log('Sistema OK:', health);
|
|
} catch (error) {
|
|
console.error('Sistema indisponível:', error);
|
|
}
|
|
}
|
|
|
|
checkSystem();
|
|
```
|
|
|
|
---
|
|
|
|
### 2. Verificar e Instalar Ollama
|
|
|
|
```typescript
|
|
async function checkAndInstallOllama() {
|
|
try {
|
|
// Verificar status
|
|
const response = await fetch('http://127.0.0.1:8080/ollama/install-check');
|
|
const status = await response.json();
|
|
|
|
console.log('Status Ollama:', {
|
|
instalado: status.installed,
|
|
rodando: status.service_running,
|
|
api: status.api_status,
|
|
});
|
|
|
|
if (!status.installed || status.api_status !== 'reachable') {
|
|
console.log('Iniciando instalação...');
|
|
|
|
const installResponse = await fetch('http://127.0.0.1:8080/ollama/install', {
|
|
method: 'POST',
|
|
});
|
|
|
|
const installResult = await installResponse.json();
|
|
|
|
if (installResult.installed && installResult.service_started) {
|
|
console.log('Ollama instalado e rodando!');
|
|
} else {
|
|
console.error('Falha na instalação:', installResult.issues);
|
|
if (installResult.manual_instructions) {
|
|
console.log('Instruções manuais:', installResult.manual_instructions);
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Erro:', error);
|
|
}
|
|
}
|
|
|
|
checkAndInstallOllama();
|
|
```
|
|
|
|
---
|
|
|
|
### 3. Listar e Instalar Modelos com Progresso
|
|
|
|
```typescript
|
|
import OllaHubClient from './api-client';
|
|
|
|
const client = new OllaHubClient('seu_token_jwt');
|
|
|
|
async function installModelWithProgress(modelName: string) {
|
|
return new Promise<void>((resolve, reject) => {
|
|
// 1. Conectar WebSocket
|
|
const ws = client.createWebSocket();
|
|
|
|
let modelInstalled = false;
|
|
|
|
ws.onopen = async () => {
|
|
console.log('WebSocket conectado');
|
|
|
|
// 2. Iniciar instalação
|
|
try {
|
|
await client.installModel(modelName);
|
|
console.log(`Instalação de ${modelName} iniciada`);
|
|
} catch (error) {
|
|
console.error('Erro ao iniciar instalação:', error);
|
|
ws.close();
|
|
reject(error);
|
|
}
|
|
};
|
|
|
|
ws.onmessage = (event) => {
|
|
const data = JSON.parse(event.data);
|
|
|
|
switch (data.kind) {
|
|
case 'Log':
|
|
console.log('📝', data.message);
|
|
break;
|
|
|
|
case 'Progress':
|
|
try {
|
|
const progress = JSON.parse(data.message);
|
|
console.log('📊 Progresso:', progress);
|
|
} catch {
|
|
console.log('📊', data.message);
|
|
}
|
|
break;
|
|
|
|
case 'Done':
|
|
console.log('✅', data.message);
|
|
modelInstalled = true;
|
|
ws.close();
|
|
resolve();
|
|
break;
|
|
|
|
case 'Error':
|
|
console.error('❌ Erro:', data.message);
|
|
ws.close();
|
|
reject(new Error(data.message));
|
|
break;
|
|
}
|
|
};
|
|
|
|
ws.onerror = (error) => {
|
|
console.error('Erro WebSocket:', error);
|
|
reject(error);
|
|
};
|
|
|
|
ws.onclose = () => {
|
|
if (!modelInstalled) {
|
|
reject(new Error('Conexão WebSocket fechada antes da conclusão'));
|
|
}
|
|
};
|
|
});
|
|
}
|
|
|
|
// Uso
|
|
async function main() {
|
|
try {
|
|
// Listar modelos disponíveis
|
|
const models = await client.listModels();
|
|
console.log('Modelos disponíveis:', models);
|
|
|
|
// Instalar um modelo
|
|
await installModelWithProgress('llama2:7b');
|
|
console.log('Modelo instalado com sucesso!');
|
|
} catch (error) {
|
|
console.error('Erro:', error);
|
|
}
|
|
}
|
|
|
|
main();
|
|
```
|
|
|
|
---
|
|
|
|
### 4. Sistema de Criptografia Completo
|
|
|
|
```typescript
|
|
import OllaHubClient from './api-client';
|
|
|
|
const client = new OllaHubClient('seu_token_jwt');
|
|
|
|
async function cryptoWorkflow() {
|
|
try {
|
|
// 1. Gerar nova chave
|
|
console.log('Gerando nova chave de criptografia...');
|
|
const { key } = await client.rotateCryptoKey();
|
|
console.log('Chave gerada:', key);
|
|
|
|
// Salvar chave localmente (ex: localStorage, secure storage)
|
|
localStorage.setItem('crypto_key', key);
|
|
|
|
// 2. Criptografar dados sensíveis
|
|
const sensitiveData = 'senha_super_secreta_123';
|
|
console.log('Criptografando dados...');
|
|
const { ciphertext } = await client.encrypt(sensitiveData);
|
|
console.log('Dados criptografados:', ciphertext);
|
|
|
|
// 3. Descriptografar
|
|
console.log('Descriptografando...');
|
|
const { plaintext } = await client.decrypt(ciphertext);
|
|
console.log('Dados descriptografados:', plaintext);
|
|
|
|
if (plaintext === sensitiveData) {
|
|
console.log('✅ Criptografia funcionando corretamente!');
|
|
}
|
|
} catch (error) {
|
|
console.error('Erro no fluxo de criptografia:', error);
|
|
}
|
|
}
|
|
|
|
// Usar chave existente
|
|
async function useExistingKey() {
|
|
const savedKey = localStorage.getItem('crypto_key');
|
|
|
|
if (!savedKey) {
|
|
console.error('Chave não encontrada');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
await client.setCryptoKey(savedKey);
|
|
console.log('Chave configurada com sucesso');
|
|
} catch (error) {
|
|
console.error('Erro ao configurar chave:', error);
|
|
}
|
|
}
|
|
|
|
cryptoWorkflow();
|
|
```
|
|
|
|
---
|
|
|
|
### 5. Gerenciamento Completo de Usuários
|
|
|
|
```typescript
|
|
import OllaHubClient from './api-client';
|
|
|
|
const client = new OllaHubClient('seu_token_jwt');
|
|
|
|
async function userManagementDemo() {
|
|
try {
|
|
// 1. Criar usuários
|
|
console.log('Criando usuários...');
|
|
const user1 = await client.createUser({
|
|
name: 'João Silva',
|
|
email: '[email protected]',
|
|
});
|
|
console.log('Usuário criado:', user1);
|
|
|
|
const user2 = await client.createUser({
|
|
name: 'Maria Santos',
|
|
email: '[email protected]',
|
|
});
|
|
console.log('Usuário criado:', user2);
|
|
|
|
// 2. Listar todos
|
|
console.log('\nListando usuários...');
|
|
const users = await client.listUsers();
|
|
console.log('Total de usuários:', users.length);
|
|
users.forEach(u => console.log(`- ${u.name} (${u.email})`));
|
|
|
|
// 3. Obter usuário específico
|
|
console.log('\nObtendo usuário específico...');
|
|
const user = await client.getUser(user1.id);
|
|
console.log('Usuário:', user);
|
|
|
|
// 4. Atualizar usuário
|
|
console.log('\nAtualizando usuário...');
|
|
const updated = await client.updateUser(user1.id, {
|
|
name: 'João Silva Atualizado',
|
|
email: '[email protected]',
|
|
});
|
|
console.log('Usuário atualizado:', updated);
|
|
|
|
// 5. Deletar usuário
|
|
console.log('\nDeletando usuário...');
|
|
await client.deleteUser(user2.id);
|
|
console.log('Usuário deletado');
|
|
|
|
// 6. Verificar lista final
|
|
const finalUsers = await client.listUsers();
|
|
console.log('\nUsuários restantes:', finalUsers.length);
|
|
} catch (error) {
|
|
console.error('Erro no gerenciamento de usuários:', error);
|
|
}
|
|
}
|
|
|
|
userManagementDemo();
|
|
```
|
|
|
|
---
|
|
|
|
### 6. Tratamento de Erros Robusto
|
|
|
|
```typescript
|
|
import OllaHubClient from './api-client';
|
|
|
|
const client = new OllaHubClient('seu_token_jwt');
|
|
|
|
async function robustRequest<T>(
|
|
fn: () => Promise<T>,
|
|
retries = 3
|
|
): Promise<T> {
|
|
for (let i = 0; i < retries; i++) {
|
|
try {
|
|
return await fn();
|
|
} catch (error: any) {
|
|
const isLastAttempt = i === retries - 1;
|
|
|
|
if (error.message.includes('401')) {
|
|
throw new Error('Token JWT inválido. Faça login novamente.');
|
|
}
|
|
|
|
if (error.message.includes('500')) {
|
|
if (isLastAttempt) {
|
|
throw new Error('Erro interno do servidor. Tente novamente mais tarde.');
|
|
}
|
|
console.warn(`Tentativa ${i + 1} falhou, tentando novamente...`);
|
|
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
|
|
continue;
|
|
}
|
|
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
throw new Error('Número máximo de tentativas excedido');
|
|
}
|
|
|
|
// Uso
|
|
async function exemploComRetry() {
|
|
try {
|
|
const models = await robustRequest(() => client.listModels());
|
|
console.log('Modelos:', models);
|
|
} catch (error) {
|
|
console.error('Erro após retries:', error);
|
|
}
|
|
}
|
|
|
|
exemploComRetry();
|
|
```
|
|
|
|
---
|
|
|
|
### 7. Hook React para WebSocket
|
|
|
|
```typescript
|
|
// useWebSocket.ts
|
|
import { useEffect, useState, useRef } from 'react';
|
|
|
|
interface WsEvent {
|
|
kind: 'Log' | 'Progress' | 'Done' | 'Error';
|
|
message: string;
|
|
}
|
|
|
|
export function useWebSocket(url: string) {
|
|
const [events, setEvents] = useState<WsEvent[]>([]);
|
|
const [connected, setConnected] = useState(false);
|
|
const wsRef = useRef<WebSocket | null>(null);
|
|
|
|
useEffect(() => {
|
|
const ws = new WebSocket(url);
|
|
wsRef.current = ws;
|
|
|
|
ws.onopen = () => {
|
|
setConnected(true);
|
|
console.log('WebSocket conectado');
|
|
};
|
|
|
|
ws.onmessage = (event) => {
|
|
const data: WsEvent = JSON.parse(event.data);
|
|
setEvents(prev => [...prev, data]);
|
|
};
|
|
|
|
ws.onerror = (error) => {
|
|
console.error('Erro WebSocket:', error);
|
|
};
|
|
|
|
ws.onclose = () => {
|
|
setConnected(false);
|
|
console.log('WebSocket desconectado');
|
|
};
|
|
|
|
return () => {
|
|
ws.close();
|
|
};
|
|
}, [url]);
|
|
|
|
return { events, connected };
|
|
}
|
|
|
|
// Uso no componente
|
|
function ModelInstallation() {
|
|
const { events, connected } = useWebSocket('ws://127.0.0.1:8080/v2/ws');
|
|
const client = new OllaHubClient('token');
|
|
|
|
const handleInstall = async (modelName: string) => {
|
|
try {
|
|
await client.installModel(modelName);
|
|
} catch (error) {
|
|
console.error('Erro:', error);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div>
|
|
<button onClick={() => handleInstall('llama2:7b')}>
|
|
Instalar Modelo
|
|
</button>
|
|
|
|
<div>
|
|
Status: {connected ? 'Conectado' : 'Desconectado'}
|
|
</div>
|
|
|
|
<div>
|
|
{events.map((event, i) => (
|
|
<div key={i}>
|
|
<strong>{event.kind}:</strong> {event.message}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 8. Validação de Dados no Frontend
|
|
|
|
```typescript
|
|
// validators.ts
|
|
export function validateEmail(email: string): boolean {
|
|
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
return re.test(email);
|
|
}
|
|
|
|
export function validateName(name: string): string | null {
|
|
if (name.length < 2) {
|
|
return 'Nome deve ter pelo menos 2 caracteres';
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function validateCryptoKey(hexKey: string): string | null {
|
|
if (!/^[0-9a-fA-F]{64}$/.test(hexKey)) {
|
|
return 'Chave deve ter 64 caracteres hexadecimais (32 bytes)';
|
|
}
|
|
return null;
|
|
}
|
|
|
|
export function validateModelName(name: string): string | null {
|
|
if (!/^[a-zA-Z0-9:._-]+$/.test(name)) {
|
|
return 'Nome do modelo contém caracteres inválidos';
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// Uso
|
|
async function createUserWithValidation(data: { name: string; email: string }) {
|
|
const nameError = validateName(data.name);
|
|
if (nameError) {
|
|
throw new Error(nameError);
|
|
}
|
|
|
|
if (!validateEmail(data.email)) {
|
|
throw new Error('Email inválido');
|
|
}
|
|
|
|
return client.createUser(data);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 9. Cache de Modelos
|
|
|
|
```typescript
|
|
// model-cache.ts
|
|
class ModelCache {
|
|
private cache: Array<{ name: string; description?: string }> | null = null;
|
|
private cacheTime: number = 0;
|
|
private readonly TTL = 5 * 60 * 1000; // 5 minutos
|
|
|
|
async getModels(client: OllaHubClient, forceRefresh = false): Promise<Array<{ name: string; description?: string }>> {
|
|
const now = Date.now();
|
|
|
|
if (!forceRefresh && this.cache && (now - this.cacheTime) < this.TTL) {
|
|
return this.cache;
|
|
}
|
|
|
|
try {
|
|
const models = await client.listModels();
|
|
this.cache = models;
|
|
this.cacheTime = now;
|
|
return models;
|
|
} catch (error) {
|
|
if (this.cache) {
|
|
console.warn('Erro ao atualizar cache, usando cache antigo:', error);
|
|
return this.cache;
|
|
}
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
clear() {
|
|
this.cache = null;
|
|
this.cacheTime = 0;
|
|
}
|
|
}
|
|
|
|
// Uso
|
|
const cache = new ModelCache();
|
|
const models = await cache.getModels(client);
|
|
```
|
|
|
|
---
|
|
|
|
### 10. Monitoramento de Saúde do Sistema
|
|
|
|
```typescript
|
|
async function monitorSystemHealth(intervalMs = 30000) {
|
|
const checkHealth = async () => {
|
|
try {
|
|
const health = await client.health();
|
|
console.log('✅ Sistema saudável:', new Date().toISOString());
|
|
return true;
|
|
} catch (error) {
|
|
console.error('❌ Sistema indisponível:', error);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// Verificação inicial
|
|
await checkHealth();
|
|
|
|
// Verificações periódicas
|
|
setInterval(checkHealth, intervalMs);
|
|
}
|
|
|
|
// Monitorar com alertas
|
|
async function monitorWithAlerts() {
|
|
let consecutiveFailures = 0;
|
|
const MAX_FAILURES = 3;
|
|
|
|
setInterval(async () => {
|
|
const isHealthy = await checkHealth();
|
|
|
|
if (!isHealthy) {
|
|
consecutiveFailures++;
|
|
|
|
if (consecutiveFailures >= MAX_FAILURES) {
|
|
console.error('🚨 ALERTA: Sistema indisponível há múltiplas verificações!');
|
|
// Enviar notificação, email, etc.
|
|
}
|
|
} else {
|
|
consecutiveFailures = 0;
|
|
}
|
|
}, 30000);
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Casos de Erro Comuns e Soluções
|
|
|
|
### Erro 401: Unauthorized
|
|
|
|
**Causa**: Token JWT ausente ou inválido.
|
|
|
|
**Solução**:
|
|
```typescript
|
|
try {
|
|
await client.listModels();
|
|
} catch (error: any) {
|
|
if (error.message.includes('401')) {
|
|
// Redirecionar para login ou renovar token
|
|
console.log('Token inválido, faça login novamente');
|
|
}
|
|
}
|
|
```
|
|
|
|
### Erro 400: Bad Request - Validação
|
|
|
|
**Causa**: Dados de entrada inválidos.
|
|
|
|
**Solução**:
|
|
```typescript
|
|
try {
|
|
await client.createUser({ name: 'A', email: 'invalid' });
|
|
} catch (error: any) {
|
|
if (error.message.includes('validação')) {
|
|
console.error('Erro de validação:', error.message);
|
|
// Mostrar erros específicos ao usuário
|
|
}
|
|
}
|
|
```
|
|
|
|
### Erro 409: Conflict - Chave Já Definida
|
|
|
|
**Causa**: Tentativa de definir chave de criptografia quando já existe.
|
|
|
|
**Solução**:
|
|
```typescript
|
|
try {
|
|
await client.setCryptoKey(key);
|
|
} catch (error: any) {
|
|
if (error.message.includes('já definida')) {
|
|
console.log('Chave já configurada, usando existente');
|
|
// Continuar com operações de criptografia
|
|
}
|
|
}
|
|
```
|
|
|
|
### WebSocket Desconectado
|
|
|
|
**Causa**: Conexão perdida durante operação.
|
|
|
|
**Solução**:
|
|
```typescript
|
|
function createResilientWebSocket(url: string, onReconnect: () => void) {
|
|
let ws: WebSocket;
|
|
let reconnectAttempts = 0;
|
|
const MAX_RECONNECTS = 5;
|
|
|
|
function connect() {
|
|
ws = new WebSocket(url);
|
|
|
|
ws.onopen = () => {
|
|
reconnectAttempts = 0;
|
|
console.log('WebSocket conectado');
|
|
};
|
|
|
|
ws.onclose = () => {
|
|
if (reconnectAttempts < MAX_RECONNECTS) {
|
|
reconnectAttempts++;
|
|
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000);
|
|
console.log(`Reconectando em ${delay}ms...`);
|
|
setTimeout(() => {
|
|
connect();
|
|
onReconnect();
|
|
}, delay);
|
|
} else {
|
|
console.error('Máximo de tentativas de reconexão atingido');
|
|
}
|
|
};
|
|
}
|
|
|
|
connect();
|
|
return ws;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Boas Práticas
|
|
|
|
1. **Sempre trate erros**: Use try/catch em todas as chamadas de API
|
|
2. **Valide dados no frontend**: Reduza requisições inválidas
|
|
3. **Use cache quando apropriado**: Reduza carga no servidor
|
|
4. **Implemente retry logic**: Para operações críticas
|
|
5. **Monitore saúde do sistema**: Detecte problemas cedo
|
|
6. **Gerencie tokens JWT**: Renove antes de expirar
|
|
7. **Use WebSocket para operações longas**: Melhor UX
|
|
8. **Log erros adequadamente**: Facilita debugging
|
|
|
|
---
|
|
|
|
**Última Atualização**: 2024
|
|
|