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:
2026-06-07 00:15:34 +08:00
parent b676ac675a
commit 1cc0c407b1
9 changed files with 142 additions and 137 deletions
+33 -29
View File
@@ -1,6 +1,9 @@
// Verstak Bridge for Firefox — Background Service Worker
// Uses browser.* WebExtensions API with chrome.* polyfill layer.
// Functionally identical to the Chrome version.
//
// IMPORTANT: All 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.
// Firefox uses browser.* namespace; provide chrome.* aliases for shared code.
if (typeof browser !== 'undefined' && typeof chrome === 'undefined') {
@@ -10,7 +13,7 @@ if (typeof browser !== 'undefined' && typeof chrome === 'undefined') {
const STORAGE_KEY = 'verstak_queue';
const BRIDGE_KEY = 'verstak_bridge_config';
const RECENT_KEY = 'verstak_recent';
const DEFAULT_CONFIG = { port: 9786, secret: '', autoGenPort: true };
const DEFAULT_CONFIG = { port: 9786, secret: '', autoGenPort: false };
const DEFAULT_DEVICE_ID = generateDeviceId();
// Session state
@@ -22,11 +25,10 @@ let session = {
startedAt: null,
};
// --- Lifecycle ---
// --- Global event listeners (registered once at script load) ---
chrome.runtime.onInstalled.addListener(() => {
setupAlarms();
setupListeners();
// Generate a stable device ID on first install
chrome.storage.local.get(BRIDGE_KEY, (data) => {
const cfg = data[BRIDGE_KEY] || {};
@@ -38,38 +40,40 @@ chrome.runtime.onInstalled.addListener(() => {
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() {
chrome.tabs.onActivated.addListener(onTabActivated);
chrome.tabs.onUpdated.addListener(onTabUpdated);
chrome.windows.onFocusChanged.addListener(onWindowFocusChanged);
chrome.alarms.onAlarm.addListener((alarm) => {
if (alarm.name === 'flushEvents') flushSession();
if (alarm.name === 'pingBridge') pingBridge();
});
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; // keep channel open for async response
});
}
// --- Tab tracking ---
function onTabActivated(activeInfo) {