@verdaccio/local-storage
Version:
Local storage implementation
260 lines (252 loc) • 9.68 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _fs = _interopRequireDefault(require("fs"));
var _path = _interopRequireDefault(require("path"));
var _debug = _interopRequireDefault(require("debug"));
var _lodash = _interopRequireDefault(require("lodash"));
var _mkdirp = _interopRequireDefault(require("mkdirp"));
var _dirUtils = require("./dir-utils");
var _core = require("@verdaccio/core");
var _localFs = _interopRequireWildcard(require("./local-fs"));
var _pkgUtils = require("./pkg-utils");
var _token = _interopRequireDefault(require("./token"));
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
const debug = (0, _debug.default)('verdaccio:plugin:local-storage:database');
class LocalDatabase extends _token.default {
constructor(config, logger) {
super(config);
_defineProperty(this, "path", void 0);
_defineProperty(this, "logger", void 0);
_defineProperty(this, "data", void 0);
_defineProperty(this, "config", void 0);
_defineProperty(this, "locked", void 0);
this.config = config;
this.path = this._dbGenPath(_core.fileUtils.Files.DatabaseName, config);
this.logger = logger;
this.locked = false;
this.data = this._fetchLocalPackages();
this._sync();
}
getSecret() {
return Promise.resolve(this.data.secret);
}
setSecret(secret) {
return new Promise(resolve => {
this.data.secret = secret;
resolve(this._sync());
});
}
add(name, cb) {
if (this.data.list.indexOf(name) === -1) {
this.data.list.push(name);
debug('the private package %o has been added', name);
cb(this._sync());
} else {
debug('the private package %o was not added', name);
cb(null);
}
}
/**
* Filter and only match those values that the query define.
**/
async filterByQuery(results, query) {
// FUTURE: apply new filters, keyword, version, ...
return results.filter(item => {
var _item$name;
return (item === null || item === void 0 ? void 0 : (_item$name = item.name) === null || _item$name === void 0 ? void 0 : _item$name.match(query.text)) !== null;
});
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async getScore(_pkg) {
// TODO: there is no particular reason to predefined scores
// could be improved by using
return Promise.resolve({
final: 1,
detail: {
maintenance: 0,
popularity: 1,
quality: 1
}
});
}
_getCustomPackageLocalStorages() {
const storages = new Map();
const {
packages
} = this.config;
if (packages) {
Object.keys(packages || {}).map(pkg => {
const {
storage
} = packages[pkg];
if (typeof storage === 'string') {
const storagePath = _path.default.join(this.getStoragePath(), storage);
debug('add custom storage for %s on %s', storage, storagePath);
storages.set(storage, storagePath);
}
});
}
return storages;
}
async search(query) {
const results = [];
const storagePath = this.getStoragePath();
const storages = this._getCustomPackageLocalStorages();
const packagesOnStorage = await this.filterByQuery(await (0, _dirUtils.searchOnStorage)(storagePath, storages), query);
debug('packages found %o', packagesOnStorage.length);
for (let storage of packagesOnStorage) {
// check if package is listed on the cache private database
const isPrivate = this.data.list.includes(storage.name);
const score = await this.getScore(storage);
results.push({
package: storage,
verdaccioPrivate: isPrivate,
verdaccioPkgCached: !isPrivate,
score
});
}
return results;
}
remove(name, cb) {
this.get((err, data) => {
if (err) {
cb(_core.errorUtils.getInternalError('error remove private package'));
this.logger.error({
err
}, 'remove the private package has failed @{err}');
debug('error on remove package %o', name);
}
const pkgName = data.indexOf(name);
if (pkgName !== -1) {
this.data.list.splice(pkgName, 1);
debug('remove package %o has been removed', name);
}
cb(this._sync());
});
}
/**
* Return all database elements.
* @return {Array}
*/
get(cb) {
const list = this.data.list;
const totalItems = this.data.list.length;
cb(null, list);
debug('get full list of packages (%o) has been fetched', totalItems);
}
getPackageStorage(packageName) {
const packageAccess = this.config.getMatchedPackagesSpec(packageName);
const packagePath = this._getLocalStoragePath(packageAccess ? packageAccess.storage : undefined);
debug('storage path selected: ', packagePath);
if (_lodash.default.isString(packagePath) === false) {
debug('the package %o has no storage defined ', packageName);
return;
}
const packageStoragePath = _path.default.join(_path.default.resolve(_path.default.dirname(this.config.self_path || ''), packagePath), packageName);
debug('storage absolute path: ', packageStoragePath);
return new _localFs.default(packageStoragePath, this.logger);
}
clean() {
this._sync();
}
getTime(time, mtime) {
return time ? time : mtime;
}
/**
* Syncronize {create} database whether does not exist.
* @return {Error|*}
*/
_sync() {
debug('sync database started');
if (this.locked) {
this.logger.error('Database is locked, please check error message printed during startup to ' + 'prevent data loss.');
return new Error('Verdaccio database is locked, please contact your administrator to checkout ' + 'logs during verdaccio startup.');
}
// Uses sync to prevent ugly race condition
try {
// https://www.npmjs.com/package/mkdirp#mkdirpsyncdir-opts
const folderName = _path.default.dirname(this.path);
_mkdirp.default.sync(folderName);
debug('sync folder %o created succeed', folderName);
} catch (err) {
debug('sync create folder has failed with error: %o', err);
return null;
}
try {
_fs.default.writeFileSync(this.path, JSON.stringify(this.data));
debug('sync write succeed');
return null;
} catch (err) {
debug('sync failed %o', err);
return err;
}
}
getBaseConfigPath() {
return _path.default.dirname(this.config.configPath);
}
/**
* The field storage could be absolute or relative.
* If relative, it will be resolved against the config path.
* If absolute, it will be returned as is.
**/
getStoragePath() {
const {
storage
} = this.config;
if (typeof storage !== 'string') {
throw new TypeError('storage field is mandatory');
}
const storagePath = _path.default.isAbsolute(storage) ? storage : _path.default.normalize(_path.default.join(this.getBaseConfigPath(), storage));
debug('storage path %o', storagePath);
return storagePath;
}
/**
* Verify the right local storage location.
* @param {String} path
* @return {String}
* @private
*/
_getLocalStoragePath(storage) {
const globalConfigStorage = this.config ? this.config.storage : undefined;
if (_lodash.default.isNil(globalConfigStorage)) {
throw new Error('global storage is required for this plugin');
} else {
if (_lodash.default.isNil(storage) === false && _lodash.default.isString(storage)) {
return _path.default.join(globalConfigStorage, storage);
}
return globalConfigStorage;
}
}
/**
* Fetch local packages.
* @private
* @return {Object}
*/
_fetchLocalPackages() {
const list = [];
const emptyDatabase = {
list,
secret: ''
};
try {
return (0, _pkgUtils.loadPrivatePackages)(this.path, this.logger);
} catch (err) {
// readFileSync is platform specific, macOS, Linux and Windows thrown an error
// Only recreate if file not found to prevent data loss
if (err.code !== _localFs.noSuchFile) {
this.locked = true;
this.logger.error('Failed to read package database file, please check the error printed below:\n', `File Path: ${this.path}\n\n ${err.message}`);
}
return emptyDatabase;
}
}
}
var _default = exports.default = LocalDatabase;
//# sourceMappingURL=local-database.js.map