Files
cnpmcore/app/port/controller/package/UpdatePackageController.ts
fengmk2 4427a4fca5 feat: use egg v4 (#747)
BREAKING CHANGE: only support egg >= 4.0.0

the first app on egg v4

https://github.com/eggjs/egg/issues/3644
2025-02-09 15:43:24 +08:00

88 lines
2.9 KiB
TypeScript

import {
UnprocessableEntityError,
BadRequestError,
} from 'egg-errors';
import {
HTTPController,
HTTPMethod,
HTTPMethodEnum,
HTTPParam,
HTTPBody,
Inject,
Context,
EggContext,
} from '@eggjs/tegg';
import { Static, Type } from 'egg-typebox-validate-fengmk2/typebox';
import { AbstractController } from '../AbstractController';
import { FULLNAME_REG_STRING } from '../../../common/PackageUtil';
import { User as UserEntity } from '../../../core/entity/User';
import { PackageManagerService } from '../../../core/service/PackageManagerService';
const MaintainerDataRule = Type.Object({
maintainers: Type.Array(Type.Object({
name: Type.String({ minLength: 1, maxLength: 100 }),
email: Type.String({ format: 'email', maxLength: 400 }),
}), { minItems: 1 }),
});
type Maintainer = Static<typeof MaintainerDataRule>;
@HTTPController()
export class UpdatePackageController extends AbstractController {
@Inject()
private packageManagerService: PackageManagerService;
// https://github.com/npm/cli/blob/latest/lib/commands/owner.js#L191
@HTTPMethod({
// PUT /:fullname/-rev/:rev
path: `/:fullname(${FULLNAME_REG_STRING})/-rev/:rev`,
method: HTTPMethodEnum.PUT,
})
async update(@Context() ctx: EggContext, @HTTPParam() fullname: string, @HTTPBody() data: Maintainer) {
if (this.isNpmCommandValid(ctx, 'unpublish')) {
// ignore it
return { ok: false };
}
// only support update maintainer
if (!this.isNpmCommandValid(ctx, 'owner')) {
const npmCommand = this.getNpmCommand(ctx);
throw new BadRequestError(`header: npm-command expected "owner", but got "${npmCommand}"`);
}
ctx.tValidate(MaintainerDataRule, data);
const ensureRes = await this.ensurePublishAccess(ctx, fullname, true);
const pkg = ensureRes.pkg!;
const registry = await this.packageManagerService.getSourceRegistry(pkg);
// make sure all maintainers exists
const users: UserEntity[] = [];
for (const maintainer of data.maintainers) {
if (registry?.userPrefix && !maintainer.name.startsWith(registry.userPrefix)) {
maintainer.name = `${registry?.userPrefix}${maintainer.name}`;
}
const user = await this.userRepository.findUserByName(maintainer.name);
if (!user) {
throw new UnprocessableEntityError(`Maintainer "${maintainer.name}" not exists`);
}
users.push(user);
}
await this.packageManagerService.replacePackageMaintainersAndDist(pkg, users);
return { ok: true };
}
private getNpmCommand(ctx: EggContext) {
// npm@6: referer: 'xxx [REDACTED]'
// npm@>=7: 'npm-command': 'xxx'
let npmCommand = ctx.get<string>('npm-command');
if (!npmCommand) {
npmCommand = ctx.get<string>('referer').split(' ', 1)[0];
}
return npmCommand;
}
private isNpmCommandValid(ctx: EggContext, expectCommand: string) {
const npmCommand = this.getNpmCommand(ctx);
return npmCommand === expectCommand;
}
}