diff --git a/extension/chrome/background.js b/extension/chrome/background.js index bcee467..c7422bb 100644 --- a/extension/chrome/background.js +++ b/extension/chrome/background.js @@ -71,13 +71,13 @@ function hostFrom(url) { } // ── 拉取最近浏览历史(增量)── -async function collectRecent() { - const since = Math.max(0, Date.now() - FETCH_WINDOW); +async function collectRecent(previousSyncTime = 0) { + const initialCutoff = Date.now() - DEDUP_SECONDS * 1000; + const cutoff = previousSyncTime > 0 ? previousSyncTime : initialCutoff; + const since = Math.max(0, Date.now() - FETCH_WINDOW, cutoff); const items = await new Promise((resolve) => { chrome.history.search({ text: '', startTime: since, maxResults: 10000 }, resolve); }); - - const cutoff = Date.now() - DEDUP_SECONDS * 1000; const seen = new Set(); const collected = []; @@ -88,7 +88,7 @@ async function collectRecent() { if (seen.has(key)) continue; seen.add(key); const ts = item.lastVisitTime || Date.now(); - if (ts < cutoff) continue; // 只收集最近窗口 + if (ts <= cutoff) continue; // 只上报上次成功同步之后的新访问 collected.push({ url: item.url, title: item.title || '', @@ -137,16 +137,27 @@ chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => { async function syncNow() { ensureAlarm(); - const { serverUrl, syncToken, deviceName } = await new Promise((resolve) => { - chrome.storage.local.get(['serverUrl', 'syncToken', 'deviceName'], resolve); + const syncStartedAt = Date.now(); + const stored = await new Promise((resolve) => { + chrome.storage.local.get( + ['serverUrl', 'syncToken', 'deviceName', 'pending', 'lastSyncTime'], + resolve, + ); }); + const { serverUrl, syncToken, deviceName } = stored; if (!serverUrl) return { ok: false, reason: 'not configured' }; - // 增量拉取最近历史,合并到待上报队列 + // 每次从持久化存储恢复游标和失败队列,兼容 MV3 service worker 休眠重启。 + pendingItems = Array.isArray(stored.pending) ? stored.pending : []; + lastSyncTime = Number(stored.lastSyncTime) || 0; let recent; - try { recent = await collectRecent(); } catch (e) { recent = []; } + try { recent = await collectRecent(lastSyncTime); } catch (e) { recent = []; } const merged = dedup(recent.concat(pendingItems)); - if (merged.length === 0) return { ok: true, skipped: true, reason: 'nothing new' }; + if (merged.length === 0) { + lastSyncTime = syncStartedAt; + await new Promise((res) => chrome.storage.local.set({ lastSyncTime }, res)); + return { ok: true, skipped: true, reason: 'nothing new' }; + } const body = JSON.stringify({ items: merged.map((it) => ({ @@ -167,11 +178,13 @@ async function syncNow() { }); if (!resp.ok) { const text = await resp.text().catch(() => ''); + pendingItems = merged; + await new Promise((res) => chrome.storage.local.set({ pending: merged }, res)); return { ok: false, status: resp.status, error: text.slice(0, 200) }; } const data = await resp.json().catch(() => ({})); pendingItems = []; - lastSyncTime = Date.now(); + lastSyncTime = syncStartedAt; await new Promise((res) => chrome.storage.local.set({ pending: [], lastSyncTime }, res)); return { ok: true, count: merged.length, total: data.total || 0 }; } catch (e) { diff --git a/extension/chrome/options.html b/extension/chrome/options.html index 49355ae..de3d829 100644 --- a/extension/chrome/options.html +++ b/extension/chrome/options.html @@ -24,28 +24,10 @@ button{margin-top:14px;padding:8px 20px;border:none;border-radius:8px;font-weigh