verdaccio/lib/file-locking.js

153 lines
3.4 KiB
JavaScript

'use strict';
/**
* file-locking.js - file system locking (replaces fs-ext)
*/
const async = require('async');
const locker = require('lockfile');
const fs = require('fs');
const path = require('path');
// locks a file by creating a lock file
const lockFile = function(name, next) {
const lockFileName = `${name}.lock`;
const lockOpts = {
wait: 1000, // time (ms) to wait when checking for stale locks
pollPeriod: 100, // how often (ms) to re-check stale locks
stale: 5 * 60 * 1000, // locks are considered stale after 5 minutes
retries: 100, // number of times to attempt to create a lock
retryWait: 100, // time (ms) between tries
};
async.series({
statdir: function(callback) {
// test to see if the directory exists
fs.stat(path.dirname(name), function(err, stats) {
if (err) {
callback(err);
} else if (!stats.isDirectory()) {
callback(new Error(path.dirname(name) + ' is not a directory'));
} else {
callback(null);
}
});
},
statfile: function(callback) {
// test to see if the file to lock exists
fs.stat(name, function(err, stats) {
if (err) {
callback(err);
} else if (!stats.isFile()) {
callback(new Error(path.dirname(name) + ' is not a file'));
} else {
callback(null);
}
});
},
lockfile: function(callback) {
// try to lock the file
locker.lock(lockFileName, lockOpts, callback);
},
}, function(err) {
if (err) {
// lock failed
return next(err);
}
// lock succeeded
return next(null);
});
};
// unlocks file by removing existing lock file
const unlockFile= function(name, next) {
const lockFileName = `${name}.lock`;
locker.unlock(lockFileName, function(err) {
if (err) {
return next(err);
}
return next(null);
});
};
/**
* Reads a local file, which involves
* optionally taking a lock
* reading the file contents
* optionally parsing JSON contents
* @param {*} name
* @param {*} options
* @param {*} next
*/
function readFile(name, options, next) {
if (typeof options === 'function' && next === null) {
next = options;
options = {};
}
options = options || {};
options.lock = options.lock || false;
options.parse = options.parse || false;
const lock = function(callback) {
if (!options.lock) {
return callback(null);
}
lockFile(name, function(err) {
if (err) {
return callback(err);
}
return callback(null);
});
};
const read = function(callback) {
fs.readFile(name, 'utf8', function(err, contents) {
if (err) {
return callback(err);
}
callback(null, contents);
});
};
const parseJSON = function(contents, callback) {
if (!options.parse) {
return callback(null, contents);
}
try {
contents = JSON.parse(contents);
return callback(null, contents);
} catch (err) {
return callback(err);
}
};
async.waterfall([
lock,
read,
parseJSON,
],
function(err, result) {
if (err) {
return next(err);
} else {
return next(null, result);
}
});
}
exports.lockFile = lockFile;
exports.unlockFile = unlockFile;
exports.readFile = readFile;