UNPKG

@verdaccio/local-storage

Version:

Local storage implementation

260 lines (252 loc) 9.68 kB
"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