diff --git a/README.md b/README.md index 5ddedd7..3973938 100644 --- a/README.md +++ b/README.md @@ -157,9 +157,9 @@ Step 1 和 Step 10 都依赖这个地址。 - `SUB2API`:后台账号管理页地址 - `账号 / 密码`:SUB2API 管理员登录信息 - `分组`:目标 OpenAI 分组,留空时默认 `codex` -- `默认代理`:创建账号时必须绑定的代理名称或代理 ID,默认 `shadowrocket` +- `默认代理`:可选,填写代理名称或代理 ID;留空时不使用代理 -插件会在 Step 1 和 Step 10 自动从 `/api/v1/admin/proxies/all` 解析这个代理,并分别把 `proxy_id` 传给 OAuth 链接生成、授权码交换和账号创建请求。如果名称匹配到多个代理,请改填代理 ID。 +插件会在 Step 1 和 Step 10 自动从 `/api/v1/admin/proxies/all` 解析这个代理,并在 OAuth 链接生成、授权码交换和账号创建请求中附带 `proxy_id`。如果名称匹配到多个代理,请改填代理 ID;留空则不会发送 `proxy_id`。 ### `Mail` diff --git a/background.js b/background.js index 559d010..0a9587a 100644 --- a/background.js +++ b/background.js @@ -141,7 +141,7 @@ const SUB2API_STEP1_RESPONSE_TIMEOUT_MS = 90000; const SUB2API_STEP9_RESPONSE_TIMEOUT_MS = 120000; const DEFAULT_SUB2API_URL = 'https://sub2api.hisence.fun/admin/accounts'; const DEFAULT_SUB2API_GROUP_NAME = 'codex'; -const DEFAULT_SUB2API_PROXY_NAME = 'shadowrocket'; +const DEFAULT_SUB2API_PROXY_NAME = ''; const DEFAULT_SUB2API_REDIRECT_URI = 'http://localhost:1455/auth/callback'; const AUTO_RUN_TIMER_ALARM_NAME = 'auto-run-timer'; const AUTO_RUN_TIMER_KIND_SCHEDULED_START = 'scheduled_start'; @@ -832,7 +832,7 @@ function normalizePersistentSettingValue(key, value) { case 'sub2apiGroupName': return String(value || '').trim(); case 'sub2apiDefaultProxyName': - return String(value || '').trim() || DEFAULT_SUB2API_PROXY_NAME; + return String(value || '').trim(); case 'customPassword': return String(value || ''); case 'autoRunSkipFailures': @@ -4619,6 +4619,7 @@ async function handleStepData(step, payload) { if (payload.sub2apiOAuthState !== undefined) updates.sub2apiOAuthState = payload.sub2apiOAuthState || null; if (payload.sub2apiGroupId !== undefined) updates.sub2apiGroupId = payload.sub2apiGroupId || null; if (payload.sub2apiDraftName !== undefined) updates.sub2apiDraftName = payload.sub2apiDraftName || null; + if (payload.sub2apiProxyId !== undefined) updates.sub2apiProxyId = payload.sub2apiProxyId || null; if (Object.keys(updates).length) { await setState(updates); } diff --git a/content/sub2api-panel.js b/content/sub2api-panel.js index 1633b31..0cdd8c7 100644 --- a/content/sub2api-panel.js +++ b/content/sub2api-panel.js @@ -4,7 +4,7 @@ console.log('[MultiPage:sub2api-panel] Content script loaded on', location.href) const SUB2API_PANEL_LISTENER_SENTINEL = 'data-multipage-sub2api-panel-listener'; const SUB2API_DEFAULT_GROUP_NAME = 'codex'; -const SUB2API_DEFAULT_PROXY_NAME = 'shadowrocket'; +const SUB2API_DEFAULT_PROXY_NAME = ''; const SUB2API_DEFAULT_REDIRECT_URI = 'http://localhost:1455/auth/callback'; const SUB2API_DEFAULT_CONCURRENCY = 10; const SUB2API_DEFAULT_PRIORITY = 1; @@ -195,10 +195,10 @@ function normalizeSub2ApiProxyPreference(value) { function resolveSub2ApiProxyPreference(payload = {}, backgroundState = {}) { if (payload.sub2apiDefaultProxyName !== undefined) { - return normalizeSub2ApiProxyPreference(payload.sub2apiDefaultProxyName) || SUB2API_DEFAULT_PROXY_NAME; + return normalizeSub2ApiProxyPreference(payload.sub2apiDefaultProxyName); } if (backgroundState.sub2apiDefaultProxyName !== undefined) { - return normalizeSub2ApiProxyPreference(backgroundState.sub2apiDefaultProxyName) || SUB2API_DEFAULT_PROXY_NAME; + return normalizeSub2ApiProxyPreference(backgroundState.sub2apiDefaultProxyName); } return SUB2API_DEFAULT_PROXY_NAME; } @@ -327,9 +327,9 @@ async function resolveSub2ApiProxy(origin, token, preference = '') { throw new Error(`SUB2API 默认代理“${configured}”不存在或未启用。可用代理:${available}`); } if (reason === 'no-preference') { - throw new Error(`SUB2API 存在多个可用代理,请在侧边栏填写默认代理名称或 ID。可用代理:${available}`); + throw new Error(`SUB2API 存在多个可用代理,请在侧边栏填写默认代理名称或 ID;留空则不使用代理。可用代理:${available}`); } - throw new Error('SUB2API 没有可用代理;当前流程要求账号必须绑定代理。'); + throw new Error('SUB2API 没有可用代理;请检查默认代理配置,或将其留空以禁用代理。'); } function buildDraftAccountName(groupName) { @@ -451,21 +451,29 @@ async function step1_generateOpenAiAuthUrl(payload = {}, options = {}) { const { origin, token } = await loginSub2Api(payload); const group = await getGroupByName(origin, token, groupName); const proxyPreference = resolveSub2ApiProxyPreference(payload); - const proxy = await resolveSub2ApiProxy(origin, token, proxyPreference); - const proxyId = normalizeProxyId(proxy.id); + const proxy = proxyPreference ? await resolveSub2ApiProxy(origin, token, proxyPreference) : null; + const proxyId = normalizeProxyId(proxy?.id); const draftName = buildDraftAccountName(group.name || groupName); log(`步骤 ${logStep}:已登录 SUB2API,使用分组 ${group.name}(#${group.id})。`); - log(`步骤 ${logStep}:已选择 SUB2API 默认代理 ${buildProxyDisplayName(proxy)}。`); + if (proxy) { + log(`步骤 ${logStep}:已选择 SUB2API 默认代理 ${buildProxyDisplayName(proxy)}。`); + } else { + log(`步骤 ${logStep}:未配置 SUB2API 默认代理,本次将不使用代理。`); + } log(`步骤 ${logStep}:正在向 SUB2API 生成 OpenAI Auth 链接,回调地址为 ${redirectUri}。`); + const authRequestBody = { + redirect_uri: redirectUri, + }; + if (proxyId) { + authRequestBody.proxy_id = proxyId; + } + const authData = await requestJson(origin, '/api/v1/admin/openai/generate-auth-url', { method: 'POST', token, - body: { - redirect_uri: redirectUri, - proxy_id: proxyId, - }, + body: authRequestBody, }); const oauthUrl = String(authData?.auth_url || '').trim(); @@ -506,8 +514,9 @@ async function step9_submitOpenAiCallback(payload = {}) { const { origin, token } = await loginSub2Api(payload); const proxyPreference = resolveSub2ApiProxyPreference(payload, backgroundState); const preferredProxyId = normalizeProxyId(payload.sub2apiProxyId || backgroundState.sub2apiProxyId); - const proxy = await resolveSub2ApiProxy(origin, token, preferredProxyId || proxyPreference); - const proxyId = normalizeProxyId(proxy.id); + const proxySelector = preferredProxyId || proxyPreference; + const proxy = proxySelector ? await resolveSub2ApiProxy(origin, token, proxySelector) : null; + const proxyId = normalizeProxyId(proxy?.id); const group = payload.sub2apiGroupId ? { id: payload.sub2apiGroupId, name: payload.sub2apiGroupName || backgroundState.sub2apiGroupName || SUB2API_DEFAULT_GROUP_NAME } : await getGroupByName(origin, token, payload.sub2apiGroupName || backgroundState.sub2apiGroupName || SUB2API_DEFAULT_GROUP_NAME); @@ -520,16 +529,23 @@ async function step9_submitOpenAiCallback(payload = {}) { } log('步骤 10:正在向 SUB2API 交换 OpenAI 授权码...'); - log(`步骤 10:使用 SUB2API 默认代理 ${buildProxyDisplayName(proxy)}。`); + if (proxy) { + log(`步骤 10:使用 SUB2API 默认代理 ${buildProxyDisplayName(proxy)}。`); + } else { + log('步骤 10:未配置 SUB2API 默认代理,本次将不使用代理。'); + } + const exchangeRequestBody = { + session_id: sessionId, + code: callback.code, + state: callback.state, + }; + if (proxyId) { + exchangeRequestBody.proxy_id = proxyId; + } const exchangeData = await requestJson(origin, '/api/v1/admin/openai/exchange-code', { method: 'POST', token, - body: { - session_id: sessionId, - code: callback.code, - state: callback.state, - proxy_id: proxyId, - }, + body: exchangeRequestBody, }); const credentials = buildOpenAiCredentials(exchangeData); @@ -547,10 +563,12 @@ async function step9_submitOpenAiCallback(payload = {}) { concurrency: SUB2API_DEFAULT_CONCURRENCY, priority: SUB2API_DEFAULT_PRIORITY, rate_multiplier: SUB2API_DEFAULT_RATE_MULTIPLIER, - proxy_id: proxyId, group_ids: [groupId], auto_pause_on_expired: true, }; + if (proxyId) { + createPayload.proxy_id = proxyId; + } if (extra) { createPayload.extra = extra; diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html index 008e0f7..f405a4e 100644 --- a/sidepanel/sidepanel.html +++ b/sidepanel/sidepanel.html @@ -143,7 +143,7 @@
账户密码 diff --git a/sidepanel/sidepanel.js b/sidepanel/sidepanel.js index 5874b20..d7ae9b7 100644 --- a/sidepanel/sidepanel.js +++ b/sidepanel/sidepanel.js @@ -205,7 +205,6 @@ const VERIFICATION_RESEND_COUNT_MAX = 20; const DEFAULT_VERIFICATION_RESEND_COUNT = 4; const DEFAULT_LOCAL_CPA_STEP9_MODE = 'submit'; const DEFAULT_CPA_CALLBACK_MODE = 'step8'; -const DEFAULT_SUB2API_PROXY_NAME = 'shadowrocket'; const MAIL_2925_MODE_PROVIDE = 'provide'; const MAIL_2925_MODE_RECEIVE = 'receive'; const DEFAULT_MAIL_2925_MODE = MAIL_2925_MODE_PROVIDE; @@ -1310,7 +1309,7 @@ function collectSettingsPayload() { sub2apiEmail: inputSub2ApiEmail.value.trim(), sub2apiPassword: inputSub2ApiPassword.value, sub2apiGroupName: inputSub2ApiGroup.value.trim(), - sub2apiDefaultProxyName: inputSub2ApiDefaultProxy.value.trim() || DEFAULT_SUB2API_PROXY_NAME, + sub2apiDefaultProxyName: inputSub2ApiDefaultProxy.value.trim(), customPassword: inputPassword.value, mailProvider: selectMailProvider.value, mail2925Mode: getSelectedMail2925Mode(), @@ -1681,7 +1680,7 @@ function applySettingsState(state) { inputSub2ApiEmail.value = state?.sub2apiEmail || ''; inputSub2ApiPassword.value = state?.sub2apiPassword || ''; inputSub2ApiGroup.value = state?.sub2apiGroupName || ''; - inputSub2ApiDefaultProxy.value = state?.sub2apiDefaultProxyName || DEFAULT_SUB2API_PROXY_NAME; + inputSub2ApiDefaultProxy.value = state?.sub2apiDefaultProxyName || ''; const restoredMailProvider = isCustomMailProvider(state?.mailProvider) || [ICLOUD_PROVIDER, 'hotmail-api', GMAIL_PROVIDER, 'luckmail-api', '163', '163-vip', 'qq', 'inbucket', '2925', 'cloudflare-temp-email'].includes(String(state?.mailProvider || '').trim()) ? String(state?.mailProvider || '163').trim() diff --git a/tests/background-account-history-settings.test.js b/tests/background-account-history-settings.test.js index 3b91b03..ef54eda 100644 --- a/tests/background-account-history-settings.test.js +++ b/tests/background-account-history-settings.test.js @@ -61,7 +61,7 @@ const DEFAULT_HOTMAIL_LOCAL_BASE_URL = 'http://127.0.0.1:17373'; const DEFAULT_ACCOUNT_RUN_HISTORY_HELPER_BASE_URL = DEFAULT_HOTMAIL_LOCAL_BASE_URL; const DEFAULT_HOTMAIL_REMOTE_BASE_URL = ''; const DEFAULT_VERIFICATION_RESEND_COUNT = 4; -const DEFAULT_SUB2API_PROXY_NAME = 'shadowrocket'; +const DEFAULT_SUB2API_PROXY_NAME = ''; const HOTMAIL_SERVICE_MODE_REMOTE = 'remote'; const HOTMAIL_SERVICE_MODE_LOCAL = 'local'; const VERIFICATION_RESEND_COUNT_MIN = 0; @@ -112,7 +112,7 @@ return { ); assert.equal( api.normalizePersistentSettingValue('sub2apiDefaultProxyName', ''), - 'shadowrocket' + '' ); assert.equal( api.normalizePersistentSettingValue('sub2apiDefaultProxyName', ' proxy-a '), diff --git a/tests/sub2api-panel-proxy.test.js b/tests/sub2api-panel-proxy.test.js index 80f26d7..c12fc38 100644 --- a/tests/sub2api-panel-proxy.test.js +++ b/tests/sub2api-panel-proxy.test.js @@ -39,7 +39,7 @@ function createSub2ApiPanelContext(fetchCalls = []) { onMessage: { addListener() {} }, sendMessage: async () => ({ email: 'flow@example.com', - sub2apiDefaultProxyName: 'shadowrocket', + sub2apiDefaultProxyName: '', }), }, }, @@ -176,3 +176,46 @@ test('SUB2API step 10 uses the same proxy for code exchange and account creation assert.equal(createCall.body.group_ids[0], 5); assert.equal(context.completed[0].step, 10); }); + +test('SUB2API step 1 omits proxy_id when default proxy is empty', async () => { + const fetchCalls = []; + const context = createSub2ApiPanelContext(fetchCalls); + + const result = await vm.runInContext(` + step1_generateOpenAiAuthUrl({ + sub2apiEmail: 'admin@example.com', + sub2apiPassword: 'secret', + sub2apiGroupName: 'codex', + sub2apiDefaultProxyName: ' ' + }, { report: false }) + `, context); + + const generateCall = fetchCalls.find((call) => call.path === '/api/v1/admin/openai/generate-auth-url'); + assert.equal(result.sub2apiProxyId, null); + assert.equal(Object.hasOwn(generateCall.body, 'proxy_id'), false); +}); + +test('SUB2API step 10 omits proxy_id when no proxy is configured', async () => { + const fetchCalls = []; + const context = createSub2ApiPanelContext(fetchCalls); + + await vm.runInContext(` + step9_submitOpenAiCallback({ + localhostUrl: 'http://localhost:1455/auth/callback?code=callback-code&state=oauth-state', + sub2apiUrl: 'https://sub.example/admin/accounts', + sub2apiEmail: 'admin@example.com', + sub2apiPassword: 'secret', + sub2apiGroupName: 'codex', + sub2apiSessionId: 'session-1', + sub2apiOAuthState: 'oauth-state', + sub2apiGroupId: 5, + sub2apiDefaultProxyName: '' + }) + `, context); + + const exchangeCall = fetchCalls.find((call) => call.path === '/api/v1/admin/openai/exchange-code'); + const createCall = fetchCalls.find((call) => call.path === '/api/v1/admin/accounts'); + + assert.equal(Object.hasOwn(exchangeCall.body, 'proxy_id'), false); + assert.equal(Object.hasOwn(createCall.body, 'proxy_id'), false); +});