130 lines
4.2 KiB
Go
130 lines
4.2 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"gorm.io/gorm"
|
||
|
||
"ws_usernode/internal/config"
|
||
"ws_usernode/internal/model"
|
||
)
|
||
|
||
// 系统设置错误。
|
||
var ErrSettingKeyUnknown = errors.New("service: 未知设置项")
|
||
var ErrSettingValueInvalid = errors.New("service: 设置值不合法")
|
||
|
||
// 设置项 key 白名单(settings 表可覆盖 config 的策略字段,PLAN F7)。
|
||
// 其余设置(SMTP、会话时长等)为启动时配置,不支持动态覆盖。
|
||
var settingKeys = map[string]bool{
|
||
"policy.default_ttl": true, // 新账号默认有效期(时长)
|
||
"policy.recycle_period": true, // 到期后回收期(时长)
|
||
"policy.audit_retention": true, // 审计保留时长(时长)
|
||
}
|
||
|
||
// SettingItem 一个设置项的生效值。
|
||
type SettingItem struct {
|
||
Key string `json:"key"`
|
||
Value string `json:"value"` // 生效值(settings 覆盖优先,否则 config 默认)
|
||
Overridden bool `json:"overridden"` // 是否被 settings 表覆盖
|
||
}
|
||
|
||
// SettingService 系统设置读写(settings 表,config 提供默认值)。
|
||
type SettingService struct {
|
||
db *gorm.DB
|
||
cfg *config.Config
|
||
}
|
||
|
||
// NewSettingService 创建设置服务。
|
||
func NewSettingService(db *gorm.DB, cfg *config.Config) *SettingService {
|
||
return &SettingService{db: db, cfg: cfg}
|
||
}
|
||
|
||
// GetAll 返回全部设置项的生效值(未覆盖的展示 config 默认值)。
|
||
func (s *SettingService) GetAll(ctx context.Context) ([]SettingItem, error) {
|
||
var rows []model.Setting
|
||
if err := s.db.WithContext(ctx).Find(&rows).Error; err != nil {
|
||
return nil, err
|
||
}
|
||
overridden := make(map[string]string, len(rows))
|
||
for _, r := range rows {
|
||
if settingKeys[r.Key] {
|
||
overridden[r.Key] = r.Value
|
||
}
|
||
}
|
||
items := make([]SettingItem, 0, len(settingKeys))
|
||
for _, key := range sortedSettingKeys() {
|
||
if v, ok := overridden[key]; ok {
|
||
items = append(items, SettingItem{Key: key, Value: v, Overridden: true})
|
||
} else {
|
||
items = append(items, SettingItem{Key: key, Value: s.defaultValue(key), Overridden: false})
|
||
}
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
// Set 更新一个设置项:校验 key 白名单与值格式(均为时长)。全部校验通过才写入。
|
||
func (s *SettingService) Set(ctx context.Context, key, value string) error {
|
||
if !settingKeys[key] {
|
||
return fmt.Errorf("%w: %q", ErrSettingKeyUnknown, key)
|
||
}
|
||
value = strings.TrimSpace(value)
|
||
if _, err := time.ParseDuration(value); err != nil {
|
||
return fmt.Errorf("%w: %q(需为时长,如 2160h)", ErrSettingValueInvalid, value)
|
||
}
|
||
return s.db.WithContext(ctx).Save(&model.Setting{Key: key, Value: value}).Error
|
||
}
|
||
|
||
// Duration 读取策略时长设置:settings 覆盖优先,无覆盖或解析失败用 fallback。
|
||
func (s *SettingService) Duration(ctx context.Context, key string, fallback time.Duration) time.Duration {
|
||
if !settingKeys[key] {
|
||
return fallback
|
||
}
|
||
var row model.Setting
|
||
err := s.db.WithContext(ctx).First(&row, "key = ?", key).Error
|
||
if err != nil {
|
||
return fallback
|
||
}
|
||
d, err := time.ParseDuration(row.Value)
|
||
if err != nil {
|
||
return fallback
|
||
}
|
||
return d
|
||
}
|
||
|
||
// DefaultTTL 新账号默认有效期(settings 覆盖 config.policy.default_ttl)。
|
||
func (s *SettingService) DefaultTTL(ctx context.Context) time.Duration {
|
||
return s.Duration(ctx, "policy.default_ttl", s.cfg.Policy.DefaultTTL)
|
||
}
|
||
|
||
// RecyclePeriod 到期后回收期。
|
||
func (s *SettingService) RecyclePeriod(ctx context.Context) time.Duration {
|
||
return s.Duration(ctx, "policy.recycle_period", s.cfg.Policy.RecyclePeriod)
|
||
}
|
||
|
||
// AuditRetention 审计保留时长。
|
||
func (s *SettingService) AuditRetention(ctx context.Context) time.Duration {
|
||
return s.Duration(ctx, "policy.audit_retention", s.cfg.Policy.AuditRetention)
|
||
}
|
||
|
||
// defaultValue 返回设置项的 config 默认值(保证遍历顺序稳定)。
|
||
func (s *SettingService) defaultValue(key string) string {
|
||
switch key {
|
||
case "policy.default_ttl":
|
||
return s.cfg.Policy.DefaultTTL.String()
|
||
case "policy.recycle_period":
|
||
return s.cfg.Policy.RecyclePeriod.String()
|
||
case "policy.audit_retention":
|
||
return s.cfg.Policy.AuditRetention.String()
|
||
}
|
||
return ""
|
||
}
|
||
|
||
func sortedSettingKeys() []string {
|
||
// 固定顺序展示,避免 map 遍历随机
|
||
return []string{"policy.default_ttl", "policy.recycle_period", "policy.audit_retention"}
|
||
}
|