navigation-utils.js 6.3 KB

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