mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-05-26 17:27:54 +02:00
263 lines
7.5 KiB
Go
263 lines
7.5 KiB
Go
package handler
|
||
|
||
import (
|
||
"net/http"
|
||
"strconv"
|
||
"strings"
|
||
|
||
"cyberstrike-ai/internal/database"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"go.uber.org/zap"
|
||
)
|
||
|
||
// ProjectHandler 项目管理处理器。
|
||
type ProjectHandler struct {
|
||
db *database.DB
|
||
logger *zap.Logger
|
||
}
|
||
|
||
// NewProjectHandler 创建项目管理处理器。
|
||
func NewProjectHandler(db *database.DB, logger *zap.Logger) *ProjectHandler {
|
||
return &ProjectHandler{db: db, logger: logger}
|
||
}
|
||
|
||
type createProjectRequest struct {
|
||
Name string `json:"name" binding:"required"`
|
||
Description string `json:"description"`
|
||
ScopeJSON string `json:"scope_json"`
|
||
Status string `json:"status"`
|
||
}
|
||
|
||
type updateProjectRequest struct {
|
||
Name string `json:"name"`
|
||
Description string `json:"description"`
|
||
ScopeJSON string `json:"scope_json"`
|
||
Status string `json:"status"`
|
||
Pinned *bool `json:"pinned"`
|
||
}
|
||
|
||
// CreateProject POST /api/projects
|
||
func (h *ProjectHandler) CreateProject(c *gin.Context) {
|
||
var req createProjectRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
p := &database.Project{
|
||
Name: strings.TrimSpace(req.Name),
|
||
Description: req.Description,
|
||
ScopeJSON: req.ScopeJSON,
|
||
Status: strings.TrimSpace(req.Status),
|
||
}
|
||
created, err := h.db.CreateProject(p)
|
||
if err != nil {
|
||
h.logger.Error("创建项目失败", zap.Error(err))
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, created)
|
||
}
|
||
|
||
// ListProjects GET /api/projects
|
||
func (h *ProjectHandler) ListProjects(c *gin.Context) {
|
||
status := c.Query("status")
|
||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "200"))
|
||
offset, _ := strconv.Atoi(c.Query("offset"))
|
||
list, err := h.db.ListProjects(status, limit, offset)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
if list == nil {
|
||
list = []*database.Project{}
|
||
}
|
||
c.JSON(http.StatusOK, list)
|
||
}
|
||
|
||
// GetProject GET /api/projects/:id
|
||
func (h *ProjectHandler) GetProject(c *gin.Context) {
|
||
p, err := h.db.GetProject(c.Param("id"))
|
||
if err != nil {
|
||
c.JSON(http.StatusNotFound, gin.H{"error": "项目不存在"})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, p)
|
||
}
|
||
|
||
// UpdateProject PUT /api/projects/:id
|
||
func (h *ProjectHandler) UpdateProject(c *gin.Context) {
|
||
id := c.Param("id")
|
||
p, err := h.db.GetProject(id)
|
||
if err != nil {
|
||
c.JSON(http.StatusNotFound, gin.H{"error": "项目不存在"})
|
||
return
|
||
}
|
||
var req updateProjectRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
if s := strings.TrimSpace(req.Name); s != "" {
|
||
p.Name = s
|
||
}
|
||
if req.Description != "" || c.Request.ContentLength > 0 {
|
||
p.Description = req.Description
|
||
}
|
||
if req.ScopeJSON != "" || c.GetHeader("Content-Type") != "" {
|
||
p.ScopeJSON = req.ScopeJSON
|
||
}
|
||
if s := strings.TrimSpace(req.Status); s != "" {
|
||
p.Status = s
|
||
}
|
||
if req.Pinned != nil {
|
||
p.Pinned = *req.Pinned
|
||
}
|
||
if err := h.db.UpdateProject(p); err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, p)
|
||
}
|
||
|
||
// DeleteProject DELETE /api/projects/:id
|
||
func (h *ProjectHandler) DeleteProject(c *gin.Context) {
|
||
if err := h.db.DeleteProject(c.Param("id")); err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||
}
|
||
|
||
type upsertFactRequest struct {
|
||
FactKey string `json:"fact_key" binding:"required"`
|
||
Category string `json:"category"`
|
||
Summary string `json:"summary" binding:"required"`
|
||
Body string `json:"body"`
|
||
Confidence string `json:"confidence"`
|
||
Pinned bool `json:"pinned"`
|
||
RelatedVulnerabilityID string `json:"related_vulnerability_id"`
|
||
}
|
||
|
||
// ListFacts GET /api/projects/:id/facts (fact_key 查询参数可获取单条详情)
|
||
func (h *ProjectHandler) ListFacts(c *gin.Context) {
|
||
projectID := c.Param("id")
|
||
if key := strings.TrimSpace(c.Query("fact_key")); key != "" {
|
||
f, err := h.db.GetProjectFactByKey(projectID, key)
|
||
if err != nil {
|
||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, f)
|
||
return
|
||
}
|
||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "100"))
|
||
offset, _ := strconv.Atoi(c.Query("offset"))
|
||
filter := database.ProjectFactListFilter{
|
||
Category: c.Query("category"),
|
||
Confidence: c.Query("confidence"),
|
||
Search: c.Query("search"),
|
||
}
|
||
list, err := h.db.ListProjectFacts(projectID, filter, limit, offset)
|
||
if err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
if list == nil {
|
||
list = []*database.ProjectFact{}
|
||
}
|
||
c.JSON(http.StatusOK, list)
|
||
}
|
||
|
||
// CreateFact POST /api/projects/:id/facts
|
||
func (h *ProjectHandler) CreateFact(c *gin.Context) {
|
||
var req upsertFactRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
f := &database.ProjectFact{
|
||
ProjectID: c.Param("id"),
|
||
FactKey: req.FactKey,
|
||
Category: req.Category,
|
||
Summary: req.Summary,
|
||
Body: req.Body,
|
||
Confidence: req.Confidence,
|
||
Pinned: req.Pinned,
|
||
RelatedVulnerabilityID: req.RelatedVulnerabilityID,
|
||
}
|
||
created, err := h.db.UpsertProjectFact(f)
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, created)
|
||
}
|
||
|
||
// UpdateFact PUT /api/projects/:id/facts/:factId
|
||
func (h *ProjectHandler) UpdateFact(c *gin.Context) {
|
||
existing, err := h.db.GetProjectFact(c.Param("factId"))
|
||
if err != nil || existing.ProjectID != c.Param("id") {
|
||
c.JSON(http.StatusNotFound, gin.H{"error": "事实不存在"})
|
||
return
|
||
}
|
||
var req upsertFactRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
if k := strings.TrimSpace(req.FactKey); k != "" {
|
||
existing.FactKey = k
|
||
}
|
||
if req.Category != "" {
|
||
existing.Category = req.Category
|
||
}
|
||
if req.Summary != "" {
|
||
existing.Summary = req.Summary
|
||
}
|
||
existing.Body = req.Body
|
||
if req.Confidence != "" {
|
||
existing.Confidence = req.Confidence
|
||
}
|
||
existing.Pinned = req.Pinned
|
||
existing.RelatedVulnerabilityID = req.RelatedVulnerabilityID
|
||
updated, err := h.db.UpsertProjectFact(existing)
|
||
if err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, updated)
|
||
}
|
||
|
||
// DeleteFact DELETE /api/projects/:id/facts/:factId
|
||
func (h *ProjectHandler) DeleteFact(c *gin.Context) {
|
||
existing, err := h.db.GetProjectFact(c.Param("factId"))
|
||
if err != nil || existing.ProjectID != c.Param("id") {
|
||
c.JSON(http.StatusNotFound, gin.H{"error": "事实不存在"})
|
||
return
|
||
}
|
||
if err := h.db.DeleteProjectFact(existing.ID); err != nil {
|
||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||
}
|
||
|
||
type deprecateFactRequest struct {
|
||
FactKey string `json:"fact_key" binding:"required"`
|
||
}
|
||
|
||
// DeprecateFact POST /api/projects/:id/facts/deprecate
|
||
func (h *ProjectHandler) DeprecateFact(c *gin.Context) {
|
||
var req deprecateFactRequest
|
||
if err := c.ShouldBindJSON(&req); err != nil {
|
||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
if err := h.db.DeprecateProjectFact(c.Param("id"), req.FactKey); err != nil {
|
||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||
}
|