Skip to main content
Glama
http-client.js10.3 kB
/** * Superset HTTP 客户端封装 * 提供标准化的方式与Superset API进行交互 */ export class SupersetHttpClient { constructor({ baseUrl, username, password, defaultHeaders = {}, defaultTimeout = 30000, maxRetries = 3, withCredentials = true, }) { this.token = null; this.refresh = null; this.csrf = null; this.cookies = {}; this.baseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl; this.username = username; this.password = password; this.defaultHeaders = { 'Content-Type': 'application/json', 'Accept': 'application/json', ...defaultHeaders, }; this.defaultTimeout = defaultTimeout; this.maxRetries = maxRetries; this.withCredentials = withCredentials; // 初始化时自动登录 this.login(); } /** * 登录Superset并获取访问令牌 */ async login() { try { console.log('登录Superset...'); const response = await this.request('/api/v1/security/login', { method: 'POST', body: { username: this.username, password: this.password, provider: 'ldap', refresh: true, }, }, false); if (response.success && response.data?.access_token) { this.token = response.data.access_token; this.refresh = response.data.refresh_token; // 登录成功后获取CSRF令牌 await this.getCsrfToken(); console.log('登录成功'); return true; } console.error('登录失败:', response.error?.message || '未知错误'); return false; } catch (error) { console.error('登录过程中发生错误:', error); return false; } } /** * 刷新访问令牌 */ async refreshAccessToken() { try { console.log('刷新访问令牌...'); if (!this.refresh) { console.warn('无法刷新访问令牌:refresh_token 为空'); return false; } const response = await this.request('/api/v1/security/refresh', { method: 'POST', headers: { 'Authorization': `Bearer ${this.refresh}` } }, false); if (response.success && response.data?.access_token) { this.token = response.data.access_token; console.log('访问令牌刷新成功'); return true; } console.error('刷新访问令牌失败:', response.error?.message || '未知错误'); return false; } catch (error) { console.error('刷新访问令牌过程中发生错误:', error); return false; } } /** * 发送GET请求 */ async get(endpoint, options = {}) { return this.request(endpoint, { ...options, method: 'GET' }); } /** * 发送POST请求 */ async post(endpoint, data, options = {}) { return this.request(endpoint, { ...options, method: 'POST', body: data }); } /** * 发送PUT请求 */ async put(endpoint, data, options = {}) { return this.request(endpoint, { ...options, method: 'PUT', body: data }); } /** * 发送DELETE请求 */ async delete(endpoint, options = {}) { return this.request(endpoint, { ...options, method: 'DELETE' }); } /** * 执行HTTP请求 */ async request(endpoint, options = {}, refreshTokenIfNeeded = true) { console.log(`执行 ${options.method || 'GET'} 请求: ${endpoint}`); // 检查是否登录 if (endpoint.lastIndexOf("/login") == -1 && !this.isAuthenticated() && !await this.login()) { return { success: false, status: 401, error: { message: 'authenticated failed', details: 'authenticated failed', status: 401, }, }; } const url = `${this.baseUrl}${endpoint.startsWith('/') ? endpoint : `/${endpoint}`}`; // 准备请求配置 const headers = { 'Content-Type': 'application/json', ...options.headers, }; // 添加认证令牌 if (this.token) { headers['Authorization'] = `Bearer ${this.token}`; } // 添加CSRF令牌 if (this.csrf) { headers['X-CSRFToken'] = this.csrf; } // 添加cookies if (this.withCredentials && Object.keys(this.cookies).length > 0) { headers['Cookie'] = Object.keys(this.cookies).map(key => `${key}=${this.cookies[key]}`).join('; '); } // 准备请求体 let body; if (options.body) { body = typeof options.body === 'string' ? options.body : JSON.stringify(options.body); } // 准备查询参数 const queryParams = options.params ? '?' + new URLSearchParams(options.params).toString() : ''; try { // 执行请求 const response = await fetch(`${url}${queryParams}`, { method: options.method || 'GET', headers, body, credentials: this.withCredentials ? 'include' : 'same-origin', }); // 处理cookies const setCookieHeader = response.headers.get('set-cookie'); console.log(setCookieHeader); if (url.lastIndexOf("csrf_token") != -1 && setCookieHeader) { console.log(this.cookies, this.token); // 简单处理cookies,实际项目中可能需要更复杂的cookie解析 setCookieHeader.split(';').forEach(cookie => { const [name, value] = cookie.split('='); if (name && value) { this.cookies[name.trim()] = value.trim(); } }); } // 尝试解析响应体 let data; const contentType = response.headers.get('content-type'); if (contentType && contentType.includes('application/json')) { data = await response.json(); } else { data = await response.text(); } // 构建响应对象 const result = { success: response.ok, status: response.status, data: data, }; // 处理错误 if (!response.ok) { result.error = { message: String(data.message || data.msg || '请求失败'), details: String(data.error || data.details || JSON.stringify(data)), status: response.status, }; // 生成并输出 curl 命令 let curlCommand = `curl -X ${options.method || 'GET'} "${url}${queryParams}"`; // 添加请求头 Object.entries(headers).forEach(([key, value]) => { curlCommand += ` -H "${key}: ${value.replace(/"/g, '\\"')}"`; }); // 添加请求体 if (body) { curlCommand += ` -d '${body.replace(/'/g, "\\'")}'`; } console.error('API请求失败,对应的curl命令:'); console.error(curlCommand); } // 处理令牌过期 if (response.status === 401 && refreshTokenIfNeeded && data.msg === 'Token has expired' && await this.refreshAccessToken()) { // 令牌已刷新,重试请求 console.log('令牌已刷新,重试请求...'); return this.request(endpoint, options, false); } return result; } catch (error) { console.error('请求执行错误:', error); // 生成并输出 curl 命令 let curlCommand = `curl -X ${options.method || 'GET'} "${url}${queryParams}"`; // 添加请求头 Object.entries(headers).forEach(([key, value]) => { curlCommand += ` -H "${key}: ${value.replace(/"/g, '\\"')}"`; }); // 添加请求体 if (body) { curlCommand += ` -d '${body.replace(/'/g, "\\'")}'`; } console.error('API请求失败,对应的curl命令:'); console.error(curlCommand); return { success: false, status: 0, error: { message: String(error), details: String(error), }, }; } } /** * 获取CSRF令牌 */ async getCsrfToken() { try { console.log('获取CSRF令牌...'); const response = await this.request('/api/v1/security/csrf_token/', { method: 'GET', }, true); if (response.success && response.data?.result) { this.csrf = response.data.result; if (this.csrf) { console.log(`获取CSRF令牌成功: ${this.csrf.substring(0, 10)}...`); } return this.csrf; } console.error('获取CSRF令牌失败:', response.error?.message || '未知错误'); return null; } catch (error) { console.error('获取CSRF令牌过程中发生错误:', error); return null; } } /** * 检查客户端是否已认证 */ isAuthenticated() { return !!this.token; } /** * 清除认证令牌 */ logout() { this.token = null; this.refresh = null; this.csrf = null; this.cookies = {}; } } //# sourceMappingURL=http-client.js.map

Latest Blog Posts

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/LiusCraft/superset-mcp-server'

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