mirror of
https://github.com/n8n-io/n8n.git
synced 2025-01-05 18:07:27 -08:00
137 lines
4 KiB
TypeScript
137 lines
4 KiB
TypeScript
import axios, { AxiosError } from 'axios';
|
|
import { ensureError } from 'n8n-workflow';
|
|
import { Service } from 'typedi';
|
|
|
|
import type { User } from '@/databases/entities/user';
|
|
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
|
|
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
|
import { EventService } from '@/events/event.service';
|
|
import { License } from '@/license';
|
|
import { Logger } from '@/logging/logger.service';
|
|
import { UrlService } from '@/services/url.service';
|
|
|
|
type LicenseError = Error & { errorId?: keyof typeof LicenseErrors };
|
|
|
|
export const LicenseErrors = {
|
|
SCHEMA_VALIDATION: 'Activation key is in the wrong format',
|
|
RESERVATION_EXHAUSTED: 'Activation key has been used too many times',
|
|
RESERVATION_EXPIRED: 'Activation key has expired',
|
|
NOT_FOUND: 'Activation key not found',
|
|
RESERVATION_CONFLICT: 'Activation key not found',
|
|
RESERVATION_DUPLICATE: 'Activation key has already been used on this instance',
|
|
};
|
|
|
|
@Service()
|
|
export class LicenseService {
|
|
constructor(
|
|
private readonly logger: Logger,
|
|
private readonly license: License,
|
|
private readonly workflowRepository: WorkflowRepository,
|
|
private readonly urlService: UrlService,
|
|
private readonly eventService: EventService,
|
|
) {}
|
|
|
|
async getLicenseData() {
|
|
const triggerCount = await this.workflowRepository.getActiveTriggerCount();
|
|
const mainPlan = this.license.getMainPlan();
|
|
|
|
return {
|
|
usage: {
|
|
activeWorkflowTriggers: {
|
|
value: triggerCount,
|
|
limit: this.license.getTriggerLimit(),
|
|
warningThreshold: 0.8,
|
|
},
|
|
},
|
|
license: {
|
|
planId: mainPlan?.productId ?? '',
|
|
planName: this.license.getPlanName(),
|
|
},
|
|
};
|
|
}
|
|
|
|
async requestEnterpriseTrial(user: User) {
|
|
await axios.post('https://enterprise.n8n.io/enterprise-trial', {
|
|
licenseType: 'enterprise',
|
|
firstName: user.firstName,
|
|
lastName: user.lastName,
|
|
email: user.email,
|
|
instanceUrl: this.urlService.getWebhookBaseUrl(),
|
|
});
|
|
}
|
|
|
|
async registerCommunityEdition({
|
|
userId,
|
|
email,
|
|
instanceId,
|
|
instanceUrl,
|
|
licenseType,
|
|
}: {
|
|
userId: User['id'];
|
|
email: string;
|
|
instanceId: string;
|
|
instanceUrl: string;
|
|
licenseType: string;
|
|
}): Promise<{ title: string; text: string }> {
|
|
try {
|
|
const {
|
|
data: { licenseKey, ...rest },
|
|
} = await axios.post<{ title: string; text: string; licenseKey: string }>(
|
|
'https://enterprise.n8n.io/community-registered',
|
|
{
|
|
email,
|
|
instanceId,
|
|
instanceUrl,
|
|
licenseType,
|
|
},
|
|
);
|
|
this.eventService.emit('license-community-plus-registered', { userId, email, licenseKey });
|
|
return rest;
|
|
} catch (e: unknown) {
|
|
if (e instanceof AxiosError) {
|
|
const error = e as AxiosError<{ message: string }>;
|
|
const errorMsg = error.response?.data?.message ?? e.message;
|
|
throw new BadRequestError('Failed to register community edition: ' + errorMsg);
|
|
} else {
|
|
this.logger.error('Failed to register community edition', { error: ensureError(e) });
|
|
throw new BadRequestError('Failed to register community edition');
|
|
}
|
|
}
|
|
}
|
|
|
|
getManagementJwt(): string {
|
|
return this.license.getManagementJwt();
|
|
}
|
|
|
|
async activateLicense(activationKey: string) {
|
|
try {
|
|
await this.license.activate(activationKey);
|
|
} catch (e) {
|
|
const message = this.mapErrorMessage(e as LicenseError, 'activate');
|
|
throw new BadRequestError(message);
|
|
}
|
|
}
|
|
|
|
async renewLicense() {
|
|
try {
|
|
await this.license.renew();
|
|
} catch (e) {
|
|
const message = this.mapErrorMessage(e as LicenseError, 'renew');
|
|
|
|
this.eventService.emit('license-renewal-attempted', { success: false });
|
|
throw new BadRequestError(message);
|
|
}
|
|
|
|
this.eventService.emit('license-renewal-attempted', { success: true });
|
|
}
|
|
|
|
private mapErrorMessage(error: LicenseError, action: 'activate' | 'renew') {
|
|
let message = error.errorId && LicenseErrors[error.errorId];
|
|
if (!message) {
|
|
message = `Failed to ${action} license: ${error.message}`;
|
|
this.logger.error(message, { stack: error.stack ?? 'n/a' });
|
|
}
|
|
return message;
|
|
}
|
|
}
|