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.
 
 
 
 
 
 

185 lines
5.9 KiB

package console
import (
"strings"
"time"
"yunyan/comm"
"yunyan/pb"
"github.com/gin-gonic/gin"
)
// ============================ 应用×服务 基础设施/运行配置 (svcruntime) ============================
//
// 「应用的服务配置」页(admin appconfig.vue)的后端:读写 app_service_config 表。
// 数据由业务服务启动时按「库优先,无则读文件回写」seed(见 comm.LoadOrSeedServiceConfig),
// console 这里提供后台查看/修改。密钥字段(DSN/密码/secret/token)返回前端脱敏为 comm.EncMask,
// 保存时前端回传占位/空则保留原密文,否则用新明文加密覆盖。
// maskSvcRuntime 返回前端前脱敏加密项的值。
func maskSvcRuntime(m *comm.AppServiceConfig) {
if m.Encrypted && m.Value != "" {
m.Value = comm.EncMask
}
}
// svcRuntimeList 列出某(应用,区域,服务)的配置项(密钥脱敏)。
func (this *serverComp) svcRuntimeList(c *gin.Context) {
var req struct {
AppName string `json:"app_name"`
Region string `json:"region"`
Service string `json:"service"`
}
_ = c.ShouldBindJSON(&req)
if strings.TrimSpace(req.AppName) == "" || strings.TrimSpace(req.Service) == "" {
writeErr(c, pb.ErrorCode_ReqParameterError, "app_name 与 service 必填")
return
}
items, err := this.module.model.listSvcRuntime(req.AppName, req.Region, req.Service)
if err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
for _, m := range items {
maskSvcRuntime(m)
}
writeOK(c, gin.H{"items": items})
}
// svcRuntimeServices 列出某(应用,区域)下已有配置的服务类型,供前端标记哪些服务已上报默认配置。
func (this *serverComp) svcRuntimeServices(c *gin.Context) {
var req struct {
AppName string `json:"app_name"`
Region string `json:"region"`
}
_ = c.ShouldBindJSON(&req)
if strings.TrimSpace(req.AppName) == "" {
writeErr(c, pb.ErrorCode_ReqParameterError, "app_name 必填")
return
}
services, err := this.module.model.listSvcRuntimeServices(req.AppName, req.Region)
if err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
writeOK(c, gin.H{"services": services})
}
// svcRuntimeSave 逐项 upsert(id>0 更新、否则新增),保存即标 source=db,末尾广播配置变更。
func (this *serverComp) svcRuntimeSave(c *gin.Context) {
var req struct {
AppName string `json:"app_name"`
Region string `json:"region"`
Service string `json:"service"`
Items []*comm.AppServiceConfig `json:"items"`
}
if err := c.ShouldBindJSON(&req); err != nil {
writeErr(c, pb.ErrorCode_ReqParameterError, err.Error())
return
}
if strings.TrimSpace(req.AppName) == "" || strings.TrimSpace(req.Service) == "" {
writeErr(c, pb.ErrorCode_ReqParameterError, "app_name 与 service 必填")
return
}
key := this.options.EncryptKey
now := time.Now().Unix()
for _, it := range req.Items {
if strings.TrimSpace(it.Key) == "" || strings.TrimSpace(it.Section) == "" {
writeErr(c, pb.ErrorCode_ReqParameterError, "配置项 section/key 必填")
return
}
// 归属三键以外层为准,防止前端串改到别的应用/服务。
it.AppName, it.Region, it.Service = req.AppName, req.Region, req.Service
it.Source = "db"
it.Updatetime = now
// 加密字段:占位/空 → 保留原密文;否则加密新明文。
if it.Encrypted {
if it.Value == "" || it.Value == comm.EncMask {
if it.Id != 0 {
if old, e := this.module.model.getSvcRuntimeItem(it.Id); e == nil {
it.Value = old.Value
}
} else {
it.Value = ""
}
} else {
ct, e := comm.Encrypt(key, it.Value)
if e != nil {
writeErr(c, pb.ErrorCode_SystemError, "加密失败: "+e.Error())
return
}
it.Value = ct
}
}
if it.Id != 0 {
old, e := this.module.model.getSvcRuntimeItem(it.Id)
if e == nil {
it.Createtime = old.Createtime
}
if e := this.module.model.saveSvcRuntimeItem(it); e != nil {
writeErr(c, pb.ErrorCode_DBError, e.Error())
return
}
} else {
it.Createtime = now
if e := this.module.model.addSvcRuntimeItem(it); e != nil {
writeErr(c, pb.ErrorCode_DBError, e.Error())
return
}
}
}
broadcastConfigChanged(comm.ConfigKindServiceConfig, "update", 0)
writeOK(c, gin.H{"saved": len(req.Items)})
}
// svcRuntimeDel 按 id 批量删除配置项。
func (this *serverComp) svcRuntimeDel(c *gin.Context) {
var req struct {
Ids []uint64 `json:"ids"`
}
_ = c.ShouldBindJSON(&req)
if len(req.Ids) == 0 {
writeErr(c, pb.ErrorCode_ReqParameterError, "ids 必填")
return
}
if err := this.module.model.delSvcRuntimeItems(req.Ids); err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
broadcastConfigChanged(comm.ConfigKindServiceConfig, "delete", 0)
writeOK(c, gin.H{"deleted": len(req.Ids)})
}
// appsBindEnv 轻量「绑定/切换部署环境」:只改某部署行的 env_id,不走完整表单+连通性复检。
// 用于给历史部署行(env_id=0)快速绑定环境,或在应用服务配置页切换环境。
func (this *serverComp) appsBindEnv(c *gin.Context) {
var req struct {
Id uint32 `json:"id"`
EnvId uint32 `json:"env_id"`
}
if err := c.ShouldBindJSON(&req); err != nil || req.Id == 0 {
writeErr(c, pb.ErrorCode_ReqParameterError, "id 必填")
return
}
model, err := this.module.model.getApp(req.Id)
if err != nil {
writeErr(c, pb.ErrorCode_DBError, "部署不存在: "+err.Error())
return
}
// env_id>0 时校验目标环境存在(0=解绑,允许)。
if req.EnvId != 0 {
if _, e := this.module.model.getEnv(req.EnvId); e != nil {
writeErr(c, pb.ErrorCode_ReqParameterError, "目标环境不存在")
return
}
}
model.EnvId = req.EnvId
model.Updatetime = time.Now().Unix()
if err := this.module.model.saveApp(model); err != nil {
writeErr(c, pb.ErrorCode_DBError, err.Error())
return
}
this.module.registry.invalidate(model.Id) // 配置变了,失效连接缓存
writeOK(c, model)
}