diff --git a/packages/cli/package.json b/packages/cli/package.json index 3009f3248b7..26dd1ace07a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -112,7 +112,7 @@ "@n8n/typeorm": "catalog:", "@n8n/utils": "workspace:*", "@n8n_io/ai-assistant-sdk": "catalog:", - "@n8n_io/license-sdk": "2.24.1", + "@n8n_io/license-sdk": "2.25.0", "@parcel/watcher": "^2.5.1", "@rudderstack/rudder-sdk-node": "3.0.0", "@sentry/node": "catalog:", diff --git a/packages/cli/src/__tests__/license.test.ts b/packages/cli/src/__tests__/license.test.ts index 62687c81389..287dea15b58 100644 --- a/packages/cli/src/__tests__/license.test.ts +++ b/packages/cli/src/__tests__/license.test.ts @@ -101,17 +101,24 @@ describe('License', () => { ); }); - test('attempts to activate license with provided key', async () => { + test('attempts to activate license with provided key (initial activation)', async () => { await license.activate(MOCK_ACTIVATION_KEY); - expect(LicenseManager.prototype.activate).toHaveBeenCalledWith(MOCK_ACTIVATION_KEY, undefined); + expect(LicenseManager.prototype.activate).toHaveBeenCalledWith(MOCK_ACTIVATION_KEY, { + eulaUri: undefined, + email: undefined, + }); }); - test('attempts to activate license with eulaUri', async () => { + test('attempts to activate license with eulaUri and userEmail (EULA acceptance)', async () => { const eulaUri = 'https://n8n.io/legal/eula/'; - await license.activate(MOCK_ACTIVATION_KEY, eulaUri); + const userEmail = 'user@example.com'; + await license.activate(MOCK_ACTIVATION_KEY, eulaUri, userEmail); - expect(LicenseManager.prototype.activate).toHaveBeenCalledWith(MOCK_ACTIVATION_KEY, eulaUri); + expect(LicenseManager.prototype.activate).toHaveBeenCalledWith(MOCK_ACTIVATION_KEY, { + eulaUri, + email: userEmail, + }); }); test('renews license', async () => { diff --git a/packages/cli/src/license.ts b/packages/cli/src/license.ts index caa9baf612d..196528fd331 100644 --- a/packages/cli/src/license.ts +++ b/packages/cli/src/license.ts @@ -198,12 +198,14 @@ export class License implements LicenseProvider { } } - async activate(activationKey: string, eulaUri?: string): Promise { + async activate(activationKey: string): Promise; + async activate(activationKey: string, eulaUri: string, userEmail: string): Promise; + async activate(activationKey: string, eulaUri?: string, userEmail?: string): Promise { if (!this.manager) { return; } - await this.manager.activate(activationKey, eulaUri); + await this.manager.activate(activationKey, { eulaUri, email: userEmail }); this.logger.debug('License activated'); } diff --git a/packages/cli/src/license/__tests__/license.service.test.ts b/packages/cli/src/license/__tests__/license.service.test.ts index 1d9fe9c2aab..d700632b8d9 100644 --- a/packages/cli/src/license/__tests__/license.service.test.ts +++ b/packages/cli/src/license/__tests__/license.service.test.ts @@ -65,16 +65,24 @@ describe('LicenseService', () => { }); describe('activateLicense', () => { - it('should activate license without eulaUri', async () => { + it('should activate license without eulaUri (initial activation)', async () => { license.activate.mockResolvedValueOnce(); await licenseService.activateLicense('activation-key'); - expect(license.activate).toHaveBeenCalledWith('activation-key', undefined); + expect(license.activate).toHaveBeenCalledWith('activation-key'); }); - it('should activate license with eulaUri', async () => { + it('should activate license with eulaUri and userEmail (EULA acceptance)', async () => { license.activate.mockResolvedValueOnce(); - await licenseService.activateLicense('activation-key', 'https://n8n.io/legal/eula/'); - expect(license.activate).toHaveBeenCalledWith('activation-key', 'https://n8n.io/legal/eula/'); + await licenseService.activateLicense( + 'activation-key', + 'https://n8n.io/legal/eula/', + 'user@example.com', + ); + expect(license.activate).toHaveBeenCalledWith( + 'activation-key', + 'https://n8n.io/legal/eula/', + 'user@example.com', + ); }); it('should throw LicenseEulaRequiredError when EULA_REQUIRED error occurs', async () => { diff --git a/packages/cli/src/license/license.controller.ts b/packages/cli/src/license/license.controller.ts index d1f1a8ff535..d00ff7407f9 100644 --- a/packages/cli/src/license/license.controller.ts +++ b/packages/cli/src/license/license.controller.ts @@ -59,7 +59,11 @@ export class LicenseController { @GlobalScope('license:manage') async activateLicense(req: LicenseRequest.Activate) { const { activationKey, eulaUri } = req.body; - await this.licenseService.activateLicense(activationKey, eulaUri); + if (eulaUri) { + await this.licenseService.activateLicense(activationKey, eulaUri, req.user.email); + } else { + await this.licenseService.activateLicense(activationKey); + } return await this.getTokenAndData(); } diff --git a/packages/cli/src/license/license.service.ts b/packages/cli/src/license/license.service.ts index 149d2825c0c..ccfe717649e 100644 --- a/packages/cli/src/license/license.service.ts +++ b/packages/cli/src/license/license.service.ts @@ -109,9 +109,23 @@ export class LicenseService { return this.license.getManagementJwt(); } - async activateLicense(activationKey: string, eulaUri?: string) { + // Overload signatures + async activateLicense(activationKey: string): Promise; + async activateLicense(activationKey: string, eulaUri: string, userEmail: string): Promise; + // Implementation signature + async activateLicense( + activationKey: string, + eulaUri?: string, + userEmail?: string, + ): Promise { try { - await this.license.activate(activationKey, eulaUri); + if (eulaUri && userEmail) { + await this.license.activate(activationKey, eulaUri, userEmail); + } else if (!eulaUri && !userEmail) { + await this.license.activate(activationKey); + } else { + throw new BadRequestError('When providing eulaUri, userEmail is required'); + } } catch (e) { // Check if this is a EULA_REQUIRED error from license server if (this.isEulaRequiredError(e)) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 56600418504..1d91b7d0161 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1680,8 +1680,8 @@ importers: specifier: 'catalog:' version: 1.20.0 '@n8n_io/license-sdk': - specifier: 2.24.1 - version: 2.24.1 + specifier: 2.25.0 + version: 2.25.0 '@parcel/watcher': specifier: ^2.5.1 version: 2.5.1 @@ -6520,8 +6520,8 @@ packages: resolution: {integrity: sha512-uXa6EDufQIV46tvqQ222441dWZYLnT219HC5H8grfAVd6Mpp2Rs4NoBM6YPWnGFWBp8XVsgXUud0EKRbJsw0eA==} engines: {node: '>=20.15', pnpm: '>=8.14'} - '@n8n_io/license-sdk@2.24.1': - resolution: {integrity: sha512-nrITEzOmFonFXD5XtzHjpY/s1kpKksYQoY5HBqhzRkGq3ldEO3nLKDl6RG9AEtkKKlKkid3lzviZJCIDINZlQw==} + '@n8n_io/license-sdk@2.25.0': + resolution: {integrity: sha512-6pNo09XHhBb+igaHfpuvLez+IxcerzRWMFUH1eqAOOE0Uf5orQp8j5p5RE7ohNZYtxlMoL/PcsQIXK5uHBycwg==} engines: {node: '>=18.12.1'} '@n8n_io/riot-tmpl@4.0.1': @@ -23037,7 +23037,7 @@ snapshots: '@n8n_io/ai-assistant-sdk@1.20.0': {} - '@n8n_io/license-sdk@2.24.1': + '@n8n_io/license-sdk@2.25.0': dependencies: crypto-js: 4.2.0 node-machine-id: 1.1.12