feat(contribution): 增强贡献模式逻辑,支持 SUB2API 和 CPA 源,优化状态处理和界面显示
This commit is contained in:
+50
-13
@@ -183,6 +183,10 @@ const DEFAULT_SUB2API_URL = 'https://sub2api.hisence.fun/admin/accounts';
|
||||
const DEFAULT_CODEX2API_URL = 'http://localhost:8080/admin/accounts';
|
||||
const DEFAULT_SUB2API_GROUP_NAME = 'codex';
|
||||
const DEFAULT_SUB2API_PROXY_NAME = '';
|
||||
const CONTRIBUTION_SOURCE_CPA = 'cpa';
|
||||
const CONTRIBUTION_SOURCE_SUB2API = 'sub2api';
|
||||
const CONTRIBUTION_SUB2API_DEFAULT_GROUP_NAME = 'codex号池';
|
||||
const CONTRIBUTION_SUB2API_PLUS_GROUP_NAME = 'openai-plus';
|
||||
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';
|
||||
@@ -222,6 +226,8 @@ const ACCOUNT_RUN_HISTORY_STORAGE_KEY = 'accountRunHistory';
|
||||
const CONTRIBUTION_RUNTIME_DEFAULTS = self.MultiPageBackgroundContributionOAuth?.RUNTIME_DEFAULTS || {
|
||||
contributionMode: false,
|
||||
contributionModeExpected: false,
|
||||
contributionSource: CONTRIBUTION_SOURCE_SUB2API,
|
||||
contributionTargetGroupName: CONTRIBUTION_SUB2API_DEFAULT_GROUP_NAME,
|
||||
contributionNickname: '',
|
||||
contributionQq: '',
|
||||
contributionSessionId: '',
|
||||
@@ -243,6 +249,40 @@ function isPlusModeState(state = {}) {
|
||||
return Boolean(state?.plusModeEnabled);
|
||||
}
|
||||
|
||||
function normalizeContributionModeSource(value = '') {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
return normalized === CONTRIBUTION_SOURCE_SUB2API
|
||||
? CONTRIBUTION_SOURCE_SUB2API
|
||||
: CONTRIBUTION_SOURCE_CPA;
|
||||
}
|
||||
|
||||
function resolveContributionModeRoutingState(state = {}) {
|
||||
const currentStatus = String(state?.contributionStatus || '').trim().toLowerCase();
|
||||
const currentSource = normalizeContributionModeSource(state?.contributionSource);
|
||||
const hasActiveSession = Boolean(
|
||||
String(state?.contributionSessionId || '').trim()
|
||||
&& currentStatus
|
||||
&& !['auto_approved', 'auto_rejected', 'expired', 'error'].includes(currentStatus)
|
||||
);
|
||||
|
||||
if (hasActiveSession) {
|
||||
return {
|
||||
source: currentSource,
|
||||
targetGroupName: currentSource === CONTRIBUTION_SOURCE_SUB2API
|
||||
? (String(state?.contributionTargetGroupName || '').trim() || CONTRIBUTION_SUB2API_DEFAULT_GROUP_NAME)
|
||||
: '',
|
||||
};
|
||||
}
|
||||
|
||||
const source = CONTRIBUTION_SOURCE_SUB2API;
|
||||
return {
|
||||
source,
|
||||
targetGroupName: isPlusModeState(state)
|
||||
? CONTRIBUTION_SUB2API_PLUS_GROUP_NAME
|
||||
: (String(state?.contributionTargetGroupName || '').trim() || CONTRIBUTION_SUB2API_DEFAULT_GROUP_NAME),
|
||||
};
|
||||
}
|
||||
|
||||
function getStepDefinitionsForState(state = {}) {
|
||||
return isPlusModeState(state) ? PLUS_STEP_DEFINITIONS : NORMAL_STEP_DEFINITIONS;
|
||||
}
|
||||
@@ -1343,11 +1383,18 @@ function buildContributionModeState(enabled, persistedSettings = {}, currentStat
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
const routing = resolveContributionModeRoutingState({
|
||||
...persistedSettings,
|
||||
...currentState,
|
||||
...currentContributionState,
|
||||
});
|
||||
return {
|
||||
...currentContributionState,
|
||||
contributionMode: true,
|
||||
contributionModeExpected: true,
|
||||
panelMode: 'cpa',
|
||||
contributionSource: routing.source,
|
||||
contributionTargetGroupName: routing.targetGroupName,
|
||||
panelMode: routing.source,
|
||||
customPassword: '',
|
||||
accountRunHistoryTextEnabled: false,
|
||||
};
|
||||
@@ -1370,14 +1417,7 @@ async function setContributionMode(enabled) {
|
||||
getState(),
|
||||
]);
|
||||
|
||||
if (normalizedEnabled) {
|
||||
await setPersistentSettings({ panelMode: 'cpa' });
|
||||
}
|
||||
|
||||
const updates = buildContributionModeState(normalizedEnabled, {
|
||||
...persistedSettings,
|
||||
...(normalizedEnabled ? { panelMode: 'cpa' } : {}),
|
||||
}, currentState);
|
||||
const updates = buildContributionModeState(normalizedEnabled, persistedSettings, currentState);
|
||||
|
||||
await setState(updates);
|
||||
const nextState = await getState();
|
||||
@@ -1550,10 +1590,7 @@ async function resetState() {
|
||||
getPersistedSettings(),
|
||||
getPersistedAliasState(),
|
||||
]);
|
||||
const contributionModeState = buildContributionModeState(Boolean(prev.contributionMode), {
|
||||
...persistedSettings,
|
||||
...(prev.contributionMode ? { panelMode: 'cpa' } : {}),
|
||||
}, prev);
|
||||
const contributionModeState = buildContributionModeState(Boolean(prev.contributionMode), persistedSettings, prev);
|
||||
await chrome.storage.session.clear();
|
||||
await chrome.storage.session.set({
|
||||
...DEFAULT_STATE,
|
||||
|
||||
@@ -6,10 +6,16 @@
|
||||
const FINAL_STATUSES = new Set(['auto_approved', 'auto_rejected', 'expired', 'error']);
|
||||
const CALLBACK_FINAL_STATUSES = new Set(['submitted']);
|
||||
const CALLBACK_WAITING_STATUSES = new Set(['idle', 'waiting', 'captured', 'failed', 'submitting']);
|
||||
const CONTRIBUTION_SOURCE_CPA = 'cpa';
|
||||
const CONTRIBUTION_SOURCE_SUB2API = 'sub2api';
|
||||
const CONTRIBUTION_SUB2API_DEFAULT_GROUP_NAME = 'codex号池';
|
||||
const CONTRIBUTION_SUB2API_PLUS_GROUP_NAME = 'openai-plus';
|
||||
|
||||
const RUNTIME_DEFAULTS = {
|
||||
contributionMode: false,
|
||||
contributionModeExpected: false,
|
||||
contributionSource: CONTRIBUTION_SOURCE_SUB2API,
|
||||
contributionTargetGroupName: CONTRIBUTION_SUB2API_DEFAULT_GROUP_NAME,
|
||||
contributionNickname: '',
|
||||
contributionQq: '',
|
||||
contributionSessionId: '',
|
||||
@@ -104,6 +110,40 @@
|
||||
return FINAL_STATUSES.has(normalizeContributionStatus(status));
|
||||
}
|
||||
|
||||
function normalizeContributionSource(value = '') {
|
||||
const normalized = normalizeString(value).toLowerCase();
|
||||
return normalized === CONTRIBUTION_SOURCE_SUB2API
|
||||
? CONTRIBUTION_SOURCE_SUB2API
|
||||
: CONTRIBUTION_SOURCE_CPA;
|
||||
}
|
||||
|
||||
function resolveContributionRouting(state = {}) {
|
||||
const currentStatus = normalizeContributionStatus(state.contributionStatus);
|
||||
const currentSource = normalizeContributionSource(state.contributionSource);
|
||||
const hasActiveSession = Boolean(
|
||||
normalizeString(state.contributionSessionId)
|
||||
&& currentStatus
|
||||
&& !FINAL_STATUSES.has(currentStatus)
|
||||
);
|
||||
|
||||
if (hasActiveSession) {
|
||||
return {
|
||||
source: currentSource,
|
||||
targetGroupName: currentSource === CONTRIBUTION_SOURCE_SUB2API
|
||||
? (normalizeString(state.contributionTargetGroupName) || CONTRIBUTION_SUB2API_DEFAULT_GROUP_NAME)
|
||||
: '',
|
||||
};
|
||||
}
|
||||
|
||||
const source = CONTRIBUTION_SOURCE_SUB2API;
|
||||
return {
|
||||
source,
|
||||
targetGroupName: Boolean(state.plusModeEnabled)
|
||||
? CONTRIBUTION_SUB2API_PLUS_GROUP_NAME
|
||||
: (normalizeString(state.contributionTargetGroupName) || CONTRIBUTION_SUB2API_DEFAULT_GROUP_NAME),
|
||||
};
|
||||
}
|
||||
|
||||
function getStatusLabel(status = '') {
|
||||
switch (normalizeContributionStatus(status)) {
|
||||
case 'started':
|
||||
@@ -111,9 +151,9 @@
|
||||
case 'waiting':
|
||||
return '等待提交回调';
|
||||
case 'processing':
|
||||
return '已提交回调,等待 CPA 确认';
|
||||
return '已提交回调,等待服务端确认';
|
||||
case 'auto_approved':
|
||||
return '贡献成功,CPA 已确认';
|
||||
return '贡献成功,服务端已确认';
|
||||
case 'auto_rejected':
|
||||
return '贡献未通过确认';
|
||||
case 'expired':
|
||||
@@ -531,6 +571,16 @@
|
||||
const callbackState = deriveCallbackState(mergedPayload, currentState);
|
||||
const updates = {
|
||||
contributionLastPollAt: Date.now(),
|
||||
contributionSource: normalizeContributionSource(
|
||||
mergedPayload.source
|
||||
|| mergedPayload.source_kind
|
||||
|| currentState.contributionSource
|
||||
),
|
||||
contributionTargetGroupName: normalizeString(
|
||||
mergedPayload.target_group_name
|
||||
|| mergedPayload.group_name
|
||||
|| currentState.contributionTargetGroupName
|
||||
),
|
||||
contributionStatus: normalizedStatus,
|
||||
contributionStatusMessage: buildStatusMessage(normalizedStatus, mergedPayload),
|
||||
contributionCallbackUrl: callbackState.callbackUrl,
|
||||
@@ -572,6 +622,7 @@
|
||||
async function startContributionFlow(options = {}) {
|
||||
const currentState = options.stateOverride || await getState();
|
||||
const shouldOpenAuthTab = options.openAuthTab !== false;
|
||||
const routing = resolveContributionRouting(currentState);
|
||||
if (!currentState.contributionMode) {
|
||||
throw new Error('请先进入贡献模式。');
|
||||
}
|
||||
@@ -595,7 +646,8 @@
|
||||
nickname: buildNickname(currentState, options.nickname),
|
||||
qq: buildContributionQq(currentState, options.qq),
|
||||
email: normalizeString(currentState.email),
|
||||
source: 'cpa',
|
||||
source: routing.source,
|
||||
target_group_name: routing.targetGroupName,
|
||||
channel: 'codex-extension',
|
||||
},
|
||||
});
|
||||
@@ -608,6 +660,12 @@
|
||||
}
|
||||
|
||||
await applyRuntimeUpdates({
|
||||
contributionSource: normalizeContributionSource(payload.source || routing.source),
|
||||
contributionTargetGroupName: normalizeString(
|
||||
payload.target_group_name
|
||||
|| payload.group_name
|
||||
|| routing.targetGroupName
|
||||
),
|
||||
contributionSessionId: sessionId,
|
||||
contributionAuthUrl: authUrl,
|
||||
contributionAuthState: authState,
|
||||
|
||||
@@ -644,6 +644,9 @@
|
||||
stateUpdates.currentStep = 0;
|
||||
}
|
||||
await setState(stateUpdates);
|
||||
if (Boolean(currentState?.contributionMode) && typeof setContributionMode === 'function') {
|
||||
await setContributionMode(true);
|
||||
}
|
||||
if (modeChanged) {
|
||||
await addLog(
|
||||
Boolean(updates.plusModeEnabled)
|
||||
|
||||
@@ -509,9 +509,6 @@ async function step9_submitOpenAiCallback(payload = {}) {
|
||||
|
||||
const sessionId = String(payload.sub2apiSessionId || backgroundState.sub2apiSessionId || '').trim();
|
||||
const expectedState = String(payload.sub2apiOAuthState || backgroundState.sub2apiOAuthState || '').trim();
|
||||
const accountName = flowEmail
|
||||
|| String(payload.sub2apiDraftName || backgroundState.sub2apiDraftName || '').trim()
|
||||
|| buildDraftAccountName(payload.sub2apiGroupName || backgroundState.sub2apiGroupName || SUB2API_DEFAULT_GROUP_NAME);
|
||||
|
||||
const { origin, token } = await loginSub2Api(payload);
|
||||
const proxyPreference = resolveSub2ApiProxyPreference(payload, backgroundState);
|
||||
@@ -552,10 +549,15 @@ async function step9_submitOpenAiCallback(payload = {}) {
|
||||
|
||||
const credentials = buildOpenAiCredentials(exchangeData);
|
||||
const extra = buildOpenAiExtra(exchangeData);
|
||||
const resolvedEmail = String(exchangeData?.email || credentials?.email || '').trim();
|
||||
const groupId = Number(group.id);
|
||||
if (!Number.isFinite(groupId) || groupId <= 0) {
|
||||
throw new Error('SUB2API 返回的目标分组 ID 无效。');
|
||||
}
|
||||
const accountName = resolvedEmail
|
||||
|| flowEmail
|
||||
|| String(payload.sub2apiDraftName || backgroundState.sub2apiDraftName || '').trim()
|
||||
|| buildDraftAccountName(payload.sub2apiGroupName || backgroundState.sub2apiGroupName || SUB2API_DEFAULT_GROUP_NAME);
|
||||
const createPayload = {
|
||||
name: accountName,
|
||||
notes: '',
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
(function attachSidepanelContributionMode(globalScope) {
|
||||
const ACTIVE_STATUSES = new Set(['started', 'waiting', 'processing']);
|
||||
const FINAL_STATUSES = new Set(['auto_approved', 'auto_rejected', 'expired', 'error']);
|
||||
const DEFAULT_COPY = '当前账号将用于支持项目维护。扩展会自动申请贡献登录地址并持续跟踪授权状态;如检测到回调地址,会自动提交,并继续等待 CPA 最终确认。';
|
||||
(function attachSidepanelContributionMode(globalScope) {
|
||||
const ACTIVE_STATUSES = new Set(['started', 'waiting', 'processing']);
|
||||
const FINAL_STATUSES = new Set(['auto_approved', 'auto_rejected', 'expired', 'error']);
|
||||
const DEFAULT_COPY = '当前账号将用于支持项目维护。扩展会自动申请贡献登录地址并持续跟踪授权状态;如检测到回调地址,会自动提交,并继续等待服务端确认。';
|
||||
const CONTRIBUTION_SOURCE_CPA = 'cpa';
|
||||
const CONTRIBUTION_SOURCE_SUB2API = 'sub2api';
|
||||
const CONTRIBUTION_SUB2API_DEFAULT_GROUP_NAME = 'codex号池';
|
||||
|
||||
function createContributionModeManager(context = {}) {
|
||||
const {
|
||||
@@ -66,6 +69,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeContributionSource(value = '') {
|
||||
const normalized = normalizeString(value).toLowerCase();
|
||||
return normalized === CONTRIBUTION_SOURCE_SUB2API
|
||||
? CONTRIBUTION_SOURCE_SUB2API
|
||||
: CONTRIBUTION_SOURCE_CPA;
|
||||
}
|
||||
|
||||
function getContributionSource(currentState = getLatestState()) {
|
||||
return normalizeContributionSource(currentState.contributionSource || currentState.panelMode);
|
||||
}
|
||||
|
||||
function getContributionSourceLabel(currentState = getLatestState()) {
|
||||
return getContributionSource(currentState) === CONTRIBUTION_SOURCE_SUB2API ? 'SUB2API' : 'CPA';
|
||||
}
|
||||
|
||||
function isContributionModeEnabled(currentState = getLatestState()) {
|
||||
return Boolean(currentState.contributionMode);
|
||||
}
|
||||
@@ -173,7 +191,15 @@
|
||||
}
|
||||
|
||||
function getSummaryText(currentState = getLatestState()) {
|
||||
return normalizeString(currentState.contributionStatusMessage) || DEFAULT_COPY;
|
||||
const statusMessage = normalizeString(currentState.contributionStatusMessage);
|
||||
if (statusMessage) {
|
||||
return statusMessage;
|
||||
}
|
||||
if (getContributionSource(currentState) === CONTRIBUTION_SOURCE_SUB2API) {
|
||||
const groupName = normalizeString(currentState.contributionTargetGroupName) || CONTRIBUTION_SUB2API_DEFAULT_GROUP_NAME;
|
||||
return `当前账号将用于支持项目维护。贡献会通过 SUB2API 完成,并固定写入 ${groupName} 分组;如检测到回调地址,扩展会自动提交并等待服务端确认。`;
|
||||
}
|
||||
return DEFAULT_COPY;
|
||||
}
|
||||
|
||||
function getContributionPortalPageUrl() {
|
||||
@@ -310,9 +336,10 @@
|
||||
const enabled = isContributionModeEnabled(currentState);
|
||||
const blocked = isModeSwitchBlocked();
|
||||
const activeElement = typeof document !== 'undefined' ? document.activeElement : null;
|
||||
const sourceLabel = getContributionSourceLabel(currentState);
|
||||
|
||||
if (enabled && dom.selectPanelMode) {
|
||||
dom.selectPanelMode.value = 'cpa';
|
||||
dom.selectPanelMode.value = getContributionSource(currentState);
|
||||
}
|
||||
|
||||
helpers.updatePanelModeUI?.();
|
||||
@@ -322,7 +349,13 @@
|
||||
dom.contributionModePanel.hidden = !enabled;
|
||||
}
|
||||
if (dom.contributionModeText) {
|
||||
dom.contributionModeText.textContent = DEFAULT_COPY;
|
||||
dom.contributionModeText.textContent = getSummaryText({
|
||||
contributionSource: currentState.contributionSource,
|
||||
contributionTargetGroupName: currentState.contributionTargetGroupName,
|
||||
});
|
||||
}
|
||||
if (dom.contributionModeBadge) {
|
||||
dom.contributionModeBadge.textContent = enabled ? sourceLabel : '';
|
||||
}
|
||||
if (dom.inputContributionNickname && activeElement !== dom.inputContributionNickname) {
|
||||
const nextNickname = normalizeString(currentState.contributionNickname);
|
||||
|
||||
@@ -115,11 +115,11 @@
|
||||
<option value="codex2api">Codex2API</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="contribution-mode-panel" class="contribution-mode-panel" hidden>
|
||||
<div class="contribution-mode-panel-header">
|
||||
<span class="section-label">贡献模式</span>
|
||||
<span class="contribution-mode-badge">CPA</span>
|
||||
</div>
|
||||
<div id="contribution-mode-panel" class="contribution-mode-panel" hidden>
|
||||
<div class="contribution-mode-panel-header">
|
||||
<span class="section-label">贡献模式</span>
|
||||
<span id="contribution-mode-badge" class="contribution-mode-badge">CPA</span>
|
||||
</div>
|
||||
<p id="contribution-mode-text" class="contribution-mode-text">
|
||||
当前账号将用于支持项目维护。扩展会自动申请贡献登录地址并持续跟踪授权状态;如检测到回调地址,会自动提交,无需手动复制。</p>
|
||||
<div class="data-row contribution-mode-field">
|
||||
|
||||
@@ -34,6 +34,7 @@ const updateReleaseList = document.getElementById('update-release-list');
|
||||
const btnOpenRelease = document.getElementById('btn-open-release');
|
||||
const settingsCard = document.getElementById('settings-card');
|
||||
const contributionModePanel = document.getElementById('contribution-mode-panel');
|
||||
const contributionModeBadge = document.getElementById('contribution-mode-badge');
|
||||
const contributionModeText = document.getElementById('contribution-mode-text');
|
||||
const inputContributionNickname = document.getElementById('input-contribution-nickname');
|
||||
const inputContributionQq = document.getElementById('input-contribution-qq');
|
||||
@@ -1733,7 +1734,9 @@ function collectSettingsPayload() {
|
||||
label: typeof DEFAULT_HERO_SMS_COUNTRY_LABEL !== 'undefined' ? DEFAULT_HERO_SMS_COUNTRY_LABEL : 'Thailand',
|
||||
};
|
||||
return {
|
||||
panelMode: selectPanelMode.value,
|
||||
...(contributionModeEnabled ? {} : {
|
||||
panelMode: selectPanelMode.value,
|
||||
}),
|
||||
vpsUrl: inputVpsUrl.value.trim(),
|
||||
vpsPassword: inputVpsPassword.value,
|
||||
localCpaStep9Mode: getSelectedLocalCpaStep9Mode(),
|
||||
@@ -4070,6 +4073,7 @@ const contributionModeManager = window.SidepanelContributionMode?.createContribu
|
||||
btnOpenAccountRecords,
|
||||
btnOpenContributionUpload,
|
||||
btnStartContribution,
|
||||
contributionModeBadge,
|
||||
contributionModePanel,
|
||||
contributionModeSummary,
|
||||
contributionModeText,
|
||||
|
||||
@@ -84,14 +84,16 @@ test('contribution oauth module exposes a factory', () => {
|
||||
assert.equal(Array.isArray(api?.RUNTIME_KEYS), true);
|
||||
});
|
||||
|
||||
test('buildContributionModeState preserves active contribution runtime while forcing CPA mode', () => {
|
||||
test('buildContributionModeState preserves active contribution runtime while keeping contribution on sub2api', () => {
|
||||
const bundle = extractFunction(backgroundSource, 'buildContributionModeState');
|
||||
|
||||
const api = new Function(`
|
||||
const api = new Function(`
|
||||
const DEFAULT_STATE = { panelMode: 'cpa' };
|
||||
const CONTRIBUTION_RUNTIME_DEFAULTS = {
|
||||
contributionMode: false,
|
||||
contributionModeExpected: false,
|
||||
contributionSource: 'sub2api',
|
||||
contributionTargetGroupName: 'codex号池',
|
||||
contributionNickname: '',
|
||||
contributionQq: '',
|
||||
contributionSessionId: '',
|
||||
@@ -107,6 +109,35 @@ const CONTRIBUTION_RUNTIME_DEFAULTS = {
|
||||
contributionAuthTabId: 0,
|
||||
};
|
||||
const CONTRIBUTION_RUNTIME_KEYS = Object.keys(CONTRIBUTION_RUNTIME_DEFAULTS);
|
||||
function isPlusModeState(state = {}) { return Boolean(state?.plusModeEnabled); }
|
||||
function normalizeContributionModeSource(value = '') {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
return normalized === 'sub2api' ? 'sub2api' : 'cpa';
|
||||
}
|
||||
function resolveContributionModeRoutingState(state = {}) {
|
||||
const currentStatus = String(state?.contributionStatus || '').trim().toLowerCase();
|
||||
const currentSource = normalizeContributionModeSource(state?.contributionSource);
|
||||
const hasActiveSession = Boolean(
|
||||
String(state?.contributionSessionId || '').trim()
|
||||
&& currentStatus
|
||||
&& !['auto_approved', 'auto_rejected', 'expired', 'error'].includes(currentStatus)
|
||||
);
|
||||
if (hasActiveSession) {
|
||||
return {
|
||||
source: currentSource,
|
||||
targetGroupName: currentSource === 'sub2api'
|
||||
? (String(state?.contributionTargetGroupName || '').trim() || 'codex号池')
|
||||
: '',
|
||||
};
|
||||
}
|
||||
const source = 'sub2api';
|
||||
return {
|
||||
source,
|
||||
targetGroupName: isPlusModeState(state)
|
||||
? 'openai-plus'
|
||||
: (String(state?.contributionTargetGroupName || '').trim() || 'codex号池'),
|
||||
};
|
||||
}
|
||||
${bundle}
|
||||
return { buildContributionModeState };
|
||||
`)();
|
||||
@@ -125,6 +156,8 @@ return { buildContributionModeState };
|
||||
{
|
||||
contributionMode: true,
|
||||
contributionModeExpected: true,
|
||||
contributionSource: 'sub2api',
|
||||
contributionTargetGroupName: 'codex号池',
|
||||
contributionNickname: '',
|
||||
contributionQq: '',
|
||||
contributionSessionId: 'session-001',
|
||||
@@ -138,7 +171,7 @@ return { buildContributionModeState };
|
||||
contributionCallbackMessage: '',
|
||||
contributionAuthOpenedAt: 0,
|
||||
contributionAuthTabId: 0,
|
||||
panelMode: 'cpa',
|
||||
panelMode: 'sub2api',
|
||||
customPassword: '',
|
||||
accountRunHistoryTextEnabled: false,
|
||||
}
|
||||
@@ -157,6 +190,8 @@ return { buildContributionModeState };
|
||||
{
|
||||
contributionMode: false,
|
||||
contributionModeExpected: false,
|
||||
contributionSource: 'sub2api',
|
||||
contributionTargetGroupName: 'codex号池',
|
||||
contributionNickname: '',
|
||||
contributionQq: '',
|
||||
contributionSessionId: '',
|
||||
@@ -175,6 +210,37 @@ return { buildContributionModeState };
|
||||
accountRunHistoryTextEnabled: true,
|
||||
}
|
||||
);
|
||||
|
||||
assert.deepStrictEqual(
|
||||
api.buildContributionModeState(true, {
|
||||
panelMode: 'cpa',
|
||||
plusModeEnabled: true,
|
||||
customPassword: 'Secret123!',
|
||||
accountRunHistoryTextEnabled: true,
|
||||
}, {}),
|
||||
{
|
||||
contributionMode: true,
|
||||
contributionModeExpected: true,
|
||||
contributionSource: 'sub2api',
|
||||
contributionTargetGroupName: 'openai-plus',
|
||||
contributionNickname: '',
|
||||
contributionQq: '',
|
||||
contributionSessionId: '',
|
||||
contributionAuthUrl: '',
|
||||
contributionAuthState: '',
|
||||
contributionCallbackUrl: '',
|
||||
contributionStatus: '',
|
||||
contributionStatusMessage: '',
|
||||
contributionLastPollAt: 0,
|
||||
contributionCallbackStatus: 'idle',
|
||||
contributionCallbackMessage: '',
|
||||
contributionAuthOpenedAt: 0,
|
||||
contributionAuthTabId: 0,
|
||||
panelMode: 'sub2api',
|
||||
customPassword: '',
|
||||
accountRunHistoryTextEnabled: false,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('resetState preserves contribution runtime across reset', () => {
|
||||
@@ -334,6 +400,8 @@ test('contribution oauth manager starts session, opens auth url, submits callbac
|
||||
let statusPollCount = 0;
|
||||
let currentState = {
|
||||
contributionMode: true,
|
||||
contributionSource: 'sub2api',
|
||||
contributionTargetGroupName: 'codex号池',
|
||||
email: 'user@example.com',
|
||||
contributionSessionId: '',
|
||||
contributionStatus: '',
|
||||
@@ -424,6 +492,8 @@ test('contribution oauth manager starts session, opens auth url, submits callbac
|
||||
assert.match(String(fetchCalls[0].options.body || ''), /"nickname":""/);
|
||||
assert.match(String(fetchCalls[0].options.body || ''), /"qq":""/);
|
||||
assert.match(String(fetchCalls[0].options.body || ''), /"email":"user@example\.com"/);
|
||||
assert.match(String(fetchCalls[0].options.body || ''), /"source":"sub2api"/);
|
||||
assert.match(String(fetchCalls[0].options.body || ''), /"target_group_name":"codex号池"/);
|
||||
assert.match(fetchCalls[1].url, /\/status\?/);
|
||||
|
||||
const callbackState = await manager.handleCapturedCallback(
|
||||
@@ -471,6 +541,78 @@ test('contribution oauth manager accepts localhost callback urls that contain er
|
||||
);
|
||||
});
|
||||
|
||||
test('contribution oauth manager switches Plus contribution traffic to sub2api openai-plus', async () => {
|
||||
const source = fs.readFileSync('background/contribution-oauth.js', 'utf8');
|
||||
const globalScope = {};
|
||||
const fetchCalls = [];
|
||||
let currentState = {
|
||||
contributionMode: true,
|
||||
plusModeEnabled: true,
|
||||
contributionSource: 'sub2api',
|
||||
contributionTargetGroupName: 'openai-plus',
|
||||
contributionSessionId: '',
|
||||
contributionStatus: '',
|
||||
contributionCallbackStatus: 'idle',
|
||||
};
|
||||
|
||||
const api = new Function('self', 'fetch', `${source}; return self.MultiPageBackgroundContributionOAuth;`)(
|
||||
globalScope,
|
||||
async (url, options = {}) => {
|
||||
fetchCalls.push({ url, options });
|
||||
if (String(url).endsWith('/start')) {
|
||||
return createMockResponse(true, 200, {
|
||||
ok: true,
|
||||
session_id: 'session-plus-001',
|
||||
state: 'oauth-state-plus-001',
|
||||
source: 'sub2api',
|
||||
target_group_name: 'openai-plus',
|
||||
auth_url: 'https://auth.example.com/oauth?state=oauth-state-plus-001',
|
||||
});
|
||||
}
|
||||
if (String(url).includes('/status?')) {
|
||||
return createMockResponse(true, 200, {
|
||||
ok: true,
|
||||
session_id: 'session-plus-001',
|
||||
status: 'waiting',
|
||||
source: 'sub2api',
|
||||
target_group_name: 'openai-plus',
|
||||
});
|
||||
}
|
||||
return createMockResponse(true, 200, { ok: true });
|
||||
}
|
||||
);
|
||||
|
||||
const manager = api.createContributionOAuthManager({
|
||||
chrome: {
|
||||
tabs: {
|
||||
async create(payload) {
|
||||
return { id: 91, url: payload.url };
|
||||
},
|
||||
async update() {
|
||||
return null;
|
||||
},
|
||||
onUpdated: { addListener() {} },
|
||||
},
|
||||
webNavigation: {
|
||||
onCommitted: { addListener() {} },
|
||||
onHistoryStateUpdated: { addListener() {} },
|
||||
},
|
||||
},
|
||||
getState: async () => currentState,
|
||||
setState: async (updates) => {
|
||||
currentState = { ...currentState, ...updates };
|
||||
},
|
||||
broadcastDataUpdate: (updates) => {
|
||||
currentState = { ...currentState, ...updates };
|
||||
},
|
||||
});
|
||||
|
||||
await manager.startContributionFlow();
|
||||
|
||||
assert.match(String(fetchCalls[0].options.body || ''), /"source":"sub2api"/);
|
||||
assert.match(String(fetchCalls[0].options.body || ''), /"target_group_name":"openai-plus"/);
|
||||
});
|
||||
|
||||
test('refreshOAuthUrlBeforeStep6 uses contribution oauth session instead of panel bridge in contribution mode', async () => {
|
||||
const bundle = extractFunction(backgroundSource, 'refreshOAuthUrlBeforeStep6');
|
||||
const calls = [];
|
||||
|
||||
@@ -191,6 +191,7 @@ return {
|
||||
`)();
|
||||
|
||||
const contributionPayload = api.collectSettingsPayload();
|
||||
assert.equal('panelMode' in contributionPayload, false);
|
||||
assert.equal('customPassword' in contributionPayload, false);
|
||||
assert.equal('accountRunHistoryTextEnabled' in contributionPayload, false);
|
||||
assert.equal('accountRunHistoryHelperBaseUrl' in contributionPayload, false);
|
||||
@@ -199,6 +200,7 @@ return {
|
||||
|
||||
api.setLatestState({ contributionMode: false });
|
||||
const normalPayload = api.collectSettingsPayload();
|
||||
assert.equal(normalPayload.panelMode, 'cpa');
|
||||
assert.equal(normalPayload.customPassword, 'Secret123!');
|
||||
assert.equal(normalPayload.accountRunHistoryTextEnabled, true);
|
||||
assert.equal(normalPayload.accountRunHistoryHelperBaseUrl, 'http://127.0.0.1:17373');
|
||||
@@ -231,6 +233,8 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
|
||||
let latestState = {
|
||||
contributionMode: false,
|
||||
panelMode: 'sub2api',
|
||||
contributionSource: 'sub2api',
|
||||
contributionTargetGroupName: 'codex号池',
|
||||
contributionSessionId: '',
|
||||
contributionStatus: '',
|
||||
contributionStatusMessage: '',
|
||||
@@ -258,6 +262,7 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
|
||||
btnOpenAccountRecords: createElement(),
|
||||
btnOpenContributionUpload: createElement(),
|
||||
btnStartContribution: createElement(),
|
||||
contributionModeBadge: createElement(),
|
||||
inputContributionNickname: createElement({ value: '贡献者昵称' }),
|
||||
inputContributionQq: createElement({ value: '123456' }),
|
||||
contributionCallbackStatus: createElement(),
|
||||
@@ -353,7 +358,9 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
|
||||
state: message.payload.enabled
|
||||
? {
|
||||
contributionMode: true,
|
||||
panelMode: 'cpa',
|
||||
panelMode: 'sub2api',
|
||||
contributionSource: 'sub2api',
|
||||
contributionTargetGroupName: 'codex号池',
|
||||
contributionNickname: '',
|
||||
contributionQq: '',
|
||||
contributionSessionId: '',
|
||||
@@ -368,6 +375,8 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
|
||||
: {
|
||||
contributionMode: false,
|
||||
panelMode: 'cpa',
|
||||
contributionSource: 'cpa',
|
||||
contributionTargetGroupName: '',
|
||||
contributionNickname: '',
|
||||
contributionQq: '',
|
||||
contributionSessionId: '',
|
||||
@@ -387,7 +396,7 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
|
||||
state: {
|
||||
...latestState,
|
||||
contributionStatus: 'processing',
|
||||
contributionStatusMessage: '已提交回调,等待 CPA 确认',
|
||||
contributionStatusMessage: '已提交回调,等待服务端确认',
|
||||
contributionCallbackStatus: 'submitted',
|
||||
contributionCallbackMessage: '已提交回调',
|
||||
},
|
||||
@@ -414,13 +423,15 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
|
||||
manager.render();
|
||||
assert.equal(dom.contributionModePanel.hidden, true);
|
||||
assert.equal(dom.btnContributionMode.disabled, false);
|
||||
assert.equal(dom.contributionModeBadge.textContent, '');
|
||||
|
||||
manager.bindEvents();
|
||||
await dom.btnContributionMode.listeners.click();
|
||||
|
||||
assert.equal(dom.contributionModePanel.hidden, false);
|
||||
assert.equal(dom.selectPanelMode.value, 'cpa');
|
||||
assert.equal(dom.selectPanelMode.value, 'sub2api');
|
||||
assert.equal(dom.selectPanelMode.disabled, true);
|
||||
assert.equal(dom.contributionModeBadge.textContent, 'SUB2API');
|
||||
assert.equal(dom.btnOpenAccountRecords.disabled, true);
|
||||
assert.equal(dom.contributionOauthStatus.textContent, '\u672a\u751f\u6210\u767b\u5f55\u5730\u5740');
|
||||
assert.equal(dom.contributionCallbackStatus.textContent, '\u7b49\u5f85\u56de\u8c03');
|
||||
@@ -453,7 +464,7 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
|
||||
assert.equal(statusState.contributionStatus, 'processing');
|
||||
assert.equal(dom.contributionOauthStatus.textContent, '\u5df2\u63d0\u4ea4\u56de\u8c03');
|
||||
assert.equal(dom.contributionCallbackStatus.textContent, '\u5df2\u63d0\u4ea4\u56de\u8c03');
|
||||
assert.equal(dom.contributionModeSummary.textContent, '\u5df2\u63d0\u4ea4\u56de\u8c03\uff0c\u7b49\u5f85 CPA \u786e\u8ba4');
|
||||
assert.equal(dom.contributionModeSummary.textContent, '\u5df2\u63d0\u4ea4\u56de\u8c03\uff0c\u7b49\u5f85\u670d\u52a1\u7aef\u786e\u8ba4');
|
||||
|
||||
dom.btnOpenContributionUpload.listeners.click();
|
||||
assert.deepStrictEqual(openedUrls, ['https://apikey.qzz.io', 'https://apikey.qzz.io/upload']);
|
||||
@@ -476,7 +487,9 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
|
||||
blocked = true;
|
||||
latestState = {
|
||||
contributionMode: true,
|
||||
panelMode: 'cpa',
|
||||
panelMode: 'sub2api',
|
||||
contributionSource: 'sub2api',
|
||||
contributionTargetGroupName: 'codex号池',
|
||||
contributionNickname: '贡献者昵称',
|
||||
contributionQq: '123456',
|
||||
contributionSessionId: 'session-002',
|
||||
@@ -487,6 +500,8 @@ test('contribution mode manager enters mode, starts main auto flow, polls contri
|
||||
contributionCallbackMessage: '\u7b49\u5f85\u56de\u8c03',
|
||||
};
|
||||
manager.render();
|
||||
assert.equal(dom.selectPanelMode.value, 'sub2api');
|
||||
assert.equal(dom.contributionModeBadge.textContent, 'SUB2API');
|
||||
assert.equal(dom.btnExitContributionMode.disabled, true);
|
||||
manager.stopPolling();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user