Skip to content

Commit fce4557

Browse files
authored
Merge branch 'master' into feat/public-api-credential-project-id
2 parents 82fa1c0 + 83ea8e1 commit fce4557

27 files changed

Lines changed: 735 additions & 250 deletions

packages/cli/src/events/__tests__/telemetry-event-relay.test.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1155,7 +1155,7 @@ describe('TelemetryEventRelay', () => {
11551155
});
11561156

11571157
describe('workflow execution events', () => {
1158-
it('should track on `first-production-workflow-succeeded` event', () => {
1158+
it('should track on `first-production-workflow-succeeded` event for personal project', () => {
11591159
const event: RelayEventMap['first-production-workflow-succeeded'] = {
11601160
projectId: 'project123',
11611161
workflowId: 'workflow123',
@@ -1171,6 +1171,22 @@ describe('TelemetryEventRelay', () => {
11711171
});
11721172
});
11731173

1174+
it('should track on `first-production-workflow-succeeded` event for team project with null userId', () => {
1175+
const event: RelayEventMap['first-production-workflow-succeeded'] = {
1176+
projectId: 'project123',
1177+
workflowId: 'workflow123',
1178+
userId: null,
1179+
};
1180+
1181+
eventService.emit('first-production-workflow-succeeded', event);
1182+
1183+
expect(telemetry.track).toHaveBeenCalledWith('Workflow first prod success', {
1184+
project_id: 'project123',
1185+
workflow_id: 'workflow123',
1186+
user_id: undefined,
1187+
});
1188+
});
1189+
11741190
it('should track on `first-workflow-data-loaded` event', () => {
11751191
const event: RelayEventMap['first-workflow-data-loaded'] = {
11761192
userId: 'user123',

packages/cli/src/events/maps/relay.event-map.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export type RelayEventMap = {
3939
'first-production-workflow-succeeded': {
4040
projectId: string;
4141
workflowId: string;
42-
userId: string;
42+
userId: string | null;
4343
};
4444

4545
'first-workflow-data-loaded': {

packages/cli/src/events/relays/telemetry.event-relay.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -936,7 +936,7 @@ export class TelemetryEventRelay extends EventRelay {
936936
this.telemetry.track('Workflow first prod success', {
937937
project_id: projectId,
938938
workflow_id: workflowId,
939-
user_id: userId,
939+
user_id: userId ?? undefined,
940940
});
941941
}
942942

packages/cli/src/modules/chat-hub/chat-hub-workflow.service.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ export class ChatHubWorkflowService {
7777

7878
const newWorkflow = new WorkflowEntity();
7979

80+
// Chat workflows are created as archived to hide them
81+
// from the user by default while they are being run.
82+
newWorkflow.isArchived = true;
83+
8084
newWorkflow.versionId = uuidv4();
8185
newWorkflow.name = `Chat ${sessionId}`;
8286
newWorkflow.active = false;
@@ -409,7 +413,7 @@ export class ChatHubWorkflowService {
409413
parameters: {},
410414
type: CHAT_TRIGGER_NODE_TYPE,
411415
typeVersion: 1.4,
412-
position: [0, 0],
416+
position: [-448, -112],
413417
id: uuidv4(),
414418
name: NODE_NAMES.CHAT_TRIGGER,
415419
webhookId: uuidv4(),
@@ -432,7 +436,7 @@ export class ChatHubWorkflowService {
432436
},
433437
type: AGENT_LANGCHAIN_NODE_TYPE,
434438
typeVersion: 3,
435-
position: [600, 0],
439+
position: [608, 0],
436440
id: uuidv4(),
437441
name: NODE_NAMES.REPLY_AGENT,
438442
};
@@ -448,7 +452,7 @@ export class ChatHubWorkflowService {
448452

449453
const { provider, model } = conversationModel;
450454
const common = {
451-
position: [600, 300] satisfies [number, number],
455+
position: [608, 304] satisfies [number, number],
452456
id: uuidv4(),
453457
name: NODE_NAMES.CHAT_MODEL,
454458
credentials,
@@ -590,7 +594,7 @@ export class ChatHubWorkflowService {
590594
},
591595
type: MEMORY_BUFFER_WINDOW_NODE_TYPE,
592596
typeVersion: 1.3,
593-
position: [480, 208],
597+
position: [224, 304],
594598
id: uuidv4(),
595599
name: NODE_NAMES.MEMORY,
596600
};
@@ -623,7 +627,7 @@ export class ChatHubWorkflowService {
623627
},
624628
type: MEMORY_MANAGER_NODE_TYPE,
625629
typeVersion: 1.1,
626-
position: [224, 0],
630+
position: [-192, 48],
627631
id: uuidv4(),
628632
name: NODE_NAMES.RESTORE_CHAT_MEMORY,
629633
};
@@ -653,7 +657,7 @@ export class ChatHubWorkflowService {
653657
},
654658
type: MERGE_NODE_TYPE,
655659
typeVersion: 3.2,
656-
position: [224, -100],
660+
position: [224, -96],
657661
id: uuidv4(),
658662
name: NODE_NAMES.MERGE,
659663
};

packages/cli/src/services/__tests__/frontend.service.test.ts

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import { Container } from '@n8n/di';
44
import { mock } from 'jest-mock-extended';
55
import type { BinaryDataConfig, InstanceSettings } from 'n8n-core';
66

7-
import { N8N_VERSION } from '@/constants';
8-
97
import type { CredentialTypes } from '@/credential-types';
108
import type { CredentialsOverwrites } from '@/credentials-overwrites';
119
import type { License } from '@/license';
@@ -206,42 +204,22 @@ describe('FrontendService', () => {
206204
it('should return public settings', () => {
207205
const expectedPublicSettings: PublicFrontendSettings = {
208206
settingsMode: 'public',
209-
instanceId: instanceSettings.instanceId,
210-
defaultLocale: globalConfig.defaultLocale,
211-
versionCli: N8N_VERSION,
212-
releaseChannel: globalConfig.generic.releaseChannel,
213-
versionNotifications: {
214-
enabled: globalConfig.versionNotifications.enabled,
215-
endpoint: globalConfig.versionNotifications.endpoint,
216-
whatsNewEnabled: globalConfig.versionNotifications.whatsNewEnabled,
217-
whatsNewEndpoint: globalConfig.versionNotifications.whatsNewEndpoint,
218-
infoUrl: globalConfig.versionNotifications.infoUrl,
219-
},
220207
userManagement: {
221-
quota: 100,
222208
smtpSetup: false,
223209
showSetupOnFirstLoad: true,
224210
authenticationMethod: 'email',
225211
},
226212
sso: {
227-
saml: { loginEnabled: false, loginLabel: '' },
213+
saml: { loginEnabled: false },
228214
ldap: { loginEnabled: false, loginLabel: '' },
229215
oidc: {
230216
loginEnabled: false,
231217
loginUrl: 'http://localhost:5678/rest/sso/oidc/login',
232-
callbackUrl: 'http://localhost:5678/rest/sso/oidc/callback',
233218
},
234219
},
235-
mfa: { enabled: false, enforced: false },
236220
authCookie: { secure: false },
237-
oauthCallbackUrls: {
238-
oauth1: 'http://localhost:5678/rest/oauth1-credential/callback',
239-
oauth2: 'http://localhost:5678/rest/oauth2-credential/callback',
240-
},
241-
banners: { dismissed: [] },
242221
previewMode: false,
243-
telemetry: { enabled: false },
244-
enterprise: { saml: false, ldap: false, oidc: false, showNonProdBanner: false },
222+
enterprise: { saml: false, ldap: false, oidc: false },
245223
};
246224

247225
const { service } = createMockService();

packages/cli/src/services/__tests__/workflow-statistics.service.integration.test.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
import { getPersonalProject, createWorkflow, testDb, mockInstance } from '@n8n/backend-test-utils';
1+
import {
2+
getPersonalProject,
3+
createTeamProject,
4+
createWorkflow,
5+
testDb,
6+
mockInstance,
7+
} from '@n8n/backend-test-utils';
28
import { GlobalConfig } from '@n8n/config';
39
import type { IWorkflowDb, Project, WorkflowEntity, User } from '@n8n/db';
410
import { WorkflowStatisticsRepository } from '@n8n/db';
@@ -243,6 +249,33 @@ describe('WorkflowStatisticsService', () => {
243249
expect(updateSettingsSpy).not.toHaveBeenCalled();
244250
expect(emitSpy).not.toHaveBeenCalled();
245251
});
252+
253+
test('emits first-production-workflow-succeeded with null userId for team project', async () => {
254+
// ARRANGE
255+
const teamProject = await createTeamProject('Team Project');
256+
const teamWorkflow = await createWorkflow({}, teamProject);
257+
const runData: IRun = {
258+
finished: true,
259+
status: 'success',
260+
data: createEmptyRunExecutionData(),
261+
mode: 'internal',
262+
startedAt: new Date(),
263+
};
264+
const emitSpy = jest.spyOn(Container.get(EventService), 'emit');
265+
const updateSettingsSpy = jest.spyOn(userService, 'updateSettings');
266+
267+
// ACT
268+
await workflowStatisticsService.workflowExecutionCompleted(teamWorkflow, runData);
269+
270+
// ASSERT
271+
expect(updateSettingsSpy).not.toHaveBeenCalled();
272+
expect(emitSpy).toHaveBeenCalledTimes(1);
273+
expect(emitSpy).toHaveBeenCalledWith('first-production-workflow-succeeded', {
274+
projectId: teamProject.id,
275+
workflowId: teamWorkflow.id,
276+
userId: null,
277+
});
278+
});
246279
});
247280

248281
describe('nodeFetchedData', () => {

packages/cli/src/services/frontend.service.ts

Lines changed: 75 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
1-
import type {
2-
FrontendSettings,
3-
IEnterpriseSettings,
4-
ITelemetrySettings,
5-
N8nEnvFeatFlags,
6-
} from '@n8n/api-types';
1+
import type { FrontendSettings, ITelemetrySettings, N8nEnvFeatFlags } from '@n8n/api-types';
72
import { LicenseState, Logger, ModuleRegistry } from '@n8n/backend-common';
83
import { GlobalConfig, SecurityConfig } from '@n8n/config';
94
import { LICENSE_FEATURES } from '@n8n/constants';
@@ -37,29 +32,63 @@ import {
3732
getWorkflowHistoryPruneTime,
3833
} from '@/workflows/workflow-history/workflow-history-helper';
3934

40-
export type PublicEnterpriseSettings = Pick<
41-
IEnterpriseSettings,
42-
'saml' | 'ldap' | 'oidc' | 'showNonProdBanner'
43-
>;
44-
45-
export type PublicFrontendSettings = Pick<
46-
FrontendSettings,
47-
| 'settingsMode'
48-
| 'instanceId'
49-
| 'defaultLocale'
50-
| 'versionCli'
51-
| 'releaseChannel'
52-
| 'versionNotifications'
53-
| 'userManagement'
54-
| 'sso'
55-
| 'mfa'
56-
| 'authCookie'
57-
| 'oauthCallbackUrls'
58-
| 'banners'
59-
| 'previewMode'
60-
| 'telemetry'
61-
> & {
62-
enterprise: PublicEnterpriseSettings;
35+
/**
36+
* IMPORTANT: Only add settings that are absolutely necessary for non-authenticated pages
37+
*/
38+
export type PublicFrontendSettings = {
39+
/** Controls initialization flow in settings store */
40+
settingsMode: FrontendSettings['settingsMode'];
41+
42+
/** Used to bypass authentication on the workflows/demo page */
43+
previewMode: FrontendSettings['previewMode'];
44+
45+
authCookie: {
46+
/** Blocks insecure access incompatible with the authentication cookie. */
47+
secure: FrontendSettings['authCookie']['secure'];
48+
};
49+
50+
userManagement: {
51+
/** Used to control login page UI behaviour and conditional SSO Login display */
52+
authenticationMethod: FrontendSettings['userManagement']['authenticationMethod'];
53+
54+
/** Enables initial owner setup */
55+
showSetupOnFirstLoad: FrontendSettings['userManagement']['showSetupOnFirstLoad'];
56+
57+
/** Determines forgot password page UX */
58+
smtpSetup: FrontendSettings['userManagement']['smtpSetup'];
59+
};
60+
61+
enterprise: {
62+
/** License check for SAML for SSO button visibility */
63+
saml: FrontendSettings['enterprise']['saml'];
64+
65+
/** License check for OIDC for SSO button visibility */
66+
oidc: FrontendSettings['enterprise']['oidc'];
67+
68+
/** License check for LDAP authentication */
69+
ldap: FrontendSettings['enterprise']['ldap'];
70+
};
71+
72+
sso: {
73+
saml: {
74+
/** Config flag for SSO button*/
75+
loginEnabled: FrontendSettings['sso']['saml']['loginEnabled'];
76+
};
77+
ldap: {
78+
/** Config flag for LDAP authentication */
79+
loginEnabled: FrontendSettings['sso']['ldap']['loginEnabled'];
80+
81+
/** Customizes login form label (defaults to "Email") */
82+
loginLabel: FrontendSettings['sso']['ldap']['loginLabel'];
83+
};
84+
oidc: {
85+
/** Config flag for SSO button*/
86+
loginEnabled: FrontendSettings['sso']['oidc']['loginEnabled'];
87+
88+
/** Required for OIDC authentication redirect URL */
89+
loginUrl: FrontendSettings['sso']['oidc']['loginUrl'];
90+
};
91+
};
6392
};
6493

6594
@Service()
@@ -491,39 +520,31 @@ export class FrontendService {
491520
getPublicSettings(): PublicFrontendSettings {
492521
// Get full settings to ensure all required properties are initialized
493522
const {
494-
instanceId,
495-
defaultLocale,
496-
versionCli,
497-
releaseChannel,
498-
versionNotifications,
499-
userManagement,
500-
sso,
501-
mfa,
523+
userManagement: { authenticationMethod, showSetupOnFirstLoad, smtpSetup },
524+
sso: { saml: ssoSaml, ldap: ssoLdap, oidc: ssoOidc },
502525
authCookie,
503-
oauthCallbackUrls,
504-
banners,
505526
previewMode,
506-
telemetry,
507-
enterprise: { saml, ldap, oidc, showNonProdBanner },
527+
enterprise: { saml, ldap, oidc },
508528
} = this.getSettings();
509529

510-
return {
530+
const publicSettings: PublicFrontendSettings = {
511531
settingsMode: 'public',
512-
instanceId,
513-
defaultLocale,
514-
versionCli,
515-
releaseChannel,
516-
versionNotifications,
517-
userManagement,
518-
sso,
519-
mfa,
532+
userManagement: { authenticationMethod, showSetupOnFirstLoad, smtpSetup },
533+
sso: {
534+
saml: {
535+
loginEnabled: ssoSaml.loginEnabled,
536+
},
537+
ldap: ssoLdap,
538+
oidc: {
539+
loginEnabled: ssoOidc.loginEnabled,
540+
loginUrl: ssoOidc.loginUrl,
541+
},
542+
},
520543
authCookie,
521-
oauthCallbackUrls,
522-
banners,
523544
previewMode,
524-
telemetry,
525-
enterprise: { saml, ldap, oidc, showNonProdBanner },
545+
enterprise: { saml, ldap, oidc },
526546
};
547+
return publicSettings;
527548
}
528549

529550
getModuleSettings() {

0 commit comments

Comments
 (0)