| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- // background/steps/sync-cpa-session.js — Step 10: Sync current ChatGPT session to CPA
- (function attachBackgroundCpaSessionSync(root, factory) {
- root.MultiPageBackgroundCpaSessionSync = factory();
- })(typeof self !== 'undefined' ? self : globalThis, function createBackgroundCpaSessionSyncModule() {
- const CHATGPT_SESSION_URL = 'https://chatgpt.com/';
- const CHATGPT_SESSION_ENDPOINT = 'https://chatgpt.com/api/auth/session';
- function createCpaSessionSyncExecutor(deps = {}) {
- const {
- addLog = async () => {},
- chrome = null,
- completeStepFromBackground = async () => {},
- createCpaApi = null,
- fetchImpl = (...args) => fetch(...args),
- getPanelMode = (state) => (state?.panelMode === 'sub2api' ? 'sub2api' : 'cpa'),
- getTabId = async () => null,
- sleepWithStop = sleep,
- throwIfStopped = () => {},
- waitForTabComplete = async () => null,
- } = deps;
- let cpaApi = null;
- function getApi() {
- if (cpaApi) return cpaApi;
- const factory = createCpaApi || self.MultiPageBackgroundCpaApi?.createCpaApi;
- if (typeof factory !== 'function') {
- throw new Error('CPA 接口模块未加载,无法同步当前 ChatGPT 会话。');
- }
- cpaApi = factory({ addLog, fetchImpl });
- return cpaApi;
- }
- async function executeStep10(state = {}) {
- throwIfStopped();
- await addLog('步骤 10:正在准备同步 ChatGPT session 到 CPA...');
- const mode = getPanelMode(state);
- if (mode === 'sub2api') {
- await addLog('步骤 10:当前为 SUB2API 模式,跳过 CPA session 同步。', 'warn');
- await completeStepFromBackground(10, {
- cpaSyncSkipped: true,
- cpaSyncSkipReason: 'sub2api-mode',
- });
- return;
- }
- if (!normalizeString(state.vpsUrl)) {
- throw new Error('步骤 10:尚未配置 CPA 地址,请先在侧边栏填写。');
- }
- if (!normalizeString(state.vpsPassword)) {
- throw new Error('步骤 10:尚未配置 CPA 管理密钥,请先在侧边栏填写。');
- }
- await addLog('步骤 10:正在读取 ChatGPT 当前登录 session...');
- const sessionState = await readCurrentChatGptSession();
- throwIfStopped();
- await addLog('步骤 10:已读取 ChatGPT session,正在生成 CPA auth JSON 并同步...');
- const result = await getApi().importCurrentChatGptSession({
- ...state,
- session: sessionState.session,
- accessToken: sessionState.accessToken,
- }, {
- logLabel: '步骤 10',
- timeoutMs: 120000,
- importTimeoutMs: 120000,
- });
- await completeStepFromBackground(10, result);
- }
- async function readCurrentChatGptSession() {
- try {
- return await fetchChatGptSessionFromBackground();
- } catch (error) {
- await addLog(`步骤 10:后台读取 ChatGPT session 失败,准备打开页面上下文重试:${error.message}`, 'warn');
- }
- return fetchChatGptSessionFromPage();
- }
- async function fetchChatGptSessionFromBackground() {
- const response = await fetchImpl(CHATGPT_SESSION_ENDPOINT, {
- credentials: 'include',
- headers: {
- Accept: 'application/json',
- },
- });
- const text = await response.text();
- let session = null;
- try {
- session = text ? JSON.parse(text) : null;
- } catch {
- throw new Error(`Session 响应不是有效 JSON:${text.slice(0, 300)}`);
- }
- if (!response.ok) {
- throw new Error(`Session 请求失败,HTTP ${response.status}: ${JSON.stringify(session)}`);
- }
- return normalizeSessionResult({ session, accessToken: session?.accessToken }, '后台');
- }
- async function fetchChatGptSessionFromPage() {
- if (!chrome?.tabs || !chrome?.scripting?.executeScript) {
- throw new Error('当前环境无法在 ChatGPT 页面上下文读取 session。');
- }
- const tabId = await resolveChatGptSessionTabId();
- if (!tabId) {
- throw new Error('未找到可读取 ChatGPT session 的标签页。');
- }
- await waitForTabComplete(tabId, { timeoutMs: 30000, retryDelayMs: 300 });
- await sleepWithStop(1000);
- const results = await chrome.scripting.executeScript({
- target: { tabId },
- func: fetchSessionInChatGptPage,
- });
- const result = results?.[0]?.result;
- if (!result || result.error) {
- throw new Error(result?.error || '页面上下文读取 ChatGPT session 失败。');
- }
- return normalizeSessionResult(result, '页面');
- }
- async function resolveChatGptSessionTabId() {
- const registeredSignupTabId = await getTabId('signup-page').catch(() => null);
- const registeredSignupTab = await getChatGptTabIfReadable(registeredSignupTabId);
- if (registeredSignupTab?.id) return registeredSignupTab.id;
- const activeTabs = await chrome.tabs.query({ active: true, currentWindow: true }).catch(() => []);
- const activeMatch = pickPreferredChatGptTab(activeTabs);
- if (activeMatch?.id) return activeMatch.id;
- const chatGptTabs = await chrome.tabs.query({ url: ['https://chatgpt.com/*'] }).catch(() => []);
- const existingMatch = pickPreferredChatGptTab(chatGptTabs);
- if (existingMatch?.id) return existingMatch.id;
- await addLog('步骤 10:未找到 ChatGPT 页面,正在打开 chatgpt.com 以读取 session...', 'info');
- const created = await chrome.tabs.create({ url: CHATGPT_SESSION_URL, active: true });
- return created?.id || null;
- }
- async function getChatGptTabIfReadable(tabId) {
- const numericTabId = Number(tabId) || 0;
- if (!numericTabId || !chrome?.tabs?.get) return null;
- const tab = await chrome.tabs.get(numericTabId).catch(() => null);
- return tab?.id && isChatGptSessionUrl(tab.url) ? tab : null;
- }
- function pickPreferredChatGptTab(tabs = []) {
- return (Array.isArray(tabs) ? tabs : [])
- .filter((tab) => Number.isInteger(tab?.id) && isChatGptSessionUrl(tab.url))
- .sort((left, right) => {
- const activeDiff = Number(Boolean(right.active)) - Number(Boolean(left.active));
- if (activeDiff) return activeDiff;
- return (Number(right.lastAccessed) || 0) - (Number(left.lastAccessed) || 0);
- })[0] || null;
- }
- function isChatGptSessionUrl(url = '') {
- try {
- const parsed = new URL(String(url || ''));
- return /^https?:$/i.test(parsed.protocol)
- && parsed.hostname.toLowerCase() === 'chatgpt.com';
- } catch {
- return false;
- }
- }
- function normalizeSessionResult(result = {}, label = 'ChatGPT') {
- const session = result?.session && typeof result.session === 'object' && !Array.isArray(result.session)
- ? result.session
- : null;
- const accessToken = normalizeString(result?.accessToken || session?.accessToken);
- if (!session && !accessToken) {
- throw new Error(`${label} 未返回有效的 ChatGPT session 或 accessToken。`);
- }
- if (!accessToken) {
- throw new Error(`${label} Session 响应中没有 accessToken。`);
- }
- return { session, accessToken };
- }
- return {
- executeStep10,
- fetchChatGptSessionFromBackground,
- isChatGptSessionUrl,
- };
- }
- async function fetchSessionInChatGptPage() {
- try {
- const response = await fetch('/api/auth/session', {
- credentials: 'include',
- headers: {
- Accept: 'application/json',
- },
- });
- const text = await response.text();
- let session = null;
- try {
- session = text ? JSON.parse(text) : null;
- } catch {
- return { error: `Session 响应不是有效 JSON:${text.slice(0, 300)}` };
- }
- if (!response.ok) {
- return { error: `Session 请求失败,HTTP ${response.status}: ${JSON.stringify(session)}` };
- }
- return {
- session,
- accessToken: session?.accessToken || '',
- };
- } catch (error) {
- return { error: error?.message || String(error || '页面上下文读取 ChatGPT session 失败。') };
- }
- }
- function normalizeString(value = '') {
- return String(value || '').trim();
- }
- function sleep(ms) {
- return new Promise((resolve) => setTimeout(resolve, ms));
- }
- return {
- createCpaSessionSyncExecutor,
- };
- });
|