新增手机号绑定邮箱后邮箱模式重登流程
This commit is contained in:
+115
-6
@@ -73,6 +73,12 @@ const NORMAL_PHONE_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.(
|
||||
plusModeEnabled: false,
|
||||
signupMethod: 'phone',
|
||||
}) || NORMAL_STEP_DEFINITIONS;
|
||||
const NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
|
||||
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
|
||||
plusModeEnabled: false,
|
||||
signupMethod: 'phone',
|
||||
phoneSignupReloginAfterBindEmailEnabled: true,
|
||||
}) || NORMAL_PHONE_STEP_DEFINITIONS;
|
||||
const PLUS_PAYPAL_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
|
||||
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
|
||||
plusModeEnabled: true,
|
||||
@@ -84,6 +90,13 @@ const PLUS_PAYPAL_PHONE_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSte
|
||||
plusPaymentMethod: 'paypal',
|
||||
signupMethod: 'phone',
|
||||
}) || PLUS_PAYPAL_STEP_DEFINITIONS;
|
||||
const PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
|
||||
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
|
||||
plusModeEnabled: true,
|
||||
plusPaymentMethod: 'paypal',
|
||||
signupMethod: 'phone',
|
||||
phoneSignupReloginAfterBindEmailEnabled: true,
|
||||
}) || PLUS_PAYPAL_PHONE_STEP_DEFINITIONS;
|
||||
const PLUS_GOPAY_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
|
||||
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
|
||||
plusModeEnabled: true,
|
||||
@@ -95,6 +108,13 @@ const PLUS_GOPAY_PHONE_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getStep
|
||||
plusPaymentMethod: 'gopay',
|
||||
signupMethod: 'phone',
|
||||
}) || PLUS_GOPAY_STEP_DEFINITIONS;
|
||||
const PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
|
||||
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
|
||||
plusModeEnabled: true,
|
||||
plusPaymentMethod: 'gopay',
|
||||
signupMethod: 'phone',
|
||||
phoneSignupReloginAfterBindEmailEnabled: true,
|
||||
}) || PLUS_GOPAY_PHONE_STEP_DEFINITIONS;
|
||||
const PLUS_GPC_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
|
||||
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
|
||||
plusModeEnabled: true,
|
||||
@@ -106,18 +126,29 @@ const PLUS_GPC_PHONE_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?
|
||||
plusPaymentMethod: 'gpc-helper',
|
||||
signupMethod: 'phone',
|
||||
}) || PLUS_GPC_STEP_DEFINITIONS;
|
||||
const PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getSteps?.({
|
||||
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
|
||||
plusModeEnabled: true,
|
||||
plusPaymentMethod: 'gpc-helper',
|
||||
signupMethod: 'phone',
|
||||
phoneSignupReloginAfterBindEmailEnabled: true,
|
||||
}) || PLUS_GPC_PHONE_STEP_DEFINITIONS;
|
||||
const PLUS_STEP_DEFINITIONS = PLUS_PAYPAL_STEP_DEFINITIONS;
|
||||
const ALL_STEP_DEFINITIONS = self.MultiPageStepDefinitions?.getAllSteps?.({
|
||||
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
|
||||
}) || [
|
||||
...NORMAL_STEP_DEFINITIONS,
|
||||
...NORMAL_PHONE_STEP_DEFINITIONS,
|
||||
...NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
|
||||
...PLUS_PAYPAL_STEP_DEFINITIONS,
|
||||
...PLUS_PAYPAL_PHONE_STEP_DEFINITIONS,
|
||||
...PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
|
||||
...PLUS_GOPAY_STEP_DEFINITIONS,
|
||||
...PLUS_GOPAY_PHONE_STEP_DEFINITIONS,
|
||||
...PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
|
||||
...PLUS_GPC_STEP_DEFINITIONS,
|
||||
...PLUS_GPC_PHONE_STEP_DEFINITIONS,
|
||||
...PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
|
||||
];
|
||||
const STEP_IDS = Array.from(new Set(ALL_STEP_DEFINITIONS
|
||||
.map((definition) => Number(definition?.id))
|
||||
@@ -644,6 +675,7 @@ function getStepDefinitionsForState(state = {}) {
|
||||
plusModeEnabled: isPlusModeState(state),
|
||||
plusPaymentMethod: normalizePlusPaymentMethod(state?.plusPaymentMethod),
|
||||
signupMethod: getSignupMethodForStepDefinitions(state),
|
||||
phoneSignupReloginAfterBindEmailEnabled: Boolean(state?.phoneSignupReloginAfterBindEmailEnabled),
|
||||
});
|
||||
if (Array.isArray(definitions)) {
|
||||
return definitions;
|
||||
@@ -908,6 +940,7 @@ const PERSISTED_SETTING_DEFAULTS = {
|
||||
autoStepDelaySeconds: null,
|
||||
step6CookieCleanupEnabled: false,
|
||||
phoneVerificationEnabled: false,
|
||||
phoneSignupReloginAfterBindEmailEnabled: false,
|
||||
phoneSmsReuseEnabled: DEFAULT_HERO_SMS_REUSE_ENABLED,
|
||||
freePhoneReuseEnabled: true,
|
||||
freePhoneReuseAutoEnabled: true,
|
||||
@@ -2799,6 +2832,7 @@ function normalizePersistentSettingValue(key, value) {
|
||||
return typeof value === 'boolean' ? value : true;
|
||||
case 'step6CookieCleanupEnabled':
|
||||
case 'phoneVerificationEnabled':
|
||||
case 'phoneSignupReloginAfterBindEmailEnabled':
|
||||
case 'phoneSmsReuseEnabled':
|
||||
case 'freePhoneReuseEnabled':
|
||||
case 'freePhoneReuseAutoEnabled':
|
||||
@@ -8600,7 +8634,12 @@ function getDownstreamStateResets(step, state = {}) {
|
||||
currentPhoneVerificationCountdownWindowTotal: 0,
|
||||
};
|
||||
}
|
||||
if (stepKey === 'oauth-login' || stepKey === 'fetch-login-code') {
|
||||
if (
|
||||
stepKey === 'oauth-login'
|
||||
|| stepKey === 'fetch-login-code'
|
||||
|| stepKey === 'relogin-bound-email'
|
||||
|| stepKey === 'fetch-bound-email-login-code'
|
||||
) {
|
||||
return {
|
||||
lastLoginCode: null,
|
||||
loginVerificationRequestedAt: null,
|
||||
@@ -9626,6 +9665,9 @@ const AUTO_RUN_BACKGROUND_COMPLETED_STEP_KEYS = new Set([
|
||||
'post-login-phone-verification',
|
||||
'bind-email',
|
||||
'fetch-bind-email-code',
|
||||
'relogin-bound-email',
|
||||
'fetch-bound-email-login-code',
|
||||
'post-bound-email-phone-verification',
|
||||
'confirm-oauth',
|
||||
]);
|
||||
const STEP_COMPLETION_SIGNAL_STEP_KEYS = new Set([
|
||||
@@ -10081,6 +10123,9 @@ const AUTH_CHAIN_NODE_IDS = new Set([
|
||||
'post-login-phone-verification',
|
||||
'bind-email',
|
||||
'fetch-bind-email-code',
|
||||
'relogin-bound-email',
|
||||
'fetch-bound-email-login-code',
|
||||
'post-bound-email-phone-verification',
|
||||
'confirm-oauth',
|
||||
'platform-verify',
|
||||
]);
|
||||
@@ -12394,6 +12439,39 @@ const step10Executor = self.MultiPageBackgroundStep10?.createStep10Executor({
|
||||
DEFAULT_SUB2API_GROUP_NAME,
|
||||
SUB2API_STEP9_RESPONSE_TIMEOUT_MS,
|
||||
});
|
||||
|
||||
function resolveBoundEmailForReloginState(state = {}) {
|
||||
return String(
|
||||
state?.step8VerificationTargetEmail
|
||||
|| state?.email
|
||||
|| state?.registrationEmailState?.current
|
||||
|| ''
|
||||
).trim();
|
||||
}
|
||||
|
||||
async function executeReloginBoundEmail(state = {}) {
|
||||
const visibleStep = Math.floor(Number(state?.visibleStep) || 0) || 10;
|
||||
const boundEmail = resolveBoundEmailForReloginState(state);
|
||||
if (!boundEmail) {
|
||||
throw new Error(`步骤 ${visibleStep}:缺少绑定邮箱,无法在绑定邮箱后切入邮箱模式 OAuth 登录。`);
|
||||
}
|
||||
await addLog(`步骤 ${visibleStep}:绑定邮箱已提交,正在刷新 OAuth 并使用绑定邮箱 ${boundEmail} 登录...`, 'info', {
|
||||
step: visibleStep,
|
||||
stepKey: 'relogin-bound-email',
|
||||
});
|
||||
return step7Executor.executeStep7({
|
||||
...state,
|
||||
forceLoginIdentifierType: 'email',
|
||||
forceEmailLogin: true,
|
||||
signupMethod: 'email',
|
||||
resolvedSignupMethod: 'email',
|
||||
accountIdentifierType: 'email',
|
||||
accountIdentifier: boundEmail,
|
||||
email: boundEmail,
|
||||
step8VerificationTargetEmail: boundEmail,
|
||||
});
|
||||
}
|
||||
|
||||
const stepExecutorsByKey = {
|
||||
'open-chatgpt': () => step1Executor.executeStep1(),
|
||||
'submit-signup-email': (state) => step2Executor.executeStep2(state),
|
||||
@@ -12413,6 +12491,9 @@ const stepExecutorsByKey = {
|
||||
'post-login-phone-verification': (state) => step8Executor.executePostLoginPhoneVerification(state),
|
||||
'bind-email': (state) => step8Executor.executeBindEmail(state),
|
||||
'fetch-bind-email-code': (state) => step8Executor.executeFetchBindEmailCode(state),
|
||||
'relogin-bound-email': (state) => executeReloginBoundEmail(state),
|
||||
'fetch-bound-email-login-code': (state) => step8Executor.executeBoundEmailLoginCode(state),
|
||||
'post-bound-email-phone-verification': (state) => step8Executor.executeBoundEmailPostLoginPhoneVerification(state),
|
||||
'confirm-oauth': (state) => step9Executor.executeStep9(state),
|
||||
'platform-verify': (state) => executeStep10(state),
|
||||
};
|
||||
@@ -12590,12 +12671,16 @@ async function acquireTopLevelAuthChainExecution(step, state = {}) {
|
||||
|
||||
const normalStepRegistry = buildStepRegistry(NORMAL_STEP_DEFINITIONS);
|
||||
const normalPhoneStepRegistry = buildStepRegistry(NORMAL_PHONE_STEP_DEFINITIONS);
|
||||
const normalPhoneBoundEmailReloginStepRegistry = buildStepRegistry(NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS);
|
||||
const plusPayPalStepRegistry = buildStepRegistry(PLUS_PAYPAL_STEP_DEFINITIONS);
|
||||
const plusPayPalPhoneStepRegistry = buildStepRegistry(PLUS_PAYPAL_PHONE_STEP_DEFINITIONS);
|
||||
const plusPayPalPhoneBoundEmailReloginStepRegistry = buildStepRegistry(PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS);
|
||||
const plusGoPayStepRegistry = buildStepRegistry(PLUS_GOPAY_STEP_DEFINITIONS);
|
||||
const plusGoPayPhoneStepRegistry = buildStepRegistry(PLUS_GOPAY_PHONE_STEP_DEFINITIONS);
|
||||
const plusGoPayPhoneBoundEmailReloginStepRegistry = buildStepRegistry(PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS);
|
||||
const plusGpcStepRegistry = buildStepRegistry(PLUS_GPC_STEP_DEFINITIONS);
|
||||
const plusGpcPhoneStepRegistry = buildStepRegistry(PLUS_GPC_PHONE_STEP_DEFINITIONS);
|
||||
const plusGpcPhoneBoundEmailReloginStepRegistry = buildStepRegistry(PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS);
|
||||
|
||||
function getStepRegistryForState(state = {}) {
|
||||
const activeFlowId = String(state?.activeFlowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID;
|
||||
@@ -12603,17 +12688,31 @@ function getStepRegistryForState(state = {}) {
|
||||
throw new Error(`当前尚未注册 flow=${activeFlowId} 的步骤执行器。`);
|
||||
}
|
||||
const signupMethod = getSignupMethodForStepDefinitions(state);
|
||||
const useBoundEmailRelogin = signupMethod === SIGNUP_METHOD_PHONE
|
||||
&& Boolean(state?.phoneSignupReloginAfterBindEmailEnabled);
|
||||
if (!isPlusModeState(state)) {
|
||||
return signupMethod === SIGNUP_METHOD_PHONE ? normalPhoneStepRegistry : normalStepRegistry;
|
||||
if (signupMethod === SIGNUP_METHOD_PHONE) {
|
||||
return useBoundEmailRelogin ? normalPhoneBoundEmailReloginStepRegistry : normalPhoneStepRegistry;
|
||||
}
|
||||
return normalStepRegistry;
|
||||
}
|
||||
const paymentMethod = normalizePlusPaymentMethod(state?.plusPaymentMethod);
|
||||
if (paymentMethod === PLUS_PAYMENT_METHOD_GPC_HELPER) {
|
||||
return signupMethod === SIGNUP_METHOD_PHONE ? plusGpcPhoneStepRegistry : plusGpcStepRegistry;
|
||||
if (signupMethod === SIGNUP_METHOD_PHONE) {
|
||||
return useBoundEmailRelogin ? plusGpcPhoneBoundEmailReloginStepRegistry : plusGpcPhoneStepRegistry;
|
||||
}
|
||||
return plusGpcStepRegistry;
|
||||
}
|
||||
if (paymentMethod === PLUS_PAYMENT_METHOD_GOPAY) {
|
||||
return signupMethod === SIGNUP_METHOD_PHONE ? plusGoPayPhoneStepRegistry : plusGoPayStepRegistry;
|
||||
if (signupMethod === SIGNUP_METHOD_PHONE) {
|
||||
return useBoundEmailRelogin ? plusGoPayPhoneBoundEmailReloginStepRegistry : plusGoPayPhoneStepRegistry;
|
||||
}
|
||||
return plusGoPayStepRegistry;
|
||||
}
|
||||
return signupMethod === SIGNUP_METHOD_PHONE ? plusPayPalPhoneStepRegistry : plusPayPalStepRegistry;
|
||||
if (signupMethod === SIGNUP_METHOD_PHONE) {
|
||||
return useBoundEmailRelogin ? plusPayPalPhoneBoundEmailReloginStepRegistry : plusPayPalPhoneStepRegistry;
|
||||
}
|
||||
return plusPayPalStepRegistry;
|
||||
}
|
||||
|
||||
async function requestOAuthUrlFromPanel(state, options = {}) {
|
||||
@@ -13043,12 +13142,22 @@ async function getPostStep6AutoRestartDecision(step, error) {
|
||||
: (typeof LAST_STEP_ID === 'number' ? LAST_STEP_ID : 10);
|
||||
const currentNodeKey = resolveStepKey(normalizedStep, latestState);
|
||||
const confirmOauthStep = findStepIdByKeyForState('confirm-oauth', latestState);
|
||||
const boundEmailReloginStep = findStepIdByKeyForState('relogin-bound-email', latestState);
|
||||
const isBoundEmailReloginTailStep = [
|
||||
'relogin-bound-email',
|
||||
'fetch-bound-email-login-code',
|
||||
'post-bound-email-phone-verification',
|
||||
].includes(currentNodeKey);
|
||||
const shouldRetryFromConfirmStep = currentNodeKey === 'platform-verify'
|
||||
&& Number.isFinite(confirmOauthStep)
|
||||
&& confirmOauthStep > 0
|
||||
&& confirmOauthStep < normalizedStep
|
||||
&& isPlatformVerifyTransientRetryError(errorMessage);
|
||||
const restartAnchorStep = shouldRetryFromConfirmStep ? confirmOauthStep : authChainStartStep;
|
||||
const restartAnchorStep = shouldRetryFromConfirmStep
|
||||
? confirmOauthStep
|
||||
: (isBoundEmailReloginTailStep && Number.isFinite(boundEmailReloginStep) && boundEmailReloginStep > 0
|
||||
? boundEmailReloginStep
|
||||
: authChainStartStep);
|
||||
if (isPhoneSmsPlatformRateLimitFailure(errorMessage)) {
|
||||
return {
|
||||
shouldRestart: false,
|
||||
|
||||
@@ -270,6 +270,8 @@
|
||||
13: 'confirm-oauth',
|
||||
14: 'platform-verify',
|
||||
15: 'platform-verify',
|
||||
16: 'confirm-oauth',
|
||||
17: 'platform-verify',
|
||||
});
|
||||
|
||||
function getStepKeyForState(step, state = {}) {
|
||||
@@ -613,12 +615,18 @@
|
||||
const stateForStep = await getState();
|
||||
const stepKey = getStepKeyForState(step, stateForStep);
|
||||
|
||||
if (stepKey === 'oauth-login') {
|
||||
await syncStepAccountIdentityFromPayload(payload);
|
||||
if (stepKey === 'oauth-login' || stepKey === 'relogin-bound-email') {
|
||||
if (stepKey === 'oauth-login') {
|
||||
await syncStepAccountIdentityFromPayload(payload);
|
||||
}
|
||||
if (payload.skipLoginVerificationStep) {
|
||||
await setState({ loginVerificationRequestedAt: null });
|
||||
const latestState = await getState();
|
||||
const loginCodeStep = findStepByKeyAfter(step, 'fetch-login-code', latestState);
|
||||
const loginCodeStep = findStepByKeyAfter(
|
||||
step,
|
||||
stepKey === 'relogin-bound-email' ? 'fetch-bound-email-login-code' : 'fetch-login-code',
|
||||
latestState
|
||||
);
|
||||
if (loginCodeStep) {
|
||||
const currentStatus = getNodeStatusByStep(loginCodeStep, latestState);
|
||||
if (!isStepProtectedFromAutoSkip(currentStatus)) {
|
||||
@@ -635,7 +643,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (stepKey === 'fetch-login-code') {
|
||||
if (stepKey === 'fetch-login-code' || stepKey === 'fetch-bound-email-login-code') {
|
||||
await setState({
|
||||
...(payload.phoneVerification || payload.loginPhoneVerification ? {
|
||||
currentPhoneVerificationCode: '',
|
||||
@@ -649,7 +657,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
if (stepKey === 'post-login-phone-verification') {
|
||||
if (stepKey === 'post-login-phone-verification' || stepKey === 'post-bound-email-phone-verification') {
|
||||
await setState({
|
||||
currentPhoneVerificationCode: '',
|
||||
signupPhoneVerificationRequestedAt: null,
|
||||
@@ -1333,10 +1341,14 @@
|
||||
const plusPaymentChanged = Object.prototype.hasOwnProperty.call(updates, 'plusPaymentMethod')
|
||||
&& normalizePlusPaymentMethodForDisplay(currentState?.plusPaymentMethod || 'paypal')
|
||||
!== normalizePlusPaymentMethodForDisplay(updates.plusPaymentMethod || 'paypal');
|
||||
const phoneSignupReloginAfterBindEmailChanged = Object.prototype.hasOwnProperty.call(updates, 'phoneSignupReloginAfterBindEmailEnabled')
|
||||
&& Boolean(currentState?.phoneSignupReloginAfterBindEmailEnabled) !== Boolean(updates.phoneSignupReloginAfterBindEmailEnabled);
|
||||
const nextPlusModeEnabled = Object.prototype.hasOwnProperty.call(updates, 'plusModeEnabled')
|
||||
? Boolean(updates.plusModeEnabled)
|
||||
: Boolean(currentState?.plusModeEnabled);
|
||||
const stepModeChanged = modeChanged || (nextPlusModeEnabled && plusPaymentChanged);
|
||||
const stepModeChanged = modeChanged
|
||||
|| (nextPlusModeEnabled && plusPaymentChanged)
|
||||
|| phoneSignupReloginAfterBindEmailChanged;
|
||||
const oauthFlowTimeoutDisabled = Object.prototype.hasOwnProperty.call(updates, 'oauthFlowTimeoutEnabled')
|
||||
&& updates.oauthFlowTimeoutEnabled === false;
|
||||
await setPersistentSettings(updates);
|
||||
|
||||
@@ -124,6 +124,34 @@
|
||||
return String(value || '').trim().toLowerCase();
|
||||
}
|
||||
|
||||
function resolveBoundEmailLoginTarget(state = {}, visibleStep = 0) {
|
||||
const email = String(
|
||||
state?.step8VerificationTargetEmail
|
||||
|| state?.email
|
||||
|| state?.registrationEmailState?.current
|
||||
|| ''
|
||||
).trim();
|
||||
if (!email) {
|
||||
throw new Error(`步骤 ${visibleStep || 0}:缺少绑定邮箱,无法使用邮箱模式重新发起 OAuth 登录。`);
|
||||
}
|
||||
return email;
|
||||
}
|
||||
|
||||
function buildBoundEmailLoginState(state = {}, visibleStep = 0) {
|
||||
const email = resolveBoundEmailLoginTarget(state, visibleStep);
|
||||
return {
|
||||
...state,
|
||||
forceLoginIdentifierType: 'email',
|
||||
forceEmailLogin: true,
|
||||
signupMethod: 'email',
|
||||
resolvedSignupMethod: 'email',
|
||||
accountIdentifierType: 'email',
|
||||
accountIdentifier: email,
|
||||
email,
|
||||
step8VerificationTargetEmail: normalizeStep8VerificationTargetEmail(email),
|
||||
};
|
||||
}
|
||||
|
||||
async function getLoginAuthStateFromContent(visibleStep, options = {}) {
|
||||
if (typeof sendToContentScriptResilient !== 'function') {
|
||||
return {};
|
||||
@@ -142,7 +170,7 @@
|
||||
retryDelayMs: 600,
|
||||
logMessage: options.logMessage || `步骤 ${visibleStep}:认证页正在切换,等待页面重新就绪...`,
|
||||
logStep: visibleStep,
|
||||
logStepKey: 'fetch-login-code',
|
||||
logStepKey: options.logStepKey || activeFetchLoginCodeStepKey || 'fetch-login-code',
|
||||
}
|
||||
);
|
||||
if (result?.error) {
|
||||
@@ -240,9 +268,11 @@
|
||||
loginVerificationRequestedAt: null,
|
||||
});
|
||||
const fromRecovery = Boolean(options.fromRecovery);
|
||||
const stepKey = options.stepKey || activeFetchLoginCodeStepKey || 'fetch-login-code';
|
||||
await addLog(
|
||||
`步骤 ${visibleStep}:当前认证页已进入 OAuth 授权页${fromRecovery ? '(轮询失败后复核)' : ''},跳过登录验证码拉取并继续后续流程。`,
|
||||
'warn'
|
||||
'warn',
|
||||
{ step: visibleStep, stepKey }
|
||||
);
|
||||
if (typeof completeNodeFromBackground === 'function') {
|
||||
await completeNodeFromBackground(options.nodeId || 'fetch-login-code', {
|
||||
@@ -258,9 +288,11 @@
|
||||
step8VerificationTargetEmail: '',
|
||||
loginVerificationRequestedAt: null,
|
||||
});
|
||||
const stepKey = options.stepKey || activeFetchLoginCodeStepKey || 'fetch-login-code';
|
||||
await addLog(
|
||||
`步骤 ${visibleStep}:当前认证页已进入手机号验证流程,跳过登录邮箱验证码,交给后续“手机号验证”步骤处理。`,
|
||||
'warn'
|
||||
'warn',
|
||||
{ step: visibleStep, stepKey }
|
||||
);
|
||||
if (typeof completeNodeFromBackground === 'function') {
|
||||
await completeNodeFromBackground(options.nodeId || 'fetch-login-code', {
|
||||
@@ -416,9 +448,10 @@
|
||||
}
|
||||
|
||||
async function completePostLoginPhoneVerificationSkippedOnOauth(visibleStep, options = {}) {
|
||||
const stepKey = options.stepKey || 'post-login-phone-verification';
|
||||
await addLog(`步骤 ${visibleStep}:当前认证页已进入 OAuth 授权页,跳过手机号验证步骤。`, 'warn', {
|
||||
step: visibleStep,
|
||||
stepKey: 'post-login-phone-verification',
|
||||
stepKey,
|
||||
});
|
||||
if (typeof completeNodeFromBackground === 'function') {
|
||||
await completeNodeFromBackground(options.nodeId || 'post-login-phone-verification', {
|
||||
@@ -428,18 +461,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function executePostLoginPhoneVerification(state) {
|
||||
async function executePostLoginPhoneVerification(state, runtime = {}) {
|
||||
const visibleStep = getVisibleStep(state, 9);
|
||||
activeFetchLoginCodeStep = visibleStep;
|
||||
activeFetchLoginCodeStepKey = 'post-login-phone-verification';
|
||||
activeFetchLoginCodeStepKey = runtime.stepKey || 'post-login-phone-verification';
|
||||
const authTabId = await ensureAuthTabForPostLoginStep(state, visibleStep);
|
||||
const pageState = await getLoginAuthStateFromContent(visibleStep, {
|
||||
timeoutMs: await getStep8ReadyTimeoutMs('确认手机号验证页或 OAuth 授权页已就绪', state?.oauthUrl || '', visibleStep),
|
||||
logMessage: `步骤 ${visibleStep}:正在确认是否需要手机号验证...`,
|
||||
logStepKey: activeFetchLoginCodeStepKey,
|
||||
});
|
||||
|
||||
if (pageState?.state === 'oauth_consent_page') {
|
||||
await completePostLoginPhoneVerificationSkippedOnOauth(visibleStep, { nodeId: state?.nodeId });
|
||||
await completePostLoginPhoneVerificationSkippedOnOauth(visibleStep, {
|
||||
nodeId: state?.nodeId || runtime.fallbackNodeId,
|
||||
stepKey: activeFetchLoginCodeStepKey,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (pageState?.state !== 'add_phone_page' && pageState?.state !== 'phone_verification_page') {
|
||||
@@ -457,7 +494,7 @@
|
||||
visibleStep,
|
||||
});
|
||||
if (typeof completeNodeFromBackground === 'function') {
|
||||
await completeNodeFromBackground(state?.nodeId || 'post-login-phone-verification', {
|
||||
await completeNodeFromBackground(state?.nodeId || runtime.fallbackNodeId || 'post-login-phone-verification', {
|
||||
phoneVerification: true,
|
||||
postLoginPhoneVerification: true,
|
||||
code: result?.code || '',
|
||||
@@ -663,6 +700,64 @@
|
||||
});
|
||||
}
|
||||
|
||||
async function executeBoundEmailLoginCode(state) {
|
||||
const visibleStep = getVisibleStep(state, 11);
|
||||
activeFetchLoginCodeStep = visibleStep;
|
||||
activeFetchLoginCodeStepKey = 'fetch-bound-email-login-code';
|
||||
const preparedState = buildBoundEmailLoginState(state, visibleStep);
|
||||
const authTabId = await getTabId('signup-page');
|
||||
|
||||
if (authTabId) {
|
||||
await chrome.tabs.update(authTabId, { active: true });
|
||||
} else {
|
||||
if (!preparedState.oauthUrl) {
|
||||
throw new Error(`步骤 ${visibleStep}:缺少登录用 OAuth 链接,请先完成绑定邮箱后刷新 OAuth 并登录。`);
|
||||
}
|
||||
await reuseOrCreateTab('signup-page', preparedState.oauthUrl);
|
||||
}
|
||||
|
||||
throwIfStopped();
|
||||
const pageState = await ensureStep8VerificationPageReady({
|
||||
visibleStep,
|
||||
authLoginStep: Math.max(1, visibleStep - 1),
|
||||
allowPhoneVerificationPage: true,
|
||||
allowAddEmailPage: false,
|
||||
timeoutMs: await getStep8ReadyTimeoutMs('确认绑定邮箱登录验证码页已就绪', preparedState?.oauthUrl || '', visibleStep),
|
||||
});
|
||||
|
||||
if (pageState?.state === 'oauth_consent_page') {
|
||||
await completeStep8WhenAuthAlreadyOnOauthConsent(visibleStep, {
|
||||
nodeId: state?.nodeId || 'fetch-bound-email-login-code',
|
||||
stepKey: 'fetch-bound-email-login-code',
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (pageState?.state === 'add_phone_page' || pageState?.state === 'phone_verification_page') {
|
||||
await completeStep8WhenDeferredToPostLoginPhone(visibleStep, pageState, {
|
||||
nodeId: state?.nodeId || 'fetch-bound-email-login-code',
|
||||
stepKey: 'fetch-bound-email-login-code',
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (pageState?.state === 'add_email_page') {
|
||||
throw new Error(`步骤 ${visibleStep}:绑定邮箱后邮箱模式登录不应再进入添加邮箱页。URL: ${pageState?.url || ''}`.trim());
|
||||
}
|
||||
if (pageState?.state !== 'verification_page') {
|
||||
throw new Error(`步骤 ${visibleStep}:绑定邮箱后获取登录验证码只处理邮箱登录验证码页,当前状态:${pageState?.state || 'unknown'}。URL: ${pageState?.url || ''}`.trim());
|
||||
}
|
||||
|
||||
return pollEmailVerificationCode(preparedState, pageState, visibleStep, {
|
||||
stickyLastResendAt: Number(preparedState?.loginVerificationRequestedAt) || 0,
|
||||
});
|
||||
}
|
||||
|
||||
async function executeBoundEmailPostLoginPhoneVerification(state) {
|
||||
return executePostLoginPhoneVerification(state, {
|
||||
stepKey: 'post-bound-email-phone-verification',
|
||||
fallbackNodeId: 'post-bound-email-phone-verification',
|
||||
});
|
||||
}
|
||||
|
||||
async function runStep8Attempt(state, runtime = {}) {
|
||||
const visibleStep = getVisibleStep(state, 8);
|
||||
activeFetchLoginCodeStep = visibleStep;
|
||||
@@ -855,6 +950,8 @@
|
||||
executePostLoginPhoneVerification,
|
||||
executeBindEmail,
|
||||
executeFetchBindEmailCode,
|
||||
executeBoundEmailLoginCode,
|
||||
executeBoundEmailPostLoginPhoneVerification,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,11 @@
|
||||
return String(value || '').trim().toLowerCase() === 'phone' ? 'phone' : 'email';
|
||||
}
|
||||
|
||||
function shouldForceStep7EmailLogin(state = {}) {
|
||||
return normalizeStep7IdentifierType(state?.forceLoginIdentifierType) === 'email'
|
||||
|| Boolean(state?.forceEmailLogin);
|
||||
}
|
||||
|
||||
function canUseConfiguredPhoneSignup(state = {}) {
|
||||
return normalizeStep7SignupMethod(state?.signupMethod) === 'phone'
|
||||
&& Boolean(state?.phoneVerificationEnabled)
|
||||
@@ -75,6 +80,10 @@
|
||||
}
|
||||
|
||||
function resolveStep7LoginIdentifierType(state = {}, fallbackType = '') {
|
||||
if (shouldForceStep7EmailLogin(state)) {
|
||||
return 'email';
|
||||
}
|
||||
|
||||
if (shouldPreferStep7PhoneSignupIdentity(state)) {
|
||||
return 'phone';
|
||||
}
|
||||
@@ -234,7 +243,19 @@
|
||||
throwIfStopped();
|
||||
attempt += 1;
|
||||
try {
|
||||
const currentState = attempt === 1 ? state : await getState();
|
||||
const rawCurrentState = attempt === 1 ? state : await getState();
|
||||
const currentState = shouldForceStep7EmailLogin(state)
|
||||
? {
|
||||
...rawCurrentState,
|
||||
forceLoginIdentifierType: 'email',
|
||||
forceEmailLogin: true,
|
||||
signupMethod: 'email',
|
||||
resolvedSignupMethod: 'email',
|
||||
accountIdentifierType: 'email',
|
||||
accountIdentifier: email,
|
||||
email,
|
||||
}
|
||||
: rawCurrentState;
|
||||
const password = currentState.password || currentState.customPassword || '';
|
||||
const currentIdentifierType = resolveStep7LoginIdentifierType(currentState, resolvedIdentifierType);
|
||||
const currentPhoneNumber = currentIdentifierType === 'phone'
|
||||
|
||||
@@ -91,7 +91,8 @@ function resolveCommandNodeId(message = {}) {
|
||||
if (visibleStep === 8 || visibleStep === 11) return 'fetch-login-code';
|
||||
if (visibleStep === 9 || visibleStep === 12) return 'post-login-phone-verification';
|
||||
if (visibleStep === 10 || visibleStep === 13) return 'confirm-oauth';
|
||||
if (visibleStep === 14 || visibleStep === 15) return 'platform-verify';
|
||||
if (visibleStep === 16) return 'confirm-oauth';
|
||||
if (visibleStep === 14 || visibleStep === 15 || visibleStep === 17) return 'platform-verify';
|
||||
if (visibleStep === 7) return 'oauth-login';
|
||||
if (visibleStep === 5) return 'fill-profile';
|
||||
if (visibleStep === 3) return 'fill-password';
|
||||
|
||||
@@ -290,6 +290,8 @@ const DEFAULT_OPENAI_NODE_BY_STEP = Object.freeze({
|
||||
13: 'confirm-oauth',
|
||||
14: 'platform-verify',
|
||||
15: 'platform-verify',
|
||||
16: 'confirm-oauth',
|
||||
17: 'platform-verify',
|
||||
});
|
||||
|
||||
function resolveReportNodeId(stepOrNodeId, data = {}) {
|
||||
|
||||
@@ -50,7 +50,11 @@
|
||||
{ id: 7, order: 70, key: 'plus-checkout-billing', title: '等待 GPC 任务完成', sourceId: 'plus-checkout', driverId: 'content/plus-checkout', command: 'plus-checkout-billing' },
|
||||
];
|
||||
|
||||
function createOpenAiAuthTail(startId, startOrder, signupMethod = SIGNUP_METHOD_EMAIL) {
|
||||
function isPhoneSignupReloginAfterBindEmailEnabled(options = {}) {
|
||||
return Boolean(options?.phoneSignupReloginAfterBindEmailEnabled);
|
||||
}
|
||||
|
||||
function createOpenAiAuthTail(startId, startOrder, signupMethod = SIGNUP_METHOD_EMAIL, options = {}) {
|
||||
const id = Number(startId) || 7;
|
||||
const order = Number(startOrder) || id * 10;
|
||||
const commonStart = [
|
||||
@@ -59,6 +63,17 @@
|
||||
];
|
||||
|
||||
if (signupMethod === SIGNUP_METHOD_PHONE) {
|
||||
if (isPhoneSignupReloginAfterBindEmailEnabled(options)) {
|
||||
return [
|
||||
...commonStart,
|
||||
{ id: id + 2, order: order + 20, key: 'bind-email', title: '绑定邮箱', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'bind-email' },
|
||||
{ id: id + 3, order: order + 30, key: 'relogin-bound-email', title: '绑定邮箱后刷新 OAuth 并登录(邮箱)', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'oauth-login' },
|
||||
{ id: id + 4, order: order + 40, key: 'fetch-bound-email-login-code', title: '获取登录验证码(邮箱)', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'submit-verification-code', mailRuleId: 'openai-login-code' },
|
||||
{ id: id + 5, order: order + 50, key: 'post-bound-email-phone-verification', title: '手机号验证', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'post-login-phone-verification' },
|
||||
{ id: id + 6, order: order + 60, key: 'confirm-oauth', title: '自动确认 OAuth', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'confirm-oauth' },
|
||||
{ id: id + 7, order: order + 70, key: 'platform-verify', title: '平台回调验证', sourceId: 'platform-panel', driverId: 'content/platform-panel', command: 'platform-verify' },
|
||||
];
|
||||
}
|
||||
return [
|
||||
...commonStart,
|
||||
{ id: id + 2, order: order + 20, key: 'bind-email', title: '绑定邮箱', sourceId: 'openai-auth', driverId: 'content/signup-page', command: 'bind-email' },
|
||||
@@ -76,21 +91,25 @@
|
||||
];
|
||||
}
|
||||
|
||||
function createOpenAiSteps(prefixSteps, startId, startOrder, signupMethod = SIGNUP_METHOD_EMAIL) {
|
||||
function createOpenAiSteps(prefixSteps, startId, startOrder, signupMethod = SIGNUP_METHOD_EMAIL, options = {}) {
|
||||
return [
|
||||
...prefixSteps,
|
||||
...createOpenAiAuthTail(startId, startOrder, signupMethod),
|
||||
...createOpenAiAuthTail(startId, startOrder, signupMethod, options),
|
||||
];
|
||||
}
|
||||
|
||||
const NORMAL_STEP_DEFINITIONS = createOpenAiSteps(NORMAL_PREFIX_STEP_DEFINITIONS, 7, 70, SIGNUP_METHOD_EMAIL);
|
||||
const NORMAL_PHONE_STEP_DEFINITIONS = createOpenAiSteps(NORMAL_PREFIX_STEP_DEFINITIONS, 7, 70, SIGNUP_METHOD_PHONE);
|
||||
const NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = createOpenAiSteps(NORMAL_PREFIX_STEP_DEFINITIONS, 7, 70, SIGNUP_METHOD_PHONE, { phoneSignupReloginAfterBindEmailEnabled: true });
|
||||
const PLUS_PAYPAL_STEP_DEFINITIONS = createOpenAiSteps(PLUS_PAYPAL_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_EMAIL);
|
||||
const PLUS_PAYPAL_PHONE_STEP_DEFINITIONS = createOpenAiSteps(PLUS_PAYPAL_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE);
|
||||
const PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = createOpenAiSteps(PLUS_PAYPAL_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE, { phoneSignupReloginAfterBindEmailEnabled: true });
|
||||
const PLUS_GOPAY_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GOPAY_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_EMAIL);
|
||||
const PLUS_GOPAY_PHONE_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GOPAY_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE);
|
||||
const PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GOPAY_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE, { phoneSignupReloginAfterBindEmailEnabled: true });
|
||||
const PLUS_GPC_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GPC_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_EMAIL);
|
||||
const PLUS_GPC_PHONE_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GPC_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE);
|
||||
const PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS = createOpenAiSteps(PLUS_GPC_PREFIX_STEP_DEFINITIONS, 10, 100, SIGNUP_METHOD_PHONE, { phoneSignupReloginAfterBindEmailEnabled: true });
|
||||
|
||||
const PHONE_SIGNUP_TITLE_OVERRIDES = Object.freeze({
|
||||
'submit-signup-email': '注册并输入手机号',
|
||||
@@ -130,17 +149,39 @@
|
||||
|
||||
function getOpenAiModeStepDefinitions(options = {}) {
|
||||
const signupMethod = getResolvedSignupMethod(options);
|
||||
const reloginAfterBindEmail = signupMethod === SIGNUP_METHOD_PHONE
|
||||
&& isPhoneSignupReloginAfterBindEmailEnabled(options);
|
||||
if (!isPlusModeEnabled(options)) {
|
||||
return signupMethod === SIGNUP_METHOD_PHONE ? NORMAL_PHONE_STEP_DEFINITIONS : NORMAL_STEP_DEFINITIONS;
|
||||
if (signupMethod === SIGNUP_METHOD_PHONE) {
|
||||
return reloginAfterBindEmail
|
||||
? NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS
|
||||
: NORMAL_PHONE_STEP_DEFINITIONS;
|
||||
}
|
||||
return NORMAL_STEP_DEFINITIONS;
|
||||
}
|
||||
const paymentMethod = normalizePlusPaymentMethod(options?.plusPaymentMethod || options?.paymentMethod);
|
||||
if (paymentMethod === PLUS_PAYMENT_METHOD_GPC_HELPER) {
|
||||
return signupMethod === SIGNUP_METHOD_PHONE ? PLUS_GPC_PHONE_STEP_DEFINITIONS : PLUS_GPC_STEP_DEFINITIONS;
|
||||
if (signupMethod === SIGNUP_METHOD_PHONE) {
|
||||
return reloginAfterBindEmail
|
||||
? PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS
|
||||
: PLUS_GPC_PHONE_STEP_DEFINITIONS;
|
||||
}
|
||||
return PLUS_GPC_STEP_DEFINITIONS;
|
||||
}
|
||||
if (paymentMethod === PLUS_PAYMENT_METHOD_GOPAY) {
|
||||
return signupMethod === SIGNUP_METHOD_PHONE ? PLUS_GOPAY_PHONE_STEP_DEFINITIONS : PLUS_GOPAY_STEP_DEFINITIONS;
|
||||
if (signupMethod === SIGNUP_METHOD_PHONE) {
|
||||
return reloginAfterBindEmail
|
||||
? PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS
|
||||
: PLUS_GOPAY_PHONE_STEP_DEFINITIONS;
|
||||
}
|
||||
return PLUS_GOPAY_STEP_DEFINITIONS;
|
||||
}
|
||||
return signupMethod === SIGNUP_METHOD_PHONE ? PLUS_PAYPAL_PHONE_STEP_DEFINITIONS : PLUS_PAYPAL_STEP_DEFINITIONS;
|
||||
if (signupMethod === SIGNUP_METHOD_PHONE) {
|
||||
return reloginAfterBindEmail
|
||||
? PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS
|
||||
: PLUS_PAYPAL_PHONE_STEP_DEFINITIONS;
|
||||
}
|
||||
return PLUS_PAYPAL_STEP_DEFINITIONS;
|
||||
}
|
||||
|
||||
function getOpenAiPlusPaymentStepTitle(options = {}) {
|
||||
@@ -172,12 +213,16 @@
|
||||
for (const step of [
|
||||
...NORMAL_STEP_DEFINITIONS,
|
||||
...NORMAL_PHONE_STEP_DEFINITIONS,
|
||||
...NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
|
||||
...PLUS_PAYPAL_STEP_DEFINITIONS,
|
||||
...PLUS_PAYPAL_PHONE_STEP_DEFINITIONS,
|
||||
...PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
|
||||
...PLUS_GOPAY_STEP_DEFINITIONS,
|
||||
...PLUS_GOPAY_PHONE_STEP_DEFINITIONS,
|
||||
...PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
|
||||
...PLUS_GPC_STEP_DEFINITIONS,
|
||||
...PLUS_GPC_PHONE_STEP_DEFINITIONS,
|
||||
...PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
|
||||
]) {
|
||||
keyed.set(`${step.id}:${step.key}`, step);
|
||||
}
|
||||
@@ -352,13 +397,17 @@
|
||||
STEP_DEFINITIONS: NORMAL_STEP_DEFINITIONS,
|
||||
NORMAL_STEP_DEFINITIONS,
|
||||
NORMAL_PHONE_STEP_DEFINITIONS,
|
||||
NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
|
||||
PLUS_STEP_DEFINITIONS: PLUS_PAYPAL_STEP_DEFINITIONS,
|
||||
PLUS_PAYPAL_STEP_DEFINITIONS,
|
||||
PLUS_PAYPAL_PHONE_STEP_DEFINITIONS,
|
||||
PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
|
||||
PLUS_GOPAY_STEP_DEFINITIONS,
|
||||
PLUS_GOPAY_PHONE_STEP_DEFINITIONS,
|
||||
PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
|
||||
PLUS_GPC_STEP_DEFINITIONS,
|
||||
PLUS_GPC_PHONE_STEP_DEFINITIONS,
|
||||
PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS,
|
||||
SIGNUP_METHOD_EMAIL,
|
||||
SIGNUP_METHOD_PHONE,
|
||||
getAllSteps,
|
||||
|
||||
@@ -1295,6 +1295,17 @@
|
||||
<button type="button" class="choice-btn" data-signup-method="phone">手机号注册</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-row" id="row-phone-signup-relogin-after-bind-email" style="display:none;">
|
||||
<span class="data-label">绑定后重登</span>
|
||||
<label class="toggle-switch" for="input-phone-signup-relogin-after-bind-email"
|
||||
title="手机号注册绑定邮箱后,跳过绑定邮箱验证码,重新刷新 OAuth 并使用绑定邮箱登录">
|
||||
<input type="checkbox" id="input-phone-signup-relogin-after-bind-email" />
|
||||
<span class="toggle-switch-track" aria-hidden="true">
|
||||
<span class="toggle-switch-thumb"></span>
|
||||
</span>
|
||||
</label>
|
||||
<span class="data-value">绑定邮箱后切入邮箱注册模式 OAuth 尾部</span>
|
||||
</div>
|
||||
<div class="data-row" id="row-account-run-history-helper-base-url" style="display:none;">
|
||||
<span class="data-label">同步服务</span>
|
||||
<input type="text" id="input-account-run-history-helper-base-url" class="data-input mono"
|
||||
|
||||
@@ -387,6 +387,8 @@ const btnTogglePhoneVerificationSection = document.getElementById('btn-toggle-ph
|
||||
const rowPhoneVerificationFold = document.getElementById('row-phone-verification-fold');
|
||||
const inputPhoneVerificationEnabled = document.getElementById('input-phone-verification-enabled');
|
||||
const rowSignupMethod = document.getElementById('row-signup-method');
|
||||
const rowPhoneSignupReloginAfterBindEmail = document.getElementById('row-phone-signup-relogin-after-bind-email');
|
||||
const inputPhoneSignupReloginAfterBindEmail = document.getElementById('input-phone-signup-relogin-after-bind-email');
|
||||
const rowSignupPhone = document.getElementById('row-signup-phone');
|
||||
const signupMethodButtons = Array.from(document.querySelectorAll('[data-signup-method]'));
|
||||
const selectPhoneSmsProvider = document.getElementById('select-phone-sms-provider');
|
||||
@@ -519,11 +521,13 @@ const SIGNUP_METHOD_EMAIL = 'email';
|
||||
const SIGNUP_METHOD_PHONE = 'phone';
|
||||
const DEFAULT_SIGNUP_METHOD = SIGNUP_METHOD_EMAIL;
|
||||
const DEFAULT_ACTIVE_FLOW_ID = 'openai';
|
||||
const DEFAULT_PHONE_SIGNUP_RELOGIN_AFTER_BIND_EMAIL_ENABLED = false;
|
||||
const PHONE_SIGNUP_REUSE_LOCK_TITLE = '手机号注册流程不使用号码复用,切回邮箱注册后会恢复原设置';
|
||||
let latestState = null;
|
||||
let currentPlusModeEnabled = false;
|
||||
let currentPlusPaymentMethod = DEFAULT_PLUS_PAYMENT_METHOD;
|
||||
let currentSignupMethod = DEFAULT_SIGNUP_METHOD;
|
||||
let currentPhoneSignupReloginAfterBindEmailEnabled = DEFAULT_PHONE_SIGNUP_RELOGIN_AFTER_BIND_EMAIL_ENABLED;
|
||||
let phoneSignupReuseUiWasLocked = false;
|
||||
let lastConfirmedOperationDelayEnabled = true;
|
||||
let heroSmsCountrySelectionOrder = [];
|
||||
@@ -544,10 +548,12 @@ const nexSmsCountrySearchTextById = new Map();
|
||||
let stepDefinitions = getStepDefinitionsForMode(false, {
|
||||
plusPaymentMethod: currentPlusPaymentMethod,
|
||||
signupMethod: currentSignupMethod,
|
||||
phoneSignupReloginAfterBindEmailEnabled: currentPhoneSignupReloginAfterBindEmailEnabled,
|
||||
});
|
||||
let workflowNodes = getWorkflowNodesForMode(false, {
|
||||
plusPaymentMethod: currentPlusPaymentMethod,
|
||||
signupMethod: currentSignupMethod,
|
||||
phoneSignupReloginAfterBindEmailEnabled: currentPhoneSignupReloginAfterBindEmailEnabled,
|
||||
});
|
||||
let STEP_IDS = stepDefinitions.map((step) => Number(step.id)).filter(Number.isFinite);
|
||||
let STEP_DEFAULT_STATUSES = Object.fromEntries(STEP_IDS.map((stepId) => [stepId, 'pending']));
|
||||
@@ -808,6 +814,9 @@ function getStepDefinitionsForMode(plusModeEnabled = false, options = {}) {
|
||||
const rawSignupMethod = typeof options === 'string'
|
||||
? currentSignupMethod
|
||||
: (options.signupMethod || currentSignupMethod || DEFAULT_SIGNUP_METHOD);
|
||||
const phoneSignupReloginAfterBindEmailEnabled = typeof options === 'string'
|
||||
? currentPhoneSignupReloginAfterBindEmailEnabled
|
||||
: Boolean(options.phoneSignupReloginAfterBindEmailEnabled ?? currentPhoneSignupReloginAfterBindEmailEnabled);
|
||||
const activeFlowId = typeof options === 'string'
|
||||
? ((typeof latestState !== 'undefined' ? latestState?.activeFlowId : '') || defaultFlowId)
|
||||
: (options.activeFlowId || (typeof latestState !== 'undefined' ? latestState?.activeFlowId : '') || defaultFlowId);
|
||||
@@ -816,6 +825,7 @@ function getStepDefinitionsForMode(plusModeEnabled = false, options = {}) {
|
||||
plusModeEnabled,
|
||||
plusPaymentMethod: normalizePlusPaymentMethod(rawPaymentMethod),
|
||||
signupMethod: normalizeSignupMethod(rawSignupMethod),
|
||||
phoneSignupReloginAfterBindEmailEnabled,
|
||||
}) || [])
|
||||
.sort((left, right) => {
|
||||
const leftOrder = Number.isFinite(left.order) ? left.order : left.id;
|
||||
@@ -834,6 +844,9 @@ function getWorkflowNodesForMode(plusModeEnabled = false, options = {}) {
|
||||
const rawSignupMethod = typeof options === 'string'
|
||||
? currentSignupMethod
|
||||
: (options.signupMethod || currentSignupMethod || DEFAULT_SIGNUP_METHOD);
|
||||
const phoneSignupReloginAfterBindEmailEnabled = typeof options === 'string'
|
||||
? currentPhoneSignupReloginAfterBindEmailEnabled
|
||||
: Boolean(options.phoneSignupReloginAfterBindEmailEnabled ?? currentPhoneSignupReloginAfterBindEmailEnabled);
|
||||
const activeFlowId = typeof options === 'string'
|
||||
? ((typeof latestState !== 'undefined' ? latestState?.activeFlowId : '') || defaultFlowId)
|
||||
: (options.activeFlowId || (typeof latestState !== 'undefined' ? latestState?.activeFlowId : '') || defaultFlowId);
|
||||
@@ -842,6 +855,7 @@ function getWorkflowNodesForMode(plusModeEnabled = false, options = {}) {
|
||||
plusModeEnabled,
|
||||
plusPaymentMethod: normalizePlusPaymentMethod(rawPaymentMethod),
|
||||
signupMethod: normalizeSignupMethod(rawSignupMethod),
|
||||
phoneSignupReloginAfterBindEmailEnabled,
|
||||
});
|
||||
if (Array.isArray(nodes) && nodes.length) {
|
||||
return nodes.slice().sort((left, right) => {
|
||||
@@ -902,18 +916,24 @@ function rebuildStepDefinitionState(plusModeEnabled = false, options = {}) {
|
||||
const rawSignupMethod = typeof options === 'string'
|
||||
? currentSignupMethod
|
||||
: (options.signupMethod || currentSignupMethod || DEFAULT_SIGNUP_METHOD);
|
||||
const phoneSignupReloginAfterBindEmailEnabled = typeof options === 'string'
|
||||
? currentPhoneSignupReloginAfterBindEmailEnabled
|
||||
: Boolean(options.phoneSignupReloginAfterBindEmailEnabled ?? currentPhoneSignupReloginAfterBindEmailEnabled);
|
||||
currentPlusPaymentMethod = normalizePlusPaymentMethod(rawPaymentMethod);
|
||||
currentSignupMethod = normalizeSignupMethod(rawSignupMethod);
|
||||
currentPhoneSignupReloginAfterBindEmailEnabled = phoneSignupReloginAfterBindEmailEnabled;
|
||||
stepDefinitions = getStepDefinitionsForMode(currentPlusModeEnabled, {
|
||||
activeFlowId: options?.activeFlowId,
|
||||
plusPaymentMethod: currentPlusPaymentMethod,
|
||||
signupMethod: currentSignupMethod,
|
||||
phoneSignupReloginAfterBindEmailEnabled: currentPhoneSignupReloginAfterBindEmailEnabled,
|
||||
});
|
||||
const nextWorkflowNodes = typeof getWorkflowNodesForMode === 'function'
|
||||
? getWorkflowNodesForMode(currentPlusModeEnabled, {
|
||||
activeFlowId: options?.activeFlowId,
|
||||
plusPaymentMethod: currentPlusPaymentMethod,
|
||||
signupMethod: currentSignupMethod,
|
||||
phoneSignupReloginAfterBindEmailEnabled: currentPhoneSignupReloginAfterBindEmailEnabled,
|
||||
})
|
||||
: stepDefinitions.map((step) => ({
|
||||
legacyStepId: Number(step.id),
|
||||
@@ -3930,6 +3950,9 @@ function collectSettingsPayload() {
|
||||
: true,
|
||||
phoneVerificationEnabled: effectivePhoneVerificationEnabled,
|
||||
signupMethod: effectiveSignupMethod,
|
||||
phoneSignupReloginAfterBindEmailEnabled: typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail
|
||||
? Boolean(inputPhoneSignupReloginAfterBindEmail.checked)
|
||||
: false,
|
||||
phoneSmsProvider: phoneSmsProviderValue,
|
||||
phoneSmsProviderOrder: phoneSmsProviderOrderValue,
|
||||
verificationResendCount: normalizeVerificationResendCount(
|
||||
@@ -7842,6 +7865,9 @@ function updateSignupMethodUI(options = {}) {
|
||||
syncStepDefinitionsForMode(stepDefinitionState.plusModeEnabled, {
|
||||
plusPaymentMethod: getSelectedPlusPaymentMethod(latestState),
|
||||
signupMethod: selectedMethod,
|
||||
phoneSignupReloginAfterBindEmailEnabled: typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail
|
||||
? Boolean(inputPhoneSignupReloginAfterBindEmail.checked)
|
||||
: currentPhoneSignupReloginAfterBindEmailEnabled,
|
||||
});
|
||||
if (typeof syncSignupPhoneInputFromState === 'function') {
|
||||
syncSignupPhoneInputFromState(latestState);
|
||||
@@ -7886,6 +7912,11 @@ function updatePhoneVerificationSettingsUI() {
|
||||
: true;
|
||||
const enabled = canShowPhoneSettings && rawEnabled;
|
||||
const showSettings = enabled && phoneVerificationSectionExpanded;
|
||||
const selectedSignupMethodForPhoneSettings = typeof getSelectedSignupMethod === 'function'
|
||||
? getSelectedSignupMethod()
|
||||
: normalizeSignupMethod(latestState?.signupMethod || DEFAULT_SIGNUP_METHOD);
|
||||
const showPhoneSignupReloginAfterBindEmail = showSettings
|
||||
&& selectedSignupMethodForPhoneSettings === SIGNUP_METHOD_PHONE;
|
||||
const normalizeProvider = typeof normalizePhoneSmsProviderValue === 'function'
|
||||
? normalizePhoneSmsProviderValue
|
||||
: ((value = '') => {
|
||||
@@ -7958,6 +7989,9 @@ function updatePhoneVerificationSettingsUI() {
|
||||
row.style.display = showSettings ? '' : 'none';
|
||||
}
|
||||
});
|
||||
if (typeof rowPhoneSignupReloginAfterBindEmail !== 'undefined' && rowPhoneSignupReloginAfterBindEmail) {
|
||||
rowPhoneSignupReloginAfterBindEmail.style.display = showPhoneSignupReloginAfterBindEmail ? '' : 'none';
|
||||
}
|
||||
if (rowHeroSmsCountry) rowHeroSmsCountry.style.display = showSettings && heroProvider ? '' : 'none';
|
||||
if (rowHeroSmsCountryFallback) rowHeroSmsCountryFallback.style.display = showSettings && heroProvider ? '' : 'none';
|
||||
if (rowHeroSmsAcquirePriority) rowHeroSmsAcquirePriority.style.display = showSettings && heroProvider ? '' : 'none';
|
||||
@@ -8003,6 +8037,12 @@ function updatePhoneVerificationSettingsUI() {
|
||||
}
|
||||
phoneSignupReuseUiWasLocked = phoneSignupReuseLocked;
|
||||
const settingsLocked = isAutoRunLockedPhase() || isAutoRunScheduledPhase();
|
||||
if (typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail) {
|
||||
inputPhoneSignupReloginAfterBindEmail.disabled = settingsLocked || !showPhoneSignupReloginAfterBindEmail;
|
||||
}
|
||||
if (typeof rowPhoneSignupReloginAfterBindEmail !== 'undefined' && rowPhoneSignupReloginAfterBindEmail) {
|
||||
rowPhoneSignupReloginAfterBindEmail.classList.toggle('is-disabled', settingsLocked || !showPhoneSignupReloginAfterBindEmail);
|
||||
}
|
||||
const freePhoneReuseEnabled = Boolean(
|
||||
!phoneSignupReuseLocked
|
||||
&& typeof inputFreePhoneReuseEnabled !== 'undefined'
|
||||
@@ -8958,6 +8998,12 @@ function syncStepDefinitionsForMode(plusModeEnabled = false, plusPaymentMethodOr
|
||||
? plusPaymentMethodOrOptions
|
||||
: (options.plusPaymentMethod || getSelectedPlusPaymentMethod(latestState));
|
||||
const nextSignupMethod = normalizeSignupMethod(options.signupMethod || currentSignupMethod || DEFAULT_SIGNUP_METHOD);
|
||||
const nextPhoneSignupReloginAfterBindEmailEnabled = Boolean(
|
||||
options.phoneSignupReloginAfterBindEmailEnabled
|
||||
?? (typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail
|
||||
? inputPhoneSignupReloginAfterBindEmail.checked
|
||||
: currentPhoneSignupReloginAfterBindEmailEnabled)
|
||||
);
|
||||
const nextPaymentMethod = normalizePlusPaymentMethod(rawPaymentMethod);
|
||||
const nextActiveFlowId = String(
|
||||
options.activeFlowId
|
||||
@@ -8971,12 +9017,14 @@ function syncStepDefinitionsForMode(plusModeEnabled = false, plusPaymentMethodOr
|
||||
plusModeEnabled: nextPlusModeEnabled,
|
||||
plusPaymentMethod: nextPaymentMethod,
|
||||
signupMethod: nextSignupMethod,
|
||||
phoneSignupReloginAfterBindEmailEnabled: nextPhoneSignupReloginAfterBindEmailEnabled,
|
||||
});
|
||||
const paymentTitleChanged = Boolean(nextPlusModeEnabled && currentPaymentStep && nextPaymentTitle && currentPaymentStep.title !== nextPaymentTitle);
|
||||
const shouldRender = Boolean(options.render)
|
||||
|| nextPlusModeEnabled !== currentPlusModeEnabled
|
||||
|| nextPaymentMethod !== currentPlusPaymentMethod
|
||||
|| nextSignupMethod !== currentSignupMethod
|
||||
|| nextPhoneSignupReloginAfterBindEmailEnabled !== currentPhoneSignupReloginAfterBindEmailEnabled
|
||||
|| paymentTitleChanged;
|
||||
if (!shouldRender) {
|
||||
return;
|
||||
@@ -8986,6 +9034,7 @@ function syncStepDefinitionsForMode(plusModeEnabled = false, plusPaymentMethodOr
|
||||
activeFlowId: nextActiveFlowId,
|
||||
plusPaymentMethod: nextPaymentMethod,
|
||||
signupMethod: nextSignupMethod,
|
||||
phoneSignupReloginAfterBindEmailEnabled: nextPhoneSignupReloginAfterBindEmailEnabled,
|
||||
});
|
||||
renderStepsList();
|
||||
}
|
||||
@@ -9008,6 +9057,7 @@ function applySettingsState(state) {
|
||||
activeFlowId: state?.flowId || state?.activeFlowId,
|
||||
plusPaymentMethod: state?.plusPaymentMethod,
|
||||
signupMethod: stepDefinitionState.signupMethod,
|
||||
phoneSignupReloginAfterBindEmailEnabled: Boolean(state?.phoneSignupReloginAfterBindEmailEnabled),
|
||||
});
|
||||
}
|
||||
const fallbackIpProxyService = '711proxy';
|
||||
@@ -9359,6 +9409,11 @@ function applySettingsState(state) {
|
||||
if (typeof setSignupMethod === 'function') {
|
||||
setSignupMethod(state?.signupMethod || DEFAULT_SIGNUP_METHOD);
|
||||
}
|
||||
if (typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined' && inputPhoneSignupReloginAfterBindEmail) {
|
||||
inputPhoneSignupReloginAfterBindEmail.checked = state?.phoneSignupReloginAfterBindEmailEnabled !== undefined
|
||||
? Boolean(state.phoneSignupReloginAfterBindEmailEnabled)
|
||||
: DEFAULT_PHONE_SIGNUP_RELOGIN_AFTER_BIND_EMAIL_ENABLED;
|
||||
}
|
||||
const restoredPhoneSmsProvider = normalizePhoneSmsProvider(state?.phoneSmsProvider);
|
||||
const previousPhoneSmsProvider = selectPhoneSmsProvider ? normalizePhoneSmsProvider(selectPhoneSmsProvider.value) : restoredPhoneSmsProvider;
|
||||
const defaultFiveSimProduct = typeof DEFAULT_FIVE_SIM_PRODUCT !== 'undefined'
|
||||
@@ -14034,6 +14089,34 @@ signupMethodButtons.forEach((button) => {
|
||||
});
|
||||
});
|
||||
|
||||
inputPhoneSignupReloginAfterBindEmail?.addEventListener('change', () => {
|
||||
const stepDefinitionState = typeof resolveStepDefinitionCapabilityState === 'function'
|
||||
? resolveStepDefinitionCapabilityState({
|
||||
...(latestState || {}),
|
||||
plusModeEnabled: typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled
|
||||
? Boolean(inputPlusModeEnabled.checked)
|
||||
: Boolean(latestState?.plusModeEnabled),
|
||||
signupMethod: getSelectedSignupMethod(),
|
||||
phoneSignupReloginAfterBindEmailEnabled: Boolean(inputPhoneSignupReloginAfterBindEmail.checked),
|
||||
}, {
|
||||
signupMethod: getSelectedSignupMethod(),
|
||||
})
|
||||
: {
|
||||
plusModeEnabled: typeof inputPlusModeEnabled !== 'undefined' && inputPlusModeEnabled
|
||||
? Boolean(inputPlusModeEnabled.checked)
|
||||
: Boolean(latestState?.plusModeEnabled),
|
||||
signupMethod: getSelectedSignupMethod(),
|
||||
};
|
||||
syncStepDefinitionsForMode(stepDefinitionState.plusModeEnabled, {
|
||||
plusPaymentMethod: getSelectedPlusPaymentMethod(latestState),
|
||||
signupMethod: stepDefinitionState.signupMethod,
|
||||
phoneSignupReloginAfterBindEmailEnabled: Boolean(inputPhoneSignupReloginAfterBindEmail.checked),
|
||||
});
|
||||
updatePhoneVerificationSettingsUI();
|
||||
markSettingsDirty(true);
|
||||
saveSettings({ silent: true }).catch(() => { });
|
||||
});
|
||||
|
||||
selectPhoneSmsProvider?.addEventListener('change', async () => {
|
||||
if (selectPhoneSmsProvider) {
|
||||
selectPhoneSmsProvider.value = normalizePhoneSmsProviderValue(selectPhoneSmsProvider.value);
|
||||
|
||||
@@ -199,6 +199,8 @@ return {
|
||||
|
||||
assert.equal(api.normalizePersistentSettingValue('accountRunHistoryTextEnabled', 1), true);
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneVerificationEnabled', 1), true);
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneSignupReloginAfterBindEmailEnabled', 1), true);
|
||||
assert.equal(api.normalizePersistentSettingValue('phoneSignupReloginAfterBindEmailEnabled', 0), false);
|
||||
assert.equal(api.normalizePersistentSettingValue('plusPaymentMethod', 'gopay'), 'gopay');
|
||||
assert.equal(api.normalizePersistentSettingValue('plusPaymentMethod', 'gpc-helper'), 'gpc-helper');
|
||||
assert.equal(api.normalizePersistentSettingValue('plusPaymentMethod', 'paypal'), 'paypal');
|
||||
|
||||
@@ -181,6 +181,7 @@ return {
|
||||
plusModeEnabled: true,
|
||||
plusPaymentMethod: 'gopay',
|
||||
signupMethod: 'phone',
|
||||
phoneSignupReloginAfterBindEmailEnabled: false,
|
||||
}]);
|
||||
assert.equal(steps[0].title, '注册并输入手机号');
|
||||
});
|
||||
|
||||
@@ -13,25 +13,37 @@ test('background imports node registry and shared workflow definitions', () => {
|
||||
assert.match(source, /PLUS_PAYPAL_STEP_DEFINITIONS/);
|
||||
assert.match(source, /PLUS_GOPAY_STEP_DEFINITIONS/);
|
||||
assert.match(source, /NORMAL_PHONE_STEP_DEFINITIONS/);
|
||||
assert.match(source, /NORMAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS/);
|
||||
assert.match(source, /PLUS_PAYPAL_PHONE_STEP_DEFINITIONS/);
|
||||
assert.match(source, /PLUS_PAYPAL_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS/);
|
||||
assert.match(source, /PLUS_GOPAY_PHONE_STEP_DEFINITIONS/);
|
||||
assert.match(source, /PLUS_GOPAY_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS/);
|
||||
assert.match(source, /PLUS_GPC_PHONE_STEP_DEFINITIONS/);
|
||||
assert.match(source, /PLUS_GPC_PHONE_BOUND_EMAIL_RELOGIN_STEP_DEFINITIONS/);
|
||||
assert.match(source, /plusPayPalStepRegistry/);
|
||||
assert.match(source, /plusGoPayStepRegistry/);
|
||||
assert.match(source, /normalPhoneStepRegistry/);
|
||||
assert.match(source, /normalPhoneBoundEmailReloginStepRegistry/);
|
||||
assert.match(source, /plusPayPalPhoneStepRegistry/);
|
||||
assert.match(source, /plusPayPalPhoneBoundEmailReloginStepRegistry/);
|
||||
assert.match(source, /plusGoPayPhoneStepRegistry/);
|
||||
assert.match(source, /plusGoPayPhoneBoundEmailReloginStepRegistry/);
|
||||
assert.match(source, /plusGpcPhoneStepRegistry/);
|
||||
assert.match(source, /plusGpcPhoneBoundEmailReloginStepRegistry/);
|
||||
assert.match(source, /const signupMethod = getSignupMethodForStepDefinitions\(state\);/);
|
||||
assert.match(source, /signupMethod === SIGNUP_METHOD_PHONE \? normalPhoneStepRegistry : normalStepRegistry/);
|
||||
assert.match(source, /const useBoundEmailRelogin = signupMethod === SIGNUP_METHOD_PHONE/);
|
||||
assert.match(source, /useBoundEmailRelogin \? normalPhoneBoundEmailReloginStepRegistry : normalPhoneStepRegistry/);
|
||||
assert.match(source, /const paymentMethod = normalizePlusPaymentMethod\(state\?\.plusPaymentMethod\);/);
|
||||
assert.match(source, /paymentMethod === PLUS_PAYMENT_METHOD_GOPAY/);
|
||||
assert.match(source, /signupMethod === SIGNUP_METHOD_PHONE \? plusGoPayPhoneStepRegistry : plusGoPayStepRegistry/);
|
||||
assert.match(source, /signupMethod === SIGNUP_METHOD_PHONE \? plusPayPalPhoneStepRegistry : plusPayPalStepRegistry/);
|
||||
assert.match(source, /signupMethod === SIGNUP_METHOD_PHONE \? plusGpcPhoneStepRegistry : plusGpcStepRegistry/);
|
||||
assert.match(source, /useBoundEmailRelogin \? plusGoPayPhoneBoundEmailReloginStepRegistry : plusGoPayPhoneStepRegistry/);
|
||||
assert.match(source, /useBoundEmailRelogin \? plusPayPalPhoneBoundEmailReloginStepRegistry : plusPayPalPhoneStepRegistry/);
|
||||
assert.match(source, /useBoundEmailRelogin \? plusGpcPhoneBoundEmailReloginStepRegistry : plusGpcPhoneStepRegistry/);
|
||||
assert.match(source, /activeStepRegistry\.executeNode\(normalizedNodeId,\s*\{/);
|
||||
assert.match(source, /'bind-email': \(state\) => step8Executor\.executeBindEmail\(state\)/);
|
||||
assert.match(source, /'fetch-bind-email-code': \(state\) => step8Executor\.executeFetchBindEmailCode\(state\)/);
|
||||
assert.match(source, /'relogin-bound-email': \(state\) => executeReloginBoundEmail\(state\)/);
|
||||
assert.match(source, /'fetch-bound-email-login-code': \(state\) => step8Executor\.executeBoundEmailLoginCode\(state\)/);
|
||||
assert.match(source, /'post-bound-email-phone-verification': \(state\) => step8Executor\.executeBoundEmailPostLoginPhoneVerification\(state\)/);
|
||||
assert.match(source, /background\/steps\/create-plus-checkout\.js/);
|
||||
assert.match(source, /background\/steps\/fill-plus-checkout\.js/);
|
||||
assert.match(source, /background\/steps\/gopay-manual-confirm\.js/);
|
||||
|
||||
@@ -797,6 +797,146 @@ test('fetch-bind-email-code rejects unexpected pages after bind-email submitted'
|
||||
]);
|
||||
});
|
||||
|
||||
test('fetch-bound-email-login-code uses bound email identity and does not allow add-email', async () => {
|
||||
const calls = {
|
||||
ensureOptions: null,
|
||||
resolveState: null,
|
||||
resolveOptions: null,
|
||||
setStates: [],
|
||||
};
|
||||
const realDateNow = Date.now;
|
||||
Date.now = () => 333000;
|
||||
|
||||
const executor = api.createStep8Executor({
|
||||
addLog: async () => {},
|
||||
chrome: {
|
||||
tabs: {
|
||||
update: async () => {},
|
||||
},
|
||||
},
|
||||
CLOUDFLARE_TEMP_EMAIL_PROVIDER: 'cloudflare-temp-email',
|
||||
confirmCustomVerificationStepBypass: async () => {},
|
||||
ensureStep8VerificationPageReady: async (options) => {
|
||||
calls.ensureOptions = options;
|
||||
return {
|
||||
state: 'verification_page',
|
||||
displayedEmail: 'bind.user@example.com',
|
||||
url: 'https://auth.openai.com/email-verification',
|
||||
};
|
||||
},
|
||||
getOAuthFlowRemainingMs: async () => 9000,
|
||||
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
|
||||
getMailConfig: () => ({
|
||||
provider: 'qq',
|
||||
label: 'QQ 邮箱',
|
||||
source: 'mail-qq',
|
||||
url: 'https://mail.qq.com',
|
||||
navigateOnReuse: false,
|
||||
}),
|
||||
getTabId: async () => 1,
|
||||
HOTMAIL_PROVIDER: 'hotmail-api',
|
||||
isTabAlive: async () => true,
|
||||
LUCKMAIL_PROVIDER: 'luckmail-api',
|
||||
resolveVerificationStep: async (_step, state, _mail, options) => {
|
||||
calls.resolveState = state;
|
||||
calls.resolveOptions = options;
|
||||
},
|
||||
reuseOrCreateTab: async () => 1,
|
||||
setState: async (payload) => {
|
||||
calls.setStates.push(payload);
|
||||
},
|
||||
shouldUseCustomRegistrationEmail: () => false,
|
||||
STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS: 25000,
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
try {
|
||||
await executor.executeBoundEmailLoginCode({
|
||||
visibleStep: 11,
|
||||
nodeId: 'fetch-bound-email-login-code',
|
||||
signupMethod: 'phone',
|
||||
resolvedSignupMethod: 'phone',
|
||||
accountIdentifierType: 'phone',
|
||||
accountIdentifier: '+447780579093',
|
||||
signupPhoneNumber: '+447780579093',
|
||||
email: 'bind.user@example.com',
|
||||
step8VerificationTargetEmail: 'bind.user@example.com',
|
||||
oauthUrl: 'https://oauth.example/latest',
|
||||
});
|
||||
} finally {
|
||||
Date.now = realDateNow;
|
||||
}
|
||||
|
||||
assert.equal(calls.ensureOptions.allowAddEmailPage, false);
|
||||
assert.equal(calls.ensureOptions.allowPhoneVerificationPage, true);
|
||||
assert.equal(calls.resolveState.signupMethod, 'email');
|
||||
assert.equal(calls.resolveState.resolvedSignupMethod, 'email');
|
||||
assert.equal(calls.resolveState.accountIdentifierType, 'email');
|
||||
assert.equal(calls.resolveState.accountIdentifier, 'bind.user@example.com');
|
||||
assert.equal(calls.resolveOptions.completionStep, 11);
|
||||
assert.equal(calls.resolveOptions.sessionKey, '11:333000');
|
||||
assert.equal(calls.resolveOptions.targetEmail, 'bind.user@example.com');
|
||||
assert.deepStrictEqual(calls.setStates, [
|
||||
{
|
||||
step8VerificationTargetEmail: 'bind.user@example.com',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('fetch-bound-email-login-code defers phone pages to bound-email phone verification step', async () => {
|
||||
const completions = [];
|
||||
const setStates = [];
|
||||
const executor = api.createStep8Executor({
|
||||
addLog: async () => {},
|
||||
chrome: {
|
||||
tabs: {
|
||||
update: async () => {},
|
||||
},
|
||||
},
|
||||
completeNodeFromBackground: async (step, payload) => {
|
||||
completions.push({ step, payload });
|
||||
},
|
||||
ensureStep8VerificationPageReady: async () => ({
|
||||
state: 'add_phone_page',
|
||||
addPhonePage: true,
|
||||
url: 'https://auth.openai.com/add-phone',
|
||||
}),
|
||||
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs) => defaultTimeoutMs,
|
||||
getTabId: async () => 1,
|
||||
reuseOrCreateTab: async () => 1,
|
||||
setState: async (payload) => {
|
||||
setStates.push(payload);
|
||||
},
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
await executor.executeBoundEmailLoginCode({
|
||||
visibleStep: 11,
|
||||
nodeId: 'fetch-bound-email-login-code',
|
||||
email: 'bind.user@example.com',
|
||||
step8VerificationTargetEmail: 'bind.user@example.com',
|
||||
oauthUrl: 'https://oauth.example/latest',
|
||||
});
|
||||
|
||||
assert.deepStrictEqual(setStates, [
|
||||
{
|
||||
step8VerificationTargetEmail: '',
|
||||
loginVerificationRequestedAt: null,
|
||||
},
|
||||
]);
|
||||
assert.deepStrictEqual(completions, [
|
||||
{
|
||||
step: 'fetch-bound-email-login-code',
|
||||
payload: {
|
||||
loginVerificationRequestedAt: null,
|
||||
skipLoginVerificationStep: true,
|
||||
addPhonePage: true,
|
||||
phoneVerificationPage: false,
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('step 8 does not submit or recover add-email inside fetch-login-code', async () => {
|
||||
const calls = {
|
||||
ensureCalls: 0,
|
||||
@@ -1189,6 +1329,90 @@ test('Plus login-code step reuses step 8 verification logic but completes visibl
|
||||
assert.deepStrictEqual(remainingStepCalls, [11, 11]);
|
||||
});
|
||||
|
||||
test('bound-email relogin code step points recovery to the relogin step in Plus mode', async () => {
|
||||
let resolvedStep = null;
|
||||
let resolvedOptions = null;
|
||||
let readyOptions = null;
|
||||
const remainingStepCalls = [];
|
||||
|
||||
const executor = api.createStep8Executor({
|
||||
addLog: async () => {},
|
||||
chrome: {
|
||||
tabs: {
|
||||
update: async () => {},
|
||||
},
|
||||
},
|
||||
CLOUDFLARE_TEMP_EMAIL_PROVIDER: 'cloudflare-temp-email',
|
||||
confirmCustomVerificationStepBypass: async () => {},
|
||||
ensureStep8VerificationPageReady: async (options) => {
|
||||
readyOptions = options;
|
||||
return { state: 'verification_page', displayedEmail: 'bound.user@example.com' };
|
||||
},
|
||||
rerunStep7ForStep8Recovery: async () => {},
|
||||
getOAuthFlowRemainingMs: async (details) => {
|
||||
remainingStepCalls.push(details.step);
|
||||
return 9000;
|
||||
},
|
||||
getOAuthFlowStepTimeoutMs: async (defaultTimeoutMs, details) => {
|
||||
remainingStepCalls.push(details.step);
|
||||
return Math.min(defaultTimeoutMs, 9000);
|
||||
},
|
||||
getMailConfig: () => ({
|
||||
provider: 'qq',
|
||||
label: 'QQ 邮箱',
|
||||
source: 'mail-qq',
|
||||
url: 'https://mail.qq.com',
|
||||
navigateOnReuse: false,
|
||||
}),
|
||||
getState: async () => ({
|
||||
email: 'bound.user@example.com',
|
||||
password: 'secret',
|
||||
plusModeEnabled: true,
|
||||
signupMethod: 'phone',
|
||||
phoneSignupReloginAfterBindEmailEnabled: true,
|
||||
}),
|
||||
getTabId: async (sourceName) => (sourceName === 'signup-page' ? 1 : 2),
|
||||
HOTMAIL_PROVIDER: 'hotmail-api',
|
||||
isTabAlive: async () => true,
|
||||
isVerificationMailPollingError: () => false,
|
||||
LUCKMAIL_PROVIDER: 'luckmail-api',
|
||||
resolveVerificationStep: async (step, _state, _mail, options) => {
|
||||
resolvedStep = step;
|
||||
resolvedOptions = options;
|
||||
await options.getRemainingTimeMs({ actionLabel: '绑定邮箱后登录验证码流程' });
|
||||
},
|
||||
reuseOrCreateTab: async () => {},
|
||||
setState: async () => {},
|
||||
shouldUseCustomRegistrationEmail: () => false,
|
||||
STANDARD_MAIL_VERIFICATION_RESEND_INTERVAL_MS: 25000,
|
||||
STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS: 8,
|
||||
throwIfStopped: () => {},
|
||||
});
|
||||
|
||||
await executor.executeBoundEmailLoginCode({
|
||||
visibleStep: 14,
|
||||
plusModeEnabled: true,
|
||||
signupMethod: 'phone',
|
||||
phoneSignupReloginAfterBindEmailEnabled: true,
|
||||
email: 'bound.user@example.com',
|
||||
password: 'secret',
|
||||
oauthUrl: 'https://oauth.example/latest',
|
||||
step8VerificationTargetEmail: 'bound.user@example.com',
|
||||
});
|
||||
|
||||
assert.equal(resolvedStep, 8);
|
||||
assert.equal(resolvedOptions.completionStep, 14);
|
||||
assert.equal(resolvedOptions.targetEmail, 'bound.user@example.com');
|
||||
assert.deepStrictEqual(readyOptions, {
|
||||
visibleStep: 14,
|
||||
authLoginStep: 13,
|
||||
allowPhoneVerificationPage: true,
|
||||
allowAddEmailPage: false,
|
||||
timeoutMs: 9000,
|
||||
});
|
||||
assert.deepStrictEqual(remainingStepCalls, [14, 14]);
|
||||
});
|
||||
|
||||
test('step 8 completes directly when auth page is already on OAuth consent page', async () => {
|
||||
const events = {
|
||||
resolveCalls: 0,
|
||||
|
||||
@@ -7,6 +7,7 @@ const {
|
||||
} = require('../mail-provider-utils');
|
||||
|
||||
const sidepanelSource = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
|
||||
const sidepanelHtml = fs.readFileSync('sidepanel/sidepanel.html', 'utf8');
|
||||
|
||||
function extractFunction(name) {
|
||||
const markers = [`async function ${name}(`, `function ${name}(`];
|
||||
@@ -347,6 +348,14 @@ return {
|
||||
assert.equal(api.shouldWarnCpaPhoneSignup('phone', 'cpa'), true);
|
||||
});
|
||||
|
||||
test('phone signup relogin-after-bind-email switch is wired into UI and step definitions', () => {
|
||||
assert.match(sidepanelHtml, /row-phone-signup-relogin-after-bind-email/);
|
||||
assert.match(sidepanelHtml, /input-phone-signup-relogin-after-bind-email/);
|
||||
assert.match(sidepanelSource, /phoneSignupReloginAfterBindEmailEnabled: typeof inputPhoneSignupReloginAfterBindEmail !== 'undefined'/);
|
||||
assert.match(sidepanelSource, /phoneSignupReloginAfterBindEmailEnabled: Boolean\(state\?\.phoneSignupReloginAfterBindEmailEnabled\)/);
|
||||
assert.match(sidepanelSource, /nextPhoneSignupReloginAfterBindEmailEnabled !== currentPhoneSignupReloginAfterBindEmailEnabled/);
|
||||
});
|
||||
|
||||
test('manual step 3 uses phone identity without requiring registration email', () => {
|
||||
const api = new Function(`
|
||||
let latestState = { signupMethod: 'phone', phoneVerificationEnabled: true, signupPhoneNumber: '+441111111111', accountIdentifierType: 'phone', accountIdentifier: '+441111111111' };
|
||||
|
||||
@@ -83,6 +83,7 @@ const window = {
|
||||
let currentPlusModeEnabled = false;
|
||||
let currentPlusPaymentMethod = 'paypal';
|
||||
let currentSignupMethod = 'email';
|
||||
let currentPhoneSignupReloginAfterBindEmailEnabled = false;
|
||||
const DEFAULT_SIGNUP_METHOD = 'email';
|
||||
let stepDefinitions = [];
|
||||
let STEP_IDS = [];
|
||||
@@ -106,7 +107,7 @@ return {
|
||||
assert.deepEqual(api.getStepIds(), [7]);
|
||||
assert.deepEqual(api.calls[0], {
|
||||
type: 'getSteps',
|
||||
options: { activeFlowId: 'openai', plusModeEnabled: true, plusPaymentMethod: 'gopay', signupMethod: 'email' },
|
||||
options: { activeFlowId: 'openai', plusModeEnabled: true, plusPaymentMethod: 'gopay', signupMethod: 'email', phoneSignupReloginAfterBindEmailEnabled: false },
|
||||
});
|
||||
assert.deepEqual(api.calls[1], { type: 'render', stepIds: [7] });
|
||||
});
|
||||
@@ -252,6 +253,7 @@ const window = {
|
||||
let currentPlusModeEnabled = false;
|
||||
let currentPlusPaymentMethod = 'paypal';
|
||||
let currentSignupMethod = 'email';
|
||||
let currentPhoneSignupReloginAfterBindEmailEnabled = false;
|
||||
const DEFAULT_SIGNUP_METHOD = 'email';
|
||||
let stepDefinitions = [];
|
||||
let STEP_IDS = [];
|
||||
@@ -275,7 +277,7 @@ return {
|
||||
assert.deepEqual(api.getStepIds(), [13]);
|
||||
assert.deepEqual(api.calls[0], {
|
||||
type: 'getSteps',
|
||||
options: { activeFlowId: 'openai', plusModeEnabled: true, plusPaymentMethod: 'gpc-helper', signupMethod: 'email' },
|
||||
options: { activeFlowId: 'openai', plusModeEnabled: true, plusPaymentMethod: 'gpc-helper', signupMethod: 'email', phoneSignupReloginAfterBindEmailEnabled: false },
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -9,8 +9,17 @@ test('step definitions module exposes ordered normal and Plus step metadata', ()
|
||||
const api = new Function('self', `${source}; return self.MultiPageStepDefinitions;`)(globalScope);
|
||||
const steps = api.getSteps();
|
||||
const phoneSteps = api.getSteps({ signupMethod: 'phone' });
|
||||
const phoneReloginSteps = api.getSteps({
|
||||
signupMethod: 'phone',
|
||||
phoneSignupReloginAfterBindEmailEnabled: true,
|
||||
});
|
||||
const plusSteps = api.getSteps({ plusModeEnabled: true });
|
||||
const plusPhoneSteps = api.getSteps({ plusModeEnabled: true, signupMethod: 'phone' });
|
||||
const plusPhoneReloginSteps = api.getSteps({
|
||||
plusModeEnabled: true,
|
||||
signupMethod: 'phone',
|
||||
phoneSignupReloginAfterBindEmailEnabled: true,
|
||||
});
|
||||
const goPaySteps = api.getSteps({ plusModeEnabled: true, plusPaymentMethod: 'gopay' });
|
||||
const gpcSteps = api.getSteps({ plusModeEnabled: true, plusPaymentMethod: 'gpc-helper' });
|
||||
|
||||
@@ -58,6 +67,27 @@ test('step definitions module exposes ordered normal and Plus step metadata', ()
|
||||
'platform-verify',
|
||||
]
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
phoneReloginSteps.map((step) => step.key),
|
||||
[
|
||||
'open-chatgpt',
|
||||
'submit-signup-email',
|
||||
'fill-password',
|
||||
'fetch-signup-code',
|
||||
'fill-profile',
|
||||
'wait-registration-success',
|
||||
'oauth-login',
|
||||
'fetch-login-code',
|
||||
'bind-email',
|
||||
'relogin-bound-email',
|
||||
'fetch-bound-email-login-code',
|
||||
'post-bound-email-phone-verification',
|
||||
'confirm-oauth',
|
||||
'platform-verify',
|
||||
]
|
||||
);
|
||||
assert.equal(phoneReloginSteps.find((step) => step.key === 'relogin-bound-email')?.title, '绑定邮箱后刷新 OAuth 并登录(邮箱)');
|
||||
assert.equal(phoneReloginSteps.some((step) => step.key === 'fetch-bind-email-code'), false);
|
||||
|
||||
assert.deepStrictEqual(
|
||||
plusSteps.map((step) => step.key),
|
||||
@@ -103,6 +133,19 @@ test('step definitions module exposes ordered normal and Plus step metadata', ()
|
||||
'platform-verify',
|
||||
]
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
plusPhoneReloginSteps.map((step) => step.key).slice(-8),
|
||||
[
|
||||
'oauth-login',
|
||||
'fetch-login-code',
|
||||
'bind-email',
|
||||
'relogin-bound-email',
|
||||
'fetch-bound-email-login-code',
|
||||
'post-bound-email-phone-verification',
|
||||
'confirm-oauth',
|
||||
'platform-verify',
|
||||
]
|
||||
);
|
||||
assert.equal(goPaySteps.some((step) => step.key === 'paypal-approve'), false);
|
||||
assert.equal(api.getStepById(8, { plusModeEnabled: true, plusPaymentMethod: 'gopay' }), null);
|
||||
assert.equal(api.getPlusPaymentStepTitle({ plusModeEnabled: true, plusPaymentMethod: 'gopay' }), '');
|
||||
@@ -110,6 +153,8 @@ test('step definitions module exposes ordered normal and Plus step metadata', ()
|
||||
assert.equal(api.getLastStepId({ plusModeEnabled: true }), 14);
|
||||
assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true, signupMethod: 'phone' }), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]);
|
||||
assert.equal(api.getLastStepId({ plusModeEnabled: true, signupMethod: 'phone' }), 15);
|
||||
assert.deepStrictEqual(api.getStepIds({ plusModeEnabled: true, signupMethod: 'phone', phoneSignupReloginAfterBindEmailEnabled: true }), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]);
|
||||
assert.equal(api.getLastStepId({ plusModeEnabled: true, signupMethod: 'phone', phoneSignupReloginAfterBindEmailEnabled: true }), 17);
|
||||
assert.equal(api.hasFlow('openai'), true);
|
||||
assert.equal(api.hasFlow('site-a'), false);
|
||||
assert.deepStrictEqual(api.getRegisteredFlowIds(), ['openai']);
|
||||
|
||||
Reference in New Issue
Block a user