Add configurable SUB2API account priority
This commit is contained in:
@@ -242,6 +242,7 @@ const DEFAULT_CODEX2API_URL = 'http://localhost:8080/admin/accounts';
|
|||||||
const DEFAULT_GPC_HELPER_API_URL = 'https://gopay.hwork.pro';
|
const DEFAULT_GPC_HELPER_API_URL = 'https://gopay.hwork.pro';
|
||||||
const DEFAULT_SUB2API_GROUP_NAME = 'codex';
|
const DEFAULT_SUB2API_GROUP_NAME = 'codex';
|
||||||
const DEFAULT_SUB2API_PROXY_NAME = '';
|
const DEFAULT_SUB2API_PROXY_NAME = '';
|
||||||
|
const DEFAULT_SUB2API_ACCOUNT_PRIORITY = 1;
|
||||||
const CONTRIBUTION_SOURCE_CPA = 'cpa';
|
const CONTRIBUTION_SOURCE_CPA = 'cpa';
|
||||||
const CONTRIBUTION_SOURCE_SUB2API = 'sub2api';
|
const CONTRIBUTION_SOURCE_SUB2API = 'sub2api';
|
||||||
const CONTRIBUTION_SUB2API_DEFAULT_GROUP_NAME = 'codex号池';
|
const CONTRIBUTION_SUB2API_DEFAULT_GROUP_NAME = 'codex号池';
|
||||||
@@ -574,6 +575,7 @@ const PERSISTED_SETTING_DEFAULTS = {
|
|||||||
sub2apiPassword: '',
|
sub2apiPassword: '',
|
||||||
sub2apiGroupName: DEFAULT_SUB2API_GROUP_NAME,
|
sub2apiGroupName: DEFAULT_SUB2API_GROUP_NAME,
|
||||||
sub2apiGroupNames: DEFAULT_SUB2API_GROUP_NAMES,
|
sub2apiGroupNames: DEFAULT_SUB2API_GROUP_NAMES,
|
||||||
|
sub2apiAccountPriority: DEFAULT_SUB2API_ACCOUNT_PRIORITY,
|
||||||
sub2apiDefaultProxyName: DEFAULT_SUB2API_PROXY_NAME,
|
sub2apiDefaultProxyName: DEFAULT_SUB2API_PROXY_NAME,
|
||||||
ipProxyEnabled: false,
|
ipProxyEnabled: false,
|
||||||
ipProxyService: DEFAULT_IP_PROXY_SERVICE,
|
ipProxyService: DEFAULT_IP_PROXY_SERVICE,
|
||||||
@@ -2105,6 +2107,18 @@ function normalizeSub2ApiGroupNames(value = '') {
|
|||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeSub2ApiAccountPriority(value, fallback = DEFAULT_SUB2API_ACCOUNT_PRIORITY) {
|
||||||
|
const rawValue = String(value ?? '').trim();
|
||||||
|
const numeric = Number(rawValue);
|
||||||
|
if (!rawValue || !Number.isSafeInteger(numeric) || numeric < 1) {
|
||||||
|
const fallbackNumber = Number(fallback);
|
||||||
|
return Number.isSafeInteger(fallbackNumber) && fallbackNumber >= 1
|
||||||
|
? fallbackNumber
|
||||||
|
: DEFAULT_SUB2API_ACCOUNT_PRIORITY;
|
||||||
|
}
|
||||||
|
return numeric;
|
||||||
|
}
|
||||||
|
|
||||||
function normalizePersistentSettingValue(key, value) {
|
function normalizePersistentSettingValue(key, value) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'panelMode':
|
case 'panelMode':
|
||||||
@@ -2125,6 +2139,8 @@ function normalizePersistentSettingValue(key, value) {
|
|||||||
return String(value || '').trim();
|
return String(value || '').trim();
|
||||||
case 'sub2apiGroupNames':
|
case 'sub2apiGroupNames':
|
||||||
return normalizeSub2ApiGroupNames(value);
|
return normalizeSub2ApiGroupNames(value);
|
||||||
|
case 'sub2apiAccountPriority':
|
||||||
|
return normalizeSub2ApiAccountPriority(value);
|
||||||
case 'sub2apiDefaultProxyName':
|
case 'sub2apiDefaultProxyName':
|
||||||
return String(value || '').trim();
|
return String(value || '').trim();
|
||||||
case 'ipProxyEnabled':
|
case 'ipProxyEnabled':
|
||||||
|
|||||||
@@ -293,6 +293,7 @@
|
|||||||
sub2apiPassword: state.sub2apiPassword,
|
sub2apiPassword: state.sub2apiPassword,
|
||||||
sub2apiGroupName: groupName,
|
sub2apiGroupName: groupName,
|
||||||
sub2apiDefaultProxyName: state.sub2apiDefaultProxyName,
|
sub2apiDefaultProxyName: state.sub2apiDefaultProxyName,
|
||||||
|
sub2apiAccountPriority: state.sub2apiAccountPriority,
|
||||||
logStep: 7,
|
logStep: 7,
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
|
|||||||
@@ -380,6 +380,7 @@
|
|||||||
sub2apiPassword: state.sub2apiPassword,
|
sub2apiPassword: state.sub2apiPassword,
|
||||||
sub2apiGroupName: state.sub2apiGroupName,
|
sub2apiGroupName: state.sub2apiGroupName,
|
||||||
sub2apiDefaultProxyName: state.sub2apiDefaultProxyName,
|
sub2apiDefaultProxyName: state.sub2apiDefaultProxyName,
|
||||||
|
sub2apiAccountPriority: state.sub2apiAccountPriority,
|
||||||
sub2apiProxyId: state.sub2apiProxyId,
|
sub2apiProxyId: state.sub2apiProxyId,
|
||||||
sub2apiSessionId: state.sub2apiSessionId,
|
sub2apiSessionId: state.sub2apiSessionId,
|
||||||
sub2apiOAuthState: state.sub2apiOAuthState,
|
sub2apiOAuthState: state.sub2apiOAuthState,
|
||||||
|
|||||||
@@ -252,6 +252,21 @@ function resolveSub2ApiProxyPreference(payload = {}, backgroundState = {}) {
|
|||||||
return SUB2API_DEFAULT_PROXY_NAME;
|
return SUB2API_DEFAULT_PROXY_NAME;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resolveSub2ApiAccountPriority(payload = {}, backgroundState = {}) {
|
||||||
|
const candidate = payload.sub2apiAccountPriority !== undefined
|
||||||
|
? payload.sub2apiAccountPriority
|
||||||
|
: backgroundState.sub2apiAccountPriority;
|
||||||
|
const rawValue = String(candidate ?? '').trim();
|
||||||
|
if (!rawValue) {
|
||||||
|
return SUB2API_DEFAULT_PRIORITY;
|
||||||
|
}
|
||||||
|
const numeric = Number(rawValue);
|
||||||
|
if (!Number.isSafeInteger(numeric) || numeric < 1) {
|
||||||
|
throw new Error('SUB2API 账号优先级必须是大于等于 1 的整数。');
|
||||||
|
}
|
||||||
|
return numeric;
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeProxyId(value) {
|
function normalizeProxyId(value) {
|
||||||
if (value === undefined || value === null || value === '') {
|
if (value === undefined || value === null || value === '') {
|
||||||
return null;
|
return null;
|
||||||
@@ -568,6 +583,7 @@ async function step9_submitOpenAiCallback(payload = {}) {
|
|||||||
const proxySelector = preferredProxyId || proxyPreference;
|
const proxySelector = preferredProxyId || proxyPreference;
|
||||||
const proxy = proxySelector ? await resolveSub2ApiProxy(origin, token, proxySelector) : null;
|
const proxy = proxySelector ? await resolveSub2ApiProxy(origin, token, proxySelector) : null;
|
||||||
const proxyId = normalizeProxyId(proxy?.id);
|
const proxyId = normalizeProxyId(proxy?.id);
|
||||||
|
const accountPriority = resolveSub2ApiAccountPriority(payload, backgroundState);
|
||||||
const storedGroupIds = Array.isArray(payload.sub2apiGroupIds)
|
const storedGroupIds = Array.isArray(payload.sub2apiGroupIds)
|
||||||
? payload.sub2apiGroupIds
|
? payload.sub2apiGroupIds
|
||||||
: (Array.isArray(backgroundState.sub2apiGroupIds) ? backgroundState.sub2apiGroupIds : []);
|
: (Array.isArray(backgroundState.sub2apiGroupIds) ? backgroundState.sub2apiGroupIds : []);
|
||||||
@@ -627,7 +643,7 @@ async function step9_submitOpenAiCallback(payload = {}) {
|
|||||||
type: 'oauth',
|
type: 'oauth',
|
||||||
credentials,
|
credentials,
|
||||||
concurrency: SUB2API_DEFAULT_CONCURRENCY,
|
concurrency: SUB2API_DEFAULT_CONCURRENCY,
|
||||||
priority: SUB2API_DEFAULT_PRIORITY,
|
priority: accountPriority,
|
||||||
rate_multiplier: SUB2API_DEFAULT_RATE_MULTIPLIER,
|
rate_multiplier: SUB2API_DEFAULT_RATE_MULTIPLIER,
|
||||||
group_ids: groupIds,
|
group_ids: groupIds,
|
||||||
auto_pause_on_expired: true,
|
auto_pause_on_expired: true,
|
||||||
|
|||||||
@@ -210,6 +210,10 @@
|
|||||||
<button id="btn-add-sub2api-group" class="btn btn-outline btn-sm data-inline-btn" type="button">添加</button>
|
<button id="btn-add-sub2api-group" class="btn btn-outline btn-sm data-inline-btn" type="button">添加</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="data-row" id="row-sub2api-account-priority" style="display:none;">
|
||||||
|
<span class="data-label">优先级</span>
|
||||||
|
<input type="number" id="input-sub2api-account-priority" class="data-input mono" value="1" min="1" step="1" />
|
||||||
|
</div>
|
||||||
<div class="data-row" id="row-sub2api-default-proxy" style="display:none;">
|
<div class="data-row" id="row-sub2api-default-proxy" style="display:none;">
|
||||||
<span class="data-label">默认代理</span>
|
<span class="data-label">默认代理</span>
|
||||||
<input type="text" id="input-sub2api-default-proxy" class="data-input" placeholder="留空则不使用代理;或填写代理名称 / ID" />
|
<input type="text" id="input-sub2api-default-proxy" class="data-input" placeholder="留空则不使用代理;或填写代理名称 / ID" />
|
||||||
|
|||||||
@@ -100,6 +100,8 @@ const btnSub2ApiGroupMenu = document.getElementById('btn-sub2api-group-menu');
|
|||||||
const sub2ApiGroupCurrent = document.getElementById('sub2api-group-current');
|
const sub2ApiGroupCurrent = document.getElementById('sub2api-group-current');
|
||||||
const sub2ApiGroupMenu = document.getElementById('sub2api-group-menu');
|
const sub2ApiGroupMenu = document.getElementById('sub2api-group-menu');
|
||||||
const btnAddSub2ApiGroup = document.getElementById('btn-add-sub2api-group');
|
const btnAddSub2ApiGroup = document.getElementById('btn-add-sub2api-group');
|
||||||
|
const rowSub2ApiAccountPriority = document.getElementById('row-sub2api-account-priority');
|
||||||
|
const inputSub2ApiAccountPriority = document.getElementById('input-sub2api-account-priority');
|
||||||
const rowSub2ApiDefaultProxy = document.getElementById('row-sub2api-default-proxy');
|
const rowSub2ApiDefaultProxy = document.getElementById('row-sub2api-default-proxy');
|
||||||
const inputSub2ApiDefaultProxy = document.getElementById('input-sub2api-default-proxy');
|
const inputSub2ApiDefaultProxy = document.getElementById('input-sub2api-default-proxy');
|
||||||
const rowIpProxyEnabled = document.getElementById('row-ip-proxy-enabled');
|
const rowIpProxyEnabled = document.getElementById('row-ip-proxy-enabled');
|
||||||
@@ -1150,6 +1152,15 @@ function normalizeSub2ApiGroupOptions(...sources) {
|
|||||||
return normalizeEditableListValues(...sources);
|
return normalizeEditableListValues(...sources);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeSub2ApiAccountPriorityValue(value) {
|
||||||
|
const rawValue = String(value ?? '').trim();
|
||||||
|
const numeric = Number(rawValue);
|
||||||
|
if (!rawValue || !Number.isSafeInteger(numeric) || numeric < 1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return numeric;
|
||||||
|
}
|
||||||
|
|
||||||
function getSelectedSub2ApiGroupName() {
|
function getSelectedSub2ApiGroupName() {
|
||||||
return String(inputSub2ApiGroup?.value || '').trim()
|
return String(inputSub2ApiGroup?.value || '').trim()
|
||||||
|| DEFAULT_SUB2API_GROUP_OPTIONS[0];
|
|| DEFAULT_SUB2API_GROUP_OPTIONS[0];
|
||||||
@@ -3181,6 +3192,12 @@ function collectSettingsPayload() {
|
|||||||
if (sub2apiGroupNames.length === 0) {
|
if (sub2apiGroupNames.length === 0) {
|
||||||
appendSub2ApiGroupNames(['codex', 'openai-plus']);
|
appendSub2ApiGroupNames(['codex', 'openai-plus']);
|
||||||
}
|
}
|
||||||
|
const sub2apiAccountPriorityNormalizer = typeof normalizeSub2ApiAccountPriorityValue === 'function'
|
||||||
|
? normalizeSub2ApiAccountPriorityValue
|
||||||
|
: ((value) => {
|
||||||
|
const numeric = Number(String(value ?? '').trim());
|
||||||
|
return Number.isSafeInteger(numeric) && numeric >= 1 ? numeric : 1;
|
||||||
|
});
|
||||||
return {
|
return {
|
||||||
...(contributionModeEnabled ? {} : {
|
...(contributionModeEnabled ? {} : {
|
||||||
panelMode: selectPanelMode.value,
|
panelMode: selectPanelMode.value,
|
||||||
@@ -3193,6 +3210,11 @@ function collectSettingsPayload() {
|
|||||||
sub2apiPassword: inputSub2ApiPassword.value,
|
sub2apiPassword: inputSub2ApiPassword.value,
|
||||||
sub2apiGroupName: selectedSub2ApiGroupName,
|
sub2apiGroupName: selectedSub2ApiGroupName,
|
||||||
sub2apiGroupNames,
|
sub2apiGroupNames,
|
||||||
|
sub2apiAccountPriority: sub2apiAccountPriorityNormalizer(
|
||||||
|
typeof inputSub2ApiAccountPriority !== 'undefined' && inputSub2ApiAccountPriority
|
||||||
|
? inputSub2ApiAccountPriority.value
|
||||||
|
: latestState?.sub2apiAccountPriority
|
||||||
|
),
|
||||||
sub2apiDefaultProxyName: inputSub2ApiDefaultProxy.value.trim(),
|
sub2apiDefaultProxyName: inputSub2ApiDefaultProxy.value.trim(),
|
||||||
ipProxyEnabled: getSelectedIpProxyEnabledSafe(),
|
ipProxyEnabled: getSelectedIpProxyEnabledSafe(),
|
||||||
ipProxyService: selectedIpProxyService,
|
ipProxyService: selectedIpProxyService,
|
||||||
@@ -7550,6 +7572,9 @@ function applyAutoRunStatus(payload = currentAutoRun) {
|
|||||||
if (typeof inputSignupPhone !== 'undefined' && inputSignupPhone) {
|
if (typeof inputSignupPhone !== 'undefined' && inputSignupPhone) {
|
||||||
inputSignupPhone.disabled = locked;
|
inputSignupPhone.disabled = locked;
|
||||||
}
|
}
|
||||||
|
if (typeof inputSub2ApiAccountPriority !== 'undefined' && inputSub2ApiAccountPriority) {
|
||||||
|
inputSub2ApiAccountPriority.disabled = locked;
|
||||||
|
}
|
||||||
inputAutoSkipFailures.disabled = scheduled;
|
inputAutoSkipFailures.disabled = scheduled;
|
||||||
|
|
||||||
const lockedRunCount = typeof getLockedRunCountFromEmailPool === 'function'
|
const lockedRunCount = typeof getLockedRunCountFromEmailPool === 'function'
|
||||||
@@ -7847,6 +7872,9 @@ function applySettingsState(state) {
|
|||||||
inputSub2ApiEmail.value = state?.sub2apiEmail || '';
|
inputSub2ApiEmail.value = state?.sub2apiEmail || '';
|
||||||
inputSub2ApiPassword.value = state?.sub2apiPassword || '';
|
inputSub2ApiPassword.value = state?.sub2apiPassword || '';
|
||||||
renderSub2ApiGroupOptions(state, state?.sub2apiGroupName || '');
|
renderSub2ApiGroupOptions(state, state?.sub2apiGroupName || '');
|
||||||
|
if (typeof inputSub2ApiAccountPriority !== 'undefined' && inputSub2ApiAccountPriority) {
|
||||||
|
inputSub2ApiAccountPriority.value = String(normalizeSub2ApiAccountPriorityValue(state?.sub2apiAccountPriority));
|
||||||
|
}
|
||||||
inputSub2ApiDefaultProxy.value = state?.sub2apiDefaultProxyName || '';
|
inputSub2ApiDefaultProxy.value = state?.sub2apiDefaultProxyName || '';
|
||||||
const normalizedIpProxyService = resolveIpProxyService(state?.ipProxyService);
|
const normalizedIpProxyService = resolveIpProxyService(state?.ipProxyService);
|
||||||
const normalizedIpProxyServiceProfiles = typeof normalizeIpProxyServiceProfiles === 'function'
|
const normalizedIpProxyServiceProfiles = typeof normalizeIpProxyServiceProfiles === 'function'
|
||||||
@@ -9613,6 +9641,7 @@ function updatePanelModeUI() {
|
|||||||
rowSub2ApiEmail.style.display = useSub2Api ? '' : 'none';
|
rowSub2ApiEmail.style.display = useSub2Api ? '' : 'none';
|
||||||
rowSub2ApiPassword.style.display = useSub2Api ? '' : 'none';
|
rowSub2ApiPassword.style.display = useSub2Api ? '' : 'none';
|
||||||
rowSub2ApiGroup.style.display = useSub2Api ? '' : 'none';
|
rowSub2ApiGroup.style.display = useSub2Api ? '' : 'none';
|
||||||
|
rowSub2ApiAccountPriority.style.display = useSub2Api ? '' : 'none';
|
||||||
rowSub2ApiDefaultProxy.style.display = useSub2Api ? '' : 'none';
|
rowSub2ApiDefaultProxy.style.display = useSub2Api ? '' : 'none';
|
||||||
rowCodex2ApiUrl.style.display = useCodex2Api ? '' : 'none';
|
rowCodex2ApiUrl.style.display = useCodex2Api ? '' : 'none';
|
||||||
rowCodex2ApiAdminKey.style.display = useCodex2Api ? '' : 'none';
|
rowCodex2ApiAdminKey.style.display = useCodex2Api ? '' : 'none';
|
||||||
@@ -10354,6 +10383,7 @@ const contributionModeManager = window.SidepanelContributionMode?.createContribu
|
|||||||
rowPhoneVerificationEnabled,
|
rowPhoneVerificationEnabled,
|
||||||
rowCustomPassword,
|
rowCustomPassword,
|
||||||
rowLocalCpaStep9Mode,
|
rowLocalCpaStep9Mode,
|
||||||
|
rowSub2ApiAccountPriority,
|
||||||
rowSub2ApiDefaultProxy,
|
rowSub2ApiDefaultProxy,
|
||||||
rowSub2ApiEmail,
|
rowSub2ApiEmail,
|
||||||
rowSub2ApiGroup,
|
rowSub2ApiGroup,
|
||||||
@@ -11700,6 +11730,16 @@ inputSub2ApiGroup.addEventListener('change', () => {
|
|||||||
markSettingsDirty(true);
|
markSettingsDirty(true);
|
||||||
saveSettings({ silent: true }).catch(() => { });
|
saveSettings({ silent: true }).catch(() => { });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
inputSub2ApiAccountPriority.addEventListener('input', () => {
|
||||||
|
markSettingsDirty(true);
|
||||||
|
scheduleSettingsAutoSave();
|
||||||
|
});
|
||||||
|
inputSub2ApiAccountPriority.addEventListener('blur', () => {
|
||||||
|
inputSub2ApiAccountPriority.value = String(normalizeSub2ApiAccountPriorityValue(inputSub2ApiAccountPriority.value));
|
||||||
|
saveSettings({ silent: true }).catch(() => { });
|
||||||
|
});
|
||||||
|
|
||||||
btnAddSub2ApiGroup?.addEventListener('click', () => {
|
btnAddSub2ApiGroup?.addEventListener('click', () => {
|
||||||
handleAddSub2ApiGroup().catch((error) => {
|
handleAddSub2ApiGroup().catch((error) => {
|
||||||
showToast(error?.message || '添加 SUB2API 分组失败。', 'error');
|
showToast(error?.message || '添加 SUB2API 分组失败。', 'error');
|
||||||
|
|||||||
@@ -116,3 +116,49 @@ test('panel bridge can request cpa oauth url via management api', async () => {
|
|||||||
globalThis.fetch = originalFetch;
|
globalThis.fetch = originalFetch;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('panel bridge forwards SUB2API account priority when requesting oauth url', async () => {
|
||||||
|
const source = fs.readFileSync('background/panel-bridge.js', 'utf8');
|
||||||
|
const sentMessages = [];
|
||||||
|
|
||||||
|
const api = new Function('self', `${source}; return self.MultiPageBackgroundPanelBridge;`)({});
|
||||||
|
const bridge = api.createPanelBridge({
|
||||||
|
addLog: async () => {},
|
||||||
|
chrome: {
|
||||||
|
tabs: {
|
||||||
|
create: async () => ({ id: 72 }),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
closeConflictingTabsForSource: async () => {},
|
||||||
|
ensureContentScriptReadyOnTab: async () => {},
|
||||||
|
getPanelMode: () => 'sub2api',
|
||||||
|
normalizeCodex2ApiUrl: (value) => value,
|
||||||
|
normalizeSub2ApiUrl: (value) => value,
|
||||||
|
rememberSourceLastUrl: async () => {},
|
||||||
|
sendToContentScript: async (sourceName, message, options) => {
|
||||||
|
sentMessages.push({ sourceName, message, options });
|
||||||
|
return {
|
||||||
|
oauthUrl: 'https://auth.openai.com/authorize?state=oauth-state',
|
||||||
|
sub2apiSessionId: 'session-123',
|
||||||
|
sub2apiOAuthState: 'oauth-state',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
sendToContentScriptResilient: async () => ({}),
|
||||||
|
waitForTabUrlFamily: async () => ({ id: 72 }),
|
||||||
|
DEFAULT_SUB2API_GROUP_NAME: 'codex',
|
||||||
|
SUB2API_STEP1_RESPONSE_TIMEOUT_MS: 90000,
|
||||||
|
});
|
||||||
|
|
||||||
|
await bridge.requestOAuthUrlFromPanel({
|
||||||
|
panelMode: 'sub2api',
|
||||||
|
sub2apiUrl: 'https://sub.example/admin/accounts',
|
||||||
|
sub2apiEmail: 'admin@example.com',
|
||||||
|
sub2apiPassword: 'secret',
|
||||||
|
sub2apiGroupName: 'codex',
|
||||||
|
sub2apiAccountPriority: 3,
|
||||||
|
}, { logLabel: '步骤 7' });
|
||||||
|
|
||||||
|
assert.equal(sentMessages.length, 1);
|
||||||
|
assert.equal(sentMessages[0].sourceName, 'sub2api-panel');
|
||||||
|
assert.equal(sentMessages[0].message.payload.sub2apiAccountPriority, 3);
|
||||||
|
});
|
||||||
|
|||||||
@@ -272,3 +272,34 @@ test('platform verify module sends Plus visible step 13 to SUB2API panel', async
|
|||||||
assert.equal(sentMessages[0].message.step, 13);
|
assert.equal(sentMessages[0].message.step, 13);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('platform verify module forwards SUB2API account priority to panel', async () => {
|
||||||
|
const source = fs.readFileSync('background/steps/platform-verify.js', 'utf8');
|
||||||
|
const sentMessages = [];
|
||||||
|
|
||||||
|
const api = new Function('self', `${source}; return self.MultiPageBackgroundStep10;`)({});
|
||||||
|
const { deps } = createDeps({
|
||||||
|
getPanelMode: () => 'sub2api',
|
||||||
|
getTabId: async () => 91,
|
||||||
|
isTabAlive: async () => true,
|
||||||
|
sendToContentScript: async (sourceName, message, options) => {
|
||||||
|
sentMessages.push({ sourceName, message, options });
|
||||||
|
return { ok: true };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const executor = api.createStep10Executor(deps);
|
||||||
|
|
||||||
|
await executor.executeStep10({
|
||||||
|
panelMode: 'sub2api',
|
||||||
|
localhostUrl: 'http://localhost:1455/auth/callback?code=callback-code&state=oauth-state',
|
||||||
|
sub2apiUrl: 'https://sub.example/admin/accounts',
|
||||||
|
sub2apiEmail: 'admin@example.com',
|
||||||
|
sub2apiPassword: 'secret',
|
||||||
|
sub2apiSessionId: 'session-1',
|
||||||
|
sub2apiOAuthState: 'oauth-state',
|
||||||
|
sub2apiAccountPriority: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(sentMessages.length, 1);
|
||||||
|
assert.equal(sentMessages[0].sourceName, 'sub2api-panel');
|
||||||
|
assert.equal(sentMessages[0].message.payload.sub2apiAccountPriority, 2);
|
||||||
|
});
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
const test = require('node:test');
|
||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const fs = require('node:fs');
|
||||||
|
|
||||||
|
const html = fs.readFileSync('sidepanel/sidepanel.html', 'utf8');
|
||||||
|
const source = fs.readFileSync('sidepanel/sidepanel.js', 'utf8');
|
||||||
|
|
||||||
|
test('sidepanel exposes SUB2API account priority below group setting', () => {
|
||||||
|
assert.match(html, /id="row-sub2api-account-priority"/);
|
||||||
|
assert.match(html, /id="input-sub2api-account-priority"/);
|
||||||
|
assert.match(html, /<span class="data-label">优先级<\/span>/);
|
||||||
|
const inputTag = html.match(/<input[^>]*id="input-sub2api-account-priority"[^>]*>/)?.[0] || '';
|
||||||
|
assert.match(inputTag, /type="number"/);
|
||||||
|
assert.match(inputTag, /min="1"/);
|
||||||
|
assert.match(inputTag, /step="1"/);
|
||||||
|
assert.ok(
|
||||||
|
html.indexOf('id="row-sub2api-account-priority"') > html.indexOf('id="row-sub2api-group"'),
|
||||||
|
'priority row should be placed below the SUB2API group row'
|
||||||
|
);
|
||||||
|
assert.ok(
|
||||||
|
html.indexOf('id="row-sub2api-account-priority"') < html.indexOf('id="row-sub2api-default-proxy"'),
|
||||||
|
'priority row should remain above the SUB2API default proxy row'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('sidepanel persists and locks SUB2API account priority setting', () => {
|
||||||
|
assert.match(
|
||||||
|
source,
|
||||||
|
/const rowSub2ApiAccountPriority = document\.getElementById\('row-sub2api-account-priority'\);/
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
source,
|
||||||
|
/const inputSub2ApiAccountPriority = document\.getElementById\('input-sub2api-account-priority'\);/
|
||||||
|
);
|
||||||
|
assert.match(source, /function normalizeSub2ApiAccountPriorityValue\(/);
|
||||||
|
assert.match(source, /const sub2apiAccountPriorityNormalizer = typeof normalizeSub2ApiAccountPriorityValue === 'function'/);
|
||||||
|
assert.match(source, /sub2apiAccountPriority: sub2apiAccountPriorityNormalizer\(/);
|
||||||
|
assert.match(
|
||||||
|
source,
|
||||||
|
/inputSub2ApiAccountPriority\.value = String\(normalizeSub2ApiAccountPriorityValue\(state\?\.sub2apiAccountPriority\)\);/
|
||||||
|
);
|
||||||
|
assert.match(source, /rowSub2ApiAccountPriority\.style\.display = useSub2Api \? '' : 'none';/);
|
||||||
|
assert.match(source, /inputSub2ApiAccountPriority\.disabled = locked;/);
|
||||||
|
assert.match(
|
||||||
|
source,
|
||||||
|
/inputSub2ApiAccountPriority\.addEventListener\('input', \(\) => \{[\s\S]*scheduleSettingsAutoSave\(\);[\s\S]*\}\);/
|
||||||
|
);
|
||||||
|
assert.match(
|
||||||
|
source,
|
||||||
|
/inputSub2ApiAccountPriority\.addEventListener\('blur', \(\) => \{[\s\S]*saveSettings\(\{ silent: true \}\)/
|
||||||
|
);
|
||||||
|
});
|
||||||
@@ -277,3 +277,49 @@ test('SUB2API step 10 omits proxy_id when no proxy is configured', async () => {
|
|||||||
assert.equal(Object.hasOwn(exchangeCall.body, 'proxy_id'), false);
|
assert.equal(Object.hasOwn(exchangeCall.body, 'proxy_id'), false);
|
||||||
assert.equal(Object.hasOwn(createCall.body, 'proxy_id'), false);
|
assert.equal(Object.hasOwn(createCall.body, 'proxy_id'), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('SUB2API step 10 creates account with configured account priority', 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,
|
||||||
|
sub2apiAccountPriority: 3
|
||||||
|
})
|
||||||
|
`, context);
|
||||||
|
|
||||||
|
const createCall = fetchCalls.find((call) => call.path === '/api/v1/admin/accounts');
|
||||||
|
assert.equal(createCall.body.priority, 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('SUB2API account priority must be an integer greater than or equal to 1', async () => {
|
||||||
|
const fetchCalls = [];
|
||||||
|
const context = createSub2ApiPanelContext(fetchCalls);
|
||||||
|
|
||||||
|
await assert.rejects(
|
||||||
|
() => 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,
|
||||||
|
sub2apiAccountPriority: 0
|
||||||
|
})
|
||||||
|
`, context),
|
||||||
|
/SUB2API 账号优先级必须是大于等于 1 的整数/
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(fetchCalls.some((call) => call.path === '/api/v1/admin/accounts'), false);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user