cnpmcore
Version:
Private NPM Registry for Enterprise
194 lines • 16.1 kB
JavaScript
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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