package system import ( "context" "errors" "kra/internal/biz/system" "time" "gorm.io/gorm" ) // DatabaseProvider is the smaller seam used by security settings, which also // need to work before the primary database is initialized. type DatabaseProvider interface { DB() *gorm.DB DatabaseReady() bool } type SecurityConfigPO struct { ID uint `gorm:"primaryKey"` CreatedAt time.Time UpdatedAt time.Time DeletedAt gorm.DeletedAt `gorm:"index"` CaptchaOpen int `gorm:"default:0"` CaptchaTimeout int `gorm:"default:3600"` KeyLong int `gorm:"default:6"` ImgWidth int `gorm:"default:240"` ImgHeight int `gorm:"default:80"` PwdMinLength int `gorm:"default:8"` PwdRequireUpper bool `gorm:"default:false"` PwdRequireLower bool `gorm:"default:false"` PwdRequireDigit bool `gorm:"default:false"` PwdRequireSpecial bool `gorm:"default:false"` LimitEnable bool `gorm:"default:false"` LimitWindow int `gorm:"default:60"` LimitCount int `gorm:"default:30"` LockEnable bool `gorm:"default:false"` LockThreshold int `gorm:"default:5"` LockDuration int `gorm:"default:30"` PwdExpireEnable bool `gorm:"default:false"` PwdExpireDays int `gorm:"default:90"` ForceNewUserChangePassword bool `gorm:"default:false"` } func (SecurityConfigPO) TableName() string { return "sys_security_config" } func DefaultSecurityConfig() SecurityConfigPO { return SecurityConfigPO{ID: 1, CaptchaTimeout: 3600, KeyLong: 6, ImgWidth: 240, ImgHeight: 80, PwdMinLength: 8, LimitWindow: 60, LimitCount: 30, LockThreshold: 5, LockDuration: 30, PwdExpireDays: 90} } type securityRepo struct{ data DatabaseProvider } func NewSecurityRepo(data DatabaseProvider) system.SecurityRepo { return &securityRepo{data: data} } func securityFromPO(v SecurityConfigPO) *system.SecurityConfig { return &system.SecurityConfig{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, CaptchaOpen: v.CaptchaOpen, CaptchaTimeout: v.CaptchaTimeout, KeyLong: v.KeyLong, ImgWidth: v.ImgWidth, ImgHeight: v.ImgHeight, PwdMinLength: v.PwdMinLength, PwdRequireUpper: v.PwdRequireUpper, PwdRequireLower: v.PwdRequireLower, PwdRequireDigit: v.PwdRequireDigit, PwdRequireSpecial: v.PwdRequireSpecial, LimitEnable: v.LimitEnable, LimitWindow: v.LimitWindow, LimitCount: v.LimitCount, LockEnable: v.LockEnable, LockThreshold: v.LockThreshold, LockDuration: v.LockDuration, PwdExpireEnable: v.PwdExpireEnable, PwdExpireDays: v.PwdExpireDays, ForceNewUserChangePassword: v.ForceNewUserChangePassword} } func securityToPO(v *system.SecurityConfig) SecurityConfigPO { return SecurityConfigPO{ID: 1, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, CaptchaOpen: v.CaptchaOpen, CaptchaTimeout: v.CaptchaTimeout, KeyLong: v.KeyLong, ImgWidth: v.ImgWidth, ImgHeight: v.ImgHeight, PwdMinLength: v.PwdMinLength, PwdRequireUpper: v.PwdRequireUpper, PwdRequireLower: v.PwdRequireLower, PwdRequireDigit: v.PwdRequireDigit, PwdRequireSpecial: v.PwdRequireSpecial, LimitEnable: v.LimitEnable, LimitWindow: v.LimitWindow, LimitCount: v.LimitCount, LockEnable: v.LockEnable, LockThreshold: v.LockThreshold, LockDuration: v.LockDuration, PwdExpireEnable: v.PwdExpireEnable, PwdExpireDays: v.PwdExpireDays, ForceNewUserChangePassword: v.ForceNewUserChangePassword} } func (r *securityRepo) SecurityConfig(ctx context.Context) (*system.SecurityConfig, error) { if !r.data.DatabaseReady() { po := DefaultSecurityConfig() po.ID = 0 return securityFromPO(po), errors.New("数据库未初始化") } db := r.data.DB().WithContext(ctx) var po SecurityConfigPO err := db.First(&po, 1).Error if errors.Is(err, gorm.ErrRecordNotFound) { po = DefaultSecurityConfig() err = db.Create(&po).Error } if err != nil { return nil, err } return securityFromPO(po), nil } func (r *securityRepo) SaveSecurityConfig(ctx context.Context, v *system.SecurityConfig) error { po := securityToPO(v) if err := r.data.DB().WithContext(ctx).Save(&po).Error; err != nil { return err } v.ID, v.CreatedAt, v.UpdatedAt = po.ID, po.CreatedAt, po.UpdatedAt return nil } func (r *securityRepo) BackfillPasswordUpdatedAt(ctx context.Context, at time.Time) error { return r.data.DB().WithContext(ctx).Table("sys_users").Where("password_updated_at IS NULL").Update("password_updated_at", at).Error }