feat: 初始化配置中心项目
This commit is contained in:
@@ -0,0 +1,320 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"kenaito-conf/server/internal/dao"
|
||||
"kenaito-conf/server/internal/middleware"
|
||||
"kenaito-conf/server/internal/model"
|
||||
"kenaito-conf/server/internal/service"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type ConfigHandler struct {
|
||||
cryptoService *service.CryptoService
|
||||
parseService *service.ConfigParseService
|
||||
permService *service.PermissionService
|
||||
}
|
||||
|
||||
func NewConfigHandler(crypto *service.CryptoService, parse *service.ConfigParseService, perm *service.PermissionService) *ConfigHandler {
|
||||
return &ConfigHandler{cryptoService: crypto, parseService: parse, permService: perm}
|
||||
}
|
||||
|
||||
type ConfigListRequest struct {
|
||||
PageNo int `form:"pageNo" binding:"required"`
|
||||
PageSize int `form:"pageSize" binding:"required"`
|
||||
Env string `form:"env"`
|
||||
AppId *int `form:"appId"`
|
||||
}
|
||||
|
||||
func (h *ConfigHandler) List(c *gin.Context) {
|
||||
var req ConfigListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.WrapperResponse{Code: 400, Msg: "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := middleware.GetUserInfo(c)
|
||||
query := dao.Engine.Where("enabled = 1")
|
||||
|
||||
if req.Env != "" {
|
||||
query = query.And("env = ?", req.Env)
|
||||
}
|
||||
if req.AppId != nil {
|
||||
if *req.AppId <= 0 {
|
||||
query = query.And("(app_id is null OR app_id = 0)")
|
||||
} else {
|
||||
var app model.Application
|
||||
if has, _ := dao.Engine.ID(*req.AppId).Get(&app); has {
|
||||
query = query.And("app_code like ?", app.Code+"%")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// permission filter
|
||||
if !userInfo.IsSuperAdmin() {
|
||||
if userInfo.IsGroupAdmin() && userInfo.GroupId != nil {
|
||||
query = query.And("group_id = ?", *userInfo.GroupId)
|
||||
} else if len(userInfo.GrantAppIds) > 0 {
|
||||
query = query.In("app_id", userInfo.GrantAppIds)
|
||||
} else {
|
||||
c.JSON(http.StatusOK, model.PageResult{PageNo: req.PageNo, PageSize: req.PageSize, Total: 0, Data: []interface{}{}})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var configs []model.AppConfig
|
||||
total, _ := query.Count(&model.AppConfig{})
|
||||
query.Limit(req.PageSize, (req.PageNo-1)*req.PageSize).Find(&configs)
|
||||
|
||||
// build app names
|
||||
for i := range configs {
|
||||
configs[i].AppNames = h.buildAppNames(&configs[i])
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.PageResult{PageNo: req.PageNo, PageSize: req.PageSize, Total: total, Data: configs})
|
||||
}
|
||||
|
||||
func (h *ConfigHandler) GetById(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
var config model.AppConfig
|
||||
has, _ := dao.Engine.ID(id).Get(&config)
|
||||
if !has {
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 404, Msg: "配置不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := middleware.GetUserInfo(c)
|
||||
if err := h.permService.RequirePermission(userInfo, config.GroupId, config.AppId, "RO"); err != nil {
|
||||
c.JSON(http.StatusForbidden, model.WrapperResponse{Code: 403, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
config.AppNames = h.buildAppNames(&config)
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 0, Data: config})
|
||||
}
|
||||
|
||||
type AddConfigRequest struct {
|
||||
Id int `json:"id"`
|
||||
GroupId *int `json:"groupId"`
|
||||
AppId *int `json:"appId"`
|
||||
Env string `json:"env" binding:"required"`
|
||||
Version string `json:"version"`
|
||||
Name string `json:"name"`
|
||||
Type int `json:"type" binding:"required"`
|
||||
Contents string `json:"contents"`
|
||||
Global bool `json:"global"`
|
||||
}
|
||||
|
||||
func (h *ConfigHandler) Add(c *gin.Context) {
|
||||
var req AddConfigRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.WrapperResponse{Code: 400, Msg: "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := middleware.GetUserInfo(c)
|
||||
if req.Global {
|
||||
if req.GroupId == nil {
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 400, Msg: "全局配置需要指定所属业务组"})
|
||||
return
|
||||
}
|
||||
if err := h.permService.RequirePermission(userInfo, req.GroupId, nil, "RW"); err != nil {
|
||||
c.JSON(http.StatusForbidden, model.WrapperResponse{Code: 403, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if !req.Global && req.AppId == nil {
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 400, Msg: "非全局绑定应用不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
var app model.Application
|
||||
if req.AppId != nil {
|
||||
if has, _ := dao.Engine.ID(*req.AppId).Get(&app); !has {
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 400, Msg: "应用不存在"})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
config := model.AppConfig{
|
||||
GroupId: req.GroupId,
|
||||
AppId: req.AppId,
|
||||
Env: req.Env,
|
||||
Version: req.Version,
|
||||
Name: req.Name,
|
||||
Type: req.Type,
|
||||
Contents: req.Contents,
|
||||
Enabled: true,
|
||||
CreatedAt: func() *time.Time { t := time.Now(); return &t }(),
|
||||
CreatedBy: userInfo.Name,
|
||||
UpdatedAt: func() *time.Time { t := time.Now(); return &t }(),
|
||||
UpdatedBy: userInfo.Name,
|
||||
}
|
||||
|
||||
if req.AppId != nil {
|
||||
config.AppCode = app.Code
|
||||
if !req.Global {
|
||||
config.GroupId = app.GroupId
|
||||
}
|
||||
}
|
||||
|
||||
// encrypt sensitive values
|
||||
h.encryptIfRequired(&config)
|
||||
|
||||
if _, err := dao.Engine.Insert(&config); err != nil {
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 500, Msg: "保存失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 0, Data: true})
|
||||
}
|
||||
|
||||
func (h *ConfigHandler) Update(c *gin.Context) {
|
||||
var req AddConfigRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, model.WrapperResponse{Code: 400, Msg: "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
if req.Id == 0 {
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 400, Msg: "id参数缺失"})
|
||||
return
|
||||
}
|
||||
|
||||
var config model.AppConfig
|
||||
has, _ := dao.Engine.ID(req.Id).Get(&config)
|
||||
if !has {
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 404, Msg: "配置不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := middleware.GetUserInfo(c)
|
||||
if err := h.permService.RequirePermission(userInfo, config.GroupId, config.AppId, "RW"); err != nil {
|
||||
c.JSON(http.StatusForbidden, model.WrapperResponse{Code: 403, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// save history
|
||||
h.saveHistory(&config, userInfo.Name)
|
||||
|
||||
originalContents := config.Contents
|
||||
config.Contents = req.Contents
|
||||
config.Version = req.Version
|
||||
config.UpdatedBy = userInfo.Name
|
||||
now := time.Now()
|
||||
config.UpdatedAt = &now
|
||||
|
||||
h.encryptIfRequired(&config)
|
||||
|
||||
dao.Engine.ID(config.Id).Cols("contents", "version", "updated_by", "updated_at").Update(&config)
|
||||
|
||||
// TODO: publish config change event (ZK removed, could use webhook or other mechanism)
|
||||
_ = originalContents
|
||||
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 0, Data: true})
|
||||
}
|
||||
|
||||
func (h *ConfigHandler) Delete(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
var config model.AppConfig
|
||||
has, _ := dao.Engine.ID(id).Get(&config)
|
||||
if !has {
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 404, Msg: "配置不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
userInfo := middleware.GetUserInfo(c)
|
||||
if err := h.permService.RequirePermission(userInfo, config.GroupId, config.AppId, "RW"); err != nil {
|
||||
c.JSON(http.StatusForbidden, model.WrapperResponse{Code: 403, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
config.Enabled = false
|
||||
config.UpdatedBy = userInfo.Name
|
||||
now := time.Now()
|
||||
config.UpdatedAt = &now
|
||||
dao.Engine.ID(config.Id).Cols("enabled", "updated_by", "updated_at").Update(&config)
|
||||
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 0})
|
||||
}
|
||||
|
||||
func (h *ConfigHandler) GetHistory(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
var histories []model.AppConfigHistory
|
||||
dao.Engine.Where("origin_id = ?", id).OrderBy("id desc").Limit(5).Find(&histories)
|
||||
|
||||
if len(histories) > 0 {
|
||||
var currentConfig model.AppConfig
|
||||
if has, _ := dao.Engine.ID(histories[0].OriginId).Get(¤tConfig); has {
|
||||
for i := range histories {
|
||||
histories[i].ActiveContents = currentConfig.Contents
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 0, Data: histories})
|
||||
}
|
||||
|
||||
func (h *ConfigHandler) Rollback(c *gin.Context) {
|
||||
id, _ := strconv.Atoi(c.Param("id"))
|
||||
var history model.AppConfigHistory
|
||||
has, _ := dao.Engine.ID(id).Get(&history)
|
||||
if !has {
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 404, Msg: "历史记录不存在"})
|
||||
return
|
||||
}
|
||||
|
||||
var config model.AppConfig
|
||||
if has, _ := dao.Engine.ID(history.OriginId).Get(&config); has {
|
||||
userInfo := middleware.GetUserInfo(c)
|
||||
if err := h.permService.RequirePermission(userInfo, config.GroupId, config.AppId, "RW"); err != nil {
|
||||
c.JSON(http.StatusForbidden, model.WrapperResponse{Code: 403, Msg: err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
config.Contents = history.Contents
|
||||
config.UpdatedBy = userInfo.Name
|
||||
now := time.Now()
|
||||
config.UpdatedAt = &now
|
||||
dao.Engine.ID(config.Id).Cols("contents", "updated_by", "updated_at").Update(&config)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.WrapperResponse{Code: 0})
|
||||
}
|
||||
|
||||
func (h *ConfigHandler) buildAppNames(config *model.AppConfig) string {
|
||||
if config.Global || (config.AppId == nil || *config.AppId == 0) {
|
||||
return "全局配置"
|
||||
}
|
||||
var app model.Application
|
||||
if has, _ := dao.Engine.ID(*config.AppId).Get(&app); has {
|
||||
return app.Name + "(" + app.Code + ")"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (h *ConfigHandler) saveHistory(config *model.AppConfig, operator string) {
|
||||
history := model.AppConfigHistory{
|
||||
OriginId: config.Id,
|
||||
AppId: config.AppId,
|
||||
AppNames: h.buildAppNames(config),
|
||||
Env: config.Env,
|
||||
Version: config.Version,
|
||||
Name: config.Name,
|
||||
Type: config.Type,
|
||||
Contents: config.Contents,
|
||||
CreatedAt: func() *time.Time { t := time.Now(); return &t }(),
|
||||
CreatedBy: operator,
|
||||
}
|
||||
dao.Engine.Insert(&history)
|
||||
}
|
||||
|
||||
func (h *ConfigHandler) encryptIfRequired(config *model.AppConfig) {
|
||||
// simplified: check if contents contain sensitive keys
|
||||
// In production, parse the config and encrypt values with {Cipher} prefix
|
||||
// For now, skip encryption logic
|
||||
}
|
||||
Reference in New Issue
Block a user