@verdaccio/store 6.0.0-6-next.21 → 6.0.0-6-next.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/CHANGELOG.md +92 -0
  2. package/build/index.d.ts +5 -4
  3. package/build/index.js +31 -15
  4. package/build/index.js.map +1 -1
  5. package/build/lib/TransFormResults.d.ts +15 -0
  6. package/build/lib/TransFormResults.js +61 -0
  7. package/build/lib/TransFormResults.js.map +1 -0
  8. package/build/lib/search-utils.d.ts +4 -0
  9. package/build/lib/search-utils.js +59 -0
  10. package/build/lib/search-utils.js.map +1 -0
  11. package/build/lib/star-utils.d.ts +14 -0
  12. package/build/{star-utils.js → lib/star-utils.js} +16 -2
  13. package/build/lib/star-utils.js.map +1 -0
  14. package/build/lib/storage-utils.d.ts +82 -0
  15. package/build/{storage-utils.js → lib/storage-utils.js} +173 -60
  16. package/build/lib/storage-utils.js.map +1 -0
  17. package/build/lib/uplink-util.d.ts +10 -0
  18. package/build/lib/uplink-util.js +49 -0
  19. package/build/lib/uplink-util.js.map +1 -0
  20. package/build/lib/versions-utils.d.ts +28 -0
  21. package/build/lib/versions-utils.js +104 -0
  22. package/build/lib/versions-utils.js.map +1 -0
  23. package/build/local-storage.d.ts +3 -165
  24. package/build/local-storage.js +6 -1166
  25. package/build/local-storage.js.map +1 -1
  26. package/build/storage.d.ts +242 -86
  27. package/build/storage.js +1681 -548
  28. package/build/storage.js.map +1 -1
  29. package/build/type.d.ts +42 -19
  30. package/build/type.js.map +1 -1
  31. package/jest.config.js +8 -2
  32. package/package.json +71 -71
  33. package/src/index.ts +5 -4
  34. package/src/lib/TransFormResults.ts +42 -0
  35. package/src/lib/search-utils.ts +50 -0
  36. package/src/{star-utils.ts → lib/star-utils.ts} +16 -5
  37. package/src/lib/storage-utils.ts +362 -0
  38. package/src/lib/uplink-util.ts +41 -0
  39. package/src/lib/versions-utils.ts +87 -0
  40. package/src/local-storage.ts +7 -1217
  41. package/src/storage.ts +1683 -635
  42. package/src/type.ts +47 -21
  43. package/test/fixtures/config/getTarballNext-getupstream.yaml +17 -0
  44. package/test/fixtures/config/syncDoubleUplinksMetadata.yaml +25 -0
  45. package/test/fixtures/config/syncNoUplinksMetadata.yaml +17 -0
  46. package/test/fixtures/config/syncSingleUplinksMetadata.yaml +21 -0
  47. package/test/fixtures/config/updateManifest-1.yaml +16 -0
  48. package/test/fixtures/manifests/foo-npmjs.json +253 -0
  49. package/test/fixtures/manifests/foo-verdaccio.json +253 -0
  50. package/test/fixtures/tarball.tgz +0 -0
  51. package/test/helpers.ts +22 -0
  52. package/test/local-store.__disabled__.ts +553 -0
  53. package/test/search.spec.ts +4 -5
  54. package/test/star.spec.ts +31 -0
  55. package/test/storage-utils.spec.ts +129 -42
  56. package/test/storage.spec.ts +943 -33
  57. package/test/versions.spec.ts +109 -0
  58. package/tsconfig.json +6 -3
  59. package/build/search.d.ts +0 -35
  60. package/build/search.js +0 -198
  61. package/build/search.js.map +0 -1
  62. package/build/star-utils.d.ts +0 -9
  63. package/build/star-utils.js.map +0 -1
  64. package/build/storage-utils.d.ts +0 -39
  65. package/build/storage-utils.js.map +0 -1
  66. package/build/uplink-util.d.ts +0 -7
  67. package/build/uplink-util.js +0 -38
  68. package/build/uplink-util.js.map +0 -1
  69. package/src/search.ts +0 -175
  70. package/src/storage-utils.ts +0 -263
  71. package/src/uplink-util.ts +0 -32
  72. package/test/local-storage.spec.ts +0 -583
@@ -1,45 +1,11 @@
1
1
  import assert from 'assert';
2
2
  import buildDebug from 'debug';
3
3
  import _ from 'lodash';
4
- import { PassThrough } from 'stream';
5
- import UrlNode from 'url';
6
4
 
7
- import { errorUtils, pkgUtils, pluginUtils, searchUtils, validatioUtils } from '@verdaccio/core';
8
- import { API_ERROR, DIST_TAGS, HTTP_STATUS, SUPPORT_ERRORS, USERS } from '@verdaccio/core';
9
- import { VerdaccioError } from '@verdaccio/core';
5
+ import { errorUtils, pluginUtils } from '@verdaccio/core';
10
6
  import { loadPlugin } from '@verdaccio/loaders';
11
7
  import LocalDatabase from '@verdaccio/local-storage';
12
- import { ReadTarball, UploadTarball } from '@verdaccio/streams';
13
- import {
14
- Author,
15
- Callback,
16
- CallbackAction,
17
- Config,
18
- DistFile,
19
- IPackageStorage,
20
- IReadTarball,
21
- IUploadTarball,
22
- Logger,
23
- MergeTags,
24
- Package,
25
- StorageUpdateCallback,
26
- StringValue,
27
- Token,
28
- TokenFilter,
29
- Version,
30
- } from '@verdaccio/types';
31
- import { getLatestVersion, isObject } from '@verdaccio/utils';
32
- import { createTarballHash, normalizeContributors } from '@verdaccio/utils';
33
-
34
- import {
35
- STORAGE,
36
- cleanUpReadme,
37
- generatePackageTemplate,
38
- generateRevision,
39
- getLatestReadme,
40
- normalizePackage,
41
- tagVersion,
42
- } from './storage-utils';
8
+ import { Config, Logger } from '@verdaccio/types';
43
9
 
44
10
  const debug = buildDebug('verdaccio:storage:local');
45
11
 
@@ -47,42 +13,6 @@ export const noSuchFile = 'ENOENT';
47
13
  export const resourceNotAvailable = 'EAGAIN';
48
14
  export type IPluginStorage = pluginUtils.IPluginStorage<Config>;
49
15
 
50
- export function normalizeSearchPackage(
51
- pkg: Package,
52
- searchItem: searchUtils.SearchItem
53
- ): searchUtils.SearchPackageBody {
54
- const latest = pkgUtils.getLatest(pkg);
55
- const version: Version = pkg.versions[latest];
56
- const result: searchUtils.SearchPackageBody = {
57
- name: version.name,
58
- scope: '',
59
- description: version.description,
60
- version: latest,
61
- keywords: version.keywords,
62
- date: pkg.time[latest],
63
- // FIXME: type
64
- author: version.author as any,
65
- // FIXME: not possible fill this out from a private package
66
- publisher: {},
67
- // FIXME: type
68
- maintainers: version.maintainers as any,
69
- links: {
70
- npm: '',
71
- homepage: version.homepage,
72
- repository: version.repository,
73
- bugs: version.bugs,
74
- },
75
- };
76
-
77
- if (typeof searchItem.package.scoped === 'string') {
78
- result.scope = searchItem.package.scoped;
79
- }
80
-
81
- return result;
82
- }
83
-
84
- export const PROTO_NAME = '__proto__';
85
-
86
16
  /**
87
17
  * Implements Storage interface (same for storage.js, local-storage.js, up-storage.js).
88
18
  */
@@ -111,1122 +41,12 @@ class LocalStorage {
111
41
  return;
112
42
  }
113
43
 
114
- public addPackage(name: string, pkg: Package, callback: Callback): void {
115
- debug(`creating a package for`, name);
116
- const storage: any = this._getLocalStorage(name);
117
-
118
- if (_.isNil(storage)) {
119
- debug(`storage is missing for %o package cannot be added`, name);
120
- return callback(errorUtils.getNotFound('this package cannot be added'));
121
- }
122
-
123
- storage.createPackage(name, generatePackageTemplate(name), (err) => {
124
- if (
125
- _.isNull(err) === false &&
126
- (err.code === STORAGE.FILE_EXIST_ERROR || err.code === HTTP_STATUS.CONFLICT)
127
- ) {
128
- debug(`error on creating a package for %o with error %o`, name, err.message);
129
- return callback(errorUtils.getConflict());
130
- }
131
-
132
- const latest = getLatestVersion(pkg);
133
- if (_.isNil(latest) === false && pkg.versions[latest]) {
134
- debug('latest version found %o for %o', latest, name);
135
- return callback(null, pkg.versions[latest]);
136
- }
137
-
138
- debug('no latest version found for %o', name);
139
- return callback();
140
- });
141
- }
142
-
143
- /**
144
- * Remove package with all it contents.
145
- */
146
- public async removePackage(name: string): Promise<void> {
147
- debug('remove package %s', name);
148
- const storage: any = this._getLocalStorage(name);
149
-
150
- if (_.isNil(storage)) {
151
- throw errorUtils.getNotFound();
152
- }
153
-
154
- return new Promise((resolve, reject) => {
155
- storage.readPackage(name, async (err, data: Package): Promise<void> => {
156
- if (_.isNil(err) === false) {
157
- if (err.code === STORAGE.NO_SUCH_FILE_ERROR || err.code === HTTP_STATUS.NOT_FOUND) {
158
- debug(`error on not found %o with error %o`, name, err.message);
159
- return reject(errorUtils.getNotFound());
160
- }
161
- return reject(err);
162
- }
163
-
164
- data = normalizePackage(data);
165
-
166
- try {
167
- await this.storagePlugin.remove(name);
168
- // remove each attachment
169
- const attachments = Object.keys(data._attachments);
170
- debug('attachments to remove %s', attachments?.length);
171
- for (let attachment of attachments) {
172
- debug('remove attachment %s', attachment);
173
- await storage.deletePackage(attachment);
174
- }
175
- // remove package.json
176
- debug('remove package.json');
177
- await storage.deletePackage(STORAGE.PACKAGE_FILE_NAME);
178
- // remove folder
179
- debug('remove package folder');
180
- await storage.removePackage();
181
- resolve();
182
- } catch (err: any) {
183
- this.logger.error({ err }, 'removed package has failed @{err.message}');
184
- throw errorUtils.getBadData(err.message);
185
- }
186
- });
187
- });
188
- }
189
-
190
- /**
191
- * Synchronize remote package info with the local one
192
- * @param {*} name
193
- * @param {*} packageInfo
194
- * @param {*} callback
195
- */
196
- public updateVersions(name: string, packageInfo: Package, callback: Callback): void {
197
- debug(`updating versions for package %o`, name);
198
- this._readCreatePackage(name, (err, packageLocalJson): void => {
199
- if (err) {
200
- return callback(err);
201
- }
202
-
203
- let change = false;
204
- // updating readme
205
- packageLocalJson.readme = getLatestReadme(packageInfo);
206
- if (packageInfo.readme !== packageLocalJson.readme) {
207
- change = true;
208
- }
209
-
210
- debug('update versions');
211
- for (const versionId in packageInfo.versions) {
212
- if (_.isNil(packageLocalJson.versions[versionId])) {
213
- let version = packageInfo.versions[versionId];
214
-
215
- // we don't keep readme for package versions,
216
- // only one readme per package
217
- version = cleanUpReadme(version);
218
- debug('clean up readme for %o', versionId);
219
- version.contributors = normalizeContributors(version.contributors as Author[]);
220
-
221
- change = true;
222
- packageLocalJson.versions[versionId] = version;
223
-
224
- if (version?.dist?.tarball) {
225
- const urlObject: any = UrlNode.parse(version.dist.tarball);
226
- const filename = urlObject.pathname.replace(/^.*\//, '');
227
-
228
- // we do NOT overwrite any existing records
229
- if (_.isNil(packageLocalJson._distfiles[filename])) {
230
- const hash: DistFile = (packageLocalJson._distfiles[filename] = {
231
- url: version.dist.tarball,
232
- sha: version.dist.shasum,
233
- });
234
- const upLink: string = version[Symbol.for('__verdaccio_uplink')];
235
-
236
- if (_.isNil(upLink) === false) {
237
- this._updateUplinkToRemoteProtocol(hash, upLink);
238
- }
239
- }
240
- }
241
- }
242
- }
243
-
244
- debug('update dist-tags');
245
- for (const tag in packageInfo[DIST_TAGS]) {
246
- if (
247
- !packageLocalJson[DIST_TAGS][tag] ||
248
- packageLocalJson[DIST_TAGS][tag] !== packageInfo[DIST_TAGS][tag]
249
- ) {
250
- change = true;
251
- packageLocalJson[DIST_TAGS][tag] = packageInfo[DIST_TAGS][tag];
252
- }
253
- }
254
-
255
- for (const up in packageInfo._uplinks) {
256
- if (Object.prototype.hasOwnProperty.call(packageInfo._uplinks, up)) {
257
- const need_change =
258
- !isObject(packageLocalJson._uplinks[up]) ||
259
- packageInfo._uplinks[up].etag !== packageLocalJson._uplinks[up].etag ||
260
- packageInfo._uplinks[up].fetched !== packageLocalJson._uplinks[up].fetched;
261
-
262
- if (need_change) {
263
- change = true;
264
- packageLocalJson._uplinks[up] = packageInfo._uplinks[up];
265
- }
266
- }
267
- }
268
-
269
- debug('update time');
270
- if ('time' in packageInfo && !_.isEqual(packageLocalJson.time, packageInfo.time)) {
271
- packageLocalJson.time = packageInfo.time;
272
- change = true;
273
- }
274
-
275
- if (change) {
276
- debug('updating package info %o', name);
277
- this._writePackage(name, packageLocalJson, function (err): void {
278
- callback(err, packageLocalJson);
279
- });
280
- } else {
281
- callback(null, packageLocalJson);
282
- }
283
- });
284
- }
285
-
286
- /**
287
- * Add a new version to a previous local package.
288
- * @param {*} name
289
- * @param {*} version
290
- * @param {*} metadata
291
- * @param {*} tag
292
- * @param {*} callback
293
- */
294
- public addVersion(
295
- name: string,
296
- version: string,
297
- metadata: Version,
298
- tag: StringValue,
299
- callback: Callback
300
- ): void {
301
- debug(`add version %s package for %s`, version, name);
302
- this._updatePackage(
303
- name,
304
- async (data, cb: Callback): Promise<void> => {
305
- debug('%s package is being updated', name);
306
- // keep only one readme per package
307
- data.readme = metadata.readme;
308
- debug('%s` readme mutated', name);
309
- // TODO: lodash remove
310
- metadata = cleanUpReadme(metadata);
311
- metadata.contributors = normalizeContributors(metadata.contributors as Author[]);
312
- debug('%s` contributors normalized', name);
313
- const hasVersion = data.versions[version] != null;
314
- if (hasVersion) {
315
- debug('%s version %s already exists', name, version);
316
- return cb(errorUtils.getConflict());
317
- }
318
-
319
- // if uploaded tarball has a different shasum, it's very likely that we
320
- // have some kind of error
321
- if (validatioUtils.isObject(metadata.dist) && _.isString(metadata.dist.tarball)) {
322
- const tarball = metadata.dist.tarball.replace(/.*\//, '');
323
-
324
- if (validatioUtils.isObject(data._attachments[tarball])) {
325
- if (
326
- _.isNil(data._attachments[tarball].shasum) === false &&
327
- _.isNil(metadata.dist.shasum) === false
328
- ) {
329
- if (data._attachments[tarball].shasum != metadata.dist.shasum) {
330
- const errorMessage =
331
- `shasum error, ` +
332
- `${data._attachments[tarball].shasum} != ${metadata.dist.shasum}`;
333
- return cb(errorUtils.getBadRequest(errorMessage));
334
- }
335
- }
336
-
337
- const currentDate = new Date().toISOString();
338
-
339
- // some old storage do not have this field #740
340
- if (_.isNil(data.time)) {
341
- data.time = {};
342
- }
343
-
344
- data.time['modified'] = currentDate;
345
-
346
- if ('created' in data.time === false) {
347
- data.time.created = currentDate;
348
- }
349
-
350
- data.time[version] = currentDate;
351
- data._attachments[tarball].version = version;
352
- }
353
- }
354
-
355
- data.versions[version] = metadata;
356
- tagVersion(data, version, tag);
357
-
358
- try {
359
- debug('%s` add on database', name);
360
- await this.storagePlugin.add(name);
361
- cb();
362
- } catch (err: any) {
363
- cb(errorUtils.getBadData(err.message));
364
- }
365
- },
366
- callback
367
- );
368
- }
369
-
370
- public async addVersionNext(
371
- name: string,
372
- version: string,
373
- metadata: Version,
374
- tag: StringValue
375
- ): Promise<void> {
376
- debug(`add version %s package for %s`, version, name);
377
- await this.updatePackageNext(name, async (data: Package): Promise<Package> => {
378
- debug('%s package is being updated', name);
379
- // keep only one readme per package
380
- data.readme = metadata.readme;
381
- debug('%s` readme mutated', name);
382
- // TODO: lodash remove
383
- metadata = cleanUpReadme(metadata);
384
- metadata.contributors = normalizeContributors(metadata.contributors as Author[]);
385
- debug('%s` contributors normalized', name);
386
- const hasVersion = data.versions[version] != null;
387
- if (hasVersion) {
388
- debug('%s version %s already exists', name, version);
389
- throw errorUtils.getConflict();
390
- }
391
-
392
- // if uploaded tarball has a different shasum, it's very likely that we
393
- // have some kind of error
394
- if (validatioUtils.isObject(metadata.dist) && _.isString(metadata.dist.tarball)) {
395
- const tarball = metadata.dist.tarball.replace(/.*\//, '');
396
-
397
- if (validatioUtils.isObject(data._attachments[tarball])) {
398
- if (
399
- _.isNil(data._attachments[tarball].shasum) === false &&
400
- _.isNil(metadata.dist.shasum) === false
401
- ) {
402
- if (data._attachments[tarball].shasum != metadata.dist.shasum) {
403
- const errorMessage =
404
- `shasum error, ` +
405
- `${data._attachments[tarball].shasum} != ${metadata.dist.shasum}`;
406
- throw errorUtils.getBadRequest(errorMessage);
407
- }
408
- }
409
-
410
- const currentDate = new Date().toISOString();
411
-
412
- // some old storage do not have this field #740
413
- if (_.isNil(data.time)) {
414
- data.time = {};
415
- }
416
-
417
- data.time['modified'] = currentDate;
418
-
419
- if ('created' in data.time === false) {
420
- data.time.created = currentDate;
421
- }
422
-
423
- data.time[version] = currentDate;
424
- data._attachments[tarball].version = version;
425
- }
426
- }
427
-
428
- data.versions[version] = metadata;
429
- tagVersion(data, version, tag);
430
-
431
- try {
432
- debug('%s` add on database', name);
433
- await this.storagePlugin.add(name);
434
- } catch (err: any) {
435
- throw errorUtils.getBadData(err.message);
436
- }
437
- return data;
438
- });
439
-
440
- // this._updatePackage(
441
- // name,
442
- // async (data, cb: Callback): Promise<void> => {
443
- // debug('%s package is being updated', name);
444
- // // keep only one readme per package
445
- // data.readme = metadata.readme;
446
- // debug('%s` readme mutated', name);
447
- // // TODO: lodash remove
448
- // metadata = cleanUpReadme(metadata);
449
- // metadata.contributors = normalizeContributors(metadata.contributors as Author[]);
450
- // debug('%s` contributors normalized', name);
451
- // const hasVersion = data.versions[version] != null;
452
- // if (hasVersion) {
453
- // debug('%s version %s already exists', name, version);
454
- // return cb(errorUtils.getConflict());
455
- // }
456
-
457
- // // if uploaded tarball has a different shasum, it's very likely that we
458
- // // have some kind of error
459
- // if (validatioUtils.isObject(metadata.dist) && _.isString(metadata.dist.tarball)) {
460
- // const tarball = metadata.dist.tarball.replace(/.*\//, '');
461
-
462
- // if (validatioUtils.isObject(data._attachments[tarball])) {
463
- // if (
464
- // _.isNil(data._attachments[tarball].shasum) === false &&
465
- // _.isNil(metadata.dist.shasum) === false
466
- // ) {
467
- // if (data._attachments[tarball].shasum != metadata.dist.shasum) {
468
- // const errorMessage =
469
- // `shasum error, ` +
470
- // `${data._attachments[tarball].shasum} != ${metadata.dist.shasum}`;
471
- // return cb(errorUtils.getBadRequest(errorMessage));
472
- // }
473
- // }
474
-
475
- // const currentDate = new Date().toISOString();
476
-
477
- // // some old storage do not have this field #740
478
- // if (_.isNil(data.time)) {
479
- // data.time = {};
480
- // }
481
-
482
- // data.time['modified'] = currentDate;
483
-
484
- // if ('created' in data.time === false) {
485
- // data.time.created = currentDate;
486
- // }
487
-
488
- // data.time[version] = currentDate;
489
- // data._attachments[tarball].version = version;
490
- // }
491
- // }
492
-
493
- // data.versions[version] = metadata;
494
- // tagVersion(data, version, tag);
495
-
496
- // try {
497
- // debug('%s` add on database', name);
498
- // await this.storagePlugin.add(name);
499
- // cb();
500
- // } catch (err: any) {
501
- // cb(errorUtils.getBadData(err.message));
502
- // }
503
- // },
504
- // callback
505
- // );
506
- }
507
-
508
- /**
509
- * Merge a new list of tags for a local packages with the existing one.
510
- * @param {*} pkgName
511
- * @param {*} tags
512
- * @param {*} callback
513
- */
514
- public mergeTags(pkgName: string, tags: MergeTags, callback: Callback): void {
515
- debug(`merge tags for`, pkgName);
516
- this._updatePackage(
517
- pkgName,
518
- (data, cb): void => {
519
- /* eslint guard-for-in: 0 */
520
- for (const tag in tags) {
521
- // this handle dist-tag rm command
522
- if (_.isNull(tags[tag])) {
523
- delete data[DIST_TAGS][tag];
524
- continue;
525
- }
526
-
527
- if (_.isNil(data.versions[tags[tag]])) {
528
- return cb(errorUtils.getNotFound(API_ERROR.VERSION_NOT_EXIST));
529
- }
530
- const version: string = tags[tag];
531
- tagVersion(data, version, tag);
532
- }
533
- cb(null);
534
- },
535
- callback
536
- );
537
- }
538
-
539
- /**
540
- * Update the package metadata, tags and attachments (tarballs).
541
- * Note: Currently supports unpublishing and deprecation.
542
- * @param {*} name
543
- * @param {*} incomingPkg
544
- * @param {*} revision
545
- * @param {*} callback
546
- * @return {Function}
547
- */
548
- public changePackage(
549
- name: string,
550
- incomingPkg: Package,
551
- revision: string | undefined,
552
- callback: Callback
553
- ): void {
554
- debug(`change package tags for %o revision %s`, name, revision);
555
- if (
556
- !validatioUtils.isObject(incomingPkg.versions) ||
557
- !validatioUtils.isObject(incomingPkg[DIST_TAGS])
558
- ) {
559
- debug(`change package bad data for %o`, name);
560
- return callback(errorUtils.getBadData());
561
- }
562
-
563
- debug(`change package udapting package for %o`, name);
564
- this._updatePackage(
565
- name,
566
- (localData: Package, cb: CallbackAction): void => {
567
- for (const version in localData.versions) {
568
- const incomingVersion = incomingPkg.versions[version];
569
- if (_.isNil(incomingVersion)) {
570
- this.logger.info({ name: name, version: version }, 'unpublishing @{name}@@{version}');
571
-
572
- // FIXME: I prefer return a new object rather mutate the metadata
573
- delete localData.versions[version];
574
- delete localData.time![version];
575
-
576
- for (const file in localData._attachments) {
577
- if (localData._attachments[file].version === version) {
578
- delete localData._attachments[file].version;
579
- }
580
- }
581
- } else if (Object.prototype.hasOwnProperty.call(incomingVersion, 'deprecated')) {
582
- const incomingDeprecated = incomingVersion.deprecated;
583
- if (incomingDeprecated != localData.versions[version].deprecated) {
584
- if (!incomingDeprecated) {
585
- this.logger.info(
586
- { name: name, version: version },
587
- 'undeprecating @{name}@@{version}'
588
- );
589
- delete localData.versions[version].deprecated;
590
- } else {
591
- this.logger.info(
592
- { name: name, version: version },
593
- 'deprecating @{name}@@{version}'
594
- );
595
- localData.versions[version].deprecated = incomingDeprecated;
596
- }
597
- localData.time!.modified = new Date().toISOString();
598
- }
599
- }
600
- }
601
-
602
- localData[USERS] = incomingPkg[USERS];
603
- localData[DIST_TAGS] = incomingPkg[DIST_TAGS];
604
- cb(null);
605
- },
606
- function (err): void {
607
- if (err) {
608
- return callback(err);
609
- }
610
- callback();
611
- }
612
- );
613
- }
614
-
615
- /**
616
- * Update the package metadata, tags and attachments (tarballs).
617
- * Note: Currently supports unpublishing and deprecation.
618
- * @param {*} name
619
- * @param {*} incomingPkg
620
- * @param {*} revision
621
- * @param {*} callback
622
- * @return {Function}
623
- */
624
- public async changePackageNext(
625
- name: string,
626
- incomingPkg: Package,
627
- revision: string | undefined
628
- ): Promise<void> {
629
- debug(`change package tags for %o revision %s`, name, revision);
630
- if (
631
- !validatioUtils.isObject(incomingPkg.versions) ||
632
- !validatioUtils.isObject(incomingPkg[DIST_TAGS])
633
- ) {
634
- debug(`change package bad data for %o`, name);
635
- throw errorUtils.getBadData();
636
- }
637
-
638
- debug(`change package udapting package for %o`, name);
639
- await this.updatePackageNext(name, async (localData: Package): Promise<Package> => {
640
- for (const version in localData.versions) {
641
- const incomingVersion = incomingPkg.versions[version];
642
- if (_.isNil(incomingVersion)) {
643
- this.logger.info({ name: name, version: version }, 'unpublishing @{name}@@{version}');
644
-
645
- // FIXME: I prefer return a new object rather mutate the metadata
646
- delete localData.versions[version];
647
- delete localData.time![version];
648
-
649
- for (const file in localData._attachments) {
650
- if (localData._attachments[file].version === version) {
651
- delete localData._attachments[file].version;
652
- }
653
- }
654
- } else if (Object.prototype.hasOwnProperty.call(incomingVersion, 'deprecated')) {
655
- const incomingDeprecated = incomingVersion.deprecated;
656
- if (incomingDeprecated != localData.versions[version].deprecated) {
657
- if (!incomingDeprecated) {
658
- this.logger.info(
659
- { name: name, version: version },
660
- 'undeprecating @{name}@@{version}'
661
- );
662
- delete localData.versions[version].deprecated;
663
- } else {
664
- this.logger.info({ name: name, version: version }, 'deprecating @{name}@@{version}');
665
- localData.versions[version].deprecated = incomingDeprecated;
666
- }
667
- localData.time!.modified = new Date().toISOString();
668
- }
669
- }
670
- }
671
-
672
- localData[USERS] = incomingPkg[USERS];
673
- localData[DIST_TAGS] = incomingPkg[DIST_TAGS];
674
- return localData;
675
- });
676
- }
677
-
678
- /**
679
- * Remove a tarball.
680
- * @param {*} name
681
- * @param {*} filename
682
- * @param {*} revision
683
- * @param {*} callback
684
- */
685
- public removeTarball(
686
- name: string,
687
- filename: string,
688
- revision: string,
689
- callback: CallbackAction
690
- ): void {
691
- debug('remove tarball %s for %s', filename, name);
692
- assert(validatioUtils.validateName(filename));
693
- this._updatePackage(
694
- name,
695
- (data, cb): void => {
696
- if (data._attachments[filename]) {
697
- // TODO: avoid using delete
698
- delete data._attachments[filename];
699
- cb(null);
700
- } else {
701
- cb(errorUtils.getNotFound('no such file available'));
702
- }
703
- },
704
- (err: VerdaccioError) => {
705
- if (err) {
706
- this.logger.error({ err }, 'remove tarball error @{err.message}');
707
- return callback(err);
708
- }
709
- const storage = this._getLocalStorage(name);
710
-
711
- if (storage) {
712
- debug('removing %s from storage', filename);
713
- storage
714
- .deletePackage(filename)
715
- .then((): void => {
716
- debug('package %s removed', filename);
717
- return callback(null);
718
- })
719
- .catch((err) => {
720
- this.logger.error({ err }, 'error removing %s from storage');
721
- return callback(null);
722
- });
723
- } else {
724
- callback(errorUtils.getInternalError());
725
- }
726
- }
727
- );
728
- }
729
-
730
- /**
731
- * Add a tarball.
732
- * @param {String} name
733
- * @param {String} filename
734
- * @return {Stream}
735
- */
736
- public addTarball(name: string, filename: string): IUploadTarball {
737
- debug(`add a tarball for %o`, name);
738
- assert(validatioUtils.validateName(filename));
739
-
740
- let length = 0;
741
- const shaOneHash = createTarballHash();
742
- const uploadStream: IUploadTarball = new UploadTarball({});
743
- const _transform = uploadStream._transform;
744
- const storage = this._getLocalStorage(name);
745
-
746
- uploadStream.abort = function (): void {};
747
- uploadStream.done = function (): void {};
748
-
749
- uploadStream._transform = function (data, ...args): void {
750
- shaOneHash.update(data);
751
- // measure the length for validation reasons
752
- length += data.length;
753
- const appliedData = [data, ...args];
754
- // FIXME: not sure about this approach, tsc complains
755
- // @ts-ignore
756
- _transform.apply(uploadStream, appliedData);
757
- };
758
-
759
- if (name === PROTO_NAME) {
760
- process.nextTick((): void => {
761
- uploadStream.emit('error', errorUtils.getForbidden());
762
- });
763
- return uploadStream;
764
- }
765
-
766
- // FIXME: this condition will never met, storage is always defined
767
- if (!storage) {
768
- process.nextTick((): void => {
769
- uploadStream.emit('error', "can't upload this package");
770
- });
771
- return uploadStream;
772
- }
773
-
774
- const writeStream: IUploadTarball = storage.writeTarball(filename);
775
-
776
- writeStream.on('error', (err) => {
777
- // @ts-ignore
778
- if (err.code === STORAGE.FILE_EXIST_ERROR || err.code === HTTP_STATUS.CONFLICT) {
779
- uploadStream.emit('error', errorUtils.getConflict());
780
- uploadStream.abort();
781
- // @ts-ignore
782
- } else if (err.code === STORAGE.NO_SUCH_FILE_ERROR || err.code === HTTP_STATUS.NOT_FOUND) {
783
- // check if package exists to throw an appropriate message
784
- this.getPackageMetadata(name, function (_err: VerdaccioError): void {
785
- if (_err) {
786
- uploadStream.emit('error', _err);
787
- } else {
788
- uploadStream.emit('error', err);
789
- }
790
- });
791
- } else {
792
- uploadStream.emit('error', err);
793
- }
794
- });
795
-
796
- writeStream.on('open', function (): void {
797
- // re-emitting open because it's handled in storage.js
798
- uploadStream.emit('open');
799
- });
800
-
801
- writeStream.on('success', (): void => {
802
- this._updatePackage(
803
- name,
804
- function updater(data, cb): void {
805
- // FUTURE: move this to tarballUtils
806
- data._attachments[filename] = {
807
- shasum: shaOneHash.digest('hex'),
808
- };
809
- cb(null);
810
- },
811
- function (err): void {
812
- if (err) {
813
- // FIXME: if the update package fails, remove tarball to avoid left
814
- // orphan tarballs
815
- uploadStream.emit('error', err);
816
- } else {
817
- uploadStream.emit('success');
818
- }
819
- }
820
- );
821
- });
822
-
823
- uploadStream.abort = function (): void {
824
- writeStream.abort();
825
- };
826
-
827
- uploadStream.done = function (): void {
828
- if (!length) {
829
- uploadStream.emit('error', errorUtils.getBadData('refusing to accept zero-length file'));
830
- writeStream.abort();
831
- } else {
832
- writeStream.done();
833
- }
834
- };
835
-
836
- uploadStream.pipe(writeStream);
837
-
838
- return uploadStream;
839
- }
840
-
841
- /**
842
- * Get a tarball.
843
- * @param {*} name
844
- * @param {*} filename
845
- * @return {ReadTarball}
846
- */
847
- public getTarball(name: string, filename: string): IReadTarball {
848
- assert(validatioUtils.validateName(filename));
849
-
850
- const storage: IPackageStorage = this._getLocalStorage(name);
851
-
852
- if (_.isNil(storage)) {
853
- return this._createFailureStreamResponse();
854
- }
855
-
856
- return this._streamSuccessReadTarBall(storage, filename);
857
- }
858
-
859
- /**
860
- * Return a stream that emits a read failure.
861
- * @private
862
- * @return {ReadTarball}
863
- */
864
- private _createFailureStreamResponse(): IReadTarball {
865
- const stream: IReadTarball = new ReadTarball({});
866
-
867
- process.nextTick((): void => {
868
- stream.emit('error', errorUtils.getNotFound('no such file available'));
869
- });
870
- return stream;
871
- }
872
-
873
- /**
874
- * Return a stream that emits the tarball data
875
- * @param {Object} storage
876
- * @param {String} filename
877
- * @private
878
- * @return {ReadTarball}
879
- */
880
- private _streamSuccessReadTarBall(storage: any, filename: string): IReadTarball {
881
- const stream: IReadTarball = new ReadTarball({});
882
- const readTarballStream = storage.readTarball(filename);
883
- const e404 = errorUtils.getNotFound;
884
-
885
- stream.abort = function (): void {
886
- if (_.isNil(readTarballStream) === false) {
887
- readTarballStream.abort();
888
- }
889
- };
890
-
891
- readTarballStream.on('error', function (err) {
892
- // @ts-ignore
893
- if (err.code === STORAGE.NO_SUCH_FILE_ERROR || err.code === HTTP_STATUS.NOT_FOUND) {
894
- stream.emit('error', e404('no such file available'));
895
- } else {
896
- stream.emit('error', err);
897
- }
898
- });
899
-
900
- readTarballStream.on('content-length', function (content): void {
901
- stream.emit('content-length', content);
902
- });
903
-
904
- readTarballStream.on('open', function (): void {
905
- // re-emitting open because it's handled in storage.js
906
- stream.emit('open');
907
- readTarballStream.pipe(stream);
908
- });
909
-
910
- return stream;
911
- }
912
-
913
- public async getPackageMetadataNext(name: string): Promise<Package> {
914
- const storage: IPackageStorage = this._getLocalStorage(name);
915
- debug('get package metadata for %o', name);
916
- if (typeof storage === 'undefined') {
917
- // TODO: this might be a better an error to throw
918
- // if storage is not there cannot be 404.
919
- throw errorUtils.getNotFound();
920
- }
921
-
922
- return await this._readPackageNext(name, storage);
923
- }
924
-
925
- /**
926
- * Retrieve a package by name.
927
- * @param {*} name
928
- * @param {*} callback
929
- * @return {Function}
930
- * @deprecated
931
- */
932
- public getPackageMetadata(name: string, callback: Callback = (): void => {}): void {
933
- const storage: IPackageStorage = this._getLocalStorage(name);
934
- debug('get package metadata for %o', name);
935
- if (typeof storage === 'undefined') {
936
- return callback(errorUtils.getNotFound());
937
- }
938
-
939
- this._readPackage(name, storage, callback);
940
- }
941
-
942
- public async search(searchStream: PassThrough, query: searchUtils.SearchQuery): Promise<void> {
943
- debug('search on each package');
944
- this.logger.info(
945
- { t: query.text, q: query.quality, p: query.popularity, m: query.maintenance, s: query.size },
946
- 'search by text @{t}| maintenance @{m}| quality @{q}| popularity @{p}'
947
- );
948
- const getMetadata = (searchItem: searchUtils.SearchItem) => {
949
- return new Promise((resolve, reject) => {
950
- this.getPackageMetadata(
951
- searchItem?.package?.name,
952
- (err: VerdaccioError, pkg: Package): void => {
953
- if (err) {
954
- this.logger.error(
955
- { err, pkgName: searchItem?.package?.name },
956
- 'error on load package @{pkgName} metaadata @{err.message}'
957
- );
958
- reject(err);
959
- }
960
-
961
- if (_.isEmpty(pkg?.versions)) {
962
- return resolve({});
963
- }
964
-
965
- const searchPackage = normalizeSearchPackage(pkg, searchItem);
966
- const searchPackageItem: searchUtils.SearchPackageItem = {
967
- package: searchPackage,
968
- score: searchItem.score,
969
- verdaccioPkgCached: searchItem.verdaccioPkgCached,
970
- verdaccioPrivate: searchItem.verdaccioPrivate,
971
- flags: searchItem?.flags,
972
- // FUTURE: find a better way to calculate the score
973
- searchScore: 1,
974
- };
975
- debug('push to stream %o', searchItem?.package?.name);
976
- resolve(searchPackageItem);
977
- }
978
- );
979
- });
980
- };
981
-
982
- if (typeof this.storagePlugin.search === 'undefined') {
983
- this.logger.info('plugin search not implemented yet');
984
- searchStream.end();
985
- } else {
986
- debug('search on each package by plugin');
987
- const items = await this.storagePlugin.search(query);
988
- try {
989
- for (const item of items) {
990
- const metadata = await getMetadata(item);
991
- searchStream.write(metadata);
992
- }
993
- debug('search local stream end');
994
- searchStream.end();
995
- } catch (err) {
996
- this.logger.error({ err, query }, 'error on search by plugin @{err.message}');
997
- searchStream.emit('error', err);
998
- }
999
- }
1000
- }
1001
-
1002
- /**
1003
- * Retrieve a wrapper that provide access to the package location.
1004
- * @param {Object} pkgName package name.
1005
- * @return {Object}
1006
- */
1007
- private _getLocalStorage(pkgName: string): IPackageStorage {
1008
- debug('get local storage for %o', pkgName);
1009
- return this.storagePlugin.getPackageStorage(pkgName);
1010
- }
1011
-
1012
- /**
1013
- * Read a json file from storage.
1014
- * @param {Object} storage
1015
- * @param {Function} callback
1016
- * @deprecated
1017
- */
1018
- private _readPackage(name: string, storage: any, callback: Callback): void {
1019
- storage.readPackage(name, (err, result): void => {
1020
- if (err) {
1021
- if (err.code === STORAGE.NO_SUCH_FILE_ERROR || err.code === HTTP_STATUS.NOT_FOUND) {
1022
- debug('package %s not found', name);
1023
- return callback(errorUtils.getNotFound());
1024
- }
1025
- return callback(this._internalError(err, STORAGE.PACKAGE_FILE_NAME, 'error reading'));
1026
- }
1027
-
1028
- callback(err, normalizePackage(result));
1029
- });
1030
- }
1031
-
1032
- private async _readPackageNext(name: string, storage: any): Promise<Package> {
1033
- try {
1034
- const result: Package = await storage.readPackageNext(name);
1035
- return normalizePackage(result);
1036
- } catch (err: any) {
1037
- if (err.code === STORAGE.NO_SUCH_FILE_ERROR || err.code === HTTP_STATUS.NOT_FOUND) {
1038
- debug('package %s not found', name);
1039
- throw errorUtils.getNotFound();
1040
- }
1041
- this.logger.error(
1042
- { err: err, file: STORAGE.PACKAGE_FILE_NAME },
1043
- `error reading @{file}: @{!err.message}`
1044
- );
1045
-
1046
- throw errorUtils.getInternalError();
1047
- }
1048
- }
1049
-
1050
- /**
1051
- * Retrieve either a previous created local package or a boilerplate.
1052
- * @param {*} pkgName
1053
- * @param {*} callback
1054
- * @return {Function}
1055
- */
1056
- private _readCreatePackage(pkgName: string, callback: Callback): void {
1057
- const storage: any = this._getLocalStorage(pkgName);
1058
- if (_.isNil(storage)) {
1059
- this._createNewPackage(pkgName, callback);
1060
- return;
1061
- }
1062
-
1063
- storage.readPackage(pkgName, (err, data): void => {
1064
- // TODO: race condition
1065
- if (_.isNil(err) === false) {
1066
- if (err.code === STORAGE.NO_SUCH_FILE_ERROR || err.code === HTTP_STATUS.NOT_FOUND) {
1067
- data = generatePackageTemplate(pkgName);
1068
- } else {
1069
- return callback(this._internalError(err, STORAGE.PACKAGE_FILE_NAME, 'error reading'));
1070
- }
1071
- }
1072
-
1073
- callback(null, normalizePackage(data));
1074
- });
1075
- }
1076
-
1077
- private _createNewPackage(name: string, callback: Callback): Callback {
1078
- return callback(null, normalizePackage(generatePackageTemplate(name)));
1079
- }
1080
-
1081
- /**
1082
- * Handle internal error
1083
- * @param {*} err
1084
- * @param {*} file
1085
- * @param {*} message
1086
- * @return {Object} Error instance
1087
- */
1088
- private _internalError(err: string, file: string, message: string): VerdaccioError {
1089
- this.logger.error({ err: err, file: file }, `${message} @{file}: @{!err.message}`);
1090
-
1091
- return errorUtils.getInternalError();
1092
- }
1093
-
1094
- /**
1095
- * @param {*} name package name
1096
- * @param {*} updateHandler function(package, cb) - update function
1097
- * @param {*} callback callback that gets invoked after it's all updated
1098
- * @return {Function}
1099
- */
1100
- private _updatePackage(
1101
- name: string,
1102
- updateHandler: StorageUpdateCallback,
1103
- onEnd: Callback
1104
- ): void {
1105
- const storage: IPackageStorage = this._getLocalStorage(name);
1106
-
1107
- if (!storage) {
1108
- return onEnd(errorUtils.getNotFound());
1109
- }
1110
-
1111
- storage.updatePackage(
1112
- name,
1113
- updateHandler,
1114
- this._writePackage.bind(this),
1115
- normalizePackage,
1116
- onEnd
1117
- );
1118
- }
1119
-
1120
- /**
1121
- * @param {*} name package name
1122
- * @param {*} updateHandler function(package, cb) - update function
1123
- * @param {*} callback callback that gets invoked after it's all updated
1124
- * @return {Function}
1125
- */
1126
- private async updatePackageNext(
1127
- name: string,
1128
- updateHandler: (manifest: Package) => Promise<Package>
1129
- ): Promise<void> {
1130
- const storage: IPackageStorage = this._getLocalStorage(name);
1131
-
1132
- if (!storage) {
1133
- throw errorUtils.getNotFound();
1134
- }
1135
-
1136
- // we update the package on the local storage
1137
- const updatedManifest: Package = await storage.updatePackageNext(name, updateHandler);
1138
- // after correctly updated write to the storage
1139
- try {
1140
- await this.writePackageNext(name, normalizePackage(updatedManifest));
1141
- } catch (err: any) {
1142
- if (err.code === resourceNotAvailable) {
1143
- throw errorUtils.getInternalError('resource temporarily unavailable');
1144
- } else if (err.code === noSuchFile) {
1145
- throw errorUtils.getNotFound();
1146
- } else {
1147
- throw err;
1148
- }
1149
- }
1150
- }
1151
-
1152
- /**
1153
- * Update the revision (_rev) string for a package.
1154
- * @param {*} name
1155
- * @param {*} json
1156
- * @param {*} callback
1157
- * @return {Function}
1158
- */
1159
- private _writePackage(name: string, json: Package, callback: Callback): void {
1160
- const storage: any = this._getLocalStorage(name);
1161
- if (_.isNil(storage)) {
1162
- return callback();
1163
- }
1164
- storage.savePackage(name, this._setDefaultRevision(json), callback);
1165
- }
1166
-
1167
- private async writePackageNext(name: string, json: Package): Promise<void> {
1168
- const storage: any = this._getLocalStorage(name);
1169
- if (_.isNil(storage)) {
1170
- // TODO: replace here 500 error
1171
- throw errorUtils.getBadData();
1172
- }
1173
- await storage.savePackageNext(name, this._setDefaultRevision(json));
1174
- }
1175
-
1176
- private _setDefaultRevision(json: Package): Package {
1177
- // calculate revision from couch db
1178
- if (_.isString(json._rev) === false) {
1179
- json._rev = STORAGE.DEFAULT_REVISION;
1180
- }
1181
-
1182
- // this is intended in debug mode we do not want modify the store revision
1183
- if (_.isNil(this.config._debug)) {
1184
- json._rev = generateRevision(json._rev);
44
+ public getStoragePlugin(): IPluginStorage {
45
+ if (this.storagePlugin === null) {
46
+ throw errorUtils.getInternalError('storage plugin is not initialized');
1185
47
  }
1186
48
 
1187
- return json;
1188
- }
1189
-
1190
- // private _deleteAttachments(storage: any, attachments: string[], callback: Callback): void {
1191
- // debug('deleting %o attachments total %o', attachments?.length);
1192
- // const unlinkNext = function (cb): void {
1193
- // if (_.isEmpty(attachments)) {
1194
- // return cb();
1195
- // }
1196
-
1197
- // const attachment = attachments.shift();
1198
- // storage.deletePackage(attachment, function (): void {
1199
- // unlinkNext(cb);
1200
- // });
1201
- // };
1202
-
1203
- // unlinkNext(function (): void {
1204
- // // try to unlink the directory, but ignore errors because it can fail
1205
- // storage.removePackage(function (err): void {
1206
- // callback(err);
1207
- // });
1208
- // });
1209
- // }
1210
-
1211
- /**
1212
- * Ensure the dist file remains as the same protocol
1213
- * @param {Object} hash metadata
1214
- * @param {String} upLinkKey registry key
1215
- * @private
1216
- */
1217
- private _updateUplinkToRemoteProtocol(hash: DistFile, upLinkKey: string): void {
1218
- // if we got this information from a known registry,
1219
- // use the same protocol for the tarball
1220
- //
1221
- // see https://github.com/rlidwka/sinopia/issues/166
1222
- const tarballUrl: any = UrlNode.parse(hash.url);
1223
- const uplinkUrl: any = UrlNode.parse(this.config.uplinks[upLinkKey].url);
1224
-
1225
- if (uplinkUrl.host === tarballUrl.host) {
1226
- tarballUrl.protocol = uplinkUrl.protocol;
1227
- hash.registry = upLinkKey;
1228
- hash.url = UrlNode.format(tarballUrl);
1229
- }
49
+ return this.storagePlugin;
1230
50
  }
1231
51
 
1232
52
  public async getSecret(config: Config): Promise<void> {
@@ -1240,7 +60,7 @@ class LocalStorage {
1240
60
 
1241
61
  if (_.isNil(Storage)) {
1242
62
  assert(this.config.storage, 'CONFIG: storage path not defined');
1243
- return new LocalDatabase(this.config, logger);
63
+ return new LocalDatabase(config, logger);
1244
64
  }
1245
65
  return Storage as IPluginStorage;
1246
66
  }
@@ -1262,36 +82,6 @@ class LocalStorage {
1262
82
 
1263
83
  return _.head(plugins);
1264
84
  }
1265
-
1266
- public saveToken(token: Token): Promise<any> {
1267
- if (_.isFunction(this.storagePlugin.saveToken) === false) {
1268
- return Promise.reject(
1269
- errorUtils.getCode(HTTP_STATUS.SERVICE_UNAVAILABLE, SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE)
1270
- );
1271
- }
1272
-
1273
- return this.storagePlugin.saveToken(token);
1274
- }
1275
-
1276
- public deleteToken(user: string, tokenKey: string): Promise<any> {
1277
- if (_.isFunction(this.storagePlugin.deleteToken) === false) {
1278
- return Promise.reject(
1279
- errorUtils.getCode(HTTP_STATUS.SERVICE_UNAVAILABLE, SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE)
1280
- );
1281
- }
1282
-
1283
- return this.storagePlugin.deleteToken(user, tokenKey);
1284
- }
1285
-
1286
- public readTokens(filter: TokenFilter): Promise<Token[]> {
1287
- if (_.isFunction(this.storagePlugin.readTokens) === false) {
1288
- return Promise.reject(
1289
- errorUtils.getCode(HTTP_STATUS.SERVICE_UNAVAILABLE, SUPPORT_ERRORS.PLUGIN_MISSING_INTERFACE)
1290
- );
1291
- }
1292
-
1293
- return this.storagePlugin.readTokens(filter);
1294
- }
1295
85
  }
1296
86
 
1297
87
  export { LocalStorage };