Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
2cebb0dc
Commit
2cebb0dc
authored
Apr 21, 2026
by
IanShaw027
Browse files
feat(settings): support dual-mode wechat oauth defaults
parent
17c6348b
Changes
15
Show whitespace changes
Inline
Side-by-side
backend/internal/handler/admin/setting_handler.go
View file @
2cebb0dc
...
...
@@ -125,6 +125,8 @@ func (h *SettingHandler) GetSettings(c *gin.Context) {
WeChatConnectEnabled
:
settings
.
WeChatConnectEnabled
,
WeChatConnectAppID
:
settings
.
WeChatConnectAppID
,
WeChatConnectAppSecretConfigured
:
settings
.
WeChatConnectAppSecretConfigured
,
WeChatConnectOpenEnabled
:
settings
.
WeChatConnectOpenEnabled
,
WeChatConnectMPEnabled
:
settings
.
WeChatConnectMPEnabled
,
WeChatConnectMode
:
settings
.
WeChatConnectMode
,
WeChatConnectScopes
:
settings
.
WeChatConnectScopes
,
WeChatConnectRedirectURL
:
settings
.
WeChatConnectRedirectURL
,
...
...
@@ -257,6 +259,8 @@ type UpdateSettingsRequest struct {
WeChatConnectEnabled
bool
`json:"wechat_connect_enabled"`
WeChatConnectAppID
string
`json:"wechat_connect_app_id"`
WeChatConnectAppSecret
string
`json:"wechat_connect_app_secret"`
WeChatConnectOpenEnabled
bool
`json:"wechat_connect_open_enabled"`
WeChatConnectMPEnabled
bool
`json:"wechat_connect_mp_enabled"`
WeChatConnectMode
string
`json:"wechat_connect_mode"`
WeChatConnectScopes
string
`json:"wechat_connect_scopes"`
WeChatConnectRedirectURL
string
`json:"wechat_connect_redirect_url"`
...
...
@@ -544,18 +548,36 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
}
req
.
WeChatConnectAppSecret
=
previousSettings
.
WeChatConnectAppSecret
}
if
req
.
WeChatConnectMode
==
""
{
req
.
WeChatConnectMode
=
"open"
}
if
req
.
WeChatConnectMode
!=
""
{
switch
req
.
WeChatConnectMode
{
case
"open"
,
"mp"
:
default
:
response
.
BadRequest
(
c
,
"WeChat mode must be open or mp"
)
return
}
}
if
!
req
.
WeChatConnectOpenEnabled
&&
!
req
.
WeChatConnectMPEnabled
{
switch
req
.
WeChatConnectMode
{
case
"mp"
:
req
.
WeChatConnectMPEnabled
=
true
default
:
req
.
WeChatConnectOpenEnabled
=
true
}
}
if
req
.
WeChatConnectMode
==
""
{
if
req
.
WeChatConnectMPEnabled
{
req
.
WeChatConnectMode
=
"mp"
}
else
{
req
.
WeChatConnectMode
=
"open"
}
}
if
req
.
WeChatConnectScopes
==
""
{
if
req
.
WeChatConnectMPEnabled
{
req
.
WeChatConnectScopes
=
service
.
DefaultWeChatConnectScopesForMode
(
"mp"
)
}
else
{
req
.
WeChatConnectScopes
=
service
.
DefaultWeChatConnectScopesForMode
(
req
.
WeChatConnectMode
)
}
}
if
req
.
WeChatConnectRedirectURL
==
""
{
response
.
BadRequest
(
c
,
"WeChat Redirect URL is required when enabled"
)
return
...
...
@@ -924,6 +946,8 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
WeChatConnectEnabled
:
req
.
WeChatConnectEnabled
,
WeChatConnectAppID
:
req
.
WeChatConnectAppID
,
WeChatConnectAppSecret
:
req
.
WeChatConnectAppSecret
,
WeChatConnectOpenEnabled
:
req
.
WeChatConnectOpenEnabled
,
WeChatConnectMPEnabled
:
req
.
WeChatConnectMPEnabled
,
WeChatConnectMode
:
req
.
WeChatConnectMode
,
WeChatConnectScopes
:
req
.
WeChatConnectScopes
,
WeChatConnectRedirectURL
:
req
.
WeChatConnectRedirectURL
,
...
...
@@ -1210,6 +1234,8 @@ func (h *SettingHandler) UpdateSettings(c *gin.Context) {
WeChatConnectEnabled
:
updatedSettings
.
WeChatConnectEnabled
,
WeChatConnectAppID
:
updatedSettings
.
WeChatConnectAppID
,
WeChatConnectAppSecretConfigured
:
updatedSettings
.
WeChatConnectAppSecretConfigured
,
WeChatConnectOpenEnabled
:
updatedSettings
.
WeChatConnectOpenEnabled
,
WeChatConnectMPEnabled
:
updatedSettings
.
WeChatConnectMPEnabled
,
WeChatConnectMode
:
updatedSettings
.
WeChatConnectMode
,
WeChatConnectScopes
:
updatedSettings
.
WeChatConnectScopes
,
WeChatConnectRedirectURL
:
updatedSettings
.
WeChatConnectRedirectURL
,
...
...
@@ -1416,6 +1442,12 @@ func diffSettings(before *service.SystemSettings, after *service.SystemSettings,
if
req
.
WeChatConnectAppSecret
!=
""
{
changed
=
append
(
changed
,
"wechat_connect_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
.
WeChatConnectMode
!=
after
.
WeChatConnectMode
{
changed
=
append
(
changed
,
"wechat_connect_mode"
)
}
...
...
backend/internal/handler/dto/settings.go
View file @
2cebb0dc
...
...
@@ -54,6 +54,8 @@ type SystemSettings struct {
WeChatConnectEnabled
bool
`json:"wechat_connect_enabled"`
WeChatConnectAppID
string
`json:"wechat_connect_app_id"`
WeChatConnectAppSecretConfigured
bool
`json:"wechat_connect_app_secret_configured"`
WeChatConnectOpenEnabled
bool
`json:"wechat_connect_open_enabled"`
WeChatConnectMPEnabled
bool
`json:"wechat_connect_mp_enabled"`
WeChatConnectMode
string
`json:"wechat_connect_mode"`
WeChatConnectScopes
string
`json:"wechat_connect_scopes"`
WeChatConnectRedirectURL
string
`json:"wechat_connect_redirect_url"`
...
...
backend/internal/handler/setting_handler_public_test.go
View file @
2cebb0dc
...
...
@@ -91,6 +91,8 @@ func TestSettingHandler_GetPublicSettings_ExposesWeChatOAuthModeCapabilities(t *
service
.
SettingKeyWeChatConnectAppSecret
:
"wx-mp-secret"
,
service
.
SettingKeyWeChatConnectMode
:
"mp"
,
service
.
SettingKeyWeChatConnectScopes
:
"snsapi_base"
,
service
.
SettingKeyWeChatConnectOpenEnabled
:
"true"
,
service
.
SettingKeyWeChatConnectMPEnabled
:
"true"
,
service
.
SettingKeyWeChatConnectRedirectURL
:
"https://api.example.com/api/v1/auth/oauth/wechat/callback"
,
service
.
SettingKeyWeChatConnectFrontendRedirectURL
:
"/auth/wechat/callback"
,
},
...
...
@@ -115,6 +117,6 @@ func TestSettingHandler_GetPublicSettings_ExposesWeChatOAuthModeCapabilities(t *
require
.
NoError
(
t
,
json
.
Unmarshal
(
recorder
.
Body
.
Bytes
(),
&
resp
))
require
.
Equal
(
t
,
0
,
resp
.
Code
)
require
.
True
(
t
,
resp
.
Data
.
WeChatOAuthEnabled
)
require
.
Fals
e
(
t
,
resp
.
Data
.
WeChatOAuthOpenEnabled
)
require
.
Tru
e
(
t
,
resp
.
Data
.
WeChatOAuthOpenEnabled
)
require
.
True
(
t
,
resp
.
Data
.
WeChatOAuthMPEnabled
)
}
backend/internal/service/domain_constants.go
View file @
2cebb0dc
...
...
@@ -115,6 +115,8 @@ const (
SettingKeyWeChatConnectEnabled
=
"wechat_connect_enabled"
SettingKeyWeChatConnectAppID
=
"wechat_connect_app_id"
SettingKeyWeChatConnectAppSecret
=
"wechat_connect_app_secret"
SettingKeyWeChatConnectOpenEnabled
=
"wechat_connect_open_enabled"
SettingKeyWeChatConnectMPEnabled
=
"wechat_connect_mp_enabled"
SettingKeyWeChatConnectMode
=
"wechat_connect_mode"
SettingKeyWeChatConnectScopes
=
"wechat_connect_scopes"
SettingKeyWeChatConnectRedirectURL
=
"wechat_connect_redirect_url"
...
...
backend/internal/service/payment_order.go
View file @
2cebb0dc
...
...
@@ -519,7 +519,7 @@ func (s *PaymentService) getWeChatPaymentOAuthCredential(ctx context.Context) (s
)
}
cfg
,
err
:=
(
&
SettingService
{
settingRepo
:
s
.
configService
.
settingRepo
})
.
GetWeChatConnectOAuthConfig
(
ctx
)
if
err
!=
nil
||
cfg
.
Mode
!=
"mp"
||
strings
.
TrimSpace
(
cfg
.
AppID
)
==
""
||
strings
.
TrimSpace
(
cfg
.
AppSecret
)
==
""
{
if
err
!=
nil
||
!
cfg
.
SupportsMode
(
"mp"
)
||
strings
.
TrimSpace
(
cfg
.
AppID
)
==
""
||
strings
.
TrimSpace
(
cfg
.
AppSecret
)
==
""
{
return
""
,
""
,
infraerrors
.
ServiceUnavailable
(
"WECHAT_PAYMENT_MP_NOT_CONFIGURED"
,
"wechat in-app payment requires a complete WeChat MP OAuth credential"
,
...
...
backend/internal/service/setting_service.go
View file @
2cebb0dc
...
...
@@ -209,6 +209,39 @@ func normalizeWeChatConnectScopeSetting(raw, mode string) string {
}
}
func
parseWeChatConnectCapabilitySettings
(
settings
map
[
string
]
string
,
enabled
bool
,
mode
string
)
(
bool
,
bool
)
{
mode
=
normalizeWeChatConnectModeSetting
(
mode
)
rawOpen
,
hasOpen
:=
settings
[
SettingKeyWeChatConnectOpenEnabled
]
rawMP
,
hasMP
:=
settings
[
SettingKeyWeChatConnectMPEnabled
]
openConfigured
:=
hasOpen
&&
strings
.
TrimSpace
(
rawOpen
)
!=
""
mpConfigured
:=
hasMP
&&
strings
.
TrimSpace
(
rawMP
)
!=
""
if
openConfigured
||
mpConfigured
{
openEnabled
:=
strings
.
TrimSpace
(
rawOpen
)
==
"true"
mpEnabled
:=
strings
.
TrimSpace
(
rawMP
)
==
"true"
return
openEnabled
,
mpEnabled
}
if
!
enabled
{
return
false
,
false
}
if
mode
==
"mp"
{
return
false
,
true
}
return
true
,
false
}
func
normalizeWeChatConnectStoredMode
(
openEnabled
,
mpEnabled
bool
,
mode
string
)
string
{
switch
{
case
mpEnabled
:
return
"mp"
case
openEnabled
:
return
"open"
default
:
return
normalizeWeChatConnectModeSetting
(
mode
)
}
}
// NewSettingService 创建系统设置服务实例
func
NewSettingService
(
settingRepo
SettingRepository
,
cfg
*
config
.
Config
)
*
SettingService
{
return
&
SettingService
{
...
...
@@ -277,6 +310,8 @@ func (s *SettingService) GetPublicSettings(ctx context.Context) (*PublicSettings
SettingKeyWeChatConnectEnabled
,
SettingKeyWeChatConnectAppID
,
SettingKeyWeChatConnectAppSecret
,
SettingKeyWeChatConnectOpenEnabled
,
SettingKeyWeChatConnectMPEnabled
,
SettingKeyWeChatConnectMode
,
SettingKeyWeChatConnectScopes
,
SettingKeyWeChatConnectRedirectURL
,
...
...
@@ -475,12 +510,19 @@ 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
)
cfg
:=
WeChatConnectOAuthConfig
{
Enabled
:
settings
[
SettingKeyWeChatConnectEnabled
]
==
"true"
,
Enabled
:
enabled
,
AppID
:
strings
.
TrimSpace
(
settings
[
SettingKeyWeChatConnectAppID
]),
AppSecret
:
strings
.
TrimSpace
(
settings
[
SettingKeyWeChatConnectAppSecret
]),
Mode
:
normalizeWeChatConnectModeSetting
(
settings
[
SettingKeyWeChatConnectMode
]),
Scopes
:
normalizeWeChatConnectScopeSetting
(
settings
[
SettingKeyWeChatConnectScopes
],
settings
[
SettingKeyWeChatConnectMode
]),
OpenEnabled
:
openEnabled
,
MPEnabled
:
mpEnabled
,
Mode
:
mode
,
Scopes
:
normalizeWeChatConnectScopeSetting
(
settings
[
SettingKeyWeChatConnectScopes
],
mode
),
RedirectURL
:
strings
.
TrimSpace
(
settings
[
SettingKeyWeChatConnectRedirectURL
]),
FrontendRedirectURL
:
strings
.
TrimSpace
(
settings
[
SettingKeyWeChatConnectFrontendRedirectURL
]),
}
...
...
@@ -488,7 +530,7 @@ func (s *SettingService) parseWeChatConnectOAuthConfig(settings map[string]strin
cfg
.
FrontendRedirectURL
=
defaultWeChatConnectFrontend
}
if
!
cfg
.
Enabled
{
if
!
cfg
.
Enabled
||
(
!
cfg
.
OpenEnabled
&&
!
cfg
.
MPEnabled
)
{
return
WeChatConnectOAuthConfig
{},
infraerrors
.
NotFound
(
"OAUTH_DISABLED"
,
"wechat oauth is disabled"
)
}
if
cfg
.
AppID
==
""
{
...
...
@@ -517,7 +559,7 @@ func (s *SettingService) weChatOAuthCapabilitiesFromSettings(settings map[string
if
err
!=
nil
{
return
false
,
false
,
false
}
return
true
,
cfg
.
Mode
==
"open"
,
cfg
.
Mode
==
"mp"
return
true
,
cfg
.
OpenEnabled
,
cfg
.
MPEnabled
}
// filterUserVisibleMenuItems filters out admin-only menu items from a raw JSON
...
...
@@ -702,7 +744,11 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting
settings
.
PaymentVisibleMethodWxpaySource
=
wxpaySource
settings
.
WeChatConnectAppID
=
strings
.
TrimSpace
(
settings
.
WeChatConnectAppID
)
settings
.
WeChatConnectAppSecret
=
strings
.
TrimSpace
(
settings
.
WeChatConnectAppSecret
)
settings
.
WeChatConnectMode
=
normalizeWeChatConnectModeSetting
(
settings
.
WeChatConnectMode
)
settings
.
WeChatConnectMode
=
normalizeWeChatConnectStoredMode
(
settings
.
WeChatConnectOpenEnabled
,
settings
.
WeChatConnectMPEnabled
,
settings
.
WeChatConnectMode
,
)
settings
.
WeChatConnectScopes
=
normalizeWeChatConnectScopeSetting
(
settings
.
WeChatConnectScopes
,
settings
.
WeChatConnectMode
)
settings
.
WeChatConnectRedirectURL
=
strings
.
TrimSpace
(
settings
.
WeChatConnectRedirectURL
)
settings
.
WeChatConnectFrontendRedirectURL
=
strings
.
TrimSpace
(
settings
.
WeChatConnectFrontendRedirectURL
)
...
...
@@ -781,6 +827,8 @@ func (s *SettingService) buildSystemSettingsUpdates(ctx context.Context, setting
// WeChat Connect OAuth 登录
updates
[
SettingKeyWeChatConnectEnabled
]
=
strconv
.
FormatBool
(
settings
.
WeChatConnectEnabled
)
updates
[
SettingKeyWeChatConnectAppID
]
=
settings
.
WeChatConnectAppID
updates
[
SettingKeyWeChatConnectOpenEnabled
]
=
strconv
.
FormatBool
(
settings
.
WeChatConnectOpenEnabled
)
updates
[
SettingKeyWeChatConnectMPEnabled
]
=
strconv
.
FormatBool
(
settings
.
WeChatConnectMPEnabled
)
updates
[
SettingKeyWeChatConnectMode
]
=
settings
.
WeChatConnectMode
updates
[
SettingKeyWeChatConnectScopes
]
=
settings
.
WeChatConnectScopes
updates
[
SettingKeyWeChatConnectRedirectURL
]
=
settings
.
WeChatConnectRedirectURL
...
...
@@ -1296,6 +1344,8 @@ func (s *SettingService) InitializeDefaultSettings(ctx context.Context) error {
SettingKeyCustomMenuItems
:
"[]"
,
SettingKeyCustomEndpoints
:
"[]"
,
SettingKeyWeChatConnectEnabled
:
"false"
,
SettingKeyWeChatConnectOpenEnabled
:
"false"
,
SettingKeyWeChatConnectMPEnabled
:
"false"
,
SettingKeyWeChatConnectMode
:
"open"
,
SettingKeyWeChatConnectScopes
:
"snsapi_login"
,
SettingKeyWeChatConnectFrontendRedirectURL
:
defaultWeChatConnectFrontend
,
...
...
@@ -1307,22 +1357,22 @@ func (s *SettingService) InitializeDefaultSettings(ctx context.Context) error {
SettingKeyAuthSourceDefaultEmailBalance
:
"0"
,
SettingKeyAuthSourceDefaultEmailConcurrency
:
"5"
,
SettingKeyAuthSourceDefaultEmailSubscriptions
:
"[]"
,
SettingKeyAuthSourceDefaultEmailGrantOnSignup
:
"
tru
e"
,
SettingKeyAuthSourceDefaultEmailGrantOnSignup
:
"
fals
e"
,
SettingKeyAuthSourceDefaultEmailGrantOnFirstBind
:
"false"
,
SettingKeyAuthSourceDefaultLinuxDoBalance
:
"0"
,
SettingKeyAuthSourceDefaultLinuxDoConcurrency
:
"5"
,
SettingKeyAuthSourceDefaultLinuxDoSubscriptions
:
"[]"
,
SettingKeyAuthSourceDefaultLinuxDoGrantOnSignup
:
"
tru
e"
,
SettingKeyAuthSourceDefaultLinuxDoGrantOnSignup
:
"
fals
e"
,
SettingKeyAuthSourceDefaultLinuxDoGrantOnFirstBind
:
"false"
,
SettingKeyAuthSourceDefaultOIDCBalance
:
"0"
,
SettingKeyAuthSourceDefaultOIDCConcurrency
:
"5"
,
SettingKeyAuthSourceDefaultOIDCSubscriptions
:
"[]"
,
SettingKeyAuthSourceDefaultOIDCGrantOnSignup
:
"
tru
e"
,
SettingKeyAuthSourceDefaultOIDCGrantOnSignup
:
"
fals
e"
,
SettingKeyAuthSourceDefaultOIDCGrantOnFirstBind
:
"false"
,
SettingKeyAuthSourceDefaultWeChatBalance
:
"0"
,
SettingKeyAuthSourceDefaultWeChatConcurrency
:
"5"
,
SettingKeyAuthSourceDefaultWeChatSubscriptions
:
"[]"
,
SettingKeyAuthSourceDefaultWeChatGrantOnSignup
:
"
tru
e"
,
SettingKeyAuthSourceDefaultWeChatGrantOnSignup
:
"
fals
e"
,
SettingKeyAuthSourceDefaultWeChatGrantOnFirstBind
:
"false"
,
SettingKeyForceEmailOnThirdPartySignup
:
"false"
,
SettingKeySMTPPort
:
"587"
,
...
...
@@ -1595,8 +1645,17 @@ 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
.
WeChatConnectMode
=
normalizeWeChatConnectModeSetting
(
settings
[
SettingKeyWeChatConnectMode
])
result
.
WeChatConnectScopes
=
normalizeWeChatConnectScopeSetting
(
settings
[
SettingKeyWeChatConnectScopes
],
settings
[
SettingKeyWeChatConnectMode
])
result
.
WeChatConnectOpenEnabled
,
result
.
WeChatConnectMPEnabled
=
parseWeChatConnectCapabilitySettings
(
settings
,
result
.
WeChatConnectEnabled
,
settings
[
SettingKeyWeChatConnectMode
],
)
result
.
WeChatConnectMode
=
normalizeWeChatConnectStoredMode
(
result
.
WeChatConnectOpenEnabled
,
result
.
WeChatConnectMPEnabled
,
settings
[
SettingKeyWeChatConnectMode
],
)
result
.
WeChatConnectScopes
=
normalizeWeChatConnectScopeSetting
(
settings
[
SettingKeyWeChatConnectScopes
],
result
.
WeChatConnectMode
)
result
.
WeChatConnectRedirectURL
=
strings
.
TrimSpace
(
settings
[
SettingKeyWeChatConnectRedirectURL
])
result
.
WeChatConnectFrontendRedirectURL
=
strings
.
TrimSpace
(
settings
[
SettingKeyWeChatConnectFrontendRedirectURL
])
if
result
.
WeChatConnectFrontendRedirectURL
==
""
{
...
...
@@ -1744,7 +1803,7 @@ func parseProviderDefaultGrantSettings(settings map[string]string, keys authSour
Balance
:
defaultAuthSourceBalance
,
Concurrency
:
defaultAuthSourceConcurrency
,
Subscriptions
:
[]
DefaultSubscriptionSetting
{},
GrantOnSignup
:
tru
e
,
GrantOnSignup
:
fals
e
,
GrantOnFirstBind
:
false
,
}
...
...
@@ -2092,6 +2151,8 @@ func (s *SettingService) GetWeChatConnectOAuthConfig(ctx context.Context) (WeCha
SettingKeyWeChatConnectEnabled
,
SettingKeyWeChatConnectAppID
,
SettingKeyWeChatConnectAppSecret
,
SettingKeyWeChatConnectOpenEnabled
,
SettingKeyWeChatConnectMPEnabled
,
SettingKeyWeChatConnectMode
,
SettingKeyWeChatConnectScopes
,
SettingKeyWeChatConnectRedirectURL
,
...
...
backend/internal/service/setting_service_auth_source_defaults_test.go
View file @
2cebb0dc
...
...
@@ -81,10 +81,12 @@ func TestSettingService_GetAuthSourceDefaultSettings_ParsesValuesAndDefaults(t *
require
.
Equal
(
t
,
0.0
,
got
.
LinuxDo
.
Balance
)
require
.
Equal
(
t
,
5
,
got
.
LinuxDo
.
Concurrency
)
require
.
Equal
(
t
,
[]
DefaultSubscriptionSetting
{},
got
.
LinuxDo
.
Subscriptions
)
require
.
Tru
e
(
t
,
got
.
LinuxDo
.
GrantOnSignup
)
require
.
Fals
e
(
t
,
got
.
LinuxDo
.
GrantOnSignup
)
require
.
True
(
t
,
got
.
LinuxDo
.
GrantOnFirstBind
)
require
.
Equal
(
t
,
5
,
got
.
OIDC
.
Concurrency
)
require
.
Equal
(
t
,
5
,
got
.
WeChat
.
Concurrency
)
require
.
False
(
t
,
got
.
OIDC
.
GrantOnSignup
)
require
.
False
(
t
,
got
.
WeChat
.
GrantOnSignup
)
require
.
True
(
t
,
got
.
ForceEmailOnThirdPartySignup
)
}
...
...
backend/internal/service/setting_service_public_test.go
View file @
2cebb0dc
...
...
@@ -99,6 +99,8 @@ func TestSettingService_GetPublicSettings_ExposesWeChatOAuthModeCapabilities(t *
SettingKeyWeChatConnectAppSecret
:
"wx-mp-secret"
,
SettingKeyWeChatConnectMode
:
"mp"
,
SettingKeyWeChatConnectScopes
:
"snsapi_base"
,
SettingKeyWeChatConnectOpenEnabled
:
"true"
,
SettingKeyWeChatConnectMPEnabled
:
"true"
,
SettingKeyWeChatConnectRedirectURL
:
"https://api.example.com/api/v1/auth/oauth/wechat/callback"
,
SettingKeyWeChatConnectFrontendRedirectURL
:
"/auth/wechat/callback"
,
},
...
...
@@ -107,6 +109,6 @@ func TestSettingService_GetPublicSettings_ExposesWeChatOAuthModeCapabilities(t *
settings
,
err
:=
svc
.
GetPublicSettings
(
context
.
Background
())
require
.
NoError
(
t
,
err
)
require
.
True
(
t
,
settings
.
WeChatOAuthEnabled
)
require
.
Fals
e
(
t
,
settings
.
WeChatOAuthOpenEnabled
)
require
.
Tru
e
(
t
,
settings
.
WeChatOAuthOpenEnabled
)
require
.
True
(
t
,
settings
.
WeChatOAuthMPEnabled
)
}
backend/internal/service/setting_service_wechat_config_test.go
View file @
2cebb0dc
...
...
@@ -59,6 +59,8 @@ func TestSettingService_GetWeChatConnectOAuthConfig_UsesDatabaseOverrides(t *tes
SettingKeyWeChatConnectAppSecret
:
"wx-db-secret"
,
SettingKeyWeChatConnectMode
:
"mp"
,
SettingKeyWeChatConnectScopes
:
"snsapi_base"
,
SettingKeyWeChatConnectOpenEnabled
:
"true"
,
SettingKeyWeChatConnectMPEnabled
:
"true"
,
SettingKeyWeChatConnectRedirectURL
:
"https://api.example.com/api/v1/auth/oauth/wechat/callback"
,
SettingKeyWeChatConnectFrontendRedirectURL
:
"/auth/wechat/callback"
,
},
...
...
@@ -70,6 +72,8 @@ func TestSettingService_GetWeChatConnectOAuthConfig_UsesDatabaseOverrides(t *tes
require
.
True
(
t
,
got
.
Enabled
)
require
.
Equal
(
t
,
"wx-db-app"
,
got
.
AppID
)
require
.
Equal
(
t
,
"wx-db-secret"
,
got
.
AppSecret
)
require
.
True
(
t
,
got
.
OpenEnabled
)
require
.
True
(
t
,
got
.
MPEnabled
)
require
.
Equal
(
t
,
"mp"
,
got
.
Mode
)
require
.
Equal
(
t
,
"snsapi_base"
,
got
.
Scopes
)
require
.
Equal
(
t
,
"https://api.example.com/api/v1/auth/oauth/wechat/callback"
,
got
.
RedirectURL
)
...
...
backend/internal/service/settings_view.go
View file @
2cebb0dc
...
...
@@ -36,6 +36,8 @@ type SystemSettings struct {
WeChatConnectAppID
string
WeChatConnectAppSecret
string
WeChatConnectAppSecretConfigured
bool
WeChatConnectOpenEnabled
bool
WeChatConnectMPEnabled
bool
WeChatConnectMode
string
WeChatConnectScopes
string
WeChatConnectRedirectURL
string
...
...
@@ -191,12 +193,30 @@ type WeChatConnectOAuthConfig struct {
Enabled
bool
AppID
string
AppSecret
string
OpenEnabled
bool
MPEnabled
bool
Mode
string
Scopes
string
RedirectURL
string
FrontendRedirectURL
string
}
func
(
cfg
WeChatConnectOAuthConfig
)
SupportsMode
(
mode
string
)
bool
{
switch
normalizeWeChatConnectModeSetting
(
mode
)
{
case
"mp"
:
return
cfg
.
MPEnabled
default
:
return
cfg
.
OpenEnabled
}
}
func
(
cfg
WeChatConnectOAuthConfig
)
ScopeForMode
(
mode
string
)
string
{
if
normalizeWeChatConnectModeSetting
(
mode
)
==
"mp"
{
return
normalizeWeChatConnectScopeSetting
(
cfg
.
Scopes
,
"mp"
)
}
return
defaultWeChatConnectScopeForMode
(
"open"
)
}
// StreamTimeoutSettings 流超时处理配置(仅控制超时后的处理方式,超时判定由网关配置控制)
type
StreamTimeoutSettings
struct
{
// Enabled 是否启用流超时处理
...
...
backend/migrations/118_wechat_dual_mode_and_auth_source_defaults.sql
0 → 100644
View file @
2cebb0dc
INSERT
INTO
settings
(
key
,
value
)
VALUES
(
'wechat_connect_open_enabled'
,
CASE
WHEN
COALESCE
((
SELECT
value
FROM
settings
WHERE
key
=
'wechat_connect_enabled'
),
'false'
)
<>
'true'
THEN
'false'
WHEN
LOWER
(
TRIM
(
COALESCE
((
SELECT
value
FROM
settings
WHERE
key
=
'wechat_connect_mode'
),
'open'
)))
=
'mp'
THEN
'false'
ELSE
'true'
END
),
(
'wechat_connect_mp_enabled'
,
CASE
WHEN
COALESCE
((
SELECT
value
FROM
settings
WHERE
key
=
'wechat_connect_enabled'
),
'false'
)
<>
'true'
THEN
'false'
WHEN
LOWER
(
TRIM
(
COALESCE
((
SELECT
value
FROM
settings
WHERE
key
=
'wechat_connect_mode'
),
'open'
)))
=
'mp'
THEN
'true'
ELSE
'false'
END
),
(
'auth_source_default_email_grant_on_signup'
,
'false'
),
(
'auth_source_default_linuxdo_grant_on_signup'
,
'false'
),
(
'auth_source_default_oidc_grant_on_signup'
,
'false'
),
(
'auth_source_default_wechat_grant_on_signup'
,
'false'
)
ON
CONFLICT
(
key
)
DO
NOTHING
;
UPDATE
settings
SET
value
=
'false'
WHERE
key
IN
(
'auth_source_default_email_grant_on_signup'
,
'auth_source_default_linuxdo_grant_on_signup'
,
'auth_source_default_oidc_grant_on_signup'
,
'auth_source_default_wechat_grant_on_signup'
);
frontend/src/api/__tests__/settings.authSourceDefaults.spec.ts
View file @
2cebb0dc
import
{
describe
,
expect
,
it
}
from
'
vitest
'
import
{
describe
,
expect
,
it
}
from
"
vitest
"
;
import
{
appendAuthSourceDefaultsToUpdateRequest
,
buildAuthSourceDefaultsState
,
type
UpdateSettingsRequest
,
}
from
'
@/api/admin/settings
'
}
from
"
@/api/admin/settings
"
;
describe
(
'
admin settings auth source defaults helpers
'
,
()
=>
{
it
(
'
builds auth source defaults state from flat settings fields
'
,
()
=>
{
describe
(
"
admin settings auth source defaults helpers
"
,
()
=>
{
it
(
"
builds auth source defaults state from flat settings fields
"
,
()
=>
{
const
state
=
buildAuthSourceDefaultsState
({
auth_source_default_email_balance
:
9.5
,
auth_source_default_email_concurrency
:
3
,
...
...
@@ -23,7 +23,7 @@ describe('admin settings auth source defaults helpers', () => {
],
auth_source_default_linuxdo_grant_on_signup
:
true
,
auth_source_default_linuxdo_grant_on_first_bind
:
false
,
})
})
;
expect
(
state
.
email
).
toEqual
({
balance
:
9.5
,
...
...
@@ -31,34 +31,43 @@ describe('admin settings auth source defaults helpers', () => {
subscriptions
:
[{
group_id
:
1
,
validity_days
:
30
}],
grant_on_signup
:
false
,
grant_on_first_bind
:
true
,
})
})
;
expect
(
state
.
linuxdo
).
toEqual
({
balance
:
6
,
concurrency
:
8
,
subscriptions
:
[{
group_id
:
2
,
validity_days
:
60
}],
grant_on_signup
:
true
,
grant_on_first_bind
:
false
,
})
})
;
expect
(
state
.
oidc
).
toEqual
({
balance
:
0
,
concurrency
:
5
,
subscriptions
:
[],
grant_on_signup
:
tru
e
,
grant_on_signup
:
fals
e
,
grant_on_first_bind
:
false
,
})
})
;
expect
(
state
.
wechat
).
toEqual
({
balance
:
0
,
concurrency
:
5
,
subscriptions
:
[],
grant_on_signup
:
tru
e
,
grant_on_signup
:
fals
e
,
grant_on_first_bind
:
false
,
})
})
});
});
it
(
"
defaults grant-on-signup to disabled when settings are missing
"
,
()
=>
{
const
state
=
buildAuthSourceDefaultsState
({});
it
(
'
appends auth source defaults back onto update payload
'
,
()
=>
{
expect
(
state
.
email
.
grant_on_signup
).
toBe
(
false
);
expect
(
state
.
linuxdo
.
grant_on_signup
).
toBe
(
false
);
expect
(
state
.
oidc
.
grant_on_signup
).
toBe
(
false
);
expect
(
state
.
wechat
.
grant_on_signup
).
toBe
(
false
);
});
it
(
"
appends auth source defaults back onto update payload
"
,
()
=>
{
const
payload
:
UpdateSettingsRequest
=
{
site_name
:
'
Sub2API
'
,
}
site_name
:
"
Sub2API
"
,
}
;
appendAuthSourceDefaultsToUpdateRequest
(
payload
,
{
email
:
{
...
...
@@ -89,13 +98,15 @@ describe('admin settings auth source defaults helpers', () => {
grant_on_signup
:
false
,
grant_on_first_bind
:
false
,
},
})
})
;
expect
(
payload
).
toMatchObject
({
site_name
:
'
Sub2API
'
,
site_name
:
"
Sub2API
"
,
auth_source_default_email_balance
:
1.25
,
auth_source_default_email_concurrency
:
2
,
auth_source_default_email_subscriptions
:
[{
group_id
:
3
,
validity_days
:
7
}],
auth_source_default_email_subscriptions
:
[
{
group_id
:
3
,
validity_days
:
7
},
],
auth_source_default_email_grant_on_signup
:
true
,
auth_source_default_email_grant_on_first_bind
:
false
,
auth_source_default_linuxdo_balance
:
0
,
...
...
@@ -105,7 +116,9 @@ describe('admin settings auth source defaults helpers', () => {
auth_source_default_linuxdo_grant_on_first_bind
:
true
,
auth_source_default_oidc_balance
:
4
,
auth_source_default_oidc_concurrency
:
9
,
auth_source_default_oidc_subscriptions
:
[{
group_id
:
9
,
validity_days
:
90
}],
auth_source_default_oidc_subscriptions
:
[
{
group_id
:
9
,
validity_days
:
90
},
],
auth_source_default_oidc_grant_on_signup
:
true
,
auth_source_default_oidc_grant_on_first_bind
:
true
,
auth_source_default_wechat_balance
:
2
,
...
...
@@ -113,6 +126,6 @@ describe('admin settings auth source defaults helpers', () => {
auth_source_default_wechat_subscriptions
:
[],
auth_source_default_wechat_grant_on_signup
:
false
,
auth_source_default_wechat_grant_on_first_bind
:
false
,
})
})
})
})
;
})
;
})
;
frontend/src/api/admin/settings.ts
View file @
2cebb0dc
...
...
@@ -167,7 +167,7 @@ export function buildAuthSourceDefaultsState(
:
[],
),
grant_on_signup
:
raw
[
`auth_source_default_
${
source
}
_grant_on_signup`
]
!
==
fals
e
,
raw
[
`auth_source_default_
${
source
}
_grant_on_signup`
]
=
==
tru
e
,
grant_on_first_bind
:
raw
[
`auth_source_default_
${
source
}
_grant_on_first_bind`
]
===
true
,
};
...
...
@@ -239,6 +239,33 @@ export function defaultWeChatConnectScopesForMode(mode: unknown): string {
:
"
snsapi_login
"
;
}
export
function
resolveWeChatConnectModeCapabilities
(
openEnabled
:
unknown
,
mpEnabled
:
unknown
,
legacyMode
:
unknown
,
):
{
openEnabled
:
boolean
;
mpEnabled
:
boolean
}
{
if
(
typeof
openEnabled
===
"
boolean
"
||
typeof
mpEnabled
===
"
boolean
"
)
{
return
{
openEnabled
:
openEnabled
===
true
,
mpEnabled
:
mpEnabled
===
true
,
};
}
return
normalizeWeChatConnectMode
(
legacyMode
)
===
"
mp
"
?
{
openEnabled
:
false
,
mpEnabled
:
true
}
:
{
openEnabled
:
true
,
mpEnabled
:
false
};
}
export
function
deriveWeChatConnectStoredMode
(
openEnabled
:
boolean
,
mpEnabled
:
boolean
,
legacyMode
:
unknown
,
):
WeChatConnectMode
{
if
(
mpEnabled
)
return
"
mp
"
;
if
(
openEnabled
)
return
"
open
"
;
return
normalizeWeChatConnectMode
(
legacyMode
);
}
/**
* System settings interface
*/
...
...
@@ -315,6 +342,8 @@ export interface SystemSettings {
wechat_connect_enabled
:
boolean
;
wechat_connect_app_id
:
string
;
wechat_connect_app_secret_configured
:
boolean
;
wechat_connect_open_enabled
?:
boolean
;
wechat_connect_mp_enabled
?:
boolean
;
wechat_connect_mode
:
string
;
wechat_connect_scopes
:
string
;
wechat_connect_redirect_url
:
string
;
...
...
@@ -472,6 +501,8 @@ export interface UpdateSettingsRequest {
wechat_connect_enabled
?:
boolean
;
wechat_connect_app_id
?:
string
;
wechat_connect_app_secret
?:
string
;
wechat_connect_open_enabled
?:
boolean
;
wechat_connect_mp_enabled
?:
boolean
;
wechat_connect_mode
?:
string
;
wechat_connect_scopes
?:
string
;
wechat_connect_redirect_url
?:
string
;
...
...
frontend/src/views/admin/SettingsView.vue
View file @
2cebb0dc
...
...
@@ -1408,7 +1408,7 @@
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-
3
"
>
<
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
"
...
...
@@ -1463,69 +1463,74 @@
}}
<
/p
>
<
/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
"
>
{{
localText
(
"
模式
"
,
"
Mode
"
)
}}
<
/label
>
<
select
data
-
testid
=
"
wechat-connect-mode
"
v
-
model
=
"
form.wechat_connect_mode
"
class
=
"
input font-mono text-sm
"
@
change
=
"
syncWeChatConnectMode
"
<
div
class
=
"
flex items-center justify-between rounded border border-gray-200 px-4 py-3 dark:border-dark-700
"
>
<
option
value
=
"
open
"
>
{{
localText
(
"
开放平台
"
,
"
Open Platform
"
)
}}
<
/option
>
<
option
value
=
"
mp
"
>
<
div
>
<
div
class
=
"
font-medium text-gray-900 dark:text-white
"
>
{{
localText
(
"
公众号 / 小程序
"
,
"
Official Account / Mini Program
"
,
"
非微信环境使用开放平台
"
,
"
Use Open outside WeChat
"
,
)
}}
<
/option
>
<
/select
>
<
p
class
=
"
mt-1.5 text-xs text-gray-500 dark:text-gray-400
"
>
<
/div
>
<
p
class
=
"
mt-0.5 text-xs text-gray-500 dark:text-gray-400
"
>
{{
localText
(
"
open 对应微信开放平台,mp 对应公众号/小程序
授权。
"
,
"
open maps to WeChat Open Platform, mp maps to Official Account / Mini Program authorization
.
"
,
"
浏览器不在微信内时,自动走开放平台扫码
授权。
"
,
"
Use Open Platform QR authorization outside the WeChat browser
.
"
,
)
}}
<
/p
>
<
/div
>
<
Toggle
v
-
model
=
"
form.wechat_connect_open_enabled
"
data
-
testid
=
"
wechat-connect-open-enabled
"
@
update
:
model
-
value
=
"
syncWeChatConnectMode
"
/>
<
/div
>
<
div
class
=
"
grid grid-cols-1 gap-6 lg:grid-cols-2
"
>
<
div
class
=
"
flex items-center justify-between rounded border border-gray-200 px-4 py-3 dark:border-dark-700
"
>
<
div
>
<
label
class
=
"
mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300
"
<
div
class
=
"
font-medium text-gray-900 dark:text-white
"
>
{{
localText
(
"
微信环境使用公众号
"
,
"
Use MP inside WeChat
"
,
)
}}
<
/div
>
<
p
class
=
"
mt-0.5 text-xs text-gray-500 dark:text-gray-400
"
>
{{
localText
(
"
Scopes
"
,
"
Scopes
"
)
}}
<
/label
>
<
input
data
-
testid
=
"
wechat-connect-scopes
"
v
-
model
=
"
form.wechat_connect_scopes
"
type
=
"
text
"
class
=
"
input font-mono text-sm
"
:
placeholder
=
"
form.wechat_connect_mode === 'mp'
? 'snsapi_userinfo'
: 'snsapi_login'
"
/>
<
p
class
=
"
mt-1.5 text-xs text-gray-500 dark:text-gray-400
"
>
{{
localText
(
"
留空时会按模式自动回填默认值
。
"
,
"
Leave empty to use the default scope for the selected mode
.
"
,
"
浏览器在微信内时,自动走公众号授权
。
"
,
"
Use Official Account authorization inside the WeChat browser
.
"
,
)
}}
<
/p
>
<
/div
>
<
Toggle
v
-
model
=
"
form.wechat_connect_mp_enabled
"
data
-
testid
=
"
wechat-connect-mp-enabled
"
@
update
:
model
-
value
=
"
syncWeChatConnectMode
"
/>
<
/div
>
<
/div
>
<
div
>
<
label
...
...
@@ -2246,13 +2251,14 @@
<
Toggle
v
-
model
=
"
form.force_email_on_third_party_signup
"
/>
<
/div
>
<
div
class
=
"
grid grid-cols-1 gap-4 xl:grid-cols-2
"
>
<
div
class
=
"
space-y-4
"
>
<
div
v
-
for
=
"
authSource in authSourceDefaultsMeta
"
:
key
=
"
authSource.source
"
class
=
"
rounded-xl border border-gray-200 p-4 dark:border-dark-700
"
>
<
div
class
=
"
mb-4
"
>
<
div
class
=
"
flex items-center justify-between gap-4
"
>
<
div
>
<
div
class
=
"
font-medium text-gray-900 dark:text-white
"
>
{{
authSource
.
title
}}
<
/div
>
...
...
@@ -2260,6 +2266,27 @@
{{
authSource
.
description
}}
<
/p
>
<
/div
>
<
Toggle
v
-
model
=
"
authSourceDefaults[authSource.source].grant_on_signup
"
:
data
-
testid
=
"
`auth-source-${authSource.source
}
-enabled`
"
/>
<
/div
>
<
div
v
-
if
=
"
authSourceDefaults[authSource.source].grant_on_signup
"
:
data
-
testid
=
"
`auth-source-${authSource.source
}
-panel`
"
class
=
"
mt-4 space-y-4 border-t border-gray-100 pt-4 dark:border-dark-700
"
>
<
p
class
=
"
text-sm text-gray-500 dark:text-gray-400
"
>
{{
localText
(
"
以下默认值会在该来源注册新用户时发放;首次绑定时授权仅作用于已有账号绑定该来源。
"
,
"
These defaults apply when a new user registers through this source. Grant on first bind only applies when an existing user binds this source.
"
,
)
}}
<
/p
>
<
div
class
=
"
grid grid-cols-1 gap-4 md:grid-cols-2
"
>
<
div
>
...
...
@@ -2297,34 +2324,6 @@
<
/div
>
<
/div
>
<
div
class
=
"
mt-4 grid grid-cols-1 gap-3 md:grid-cols-2
"
>
<
div
class
=
"
flex items-center justify-between rounded border border-gray-200 px-4 py-3 dark:border-dark-700
"
>
<
div
>
<
label
class
=
"
font-medium text-gray-900 dark:text-white
"
>
{{
localText
(
"
注册即授权
"
,
"
Grant on signup
"
)
}}
<
/label
>
<
p
class
=
"
mt-0.5 text-xs text-gray-500 dark:text-gray-400
"
>
{{
localText
(
"
来源首次注册成功后立即发放默认权益。
"
,
"
Grant default entitlements immediately after signup.
"
,
)
}}
<
/p
>
<
/div
>
<
Toggle
v
-
model
=
"
authSourceDefaults[authSource.source].grant_on_signup
"
/>
<
/div
>
<
div
class
=
"
flex items-center justify-between rounded border border-gray-200 px-4 py-3 dark:border-dark-700
"
>
...
...
@@ -2341,8 +2340,8 @@
>
{{
localText
(
"
来源首次绑定到现有账号
时发放默认权益。
"
,
"
Grant default entitlements when
the source is
first b
ou
nd t
o an existing user
.
"
,
"
已有账号首次绑定该来源
时发放默认权益。
"
,
"
Grant default entitlements when
an existing user
first b
i
nd
s
t
his source
.
"
,
)
}}
<
/p
>
...
...
@@ -2354,11 +2353,7 @@
"
/>
<
/div
>
<
/div
>
<
div
class
=
"
mt-4 border-t border-gray-100 pt-4 dark:border-dark-700
"
>
<
div
class
=
"
mb-3 flex items-center justify-between
"
>
<
div
>
<
label
...
...
@@ -4710,12 +4705,13 @@ import { useI18n } from "vue-i18n";
import
{
adminAPI
}
from
"
@/api
"
;
import
{
appendAuthSourceDefaultsToUpdateRequest
,
defaultWeChatConnectScopesForMode
,
buildAuthSourceDefaultsState
,
defaultWeChatConnectScopesForMode
,
deriveWeChatConnectStoredMode
,
getPaymentVisibleMethodSourceOptions
,
normalizePaymentVisibleMethodSource
,
normalizeDefaultSubscriptionSettings
,
normaliz
eWeChatConnectMode
,
resolv
eWeChatConnectMode
Capabilities
,
}
from
"
@/api/admin/settings
"
;
import
type
{
AuthSourceDefaultsState
,
...
...
@@ -4859,11 +4855,16 @@ interface DefaultSubscriptionGroupOption {
[
key
:
string
]:
unknown
;
}
type
SettingsForm
=
SystemSettings
&
{
type
SettingsForm
=
Omit
<
SystemSettings
,
"
wechat_connect_open_enabled
"
|
"
wechat_connect_mp_enabled
"
>
&
{
smtp_password
:
string
;
turnstile_secret_key
:
string
;
linuxdo_connect_client_secret
:
string
;
wechat_connect_app_secret
:
string
;
wechat_connect_open_enabled
:
boolean
;
wechat_connect_mp_enabled
:
boolean
;
oidc_connect_client_secret
:
string
;
force_email_on_third_party_signup
:
boolean
;
payment_visible_method_alipay_source
:
string
;
...
...
@@ -4958,6 +4959,8 @@ const form = reactive<SettingsForm>({
wechat_connect_app_id
:
""
,
wechat_connect_app_secret
:
""
,
wechat_connect_app_secret_configured
:
false
,
wechat_connect_open_enabled
:
false
,
wechat_connect_mp_enabled
:
false
,
wechat_connect_mode
:
"
open
"
,
wechat_connect_scopes
:
"
snsapi_login
"
,
wechat_connect_redirect_url
:
""
,
...
...
@@ -5452,14 +5455,21 @@ const wechatRedirectUrlSuggestion = computed(() => {
}
);
function
syncWeChatConnectMode
()
{
form
.
wechat_connect_mode
=
normalizeWeChatConnectMode
(
const
capabilities
=
resolveWeChatConnectModeCapabilities
(
form
.
wechat_connect_open_enabled
,
form
.
wechat_connect_mp_enabled
,
form
.
wechat_connect_mode
,
);
form
.
wechat_connect_open_enabled
=
capabilities
.
openEnabled
;
form
.
wechat_connect_mp_enabled
=
capabilities
.
mpEnabled
;
form
.
wechat_connect_mode
=
deriveWeChatConnectStoredMode
(
capabilities
.
openEnabled
,
capabilities
.
mpEnabled
,
form
.
wechat_connect_mode
,
);
if
(
!
form
.
wechat_connect_scopes
.
trim
())
{
form
.
wechat_connect_scopes
=
defaultWeChatConnectScopesForMode
(
form
.
wechat_connect_mode
,
);
}
}
async
function
setAndCopyWeChatRedirectUrl
()
{
...
...
@@ -5608,16 +5618,21 @@ async function loadSettings() {
form
.
turnstile_secret_key
=
""
;
form
.
linuxdo_connect_client_secret
=
""
;
form
.
wechat_connect_app_secret
=
""
;
form
.
wechat_connect_mode
=
normalizeWeChatConnectMode
(
const
wechatCapabilities
=
resolveWeChatConnectModeCapabilities
(
settings
.
wechat_connect_open_enabled
,
settings
.
wechat_connect_mp_enabled
,
settings
.
wechat_connect_mode
,
);
const
wechatConnectScopes
=
typeof
settings
.
wechat_connect_scopes
===
"
string
"
?
settings
.
wechat_connect_scopes
.
trim
()
:
""
;
form
.
wechat_connect_scopes
=
wechatConnectScopes
||
defaultWeChatConnectScopesForMode
(
form
.
wechat_connect_mode
);
form
.
wechat_connect_open_enabled
=
wechatCapabilities
.
openEnabled
;
form
.
wechat_connect_mp_enabled
=
wechatCapabilities
.
mpEnabled
;
form
.
wechat_connect_mode
=
deriveWeChatConnectStoredMode
(
wechatCapabilities
.
openEnabled
,
wechatCapabilities
.
mpEnabled
,
settings
.
wechat_connect_mode
,
);
form
.
wechat_connect_scopes
=
defaultWeChatConnectScopesForMode
(
form
.
wechat_connect_mode
,
);
form
.
oidc_connect_client_secret
=
""
;
// Load web search emulation config separately
...
...
@@ -5789,6 +5804,12 @@ async function saveSettings() {
// Optional URL fields: auto-clear invalid values so they don't cause backend 400 errors
if
(
!
isValidHttpUrl
(
form
.
frontend_url
))
form
.
frontend_url
=
""
;
if
(
!
isValidHttpUrl
(
form
.
doc_url
))
form
.
doc_url
=
""
;
syncWeChatConnectMode
();
const
wechatStoredMode
=
deriveWeChatConnectStoredMode
(
form
.
wechat_connect_open_enabled
,
form
.
wechat_connect_mp_enabled
,
form
.
wechat_connect_mode
,
);
const
payload
:
UpdateSettingsRequest
=
{
registration_enabled
:
form
.
registration_enabled
,
...
...
@@ -5837,10 +5858,11 @@ async function saveSettings() {
wechat_connect_enabled
:
form
.
wechat_connect_enabled
,
wechat_connect_app_id
:
form
.
wechat_connect_app_id
,
wechat_connect_app_secret
:
form
.
wechat_connect_app_secret
||
undefined
,
wechat_connect_mode
:
normalizeWeChatConnectMode
(
form
.
wechat_connect_mode
),
wechat_connect_open_enabled
:
form
.
wechat_connect_open_enabled
,
wechat_connect_mp_enabled
:
form
.
wechat_connect_mp_enabled
,
wechat_connect_mode
:
wechatStoredMode
,
wechat_connect_scopes
:
form
.
wechat_connect_scopes
.
trim
()
||
defaultWeChatConnectScopesForMode
(
form
.
wechat_connect_mode
),
defaultWeChatConnectScopesForMode
(
wechatStoredMode
),
wechat_connect_redirect_url
:
form
.
wechat_connect_redirect_url
,
wechat_connect_frontend_redirect_url
:
form
.
wechat_connect_frontend_redirect_url
,
...
...
@@ -5967,16 +5989,21 @@ async function saveSettings() {
form
.
turnstile_secret_key
=
""
;
form
.
linuxdo_connect_client_secret
=
""
;
form
.
wechat_connect_app_secret
=
""
;
form
.
wechat_connect_mode
=
normalizeWeChatConnectMode
(
const
updatedWechatCapabilities
=
resolveWeChatConnectModeCapabilities
(
updated
.
wechat_connect_open_enabled
,
updated
.
wechat_connect_mp_enabled
,
updated
.
wechat_connect_mode
,
);
const
updatedWechatConnectScopes
=
typeof
updated
.
wechat_connect_scopes
===
"
string
"
?
updated
.
wechat_connect_scopes
.
trim
()
:
""
;
form
.
wechat_connect_scopes
=
updatedWechatConnectScopes
||
defaultWeChatConnectScopesForMode
(
form
.
wechat_connect_mode
);
form
.
wechat_connect_open_enabled
=
updatedWechatCapabilities
.
openEnabled
;
form
.
wechat_connect_mp_enabled
=
updatedWechatCapabilities
.
mpEnabled
;
form
.
wechat_connect_mode
=
deriveWeChatConnectStoredMode
(
updatedWechatCapabilities
.
openEnabled
,
updatedWechatCapabilities
.
mpEnabled
,
updated
.
wechat_connect_mode
,
);
form
.
wechat_connect_scopes
=
defaultWeChatConnectScopesForMode
(
form
.
wechat_connect_mode
,
);
form
.
oidc_connect_client_secret
=
""
;
// Save web search emulation config separately (errors handled internally)
const
wsOk
=
await
saveWebSearchConfig
();
...
...
frontend/src/views/admin/__tests__/SettingsView.spec.ts
View file @
2cebb0dc
...
...
@@ -111,9 +111,11 @@ const ToggleStub = defineComponent({
},
},
emits
:
[
"
update:modelValue
"
],
setup
(
props
,
{
emit
})
{
inheritAttrs
:
false
,
setup
(
props
,
{
attrs
,
emit
})
{
return
()
=>
h
(
"
input
"
,
{
...
attrs
,
class
:
"
toggle-stub
"
,
type
:
"
checkbox
"
,
checked
:
props
.
modelValue
,
...
...
@@ -217,6 +219,8 @@ const baseSettingsResponse = {
wechat_connect_enabled
:
true
,
wechat_connect_app_id
:
"
wx-app-id-123
"
,
wechat_connect_app_secret_configured
:
true
,
wechat_connect_open_enabled
:
false
,
wechat_connect_mp_enabled
:
true
,
wechat_connect_mode
:
"
mp
"
,
wechat_connect_scopes
:
""
,
wechat_connect_redirect_url
:
...
...
@@ -334,6 +338,16 @@ async function openSecurityTab(wrapper: ReturnType<typeof mountView>) {
await
flushPromises
();
}
async
function
openUsersTab
(
wrapper
:
ReturnType
<
typeof
mountView
>
)
{
const
usersTabButton
=
wrapper
.
findAll
(
"
button
"
)
.
find
((
node
)
=>
node
.
text
().
includes
(
"
admin.settings.tabs.users
"
));
expect
(
usersTabButton
).
toBeDefined
();
await
usersTabButton
?.
trigger
(
"
click
"
);
await
flushPromises
();
}
describe
(
"
admin SettingsView payment visible method controls
"
,
()
=>
{
beforeEach
(()
=>
{
getSettings
.
mockReset
();
...
...
@@ -595,16 +609,19 @@ describe("admin SettingsView wechat connect controls", () => {
).
toBe
(
"
wx-app-id-123
"
);
expect
(
(
wrapper
.
get
(
'
[data-testid="wechat-connect-
mode
"]
'
)
.
element
as
HTML
Selec
tElement
).
value
,
).
toBe
(
"
mp
"
);
wrapper
.
get
(
'
[data-testid="wechat-connect-
open-enabled
"]
'
)
.
element
as
HTML
Inpu
tElement
).
checked
,
).
toBe
(
false
);
expect
(
(
wrapper
.
get
(
'
[data-testid="wechat-connect-
scopes
"]
'
)
wrapper
.
get
(
'
[data-testid="wechat-connect-
mp-enabled
"]
'
)
.
element
as
HTMLInputElement
).
value
,
).
toBe
(
"
snsapi_userinfo
"
);
).
checked
,
).
toBe
(
true
);
expect
(
wrapper
.
find
(
'
[data-testid="wechat-connect-scopes"]
'
).
exists
()).
toBe
(
false
,
);
expect
(
wrapper
.
get
(
'
[data-testid="wechat-connect-app-secret"]
'
)
...
...
@@ -630,10 +647,12 @@ describe("admin SettingsView wechat connect controls", () => {
await
wrapper
.
get
(
'
[data-testid="wechat-connect-app-secret"]
'
)
.
setValue
(
"
new-secret
"
);
await
wrapper
.
get
(
'
[data-testid="wechat-connect-mode"]
'
).
setValue
(
"
open
"
);
await
wrapper
.
get
(
'
[data-testid="wechat-connect-scopes"]
'
)
.
setValue
(
"
snsapi_base
"
);
.
get
(
'
[data-testid="wechat-connect-open-enabled"]
'
)
.
setValue
(
true
);
await
wrapper
.
get
(
'
[data-testid="wechat-connect-mp-enabled"]
'
)
.
setValue
(
true
);
await
wrapper
.
get
(
'
[data-testid="wechat-connect-redirect-url"]
'
)
.
setValue
(
"
https://admin.example.com/api/v1/auth/oauth/wechat/callback
"
);
...
...
@@ -649,8 +668,8 @@ describe("admin SettingsView wechat connect controls", () => {
wechat_connect_enabled
:
true
,
wechat_connect_app_id
:
"
wx-app-id-updated
"
,
wechat_connect_app_secret
:
"
new-secret
"
,
wechat_connect_
mode
:
"
open
"
,
wechat_connect_
scopes
:
"
snsapi_base
"
,
wechat_connect_
open_enabled
:
true
,
wechat_connect_
mp_enabled
:
true
,
wechat_connect_redirect_url
:
"
https://admin.example.com/api/v1/auth/oauth/wechat/callback
"
,
wechat_connect_frontend_redirect_url
:
"
/auth/wechat/callback
"
,
...
...
@@ -668,4 +687,31 @@ describe("admin SettingsView wechat connect controls", () => {
.
attributes
(
"
placeholder
"
),
).
toContain
(
"
密钥已配置
"
);
});
it
(
"
collapses auth source defaults until the source is enabled
"
,
async
()
=>
{
const
wrapper
=
mountView
();
await
flushPromises
();
await
openUsersTab
(
wrapper
);
expect
(
(
wrapper
.
get
(
'
[data-testid="auth-source-email-enabled"]
'
)
.
element
as
HTMLInputElement
).
checked
,
).
toBe
(
false
);
expect
(
wrapper
.
find
(
'
[data-testid="auth-source-email-panel"]
'
).
exists
(),
).
toBe
(
false
);
expect
(
wrapper
.
text
()).
not
.
toContain
(
"
注册即授权
"
);
await
wrapper
.
get
(
'
[data-testid="auth-source-email-enabled"]
'
)
.
setValue
(
true
);
expect
(
wrapper
.
find
(
'
[data-testid="auth-source-email-panel"]
'
).
exists
(),
).
toBe
(
true
);
expect
(
wrapper
.
text
()).
toContain
(
"
首次绑定时授权
"
);
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment