@verdaccio/api 6.0.0-6-next.12 → 6.0.0-6-next.16
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.
- package/CHANGELOG.md +100 -0
- package/build/dist-tags.d.ts +2 -2
- package/build/dist-tags.js +8 -8
- package/build/dist-tags.js.map +1 -1
- package/build/index.d.ts +2 -2
- package/build/index.js +12 -13
- package/build/index.js.map +1 -1
- package/build/package.d.ts +2 -2
- package/build/package.js +6 -6
- package/build/package.js.map +1 -1
- package/build/publish.d.ts +7 -7
- package/build/publish.js +24 -22
- package/build/publish.js.map +1 -1
- package/build/search.d.ts +1 -1
- package/build/search.js +9 -102
- package/build/search.js.map +1 -1
- package/build/star.d.ts +2 -2
- package/build/star.js +6 -5
- package/build/star.js.map +1 -1
- package/build/stars.d.ts +2 -2
- package/build/stars.js +3 -3
- package/build/stars.js.map +1 -1
- package/build/user.js +12 -12
- package/build/user.js.map +1 -1
- package/build/v1/profile.js +10 -10
- package/build/v1/profile.js.map +1 -1
- package/build/v1/search.d.ts +9 -1
- package/build/v1/search.js +71 -88
- package/build/v1/search.js.map +1 -1
- package/build/v1/token.d.ts +2 -2
- package/build/v1/token.js +12 -12
- package/build/v1/token.js.map +1 -1
- package/build/whoami.js +0 -1
- package/build/whoami.js.map +1 -1
- package/package.json +16 -13
- package/src/dist-tags.ts +10 -10
- package/src/index.ts +12 -11
- package/src/package.ts +5 -10
- package/src/publish.ts +28 -19
- package/src/search.ts +9 -100
- package/src/star.ts +4 -3
- package/src/stars.ts +3 -3
- package/src/user.ts +7 -7
- package/src/v1/profile.ts +7 -7
- package/src/v1/search.ts +64 -89
- package/src/v1/token.ts +13 -18
- package/src/whoami.ts +0 -1
- package/test/integration/_helper.ts +3 -2
- package/test/integration/package.spec.ts +3 -3
- package/test/integration/ping.spec.ts +1 -1
- package/test/integration/publish.spec.ts +3 -8
- package/test/integration/user.spec.ts +6 -8
- package/test/integration/whoami.spec.ts +1 -1
- package/test/unit/publish.spec.ts +11 -15
- package/tsconfig.json +1 -1
package/src/package.ts
CHANGED
|
@@ -3,11 +3,11 @@ import { Router } from 'express';
|
|
|
3
3
|
import buildDebug from 'debug';
|
|
4
4
|
|
|
5
5
|
import { allow } from '@verdaccio/middleware';
|
|
6
|
-
import { getVersion
|
|
7
|
-
import { HEADERS, DIST_TAGS, API_ERROR } from '@verdaccio/
|
|
6
|
+
import { getVersion } from '@verdaccio/utils';
|
|
7
|
+
import { HEADERS, DIST_TAGS, API_ERROR, errorUtils } from '@verdaccio/core';
|
|
8
8
|
import { Config, Package } from '@verdaccio/types';
|
|
9
9
|
import { IAuth } from '@verdaccio/auth';
|
|
10
|
-
import {
|
|
10
|
+
import { Storage } from '@verdaccio/store';
|
|
11
11
|
import { convertDistRemoteToLocalTarballUrls } from '@verdaccio/tarball';
|
|
12
12
|
import { $RequestExtend, $ResponseExtend, $NextFunctionVer } from '../types/custom';
|
|
13
13
|
|
|
@@ -34,12 +34,7 @@ const downloadStream = (
|
|
|
34
34
|
stream.pipe(res);
|
|
35
35
|
};
|
|
36
36
|
|
|
37
|
-
export default function (
|
|
38
|
-
route: Router,
|
|
39
|
-
auth: IAuth,
|
|
40
|
-
storage: IStorageHandler,
|
|
41
|
-
config: Config
|
|
42
|
-
): void {
|
|
37
|
+
export default function (route: Router, auth: IAuth, storage: Storage, config: Config): void {
|
|
43
38
|
const can = allow(auth);
|
|
44
39
|
// TODO: anonymous user?
|
|
45
40
|
route.get(
|
|
@@ -85,7 +80,7 @@ export default function (
|
|
|
85
80
|
}
|
|
86
81
|
|
|
87
82
|
debug('package version not found %o', queryVersion);
|
|
88
|
-
return next(
|
|
83
|
+
return next(errorUtils.getNotFound(`${API_ERROR.VERSION_NOT_EXIST}: ${queryVersion}`));
|
|
89
84
|
};
|
|
90
85
|
|
|
91
86
|
debug('get package name %o', name);
|
package/src/publish.ts
CHANGED
|
@@ -4,14 +4,21 @@ import mime from 'mime';
|
|
|
4
4
|
import { Router } from 'express';
|
|
5
5
|
import buildDebug from 'debug';
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
|
|
7
|
+
import {
|
|
8
|
+
API_MESSAGE,
|
|
9
|
+
HEADERS,
|
|
10
|
+
DIST_TAGS,
|
|
11
|
+
API_ERROR,
|
|
12
|
+
HTTP_STATUS,
|
|
13
|
+
errorUtils,
|
|
14
|
+
} from '@verdaccio/core';
|
|
15
|
+
import { validateMetadata, isObject, hasDiffOneKey } from '@verdaccio/utils';
|
|
9
16
|
import { media, expectJson, allow } from '@verdaccio/middleware';
|
|
10
17
|
import { notify } from '@verdaccio/hooks';
|
|
11
|
-
import { Config, Callback, MergeTags, Version, Package } from '@verdaccio/types';
|
|
18
|
+
import { Config, Callback, MergeTags, Version, Package, CallbackAction } from '@verdaccio/types';
|
|
12
19
|
import { logger } from '@verdaccio/logger';
|
|
13
20
|
import { IAuth } from '@verdaccio/auth';
|
|
14
|
-
import {
|
|
21
|
+
import { Storage } from '@verdaccio/store';
|
|
15
22
|
import { $RequestExtend, $ResponseExtend, $NextFunctionVer } from '../types/custom';
|
|
16
23
|
|
|
17
24
|
import star from './star';
|
|
@@ -22,7 +29,7 @@ const debug = buildDebug('verdaccio:api:publish');
|
|
|
22
29
|
export default function publish(
|
|
23
30
|
router: Router,
|
|
24
31
|
auth: IAuth,
|
|
25
|
-
storage:
|
|
32
|
+
storage: Storage,
|
|
26
33
|
config: Config
|
|
27
34
|
): void {
|
|
28
35
|
const can = allow(auth);
|
|
@@ -138,7 +145,7 @@ export default function publish(
|
|
|
138
145
|
/**
|
|
139
146
|
* Publish a package
|
|
140
147
|
*/
|
|
141
|
-
export function publishPackage(storage:
|
|
148
|
+
export function publishPackage(storage: Storage, config: Config, auth: IAuth): any {
|
|
142
149
|
const starApi = star(storage);
|
|
143
150
|
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
|
144
151
|
const packageName = req.params.package;
|
|
@@ -172,7 +179,7 @@ export function publishPackage(storage: IStorageHandler, config: Config, auth: I
|
|
|
172
179
|
/**
|
|
173
180
|
* Add new package version in storage
|
|
174
181
|
*/
|
|
175
|
-
const createVersion = function (version: string, metadata: Version, cb:
|
|
182
|
+
const createVersion = function (version: string, metadata: Version, cb: CallbackAction): void {
|
|
176
183
|
debug('add a new package version %o to storage %o', version, metadata);
|
|
177
184
|
storage.addVersion(packageName, version, metadata, null, cb);
|
|
178
185
|
};
|
|
@@ -180,7 +187,7 @@ export function publishPackage(storage: IStorageHandler, config: Config, auth: I
|
|
|
180
187
|
/**
|
|
181
188
|
* Add new tags in storage
|
|
182
189
|
*/
|
|
183
|
-
const addTags = function (tags: MergeTags, cb:
|
|
190
|
+
const addTags = function (tags: MergeTags, cb: CallbackAction): void {
|
|
184
191
|
debug('add new tag %o to storage', packageName);
|
|
185
192
|
storage.mergeTags(packageName, tags, cb);
|
|
186
193
|
};
|
|
@@ -224,7 +231,7 @@ export function publishPackage(storage: IStorageHandler, config: Config, auth: I
|
|
|
224
231
|
// if this happens in normal circumstances, report it as a bug
|
|
225
232
|
debug('invalid body format');
|
|
226
233
|
logger.info({ packageName }, `wrong package format on publish a package @{packageName}`);
|
|
227
|
-
return next(
|
|
234
|
+
return next(errorUtils.getBadRequest(API_ERROR.UNSUPORTED_REGISTRY_CALL));
|
|
228
235
|
}
|
|
229
236
|
|
|
230
237
|
if (error && error.status !== HTTP_STATUS.CONFLICT) {
|
|
@@ -322,7 +329,7 @@ export function publishPackage(storage: IStorageHandler, config: Config, auth: I
|
|
|
322
329
|
} catch (error: any) {
|
|
323
330
|
debug('error on publish, bad package format %o', packageName);
|
|
324
331
|
logger.error({ packageName }, 'error on publish, bad package data for @{packageName}');
|
|
325
|
-
return next(
|
|
332
|
+
return next(errorUtils.getBadData(API_ERROR.BAD_PACKAGE_DATA));
|
|
326
333
|
}
|
|
327
334
|
};
|
|
328
335
|
}
|
|
@@ -330,25 +337,27 @@ export function publishPackage(storage: IStorageHandler, config: Config, auth: I
|
|
|
330
337
|
/**
|
|
331
338
|
* un-publish a package
|
|
332
339
|
*/
|
|
333
|
-
export function unPublishPackage(storage:
|
|
334
|
-
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer)
|
|
340
|
+
export function unPublishPackage(storage: Storage) {
|
|
341
|
+
return async function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
|
|
335
342
|
const packageName = req.params.package;
|
|
336
343
|
|
|
337
344
|
logger.debug({ packageName }, `unpublishing @{packageName}`);
|
|
338
|
-
|
|
345
|
+
try {
|
|
346
|
+
await storage.removePackage(packageName);
|
|
347
|
+
} catch (err) {
|
|
339
348
|
if (err) {
|
|
340
349
|
return next(err);
|
|
341
350
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
});
|
|
351
|
+
}
|
|
352
|
+
res.status(HTTP_STATUS.CREATED);
|
|
353
|
+
return next({ ok: API_MESSAGE.PKG_REMOVED });
|
|
345
354
|
};
|
|
346
355
|
}
|
|
347
356
|
|
|
348
357
|
/**
|
|
349
358
|
* Delete tarball
|
|
350
359
|
*/
|
|
351
|
-
export function removeTarball(storage:
|
|
360
|
+
export function removeTarball(storage: Storage) {
|
|
352
361
|
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
|
353
362
|
const packageName = req.params.package;
|
|
354
363
|
const { filename, revision } = req.params;
|
|
@@ -374,7 +383,7 @@ export function removeTarball(storage: IStorageHandler) {
|
|
|
374
383
|
/**
|
|
375
384
|
* Adds a new version
|
|
376
385
|
*/
|
|
377
|
-
export function addVersion(storage:
|
|
386
|
+
export function addVersion(storage: Storage) {
|
|
378
387
|
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
|
379
388
|
const { version, tag } = req.params;
|
|
380
389
|
const packageName = req.params.package;
|
|
@@ -398,7 +407,7 @@ export function addVersion(storage: IStorageHandler) {
|
|
|
398
407
|
/**
|
|
399
408
|
* uploadPackageTarball
|
|
400
409
|
*/
|
|
401
|
-
export function uploadPackageTarball(storage:
|
|
410
|
+
export function uploadPackageTarball(storage: Storage) {
|
|
402
411
|
return function (req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer): void {
|
|
403
412
|
const packageName = req.params.package;
|
|
404
413
|
const stream = storage.addTarball(packageName, req.params.filename);
|
package/src/search.ts
CHANGED
|
@@ -1,102 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
res.status(200);
|
|
12
|
-
res.set(HEADERS.CONTENT_TYPE, HEADERS.JSON_CHARSET);
|
|
13
|
-
|
|
14
|
-
/*
|
|
15
|
-
* Offical NPM registry (registry.npmjs.org) no longer return whole database,
|
|
16
|
-
* They only return packages matched with keyword in `referer: search pkg-name`,
|
|
17
|
-
* And NPM client will request server in every search.
|
|
18
|
-
*
|
|
19
|
-
* The magic number 99999 was sent by NPM registry. Modify it may caused strange
|
|
20
|
-
* behaviour in the future.
|
|
21
|
-
*
|
|
22
|
-
* BTW: NPM will not return result if user-agent does not contain string 'npm',
|
|
23
|
-
* See: method 'request' in up-storage.js
|
|
24
|
-
*
|
|
25
|
-
* If there is no cache in local, NPM will request /-/all, then get response with
|
|
26
|
-
* _updated: 99999, 'Date' in response header was Mon, 10 Oct 1983 00:12:48 GMT,
|
|
27
|
-
* this will make NPM always query from server
|
|
28
|
-
*
|
|
29
|
-
* Data structure also different, whel request /-/all, response is an object, but
|
|
30
|
-
* when request /-/all/since, response is an array
|
|
31
|
-
*/
|
|
32
|
-
const respShouldBeArray = req.path.endsWith('/since');
|
|
33
|
-
if (!respShouldBeArray) {
|
|
34
|
-
res.set('Date', 'Mon, 10 Oct 1983 00:12:48 GMT');
|
|
35
|
-
}
|
|
36
|
-
const check_finish = function (): void {
|
|
37
|
-
if (!received_end) {
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
if (processing_pkgs) {
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
if (response_finished) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
response_finished = true;
|
|
47
|
-
if (respShouldBeArray) {
|
|
48
|
-
res.end(']\n');
|
|
49
|
-
} else {
|
|
50
|
-
res.end('}\n');
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
if (respShouldBeArray) {
|
|
55
|
-
res.write('[');
|
|
56
|
-
} else {
|
|
57
|
-
res.write('{"_updated":' + 99999);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const stream = storage.search(req.query.startkey || 0, { req: req });
|
|
61
|
-
|
|
62
|
-
stream.on('data', function each(pkg) {
|
|
63
|
-
processing_pkgs++;
|
|
64
|
-
|
|
65
|
-
auth.allow_access({ packageName: pkg.name }, req.remote_user, function (err, allowed) {
|
|
66
|
-
processing_pkgs--;
|
|
67
|
-
|
|
68
|
-
if (err) {
|
|
69
|
-
if (err.status && String(err.status).match(/^4\d\d$/)) {
|
|
70
|
-
// auth plugin returns 4xx user error,
|
|
71
|
-
// that's equivalent of !allowed basically
|
|
72
|
-
allowed = false;
|
|
73
|
-
} else {
|
|
74
|
-
stream.abort(err);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (allowed) {
|
|
79
|
-
if (respShouldBeArray) {
|
|
80
|
-
res.write(`${firstPackage ? '' : ','}${JSON.stringify(pkg)}\n`);
|
|
81
|
-
if (firstPackage) {
|
|
82
|
-
firstPackage = false;
|
|
83
|
-
}
|
|
84
|
-
} else {
|
|
85
|
-
res.write(',\n' + JSON.stringify(pkg.name) + ':' + JSON.stringify(pkg));
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
check_finish();
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
stream.on('error', function () {
|
|
94
|
-
res.socket.destroy();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
stream.on('end', function () {
|
|
98
|
-
received_end = true;
|
|
99
|
-
check_finish();
|
|
100
|
-
});
|
|
1
|
+
import { HTTP_STATUS } from '@verdaccio/core';
|
|
2
|
+
import { logger } from '@verdaccio/logger';
|
|
3
|
+
|
|
4
|
+
export default function (route): void {
|
|
5
|
+
// TODO: next major version, remove this
|
|
6
|
+
route.get('/-/all(/since)?', function (_req, res) {
|
|
7
|
+
logger.warn('search endpoint has been removed, please use search v1');
|
|
8
|
+
res.status(HTTP_STATUS.NOT_FOUND);
|
|
9
|
+
res.json({ error: 'not found, endpoint was removed' });
|
|
101
10
|
});
|
|
102
11
|
}
|
package/src/star.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { USERS, HTTP_STATUS } from '@verdaccio/
|
|
1
|
+
import { USERS, HTTP_STATUS } from '@verdaccio/core';
|
|
2
2
|
import { Response } from 'express';
|
|
3
3
|
import _ from 'lodash';
|
|
4
4
|
import buildDebug from 'debug';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { Storage } from '@verdaccio/store';
|
|
7
7
|
import { $RequestExtend, $NextFunctionVer } from '../types/custom';
|
|
8
8
|
|
|
9
9
|
const debug = buildDebug('verdaccio:api:publish:star');
|
|
10
10
|
|
|
11
11
|
export default function (
|
|
12
|
-
storage:
|
|
12
|
+
storage: Storage
|
|
13
13
|
): (req: $RequestExtend, res: Response, next: $NextFunctionVer) => void {
|
|
14
14
|
const validateInputs = (newUsers, localUsers, username, isStar): boolean => {
|
|
15
15
|
const isExistlocalUsers = _.isNil(localUsers[username]) === false;
|
|
@@ -40,6 +40,7 @@ export default function (
|
|
|
40
40
|
};
|
|
41
41
|
|
|
42
42
|
debug('get package info package for %o', name);
|
|
43
|
+
// @ts-ignore
|
|
43
44
|
storage.getPackage({
|
|
44
45
|
name,
|
|
45
46
|
req,
|
package/src/stars.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import { Response, Router } from 'express';
|
|
3
3
|
|
|
4
|
-
import { USERS, HTTP_STATUS } from '@verdaccio/
|
|
4
|
+
import { USERS, HTTP_STATUS } from '@verdaccio/core';
|
|
5
5
|
import { Package } from '@verdaccio/types';
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import { Storage } from '@verdaccio/store';
|
|
8
8
|
import { $RequestExtend, $NextFunctionVer } from '../types/custom';
|
|
9
9
|
|
|
10
10
|
type Packages = Package[];
|
|
11
11
|
|
|
12
|
-
export default function (route: Router, storage:
|
|
12
|
+
export default function (route: Router, storage: Storage): void {
|
|
13
13
|
route.get(
|
|
14
14
|
'/-/_view/starredByUser',
|
|
15
15
|
(req: $RequestExtend, res: Response, next: $NextFunctionVer): void => {
|
package/src/user.ts
CHANGED
|
@@ -2,14 +2,14 @@ import _ from 'lodash';
|
|
|
2
2
|
import { Response, Router } from 'express';
|
|
3
3
|
import buildDebug from 'debug';
|
|
4
4
|
|
|
5
|
-
import { getAuthenticatedMessage, validatePassword
|
|
5
|
+
import { getAuthenticatedMessage, validatePassword } from '@verdaccio/utils';
|
|
6
6
|
import { getApiToken } from '@verdaccio/auth';
|
|
7
7
|
import { logger } from '@verdaccio/logger';
|
|
8
8
|
import { createRemoteUser } from '@verdaccio/config';
|
|
9
9
|
|
|
10
10
|
import { Config, RemoteUser } from '@verdaccio/types';
|
|
11
11
|
import { IAuth } from '@verdaccio/auth';
|
|
12
|
-
import { API_ERROR, API_MESSAGE, HTTP_STATUS } from '@verdaccio/
|
|
12
|
+
import { API_ERROR, API_MESSAGE, HTTP_STATUS, errorUtils } from '@verdaccio/core';
|
|
13
13
|
import { $RequestExtend, $NextFunctionVer } from '../types/custom';
|
|
14
14
|
|
|
15
15
|
const debug = buildDebug('verdaccio:api:user');
|
|
@@ -47,7 +47,7 @@ export default function (route: Router, auth: IAuth, config: Config): void {
|
|
|
47
47
|
'authenticating for user @{username} failed. Error: @{err.message}'
|
|
48
48
|
);
|
|
49
49
|
return next(
|
|
50
|
-
|
|
50
|
+
errorUtils.getCode(HTTP_STATUS.UNAUTHORIZED, API_ERROR.BAD_USERNAME_PASSWORD)
|
|
51
51
|
);
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -55,7 +55,7 @@ export default function (route: Router, auth: IAuth, config: Config): void {
|
|
|
55
55
|
const token = await getApiToken(auth, config, restoredRemoteUser, password);
|
|
56
56
|
debug('login: new token');
|
|
57
57
|
if (!token) {
|
|
58
|
-
return next(
|
|
58
|
+
return next(errorUtils.getUnauthorized());
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
res.status(HTTP_STATUS.CREATED);
|
|
@@ -73,7 +73,7 @@ export default function (route: Router, auth: IAuth, config: Config): void {
|
|
|
73
73
|
if (validatePassword(password) === false) {
|
|
74
74
|
debug('adduser: invalid password');
|
|
75
75
|
// eslint-disable-next-line new-cap
|
|
76
|
-
return next(
|
|
76
|
+
return next(errorUtils.getCode(HTTP_STATUS.BAD_REQUEST, API_ERROR.PASSWORD_SHORT()));
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
auth.add_user(name, password, async function (err, user): Promise<void> {
|
|
@@ -84,7 +84,7 @@ export default function (route: Router, auth: IAuth, config: Config): void {
|
|
|
84
84
|
// and npm accepts only an 409 error.
|
|
85
85
|
// So, changing status code here.
|
|
86
86
|
return next(
|
|
87
|
-
|
|
87
|
+
errorUtils.getCode(err.status, err.message) || errorUtils.getConflict(err.message)
|
|
88
88
|
);
|
|
89
89
|
}
|
|
90
90
|
return next(err);
|
|
@@ -94,7 +94,7 @@ export default function (route: Router, auth: IAuth, config: Config): void {
|
|
|
94
94
|
name && password ? await getApiToken(auth, config, user, password) : undefined;
|
|
95
95
|
debug('adduser: new token %o', token);
|
|
96
96
|
if (!token) {
|
|
97
|
-
return next(
|
|
97
|
+
return next(errorUtils.getUnauthorized());
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
req.remote_user = user;
|
package/src/v1/profile.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import _ from 'lodash';
|
|
2
2
|
import { Response, Router } from 'express';
|
|
3
3
|
|
|
4
|
-
import { API_ERROR, APP_ERROR, HTTP_STATUS, SUPPORT_ERRORS } from '@verdaccio/
|
|
5
|
-
import {
|
|
4
|
+
import { API_ERROR, APP_ERROR, HTTP_STATUS, SUPPORT_ERRORS, errorUtils } from '@verdaccio/core';
|
|
5
|
+
import { validatePassword } from '@verdaccio/utils';
|
|
6
6
|
import { IAuth } from '@verdaccio/auth';
|
|
7
7
|
import { $RequestExtend, $NextFunctionVer } from '../../types/custom';
|
|
8
8
|
|
|
@@ -61,7 +61,7 @@ export default function (route: Router, auth: IAuth): void {
|
|
|
61
61
|
if (_.isNil(password) === false) {
|
|
62
62
|
if (validatePassword(password.new) === false) {
|
|
63
63
|
/* eslint new-cap:off */
|
|
64
|
-
return next(
|
|
64
|
+
return next(errorUtils.getCode(HTTP_STATUS.UNAUTHORIZED, API_ERROR.PASSWORD_SHORT()));
|
|
65
65
|
/* eslint new-cap:off */
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -72,22 +72,22 @@ export default function (route: Router, auth: IAuth): void {
|
|
|
72
72
|
(err, isUpdated): $NextFunctionVer => {
|
|
73
73
|
if (_.isNull(err) === false) {
|
|
74
74
|
return next(
|
|
75
|
-
|
|
75
|
+
errorUtils.getCode(err.status, err.message) || errorUtils.getConflict(err.message)
|
|
76
76
|
);
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
if (isUpdated) {
|
|
80
80
|
return next(buildProfile(req.remote_user.name));
|
|
81
81
|
}
|
|
82
|
-
return next(
|
|
82
|
+
return next(errorUtils.getInternalError(API_ERROR.INTERNAL_SERVER_ERROR));
|
|
83
83
|
}
|
|
84
84
|
);
|
|
85
85
|
} else if (_.isNil(tfa) === false) {
|
|
86
86
|
return next(
|
|
87
|
-
|
|
87
|
+
errorUtils.getCode(HTTP_STATUS.SERVICE_UNAVAILABLE, SUPPORT_ERRORS.TFA_DISABLED)
|
|
88
88
|
);
|
|
89
89
|
} else {
|
|
90
|
-
return next(
|
|
90
|
+
return next(errorUtils.getCode(HTTP_STATUS.INTERNAL_ERROR, APP_ERROR.PROFILE_ERROR));
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
);
|
package/src/v1/search.ts
CHANGED
|
@@ -1,106 +1,81 @@
|
|
|
1
|
-
import
|
|
1
|
+
import _ from 'lodash';
|
|
2
|
+
import buildDebug from 'debug';
|
|
3
|
+
import { logger } from '@verdaccio/logger';
|
|
4
|
+
import { IAuth } from '@verdaccio/auth';
|
|
5
|
+
import { HTTP_STATUS, searchUtils } from '@verdaccio/core';
|
|
6
|
+
import { Storage } from '@verdaccio/store';
|
|
2
7
|
import { Package } from '@verdaccio/types';
|
|
3
8
|
|
|
4
|
-
|
|
5
|
-
const personMatch = (person, search) => {
|
|
6
|
-
if (typeof person === 'string') {
|
|
7
|
-
return person.includes(search);
|
|
8
|
-
}
|
|
9
|
+
const debug = buildDebug('verdaccio:api:search');
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Endpoint for npm search v1
|
|
13
|
+
* Empty value
|
|
14
|
+
* - {"objects":[],"total":0,"time":"Sun Jul 25 2021 14:09:11 GMT+0000 (Coordinated Universal Time)"}
|
|
15
|
+
* req: 'GET /-/v1/search?text=react&size=20&frpom=0&quality=0.65&popularity=0.98&maintenance=0.5'
|
|
16
|
+
*/
|
|
17
|
+
export default function (route, auth: IAuth, storage: Storage): void {
|
|
18
|
+
function checkAccess(pkg: any, auth: any, remoteUser): Promise<Package | null> {
|
|
19
|
+
return new Promise((resolve, reject) => {
|
|
20
|
+
auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {
|
|
21
|
+
if (err) {
|
|
22
|
+
if (err.status && String(err.status).match(/^4\d\d$/)) {
|
|
23
|
+
// auth plugin returns 4xx user error,
|
|
24
|
+
// that's equivalent of !allowed basically
|
|
25
|
+
allowed = false;
|
|
26
|
+
return resolve(null);
|
|
27
|
+
} else {
|
|
28
|
+
reject(err);
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
return resolve(allowed ? pkg : null);
|
|
14
32
|
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return false;
|
|
19
|
-
};
|
|
20
|
-
const matcher = function (q) {
|
|
21
|
-
const match = q.match(/author:(.*)/);
|
|
22
|
-
if (match !== null) {
|
|
23
|
-
return (pkg) => personMatch(pkg.author, match[1]);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// TODO: maintainer, keywords, not/is unstable insecure, boost-exact
|
|
27
|
-
// TODO implement some scoring system for freetext
|
|
28
|
-
return (pkg) => {
|
|
29
|
-
return ['name', 'displayName', 'description']
|
|
30
|
-
.map((k) => pkg[k])
|
|
31
|
-
.filter((x) => x !== undefined)
|
|
32
|
-
.some((txt) => txt.includes(q));
|
|
33
|
-
};
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const textMatchers = (textSearch || '').split(' ').map(matcher);
|
|
37
|
-
return (pkg) => textMatchers.every((m) => m(pkg));
|
|
38
|
-
}
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
'text',
|
|
46
|
-
'size',
|
|
47
|
-
'from' /* , 'quality', 'popularity', 'maintenance' */,
|
|
48
|
-
].map((k) => req.query[k]);
|
|
37
|
+
route.get('/-/v1/search', async (req, res, next) => {
|
|
38
|
+
const { query, url } = req;
|
|
39
|
+
let [size, from] = ['size', 'from'].map((k) => query[k]);
|
|
40
|
+
let data;
|
|
41
|
+
const abort = new AbortController();
|
|
49
42
|
|
|
50
|
-
|
|
51
|
-
|
|
43
|
+
req.on('aborted', () => {
|
|
44
|
+
abort.abort();
|
|
45
|
+
});
|
|
52
46
|
|
|
53
|
-
|
|
47
|
+
size = parseInt(size, 10) || 20;
|
|
48
|
+
from = parseInt(from, 10) || 0;
|
|
54
49
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
50
|
+
try {
|
|
51
|
+
data = await storage.searchManager?.search({
|
|
52
|
+
query,
|
|
53
|
+
url,
|
|
54
|
+
abort,
|
|
55
|
+
});
|
|
56
|
+
debug('stream finish');
|
|
57
|
+
const checkAccessPromises: searchUtils.SearchItemPkg[] = await Promise.all(
|
|
58
|
+
data.map((pkgItem) => {
|
|
59
|
+
return checkAccess(pkgItem, auth, req.remote_user);
|
|
60
|
+
})
|
|
61
|
+
);
|
|
58
62
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
const final: searchUtils.SearchItemPkg[] = checkAccessPromises
|
|
64
|
+
.filter((i) => !_.isNull(i))
|
|
65
|
+
.slice(from, size);
|
|
66
|
+
logger.debug(`search results ${final?.length}`);
|
|
62
67
|
|
|
63
|
-
const
|
|
64
|
-
return {
|
|
65
|
-
package: pkg,
|
|
66
|
-
flags: {
|
|
67
|
-
unstable: Object.keys(pkg.versions).some((v) => semver.satisfies(v, '^1.0.0'))
|
|
68
|
-
? undefined
|
|
69
|
-
: true,
|
|
70
|
-
},
|
|
71
|
-
score: {
|
|
72
|
-
final: 1,
|
|
73
|
-
detail: {
|
|
74
|
-
quality: 1,
|
|
75
|
-
popularity: 1,
|
|
76
|
-
maintenance: 0,
|
|
77
|
-
},
|
|
78
|
-
},
|
|
79
|
-
searchScore: 100000,
|
|
80
|
-
};
|
|
81
|
-
});
|
|
82
|
-
const response = {
|
|
68
|
+
const response: searchUtils.SearchResults = {
|
|
83
69
|
objects: final,
|
|
84
70
|
total: final.length,
|
|
85
71
|
time: new Date().toUTCString(),
|
|
86
72
|
};
|
|
87
73
|
|
|
88
|
-
res.status(
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
resultBuf.push(pkg);
|
|
96
|
-
if (!completed && resultBuf.length >= size + from) {
|
|
97
|
-
sendResponse();
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
resultStream.on('end', () => {
|
|
101
|
-
if (!completed) {
|
|
102
|
-
sendResponse();
|
|
103
|
-
}
|
|
104
|
-
});
|
|
74
|
+
res.status(HTTP_STATUS.OK).json(response);
|
|
75
|
+
} catch (error) {
|
|
76
|
+
logger.error({ error }, 'search endpoint has failed @{error.message}');
|
|
77
|
+
next(next);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
105
80
|
});
|
|
106
81
|
}
|