feat: 工具配置增强、站点拨测与日志中心多项改进
- 工具配置:新增卸载 API/UI、Go 工具链、流水线构建自动安装依赖 - 修复 ListTools VerifyTool 递归栈溢出导致获取工具配置失败 - Node.js Alpine 安装改用 unofficial-builds musl 预编译包与 index.json 解析 LTS - 站点拨测:详情页路由、告警自动恢复、并发拨测与默认可观测频率 - 日志中心:RBAC、查询构建器、ES/Loki 增强与审计 request/response 展示 - 发布配置:构建节点、流水线远程执行、Deploy RBAC 导航拆分
This commit is contained in:
1 parent
cde7bbe540
commit
7e1c8f583c
84 files changed
+7729
-1507
No files matched your search
@@ -0,0 +1,201 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"ezops/model"
|
||||
"ezops/pkg/logaccess"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// LogScopeRoleHandler 日志查询权限 // Logan.Li
|
||||
type LogScopeRoleHandler struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewLogScopeRoleHandler(db *gorm.DB) *LogScopeRoleHandler {
|
||||
return &LogScopeRoleHandler{db: db}
|
||||
}
|
||||
|
||||
// ListLogScopeRoles GET /log-scope-roles
|
||||
func (h *LogScopeRoleHandler) ListLogScopeRoles(c *gin.Context) {
|
||||
var roles []model.LogScopeRole
|
||||
h.db.Order("created_at DESC").Find(&roles)
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Data: model.ListResponse{List: roles, Total: int64(len(roles))}})
|
||||
}
|
||||
|
||||
// CreateLogScopeRole POST /log-scope-roles
|
||||
func (h *LogScopeRoleHandler) CreateLogScopeRole(c *gin.Context) {
|
||||
var req model.CreateLogScopeRoleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
if _, err := regexp.Compile(req.Pattern); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: "正则表达式无效: " + err.Error()})
|
||||
return
|
||||
}
|
||||
engine := strings.TrimSpace(req.Engine)
|
||||
if engine == "" {
|
||||
engine = "*"
|
||||
}
|
||||
role := req.Role
|
||||
if role == "" {
|
||||
role = "viewer"
|
||||
}
|
||||
var dup int64
|
||||
h.db.Model(&model.LogScopeRole{}).Where("name = ? OR code = ?", req.Name, req.Code).Count(&dup)
|
||||
if dup > 0 {
|
||||
c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: "名称或编码已存在"})
|
||||
return
|
||||
}
|
||||
item := model.LogScopeRole{
|
||||
Name: req.Name, Code: req.Code, Engine: engine, MatchField: req.MatchField,
|
||||
Pattern: req.Pattern, DatasourceID: req.DatasourceID, Role: role,
|
||||
Description: req.Description, Status: 1,
|
||||
}
|
||||
if err := h.db.Create(&item).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "创建失败"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Message: "创建成功", Data: item})
|
||||
}
|
||||
|
||||
// UpdateLogScopeRole PUT /log-scope-roles/:id
|
||||
func (h *LogScopeRoleHandler) UpdateLogScopeRole(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
var item model.LogScopeRole
|
||||
if err := h.db.First(&item, id).Error; err != nil {
|
||||
c.JSON(http.StatusNotFound, model.Response{Code: 404, Message: "策略不存在"})
|
||||
return
|
||||
}
|
||||
var req model.UpdateLogScopeRoleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
updates := map[string]interface{}{}
|
||||
if req.Name != "" {
|
||||
updates["name"] = req.Name
|
||||
}
|
||||
if req.Code != "" {
|
||||
updates["code"] = req.Code
|
||||
}
|
||||
if req.Engine != "" {
|
||||
updates["engine"] = req.Engine
|
||||
}
|
||||
if req.MatchField != "" {
|
||||
updates["match_field"] = req.MatchField
|
||||
}
|
||||
if req.Pattern != "" {
|
||||
if _, err := regexp.Compile(req.Pattern); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: "正则无效"})
|
||||
return
|
||||
}
|
||||
updates["pattern"] = req.Pattern
|
||||
}
|
||||
if req.DatasourceID != nil {
|
||||
updates["datasource_id"] = req.DatasourceID
|
||||
}
|
||||
if req.Role != "" {
|
||||
updates["role"] = req.Role
|
||||
}
|
||||
if req.Description != "" {
|
||||
updates["description"] = req.Description
|
||||
}
|
||||
if req.Status != nil {
|
||||
updates["status"] = *req.Status
|
||||
}
|
||||
if len(updates) > 0 {
|
||||
h.db.Model(&item).Updates(updates)
|
||||
}
|
||||
h.db.First(&item, id)
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Message: "更新成功", Data: item})
|
||||
}
|
||||
|
||||
// DeleteLogScopeRole DELETE /log-scope-roles/:id
|
||||
func (h *LogScopeRoleHandler) DeleteLogScopeRole(c *gin.Context) {
|
||||
id, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
h.db.Where("log_scope_role_id = ?", id).Delete(&model.UserLogScopeRole{})
|
||||
if err := h.db.Delete(&model.LogScopeRole{}, id).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "删除失败"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Message: "删除成功"})
|
||||
}
|
||||
|
||||
// ListUserLogScopeRoles GET /log-scope-roles/users/:id
|
||||
func (h *LogScopeRoleHandler) ListUserLogScopeRoles(c *gin.Context) {
|
||||
uid, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
var rows []model.UserLogScopeRole
|
||||
h.db.Preload("LogScopeRole").Where("user_id = ?", uid).Find(&rows)
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Data: model.ListResponse{List: rows, Total: int64(len(rows))}})
|
||||
}
|
||||
|
||||
// AssignLogScopeRole POST /log-scope-roles/users/:id
|
||||
func (h *LogScopeRoleHandler) AssignLogScopeRole(c *gin.Context) {
|
||||
uid, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err != nil || uid == 0 {
|
||||
c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: "无效的用户 ID"})
|
||||
return
|
||||
}
|
||||
var req model.AssignLogScopeRoleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: "请求参数错误: " + err.Error()})
|
||||
return
|
||||
}
|
||||
var role model.LogScopeRole
|
||||
if err := h.db.First(&role, req.LogScopeRoleID).Error; err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: "策略不存在"})
|
||||
return
|
||||
}
|
||||
var existing model.UserLogScopeRole
|
||||
if err := h.db.Where("user_id = ? AND log_scope_role_id = ?", uid, req.LogScopeRoleID).Take(&existing).Error; err == nil {
|
||||
c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: "已分配该策略"})
|
||||
return
|
||||
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "查询失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
row := model.UserLogScopeRole{UserID: uint(uid), LogScopeRoleID: req.LogScopeRoleID}
|
||||
if err := h.db.Create(&row).Error; err != nil {
|
||||
c.JSON(http.StatusInternalServerError, model.Response{Code: 500, Message: "分配失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
h.db.Preload("LogScopeRole").Take(&row, row.ID)
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Message: "分配成功", Data: row})
|
||||
}
|
||||
|
||||
// RemoveLogScopeRole DELETE /log-scope-roles/users/:userId/:assignId
|
||||
func (h *LogScopeRoleHandler) RemoveLogScopeRole(c *gin.Context) {
|
||||
assignID, _ := strconv.ParseUint(c.Param("assignId"), 10, 64)
|
||||
h.db.Delete(&model.UserLogScopeRole{}, assignID)
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Message: "已撤销"})
|
||||
}
|
||||
|
||||
// PreviewLogScope POST /log-scope-roles/preview
|
||||
func (h *LogScopeRoleHandler) PreviewLogScope(c *gin.Context) {
|
||||
var req model.PreviewLogScopeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.Response{Code: 400, Message: err.Error()})
|
||||
return
|
||||
}
|
||||
var roles []model.LogScopeRole
|
||||
h.db.Where("status = 1").Find(&roles)
|
||||
qc := logaccess.QueryContext{
|
||||
Engine: req.Engine, DatasourceID: req.DatasourceID,
|
||||
Index: req.Index, Query: req.Query,
|
||||
}
|
||||
matched := logaccess.MatchRolesForPreview(roles, qc, req.SampleValue)
|
||||
c.JSON(http.StatusOK, model.Response{Code: 200, Data: gin.H{
|
||||
"allowed": len(matched) > 0,
|
||||
"matched": matched,
|
||||
"total": len(matched),
|
||||
}})
|
||||
}
|
||||
Reference in new issue
Block a user