diff --git a/.changeset/fuzzy-onions-draw.md b/.changeset/fuzzy-onions-draw.md new file mode 100644 index 000000000..9b9901d10 --- /dev/null +++ b/.changeset/fuzzy-onions-draw.md @@ -0,0 +1,10 @@ +--- +'@verdaccio/api': minor +'@verdaccio/fastify-migration': minor +'@verdaccio/hooks': minor +'@verdaccio/logger-prettify': minor +'@verdaccio/proxy': minor +'@verdaccio/store': minor +--- + +abort search request support for proxy diff --git a/packages/api/package.json b/packages/api/package.json index d14a57d96..550e4f714 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -48,6 +48,7 @@ "@verdaccio/store": "workspace:6.0.0-6-next.13", "@verdaccio/tarball": "workspace:11.0.0-6-next.7", "@verdaccio/utils": "workspace:6.0.0-6-next.6", + "abortcontroller-polyfill": "1.7.3", "cookies": "0.8.0", "debug": "4.3.2", "express": "4.17.1", diff --git a/packages/api/src/index.ts b/packages/api/src/index.ts index ea81d56a9..d2d41e44c 100644 --- a/packages/api/src/index.ts +++ b/packages/api/src/index.ts @@ -10,6 +10,7 @@ import { IAuth } from '@verdaccio/auth'; import { Storage } from '@verdaccio/store'; import { Config } from '@verdaccio/types'; import bodyParser from 'body-parser'; +import semver from 'semver'; import whoami from './whoami'; import ping from './ping'; @@ -23,6 +24,10 @@ import profile from './v1/profile'; import token from './v1/token'; import v1Search from './v1/search'; +if (semver.lte(process.version, 'v15.0.0')) { + global.AbortController = require('abortcontroller-polyfill/dist/cjs-ponyfill').AbortController; +} + export default function (config: Config, auth: IAuth, storage: Storage): Router { /* eslint new-cap:off */ const app = express.Router(); diff --git a/packages/api/src/v1/search.ts b/packages/api/src/v1/search.ts index 524e011cf..b5db3b765 100644 --- a/packages/api/src/v1/search.ts +++ b/packages/api/src/v1/search.ts @@ -35,16 +35,23 @@ export default function (route, auth: IAuth, storage: Storage): void { } route.get('/-/v1/search', async (req, res, next) => { - let [size, from] = ['size', 'from'].map((k) => req.query[k]); + const { query, url } = req; + let [size, from] = ['size', 'from'].map((k) => query[k]); let data; + const abort = new AbortController(); + + req.on('aborted', () => { + abort.abort(); + }); size = parseInt(size, 10) || 20; from = parseInt(from, 10) || 0; try { data = await storage.searchManager?.search({ - query: req.query, - url: req.url, + query, + url, + abort, }); debug('stream finish'); const checkAccessPromises: searchUtils.SearchItemPkg[] = await Promise.all( diff --git a/packages/core/server/package.json b/packages/core/server/package.json index e35103ae9..842651727 100644 --- a/packages/core/server/package.json +++ b/packages/core/server/package.json @@ -38,10 +38,12 @@ "@verdaccio/auth": "workspace:6.0.0-6-next.12", "@verdaccio/logger": "workspace:6.0.0-6-next.5", "@verdaccio/store": "workspace:6.0.0-6-next.13", + "abortcontroller-polyfill": "1.7.3", "core-js": "3.17.2", "debug": "4.3.2", "fastify": "3.20.2", - "fastify-plugin": "3.0.0" + "fastify-plugin": "3.0.0", + "semver": "7.3.5" }, "devDependencies": { "@types/node": "16.9.1", diff --git a/packages/core/server/src/endpoints/search.ts b/packages/core/server/src/endpoints/search.ts index 7a053518b..f9b1c8a00 100644 --- a/packages/core/server/src/endpoints/search.ts +++ b/packages/core/server/src/endpoints/search.ts @@ -9,12 +9,18 @@ async function searchRoute(fastify) { // TODO: add validations for query, some parameters are mandatory // TODO: review which query fields are mandatory + const abort = new AbortController(); + + request.on('aborted', () => { + abort.abort(); + }); const { url, query } = request; const storage = fastify.storage; const data = await storage.searchManager?.search({ - query: query, - url: url, + query, + url, + abort, }); logger.http('search endpoint'); diff --git a/packages/core/server/src/index.ts b/packages/core/server/src/index.ts index 7ddad5814..202dc66f6 100644 --- a/packages/core/server/src/index.ts +++ b/packages/core/server/src/index.ts @@ -1 +1,7 @@ +import semver from 'semver'; + +if (semver.lte(process.version, 'v15.0.0')) { + global.AbortController = require('abortcontroller-polyfill/dist/cjs-ponyfill').AbortController; +} + export { default } from './server'; diff --git a/packages/hooks/package.json b/packages/hooks/package.json index de8c20cdc..4f52618cc 100644 --- a/packages/hooks/package.json +++ b/packages/hooks/package.json @@ -35,7 +35,7 @@ "core-js": "3.17.2", "debug": "4.3.2", "handlebars": "4.7.7", - "undici": "4.4.7", + "undici": "4.7.3", "undici-fetch": "1.0.0-rc.4" }, "devDependencies": { diff --git a/packages/logger-prettify/package.json b/packages/logger-prettify/package.json index 7f600a3d3..c5d7b643f 100644 --- a/packages/logger-prettify/package.json +++ b/packages/logger-prettify/package.json @@ -26,7 +26,7 @@ "verdaccio" ], "engines": { - "node": ">=10", + "node": ">=14", "npm": ">=6" }, "scripts": { diff --git a/packages/proxy/package.json b/packages/proxy/package.json index 041913693..20401085d 100644 --- a/packages/proxy/package.json +++ b/packages/proxy/package.json @@ -26,7 +26,7 @@ "verdaccio" ], "engines": { - "node": ">=10", + "node": ">=14", "npm": ">=6" }, "scripts": { @@ -51,7 +51,7 @@ "lodash": "4.17.20", "node-fetch": "2.6.1", "request": "2.87.0", - "undici": "4.4.7", + "undici": "4.7.3", "undici-fetch": "1.0.0-rc.4" }, "devDependencies": { diff --git a/packages/proxy/src/up-storage.ts b/packages/proxy/src/up-storage.ts index 1ab737f72..b0e4baf0f 100644 --- a/packages/proxy/src/up-storage.ts +++ b/packages/proxy/src/up-storage.ts @@ -54,7 +54,7 @@ export type ProxySearchParams = { headers?: Headers; url: string; query?: searchUtils.SearchQuery; - abort?: AbortController; + abort: AbortController; }; export interface IProxy { config: UpLinkConfLocal; diff --git a/packages/store/package.json b/packages/store/package.json index f88e7523c..87eb8afc1 100644 --- a/packages/store/package.json +++ b/packages/store/package.json @@ -61,7 +61,7 @@ "@types/node": "16.9.1", "@verdaccio/mock": "workspace:6.0.0-6-next.9", "@verdaccio/types": "workspace:11.0.0-6-next.8", - "undici": "4.4.7", + "undici": "4.7.3", "undici-fetch": "1.0.0-rc.4", "tmp-promise": "3.0.2" }, diff --git a/packages/store/src/search.ts b/packages/store/src/search.ts index fa1d274f2..b300d9717 100644 --- a/packages/store/src/search.ts +++ b/packages/store/src/search.ts @@ -88,6 +88,12 @@ export class SearchManager { return uplinksList; } + /** + * Handle search on packages and proxies. + * Iterate all proxies configured and search in all endpoints in v2 and pipe all responses + * to a stream, once the proxies request has finished search in local storage for all packages + * (privated and cached). + */ public async search(options: ProxySearchParams): Promise { const transformResults = new TransFormResults({ objectMode: true }); const streamPassThrough = new PassThrough({ objectMode: true }); @@ -148,9 +154,7 @@ export class SearchManager { options: ProxySearchParams, searchPassThrough: PassThrough ): Promise { - // TODO: review how to handle abort - const abortController = new AbortController(); - return uplink.search({ ...options, abort: abortController }).then((bodyStream) => { + return uplink.search({ ...options }).then((bodyStream) => { bodyStream.pipe(searchPassThrough, { end: false }); bodyStream.on('error', (err: VerdaccioError): void => { logger.error( diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5daea35ec..dffff5749 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -199,6 +199,7 @@ importers: '@verdaccio/tarball': workspace:11.0.0-6-next.7 '@verdaccio/types': workspace:11.0.0-6-next.8 '@verdaccio/utils': workspace:6.0.0-6-next.6 + abortcontroller-polyfill: 1.7.3 body-parser: 1.19.0 cookies: 0.8.0 debug: 4.3.2 @@ -217,6 +218,7 @@ importers: '@verdaccio/store': link:../store '@verdaccio/tarball': link:../core/tarball '@verdaccio/utils': link:../utils + abortcontroller-polyfill: 1.7.3 cookies: 0.8.0 debug: 4.3.2 express: 4.17.1 @@ -413,20 +415,24 @@ importers: '@verdaccio/logger': workspace:6.0.0-6-next.5 '@verdaccio/store': workspace:6.0.0-6-next.13 '@verdaccio/types': workspace:11.0.0-6-next.8 + abortcontroller-polyfill: 1.7.3 core-js: 3.17.2 debug: 4.3.2 fastify: 3.20.2 fastify-plugin: 3.0.0 + semver: 7.3.5 ts-node: 10.2.1 dependencies: '@verdaccio/auth': link:../../auth '@verdaccio/config': link:../../config '@verdaccio/logger': link:../../logger '@verdaccio/store': link:../../store + abortcontroller-polyfill: 1.7.3 core-js: 3.17.2 debug: 4.3.2 fastify: 3.20.2 fastify-plugin: 3.0.0 + semver: 7.3.5 devDependencies: '@types/node': 16.9.1 '@verdaccio/types': link:../types @@ -491,7 +497,7 @@ importers: core-js: 3.17.2 debug: 4.3.2 handlebars: 4.7.7 - undici: 4.4.7 + undici: 4.7.3 undici-fetch: 1.0.0-rc.4 dependencies: '@verdaccio/core': link:../core/core @@ -499,7 +505,7 @@ importers: core-js: 3.17.2 debug: 4.3.2 handlebars: 4.7.7 - undici: 4.4.7 + undici: 4.7.3 undici-fetch: 1.0.0-rc.4 devDependencies: '@types/node': 16.9.1 @@ -915,7 +921,7 @@ importers: node-mocks-http: 1.10.1 request: 2.87.0 semver: 7.3.5 - undici: 4.4.7 + undici: 4.7.3 undici-fetch: 1.0.0-rc.4 dependencies: '@verdaccio/config': link:../config @@ -930,7 +936,7 @@ importers: lodash: 4.17.20 node-fetch: 2.6.1 request: 2.87.0 - undici: 4.4.7 + undici: 4.7.3 undici-fetch: 1.0.0-rc.4 devDependencies: '@types/node': 16.9.1 @@ -1032,7 +1038,7 @@ importers: merge2: 1.4.1 semver: 7.1.2 tmp-promise: 3.0.2 - undici: 4.4.7 + undici: 4.7.3 undici-fetch: 1.0.0-rc.4 dependencies: '@verdaccio/config': link:../config @@ -1057,7 +1063,7 @@ importers: '@verdaccio/mock': link:../mock '@verdaccio/types': link:../core/types tmp-promise: 3.0.2 - undici: 4.4.7 + undici: 4.7.3 undici-fetch: 1.0.0-rc.4 packages/tools/benchmark: @@ -20049,14 +20055,14 @@ packages: dependencies: undici: 4.5.1 - /undici/4.4.7: - resolution: {integrity: sha512-41YDu0wuKPhvd2oPDHRe0ufai70O8nOyL6vgpWkv1DUPTwOx59GhZVRvZwinBLAiKJHta/91gSb7wmrDghuJIw==} - engines: {node: '>=12.18'} - /undici/4.5.1: resolution: {integrity: sha512-1Kmphp4SMwVbSauz9xH4gxt0m3sLGs5qRHs/XYgjeO3bNSt6hspDZqMhM8+ETu9ynB5bq9e6mnwcDz+NVCQ3UQ==} engines: {node: '>=12.18'} + /undici/4.7.3: + resolution: {integrity: sha512-ecY0KLuZ3EVbiR+Z2kpxh3V3tLGhkxuNv5jhIYskZXO8KgpwcFxqScIMs6JO523VFnfRnN2Fm6yhLxc+BQvezw==} + engines: {node: '>=12.18'} + /unherit/1.1.3: resolution: {integrity: sha512-Ft16BJcnapDKp0+J/rqFC3Rrk6Y/Ng4nzsC028k2jdDII/rdZ7Wd3pPT/6+vIIxRagwRc9K0IUX0Ra4fKvw+WQ==} dependencies: