package handler import ( "net/http" "time" "cyberstrike-ai/internal/database" "cyberstrike-ai/internal/storage" "github.com/gin-gonic/gin" "go.uber.org/zap" ) // OpenAPIHandler OpenAPI处理器 type OpenAPIHandler struct { db *database.DB logger *zap.Logger resultStorage storage.ResultStorage conversationHdlr *ConversationHandler agentHdlr *AgentHandler } // NewOpenAPIHandler 创建新的OpenAPI处理器 func NewOpenAPIHandler(db *database.DB, logger *zap.Logger, resultStorage storage.ResultStorage, conversationHdlr *ConversationHandler, agentHdlr *AgentHandler) *OpenAPIHandler { return &OpenAPIHandler{ db: db, logger: logger, resultStorage: resultStorage, conversationHdlr: conversationHdlr, agentHdlr: agentHdlr, } } // GetOpenAPISpec 获取OpenAPI规范 func (h *OpenAPIHandler) GetOpenAPISpec(c *gin.Context) { host := c.Request.Host scheme := "http" if c.Request.TLS != nil { scheme = "https" } spec := map[string]interface{}{ "openapi": "3.0.0", "info": map[string]interface{}{ "title": "CyberStrikeAI API", "description": "AI驱动的自动化安全测试平台API文档", "version": "1.0.0", "contact": map[string]interface{}{ "name": "CyberStrikeAI", }, }, "servers": []map[string]interface{}{ { "url": scheme + "://" + host, "description": "当前服务器", }, }, "components": map[string]interface{}{ "securitySchemes": map[string]interface{}{ "bearerAuth": map[string]interface{}{ "type": "http", "scheme": "bearer", "bearerFormat": "JWT", "description": "使用Bearer Token进行认证。Token通过 /api/auth/login 接口获取。", }, }, "schemas": map[string]interface{}{ "CreateConversationRequest": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "title": map[string]interface{}{ "type": "string", "description": "对话标题", "example": "Web应用安全测试", }, }, }, "Conversation": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "id": map[string]interface{}{ "type": "string", "description": "对话ID", "example": "550e8400-e29b-41d4-a716-446655440000", }, "title": map[string]interface{}{ "type": "string", "description": "对话标题", "example": "Web应用安全测试", }, "createdAt": map[string]interface{}{ "type": "string", "format": "date-time", "description": "创建时间", }, "updatedAt": map[string]interface{}{ "type": "string", "format": "date-time", "description": "更新时间", }, }, }, "ConversationDetail": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "id": map[string]interface{}{ "type": "string", "description": "对话ID", }, "title": map[string]interface{}{ "type": "string", "description": "对话标题", }, "status": map[string]interface{}{ "type": "string", "description": "对话状态:active(进行中)、completed(已完成)、failed(失败)", "enum": []string{"active", "completed", "failed"}, }, "createdAt": map[string]interface{}{ "type": "string", "format": "date-time", "description": "创建时间", }, "updatedAt": map[string]interface{}{ "type": "string", "format": "date-time", "description": "更新时间", }, "messages": map[string]interface{}{ "type": "array", "description": "消息列表", "items": map[string]interface{}{ "$ref": "#/components/schemas/Message", }, }, "messageCount": map[string]interface{}{ "type": "integer", "description": "消息数量", }, }, }, "Message": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "id": map[string]interface{}{ "type": "string", "description": "消息ID", }, "conversationId": map[string]interface{}{ "type": "string", "description": "对话ID", }, "role": map[string]interface{}{ "type": "string", "description": "消息角色:user(用户)、assistant(助手)", "enum": []string{"user", "assistant"}, }, "content": map[string]interface{}{ "type": "string", "description": "消息内容", }, "createdAt": map[string]interface{}{ "type": "string", "format": "date-time", "description": "创建时间", }, }, }, "ConversationResults": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "conversationId": map[string]interface{}{ "type": "string", "description": "对话ID", }, "messages": map[string]interface{}{ "type": "array", "description": "消息列表", "items": map[string]interface{}{ "$ref": "#/components/schemas/Message", }, }, "vulnerabilities": map[string]interface{}{ "type": "array", "description": "发现的漏洞列表", "items": map[string]interface{}{ "$ref": "#/components/schemas/Vulnerability", }, }, "executionResults": map[string]interface{}{ "type": "array", "description": "执行结果列表", "items": map[string]interface{}{ "$ref": "#/components/schemas/ExecutionResult", }, }, }, }, "Vulnerability": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "id": map[string]interface{}{ "type": "string", "description": "漏洞ID", }, "title": map[string]interface{}{ "type": "string", "description": "漏洞标题", }, "description": map[string]interface{}{ "type": "string", "description": "漏洞描述", }, "severity": map[string]interface{}{ "type": "string", "description": "严重程度", "enum": []string{"critical", "high", "medium", "low", "info"}, }, "status": map[string]interface{}{ "type": "string", "description": "状态", "enum": []string{"open", "closed", "fixed"}, }, "target": map[string]interface{}{ "type": "string", "description": "受影响的目标", }, }, }, "ExecutionResult": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "id": map[string]interface{}{ "type": "string", "description": "执行ID", }, "toolName": map[string]interface{}{ "type": "string", "description": "工具名称", }, "status": map[string]interface{}{ "type": "string", "description": "执行状态", "enum": []string{"success", "failed", "running"}, }, "result": map[string]interface{}{ "type": "string", "description": "执行结果", }, "createdAt": map[string]interface{}{ "type": "string", "format": "date-time", "description": "创建时间", }, }, }, "Error": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "error": map[string]interface{}{ "type": "string", "description": "错误信息", }, }, }, }, }, "security": []map[string]interface{}{ { "bearerAuth": []string{}, }, }, "paths": map[string]interface{}{ "/api/conversations": map[string]interface{}{ "post": map[string]interface{}{ "tags": []string{"对话管理"}, "summary": "创建对话", "description": "创建一个新的安全测试对话。\n\n**重要说明**:\n- ✅ 创建的对话会**立即保存到数据库**\n- ✅ 前端页面会**自动刷新**显示新对话\n- ✅ 与前端创建的对话**完全一致**\n\n**创建对话的两种方式**:\n\n**方式1(推荐):** 直接使用 `/api/agent-loop` 发送消息,**不提供** `conversationId` 参数,系统会自动创建新对话并发送消息。这是最简单的方式,一步完成创建和发送。\n\n**方式2:** 先调用此端点创建空对话,然后使用返回的 `conversationId` 调用 `/api/agent-loop` 发送消息。适用于需要先创建对话,稍后再发送消息的场景。\n\n**示例**:\n```json\n{\n \"title\": \"Web应用安全测试\"\n}\n```", "operationId": "createConversation", "requestBody": map[string]interface{}{ "required": true, "content": map[string]interface{}{ "application/json": map[string]interface{}{ "schema": map[string]interface{}{ "$ref": "#/components/schemas/CreateConversationRequest", }, }, }, }, "responses": map[string]interface{}{ "200": map[string]interface{}{ "description": "对话创建成功", "content": map[string]interface{}{ "application/json": map[string]interface{}{ "schema": map[string]interface{}{ "$ref": "#/components/schemas/Conversation", }, }, }, }, "400": map[string]interface{}{ "description": "请求参数错误", }, "401": map[string]interface{}{ "description": "未授权,需要有效的Token", }, "500": map[string]interface{}{ "description": "服务器内部错误", }, }, }, "get": map[string]interface{}{ "tags": []string{"对话管理"}, "summary": "列出对话", "description": "获取对话列表,支持分页和搜索", "operationId": "listConversations", "parameters": []map[string]interface{}{ { "name": "limit", "in": "query", "required": false, "description": "返回数量限制", "schema": map[string]interface{}{ "type": "integer", "default": 50, "minimum": 1, "maximum": 100, }, }, { "name": "offset", "in": "query", "required": false, "description": "偏移量", "schema": map[string]interface{}{ "type": "integer", "default": 0, "minimum": 0, }, }, { "name": "search", "in": "query", "required": false, "description": "搜索关键词", "schema": map[string]interface{}{ "type": "string", }, }, }, "responses": map[string]interface{}{ "200": map[string]interface{}{ "description": "获取成功", "content": map[string]interface{}{ "application/json": map[string]interface{}{ "schema": map[string]interface{}{ "type": "array", "items": map[string]interface{}{ "$ref": "#/components/schemas/Conversation", }, }, }, }, }, "401": map[string]interface{}{ "description": "未授权,需要有效的Token", }, }, }, }, "/api/conversations/{id}": map[string]interface{}{ "get": map[string]interface{}{ "tags": []string{"对话管理"}, "summary": "查看对话详情", "description": "获取指定对话的详细信息,包括对话信息和消息列表", "operationId": "getConversation", "parameters": []map[string]interface{}{ { "name": "id", "in": "path", "required": true, "description": "对话ID", "schema": map[string]interface{}{ "type": "string", }, }, }, "responses": map[string]interface{}{ "200": map[string]interface{}{ "description": "获取成功", "content": map[string]interface{}{ "application/json": map[string]interface{}{ "schema": map[string]interface{}{ "$ref": "#/components/schemas/ConversationDetail", }, }, }, }, "404": map[string]interface{}{ "description": "对话不存在", }, "401": map[string]interface{}{ "description": "未授权,需要有效的Token", }, }, }, "delete": map[string]interface{}{ "tags": []string{"对话管理"}, "summary": "删除对话", "description": "删除指定的对话及其所有相关数据(消息、漏洞等)。**此操作不可恢复**。", "operationId": "deleteConversation", "parameters": []map[string]interface{}{ { "name": "id", "in": "path", "required": true, "description": "对话ID", "schema": map[string]interface{}{ "type": "string", }, }, }, "responses": map[string]interface{}{ "200": map[string]interface{}{ "description": "删除成功", "content": map[string]interface{}{ "application/json": map[string]interface{}{ "schema": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "message": map[string]interface{}{ "type": "string", "description": "成功消息", "example": "删除成功", }, }, }, }, }, }, "404": map[string]interface{}{ "description": "对话不存在", }, "401": map[string]interface{}{ "description": "未授权,需要有效的Token", }, "500": map[string]interface{}{ "description": "服务器内部错误", }, }, }, }, "/api/conversations/{id}/results": map[string]interface{}{ "get": map[string]interface{}{ "tags": []string{"结果查询"}, "summary": "获取对话结果", "description": "获取指定对话的执行结果,包括消息、漏洞信息和执行结果", "operationId": "getConversationResults", "parameters": []map[string]interface{}{ { "name": "id", "in": "path", "required": true, "description": "对话ID", "schema": map[string]interface{}{ "type": "string", }, }, }, "responses": map[string]interface{}{ "200": map[string]interface{}{ "description": "获取成功", "content": map[string]interface{}{ "application/json": map[string]interface{}{ "schema": map[string]interface{}{ "$ref": "#/components/schemas/ConversationResults", }, }, }, }, "404": map[string]interface{}{ "description": "对话不存在或结果不存在", }, "401": map[string]interface{}{ "description": "未授权,需要有效的Token", }, }, }, }, "/api/agent-loop": map[string]interface{}{ "post": map[string]interface{}{ "tags": []string{"对话交互"}, "summary": "发送消息并获取AI回复(核心端点)", "description": "向AI发送消息并获取回复。**这是与AI交互的核心端点**,与前端聊天功能完全一致。\n\n**重要说明**:\n- ✅ 通过此API创建/发送的消息会**立即保存到数据库**\n- ✅ 前端页面会**自动刷新**显示新创建的对话和消息\n- ✅ 所有操作都有**完整的交互痕迹**,就像在前端操作一样\n- ✅ 支持角色配置,可以指定使用哪个测试角色\n\n**推荐使用流程**:\n\n1. **先创建对话**:调用 `POST /api/conversations` 创建新对话,获取 `conversationId`\n2. **再发送消息**:使用返回的 `conversationId` 调用此端点发送消息\n\n**使用示例**:\n\n**步骤1 - 创建对话:**\n```json\nPOST /api/conversations\n{\n \"title\": \"Web应用安全测试\"\n}\n```\n\n**步骤2 - 发送消息:**\n```json\nPOST /api/agent-loop\n{\n \"conversationId\": \"返回的对话ID\",\n \"message\": \"扫描 http://example.com 的SQL注入漏洞\",\n \"role\": \"渗透测试\"\n}\n```\n\n**其他方式**:\n\n如果不提供 `conversationId`,系统会自动创建新对话并发送消息。但**推荐先创建对话**,这样可以更好地管理对话列表。\n\n**响应**:返回AI的回复、对话ID和MCP执行ID列表。前端会自动刷新显示新消息。", "operationId": "sendMessage", "requestBody": map[string]interface{}{ "required": true, "content": map[string]interface{}{ "application/json": map[string]interface{}{ "schema": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "message": map[string]interface{}{ "type": "string", "description": "要发送的消息(必需)", "example": "扫描 http://example.com 的SQL注入漏洞", }, "conversationId": map[string]interface{}{ "type": "string", "description": "对话ID(可选)。\n- **不提供**:自动创建新对话并发送消息(推荐)\n- **提供**:消息会添加到指定对话中(对话必须存在)", "example": "550e8400-e29b-41d4-a716-446655440000", }, "role": map[string]interface{}{ "type": "string", "description": "角色名称(可选),如:默认、渗透测试、Web应用扫描等", "example": "默认", }, }, "required": []string{"message"}, }, }, }, }, "responses": map[string]interface{}{ "200": map[string]interface{}{ "description": "消息发送成功,返回AI回复", "content": map[string]interface{}{ "application/json": map[string]interface{}{ "schema": map[string]interface{}{ "type": "object", "properties": map[string]interface{}{ "response": map[string]interface{}{ "type": "string", "description": "AI的回复内容", }, "conversationId": map[string]interface{}{ "type": "string", "description": "对话ID", }, "mcpExecutionIds": map[string]interface{}{ "type": "array", "description": "MCP执行ID列表", "items": map[string]interface{}{ "type": "string", }, }, "time": map[string]interface{}{ "type": "string", "format": "date-time", "description": "响应时间", }, }, }, }, }, }, "400": map[string]interface{}{ "description": "请求参数错误", }, "401": map[string]interface{}{ "description": "未授权,需要有效的Token", }, "500": map[string]interface{}{ "description": "服务器内部错误", }, }, }, }, }, } c.JSON(http.StatusOK, spec) } // GetConversationResults 获取对话结果(OpenAPI端点) // 注意:创建对话和获取对话详情直接使用标准的 /api/conversations 端点 // 这个端点只是为了提供结果聚合功能 func (h *OpenAPIHandler) GetConversationResults(c *gin.Context) { conversationID := c.Param("id") // 验证对话是否存在 conv, err := h.db.GetConversation(conversationID) if err != nil { h.logger.Error("获取对话失败", zap.Error(err)) c.JSON(http.StatusNotFound, gin.H{"error": "对话不存在"}) return } // 获取消息列表 messages, err := h.db.GetMessages(conversationID) if err != nil { h.logger.Error("获取消息失败", zap.Error(err)) c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } // 获取漏洞列表 vulnList, err := h.db.ListVulnerabilities(1000, 0, "", conversationID, "", "") if err != nil { h.logger.Warn("获取漏洞列表失败", zap.Error(err)) vulnList = []*database.Vulnerability{} } vulnerabilities := make([]database.Vulnerability, len(vulnList)) for i, v := range vulnList { vulnerabilities[i] = *v } // 获取执行结果(从MCP执行记录中获取) executionResults := []map[string]interface{}{} for _, msg := range messages { if len(msg.MCPExecutionIDs) > 0 { for _, execID := range msg.MCPExecutionIDs { // 尝试从结果存储中获取执行结果 if h.resultStorage != nil { result, err := h.resultStorage.GetResult(execID) if err == nil && result != "" { // 获取元数据以获取工具名称和创建时间 metadata, err := h.resultStorage.GetResultMetadata(execID) toolName := "unknown" createdAt := time.Now() if err == nil && metadata != nil { toolName = metadata.ToolName createdAt = metadata.CreatedAt } executionResults = append(executionResults, map[string]interface{}{ "id": execID, "toolName": toolName, "status": "success", "result": result, "createdAt": createdAt.Format(time.RFC3339), }) } } } } } response := map[string]interface{}{ "conversationId": conv.ID, "messages": messages, "vulnerabilities": vulnerabilities, "executionResults": executionResults, } c.JSON(http.StatusOK, response) }