mirror of
https://github.com/Wei-Shaw/sub2api.git
synced 2026-10-29 08:11:32 +00:00
feat: add default-off switch for email domain registration quota
PR #5423 relaxed the email suffix whitelist: once a whitelist is configured, non-whitelisted registrable domains are each allowed to register one account. That behavior activated unconditionally. Add registration_email_domain_quota_enabled (default false) to gate it: - Off (default): restore pre-#5423 strict whitelist semantics — with a non-empty whitelist, non-whitelisted domains are rejected with EMAIL_SUFFIX_NOT_ALLOWED; the register/verify views restore the client-side whitelist pre-check and allowed-domain hint. - On: keep #5423 behavior — one account per non-whitelisted registrable domain (EMAIL_DOMAIN_REGISTRATION_LIMIT). - Empty whitelist keeps allowing all domains in both states. Gating lives in validateRegistrationEmailQuota and (as a race-safety backstop) createUserWithRegistrationEmailGuard; the repository-level domain lock + in-tx recheck is unchanged. The admin update field is *bool (omitted = keep current) so stale full-payload saves cannot silently flip the switch. Email binding and OAuth auto-signup keep their strict policy, and pending-OAuth bind-login for existing accounts is unaffected because the handler resolves existing emails before the quota check. Frontend adds the toggle to admin settings (zh/en copy; whitelist hint restored to strict wording, quota wording moved to the new toggle) and exposes the flag via public settings + SSR injection payload. Tests: #5423 quota tests now enable the switch explicitly; new default-off regression tests cover register/send-code/async/pending OAuth/OIDC create-account plus both register views; API contract JSON and the injection drift guard are updated.
This commit is contained in:
@@ -392,6 +392,7 @@ export interface SystemSettings {
|
||||
registration_enabled: boolean;
|
||||
email_verify_enabled: boolean;
|
||||
registration_email_suffix_whitelist: string[];
|
||||
registration_email_domain_quota_enabled: boolean;
|
||||
promo_code_enabled: boolean;
|
||||
password_reset_enabled: boolean;
|
||||
frontend_url: string;
|
||||
@@ -734,6 +735,7 @@ export interface UpdateSettingsRequest {
|
||||
registration_enabled?: boolean;
|
||||
email_verify_enabled?: boolean;
|
||||
registration_email_suffix_whitelist?: string[];
|
||||
registration_email_domain_quota_enabled?: boolean;
|
||||
promo_code_enabled?: boolean;
|
||||
password_reset_enabled?: boolean;
|
||||
frontend_url?: string;
|
||||
|
||||
@@ -128,9 +128,12 @@ export default {
|
||||
emailVerificationHint: 'Require email verification for new registrations',
|
||||
emailSuffixWhitelist: 'Email Domain Whitelist',
|
||||
emailSuffixWhitelistHint:
|
||||
"Emails from allowlist domains can register without a quota. When the allowlist is not empty, every other registrable domain can register one account. Empty the allowlist to remove the quota for all domains (for example, {'@'}qq.com, {'@'}gmail.com, *.edu.cn).",
|
||||
"Only email addresses from the specified domains can register; leave empty for no restriction (for example, {'@'}qq.com, {'@'}gmail.com, *.edu.cn)",
|
||||
emailSuffixWhitelistPlaceholder: "{'@'}example.com, *.edu.cn",
|
||||
emailSuffixWhitelistInputHint: 'Empty the allowlist to remove the registration quota. Use *.edu.cn to match edu.cn and its subdomains.',
|
||||
emailSuffixWhitelistInputHint: 'Leave empty for no restriction. Use *.edu.cn to match edu.cn and its subdomains.',
|
||||
emailDomainQuota: 'Non-allowlist Domain Quota',
|
||||
emailDomainQuotaHint:
|
||||
'When enabled and the allowlist is not empty, every other registrable domain can register one account. When disabled, non-allowlist domains are rejected. Has no effect while the allowlist is empty',
|
||||
promoCode: 'Promo Code',
|
||||
promoCodeHint: 'Allow users to use promo codes during registration',
|
||||
invitationCode: 'Invitation Code Registration',
|
||||
|
||||
@@ -128,9 +128,12 @@ export default {
|
||||
emailVerificationHint: '新用户注册时需要验证邮箱',
|
||||
emailSuffixWhitelist: '邮箱域名白名单',
|
||||
emailSuffixWhitelistHint:
|
||||
"白名单域名的邮箱可无限注册;白名单非空时,其他可注册主域名各限注册一个账户。清空白名单后,所有域名均不限制注册数量(例如 {'@'}qq.com, {'@'}gmail.com, *.edu.cn)",
|
||||
"仅允许使用指定域名的邮箱注册账号;留空则不限制(例如 {'@'}qq.com, {'@'}gmail.com, *.edu.cn)",
|
||||
emailSuffixWhitelistPlaceholder: "{'@'}example.com, *.edu.cn",
|
||||
emailSuffixWhitelistInputHint: '清空白名单后不限制注册数量。使用 *.edu.cn 可匹配 edu.cn 及其子域名。',
|
||||
emailSuffixWhitelistInputHint: '留空则不限制。使用 *.edu.cn 可匹配 edu.cn 及其子域名。',
|
||||
emailDomainQuota: '非白名单域名限量注册',
|
||||
emailDomainQuotaHint:
|
||||
'开启后,白名单非空时,其他可注册主域名各限注册一个账户;关闭时非白名单域名直接拒绝注册。白名单为空时本开关无效果',
|
||||
promoCode: '优惠码',
|
||||
promoCodeHint: '允许用户在注册时使用优惠码',
|
||||
invitationCode: '邀请码注册',
|
||||
|
||||
@@ -209,6 +209,7 @@ export interface PublicSettings {
|
||||
email_verify_enabled: boolean
|
||||
force_email_on_third_party_signup: boolean
|
||||
registration_email_suffix_whitelist: string[]
|
||||
registration_email_domain_quota_enabled?: boolean
|
||||
promo_code_enabled: boolean
|
||||
password_reset_enabled: boolean
|
||||
invitation_code_enabled: boolean
|
||||
|
||||
@@ -1486,6 +1486,23 @@
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Email Domain Quota -->
|
||||
<div
|
||||
class="flex items-center justify-between border-t border-gray-100 pt-4 dark:border-dark-700"
|
||||
>
|
||||
<div>
|
||||
<label class="font-medium text-gray-900 dark:text-white">{{
|
||||
t("admin.settings.registration.emailDomainQuota")
|
||||
}}</label>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
{{ t("admin.settings.registration.emailDomainQuotaHint") }}
|
||||
</p>
|
||||
</div>
|
||||
<Toggle
|
||||
v-model="form.registration_email_domain_quota_enabled"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Promo Code -->
|
||||
<div
|
||||
class="flex items-center justify-between border-t border-gray-100 pt-4 dark:border-dark-700"
|
||||
@@ -9401,6 +9418,7 @@ const form = reactive<SettingsForm>({
|
||||
registration_enabled: true,
|
||||
email_verify_enabled: false,
|
||||
registration_email_suffix_whitelist: [],
|
||||
registration_email_domain_quota_enabled: false,
|
||||
promo_code_enabled: true,
|
||||
invitation_code_enabled: false,
|
||||
password_reset_enabled: false,
|
||||
@@ -11023,6 +11041,8 @@ async function saveSettings() {
|
||||
registrationEmailSuffixWhitelistTags.value.map((suffix) =>
|
||||
suffix.startsWith("*.") ? suffix : `@${suffix}`,
|
||||
),
|
||||
registration_email_domain_quota_enabled:
|
||||
form.registration_email_domain_quota_enabled,
|
||||
promo_code_enabled: form.promo_code_enabled,
|
||||
invitation_code_enabled: form.invitation_code_enabled,
|
||||
password_reset_enabled: form.password_reset_enabled,
|
||||
|
||||
@@ -605,12 +605,18 @@ describe("admin SettingsView email domain quota copy", () => {
|
||||
expect(enCommon.auth.emailDomainRegistrationLimit).toContain("mainstream email");
|
||||
expect(enCommon.auth.emailDomainRegistrationLimit).toContain("contact support");
|
||||
|
||||
const zhHint = zhSettings.settings.registration.emailSuffixWhitelistHint;
|
||||
const enHint = enSettings.settings.registration.emailSuffixWhitelistHint;
|
||||
expect(zhHint).toContain("其他可注册主域名各限注册一个账户");
|
||||
expect(zhHint).toContain("清空白名单");
|
||||
expect(enHint).toContain("one account");
|
||||
expect(enHint).toContain("empty");
|
||||
// 白名单 hint 描述严格默认语义;额度语义移入独立开关的 hint。
|
||||
const zhWhitelistHint = zhSettings.settings.registration.emailSuffixWhitelistHint;
|
||||
const enWhitelistHint = enSettings.settings.registration.emailSuffixWhitelistHint;
|
||||
expect(zhWhitelistHint).toContain("留空则不限制");
|
||||
expect(enWhitelistHint).toContain("leave empty for no restriction");
|
||||
|
||||
const zhQuotaHint = zhSettings.settings.registration.emailDomainQuotaHint;
|
||||
const enQuotaHint = enSettings.settings.registration.emailDomainQuotaHint;
|
||||
expect(zhQuotaHint).toContain("其他可注册主域名各限注册一个账户");
|
||||
expect(zhQuotaHint).toContain("关闭时非白名单域名直接拒绝");
|
||||
expect(enQuotaHint).toContain("one account");
|
||||
expect(enQuotaHint).toContain("When disabled");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -196,13 +196,18 @@ import {
|
||||
import { apiClient } from '@/api/client'
|
||||
import { buildAuthErrorMessage } from '@/utils/authError'
|
||||
import { extractApiErrorCode } from '@/utils/apiError'
|
||||
import {
|
||||
formatRegistrationEmailSuffixWhitelistForMessage,
|
||||
isRegistrationEmailSuffixAllowed,
|
||||
normalizeRegistrationEmailSuffixWhitelist
|
||||
} from '@/utils/registrationEmailPolicy'
|
||||
import {
|
||||
clearAllAffiliateReferralCodes,
|
||||
loadAffiliateReferralCode,
|
||||
oauthAffiliatePayload
|
||||
} from '@/utils/oauthAffiliate'
|
||||
|
||||
const { t } = useI18n()
|
||||
const { t, locale } = useI18n()
|
||||
|
||||
// ==================== Router & Stores ====================
|
||||
|
||||
@@ -266,6 +271,9 @@ const aliyunCaptchaSceneId = ref<string>('')
|
||||
const aliyunCaptchaPrefix = ref<string>('')
|
||||
const aliyunCaptchaRegion = ref<string>('cn')
|
||||
const siteName = ref<string>('Sub2API')
|
||||
const registrationEmailSuffixWhitelist = ref<string[]>([])
|
||||
// 域名限量注册开关:开启时非白名单域名可注册 1 个账户(由后端判定),前端不做白名单预检。
|
||||
const emailDomainQuotaEnabled = ref<boolean>(false)
|
||||
|
||||
// Turnstile for resend
|
||||
const turnstileRef = ref<InstanceType<typeof TurnstileWidget> | null>(null)
|
||||
@@ -365,6 +373,10 @@ onMounted(async () => {
|
||||
aliyunCaptchaPrefix.value = settings.aliyun_captcha_prefix || ''
|
||||
aliyunCaptchaRegion.value = settings.aliyun_captcha_region || 'cn'
|
||||
siteName.value = settings.site_name || 'Sub2API'
|
||||
registrationEmailSuffixWhitelist.value = normalizeRegistrationEmailSuffixWhitelist(
|
||||
settings.registration_email_suffix_whitelist || []
|
||||
)
|
||||
emailDomainQuotaEnabled.value = settings.registration_email_domain_quota_enabled === true
|
||||
} catch (error) {
|
||||
console.error('Failed to load public settings:', error)
|
||||
}
|
||||
@@ -473,6 +485,13 @@ function isPendingOAuthFlow(): boolean {
|
||||
return Boolean(pendingProvider.value.trim())
|
||||
}
|
||||
|
||||
// 域名限量注册开启时交由后端按额度判定;pending OAuth / 换绑流程沿用后端策略,前端不预检。
|
||||
function shouldBypassRegistrationEmailPolicy(): boolean {
|
||||
return (
|
||||
emailDomainQuotaEnabled.value || isPendingOAuthFlow() || Boolean(pendingAuthToken.value.trim())
|
||||
)
|
||||
}
|
||||
|
||||
function resolvePendingOAuthCallbackRoute(provider: string): string {
|
||||
switch (provider.trim().toLowerCase()) {
|
||||
case 'linuxdo':
|
||||
@@ -514,6 +533,12 @@ async function sendCode(): Promise<void> {
|
||||
let captchaProofUsed = false
|
||||
|
||||
try {
|
||||
if (!shouldBypassRegistrationEmailPolicy() && !isRegistrationEmailSuffixAllowed(email.value, registrationEmailSuffixWhitelist.value)) {
|
||||
errorMessage.value = buildEmailSuffixNotAllowedMessage()
|
||||
appStore.showError(errorMessage.value)
|
||||
return
|
||||
}
|
||||
|
||||
const requestPayload = {
|
||||
email: email.value,
|
||||
[pendingAuthTokenField.value]: pendingAuthToken.value || undefined,
|
||||
@@ -635,6 +660,12 @@ async function handleVerify(): Promise<void> {
|
||||
return
|
||||
}
|
||||
|
||||
if (!shouldBypassRegistrationEmailPolicy() && !isRegistrationEmailSuffixAllowed(email.value, registrationEmailSuffixWhitelist.value)) {
|
||||
errorMessage.value = buildEmailSuffixNotAllowedMessage()
|
||||
appStore.showError(errorMessage.value)
|
||||
return
|
||||
}
|
||||
|
||||
if (!(await acquireCreateAccountActionProof())) {
|
||||
return
|
||||
}
|
||||
@@ -735,6 +766,22 @@ function handleBack(): void {
|
||||
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: formatRegistrationEmailSuffixWhitelistForMessage(normalizedWhitelist, {
|
||||
separator,
|
||||
more: (count) => t('auth.emailSuffixAllowedMore', { count })
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function buildRegistrationErrorMessage(error: unknown, fallback: string): string {
|
||||
if (extractApiErrorCode(error) === 'EMAIL_DOMAIN_REGISTRATION_LIMIT') {
|
||||
return t('auth.emailDomainRegistrationLimit')
|
||||
|
||||
@@ -354,6 +354,11 @@ import {
|
||||
} from '@/api/auth'
|
||||
import { buildAuthErrorMessage } from '@/utils/authError'
|
||||
import { extractApiErrorCode, extractI18nErrorMessage } from '@/utils/apiError'
|
||||
import {
|
||||
formatRegistrationEmailSuffixWhitelistForMessage,
|
||||
isRegistrationEmailSuffixAllowed,
|
||||
normalizeRegistrationEmailSuffixWhitelist
|
||||
} from '@/utils/registrationEmailPolicy'
|
||||
import {
|
||||
clearAffiliateReferralCode,
|
||||
loadAffiliateReferralCode,
|
||||
@@ -361,7 +366,7 @@ import {
|
||||
} from '@/utils/oauthAffiliate'
|
||||
import type { LoginAgreementDocument } from '@/types'
|
||||
|
||||
const { t } = useI18n()
|
||||
const { t, locale } = useI18n()
|
||||
const LOGIN_AGREEMENT_STORAGE_KEY = 'sub2api_login_agreement_consent'
|
||||
|
||||
// ==================== Router & Stores ====================
|
||||
@@ -400,6 +405,9 @@ const oidcOAuthEnabled = ref<boolean>(false)
|
||||
const oidcOAuthProviderName = ref<string>('OIDC')
|
||||
const githubOAuthEnabled = ref<boolean>(false)
|
||||
const googleOAuthEnabled = ref<boolean>(false)
|
||||
const registrationEmailSuffixWhitelist = ref<string[]>([])
|
||||
// 域名限量注册开关:开启时非白名单域名可注册 1 个账户(由后端判定),前端不做白名单预检。
|
||||
const emailDomainQuotaEnabled = ref<boolean>(false)
|
||||
const loginAgreementEnabled = ref<boolean>(false)
|
||||
const loginAgreementMode = ref<'modal' | 'checkbox' | string>('modal')
|
||||
const loginAgreementUpdatedAt = ref<string>('')
|
||||
@@ -532,6 +540,10 @@ onMounted(async () => {
|
||||
oidcOAuthProviderName.value = settings.oidc_oauth_provider_name || 'OIDC'
|
||||
githubOAuthEnabled.value = settings.github_oauth_enabled
|
||||
googleOAuthEnabled.value = settings.google_oauth_enabled
|
||||
registrationEmailSuffixWhitelist.value = normalizeRegistrationEmailSuffixWhitelist(
|
||||
settings.registration_email_suffix_whitelist || []
|
||||
)
|
||||
emailDomainQuotaEnabled.value = settings.registration_email_domain_quota_enabled === true
|
||||
applyLoginAgreementSettings(settings)
|
||||
|
||||
// Read promo code from URL parameter only if promo code is enabled
|
||||
@@ -850,6 +862,22 @@ 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: formatRegistrationEmailSuffixWhitelistForMessage(normalizedWhitelist, {
|
||||
separator,
|
||||
more: (count) => t('auth.emailSuffixAllowedMore', { count })
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function validateForm(): boolean {
|
||||
// Reset errors
|
||||
errors.email = ''
|
||||
@@ -874,6 +902,13 @@ function validateForm(): boolean {
|
||||
} else if (!validateEmail(formData.email)) {
|
||||
errors.email = t('auth.invalidEmail')
|
||||
isValid = false
|
||||
} else if (
|
||||
!emailDomainQuotaEnabled.value &&
|
||||
!isRegistrationEmailSuffixAllowed(formData.email, registrationEmailSuffixWhitelist.value)
|
||||
) {
|
||||
// 域名限量注册关闭时保持严格白名单预检;开启时交给后端按域名额度判定
|
||||
errors.email = buildEmailSuffixNotAllowedMessage()
|
||||
isValid = false
|
||||
}
|
||||
|
||||
// Password validation
|
||||
|
||||
@@ -316,6 +316,7 @@ describe('EmailVerifyView', () => {
|
||||
turnstile_site_key: '',
|
||||
site_name: 'Sub2API',
|
||||
registration_email_suffix_whitelist: ['allowed.com'],
|
||||
registration_email_domain_quota_enabled: true,
|
||||
})
|
||||
sessionStorage.setItem(
|
||||
'register_data',
|
||||
@@ -350,6 +351,7 @@ describe('EmailVerifyView', () => {
|
||||
turnstile_site_key: '',
|
||||
site_name: 'Sub2API',
|
||||
registration_email_suffix_whitelist: ['allowed.com'],
|
||||
registration_email_domain_quota_enabled: true,
|
||||
})
|
||||
sendVerifyCodeMock.mockRejectedValueOnce({
|
||||
reason: 'EMAIL_DOMAIN_REGISTRATION_LIMIT',
|
||||
@@ -387,6 +389,7 @@ describe('EmailVerifyView', () => {
|
||||
turnstile_site_key: '',
|
||||
site_name: 'Sub2API',
|
||||
registration_email_suffix_whitelist: ['allowed.com'],
|
||||
registration_email_domain_quota_enabled: true,
|
||||
})
|
||||
sessionStorage.setItem(
|
||||
'register_data',
|
||||
@@ -422,6 +425,39 @@ describe('EmailVerifyView', () => {
|
||||
)
|
||||
})
|
||||
|
||||
// 域名限量注册开关默认关闭:恢复 PR5423 之前的客户端白名单预检,非白名单域名不发送验证码。
|
||||
it('blocks sending a verification code for a non-whitelist email domain when the quota switch is disabled', async () => {
|
||||
getPublicSettingsMock.mockResolvedValue({
|
||||
turnstile_enabled: false,
|
||||
turnstile_site_key: '',
|
||||
site_name: 'Sub2API',
|
||||
registration_email_suffix_whitelist: ['allowed.com'],
|
||||
})
|
||||
sessionStorage.setItem(
|
||||
'register_data',
|
||||
JSON.stringify({
|
||||
email: 'first@custom.example',
|
||||
password: 'secret-123',
|
||||
})
|
||||
)
|
||||
|
||||
mount(EmailVerifyView, {
|
||||
global: {
|
||||
stubs: {
|
||||
AuthLayout: { template: '<div><slot /><slot name="footer" /></div>' },
|
||||
Icon: true,
|
||||
TurnstileWidget: true,
|
||||
transition: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
await flushPromises()
|
||||
|
||||
expect(sendVerifyCodeMock).not.toHaveBeenCalled()
|
||||
expect(showErrorMock).toHaveBeenCalledWith('auth.emailSuffixNotAllowedWithAllowed')
|
||||
})
|
||||
|
||||
it('uses the pending oauth verify-code endpoint when auth store only carries the pending provider', async () => {
|
||||
authStoreState.pendingAuthSession = {
|
||||
token: '',
|
||||
|
||||
@@ -122,7 +122,8 @@ describe('RegisterView invitation layout', () => {
|
||||
getPublicSettingsMock.mockResolvedValueOnce({
|
||||
...publicSettings,
|
||||
turnstile_enabled: false,
|
||||
registration_email_suffix_whitelist: ['allowed.com']
|
||||
registration_email_suffix_whitelist: ['allowed.com'],
|
||||
registration_email_domain_quota_enabled: true
|
||||
})
|
||||
|
||||
const wrapper = mountRegister()
|
||||
@@ -142,7 +143,8 @@ describe('RegisterView invitation layout', () => {
|
||||
getPublicSettingsMock.mockResolvedValueOnce({
|
||||
...publicSettings,
|
||||
turnstile_enabled: false,
|
||||
registration_email_suffix_whitelist: ['allowed.com']
|
||||
registration_email_suffix_whitelist: ['allowed.com'],
|
||||
registration_email_domain_quota_enabled: true
|
||||
})
|
||||
registerMock.mockRejectedValueOnce({
|
||||
reason: 'EMAIL_DOMAIN_REGISTRATION_LIMIT',
|
||||
@@ -160,4 +162,45 @@ describe('RegisterView invitation layout', () => {
|
||||
'该邮箱域名无法注册新账户。请使用主流邮箱注册;如需使用企业邮箱,请联系客服添加域名白名单。'
|
||||
)
|
||||
})
|
||||
|
||||
// 域名限量注册开关默认关闭:恢复 PR5423 之前的客户端白名单预检,非白名单域名不发起注册请求。
|
||||
it('rejects a non-whitelist email domain locally when the domain quota switch is disabled', async () => {
|
||||
getPublicSettingsMock.mockResolvedValueOnce({
|
||||
...publicSettings,
|
||||
turnstile_enabled: false,
|
||||
registration_email_suffix_whitelist: ['allowed.com']
|
||||
})
|
||||
|
||||
const wrapper = mountRegister()
|
||||
await flushPromises()
|
||||
await wrapper.get('#email').setValue('first@custom.example')
|
||||
await wrapper.get('#password').setValue('secret-123')
|
||||
await wrapper.get('form').trigger('submit.prevent')
|
||||
await flushPromises()
|
||||
|
||||
expect(registerMock).not.toHaveBeenCalled()
|
||||
// 校验失败通过 validationToastMessage watcher 弹 toast
|
||||
expect(showErrorMock).toHaveBeenCalledWith('auth.emailSuffixNotAllowedWithAllowed')
|
||||
expect(wrapper.get('#email').classes()).toContain('input-error')
|
||||
})
|
||||
|
||||
it('still submits whitelisted email domains when the domain quota switch is disabled', async () => {
|
||||
getPublicSettingsMock.mockResolvedValueOnce({
|
||||
...publicSettings,
|
||||
turnstile_enabled: false,
|
||||
registration_email_suffix_whitelist: ['allowed.com']
|
||||
})
|
||||
|
||||
const wrapper = mountRegister()
|
||||
await flushPromises()
|
||||
await wrapper.get('#email').setValue('user@allowed.com')
|
||||
await wrapper.get('#password').setValue('secret-123')
|
||||
await wrapper.get('form').trigger('submit.prevent')
|
||||
await flushPromises()
|
||||
|
||||
expect(registerMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ email: 'user@allowed.com' })
|
||||
)
|
||||
expect(showErrorMock).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user