Skip to main content
Glama
hono.ts31.5 kB
import { Hono } from 'hono'; import { serve } from '@hono/node-server'; import { serveStatic } from '@hono/node-server/serve-static'; import { cors } from 'hono/cors'; import { logger } from 'hono/logger'; import path from 'path'; import { apiTest } from './apitest.js'; import { executeTasksAndSaveResults } from './server/apiTest.js'; import { exportToExcel } from './server/createExcel.js'; import { dbClient } from './db/db.js'; const app = new Hono(); // 中间件配置 app.use('*', logger()); app.use('*', cors()); // 静态文件服务 app.use('/static/*', serveStatic({ root: path.join(process.cwd(), 'public'), rewriteRequestPath: (path) => path.replace(/^\/static/, '') })); // 首页路由 app.get('/', (c) => { return c.html(` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>API测试管理系统</title> <link rel="stylesheet" href="/static/css/style.css"> </head> <body> <div class="container"> <!-- Hero区域 --> <header class="hero"> <h1 class="hero-title">API测试管理系统</h1> <h2 class="hero-subtitle">高效的API测试解决方案</h2> <p class="hero-description">基于Hono框架构建,提供完整的API测试、计划管理和结果导出功能</p> </header> <!-- 功能导航区域 --> <main class="features-grid"> <div class="feature-card" onclick="location.href='/api-test'"> <div class="feature-icon"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M12 2L2 7l10 5 10-5-10-5z"/> <path d="M2 17l10 5 10-5"/> <path d="M2 12l10 5 10-5"/> </svg> </div> <h3>API测试</h3> <p>单个API接口测试,支持各种HTTP方法</p> </div> <div class="feature-card" onclick="location.href='/test-plans'"> <div class="feature-icon"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M9 11H5a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7a2 2 0 0 0-2-2h-4"/> <path d="M9 7V3a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v4"/> </svg> </div> <h3>测试计划管理</h3> <p>创建和管理测试计划,批量组织测试任务</p> </div> <div class="feature-card" onclick="location.href='/test-execution'"> <div class="feature-icon"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <circle cx="12" cy="12" r="10"/> <polygon points="10,8 16,12 10,16 10,8"/> </svg> </div> <h3>批量执行</h3> <p>执行测试计划中的所有任务,实时查看进度</p> </div> <div class="feature-card" onclick="location.href='/test-results'"> <div class="feature-icon"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/> <polyline points="14,2 14,8 20,8"/> <line x1="16" y1="13" x2="8" y2="13"/> <line x1="16" y1="17" x2="8" y2="17"/> <polyline points="10,9 9,9 8,9"/> </svg> </div> <h3>结果查看</h3> <p>查看测试结果详情,编辑总结和建议</p> </div> <div class="feature-card" onclick="location.href='/export'"> <div class="feature-icon"> <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/> <polyline points="7,10 12,15 17,10"/> <line x1="12" y1="15" x2="12" y2="3"/> </svg> </div> <h3>导出管理</h3> <p>将测试结果导出为Excel格式报告</p> </div> </main> </div> <script src="/static/js/main.js"></script> </body> </html> `); }); // API路由 /** * 健康检查接口 */ app.get('/api/health', (c) => { return c.json({ status: 'ok', timestamp: new Date().toISOString(), service: 'API测试管理系统', version: '1.0.0' }); }); /** * 获取所有测试计划 */ app.get('/api/plans', async (c) => { try { const result = await dbClient.getAllPlans(); return c.json(result.data || []); } catch (error) { return c.json({ message: error instanceof Error ? error.message : '获取计划失败' }, 500); } }); /** * 获取指定测试计划 */ app.get('/api/plans/:id', async (c) => { try { const id = c.req.param('id'); const result = await dbClient.getPlanById(id); if (result.state === 1) { return c.json(result.data); } else { return c.json({ message: result.message }, 404); } } catch (error) { return c.json({ message: error instanceof Error ? error.message : '获取计划失败' }, 500); } }); /** * 删除测试计划 */ app.delete('/api/plans/:id', async (c) => { try { const id = c.req.param('id'); const result = await dbClient.deletePlan(id); return c.json({ success: result.state === 1, message: result.message }); } catch (error) { return c.json({ success: false, message: error instanceof Error ? error.message : '删除失败' }, 500); } }); /** * 获取所有测试任务 */ app.get('/api/tasks', async (c) => { try { const planId = c.req.query('plan_id'); let result; if (planId) { result = await dbClient.getAllTable(planId); } else { // 获取所有任务(需要实现这个方法) result = await dbClient.getAllTasks(); } return c.json(result.data || []); } catch (error) { return c.json({ message: error instanceof Error ? error.message : '获取任务失败' }, 500); } }); /** * 获取指定测试任务 */ app.get('/api/tasks/:id', async (c) => { try { const id = c.req.param('id'); const result = await dbClient.getTaskById(id); if (result.state === 1) { return c.json(result.data); } else { return c.json({ message: result.message }, 404); } } catch (error) { return c.json({ message: error instanceof Error ? error.message : '获取任务失败' }, 500); } }); /** * 删除测试任务 */ app.delete('/api/tasks/:id', async (c) => { try { const id = c.req.param('id'); const result = await dbClient.deleteTask(id); return c.json({ success: result.state === 1, message: result.message }); } catch (error) { return c.json({ success: false, message: error instanceof Error ? error.message : '删除失败' }, 500); } }); /** * 单个API测试接口 * 接收URL、方法、参数等,返回测试结果 */ app.post('/api/test', async (c) => { try { const body = await c.req.json(); const { url, method, query, headers, body: requestBody } = body; // 参数验证 const { validateRequired, validateURL, validateHTTPMethod, validateJSON } = await import('./utils/参数验证工具.js'); // 验证URL const urlRequiredValidation = validateRequired(url, '请求URL'); if (!urlRequiredValidation.state) { return c.json({ state: false, message: urlRequiredValidation.message, data: null }, 400); } const urlFormatValidation = validateURL(url, '请求URL'); if (!urlFormatValidation.state) { return c.json({ state: false, message: urlFormatValidation.message, data: null }, 400); } // 验证HTTP方法 const methodRequiredValidation = validateRequired(method, '请求方法'); if (!methodRequiredValidation.state) { return c.json({ state: false, message: methodRequiredValidation.message, data: null }, 400); } const methodFormatValidation = validateHTTPMethod(method, '请求方法'); if (!methodFormatValidation.state) { return c.json({ state: false, message: methodFormatValidation.message, data: null }, 400); } // 验证请求头(可选) if (headers !== undefined && headers !== null && headers !== '') { const headersValidation = validateJSON(headers, '请求头'); if (!headersValidation.state) { return c.json({ state: false, message: headersValidation.message, data: null }, 400); } } // 验证请求体(可选) if (requestBody !== undefined && requestBody !== null && requestBody !== '') { const bodyValidation = validateJSON(requestBody, '请求体'); if (!bodyValidation.state) { return c.json({ state: false, message: bodyValidation.message, data: null }, 400); } } const result = await apiTest({ url, method, query, headers, body: requestBody }); return c.json({ state: true, message: result.message, data: result }); } catch (error) { return c.json({ state: false, message: error instanceof Error ? error.message : '未知错误', data: null }, 500); } }); /** * 创建测试计划和任务 */ app.post('/api/test-plans', async (c) => { try { const body = await c.req.json(); const { planName, tasks } = body; // 参数验证 const { validateRequired, validateStringLength, validateTestTasks } = await import('./utils/参数验证工具.js'); // 验证计划名称 const planNameRequiredValidation = validateRequired(planName, '测试计划名称'); if (!planNameRequiredValidation.state) { return c.json({ state: false, message: planNameRequiredValidation.message, data: null }, 400); } const planNameLengthValidation = validateStringLength(planName, '测试计划名称', 1, 100); if (!planNameLengthValidation.state) { return c.json({ state: false, message: planNameLengthValidation.message, data: null }, 400); } // 验证任务列表 const tasksValidation = validateTestTasks(tasks); if (!tasksValidation.state) { return c.json({ state: false, message: tasksValidation.message, data: null }, 400); } const result = await dbClient.createTestPlanWithTasks(planName, tasks); return c.json({ state: result.state === 1, message: result.message, data: result.data }); } catch (error) { return c.json({ state: false, message: error instanceof Error ? error.message : '创建失败', data: null }, 500); } }); /** * 获取所有测试计划或指定计划的任务 */ app.get('/api/test-plans', async (c) => { try { const uuid = c.req.query('uuid'); const result = await dbClient.getAllTable(uuid); return c.json(result); } catch (error) { return c.json({ state: false, message: error instanceof Error ? error.message : '查询失败', data: null }, 500); } }); /** * 执行测试计划 */ app.post('/api/test-plans/:uuid/execute', async (c) => { try { const uuid = c.req.param('uuid'); // 参数验证 const { validateUUID } = await import('./utils/参数验证工具.js'); // 验证UUID格式 const uuidValidation = validateUUID(uuid, '测试计划UUID'); if (!uuidValidation.state) { return c.json({ state: false, message: uuidValidation.message, data: null }, 400); } const result = await executeTasksAndSaveResults(uuid); // 检查执行结果 if (!result.data || result.data.length === 0) { return c.json({ state: false, message: '测试计划不存在或没有可执行的任务', data: null }, 404); } return c.json({ state: true, message: '执行成功', data: result.data?.map((item) => ({ uuid: item.data?.uuid, name: item.data?.name, res: item.data?.res })) }); } catch (error) { return c.json({ state: false, message: error instanceof Error ? error.message : '执行失败', data: null }, 500); } }); /** * 向测试计划添加任务 */ app.post('/api/test-plans/:uuid/tasks', async (c) => { try { const uuid = c.req.param('uuid'); const body = await c.req.json(); const { tasks } = body; // 参数验证 const { validateUUID, validateTestTasks } = await import('./utils/参数验证工具.js'); // 验证UUID格式 const uuidValidation = validateUUID(uuid, '测试计划UUID'); if (!uuidValidation.state) { return c.json({ state: false, message: uuidValidation.message, data: null }, 400); } // 验证任务列表 const tasksValidation = validateTestTasks(tasks); if (!tasksValidation.state) { return c.json({ state: false, message: tasksValidation.message, data: null }, 400); } const result = await dbClient.addTasksToPlan(uuid, tasks); return c.json({ state: result.state === 1, message: result.message, data: result.data }); } catch (error) { return c.json({ state: false, message: error instanceof Error ? error.message : '添加任务失败', data: null }, 500); } }); /** * 批量更新任务总结 */ app.put('/api/tasks/batch-update', async (c) => { try { const body = await c.req.json(); const { tasks } = body; const result = await dbClient.updateTaskWithSummary(tasks); return c.json({ state: result.state === 1, message: result.message, data: result.data }); } catch (error) { return c.json({ state: false, message: error instanceof Error ? error.message : '批量更新失败', data: null }, 500); } }); /** * 更新单个任务 */ app.put('/api/tasks/:uuid', async (c) => { try { const uuid = c.req.param('uuid'); const body = await c.req.json(); // 参数验证 const { validateUUID } = await import('./utils/参数验证工具.js'); // 验证UUID格式 const uuidValidation = validateUUID(uuid, '任务UUID'); if (!uuidValidation.state) { return c.json({ state: false, message: uuidValidation.message, data: null }, 400); } // 验证请求体不为空 if (!body || Object.keys(body).length === 0) { return c.json({ state: false, message: '请求体不能为空', data: null }, 400); } const result = await dbClient.updateTaskByUuid(uuid, body); return c.json({ state: result.state === 1, message: result.message, data: result.data }); } catch (error) { return c.json({ state: false, message: error instanceof Error ? error.message : '更新失败', data: null }, 500); } }); /** * 导出测试结果到Excel */ app.post('/api/export/:uuid', async (c) => { try { const uuid = c.req.param('uuid'); // 参数验证 const { validateUUID } = await import('./utils/参数验证工具.js'); // 验证UUID格式 const uuidValidation = validateUUID(uuid, '测试计划UUID'); if (!uuidValidation.state) { return c.json({ state: false, message: uuidValidation.message, data: null }, 400); } const result = await exportToExcel(uuid); return c.json({ state: result.state, message: result.message, filePath: result.data }); } catch (error) { return c.json({ state: false, message: error instanceof Error ? error.message : '导出Excel失败', data: null }, 500); } }); // 页面路由 app.get('/api-test', (c) => { return c.html(` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>API测试 - API测试管理系统</title> <link rel="stylesheet" href="/static/css/style.css"> </head> <body> <div class="container"> <nav class="breadcrumb"> <a href="/">首页</a> > API测试 </nav> <h1>API测试</h1> <div class="api-test-form"> <form id="apiTestForm"> <div class="form-group"> <label for="url">请求URL *</label> <input type="url" id="url" name="url" required placeholder="https://api.example.com/endpoint"> </div> <div class="form-group"> <label for="method">请求方法</label> <select id="method" name="method"> <option value="GET">GET</option> <option value="POST">POST</option> <option value="PUT">PUT</option> <option value="DELETE">DELETE</option> </select> </div> <div class="form-group"> <label for="headers">请求头 (JSON格式)</label> <textarea id="headers" name="headers" placeholder='{"Content-Type": "application/json"}'></textarea> </div> <div class="form-group"> <label for="query">查询参数 (JSON格式)</label> <textarea id="query" name="query" placeholder='{"page": "1", "limit": "10"}'></textarea> </div> <div class="form-group"> <label for="body">请求体 (JSON格式)</label> <textarea id="body" name="body" placeholder='{"name": "test", "value": "example"}'></textarea> </div> <button type="submit" class="btn-primary">发送请求</button> </form> </div> <div id="result" class="result-section" style="display: none;"> <h2>响应结果</h2> <div id="resultContent"></div> </div> </div> <script src="/static/js/api-test.js"></script> </body> </html> `); }); // 测试计划管理页面 app.get('/test-plans', (c) => { return c.html(` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>测试计划管理 - API测试管理系统</title> <link rel="stylesheet" href="/static/css/style.css"> </head> <body> <div class="container"> <nav class="breadcrumb"> <a href="/">首页</a> > 测试计划管理 </nav> <div class="page-header"> <h1>测试计划管理</h1> <button class="btn-primary" onclick="showCreatePlanModal()">创建新计划</button> </div> <div id="plansList" class="plans-list"> <!-- 计划列表将通过JavaScript动态加载 --> </div> <!-- 创建计划模态框 --> <div id="createPlanModal" class="modal"> <div class="modal-content"> <span class="close">&times;</span> <h2>创建测试计划</h2> <form id="createPlanForm"> <div class="form-group"> <label for="planName">计划名称 *</label> <input type="text" id="planName" name="planName" required placeholder="输入测试计划名称"> </div> <div class="tasks-section"> <h3>测试任务</h3> <div id="tasksList"> <!-- 任务列表 --> </div> <button type="button" class="btn-secondary" onclick="addTask()">添加任务</button> </div> <div class="form-actions"> <button type="submit" class="btn-primary">创建计划</button> <button type="button" class="btn-secondary" onclick="closeCreatePlanModal()">取消</button> </div> </form> </div> </div> <!-- 查看计划详情模态框 --> <div id="planDetailsModal" class="modal"> <div class="modal-content"> <span class="close">&times;</span> <h2 id="planDetailsTitle">计划详情</h2> <div id="planDetailsContent"> <!-- 计划详情内容 --> </div> </div> </div> </div> <script src="/static/js/test-plans.js"></script> </body> </html> `); }); // 测试执行页面 app.get('/test-execution', (c) => { return c.html(` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>测试执行 - API测试管理系统</title> <link rel="stylesheet" href="/static/css/style.css"> </head> <body> <div class="container"> <nav class="breadcrumb"> <a href="/">首页</a> > 测试执行 </nav> <h1>批量测试执行</h1> <div class="execution-section"> <div class="form-group"> <label for="planSelect">选择测试计划 *</label> <select id="planSelect" name="planSelect" required> <option value="">请选择测试计划</option> </select> </div> <div class="execution-controls"> <button id="executeBtn" class="btn-primary" onclick="executeTestPlan()">开始执行</button> <button id="stopBtn" class="btn-secondary" onclick="stopExecution()" disabled>停止执行</button> </div> <div id="executionProgress" class="execution-progress" style="display: none;"> <h3>执行进度</h3> <div class="progress-bar"> <div id="progressFill" class="progress-fill" style="width: 0%"></div> </div> <div id="progressText" class="progress-text">准备中...</div> </div> <div id="executionResults" class="execution-results" style="display: none;"> <h3>执行结果</h3> <div id="resultsContent"> <!-- 执行结果内容 --> </div> </div> </div> </div> <script src="/static/js/test-execution.js"></script> </body> </html> `); }); // 测试结果查看页面 app.get('/test-results', (c) => { return c.html(` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>测试结果 - API测试管理系统</title> <link rel="stylesheet" href="/static/css/style.css"> </head> <body> <div class="container"> <nav class="breadcrumb"> <a href="/">首页</a> > 测试结果 </nav> <div class="page-header"> <h1>测试结果查看</h1> <div class="filter-controls"> <select id="planFilter"> <option value="">所有计划</option> </select> <select id="statusFilter"> <option value="">所有状态</option> <option value="success">成功</option> <option value="error">失败</option> <option value="pending">待处理</option> </select> </div> </div> <div id="resultsList" class="results-list"> <!-- 结果列表将通过JavaScript动态加载 --> </div> <!-- 编辑结果模态框 --> <div id="editResultModal" class="modal"> <div class="modal-content"> <span class="close">&times;</span> <h2>编辑测试结果</h2> <form id="editResultForm"> <div class="form-group"> <label for="taskName">任务名称</label> <input type="text" id="taskName" name="taskName" readonly> </div> <div class="form-group"> <label for="summary">测试总结 *</label> <textarea id="summary" name="summary" required placeholder="请输入测试总结"></textarea> </div> <div class="form-group"> <label for="suggest">改进建议</label> <textarea id="suggest" name="suggest" placeholder="请输入改进建议"></textarea> </div> <div class="form-actions"> <button type="submit" class="btn-primary">保存</button> <button type="button" class="btn-secondary" onclick="closeEditResultModal()">取消</button> </div> </form> </div> </div> </div> <script src="/static/js/test-results.js"></script> </body> </html> `); }); // 导出管理页面 app.get('/export', (c) => { return c.html(` <!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>导出管理 - API测试管理系统</title> <link rel="stylesheet" href="/static/css/style.css"> </head> <body> <div class="container"> <nav class="breadcrumb"> <a href="/">首页</a> > 导出管理 </nav> <h1>导出管理</h1> <div class="export-section"> <div class="form-group"> <label for="exportPlanSelect">选择测试计划 *</label> <select id="exportPlanSelect" name="exportPlanSelect" required> <option value="">请选择要导出的测试计划</option> </select> </div> <div class="export-options"> <h3>导出选项</h3> <div class="checkbox-group"> <label> <input type="checkbox" id="includeDetails" checked> 包含详细信息 </label> <label> <input type="checkbox" id="includeSummary" checked> 包含测试总结 </label> <label> <input type="checkbox" id="includeTimestamp" checked> 包含时间戳 </label> </div> </div> <div class="export-controls"> <button id="exportBtn" class="btn-primary" onclick="exportToExcel()">导出Excel</button> <button id="previewBtn" class="btn-secondary" onclick="previewExport()">预览</button> </div> <div id="exportStatus" class="export-status" style="display: none;"> <!-- 导出状态信息 --> </div> <div id="exportPreview" class="export-preview" style="display: none;"> <h3>导出预览</h3> <div id="previewContent"> <!-- 预览内容 --> </div> </div> </div> </div> <script src="/static/js/export.js"></script> </body> </html> `); }); const port = 9999; console.log(`服务器运行在 http://localhost:${port}`); serve({ fetch: app.fetch, port });

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/Actrue/api-test-mcp'

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