feat(core): Make detaching floatable entitlements on shutdown configurable (#14266)

Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
This commit is contained in:
Cornelius Suermann
2025-04-04 17:35:32 +02:00
committed by GitHub
co-authored by Iván Ovejero
parent 39e2d35a71
commit c9565fc0be
9 changed files with 55 additions and 48 deletions
+1 -1
View File
@@ -25,7 +25,7 @@ function makeDateWithHourOffset(offsetInHours: number): Date {
const licenseConfig: GlobalConfig['license'] = {
serverUrl: MOCK_SERVER_URL,
autoRenewalEnabled: true,
autoRenewOffset: MOCK_RENEW_OFFSET,
detachFloatingOnShutdown: true,
activationKey: MOCK_ACTIVATION_KEY,
tenantId: 1,
cert: '',
+3 -13
View File
@@ -1,13 +1,11 @@
import { Container } from '@n8n/di';
import { SETTINGS_LICENSE_CERT_KEY } from '@/constants';
import { SettingsRepository } from '@/databases/repositories/settings.repository';
import { License } from '@/license';
import { BaseCommand } from '../base-command';
export class ClearLicenseCommand extends BaseCommand {
static description = 'Clear license';
static description = 'Clear local license certificate';
static examples = ['$ n8n license:clear'];
@@ -17,20 +15,12 @@ export class ClearLicenseCommand extends BaseCommand {
// Attempt to invoke shutdown() to force any floating entitlements to be released
const license = Container.get(License);
await license.init({ isCli: true });
try {
await license.shutdown();
} catch {
this.logger.info('License shutdown failed. Continuing with clearing license from database.');
}
await Container.get(SettingsRepository).delete({
key: SETTINGS_LICENSE_CERT_KEY,
});
await license.clear();
this.logger.info('Done. Restart n8n to take effect.');
}
async catch(error: Error) {
this.logger.error('Error updating database. See log messages for details.');
this.logger.error('Error. See log messages for details.');
this.logger.error('\nGOT ERROR');
this.logger.info('====================================');
this.logger.error(error.message);
+12 -1
View File
@@ -14,6 +14,7 @@ import {
LICENSE_QUOTAS,
N8N_VERSION,
SETTINGS_LICENSE_CERT_KEY,
Time,
UNLIMITED_LICENSE_QUOTA,
} from './constants';
import type { BooleanLicenseFeature, NumericLicenseFeature } from './interfaces';
@@ -60,7 +61,7 @@ export class License {
const isMainInstance = instanceType === 'main';
const server = this.globalConfig.license.serverUrl;
const offlineMode = !isMainInstance;
const autoRenewOffset = this.globalConfig.license.autoRenewOffset;
const autoRenewOffset = 72 * Time.hours.toSeconds;
const saveCertStr = isMainInstance
? async (value: TLicenseBlock) => await this.saveCertStr(value)
: async () => {};
@@ -92,6 +93,7 @@ export class License {
autoRenewEnabled: shouldRenew,
renewOnInit: shouldRenew,
autoRenewOffset,
detachFloatingOnShutdown: this.globalConfig.license.detachFloatingOnShutdown,
offlineMode,
logger: this.logger,
loadCertStr: async () => await this.loadCertStr(),
@@ -194,6 +196,15 @@ export class License {
this.logger.debug('License renewed');
}
async clear() {
if (!this.manager) {
return;
}
await this.manager.clear();
this.logger.info('License cleared');
}
@OnShutdown()
async shutdown() {
// Shut down License manager to unclaim any floating entitlements