Skip to main content
Glama

MCPDemo - Visual SQL Chat Platform

by Ayi456
BaseApiService.ts6.19 kB
/** * API服务基类 * 提供统一的请求处理、错误处理和重试机制 */ import { API_CONFIG, ERROR_MESSAGES } from '../config/constants'; import { ApiResponse } from '../types'; export interface RequestOptions extends RequestInit { retry?: boolean; retryAttempts?: number; retryDelay?: number; timeout?: number; } export class BaseApiService { protected baseUrl: string; protected defaultHeaders: Record<string, string>; constructor(baseUrl: string = API_CONFIG.BASE_URL) { this.baseUrl = baseUrl; this.defaultHeaders = { 'Content-Type': 'application/json', 'Accept': 'application/json', }; } /** * 获取认证headers */ protected getAuthHeaders(): Record<string, string> { const headers: Record<string, string> = {}; try { // 从localStorage获取认证信息 const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}'); const accessId = userInfo.access_id; const accessKey = localStorage.getItem('accessKey'); if (accessId && accessKey) { headers['AccessID'] = accessId; headers['AccessKey'] = accessKey; } } catch (error) { console.warn('Failed to get auth headers:', error); } return headers; } /** * 创建AbortController用于请求超时 */ private createTimeoutController(timeout: number): AbortController { const controller = new AbortController(); setTimeout(() => controller.abort(), timeout); return controller; } /** * 延迟函数 */ private delay(ms: number): Promise<void> { return new Promise(resolve => setTimeout(resolve, ms)); } /** * 发送HTTP请求(带重试机制) */ protected async request<T>( endpoint: string, options: RequestOptions = {} ): Promise<T> { const { retry = true, retryAttempts = API_CONFIG.RETRY_ATTEMPTS, retryDelay = API_CONFIG.RETRY_DELAY, timeout = API_CONFIG.TIMEOUT, ...fetchOptions } = options; const url = `${this.baseUrl}${endpoint}`; let lastError: Error | null = null; let attempts = retry ? retryAttempts : 1; for (let i = 0; i < attempts; i++) { try { const controller = this.createTimeoutController(timeout); const response = await fetch(url, { ...fetchOptions, signal: controller.signal, headers: { ...this.defaultHeaders, ...this.getAuthHeaders(), ...fetchOptions.headers, }, }); // 处理非2xx响应 if (!response.ok) { const error = await this.parseErrorResponse(response); throw error; } // 解析响应 const result = await this.parseResponse<T>(response); return result; } catch (error: any) { lastError = error; // 如果是最后一次尝试,或者错误不应该重试,则抛出错误 if (i === attempts - 1 || !this.shouldRetry(error)) { throw this.normalizeError(error); } // 等待后重试 await this.delay(retryDelay * (i + 1)); // 指数退避 } } throw lastError || new Error(ERROR_MESSAGES.NETWORK_ERROR); } /** * 解析错误响应 */ private async parseErrorResponse(response: Response): Promise<Error> { try { const data = await response.json(); const error: any = new Error(data.message || data.error || `HTTP ${response.status}`); // 保存完整的响应数据以便上层使用 error.response = { status: response.status, errorCode: data.errorCode, hint: data.hint, ...data }; return error; } catch { return new Error(`HTTP ${response.status}: ${response.statusText}`); } } /** * 解析成功响应 */ private async parseResponse<T>(response: Response): Promise<T> { try { const data = await response.json(); // 如果响应有标准格式 if ('success' in data) { const apiResponse = data as ApiResponse<T>; if (!apiResponse.success) { throw new Error(apiResponse.message || apiResponse.error || 'Operation failed'); } return apiResponse.data as T; } // 直接返回数据 return data as T; } catch (error) { if (error instanceof Error) throw error; throw new Error('Failed to parse response'); } } /** * 判断错误是否应该重试 */ private shouldRetry(error: Error): boolean { // 网络错误和超时错误可以重试 if (error.name === 'AbortError') return true; if (error.message.includes('fetch')) return true; if (error.message.includes('network')) return true; // 5xx服务器错误可以重试 if (error.message.includes('HTTP 5')) return true; // 其他错误不重试 return false; } /** * 规范化错误对象 */ private normalizeError(error: any): Error { if (error instanceof Error) return error; if (typeof error === 'string') return new Error(error); if (error?.message) return new Error(error.message); return new Error(ERROR_MESSAGES.NETWORK_ERROR); } /** * GET请求 */ protected get<T>(endpoint: string, options?: RequestOptions): Promise<T> { return this.request<T>(endpoint, { ...options, method: 'GET', }); } /** * POST请求 */ protected post<T>(endpoint: string, data?: any, options?: RequestOptions): Promise<T> { return this.request<T>(endpoint, { ...options, method: 'POST', body: data ? JSON.stringify(data) : undefined, }); } /** * PUT请求 */ protected put<T>(endpoint: string, data?: any, options?: RequestOptions): Promise<T> { return this.request<T>(endpoint, { ...options, method: 'PUT', body: data ? JSON.stringify(data) : undefined, }); } /** * DELETE请求 */ protected delete<T>(endpoint: string, options?: RequestOptions): Promise<T> { return this.request<T>(endpoint, { ...options, method: 'DELETE', }); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Ayi456/visual-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server