Commit 54dc1767 authored by IanShaw027's avatar IanShaw027
Browse files

feat(settings): support per-channel WeChat OAuth and persist payment options

parent d5819181
...@@ -753,7 +753,13 @@ func (h *AuthHandler) ensureWeChatBindOwnership( ...@@ -753,7 +753,13 @@ func (h *AuthHandler) ensureWeChatBindOwnership(
} }
for _, identity := range identities { for _, identity := range identities {
if identity != nil && identity.UserID != userID { 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( ...@@ -778,7 +784,13 @@ func (h *AuthHandler) ensureWeChatBindOwnership(
} }
for _, channel := range channels { for _, channel := range channels {
if channel != nil && channel.Edges.Identity != nil && channel.Edges.Identity.UserID != userID { 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 return nil
...@@ -960,8 +972,8 @@ func (h *AuthHandler) getWeChatOAuthConfig(ctx context.Context, rawMode string, ...@@ -960,8 +972,8 @@ func (h *AuthHandler) getWeChatOAuthConfig(ctx context.Context, rawMode string,
cfg := wechatOAuthConfig{ cfg := wechatOAuthConfig{
mode: mode, mode: mode,
appID: strings.TrimSpace(effective.AppID), appID: strings.TrimSpace(effective.AppIDForMode(mode)),
appSecret: strings.TrimSpace(effective.AppSecret), appSecret: strings.TrimSpace(effective.AppSecretForMode(mode)),
redirectURI: firstNonEmpty(strings.TrimSpace(effective.RedirectURL), resolveWeChatOAuthAbsoluteURL(apiBaseURL, c, "/api/v1/auth/oauth/wechat/callback")), redirectURI: firstNonEmpty(strings.TrimSpace(effective.RedirectURL), resolveWeChatOAuthAbsoluteURL(apiBaseURL, c, "/api/v1/auth/oauth/wechat/callback")),
frontendCallback: firstNonEmpty(strings.TrimSpace(effective.FrontendRedirectURL), wechatOAuthDefaultFrontendCB), frontendCallback: firstNonEmpty(strings.TrimSpace(effective.FrontendRedirectURL), wechatOAuthDefaultFrontendCB),
scope: effective.ScopeForMode(mode), scope: effective.ScopeForMode(mode),
......
...@@ -54,8 +54,15 @@ type SystemSettings struct { ...@@ -54,8 +54,15 @@ type SystemSettings struct {
WeChatConnectEnabled bool `json:"wechat_connect_enabled"` WeChatConnectEnabled bool `json:"wechat_connect_enabled"`
WeChatConnectAppID string `json:"wechat_connect_app_id"` WeChatConnectAppID string `json:"wechat_connect_app_id"`
WeChatConnectAppSecretConfigured bool `json:"wechat_connect_app_secret_configured"` 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"` WeChatConnectOpenEnabled bool `json:"wechat_connect_open_enabled"`
WeChatConnectMPEnabled bool `json:"wechat_connect_mp_enabled"` WeChatConnectMPEnabled bool `json:"wechat_connect_mp_enabled"`
WeChatConnectMobileEnabled bool `json:"wechat_connect_mobile_enabled"`
WeChatConnectMode string `json:"wechat_connect_mode"` WeChatConnectMode string `json:"wechat_connect_mode"`
WeChatConnectScopes string `json:"wechat_connect_scopes"` WeChatConnectScopes string `json:"wechat_connect_scopes"`
WeChatConnectRedirectURL string `json:"wechat_connect_redirect_url"` WeChatConnectRedirectURL string `json:"wechat_connect_redirect_url"`
...@@ -212,6 +219,7 @@ type PublicSettings struct { ...@@ -212,6 +219,7 @@ type PublicSettings struct {
WeChatOAuthEnabled bool `json:"wechat_oauth_enabled"` WeChatOAuthEnabled bool `json:"wechat_oauth_enabled"`
WeChatOAuthOpenEnabled bool `json:"wechat_oauth_open_enabled"` WeChatOAuthOpenEnabled bool `json:"wechat_oauth_open_enabled"`
WeChatOAuthMPEnabled bool `json:"wechat_oauth_mp_enabled"` WeChatOAuthMPEnabled bool `json:"wechat_oauth_mp_enabled"`
WeChatOAuthMobileEnabled bool `json:"wechat_oauth_mobile_enabled"`
OIDCOAuthEnabled bool `json:"oidc_oauth_enabled"` OIDCOAuthEnabled bool `json:"oidc_oauth_enabled"`
OIDCOAuthProviderName string `json:"oidc_oauth_provider_name"` OIDCOAuthProviderName string `json:"oidc_oauth_provider_name"`
SoraClientEnabled bool `json:"sora_client_enabled"` SoraClientEnabled bool `json:"sora_client_enabled"`
......
...@@ -60,6 +60,7 @@ func (h *SettingHandler) GetPublicSettings(c *gin.Context) { ...@@ -60,6 +60,7 @@ func (h *SettingHandler) GetPublicSettings(c *gin.Context) {
WeChatOAuthEnabled: settings.WeChatOAuthEnabled, WeChatOAuthEnabled: settings.WeChatOAuthEnabled,
WeChatOAuthOpenEnabled: settings.WeChatOAuthOpenEnabled, WeChatOAuthOpenEnabled: settings.WeChatOAuthOpenEnabled,
WeChatOAuthMPEnabled: settings.WeChatOAuthMPEnabled, WeChatOAuthMPEnabled: settings.WeChatOAuthMPEnabled,
WeChatOAuthMobileEnabled: settings.WeChatOAuthMobileEnabled,
OIDCOAuthEnabled: settings.OIDCOAuthEnabled, OIDCOAuthEnabled: settings.OIDCOAuthEnabled,
OIDCOAuthProviderName: settings.OIDCOAuthProviderName, OIDCOAuthProviderName: settings.OIDCOAuthProviderName,
BackendModeEnabled: settings.BackendModeEnabled, BackendModeEnabled: settings.BackendModeEnabled,
......
...@@ -115,8 +115,15 @@ const ( ...@@ -115,8 +115,15 @@ const (
SettingKeyWeChatConnectEnabled = "wechat_connect_enabled" SettingKeyWeChatConnectEnabled = "wechat_connect_enabled"
SettingKeyWeChatConnectAppID = "wechat_connect_app_id" SettingKeyWeChatConnectAppID = "wechat_connect_app_id"
SettingKeyWeChatConnectAppSecret = "wechat_connect_app_secret" 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" SettingKeyWeChatConnectOpenEnabled = "wechat_connect_open_enabled"
SettingKeyWeChatConnectMPEnabled = "wechat_connect_mp_enabled" SettingKeyWeChatConnectMPEnabled = "wechat_connect_mp_enabled"
SettingKeyWeChatConnectMobileEnabled = "wechat_connect_mobile_enabled"
SettingKeyWeChatConnectMode = "wechat_connect_mode" SettingKeyWeChatConnectMode = "wechat_connect_mode"
SettingKeyWeChatConnectScopes = "wechat_connect_scopes" SettingKeyWeChatConnectScopes = "wechat_connect_scopes"
SettingKeyWeChatConnectRedirectURL = "wechat_connect_redirect_url" SettingKeyWeChatConnectRedirectURL = "wechat_connect_redirect_url"
......
...@@ -519,13 +519,15 @@ func (s *PaymentService) getWeChatPaymentOAuthCredential(ctx context.Context) (s ...@@ -519,13 +519,15 @@ func (s *PaymentService) getWeChatPaymentOAuthCredential(ctx context.Context) (s
) )
} }
cfg, err := (&SettingService{settingRepo: s.configService.settingRepo}).GetWeChatConnectOAuthConfig(ctx) 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( return "", "", infraerrors.ServiceUnavailable(
"WECHAT_PAYMENT_MP_NOT_CONFIGURED", "WECHAT_PAYMENT_MP_NOT_CONFIGURED",
"wechat in-app payment requires a complete WeChat MP OAuth credential", "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 { func classifyCreatePaymentError(req CreateOrderRequest, providerKey string, err error) error {
......
...@@ -181,14 +181,19 @@ func normalizeWeChatConnectModeSetting(raw string) string { ...@@ -181,14 +181,19 @@ func normalizeWeChatConnectModeSetting(raw string) string {
switch strings.ToLower(strings.TrimSpace(raw)) { switch strings.ToLower(strings.TrimSpace(raw)) {
case "mp": case "mp":
return "mp" return "mp"
case "mobile":
return "mobile"
default: default:
return "open" return "open"
} }
} }
func defaultWeChatConnectScopeForMode(mode string) string { func defaultWeChatConnectScopeForMode(mode string) string {
if normalizeWeChatConnectModeSetting(mode) == "mp" { switch normalizeWeChatConnectModeSetting(mode) {
case "mp":
return "snsapi_userinfo" return "snsapi_userinfo"
case "mobile":
return ""
} }
return defaultWeChatConnectScopes return defaultWeChatConnectScopes
} }
...@@ -204,37 +209,47 @@ func normalizeWeChatConnectScopeSetting(raw, mode string) string { ...@@ -204,37 +209,47 @@ func normalizeWeChatConnectScopeSetting(raw, mode string) string {
default: default:
return defaultWeChatConnectScopeForMode(mode) return defaultWeChatConnectScopeForMode(mode)
} }
case "mobile":
return ""
default: default:
return defaultWeChatConnectScopes 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) mode = normalizeWeChatConnectModeSetting(mode)
rawOpen, hasOpen := settings[SettingKeyWeChatConnectOpenEnabled] rawOpen, hasOpen := settings[SettingKeyWeChatConnectOpenEnabled]
rawMP, hasMP := settings[SettingKeyWeChatConnectMPEnabled] rawMP, hasMP := settings[SettingKeyWeChatConnectMPEnabled]
rawMobile, hasMobile := settings[SettingKeyWeChatConnectMobileEnabled]
openConfigured := hasOpen && strings.TrimSpace(rawOpen) != "" openConfigured := hasOpen && strings.TrimSpace(rawOpen) != ""
mpConfigured := hasMP && strings.TrimSpace(rawMP) != "" mpConfigured := hasMP && strings.TrimSpace(rawMP) != ""
mobileConfigured := hasMobile && strings.TrimSpace(rawMobile) != ""
if openConfigured || mpConfigured { if openConfigured || mpConfigured || mobileConfigured {
openEnabled := strings.TrimSpace(rawOpen) == "true" openEnabled := strings.TrimSpace(rawOpen) == "true"
mpEnabled := strings.TrimSpace(rawMP) == "true" mpEnabled := strings.TrimSpace(rawMP) == "true"
return openEnabled, mpEnabled mobileEnabled := strings.TrimSpace(rawMobile) == "true"
return openEnabled, mpEnabled, mobileEnabled
} }
if !enabled { if !enabled {
return false, false return false, false, false
} }
if mode == "mp" { if mode == "mp" {
return false, true return false, true, false
}
if mode == "mobile" {
return false, false, true
} }
return true, false return true, false, false
} }
func normalizeWeChatConnectStoredMode(openEnabled, mpEnabled bool, mode string) string { func normalizeWeChatConnectStoredMode(openEnabled, mpEnabled, mobileEnabled bool, mode string) string {
switch { switch {
case mpEnabled: case mpEnabled:
return "mp" return "mp"
case mobileEnabled:
return "mobile"
case openEnabled: case openEnabled:
return "open" return "open"
default: default:
...@@ -310,8 +325,15 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings ...@@ -310,8 +325,15 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings
SettingKeyWeChatConnectEnabled, SettingKeyWeChatConnectEnabled,
SettingKeyWeChatConnectAppID, SettingKeyWeChatConnectAppID,
SettingKeyWeChatConnectAppSecret, SettingKeyWeChatConnectAppSecret,
SettingKeyWeChatConnectOpenAppID,
SettingKeyWeChatConnectOpenAppSecret,
SettingKeyWeChatConnectMPAppID,
SettingKeyWeChatConnectMPAppSecret,
SettingKeyWeChatConnectMobileAppID,
SettingKeyWeChatConnectMobileAppSecret,
SettingKeyWeChatConnectOpenEnabled, SettingKeyWeChatConnectOpenEnabled,
SettingKeyWeChatConnectMPEnabled, SettingKeyWeChatConnectMPEnabled,
SettingKeyWeChatConnectMobileEnabled,
SettingKeyWeChatConnectMode, SettingKeyWeChatConnectMode,
SettingKeyWeChatConnectScopes, SettingKeyWeChatConnectScopes,
SettingKeyWeChatConnectRedirectURL, SettingKeyWeChatConnectRedirectURL,
...@@ -350,7 +372,7 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings ...@@ -350,7 +372,7 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings
if oidcProviderName == "" { if oidcProviderName == "" {
oidcProviderName = "OIDC" oidcProviderName = "OIDC"
} }
weChatEnabled, weChatOpenEnabled, weChatMPEnabled := s.weChatOAuthCapabilitiesFromSettings(settings) weChatEnabled, weChatOpenEnabled, weChatMPEnabled, weChatMobileEnabled := s.weChatOAuthCapabilitiesFromSettings(settings)
// Password reset requires email verification to be enabled // Password reset requires email verification to be enabled
emailVerifyEnabled := settings[SettingKeyEmailVerifyEnabled] == "true" emailVerifyEnabled := settings[SettingKeyEmailVerifyEnabled] == "true"
...@@ -397,6 +419,7 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings ...@@ -397,6 +419,7 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings
WeChatOAuthEnabled: weChatEnabled, WeChatOAuthEnabled: weChatEnabled,
WeChatOAuthOpenEnabled: weChatOpenEnabled, WeChatOAuthOpenEnabled: weChatOpenEnabled,
WeChatOAuthMPEnabled: weChatMPEnabled, WeChatOAuthMPEnabled: weChatMPEnabled,
WeChatOAuthMobileEnabled: weChatMobileEnabled,
BackendModeEnabled: settings[SettingKeyBackendModeEnabled] == "true", BackendModeEnabled: settings[SettingKeyBackendModeEnabled] == "true",
PaymentEnabled: settings[SettingPaymentEnabled] == "true", PaymentEnabled: settings[SettingPaymentEnabled] == "true",
OIDCOAuthEnabled: oidcEnabled, OIDCOAuthEnabled: oidcEnabled,
...@@ -456,6 +479,7 @@ func (s *SettingService) GetPublicSettingsForInjection(ctx context.Context) (any ...@@ -456,6 +479,7 @@ func (s *SettingService) GetPublicSettingsForInjection(ctx context.Context) (any
WeChatOAuthEnabled bool `json:"wechat_oauth_enabled"` WeChatOAuthEnabled bool `json:"wechat_oauth_enabled"`
WeChatOAuthOpenEnabled bool `json:"wechat_oauth_open_enabled"` WeChatOAuthOpenEnabled bool `json:"wechat_oauth_open_enabled"`
WeChatOAuthMPEnabled bool `json:"wechat_oauth_mp_enabled"` WeChatOAuthMPEnabled bool `json:"wechat_oauth_mp_enabled"`
WeChatOAuthMobileEnabled bool `json:"wechat_oauth_mobile_enabled"`
BackendModeEnabled bool `json:"backend_mode_enabled"` BackendModeEnabled bool `json:"backend_mode_enabled"`
PaymentEnabled bool `json:"payment_enabled"` PaymentEnabled bool `json:"payment_enabled"`
OIDCOAuthEnabled bool `json:"oidc_oauth_enabled"` OIDCOAuthEnabled bool `json:"oidc_oauth_enabled"`
...@@ -493,6 +517,7 @@ func (s *SettingService) GetPublicSettingsForInjection(ctx context.Context) (any ...@@ -493,6 +517,7 @@ func (s *SettingService) GetPublicSettingsForInjection(ctx context.Context) (any
WeChatOAuthEnabled: settings.WeChatOAuthEnabled, WeChatOAuthEnabled: settings.WeChatOAuthEnabled,
WeChatOAuthOpenEnabled: settings.WeChatOAuthOpenEnabled, WeChatOAuthOpenEnabled: settings.WeChatOAuthOpenEnabled,
WeChatOAuthMPEnabled: settings.WeChatOAuthMPEnabled, WeChatOAuthMPEnabled: settings.WeChatOAuthMPEnabled,
WeChatOAuthMobileEnabled: settings.WeChatOAuthMobileEnabled,
BackendModeEnabled: settings.BackendModeEnabled, BackendModeEnabled: settings.BackendModeEnabled,
PaymentEnabled: settings.PaymentEnabled, PaymentEnabled: settings.PaymentEnabled,
OIDCOAuthEnabled: settings.OIDCOAuthEnabled, OIDCOAuthEnabled: settings.OIDCOAuthEnabled,
...@@ -512,15 +537,22 @@ func DefaultWeChatConnectScopesForMode(mode string) string { ...@@ -512,15 +537,22 @@ func DefaultWeChatConnectScopesForMode(mode string) string {
func (s *SettingService) parseWeChatConnectOAuthConfig(settings map[string]string) (WeChatConnectOAuthConfig, error) { func (s *SettingService) parseWeChatConnectOAuthConfig(settings map[string]string) (WeChatConnectOAuthConfig, error) {
enabled := settings[SettingKeyWeChatConnectEnabled] == "true" enabled := settings[SettingKeyWeChatConnectEnabled] == "true"
mode := normalizeWeChatConnectModeSetting(settings[SettingKeyWeChatConnectMode]) mode := normalizeWeChatConnectModeSetting(settings[SettingKeyWeChatConnectMode])
openEnabled, mpEnabled := parseWeChatConnectCapabilitySettings(settings, enabled, mode) openEnabled, mpEnabled, mobileEnabled := parseWeChatConnectCapabilitySettings(settings, enabled, mode)
mode = normalizeWeChatConnectStoredMode(openEnabled, mpEnabled, mode) mode = normalizeWeChatConnectStoredMode(openEnabled, mpEnabled, mobileEnabled, mode)
cfg := WeChatConnectOAuthConfig{ cfg := WeChatConnectOAuthConfig{
Enabled: enabled, Enabled: enabled,
AppID: strings.TrimSpace(settings[SettingKeyWeChatConnectAppID]), LegacyAppID: strings.TrimSpace(settings[SettingKeyWeChatConnectAppID]),
AppSecret: strings.TrimSpace(settings[SettingKeyWeChatConnectAppSecret]), 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, OpenEnabled: openEnabled,
MPEnabled: mpEnabled, MPEnabled: mpEnabled,
MobileEnabled: mobileEnabled,
Mode: mode, Mode: mode,
Scopes: normalizeWeChatConnectScopeSetting(settings[SettingKeyWeChatConnectScopes], mode), Scopes: normalizeWeChatConnectScopeSetting(settings[SettingKeyWeChatConnectScopes], mode),
RedirectURL: strings.TrimSpace(settings[SettingKeyWeChatConnectRedirectURL]), RedirectURL: strings.TrimSpace(settings[SettingKeyWeChatConnectRedirectURL]),
...@@ -533,11 +565,29 @@ func (s *SettingService) parseWeChatConnectOAuthConfig(settings map[string]strin ...@@ -533,11 +565,29 @@ func (s *SettingService) parseWeChatConnectOAuthConfig(settings map[string]strin
if !cfg.Enabled || (!cfg.OpenEnabled && !cfg.MPEnabled) { if !cfg.Enabled || (!cfg.OpenEnabled && !cfg.MPEnabled) {
return WeChatConnectOAuthConfig{}, infraerrors.NotFound("OAUTH_DISABLED", "wechat oauth is disabled") return WeChatConnectOAuthConfig{}, infraerrors.NotFound("OAUTH_DISABLED", "wechat oauth is disabled")
} }
if cfg.AppID == "" { if cfg.OpenEnabled {
return WeChatConnectOAuthConfig{}, infraerrors.InternalServer("OAUTH_CONFIG_INVALID", "wechat oauth app id not configured") 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.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.AppSecret == "" { if cfg.MobileEnabled {
return WeChatConnectOAuthConfig{}, infraerrors.InternalServer("OAUTH_CONFIG_INVALID", "wechat oauth app secret not configured") 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 == "" { if cfg.RedirectURL == "" {
return WeChatConnectOAuthConfig{}, infraerrors.InternalServer("OAUTH_CONFIG_INVALID", "wechat oauth redirect url not configured") 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 ...@@ -554,12 +604,34 @@ func (s *SettingService) parseWeChatConnectOAuthConfig(settings map[string]strin
return cfg, nil return cfg, nil
} }
func (s *SettingService) weChatOAuthCapabilitiesFromSettings(settings map[string]string) (bool, bool, bool) { func (s *SettingService) weChatOAuthCapabilitiesFromSettings(settings map[string]string) (bool, bool, bool, bool) {
cfg, err := s.parseWeChatConnectOAuthConfig(settings) if settings[SettingKeyWeChatConnectEnabled] != "true" {
if err != nil { return false, false, false, false
return false, false, false }
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
} }
return true, cfg.OpenEnabled, cfg.MPEnabled
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 // filterUserVisibleMenuItems filters out admin-only menu items from a raw JSON
...@@ -744,9 +816,16 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting ...@@ -744,9 +816,16 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting
settings.PaymentVisibleMethodWxpaySource = wxpaySource settings.PaymentVisibleMethodWxpaySource = wxpaySource
settings.WeChatConnectAppID = strings.TrimSpace(settings.WeChatConnectAppID) settings.WeChatConnectAppID = strings.TrimSpace(settings.WeChatConnectAppID)
settings.WeChatConnectAppSecret = strings.TrimSpace(settings.WeChatConnectAppSecret) 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.WeChatConnectMode = normalizeWeChatConnectStoredMode(
settings.WeChatConnectOpenEnabled, settings.WeChatConnectOpenEnabled,
settings.WeChatConnectMPEnabled, settings.WeChatConnectMPEnabled,
settings.WeChatConnectMobileEnabled,
settings.WeChatConnectMode, settings.WeChatConnectMode,
) )
settings.WeChatConnectScopes = normalizeWeChatConnectScopeSetting(settings.WeChatConnectScopes, settings.WeChatConnectMode) settings.WeChatConnectScopes = normalizeWeChatConnectScopeSetting(settings.WeChatConnectScopes, settings.WeChatConnectMode)
...@@ -827,8 +906,12 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting ...@@ -827,8 +906,12 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting
// WeChat Connect OAuth 登录 // WeChat Connect OAuth 登录
updates[SettingKeyWeChatConnectEnabled] = strconv.FormatBool(settings.WeChatConnectEnabled) updates[SettingKeyWeChatConnectEnabled] = strconv.FormatBool(settings.WeChatConnectEnabled)
updates[SettingKeyWeChatConnectAppID] = settings.WeChatConnectAppID updates[SettingKeyWeChatConnectAppID] = settings.WeChatConnectAppID
updates[SettingKeyWeChatConnectOpenAppID] = settings.WeChatConnectOpenAppID
updates[SettingKeyWeChatConnectMPAppID] = settings.WeChatConnectMPAppID
updates[SettingKeyWeChatConnectMobileAppID] = settings.WeChatConnectMobileAppID
updates[SettingKeyWeChatConnectOpenEnabled] = strconv.FormatBool(settings.WeChatConnectOpenEnabled) updates[SettingKeyWeChatConnectOpenEnabled] = strconv.FormatBool(settings.WeChatConnectOpenEnabled)
updates[SettingKeyWeChatConnectMPEnabled] = strconv.FormatBool(settings.WeChatConnectMPEnabled) updates[SettingKeyWeChatConnectMPEnabled] = strconv.FormatBool(settings.WeChatConnectMPEnabled)
updates[SettingKeyWeChatConnectMobileEnabled] = strconv.FormatBool(settings.WeChatConnectMobileEnabled)
updates[SettingKeyWeChatConnectMode] = settings.WeChatConnectMode updates[SettingKeyWeChatConnectMode] = settings.WeChatConnectMode
updates[SettingKeyWeChatConnectScopes] = settings.WeChatConnectScopes updates[SettingKeyWeChatConnectScopes] = settings.WeChatConnectScopes
updates[SettingKeyWeChatConnectRedirectURL] = settings.WeChatConnectRedirectURL updates[SettingKeyWeChatConnectRedirectURL] = settings.WeChatConnectRedirectURL
...@@ -836,6 +919,15 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting ...@@ -836,6 +919,15 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting
if settings.WeChatConnectAppSecret != "" { if settings.WeChatConnectAppSecret != "" {
updates[SettingKeyWeChatConnectAppSecret] = 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设置 // OEM设置
updates[SettingKeySiteName] = settings.SiteName updates[SettingKeySiteName] = settings.SiteName
...@@ -1344,8 +1436,15 @@ func (s *SettingService) InitializeDefaultSettings(ctx context.Context) error { ...@@ -1344,8 +1436,15 @@ func (s *SettingService) InitializeDefaultSettings(ctx context.Context) error {
SettingKeyCustomMenuItems: "[]", SettingKeyCustomMenuItems: "[]",
SettingKeyCustomEndpoints: "[]", SettingKeyCustomEndpoints: "[]",
SettingKeyWeChatConnectEnabled: "false", SettingKeyWeChatConnectEnabled: "false",
SettingKeyWeChatConnectOpenAppID: "",
SettingKeyWeChatConnectOpenAppSecret: "",
SettingKeyWeChatConnectMPAppID: "",
SettingKeyWeChatConnectMPAppSecret: "",
SettingKeyWeChatConnectMobileAppID: "",
SettingKeyWeChatConnectMobileAppSecret: "",
SettingKeyWeChatConnectOpenEnabled: "false", SettingKeyWeChatConnectOpenEnabled: "false",
SettingKeyWeChatConnectMPEnabled: "false", SettingKeyWeChatConnectMPEnabled: "false",
SettingKeyWeChatConnectMobileEnabled: "false",
SettingKeyWeChatConnectMode: "open", SettingKeyWeChatConnectMode: "open",
SettingKeyWeChatConnectScopes: "snsapi_login", SettingKeyWeChatConnectScopes: "snsapi_login",
SettingKeyWeChatConnectFrontendRedirectURL: defaultWeChatConnectFrontend, SettingKeyWeChatConnectFrontendRedirectURL: defaultWeChatConnectFrontend,
...@@ -1645,7 +1744,16 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin ...@@ -1645,7 +1744,16 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin
result.WeChatConnectAppID = strings.TrimSpace(settings[SettingKeyWeChatConnectAppID]) result.WeChatConnectAppID = strings.TrimSpace(settings[SettingKeyWeChatConnectAppID])
result.WeChatConnectAppSecret = strings.TrimSpace(settings[SettingKeyWeChatConnectAppSecret]) result.WeChatConnectAppSecret = strings.TrimSpace(settings[SettingKeyWeChatConnectAppSecret])
result.WeChatConnectAppSecretConfigured = result.WeChatConnectAppSecret != "" 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, settings,
result.WeChatConnectEnabled, result.WeChatConnectEnabled,
settings[SettingKeyWeChatConnectMode], settings[SettingKeyWeChatConnectMode],
...@@ -1653,6 +1761,7 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin ...@@ -1653,6 +1761,7 @@ func (s *SettingService) parseSettings(settings map[string]string) *SystemSettin
result.WeChatConnectMode = normalizeWeChatConnectStoredMode( result.WeChatConnectMode = normalizeWeChatConnectStoredMode(
result.WeChatConnectOpenEnabled, result.WeChatConnectOpenEnabled,
result.WeChatConnectMPEnabled, result.WeChatConnectMPEnabled,
result.WeChatConnectMobileEnabled,
settings[SettingKeyWeChatConnectMode], settings[SettingKeyWeChatConnectMode],
) )
result.WeChatConnectScopes = normalizeWeChatConnectScopeSetting(settings[SettingKeyWeChatConnectScopes], result.WeChatConnectMode) result.WeChatConnectScopes = normalizeWeChatConnectScopeSetting(settings[SettingKeyWeChatConnectScopes], result.WeChatConnectMode)
...@@ -2151,8 +2260,15 @@ func (s *SettingService) GetWeChatConnectOAuthConfig(ctx context.Context) (WeCha ...@@ -2151,8 +2260,15 @@ func (s *SettingService) GetWeChatConnectOAuthConfig(ctx context.Context) (WeCha
SettingKeyWeChatConnectEnabled, SettingKeyWeChatConnectEnabled,
SettingKeyWeChatConnectAppID, SettingKeyWeChatConnectAppID,
SettingKeyWeChatConnectAppSecret, SettingKeyWeChatConnectAppSecret,
SettingKeyWeChatConnectOpenAppID,
SettingKeyWeChatConnectOpenAppSecret,
SettingKeyWeChatConnectMPAppID,
SettingKeyWeChatConnectMPAppSecret,
SettingKeyWeChatConnectMobileAppID,
SettingKeyWeChatConnectMobileAppSecret,
SettingKeyWeChatConnectOpenEnabled, SettingKeyWeChatConnectOpenEnabled,
SettingKeyWeChatConnectMPEnabled, SettingKeyWeChatConnectMPEnabled,
SettingKeyWeChatConnectMobileEnabled,
SettingKeyWeChatConnectMode, SettingKeyWeChatConnectMode,
SettingKeyWeChatConnectScopes, SettingKeyWeChatConnectScopes,
SettingKeyWeChatConnectRedirectURL, SettingKeyWeChatConnectRedirectURL,
......
package service 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 { type SystemSettings struct {
RegistrationEnabled bool RegistrationEnabled bool
EmailVerifyEnabled bool EmailVerifyEnabled bool
...@@ -32,16 +43,26 @@ type SystemSettings struct { ...@@ -32,16 +43,26 @@ type SystemSettings struct {
LinuxDoConnectRedirectURL string LinuxDoConnectRedirectURL string
// WeChat Connect OAuth 登录 // WeChat Connect OAuth 登录
WeChatConnectEnabled bool WeChatConnectEnabled bool
WeChatConnectAppID string WeChatConnectAppID string
WeChatConnectAppSecret string WeChatConnectAppSecret string
WeChatConnectAppSecretConfigured bool WeChatConnectAppSecretConfigured bool
WeChatConnectOpenEnabled bool WeChatConnectOpenAppID string
WeChatConnectMPEnabled bool WeChatConnectOpenAppSecret string
WeChatConnectMode string WeChatConnectOpenAppSecretConfigured bool
WeChatConnectScopes string WeChatConnectMPAppID string
WeChatConnectRedirectURL string WeChatConnectMPAppSecret string
WeChatConnectFrontendRedirectURL 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 登录 // Generic OIDC OAuth 登录
OIDCConnectEnabled bool OIDCConnectEnabled bool
...@@ -173,15 +194,16 @@ type PublicSettings struct { ...@@ -173,15 +194,16 @@ type PublicSettings struct {
CustomMenuItems string // JSON array of custom menu items CustomMenuItems string // JSON array of custom menu items
CustomEndpoints string // JSON array of custom endpoints CustomEndpoints string // JSON array of custom endpoints
LinuxDoOAuthEnabled bool LinuxDoOAuthEnabled bool
WeChatOAuthEnabled bool WeChatOAuthEnabled bool
WeChatOAuthOpenEnabled bool WeChatOAuthOpenEnabled bool
WeChatOAuthMPEnabled bool WeChatOAuthMPEnabled bool
BackendModeEnabled bool WeChatOAuthMobileEnabled bool
PaymentEnabled bool BackendModeEnabled bool
OIDCOAuthEnabled bool PaymentEnabled bool
OIDCOAuthProviderName string OIDCOAuthEnabled bool
Version string OIDCOAuthProviderName string
Version string
BalanceLowNotifyEnabled bool BalanceLowNotifyEnabled bool
AccountQuotaNotifyEnabled bool AccountQuotaNotifyEnabled bool
...@@ -191,10 +213,17 @@ type PublicSettings struct { ...@@ -191,10 +213,17 @@ type PublicSettings struct {
type WeChatConnectOAuthConfig struct { type WeChatConnectOAuthConfig struct {
Enabled bool Enabled bool
AppID string LegacyAppID string
AppSecret string LegacyAppSecret string
OpenAppID string
OpenAppSecret string
MPAppID string
MPAppSecret string
MobileAppID string
MobileAppSecret string
OpenEnabled bool OpenEnabled bool
MPEnabled bool MPEnabled bool
MobileEnabled bool
Mode string Mode string
Scopes string Scopes string
RedirectURL string RedirectURL string
...@@ -205,18 +234,43 @@ func (cfg WeChatConnectOAuthConfig) SupportsMode(mode string) bool { ...@@ -205,18 +234,43 @@ func (cfg WeChatConnectOAuthConfig) SupportsMode(mode string) bool {
switch normalizeWeChatConnectModeSetting(mode) { switch normalizeWeChatConnectModeSetting(mode) {
case "mp": case "mp":
return cfg.MPEnabled return cfg.MPEnabled
case "mobile":
return cfg.MobileEnabled
default: default:
return cfg.OpenEnabled return cfg.OpenEnabled
} }
} }
func (cfg WeChatConnectOAuthConfig) ScopeForMode(mode string) string { func (cfg WeChatConnectOAuthConfig) ScopeForMode(mode string) string {
if normalizeWeChatConnectModeSetting(mode) == "mp" { switch normalizeWeChatConnectModeSetting(mode) {
case "mp":
return normalizeWeChatConnectScopeSetting(cfg.Scopes, "mp") return normalizeWeChatConnectScopeSetting(cfg.Scopes, "mp")
case "mobile":
return ""
} }
return defaultWeChatConnectScopeForMode("open") 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 流超时处理配置(仅控制超时后的处理方式,超时判定由网关配置控制) // StreamTimeoutSettings 流超时处理配置(仅控制超时后的处理方式,超时判定由网关配置控制)
type StreamTimeoutSettings struct { type StreamTimeoutSettings struct {
// Enabled 是否启用流超时处理 // Enabled 是否启用流超时处理
......
...@@ -32,7 +32,7 @@ export type PaymentVisibleMethodSource = ...@@ -32,7 +32,7 @@ export type PaymentVisibleMethodSource =
| "easypay_alipay" | "easypay_alipay"
| "official_wxpay" | "official_wxpay"
| "easypay_wxpay"; | "easypay_wxpay";
export type WeChatConnectMode = "open" | "mp"; export type WeChatConnectMode = "open" | "mp" | "mobile";
export interface PaymentVisibleMethodSourceOption { export interface PaymentVisibleMethodSourceOption {
value: PaymentVisibleMethodSource; value: PaymentVisibleMethodSource;
...@@ -108,11 +108,16 @@ const PAYMENT_VISIBLE_METHOD_SOURCE_ALIASES: Record< ...@@ -108,11 +108,16 @@ const PAYMENT_VISIBLE_METHOD_SOURCE_ALIASES: Record<
}, },
}; };
const WECHAT_CONNECT_MODE_OPTIONS: WeChatConnectModeOption[] = [ const WECHAT_CONNECT_MODE_OPTIONS: WeChatConnectModeOption[] = [
{ value: "open", labelZh: "微信开放平台", labelEn: "WeChat Open Platform" }, { value: "open", labelZh: "PC 应用", labelEn: "PC App" },
{ {
value: "mp", value: "mp",
labelZh: "微信公众号 / 小程序", labelZh: "公众号",
labelEn: "WeChat Official Account / Mini Program", labelEn: "Official Account",
},
{
value: "mobile",
labelZh: "移动应用",
labelEn: "Mobile App",
}, },
]; ];
const WECHAT_CONNECT_MODE_ALIASES: Record<string, WeChatConnectMode> = { const WECHAT_CONNECT_MODE_ALIASES: Record<string, WeChatConnectMode> = {
...@@ -124,6 +129,9 @@ const WECHAT_CONNECT_MODE_ALIASES: Record<string, WeChatConnectMode> = { ...@@ -124,6 +129,9 @@ const WECHAT_CONNECT_MODE_ALIASES: Record<string, WeChatConnectMode> = {
official_account: "mp", official_account: "mp",
wechat_mp: "mp", wechat_mp: "mp",
mini_program: "mp", mini_program: "mp",
mobile: "mobile",
mobile_app: "mobile",
native_app: "mobile",
}; };
export function normalizeDefaultSubscriptionSettings( export function normalizeDefaultSubscriptionSettings(
...@@ -234,34 +242,52 @@ export function normalizeWeChatConnectMode(source: unknown): WeChatConnectMode { ...@@ -234,34 +242,52 @@ export function normalizeWeChatConnectMode(source: unknown): WeChatConnectMode {
} }
export function defaultWeChatConnectScopesForMode(mode: unknown): string { export function defaultWeChatConnectScopesForMode(mode: unknown): string {
return normalizeWeChatConnectMode(mode) === "mp" switch (normalizeWeChatConnectMode(mode)) {
? "snsapi_userinfo" case "mp":
: "snsapi_login"; return "snsapi_userinfo";
case "mobile":
return "";
default:
return "snsapi_login";
}
} }
export function resolveWeChatConnectModeCapabilities( export function resolveWeChatConnectModeCapabilities(
openEnabled: unknown, openEnabled: unknown,
mpEnabled: unknown, mpEnabled: unknown,
mobileEnabled: unknown,
legacyMode: unknown, legacyMode: unknown,
): { openEnabled: boolean; mpEnabled: boolean } { ): { openEnabled: boolean; mpEnabled: boolean; mobileEnabled: boolean } {
if (typeof openEnabled === "boolean" || typeof mpEnabled === "boolean") { if (
typeof openEnabled === "boolean" ||
typeof mpEnabled === "boolean" ||
typeof mobileEnabled === "boolean"
) {
return { return {
openEnabled: openEnabled === true, openEnabled: openEnabled === true,
mpEnabled: mpEnabled === true, mpEnabled: mpEnabled === true,
mobileEnabled: mobileEnabled === true,
}; };
} }
return normalizeWeChatConnectMode(legacyMode) === "mp" switch (normalizeWeChatConnectMode(legacyMode)) {
? { openEnabled: false, mpEnabled: true } case "mp":
: { openEnabled: true, mpEnabled: false }; 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( export function deriveWeChatConnectStoredMode(
openEnabled: boolean, openEnabled: boolean,
mpEnabled: boolean, mpEnabled: boolean,
mobileEnabled: boolean,
legacyMode: unknown, legacyMode: unknown,
): WeChatConnectMode { ): WeChatConnectMode {
if (mpEnabled) return "mp"; if (mpEnabled) return "mp";
if (mobileEnabled) return "mobile";
if (openEnabled) return "open"; if (openEnabled) return "open";
return normalizeWeChatConnectMode(legacyMode); return normalizeWeChatConnectMode(legacyMode);
} }
...@@ -342,8 +368,15 @@ export interface SystemSettings { ...@@ -342,8 +368,15 @@ export interface SystemSettings {
wechat_connect_enabled: boolean; wechat_connect_enabled: boolean;
wechat_connect_app_id: string; wechat_connect_app_id: string;
wechat_connect_app_secret_configured: boolean; 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_open_enabled?: boolean;
wechat_connect_mp_enabled?: boolean; wechat_connect_mp_enabled?: boolean;
wechat_connect_mobile_enabled?: boolean;
wechat_connect_mode: string; wechat_connect_mode: string;
wechat_connect_scopes: string; wechat_connect_scopes: string;
wechat_connect_redirect_url: string; wechat_connect_redirect_url: string;
...@@ -501,8 +534,15 @@ export interface UpdateSettingsRequest { ...@@ -501,8 +534,15 @@ export interface UpdateSettingsRequest {
wechat_connect_enabled?: boolean; wechat_connect_enabled?: boolean;
wechat_connect_app_id?: string; wechat_connect_app_id?: string;
wechat_connect_app_secret?: 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_open_enabled?: boolean;
wechat_connect_mp_enabled?: boolean; wechat_connect_mp_enabled?: boolean;
wechat_connect_mobile_enabled?: boolean;
wechat_connect_mode?: string; wechat_connect_mode?: string;
wechat_connect_scopes?: string; wechat_connect_scopes?: string;
wechat_connect_redirect_url?: string; wechat_connect_redirect_url?: string;
......
...@@ -57,6 +57,11 @@ const disabledHint = computed(() => { ...@@ -57,6 +57,11 @@ const disabledHint = computed(() => {
return t('auth.oauthFlow.wechatSystemBrowserOnly') return t('auth.oauthFlow.wechatSystemBrowserOnly')
case 'wechat_browser_required': case 'wechat_browser_required':
return t('auth.oauthFlow.wechatBrowserOnly') 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': case 'not_configured':
return t('auth.oauthFlow.wechatNotConfigured') return t('auth.oauthFlow.wechatNotConfigured')
default: default:
......
...@@ -344,6 +344,7 @@ export const useAppStore = defineStore('app', () => { ...@@ -344,6 +344,7 @@ export const useAppStore = defineStore('app', () => {
wechat_oauth_enabled: false, wechat_oauth_enabled: false,
wechat_oauth_open_enabled: false, wechat_oauth_open_enabled: false,
wechat_oauth_mp_enabled: false, wechat_oauth_mp_enabled: false,
wechat_oauth_mobile_enabled: false,
oidc_oauth_enabled: false, oidc_oauth_enabled: false,
oidc_oauth_provider_name: 'OIDC', oidc_oauth_provider_name: 'OIDC',
backend_mode_enabled: false, backend_mode_enabled: false,
......
...@@ -168,6 +168,7 @@ export interface PublicSettings { ...@@ -168,6 +168,7 @@ export interface PublicSettings {
wechat_oauth_enabled: boolean wechat_oauth_enabled: boolean
wechat_oauth_open_enabled?: boolean wechat_oauth_open_enabled?: boolean
wechat_oauth_mp_enabled?: boolean wechat_oauth_mp_enabled?: boolean
wechat_oauth_mobile_enabled?: boolean
oidc_oauth_enabled: boolean oidc_oauth_enabled: boolean
oidc_oauth_provider_name: string oidc_oauth_provider_name: string
backend_mode_enabled: boolean backend_mode_enabled: boolean
......
This diff is collapsed.
...@@ -184,7 +184,7 @@ import TotpLoginModal from '@/components/auth/TotpLoginModal.vue' ...@@ -184,7 +184,7 @@ import TotpLoginModal from '@/components/auth/TotpLoginModal.vue'
import Icon from '@/components/icons/Icon.vue' import Icon from '@/components/icons/Icon.vue'
import TurnstileWidget from '@/components/TurnstileWidget.vue' import TurnstileWidget from '@/components/TurnstileWidget.vue'
import { useAuthStore, useAppStore } from '@/stores' import { useAuthStore, useAppStore } from '@/stores'
import { getPublicSettings, isTotp2FARequired } from '@/api/auth' import { getPublicSettings, isTotp2FARequired, isWeChatWebOAuthEnabled } from '@/api/auth'
import type { TotpLoginResponse } from '@/types' import type { TotpLoginResponse } from '@/types'
const { t } = useI18n() const { t } = useI18n()
...@@ -258,7 +258,7 @@ onMounted(async () => { ...@@ -258,7 +258,7 @@ onMounted(async () => {
turnstileEnabled.value = settings.turnstile_enabled turnstileEnabled.value = settings.turnstile_enabled
turnstileSiteKey.value = settings.turnstile_site_key || '' turnstileSiteKey.value = settings.turnstile_site_key || ''
linuxdoOAuthEnabled.value = settings.linuxdo_oauth_enabled linuxdoOAuthEnabled.value = settings.linuxdo_oauth_enabled
wechatOAuthEnabled.value = settings.wechat_oauth_enabled wechatOAuthEnabled.value = isWeChatWebOAuthEnabled(settings)
backendModeEnabled.value = settings.backend_mode_enabled backendModeEnabled.value = settings.backend_mode_enabled
oidcOAuthEnabled.value = settings.oidc_oauth_enabled oidcOAuthEnabled.value = settings.oidc_oauth_enabled
oidcOAuthProviderName.value = settings.oidc_oauth_provider_name || 'OIDC' oidcOAuthProviderName.value = settings.oidc_oauth_provider_name || 'OIDC'
......
...@@ -282,7 +282,12 @@ import WechatOAuthSection from '@/components/auth/WechatOAuthSection.vue' ...@@ -282,7 +282,12 @@ import WechatOAuthSection from '@/components/auth/WechatOAuthSection.vue'
import Icon from '@/components/icons/Icon.vue' import Icon from '@/components/icons/Icon.vue'
import TurnstileWidget from '@/components/TurnstileWidget.vue' import TurnstileWidget from '@/components/TurnstileWidget.vue'
import { useAuthStore, useAppStore } from '@/stores' 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 { buildAuthErrorMessage } from '@/utils/authError'
import { import {
isRegistrationEmailSuffixAllowed, isRegistrationEmailSuffixAllowed,
...@@ -385,7 +390,7 @@ onMounted(async () => { ...@@ -385,7 +390,7 @@ onMounted(async () => {
turnstileSiteKey.value = settings.turnstile_site_key || '' turnstileSiteKey.value = settings.turnstile_site_key || ''
siteName.value = settings.site_name || 'Sub2API' siteName.value = settings.site_name || 'Sub2API'
linuxdoOAuthEnabled.value = settings.linuxdo_oauth_enabled linuxdoOAuthEnabled.value = settings.linuxdo_oauth_enabled
wechatOAuthEnabled.value = settings.wechat_oauth_enabled wechatOAuthEnabled.value = isWeChatWebOAuthEnabled(settings)
oidcOAuthEnabled.value = settings.oidc_oauth_enabled oidcOAuthEnabled.value = settings.oidc_oauth_enabled
oidcOAuthProviderName.value = settings.oidc_oauth_provider_name || 'OIDC' oidcOAuthProviderName.value = settings.oidc_oauth_provider_name || 'OIDC'
registrationEmailSuffixWhitelist.value = normalizeRegistrationEmailSuffixWhitelist( registrationEmailSuffixWhitelist.value = normalizeRegistrationEmailSuffixWhitelist(
......
...@@ -504,6 +504,8 @@ function resolveWeChatOAuthUnavailableMessage(): string { ...@@ -504,6 +504,8 @@ function resolveWeChatOAuthUnavailableMessage(): string {
return t('auth.oauthFlow.wechatSystemBrowserOnly') return t('auth.oauthFlow.wechatSystemBrowserOnly')
case 'wechat_browser_required': case 'wechat_browser_required':
return t('auth.oauthFlow.wechatBrowserOnly') 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': case 'not_configured':
return t('auth.oauthFlow.wechatNotConfigured') return t('auth.oauthFlow.wechatNotConfigured')
default: default:
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment