UNPKG

cnpmcore

Version:

Private NPM Registry for Enterprise

194 lines 16.1 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; import { PassThrough } from 'node:stream'; import { HTTPContext, Context, HTTPController, HTTPMethod, HTTPMethodEnum, HTTPParam, Inject } from 'egg'; import { NotFoundError } from 'egg/errors'; import { SyncMode } from "../../../common/constants.js"; import { FULLNAME_REG_STRING, getScopeAndName } from "../../../common/PackageUtil.js"; import { AbstractController } from "../AbstractController.js"; let DownloadPackageVersionTarController = class DownloadPackageVersionTarController extends AbstractController { // Support OPTIONS Request on tgz download async downloadForOptions(ctx) { ctx.set('access-control-allow-origin', '*'); ctx.set('access-control-allow-methods', 'GET,HEAD'); ctx.status = 204; } async download(ctx, fullname, filenameWithVersion) { const [scope, name] = getScopeAndName(fullname); await this.userRoleManager.checkReadAccess(ctx, scope, name); // tgz file storeKey: `/packages/${this.fullname}/${version}/${filename}` const version = this.getAndCheckVersionFromFilename(ctx, fullname, filenameWithVersion); const storeKey = `/packages/${fullname}/${version}/${filenameWithVersion}.tgz`; const downloadUrl = await this.nfsAdapter.getDownloadUrl(storeKey); if (this.config.cnpmcore.syncMode === SyncMode.all && downloadUrl) { // try nfs url first, avoid db query this.packageManagerService.plusPackageVersionCounter(fullname, version); ctx.redirect(downloadUrl); return; } // check package version in database const allowSync = this.getAllowSync(ctx); let pkg; let packageVersion; try { pkg = await this.getPackageEntityByFullname(fullname, allowSync); packageVersion = await this.getPackageVersionEntity(pkg, version, allowSync); } catch (error) { if (this.config.cnpmcore.syncMode === SyncMode.proxy) { // proxy mode package version not found. const tgzStream = await this.getTgzProxyStream(ctx, fullname, version); this.packageManagerService.plusPackageVersionCounter(fullname, version); const passThroughRemoteStream = new PassThrough(); tgzStream.pipe(passThroughRemoteStream); ctx.attachment(`${filenameWithVersion}.tgz`); return passThroughRemoteStream; } throw error; } // read by nfs url if (downloadUrl) { this.packageManagerService.plusPackageVersionCounter(fullname, version); ctx.redirect(downloadUrl); return; } // read from database ctx.logger.info('[PackageController:downloadVersionTar] %s@%s, packageVersionId: %s', pkg.fullname, version, packageVersion.packageVersionId); const urlOrStream = await this.packageManagerService.downloadPackageVersionTar(packageVersion); if (!urlOrStream) { throw new NotFoundError(`"${filenameWithVersion}.tgz" not found`); } this.packageManagerService.plusPackageVersionCounter(fullname, version); if (typeof urlOrStream === 'string') { ctx.redirect(urlOrStream); return; } ctx.attachment(`${filenameWithVersion}.tgz`); return urlOrStream; } async deprecatedDownload(ctx, fullname, fullnameWithVersion) { // /@emotion/utils/download/@emotion/utils-0.11.3.tgz // => /@emotion/utils/-/utils-0.11.3.tgz const filenameWithVersion = getScopeAndName(fullnameWithVersion)[1]; return await this.download(ctx, fullname, filenameWithVersion); } async getTgzProxyStream(ctx, fullname, version) { const { headers, status, res } = await this.proxyCacheService.getPackageVersionTarResponse(fullname, ctx); ctx.status = status; ctx.set(headers); ctx.runInBackground(async () => { const task = await this.packageSyncerService.createTask(fullname, { authorIp: ctx.ip, authorId: `pid_${process.pid}`, tips: `Sync specific version in proxy mode cause by "${ctx.href}"`, skipDependencies: true, specificVersions: [version], }); ctx.logger.info('[DownloadPackageVersionTarController.createSyncTask:success] taskId: %s, fullname: %s', task.taskId, fullname); }); return res; } // Compatible Verdaccio path style async downloadVerdaccioPathStyleorOptions(ctx) { return this.downloadForOptions(ctx); } async downloadVerdaccioPathStyle(ctx, fullname, filenameWithVersion) { return this.download(ctx, fullname, filenameWithVersion); } }; __decorate([ Inject(), __metadata("design:type", Function) ], DownloadPackageVersionTarController.prototype, "packageManagerService", void 0); __decorate([ Inject(), __metadata("design:type", Function) ], DownloadPackageVersionTarController.prototype, "registryManagerService", void 0); __decorate([ Inject(), __metadata("design:type", Function) ], DownloadPackageVersionTarController.prototype, "proxyCacheService", void 0); __decorate([ Inject(), __metadata("design:type", Function) ], DownloadPackageVersionTarController.prototype, "packageSyncerService", void 0); __decorate([ Inject(), __metadata("design:type", Function) ], DownloadPackageVersionTarController.prototype, "nfsAdapter", void 0); __decorate([ HTTPMethod({ // GET /:fullname/-/:filenameWithVersion.tgz path: `/:fullname(${FULLNAME_REG_STRING})/-/:filenameWithVersion.tgz`, method: HTTPMethodEnum.OPTIONS, }), __param(0, HTTPContext()), __metadata("design:type", Function), __metadata("design:paramtypes", [Context]), __metadata("design:returntype", Promise) ], DownloadPackageVersionTarController.prototype, "downloadForOptions", null); __decorate([ HTTPMethod({ // GET /:fullname/-/:filenameWithVersion.tgz path: `/:fullname(${FULLNAME_REG_STRING})/-/:filenameWithVersion.tgz`, method: HTTPMethodEnum.GET, }), __param(0, HTTPContext()), __param(1, HTTPParam()), __param(2, HTTPParam()), __metadata("design:type", Function), __metadata("design:paramtypes", [Context, String, String]), __metadata("design:returntype", Promise) ], DownloadPackageVersionTarController.prototype, "download", null); __decorate([ HTTPMethod({ // GET /:fullname/download/:fullnameWithVersion.tgz path: `/:fullname(${FULLNAME_REG_STRING})/download/:fullnameWithVersion+.tgz`, method: HTTPMethodEnum.GET, }), __param(0, HTTPContext()), __param(1, HTTPParam()), __param(2, HTTPParam()), __metadata("design:type", Function), __metadata("design:paramtypes", [Context, String, String]), __metadata("design:returntype", Promise) ], DownloadPackageVersionTarController.prototype, "deprecatedDownload", null); __decorate([ HTTPMethod({ // GET /:fullname/-/:scope/:filenameWithVersion.tgz path: `/:fullname(${FULLNAME_REG_STRING})/-/:scope/:filenameWithVersion.tgz`, method: HTTPMethodEnum.OPTIONS, }), __param(0, HTTPContext()), __metadata("design:type", Function), __metadata("design:paramtypes", [Context]), __metadata("design:returntype", Promise) ], DownloadPackageVersionTarController.prototype, "downloadVerdaccioPathStyleorOptions", null); __decorate([ HTTPMethod({ // GET /:fullname/-/:scope/:filenameWithVersion.tgz path: `/:fullname(${FULLNAME_REG_STRING})/-/:scope/:filenameWithVersion.tgz`, method: HTTPMethodEnum.GET, }), __param(0, HTTPContext()), __param(1, HTTPParam()), __param(2, HTTPParam()), __metadata("design:type", Function), __metadata("design:paramtypes", [Context, String, String]), __metadata("design:returntype", Promise) ], DownloadPackageVersionTarController.prototype, "downloadVerdaccioPathStyle", null); DownloadPackageVersionTarController = __decorate([ HTTPController() ], DownloadPackageVersionTarController); export { DownloadPackageVersionTarController }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRG93bmxvYWRQYWNrYWdlVmVyc2lvblRhci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL2FwcC9wb3J0L2NvbnRyb2xsZXIvcGFja2FnZS9Eb3dubG9hZFBhY2thZ2VWZXJzaW9uVGFyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7OztBQUFBLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFMUMsT0FBTyxFQUFFLFdBQVcsRUFBRSxPQUFPLEVBQUUsY0FBYyxFQUFFLFVBQVUsRUFBRSxjQUFjLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxNQUFNLEtBQUssQ0FBQztBQUMxRyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sWUFBWSxDQUFDO0FBRzNDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsZUFBZSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFLdEYsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFHdkQsSUFBTSxtQ0FBbUMsR0FBekMsTUFBTSxtQ0FBb0MsU0FBUSxrQkFBa0I7SUFZekUsMENBQTBDO0lBTXBDLEFBQU4sS0FBSyxDQUFDLGtCQUFrQixDQUFnQixHQUFZO1FBQ2xELEdBQUcsQ0FBQyxHQUFHLENBQUMsNkJBQTZCLEVBQUUsR0FBRyxDQUFDLENBQUM7UUFDNUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyw4QkFBOEIsRUFBRSxVQUFVLENBQUMsQ0FBQztRQUNwRCxHQUFHLENBQUMsTUFBTSxHQUFHLEdBQUcsQ0FBQztJQUNuQixDQUFDO0lBT0ssQUFBTixLQUFLLENBQUMsUUFBUSxDQUFnQixHQUFZLEVBQWUsUUFBZ0IsRUFBZSxtQkFBMkI7UUFDakgsTUFBTSxDQUFDLEtBQUssRUFBRSxJQUFJLENBQUMsR0FBRyxlQUFlLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDaEQsTUFB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