fix: исправление 6 пунктов из ревью
Critical: - bridge: AutoGenPort=false по умолчанию, не генерируем secret если пустой → extension и bridge совпадают на port 9786 и empty secret - bridgeConfig: убрана авто-генерация secret, убран secret из BridgeInfo High: - extension/background.js + extension-firefox/background.js: все chrome.* listeners вынесены в global scope (не внутри onInstalled/onStartup) → MV3 service worker корректно перезапускается - UI: acceptBrowserEvent вызывает AcceptBrowserEvent, attachBrowserEvent вызывает AttachBrowserEventToNode (к текущему selectedNode), а не DismissBrowserEvent - watcher: при Create проверяется isUnderVault(absPath, vaultRoot) — если файл уже в vault, используется AddExternal вместо CopyIntoVault → нет дублирования файлов с timestamp-суффиксом Medium: - bridge.Event: добавлено поле DeviceID, handleEvents обогащает events из batch.DeviceID → device_id сохраняется в DB как chrome-*/firefox-*, а не evt_* - config: FileWatcher изменён на *bool — nil означает default true, false = явно выключено → старые config.json без поля file_watcher получают true
This commit is contained in:
+49
-58
@@ -1,12 +1,17 @@
|
||||
// Verstak Bridge — Background Service Worker
|
||||
// Verstak Bridge — Background Service Worker (Chrome)
|
||||
// Tracks active tab changes and queues browser events.
|
||||
// Pushes events to the Verstak HTTP bridge when available.
|
||||
//
|
||||
// IMPORTANT: All chrome.* event listeners are registered at global scope,
|
||||
// NOT inside onInstalled/onStartup. MV3 service workers can be restarted at
|
||||
// any time; listeners inside lifecycle hooks are lost on restart.
|
||||
// See: https://developer.chrome.com/docs/extensions/develop/concepts/service-workers/events
|
||||
|
||||
const STORAGE_KEY = 'verstak_queue';
|
||||
const BRIDGE_KEY = 'verstak_bridge_config';
|
||||
const DEFAULT_CONFIG = { port: 9786, secret: '', autoGenPort: true };
|
||||
const FLUSH_INTERVAL_MS = 30_000; // flush every 30s
|
||||
const IDLE_RESET_MS = 60_000; // reset session after 60s idle
|
||||
const RECENT_KEY = 'verstak_recent';
|
||||
const DEFAULT_CONFIG = { port: 9786, secret: '', autoGenPort: false };
|
||||
const IDLE_RESET_MS = 60_000;
|
||||
|
||||
// Session state
|
||||
let session = {
|
||||
@@ -17,43 +22,55 @@ let session = {
|
||||
startedAt: null,
|
||||
};
|
||||
|
||||
// Start tracking on install
|
||||
// --- Global event listeners (registered once at script load) ---
|
||||
|
||||
chrome.runtime.onInstalled.addListener(() => {
|
||||
setupAlarms();
|
||||
setupListeners();
|
||||
});
|
||||
|
||||
// Wake up on browser start
|
||||
chrome.runtime.onStartup.addListener(() => {
|
||||
setupAlarms();
|
||||
setupListeners();
|
||||
});
|
||||
|
||||
// Tab tracking — global scope
|
||||
chrome.tabs.onActivated.addListener(onTabActivated);
|
||||
chrome.tabs.onUpdated.addListener(onTabUpdated);
|
||||
chrome.windows.onFocusChanged.addListener(onWindowFocusChanged);
|
||||
|
||||
// Alarm handler — global scope
|
||||
chrome.alarms.onAlarm.addListener((alarm) => {
|
||||
if (alarm.name === 'flushEvents') flushSession();
|
||||
if (alarm.name === 'pingBridge') pingBridge();
|
||||
});
|
||||
|
||||
// Message handler — global scope
|
||||
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
||||
if (msg.type === 'FORCE_FLUSH') {
|
||||
flushSession();
|
||||
flushQueue();
|
||||
sendResponse({ ok: true });
|
||||
}
|
||||
if (msg.type === 'SET_TRACKING') {
|
||||
chrome.storage.local.set({ 'verstak_tracking_enabled': msg.enabled });
|
||||
sendResponse({ ok: true });
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// --- Alarm setup ---
|
||||
|
||||
function setupAlarms() {
|
||||
chrome.alarms.create('flushEvents', { periodInMinutes: 0.5 });
|
||||
chrome.alarms.create('pingBridge', { periodInMinutes: 1 });
|
||||
}
|
||||
|
||||
function setupListeners() {
|
||||
// Track active tab changes
|
||||
chrome.tabs.onActivated.addListener(onTabActivated);
|
||||
chrome.tabs.onUpdated.addListener(onTabUpdated);
|
||||
chrome.windows.onFocusChanged.addListener(onWindowFocusChanged);
|
||||
// --- Tab tracking ---
|
||||
|
||||
// Listen for flush alarm
|
||||
chrome.alarms.onAlarm.addListener((alarm) => {
|
||||
if (alarm.name === 'flushEvents') flushSession();
|
||||
if (alarm.name === 'pingBridge') pingBridge();
|
||||
});
|
||||
}
|
||||
|
||||
// Called when user switches tabs
|
||||
function onTabActivated(activeInfo) {
|
||||
flushSession();
|
||||
startTracking(activeInfo.tabId);
|
||||
}
|
||||
|
||||
// Called when a tab's URL or title changes
|
||||
function onTabUpdated(tabId, changeInfo, tab) {
|
||||
if (changeInfo.status === 'complete' && tab.active) {
|
||||
flushSession();
|
||||
@@ -61,14 +78,11 @@ function onTabUpdated(tabId, changeInfo, tab) {
|
||||
}
|
||||
}
|
||||
|
||||
// Called when window focus changes
|
||||
function onWindowFocusChanged(windowId) {
|
||||
if (windowId === chrome.windows.WINDOW_ID_NONE) {
|
||||
// Window lost focus — flush and stop session
|
||||
flushSession();
|
||||
session = { url: '', title: '', domain: '', tabId: -1, startedAt: null };
|
||||
} else {
|
||||
// Window gained focus — get active tab and start tracking
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||||
if (tabs && tabs.length > 0) {
|
||||
startTracking(tabs[0].id);
|
||||
@@ -86,19 +100,20 @@ function startTracking(tabId) {
|
||||
url: tab.url,
|
||||
title: tab.title || '',
|
||||
domain: extractDomain(tab.url),
|
||||
tabId: tabId,
|
||||
tabId,
|
||||
startedAt: Date.now(),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// --- Session flush ---
|
||||
|
||||
function flushSession() {
|
||||
if (!session.url || !session.startedAt) return;
|
||||
|
||||
const duration = Math.round((Date.now() - session.startedAt) / 1000);
|
||||
if (duration < 2) {
|
||||
// Skip very short visits
|
||||
session.startedAt = Date.now(); // reset timer
|
||||
session.startedAt = Date.now();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -114,7 +129,7 @@ function flushSession() {
|
||||
};
|
||||
|
||||
queueEvent(event);
|
||||
session.startedAt = Date.now(); // reset for next segment
|
||||
session.startedAt = Date.now();
|
||||
}
|
||||
|
||||
function queueEvent(event) {
|
||||
@@ -122,13 +137,11 @@ function queueEvent(event) {
|
||||
const queue = data[STORAGE_KEY] || [];
|
||||
queue.push(event);
|
||||
|
||||
// Keep max 500 events in queue
|
||||
if (queue.length > 500) {
|
||||
queue.splice(0, queue.length - 500);
|
||||
}
|
||||
|
||||
chrome.storage.local.set({ [STORAGE_KEY]: queue }, () => {
|
||||
// Immediately try to flush if we have enough events
|
||||
if (queue.length >= 5) {
|
||||
flushQueue();
|
||||
}
|
||||
@@ -136,6 +149,8 @@ function queueEvent(event) {
|
||||
});
|
||||
}
|
||||
|
||||
// --- Bridge communication ---
|
||||
|
||||
function flushQueue() {
|
||||
chrome.storage.local.get([STORAGE_KEY, BRIDGE_KEY], (data) => {
|
||||
const queue = data[STORAGE_KEY] || [];
|
||||
@@ -161,23 +176,17 @@ function flushQueue() {
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.ok) {
|
||||
// Update recent events
|
||||
for (const ev of queue) {
|
||||
updateRecent(ev);
|
||||
}
|
||||
// Clear queue on success
|
||||
chrome.storage.local.set({ [STORAGE_KEY]: [] });
|
||||
// Notify popup if open
|
||||
chrome.runtime.sendMessage({ type: 'UI_UPDATE' }).catch(() => {});
|
||||
} else if (res.status === 401) {
|
||||
console.warn('[verstak] bridge auth failed, check secret');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
.catch(() => {
|
||||
// Bridge not available — keep events in queue
|
||||
if (queue.length % 10 === 0) {
|
||||
console.debug('[verstak] bridge unavailable:', err.message);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -202,23 +211,8 @@ function pingBridge() {
|
||||
});
|
||||
}
|
||||
|
||||
// Listen for messages from popup
|
||||
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
||||
if (msg.type === 'FORCE_FLUSH') {
|
||||
flushSession();
|
||||
flushQueue();
|
||||
sendResponse({ ok: true });
|
||||
}
|
||||
if (msg.type === 'SET_TRACKING') {
|
||||
// Tracking is always on at the background level;
|
||||
// the popup just shows the setting.
|
||||
chrome.storage.local.set({ 'verstak_tracking_enabled': msg.enabled });
|
||||
sendResponse({ ok: true });
|
||||
}
|
||||
});
|
||||
// --- Recent events ---
|
||||
|
||||
// Update recent events from flushed queue entries.
|
||||
// This is called by flushQueue on successful delivery.
|
||||
function updateRecent(event) {
|
||||
chrome.storage.local.get(RECENT_KEY, (data) => {
|
||||
const recent = data[RECENT_KEY] || [];
|
||||
@@ -229,7 +223,6 @@ function updateRecent(event) {
|
||||
active_seconds: event.active_seconds,
|
||||
ts: new Date().toISOString(),
|
||||
});
|
||||
// Keep last 50
|
||||
if (recent.length > 50) {
|
||||
recent.splice(0, recent.length - 50);
|
||||
}
|
||||
@@ -237,9 +230,7 @@ function updateRecent(event) {
|
||||
});
|
||||
}
|
||||
|
||||
const RECENT_KEY = 'verstak_recent';
|
||||
|
||||
// ... existing code continues below ...
|
||||
// --- Helpers ---
|
||||
|
||||
function extractDomain(url) {
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user