Files
fellakte/main/lock.js
T
2026-09-14 18:57:05 +00:00

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 };