@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 +11 -0
- package/build/v1/search.js +41 -141
- package/build/v1/search.js.map +1 -1
- package/package.json +8 -7
- package/src/v1/search.ts +34 -137
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
|
package/build/v1/search.js
CHANGED
|
@@ -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
|
-
|
|
140
|
-
|
|
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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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(
|
|
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
|
package/build/v1/search.js.map
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
38
|
-
"@verdaccio/logger": "6.0.0-6-next.
|
|
39
|
-
"@verdaccio/middleware": "6.0.0-6-next.
|
|
40
|
-
"@verdaccio/store": "6.0.0-6-next.
|
|
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
|
-
"@
|
|
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
|
|
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
|
-
|
|
153
|
-
data.
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
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
|
}
|