Release / update-version (push) Has been cancelled
Release / build-frontend (push) Has been cancelled
Release / release (push) Has been cancelled
Release / sync-version-file (push) Has been cancelled
CI / shell (push) Canceled after 0s
CI / test (push) Canceled after 0s
CI / frontend (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
Security Scan / backend-security (push) Canceled after 0s
Security Scan / frontend-security (push) Canceled after 0s
864 lines
33 KiB
Go
864 lines
33 KiB
Go
package admin
|
||
|
||
import (
|
||
"log/slog"
|
||
|
||
"github.com/Wei-Shaw/sub2api/internal/handler/dto"
|
||
"github.com/Wei-Shaw/sub2api/internal/server/middleware"
|
||
"github.com/Wei-Shaw/sub2api/internal/service"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
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, beforeAuthSourceDefaults, afterAuthSourceDefaults, req)
|
||
if len(changed) == 0 {
|
||
return
|
||
}
|
||
|
||
subject, _ := middleware.GetAuthSubjectFromContext(c)
|
||
role, _ := middleware.GetUserRoleFromContext(c)
|
||
slog.Info("settings updated",
|
||
"audit", true,
|
||
"user_id", subject.UserID,
|
||
"role", role,
|
||
"changed", changed,
|
||
)
|
||
}
|
||
|
||
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")
|
||
}
|
||
if before.EmailVerifyEnabled != after.EmailVerifyEnabled {
|
||
changed = append(changed, "email_verify_enabled")
|
||
}
|
||
if !equalStringSlice(before.RegistrationEmailSuffixWhitelist, after.RegistrationEmailSuffixWhitelist) {
|
||
changed = append(changed, "registration_email_suffix_whitelist")
|
||
}
|
||
if before.RegistrationEmailDomainQuotaEnabled != after.RegistrationEmailDomainQuotaEnabled {
|
||
changed = append(changed, "registration_email_domain_quota_enabled")
|
||
}
|
||
if before.PromoCodeEnabled != after.PromoCodeEnabled {
|
||
changed = append(changed, "promo_code_enabled")
|
||
}
|
||
if before.InvitationCodeEnabled != after.InvitationCodeEnabled {
|
||
changed = append(changed, "invitation_code_enabled")
|
||
}
|
||
if before.PasswordResetEnabled != after.PasswordResetEnabled {
|
||
changed = append(changed, "password_reset_enabled")
|
||
}
|
||
if before.FrontendURL != after.FrontendURL {
|
||
changed = append(changed, "frontend_url")
|
||
}
|
||
if before.TotpEnabled != after.TotpEnabled {
|
||
changed = append(changed, "totp_enabled")
|
||
}
|
||
if before.PasskeyEnabled != after.PasskeyEnabled {
|
||
changed = append(changed, "passkey_enabled")
|
||
}
|
||
if before.SessionBindingEnabled != after.SessionBindingEnabled {
|
||
changed = append(changed, "session_binding_enabled")
|
||
}
|
||
if before.StepUpEnabled != after.StepUpEnabled {
|
||
changed = append(changed, "step_up_enabled")
|
||
}
|
||
if before.LoginAgreementEnabled != after.LoginAgreementEnabled {
|
||
changed = append(changed, "login_agreement_enabled")
|
||
}
|
||
if before.LoginAgreementMode != after.LoginAgreementMode {
|
||
changed = append(changed, "login_agreement_mode")
|
||
}
|
||
if before.LoginAgreementUpdatedAt != after.LoginAgreementUpdatedAt {
|
||
changed = append(changed, "login_agreement_updated_at")
|
||
}
|
||
if !equalLoginAgreementDocuments(before.LoginAgreementDocuments, after.LoginAgreementDocuments) {
|
||
changed = append(changed, "login_agreement_documents")
|
||
}
|
||
if before.SMTPHost != after.SMTPHost {
|
||
changed = append(changed, "smtp_host")
|
||
}
|
||
if before.SMTPPort != after.SMTPPort {
|
||
changed = append(changed, "smtp_port")
|
||
}
|
||
if before.SMTPUsername != after.SMTPUsername {
|
||
changed = append(changed, "smtp_username")
|
||
}
|
||
if req.SMTPPassword != "" {
|
||
changed = append(changed, "smtp_password")
|
||
}
|
||
if before.SMTPFrom != after.SMTPFrom {
|
||
changed = append(changed, "smtp_from_email")
|
||
}
|
||
if before.SMTPFromName != after.SMTPFromName {
|
||
changed = append(changed, "smtp_from_name")
|
||
}
|
||
if before.SMTPUseTLS != after.SMTPUseTLS {
|
||
changed = append(changed, "smtp_use_tls")
|
||
}
|
||
if before.TurnstileEnabled != after.TurnstileEnabled {
|
||
changed = append(changed, "turnstile_enabled")
|
||
}
|
||
if before.TurnstileSiteKey != after.TurnstileSiteKey {
|
||
changed = append(changed, "turnstile_site_key")
|
||
}
|
||
if req.TurnstileSecretKey != "" {
|
||
changed = append(changed, "turnstile_secret_key")
|
||
}
|
||
if before.TencentCaptchaEnabled != after.TencentCaptchaEnabled {
|
||
changed = append(changed, "tencent_captcha_enabled")
|
||
}
|
||
if before.TencentCaptchaAppID != after.TencentCaptchaAppID {
|
||
changed = append(changed, "tencent_captcha_app_id")
|
||
}
|
||
if req.TencentCaptchaAppSecretKey != "" {
|
||
changed = append(changed, "tencent_captcha_app_secret_key")
|
||
}
|
||
if req.TencentCaptchaCloudSecretID != "" {
|
||
changed = append(changed, "tencent_captcha_cloud_secret_id")
|
||
}
|
||
if req.TencentCaptchaCloudSecretKey != "" {
|
||
changed = append(changed, "tencent_captcha_cloud_secret_key")
|
||
}
|
||
if before.TencentCaptchaRegion != after.TencentCaptchaRegion {
|
||
changed = append(changed, "tencent_captcha_region")
|
||
}
|
||
if before.AliyunCaptchaEnabled != after.AliyunCaptchaEnabled {
|
||
changed = append(changed, "aliyun_captcha_enabled")
|
||
}
|
||
if before.AliyunCaptchaAccessKeyID != after.AliyunCaptchaAccessKeyID {
|
||
changed = append(changed, "aliyun_captcha_access_key_id")
|
||
}
|
||
if req.AliyunCaptchaAccessKeySecret != "" {
|
||
changed = append(changed, "aliyun_captcha_access_key_secret")
|
||
}
|
||
if before.AliyunCaptchaSceneID != after.AliyunCaptchaSceneID {
|
||
changed = append(changed, "aliyun_captcha_scene_id")
|
||
}
|
||
if before.AliyunCaptchaPrefix != after.AliyunCaptchaPrefix {
|
||
changed = append(changed, "aliyun_captcha_prefix")
|
||
}
|
||
if before.AliyunCaptchaRegion != after.AliyunCaptchaRegion {
|
||
changed = append(changed, "aliyun_captcha_region")
|
||
}
|
||
if before.APIKeyACLTrustForwardedIP != after.APIKeyACLTrustForwardedIP {
|
||
changed = append(changed, "api_key_acl_trust_forwarded_ip")
|
||
}
|
||
if !equalStringSlice(before.ForwardedClientIPHeaders, after.ForwardedClientIPHeaders) {
|
||
changed = append(changed, "forwarded_client_ip_headers")
|
||
}
|
||
if before.LinuxDoConnectEnabled != after.LinuxDoConnectEnabled {
|
||
changed = append(changed, "linuxdo_connect_enabled")
|
||
}
|
||
if before.LinuxDoConnectClientID != after.LinuxDoConnectClientID {
|
||
changed = append(changed, "linuxdo_connect_client_id")
|
||
}
|
||
if req.LinuxDoConnectClientSecret != "" {
|
||
changed = append(changed, "linuxdo_connect_client_secret")
|
||
}
|
||
if before.LinuxDoConnectRedirectURL != after.LinuxDoConnectRedirectURL {
|
||
changed = append(changed, "linuxdo_connect_redirect_url")
|
||
}
|
||
if before.DingTalkConnectEnabled != after.DingTalkConnectEnabled {
|
||
changed = append(changed, "dingtalk_connect_enabled")
|
||
}
|
||
if before.DingTalkConnectClientID != after.DingTalkConnectClientID {
|
||
changed = append(changed, "dingtalk_connect_client_id")
|
||
}
|
||
if req.DingTalkConnectClientSecret != "" {
|
||
changed = append(changed, "dingtalk_connect_client_secret")
|
||
}
|
||
if before.DingTalkConnectRedirectURL != after.DingTalkConnectRedirectURL {
|
||
changed = append(changed, "dingtalk_connect_redirect_url")
|
||
}
|
||
if before.DingTalkConnectCorpRestrictionPolicy != after.DingTalkConnectCorpRestrictionPolicy {
|
||
changed = append(changed, "dingtalk_connect_corp_restriction_policy")
|
||
}
|
||
if before.DingTalkConnectInternalCorpID != after.DingTalkConnectInternalCorpID {
|
||
changed = append(changed, "dingtalk_connect_internal_corp_id")
|
||
}
|
||
if before.DingTalkConnectBypassRegistration != after.DingTalkConnectBypassRegistration {
|
||
changed = append(changed, "dingtalk_connect_bypass_registration")
|
||
}
|
||
if before.DingTalkConnectSyncCorpEmail != after.DingTalkConnectSyncCorpEmail {
|
||
changed = append(changed, "dingtalk_connect_sync_corp_email")
|
||
}
|
||
if before.DingTalkConnectSyncDisplayName != after.DingTalkConnectSyncDisplayName {
|
||
changed = append(changed, "dingtalk_connect_sync_display_name")
|
||
}
|
||
if before.DingTalkConnectSyncDept != after.DingTalkConnectSyncDept {
|
||
changed = append(changed, "dingtalk_connect_sync_dept")
|
||
}
|
||
if before.DingTalkConnectSyncCorpEmailAttrKey != after.DingTalkConnectSyncCorpEmailAttrKey {
|
||
changed = append(changed, "dingtalk_connect_sync_corp_email_attr_key")
|
||
}
|
||
if before.DingTalkConnectSyncDisplayNameAttrKey != after.DingTalkConnectSyncDisplayNameAttrKey {
|
||
changed = append(changed, "dingtalk_connect_sync_display_name_attr_key")
|
||
}
|
||
if before.DingTalkConnectSyncDeptAttrKey != after.DingTalkConnectSyncDeptAttrKey {
|
||
changed = append(changed, "dingtalk_connect_sync_dept_attr_key")
|
||
}
|
||
if before.WeChatConnectEnabled != after.WeChatConnectEnabled {
|
||
changed = append(changed, "wechat_connect_enabled")
|
||
}
|
||
if before.WeChatConnectAppID != after.WeChatConnectAppID {
|
||
changed = append(changed, "wechat_connect_app_id")
|
||
}
|
||
if req.WeChatConnectAppSecret != "" {
|
||
changed = append(changed, "wechat_connect_app_secret")
|
||
}
|
||
if before.WeChatConnectOpenAppID != after.WeChatConnectOpenAppID {
|
||
changed = append(changed, "wechat_connect_open_app_id")
|
||
}
|
||
if req.WeChatConnectOpenAppSecret != "" {
|
||
changed = append(changed, "wechat_connect_open_app_secret")
|
||
}
|
||
if before.WeChatConnectMPAppID != after.WeChatConnectMPAppID {
|
||
changed = append(changed, "wechat_connect_mp_app_id")
|
||
}
|
||
if req.WeChatConnectMPAppSecret != "" {
|
||
changed = append(changed, "wechat_connect_mp_app_secret")
|
||
}
|
||
if before.WeChatConnectMobileAppID != after.WeChatConnectMobileAppID {
|
||
changed = append(changed, "wechat_connect_mobile_app_id")
|
||
}
|
||
if req.WeChatConnectMobileAppSecret != "" {
|
||
changed = append(changed, "wechat_connect_mobile_app_secret")
|
||
}
|
||
if before.WeChatConnectOpenEnabled != after.WeChatConnectOpenEnabled {
|
||
changed = append(changed, "wechat_connect_open_enabled")
|
||
}
|
||
if before.WeChatConnectMPEnabled != after.WeChatConnectMPEnabled {
|
||
changed = append(changed, "wechat_connect_mp_enabled")
|
||
}
|
||
if before.WeChatConnectMobileEnabled != after.WeChatConnectMobileEnabled {
|
||
changed = append(changed, "wechat_connect_mobile_enabled")
|
||
}
|
||
if before.WeChatConnectMode != after.WeChatConnectMode {
|
||
changed = append(changed, "wechat_connect_mode")
|
||
}
|
||
if before.WeChatConnectScopes != after.WeChatConnectScopes {
|
||
changed = append(changed, "wechat_connect_scopes")
|
||
}
|
||
if before.WeChatConnectRedirectURL != after.WeChatConnectRedirectURL {
|
||
changed = append(changed, "wechat_connect_redirect_url")
|
||
}
|
||
if before.WeChatConnectFrontendRedirectURL != after.WeChatConnectFrontendRedirectURL {
|
||
changed = append(changed, "wechat_connect_frontend_redirect_url")
|
||
}
|
||
if before.OIDCConnectEnabled != after.OIDCConnectEnabled {
|
||
changed = append(changed, "oidc_connect_enabled")
|
||
}
|
||
if before.OIDCConnectProviderName != after.OIDCConnectProviderName {
|
||
changed = append(changed, "oidc_connect_provider_name")
|
||
}
|
||
if before.OIDCConnectClientID != after.OIDCConnectClientID {
|
||
changed = append(changed, "oidc_connect_client_id")
|
||
}
|
||
if req.OIDCConnectClientSecret != "" {
|
||
changed = append(changed, "oidc_connect_client_secret")
|
||
}
|
||
if before.OIDCConnectIssuerURL != after.OIDCConnectIssuerURL {
|
||
changed = append(changed, "oidc_connect_issuer_url")
|
||
}
|
||
if before.OIDCConnectDiscoveryURL != after.OIDCConnectDiscoveryURL {
|
||
changed = append(changed, "oidc_connect_discovery_url")
|
||
}
|
||
if before.OIDCConnectAuthorizeURL != after.OIDCConnectAuthorizeURL {
|
||
changed = append(changed, "oidc_connect_authorize_url")
|
||
}
|
||
if before.OIDCConnectTokenURL != after.OIDCConnectTokenURL {
|
||
changed = append(changed, "oidc_connect_token_url")
|
||
}
|
||
if before.OIDCConnectUserInfoURL != after.OIDCConnectUserInfoURL {
|
||
changed = append(changed, "oidc_connect_userinfo_url")
|
||
}
|
||
if before.OIDCConnectJWKSURL != after.OIDCConnectJWKSURL {
|
||
changed = append(changed, "oidc_connect_jwks_url")
|
||
}
|
||
if before.OIDCConnectScopes != after.OIDCConnectScopes {
|
||
changed = append(changed, "oidc_connect_scopes")
|
||
}
|
||
if before.OIDCConnectRedirectURL != after.OIDCConnectRedirectURL {
|
||
changed = append(changed, "oidc_connect_redirect_url")
|
||
}
|
||
if before.OIDCConnectFrontendRedirectURL != after.OIDCConnectFrontendRedirectURL {
|
||
changed = append(changed, "oidc_connect_frontend_redirect_url")
|
||
}
|
||
if before.OIDCConnectTokenAuthMethod != after.OIDCConnectTokenAuthMethod {
|
||
changed = append(changed, "oidc_connect_token_auth_method")
|
||
}
|
||
if before.OIDCConnectUsePKCE != after.OIDCConnectUsePKCE {
|
||
changed = append(changed, "oidc_connect_use_pkce")
|
||
}
|
||
if before.OIDCConnectValidateIDToken != after.OIDCConnectValidateIDToken {
|
||
changed = append(changed, "oidc_connect_validate_id_token")
|
||
}
|
||
if before.OIDCConnectAllowedSigningAlgs != after.OIDCConnectAllowedSigningAlgs {
|
||
changed = append(changed, "oidc_connect_allowed_signing_algs")
|
||
}
|
||
if before.OIDCConnectClockSkewSeconds != after.OIDCConnectClockSkewSeconds {
|
||
changed = append(changed, "oidc_connect_clock_skew_seconds")
|
||
}
|
||
if before.OIDCConnectRequireEmailVerified != after.OIDCConnectRequireEmailVerified {
|
||
changed = append(changed, "oidc_connect_require_email_verified")
|
||
}
|
||
if before.OIDCConnectUserInfoEmailPath != after.OIDCConnectUserInfoEmailPath {
|
||
changed = append(changed, "oidc_connect_userinfo_email_path")
|
||
}
|
||
if before.OIDCConnectUserInfoIDPath != after.OIDCConnectUserInfoIDPath {
|
||
changed = append(changed, "oidc_connect_userinfo_id_path")
|
||
}
|
||
if before.OIDCConnectUserInfoUsernamePath != after.OIDCConnectUserInfoUsernamePath {
|
||
changed = append(changed, "oidc_connect_userinfo_username_path")
|
||
}
|
||
if before.SiteName != after.SiteName {
|
||
changed = append(changed, "site_name")
|
||
}
|
||
if before.SiteLogo != after.SiteLogo {
|
||
changed = append(changed, "site_logo")
|
||
}
|
||
if before.SiteSubtitle != after.SiteSubtitle {
|
||
changed = append(changed, "site_subtitle")
|
||
}
|
||
if before.APIBaseURL != after.APIBaseURL {
|
||
changed = append(changed, "api_base_url")
|
||
}
|
||
if before.ContactInfo != after.ContactInfo {
|
||
changed = append(changed, "contact_info")
|
||
}
|
||
if before.DocURL != after.DocURL {
|
||
changed = append(changed, "doc_url")
|
||
}
|
||
if before.HomeContent != after.HomeContent {
|
||
changed = append(changed, "home_content")
|
||
}
|
||
if before.CompactHomeEnabled != after.CompactHomeEnabled {
|
||
changed = append(changed, "compact_home_enabled")
|
||
}
|
||
if before.HideCcsImportButton != after.HideCcsImportButton {
|
||
changed = append(changed, "hide_ccs_import_button")
|
||
}
|
||
if before.DefaultConcurrency != after.DefaultConcurrency {
|
||
changed = append(changed, "default_concurrency")
|
||
}
|
||
if before.DefaultBalance != after.DefaultBalance {
|
||
changed = append(changed, "default_balance")
|
||
}
|
||
if before.AffiliateRebateRate != after.AffiliateRebateRate {
|
||
changed = append(changed, "affiliate_rebate_rate")
|
||
}
|
||
if before.AffiliateRebateFreezeHours != after.AffiliateRebateFreezeHours {
|
||
changed = append(changed, "affiliate_rebate_freeze_hours")
|
||
}
|
||
if before.AffiliateRebateDurationDays != after.AffiliateRebateDurationDays {
|
||
changed = append(changed, "affiliate_rebate_duration_days")
|
||
}
|
||
if before.AffiliateRebatePerInviteeCap != after.AffiliateRebatePerInviteeCap {
|
||
changed = append(changed, "affiliate_rebate_per_invitee_cap")
|
||
}
|
||
if before.AdminRechargeRebateEnabled != after.AdminRechargeRebateEnabled {
|
||
changed = append(changed, "affiliate_admin_recharge_enabled")
|
||
}
|
||
if !equalDefaultSubscriptions(before.DefaultSubscriptions, after.DefaultSubscriptions) {
|
||
changed = append(changed, "default_subscriptions")
|
||
}
|
||
if before.EnableModelFallback != after.EnableModelFallback {
|
||
changed = append(changed, "enable_model_fallback")
|
||
}
|
||
if before.FallbackModelAnthropic != after.FallbackModelAnthropic {
|
||
changed = append(changed, "fallback_model_anthropic")
|
||
}
|
||
if before.FallbackModelOpenAI != after.FallbackModelOpenAI {
|
||
changed = append(changed, "fallback_model_openai")
|
||
}
|
||
if before.FallbackModelGemini != after.FallbackModelGemini {
|
||
changed = append(changed, "fallback_model_gemini")
|
||
}
|
||
if before.FallbackModelAntigravity != after.FallbackModelAntigravity {
|
||
changed = append(changed, "fallback_model_antigravity")
|
||
}
|
||
if before.EnableIdentityPatch != after.EnableIdentityPatch {
|
||
changed = append(changed, "enable_identity_patch")
|
||
}
|
||
if before.IdentityPatchPrompt != after.IdentityPatchPrompt {
|
||
changed = append(changed, "identity_patch_prompt")
|
||
}
|
||
if before.OpsMonitoringEnabled != after.OpsMonitoringEnabled {
|
||
changed = append(changed, "ops_monitoring_enabled")
|
||
}
|
||
if before.OpsRealtimeMonitoringEnabled != after.OpsRealtimeMonitoringEnabled {
|
||
changed = append(changed, "ops_realtime_monitoring_enabled")
|
||
}
|
||
if before.OpsQueryModeDefault != after.OpsQueryModeDefault {
|
||
changed = append(changed, "ops_query_mode_default")
|
||
}
|
||
if before.OpsMetricsIntervalSeconds != after.OpsMetricsIntervalSeconds {
|
||
changed = append(changed, "ops_metrics_interval_seconds")
|
||
}
|
||
if before.MinClaudeCodeVersion != after.MinClaudeCodeVersion {
|
||
changed = append(changed, "min_claude_code_version")
|
||
}
|
||
if before.MaxClaudeCodeVersion != after.MaxClaudeCodeVersion {
|
||
changed = append(changed, "max_claude_code_version")
|
||
}
|
||
if before.MinCodexVersion != after.MinCodexVersion {
|
||
changed = append(changed, "min_codex_version")
|
||
}
|
||
if before.MaxCodexVersion != after.MaxCodexVersion {
|
||
changed = append(changed, "max_codex_version")
|
||
}
|
||
if before.CodexCLIOnlyAllowAppServerClients != after.CodexCLIOnlyAllowAppServerClients {
|
||
changed = append(changed, "codex_cli_only_allow_app_server_clients")
|
||
}
|
||
if before.CodexCLIOnlyEngineFingerprintSignals != after.CodexCLIOnlyEngineFingerprintSignals {
|
||
changed = append(changed, "codex_cli_only_engine_fingerprint_signals")
|
||
}
|
||
if before.CodexCLIOnlyBlacklist != after.CodexCLIOnlyBlacklist {
|
||
changed = append(changed, "codex_cli_only_blacklist")
|
||
}
|
||
if before.CodexCLIOnlyWhitelist != after.CodexCLIOnlyWhitelist {
|
||
changed = append(changed, "codex_cli_only_whitelist")
|
||
}
|
||
if before.AllowUngroupedKeyScheduling != after.AllowUngroupedKeyScheduling {
|
||
changed = append(changed, "allow_ungrouped_key_scheduling")
|
||
}
|
||
if before.BackendModeEnabled != after.BackendModeEnabled {
|
||
changed = append(changed, "backend_mode_enabled")
|
||
}
|
||
if before.PurchaseSubscriptionEnabled != after.PurchaseSubscriptionEnabled {
|
||
changed = append(changed, "purchase_subscription_enabled")
|
||
}
|
||
if before.PurchaseSubscriptionURL != after.PurchaseSubscriptionURL {
|
||
changed = append(changed, "purchase_subscription_url")
|
||
}
|
||
if before.TableDefaultPageSize != after.TableDefaultPageSize {
|
||
changed = append(changed, "table_default_page_size")
|
||
}
|
||
if !equalIntSlice(before.TablePageSizeOptions, after.TablePageSizeOptions) {
|
||
changed = append(changed, "table_page_size_options")
|
||
}
|
||
if before.CustomMenuItems != after.CustomMenuItems {
|
||
changed = append(changed, "custom_menu_items")
|
||
}
|
||
if before.CustomEndpoints != after.CustomEndpoints {
|
||
changed = append(changed, "custom_endpoints")
|
||
}
|
||
if before.EnableFingerprintUnification != after.EnableFingerprintUnification {
|
||
changed = append(changed, "enable_fingerprint_unification")
|
||
}
|
||
if before.EnableMetadataPassthrough != after.EnableMetadataPassthrough {
|
||
changed = append(changed, "enable_metadata_passthrough")
|
||
}
|
||
if before.EnableCCHSigning != after.EnableCCHSigning {
|
||
changed = append(changed, "enable_cch_signing")
|
||
}
|
||
if before.EnableClaudeOAuthSystemPromptInjection != after.EnableClaudeOAuthSystemPromptInjection {
|
||
changed = append(changed, "enable_claude_oauth_system_prompt_injection")
|
||
}
|
||
if before.ClaudeOAuthSystemPrompt != after.ClaudeOAuthSystemPrompt {
|
||
changed = append(changed, "claude_oauth_system_prompt")
|
||
}
|
||
if before.ClaudeOAuthSystemPromptBlocks != after.ClaudeOAuthSystemPromptBlocks {
|
||
changed = append(changed, "claude_oauth_system_prompt_blocks")
|
||
}
|
||
if before.EnableAnthropicCacheTTL1hInjection != after.EnableAnthropicCacheTTL1hInjection {
|
||
changed = append(changed, "enable_anthropic_cache_ttl_1h_injection")
|
||
}
|
||
if before.RewriteMessageCacheControl != after.RewriteMessageCacheControl {
|
||
changed = append(changed, "rewrite_message_cache_control")
|
||
}
|
||
if before.EnableClientDatelineNormalization != after.EnableClientDatelineNormalization {
|
||
changed = append(changed, "enable_client_dateline_normalization")
|
||
}
|
||
if before.AntigravityUserAgentVersion != after.AntigravityUserAgentVersion {
|
||
changed = append(changed, "antigravity_user_agent_version")
|
||
}
|
||
if before.OpenAICodexUserAgent != after.OpenAICodexUserAgent {
|
||
changed = append(changed, "openai_codex_user_agent")
|
||
}
|
||
if before.OpenAICodexClientVersion != after.OpenAICodexClientVersion {
|
||
changed = append(changed, "openai_codex_client_version")
|
||
}
|
||
if before.OpenAICodexVersionAutoSyncEnabled != after.OpenAICodexVersionAutoSyncEnabled {
|
||
changed = append(changed, "openai_codex_version_auto_sync_enabled")
|
||
}
|
||
if before.PaymentVisibleMethodAlipaySource != after.PaymentVisibleMethodAlipaySource {
|
||
changed = append(changed, "payment_visible_method_alipay_source")
|
||
}
|
||
if before.PaymentVisibleMethodWxpaySource != after.PaymentVisibleMethodWxpaySource {
|
||
changed = append(changed, "payment_visible_method_wxpay_source")
|
||
}
|
||
if before.PaymentVisibleMethodAlipayEnabled != after.PaymentVisibleMethodAlipayEnabled {
|
||
changed = append(changed, "payment_visible_method_alipay_enabled")
|
||
}
|
||
if before.PaymentVisibleMethodWxpayEnabled != after.PaymentVisibleMethodWxpayEnabled {
|
||
changed = append(changed, "payment_visible_method_wxpay_enabled")
|
||
}
|
||
if before.OpenAILowUpstreamRatePriorityEnabled != after.OpenAILowUpstreamRatePriorityEnabled {
|
||
changed = append(changed, "openai_low_upstream_rate_priority_enabled")
|
||
}
|
||
if before.OpenAIOAuthSchedulingRateMultiplier != after.OpenAIOAuthSchedulingRateMultiplier {
|
||
changed = append(changed, "openai_oauth_scheduling_rate_multiplier")
|
||
}
|
||
if before.OpenAIAdvancedSchedulerEnabled != after.OpenAIAdvancedSchedulerEnabled {
|
||
changed = append(changed, "openai_advanced_scheduler_enabled")
|
||
}
|
||
if before.OpenAIAdvancedSchedulerStickyWeightedEnabled != after.OpenAIAdvancedSchedulerStickyWeightedEnabled {
|
||
changed = append(changed, "openai_advanced_scheduler_sticky_weighted_enabled")
|
||
}
|
||
if before.OpenAIAdvancedSchedulerSubscriptionPriorityEnabled != after.OpenAIAdvancedSchedulerSubscriptionPriorityEnabled {
|
||
changed = append(changed, "openai_advanced_scheduler_subscription_priority_enabled")
|
||
}
|
||
if before.OpenAIAdvancedSchedulerLBTopK != after.OpenAIAdvancedSchedulerLBTopK {
|
||
changed = append(changed, "openai_advanced_scheduler_lb_top_k")
|
||
}
|
||
if before.OpenAIAdvancedSchedulerWeightPriority != after.OpenAIAdvancedSchedulerWeightPriority {
|
||
changed = append(changed, "openai_advanced_scheduler_weight_priority")
|
||
}
|
||
if before.OpenAIAdvancedSchedulerWeightLoad != after.OpenAIAdvancedSchedulerWeightLoad {
|
||
changed = append(changed, "openai_advanced_scheduler_weight_load")
|
||
}
|
||
if before.OpenAIAdvancedSchedulerWeightQueue != after.OpenAIAdvancedSchedulerWeightQueue {
|
||
changed = append(changed, "openai_advanced_scheduler_weight_queue")
|
||
}
|
||
if before.OpenAIAdvancedSchedulerWeightErrorRate != after.OpenAIAdvancedSchedulerWeightErrorRate {
|
||
changed = append(changed, "openai_advanced_scheduler_weight_error_rate")
|
||
}
|
||
if before.OpenAIAdvancedSchedulerWeightTTFT != after.OpenAIAdvancedSchedulerWeightTTFT {
|
||
changed = append(changed, "openai_advanced_scheduler_weight_ttft")
|
||
}
|
||
if before.OpenAIAdvancedSchedulerWeightReset != after.OpenAIAdvancedSchedulerWeightReset {
|
||
changed = append(changed, "openai_advanced_scheduler_weight_reset")
|
||
}
|
||
if before.OpenAIAdvancedSchedulerWeightQuotaHeadroom != after.OpenAIAdvancedSchedulerWeightQuotaHeadroom {
|
||
changed = append(changed, "openai_advanced_scheduler_weight_quota_headroom")
|
||
}
|
||
if before.OpenAIAdvancedSchedulerWeightUpstreamCost != after.OpenAIAdvancedSchedulerWeightUpstreamCost {
|
||
changed = append(changed, "openai_advanced_scheduler_weight_upstream_cost")
|
||
}
|
||
if before.OpenAIAdvancedSchedulerWeightPreviousResponse != after.OpenAIAdvancedSchedulerWeightPreviousResponse {
|
||
changed = append(changed, "openai_advanced_scheduler_weight_previous_response")
|
||
}
|
||
if before.OpenAIAdvancedSchedulerWeightSessionSticky != after.OpenAIAdvancedSchedulerWeightSessionSticky {
|
||
changed = append(changed, "openai_advanced_scheduler_weight_session_sticky")
|
||
}
|
||
// 余额、订阅到期与账号限额通知
|
||
if before.BalanceLowNotifyEnabled != after.BalanceLowNotifyEnabled {
|
||
changed = append(changed, "balance_low_notify_enabled")
|
||
}
|
||
if before.BalanceLowNotifyThreshold != after.BalanceLowNotifyThreshold {
|
||
changed = append(changed, "balance_low_notify_threshold")
|
||
}
|
||
if before.BalanceLowNotifyRechargeURL != after.BalanceLowNotifyRechargeURL {
|
||
changed = append(changed, "balance_low_notify_recharge_url")
|
||
}
|
||
if before.SubscriptionExpiryNotifyEnabled != after.SubscriptionExpiryNotifyEnabled {
|
||
changed = append(changed, "subscription_expiry_notify_enabled")
|
||
}
|
||
if before.AccountQuotaNotifyEnabled != after.AccountQuotaNotifyEnabled {
|
||
changed = append(changed, "account_quota_notify_enabled")
|
||
}
|
||
if !equalNotifyEmailEntries(before.AccountQuotaNotifyEmails, after.AccountQuotaNotifyEmails) {
|
||
changed = append(changed, "account_quota_notify_emails")
|
||
}
|
||
if before.ChannelMonitorEnabled != after.ChannelMonitorEnabled {
|
||
changed = append(changed, "channel_monitor_enabled")
|
||
}
|
||
if before.ChannelMonitorDefaultIntervalSeconds != after.ChannelMonitorDefaultIntervalSeconds {
|
||
changed = append(changed, "channel_monitor_default_interval_seconds")
|
||
}
|
||
if before.AvailableChannelsEnabled != after.AvailableChannelsEnabled {
|
||
changed = append(changed, "available_channels_enabled")
|
||
}
|
||
if before.ModelPlazaEnabled != after.ModelPlazaEnabled {
|
||
changed = append(changed, "model_plaza_enabled")
|
||
}
|
||
if before.ModelPlazaRequireAuth != after.ModelPlazaRequireAuth {
|
||
changed = append(changed, "model_plaza_require_auth")
|
||
}
|
||
if before.ModelPlazaDescription != after.ModelPlazaDescription {
|
||
changed = append(changed, "model_plaza_description")
|
||
}
|
||
if before.AffiliateEnabled != after.AffiliateEnabled {
|
||
changed = append(changed, "affiliate_enabled")
|
||
}
|
||
if before.RiskControlEnabled != after.RiskControlEnabled {
|
||
changed = append(changed, "risk_control_enabled")
|
||
}
|
||
if before.CyberSessionBlockEnabled != after.CyberSessionBlockEnabled {
|
||
changed = append(changed, "cyber_session_block_enabled")
|
||
}
|
||
if before.CyberSessionBlockTTLSeconds != after.CyberSessionBlockTTLSeconds {
|
||
changed = append(changed, "cyber_session_block_ttl_seconds")
|
||
}
|
||
// Default platform quotas(JSON map,整体比较)
|
||
if !equalPlatformQuotaSettings(before.DefaultPlatformQuotas, after.DefaultPlatformQuotas) {
|
||
changed = append(changed, service.SettingKeyDefaultPlatformQuotas)
|
||
}
|
||
if !equalAccountSchedulingThresholds(before.AccountSchedulingThresholds, after.AccountSchedulingThresholds) {
|
||
changed = append(changed, service.SettingKeyAccountSchedulingThresholds)
|
||
}
|
||
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},
|
||
{name: "github", before: before.GitHub, after: after.GitHub},
|
||
{name: "google", before: before.Google, after: after.Google},
|
||
{name: "dingtalk", before: before.DingTalk, after: after.DingTalk},
|
||
}
|
||
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")
|
||
}
|
||
// Platform quotas diff:整体替换语义,发单个 JSON key。
|
||
if !equalPlatformQuotaSettings(field.before.PlatformQuotas, field.after.PlatformQuotas) {
|
||
changed = append(changed, service.SettingKeyAuthSourcePlatformQuotas(field.name))
|
||
}
|
||
}
|
||
if before.ForceEmailOnThirdPartySignup != after.ForceEmailOnThirdPartySignup {
|
||
changed = append(changed, "force_email_on_third_party_signup")
|
||
}
|
||
return changed
|
||
}
|
||
|
||
func normalizeDefaultSubscriptions(input []dto.DefaultSubscriptionSetting) []dto.DefaultSubscriptionSetting {
|
||
if len(input) == 0 {
|
||
return nil
|
||
}
|
||
normalized := make([]dto.DefaultSubscriptionSetting, 0, len(input))
|
||
for _, item := range input {
|
||
if item.GroupID <= 0 || item.ValidityDays <= 0 {
|
||
continue
|
||
}
|
||
if item.ValidityDays > service.MaxValidityDays {
|
||
item.ValidityDays = service.MaxValidityDays
|
||
}
|
||
normalized = append(normalized, item)
|
||
}
|
||
return normalized
|
||
}
|
||
|
||
func normalizeOptionalDefaultSubscriptions(input *[]dto.DefaultSubscriptionSetting) *[]dto.DefaultSubscriptionSetting {
|
||
if input == nil {
|
||
return nil
|
||
}
|
||
normalized := normalizeDefaultSubscriptions(*input)
|
||
return &normalized
|
||
}
|
||
|
||
func float64ValueOrDefault(value *float64, fallback float64) float64 {
|
||
if value == nil {
|
||
return fallback
|
||
}
|
||
return *value
|
||
}
|
||
|
||
func intValueOrDefault(value *int, fallback int) int {
|
||
if value == nil {
|
||
return fallback
|
||
}
|
||
return *value
|
||
}
|
||
|
||
func boolValueOrDefault(value *bool, fallback bool) bool {
|
||
if value == nil {
|
||
return fallback
|
||
}
|
||
return *value
|
||
}
|
||
|
||
func defaultSubscriptionsValueOrDefault(input *[]dto.DefaultSubscriptionSetting, fallback []service.DefaultSubscriptionSetting) []service.DefaultSubscriptionSetting {
|
||
if input == nil {
|
||
return fallback
|
||
}
|
||
result := make([]service.DefaultSubscriptionSetting, 0, len(*input))
|
||
for _, item := range *input {
|
||
result = append(result, service.DefaultSubscriptionSetting{
|
||
GroupID: item.GroupID,
|
||
ValidityDays: item.ValidityDays,
|
||
})
|
||
}
|
||
return result
|
||
}
|
||
|
||
// platformQuotasValueOrDefault 处理 auth-source platform quota 的 nil 语义:
|
||
// nil = 请求未包含该字段(保留 fallback),non-nil(含 empty map)= 整体覆盖。
|
||
// 注意:JSON null 与字段省略等价——两者均反序列化为 nil map,因此都保留旧值;
|
||
// 若要清空某 source 的所有 quota 配置,须显式发空对象 {}。
|
||
func platformQuotasValueOrDefault(value, fallback map[string]*service.DefaultPlatformQuotaSetting) map[string]*service.DefaultPlatformQuotaSetting {
|
||
if value == nil {
|
||
return fallback
|
||
}
|
||
return value
|
||
}
|
||
|
||
func equalStringSlice(a, b []string) bool {
|
||
if len(a) != len(b) {
|
||
return false
|
||
}
|
||
for i := range a {
|
||
if a[i] != b[i] {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
func equalDefaultSubscriptions(a, b []service.DefaultSubscriptionSetting) bool {
|
||
if len(a) != len(b) {
|
||
return false
|
||
}
|
||
for i := range a {
|
||
if a[i].GroupID != b[i].GroupID || a[i].ValidityDays != b[i].ValidityDays {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
func equalLoginAgreementDocuments(a, b []service.LoginAgreementDocument) bool {
|
||
if len(a) != len(b) {
|
||
return false
|
||
}
|
||
for i := range a {
|
||
if a[i].ID != b[i].ID || a[i].Title != b[i].Title || a[i].ContentMD != b[i].ContentMD {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
func equalIntSlice(a, b []int) bool {
|
||
if len(a) != len(b) {
|
||
return false
|
||
}
|
||
for i := range a {
|
||
if a[i] != b[i] {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
func equalNotifyEmailEntries(a, b []service.NotifyEmailEntry) bool {
|
||
if len(a) != len(b) {
|
||
return false
|
||
}
|
||
for i := range a {
|
||
if a[i].Email != b[i].Email || a[i].Verified != b[i].Verified || a[i].Disabled != b[i].Disabled {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
// equalNullableFloat compares two *float64 values treating nil as a distinct case.
|
||
func equalNullableFloat(a, b *float64) bool {
|
||
if a == nil && b == nil {
|
||
return true
|
||
}
|
||
if a == nil || b == nil {
|
||
return false
|
||
}
|
||
return *a == *b
|
||
}
|
||
|
||
// slotOf returns the *float64 for the given window from a DefaultPlatformQuotaSetting.
|
||
func slotOf(s *service.DefaultPlatformQuotaSetting, win string) *float64 {
|
||
if s == nil {
|
||
return nil
|
||
}
|
||
switch win {
|
||
case "daily":
|
||
return s.DailyLimitUSD
|
||
case "weekly":
|
||
return s.WeeklyLimitUSD
|
||
case "monthly":
|
||
return s.MonthlyLimitUSD
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// equalPlatformQuotaSettings reports whether two platform-quota maps are identical across all allowed slots.
|
||
func equalAccountSchedulingThresholds(before, after map[string]int) bool {
|
||
for _, platform := range service.AllowedSchedulingThresholdPlatforms {
|
||
beforeValue := 100
|
||
if before != nil {
|
||
if value, ok := before[platform]; ok {
|
||
beforeValue = value
|
||
}
|
||
}
|
||
afterValue := 100
|
||
if after != nil {
|
||
if value, ok := after[platform]; ok {
|
||
afterValue = value
|
||
}
|
||
}
|
||
if beforeValue != afterValue {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
func equalPlatformQuotaSettings(before, after map[string]*service.DefaultPlatformQuotaSetting) bool {
|
||
for _, platform := range service.AllowedQuotaPlatforms {
|
||
b := before[platform]
|
||
a := after[platform]
|
||
if !equalNullableFloat(slotOf(b, "daily"), slotOf(a, "daily")) {
|
||
return false
|
||
}
|
||
if !equalNullableFloat(slotOf(b, "weekly"), slotOf(a, "weekly")) {
|
||
return false
|
||
}
|
||
if !equalNullableFloat(slotOf(b, "monthly"), slotOf(a, "monthly")) {
|
||
return false
|
||
}
|
||
}
|
||
return true
|
||
}
|
||
|
||
func stringSetting(value *string, fallback string) string {
|
||
if value == nil {
|
||
return fallback
|
||
}
|
||
return *value
|
||
}
|