navigation-utils.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. (function attachBackgroundNavigationUtils(root, factory) {
  2. root.MultiPageBackgroundNavigationUtils = factory();
  3. })(typeof self !== 'undefined' ? self : globalThis, function createBackgroundNavigationUtilsModule() {
  4. function createNavigationUtils(deps = {}) {
  5. const {
  6. DEFAULT_SUB2API_URL,
  7. normalizeCpaCallbackMode,
  8. normalizeLocalCpaStep9Mode,
  9. } = deps;
  10. function parseUrlSafely(rawUrl) {
  11. if (!rawUrl) return null;
  12. try {
  13. return new URL(rawUrl);
  14. } catch {
  15. return null;
  16. }
  17. }
  18. function normalizeSub2ApiUrl(rawUrl) {
  19. const input = (rawUrl || '').trim() || DEFAULT_SUB2API_URL;
  20. const withProtocol = /^https?:\/\//i.test(input) ? input : `https://${input}`;
  21. const parsed = new URL(withProtocol);
  22. if (!parsed.pathname || parsed.pathname === '/') {
  23. parsed.pathname = '/admin/accounts';
  24. }
  25. parsed.hash = '';
  26. return parsed.toString();
  27. }
  28. function getPanelMode(state = {}) {
  29. return state.panelMode === 'sub2api' ? 'sub2api' : 'cpa';
  30. }
  31. function getPanelModeLabel(modeOrState) {
  32. const mode = typeof modeOrState === 'string' ? modeOrState : getPanelMode(modeOrState);
  33. return mode === 'sub2api' ? 'SUB2API' : 'CPA';
  34. }
  35. function isSignupPageHost(hostname = '') {
  36. return ['auth0.openai.com', 'auth.openai.com', 'accounts.openai.com'].includes(hostname);
  37. }
  38. function isSignupEntryHost(hostname = '') {
  39. return ['chatgpt.com', 'chat.openai.com'].includes(hostname);
  40. }
  41. function isSignupPasswordPageUrl(rawUrl) {
  42. const parsed = parseUrlSafely(rawUrl);
  43. if (!parsed) return false;
  44. return isSignupPageHost(parsed.hostname)
  45. && /\/create-account\/password(?:[/?#]|$)/i.test(parsed.pathname || '');
  46. }
  47. function is163MailHost(hostname = '') {
  48. return hostname === 'mail.163.com'
  49. || hostname.endsWith('.mail.163.com')
  50. || hostname === 'webmail.vip.163.com';
  51. }
  52. function isLocalhostOAuthCallbackUrl(rawUrl) {
  53. const parsed = parseUrlSafely(rawUrl);
  54. if (!parsed) return false;
  55. if (!['http:', 'https:'].includes(parsed.protocol)) return false;
  56. if (!['localhost', '127.0.0.1'].includes(parsed.hostname)) return false;
  57. if (!['/auth/callback', '/codex/callback'].includes(parsed.pathname)) return false;
  58. const code = (parsed.searchParams.get('code') || '').trim();
  59. const state = (parsed.searchParams.get('state') || '').trim();
  60. return Boolean(code && state);
  61. }
  62. function isLocalCpaUrl(rawUrl) {
  63. const parsed = parseUrlSafely(rawUrl);
  64. if (!parsed) return false;
  65. if (!['http:', 'https:'].includes(parsed.protocol)) return false;
  66. return ['localhost', '127.0.0.1'].includes(parsed.hostname);
  67. }
  68. function shouldBypassStep9ForLocalCpa(state) {
  69. return normalizeLocalCpaStep9Mode(state?.localCpaStep9Mode) === 'bypass'
  70. && Boolean(state?.localhostUrl)
  71. && isLocalCpaUrl(state?.vpsUrl);
  72. }
  73. function shouldSkipLoginVerificationForCpaCallback(state) {
  74. return getPanelMode(state) === 'cpa'
  75. && normalizeCpaCallbackMode(state?.cpaCallbackMode) === 'step6';
  76. }
  77. function matchesSourceUrlFamily(source, candidateUrl, referenceUrl) {
  78. const candidate = parseUrlSafely(candidateUrl);
  79. if (!candidate) return false;
  80. const reference = parseUrlSafely(referenceUrl);
  81. switch (source) {
  82. case 'signup-page':
  83. return isSignupPageHost(candidate.hostname) || isSignupEntryHost(candidate.hostname);
  84. case 'duck-mail':
  85. return candidate.hostname === 'duckduckgo.com' && candidate.pathname.startsWith('/email/');
  86. case 'qq-mail':
  87. return candidate.hostname === 'mail.qq.com' || candidate.hostname === 'wx.mail.qq.com';
  88. case 'mail-163':
  89. return is163MailHost(candidate.hostname);
  90. case 'gmail-mail':
  91. return candidate.hostname === 'mail.google.com';
  92. case 'inbucket-mail':
  93. return Boolean(reference)
  94. && candidate.origin === reference.origin
  95. && candidate.pathname.startsWith('/m/');
  96. case 'mail-2925':
  97. return candidate.hostname === '2925.com' || candidate.hostname === 'www.2925.com';
  98. case 'vps-panel':
  99. return Boolean(reference)
  100. && candidate.origin === reference.origin
  101. && candidate.pathname === reference.pathname;
  102. case 'sub2api-panel':
  103. return Boolean(reference)
  104. && candidate.origin === reference.origin
  105. && (
  106. candidate.pathname.startsWith('/admin/accounts')
  107. || candidate.pathname.startsWith('/login')
  108. || candidate.pathname === '/'
  109. );
  110. default:
  111. return false;
  112. }
  113. }
  114. function getStep8CallbackUrlFromNavigation(details, signupTabId) {
  115. if (!Number.isInteger(signupTabId) || !details) return '';
  116. if (details.tabId !== signupTabId) return '';
  117. if (details.frameId !== 0) return '';
  118. return isLocalhostOAuthCallbackUrl(details.url) ? details.url : '';
  119. }
  120. function getStep8CallbackUrlFromTabUpdate(tabId, changeInfo, tab, signupTabId) {
  121. if (!Number.isInteger(signupTabId) || tabId !== signupTabId) return '';
  122. const candidates = [changeInfo?.url, tab?.url];
  123. for (const candidate of candidates) {
  124. if (isLocalhostOAuthCallbackUrl(candidate)) {
  125. return candidate;
  126. }
  127. }
  128. return '';
  129. }
  130. return {
  131. getPanelMode,
  132. getPanelModeLabel,
  133. getStep8CallbackUrlFromNavigation,
  134. getStep8CallbackUrlFromTabUpdate,
  135. is163MailHost,
  136. isLocalCpaUrl,
  137. isLocalhostOAuthCallbackUrl,
  138. isSignupEntryHost,
  139. isSignupPageHost,
  140. isSignupPasswordPageUrl,
  141. matchesSourceUrlFamily,
  142. normalizeSub2ApiUrl,
  143. parseUrlSafely,
  144. shouldBypassStep9ForLocalCpa,
  145. shouldSkipLoginVerificationForCpaCallback,
  146. };
  147. }
  148. return {
  149. createNavigationUtils,
  150. };
  151. });