fix settings auth source default persistence

This commit is contained in:
IanShaw027
2026-04-21 02:08:04 +08:00
parent cd0338fbae
commit e12599c1b9
7 changed files with 398 additions and 75 deletions

View File

@@ -1011,10 +1011,6 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
}(),
}
if err := h.settingService.UpdateSettings(c.Request.Context(), settings); err != nil {
response.ErrorFrom(c, err)
return
}
authSourceDefaults := &service.AuthSourceDefaultSettings{
Email: service.ProviderDefaultGrantSettings{
Balance: float64ValueOrDefault(req.AuthSourceDefaultEmailBalance, previousAuthSourceDefaults.Email.Balance),
@@ -1046,7 +1042,7 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
},
ForceEmailOnThirdPartySignup: boolValueOrDefault(req.ForceEmailOnThirdPartySignup, previousAuthSourceDefaults.ForceEmailOnThirdPartySignup),
}
if err := h.settingService.UpdateAuthSourceDefaultSettings(c.Request.Context(), authSourceDefaults); err != nil {
if err := h.settingService.UpdateSettingsWithAuthSourceDefaults(c.Request.Context(), settings, authSourceDefaults); err != nil {
response.ErrorFrom(c, err)
return
}
@@ -1086,7 +1082,7 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
}
}
h.auditSettingsUpdate(c, previousSettings, settings, req)
h.auditSettingsUpdate(c, previousSettings, settings, previousAuthSourceDefaults, authSourceDefaults, req)
// 重新获取设置返回
updatedSettings, err := h.settingService.GetAllSettings(c.Request.Context())
@@ -1245,12 +1241,12 @@ func hasPaymentFields(req UpdateSettingsRequest) bool {
req.PaymentCancelRateLimitUnit != nil || req.PaymentCancelRateLimitMode != nil
}
func (h *SettingHandler) auditSettingsUpdate(c *gin.Context, before *service.SystemSettings, after *service.SystemSettings, req UpdateSettingsRequest) {
func (h *SettingHandler) auditSettingsUpdate(c *gin.Context, before *service.SystemSettings, after *service.SystemSettings, beforeAuthSourceDefaults *service.AuthSourceDefaultSettings, afterAuthSourceDefaults *service.AuthSourceDefaultSettings, req UpdateSettingsRequest) {
if before == nil || after == nil {
return
}
changed := diffSettings(before, after, req)
changed := diffSettings(before, after, beforeAuthSourceDefaults, afterAuthSourceDefaults, req)
if len(changed) == 0 {
return
}
@@ -1265,7 +1261,7 @@ func (h *SettingHandler) auditSettingsUpdate(c *gin.Context, before *service.Sys
)
}
func diffSettings(before *service.SystemSettings, after *service.SystemSettings, req UpdateSettingsRequest) []string {
func diffSettings(before *service.SystemSettings, after *service.SystemSettings, beforeAuthSourceDefaults *service.AuthSourceDefaultSettings, afterAuthSourceDefaults *service.AuthSourceDefaultSettings, req UpdateSettingsRequest) []string {
changed := make([]string, 0, 20)
if before.RegistrationEnabled != after.RegistrationEnabled {
changed = append(changed, "registration_enabled")
@@ -1535,6 +1531,50 @@ func diffSettings(before *service.SystemSettings, after *service.SystemSettings,
if !equalNotifyEmailEntries(before.AccountQuotaNotifyEmails, after.AccountQuotaNotifyEmails) {
changed = append(changed, "account_quota_notify_emails")
}
changed = appendAuthSourceDefaultChanges(changed, beforeAuthSourceDefaults, afterAuthSourceDefaults)
return changed
}
func appendAuthSourceDefaultChanges(changed []string, before *service.AuthSourceDefaultSettings, after *service.AuthSourceDefaultSettings) []string {
if before == nil {
before = &service.AuthSourceDefaultSettings{}
}
if after == nil {
after = &service.AuthSourceDefaultSettings{}
}
type providerDefaultGrantField struct {
name string
before service.ProviderDefaultGrantSettings
after service.ProviderDefaultGrantSettings
}
fields := []providerDefaultGrantField{
{name: "email", before: before.Email, after: after.Email},
{name: "linuxdo", before: before.LinuxDo, after: after.LinuxDo},
{name: "oidc", before: before.OIDC, after: after.OIDC},
{name: "wechat", before: before.WeChat, after: after.WeChat},
}
for _, field := range fields {
if field.before.Balance != field.after.Balance {
changed = append(changed, "auth_source_default_"+field.name+"_balance")
}
if field.before.Concurrency != field.after.Concurrency {
changed = append(changed, "auth_source_default_"+field.name+"_concurrency")
}
if !equalDefaultSubscriptions(field.before.Subscriptions, field.after.Subscriptions) {
changed = append(changed, "auth_source_default_"+field.name+"_subscriptions")
}
if field.before.GrantOnSignup != field.after.GrantOnSignup {
changed = append(changed, "auth_source_default_"+field.name+"_grant_on_signup")
}
if field.before.GrantOnFirstBind != field.after.GrantOnFirstBind {
changed = append(changed, "auth_source_default_"+field.name+"_grant_on_first_bind")
}
}
if before.ForceEmailOnThirdPartySignup != after.ForceEmailOnThirdPartySignup {
changed = append(changed, "force_email_on_third_party_signup")
}
return changed
}

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"
@@ -66,6 +67,58 @@ func (s *settingHandlerRepoStub) Delete(ctx context.Context, key string) error {
panic("unexpected Delete call")
}
type failingAuthSourceSettingsRepoStub struct {
values map[string]string
err error
}
func (s *failingAuthSourceSettingsRepoStub) Get(ctx context.Context, key string) (*service.Setting, error) {
panic("unexpected Get call")
}
func (s *failingAuthSourceSettingsRepoStub) GetValue(ctx context.Context, key string) (string, error) {
panic("unexpected GetValue call")
}
func (s *failingAuthSourceSettingsRepoStub) Set(ctx context.Context, key, value string) error {
panic("unexpected Set call")
}
func (s *failingAuthSourceSettingsRepoStub) GetMultiple(ctx context.Context, keys []string) (map[string]string, error) {
out := make(map[string]string, len(keys))
for _, key := range keys {
if value, ok := s.values[key]; ok {
out[key] = value
}
}
return out, nil
}
func (s *failingAuthSourceSettingsRepoStub) SetMultiple(ctx context.Context, settings map[string]string) error {
if _, ok := settings[service.SettingKeyAuthSourceDefaultEmailBalance]; ok {
return s.err
}
for key, value := range settings {
if s.values == nil {
s.values = map[string]string{}
}
s.values[key] = value
}
return nil
}
func (s *failingAuthSourceSettingsRepoStub) GetAll(ctx context.Context) (map[string]string, error) {
out := make(map[string]string, len(s.values))
for key, value := range s.values {
out[key] = value
}
return out, nil
}
func (s *failingAuthSourceSettingsRepoStub) Delete(ctx context.Context, key string) error {
panic("unexpected Delete call")
}
func TestSettingHandler_GetSettings_InjectsAuthSourceDefaults(t *testing.T) {
gin.SetMode(gin.TestMode)
repo := &settingHandlerRepoStub{
@@ -221,3 +274,73 @@ func TestSettingHandler_UpdateSettings_RejectsInvalidPaymentVisibleMethodSource(
require.Equal(t, http.StatusBadRequest, rec.Code)
require.NotContains(t, repo.values, service.SettingPaymentVisibleMethodAlipaySource)
}
func TestSettingHandler_UpdateSettings_DoesNotPersistPartialSystemSettingsWhenAuthSourceDefaultsFail(t *testing.T) {
gin.SetMode(gin.TestMode)
repo := &failingAuthSourceSettingsRepoStub{
values: map[string]string{
service.SettingKeyRegistrationEnabled: "false",
service.SettingKeyPromoCodeEnabled: "true",
service.SettingKeyAuthSourceDefaultEmailBalance: "9.5",
service.SettingKeyAuthSourceDefaultEmailConcurrency: "8",
service.SettingKeyAuthSourceDefaultEmailSubscriptions: `[{"group_id":31,"validity_days":15}]`,
},
err: errors.New("write auth source defaults failed"),
}
svc := service.NewSettingService(repo, &config.Config{Default: config.DefaultConfig{UserConcurrency: 5}})
handler := NewSettingHandler(svc, nil, nil, nil, nil, nil)
body := map[string]any{
"registration_enabled": true,
"promo_code_enabled": true,
"auth_source_default_email_balance": 12.75,
}
rawBody, err := json.Marshal(body)
require.NoError(t, err)
rec := httptest.NewRecorder()
c, _ := gin.CreateTestContext(rec)
c.Request = httptest.NewRequest(http.MethodPut, "/api/v1/admin/settings", bytes.NewReader(rawBody))
c.Request.Header.Set("Content-Type", "application/json")
handler.UpdateSettings(c)
require.Equal(t, http.StatusInternalServerError, rec.Code)
require.Equal(t, "false", repo.values[service.SettingKeyRegistrationEnabled])
require.Equal(t, "9.5", repo.values[service.SettingKeyAuthSourceDefaultEmailBalance])
}
func TestDiffSettings_IncludesAuthSourceDefaultsAndForceEmail(t *testing.T) {
changed := diffSettings(
&service.SystemSettings{},
&service.SystemSettings{},
&service.AuthSourceDefaultSettings{
Email: service.ProviderDefaultGrantSettings{
Balance: 0,
Concurrency: 5,
Subscriptions: nil,
GrantOnSignup: true,
GrantOnFirstBind: false,
},
ForceEmailOnThirdPartySignup: false,
},
&service.AuthSourceDefaultSettings{
Email: service.ProviderDefaultGrantSettings{
Balance: 12.5,
Concurrency: 7,
Subscriptions: []service.DefaultSubscriptionSetting{{GroupID: 21, ValidityDays: 30}},
GrantOnSignup: false,
GrantOnFirstBind: true,
},
ForceEmailOnThirdPartySignup: true,
},
UpdateSettingsRequest{},
)
require.Contains(t, changed, "auth_source_default_email_balance")
require.Contains(t, changed, "auth_source_default_email_concurrency")
require.Contains(t, changed, "auth_source_default_email_subscriptions")
require.Contains(t, changed, "auth_source_default_email_grant_on_signup")
require.Contains(t, changed, "auth_source_default_email_grant_on_first_bind")
require.Contains(t, changed, "force_email_on_third_party_signup")
}