Add Chrome extension and Windows native host
This commit is contained in:
1 parent
6f2b238988
commit
f889c6129d
8 files changed
+201
No files matched your search
@@ -0,0 +1,7 @@
|
||||
// Copy/edit locally after unpacking the extension. Do not commit a real token.
|
||||
export const CONFIG = {
|
||||
// Example: "https://history.example.com/v1/visits"
|
||||
collectorUrl: "",
|
||||
apiToken: "",
|
||||
deviceId: "windows-chrome"
|
||||
};
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "Local History Archive",
|
||||
"version": "1.0.0",
|
||||
"description": "Writes Chrome visits to a local, append-only archive via Native Messaging.",
|
||||
"permissions": [
|
||||
"history",
|
||||
"nativeMessaging",
|
||||
"storage",
|
||||
"alarms"
|
||||
],
|
||||
"background": {
|
||||
"service_worker": "service-worker.js",
|
||||
"type": "module"
|
||||
},
|
||||
"action": {
|
||||
"default_title": "Local History Archive"
|
||||
},
|
||||
"host_permissions": [
|
||||
"https://*/*",
|
||||
"http://*/*"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { CONFIG } from "./config.js";
|
||||
|
||||
const HOST = "si.komelt.local_history_archive";
|
||||
const QUEUE_KEY = "pendingVisits";
|
||||
const MAX_QUEUE = 2000;
|
||||
|
||||
async function queued() {
|
||||
const { [QUEUE_KEY]: visits = [] } = await chrome.storage.local.get(QUEUE_KEY);
|
||||
return visits;
|
||||
}
|
||||
async function persist(visits) {
|
||||
await chrome.storage.local.set({ [QUEUE_KEY]: visits.slice(-MAX_QUEUE) });
|
||||
}
|
||||
async function archiveLocally(visits) {
|
||||
if (!visits.length) return true;
|
||||
try {
|
||||
const reply = await chrome.runtime.sendNativeMessage(HOST, { type: "visits", visits });
|
||||
return reply?.ok === true;
|
||||
} catch (error) {
|
||||
console.warn("Local archive unavailable", error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
async function sendToCollector(visits) {
|
||||
// Empty settings deliberately disable sending; the local SQLite archive still works.
|
||||
if (!CONFIG.collectorUrl || !CONFIG.apiToken) return true;
|
||||
try {
|
||||
const response = await fetch(CONFIG.collectorUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": `Bearer ${CONFIG.apiToken}`
|
||||
},
|
||||
body: JSON.stringify({ device_id: CONFIG.deviceId, visits })
|
||||
});
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.warn("Collector unavailable", error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
async function flush() {
|
||||
const visits = await queued();
|
||||
if (!visits.length) return;
|
||||
const [localOk, remoteOk] = await Promise.all([archiveLocally(visits), sendToCollector(visits)]);
|
||||
if (localOk && remoteOk) await persist([]);
|
||||
}
|
||||
chrome.history.onVisited.addListener(async (item) => {
|
||||
const visits = await queued();
|
||||
visits.push({
|
||||
url: item.url, title: item.title || "",
|
||||
visit_time: new Date(item.lastVisitTime || Date.now()).toISOString(),
|
||||
transition: "observed", source: "chrome-extension"
|
||||
});
|
||||
await persist(visits);
|
||||
await flush();
|
||||
});
|
||||
chrome.runtime.onStartup.addListener(flush);
|
||||
chrome.runtime.onInstalled.addListener(flush);
|
||||
chrome.alarms.create("local-history-archive-flush", { periodInMinutes: 1 });
|
||||
chrome.alarms.onAlarm.addListener(flush);
|
||||
Reference in new issue
Block a user