mcp-codebase-search
Version:
MCP server for semantic codebase search using embeddings
281 lines (243 loc) • 8.05 kB
JavaScript
/**
* 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;
}