@verdaccio/api 6.0.0-6-next.14 → 6.0.0-6-next.15

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 CHANGED
@@ -1,5 +1,16 @@
1
1
  # @verdaccio/api
2
2
 
3
+ ## 6.0.0-6-next.15
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [2c594910]
8
+ - @verdaccio/logger@6.0.0-6-next.5
9
+ - @verdaccio/auth@6.0.0-6-next.12
10
+ - @verdaccio/hooks@6.0.0-6-next.7
11
+ - @verdaccio/middleware@6.0.0-6-next.12
12
+ - @verdaccio/store@6.0.0-6-next.13
13
+
3
14
  ## 6.0.0-6-next.14
4
15
 
5
16
  ### Major Changes
@@ -5,8 +5,6 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = _default;
7
7
 
8
- var _stream = require("stream");
9
-
10
8
  var _lodash = _interopRequireDefault(require("lodash"));
11
9
 
12
10
  var _debug = _interopRequireDefault(require("debug"));
@@ -18,115 +16,6 @@ var _commonsApi = require("@verdaccio/commons-api");
18
16
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
19
17
 
20
18
  const debug = (0, _debug.default)('verdaccio:api:search');
21
-
22
- // const personMatch = (person, search) => {
23
- // if (typeof person === 'string') {
24
- // return person.includes(search);
25
- // }
26
- // if (typeof person === 'object') {
27
- // for (const field of Object.values(person)) {
28
- // if (typeof field === 'string' && field.includes(search)) {
29
- // return true;
30
- // }
31
- // }
32
- // }
33
- // return false;
34
- // };
35
- // const matcher = function (query) {
36
- // const match = query.match(/author:(.*)/);
37
- // if (match !== null) {
38
- // return function (pkg) {
39
- // return personMatch(pkg.author, match[1]);
40
- // };
41
- // }
42
- // // TODO: maintainer, keywords, boost-exact
43
- // // TODO implement some scoring system for freetext
44
- // return (pkg) => {
45
- // return ['name', 'displayName', 'description']
46
- // .map((k) => {
47
- // return pkg[k];
48
- // })
49
- // .filter((x) => {
50
- // return x !== undefined;
51
- // })
52
- // .some((txt) => {
53
- // return txt.includes(query);
54
- // });
55
- // };
56
- // };
57
- function removeDuplicates(results) {
58
- const pkgNames = [];
59
- return results.filter(pkg => {
60
- var _pkg$package, _pkg$package2;
61
-
62
- if (pkgNames.includes(pkg === null || pkg === void 0 ? void 0 : (_pkg$package = pkg.package) === null || _pkg$package === void 0 ? void 0 : _pkg$package.name)) {
63
- return false;
64
- }
65
-
66
- pkgNames.push(pkg === null || pkg === void 0 ? void 0 : (_pkg$package2 = pkg.package) === null || _pkg$package2 === void 0 ? void 0 : _pkg$package2.name);
67
- return true;
68
- });
69
- }
70
-
71
- function checkAccess(pkg, auth, remoteUser) {
72
- return new Promise((resolve, reject) => {
73
- var _pkg$package3;
74
-
75
- auth.allow_access({
76
- packageName: pkg === null || pkg === void 0 ? void 0 : (_pkg$package3 = pkg.package) === null || _pkg$package3 === void 0 ? void 0 : _pkg$package3.name
77
- }, remoteUser, function (err, allowed) {
78
- if (err) {
79
- if (err.status && String(err.status).match(/^4\d\d$/)) {
80
- // auth plugin returns 4xx user error,
81
- // that's equivalent of !allowed basically
82
- allowed = false;
83
- return resolve(null);
84
- } else {
85
- reject(err);
86
- }
87
- } else {
88
- return resolve(allowed ? pkg : null);
89
- }
90
- });
91
- });
92
- }
93
-
94
- class TransFormResults extends _stream.Transform {
95
- constructor(options) {
96
- super(options);
97
- }
98
- /**
99
- * Transform either array of packages or a single package into a stream of packages.
100
- * From uplinks the chunks are array but from local packages are objects.
101
- * @param {string} chunk
102
- * @param {string} encoding
103
- * @param {function} done
104
- * @returns {void}
105
- * @override
106
- */
107
-
108
-
109
- _transform(chunk, _encoding, callback) {
110
- if (_lodash.default.isArray(chunk)) {
111
- chunk.filter(pkgItem => {
112
- var _pkgItem$package;
113
-
114
- debug(`streaming remote pkg name ${pkgItem === null || pkgItem === void 0 ? void 0 : (_pkgItem$package = pkgItem.package) === null || _pkgItem$package === void 0 ? void 0 : _pkgItem$package.name}`);
115
- return true;
116
- }).forEach(pkgItem => {
117
- this.push(pkgItem);
118
- });
119
- return callback();
120
- } else {
121
- var _chunk$package;
122
-
123
- debug(`streaming local pkg name ${chunk === null || chunk === void 0 ? void 0 : (_chunk$package = chunk.package) === null || _chunk$package === void 0 ? void 0 : _chunk$package.name}`);
124
- this.push(chunk);
125
- return callback();
126
- }
127
- }
128
-
129
- }
130
19
  /**
131
20
  * Endpoint for npm search v1
132
21
  * Empty value
@@ -134,41 +23,45 @@ class TransFormResults extends _stream.Transform {
134
23
  * req: 'GET /-/v1/search?text=react&size=20&frpom=0&quality=0.65&popularity=0.98&maintenance=0.5'
135
24
  */
136
25
 
137
-
138
26
  function _default(route, auth, storage) {
139
- route.get('/-/v1/search', async (req, res, next) => {
140
- var _storage$searchManage;
27
+ function checkAccess(pkg, auth, remoteUser) {
28
+ return new Promise((resolve, reject) => {
29
+ var _pkg$package;
30
+
31
+ auth.allow_access({
32
+ packageName: pkg === null || pkg === void 0 ? void 0 : (_pkg$package = pkg.package) === null || _pkg$package === void 0 ? void 0 : _pkg$package.name
33
+ }, remoteUser, function (err, allowed) {
34
+ if (err) {
35
+ if (err.status && String(err.status).match(/^4\d\d$/)) {
36
+ // auth plugin returns 4xx user error,
37
+ // that's equivalent of !allowed basically
38
+ allowed = false;
39
+ return resolve(null);
40
+ } else {
41
+ reject(err);
42
+ }
43
+ } else {
44
+ return resolve(allowed ? pkg : null);
45
+ }
46
+ });
47
+ });
48
+ }
141
49
 
50
+ route.get('/-/v1/search', async (req, res, next) => {
142
51
  let [size, from] = ['size', 'from'].map(k => req.query[k]);
52
+ let data;
143
53
  size = parseInt(size, 10) || 20;
144
54
  from = parseInt(from, 10) || 0;
145
- const data = [];
146
- const transformResults = new TransFormResults({
147
- objectMode: true
148
- });
149
- const streamPassThrough = new _stream.PassThrough({
150
- objectMode: true
151
- });
152
- (_storage$searchManage = storage.searchManager) === null || _storage$searchManage === void 0 ? void 0 : _storage$searchManage.search(streamPassThrough, {
153
- query: req.query,
154
- url: req.url
155
- });
156
- const outPutStream = new _stream.PassThrough({
157
- objectMode: true
158
- });
159
- (0, _stream.pipeline)(streamPassThrough, transformResults, outPutStream, err => {
160
- if (err) {
161
- next((0, _commonsApi.getInternalError)(err ? err.message : 'unknown error'));
162
- } else {
163
- debug('Pipeline succeeded.');
164
- }
165
- });
166
- outPutStream.on('data', chunk => {
167
- data.push(chunk);
168
- });
169
- outPutStream.on('finish', async () => {
55
+
56
+ try {
57
+ var _storage$searchManage;
58
+
59
+ data = await ((_storage$searchManage = storage.searchManager) === null || _storage$searchManage === void 0 ? void 0 : _storage$searchManage.search({
60
+ query: req.query,
61
+ url: req.url
62
+ }));
170
63
  debug('stream finish');
171
- const checkAccessPromises = await Promise.all(removeDuplicates(data).map(pkgItem => {
64
+ const checkAccessPromises = await Promise.all(data.map(pkgItem => {
172
65
  return checkAccess(pkgItem, auth, req.remote_user);
173
66
  }));
174
67
  const final = checkAccessPromises.filter(i => !_lodash.default.isNull(i)).slice(from, size);
@@ -181,7 +74,14 @@ function _default(route, auth, storage) {
181
74
  time: new Date().toUTCString()
182
75
  };
183
76
  res.status(_commonsApi.HTTP_STATUS.OK).json(response);
184
- });
77
+ } catch (error) {
78
+ _logger.logger.error({
79
+ error
80
+ }, 'search endpoint has failed @{error.message}');
81
+
82
+ next(next);
83
+ return;
84
+ }
185
85
  });
186
86
  }
187
87
  //# sourceMappingURL=search.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/v1/search.ts"],"names":["debug","removeDuplicates","results","pkgNames","filter","pkg","includes","package","name","push","checkAccess","auth","remoteUser","Promise","resolve","reject","allow_access","packageName","err","allowed","status","String","match","TransFormResults","Transform","constructor","options","_transform","chunk","_encoding","callback","_","isArray","pkgItem","forEach","route","storage","get","req","res","next","size","from","map","k","query","parseInt","data","transformResults","objectMode","streamPassThrough","PassThrough","searchManager","search","url","outPutStream","message","on","checkAccessPromises","all","remote_user","final","i","isNull","slice","logger","length","response","objects","total","time","Date","toUTCString","HTTP_STATUS","OK","json"],"mappings":"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","sourcesContent":["import { Transform, pipeline, PassThrough } from 'stream';\nimport _ from 'lodash';\nimport buildDebug from 'debug';\nimport { Package } from '@verdaccio/types';\nimport { logger } from '@verdaccio/logger';\nimport { IAuth } from '@verdaccio/auth';\nimport { searchUtils } from '@verdaccio/core';\nimport { HTTP_STATUS, getInternalError } from '@verdaccio/commons-api';\nimport { Storage } from '@verdaccio/store';\n\nconst debug = buildDebug('verdaccio:api:search');\n\ntype SearchResults = {\n objects: searchUtils.SearchItemPkg[];\n total: number;\n time: string;\n};\n\n// const personMatch = (person, search) => {\n// if (typeof person === 'string') {\n// return person.includes(search);\n// }\n\n// if (typeof person === 'object') {\n// for (const field of Object.values(person)) {\n// if (typeof field === 'string' && field.includes(search)) {\n// return true;\n// }\n// }\n// }\n\n// return false;\n// };\n\n// const matcher = function (query) {\n// const match = query.match(/author:(.*)/);\n// if (match !== null) {\n// return function (pkg) {\n// return personMatch(pkg.author, match[1]);\n// };\n// }\n\n// // TODO: maintainer, keywords, boost-exact\n// // TODO implement some scoring system for freetext\n// return (pkg) => {\n// return ['name', 'displayName', 'description']\n// .map((k) => {\n// return pkg[k];\n// })\n// .filter((x) => {\n// return x !== undefined;\n// })\n// .some((txt) => {\n// return txt.includes(query);\n// });\n// };\n// };\n\nfunction removeDuplicates(results) {\n const pkgNames: any[] = [];\n return results.filter((pkg) => {\n if (pkgNames.includes(pkg?.package?.name)) {\n return false;\n }\n pkgNames.push(pkg?.package?.name);\n return true;\n });\n}\n\nfunction checkAccess(pkg: any, auth: any, remoteUser): Promise<Package | null> {\n return new Promise((resolve, reject) => {\n auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {\n if (err) {\n if (err.status && String(err.status).match(/^4\\d\\d$/)) {\n // auth plugin returns 4xx user error,\n // that's equivalent of !allowed basically\n allowed = false;\n return resolve(null);\n } else {\n reject(err);\n }\n } else {\n return resolve(allowed ? pkg : null);\n }\n });\n });\n}\n\nclass TransFormResults extends Transform {\n public constructor(options) {\n super(options);\n }\n\n /**\n * Transform either array of packages or a single package into a stream of packages.\n * From uplinks the chunks are array but from local packages are objects.\n * @param {string} chunk\n * @param {string} encoding\n * @param {function} done\n * @returns {void}\n * @override\n */\n public _transform(chunk, _encoding, callback) {\n if (_.isArray(chunk)) {\n (chunk as searchUtils.SearchItem[])\n .filter((pkgItem) => {\n debug(`streaming remote pkg name ${pkgItem?.package?.name}`);\n return true;\n })\n .forEach((pkgItem) => {\n this.push(pkgItem);\n });\n return callback();\n } else {\n debug(`streaming local pkg name ${chunk?.package?.name}`);\n this.push(chunk);\n return callback();\n }\n }\n}\n\n/**\n * Endpoint for npm search v1\n * Empty value\n * - {\"objects\":[],\"total\":0,\"time\":\"Sun Jul 25 2021 14:09:11 GMT+0000 (Coordinated Universal Time)\"}\n * req: 'GET /-/v1/search?text=react&size=20&frpom=0&quality=0.65&popularity=0.98&maintenance=0.5'\n */\nexport default function (route, auth: IAuth, storage: Storage): void {\n route.get('/-/v1/search', async (req, res, next) => {\n let [size, from] = ['size', 'from'].map((k) => req.query[k]);\n\n size = parseInt(size, 10) || 20;\n from = parseInt(from, 10) || 0;\n const data: any[] = [];\n const transformResults = new TransFormResults({ objectMode: true });\n\n const streamPassThrough = new PassThrough({ objectMode: true });\n storage.searchManager?.search(streamPassThrough, {\n query: req.query,\n url: req.url,\n });\n\n const outPutStream = new PassThrough({ objectMode: true });\n pipeline(streamPassThrough, transformResults, outPutStream, (err) => {\n if (err) {\n next(getInternalError(err ? err.message : 'unknown error'));\n } else {\n debug('Pipeline succeeded.');\n }\n });\n\n outPutStream.on('data', (chunk) => {\n data.push(chunk);\n });\n\n outPutStream.on('finish', async () => {\n debug('stream finish');\n const checkAccessPromises: searchUtils.SearchItemPkg[] = await Promise.all(\n removeDuplicates(data).map((pkgItem) => {\n return checkAccess(pkgItem, auth, req.remote_user);\n })\n );\n\n const final: searchUtils.SearchItemPkg[] = checkAccessPromises\n .filter((i) => !_.isNull(i))\n .slice(from, size);\n logger.debug(`search results ${final?.length}`);\n\n const response: SearchResults = {\n objects: final,\n total: final.length,\n time: new Date().toUTCString(),\n };\n\n res.status(HTTP_STATUS.OK).json(response);\n });\n });\n}\n"],"file":"search.js"}
1
+ {"version":3,"sources":["../../src/v1/search.ts"],"names":["debug","route","auth","storage","checkAccess","pkg","remoteUser","Promise","resolve","reject","allow_access","packageName","package","name","err","allowed","status","String","match","get","req","res","next","size","from","map","k","query","data","parseInt","searchManager","search","url","checkAccessPromises","all","pkgItem","remote_user","final","filter","i","_","isNull","slice","logger","length","response","objects","total","time","Date","toUTCString","HTTP_STATUS","OK","json","error"],"mappings":"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","sourcesContent":["import _ from 'lodash';\nimport buildDebug from 'debug';\nimport { logger } from '@verdaccio/logger';\nimport { IAuth } from '@verdaccio/auth';\nimport { searchUtils } from '@verdaccio/core';\nimport { HTTP_STATUS } from '@verdaccio/commons-api';\nimport { Storage } from '@verdaccio/store';\nimport { Package } from '@verdaccio/types';\n\nconst debug = buildDebug('verdaccio:api:search');\n\n/**\n * Endpoint for npm search v1\n * Empty value\n * - {\"objects\":[],\"total\":0,\"time\":\"Sun Jul 25 2021 14:09:11 GMT+0000 (Coordinated Universal Time)\"}\n * req: 'GET /-/v1/search?text=react&size=20&frpom=0&quality=0.65&popularity=0.98&maintenance=0.5'\n */\nexport default function (route, auth: IAuth, storage: Storage): void {\n function checkAccess(pkg: any, auth: any, remoteUser): Promise<Package | null> {\n return new Promise((resolve, reject) => {\n auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {\n if (err) {\n if (err.status && String(err.status).match(/^4\\d\\d$/)) {\n // auth plugin returns 4xx user error,\n // that's equivalent of !allowed basically\n allowed = false;\n return resolve(null);\n } else {\n reject(err);\n }\n } else {\n return resolve(allowed ? pkg : null);\n }\n });\n });\n }\n\n route.get('/-/v1/search', async (req, res, next) => {\n let [size, from] = ['size', 'from'].map((k) => req.query[k]);\n let data;\n\n size = parseInt(size, 10) || 20;\n from = parseInt(from, 10) || 0;\n\n try {\n data = await storage.searchManager?.search({\n query: req.query,\n url: req.url,\n });\n debug('stream finish');\n const checkAccessPromises: searchUtils.SearchItemPkg[] = await Promise.all(\n data.map((pkgItem) => {\n return checkAccess(pkgItem, auth, req.remote_user);\n })\n );\n\n const final: searchUtils.SearchItemPkg[] = checkAccessPromises\n .filter((i) => !_.isNull(i))\n .slice(from, size);\n logger.debug(`search results ${final?.length}`);\n\n const response: searchUtils.SearchResults = {\n objects: final,\n total: final.length,\n time: new Date().toUTCString(),\n };\n\n res.status(HTTP_STATUS.OK).json(response);\n } catch (error) {\n logger.error({ error }, 'search endpoint has failed @{error.message}');\n next(next);\n return;\n }\n });\n}\n"],"file":"search.js"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@verdaccio/api",
3
- "version": "6.0.0-6-next.14",
3
+ "version": "6.0.0-6-next.15",
4
4
  "description": "loaders logic",
5
5
  "main": "./build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -30,14 +30,14 @@
30
30
  },
31
31
  "license": "MIT",
32
32
  "dependencies": {
33
- "@verdaccio/auth": "6.0.0-6-next.11",
33
+ "@verdaccio/auth": "6.0.0-6-next.12",
34
34
  "@verdaccio/commons-api": "11.0.0-6-next.4",
35
35
  "@verdaccio/config": "6.0.0-6-next.8",
36
36
  "@verdaccio/core": "6.0.0-6-next.1",
37
- "@verdaccio/hooks": "6.0.0-6-next.6",
38
- "@verdaccio/logger": "6.0.0-6-next.4",
39
- "@verdaccio/middleware": "6.0.0-6-next.11",
40
- "@verdaccio/store": "6.0.0-6-next.12",
37
+ "@verdaccio/hooks": "6.0.0-6-next.7",
38
+ "@verdaccio/logger": "6.0.0-6-next.5",
39
+ "@verdaccio/middleware": "6.0.0-6-next.12",
40
+ "@verdaccio/store": "6.0.0-6-next.13",
41
41
  "@verdaccio/tarball": "11.0.0-6-next.7",
42
42
  "@verdaccio/utils": "6.0.0-6-next.6",
43
43
  "cookies": "0.8.0",
@@ -48,7 +48,8 @@
48
48
  "semver": "7.3.5"
49
49
  },
50
50
  "devDependencies": {
51
- "@verdaccio/server": "6.0.0-6-next.19",
51
+ "@types/node": "16.9.1",
52
+ "@verdaccio/server": "6.0.0-6-next.20",
52
53
  "@verdaccio/types": "11.0.0-6-next.8",
53
54
  "body-parser": "1.19.0",
54
55
  "lodash": "4.17.21",
package/src/v1/search.ts CHANGED
@@ -1,124 +1,14 @@
1
- import { Transform, pipeline, PassThrough } from 'stream';
2
1
  import _ from 'lodash';
3
2
  import buildDebug from 'debug';
4
- import { Package } from '@verdaccio/types';
5
3
  import { logger } from '@verdaccio/logger';
6
4
  import { IAuth } from '@verdaccio/auth';
7
5
  import { searchUtils } from '@verdaccio/core';
8
- import { HTTP_STATUS, getInternalError } from '@verdaccio/commons-api';
6
+ import { HTTP_STATUS } from '@verdaccio/commons-api';
9
7
  import { Storage } from '@verdaccio/store';
8
+ import { Package } from '@verdaccio/types';
10
9
 
11
10
  const debug = buildDebug('verdaccio:api:search');
12
11
 
13
- type SearchResults = {
14
- objects: searchUtils.SearchItemPkg[];
15
- total: number;
16
- time: string;
17
- };
18
-
19
- // const personMatch = (person, search) => {
20
- // if (typeof person === 'string') {
21
- // return person.includes(search);
22
- // }
23
-
24
- // if (typeof person === 'object') {
25
- // for (const field of Object.values(person)) {
26
- // if (typeof field === 'string' && field.includes(search)) {
27
- // return true;
28
- // }
29
- // }
30
- // }
31
-
32
- // return false;
33
- // };
34
-
35
- // const matcher = function (query) {
36
- // const match = query.match(/author:(.*)/);
37
- // if (match !== null) {
38
- // return function (pkg) {
39
- // return personMatch(pkg.author, match[1]);
40
- // };
41
- // }
42
-
43
- // // TODO: maintainer, keywords, boost-exact
44
- // // TODO implement some scoring system for freetext
45
- // return (pkg) => {
46
- // return ['name', 'displayName', 'description']
47
- // .map((k) => {
48
- // return pkg[k];
49
- // })
50
- // .filter((x) => {
51
- // return x !== undefined;
52
- // })
53
- // .some((txt) => {
54
- // return txt.includes(query);
55
- // });
56
- // };
57
- // };
58
-
59
- function removeDuplicates(results) {
60
- const pkgNames: any[] = [];
61
- return results.filter((pkg) => {
62
- if (pkgNames.includes(pkg?.package?.name)) {
63
- return false;
64
- }
65
- pkgNames.push(pkg?.package?.name);
66
- return true;
67
- });
68
- }
69
-
70
- function checkAccess(pkg: any, auth: any, remoteUser): Promise<Package | null> {
71
- return new Promise((resolve, reject) => {
72
- auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {
73
- if (err) {
74
- if (err.status && String(err.status).match(/^4\d\d$/)) {
75
- // auth plugin returns 4xx user error,
76
- // that's equivalent of !allowed basically
77
- allowed = false;
78
- return resolve(null);
79
- } else {
80
- reject(err);
81
- }
82
- } else {
83
- return resolve(allowed ? pkg : null);
84
- }
85
- });
86
- });
87
- }
88
-
89
- class TransFormResults extends Transform {
90
- public constructor(options) {
91
- super(options);
92
- }
93
-
94
- /**
95
- * Transform either array of packages or a single package into a stream of packages.
96
- * From uplinks the chunks are array but from local packages are objects.
97
- * @param {string} chunk
98
- * @param {string} encoding
99
- * @param {function} done
100
- * @returns {void}
101
- * @override
102
- */
103
- public _transform(chunk, _encoding, callback) {
104
- if (_.isArray(chunk)) {
105
- (chunk as searchUtils.SearchItem[])
106
- .filter((pkgItem) => {
107
- debug(`streaming remote pkg name ${pkgItem?.package?.name}`);
108
- return true;
109
- })
110
- .forEach((pkgItem) => {
111
- this.push(pkgItem);
112
- });
113
- return callback();
114
- } else {
115
- debug(`streaming local pkg name ${chunk?.package?.name}`);
116
- this.push(chunk);
117
- return callback();
118
- }
119
- }
120
- }
121
-
122
12
  /**
123
13
  * Endpoint for npm search v1
124
14
  * Empty value
@@ -126,37 +16,40 @@ class TransFormResults extends Transform {
126
16
  * req: 'GET /-/v1/search?text=react&size=20&frpom=0&quality=0.65&popularity=0.98&maintenance=0.5'
127
17
  */
128
18
  export default function (route, auth: IAuth, storage: Storage): void {
19
+ function checkAccess(pkg: any, auth: any, remoteUser): Promise<Package | null> {
20
+ return new Promise((resolve, reject) => {
21
+ auth.allow_access({ packageName: pkg?.package?.name }, remoteUser, function (err, allowed) {
22
+ if (err) {
23
+ if (err.status && String(err.status).match(/^4\d\d$/)) {
24
+ // auth plugin returns 4xx user error,
25
+ // that's equivalent of !allowed basically
26
+ allowed = false;
27
+ return resolve(null);
28
+ } else {
29
+ reject(err);
30
+ }
31
+ } else {
32
+ return resolve(allowed ? pkg : null);
33
+ }
34
+ });
35
+ });
36
+ }
37
+
129
38
  route.get('/-/v1/search', async (req, res, next) => {
130
39
  let [size, from] = ['size', 'from'].map((k) => req.query[k]);
40
+ let data;
131
41
 
132
42
  size = parseInt(size, 10) || 20;
133
43
  from = parseInt(from, 10) || 0;
134
- const data: any[] = [];
135
- const transformResults = new TransFormResults({ objectMode: true });
136
-
137
- const streamPassThrough = new PassThrough({ objectMode: true });
138
- storage.searchManager?.search(streamPassThrough, {
139
- query: req.query,
140
- url: req.url,
141
- });
142
-
143
- const outPutStream = new PassThrough({ objectMode: true });
144
- pipeline(streamPassThrough, transformResults, outPutStream, (err) => {
145
- if (err) {
146
- next(getInternalError(err ? err.message : 'unknown error'));
147
- } else {
148
- debug('Pipeline succeeded.');
149
- }
150
- });
151
44
 
152
- outPutStream.on('data', (chunk) => {
153
- data.push(chunk);
154
- });
155
-
156
- outPutStream.on('finish', async () => {
45
+ try {
46
+ data = await storage.searchManager?.search({
47
+ query: req.query,
48
+ url: req.url,
49
+ });
157
50
  debug('stream finish');
158
51
  const checkAccessPromises: searchUtils.SearchItemPkg[] = await Promise.all(
159
- removeDuplicates(data).map((pkgItem) => {
52
+ data.map((pkgItem) => {
160
53
  return checkAccess(pkgItem, auth, req.remote_user);
161
54
  })
162
55
  );
@@ -166,13 +59,17 @@ export default function (route, auth: IAuth, storage: Storage): void {
166
59
  .slice(from, size);
167
60
  logger.debug(`search results ${final?.length}`);
168
61
 
169
- const response: SearchResults = {
62
+ const response: searchUtils.SearchResults = {
170
63
  objects: final,
171
64
  total: final.length,
172
65
  time: new Date().toUTCString(),
173
66
  };
174
67
 
175
68
  res.status(HTTP_STATUS.OK).json(response);
176
- });
69
+ } catch (error) {
70
+ logger.error({ error }, 'search endpoint has failed @{error.message}');
71
+ next(next);
72
+ return;
73
+ }
177
74
  });
178
75
  }