Files
usernode/internal/service/settings.go
T

130 lines
4.2 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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"}
}