diff --git a/internal/handler/openapi.go b/internal/handler/openapi.go index 95ef3814..a3227626 100644 --- a/internal/handler/openapi.go +++ b/internal/handler/openapi.go @@ -4411,6 +4411,7 @@ func (h *OpenAPIHandler) GetOpenAPISpec(c *gin.Context) { }, } + enrichSpecWithI18nKeys(spec) c.JSON(http.StatusOK, spec) } diff --git a/internal/handler/openapi_i18n.go b/internal/handler/openapi_i18n.go new file mode 100644 index 00000000..3479766e --- /dev/null +++ b/internal/handler/openapi_i18n.go @@ -0,0 +1,139 @@ +package handler + +// apiDocI18n 为 OpenAPI 文档提供 x-i18n-* 扩展键,供前端 apiDocs 国际化使用。 +// 前端通过 apiDocs.tags.* / apiDocs.summary.* / apiDocs.response.* 翻译。 + +var apiDocI18nTagToKey = map[string]string{ + "认证": "auth", "对话管理": "conversationManagement", "对话交互": "conversationInteraction", + "批量任务": "batchTasks", "对话分组": "conversationGroups", "漏洞管理": "vulnerabilityManagement", + "角色管理": "roleManagement", "Skills管理": "skillsManagement", "监控": "monitoring", + "配置管理": "configManagement", "外部MCP管理": "externalMCPManagement", "攻击链": "attackChain", + "知识库": "knowledgeBase", "MCP": "mcp", +} + +var apiDocI18nSummaryToKey = map[string]string{ + "用户登录": "login", "用户登出": "logout", "修改密码": "changePassword", "验证Token": "validateToken", + "创建对话": "createConversation", "列出对话": "listConversations", "查看对话详情": "getConversationDetail", + "更新对话": "updateConversation", "删除对话": "deleteConversation", "获取对话结果": "getConversationResult", + "发送消息并获取AI回复(非流式)": "sendMessageNonStream", "发送消息并获取AI回复(流式)": "sendMessageStream", + "取消任务": "cancelTask", "列出运行中的任务": "listRunningTasks", "列出已完成的任务": "listCompletedTasks", + "创建批量任务队列": "createBatchQueue", "列出批量任务队列": "listBatchQueues", "获取批量任务队列": "getBatchQueue", + "删除批量任务队列": "deleteBatchQueue", "启动批量任务队列": "startBatchQueue", "暂停批量任务队列": "pauseBatchQueue", + "添加任务到队列": "addTaskToQueue", "SQL注入扫描": "sqlInjectionScan", "端口扫描": "portScan", + "更新批量任务": "updateBatchTask", "删除批量任务": "deleteBatchTask", + "创建分组": "createGroup", "列出分组": "listGroups", "获取分组": "getGroup", "更新分组": "updateGroup", + "删除分组": "deleteGroup", "获取分组中的对话": "getGroupConversations", "添加对话到分组": "addConversationToGroup", + "从分组移除对话": "removeConversationFromGroup", + "列出漏洞": "listVulnerabilities", "创建漏洞": "createVulnerability", "获取漏洞统计": "getVulnerabilityStats", + "获取漏洞": "getVulnerability", "更新漏洞": "updateVulnerability", "删除漏洞": "deleteVulnerability", + "列出角色": "listRoles", "创建角色": "createRole", "获取角色": "getRole", "更新角色": "updateRole", "删除角色": "deleteRole", + "获取可用Skills列表": "getAvailableSkills", "列出Skills": "listSkills", "创建Skill": "createSkill", + "获取Skill统计": "getSkillStats", "清空Skill统计": "clearSkillStats", "获取Skill": "getSkill", + "更新Skill": "updateSkill", "删除Skill": "deleteSkill", "获取绑定角色": "getBoundRoles", + "获取监控信息": "getMonitorInfo", "获取执行记录": "getExecutionRecords", "删除执行记录": "deleteExecutionRecord", + "批量删除执行记录": "batchDeleteExecutionRecords", "获取统计信息": "getStats", + "获取配置": "getConfig", "更新配置": "updateConfig", "获取工具配置": "getToolConfig", "应用配置": "applyConfig", + "列出外部MCP": "listExternalMCP", "获取外部MCP统计": "getExternalMCPStats", "获取外部MCP": "getExternalMCP", + "添加或更新外部MCP": "addOrUpdateExternalMCP", "stdio模式配置": "stdioModeConfig", "SSE模式配置": "sseModeConfig", + "删除外部MCP": "deleteExternalMCP", "启动外部MCP": "startExternalMCP", "停止外部MCP": "stopExternalMCP", + "获取攻击链": "getAttackChain", "重新生成攻击链": "regenerateAttackChain", + "设置对话置顶": "pinConversation", "设置分组置顶": "pinGroup", "设置分组中对话的置顶": "pinGroupConversation", + "获取分类": "getCategories", "列出知识项": "listKnowledgeItems", "创建知识项": "createKnowledgeItem", + "获取知识项": "getKnowledgeItem", "更新知识项": "updateKnowledgeItem", "删除知识项": "deleteKnowledgeItem", + "获取索引状态": "getIndexStatus", "重建索引": "rebuildIndex", "扫描知识库": "scanKnowledgeBase", + "搜索知识库": "searchKnowledgeBase", "基础搜索": "basicSearch", "按风险类型搜索": "searchByRiskType", + "获取检索日志": "getRetrievalLogs", "删除检索日志": "deleteRetrievalLog", + "MCP端点": "mcpEndpoint", "列出所有工具": "listAllTools", "调用工具": "invokeTool", "初始化连接": "initConnection", + "成功响应": "successResponse", "错误响应": "errorResponse", +} + +var apiDocI18nResponseDescToKey = map[string]string{ + "获取成功": "getSuccess", "未授权": "unauthorized", "未授权,需要有效的Token": "unauthorizedToken", + "创建成功": "createSuccess", "请求参数错误": "badRequest", "对话不存在": "conversationNotFound", + "对话不存在或结果不存在": "conversationOrResultNotFound", "请求参数错误(如task为空)": "badRequestTaskEmpty", + "请求参数错误或分组名称已存在": "badRequestGroupNameExists", "分组不存在": "groupNotFound", + "请求参数错误(如配置格式不正确、缺少必需字段等)": "badRequestConfig", + "请求参数错误(如query为空)": "badRequestQueryEmpty", "方法不允许(仅支持POST请求)": "methodNotAllowed", + "登录成功": "loginSuccess", "密码错误": "invalidPassword", "登出成功": "logoutSuccess", + "密码修改成功": "passwordChanged", "Token有效": "tokenValid", "Token无效或已过期": "tokenInvalid", + "对话创建成功": "conversationCreated", "服务器内部错误": "internalError", "更新成功": "updateSuccess", + "删除成功": "deleteSuccess", "队列不存在": "queueNotFound", "启动成功": "startSuccess", + "暂停成功": "pauseSuccess", "添加成功": "addSuccess", + "任务不存在": "taskNotFound", "对话或分组不存在": "conversationOrGroupNotFound", + "取消请求已提交": "cancelSubmitted", "未找到正在执行的任务": "noRunningTask", + "消息发送成功,返回AI回复": "messageSent", "流式响应(Server-Sent Events)": "streamResponse", +} + +// enrichSpecWithI18nKeys 在 spec 的每个 operation 上写入 x-i18n-tags、x-i18n-summary, +// 在每个 response 上写入 x-i18n-description,供前端按 key 做国际化。 +func enrichSpecWithI18nKeys(spec map[string]interface{}) { + paths, _ := spec["paths"].(map[string]interface{}) + if paths == nil { + return + } + for _, pathItem := range paths { + pm, _ := pathItem.(map[string]interface{}) + if pm == nil { + continue + } + for _, method := range []string{"get", "post", "put", "delete", "patch"} { + opVal, ok := pm[method] + if !ok { + continue + } + op, _ := opVal.(map[string]interface{}) + if op == nil { + continue + } + // x-i18n-tags: 与 tags 一一对应的 i18n 键数组(spec 中 tags 为 []string) + switch tags := op["tags"].(type) { + case []string: + if len(tags) > 0 { + keys := make([]string, 0, len(tags)) + for _, s := range tags { + if k := apiDocI18nTagToKey[s]; k != "" { + keys = append(keys, k) + } else { + keys = append(keys, s) + } + } + op["x-i18n-tags"] = keys + } + case []interface{}: + if len(tags) > 0 { + keys := make([]interface{}, 0, len(tags)) + for _, t := range tags { + if s, ok := t.(string); ok { + if k := apiDocI18nTagToKey[s]; k != "" { + keys = append(keys, k) + } else { + keys = append(keys, s) + } + } + } + if len(keys) > 0 { + op["x-i18n-tags"] = keys + } + } + } + // x-i18n-summary + if summary, _ := op["summary"].(string); summary != "" { + if k := apiDocI18nSummaryToKey[summary]; k != "" { + op["x-i18n-summary"] = k + } + } + // responses -> 每个 status -> x-i18n-description + if respMap, _ := op["responses"].(map[string]interface{}); respMap != nil { + for _, rv := range respMap { + if r, _ := rv.(map[string]interface{}); r != nil { + if desc, _ := r["description"].(string); desc != "" { + if k := apiDocI18nResponseDescToKey[desc]; k != "" { + r["x-i18n-description"] = k + } + } + } + } + } + } + } +} diff --git a/web/static/i18n/en-US.json b/web/static/i18n/en-US.json index 949eea63..38105db0 100644 --- a/web/static/i18n/en-US.json +++ b/web/static/i18n/en-US.json @@ -1,4 +1,8 @@ { + "lang": { + "zhCN": "中文", + "enUS": "English" + }, "common": { "ok": "OK", "cancel": "Cancel", @@ -132,10 +136,51 @@ "executeFailed": "Execution failed", "callOpenAIFailed": "Call OpenAI failed", "systemReadyMessage": "System is ready. Please enter your test requirements, and the system will automatically perform the corresponding security tests.", - "addNewGroup": "+ New group" + "addNewGroup": "+ New group", + "callNumber": "Call #{{n}}", + "iterationRound": "Iteration {{n}}", + "aiThinking": "AI thinking", + "toolCallsDetected": "Detected {{count}} tool call(s)", + "callTool": "Call tool: {{name}} ({{index}}/{{total}})", + "toolExecComplete": "Tool {{name}} completed", + "toolExecFailed": "Tool {{name}} failed", + "knowledgeRetrieval": "Knowledge retrieval", + "knowledgeRetrievalTag": "Knowledge retrieval", + "error": "Error", + "taskCancelled": "Task cancelled", + "unknownTool": "Unknown tool", + "noDescription": "No description", + "noResponseData": "No response data", + "loading": "Loading...", + "loadFailed": "Load failed: {{message}}", + "noAttackChainData": "No attack chain data", + "copyFailedManual": "Copy failed, please select and copy manually", + "searching": "Searching...", + "loadFailedRetry": "Load failed, please retry", + "dataFormatError": "Data format error", + "progressInProgress": "Penetration test in progress..." + }, + "progress": { + "callingAI": "Calling AI model...", + "callingTool": "Calling tool: {{name}}", + "lastIterSummary": "Last iteration: generating summary and next steps...", + "summaryDone": "Summary complete", + "generatingFinalReply": "Generating final reply...", + "maxIterSummary": "Max iterations reached, generating summary..." + }, + "timeline": { + "params": "Parameters:", + "executionResult": "Execution result:", + "executionId": "Execution ID:", + "noResult": "No result", + "running": "Running...", + "completed": "Completed", + "execFailed": "Execution failed" }, "tasks": { "title": "Task management", + "stopTask": "Stop task", + "collapseDetail": "Collapse details", "newTask": "New task", "autoRefresh": "Auto refresh", "historyHint": "Tip: Completed task history available. Check \"Show history\" to view.", @@ -500,7 +545,214 @@ "loadCallStatsError": "Failed to load call statistics" }, "apiDocs": { - "curlCopied": "curl command copied to clipboard!" + "pageTitle": "API Docs - CyberStrikeAI", + "title": "API Docs", + "subtitle": "CyberStrikeAI platform API documentation with online testing", + "authTitle": "API Authentication", + "authAllNeedToken": "All API endpoints require Token authentication.", + "authGetToken": "1. Get Token:", + "authGetTokenDesc": "After logging in on the frontend, the Token is saved automatically. You can also get it via:", + "authUseToken": "2. Use Token:", + "authUseTokenDesc": "Add the Authorization header:", + "authTip": "💡 This page will use your logged-in Token automatically; no need to fill it manually.", + "tokenDetected": "✓ Token detected - You can test API endpoints directly", + "tokenNotDetected": "⚠ No Token detected - Please log in on the frontend first, then refresh this page. When testing, add Authorization: Bearer token in the request header", + "sidebarGroupTitle": "API Groups", + "allApis": "All APIs", + "loading": "Loading...", + "loadingDesc": "Loading API documentation", + "errorLoginRequired": "Login required to view API docs. Please log in on the frontend first, then refresh this page.", + "errorLoadSpec": "Failed to load API spec: ", + "errorLoadFailed": "Failed to load API docs: ", + "errorSpecInvalid": "Invalid API spec format", + "loadFailed": "Load failed", + "backToLogin": "Back to login", + "noApis": "No APIs", + "noEndpointsInGroup": "No API endpoints in this group", + "sectionDescription": "Description", + "viewDetailDesc": "View details", + "hideDetailDesc": "Hide details", + "noDescription": "No description", + "sectionParams": "Parameters", + "paramName": "Parameter", + "type": "Type", + "description": "Description", + "required": "Required", + "optional": "Optional", + "sectionRequestBody": "Request body", + "example": "Example", + "exampleJson": "Example JSON:", + "sectionResponse": "Response", + "testSection": "Test", + "requestBodyJson": "Request body (JSON)", + "queryParams": "Query parameters:", + "sendRequest": "Send request", + "copyCurl": "Copy cURL", + "clearResult": "Clear result", + "copyCurlTitle": "Copy cURL command", + "clearResultTitle": "Clear test result", + "sendingRequest": "Sending request...", + "errorPathParamRequired": "Path parameter {{name}} is required", + "errorQueryParamRequired": "Query parameter {{name}} is required", + "errorTokenRequired": "No Token detected. Please log in on the frontend and refresh, or add Authorization: Bearer your_token in the request header", + "errorJsonInvalid": "Invalid request body JSON: ", + "requestFailed": "Request failed: ", + "copied": "Copied", + "curlCopied": "curl command copied to clipboard!", + "copyFailedManual": "Copy failed, please copy manually:\n\n", + "curlGenFailed": "Failed to generate cURL command: ", + "requestBodyPlaceholder": "Enter request body in JSON format", + "tags": { + "auth": "Authentication", + "conversationManagement": "Conversation Management", + "conversationInteraction": "Conversation Interaction", + "batchTasks": "Batch Tasks", + "conversationGroups": "Conversation Groups", + "vulnerabilityManagement": "Vulnerability Management", + "roleManagement": "Role Management", + "skillsManagement": "Skills Management", + "monitoring": "Monitoring", + "configManagement": "Configuration Management", + "externalMCPManagement": "External MCP Management", + "attackChain": "Attack Chain", + "knowledgeBase": "Knowledge Base", + "mcp": "MCP" + }, + "summary": { + "login": "User login", + "logout": "User logout", + "changePassword": "Change password", + "validateToken": "Validate Token", + "createConversation": "Create conversation", + "listConversations": "List conversations", + "getConversationDetail": "Get conversation detail", + "updateConversation": "Update conversation", + "deleteConversation": "Delete conversation", + "getConversationResult": "Get conversation result", + "sendMessageNonStream": "Send message and get AI reply (non-stream)", + "sendMessageStream": "Send message and get AI reply (stream)", + "cancelTask": "Cancel task", + "listRunningTasks": "List running tasks", + "listCompletedTasks": "List completed tasks", + "createBatchQueue": "Create batch task queue", + "listBatchQueues": "List batch task queues", + "getBatchQueue": "Get batch task queue", + "deleteBatchQueue": "Delete batch task queue", + "startBatchQueue": "Start batch task queue", + "pauseBatchQueue": "Pause batch task queue", + "addTaskToQueue": "Add task to queue", + "sqlInjectionScan": "SQL injection scan", + "portScan": "Port scan", + "updateBatchTask": "Update batch task", + "deleteBatchTask": "Delete batch task", + "createGroup": "Create group", + "listGroups": "List groups", + "getGroup": "Get group", + "updateGroup": "Update group", + "deleteGroup": "Delete group", + "getGroupConversations": "Get conversations in group", + "addConversationToGroup": "Add conversation to group", + "removeConversationFromGroup": "Remove conversation from group", + "listVulnerabilities": "List vulnerabilities", + "createVulnerability": "Create vulnerability", + "getVulnerabilityStats": "Get vulnerability statistics", + "getVulnerability": "Get vulnerability", + "updateVulnerability": "Update vulnerability", + "deleteVulnerability": "Delete vulnerability", + "listRoles": "List roles", + "createRole": "Create role", + "getRole": "Get role", + "updateRole": "Update role", + "deleteRole": "Delete role", + "getAvailableSkills": "Get available Skills list", + "listSkills": "List Skills", + "createSkill": "Create Skill", + "getSkillStats": "Get Skill statistics", + "clearSkillStats": "Clear Skill statistics", + "getSkill": "Get Skill", + "updateSkill": "Update Skill", + "deleteSkill": "Delete Skill", + "getBoundRoles": "Get bound roles", + "clearSkillStatsAlt": "Clear Skill statistics", + "getMonitorInfo": "Get monitoring info", + "getExecutionRecords": "Get execution records", + "deleteExecutionRecord": "Delete execution record", + "batchDeleteExecutionRecords": "Batch delete execution records", + "getStats": "Get statistics", + "getConfig": "Get configuration", + "updateConfig": "Update configuration", + "getToolConfig": "Get tool configuration", + "applyConfig": "Apply configuration", + "listExternalMCP": "List external MCP", + "getExternalMCPStats": "Get external MCP statistics", + "getExternalMCP": "Get external MCP", + "addOrUpdateExternalMCP": "Add or update external MCP", + "stdioModeConfig": "stdio mode config", + "sseModeConfig": "SSE mode config", + "deleteExternalMCP": "Delete external MCP", + "startExternalMCP": "Start external MCP", + "stopExternalMCP": "Stop external MCP", + "getAttackChain": "Get attack chain", + "regenerateAttackChain": "Regenerate attack chain", + "pinConversation": "Pin conversation", + "pinGroup": "Pin group", + "pinGroupConversation": "Pin conversation in group", + "getCategories": "Get categories", + "listKnowledgeItems": "List knowledge items", + "createKnowledgeItem": "Create knowledge item", + "getKnowledgeItem": "Get knowledge item", + "updateKnowledgeItem": "Update knowledge item", + "deleteKnowledgeItem": "Delete knowledge item", + "getIndexStatus": "Get index status", + "rebuildIndex": "Rebuild index", + "scanKnowledgeBase": "Scan knowledge base", + "searchKnowledgeBase": "Search knowledge base", + "basicSearch": "Basic search", + "searchByRiskType": "Search by risk type", + "getRetrievalLogs": "Get retrieval logs", + "deleteRetrievalLog": "Delete retrieval log", + "mcpEndpoint": "MCP endpoint", + "listAllTools": "List all tools", + "invokeTool": "Invoke tool", + "initConnection": "Initialize connection", + "successResponse": "Success response", + "errorResponse": "Error response" + }, + "response": { + "getSuccess": "Success", + "unauthorized": "Unauthorized", + "unauthorizedToken": "Unauthorized, valid Token required", + "createSuccess": "Created successfully", + "badRequest": "Bad request", + "conversationNotFound": "Conversation not found", + "conversationOrResultNotFound": "Conversation or result not found", + "badRequestTaskEmpty": "Bad request (e.g. task is empty)", + "badRequestGroupNameExists": "Bad request or group name already exists", + "groupNotFound": "Group not found", + "badRequestConfig": "Bad request (e.g. invalid config or missing required fields)", + "badRequestQueryEmpty": "Bad request (e.g. query is empty)", + "methodNotAllowed": "Method not allowed (POST only)", + "loginSuccess": "Login successful", + "invalidPassword": "Invalid password", + "logoutSuccess": "Logout successful", + "passwordChanged": "Password changed successfully", + "tokenValid": "Token valid", + "tokenInvalid": "Token invalid or expired", + "conversationCreated": "Conversation created", + "internalError": "Internal server error", + "updateSuccess": "Updated successfully", + "deleteSuccess": "Deleted successfully", + "queueNotFound": "Queue not found", + "startSuccess": "Started successfully", + "pauseSuccess": "Paused successfully", + "addSuccess": "Added successfully", + "taskNotFound": "Task not found", + "conversationOrGroupNotFound": "Conversation or group not found", + "cancelSubmitted": "Cancel request submitted", + "noRunningTask": "No running task found", + "messageSent": "Message sent, AI reply returned", + "streamResponse": "Stream response (Server-Sent Events)" + } }, "chatGroup": { "search": "Search", @@ -799,6 +1051,13 @@ "execInfo": "Execution info", "tool": "Tool", "status": "Status", + "statusPending": "Pending", + "statusRunning": "Running", + "statusCompleted": "Completed", + "statusFailed": "Failed", + "unknown": "Unknown", + "getDetailFailed": "Failed to get details", + "execSuccessNoContent": "Execution succeeded with no displayable content.", "time": "Time", "executionId": "Execution ID", "requestParams": "Request params", diff --git a/web/static/i18n/zh-CN.json b/web/static/i18n/zh-CN.json index 072de55d..637db159 100644 --- a/web/static/i18n/zh-CN.json +++ b/web/static/i18n/zh-CN.json @@ -1,4 +1,8 @@ { + "lang": { + "zhCN": "中文", + "enUS": "English" + }, "common": { "ok": "确定", "cancel": "取消", @@ -132,10 +136,51 @@ "executeFailed": "执行失败", "callOpenAIFailed": "调用OpenAI失败", "systemReadyMessage": "系统已就绪。请输入您的测试需求,系统将自动执行相应的安全测试。", - "addNewGroup": "+ 新增分组" + "addNewGroup": "+ 新增分组", + "callNumber": "调用 #{{n}}", + "iterationRound": "第 {{n}} 轮迭代", + "aiThinking": "AI思考", + "toolCallsDetected": "检测到 {{count}} 个工具调用", + "callTool": "调用工具: {{name}} ({{index}}/{{total}})", + "toolExecComplete": "工具 {{name}} 执行完成", + "toolExecFailed": "工具 {{name}} 执行失败", + "knowledgeRetrieval": "知识检索", + "knowledgeRetrievalTag": "知识检索", + "error": "错误", + "taskCancelled": "任务已取消", + "unknownTool": "未知工具", + "noDescription": "暂无描述", + "noResponseData": "暂无响应数据", + "loading": "加载中...", + "loadFailed": "加载失败: {{message}}", + "noAttackChainData": "暂无攻击链数据", + "copyFailedManual": "复制失败,请手动选择内容复制", + "searching": "搜索中...", + "loadFailedRetry": "加载失败,请重试", + "dataFormatError": "数据格式错误", + "progressInProgress": "渗透测试进行中..." + }, + "progress": { + "callingAI": "正在调用AI模型...", + "callingTool": "正在调用工具: {{name}}", + "lastIterSummary": "最后一次迭代:正在生成总结和下一步计划...", + "summaryDone": "总结生成完成", + "generatingFinalReply": "正在生成最终回复...", + "maxIterSummary": "达到最大迭代次数,正在生成总结..." + }, + "timeline": { + "params": "参数:", + "executionResult": "执行结果:", + "executionId": "执行ID:", + "noResult": "无结果", + "running": "执行中...", + "completed": "已完成", + "execFailed": "执行失败" }, "tasks": { "title": "任务管理", + "stopTask": "停止任务", + "collapseDetail": "收起详情", "newTask": "新建任务", "autoRefresh": "自动刷新", "historyHint": "提示:有已完成的任务历史,请勾选\"显示历史记录\"查看", @@ -500,7 +545,214 @@ "loadCallStatsError": "无法加载调用统计" }, "apiDocs": { - "curlCopied": "curl命令已复制到剪贴板!" + "pageTitle": "API 文档 - CyberStrikeAI", + "title": "API 文档", + "subtitle": "CyberStrikeAI 平台 API 接口文档,支持在线测试", + "authTitle": "API 认证说明", + "authAllNeedToken": "所有 API 接口都需要 Token 认证。", + "authGetToken": "1. 获取 Token:", + "authGetTokenDesc": "在前端页面登录后,Token 会自动保存。您也可以通过以下方式获取:", + "authUseToken": "2. 使用 Token:", + "authUseTokenDesc": "在请求头中添加 Authorization 字段:", + "authTip": "💡 提示:本页面会自动使用您已登录的 Token,无需手动填写。", + "tokenDetected": "✓ 已检测到 Token - 您可以直接测试 API 接口", + "tokenNotDetected": "⚠ 未检测到 Token - 请先在前端页面登录,然后刷新此页面。测试接口时需要在请求头中添加 Authorization: Bearer token", + "sidebarGroupTitle": "API 分组", + "allApis": "全部接口", + "loading": "加载中...", + "loadingDesc": "正在加载 API 文档", + "errorLoginRequired": "需要登录才能查看API文档。请先在前端页面登录,然后刷新此页面。", + "errorLoadSpec": "加载API规范失败: ", + "errorLoadFailed": "加载API文档失败: ", + "errorSpecInvalid": "API规范格式错误", + "loadFailed": "加载失败", + "backToLogin": "返回首页登录", + "noApis": "暂无API", + "noEndpointsInGroup": "该分组下没有API端点", + "sectionDescription": "描述", + "viewDetailDesc": "查看详细说明", + "hideDetailDesc": "隐藏详细说明", + "noDescription": "无描述", + "sectionParams": "参数", + "paramName": "参数名", + "type": "类型", + "description": "描述", + "required": "必需", + "optional": "可选", + "sectionRequestBody": "请求体", + "example": "示例", + "exampleJson": "示例JSON:", + "sectionResponse": "响应", + "testSection": "测试接口", + "requestBodyJson": "请求体 (JSON)", + "queryParams": "查询参数:", + "sendRequest": "发送请求", + "copyCurl": "复制curl", + "clearResult": "清除结果", + "copyCurlTitle": "复制curl命令", + "clearResultTitle": "清除测试结果", + "sendingRequest": "发送请求中...", + "errorPathParamRequired": "路径参数 {{name}} 不能为空", + "errorQueryParamRequired": "查询参数 {{name}} 不能为空", + "errorTokenRequired": "未检测到 Token。请先在前端页面登录,然后刷新此页面。或者手动在请求头中添加 Authorization: Bearer your_token", + "errorJsonInvalid": "请求体JSON格式错误: ", + "requestFailed": "请求失败: ", + "copied": "已复制", + "curlCopied": "curl命令已复制到剪贴板!", + "copyFailedManual": "复制失败,请手动复制:\n\n", + "curlGenFailed": "生成curl命令失败: ", + "requestBodyPlaceholder": "请输入JSON格式的请求体", + "tags": { + "auth": "认证", + "conversationManagement": "对话管理", + "conversationInteraction": "对话交互", + "batchTasks": "批量任务", + "conversationGroups": "对话分组", + "vulnerabilityManagement": "漏洞管理", + "roleManagement": "角色管理", + "skillsManagement": "Skills管理", + "monitoring": "监控", + "configManagement": "配置管理", + "externalMCPManagement": "外部MCP管理", + "attackChain": "攻击链", + "knowledgeBase": "知识库", + "mcp": "MCP" + }, + "summary": { + "login": "用户登录", + "logout": "用户登出", + "changePassword": "修改密码", + "validateToken": "验证Token", + "createConversation": "创建对话", + "listConversations": "列出对话", + "getConversationDetail": "查看对话详情", + "updateConversation": "更新对话", + "deleteConversation": "删除对话", + "getConversationResult": "获取对话结果", + "sendMessageNonStream": "发送消息并获取AI回复(非流式)", + "sendMessageStream": "发送消息并获取AI回复(流式)", + "cancelTask": "取消任务", + "listRunningTasks": "列出运行中的任务", + "listCompletedTasks": "列出已完成的任务", + "createBatchQueue": "创建批量任务队列", + "listBatchQueues": "列出批量任务队列", + "getBatchQueue": "获取批量任务队列", + "deleteBatchQueue": "删除批量任务队列", + "startBatchQueue": "启动批量任务队列", + "pauseBatchQueue": "暂停批量任务队列", + "addTaskToQueue": "添加任务到队列", + "sqlInjectionScan": "SQL注入扫描", + "portScan": "端口扫描", + "updateBatchTask": "更新批量任务", + "deleteBatchTask": "删除批量任务", + "createGroup": "创建分组", + "listGroups": "列出分组", + "getGroup": "获取分组", + "updateGroup": "更新分组", + "deleteGroup": "删除分组", + "getGroupConversations": "获取分组中的对话", + "addConversationToGroup": "添加对话到分组", + "removeConversationFromGroup": "从分组移除对话", + "listVulnerabilities": "列出漏洞", + "createVulnerability": "创建漏洞", + "getVulnerabilityStats": "获取漏洞统计", + "getVulnerability": "获取漏洞", + "updateVulnerability": "更新漏洞", + "deleteVulnerability": "删除漏洞", + "listRoles": "列出角色", + "createRole": "创建角色", + "getRole": "获取角色", + "updateRole": "更新角色", + "deleteRole": "删除角色", + "getAvailableSkills": "获取可用Skills列表", + "listSkills": "列出Skills", + "createSkill": "创建Skill", + "getSkillStats": "获取Skill统计", + "clearSkillStats": "清空Skill统计", + "getSkill": "获取Skill", + "updateSkill": "更新Skill", + "deleteSkill": "删除Skill", + "getBoundRoles": "获取绑定角色", + "clearSkillStatsAlt": "清空Skill统计", + "getMonitorInfo": "获取监控信息", + "getExecutionRecords": "获取执行记录", + "deleteExecutionRecord": "删除执行记录", + "batchDeleteExecutionRecords": "批量删除执行记录", + "getStats": "获取统计信息", + "getConfig": "获取配置", + "updateConfig": "更新配置", + "getToolConfig": "获取工具配置", + "applyConfig": "应用配置", + "listExternalMCP": "列出外部MCP", + "getExternalMCPStats": "获取外部MCP统计", + "getExternalMCP": "获取外部MCP", + "addOrUpdateExternalMCP": "添加或更新外部MCP", + "stdioModeConfig": "stdio模式配置", + "sseModeConfig": "SSE模式配置", + "deleteExternalMCP": "删除外部MCP", + "startExternalMCP": "启动外部MCP", + "stopExternalMCP": "停止外部MCP", + "getAttackChain": "获取攻击链", + "regenerateAttackChain": "重新生成攻击链", + "pinConversation": "设置对话置顶", + "pinGroup": "设置分组置顶", + "pinGroupConversation": "设置分组中对话的置顶", + "getCategories": "获取分类", + "listKnowledgeItems": "列出知识项", + "createKnowledgeItem": "创建知识项", + "getKnowledgeItem": "获取知识项", + "updateKnowledgeItem": "更新知识项", + "deleteKnowledgeItem": "删除知识项", + "getIndexStatus": "获取索引状态", + "rebuildIndex": "重建索引", + "scanKnowledgeBase": "扫描知识库", + "searchKnowledgeBase": "搜索知识库", + "basicSearch": "基础搜索", + "searchByRiskType": "按风险类型搜索", + "getRetrievalLogs": "获取检索日志", + "deleteRetrievalLog": "删除检索日志", + "mcpEndpoint": "MCP端点", + "listAllTools": "列出所有工具", + "invokeTool": "调用工具", + "initConnection": "初始化连接", + "successResponse": "成功响应", + "errorResponse": "错误响应" + }, + "response": { + "getSuccess": "获取成功", + "unauthorized": "未授权", + "unauthorizedToken": "未授权,需要有效的Token", + "createSuccess": "创建成功", + "badRequest": "请求参数错误", + "conversationNotFound": "对话不存在", + "conversationOrResultNotFound": "对话不存在或结果不存在", + "badRequestTaskEmpty": "请求参数错误(如task为空)", + "badRequestGroupNameExists": "请求参数错误或分组名称已存在", + "groupNotFound": "分组不存在", + "badRequestConfig": "请求参数错误(如配置格式不正确、缺少必需字段等)", + "badRequestQueryEmpty": "请求参数错误(如query为空)", + "methodNotAllowed": "方法不允许(仅支持POST请求)", + "loginSuccess": "登录成功", + "invalidPassword": "密码错误", + "logoutSuccess": "登出成功", + "passwordChanged": "密码修改成功", + "tokenValid": "Token有效", + "tokenInvalid": "Token无效或已过期", + "conversationCreated": "对话创建成功", + "internalError": "服务器内部错误", + "updateSuccess": "更新成功", + "deleteSuccess": "删除成功", + "queueNotFound": "队列不存在", + "startSuccess": "启动成功", + "pauseSuccess": "暂停成功", + "addSuccess": "添加成功", + "taskNotFound": "任务不存在", + "conversationOrGroupNotFound": "对话或分组不存在", + "cancelSubmitted": "取消请求已提交", + "noRunningTask": "未找到正在执行的任务", + "messageSent": "消息发送成功,返回AI回复", + "streamResponse": "流式响应(Server-Sent Events)" + } }, "chatGroup": { "search": "搜索", @@ -799,6 +1051,13 @@ "execInfo": "执行信息", "tool": "工具", "status": "状态", + "statusPending": "等待中", + "statusRunning": "执行中", + "statusCompleted": "已完成", + "statusFailed": "失败", + "unknown": "未知", + "getDetailFailed": "获取详情失败", + "execSuccessNoContent": "执行成功,未返回可展示的文本内容。", "time": "时间", "executionId": "执行 ID", "requestParams": "请求参数", diff --git a/web/static/js/api-docs.js b/web/static/js/api-docs.js index be7266c8..2e0ae1db 100644 --- a/web/static/js/api-docs.js +++ b/web/static/js/api-docs.js @@ -3,13 +3,74 @@ let apiSpec = null; let currentToken = null; +function _t(key, opts) { + return typeof window.t === 'function' ? window.t(key, opts) : key; +} + +function waitForI18n() { + return new Promise(function (resolve) { + if (window.t) return resolve(); + var n = 0; + var iv = setInterval(function () { + if (window.t) { clearInterval(iv); resolve(); return; } + n++; + if (n >= 100) { clearInterval(iv); resolve(); } + }, 50); + }); +} + +// 从 OpenAPI spec 的 x-i18n-tags 构建 tag -> i18n key 映射(方案 A:后端提供键) +var apiSpecTagToKey = {}; +function buildApiSpecTagToKey() { + apiSpecTagToKey = {}; + if (!apiSpec || !apiSpec.paths) return; + Object.keys(apiSpec.paths).forEach(function (path) { + var pathItem = apiSpec.paths[path]; + if (!pathItem || typeof pathItem !== 'object') return; + ['get', 'post', 'put', 'delete', 'patch'].forEach(function (method) { + var op = pathItem[method]; + if (!op || !op.tags || !op['x-i18n-tags']) return; + var tags = op.tags; + var keys = op['x-i18n-tags']; + for (var i = 0; i < tags.length && i < keys.length; i++) { + apiSpecTagToKey[tags[i]] = typeof keys[i] === 'string' ? keys[i] : keys[i]; + } + }); + }); +} +function translateApiDocTag(tag) { + if (!tag) return tag; + var key = apiSpecTagToKey[tag]; + return key ? _t('apiDocs.tags.' + key) : tag; +} +function translateApiDocSummaryFromOp(op) { + var key = op && op['x-i18n-summary']; + if (key) return _t('apiDocs.summary.' + key); + return op && op.summary ? op.summary : ''; +} +function translateApiDocResponseDescFromResp(resp) { + if (!resp) return ''; + var key = resp['x-i18n-description']; + if (key) return _t('apiDocs.response.' + key); + return resp.description || ''; +} + // 初始化 document.addEventListener('DOMContentLoaded', async () => { + await waitForI18n(); await loadToken(); await loadAPISpec(); if (apiSpec) { renderAPIDocs(); } + document.addEventListener('languagechange', function () { + if (typeof window.applyTranslations === 'function') { + window.applyTranslations(document); + } + if (apiSpec) { + renderAPIDocs(); + } + }); }); // 加载token @@ -43,22 +104,25 @@ async function loadAPISpec() { const response = await fetch(url); if (!response.ok) { if (response.status === 401) { - showError('需要登录才能查看API文档。请先在前端页面登录,然后刷新此页面。'); + showError(_t('apiDocs.errorLoginRequired')); return; } - throw new Error('加载API规范失败: ' + response.status); + throw new Error(_t('apiDocs.errorLoadSpec') + response.status); } apiSpec = await response.json(); + buildApiSpecTagToKey(); } catch (error) { console.error('加载API规范失败:', error); - showError('加载API文档失败: ' + error.message); + showError(_t('apiDocs.errorLoadFailed') + error.message); } } // 显示错误 function showError(message) { const main = document.getElementById('api-docs-main'); + const loadFailed = _t('apiDocs.loadFailed'); + const backToLogin = _t('apiDocs.backToLogin'); main.innerHTML = `
${message}
+${escapeHtml(message)}
⚠ 未检测到 Token - 请先在前端页面登录,然后刷新此页面。测试接口时需要在请求头中添加 Authorization: Bearer token
'; + tokenStatus.innerHTML = '' + escapeHtml(_t('apiDocs.tokenNotDetected')) + '
'; } } @@ -127,11 +191,14 @@ function renderSidebar() { const groupList = document.getElementById('api-group-list'); const allGroups = Array.from(groups).sort(); - + while (groupList.children.length > 1) { + groupList.removeChild(groupList.lastChild); + } allGroups.forEach(group => { const li = document.createElement('li'); li.className = 'api-group-item'; - li.innerHTML = `${group}`; + const groupLabel = translateApiDocTag(group); + li.innerHTML = `${escapeHtml(groupLabel)}`; groupList.appendChild(li); }); @@ -176,7 +243,7 @@ function renderEndpoints(filterGroup = null) { }); if (endpoints.length === 0) { - main.innerHTML = '该分组下没有API端点
' + escapeHtml(_t('apiDocs.noEndpointsInGroup')) + '
| 参数名 | -类型 | -描述 | -必需 | +${paramName} | +${typeLabel} | +${descLabel} | +${requiredLabel} |
|---|
| 参数名 | -类型 | -描述 | -必需 | -示例 | +${pName} | +${tLabel} | +${dLabel} | +${reqLabel} | +${exLabel} |
|---|