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
27abae21
Unverified
Commit
27abae21
authored
Mar 04, 2026
by
Wesley Liddick
Committed by
GitHub
Mar 04, 2026
Browse files
Merge pull request #724 from PMExtra/feat/registration-email-domain-whitelist
feat(registration): add email domain whitelist policy
parents
0819c8a5
29fb447d
Changes
25
Hide whitespace changes
Inline
Side-by-side
frontend/src/utils/authError.ts
0 → 100644
View file @
27abae21
interface
APIErrorLike
{
message
?:
string
response
?:
{
data
?:
{
detail
?:
string
message
?:
string
}
}
}
function
extractErrorMessage
(
error
:
unknown
):
string
{
const
err
=
(
error
||
{})
as
APIErrorLike
return
err
.
response
?.
data
?.
detail
||
err
.
response
?.
data
?.
message
||
err
.
message
||
''
}
export
function
buildAuthErrorMessage
(
error
:
unknown
,
options
:
{
fallback
:
string
}
):
string
{
const
{
fallback
}
=
options
const
message
=
extractErrorMessage
(
error
)
return
message
||
fallback
}
frontend/src/utils/registrationEmailPolicy.ts
0 → 100644
View file @
27abae21
const
EMAIL_SUFFIX_TOKEN_SPLIT_RE
=
/
[\s
,,
]
+/
const
EMAIL_SUFFIX_INVALID_CHAR_RE
=
/
[^
a-z0-9.-
]
/g
const
EMAIL_SUFFIX_INVALID_CHAR_CHECK_RE
=
/
[^
a-z0-9.-
]
/
const
EMAIL_SUFFIX_PREFIX_RE
=
/^@+/
const
EMAIL_SUFFIX_DOMAIN_PATTERN
=
/^
[
a-z0-9
](?:[
a-z0-9-
]{0,61}[
a-z0-9
])?(?:\.[
a-z0-9
](?:[
a-z0-9-
]{0,61}[
a-z0-9
])?)
+$/
// normalizeRegistrationEmailSuffixDomain converts raw input into a canonical domain token.
// It removes leading "@", lowercases input, and strips all invalid characters.
export
function
normalizeRegistrationEmailSuffixDomain
(
raw
:
string
):
string
{
let
value
=
String
(
raw
||
''
).
trim
().
toLowerCase
()
if
(
!
value
)
{
return
''
}
value
=
value
.
replace
(
EMAIL_SUFFIX_PREFIX_RE
,
''
)
value
=
value
.
replace
(
EMAIL_SUFFIX_INVALID_CHAR_RE
,
''
)
return
value
}
export
function
normalizeRegistrationEmailSuffixDomains
(
items
:
string
[]
|
null
|
undefined
):
string
[]
{
if
(
!
items
||
items
.
length
===
0
)
{
return
[]
}
const
seen
=
new
Set
<
string
>
()
const
normalized
:
string
[]
=
[]
for
(
const
item
of
items
)
{
const
domain
=
normalizeRegistrationEmailSuffixDomain
(
item
)
if
(
!
isRegistrationEmailSuffixDomainValid
(
domain
)
||
seen
.
has
(
domain
))
{
continue
}
seen
.
add
(
domain
)
normalized
.
push
(
domain
)
}
return
normalized
}
export
function
parseRegistrationEmailSuffixWhitelistInput
(
input
:
string
):
string
[]
{
if
(
!
input
||
!
input
.
trim
())
{
return
[]
}
const
seen
=
new
Set
<
string
>
()
const
normalized
:
string
[]
=
[]
for
(
const
token
of
input
.
split
(
EMAIL_SUFFIX_TOKEN_SPLIT_RE
))
{
const
domain
=
normalizeRegistrationEmailSuffixDomainStrict
(
token
)
if
(
!
isRegistrationEmailSuffixDomainValid
(
domain
)
||
seen
.
has
(
domain
))
{
continue
}
seen
.
add
(
domain
)
normalized
.
push
(
domain
)
}
return
normalized
}
export
function
normalizeRegistrationEmailSuffixWhitelist
(
items
:
string
[]
|
null
|
undefined
):
string
[]
{
return
normalizeRegistrationEmailSuffixDomains
(
items
).
map
((
domain
)
=>
`@
${
domain
}
`
)
}
function
extractRegistrationEmailDomain
(
email
:
string
):
string
{
const
raw
=
String
(
email
||
''
).
trim
().
toLowerCase
()
if
(
!
raw
)
{
return
''
}
const
atIndex
=
raw
.
indexOf
(
'
@
'
)
if
(
atIndex
<=
0
||
atIndex
>=
raw
.
length
-
1
)
{
return
''
}
if
(
raw
.
indexOf
(
'
@
'
,
atIndex
+
1
)
!==
-
1
)
{
return
''
}
return
raw
.
slice
(
atIndex
+
1
)
}
export
function
isRegistrationEmailSuffixAllowed
(
email
:
string
,
whitelist
:
string
[]
|
null
|
undefined
):
boolean
{
const
normalizedWhitelist
=
normalizeRegistrationEmailSuffixWhitelist
(
whitelist
)
if
(
normalizedWhitelist
.
length
===
0
)
{
return
true
}
const
emailDomain
=
extractRegistrationEmailDomain
(
email
)
if
(
!
emailDomain
)
{
return
false
}
const
emailSuffix
=
`@
${
emailDomain
}
`
return
normalizedWhitelist
.
includes
(
emailSuffix
)
}
// Pasted domains should be strict: any invalid character drops the whole token.
function
normalizeRegistrationEmailSuffixDomainStrict
(
raw
:
string
):
string
{
let
value
=
String
(
raw
||
''
).
trim
().
toLowerCase
()
if
(
!
value
)
{
return
''
}
value
=
value
.
replace
(
EMAIL_SUFFIX_PREFIX_RE
,
''
)
if
(
!
value
||
EMAIL_SUFFIX_INVALID_CHAR_CHECK_RE
.
test
(
value
))
{
return
''
}
return
value
}
export
function
isRegistrationEmailSuffixDomainValid
(
domain
:
string
):
boolean
{
if
(
!
domain
)
{
return
false
}
return
EMAIL_SUFFIX_DOMAIN_PATTERN
.
test
(
domain
)
}
frontend/src/views/admin/SettingsView.vue
View file @
27abae21
...
...
@@ -324,6 +324,56 @@
<Toggle
v-model=
"form.email_verify_enabled"
/>
</div>
<!-- Email Suffix Whitelist -->
<div
class=
"border-t border-gray-100 pt-4 dark:border-dark-700"
>
<label
class=
"font-medium text-gray-900 dark:text-white"
>
{{
t('admin.settings.registration.emailSuffixWhitelist')
}}
</label>
<p
class=
"mt-1 text-sm text-gray-500 dark:text-gray-400"
>
{{ t('admin.settings.registration.emailSuffixWhitelistHint') }}
</p>
<div
class=
"mt-3 rounded-lg border border-gray-300 bg-white p-2 dark:border-dark-500 dark:bg-dark-700"
>
<div
class=
"flex flex-wrap items-center gap-2"
>
<span
v-for=
"suffix in registrationEmailSuffixWhitelistTags"
:key=
"suffix"
class=
"inline-flex items-center gap-1 rounded bg-gray-100 px-2 py-1 text-xs font-mono text-gray-700 dark:bg-dark-600 dark:text-gray-200"
>
<span
class=
"text-gray-400 dark:text-gray-500"
>
@
</span>
<span>
{{ suffix }}
</span>
<button
type=
"button"
class=
"rounded-full text-gray-500 hover:bg-gray-200 hover:text-gray-700 dark:text-gray-300 dark:hover:bg-dark-500 dark:hover:text-white"
@
click=
"removeRegistrationEmailSuffixWhitelistTag(suffix)"
>
<Icon
name=
"x"
size=
"xs"
class=
"h-3.5 w-3.5"
:stroke-width=
"2"
/>
</button>
</span>
<div
class=
"flex min-w-[220px] flex-1 items-center gap-1 rounded border border-transparent px-2 py-1 focus-within:border-primary-300 dark:focus-within:border-primary-700"
>
<span
class=
"font-mono text-sm text-gray-400 dark:text-gray-500"
>
@
</span>
<input
v-model=
"registrationEmailSuffixWhitelistDraft"
type=
"text"
class=
"w-full bg-transparent text-sm font-mono text-gray-900 outline-none placeholder:text-gray-400 dark:text-white dark:placeholder:text-gray-500"
:placeholder=
"t('admin.settings.registration.emailSuffixWhitelistPlaceholder')"
@
input=
"handleRegistrationEmailSuffixWhitelistDraftInput"
@
keydown=
"handleRegistrationEmailSuffixWhitelistDraftKeydown"
@
blur=
"commitRegistrationEmailSuffixWhitelistDraft"
@
paste=
"handleRegistrationEmailSuffixWhitelistPaste"
/>
</div>
</div>
</div>
<p
class=
"mt-2 text-xs text-gray-500 dark:text-gray-400"
>
{{ t('admin.settings.registration.emailSuffixWhitelistInputHint') }}
</p>
</div>
<!-- Promo Code -->
<div
class=
"flex items-center justify-between border-t border-gray-100 pt-4 dark:border-dark-700"
...
...
@@ -1364,6 +1414,12 @@ import ImageUpload from '@/components/common/ImageUpload.vue'
import
{
useClipboard
}
from
'
@/composables/useClipboard
'
import
{
useAppStore
}
from
'
@/stores
'
import
{
useAdminSettingsStore
}
from
'
@/stores/adminSettings
'
import
{
isRegistrationEmailSuffixDomainValid
,
normalizeRegistrationEmailSuffixDomain
,
normalizeRegistrationEmailSuffixDomains
,
parseRegistrationEmailSuffixWhitelistInput
}
from
'
@/utils/registrationEmailPolicy
'
const
{
t
}
=
useI18n
()
const
appStore
=
useAppStore
()
...
...
@@ -1375,6 +1431,8 @@ const saving = ref(false)
const
testingSmtp
=
ref
(
false
)
const
sendingTestEmail
=
ref
(
false
)
const
testEmailAddress
=
ref
(
''
)
const
registrationEmailSuffixWhitelistTags
=
ref
<
string
[]
>
([])
const
registrationEmailSuffixWhitelistDraft
=
ref
(
''
)
// Admin API Key 状态
const
adminApiKeyLoading
=
ref
(
true
)
...
...
@@ -1414,6 +1472,7 @@ type SettingsForm = SystemSettings & {
const
form
=
reactive
<
SettingsForm
>
({
registration_enabled
:
true
,
email_verify_enabled
:
false
,
registration_email_suffix_whitelist
:
[],
promo_code_enabled
:
true
,
invitation_code_enabled
:
false
,
password_reset_enabled
:
false
,
...
...
@@ -1484,6 +1543,74 @@ const defaultSubscriptionGroupOptions = computed<DefaultSubscriptionGroupOption[
}))
)
const
registrationEmailSuffixWhitelistSeparatorKeys
=
new
Set
([
'
'
,
'
,
'
,
'
,
'
,
'
Enter
'
,
'
Tab
'
])
function
removeRegistrationEmailSuffixWhitelistTag
(
suffix
:
string
)
{
registrationEmailSuffixWhitelistTags
.
value
=
registrationEmailSuffixWhitelistTags
.
value
.
filter
(
(
item
)
=>
item
!==
suffix
)
}
function
addRegistrationEmailSuffixWhitelistTag
(
raw
:
string
)
{
const
suffix
=
normalizeRegistrationEmailSuffixDomain
(
raw
)
if
(
!
isRegistrationEmailSuffixDomainValid
(
suffix
)
||
registrationEmailSuffixWhitelistTags
.
value
.
includes
(
suffix
)
)
{
return
}
registrationEmailSuffixWhitelistTags
.
value
=
[
...
registrationEmailSuffixWhitelistTags
.
value
,
suffix
]
}
function
commitRegistrationEmailSuffixWhitelistDraft
()
{
if
(
!
registrationEmailSuffixWhitelistDraft
.
value
)
{
return
}
addRegistrationEmailSuffixWhitelistTag
(
registrationEmailSuffixWhitelistDraft
.
value
)
registrationEmailSuffixWhitelistDraft
.
value
=
''
}
function
handleRegistrationEmailSuffixWhitelistDraftInput
()
{
registrationEmailSuffixWhitelistDraft
.
value
=
normalizeRegistrationEmailSuffixDomain
(
registrationEmailSuffixWhitelistDraft
.
value
)
}
function
handleRegistrationEmailSuffixWhitelistDraftKeydown
(
event
:
KeyboardEvent
)
{
if
(
event
.
isComposing
)
{
return
}
if
(
registrationEmailSuffixWhitelistSeparatorKeys
.
has
(
event
.
key
))
{
event
.
preventDefault
()
commitRegistrationEmailSuffixWhitelistDraft
()
return
}
if
(
event
.
key
===
'
Backspace
'
&&
!
registrationEmailSuffixWhitelistDraft
.
value
&&
registrationEmailSuffixWhitelistTags
.
value
.
length
>
0
)
{
registrationEmailSuffixWhitelistTags
.
value
.
pop
()
}
}
function
handleRegistrationEmailSuffixWhitelistPaste
(
event
:
ClipboardEvent
)
{
const
text
=
event
.
clipboardData
?.
getData
(
'
text
'
)
||
''
if
(
!
text
.
trim
())
{
return
}
event
.
preventDefault
()
const
tokens
=
parseRegistrationEmailSuffixWhitelistInput
(
text
)
for
(
const
token
of
tokens
)
{
addRegistrationEmailSuffixWhitelistTag
(
token
)
}
}
// LinuxDo OAuth redirect URL suggestion
const
linuxdoRedirectUrlSuggestion
=
computed
(()
=>
{
if
(
typeof
window
===
'
undefined
'
)
return
''
...
...
@@ -1546,6 +1673,10 @@ async function loadSettings() {
validity_days
:
item
.
validity_days
}))
:
[]
registrationEmailSuffixWhitelistTags
.
value
=
normalizeRegistrationEmailSuffixDomains
(
settings
.
registration_email_suffix_whitelist
)
registrationEmailSuffixWhitelistDraft
.
value
=
''
form
.
smtp_password
=
''
form
.
turnstile_secret_key
=
''
form
.
linuxdo_connect_client_secret
=
''
...
...
@@ -1615,6 +1746,9 @@ async function saveSettings() {
const
payload
:
UpdateSettingsRequest
=
{
registration_enabled
:
form
.
registration_enabled
,
email_verify_enabled
:
form
.
email_verify_enabled
,
registration_email_suffix_whitelist
:
registrationEmailSuffixWhitelistTags
.
value
.
map
(
(
suffix
)
=>
`@
${
suffix
}
`
),
promo_code_enabled
:
form
.
promo_code_enabled
,
invitation_code_enabled
:
form
.
invitation_code_enabled
,
password_reset_enabled
:
form
.
password_reset_enabled
,
...
...
@@ -1660,6 +1794,10 @@ async function saveSettings() {
}
const
updated
=
await
adminAPI
.
settings
.
updateSettings
(
payload
)
Object
.
assign
(
form
,
updated
)
registrationEmailSuffixWhitelistTags
.
value
=
normalizeRegistrationEmailSuffixDomains
(
updated
.
registration_email_suffix_whitelist
)
registrationEmailSuffixWhitelistDraft
.
value
=
''
form
.
smtp_password
=
''
form
.
turnstile_secret_key
=
''
form
.
linuxdo_connect_client_secret
=
''
...
...
frontend/src/views/auth/EmailVerifyView.vue
View file @
27abae21
...
...
@@ -177,8 +177,13 @@ import Icon from '@/components/icons/Icon.vue'
import
TurnstileWidget
from
'
@/components/TurnstileWidget.vue
'
import
{
useAuthStore
,
useAppStore
}
from
'
@/stores
'
import
{
getPublicSettings
,
sendVerifyCode
}
from
'
@/api/auth
'
import
{
buildAuthErrorMessage
}
from
'
@/utils/authError
'
import
{
isRegistrationEmailSuffixAllowed
,
normalizeRegistrationEmailSuffixWhitelist
}
from
'
@/utils/registrationEmailPolicy
'
const
{
t
}
=
useI18n
()
const
{
t
,
locale
}
=
useI18n
()
// ==================== Router & Stores ====================
...
...
@@ -208,6 +213,7 @@ const hasRegisterData = ref<boolean>(false)
const
turnstileEnabled
=
ref
<
boolean
>
(
false
)
const
turnstileSiteKey
=
ref
<
string
>
(
''
)
const
siteName
=
ref
<
string
>
(
'
Sub2API
'
)
const
registrationEmailSuffixWhitelist
=
ref
<
string
[]
>
([])
// Turnstile for resend
const
turnstileRef
=
ref
<
InstanceType
<
typeof
TurnstileWidget
>
|
null
>
(
null
)
...
...
@@ -244,6 +250,9 @@ onMounted(async () => {
turnstileEnabled
.
value
=
settings
.
turnstile_enabled
turnstileSiteKey
.
value
=
settings
.
turnstile_site_key
||
''
siteName
.
value
=
settings
.
site_name
||
'
Sub2API
'
registrationEmailSuffixWhitelist
.
value
=
normalizeRegistrationEmailSuffixWhitelist
(
settings
.
registration_email_suffix_whitelist
||
[]
)
}
catch
(
error
)
{
console
.
error
(
'
Failed to load public settings:
'
,
error
)
}
...
...
@@ -306,6 +315,12 @@ async function sendCode(): Promise<void> {
errorMessage
.
value
=
''
try
{
if
(
!
isRegistrationEmailSuffixAllowed
(
email
.
value
,
registrationEmailSuffixWhitelist
.
value
))
{
errorMessage
.
value
=
buildEmailSuffixNotAllowedMessage
()
appStore
.
showError
(
errorMessage
.
value
)
return
}
const
response
=
await
sendVerifyCode
({
email
:
email
.
value
,
// 优先使用重发时新获取的 token(因为初始 token 可能已被使用)
...
...
@@ -320,15 +335,9 @@ async function sendCode(): Promise<void> {
showResendTurnstile
.
value
=
false
resendTurnstileToken
.
value
=
''
}
catch
(
error
:
unknown
)
{
const
err
=
error
as
{
message
?:
string
;
response
?:
{
data
?:
{
detail
?:
string
}
}
}
if
(
err
.
response
?.
data
?.
detail
)
{
errorMessage
.
value
=
err
.
response
.
data
.
detail
}
else
if
(
err
.
message
)
{
errorMessage
.
value
=
err
.
message
}
else
{
errorMessage
.
value
=
'
Failed to send verification code. Please try again.
'
}
errorMessage
.
value
=
buildAuthErrorMessage
(
error
,
{
fallback
:
'
Failed to send verification code. Please try again.
'
})
appStore
.
showError
(
errorMessage
.
value
)
}
finally
{
...
...
@@ -380,6 +389,12 @@ async function handleVerify(): Promise<void> {
isLoading
.
value
=
true
try
{
if
(
!
isRegistrationEmailSuffixAllowed
(
email
.
value
,
registrationEmailSuffixWhitelist
.
value
))
{
errorMessage
.
value
=
buildEmailSuffixNotAllowedMessage
()
appStore
.
showError
(
errorMessage
.
value
)
return
}
// Register with verification code
await
authStore
.
register
({
email
:
email
.
value
,
...
...
@@ -399,15 +414,9 @@ async function handleVerify(): Promise<void> {
// Redirect to dashboard
await
router
.
push
(
'
/dashboard
'
)
}
catch
(
error
:
unknown
)
{
const
err
=
error
as
{
message
?:
string
;
response
?:
{
data
?:
{
detail
?:
string
}
}
}
if
(
err
.
response
?.
data
?.
detail
)
{
errorMessage
.
value
=
err
.
response
.
data
.
detail
}
else
if
(
err
.
message
)
{
errorMessage
.
value
=
err
.
message
}
else
{
errorMessage
.
value
=
'
Verification failed. Please try again.
'
}
errorMessage
.
value
=
buildAuthErrorMessage
(
error
,
{
fallback
:
'
Verification failed. Please try again.
'
})
appStore
.
showError
(
errorMessage
.
value
)
}
finally
{
...
...
@@ -422,6 +431,19 @@ function handleBack(): void {
// Go back to registration
router
.
push
(
'
/register
'
)
}
function
buildEmailSuffixNotAllowedMessage
():
string
{
const
normalizedWhitelist
=
normalizeRegistrationEmailSuffixWhitelist
(
registrationEmailSuffixWhitelist
.
value
)
if
(
normalizedWhitelist
.
length
===
0
)
{
return
t
(
'
auth.emailSuffixNotAllowed
'
)
}
const
separator
=
String
(
locale
.
value
||
''
).
toLowerCase
().
startsWith
(
'
zh
'
)
?
'
、
'
:
'
,
'
return
t
(
'
auth.emailSuffixNotAllowedWithAllowed
'
,
{
suffixes
:
normalizedWhitelist
.
join
(
separator
)
})
}
</
script
>
<
style
scoped
>
...
...
frontend/src/views/auth/RegisterView.vue
View file @
27abae21
...
...
@@ -293,8 +293,13 @@ import Icon from '@/components/icons/Icon.vue'
import
TurnstileWidget
from
'
@/components/TurnstileWidget.vue
'
import
{
useAuthStore
,
useAppStore
}
from
'
@/stores
'
import
{
getPublicSettings
,
validatePromoCode
,
validateInvitationCode
}
from
'
@/api/auth
'
import
{
buildAuthErrorMessage
}
from
'
@/utils/authError
'
import
{
isRegistrationEmailSuffixAllowed
,
normalizeRegistrationEmailSuffixWhitelist
}
from
'
@/utils/registrationEmailPolicy
'
const
{
t
}
=
useI18n
()
const
{
t
,
locale
}
=
useI18n
()
// ==================== Router & Stores ====================
...
...
@@ -319,6 +324,7 @@ const turnstileEnabled = ref<boolean>(false)
const
turnstileSiteKey
=
ref
<
string
>
(
''
)
const
siteName
=
ref
<
string
>
(
'
Sub2API
'
)
const
linuxdoOAuthEnabled
=
ref
<
boolean
>
(
false
)
const
registrationEmailSuffixWhitelist
=
ref
<
string
[]
>
([])
// Turnstile
const
turnstileRef
=
ref
<
InstanceType
<
typeof
TurnstileWidget
>
|
null
>
(
null
)
...
...
@@ -370,6 +376,9 @@ onMounted(async () => {
turnstileSiteKey
.
value
=
settings
.
turnstile_site_key
||
''
siteName
.
value
=
settings
.
site_name
||
'
Sub2API
'
linuxdoOAuthEnabled
.
value
=
settings
.
linuxdo_oauth_enabled
registrationEmailSuffixWhitelist
.
value
=
normalizeRegistrationEmailSuffixWhitelist
(
settings
.
registration_email_suffix_whitelist
||
[]
)
// Read promo code from URL parameter only if promo code is enabled
if
(
promoCodeEnabled
.
value
)
{
...
...
@@ -557,6 +566,19 @@ function validateEmail(email: string): boolean {
return
emailRegex
.
test
(
email
)
}
function
buildEmailSuffixNotAllowedMessage
():
string
{
const
normalizedWhitelist
=
normalizeRegistrationEmailSuffixWhitelist
(
registrationEmailSuffixWhitelist
.
value
)
if
(
normalizedWhitelist
.
length
===
0
)
{
return
t
(
'
auth.emailSuffixNotAllowed
'
)
}
const
separator
=
String
(
locale
.
value
||
''
).
toLowerCase
().
startsWith
(
'
zh
'
)
?
'
、
'
:
'
,
'
return
t
(
'
auth.emailSuffixNotAllowedWithAllowed
'
,
{
suffixes
:
normalizedWhitelist
.
join
(
separator
)
}
)
}
function
validateForm
():
boolean
{
// Reset errors
errors
.
email
=
''
...
...
@@ -573,6 +595,11 @@ function validateForm(): boolean {
}
else
if
(
!
validateEmail
(
formData
.
email
))
{
errors
.
email
=
t
(
'
auth.invalidEmail
'
)
isValid
=
false
}
else
if
(
!
isRegistrationEmailSuffixAllowed
(
formData
.
email
,
registrationEmailSuffixWhitelist
.
value
)
)
{
errors
.
email
=
buildEmailSuffixNotAllowedMessage
()
isValid
=
false
}
// Password validation
...
...
@@ -694,15 +721,9 @@ async function handleRegister(): Promise<void> {
}
// Handle registration error
const
err
=
error
as
{
message
?:
string
;
response
?:
{
data
?:
{
detail
?:
string
}
}
}
if
(
err
.
response
?.
data
?.
detail
)
{
errorMessage
.
value
=
err
.
response
.
data
.
detail
}
else
if
(
err
.
message
)
{
errorMessage
.
value
=
err
.
message
}
else
{
errorMessage
.
value
=
t
(
'
auth.registrationFailed
'
)
}
errorMessage
.
value
=
buildAuthErrorMessage
(
error
,
{
fallback
:
t
(
'
auth.registrationFailed
'
)
}
)
// Also show error toast
appStore
.
showError
(
errorMessage
.
value
)
...
...
Prev
1
2
Next
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