UNPKG

mcp-codebase-search

Version:

MCP server for semantic codebase search using embeddings

281 lines (243 loc) 8.05 kB
/** * api.js - Módulo de API REST para aplicação web * * Este módulo contém funções para fazer requisições HTTP e gerenciar * autenticação de usuários em uma aplicação web. */ const API_BASE_URL = 'https://api.exemplo.com/v1'; /** * Classe para gerenciar requisições HTTP */ class ApiClient { constructor(baseUrl = API_BASE_URL) { this.baseUrl = baseUrl; this.token = localStorage.getItem('auth_token'); } /** * Configura o token de autenticação * @param {string} token - Token JWT */ setAuthToken(token) { this.token = token; if (token) { localStorage.setItem('auth_token', token); } else { localStorage.removeItem('auth_token'); } } /** * Faz uma requisição HTTP genérica * @param {string} endpoint - Endpoint da API * @param {Object} options - Opções da requisição * @returns {Promise<Object>} Resposta da API */ async request(endpoint, options = {}) { const url = `${this.baseUrl}${endpoint}`; const config = { headers: { 'Content-Type': 'application/json', ...options.headers }, ...options }; // Adicionar token de autenticação se disponível if (this.token) { config.headers.Authorization = `Bearer ${this.token}`; } try { const response = await fetch(url, config); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { return await response.json(); } return await response.text(); } catch (error) { console.error('Erro na requisição:', error); throw error; } } /** * Faz login do usuário * @param {string} email - Email do usuário * @param {string} password - Senha do usuário * @returns {Promise<Object>} Dados do usuário e token */ async login(email, password) { const response = await this.request('/auth/login', { method: 'POST', body: JSON.stringify({ email, password }) }); if (response.token) { this.setAuthToken(response.token); } return response; } /** * Faz logout do usuário * @returns {Promise<void>} */ async logout() { try { await this.request('/auth/logout', { method: 'POST' }); } catch (error) { console.warn('Erro no logout:', error); } finally { this.setAuthToken(null); } } /** * Busca dados do usuário atual * @returns {Promise<Object>} Dados do usuário */ async getCurrentUser() { return await this.request('/auth/me'); } /** * Busca lista de itens com paginação * @param {number} page - Número da página * @param {number} limit - Itens por página * @param {Object} filters - Filtros de busca * @returns {Promise<Object>} Lista paginada */ async getItems(page = 1, limit = 10, filters = {}) { const params = new URLSearchParams({ page: page.toString(), limit: limit.toString(), ...filters }); return await this.request(`/items?${params}`); } /** * Cria um novo item * @param {Object} itemData - Dados do item * @returns {Promise<Object>} Item criado */ async createItem(itemData) { return await this.request('/items', { method: 'POST', body: JSON.stringify(itemData) }); } /** * Atualiza um item existente * @param {number} itemId - ID do item * @param {Object} itemData - Dados atualizados * @returns {Promise<Object>} Item atualizado */ async updateItem(itemId, itemData) { return await this.request(`/items/${itemId}`, { method: 'PUT', body: JSON.stringify(itemData) }); } /** * Remove um item * @param {number} itemId - ID do item * @returns {Promise<void>} */ async deleteItem(itemId) { return await this.request(`/items/${itemId}`, { method: 'DELETE' }); } /** * Faz upload de arquivo * @param {File} file - Arquivo a ser enviado * @param {Function} onProgress - Callback de progresso * @returns {Promise<Object>} Resposta do upload */ async uploadFile(file, onProgress = null) { const formData = new FormData(); formData.append('file', file); const config = { method: 'POST', body: formData, headers: {} }; // Não definir Content-Type para FormData (deixar o browser definir) if (this.token) { config.headers.Authorization = `Bearer ${this.token}`; } // Implementar progresso se callback fornecido if (onProgress && typeof onProgress === 'function') { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.upload.addEventListener('progress', (event) => { if (event.lengthComputable) { const percentComplete = (event.loaded / event.total) * 100; onProgress(percentComplete); } }); xhr.addEventListener('load', () => { if (xhr.status >= 200 && xhr.status < 300) { resolve(JSON.parse(xhr.responseText)); } else { reject(new Error(`Upload failed: ${xhr.statusText}`)); } }); xhr.addEventListener('error', () => { reject(new Error('Upload failed')); }); xhr.open('POST', `${this.baseUrl}/upload`); if (this.token) { xhr.setRequestHeader('Authorization', `Bearer ${this.token}`); } xhr.send(formData); }); } return await this.request('/upload', config); } } /** * Funções utilitárias para validação */ const ValidationUtils = { /** * Valida formato de email * @param {string} email - Email a ser validado * @returns {boolean} True se válido */ isValidEmail(email) { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return emailRegex.test(email); }, /** * Valida força da senha * @param {string} password - Senha a ser validada * @returns {Object} Resultado da validação */ validatePassword(password) { const result = { isValid: false, errors: [] }; if (password.length < 8) { result.errors.push('Senha deve ter pelo menos 8 caracteres'); } if (!/[A-Z]/.test(password)) { result.errors.push('Senha deve conter pelo menos uma letra maiúscula'); } if (!/[a-z]/.test(password)) { result.errors.push('Senha deve conter pelo menos uma letra minúscula'); } if (!/\d/.test(password)) { result.errors.push('Senha deve conter pelo menos um número'); } result.isValid = result.errors.length === 0; return result; } }; // Exportar para uso em módulos if (typeof module !== 'undefined' && module.exports) { module.exports = { ApiClient, ValidationUtils }; } // Disponibilizar globalmente no browser if (typeof window !== 'undefined') { window.ApiClient = ApiClient; window.ValidationUtils = ValidationUtils; }