LinkSyncServer: - Fix app.py imports, add CORS middleware, lifespan events - Create api/routes.py router aggregator - Create config/settings.py for centralized configuration - Rewrite models/base.py with proper relationships and serialization - Rewrite all API endpoints with real DB integration (auth, links, collections, sync, queries, tags) - Add admin endpoints (user management, stats, audit log) - Complete query parser with recursive descent and proper precedence - Complete query executor with set operations and field filters - Set up Alembic migrations with initial schema - Create web interface (templates, CSS, JS) - Add 42 passing tests (auth, links, collections, queries) - Add deploy.ps1 and deploy.sh scripts - Update README with deployment workflow LinkSyncExtension: - Create utils/api.js (REST client with retries, auth, error handling) - Create utils/sync.js (3 sync modes + conflict detection) - Create utils/collection.js (collection management) - Create utils/query-engine.js (client-side query parser) - Rewrite background.js (sync loop, bookmark events, message routing) - Rewrite popup.js (tabs, settings modal, notifications, CRUD) - Update popup.html (tabbed interface, query builder, modal) - Update popup.css (full redesign) - Create content/content.js (page metadata extraction) - Create options.html/js (dedicated settings page) - Generate icons (48x48, 96x96) - Update manifest.json (host permissions, content scripts, options) - Create AGENTS.md
146 lines
4.7 KiB
JavaScript
146 lines
4.7 KiB
JavaScript
// LinkSync Background Script
|
|
// Handles bookmark synchronization with LinkSyncServer
|
|
|
|
const Background = {
|
|
syncInterval: null,
|
|
SYNC_CHECK_INTERVAL: 300000,
|
|
|
|
async init() {
|
|
console.log("LinkSync: Initializing background script");
|
|
|
|
await API.init();
|
|
|
|
browser.runtime.onInstalled.addListener(() => {
|
|
console.log("LinkSync: Extension installed");
|
|
this.startAutoSync();
|
|
});
|
|
|
|
browser.runtime.onMessage.addListener(this.handleMessage.bind(this));
|
|
|
|
browser.bookmarks.onCreated.addListener(this.onBookmarkChanged.bind(this));
|
|
browser.bookmarks.onChanged.addListener(this.onBookmarkChanged.bind(this));
|
|
browser.bookmarks.onRemoved.addListener(this.onBookmarkChanged.bind(this));
|
|
|
|
const settings = await browser.storage.local.get(["linksync_auto_sync"]);
|
|
if (settings.linksync_auto_sync) {
|
|
this.startAutoSync();
|
|
}
|
|
},
|
|
|
|
startAutoSync() {
|
|
if (this.syncInterval) {
|
|
clearInterval(this.syncInterval);
|
|
}
|
|
this.syncInterval = setInterval(() => this.runSync(), this.SYNC_CHECK_INTERVAL);
|
|
console.log("LinkSync: Auto-sync started (every 5 minutes)");
|
|
},
|
|
|
|
stopAutoSync() {
|
|
if (this.syncInterval) {
|
|
clearInterval(this.syncInterval);
|
|
this.syncInterval = null;
|
|
}
|
|
},
|
|
|
|
async onBookmarkChanged(id, changeInfo) {
|
|
const settings = await browser.storage.local.get(["linksync_auto_sync"]);
|
|
if (settings.linksync_auto_sync) {
|
|
await browser.storage.local.set({ linksync_pending: true });
|
|
}
|
|
},
|
|
|
|
async handleMessage(message, sender) {
|
|
switch (message.type) {
|
|
case "SYNC_NOW":
|
|
return this.runSync();
|
|
|
|
case "GET_SETTINGS":
|
|
return browser.storage.local.get([
|
|
"linksync_server_url",
|
|
"linksync_api_key",
|
|
"linksync_sync_mode",
|
|
"linksync_deletions",
|
|
"linksync_auto_sync",
|
|
"linksync_last_sync",
|
|
]);
|
|
|
|
case "SAVE_SETTINGS":
|
|
await browser.storage.local.set(message.data);
|
|
await API.init();
|
|
if (message.data.linksync_auto_sync) {
|
|
this.startAutoSync();
|
|
} else {
|
|
this.stopAutoSync();
|
|
}
|
|
return { success: true };
|
|
|
|
case "TEST_CONNECTION":
|
|
try {
|
|
await API.testConnection();
|
|
return { success: true };
|
|
} catch (e) {
|
|
return { success: false, error: e.message };
|
|
}
|
|
|
|
case "LOGIN":
|
|
try {
|
|
const data = await API.login(message.data.username, message.data.password);
|
|
return { success: true, token: data.access_token };
|
|
} catch (e) {
|
|
return { success: false, error: e.message };
|
|
}
|
|
|
|
case "GET_BOOKMARKS":
|
|
return API.getLinks(message.data || {});
|
|
|
|
case "CREATE_BOOKMARK":
|
|
return API.createLink(message.data);
|
|
|
|
case "UPDATE_BOOKMARK":
|
|
return API.updateLink(message.data.id, message.data);
|
|
|
|
case "DELETE_BOOKMARK":
|
|
return API.deleteLink(message.data.id);
|
|
|
|
case "GET_COLLECTIONS":
|
|
return CollectionManager.listCollections();
|
|
|
|
case "EXECUTE_QUERY":
|
|
return CollectionManager.executeQuery(message.data.expression, message.data.limit);
|
|
|
|
case "PARSE_QUERY":
|
|
return CollectionManager.parseQuery(message.data.expression);
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
},
|
|
|
|
async runSync() {
|
|
try {
|
|
await browser.storage.local.set({ linksync_syncing: true });
|
|
|
|
const actions = await SyncEngine.runSync();
|
|
|
|
await browser.storage.local.set({
|
|
linksync_syncing: false,
|
|
linksync_last_sync: new Date().toISOString(),
|
|
linksync_pending: false,
|
|
linksync_sync_result: actions,
|
|
});
|
|
|
|
console.log(`LinkSync: Sync completed with ${actions.length} actions`);
|
|
return { success: true, actions };
|
|
} catch (error) {
|
|
console.error("LinkSync: Sync failed:", error);
|
|
await browser.storage.local.set({
|
|
linksync_syncing: false,
|
|
linksync_sync_error: error.message,
|
|
});
|
|
return { success: false, error: error.message };
|
|
}
|
|
},
|
|
};
|
|
|
|
document.addEventListener("DOMContentLoaded", () => Background.init());
|