304 lines
9.3 KiB
Go
304 lines
9.3 KiB
Go
package data
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"kra/internal/conf"
|
|
|
|
"google.golang.org/protobuf/encoding/protojson"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/types/known/durationpb"
|
|
)
|
|
|
|
type configurationEnvelope struct {
|
|
Data json.RawMessage `json:"data"`
|
|
Admin json.RawMessage `json:"admin"`
|
|
Email *struct {
|
|
To string `json:"to"`
|
|
From string `json:"from"`
|
|
Host string `json:"host"`
|
|
Secret string `json:"secret"`
|
|
Nickname string `json:"nickname"`
|
|
Port int32 `json:"port"`
|
|
IsSSL bool `json:"is-ssl"`
|
|
IsLoginAuth bool `json:"is-loginauth"`
|
|
} `json:"email"`
|
|
}
|
|
|
|
func (r *initializationRepo) ConfigurationJSON() (json.RawMessage, error) {
|
|
dataConfig, adminConfig := r.data.runtime.Values()
|
|
admin := map[string]any{"routerPrefix": ""}
|
|
email := map[string]any{}
|
|
if adminConfig != nil {
|
|
admin["routerPrefix"] = adminConfig.RouterPrefix
|
|
if adminConfig.System != nil {
|
|
admin["system"] = map[string]any{"useRedis": adminConfig.System.UseRedis, "useMultipoint": adminConfig.System.UseMultipoint, "useStrictAuth": adminConfig.System.UseStrictAuth, "disableAutoMigrate": adminConfig.System.DisableAutoMigrate, "useMongo": adminConfig.System.UseMongo}
|
|
}
|
|
if adminConfig.Jwt != nil {
|
|
admin["jwt"] = map[string]any{"signingKey": "******", "expiresTime": durationString(adminConfig.Jwt.ExpiresTime), "bufferTime": durationString(adminConfig.Jwt.BufferTime), "issuer": adminConfig.Jwt.Issuer}
|
|
}
|
|
if adminConfig.Captcha != nil {
|
|
admin["captcha"] = map[string]any{"keyLong": adminConfig.Captcha.KeyLong, "imgWidth": adminConfig.Captcha.ImgWidth, "imgHeight": adminConfig.Captcha.ImgHeight, "storeExpiration": durationString(adminConfig.Captcha.StoreExpiration)}
|
|
}
|
|
if adminConfig.Local != nil {
|
|
admin["local"] = map[string]any{"storePath": adminConfig.Local.StorePath, "pathPrefix": adminConfig.Local.PathPrefix}
|
|
}
|
|
if adminConfig.Media != nil {
|
|
admin["media"] = map[string]any{"sessionTtl": adminConfig.Media.SessionTtl, "maxFileSize": adminConfig.Media.MaxFileSize}
|
|
}
|
|
if adminConfig.Email != nil {
|
|
email = map[string]any{"to": adminConfig.Email.To, "from": adminConfig.Email.From, "host": adminConfig.Email.Host, "secret": "******", "nickname": adminConfig.Email.Nickname, "port": adminConfig.Email.Port, "is-ssl": adminConfig.Email.IsSsl, "is-loginauth": adminConfig.Email.IsLoginAuth}
|
|
}
|
|
if adminConfig.Storage != nil {
|
|
storage := proto.Clone(adminConfig.Storage).(*conf.AdminBackend_Storage)
|
|
maskStorageSecrets(storage)
|
|
admin["storage"] = storage
|
|
}
|
|
if adminConfig.Zap != nil {
|
|
admin["zap"] = adminConfig.Zap
|
|
}
|
|
if adminConfig.Cors != nil {
|
|
admin["cors"] = adminConfig.Cors
|
|
}
|
|
if adminConfig.App != nil {
|
|
admin["app"] = adminConfig.App
|
|
}
|
|
}
|
|
maskDataSecrets(dataConfig)
|
|
dataMap := map[string]any{}
|
|
if dataConfig != nil {
|
|
raw, err := protojson.MarshalOptions{UseProtoNames: true}.Marshal(dataConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = json.Unmarshal(raw, &dataMap); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return json.Marshal(map[string]any{"config": map[string]any{"admin": admin, "email": email, "data": dataMap}})
|
|
}
|
|
|
|
func (r *initializationRepo) SaveConfigurationJSON(ctx context.Context, raw json.RawMessage) error {
|
|
currentData, currentAdmin := r.data.runtime.Values()
|
|
if currentAdmin == nil {
|
|
return nil
|
|
}
|
|
nextData := cloneDataConfig(currentData)
|
|
nextAdmin := proto.Clone(currentAdmin).(*conf.AdminBackend)
|
|
var value configurationEnvelope
|
|
if err := json.Unmarshal(raw, &value); err != nil {
|
|
return err
|
|
}
|
|
options := protojson.UnmarshalOptions{DiscardUnknown: true}
|
|
if len(value.Data) > 0 && string(value.Data) != "null" {
|
|
patch := &conf.Data{}
|
|
if err := options.Unmarshal(value.Data, patch); err != nil {
|
|
return err
|
|
}
|
|
applyDataPatch(nextData, patch)
|
|
}
|
|
if len(value.Admin) > 0 && string(value.Admin) != "null" {
|
|
patch := &conf.AdminBackend{}
|
|
if err := options.Unmarshal(value.Admin, patch); err != nil {
|
|
return err
|
|
}
|
|
applyAdminPatch(nextAdmin, patch)
|
|
}
|
|
preserveDataSecrets(nextData, currentData)
|
|
preserveAdminSecrets(nextAdmin, currentAdmin)
|
|
if value.Email != nil {
|
|
if nextAdmin.Email == nil {
|
|
nextAdmin.Email = &conf.AdminBackend_Email{}
|
|
}
|
|
nextAdmin.Email.To, nextAdmin.Email.From, nextAdmin.Email.Host = value.Email.To, value.Email.From, value.Email.Host
|
|
nextAdmin.Email.Nickname, nextAdmin.Email.Port = value.Email.Nickname, value.Email.Port
|
|
nextAdmin.Email.IsSsl, nextAdmin.Email.IsLoginAuth = value.Email.IsSSL, value.Email.IsLoginAuth
|
|
if value.Email.Secret != "" && value.Email.Secret != "******" {
|
|
nextAdmin.Email.Secret = value.Email.Secret
|
|
}
|
|
}
|
|
nextAdmin.ConfigPath = currentAdmin.ConfigPath
|
|
dataRaw, err := protojson.MarshalOptions{UseProtoNames: true}.Marshal(nextData)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
adminRaw, err := protojson.MarshalOptions{UseProtoNames: true}.Marshal(nextAdmin)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return r.PersistRuntimeConfig(ctx, dataRaw, adminRaw)
|
|
}
|
|
|
|
func (r *initializationRepo) DiskMountPoints() []string {
|
|
config := r.data.runtime.Admin()
|
|
if config == nil {
|
|
return nil
|
|
}
|
|
points := make([]string, 0, len(config.DiskList))
|
|
for _, item := range config.DiskList {
|
|
if item != nil && item.MountPoint != "" {
|
|
points = append(points, item.MountPoint)
|
|
}
|
|
}
|
|
return points
|
|
}
|
|
|
|
func cloneDataConfig(value *conf.Data) *conf.Data {
|
|
if value == nil {
|
|
return &conf.Data{}
|
|
}
|
|
return proto.Clone(value).(*conf.Data)
|
|
}
|
|
|
|
func durationString(value *durationpb.Duration) string {
|
|
if value == nil {
|
|
return "0s"
|
|
}
|
|
return value.AsDuration().String()
|
|
}
|
|
|
|
func applyDataPatch(target, patch *conf.Data) {
|
|
if patch.Database != nil {
|
|
target.Database = patch.Database
|
|
}
|
|
if patch.Redis != nil {
|
|
target.Redis = patch.Redis
|
|
}
|
|
if patch.Mongo != nil {
|
|
target.Mongo = patch.Mongo
|
|
}
|
|
if patch.DatabaseList != nil {
|
|
target.DatabaseList = patch.DatabaseList
|
|
}
|
|
if patch.RedisList != nil {
|
|
target.RedisList = patch.RedisList
|
|
}
|
|
}
|
|
|
|
func applyAdminPatch(target, patch *conf.AdminBackend) {
|
|
target.RouterPrefix = patch.RouterPrefix
|
|
if patch.System != nil {
|
|
target.System = patch.System
|
|
}
|
|
if patch.Jwt != nil {
|
|
target.Jwt = patch.Jwt
|
|
}
|
|
if patch.Captcha != nil {
|
|
target.Captcha = patch.Captcha
|
|
}
|
|
if patch.Local != nil {
|
|
target.Local = patch.Local
|
|
}
|
|
if patch.Media != nil {
|
|
target.Media = patch.Media
|
|
}
|
|
if patch.Storage != nil {
|
|
target.Storage = patch.Storage
|
|
}
|
|
if patch.Zap != nil {
|
|
target.Zap = patch.Zap
|
|
}
|
|
if patch.Cors != nil {
|
|
target.Cors = patch.Cors
|
|
}
|
|
if patch.App != nil {
|
|
target.App = patch.App
|
|
}
|
|
}
|
|
|
|
func maskDataSecrets(value *conf.Data) {
|
|
if value == nil {
|
|
return
|
|
}
|
|
if value.Database != nil {
|
|
value.Database.Password = "******"
|
|
}
|
|
if value.Redis != nil {
|
|
value.Redis.Password = "******"
|
|
}
|
|
if value.Mongo != nil {
|
|
value.Mongo.Password = "******"
|
|
}
|
|
for _, item := range value.DatabaseList {
|
|
if item != nil {
|
|
item.Password = "******"
|
|
}
|
|
}
|
|
for _, item := range value.RedisList {
|
|
if item != nil {
|
|
item.Password = "******"
|
|
}
|
|
}
|
|
}
|
|
|
|
func preserveDataSecrets(next, current *conf.Data) {
|
|
if next == nil || current == nil {
|
|
return
|
|
}
|
|
if next.Database != nil && current.Database != nil && maskedSecret(next.Database.Password) {
|
|
next.Database.Password = current.Database.Password
|
|
}
|
|
if next.Redis != nil && current.Redis != nil && maskedSecret(next.Redis.Password) {
|
|
next.Redis.Password = current.Redis.Password
|
|
}
|
|
if next.Mongo != nil && current.Mongo != nil && maskedSecret(next.Mongo.Password) {
|
|
next.Mongo.Password = current.Mongo.Password
|
|
}
|
|
for i, item := range next.DatabaseList {
|
|
if item != nil && i < len(current.DatabaseList) && current.DatabaseList[i] != nil && maskedSecret(item.Password) {
|
|
item.Password = current.DatabaseList[i].Password
|
|
}
|
|
}
|
|
for i, item := range next.RedisList {
|
|
if item != nil && i < len(current.RedisList) && current.RedisList[i] != nil && maskedSecret(item.Password) {
|
|
item.Password = current.RedisList[i].Password
|
|
}
|
|
}
|
|
}
|
|
|
|
func preserveAdminSecrets(next, current *conf.AdminBackend) {
|
|
if next.Jwt != nil && current.Jwt != nil && maskedSecret(next.Jwt.SigningKey) {
|
|
next.Jwt.SigningKey = current.Jwt.SigningKey
|
|
}
|
|
if next.Email != nil && current.Email != nil && maskedSecret(next.Email.Secret) {
|
|
next.Email.Secret = current.Email.Secret
|
|
}
|
|
preserveStorageSecrets(next.Storage, current.Storage)
|
|
}
|
|
|
|
func maskedSecret(value string) bool { return value == "" || value == "******" }
|
|
|
|
func objectStores(storage *conf.AdminBackend_Storage) []*conf.AdminBackend_ObjectStore {
|
|
if storage == nil {
|
|
return nil
|
|
}
|
|
return []*conf.AdminBackend_ObjectStore{storage.AliyunOss, storage.HuaweiObs, storage.TencentCos, storage.AwsS3, storage.CloudflareR2, storage.Minio}
|
|
}
|
|
|
|
func maskStorageSecrets(storage *conf.AdminBackend_Storage) {
|
|
if storage.Qiniu != nil {
|
|
storage.Qiniu.SecretKey = "******"
|
|
}
|
|
for _, item := range objectStores(storage) {
|
|
if item != nil {
|
|
item.SecretKey = "******"
|
|
}
|
|
}
|
|
}
|
|
|
|
func preserveStorageSecrets(next, current *conf.AdminBackend_Storage) {
|
|
if next == nil || current == nil {
|
|
return
|
|
}
|
|
if next.Qiniu != nil && current.Qiniu != nil && maskedSecret(next.Qiniu.SecretKey) {
|
|
next.Qiniu.SecretKey = current.Qiniu.SecretKey
|
|
}
|
|
nextItems, currentItems := objectStores(next), objectStores(current)
|
|
for i := range nextItems {
|
|
if nextItems[i] != nil && currentItems[i] != nil && maskedSecret(nextItems[i].SecretKey) {
|
|
nextItems[i].SecretKey = currentItems[i].SecretKey
|
|
}
|
|
}
|
|
}
|