cb-template
Version:
A Node.js server-side template engine that supports multi-level template inheritance.
127 lines (103 loc) • 2.85 kB
JavaScript
import fs from 'node:fs';
// Lock timeout duration
const lockTimeout = 1000 * 60 * 5;
const getLockFile = (file) => `${file}.lock`;
// Acquire lock
const getLock = (file, callback) => {
const lockfilePath = getLockFile(file);
// Use mkdir to create lock
fs.mkdir(lockfilePath, (err) => {
// Create unlock function
const release = (releaseCallback) => {
if (!releaseCallback) {
releaseCallback = () => {};
}
unlock(file, releaseCallback);
};
callback(err, release);
});
};
// Unlock
const unlock = (file, callback) => {
const lockfilePath = getLockFile(file);
fs.rm(lockfilePath, { recursive: true, force: true }, (err) => {
if (err && err.code !== 'ENOENT') {
return callback(err);
}
callback();
});
};
// Lock
const lock = (file, callback) => {
// Acquire lock
getLock(file, (err, release) => {
if (!err) {
// Lock acquired
return callback(null, release);
}
// If error is not lock already exists, return error
if (err.code !== 'EEXIST') {
return callback(err);
}
// Lock already exists case
const lockfilePath = getLockFile(file);
// Check if lock is too stale
fs.stat(lockfilePath, (err, stat) => {
if (err) {
// If unable to get lock time, return error
return callback(err);
}
if (Date.now() - stat.mtime.getTime() > lockTimeout) {
// Lock is too stale, unlock first then try to acquire lock
// Unlock first
unlock(file, (err) => {
if (err) {
// Unlock failed, return error
return callback(err);
}
// Then try to acquire lock again
getLock(file, (err, release) => {
if (!err) {
// Lock acquired
return callback(null, release);
}
else {
// Failed to acquire lock
return callback(err);
}
});
});
}
else {
// Lock has not expired, return lock already exists error
const lockedError = new Error('File is already locked');
lockedError.code = 'ELOCKED';
return callback(lockedError);
}
});
});
};
// Check if already locked
const isLocked = (file, callback) => {
const lockfilePath = getLockFile(file);
// Check if lock exists
fs.stat(lockfilePath, (err, stat) => {
if (err) {
// If lock does not exist, indicates unlocked, otherwise all indicate locked
return err.code === 'ENOENT' ? callback(false) : callback(true);
}
if (Date.now() - stat.mtime.getTime() > lockTimeout) {
// Lock is too stale, consider unlocked
return callback(false);
}
else {
// Locked
return callback(true);
}
});
};
export {
lock,
unlock,
isLocked
};