navigation-utils.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  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 isSignupEmailVerificationPageUrl(rawUrl) {
  48. const parsed = parseUrlSafely(rawUrl);
  49. if (!parsed) return false;
  50. return isSignupPageHost(parsed.hostname)
  51. && /\/email-verification(?:[/?#]|$)/i.test(parsed.pathname || '');
  52. }
  53. function is163MailHost(hostname = '') {
  54. return hostname === 'mail.163.com'
  55. || hostname.endsWith('.mail.163.com')
  56. || hostname === 'webmail.vip.163.com';
  57. }
  58. function isLocalhostOAuthCallbackUrl(rawUrl) {
  59. const parsed = parseUrlSafely(rawUrl);
  60. if (!parsed) return false;
  61. if (!['http:', 'https:'].includes(parsed.protocol)) return false;
  62. if (!['localhost', '127.0.0.1'].includes(parsed.hostname)) return false;
  63. if (!['/auth/callback', '/codex/callback'].includes(parsed.pathname)) return false;
  64. const code = (parsed.searchParams.get('code') || '').trim();
  65. const state = (parsed.searchParams.get('state') || '').trim();
  66. return Boolean(code && state);
  67. }
  68. function isLocalCpaUrl(rawUrl) {
  69. const parsed = parseUrlSafely(rawUrl);
  70. if (!parsed) return false;
  71. if (!['http:', 'https:'].includes(parsed.protocol)) return false;
  72. return ['localhost', '127.0.0.1'].includes(parsed.hostname);
  73. }
  74. function shouldBypassStep9ForLocalCpa(state) {
  75. return normalizeLocalCpaStep9Mode(state?.localCpaStep9Mode) === 'bypass'
  76. && Boolean(state?.localhostUrl)
  77. && isLocalCpaUrl(state?.vpsUrl);
  78. }
  79. function shouldSkipLoginVerificationForCpaCallback(state) {
  80. return getPanelMode(state) === 'cpa'
  81. && normalizeCpaCallbackMode(state?.cpaCallbackMode) === 'step6';
  82. }
  83. function matchesSourceUrlFamily(source, candidateUrl, referenceUrl) {
  84. const candidate = parseUrlSafely(candidateUrl);
  85. if (!candidate) return false;
  86. const reference = parseUrlSafely(referenceUrl);
  87. switch (source) {
  88. case 'signup-page':
  89. return isSignupPageHost(candidate.hostname) || isSignupEntryHost(candidate.hostname);
  90. case 'duck-mail':
  91. return candidate.hostname === 'duckduckgo.com' && candidate.pathname.startsWith('/email/');
  92. case 'qq-mail':
  93. return candidate.hostname === 'mail.qq.com' || candidate.hostname === 'wx.mail.qq.com';
  94. case 'mail-163':
  95. return is163MailHost(candidate.hostname);
  96. case 'gmail-mail':
  97. return candidate.hostname === 'mail.google.com';
  98. case 'inbucket-mail':
  99. return Boolean(reference)
  100. && candidate.origin === reference.origin
  101. && candidate.pathname.startsWith('/m/');
  102. case 'mail-2925':
  103. return candidate.hostname === '2925.com' || candidate.hostname === 'www.2925.com';
  104. case 'vps-panel':
  105. return Boolean(reference)
  106. && candidate.origin === reference.origin
  107. && candidate.pathname === reference.pathname;
  108. case 'sub2api-panel':
  109. return Boolean(reference)
  110. && candidate.origin === reference.origin
  111. && (
  112. candidate.pathname.startsWith('/admin/accounts')
  113. || candidate.pathname.startsWith('/login')
  114. || candidate.pathname === '/'
  115. );
  116. default:
  117. return false;
  118. }
  119. }
  120. function getStep8CallbackUrlFromNavigation(details, signupTabId) {
  121. if (!Number.isInteger(signupTabId) || !details) return '';
  122. if (details.tabId !== signupTabId) return '';
  123. if (details.frameId !== 0) return '';
  124. return isLocalhostOAuthCallbackUrl(details.url) ? details.url : '';
  125. }
  126. function getStep8CallbackUrlFromTabUpdate(tabId, changeInfo, tab, signupTabId) {
  127. if (!Number.isInteger(signupTabId) || tabId !== signupTabId) return '';
  128. const candidates = [changeInfo?.url, tab?.url];
  129. for (const candidate of candidates) {
  130. if (isLocalhostOAuthCallbackUrl(candidate)) {
  131. return candidate;
  132. }
  133. }
  134. return '';
  135. }
  136. return {
  137. getPanelMode,
  138. getPanelModeLabel,
  139. getStep8CallbackUrlFromNavigation,
  140. getStep8CallbackUrlFromTabUpdate,
  141. is163MailHost,
  142. isLocalCpaUrl,
  143. isLocalhostOAuthCallbackUrl,
  144. isSignupEmailVerificationPageUrl,
  145. isSignupEntryHost,
  146. isSignupPageHost,
  147. isSignupPasswordPageUrl,
  148. matchesSourceUrlFamily,
  149. normalizeSub2ApiUrl,
  150. parseUrlSafely,
  151. shouldBypassStep9ForLocalCpa,
  152. shouldSkipLoginVerificationForCpaCallback,
  153. };
  154. }
  155. return {
  156. createNavigationUtils,
  157. };
  158. });