Bläddra i källkod

feat: 增强步骤 9 的认证状态检测,确保仅在出现“认证成功!”状态徽标后判定成功,并自动关闭残留 localhost 标签页

QLHazyCoder 4 månader sedan
förälder
incheckning
cf3f0f1665
3 ändrade filer med 70 tillägg och 17 borttagningar
  1. 2 1
      README.md
  2. 37 0
      background.js
  3. 31 16
      content/vps-panel.js

+ 2 - 1
README.md

@@ -257,7 +257,8 @@ Step 3 使用的注册邮箱。
 
 
 - 自动填写 localhost 回调地址
 - 自动填写 localhost 回调地址
 - 自动点击“提交回调 URL”
 - 自动点击“提交回调 URL”
-- 等待“认证成功”状态
+- 必须等到 VPS 面板出现精确的 `认证成功!` 状态徽标后,才判定成功
+- 成功后会自动关闭匹配 `http://localhost:1455/auth` 这一类前缀的 localhost 残留页面
 
 
 ## Duck 邮箱自动获取
 ## Duck 邮箱自动获取
 
 

+ 37 - 0
background.js

@@ -222,6 +222,18 @@ function isSignupPageHost(hostname = '') {
   return ['auth0.openai.com', 'auth.openai.com', 'accounts.openai.com'].includes(hostname);
   return ['auth0.openai.com', 'auth.openai.com', 'accounts.openai.com'].includes(hostname);
 }
 }
 
 
+function buildLocalhostCleanupPrefix(rawUrl) {
+  const parsed = parseUrlSafely(rawUrl);
+  if (!parsed || parsed.hostname !== 'localhost') return '';
+
+  const segments = parsed.pathname.split('/').filter(Boolean);
+  if (!segments.length) {
+    return parsed.origin;
+  }
+
+  return `${parsed.origin}/${segments[0]}`;
+}
+
 function matchesSourceUrlFamily(source, candidateUrl, referenceUrl) {
 function matchesSourceUrlFamily(source, candidateUrl, referenceUrl) {
   const candidate = parseUrlSafely(candidateUrl);
   const candidate = parseUrlSafely(candidateUrl);
   if (!candidate) return false;
   if (!candidate) return false;
@@ -284,6 +296,24 @@ async function closeConflictingTabsForSource(source, currentUrl, options = {}) {
   await addLog(`已关闭 ${matchedIds.length} 个旧的${getSourceLabel(source)}标签页。`, 'info');
   await addLog(`已关闭 ${matchedIds.length} 个旧的${getSourceLabel(source)}标签页。`, 'info');
 }
 }
 
 
+async function closeTabsByUrlPrefix(prefix, options = {}) {
+  if (!prefix) return 0;
+
+  const { excludeTabIds = [] } = options;
+  const excluded = new Set(excludeTabIds.filter(id => Number.isInteger(id)));
+  const tabs = await chrome.tabs.query({});
+  const matchedIds = tabs
+    .filter((tab) => Number.isInteger(tab.id) && !excluded.has(tab.id))
+    .filter((tab) => typeof tab.url === 'string' && tab.url.startsWith(prefix))
+    .map((tab) => tab.id);
+
+  if (!matchedIds.length) return 0;
+
+  await chrome.tabs.remove(matchedIds).catch(() => {});
+  await addLog(`已关闭 ${matchedIds.length} 个匹配 ${prefix} 的 localhost 残留标签页。`, 'info');
+  return matchedIds.length;
+}
+
 // ============================================================
 // ============================================================
 // Command Queue (for content scripts not yet ready)
 // Command Queue (for content scripts not yet ready)
 // ============================================================
 // ============================================================
@@ -897,6 +927,13 @@ async function handleStepData(step, payload) {
         broadcastDataUpdate({ localhostUrl: payload.localhostUrl });
         broadcastDataUpdate({ localhostUrl: payload.localhostUrl });
       }
       }
       break;
       break;
+    case 9: {
+      const localhostPrefix = buildLocalhostCleanupPrefix(payload.localhostUrl);
+      if (localhostPrefix) {
+        await closeTabsByUrlPrefix(localhostPrefix);
+      }
+      break;
+    }
   }
   }
 }
 }
 
 

+ 31 - 16
content/vps-panel.js

@@ -76,6 +76,34 @@ function getActionText(el) {
     .trim();
     .trim();
 }
 }
 
 
+function getStatusBadgeElement() {
+  const candidates = document.querySelectorAll('.status-badge');
+  return Array.from(candidates).find(isVisibleElement) || null;
+}
+
+function getStatusBadgeText() {
+  const statusEl = getStatusBadgeElement();
+  return statusEl ? (statusEl.textContent || '').replace(/\s+/g, ' ').trim() : '';
+}
+
+async function waitForExactSuccessBadge(timeout = 30000) {
+  const start = Date.now();
+
+  while (Date.now() - start < timeout) {
+    throwIfStopped();
+    const statusText = getStatusBadgeText();
+    if (statusText === '认证成功!') {
+      return statusText;
+    }
+    await sleep(200);
+  }
+
+  const finalText = getStatusBadgeText();
+  throw new Error(finalText
+    ? `VPS 面板状态不是“认证成功!”,当前为“${finalText}”。`
+    : 'VPS 面板长时间未出现“认证成功!”状态徽标。');
+}
+
 function findManagementKeyInput() {
 function findManagementKeyInput() {
   const candidates = document.querySelectorAll(
   const candidates = document.querySelectorAll(
     '.LoginPage-module__loginCard___OgP-R input[type="password"], input[placeholder*="管理密钥"], input[aria-label*="管理密钥"]'
     '.LoginPage-module__loginCard___OgP-R input[type="password"], input[placeholder*="管理密钥"], input[aria-label*="管理密钥"]'
@@ -300,20 +328,7 @@ async function step9_vpsVerify(payload) {
   simulateClick(submitBtn);
   simulateClick(submitBtn);
   log('步骤 9:已点击“提交回调 URL”,正在等待认证结果...');
   log('步骤 9:已点击“提交回调 URL”,正在等待认证结果...');
 
 
-  // Wait for "认证成功!" status badge to appear
-  try {
-    await waitForElementByText('.status-badge, [class*="status"]', /认证成功/, 30000);
-    log('步骤 9:认证成功!', 'ok');
-  } catch {
-    // Check if there's an error message instead
-    const statusEl = document.querySelector('.status-badge, [class*="status"]');
-    const statusText = statusEl ? statusEl.textContent : 'unknown';
-    if (/成功|success/i.test(statusText)) {
-      log('步骤 9:认证成功!', 'ok');
-    } else {
-      log(`步骤 9:提交后的状态为“${statusText}”,可能仍在处理中。`, 'warn');
-    }
-  }
-
-  reportComplete(9);
+  const verifiedStatus = await waitForExactSuccessBadge();
+  log(`步骤 9:${verifiedStatus}`, 'ok');
+  reportComplete(9, { localhostUrl, verifiedStatus });
 }
 }