feat(settings): support per-channel WeChat OAuth and persist payment options
This commit is contained in:
@@ -43,6 +43,15 @@ func scopesContainOpenID(scopes string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func firstNonEmpty(values ...string) string {
|
||||
for _, value := range values {
|
||||
if trimmed := strings.TrimSpace(value); trimmed != "" {
|
||||
return trimmed
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// SettingHandler 系统设置处理器
|
||||
type SettingHandler struct {
|
||||
settingService *service.SettingService
|
||||
@@ -99,126 +108,133 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
|
||||
}
|
||||
|
||||
payload := dto.SystemSettings{
|
||||
RegistrationEnabled: settings.RegistrationEnabled,
|
||||
EmailVerifyEnabled: settings.EmailVerifyEnabled,
|
||||
RegistrationEmailSuffixWhitelist: settings.RegistrationEmailSuffixWhitelist,
|
||||
PromoCodeEnabled: settings.PromoCodeEnabled,
|
||||
PasswordResetEnabled: settings.PasswordResetEnabled,
|
||||
FrontendURL: settings.FrontendURL,
|
||||
InvitationCodeEnabled: settings.InvitationCodeEnabled,
|
||||
TotpEnabled: settings.TotpEnabled,
|
||||
TotpEncryptionKeyConfigured: h.settingService.IsTotpEncryptionKeyConfigured(),
|
||||
SMTPHost: settings.SMTPHost,
|
||||
SMTPPort: settings.SMTPPort,
|
||||
SMTPUsername: settings.SMTPUsername,
|
||||
SMTPPasswordConfigured: settings.SMTPPasswordConfigured,
|
||||
SMTPFrom: settings.SMTPFrom,
|
||||
SMTPFromName: settings.SMTPFromName,
|
||||
SMTPUseTLS: settings.SMTPUseTLS,
|
||||
TurnstileEnabled: settings.TurnstileEnabled,
|
||||
TurnstileSiteKey: settings.TurnstileSiteKey,
|
||||
TurnstileSecretKeyConfigured: settings.TurnstileSecretKeyConfigured,
|
||||
LinuxDoConnectEnabled: settings.LinuxDoConnectEnabled,
|
||||
LinuxDoConnectClientID: settings.LinuxDoConnectClientID,
|
||||
LinuxDoConnectClientSecretConfigured: settings.LinuxDoConnectClientSecretConfigured,
|
||||
LinuxDoConnectRedirectURL: settings.LinuxDoConnectRedirectURL,
|
||||
WeChatConnectEnabled: settings.WeChatConnectEnabled,
|
||||
WeChatConnectAppID: settings.WeChatConnectAppID,
|
||||
WeChatConnectAppSecretConfigured: settings.WeChatConnectAppSecretConfigured,
|
||||
WeChatConnectOpenEnabled: settings.WeChatConnectOpenEnabled,
|
||||
WeChatConnectMPEnabled: settings.WeChatConnectMPEnabled,
|
||||
WeChatConnectMode: settings.WeChatConnectMode,
|
||||
WeChatConnectScopes: settings.WeChatConnectScopes,
|
||||
WeChatConnectRedirectURL: settings.WeChatConnectRedirectURL,
|
||||
WeChatConnectFrontendRedirectURL: settings.WeChatConnectFrontendRedirectURL,
|
||||
OIDCConnectEnabled: settings.OIDCConnectEnabled,
|
||||
OIDCConnectProviderName: settings.OIDCConnectProviderName,
|
||||
OIDCConnectClientID: settings.OIDCConnectClientID,
|
||||
OIDCConnectClientSecretConfigured: settings.OIDCConnectClientSecretConfigured,
|
||||
OIDCConnectIssuerURL: settings.OIDCConnectIssuerURL,
|
||||
OIDCConnectDiscoveryURL: settings.OIDCConnectDiscoveryURL,
|
||||
OIDCConnectAuthorizeURL: settings.OIDCConnectAuthorizeURL,
|
||||
OIDCConnectTokenURL: settings.OIDCConnectTokenURL,
|
||||
OIDCConnectUserInfoURL: settings.OIDCConnectUserInfoURL,
|
||||
OIDCConnectJWKSURL: settings.OIDCConnectJWKSURL,
|
||||
OIDCConnectScopes: settings.OIDCConnectScopes,
|
||||
OIDCConnectRedirectURL: settings.OIDCConnectRedirectURL,
|
||||
OIDCConnectFrontendRedirectURL: settings.OIDCConnectFrontendRedirectURL,
|
||||
OIDCConnectTokenAuthMethod: settings.OIDCConnectTokenAuthMethod,
|
||||
OIDCConnectUsePKCE: settings.OIDCConnectUsePKCE,
|
||||
OIDCConnectValidateIDToken: settings.OIDCConnectValidateIDToken,
|
||||
OIDCConnectAllowedSigningAlgs: settings.OIDCConnectAllowedSigningAlgs,
|
||||
OIDCConnectClockSkewSeconds: settings.OIDCConnectClockSkewSeconds,
|
||||
OIDCConnectRequireEmailVerified: settings.OIDCConnectRequireEmailVerified,
|
||||
OIDCConnectUserInfoEmailPath: settings.OIDCConnectUserInfoEmailPath,
|
||||
OIDCConnectUserInfoIDPath: settings.OIDCConnectUserInfoIDPath,
|
||||
OIDCConnectUserInfoUsernamePath: settings.OIDCConnectUserInfoUsernamePath,
|
||||
SiteName: settings.SiteName,
|
||||
SiteLogo: settings.SiteLogo,
|
||||
SiteSubtitle: settings.SiteSubtitle,
|
||||
APIBaseURL: settings.APIBaseURL,
|
||||
ContactInfo: settings.ContactInfo,
|
||||
DocURL: settings.DocURL,
|
||||
HomeContent: settings.HomeContent,
|
||||
HideCcsImportButton: settings.HideCcsImportButton,
|
||||
PurchaseSubscriptionEnabled: settings.PurchaseSubscriptionEnabled,
|
||||
PurchaseSubscriptionURL: settings.PurchaseSubscriptionURL,
|
||||
TableDefaultPageSize: settings.TableDefaultPageSize,
|
||||
TablePageSizeOptions: settings.TablePageSizeOptions,
|
||||
CustomMenuItems: dto.ParseCustomMenuItems(settings.CustomMenuItems),
|
||||
CustomEndpoints: dto.ParseCustomEndpoints(settings.CustomEndpoints),
|
||||
DefaultConcurrency: settings.DefaultConcurrency,
|
||||
DefaultBalance: settings.DefaultBalance,
|
||||
DefaultSubscriptions: defaultSubscriptions,
|
||||
EnableModelFallback: settings.EnableModelFallback,
|
||||
FallbackModelAnthropic: settings.FallbackModelAnthropic,
|
||||
FallbackModelOpenAI: settings.FallbackModelOpenAI,
|
||||
FallbackModelGemini: settings.FallbackModelGemini,
|
||||
FallbackModelAntigravity: settings.FallbackModelAntigravity,
|
||||
EnableIdentityPatch: settings.EnableIdentityPatch,
|
||||
IdentityPatchPrompt: settings.IdentityPatchPrompt,
|
||||
OpsMonitoringEnabled: opsEnabled && settings.OpsMonitoringEnabled,
|
||||
OpsRealtimeMonitoringEnabled: settings.OpsRealtimeMonitoringEnabled,
|
||||
OpsQueryModeDefault: settings.OpsQueryModeDefault,
|
||||
OpsMetricsIntervalSeconds: settings.OpsMetricsIntervalSeconds,
|
||||
MinClaudeCodeVersion: settings.MinClaudeCodeVersion,
|
||||
MaxClaudeCodeVersion: settings.MaxClaudeCodeVersion,
|
||||
AllowUngroupedKeyScheduling: settings.AllowUngroupedKeyScheduling,
|
||||
BackendModeEnabled: settings.BackendModeEnabled,
|
||||
EnableFingerprintUnification: settings.EnableFingerprintUnification,
|
||||
EnableMetadataPassthrough: settings.EnableMetadataPassthrough,
|
||||
EnableCCHSigning: settings.EnableCCHSigning,
|
||||
WebSearchEmulationEnabled: settings.WebSearchEmulationEnabled,
|
||||
PaymentVisibleMethodAlipaySource: settings.PaymentVisibleMethodAlipaySource,
|
||||
PaymentVisibleMethodWxpaySource: settings.PaymentVisibleMethodWxpaySource,
|
||||
PaymentVisibleMethodAlipayEnabled: settings.PaymentVisibleMethodAlipayEnabled,
|
||||
PaymentVisibleMethodWxpayEnabled: settings.PaymentVisibleMethodWxpayEnabled,
|
||||
OpenAIAdvancedSchedulerEnabled: settings.OpenAIAdvancedSchedulerEnabled,
|
||||
BalanceLowNotifyEnabled: settings.BalanceLowNotifyEnabled,
|
||||
BalanceLowNotifyThreshold: settings.BalanceLowNotifyThreshold,
|
||||
BalanceLowNotifyRechargeURL: settings.BalanceLowNotifyRechargeURL,
|
||||
AccountQuotaNotifyEnabled: settings.AccountQuotaNotifyEnabled,
|
||||
AccountQuotaNotifyEmails: dto.NotifyEmailEntriesFromService(settings.AccountQuotaNotifyEmails),
|
||||
PaymentEnabled: paymentCfg.Enabled,
|
||||
PaymentMinAmount: paymentCfg.MinAmount,
|
||||
PaymentMaxAmount: paymentCfg.MaxAmount,
|
||||
PaymentDailyLimit: paymentCfg.DailyLimit,
|
||||
PaymentOrderTimeoutMin: paymentCfg.OrderTimeoutMin,
|
||||
PaymentMaxPendingOrders: paymentCfg.MaxPendingOrders,
|
||||
PaymentEnabledTypes: paymentCfg.EnabledTypes,
|
||||
PaymentBalanceDisabled: paymentCfg.BalanceDisabled,
|
||||
PaymentBalanceRechargeMultiplier: paymentCfg.BalanceRechargeMultiplier,
|
||||
PaymentRechargeFeeRate: paymentCfg.RechargeFeeRate,
|
||||
PaymentLoadBalanceStrat: paymentCfg.LoadBalanceStrategy,
|
||||
PaymentProductNamePrefix: paymentCfg.ProductNamePrefix,
|
||||
PaymentProductNameSuffix: paymentCfg.ProductNameSuffix,
|
||||
PaymentHelpImageURL: paymentCfg.HelpImageURL,
|
||||
PaymentHelpText: paymentCfg.HelpText,
|
||||
PaymentCancelRateLimitEnabled: paymentCfg.CancelRateLimitEnabled,
|
||||
PaymentCancelRateLimitMax: paymentCfg.CancelRateLimitMax,
|
||||
PaymentCancelRateLimitWindow: paymentCfg.CancelRateLimitWindow,
|
||||
PaymentCancelRateLimitUnit: paymentCfg.CancelRateLimitUnit,
|
||||
PaymentCancelRateLimitMode: paymentCfg.CancelRateLimitMode,
|
||||
RegistrationEnabled: settings.RegistrationEnabled,
|
||||
EmailVerifyEnabled: settings.EmailVerifyEnabled,
|
||||
RegistrationEmailSuffixWhitelist: settings.RegistrationEmailSuffixWhitelist,
|
||||
PromoCodeEnabled: settings.PromoCodeEnabled,
|
||||
PasswordResetEnabled: settings.PasswordResetEnabled,
|
||||
FrontendURL: settings.FrontendURL,
|
||||
InvitationCodeEnabled: settings.InvitationCodeEnabled,
|
||||
TotpEnabled: settings.TotpEnabled,
|
||||
TotpEncryptionKeyConfigured: h.settingService.IsTotpEncryptionKeyConfigured(),
|
||||
SMTPHost: settings.SMTPHost,
|
||||
SMTPPort: settings.SMTPPort,
|
||||
SMTPUsername: settings.SMTPUsername,
|
||||
SMTPPasswordConfigured: settings.SMTPPasswordConfigured,
|
||||
SMTPFrom: settings.SMTPFrom,
|
||||
SMTPFromName: settings.SMTPFromName,
|
||||
SMTPUseTLS: settings.SMTPUseTLS,
|
||||
TurnstileEnabled: settings.TurnstileEnabled,
|
||||
TurnstileSiteKey: settings.TurnstileSiteKey,
|
||||
TurnstileSecretKeyConfigured: settings.TurnstileSecretKeyConfigured,
|
||||
LinuxDoConnectEnabled: settings.LinuxDoConnectEnabled,
|
||||
LinuxDoConnectClientID: settings.LinuxDoConnectClientID,
|
||||
LinuxDoConnectClientSecretConfigured: settings.LinuxDoConnectClientSecretConfigured,
|
||||
LinuxDoConnectRedirectURL: settings.LinuxDoConnectRedirectURL,
|
||||
WeChatConnectEnabled: settings.WeChatConnectEnabled,
|
||||
WeChatConnectAppID: settings.WeChatConnectAppID,
|
||||
WeChatConnectAppSecretConfigured: settings.WeChatConnectAppSecretConfigured,
|
||||
WeChatConnectOpenAppID: settings.WeChatConnectOpenAppID,
|
||||
WeChatConnectOpenAppSecretConfigured: settings.WeChatConnectOpenAppSecretConfigured,
|
||||
WeChatConnectMPAppID: settings.WeChatConnectMPAppID,
|
||||
WeChatConnectMPAppSecretConfigured: settings.WeChatConnectMPAppSecretConfigured,
|
||||
WeChatConnectMobileAppID: settings.WeChatConnectMobileAppID,
|
||||
WeChatConnectMobileAppSecretConfigured: settings.WeChatConnectMobileAppSecretConfigured,
|
||||
WeChatConnectOpenEnabled: settings.WeChatConnectOpenEnabled,
|
||||
WeChatConnectMPEnabled: settings.WeChatConnectMPEnabled,
|
||||
WeChatConnectMobileEnabled: settings.WeChatConnectMobileEnabled,
|
||||
WeChatConnectMode: settings.WeChatConnectMode,
|
||||
WeChatConnectScopes: settings.WeChatConnectScopes,
|
||||
WeChatConnectRedirectURL: settings.WeChatConnectRedirectURL,
|
||||
WeChatConnectFrontendRedirectURL: settings.WeChatConnectFrontendRedirectURL,
|
||||
OIDCConnectEnabled: settings.OIDCConnectEnabled,
|
||||
OIDCConnectProviderName: settings.OIDCConnectProviderName,
|
||||
OIDCConnectClientID: settings.OIDCConnectClientID,
|
||||
OIDCConnectClientSecretConfigured: settings.OIDCConnectClientSecretConfigured,
|
||||
OIDCConnectIssuerURL: settings.OIDCConnectIssuerURL,
|
||||
OIDCConnectDiscoveryURL: settings.OIDCConnectDiscoveryURL,
|
||||
OIDCConnectAuthorizeURL: settings.OIDCConnectAuthorizeURL,
|
||||
OIDCConnectTokenURL: settings.OIDCConnectTokenURL,
|
||||
OIDCConnectUserInfoURL: settings.OIDCConnectUserInfoURL,
|
||||
OIDCConnectJWKSURL: settings.OIDCConnectJWKSURL,
|
||||
OIDCConnectScopes: settings.OIDCConnectScopes,
|
||||
OIDCConnectRedirectURL: settings.OIDCConnectRedirectURL,
|
||||
OIDCConnectFrontendRedirectURL: settings.OIDCConnectFrontendRedirectURL,
|
||||
OIDCConnectTokenAuthMethod: settings.OIDCConnectTokenAuthMethod,
|
||||
OIDCConnectUsePKCE: settings.OIDCConnectUsePKCE,
|
||||
OIDCConnectValidateIDToken: settings.OIDCConnectValidateIDToken,
|
||||
OIDCConnectAllowedSigningAlgs: settings.OIDCConnectAllowedSigningAlgs,
|
||||
OIDCConnectClockSkewSeconds: settings.OIDCConnectClockSkewSeconds,
|
||||
OIDCConnectRequireEmailVerified: settings.OIDCConnectRequireEmailVerified,
|
||||
OIDCConnectUserInfoEmailPath: settings.OIDCConnectUserInfoEmailPath,
|
||||
OIDCConnectUserInfoIDPath: settings.OIDCConnectUserInfoIDPath,
|
||||
OIDCConnectUserInfoUsernamePath: settings.OIDCConnectUserInfoUsernamePath,
|
||||
SiteName: settings.SiteName,
|
||||
SiteLogo: settings.SiteLogo,
|
||||
SiteSubtitle: settings.SiteSubtitle,
|
||||
APIBaseURL: settings.APIBaseURL,
|
||||
ContactInfo: settings.ContactInfo,
|
||||
DocURL: settings.DocURL,
|
||||
HomeContent: settings.HomeContent,
|
||||
HideCcsImportButton: settings.HideCcsImportButton,
|
||||
PurchaseSubscriptionEnabled: settings.PurchaseSubscriptionEnabled,
|
||||
PurchaseSubscriptionURL: settings.PurchaseSubscriptionURL,
|
||||
TableDefaultPageSize: settings.TableDefaultPageSize,
|
||||
TablePageSizeOptions: settings.TablePageSizeOptions,
|
||||
CustomMenuItems: dto.ParseCustomMenuItems(settings.CustomMenuItems),
|
||||
CustomEndpoints: dto.ParseCustomEndpoints(settings.CustomEndpoints),
|
||||
DefaultConcurrency: settings.DefaultConcurrency,
|
||||
DefaultBalance: settings.DefaultBalance,
|
||||
DefaultSubscriptions: defaultSubscriptions,
|
||||
EnableModelFallback: settings.EnableModelFallback,
|
||||
FallbackModelAnthropic: settings.FallbackModelAnthropic,
|
||||
FallbackModelOpenAI: settings.FallbackModelOpenAI,
|
||||
FallbackModelGemini: settings.FallbackModelGemini,
|
||||
FallbackModelAntigravity: settings.FallbackModelAntigravity,
|
||||
EnableIdentityPatch: settings.EnableIdentityPatch,
|
||||
IdentityPatchPrompt: settings.IdentityPatchPrompt,
|
||||
OpsMonitoringEnabled: opsEnabled && settings.OpsMonitoringEnabled,
|
||||
OpsRealtimeMonitoringEnabled: settings.OpsRealtimeMonitoringEnabled,
|
||||
OpsQueryModeDefault: settings.OpsQueryModeDefault,
|
||||
OpsMetricsIntervalSeconds: settings.OpsMetricsIntervalSeconds,
|
||||
MinClaudeCodeVersion: settings.MinClaudeCodeVersion,
|
||||
MaxClaudeCodeVersion: settings.MaxClaudeCodeVersion,
|
||||
AllowUngroupedKeyScheduling: settings.AllowUngroupedKeyScheduling,
|
||||
BackendModeEnabled: settings.BackendModeEnabled,
|
||||
EnableFingerprintUnification: settings.EnableFingerprintUnification,
|
||||
EnableMetadataPassthrough: settings.EnableMetadataPassthrough,
|
||||
EnableCCHSigning: settings.EnableCCHSigning,
|
||||
WebSearchEmulationEnabled: settings.WebSearchEmulationEnabled,
|
||||
PaymentVisibleMethodAlipaySource: settings.PaymentVisibleMethodAlipaySource,
|
||||
PaymentVisibleMethodWxpaySource: settings.PaymentVisibleMethodWxpaySource,
|
||||
PaymentVisibleMethodAlipayEnabled: settings.PaymentVisibleMethodAlipayEnabled,
|
||||
PaymentVisibleMethodWxpayEnabled: settings.PaymentVisibleMethodWxpayEnabled,
|
||||
OpenAIAdvancedSchedulerEnabled: settings.OpenAIAdvancedSchedulerEnabled,
|
||||
BalanceLowNotifyEnabled: settings.BalanceLowNotifyEnabled,
|
||||
BalanceLowNotifyThreshold: settings.BalanceLowNotifyThreshold,
|
||||
BalanceLowNotifyRechargeURL: settings.BalanceLowNotifyRechargeURL,
|
||||
AccountQuotaNotifyEnabled: settings.AccountQuotaNotifyEnabled,
|
||||
AccountQuotaNotifyEmails: dto.NotifyEmailEntriesFromService(settings.AccountQuotaNotifyEmails),
|
||||
PaymentEnabled: paymentCfg.Enabled,
|
||||
PaymentMinAmount: paymentCfg.MinAmount,
|
||||
PaymentMaxAmount: paymentCfg.MaxAmount,
|
||||
PaymentDailyLimit: paymentCfg.DailyLimit,
|
||||
PaymentOrderTimeoutMin: paymentCfg.OrderTimeoutMin,
|
||||
PaymentMaxPendingOrders: paymentCfg.MaxPendingOrders,
|
||||
PaymentEnabledTypes: paymentCfg.EnabledTypes,
|
||||
PaymentBalanceDisabled: paymentCfg.BalanceDisabled,
|
||||
PaymentBalanceRechargeMultiplier: paymentCfg.BalanceRechargeMultiplier,
|
||||
PaymentRechargeFeeRate: paymentCfg.RechargeFeeRate,
|
||||
PaymentLoadBalanceStrat: paymentCfg.LoadBalanceStrategy,
|
||||
PaymentProductNamePrefix: paymentCfg.ProductNamePrefix,
|
||||
PaymentProductNameSuffix: paymentCfg.ProductNameSuffix,
|
||||
PaymentHelpImageURL: paymentCfg.HelpImageURL,
|
||||
PaymentHelpText: paymentCfg.HelpText,
|
||||
PaymentCancelRateLimitEnabled: paymentCfg.CancelRateLimitEnabled,
|
||||
PaymentCancelRateLimitMax: paymentCfg.CancelRateLimitMax,
|
||||
PaymentCancelRateLimitWindow: paymentCfg.CancelRateLimitWindow,
|
||||
PaymentCancelRateLimitUnit: paymentCfg.CancelRateLimitUnit,
|
||||
PaymentCancelRateLimitMode: paymentCfg.CancelRateLimitMode,
|
||||
}
|
||||
response.Success(c, systemSettingsResponseData(payload, authSourceDefaults))
|
||||
}
|
||||
@@ -259,8 +275,15 @@ type UpdateSettingsRequest struct {
|
||||
WeChatConnectEnabled bool `json:"wechat_connect_enabled"`
|
||||
WeChatConnectAppID string `json:"wechat_connect_app_id"`
|
||||
WeChatConnectAppSecret string `json:"wechat_connect_app_secret"`
|
||||
WeChatConnectOpenAppID string `json:"wechat_connect_open_app_id"`
|
||||
WeChatConnectOpenAppSecret string `json:"wechat_connect_open_app_secret"`
|
||||
WeChatConnectMPAppID string `json:"wechat_connect_mp_app_id"`
|
||||
WeChatConnectMPAppSecret string `json:"wechat_connect_mp_app_secret"`
|
||||
WeChatConnectMobileAppID string `json:"wechat_connect_mobile_app_id"`
|
||||
WeChatConnectMobileAppSecret string `json:"wechat_connect_mobile_app_secret"`
|
||||
WeChatConnectOpenEnabled bool `json:"wechat_connect_open_enabled"`
|
||||
WeChatConnectMPEnabled bool `json:"wechat_connect_mp_enabled"`
|
||||
WeChatConnectMobileEnabled bool `json:"wechat_connect_mobile_enabled"`
|
||||
WeChatConnectMode string `json:"wechat_connect_mode"`
|
||||
WeChatConnectScopes string `json:"wechat_connect_scopes"`
|
||||
WeChatConnectRedirectURL string `json:"wechat_connect_redirect_url"`
|
||||
@@ -532,34 +555,35 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
|
||||
if req.WeChatConnectEnabled {
|
||||
req.WeChatConnectAppID = strings.TrimSpace(req.WeChatConnectAppID)
|
||||
req.WeChatConnectAppSecret = strings.TrimSpace(req.WeChatConnectAppSecret)
|
||||
req.WeChatConnectOpenAppID = strings.TrimSpace(req.WeChatConnectOpenAppID)
|
||||
req.WeChatConnectOpenAppSecret = strings.TrimSpace(req.WeChatConnectOpenAppSecret)
|
||||
req.WeChatConnectMPAppID = strings.TrimSpace(req.WeChatConnectMPAppID)
|
||||
req.WeChatConnectMPAppSecret = strings.TrimSpace(req.WeChatConnectMPAppSecret)
|
||||
req.WeChatConnectMobileAppID = strings.TrimSpace(req.WeChatConnectMobileAppID)
|
||||
req.WeChatConnectMobileAppSecret = strings.TrimSpace(req.WeChatConnectMobileAppSecret)
|
||||
req.WeChatConnectMode = strings.ToLower(strings.TrimSpace(req.WeChatConnectMode))
|
||||
req.WeChatConnectScopes = strings.TrimSpace(req.WeChatConnectScopes)
|
||||
req.WeChatConnectRedirectURL = strings.TrimSpace(req.WeChatConnectRedirectURL)
|
||||
req.WeChatConnectFrontendRedirectURL = strings.TrimSpace(req.WeChatConnectFrontendRedirectURL)
|
||||
|
||||
if req.WeChatConnectAppID == "" {
|
||||
response.BadRequest(c, "WeChat App ID is required when enabled")
|
||||
if req.WeChatConnectMPEnabled && req.WeChatConnectMobileEnabled {
|
||||
response.BadRequest(c, "WeChat Official Account and Mobile App cannot be enabled at the same time")
|
||||
return
|
||||
}
|
||||
if req.WeChatConnectAppSecret == "" {
|
||||
if previousSettings.WeChatConnectAppSecret == "" {
|
||||
response.BadRequest(c, "WeChat App Secret is required when enabled")
|
||||
return
|
||||
}
|
||||
req.WeChatConnectAppSecret = previousSettings.WeChatConnectAppSecret
|
||||
}
|
||||
if req.WeChatConnectMode != "" {
|
||||
switch req.WeChatConnectMode {
|
||||
case "open", "mp":
|
||||
case "open", "mp", "mobile":
|
||||
default:
|
||||
response.BadRequest(c, "WeChat mode must be open or mp")
|
||||
response.BadRequest(c, "WeChat mode must be open, mp, or mobile")
|
||||
return
|
||||
}
|
||||
}
|
||||
if !req.WeChatConnectOpenEnabled && !req.WeChatConnectMPEnabled {
|
||||
if !req.WeChatConnectOpenEnabled && !req.WeChatConnectMPEnabled && !req.WeChatConnectMobileEnabled {
|
||||
switch req.WeChatConnectMode {
|
||||
case "mp":
|
||||
req.WeChatConnectMPEnabled = true
|
||||
case "mobile":
|
||||
req.WeChatConnectMobileEnabled = true
|
||||
default:
|
||||
req.WeChatConnectOpenEnabled = true
|
||||
}
|
||||
@@ -567,10 +591,61 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
|
||||
if req.WeChatConnectMode == "" {
|
||||
if req.WeChatConnectMPEnabled {
|
||||
req.WeChatConnectMode = "mp"
|
||||
} else if req.WeChatConnectMobileEnabled {
|
||||
req.WeChatConnectMode = "mobile"
|
||||
} else {
|
||||
req.WeChatConnectMode = "open"
|
||||
}
|
||||
}
|
||||
|
||||
req.WeChatConnectOpenAppID = strings.TrimSpace(firstNonEmpty(req.WeChatConnectOpenAppID, req.WeChatConnectAppID))
|
||||
req.WeChatConnectMPAppID = strings.TrimSpace(firstNonEmpty(req.WeChatConnectMPAppID, req.WeChatConnectAppID))
|
||||
req.WeChatConnectMobileAppID = strings.TrimSpace(firstNonEmpty(req.WeChatConnectMobileAppID, req.WeChatConnectAppID))
|
||||
|
||||
if req.WeChatConnectOpenAppSecret == "" {
|
||||
req.WeChatConnectOpenAppSecret = strings.TrimSpace(firstNonEmpty(previousSettings.WeChatConnectOpenAppSecret, previousSettings.WeChatConnectAppSecret, req.WeChatConnectAppSecret))
|
||||
}
|
||||
if req.WeChatConnectMPAppSecret == "" {
|
||||
req.WeChatConnectMPAppSecret = strings.TrimSpace(firstNonEmpty(previousSettings.WeChatConnectMPAppSecret, previousSettings.WeChatConnectAppSecret, req.WeChatConnectAppSecret))
|
||||
}
|
||||
if req.WeChatConnectMobileAppSecret == "" {
|
||||
req.WeChatConnectMobileAppSecret = strings.TrimSpace(firstNonEmpty(previousSettings.WeChatConnectMobileAppSecret, previousSettings.WeChatConnectAppSecret, req.WeChatConnectAppSecret))
|
||||
}
|
||||
if req.WeChatConnectAppSecret == "" {
|
||||
req.WeChatConnectAppSecret = strings.TrimSpace(firstNonEmpty(req.WeChatConnectOpenAppSecret, req.WeChatConnectMPAppSecret, req.WeChatConnectMobileAppSecret, previousSettings.WeChatConnectAppSecret))
|
||||
}
|
||||
|
||||
if req.WeChatConnectOpenEnabled {
|
||||
if req.WeChatConnectOpenAppID == "" {
|
||||
response.BadRequest(c, "WeChat PC App ID is required when enabled")
|
||||
return
|
||||
}
|
||||
if req.WeChatConnectOpenAppSecret == "" {
|
||||
response.BadRequest(c, "WeChat PC App Secret is required when enabled")
|
||||
return
|
||||
}
|
||||
}
|
||||
if req.WeChatConnectMPEnabled {
|
||||
if req.WeChatConnectMPAppID == "" {
|
||||
response.BadRequest(c, "WeChat Official Account App ID is required when enabled")
|
||||
return
|
||||
}
|
||||
if req.WeChatConnectMPAppSecret == "" {
|
||||
response.BadRequest(c, "WeChat Official Account App Secret is required when enabled")
|
||||
return
|
||||
}
|
||||
}
|
||||
if req.WeChatConnectMobileEnabled {
|
||||
if req.WeChatConnectMobileAppID == "" {
|
||||
response.BadRequest(c, "WeChat Mobile App ID is required when enabled")
|
||||
return
|
||||
}
|
||||
if req.WeChatConnectMobileAppSecret == "" {
|
||||
response.BadRequest(c, "WeChat Mobile App Secret is required when enabled")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if req.WeChatConnectScopes == "" {
|
||||
if req.WeChatConnectMPEnabled {
|
||||
req.WeChatConnectScopes = service.DefaultWeChatConnectScopesForMode("mp")
|
||||
@@ -946,8 +1021,15 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
|
||||
WeChatConnectEnabled: req.WeChatConnectEnabled,
|
||||
WeChatConnectAppID: req.WeChatConnectAppID,
|
||||
WeChatConnectAppSecret: req.WeChatConnectAppSecret,
|
||||
WeChatConnectOpenAppID: req.WeChatConnectOpenAppID,
|
||||
WeChatConnectOpenAppSecret: req.WeChatConnectOpenAppSecret,
|
||||
WeChatConnectMPAppID: req.WeChatConnectMPAppID,
|
||||
WeChatConnectMPAppSecret: req.WeChatConnectMPAppSecret,
|
||||
WeChatConnectMobileAppID: req.WeChatConnectMobileAppID,
|
||||
WeChatConnectMobileAppSecret: req.WeChatConnectMobileAppSecret,
|
||||
WeChatConnectOpenEnabled: req.WeChatConnectOpenEnabled,
|
||||
WeChatConnectMPEnabled: req.WeChatConnectMPEnabled,
|
||||
WeChatConnectMobileEnabled: req.WeChatConnectMobileEnabled,
|
||||
WeChatConnectMode: req.WeChatConnectMode,
|
||||
WeChatConnectScopes: req.WeChatConnectScopes,
|
||||
WeChatConnectRedirectURL: req.WeChatConnectRedirectURL,
|
||||
@@ -1208,125 +1290,132 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
|
||||
}
|
||||
|
||||
payload := dto.SystemSettings{
|
||||
RegistrationEnabled: updatedSettings.RegistrationEnabled,
|
||||
EmailVerifyEnabled: updatedSettings.EmailVerifyEnabled,
|
||||
RegistrationEmailSuffixWhitelist: updatedSettings.RegistrationEmailSuffixWhitelist,
|
||||
PromoCodeEnabled: updatedSettings.PromoCodeEnabled,
|
||||
PasswordResetEnabled: updatedSettings.PasswordResetEnabled,
|
||||
FrontendURL: updatedSettings.FrontendURL,
|
||||
InvitationCodeEnabled: updatedSettings.InvitationCodeEnabled,
|
||||
TotpEnabled: updatedSettings.TotpEnabled,
|
||||
TotpEncryptionKeyConfigured: h.settingService.IsTotpEncryptionKeyConfigured(),
|
||||
SMTPHost: updatedSettings.SMTPHost,
|
||||
SMTPPort: updatedSettings.SMTPPort,
|
||||
SMTPUsername: updatedSettings.SMTPUsername,
|
||||
SMTPPasswordConfigured: updatedSettings.SMTPPasswordConfigured,
|
||||
SMTPFrom: updatedSettings.SMTPFrom,
|
||||
SMTPFromName: updatedSettings.SMTPFromName,
|
||||
SMTPUseTLS: updatedSettings.SMTPUseTLS,
|
||||
TurnstileEnabled: updatedSettings.TurnstileEnabled,
|
||||
TurnstileSiteKey: updatedSettings.TurnstileSiteKey,
|
||||
TurnstileSecretKeyConfigured: updatedSettings.TurnstileSecretKeyConfigured,
|
||||
LinuxDoConnectEnabled: updatedSettings.LinuxDoConnectEnabled,
|
||||
LinuxDoConnectClientID: updatedSettings.LinuxDoConnectClientID,
|
||||
LinuxDoConnectClientSecretConfigured: updatedSettings.LinuxDoConnectClientSecretConfigured,
|
||||
LinuxDoConnectRedirectURL: updatedSettings.LinuxDoConnectRedirectURL,
|
||||
WeChatConnectEnabled: updatedSettings.WeChatConnectEnabled,
|
||||
WeChatConnectAppID: updatedSettings.WeChatConnectAppID,
|
||||
WeChatConnectAppSecretConfigured: updatedSettings.WeChatConnectAppSecretConfigured,
|
||||
WeChatConnectOpenEnabled: updatedSettings.WeChatConnectOpenEnabled,
|
||||
WeChatConnectMPEnabled: updatedSettings.WeChatConnectMPEnabled,
|
||||
WeChatConnectMode: updatedSettings.WeChatConnectMode,
|
||||
WeChatConnectScopes: updatedSettings.WeChatConnectScopes,
|
||||
WeChatConnectRedirectURL: updatedSettings.WeChatConnectRedirectURL,
|
||||
WeChatConnectFrontendRedirectURL: updatedSettings.WeChatConnectFrontendRedirectURL,
|
||||
OIDCConnectEnabled: updatedSettings.OIDCConnectEnabled,
|
||||
OIDCConnectProviderName: updatedSettings.OIDCConnectProviderName,
|
||||
OIDCConnectClientID: updatedSettings.OIDCConnectClientID,
|
||||
OIDCConnectClientSecretConfigured: updatedSettings.OIDCConnectClientSecretConfigured,
|
||||
OIDCConnectIssuerURL: updatedSettings.OIDCConnectIssuerURL,
|
||||
OIDCConnectDiscoveryURL: updatedSettings.OIDCConnectDiscoveryURL,
|
||||
OIDCConnectAuthorizeURL: updatedSettings.OIDCConnectAuthorizeURL,
|
||||
OIDCConnectTokenURL: updatedSettings.OIDCConnectTokenURL,
|
||||
OIDCConnectUserInfoURL: updatedSettings.OIDCConnectUserInfoURL,
|
||||
OIDCConnectJWKSURL: updatedSettings.OIDCConnectJWKSURL,
|
||||
OIDCConnectScopes: updatedSettings.OIDCConnectScopes,
|
||||
OIDCConnectRedirectURL: updatedSettings.OIDCConnectRedirectURL,
|
||||
OIDCConnectFrontendRedirectURL: updatedSettings.OIDCConnectFrontendRedirectURL,
|
||||
OIDCConnectTokenAuthMethod: updatedSettings.OIDCConnectTokenAuthMethod,
|
||||
OIDCConnectUsePKCE: updatedSettings.OIDCConnectUsePKCE,
|
||||
OIDCConnectValidateIDToken: updatedSettings.OIDCConnectValidateIDToken,
|
||||
OIDCConnectAllowedSigningAlgs: updatedSettings.OIDCConnectAllowedSigningAlgs,
|
||||
OIDCConnectClockSkewSeconds: updatedSettings.OIDCConnectClockSkewSeconds,
|
||||
OIDCConnectRequireEmailVerified: updatedSettings.OIDCConnectRequireEmailVerified,
|
||||
OIDCConnectUserInfoEmailPath: updatedSettings.OIDCConnectUserInfoEmailPath,
|
||||
OIDCConnectUserInfoIDPath: updatedSettings.OIDCConnectUserInfoIDPath,
|
||||
OIDCConnectUserInfoUsernamePath: updatedSettings.OIDCConnectUserInfoUsernamePath,
|
||||
SiteName: updatedSettings.SiteName,
|
||||
SiteLogo: updatedSettings.SiteLogo,
|
||||
SiteSubtitle: updatedSettings.SiteSubtitle,
|
||||
APIBaseURL: updatedSettings.APIBaseURL,
|
||||
ContactInfo: updatedSettings.ContactInfo,
|
||||
DocURL: updatedSettings.DocURL,
|
||||
HomeContent: updatedSettings.HomeContent,
|
||||
HideCcsImportButton: updatedSettings.HideCcsImportButton,
|
||||
PurchaseSubscriptionEnabled: updatedSettings.PurchaseSubscriptionEnabled,
|
||||
PurchaseSubscriptionURL: updatedSettings.PurchaseSubscriptionURL,
|
||||
TableDefaultPageSize: updatedSettings.TableDefaultPageSize,
|
||||
TablePageSizeOptions: updatedSettings.TablePageSizeOptions,
|
||||
CustomMenuItems: dto.ParseCustomMenuItems(updatedSettings.CustomMenuItems),
|
||||
CustomEndpoints: dto.ParseCustomEndpoints(updatedSettings.CustomEndpoints),
|
||||
DefaultConcurrency: updatedSettings.DefaultConcurrency,
|
||||
DefaultBalance: updatedSettings.DefaultBalance,
|
||||
DefaultSubscriptions: updatedDefaultSubscriptions,
|
||||
EnableModelFallback: updatedSettings.EnableModelFallback,
|
||||
FallbackModelAnthropic: updatedSettings.FallbackModelAnthropic,
|
||||
FallbackModelOpenAI: updatedSettings.FallbackModelOpenAI,
|
||||
FallbackModelGemini: updatedSettings.FallbackModelGemini,
|
||||
FallbackModelAntigravity: updatedSettings.FallbackModelAntigravity,
|
||||
EnableIdentityPatch: updatedSettings.EnableIdentityPatch,
|
||||
IdentityPatchPrompt: updatedSettings.IdentityPatchPrompt,
|
||||
OpsMonitoringEnabled: updatedSettings.OpsMonitoringEnabled,
|
||||
OpsRealtimeMonitoringEnabled: updatedSettings.OpsRealtimeMonitoringEnabled,
|
||||
OpsQueryModeDefault: updatedSettings.OpsQueryModeDefault,
|
||||
OpsMetricsIntervalSeconds: updatedSettings.OpsMetricsIntervalSeconds,
|
||||
MinClaudeCodeVersion: updatedSettings.MinClaudeCodeVersion,
|
||||
MaxClaudeCodeVersion: updatedSettings.MaxClaudeCodeVersion,
|
||||
AllowUngroupedKeyScheduling: updatedSettings.AllowUngroupedKeyScheduling,
|
||||
BackendModeEnabled: updatedSettings.BackendModeEnabled,
|
||||
EnableFingerprintUnification: updatedSettings.EnableFingerprintUnification,
|
||||
EnableMetadataPassthrough: updatedSettings.EnableMetadataPassthrough,
|
||||
EnableCCHSigning: updatedSettings.EnableCCHSigning,
|
||||
PaymentVisibleMethodAlipaySource: updatedSettings.PaymentVisibleMethodAlipaySource,
|
||||
PaymentVisibleMethodWxpaySource: updatedSettings.PaymentVisibleMethodWxpaySource,
|
||||
PaymentVisibleMethodAlipayEnabled: updatedSettings.PaymentVisibleMethodAlipayEnabled,
|
||||
PaymentVisibleMethodWxpayEnabled: updatedSettings.PaymentVisibleMethodWxpayEnabled,
|
||||
OpenAIAdvancedSchedulerEnabled: updatedSettings.OpenAIAdvancedSchedulerEnabled,
|
||||
BalanceLowNotifyEnabled: updatedSettings.BalanceLowNotifyEnabled,
|
||||
BalanceLowNotifyThreshold: updatedSettings.BalanceLowNotifyThreshold,
|
||||
BalanceLowNotifyRechargeURL: updatedSettings.BalanceLowNotifyRechargeURL,
|
||||
AccountQuotaNotifyEnabled: updatedSettings.AccountQuotaNotifyEnabled,
|
||||
AccountQuotaNotifyEmails: dto.NotifyEmailEntriesFromService(updatedSettings.AccountQuotaNotifyEmails),
|
||||
PaymentEnabled: updatedPaymentCfg.Enabled,
|
||||
PaymentMinAmount: updatedPaymentCfg.MinAmount,
|
||||
PaymentMaxAmount: updatedPaymentCfg.MaxAmount,
|
||||
PaymentDailyLimit: updatedPaymentCfg.DailyLimit,
|
||||
PaymentOrderTimeoutMin: updatedPaymentCfg.OrderTimeoutMin,
|
||||
PaymentMaxPendingOrders: updatedPaymentCfg.MaxPendingOrders,
|
||||
PaymentEnabledTypes: updatedPaymentCfg.EnabledTypes,
|
||||
PaymentBalanceDisabled: updatedPaymentCfg.BalanceDisabled,
|
||||
PaymentBalanceRechargeMultiplier: updatedPaymentCfg.BalanceRechargeMultiplier,
|
||||
PaymentRechargeFeeRate: updatedPaymentCfg.RechargeFeeRate,
|
||||
PaymentLoadBalanceStrat: updatedPaymentCfg.LoadBalanceStrategy,
|
||||
PaymentProductNamePrefix: updatedPaymentCfg.ProductNamePrefix,
|
||||
PaymentProductNameSuffix: updatedPaymentCfg.ProductNameSuffix,
|
||||
PaymentHelpImageURL: updatedPaymentCfg.HelpImageURL,
|
||||
PaymentHelpText: updatedPaymentCfg.HelpText,
|
||||
PaymentCancelRateLimitEnabled: updatedPaymentCfg.CancelRateLimitEnabled,
|
||||
PaymentCancelRateLimitMax: updatedPaymentCfg.CancelRateLimitMax,
|
||||
PaymentCancelRateLimitWindow: updatedPaymentCfg.CancelRateLimitWindow,
|
||||
PaymentCancelRateLimitUnit: updatedPaymentCfg.CancelRateLimitUnit,
|
||||
PaymentCancelRateLimitMode: updatedPaymentCfg.CancelRateLimitMode,
|
||||
RegistrationEnabled: updatedSettings.RegistrationEnabled,
|
||||
EmailVerifyEnabled: updatedSettings.EmailVerifyEnabled,
|
||||
RegistrationEmailSuffixWhitelist: updatedSettings.RegistrationEmailSuffixWhitelist,
|
||||
PromoCodeEnabled: updatedSettings.PromoCodeEnabled,
|
||||
PasswordResetEnabled: updatedSettings.PasswordResetEnabled,
|
||||
FrontendURL: updatedSettings.FrontendURL,
|
||||
InvitationCodeEnabled: updatedSettings.InvitationCodeEnabled,
|
||||
TotpEnabled: updatedSettings.TotpEnabled,
|
||||
TotpEncryptionKeyConfigured: h.settingService.IsTotpEncryptionKeyConfigured(),
|
||||
SMTPHost: updatedSettings.SMTPHost,
|
||||
SMTPPort: updatedSettings.SMTPPort,
|
||||
SMTPUsername: updatedSettings.SMTPUsername,
|
||||
SMTPPasswordConfigured: updatedSettings.SMTPPasswordConfigured,
|
||||
SMTPFrom: updatedSettings.SMTPFrom,
|
||||
SMTPFromName: updatedSettings.SMTPFromName,
|
||||
SMTPUseTLS: updatedSettings.SMTPUseTLS,
|
||||
TurnstileEnabled: updatedSettings.TurnstileEnabled,
|
||||
TurnstileSiteKey: updatedSettings.TurnstileSiteKey,
|
||||
TurnstileSecretKeyConfigured: updatedSettings.TurnstileSecretKeyConfigured,
|
||||
LinuxDoConnectEnabled: updatedSettings.LinuxDoConnectEnabled,
|
||||
LinuxDoConnectClientID: updatedSettings.LinuxDoConnectClientID,
|
||||
LinuxDoConnectClientSecretConfigured: updatedSettings.LinuxDoConnectClientSecretConfigured,
|
||||
LinuxDoConnectRedirectURL: updatedSettings.LinuxDoConnectRedirectURL,
|
||||
WeChatConnectEnabled: updatedSettings.WeChatConnectEnabled,
|
||||
WeChatConnectAppID: updatedSettings.WeChatConnectAppID,
|
||||
WeChatConnectAppSecretConfigured: updatedSettings.WeChatConnectAppSecretConfigured,
|
||||
WeChatConnectOpenAppID: updatedSettings.WeChatConnectOpenAppID,
|
||||
WeChatConnectOpenAppSecretConfigured: updatedSettings.WeChatConnectOpenAppSecretConfigured,
|
||||
WeChatConnectMPAppID: updatedSettings.WeChatConnectMPAppID,
|
||||
WeChatConnectMPAppSecretConfigured: updatedSettings.WeChatConnectMPAppSecretConfigured,
|
||||
WeChatConnectMobileAppID: updatedSettings.WeChatConnectMobileAppID,
|
||||
WeChatConnectMobileAppSecretConfigured: updatedSettings.WeChatConnectMobileAppSecretConfigured,
|
||||
WeChatConnectOpenEnabled: updatedSettings.WeChatConnectOpenEnabled,
|
||||
WeChatConnectMPEnabled: updatedSettings.WeChatConnectMPEnabled,
|
||||
WeChatConnectMobileEnabled: updatedSettings.WeChatConnectMobileEnabled,
|
||||
WeChatConnectMode: updatedSettings.WeChatConnectMode,
|
||||
WeChatConnectScopes: updatedSettings.WeChatConnectScopes,
|
||||
WeChatConnectRedirectURL: updatedSettings.WeChatConnectRedirectURL,
|
||||
WeChatConnectFrontendRedirectURL: updatedSettings.WeChatConnectFrontendRedirectURL,
|
||||
OIDCConnectEnabled: updatedSettings.OIDCConnectEnabled,
|
||||
OIDCConnectProviderName: updatedSettings.OIDCConnectProviderName,
|
||||
OIDCConnectClientID: updatedSettings.OIDCConnectClientID,
|
||||
OIDCConnectClientSecretConfigured: updatedSettings.OIDCConnectClientSecretConfigured,
|
||||
OIDCConnectIssuerURL: updatedSettings.OIDCConnectIssuerURL,
|
||||
OIDCConnectDiscoveryURL: updatedSettings.OIDCConnectDiscoveryURL,
|
||||
OIDCConnectAuthorizeURL: updatedSettings.OIDCConnectAuthorizeURL,
|
||||
OIDCConnectTokenURL: updatedSettings.OIDCConnectTokenURL,
|
||||
OIDCConnectUserInfoURL: updatedSettings.OIDCConnectUserInfoURL,
|
||||
OIDCConnectJWKSURL: updatedSettings.OIDCConnectJWKSURL,
|
||||
OIDCConnectScopes: updatedSettings.OIDCConnectScopes,
|
||||
OIDCConnectRedirectURL: updatedSettings.OIDCConnectRedirectURL,
|
||||
OIDCConnectFrontendRedirectURL: updatedSettings.OIDCConnectFrontendRedirectURL,
|
||||
OIDCConnectTokenAuthMethod: updatedSettings.OIDCConnectTokenAuthMethod,
|
||||
OIDCConnectUsePKCE: updatedSettings.OIDCConnectUsePKCE,
|
||||
OIDCConnectValidateIDToken: updatedSettings.OIDCConnectValidateIDToken,
|
||||
OIDCConnectAllowedSigningAlgs: updatedSettings.OIDCConnectAllowedSigningAlgs,
|
||||
OIDCConnectClockSkewSeconds: updatedSettings.OIDCConnectClockSkewSeconds,
|
||||
OIDCConnectRequireEmailVerified: updatedSettings.OIDCConnectRequireEmailVerified,
|
||||
OIDCConnectUserInfoEmailPath: updatedSettings.OIDCConnectUserInfoEmailPath,
|
||||
OIDCConnectUserInfoIDPath: updatedSettings.OIDCConnectUserInfoIDPath,
|
||||
OIDCConnectUserInfoUsernamePath: updatedSettings.OIDCConnectUserInfoUsernamePath,
|
||||
SiteName: updatedSettings.SiteName,
|
||||
SiteLogo: updatedSettings.SiteLogo,
|
||||
SiteSubtitle: updatedSettings.SiteSubtitle,
|
||||
APIBaseURL: updatedSettings.APIBaseURL,
|
||||
ContactInfo: updatedSettings.ContactInfo,
|
||||
DocURL: updatedSettings.DocURL,
|
||||
HomeContent: updatedSettings.HomeContent,
|
||||
HideCcsImportButton: updatedSettings.HideCcsImportButton,
|
||||
PurchaseSubscriptionEnabled: updatedSettings.PurchaseSubscriptionEnabled,
|
||||
PurchaseSubscriptionURL: updatedSettings.PurchaseSubscriptionURL,
|
||||
TableDefaultPageSize: updatedSettings.TableDefaultPageSize,
|
||||
TablePageSizeOptions: updatedSettings.TablePageSizeOptions,
|
||||
CustomMenuItems: dto.ParseCustomMenuItems(updatedSettings.CustomMenuItems),
|
||||
CustomEndpoints: dto.ParseCustomEndpoints(updatedSettings.CustomEndpoints),
|
||||
DefaultConcurrency: updatedSettings.DefaultConcurrency,
|
||||
DefaultBalance: updatedSettings.DefaultBalance,
|
||||
DefaultSubscriptions: updatedDefaultSubscriptions,
|
||||
EnableModelFallback: updatedSettings.EnableModelFallback,
|
||||
FallbackModelAnthropic: updatedSettings.FallbackModelAnthropic,
|
||||
FallbackModelOpenAI: updatedSettings.FallbackModelOpenAI,
|
||||
FallbackModelGemini: updatedSettings.FallbackModelGemini,
|
||||
FallbackModelAntigravity: updatedSettings.FallbackModelAntigravity,
|
||||
EnableIdentityPatch: updatedSettings.EnableIdentityPatch,
|
||||
IdentityPatchPrompt: updatedSettings.IdentityPatchPrompt,
|
||||
OpsMonitoringEnabled: updatedSettings.OpsMonitoringEnabled,
|
||||
OpsRealtimeMonitoringEnabled: updatedSettings.OpsRealtimeMonitoringEnabled,
|
||||
OpsQueryModeDefault: updatedSettings.OpsQueryModeDefault,
|
||||
OpsMetricsIntervalSeconds: updatedSettings.OpsMetricsIntervalSeconds,
|
||||
MinClaudeCodeVersion: updatedSettings.MinClaudeCodeVersion,
|
||||
MaxClaudeCodeVersion: updatedSettings.MaxClaudeCodeVersion,
|
||||
AllowUngroupedKeyScheduling: updatedSettings.AllowUngroupedKeyScheduling,
|
||||
BackendModeEnabled: updatedSettings.BackendModeEnabled,
|
||||
EnableFingerprintUnification: updatedSettings.EnableFingerprintUnification,
|
||||
EnableMetadataPassthrough: updatedSettings.EnableMetadataPassthrough,
|
||||
EnableCCHSigning: updatedSettings.EnableCCHSigning,
|
||||
PaymentVisibleMethodAlipaySource: updatedSettings.PaymentVisibleMethodAlipaySource,
|
||||
PaymentVisibleMethodWxpaySource: updatedSettings.PaymentVisibleMethodWxpaySource,
|
||||
PaymentVisibleMethodAlipayEnabled: updatedSettings.PaymentVisibleMethodAlipayEnabled,
|
||||
PaymentVisibleMethodWxpayEnabled: updatedSettings.PaymentVisibleMethodWxpayEnabled,
|
||||
OpenAIAdvancedSchedulerEnabled: updatedSettings.OpenAIAdvancedSchedulerEnabled,
|
||||
BalanceLowNotifyEnabled: updatedSettings.BalanceLowNotifyEnabled,
|
||||
BalanceLowNotifyThreshold: updatedSettings.BalanceLowNotifyThreshold,
|
||||
BalanceLowNotifyRechargeURL: updatedSettings.BalanceLowNotifyRechargeURL,
|
||||
AccountQuotaNotifyEnabled: updatedSettings.AccountQuotaNotifyEnabled,
|
||||
AccountQuotaNotifyEmails: dto.NotifyEmailEntriesFromService(updatedSettings.AccountQuotaNotifyEmails),
|
||||
PaymentEnabled: updatedPaymentCfg.Enabled,
|
||||
PaymentMinAmount: updatedPaymentCfg.MinAmount,
|
||||
PaymentMaxAmount: updatedPaymentCfg.MaxAmount,
|
||||
PaymentDailyLimit: updatedPaymentCfg.DailyLimit,
|
||||
PaymentOrderTimeoutMin: updatedPaymentCfg.OrderTimeoutMin,
|
||||
PaymentMaxPendingOrders: updatedPaymentCfg.MaxPendingOrders,
|
||||
PaymentEnabledTypes: updatedPaymentCfg.EnabledTypes,
|
||||
PaymentBalanceDisabled: updatedPaymentCfg.BalanceDisabled,
|
||||
PaymentBalanceRechargeMultiplier: updatedPaymentCfg.BalanceRechargeMultiplier,
|
||||
PaymentRechargeFeeRate: updatedPaymentCfg.RechargeFeeRate,
|
||||
PaymentLoadBalanceStrat: updatedPaymentCfg.LoadBalanceStrategy,
|
||||
PaymentProductNamePrefix: updatedPaymentCfg.ProductNamePrefix,
|
||||
PaymentProductNameSuffix: updatedPaymentCfg.ProductNameSuffix,
|
||||
PaymentHelpImageURL: updatedPaymentCfg.HelpImageURL,
|
||||
PaymentHelpText: updatedPaymentCfg.HelpText,
|
||||
PaymentCancelRateLimitEnabled: updatedPaymentCfg.CancelRateLimitEnabled,
|
||||
PaymentCancelRateLimitMax: updatedPaymentCfg.CancelRateLimitMax,
|
||||
PaymentCancelRateLimitWindow: updatedPaymentCfg.CancelRateLimitWindow,
|
||||
PaymentCancelRateLimitUnit: updatedPaymentCfg.CancelRateLimitUnit,
|
||||
PaymentCancelRateLimitMode: updatedPaymentCfg.CancelRateLimitMode,
|
||||
}
|
||||
response.Success(c, systemSettingsResponseData(payload, updatedAuthSourceDefaults))
|
||||
}
|
||||
@@ -1442,12 +1531,33 @@ func diffSettings(before *service.SystemSettings, after *service.SystemSettings,
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -753,7 +753,13 @@ func (h *AuthHandler) ensureWeChatBindOwnership(
|
||||
}
|
||||
for _, identity := range identities {
|
||||
if identity != nil && identity.UserID != userID {
|
||||
return infraerrors.Conflict("AUTH_IDENTITY_OWNERSHIP_CONFLICT", "auth identity already belongs to another user")
|
||||
activeOwner, lookupErr := findActiveUserByID(ctx, client, identity.UserID)
|
||||
if lookupErr != nil {
|
||||
return lookupErr
|
||||
}
|
||||
if activeOwner != nil {
|
||||
return infraerrors.Conflict("AUTH_IDENTITY_OWNERSHIP_CONFLICT", "auth identity already belongs to another user")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -778,7 +784,13 @@ func (h *AuthHandler) ensureWeChatBindOwnership(
|
||||
}
|
||||
for _, channel := range channels {
|
||||
if channel != nil && channel.Edges.Identity != nil && channel.Edges.Identity.UserID != userID {
|
||||
return infraerrors.Conflict("AUTH_IDENTITY_CHANNEL_OWNERSHIP_CONFLICT", "auth identity channel already belongs to another user")
|
||||
activeOwner, lookupErr := findActiveUserByID(ctx, client, channel.Edges.Identity.UserID)
|
||||
if lookupErr != nil {
|
||||
return lookupErr
|
||||
}
|
||||
if activeOwner != nil {
|
||||
return infraerrors.Conflict("AUTH_IDENTITY_CHANNEL_OWNERSHIP_CONFLICT", "auth identity channel already belongs to another user")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@@ -960,8 +972,8 @@ func (h *AuthHandler) getWeChatOAuthConfig(ctx context.Context, rawMode string,
|
||||
|
||||
cfg := wechatOAuthConfig{
|
||||
mode: mode,
|
||||
appID: strings.TrimSpace(effective.AppID),
|
||||
appSecret: strings.TrimSpace(effective.AppSecret),
|
||||
appID: strings.TrimSpace(effective.AppIDForMode(mode)),
|
||||
appSecret: strings.TrimSpace(effective.AppSecretForMode(mode)),
|
||||
redirectURI: firstNonEmpty(strings.TrimSpace(effective.RedirectURL), resolveWeChatOAuthAbsoluteURL(apiBaseURL, c, "/api/v1/auth/oauth/wechat/callback")),
|
||||
frontendCallback: firstNonEmpty(strings.TrimSpace(effective.FrontendRedirectURL), wechatOAuthDefaultFrontendCB),
|
||||
scope: effective.ScopeForMode(mode),
|
||||
|
||||
@@ -54,8 +54,15 @@ type SystemSettings struct {
|
||||
WeChatConnectEnabled bool `json:"wechat_connect_enabled"`
|
||||
WeChatConnectAppID string `json:"wechat_connect_app_id"`
|
||||
WeChatConnectAppSecretConfigured bool `json:"wechat_connect_app_secret_configured"`
|
||||
WeChatConnectOpenAppID string `json:"wechat_connect_open_app_id"`
|
||||
WeChatConnectOpenAppSecretConfigured bool `json:"wechat_connect_open_app_secret_configured"`
|
||||
WeChatConnectMPAppID string `json:"wechat_connect_mp_app_id"`
|
||||
WeChatConnectMPAppSecretConfigured bool `json:"wechat_connect_mp_app_secret_configured"`
|
||||
WeChatConnectMobileAppID string `json:"wechat_connect_mobile_app_id"`
|
||||
WeChatConnectMobileAppSecretConfigured bool `json:"wechat_connect_mobile_app_secret_configured"`
|
||||
WeChatConnectOpenEnabled bool `json:"wechat_connect_open_enabled"`
|
||||
WeChatConnectMPEnabled bool `json:"wechat_connect_mp_enabled"`
|
||||
WeChatConnectMobileEnabled bool `json:"wechat_connect_mobile_enabled"`
|
||||
WeChatConnectMode string `json:"wechat_connect_mode"`
|
||||
WeChatConnectScopes string `json:"wechat_connect_scopes"`
|
||||
WeChatConnectRedirectURL string `json:"wechat_connect_redirect_url"`
|
||||
@@ -212,6 +219,7 @@ type PublicSettings struct {
|
||||
WeChatOAuthEnabled bool `json:"wechat_oauth_enabled"`
|
||||
WeChatOAuthOpenEnabled bool `json:"wechat_oauth_open_enabled"`
|
||||
WeChatOAuthMPEnabled bool `json:"wechat_oauth_mp_enabled"`
|
||||
WeChatOAuthMobileEnabled bool `json:"wechat_oauth_mobile_enabled"`
|
||||
OIDCOAuthEnabled bool `json:"oidc_oauth_enabled"`
|
||||
OIDCOAuthProviderName string `json:"oidc_oauth_provider_name"`
|
||||
SoraClientEnabled bool `json:"sora_client_enabled"`
|
||||
|
||||
@@ -60,6 +60,7 @@ func (h *SettingHandler) GetPublicSettings(c *gin.Context) {
|
||||
WeChatOAuthEnabled: settings.WeChatOAuthEnabled,
|
||||
WeChatOAuthOpenEnabled: settings.WeChatOAuthOpenEnabled,
|
||||
WeChatOAuthMPEnabled: settings.WeChatOAuthMPEnabled,
|
||||
WeChatOAuthMobileEnabled: settings.WeChatOAuthMobileEnabled,
|
||||
OIDCOAuthEnabled: settings.OIDCOAuthEnabled,
|
||||
OIDCOAuthProviderName: settings.OIDCOAuthProviderName,
|
||||
BackendModeEnabled: settings.BackendModeEnabled,
|
||||
|
||||
@@ -115,8 +115,15 @@ const (
|
||||
SettingKeyWeChatConnectEnabled = "wechat_connect_enabled"
|
||||
SettingKeyWeChatConnectAppID = "wechat_connect_app_id"
|
||||
SettingKeyWeChatConnectAppSecret = "wechat_connect_app_secret"
|
||||
SettingKeyWeChatConnectOpenAppID = "wechat_connect_open_app_id"
|
||||
SettingKeyWeChatConnectOpenAppSecret = "wechat_connect_open_app_secret"
|
||||
SettingKeyWeChatConnectMPAppID = "wechat_connect_mp_app_id"
|
||||
SettingKeyWeChatConnectMPAppSecret = "wechat_connect_mp_app_secret"
|
||||
SettingKeyWeChatConnectMobileAppID = "wechat_connect_mobile_app_id"
|
||||
SettingKeyWeChatConnectMobileAppSecret = "wechat_connect_mobile_app_secret"
|
||||
SettingKeyWeChatConnectOpenEnabled = "wechat_connect_open_enabled"
|
||||
SettingKeyWeChatConnectMPEnabled = "wechat_connect_mp_enabled"
|
||||
SettingKeyWeChatConnectMobileEnabled = "wechat_connect_mobile_enabled"
|
||||
SettingKeyWeChatConnectMode = "wechat_connect_mode"
|
||||
SettingKeyWeChatConnectScopes = "wechat_connect_scopes"
|
||||
SettingKeyWeChatConnectRedirectURL = "wechat_connect_redirect_url"
|
||||
|
||||
@@ -519,13 +519,15 @@ func (s *PaymentService) getWeChatPaymentOAuthCredential(ctx context.Context) (s
|
||||
)
|
||||
}
|
||||
cfg, err := (&SettingService{settingRepo: s.configService.settingRepo}).GetWeChatConnectOAuthConfig(ctx)
|
||||
if err != nil || !cfg.SupportsMode("mp") || strings.TrimSpace(cfg.AppID) == "" || strings.TrimSpace(cfg.AppSecret) == "" {
|
||||
appID := strings.TrimSpace(cfg.AppIDForMode("mp"))
|
||||
appSecret := strings.TrimSpace(cfg.AppSecretForMode("mp"))
|
||||
if err != nil || !cfg.SupportsMode("mp") || appID == "" || appSecret == "" {
|
||||
return "", "", infraerrors.ServiceUnavailable(
|
||||
"WECHAT_PAYMENT_MP_NOT_CONFIGURED",
|
||||
"wechat in-app payment requires a complete WeChat MP OAuth credential",
|
||||
)
|
||||
}
|
||||
return strings.TrimSpace(cfg.AppID), strings.TrimSpace(cfg.AppSecret), nil
|
||||
return appID, appSecret, nil
|
||||
}
|
||||
|
||||
func classifyCreatePaymentError(req CreateOrderRequest, providerKey string, err error) error {
|
||||
|
||||
@@ -181,14 +181,19 @@ func normalizeWeChatConnectModeSetting(raw string) string {
|
||||
switch strings.ToLower(strings.TrimSpace(raw)) {
|
||||
case "mp":
|
||||
return "mp"
|
||||
case "mobile":
|
||||
return "mobile"
|
||||
default:
|
||||
return "open"
|
||||
}
|
||||
}
|
||||
|
||||
func defaultWeChatConnectScopeForMode(mode string) string {
|
||||
if normalizeWeChatConnectModeSetting(mode) == "mp" {
|
||||
switch normalizeWeChatConnectModeSetting(mode) {
|
||||
case "mp":
|
||||
return "snsapi_userinfo"
|
||||
case "mobile":
|
||||
return ""
|
||||
}
|
||||
return defaultWeChatConnectScopes
|
||||
}
|
||||
@@ -204,37 +209,47 @@ func normalizeWeChatConnectScopeSetting(raw, mode string) string {
|
||||
default:
|
||||
return defaultWeChatConnectScopeForMode(mode)
|
||||
}
|
||||
case "mobile":
|
||||
return ""
|
||||
default:
|
||||
return defaultWeChatConnectScopes
|
||||
}
|
||||
}
|
||||
|
||||
func parseWeChatConnectCapabilitySettings(settings map[string]string, enabled bool, mode string) (bool, bool) {
|
||||
func parseWeChatConnectCapabilitySettings(settings map[string]string, enabled bool, mode string) (bool, bool, bool) {
|
||||
mode = normalizeWeChatConnectModeSetting(mode)
|
||||
rawOpen, hasOpen := settings[SettingKeyWeChatConnectOpenEnabled]
|
||||
rawMP, hasMP := settings[SettingKeyWeChatConnectMPEnabled]
|
||||
rawMobile, hasMobile := settings[SettingKeyWeChatConnectMobileEnabled]
|
||||
openConfigured := hasOpen && strings.TrimSpace(rawOpen) != ""
|
||||
mpConfigured := hasMP && strings.TrimSpace(rawMP) != ""
|
||||
mobileConfigured := hasMobile && strings.TrimSpace(rawMobile) != ""
|
||||
|
||||
if openConfigured || mpConfigured {
|
||||
if openConfigured || mpConfigured || mobileConfigured {
|
||||
openEnabled := strings.TrimSpace(rawOpen) == "true"
|
||||
mpEnabled := strings.TrimSpace(rawMP) == "true"
|
||||
return openEnabled, mpEnabled
|
||||
mobileEnabled := strings.TrimSpace(rawMobile) == "true"
|
||||
return openEnabled, mpEnabled, mobileEnabled
|
||||
}
|
||||
|
||||
if !enabled {
|
||||
return false, false
|
||||
return false, false, false
|
||||
}
|
||||
if mode == "mp" {
|
||||
return false, true
|
||||
return false, true, false
|
||||
}
|
||||
return true, false
|
||||
if mode == "mobile" {
|
||||
return false, false, true
|
||||
}
|
||||
return true, false, false
|
||||
}
|
||||
|
||||
func normalizeWeChatConnectStoredMode(openEnabled, mpEnabled bool, mode string) string {
|
||||
func normalizeWeChatConnectStoredMode(openEnabled, mpEnabled, mobileEnabled bool, mode string) string {
|
||||
switch {
|
||||
case mpEnabled:
|
||||
return "mp"
|
||||
case mobileEnabled:
|
||||
return "mobile"
|
||||
case openEnabled:
|
||||
return "open"
|
||||
default:
|
||||
@@ -310,8 +325,15 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings
|
||||
SettingKeyWeChatConnectEnabled,
|
||||
SettingKeyWeChatConnectAppID,
|
||||
SettingKeyWeChatConnectAppSecret,
|
||||
SettingKeyWeChatConnectOpenAppID,
|
||||
SettingKeyWeChatConnectOpenAppSecret,
|
||||
SettingKeyWeChatConnectMPAppID,
|
||||
SettingKeyWeChatConnectMPAppSecret,
|
||||
SettingKeyWeChatConnectMobileAppID,
|
||||
SettingKeyWeChatConnectMobileAppSecret,
|
||||
SettingKeyWeChatConnectOpenEnabled,
|
||||
SettingKeyWeChatConnectMPEnabled,
|
||||
SettingKeyWeChatConnectMobileEnabled,
|
||||
SettingKeyWeChatConnectMode,
|
||||
SettingKeyWeChatConnectScopes,
|
||||
SettingKeyWeChatConnectRedirectURL,
|
||||
@@ -350,7 +372,7 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings
|
||||
if oidcProviderName == "" {
|
||||
oidcProviderName = "OIDC"
|
||||
}
|
||||
weChatEnabled, weChatOpenEnabled, weChatMPEnabled := s.weChatOAuthCapabilitiesFromSettings(settings)
|
||||
weChatEnabled, weChatOpenEnabled, weChatMPEnabled, weChatMobileEnabled := s.weChatOAuthCapabilitiesFromSettings(settings)
|
||||
|
||||
// Password reset requires email verification to be enabled
|
||||
emailVerifyEnabled := settings[SettingKeyEmailVerifyEnabled] == "true"
|
||||
@@ -397,6 +419,7 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings
|
||||
WeChatOAuthEnabled: weChatEnabled,
|
||||
WeChatOAuthOpenEnabled: weChatOpenEnabled,
|
||||
WeChatOAuthMPEnabled: weChatMPEnabled,
|
||||
WeChatOAuthMobileEnabled: weChatMobileEnabled,
|
||||
BackendModeEnabled: settings[SettingKeyBackendModeEnabled] == "true",
|
||||
PaymentEnabled: settings[SettingPaymentEnabled] == "true",
|
||||
OIDCOAuthEnabled: oidcEnabled,
|
||||
@@ -456,6 +479,7 @@ func (s *SettingService) GetPublicSettingsForInjection(ctx context.Context) (any
|
||||
WeChatOAuthEnabled bool `json:"wechat_oauth_enabled"`
|
||||
WeChatOAuthOpenEnabled bool `json:"wechat_oauth_open_enabled"`
|
||||
WeChatOAuthMPEnabled bool `json:"wechat_oauth_mp_enabled"`
|
||||
WeChatOAuthMobileEnabled bool `json:"wechat_oauth_mobile_enabled"`
|
||||
BackendModeEnabled bool `json:"backend_mode_enabled"`
|
||||
PaymentEnabled bool `json:"payment_enabled"`
|
||||
OIDCOAuthEnabled bool `json:"oidc_oauth_enabled"`
|
||||
@@ -493,6 +517,7 @@ func (s *SettingService) GetPublicSettingsForInjection(ctx context.Context) (any
|
||||
WeChatOAuthEnabled: settings.WeChatOAuthEnabled,
|
||||
WeChatOAuthOpenEnabled: settings.WeChatOAuthOpenEnabled,
|
||||
WeChatOAuthMPEnabled: settings.WeChatOAuthMPEnabled,
|
||||
WeChatOAuthMobileEnabled: settings.WeChatOAuthMobileEnabled,
|
||||
BackendModeEnabled: settings.BackendModeEnabled,
|
||||
PaymentEnabled: settings.PaymentEnabled,
|
||||
OIDCOAuthEnabled: settings.OIDCOAuthEnabled,
|
||||
@@ -512,15 +537,22 @@ func DefaultWeChatConnectScopesForMode(mode string) string {
|
||||
func (s *SettingService) parseWeChatConnectOAuthConfig(settings map[string]string) (WeChatConnectOAuthConfig, error) {
|
||||
enabled := settings[SettingKeyWeChatConnectEnabled] == "true"
|
||||
mode := normalizeWeChatConnectModeSetting(settings[SettingKeyWeChatConnectMode])
|
||||
openEnabled, mpEnabled := parseWeChatConnectCapabilitySettings(settings, enabled, mode)
|
||||
mode = normalizeWeChatConnectStoredMode(openEnabled, mpEnabled, mode)
|
||||
openEnabled, mpEnabled, mobileEnabled := parseWeChatConnectCapabilitySettings(settings, enabled, mode)
|
||||
mode = normalizeWeChatConnectStoredMode(openEnabled, mpEnabled, mobileEnabled, mode)
|
||||
|
||||
cfg := WeChatConnectOAuthConfig{
|
||||
Enabled: enabled,
|
||||
AppID: strings.TrimSpace(settings[SettingKeyWeChatConnectAppID]),
|
||||
AppSecret: strings.TrimSpace(settings[SettingKeyWeChatConnectAppSecret]),
|
||||
LegacyAppID: strings.TrimSpace(settings[SettingKeyWeChatConnectAppID]),
|
||||
LegacyAppSecret: strings.TrimSpace(settings[SettingKeyWeChatConnectAppSecret]),
|
||||
OpenAppID: strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectOpenAppID], settings[SettingKeyWeChatConnectAppID])),
|
||||
OpenAppSecret: strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectOpenAppSecret], settings[SettingKeyWeChatConnectAppSecret])),
|
||||
MPAppID: strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectMPAppID], settings[SettingKeyWeChatConnectAppID])),
|
||||
MPAppSecret: strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectMPAppSecret], settings[SettingKeyWeChatConnectAppSecret])),
|
||||
MobileAppID: strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectMobileAppID], settings[SettingKeyWeChatConnectAppID])),
|
||||
MobileAppSecret: strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectMobileAppSecret], settings[SettingKeyWeChatConnectAppSecret])),
|
||||
OpenEnabled: openEnabled,
|
||||
MPEnabled: mpEnabled,
|
||||
MobileEnabled: mobileEnabled,
|
||||
Mode: mode,
|
||||
Scopes: normalizeWeChatConnectScopeSetting(settings[SettingKeyWeChatConnectScopes], mode),
|
||||
RedirectURL: strings.TrimSpace(settings[SettingKeyWeChatConnectRedirectURL]),
|
||||
@@ -533,11 +565,29 @@ func (s *SettingService) parseWeChatConnectOAuthConfig(settings map[string]strin
|
||||
if !cfg.Enabled || (!cfg.OpenEnabled && !cfg.MPEnabled) {
|
||||
return WeChatConnectOAuthConfig{}, infraerrors.NotFound("OAUTH_DISABLED", "wechat oauth is disabled")
|
||||
}
|
||||
if cfg.AppID == "" {
|
||||
return WeChatConnectOAuthConfig{}, infraerrors.InternalServer("OAUTH_CONFIG_INVALID", "wechat oauth app id not configured")
|
||||
if cfg.OpenEnabled {
|
||||
if cfg.AppIDForMode("open") == "" {
|
||||
return WeChatConnectOAuthConfig{}, infraerrors.InternalServer("OAUTH_CONFIG_INVALID", "wechat oauth pc app id not configured")
|
||||
}
|
||||
if cfg.AppSecretForMode("open") == "" {
|
||||
return WeChatConnectOAuthConfig{}, infraerrors.InternalServer("OAUTH_CONFIG_INVALID", "wechat oauth pc app secret not configured")
|
||||
}
|
||||
}
|
||||
if cfg.AppSecret == "" {
|
||||
return WeChatConnectOAuthConfig{}, infraerrors.InternalServer("OAUTH_CONFIG_INVALID", "wechat oauth app secret not configured")
|
||||
if cfg.MPEnabled {
|
||||
if cfg.AppIDForMode("mp") == "" {
|
||||
return WeChatConnectOAuthConfig{}, infraerrors.InternalServer("OAUTH_CONFIG_INVALID", "wechat oauth official account app id not configured")
|
||||
}
|
||||
if cfg.AppSecretForMode("mp") == "" {
|
||||
return WeChatConnectOAuthConfig{}, infraerrors.InternalServer("OAUTH_CONFIG_INVALID", "wechat oauth official account app secret not configured")
|
||||
}
|
||||
}
|
||||
if cfg.MobileEnabled {
|
||||
if cfg.AppIDForMode("mobile") == "" {
|
||||
return WeChatConnectOAuthConfig{}, infraerrors.InternalServer("OAUTH_CONFIG_INVALID", "wechat oauth mobile app id not configured")
|
||||
}
|
||||
if cfg.AppSecretForMode("mobile") == "" {
|
||||
return WeChatConnectOAuthConfig{}, infraerrors.InternalServer("OAUTH_CONFIG_INVALID", "wechat oauth mobile app secret not configured")
|
||||
}
|
||||
}
|
||||
if cfg.RedirectURL == "" {
|
||||
return WeChatConnectOAuthConfig{}, infraerrors.InternalServer("OAUTH_CONFIG_INVALID", "wechat oauth redirect url not configured")
|
||||
@@ -554,12 +604,34 @@ func (s *SettingService) parseWeChatConnectOAuthConfig(settings map[string]strin
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (s *SettingService) weChatOAuthCapabilitiesFromSettings(settings map[string]string) (bool, bool, bool) {
|
||||
cfg, err := s.parseWeChatConnectOAuthConfig(settings)
|
||||
if err != nil {
|
||||
return false, false, false
|
||||
func (s *SettingService) weChatOAuthCapabilitiesFromSettings(settings map[string]string) (bool, bool, bool, bool) {
|
||||
if settings[SettingKeyWeChatConnectEnabled] != "true" {
|
||||
return false, false, false, false
|
||||
}
|
||||
return true, cfg.OpenEnabled, cfg.MPEnabled
|
||||
|
||||
mode := normalizeWeChatConnectModeSetting(settings[SettingKeyWeChatConnectMode])
|
||||
openEnabled, mpEnabled, mobileEnabled := parseWeChatConnectCapabilitySettings(settings, true, mode)
|
||||
redirectURL := strings.TrimSpace(settings[SettingKeyWeChatConnectRedirectURL])
|
||||
frontendRedirectURL := strings.TrimSpace(settings[SettingKeyWeChatConnectFrontendRedirectURL])
|
||||
if frontendRedirectURL == "" {
|
||||
frontendRedirectURL = defaultWeChatConnectFrontend
|
||||
}
|
||||
|
||||
legacyAppID := strings.TrimSpace(settings[SettingKeyWeChatConnectAppID])
|
||||
legacyAppSecret := strings.TrimSpace(settings[SettingKeyWeChatConnectAppSecret])
|
||||
openAppID := strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectOpenAppID], legacyAppID))
|
||||
openAppSecret := strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectOpenAppSecret], legacyAppSecret))
|
||||
mpAppID := strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectMPAppID], legacyAppID))
|
||||
mpAppSecret := strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectMPAppSecret], legacyAppSecret))
|
||||
mobileAppID := strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectMobileAppID], legacyAppID))
|
||||
mobileAppSecret := strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectMobileAppSecret], legacyAppSecret))
|
||||
|
||||
webRedirectReady := redirectURL != "" && frontendRedirectURL != ""
|
||||
openReady := openEnabled && webRedirectReady && openAppID != "" && openAppSecret != ""
|
||||
mpReady := mpEnabled && webRedirectReady && mpAppID != "" && mpAppSecret != ""
|
||||
mobileReady := mobileEnabled && mobileAppID != "" && mobileAppSecret != ""
|
||||
|
||||
return openReady || mpReady || mobileReady, openReady, mpReady, mobileReady
|
||||
}
|
||||
|
||||
// filterUserVisibleMenuItems filters out admin-only menu items from a raw JSON
|
||||
@@ -744,9 +816,16 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting
|
||||
settings.PaymentVisibleMethodWxpaySource = wxpaySource
|
||||
settings.WeChatConnectAppID = strings.TrimSpace(settings.WeChatConnectAppID)
|
||||
settings.WeChatConnectAppSecret = strings.TrimSpace(settings.WeChatConnectAppSecret)
|
||||
settings.WeChatConnectOpenAppID = strings.TrimSpace(firstNonEmpty(settings.WeChatConnectOpenAppID, settings.WeChatConnectAppID))
|
||||
settings.WeChatConnectOpenAppSecret = strings.TrimSpace(firstNonEmpty(settings.WeChatConnectOpenAppSecret, settings.WeChatConnectAppSecret))
|
||||
settings.WeChatConnectMPAppID = strings.TrimSpace(firstNonEmpty(settings.WeChatConnectMPAppID, settings.WeChatConnectAppID))
|
||||
settings.WeChatConnectMPAppSecret = strings.TrimSpace(firstNonEmpty(settings.WeChatConnectMPAppSecret, settings.WeChatConnectAppSecret))
|
||||
settings.WeChatConnectMobileAppID = strings.TrimSpace(firstNonEmpty(settings.WeChatConnectMobileAppID, settings.WeChatConnectAppID))
|
||||
settings.WeChatConnectMobileAppSecret = strings.TrimSpace(firstNonEmpty(settings.WeChatConnectMobileAppSecret, settings.WeChatConnectAppSecret))
|
||||
settings.WeChatConnectMode = normalizeWeChatConnectStoredMode(
|
||||
settings.WeChatConnectOpenEnabled,
|
||||
settings.WeChatConnectMPEnabled,
|
||||
settings.WeChatConnectMobileEnabled,
|
||||
settings.WeChatConnectMode,
|
||||
)
|
||||
settings.WeChatConnectScopes = normalizeWeChatConnectScopeSetting(settings.WeChatConnectScopes, settings.WeChatConnectMode)
|
||||
@@ -827,8 +906,12 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting
|
||||
// WeChat Connect OAuth 登录
|
||||
updates[SettingKeyWeChatConnectEnabled] = strconv.FormatBool(settings.WeChatConnectEnabled)
|
||||
updates[SettingKeyWeChatConnectAppID] = settings.WeChatConnectAppID
|
||||
updates[SettingKeyWeChatConnectOpenAppID] = settings.WeChatConnectOpenAppID
|
||||
updates[SettingKeyWeChatConnectMPAppID] = settings.WeChatConnectMPAppID
|
||||
updates[SettingKeyWeChatConnectMobileAppID] = settings.WeChatConnectMobileAppID
|
||||
updates[SettingKeyWeChatConnectOpenEnabled] = strconv.FormatBool(settings.WeChatConnectOpenEnabled)
|
||||
updates[SettingKeyWeChatConnectMPEnabled] = strconv.FormatBool(settings.WeChatConnectMPEnabled)
|
||||
updates[SettingKeyWeChatConnectMobileEnabled] = strconv.FormatBool(settings.WeChatConnectMobileEnabled)
|
||||
updates[SettingKeyWeChatConnectMode] = settings.WeChatConnectMode
|
||||
updates[SettingKeyWeChatConnectScopes] = settings.WeChatConnectScopes
|
||||
updates[SettingKeyWeChatConnectRedirectURL] = settings.WeChatConnectRedirectURL
|
||||
@@ -836,6 +919,15 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting
|
||||
if settings.WeChatConnectAppSecret != "" {
|
||||
updates[SettingKeyWeChatConnectAppSecret] = settings.WeChatConnectAppSecret
|
||||
}
|
||||
if settings.WeChatConnectOpenAppSecret != "" {
|
||||
updates[SettingKeyWeChatConnectOpenAppSecret] = settings.WeChatConnectOpenAppSecret
|
||||
}
|
||||
if settings.WeChatConnectMPAppSecret != "" {
|
||||
updates[SettingKeyWeChatConnectMPAppSecret] = settings.WeChatConnectMPAppSecret
|
||||
}
|
||||
if settings.WeChatConnectMobileAppSecret != "" {
|
||||
updates[SettingKeyWeChatConnectMobileAppSecret] = settings.WeChatConnectMobileAppSecret
|
||||
}
|
||||
|
||||
// OEM设置
|
||||
updates[SettingKeySiteName] = settings.SiteName
|
||||
@@ -1344,8 +1436,15 @@ func (s *SettingService) InitializeDefaultSettings(ctx context.Context) error {
|
||||
SettingKeyCustomMenuItems: "[]",
|
||||
SettingKeyCustomEndpoints: "[]",
|
||||
SettingKeyWeChatConnectEnabled: "false",
|
||||
SettingKeyWeChatConnectOpenAppID: "",
|
||||
SettingKeyWeChatConnectOpenAppSecret: "",
|
||||
SettingKeyWeChatConnectMPAppID: "",
|
||||
SettingKeyWeChatConnectMPAppSecret: "",
|
||||
SettingKeyWeChatConnectMobileAppID: "",
|
||||
SettingKeyWeChatConnectMobileAppSecret: "",
|
||||
SettingKeyWeChatConnectOpenEnabled: "false",
|
||||
SettingKeyWeChatConnectMPEnabled: "false",
|
||||
SettingKeyWeChatConnectMobileEnabled: "false",
|
||||
SettingKeyWeChatConnectMode: "open",
|
||||
SettingKeyWeChatConnectScopes: "snsapi_login",
|
||||
SettingKeyWeChatConnectFrontendRedirectURL: defaultWeChatConnectFrontend,
|
||||
@@ -1645,7 +1744,16 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin
|
||||
result.WeChatConnectAppID = strings.TrimSpace(settings[SettingKeyWeChatConnectAppID])
|
||||
result.WeChatConnectAppSecret = strings.TrimSpace(settings[SettingKeyWeChatConnectAppSecret])
|
||||
result.WeChatConnectAppSecretConfigured = result.WeChatConnectAppSecret != ""
|
||||
result.WeChatConnectOpenEnabled, result.WeChatConnectMPEnabled = parseWeChatConnectCapabilitySettings(
|
||||
result.WeChatConnectOpenAppID = strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectOpenAppID], result.WeChatConnectAppID))
|
||||
result.WeChatConnectOpenAppSecret = strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectOpenAppSecret], result.WeChatConnectAppSecret))
|
||||
result.WeChatConnectOpenAppSecretConfigured = result.WeChatConnectOpenAppSecret != ""
|
||||
result.WeChatConnectMPAppID = strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectMPAppID], result.WeChatConnectAppID))
|
||||
result.WeChatConnectMPAppSecret = strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectMPAppSecret], result.WeChatConnectAppSecret))
|
||||
result.WeChatConnectMPAppSecretConfigured = result.WeChatConnectMPAppSecret != ""
|
||||
result.WeChatConnectMobileAppID = strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectMobileAppID], result.WeChatConnectAppID))
|
||||
result.WeChatConnectMobileAppSecret = strings.TrimSpace(firstNonEmpty(settings[SettingKeyWeChatConnectMobileAppSecret], result.WeChatConnectAppSecret))
|
||||
result.WeChatConnectMobileAppSecretConfigured = result.WeChatConnectMobileAppSecret != ""
|
||||
result.WeChatConnectOpenEnabled, result.WeChatConnectMPEnabled, result.WeChatConnectMobileEnabled = parseWeChatConnectCapabilitySettings(
|
||||
settings,
|
||||
result.WeChatConnectEnabled,
|
||||
settings[SettingKeyWeChatConnectMode],
|
||||
@@ -1653,6 +1761,7 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin
|
||||
result.WeChatConnectMode = normalizeWeChatConnectStoredMode(
|
||||
result.WeChatConnectOpenEnabled,
|
||||
result.WeChatConnectMPEnabled,
|
||||
result.WeChatConnectMobileEnabled,
|
||||
settings[SettingKeyWeChatConnectMode],
|
||||
)
|
||||
result.WeChatConnectScopes = normalizeWeChatConnectScopeSetting(settings[SettingKeyWeChatConnectScopes], result.WeChatConnectMode)
|
||||
@@ -2151,8 +2260,15 @@ func (s *SettingService) GetWeChatConnectOAuthConfig(ctx context.Context) (WeCha
|
||||
SettingKeyWeChatConnectEnabled,
|
||||
SettingKeyWeChatConnectAppID,
|
||||
SettingKeyWeChatConnectAppSecret,
|
||||
SettingKeyWeChatConnectOpenAppID,
|
||||
SettingKeyWeChatConnectOpenAppSecret,
|
||||
SettingKeyWeChatConnectMPAppID,
|
||||
SettingKeyWeChatConnectMPAppSecret,
|
||||
SettingKeyWeChatConnectMobileAppID,
|
||||
SettingKeyWeChatConnectMobileAppSecret,
|
||||
SettingKeyWeChatConnectOpenEnabled,
|
||||
SettingKeyWeChatConnectMPEnabled,
|
||||
SettingKeyWeChatConnectMobileEnabled,
|
||||
SettingKeyWeChatConnectMode,
|
||||
SettingKeyWeChatConnectScopes,
|
||||
SettingKeyWeChatConnectRedirectURL,
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
package service
|
||||
|
||||
import "strings"
|
||||
|
||||
func firstNonEmpty(values ...string) string {
|
||||
for _, value := range values {
|
||||
if trimmed := strings.TrimSpace(value); trimmed != "" {
|
||||
return trimmed
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type SystemSettings struct {
|
||||
RegistrationEnabled bool
|
||||
EmailVerifyEnabled bool
|
||||
@@ -32,16 +43,26 @@ type SystemSettings struct {
|
||||
LinuxDoConnectRedirectURL string
|
||||
|
||||
// WeChat Connect OAuth 登录
|
||||
WeChatConnectEnabled bool
|
||||
WeChatConnectAppID string
|
||||
WeChatConnectAppSecret string
|
||||
WeChatConnectAppSecretConfigured bool
|
||||
WeChatConnectOpenEnabled bool
|
||||
WeChatConnectMPEnabled bool
|
||||
WeChatConnectMode string
|
||||
WeChatConnectScopes string
|
||||
WeChatConnectRedirectURL string
|
||||
WeChatConnectFrontendRedirectURL string
|
||||
WeChatConnectEnabled bool
|
||||
WeChatConnectAppID string
|
||||
WeChatConnectAppSecret string
|
||||
WeChatConnectAppSecretConfigured bool
|
||||
WeChatConnectOpenAppID string
|
||||
WeChatConnectOpenAppSecret string
|
||||
WeChatConnectOpenAppSecretConfigured bool
|
||||
WeChatConnectMPAppID string
|
||||
WeChatConnectMPAppSecret string
|
||||
WeChatConnectMPAppSecretConfigured bool
|
||||
WeChatConnectMobileAppID string
|
||||
WeChatConnectMobileAppSecret string
|
||||
WeChatConnectMobileAppSecretConfigured bool
|
||||
WeChatConnectOpenEnabled bool
|
||||
WeChatConnectMPEnabled bool
|
||||
WeChatConnectMobileEnabled bool
|
||||
WeChatConnectMode string
|
||||
WeChatConnectScopes string
|
||||
WeChatConnectRedirectURL string
|
||||
WeChatConnectFrontendRedirectURL string
|
||||
|
||||
// Generic OIDC OAuth 登录
|
||||
OIDCConnectEnabled bool
|
||||
@@ -173,15 +194,16 @@ type PublicSettings struct {
|
||||
CustomMenuItems string // JSON array of custom menu items
|
||||
CustomEndpoints string // JSON array of custom endpoints
|
||||
|
||||
LinuxDoOAuthEnabled bool
|
||||
WeChatOAuthEnabled bool
|
||||
WeChatOAuthOpenEnabled bool
|
||||
WeChatOAuthMPEnabled bool
|
||||
BackendModeEnabled bool
|
||||
PaymentEnabled bool
|
||||
OIDCOAuthEnabled bool
|
||||
OIDCOAuthProviderName string
|
||||
Version string
|
||||
LinuxDoOAuthEnabled bool
|
||||
WeChatOAuthEnabled bool
|
||||
WeChatOAuthOpenEnabled bool
|
||||
WeChatOAuthMPEnabled bool
|
||||
WeChatOAuthMobileEnabled bool
|
||||
BackendModeEnabled bool
|
||||
PaymentEnabled bool
|
||||
OIDCOAuthEnabled bool
|
||||
OIDCOAuthProviderName string
|
||||
Version string
|
||||
|
||||
BalanceLowNotifyEnabled bool
|
||||
AccountQuotaNotifyEnabled bool
|
||||
@@ -191,10 +213,17 @@ type PublicSettings struct {
|
||||
|
||||
type WeChatConnectOAuthConfig struct {
|
||||
Enabled bool
|
||||
AppID string
|
||||
AppSecret string
|
||||
LegacyAppID string
|
||||
LegacyAppSecret string
|
||||
OpenAppID string
|
||||
OpenAppSecret string
|
||||
MPAppID string
|
||||
MPAppSecret string
|
||||
MobileAppID string
|
||||
MobileAppSecret string
|
||||
OpenEnabled bool
|
||||
MPEnabled bool
|
||||
MobileEnabled bool
|
||||
Mode string
|
||||
Scopes string
|
||||
RedirectURL string
|
||||
@@ -205,18 +234,43 @@ func (cfg WeChatConnectOAuthConfig) SupportsMode(mode string) bool {
|
||||
switch normalizeWeChatConnectModeSetting(mode) {
|
||||
case "mp":
|
||||
return cfg.MPEnabled
|
||||
case "mobile":
|
||||
return cfg.MobileEnabled
|
||||
default:
|
||||
return cfg.OpenEnabled
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg WeChatConnectOAuthConfig) ScopeForMode(mode string) string {
|
||||
if normalizeWeChatConnectModeSetting(mode) == "mp" {
|
||||
switch normalizeWeChatConnectModeSetting(mode) {
|
||||
case "mp":
|
||||
return normalizeWeChatConnectScopeSetting(cfg.Scopes, "mp")
|
||||
case "mobile":
|
||||
return ""
|
||||
}
|
||||
return defaultWeChatConnectScopeForMode("open")
|
||||
}
|
||||
|
||||
func (cfg WeChatConnectOAuthConfig) AppIDForMode(mode string) string {
|
||||
switch normalizeWeChatConnectModeSetting(mode) {
|
||||
case "mp":
|
||||
return strings.TrimSpace(firstNonEmpty(cfg.MPAppID, cfg.LegacyAppID))
|
||||
case "mobile":
|
||||
return strings.TrimSpace(firstNonEmpty(cfg.MobileAppID, cfg.LegacyAppID))
|
||||
}
|
||||
return strings.TrimSpace(firstNonEmpty(cfg.OpenAppID, cfg.LegacyAppID))
|
||||
}
|
||||
|
||||
func (cfg WeChatConnectOAuthConfig) AppSecretForMode(mode string) string {
|
||||
switch normalizeWeChatConnectModeSetting(mode) {
|
||||
case "mp":
|
||||
return strings.TrimSpace(firstNonEmpty(cfg.MPAppSecret, cfg.LegacyAppSecret))
|
||||
case "mobile":
|
||||
return strings.TrimSpace(firstNonEmpty(cfg.MobileAppSecret, cfg.LegacyAppSecret))
|
||||
}
|
||||
return strings.TrimSpace(firstNonEmpty(cfg.OpenAppSecret, cfg.LegacyAppSecret))
|
||||
}
|
||||
|
||||
// StreamTimeoutSettings 流超时处理配置(仅控制超时后的处理方式,超时判定由网关配置控制)
|
||||
type StreamTimeoutSettings struct {
|
||||
// Enabled 是否启用流超时处理
|
||||
|
||||
@@ -32,7 +32,7 @@ export type PaymentVisibleMethodSource =
|
||||
| "easypay_alipay"
|
||||
| "official_wxpay"
|
||||
| "easypay_wxpay";
|
||||
export type WeChatConnectMode = "open" | "mp";
|
||||
export type WeChatConnectMode = "open" | "mp" | "mobile";
|
||||
|
||||
export interface PaymentVisibleMethodSourceOption {
|
||||
value: PaymentVisibleMethodSource;
|
||||
@@ -108,11 +108,16 @@ const PAYMENT_VISIBLE_METHOD_SOURCE_ALIASES: Record<
|
||||
},
|
||||
};
|
||||
const WECHAT_CONNECT_MODE_OPTIONS: WeChatConnectModeOption[] = [
|
||||
{ value: "open", labelZh: "微信开放平台", labelEn: "WeChat Open Platform" },
|
||||
{ value: "open", labelZh: "PC 应用", labelEn: "PC App" },
|
||||
{
|
||||
value: "mp",
|
||||
labelZh: "微信公众号 / 小程序",
|
||||
labelEn: "WeChat Official Account / Mini Program",
|
||||
labelZh: "公众号",
|
||||
labelEn: "Official Account",
|
||||
},
|
||||
{
|
||||
value: "mobile",
|
||||
labelZh: "移动应用",
|
||||
labelEn: "Mobile App",
|
||||
},
|
||||
];
|
||||
const WECHAT_CONNECT_MODE_ALIASES: Record<string, WeChatConnectMode> = {
|
||||
@@ -124,6 +129,9 @@ const WECHAT_CONNECT_MODE_ALIASES: Record<string, WeChatConnectMode> = {
|
||||
official_account: "mp",
|
||||
wechat_mp: "mp",
|
||||
mini_program: "mp",
|
||||
mobile: "mobile",
|
||||
mobile_app: "mobile",
|
||||
native_app: "mobile",
|
||||
};
|
||||
|
||||
export function normalizeDefaultSubscriptionSettings(
|
||||
@@ -234,34 +242,52 @@ export function normalizeWeChatConnectMode(source: unknown): WeChatConnectMode {
|
||||
}
|
||||
|
||||
export function defaultWeChatConnectScopesForMode(mode: unknown): string {
|
||||
return normalizeWeChatConnectMode(mode) === "mp"
|
||||
? "snsapi_userinfo"
|
||||
: "snsapi_login";
|
||||
switch (normalizeWeChatConnectMode(mode)) {
|
||||
case "mp":
|
||||
return "snsapi_userinfo";
|
||||
case "mobile":
|
||||
return "";
|
||||
default:
|
||||
return "snsapi_login";
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveWeChatConnectModeCapabilities(
|
||||
openEnabled: unknown,
|
||||
mpEnabled: unknown,
|
||||
mobileEnabled: unknown,
|
||||
legacyMode: unknown,
|
||||
): { openEnabled: boolean; mpEnabled: boolean } {
|
||||
if (typeof openEnabled === "boolean" || typeof mpEnabled === "boolean") {
|
||||
): { openEnabled: boolean; mpEnabled: boolean; mobileEnabled: boolean } {
|
||||
if (
|
||||
typeof openEnabled === "boolean" ||
|
||||
typeof mpEnabled === "boolean" ||
|
||||
typeof mobileEnabled === "boolean"
|
||||
) {
|
||||
return {
|
||||
openEnabled: openEnabled === true,
|
||||
mpEnabled: mpEnabled === true,
|
||||
mobileEnabled: mobileEnabled === true,
|
||||
};
|
||||
}
|
||||
|
||||
return normalizeWeChatConnectMode(legacyMode) === "mp"
|
||||
? { openEnabled: false, mpEnabled: true }
|
||||
: { openEnabled: true, mpEnabled: false };
|
||||
switch (normalizeWeChatConnectMode(legacyMode)) {
|
||||
case "mp":
|
||||
return { openEnabled: false, mpEnabled: true, mobileEnabled: false };
|
||||
case "mobile":
|
||||
return { openEnabled: false, mpEnabled: false, mobileEnabled: true };
|
||||
default:
|
||||
return { openEnabled: true, mpEnabled: false, mobileEnabled: false };
|
||||
}
|
||||
}
|
||||
|
||||
export function deriveWeChatConnectStoredMode(
|
||||
openEnabled: boolean,
|
||||
mpEnabled: boolean,
|
||||
mobileEnabled: boolean,
|
||||
legacyMode: unknown,
|
||||
): WeChatConnectMode {
|
||||
if (mpEnabled) return "mp";
|
||||
if (mobileEnabled) return "mobile";
|
||||
if (openEnabled) return "open";
|
||||
return normalizeWeChatConnectMode(legacyMode);
|
||||
}
|
||||
@@ -342,8 +368,15 @@ export interface SystemSettings {
|
||||
wechat_connect_enabled: boolean;
|
||||
wechat_connect_app_id: string;
|
||||
wechat_connect_app_secret_configured: boolean;
|
||||
wechat_connect_open_app_id?: string;
|
||||
wechat_connect_open_app_secret_configured?: boolean;
|
||||
wechat_connect_mp_app_id?: string;
|
||||
wechat_connect_mp_app_secret_configured?: boolean;
|
||||
wechat_connect_mobile_app_id?: string;
|
||||
wechat_connect_mobile_app_secret_configured?: boolean;
|
||||
wechat_connect_open_enabled?: boolean;
|
||||
wechat_connect_mp_enabled?: boolean;
|
||||
wechat_connect_mobile_enabled?: boolean;
|
||||
wechat_connect_mode: string;
|
||||
wechat_connect_scopes: string;
|
||||
wechat_connect_redirect_url: string;
|
||||
@@ -501,8 +534,15 @@ export interface UpdateSettingsRequest {
|
||||
wechat_connect_enabled?: boolean;
|
||||
wechat_connect_app_id?: string;
|
||||
wechat_connect_app_secret?: string;
|
||||
wechat_connect_open_app_id?: string;
|
||||
wechat_connect_open_app_secret?: string;
|
||||
wechat_connect_mp_app_id?: string;
|
||||
wechat_connect_mp_app_secret?: string;
|
||||
wechat_connect_mobile_app_id?: string;
|
||||
wechat_connect_mobile_app_secret?: string;
|
||||
wechat_connect_open_enabled?: boolean;
|
||||
wechat_connect_mp_enabled?: boolean;
|
||||
wechat_connect_mobile_enabled?: boolean;
|
||||
wechat_connect_mode?: string;
|
||||
wechat_connect_scopes?: string;
|
||||
wechat_connect_redirect_url?: string;
|
||||
|
||||
@@ -57,6 +57,11 @@ const disabledHint = computed(() => {
|
||||
return t('auth.oauthFlow.wechatSystemBrowserOnly')
|
||||
case 'wechat_browser_required':
|
||||
return t('auth.oauthFlow.wechatBrowserOnly')
|
||||
case 'native_app_required':
|
||||
return localizeWeChatHint(
|
||||
'当前仅配置微信移动应用登录,需要在原生 App 中通过微信 SDK 发起授权。',
|
||||
'This site only has WeChat mobile app login configured. Continue from the native app through the WeChat SDK.',
|
||||
)
|
||||
case 'not_configured':
|
||||
return t('auth.oauthFlow.wechatNotConfigured')
|
||||
default:
|
||||
|
||||
@@ -344,6 +344,7 @@ export const useAppStore = defineStore('app', () => {
|
||||
wechat_oauth_enabled: false,
|
||||
wechat_oauth_open_enabled: false,
|
||||
wechat_oauth_mp_enabled: false,
|
||||
wechat_oauth_mobile_enabled: false,
|
||||
oidc_oauth_enabled: false,
|
||||
oidc_oauth_provider_name: 'OIDC',
|
||||
backend_mode_enabled: false,
|
||||
|
||||
@@ -168,6 +168,7 @@ export interface PublicSettings {
|
||||
wechat_oauth_enabled: boolean
|
||||
wechat_oauth_open_enabled?: boolean
|
||||
wechat_oauth_mp_enabled?: boolean
|
||||
wechat_oauth_mobile_enabled?: boolean
|
||||
oidc_oauth_enabled: boolean
|
||||
oidc_oauth_provider_name: string
|
||||
backend_mode_enabled: boolean
|
||||
|
||||
@@ -1398,101 +1398,253 @@
|
||||
v-if="form.wechat_connect_enabled"
|
||||
class="space-y-6 border-t border-gray-100 pt-4 dark:border-dark-700"
|
||||
>
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
<div>
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
class="rounded-lg border border-gray-200 p-4 dark:border-dark-700"
|
||||
>
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<h3 class="font-medium text-gray-900 dark:text-white">
|
||||
{{ localText("PC 应用", "PC App") }}
|
||||
</h3>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
{{
|
||||
localText(
|
||||
"桌面浏览器通过微信开放平台扫码登录。可与公众号或移动应用同时存在。",
|
||||
"Desktop browsers sign in through WeChat Open Platform QR login. This can coexist with Official Account or Mobile App.",
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
<Toggle
|
||||
:model-value="form.wechat_connect_open_enabled"
|
||||
data-testid="wechat-connect-open-enabled"
|
||||
@update:model-value="handleWeChatOpenEnabledChange"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="form.wechat_connect_open_enabled"
|
||||
class="mt-4 grid grid-cols-1 gap-4 lg:grid-cols-2"
|
||||
>
|
||||
{{ t("admin.settings.wechatConnect.appIdLabel") }}
|
||||
</label>
|
||||
<input
|
||||
data-testid="wechat-connect-app-id"
|
||||
v-model="form.wechat_connect_app_id"
|
||||
type="text"
|
||||
class="input font-mono text-sm"
|
||||
:placeholder="t('admin.settings.wechatConnect.appIdPlaceholder')"
|
||||
/>
|
||||
<div>
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{{ localText("PC AppID", "PC App ID") }}
|
||||
</label>
|
||||
<input
|
||||
v-model="form.wechat_connect_open_app_id"
|
||||
type="text"
|
||||
class="input font-mono text-sm"
|
||||
:placeholder="
|
||||
localText(
|
||||
'微信开放平台 PC 应用 AppID',
|
||||
'WeChat Open Platform PC App ID',
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{{ localText("PC AppSecret", "PC App Secret") }}
|
||||
</label>
|
||||
<input
|
||||
v-model="form.wechat_connect_open_app_secret"
|
||||
type="password"
|
||||
class="input font-mono text-sm"
|
||||
:placeholder="
|
||||
form.wechat_connect_open_app_secret_configured
|
||||
? localText(
|
||||
'密钥已配置,留空以保留当前值。',
|
||||
'Secret configured. Leave empty to keep the current value.',
|
||||
)
|
||||
: localText(
|
||||
'微信开放平台 PC 应用 AppSecret',
|
||||
'WeChat Open Platform PC App Secret',
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
<div
|
||||
class="rounded-lg border border-gray-200 p-4 dark:border-dark-700"
|
||||
>
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<h3 class="font-medium text-gray-900 dark:text-white">
|
||||
{{ localText("公众号", "Official Account") }}
|
||||
</h3>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
{{
|
||||
localText(
|
||||
"仅在微信内浏览器可用;非微信环境下会显示不可用。",
|
||||
"Only available inside the WeChat browser. It is shown as unavailable outside WeChat.",
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
<Toggle
|
||||
:model-value="form.wechat_connect_mp_enabled"
|
||||
data-testid="wechat-connect-mp-enabled"
|
||||
@update:model-value="handleWeChatMPEnabledChange"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="form.wechat_connect_mp_enabled"
|
||||
class="mt-4 grid grid-cols-1 gap-4 lg:grid-cols-2"
|
||||
>
|
||||
{{ t("admin.settings.wechatConnect.appSecretLabel") }}
|
||||
</label>
|
||||
<input
|
||||
data-testid="wechat-connect-app-secret"
|
||||
v-model="form.wechat_connect_app_secret"
|
||||
type="password"
|
||||
class="input font-mono text-sm"
|
||||
:placeholder="
|
||||
form.wechat_connect_app_secret_configured
|
||||
? t('admin.settings.wechatConnect.appSecretConfiguredPlaceholder')
|
||||
: t('admin.settings.wechatConnect.appSecretPlaceholder')
|
||||
"
|
||||
/>
|
||||
<p class="mt-1.5 text-xs text-gray-500 dark:text-gray-400">
|
||||
{{
|
||||
form.wechat_connect_app_secret_configured
|
||||
? t('admin.settings.wechatConnect.appSecretConfiguredHint')
|
||||
: t('admin.settings.wechatConnect.appSecretHint')
|
||||
}}
|
||||
</p>
|
||||
<div>
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{{ localText("公众号 AppID", "Official Account App ID") }}
|
||||
</label>
|
||||
<input
|
||||
v-model="form.wechat_connect_mp_app_id"
|
||||
type="text"
|
||||
class="input font-mono text-sm"
|
||||
:placeholder="
|
||||
localText(
|
||||
'公众号 AppID',
|
||||
'Official Account App ID',
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{{
|
||||
localText(
|
||||
"公众号 AppSecret",
|
||||
"Official Account App Secret",
|
||||
)
|
||||
}}
|
||||
</label>
|
||||
<input
|
||||
v-model="form.wechat_connect_mp_app_secret"
|
||||
type="password"
|
||||
class="input font-mono text-sm"
|
||||
:placeholder="
|
||||
form.wechat_connect_mp_app_secret_configured
|
||||
? localText(
|
||||
'密钥已配置,留空以保留当前值。',
|
||||
'Secret configured. Leave empty to keep the current value.',
|
||||
)
|
||||
: localText(
|
||||
'公众号 AppSecret',
|
||||
'Official Account App Secret',
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="rounded-lg border border-gray-200 p-4 dark:border-dark-700"
|
||||
>
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div>
|
||||
<h3 class="font-medium text-gray-900 dark:text-white">
|
||||
{{ localText("移动应用", "Mobile App") }}
|
||||
</h3>
|
||||
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">
|
||||
{{
|
||||
localText(
|
||||
"原生移动端通过微信 SDK 唤起授权,网页端不会直接发起该流程。",
|
||||
"Native mobile clients start authorization through the WeChat SDK. The web UI does not launch this flow directly.",
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
<Toggle
|
||||
:model-value="form.wechat_connect_mobile_enabled"
|
||||
data-testid="wechat-connect-mobile-enabled"
|
||||
@update:model-value="handleWeChatMobileEnabledChange"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="form.wechat_connect_mobile_enabled"
|
||||
class="mt-4 grid grid-cols-1 gap-4 lg:grid-cols-2"
|
||||
>
|
||||
<div>
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{{ localText("移动应用 AppID", "Mobile App ID") }}
|
||||
</label>
|
||||
<input
|
||||
v-model="form.wechat_connect_mobile_app_id"
|
||||
type="text"
|
||||
class="input font-mono text-sm"
|
||||
:placeholder="
|
||||
localText(
|
||||
'移动应用 AppID',
|
||||
'Mobile App ID',
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{{ localText("移动应用 AppSecret", "Mobile App Secret") }}
|
||||
</label>
|
||||
<input
|
||||
v-model="form.wechat_connect_mobile_app_secret"
|
||||
type="password"
|
||||
class="input font-mono text-sm"
|
||||
:placeholder="
|
||||
form.wechat_connect_mobile_app_secret_configured
|
||||
? localText(
|
||||
'密钥已配置,留空以保留当前值。',
|
||||
'Secret configured. Leave empty to keep the current value.',
|
||||
)
|
||||
: localText(
|
||||
'移动应用 AppSecret',
|
||||
'Mobile App Secret',
|
||||
)
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
<div class="space-y-3">
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{{ t("admin.settings.wechatConnect.modeLabel") }}
|
||||
</label>
|
||||
<div
|
||||
class="flex items-center justify-between rounded border border-gray-200 px-4 py-3 dark:border-dark-700"
|
||||
>
|
||||
<div>
|
||||
<div class="font-medium text-gray-900 dark:text-white">
|
||||
{{ t("admin.settings.wechatConnect.openModeLabel") }}
|
||||
</div>
|
||||
<p
|
||||
class="mt-0.5 text-xs text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ t("admin.settings.wechatConnect.openModeHint") }}
|
||||
</p>
|
||||
</div>
|
||||
<Toggle
|
||||
v-model="form.wechat_connect_open_enabled"
|
||||
data-testid="wechat-connect-open-enabled"
|
||||
@update:model-value="syncWeChatConnectMode"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="flex items-center justify-between rounded border border-gray-200 px-4 py-3 dark:border-dark-700"
|
||||
>
|
||||
<div>
|
||||
<div class="font-medium text-gray-900 dark:text-white">
|
||||
{{ t("admin.settings.wechatConnect.mpModeLabel") }}
|
||||
</div>
|
||||
<p
|
||||
class="mt-0.5 text-xs text-gray-500 dark:text-gray-400"
|
||||
>
|
||||
{{ t("admin.settings.wechatConnect.mpModeHint") }}
|
||||
</p>
|
||||
</div>
|
||||
<Toggle
|
||||
v-model="form.wechat_connect_mp_enabled"
|
||||
data-testid="wechat-connect-mp-enabled"
|
||||
@update:model-value="syncWeChatConnectMode"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
form.wechat_connect_open_enabled &&
|
||||
(form.wechat_connect_mp_enabled ||
|
||||
form.wechat_connect_mobile_enabled)
|
||||
"
|
||||
class="rounded-lg border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-700 dark:border-amber-900/40 dark:bg-amber-900/10 dark:text-amber-300"
|
||||
>
|
||||
{{
|
||||
localText(
|
||||
"如果同时启用 PC 应用和公众号/移动应用,这些应用需要挂在同一个微信开放平台主体下,否则 UnionID 无法稳定归并账号。",
|
||||
"When PC App is enabled together with Official Account or Mobile App, they should belong to the same WeChat Open Platform account so UnionID can merge identities reliably.",
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||
<div>
|
||||
<label
|
||||
class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
|
||||
>
|
||||
{{ t("admin.settings.wechatConnect.redirectUrlLabel") }}
|
||||
{{
|
||||
localText(
|
||||
"浏览器回调地址",
|
||||
"Browser Redirect URL",
|
||||
)
|
||||
}}
|
||||
</label>
|
||||
<input
|
||||
data-testid="wechat-connect-redirect-url"
|
||||
@@ -1501,6 +1653,14 @@
|
||||
class="input font-mono text-sm"
|
||||
:placeholder="t('admin.settings.wechatConnect.redirectUrlPlaceholder')"
|
||||
/>
|
||||
<p class="mt-1.5 text-xs text-gray-500 dark:text-gray-400">
|
||||
{{
|
||||
localText(
|
||||
"用于 PC 应用和公众号的网页回调。移动应用走原生 SDK 时不直接使用这个浏览器回调。",
|
||||
"Used by PC App and Official Account browser callbacks. Native mobile SDK flows do not start from this browser callback directly.",
|
||||
)
|
||||
}}
|
||||
</p>
|
||||
<div
|
||||
class="mt-2 flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3"
|
||||
>
|
||||
@@ -4594,6 +4754,7 @@ import type {
|
||||
SystemSettings,
|
||||
UpdateSettingsRequest,
|
||||
DefaultSubscriptionSetting,
|
||||
WeChatConnectMode,
|
||||
WebSearchEmulationConfig,
|
||||
WebSearchProviderConfig,
|
||||
WebSearchTestResult,
|
||||
@@ -4731,14 +4892,20 @@ interface DefaultSubscriptionGroupOption {
|
||||
|
||||
type SettingsForm = Omit<
|
||||
SystemSettings,
|
||||
"wechat_connect_open_enabled" | "wechat_connect_mp_enabled"
|
||||
| "wechat_connect_open_enabled"
|
||||
| "wechat_connect_mp_enabled"
|
||||
| "wechat_connect_mobile_enabled"
|
||||
> & {
|
||||
smtp_password: string;
|
||||
turnstile_secret_key: string;
|
||||
linuxdo_connect_client_secret: string;
|
||||
wechat_connect_app_secret: string;
|
||||
wechat_connect_open_app_secret: string;
|
||||
wechat_connect_mp_app_secret: string;
|
||||
wechat_connect_mobile_app_secret: string;
|
||||
wechat_connect_open_enabled: boolean;
|
||||
wechat_connect_mp_enabled: boolean;
|
||||
wechat_connect_mobile_enabled: boolean;
|
||||
oidc_connect_client_secret: string;
|
||||
force_email_on_third_party_signup: boolean;
|
||||
payment_visible_method_alipay_source: string;
|
||||
@@ -4833,8 +5000,18 @@ const form = reactive<SettingsForm>({
|
||||
wechat_connect_app_id: "",
|
||||
wechat_connect_app_secret: "",
|
||||
wechat_connect_app_secret_configured: false,
|
||||
wechat_connect_open_app_id: "",
|
||||
wechat_connect_open_app_secret: "",
|
||||
wechat_connect_open_app_secret_configured: false,
|
||||
wechat_connect_mp_app_id: "",
|
||||
wechat_connect_mp_app_secret: "",
|
||||
wechat_connect_mp_app_secret_configured: false,
|
||||
wechat_connect_mobile_app_id: "",
|
||||
wechat_connect_mobile_app_secret: "",
|
||||
wechat_connect_mobile_app_secret_configured: false,
|
||||
wechat_connect_open_enabled: false,
|
||||
wechat_connect_mp_enabled: false,
|
||||
wechat_connect_mobile_enabled: false,
|
||||
wechat_connect_mode: "open",
|
||||
wechat_connect_scopes: "snsapi_login",
|
||||
wechat_connect_redirect_url: "",
|
||||
@@ -5315,17 +5492,28 @@ const wechatRedirectUrlSuggestion = computed(() => {
|
||||
return `${origin}/api/v1/auth/oauth/wechat/callback`;
|
||||
});
|
||||
|
||||
function syncWeChatConnectMode() {
|
||||
function syncWeChatConnectMode(preferredMode?: WeChatConnectMode) {
|
||||
if (form.wechat_connect_mp_enabled && form.wechat_connect_mobile_enabled) {
|
||||
if (preferredMode === "mobile") {
|
||||
form.wechat_connect_mp_enabled = false;
|
||||
} else {
|
||||
form.wechat_connect_mobile_enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
const capabilities = resolveWeChatConnectModeCapabilities(
|
||||
form.wechat_connect_open_enabled,
|
||||
form.wechat_connect_mp_enabled,
|
||||
form.wechat_connect_mobile_enabled,
|
||||
form.wechat_connect_mode,
|
||||
);
|
||||
form.wechat_connect_open_enabled = capabilities.openEnabled;
|
||||
form.wechat_connect_mp_enabled = capabilities.mpEnabled;
|
||||
form.wechat_connect_mobile_enabled = capabilities.mobileEnabled;
|
||||
form.wechat_connect_mode = deriveWeChatConnectStoredMode(
|
||||
capabilities.openEnabled,
|
||||
capabilities.mpEnabled,
|
||||
capabilities.mobileEnabled,
|
||||
form.wechat_connect_mode,
|
||||
);
|
||||
form.wechat_connect_scopes = defaultWeChatConnectScopesForMode(
|
||||
@@ -5333,6 +5521,27 @@ function syncWeChatConnectMode() {
|
||||
);
|
||||
}
|
||||
|
||||
function handleWeChatOpenEnabledChange(value: boolean) {
|
||||
form.wechat_connect_open_enabled = value;
|
||||
syncWeChatConnectMode(value ? "open" : undefined);
|
||||
}
|
||||
|
||||
function handleWeChatMPEnabledChange(value: boolean) {
|
||||
form.wechat_connect_mp_enabled = value;
|
||||
if (value) {
|
||||
form.wechat_connect_mobile_enabled = false;
|
||||
}
|
||||
syncWeChatConnectMode(value ? "mp" : undefined);
|
||||
}
|
||||
|
||||
function handleWeChatMobileEnabledChange(value: boolean) {
|
||||
form.wechat_connect_mobile_enabled = value;
|
||||
if (value) {
|
||||
form.wechat_connect_mp_enabled = false;
|
||||
}
|
||||
syncWeChatConnectMode(value ? "mobile" : undefined);
|
||||
}
|
||||
|
||||
async function setAndCopyWeChatRedirectUrl() {
|
||||
const url = wechatRedirectUrlSuggestion.value;
|
||||
if (!url) return;
|
||||
@@ -5476,16 +5685,22 @@ async function loadSettings() {
|
||||
form.turnstile_secret_key = "";
|
||||
form.linuxdo_connect_client_secret = "";
|
||||
form.wechat_connect_app_secret = "";
|
||||
form.wechat_connect_open_app_secret = "";
|
||||
form.wechat_connect_mp_app_secret = "";
|
||||
form.wechat_connect_mobile_app_secret = "";
|
||||
const wechatCapabilities = resolveWeChatConnectModeCapabilities(
|
||||
settings.wechat_connect_open_enabled,
|
||||
settings.wechat_connect_mp_enabled,
|
||||
settings.wechat_connect_mobile_enabled,
|
||||
settings.wechat_connect_mode,
|
||||
);
|
||||
form.wechat_connect_open_enabled = wechatCapabilities.openEnabled;
|
||||
form.wechat_connect_mp_enabled = wechatCapabilities.mpEnabled;
|
||||
form.wechat_connect_mobile_enabled = wechatCapabilities.mobileEnabled;
|
||||
form.wechat_connect_mode = deriveWeChatConnectStoredMode(
|
||||
wechatCapabilities.openEnabled,
|
||||
wechatCapabilities.mpEnabled,
|
||||
wechatCapabilities.mobileEnabled,
|
||||
settings.wechat_connect_mode,
|
||||
);
|
||||
form.wechat_connect_scopes = defaultWeChatConnectScopesForMode(
|
||||
@@ -5649,6 +5864,16 @@ async function saveSettings() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (form.wechat_connect_mp_enabled && form.wechat_connect_mobile_enabled) {
|
||||
appStore.showError(
|
||||
localText(
|
||||
"公众号和移动应用不能同时启用。",
|
||||
"Official Account and Mobile App cannot be enabled at the same time.",
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate URL fields — novalidate disables browser-native checks, so we validate here
|
||||
const isValidHttpUrl = (url: string): boolean => {
|
||||
if (!url) return true;
|
||||
@@ -5666,6 +5891,7 @@ async function saveSettings() {
|
||||
const wechatStoredMode = deriveWeChatConnectStoredMode(
|
||||
form.wechat_connect_open_enabled,
|
||||
form.wechat_connect_mp_enabled,
|
||||
form.wechat_connect_mobile_enabled,
|
||||
form.wechat_connect_mode,
|
||||
);
|
||||
|
||||
@@ -5714,10 +5940,24 @@ async function saveSettings() {
|
||||
form.linuxdo_connect_client_secret || undefined,
|
||||
linuxdo_connect_redirect_url: form.linuxdo_connect_redirect_url,
|
||||
wechat_connect_enabled: form.wechat_connect_enabled,
|
||||
wechat_connect_app_id: form.wechat_connect_app_id,
|
||||
wechat_connect_app_id:
|
||||
form.wechat_connect_open_app_id ||
|
||||
form.wechat_connect_mp_app_id ||
|
||||
form.wechat_connect_mobile_app_id ||
|
||||
form.wechat_connect_app_id,
|
||||
wechat_connect_app_secret: form.wechat_connect_app_secret || undefined,
|
||||
wechat_connect_open_app_id: form.wechat_connect_open_app_id,
|
||||
wechat_connect_open_app_secret:
|
||||
form.wechat_connect_open_app_secret || undefined,
|
||||
wechat_connect_mp_app_id: form.wechat_connect_mp_app_id,
|
||||
wechat_connect_mp_app_secret:
|
||||
form.wechat_connect_mp_app_secret || undefined,
|
||||
wechat_connect_mobile_app_id: form.wechat_connect_mobile_app_id,
|
||||
wechat_connect_mobile_app_secret:
|
||||
form.wechat_connect_mobile_app_secret || undefined,
|
||||
wechat_connect_open_enabled: form.wechat_connect_open_enabled,
|
||||
wechat_connect_mp_enabled: form.wechat_connect_mp_enabled,
|
||||
wechat_connect_mobile_enabled: form.wechat_connect_mobile_enabled,
|
||||
wechat_connect_mode: wechatStoredMode,
|
||||
wechat_connect_scopes:
|
||||
defaultWeChatConnectScopesForMode(wechatStoredMode),
|
||||
@@ -5847,16 +6087,23 @@ async function saveSettings() {
|
||||
form.turnstile_secret_key = "";
|
||||
form.linuxdo_connect_client_secret = "";
|
||||
form.wechat_connect_app_secret = "";
|
||||
form.wechat_connect_open_app_secret = "";
|
||||
form.wechat_connect_mp_app_secret = "";
|
||||
form.wechat_connect_mobile_app_secret = "";
|
||||
const updatedWechatCapabilities = resolveWeChatConnectModeCapabilities(
|
||||
updated.wechat_connect_open_enabled,
|
||||
updated.wechat_connect_mp_enabled,
|
||||
updated.wechat_connect_mobile_enabled,
|
||||
updated.wechat_connect_mode,
|
||||
);
|
||||
form.wechat_connect_open_enabled = updatedWechatCapabilities.openEnabled;
|
||||
form.wechat_connect_mp_enabled = updatedWechatCapabilities.mpEnabled;
|
||||
form.wechat_connect_mobile_enabled =
|
||||
updatedWechatCapabilities.mobileEnabled;
|
||||
form.wechat_connect_mode = deriveWeChatConnectStoredMode(
|
||||
updatedWechatCapabilities.openEnabled,
|
||||
updatedWechatCapabilities.mpEnabled,
|
||||
updatedWechatCapabilities.mobileEnabled,
|
||||
updated.wechat_connect_mode,
|
||||
);
|
||||
form.wechat_connect_scopes = defaultWeChatConnectScopesForMode(
|
||||
|
||||
@@ -184,7 +184,7 @@ import TotpLoginModal from '@/components/auth/TotpLoginModal.vue'
|
||||
import Icon from '@/components/icons/Icon.vue'
|
||||
import TurnstileWidget from '@/components/TurnstileWidget.vue'
|
||||
import { useAuthStore, useAppStore } from '@/stores'
|
||||
import { getPublicSettings, isTotp2FARequired } from '@/api/auth'
|
||||
import { getPublicSettings, isTotp2FARequired, isWeChatWebOAuthEnabled } from '@/api/auth'
|
||||
import type { TotpLoginResponse } from '@/types'
|
||||
|
||||
const { t } = useI18n()
|
||||
@@ -258,7 +258,7 @@ onMounted(async () => {
|
||||
turnstileEnabled.value = settings.turnstile_enabled
|
||||
turnstileSiteKey.value = settings.turnstile_site_key || ''
|
||||
linuxdoOAuthEnabled.value = settings.linuxdo_oauth_enabled
|
||||
wechatOAuthEnabled.value = settings.wechat_oauth_enabled
|
||||
wechatOAuthEnabled.value = isWeChatWebOAuthEnabled(settings)
|
||||
backendModeEnabled.value = settings.backend_mode_enabled
|
||||
oidcOAuthEnabled.value = settings.oidc_oauth_enabled
|
||||
oidcOAuthProviderName.value = settings.oidc_oauth_provider_name || 'OIDC'
|
||||
|
||||
@@ -282,7 +282,12 @@ import WechatOAuthSection from '@/components/auth/WechatOAuthSection.vue'
|
||||
import Icon from '@/components/icons/Icon.vue'
|
||||
import TurnstileWidget from '@/components/TurnstileWidget.vue'
|
||||
import { useAuthStore, useAppStore } from '@/stores'
|
||||
import { getPublicSettings, validatePromoCode, validateInvitationCode } from '@/api/auth'
|
||||
import {
|
||||
getPublicSettings,
|
||||
isWeChatWebOAuthEnabled,
|
||||
validatePromoCode,
|
||||
validateInvitationCode
|
||||
} from '@/api/auth'
|
||||
import { buildAuthErrorMessage } from '@/utils/authError'
|
||||
import {
|
||||
isRegistrationEmailSuffixAllowed,
|
||||
@@ -385,7 +390,7 @@ onMounted(async () => {
|
||||
turnstileSiteKey.value = settings.turnstile_site_key || ''
|
||||
siteName.value = settings.site_name || 'Sub2API'
|
||||
linuxdoOAuthEnabled.value = settings.linuxdo_oauth_enabled
|
||||
wechatOAuthEnabled.value = settings.wechat_oauth_enabled
|
||||
wechatOAuthEnabled.value = isWeChatWebOAuthEnabled(settings)
|
||||
oidcOAuthEnabled.value = settings.oidc_oauth_enabled
|
||||
oidcOAuthProviderName.value = settings.oidc_oauth_provider_name || 'OIDC'
|
||||
registrationEmailSuffixWhitelist.value = normalizeRegistrationEmailSuffixWhitelist(
|
||||
|
||||
@@ -504,6 +504,8 @@ function resolveWeChatOAuthUnavailableMessage(): string {
|
||||
return t('auth.oauthFlow.wechatSystemBrowserOnly')
|
||||
case 'wechat_browser_required':
|
||||
return t('auth.oauthFlow.wechatBrowserOnly')
|
||||
case 'native_app_required':
|
||||
return 'This WeChat sign-in flow is only available from the native mobile app.'
|
||||
case 'not_configured':
|
||||
return t('auth.oauthFlow.wechatNotConfigured')
|
||||
default:
|
||||
|
||||
Reference in New Issue
Block a user