19 KiB
19 KiB
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
// 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
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
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
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
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
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
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
// 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
// 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
// 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
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:
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:
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:
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:
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
- Sempre trate erros: Use try/catch em todas as chamadas de API
- Valide dados no frontend: Reduza requisições inválidas
- Use cache quando apropriado: Reduza carga no servidor
- Implemente retry logic: Para operações críticas
- Monitore saúde do sistema: Detecte problemas cedo
- Gerencie tokens JWT: Renove antes de expirar
- Use WebSocket para operações longas: Melhor UX
- Log erros adequadamente: Facilita debugging
Última Atualização: 2024