You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
5.2 KiB
173 lines
5.2 KiB
package console
|
|
|
|
import (
|
|
"strings"
|
|
"time"
|
|
|
|
"yunyan/comm"
|
|
"yunyan/pb"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// ============================ 应用业务功能配置 (modulecfg) ============================
|
|
//
|
|
// 「应用服务配置」页(admin appconfig.vue)下半部分的后端:读写**应用业务库**里的 app_module_config 表
|
|
// (登录/短信/邮件/支付…)。与基础设施配置(svcconf, 存 console 公共库)分属两个配置域。
|
|
//
|
|
// 业务库连接按部署 appId 从 registry 动态取(与 analyze/重置设备码同机制);
|
|
// 密钥字段(是否密钥以 comm.ModuleCatalog 为准)AES-GCM 加密落库、返回前端脱敏为 comm.EncMask。
|
|
|
|
// moduleCfgCatalog 返回业务功能配置的字段目录(前端据此渲染各模块表单、分组)。与选中应用无关。
|
|
func (this *serverComp) moduleCfgCatalog(c *gin.Context) {
|
|
writeOK(c, gin.H{"modules": comm.ModuleCatalog})
|
|
}
|
|
|
|
// moduleCfgList 列出某部署业务库里的配置项;module 为空则返回全部(用于标记哪些模块已配置)。密钥脱敏。
|
|
func (this *serverComp) moduleCfgList(c *gin.Context) {
|
|
var req struct {
|
|
AppId uint32 `json:"app_id"`
|
|
Module string `json:"module"`
|
|
}
|
|
_ = c.ShouldBindJSON(&req)
|
|
if req.AppId == 0 {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, "app_id 必填")
|
|
return
|
|
}
|
|
db, err := this.module.registry.getServiceDB(req.AppId)
|
|
if err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, "连接应用业务库失败: "+err.Error())
|
|
return
|
|
}
|
|
if err := ensureModuleConfigTable(db); err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, err.Error())
|
|
return
|
|
}
|
|
rows, err := listModuleConfig(db, strings.TrimSpace(req.Module))
|
|
if err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, err.Error())
|
|
return
|
|
}
|
|
for _, m := range rows {
|
|
if m.Encrypted && m.Value != "" {
|
|
m.Value = comm.EncMask
|
|
}
|
|
}
|
|
writeOK(c, gin.H{"items": rows})
|
|
}
|
|
|
|
// moduleCfgSave 保存某模块的配置到应用业务库:逐字段 upsert。
|
|
// 是否密钥以目录为准(不信前端);密钥字段回传占位/空则保留原密文,否则加密新明文;目录外的键忽略。
|
|
func (this *serverComp) moduleCfgSave(c *gin.Context) {
|
|
var req struct {
|
|
AppId uint32 `json:"app_id"`
|
|
Module string `json:"module"`
|
|
Items []struct {
|
|
Key string `json:"key"`
|
|
Value string `json:"value"`
|
|
} `json:"items"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, err.Error())
|
|
return
|
|
}
|
|
req.Module = strings.TrimSpace(req.Module)
|
|
if req.AppId == 0 || req.Module == "" {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, "app_id 与 module 必填")
|
|
return
|
|
}
|
|
def := comm.FindModuleDef(req.Module)
|
|
if def == nil {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, "未知模块: "+req.Module)
|
|
return
|
|
}
|
|
// 目录内合法字段集,过滤前端多传的键。
|
|
allowed := make(map[string]bool, len(def.Fields))
|
|
for _, f := range def.Fields {
|
|
allowed[f.Key] = true
|
|
}
|
|
|
|
db, err := this.module.registry.getServiceDB(req.AppId)
|
|
if err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, "连接应用业务库失败: "+err.Error())
|
|
return
|
|
}
|
|
if err := ensureModuleConfigTable(db); err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, err.Error())
|
|
return
|
|
}
|
|
|
|
encKey := this.options.EncryptKey
|
|
now := time.Now().Unix()
|
|
saved := 0
|
|
for _, it := range req.Items {
|
|
key := strings.TrimSpace(it.Key)
|
|
if key == "" || !allowed[key] {
|
|
continue
|
|
}
|
|
old, e := getModuleConfigItem(db, req.Module, key)
|
|
if e != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, e.Error())
|
|
return
|
|
}
|
|
secret := comm.ModuleFieldIsSecret(req.Module, key)
|
|
value := it.Value
|
|
if secret {
|
|
// 占位/空 → 保留原密文;否则加密新明文。
|
|
if value == "" || value == comm.EncMask {
|
|
if old != nil {
|
|
value = old.Value
|
|
} else {
|
|
value = ""
|
|
}
|
|
} else {
|
|
ct, ee := comm.Encrypt(encKey, value)
|
|
if ee != nil {
|
|
writeErr(c, pb.ErrorCode_SystemError, "加密失败: "+ee.Error())
|
|
return
|
|
}
|
|
value = ct
|
|
}
|
|
}
|
|
row := &comm.AppModuleConfig{
|
|
Module: req.Module, Key: key, Value: value, Encrypted: secret, Updatetime: now,
|
|
}
|
|
if old != nil {
|
|
row.Id, row.Createtime = old.Id, old.Createtime
|
|
} else {
|
|
row.Createtime = now
|
|
}
|
|
if e := upsertModuleConfigItem(db, row); e != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, e.Error())
|
|
return
|
|
}
|
|
saved++
|
|
}
|
|
broadcastConfigChanged(comm.ConfigKindModuleConfig, "update", 0)
|
|
writeOK(c, gin.H{"saved": saved})
|
|
}
|
|
|
|
// moduleCfgDel 清空某模块在业务库里的全部配置。
|
|
func (this *serverComp) moduleCfgDel(c *gin.Context) {
|
|
var req struct {
|
|
AppId uint32 `json:"app_id"`
|
|
Module string `json:"module"`
|
|
}
|
|
_ = c.ShouldBindJSON(&req)
|
|
req.Module = strings.TrimSpace(req.Module)
|
|
if req.AppId == 0 || req.Module == "" {
|
|
writeErr(c, pb.ErrorCode_ReqParameterError, "app_id 与 module 必填")
|
|
return
|
|
}
|
|
db, err := this.module.registry.getServiceDB(req.AppId)
|
|
if err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, "连接应用业务库失败: "+err.Error())
|
|
return
|
|
}
|
|
if err := delModuleConfig(db, req.Module); err != nil {
|
|
writeErr(c, pb.ErrorCode_DBError, err.Error())
|
|
return
|
|
}
|
|
broadcastConfigChanged(comm.ConfigKindModuleConfig, "delete", 0)
|
|
writeOK(c, gin.H{"deleted": req.Module})
|
|
}
|
|
|