Files
OllaHubBackend/docs/API_EXAMPLES.md
T

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

  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