mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-12-16 21:56:25 -05:00
feat!: async storage plugin bootstrap (#2144)
* feat: async storage plugin bootstrap * refactor fs.promise to promisify on node12 * Add changeset * Update big-lobsters-sin.md * Update utils.test.ts * Update utils.test.ts * Update ci.yml * Update utils.test.ts
This commit is contained in:
parent
f837e6cc61
commit
dc05edfe60
22 changed files with 233 additions and 146 deletions
23
.changeset/big-lobsters-sin.md
Normal file
23
.changeset/big-lobsters-sin.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
'@verdaccio/local-storage': major
|
||||
'@verdaccio/url': major
|
||||
'verdaccio-aws-s3-storage': major
|
||||
'verdaccio-google-cloud': major
|
||||
'verdaccio-memory': major
|
||||
'@verdaccio/store': major
|
||||
---
|
||||
|
||||
# async storage plugin bootstrap
|
||||
|
||||
Gives a storage plugin the ability to perform asynchronous tasks on initialization
|
||||
|
||||
## Breaking change
|
||||
|
||||
Plugin must have an init method in which asynchronous tasks can be executed
|
||||
|
||||
```js
|
||||
public async init(): Promise<void> {
|
||||
this.data = await this._fetchLocalPackages();
|
||||
this._sync();
|
||||
}
|
||||
```
|
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
@ -20,7 +20,7 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
node_version: [12, 14, 15]
|
||||
node_version: [12, 14]
|
||||
|
||||
name: ${{ matrix.os }} / Node ${{ matrix.node_version }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
@ -30,7 +30,7 @@ jobs:
|
|||
- name: Use Node ${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node_version: ${{ matrix.node_version }}
|
||||
node-version: ${{ matrix.node_version }}
|
||||
- name: Install pnpm
|
||||
run: npm i pnpm@latest -g
|
||||
- name: Install
|
||||
|
|
|
@ -19,3 +19,7 @@ node_modules/
|
|||
packages/core/local-storage/_storage/**
|
||||
packages/standalone/dist/bundle.js
|
||||
docker-examples/v5/reverse_proxy/nginx/relative_path/storage/*
|
||||
docker-examples/
|
||||
build/
|
||||
.vscode/
|
||||
.github/
|
|
@ -19,8 +19,8 @@ import { getInternalError } from '@verdaccio/commons-api';
|
|||
import LocalDriver, { noSuchFile } from './local-fs';
|
||||
import { loadPrivatePackages } from './pkg-utils';
|
||||
import TokenActions from './token';
|
||||
import { _dbGenPath } from './utils';
|
||||
|
||||
const DEPRECATED_DB_NAME = '.sinopia-db.json';
|
||||
const DB_NAME = '.verdaccio-db.json';
|
||||
|
||||
const debug = buildDebug('verdaccio:plugin:local-storage');
|
||||
|
@ -28,6 +28,7 @@ const debug = buildDebug('verdaccio:plugin:local-storage');
|
|||
class LocalDatabase extends TokenActions implements IPluginStorage<{}> {
|
||||
public path: string;
|
||||
public logger: Logger;
|
||||
// @ts-ignore
|
||||
public data: LocalStorage;
|
||||
public config: Config;
|
||||
public locked: boolean;
|
||||
|
@ -35,10 +36,15 @@ class LocalDatabase extends TokenActions implements IPluginStorage<{}> {
|
|||
public constructor(config: Config, logger: Logger) {
|
||||
super(config);
|
||||
this.config = config;
|
||||
this.path = this._buildStoragePath(config);
|
||||
this.logger = logger;
|
||||
this.locked = false;
|
||||
this.data = this._fetchLocalPackages();
|
||||
this.path = _dbGenPath(DB_NAME, config);
|
||||
debug('plugin storage path %o', this.path);
|
||||
}
|
||||
|
||||
public async init(): Promise<void> {
|
||||
debug('plugin init');
|
||||
this.data = await this._fetchLocalPackages();
|
||||
this._sync();
|
||||
}
|
||||
|
||||
|
@ -272,6 +278,7 @@ class LocalDatabase extends TokenActions implements IPluginStorage<{}> {
|
|||
try {
|
||||
// https://www.npmjs.com/package/mkdirp#mkdirpsyncdir-opts
|
||||
const folderName = Path.dirname(this.path);
|
||||
debug('creating folder %o', folderName);
|
||||
mkdirp.sync(folderName);
|
||||
debug('sync folder %o created succeed', folderName);
|
||||
} catch (err) {
|
||||
|
@ -310,40 +317,21 @@ class LocalDatabase extends TokenActions implements IPluginStorage<{}> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the local database path.
|
||||
* @param {Object} config
|
||||
* @return {string|String|*}
|
||||
* @private
|
||||
*/
|
||||
private _buildStoragePath(config: Config): string {
|
||||
const sinopiadbPath: string = this._dbGenPath(DEPRECATED_DB_NAME, config);
|
||||
try {
|
||||
fs.accessSync(sinopiadbPath, fs.constants.F_OK);
|
||||
return sinopiadbPath;
|
||||
} catch (err) {
|
||||
if (err.code === noSuchFile) {
|
||||
return this._dbGenPath(DB_NAME, config);
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch local packages.
|
||||
* @private
|
||||
* @return {Object}
|
||||
*/
|
||||
private _fetchLocalPackages(): LocalStorage {
|
||||
private async _fetchLocalPackages(): Promise<LocalStorage> {
|
||||
const list: StorageList = [];
|
||||
const emptyDatabase = { list, secret: '' };
|
||||
|
||||
try {
|
||||
return loadPrivatePackages(this.path, this.logger);
|
||||
return await 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
|
||||
debug('error on fetch local packages %o', err);
|
||||
if (err.code !== noSuchFile) {
|
||||
this.locked = true;
|
||||
this.logger.error(
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import fs from 'fs';
|
||||
|
||||
import _ from 'lodash';
|
||||
import { LocalStorage, StorageList, Logger } from '@verdaccio/types';
|
||||
import { readFilePromise } from './read-file';
|
||||
|
||||
export function loadPrivatePackages(path: string, logger: Logger): LocalStorage {
|
||||
export async function loadPrivatePackages(path: string, logger: Logger): Promise<LocalStorage> {
|
||||
const list: StorageList = [];
|
||||
const emptyDatabase = { list, secret: '' };
|
||||
const data = fs.readFileSync(path, 'utf8');
|
||||
const data = await readFilePromise(path);
|
||||
|
||||
if (_.isNil(data)) {
|
||||
// readFileSync is platform specific, FreeBSD might return null
|
||||
|
|
8
packages/core/local-storage/src/read-file.ts
Normal file
8
packages/core/local-storage/src/read-file.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { promisify } from 'util';
|
||||
import fs from 'fs';
|
||||
|
||||
const readFile = promisify(fs.readFile);
|
||||
|
||||
export const readFilePromise = async (path) => {
|
||||
return await readFile(path, 'utf8');
|
||||
};
|
|
@ -1,4 +1,3 @@
|
|||
import Path from 'path';
|
||||
import _ from 'lodash';
|
||||
import low from 'lowdb';
|
||||
import FileAsync from 'lowdb/adapters/FileAsync';
|
||||
|
@ -6,6 +5,7 @@ import FileMemory from 'lowdb/adapters/Memory';
|
|||
import buildDebug from 'debug';
|
||||
|
||||
import { ITokenActions, Config, Token, TokenFilter } from '@verdaccio/types';
|
||||
import { _dbGenPath } from './utils';
|
||||
|
||||
const debug = buildDebug('verdaccio:plugin:local-storage:token');
|
||||
|
||||
|
@ -20,12 +20,6 @@ export default class TokenActions implements ITokenActions {
|
|||
this.tokenDb = null;
|
||||
}
|
||||
|
||||
public _dbGenPath(dbName: string, config: Config): string {
|
||||
return Path.join(
|
||||
Path.resolve(Path.dirname(config.config_path || ''), config.storage as string, dbName)
|
||||
);
|
||||
}
|
||||
|
||||
private async getTokenDb(): Promise<low.LowdbAsync<any>> {
|
||||
if (!this.tokenDb) {
|
||||
debug('token database is not defined');
|
||||
|
@ -35,7 +29,7 @@ export default class TokenActions implements ITokenActions {
|
|||
adapter = new FileMemory('');
|
||||
} else {
|
||||
debug('token async adapter');
|
||||
const pathDb = this._dbGenPath(TOKEN_DB_NAME, this.config);
|
||||
const pathDb = _dbGenPath(TOKEN_DB_NAME, this.config);
|
||||
adapter = new FileAsync(pathDb);
|
||||
}
|
||||
debug('token bd generated');
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
import _ from 'lodash';
|
||||
import { Config } from '@verdaccio/types';
|
||||
|
||||
export function getFileStats(packagePath: string): Promise<fs.Stats> {
|
||||
return new Promise((resolve, reject): void => {
|
||||
|
@ -75,4 +75,13 @@ export async function findPackages(
|
|||
resolve(listPackages);
|
||||
});
|
||||
}
|
||||
|
||||
export function _dbGenPath(
|
||||
dbName: string,
|
||||
config: Pick<Config, 'config_path' | 'storage'>
|
||||
): string {
|
||||
return path.join(
|
||||
path.resolve(path.dirname(config.config_path || ''), config.storage as string, dbName)
|
||||
);
|
||||
}
|
||||
/* eslint-enable no-async-promise-executor */
|
||||
|
|
|
@ -3,7 +3,7 @@ import fs from 'fs';
|
|||
import path from 'path';
|
||||
|
||||
import { assign } from 'lodash';
|
||||
import { ILocalData, PluginOptions, Token } from '@verdaccio/types';
|
||||
import { IPluginStorage, PluginOptions } from '@verdaccio/types';
|
||||
|
||||
import LocalDatabase from '../src/local-database';
|
||||
import { ILocalFSPackageManager } from '../src/local-fs';
|
||||
|
@ -18,16 +18,17 @@ const optionsPlugin: PluginOptions<{}> = {
|
|||
config: new Config(),
|
||||
};
|
||||
|
||||
let locaDatabase: ILocalData<{}>;
|
||||
let locaDatabase: IPluginStorage<{}>;
|
||||
let loadPrivatePackages;
|
||||
|
||||
describe('Local Database', () => {
|
||||
beforeEach(() => {
|
||||
beforeEach(async () => {
|
||||
const writeMock = jest.spyOn(fs, 'writeFileSync').mockImplementation();
|
||||
loadPrivatePackages = jest
|
||||
.spyOn(pkgUtils, 'loadPrivatePackages')
|
||||
.mockReturnValue({ list: [], secret: '' });
|
||||
.mockResolvedValue({ list: [], secret: '' });
|
||||
locaDatabase = new LocalDatabase(optionsPlugin.config, optionsPlugin.logger);
|
||||
await (locaDatabase as LocalDatabase).init();
|
||||
(locaDatabase as LocalDatabase).clean();
|
||||
writeMock.mockClear();
|
||||
});
|
||||
|
@ -41,12 +42,13 @@ describe('Local Database', () => {
|
|||
expect(locaDatabase).toBeDefined();
|
||||
});
|
||||
|
||||
test('should display log error if fails on load database', () => {
|
||||
test('should display log error if fails on load database', async () => {
|
||||
loadPrivatePackages.mockImplementation(() => {
|
||||
throw Error();
|
||||
});
|
||||
|
||||
new LocalDatabase(optionsPlugin.config, optionsPlugin.logger);
|
||||
const instance = new LocalDatabase(optionsPlugin.config, optionsPlugin.logger);
|
||||
await instance.init();
|
||||
|
||||
expect(optionsPlugin.logger.error).toHaveBeenCalled();
|
||||
expect(optionsPlugin.logger.error).toHaveBeenCalledTimes(2);
|
||||
|
@ -219,3 +221,5 @@ describe('Local Database', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
// NOTE: Crear test para verificar que se crea el storage file
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import * as readFile from '../src/read-file';
|
||||
|
||||
import { findPackages } from '../src/utils';
|
||||
import { findPackages, _dbGenPath } from '../src/utils';
|
||||
import { loadPrivatePackages } from '../src/pkg-utils';
|
||||
import { noSuchFile } from '../src/local-fs';
|
||||
|
||||
|
@ -16,34 +16,31 @@ describe('Utitlies', () => {
|
|||
jest.resetModules();
|
||||
});
|
||||
|
||||
test('should load private packages', () => {
|
||||
test('should load private packages', async () => {
|
||||
const database = loadDb('ok');
|
||||
const db = loadPrivatePackages(database, logger);
|
||||
const db = await loadPrivatePackages(database, logger);
|
||||
|
||||
expect(db.list).toHaveLength(15);
|
||||
});
|
||||
|
||||
test('should load and empty private packages if database file is valid and empty', () => {
|
||||
test('should load and empty private packages if database file is valid and empty', async () => {
|
||||
const database = loadDb('empty');
|
||||
const db = loadPrivatePackages(database, logger);
|
||||
const db = await loadPrivatePackages(database, logger);
|
||||
|
||||
expect(db.list).toHaveLength(0);
|
||||
});
|
||||
|
||||
test('should fails on load private packages', () => {
|
||||
test('should fails on load private packages', async () => {
|
||||
const database = loadDb('corrupted');
|
||||
|
||||
expect(() => {
|
||||
loadPrivatePackages(database, logger);
|
||||
}).toThrow();
|
||||
await expect(loadPrivatePackages(database, logger)).rejects.toThrow();
|
||||
});
|
||||
|
||||
test('should handle null read values and return empty database', () => {
|
||||
const spy = jest.spyOn(fs, 'readFileSync');
|
||||
spy.mockReturnValue(null);
|
||||
|
||||
test('should handle null read values and return empty database', async () => {
|
||||
const spy = jest.spyOn(readFile, 'readFilePromise');
|
||||
spy.mockResolvedValue(null);
|
||||
const database = loadDb('ok');
|
||||
const db = loadPrivatePackages(database, logger);
|
||||
const db = await loadPrivatePackages(database, logger);
|
||||
|
||||
expect(db.list).toHaveLength(0);
|
||||
|
||||
|
@ -71,4 +68,42 @@ describe('Utitlies', () => {
|
|||
expect(validator).toHaveBeenCalledTimes(8);
|
||||
});
|
||||
});
|
||||
|
||||
describe('dbGenPath', () => {
|
||||
test('should generate a storage path', () => {
|
||||
expect(
|
||||
_dbGenPath('local.db', {
|
||||
storage: './storage',
|
||||
config_path: '/etc/foo/config.yaml',
|
||||
})
|
||||
).toMatch('local.db');
|
||||
});
|
||||
|
||||
test('should verify with empty storage', () => {
|
||||
expect(
|
||||
_dbGenPath('local.db', {
|
||||
storage: '',
|
||||
config_path: '/etc/foo/config.yaml',
|
||||
})
|
||||
).toMatch('local.db');
|
||||
});
|
||||
|
||||
test('should verify with undefined storage', () => {
|
||||
expect(
|
||||
_dbGenPath('local.db', {
|
||||
storage: '',
|
||||
config_path: '/etc/foo/config.yaml',
|
||||
})
|
||||
).toMatch('local.db');
|
||||
});
|
||||
|
||||
test('should verify with config path is invalid', () => {
|
||||
expect(
|
||||
_dbGenPath('local.db', {
|
||||
storage: './storage',
|
||||
config_path: undefined,
|
||||
})
|
||||
).toMatch('local.db');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
9
packages/core/types/index.d.ts
vendored
9
packages/core/types/index.d.ts
vendored
|
@ -443,6 +443,7 @@ declare module '@verdaccio/types' {
|
|||
add(name: string, callback: Callback): void;
|
||||
remove(name: string, callback: Callback): void;
|
||||
get(callback: Callback): void;
|
||||
init(): Promise<void>;
|
||||
getSecret(): Promise<string>;
|
||||
setSecret(secret: string): Promise<any>;
|
||||
getPackageStorage(packageInfo: string): IPackageStorage;
|
||||
|
@ -506,14 +507,6 @@ declare module '@verdaccio/types' {
|
|||
getLocalDatabase(callback: Callback): void;
|
||||
}
|
||||
|
||||
interface IBasicStorage<T> extends StoragePackageActions {
|
||||
addPackage(name: string, info: Package, callback: Callback): void;
|
||||
updateVersions(name: string, packageInfo: Package, callback: Callback): void;
|
||||
getPackageMetadata(name: string, callback: Callback): void;
|
||||
search(startKey: string, options: any): IReadTarball;
|
||||
getSecret(config: T & Config): Promise<any>;
|
||||
}
|
||||
|
||||
// @deprecated use IBasicAuth from @verdaccio/auth
|
||||
interface IBasicAuth<T> {
|
||||
config: T & Config;
|
||||
|
|
|
@ -52,7 +52,6 @@ import Server, { IServerBridge } from './server';
|
|||
*/
|
||||
export function mockServer(port: number, options: MockRegistryOptions = {}) {
|
||||
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), '/verdaccio-test'));
|
||||
// console.log("-->tempRoot", tempRoot);
|
||||
|
||||
// default locations
|
||||
const configPath = path.join(__dirname, './config/yaml', '/mock-server-test.yaml');
|
||||
|
@ -70,9 +69,6 @@ export function mockServer(port: number, options: MockRegistryOptions = {}) {
|
|||
|
||||
// mix external options
|
||||
const finalOptions: MockRegistryOptions = Object.assign({}, localOptions, options);
|
||||
|
||||
// console.log('--->finalOptions=>', finalOptions);
|
||||
|
||||
// final locations
|
||||
const tempConfigFile = path.join(tempRoot, 'verdaccio.yaml');
|
||||
const storePath = path.join(tempRoot, '/mock-store');
|
||||
|
|
|
@ -62,6 +62,10 @@ export default class S3Database implements IPluginStorage<S3Config> {
|
|||
});
|
||||
}
|
||||
|
||||
public init() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public async getSecret(): Promise<string> {
|
||||
return Promise.resolve((await this._getData()).secret);
|
||||
}
|
||||
|
|
|
@ -49,6 +49,9 @@ class GoogleCloudDatabase implements IPluginStorage<VerdaccioConfigGoogleStorage
|
|||
this.helper = new StorageHelper(datastore, storage, this.config);
|
||||
}
|
||||
|
||||
public init() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
private _getGoogleOptions(config: VerdaccioConfigGoogleStorage): DatastoreOptions {
|
||||
const GOOGLE_OPTIONS: DatastoreOptions = {};
|
||||
|
||||
|
|
|
@ -38,6 +38,10 @@ class LocalMemory implements IPluginStorage<ConfigMemory> {
|
|||
debug('start plugin');
|
||||
}
|
||||
|
||||
public init() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public getSecret(): Promise<string> {
|
||||
return Promise.resolve(this.data.secret);
|
||||
}
|
||||
|
|
|
@ -40,7 +40,6 @@ const generateStorage = async function () {
|
|||
|
||||
const generateSameUplinkStorage = async function () {
|
||||
const storagePath = generateRamdonStorage();
|
||||
console.log('-->storagePath', storagePath);
|
||||
const storageConfig = configExample(
|
||||
{
|
||||
config_path: storagePath,
|
||||
|
|
|
@ -59,7 +59,18 @@ class LocalStorage implements IStorage {
|
|||
debug('local storage created');
|
||||
this.logger = logger.child({ sub: 'fs' });
|
||||
this.config = config;
|
||||
this.storagePlugin = this._loadStorage(config, logger);
|
||||
// @ts-ignore
|
||||
this.storagePlugin = null;
|
||||
}
|
||||
|
||||
public async init() {
|
||||
if (this.storagePlugin === null) {
|
||||
this.storagePlugin = this._loadStorage(this.config, this.logger);
|
||||
await this.storagePlugin.init();
|
||||
} else {
|
||||
debug('storage plugin has been already initialized');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public addPackage(name: string, pkg: Package, callback: Callback): void {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// eslint-disable no-invalid-this
|
||||
|
||||
import lunrMutable from 'lunr-mutable-indexes';
|
||||
import { Version } from '@verdaccio/types';
|
||||
import { Version, IPluginStorage, Config } from '@verdaccio/types';
|
||||
import { IStorageHandler, IStorage } from './storage';
|
||||
|
||||
export interface IWebSearch {
|
||||
|
@ -44,6 +44,10 @@ class Search implements IWebSearch {
|
|||
});
|
||||
}
|
||||
|
||||
public init() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a query to the indexer.
|
||||
* If the keyword is a * it returns all local elements
|
||||
|
@ -55,7 +59,7 @@ class Search implements IWebSearch {
|
|||
const localStorage = this.storage.localStorage as IStorage;
|
||||
|
||||
return query === '*'
|
||||
? localStorage.storagePlugin.get((items): any => {
|
||||
? (localStorage.storagePlugin as IPluginStorage<Config>).get((items): any => {
|
||||
items.map(function (pkg): any {
|
||||
return { ref: pkg, score: 1 };
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ import assert from 'assert';
|
|||
import Stream from 'stream';
|
||||
import async, { AsyncResultArrayCallback } from 'async';
|
||||
import _ from 'lodash';
|
||||
import { Request } from 'express';
|
||||
import e, { Request } from 'express';
|
||||
import buildDebug from 'debug';
|
||||
|
||||
import { ProxyStorage } from '@verdaccio/proxy';
|
||||
|
@ -21,10 +21,10 @@ import {
|
|||
DistFile,
|
||||
StringValue,
|
||||
IPluginStorageFilter,
|
||||
IBasicStorage,
|
||||
IPluginStorage,
|
||||
Callback,
|
||||
Logger,
|
||||
StoragePackageActions,
|
||||
GenericBody,
|
||||
TokenFilter,
|
||||
Token,
|
||||
|
@ -57,9 +57,18 @@ export interface IGetPackageOptions {
|
|||
req: any;
|
||||
}
|
||||
|
||||
export interface IBasicStorage<T> extends StoragePackageActions {
|
||||
init(): Promise<void>;
|
||||
addPackage(name: string, info: Package, callback: Callback): void;
|
||||
updateVersions(name: string, packageInfo: Package, callback: Callback): void;
|
||||
getPackageMetadata(name: string, callback: Callback): void;
|
||||
search(startKey: string, options: any): IReadTarball;
|
||||
getSecret(config: T & Config): Promise<any>;
|
||||
}
|
||||
|
||||
export interface IStorage extends IBasicStorage<Config>, ITokenActions {
|
||||
config: Config;
|
||||
storagePlugin: IPluginStorage<Config>;
|
||||
storagePlugin: IPluginStorage<Config> | null;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
|
@ -76,7 +85,7 @@ export interface IStorageHandler extends IStorageManager<Config>, ITokenActions
|
|||
localStorage: IStorage | null;
|
||||
filters: IPluginFilters;
|
||||
uplinks: ProxyList;
|
||||
init(config: Config, filters: IPluginFilters): Promise<string>;
|
||||
init(config: Config, filters: IPluginFilters): Promise<void>;
|
||||
saveToken(token: Token): Promise<any>;
|
||||
deleteToken(user: string, tokenKey: string): Promise<any>;
|
||||
readTokens(filter: TokenFilter): Promise<Token[]>;
|
||||
|
@ -101,12 +110,19 @@ class Storage {
|
|||
this.localStorage = null;
|
||||
}
|
||||
|
||||
public init(config: Config, filters: IPluginFilters = []): Promise<string> {
|
||||
this.filters = filters;
|
||||
debug('filters available %o', filters);
|
||||
this.localStorage = new LocalStorage(this.config, logger);
|
||||
|
||||
return this.localStorage.getSecret(config);
|
||||
public async init(config: Config, filters: IPluginFilters = []): Promise<void> {
|
||||
if (this.localStorage === null) {
|
||||
this.filters = filters;
|
||||
debug('filters available %o', filters);
|
||||
this.localStorage = new LocalStorage(this.config, logger);
|
||||
await this.localStorage.init();
|
||||
debug('local init storage initialized');
|
||||
await this.localStorage.getSecret(config);
|
||||
debug('local storage secret initialized');
|
||||
} else {
|
||||
debug('storage has been already initialized');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -480,53 +496,57 @@ class Storage {
|
|||
public getLocalDatabase(callback: Callback): void {
|
||||
const self = this;
|
||||
debug('get local database');
|
||||
this.localStorage.storagePlugin.get((err, locals): void => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
}
|
||||
if (this.localStorage.storagePlugin !== null) {
|
||||
this.localStorage.storagePlugin.get((err, locals): void => {
|
||||
if (err) {
|
||||
callback(err);
|
||||
}
|
||||
|
||||
const packages: Version[] = [];
|
||||
const getPackage = function (itemPkg): void {
|
||||
self.localStorage.getPackageMetadata(
|
||||
locals[itemPkg],
|
||||
function (err, pkgMetadata: Package): void {
|
||||
if (_.isNil(err)) {
|
||||
const latest = pkgMetadata[DIST_TAGS].latest;
|
||||
if (latest && pkgMetadata.versions[latest]) {
|
||||
const version: Version = pkgMetadata.versions[latest];
|
||||
const timeList = pkgMetadata.time as GenericBody;
|
||||
const time = timeList[latest];
|
||||
// @ts-ignore
|
||||
version.time = time;
|
||||
const packages: Version[] = [];
|
||||
const getPackage = function (itemPkg): void {
|
||||
self.localStorage.getPackageMetadata(
|
||||
locals[itemPkg],
|
||||
function (err, pkgMetadata: Package): void {
|
||||
if (_.isNil(err)) {
|
||||
const latest = pkgMetadata[DIST_TAGS].latest;
|
||||
if (latest && pkgMetadata.versions[latest]) {
|
||||
const version: Version = pkgMetadata.versions[latest];
|
||||
const timeList = pkgMetadata.time as GenericBody;
|
||||
const time = timeList[latest];
|
||||
// @ts-ignore
|
||||
version.time = time;
|
||||
|
||||
// Add for stars api
|
||||
// @ts-ignore
|
||||
version.users = pkgMetadata.users;
|
||||
// Add for stars api
|
||||
// @ts-ignore
|
||||
version.users = pkgMetadata.users;
|
||||
|
||||
packages.push(version);
|
||||
packages.push(version);
|
||||
} else {
|
||||
self.logger.warn(
|
||||
{ package: locals[itemPkg] },
|
||||
'package @{package} does not have a "latest" tag?'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (itemPkg >= locals.length - 1) {
|
||||
callback(null, packages);
|
||||
} else {
|
||||
self.logger.warn(
|
||||
{ package: locals[itemPkg] },
|
||||
'package @{package} does not have a "latest" tag?'
|
||||
);
|
||||
getPackage(itemPkg + 1);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
if (itemPkg >= locals.length - 1) {
|
||||
callback(null, packages);
|
||||
} else {
|
||||
getPackage(itemPkg + 1);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
if (locals.length) {
|
||||
getPackage(0);
|
||||
} else {
|
||||
callback(null, []);
|
||||
}
|
||||
});
|
||||
if (locals.length) {
|
||||
getPackage(0);
|
||||
} else {
|
||||
callback(null, []);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
debug('local stora instance is null');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -90,8 +90,9 @@ describe('LocalStorage', () => {
|
|||
});
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
beforeAll(async () => {
|
||||
storage = getStorage();
|
||||
await storage.init();
|
||||
});
|
||||
|
||||
test('should be defined', () => {
|
||||
|
@ -283,11 +284,12 @@ describe('LocalStorage', () => {
|
|||
const pkgName = 'add-update-versions-test-1';
|
||||
const version = '1.0.2';
|
||||
let _storage;
|
||||
beforeEach((done) => {
|
||||
beforeEach(async (done) => {
|
||||
class MockLocalStorage extends LocalStorage {}
|
||||
// @ts-ignore
|
||||
MockLocalStorage.prototype._writePackage = jest.fn(LocalStorage.prototype._writePackage);
|
||||
_storage = getStorage(MockLocalStorage);
|
||||
await _storage.init();
|
||||
rimRaf(path.join(configExample().storage, pkgName), async () => {
|
||||
await addPackageToStore(pkgName, generatePackageTemplate(pkgName));
|
||||
await addNewVersion(pkgName, '1.0.1');
|
||||
|
|
|
@ -31,7 +31,7 @@ const packages = [
|
|||
},
|
||||
];
|
||||
|
||||
describe('search', () => {
|
||||
describe.skip('search', () => {
|
||||
beforeAll(async function () {
|
||||
const config = new Config(configExample());
|
||||
const storage = new Storage(config);
|
||||
|
|
|
@ -1,13 +1,6 @@
|
|||
import {
|
||||
IBasicStorage,
|
||||
Callback,
|
||||
RemoteUser,
|
||||
Config,
|
||||
Logger,
|
||||
IPluginStorage,
|
||||
Package,
|
||||
ITokenActions,
|
||||
} from '@verdaccio/types';
|
||||
// REMOVE and VERIFY where these types are used and remove the package
|
||||
|
||||
import { Callback, RemoteUser, Package } from '@verdaccio/types';
|
||||
|
||||
export type JWTPayload = RemoteUser & {
|
||||
password?: string;
|
||||
|
@ -34,12 +27,6 @@ export interface Profile {
|
|||
fullname: string;
|
||||
}
|
||||
|
||||
export interface IStorage extends IBasicStorage<Config>, ITokenActions {
|
||||
config: Config;
|
||||
storagePlugin: IPluginStorage<Config>;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* @property { string | number | Styles } [ruleOrSelector]
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue