fix: update API URLs and improve error handling for SUB2API configuration

This commit is contained in:
QLHazyCoder
2026-05-08 09:11:19 +08:00
parent 144b144042
commit 6f9a9913b5
12 changed files with 62 additions and 16 deletions
+14 -5
View File
@@ -237,7 +237,7 @@ const STEP7_MAIL_POLLING_RECOVERY_MAX_ATTEMPTS = 8;
const OAUTH_FLOW_TIMEOUT_MS = 5 * 60 * 1000;
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_URL = '';
const DEFAULT_CODEX2API_URL = 'http://localhost:8080/admin/accounts';
const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.qlhazycoder.top';
const DEFAULT_SUB2API_GROUP_NAME = 'codex';
@@ -2305,15 +2305,23 @@ function normalizePersistentSettingValue(key, value) {
);
case 'gopayHelperApiUrl':
{
const legacyGpcHelperApiUrl = 'https://gpc.leftcode.xyz';
const defaultGpcHelperApiUrl = PERSISTED_SETTING_DEFAULTS.gopayHelperApiUrl
|| (typeof DEFAULT_GPC_HELPER_API_URL !== 'undefined' ? DEFAULT_GPC_HELPER_API_URL : 'https://gpc.qlhazycoder.top');
const normalizedGpcHelperApiUrl = self.GoPayUtils?.normalizeGpcHelperBaseUrl
? self.GoPayUtils.normalizeGpcHelperBaseUrl(value || defaultGpcHelperApiUrl)
: String(value || defaultGpcHelperApiUrl).trim().replace(/\/+$/g, '');
return normalizedGpcHelperApiUrl === legacyGpcHelperApiUrl
? defaultGpcHelperApiUrl
: normalizedGpcHelperApiUrl;
if (!self.GoPayUtils?.normalizeGpcHelperBaseUrl) {
try {
const parsed = new URL(normalizedGpcHelperApiUrl);
const hostname = parsed.hostname.toLowerCase();
if (hostname !== 'gpc.qlhazycoder.top' && hostname !== 'localhost' && hostname !== '127.0.0.1') {
return defaultGpcHelperApiUrl;
}
} catch {
return defaultGpcHelperApiUrl;
}
}
return normalizedGpcHelperApiUrl;
}
case 'gopayHelperApiKey':
case 'gopayHelperCardKey':
@@ -6777,6 +6785,7 @@ function normalizeSub2ApiUrl(rawUrl) {
return navigationUtils.normalizeSub2ApiUrl(rawUrl);
}
const input = (rawUrl || '').trim() || DEFAULT_SUB2API_URL;
if (!input) return '';
const withProtocol = /^https?:\/\//i.test(input) ? input : `https://${input}`;
const parsed = new URL(withProtocol);
if (!parsed.pathname || parsed.pathname === '/') {
-2
View File
@@ -37,12 +37,10 @@
const MAIL2925_COOKIE_DOMAINS = [
'2925.com',
'www.2925.com',
'mail2.xiyouji.com',
];
const MAIL2925_COOKIE_ORIGINS = [
'https://2925.com',
'https://www.2925.com',
'https://mail2.xiyouji.com',
];
const MAIL2925_LIMIT_ERROR_PREFIX = 'MAIL2925_LIMIT_REACHED::';
const MAIL2925_THREAD_TERMINATED_ERROR_PREFIX = 'MAIL2925_THREAD_TERMINATED::';
+1
View File
@@ -19,6 +19,7 @@
function normalizeSub2ApiUrl(rawUrl) {
const input = (rawUrl || '').trim() || DEFAULT_SUB2API_URL;
if (!input) return '';
const withProtocol = /^https?:\/\//i.test(input) ? input : `https://${input}`;
const parsed = new URL(withProtocol);
if (!parsed.pathname || parsed.pathname === '/') {
+3
View File
@@ -251,6 +251,9 @@
const sub2apiUrl = normalizeSub2ApiUrl(state.sub2apiUrl);
const groupName = (state.sub2apiGroupName || DEFAULT_SUB2API_GROUP_NAME).trim() || DEFAULT_SUB2API_GROUP_NAME;
if (!sub2apiUrl) {
throw new Error('SUB2API URL is not configured. Please fill it in the side panel first.');
}
if (!state.sub2apiEmail) {
throw new Error('尚未配置 SUB2API 登录邮箱,请先在侧边栏填写。');
}
+3
View File
@@ -343,6 +343,9 @@
}
const sub2apiUrl = normalizeSub2ApiUrl(state.sub2apiUrl);
if (!sub2apiUrl) {
throw new Error('SUB2API URL is not configured. Please fill it in the side panel first.');
}
const injectFiles = ['content/utils.js', 'content/sub2api-panel.js'];
await addStepLog(visibleStep, '正在打开 SUB2API 后台...');
+10 -3
View File
@@ -5,7 +5,7 @@
const PLUS_PAYMENT_METHOD_GOPAY = 'gopay';
const PLUS_PAYMENT_METHOD_GPC_HELPER = 'gpc-helper';
const DEFAULT_GPC_HELPER_API_URL = 'https://gpc.qlhazycoder.top';
const LEGACY_GPC_HELPER_API_URL = 'https://gpc.leftcode.xyz';
const ALLOWED_GPC_HELPER_REMOTE_HOST = 'gpc.qlhazycoder.top';
function normalizePlusPaymentMethod(value = '') {
const normalized = String(value || '').trim().toLowerCase();
@@ -67,10 +67,17 @@
normalized = normalized.replace(/\/api\/gp\/balance(?:\?.*)?$/i, '');
normalized = normalized.replace(/\/api\/card\/balance(?:\?.*)?$/i, '');
normalized = normalized.replace(/\/api\/card\/redeem-api-key(?:\?.*)?$/i, '');
if (normalized === LEGACY_GPC_HELPER_API_URL) {
try {
const parsed = new URL(normalized);
const hostname = parsed.hostname.toLowerCase();
if (hostname === ALLOWED_GPC_HELPER_REMOTE_HOST || hostname === 'localhost' || hostname === '127.0.0.1') {
return normalized || DEFAULT_GPC_HELPER_API_URL;
}
return DEFAULT_GPC_HELPER_API_URL;
} catch {
return DEFAULT_GPC_HELPER_API_URL;
}
return normalized || DEFAULT_GPC_HELPER_API_URL;
}
function buildGpcHelperApiUrl(apiUrl = '', path = '') {
+5 -3
View File
@@ -6,7 +6,6 @@
root.HotmailUtils = factory();
})(typeof self !== 'undefined' ? self : globalThis, function createHotmailUtils() {
const HOTMAIL_MAIL_API_URL = 'https://apple.882263.xyz/api/mail-new';
const HOTMAIL_SERVICE_MODE_REMOTE = 'remote';
const HOTMAIL_SERVICE_MODE_LOCAL = 'local';
@@ -287,8 +286,11 @@
return list.map((message) => normalizeHotmailMailApiMessage(message));
}
function buildHotmailMailApiLatestUrl(options) {
const apiUrl = String(options?.apiUrl || '').trim() || HOTMAIL_MAIL_API_URL;
function buildHotmailMailApiLatestUrl(options = {}) {
const apiUrl = String(options?.apiUrl || '').trim();
if (!apiUrl) {
throw new Error('Hotmail mail API URL is required.');
}
const url = new URL(apiUrl);
url.searchParams.set('refresh_token', String(options?.refreshToken || ''));
url.searchParams.set('client_id', String(options?.clientId || ''));
+1 -1
View File
@@ -179,7 +179,7 @@
<div class="data-row" id="row-sub2api-url" style="display:none;">
<span class="data-label">SUB2API</span>
<input type="text" id="input-sub2api-url" class="data-input"
placeholder="https://sub2api.hisence.fun/admin/accounts" />
placeholder="https://your-sub2api-domain/admin/accounts" />
</div>
<div class="data-row" id="row-sub2api-email" style="display:none;">
<span class="data-label">账号</span>
@@ -71,6 +71,20 @@ test('navigation utils support codex2api mode and url normalization', () => {
assert.equal(utils.getPanelModeLabel('codex2api'), 'Codex2API');
});
test('navigation utils leaves SUB2API url empty when no default is configured', () => {
const source = fs.readFileSync('background/navigation-utils.js', 'utf8');
const globalScope = {};
const api = new Function('self', `${source}; return self.MultiPageBackgroundNavigationUtils;`)(globalScope);
const utils = api.createNavigationUtils({
DEFAULT_CODEX2API_URL: 'http://localhost:8080/admin/accounts',
DEFAULT_SUB2API_URL: '',
normalizeLocalCpaStep9Mode: (value) => value,
});
assert.equal(utils.normalizeSub2ApiUrl(''), '');
});
test('navigation utils recognize the iCloud mail tab family on both supported hosts', () => {
const source = fs.readFileSync('background/navigation-utils.js', 'utf8');
const globalScope = {};
+1
View File
@@ -28,6 +28,7 @@ test('GoPay utils builds GPC queue task and balance URLs from helper endpoints',
const api = loadGoPayUtils();
assert.equal(api.DEFAULT_GPC_HELPER_API_URL, 'https://gpc.qlhazycoder.top');
assert.equal(api.normalizeGpcHelperBaseUrl(''), 'https://gpc.qlhazycoder.top');
assert.equal(api.normalizeGpcHelperBaseUrl('https://example.com/api/gp/tasks'), 'https://gpc.qlhazycoder.top');
assert.equal(
api.buildGpcHelperApiUrl('', '/api/checkout/start'),
'https://gpc.qlhazycoder.top/api/checkout/start'
+9 -1
View File
@@ -358,13 +358,14 @@ test('pickVerificationMessageWithTimeFallback can ignore afterTimestamp while ke
test('buildHotmailMailApiLatestUrl includes email, client id, refresh token, and mailbox', () => {
const url = new URL(buildHotmailMailApiLatestUrl({
apiUrl: 'https://example.com/api/mail-new',
clientId: 'client-123',
email: 'user@hotmail.com',
refreshToken: 'refresh-token-xyz',
mailbox: 'Junk',
}));
assert.equal(url.origin + url.pathname, 'https://apple.882263.xyz/api/mail-new');
assert.equal(url.origin + url.pathname, 'https://example.com/api/mail-new');
assert.equal(url.searchParams.get('client_id'), 'client-123');
assert.equal(url.searchParams.get('email'), 'user@hotmail.com');
assert.equal(url.searchParams.get('refresh_token'), 'refresh-token-xyz');
@@ -372,6 +373,13 @@ test('buildHotmailMailApiLatestUrl includes email, client id, refresh token, and
assert.equal(url.searchParams.get('response_type'), 'json');
});
test('buildHotmailMailApiLatestUrl requires an explicit api url', () => {
assert.throws(
() => buildHotmailMailApiLatestUrl({ email: 'user@hotmail.com' }),
/Hotmail mail API URL is required/
);
});
test('buildHotmailMailApiLatestUrl supports custom api url and can omit response_type', () => {
const url = new URL(buildHotmailMailApiLatestUrl({
apiUrl: 'https://example.com/custom-mail-api',
+1 -1
View File
@@ -549,7 +549,7 @@ Plus 模式通过 `plusModeEnabled` 开启,目标是在普通注册资料完
Plus 模式可见步骤:
1. 第 1~5 步:沿用普通注册入口、邮箱、密码、注册验证码、资料填写链路。
2. 第 6 步 `创建 Plus Checkout`PayPal / GoPay 打开已登录 ChatGPT 页面,通过 `/api/auth/session` 读取 accessToken,再请求 `https://chatgpt.com/backend-api/payments/checkout` 创建 `chatgptplusplan` 的 checkout session。PayPal 使用 `DE / EUR``https://chatgpt.com/checkout/openai_ie/{checkout_session_id}`GoPay 使用 `ID / IDR``https://chatgpt.com/checkout/openai_llc/{checkout_session_id}`GPC helper 模式改为把 accessToken、手机号、国家区号、`otp_channel` 等提交到 `gopayHelperApiUrl``/api/gp/tasks`,并通过 `gopayHelperApiKey` 发送 `X-API-Key` 认证,创建后保存 `task_id`;当前默认 GPC API 地址为 `https://gpc.qlhazycoder.top`,旧的 `https://gpc.leftcode.xyz` 配置会兼容归一到新地址
2. 第 6 步 `创建 Plus Checkout`PayPal / GoPay 打开已登录 ChatGPT 页面,通过 `/api/auth/session` 读取 accessToken,再请求 `https://chatgpt.com/backend-api/payments/checkout` 创建 `chatgptplusplan` 的 checkout session。PayPal 使用 `DE / EUR``https://chatgpt.com/checkout/openai_ie/{checkout_session_id}`GoPay 使用 `ID / IDR``https://chatgpt.com/checkout/openai_llc/{checkout_session_id}`GPC helper 模式改为把 accessToken、手机号、国家区号、`otp_channel` 等提交到 `gopayHelperApiUrl``/api/gp/tasks`,并通过 `gopayHelperApiKey` 发送 `X-API-Key` 认证,创建后保存 `task_id`;当前默认 GPC API 地址为 `https://gpc.qlhazycoder.top`
3. 第 7 步 `填写账单并提交订阅`PayPal / GoPay 仍走 checkout 页面与 Stripe iframe 自动化;GPC helper 模式不再操作 checkout iframe,而是轮询 `/api/gp/tasks/{task_id}`,根据远端 `api_waiting_for` 依次向 `/otp``/pin` 提交验证码与 PIN。若侧栏启用本地 OTP helper,后台会轮询 `gopayHelperLocalSmsHelperUrl``/latest-otp?phone=...&consume=1` 接口,按当前 GPC 手机号读取并消费当前 OTP 通道的验证码;未开启本地 helper 时才弹出手动 OTP 输入框。仓库内置的 `scripts/gpc_sms_helper_macos.py` 是 macOS Messages/本地通知兼容读取实现,其他通道需要提供兼容的本地 `/latest-otp``/otp` 接口。
4. 仅 PayPal / GoPay 会继续显示第 8 步:该步按当前支付方式显示为 `PayPal 登录与授权``GoPay 手机验证与授权`,底层 step key 仍为 `paypal-approve`
5. 第 9 步 `订阅回跳确认` 仅在 PayPal / GoPay 模式下等待授权后回跳到 ChatGPT / OpenAI 页面,页面加载完成后固定等待 1 秒。GPC helper 模式在第 7 步任务完成后会直接进入 Plus 可见第 10 步 OAuth 登录。