refactor(core): Move license endpoints to a decorated controller class (no-changelog) (#8074)

This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2023-12-19 12:13:19 +01:00
committed by GitHub
parent 63a6e7e034
commit a63d94f28c
13 changed files with 224 additions and 193 deletions

View File

@@ -1,129 +1,37 @@
import express from 'express';
import { Container } from 'typedi';
import { Service } from 'typedi';
import { Authorized, Get, Post, RequireGlobalScope, RestController } from '@/decorators';
import { LicenseRequest } from '@/requests';
import { LicenseService } from './license.service';
import { Logger } from '@/Logger';
import * as ResponseHelper from '@/ResponseHelper';
import type { ILicensePostResponse, ILicenseReadResponse } from '@/Interfaces';
import { LicenseService } from './License.service';
import { License } from '@/License';
import type { AuthenticatedRequest, LicenseRequest } from '@/requests';
import { InternalHooks } from '@/InternalHooks';
import { UnauthorizedError } from '@/errors/response-errors/unauthorized.error';
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
@Service()
@Authorized()
@RestController('/license')
export class LicenseController {
constructor(private readonly licenseService: LicenseService) {}
export const licenseController = express.Router();
const OWNER_ROUTES = ['/activate', '/renew'];
/**
* Owner checking
*/
licenseController.use((req: AuthenticatedRequest, res, next) => {
if (OWNER_ROUTES.includes(req.path) && req.user) {
if (!req.user.isOwner) {
Container.get(Logger).info('Non-owner attempted to activate or renew a license', {
userId: req.user.id,
});
ResponseHelper.sendErrorResponse(
res,
new UnauthorizedError('Only an instance owner may activate or renew a license'),
);
return;
}
@Get('/')
async getLicenseData() {
return this.licenseService.getLicenseData();
}
next();
});
/**
* GET /license
* Get the license data, usable by everyone
*/
licenseController.get(
'/',
ResponseHelper.send(async (): Promise<ILicenseReadResponse> => {
return LicenseService.getLicenseData();
}),
);
@Post('/activate')
@RequireGlobalScope('license:manage')
async activateLicense(req: LicenseRequest.Activate) {
const { activationKey } = req.body;
await this.licenseService.activateLicense(activationKey);
return this.getTokenAndData();
}
/**
* POST /license/activate
* Only usable by the instance owner, activates a license.
*/
licenseController.post(
'/activate',
ResponseHelper.send(async (req: LicenseRequest.Activate): Promise<ILicensePostResponse> => {
// Call the license manager activate function and tell it to throw an error
const license = Container.get(License);
try {
await license.activate(req.body.activationKey);
} catch (e) {
const error = e as Error & { errorId?: string };
@Post('/renew')
@RequireGlobalScope('license:manage')
async renewLicense() {
await this.licenseService.renewLicense();
return this.getTokenAndData();
}
let message = 'Failed to activate license';
//override specific error messages (to map License Server vocabulary to n8n terms)
switch (error.errorId ?? 'UNSPECIFIED') {
case 'SCHEMA_VALIDATION':
message = 'Activation key is in the wrong format';
break;
case 'RESERVATION_EXHAUSTED':
message =
'Activation key has been used too many times. Please contact sales@n8n.io if you would like to extend it';
break;
case 'RESERVATION_EXPIRED':
message = 'Activation key has expired';
break;
case 'NOT_FOUND':
case 'RESERVATION_CONFLICT':
message = 'Activation key not found';
break;
case 'RESERVATION_DUPLICATE':
message = 'Activation key has already been used on this instance';
break;
default:
message += `: ${error.message}`;
Container.get(Logger).error(message, { stack: error.stack ?? 'n/a' });
}
throw new BadRequestError(message);
}
// Return the read data, plus the management JWT
return {
managementToken: license.getManagementJwt(),
...(await LicenseService.getLicenseData()),
};
}),
);
/**
* POST /license/renew
* Only usable by instance owner, renews a license
*/
licenseController.post(
'/renew',
ResponseHelper.send(async (): Promise<ILicensePostResponse> => {
// Call the license manager activate function and tell it to throw an error
const license = Container.get(License);
try {
await license.renew();
} catch (e) {
const error = e as Error & { errorId?: string };
// not awaiting so as not to make the endpoint hang
void Container.get(InternalHooks).onLicenseRenewAttempt({ success: false });
if (error instanceof Error) {
throw new BadRequestError(error.message);
}
}
// not awaiting so as not to make the endpoint hang
void Container.get(InternalHooks).onLicenseRenewAttempt({ success: true });
// Return the read data, plus the management JWT
return {
managementToken: license.getManagementJwt(),
...(await LicenseService.getLicenseData()),
};
}),
);
private async getTokenAndData() {
const managementToken = this.licenseService.getManagementJwt();
const data = await this.licenseService.getLicenseData();
return { ...data, managementToken };
}
}