71 lines
2.2 KiB
JavaScript
71 lines
2.2 KiB
JavaScript
// lock.js — soft lock so the second PC gets warned instead of silently
|
|
// overwriting the other PC's work. This is advisory, not a hard database lock:
|
|
// sql.js rewrites the whole .db file on every save, so if both PCs really did
|
|
// write at the same time, the last save wins. The heartbeat file makes that
|
|
// situation visible before it happens instead of after.
|
|
"use strict";
|
|
|
|
const fs = require("fs");
|
|
const path = require("path");
|
|
const os = require("os");
|
|
|
|
const HEARTBEAT_MS = 10_000;
|
|
const STALE_AFTER_MS = 30_000;
|
|
|
|
let lockPath = null;
|
|
let heartbeatTimer = null;
|
|
let myToken = null;
|
|
|
|
function readLock(dataFolder) {
|
|
const p = path.join(dataFolder, ".fellakte.lock");
|
|
if (!fs.existsSync(p)) return null;
|
|
try {
|
|
return JSON.parse(fs.readFileSync(p, "utf8"));
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks whether another PC currently looks active.
|
|
* Returns { active:boolean, info } — info is the other host's lock contents when active.
|
|
*/
|
|
function checkOther(dataFolder) {
|
|
const info = readLock(dataFolder);
|
|
if (!info) return { active: false, info: null };
|
|
const age = Date.now() - info.updatedAt;
|
|
const isOther = info.host !== os.hostname() || info.pid !== process.pid;
|
|
return { active: isOther && age < STALE_AFTER_MS, info };
|
|
}
|
|
|
|
function acquire(dataFolder) {
|
|
lockPath = path.join(dataFolder, ".fellakte.lock");
|
|
myToken = { host: os.hostname(), pid: process.pid, updatedAt: Date.now() };
|
|
fs.writeFileSync(lockPath, JSON.stringify(myToken));
|
|
heartbeatTimer = setInterval(() => {
|
|
myToken.updatedAt = Date.now();
|
|
try {
|
|
fs.writeFileSync(lockPath, JSON.stringify(myToken));
|
|
} catch {
|
|
// shared drive briefly unreachable — next tick will retry
|
|
}
|
|
}, HEARTBEAT_MS);
|
|
}
|
|
|
|
function release() {
|
|
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
heartbeatTimer = null;
|
|
if (lockPath && fs.existsSync(lockPath)) {
|
|
try {
|
|
const current = readLock(path.dirname(lockPath));
|
|
if (current && current.host === os.hostname() && current.pid === process.pid) {
|
|
fs.unlinkSync(lockPath);
|
|
}
|
|
} catch {
|
|
// best effort — a stale lock will simply age out after STALE_AFTER_MS
|
|
}
|
|
}
|
|
}
|
|
|
|
module.exports = { checkOther, acquire, release };
|