navigation-utils.js 5.9 KB

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