mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
refactor(core): Clean up event relays (no-changelog) (#10284)
This commit is contained in:
parent
55f2ffe256
commit
aa0a470dce
|
@ -13,7 +13,7 @@ import { Logger } from '@/Logger';
|
|||
import { jsonParse, type IDataObject, ApplicationError } from 'n8n-workflow';
|
||||
import { EXTERNAL_SECRETS_INITIAL_BACKOFF, EXTERNAL_SECRETS_MAX_BACKOFF } from './constants';
|
||||
import { License } from '@/License';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
import { updateIntervalTime } from './externalSecretsHelper.ee';
|
||||
import { ExternalSecretsProviders } from './ExternalSecretsProviders.ee';
|
||||
import { OrchestrationService } from '@/services/orchestration.service';
|
||||
|
|
|
@ -9,8 +9,8 @@ import { Telemetry } from '@/telemetry';
|
|||
import { MessageEventBus } from './eventbus/MessageEventBus/MessageEventBus';
|
||||
|
||||
/**
|
||||
* @deprecated Do not add to this class. To add audit or telemetry events, use
|
||||
* `EventService` to emit the event and then use the `AuditEventRelay` or
|
||||
* @deprecated Do not add to this class. To add log streaming or telemetry events, use
|
||||
* `EventService` to emit the event and then use the `LogStreamingEventRelay` or
|
||||
* `TelemetryEventRelay` to forward them to the event bus or telemetry.
|
||||
*/
|
||||
@Service()
|
||||
|
|
|
@ -6,7 +6,7 @@ import { NON_SENSIBLE_LDAP_CONFIG_PROPERTIES } from './constants';
|
|||
import { getLdapSynchronizations } from './helpers.ee';
|
||||
import { LdapConfiguration } from './types';
|
||||
import { LdapService } from './ldap.service.ee';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
@RestController('/ldap')
|
||||
export class LdapController {
|
||||
|
|
|
@ -44,7 +44,7 @@ import {
|
|||
LDAP_LOGIN_ENABLED,
|
||||
LDAP_LOGIN_LABEL,
|
||||
} from './constants';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
@Service()
|
||||
export class LdapService {
|
||||
|
|
|
@ -15,7 +15,7 @@ import { UserRepository } from '@db/repositories/user.repository';
|
|||
import { UrlService } from '@/services/url.service';
|
||||
import type { AuthenticatedRequest } from '@/requests';
|
||||
import { GlobalConfig } from '@n8n/config';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
async function createApiRouter(
|
||||
version: string,
|
||||
|
|
|
@ -17,7 +17,7 @@ import { Container } from 'typedi';
|
|||
import { CredentialsRepository } from '@db/repositories/credentials.repository';
|
||||
import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository';
|
||||
import { ProjectRepository } from '@/databases/repositories/project.repository';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
export async function getCredentials(credentialId: string): Promise<ICredentialsDb | null> {
|
||||
return await Container.get(CredentialsRepository).findOneBy({ id: credentialId });
|
||||
|
|
|
@ -10,7 +10,7 @@ import {
|
|||
getTrackingInformationFromPullResult,
|
||||
isSourceControlLicensed,
|
||||
} from '@/environments/sourceControl/sourceControlHelper.ee';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
export = {
|
||||
pull: [
|
||||
|
|
|
@ -32,7 +32,7 @@ import { SharedWorkflowRepository } from '@/databases/repositories/sharedWorkflo
|
|||
import { TagRepository } from '@/databases/repositories/tag.repository';
|
||||
import { WorkflowRepository } from '@/databases/repositories/workflow.repository';
|
||||
import { ProjectRepository } from '@/databases/repositories/project.repository';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
import { z } from 'zod';
|
||||
import { EnterpriseWorkflowService } from '@/workflows/workflow.service.ee';
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ import { MessageEventBus } from '@/eventbus/MessageEventBus/MessageEventBus';
|
|||
import { handleMfaDisable, isMfaFeatureEnabled } from '@/Mfa/helpers';
|
||||
import type { FrontendService } from '@/services/frontend.service';
|
||||
import { OrchestrationService } from '@/services/orchestration.service';
|
||||
import { AuditEventRelay } from './eventbus/audit-event-relay.service';
|
||||
import { LogStreamingEventRelay } from '@/events/log-streaming-event-relay';
|
||||
|
||||
import '@/controllers/activeWorkflows.controller';
|
||||
import '@/controllers/auth.controller';
|
||||
|
@ -64,7 +64,7 @@ import '@/ExternalSecrets/ExternalSecrets.controller.ee';
|
|||
import '@/license/license.controller';
|
||||
import '@/workflows/workflowHistory/workflowHistory.controller.ee';
|
||||
import '@/workflows/workflows.controller';
|
||||
import { EventService } from './eventbus/event.service';
|
||||
import { EventService } from './events/event.service';
|
||||
|
||||
const exec = promisify(callbackExec);
|
||||
|
||||
|
@ -250,7 +250,7 @@ export class Server extends AbstractServer {
|
|||
// ----------------------------------------
|
||||
const eventBus = Container.get(MessageEventBus);
|
||||
await eventBus.initialize();
|
||||
Container.get(AuditEventRelay).init();
|
||||
Container.get(LogStreamingEventRelay).init();
|
||||
|
||||
if (this.endpointPresetCredentials !== '') {
|
||||
// POST endpoint to set preset credentials
|
||||
|
|
|
@ -16,7 +16,7 @@ import { toError } from '@/utils';
|
|||
|
||||
import type { InviteEmailData, PasswordResetData, SendEmailResult } from './Interfaces';
|
||||
import { NodeMailer } from './NodeMailer';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
type Template = HandlebarsTemplateDelegate<unknown>;
|
||||
type TemplateName = 'invite' | 'passwordReset' | 'workflowShared' | 'credentialsShared';
|
||||
|
|
|
@ -70,7 +70,7 @@ import { WorkflowRepository } from './databases/repositories/workflow.repository
|
|||
import { UrlService } from './services/url.service';
|
||||
import { WorkflowExecutionService } from './workflows/workflowExecution.service';
|
||||
import { MessageEventBus } from '@/eventbus/MessageEventBus/MessageEventBus';
|
||||
import { EventService } from './eventbus/event.service';
|
||||
import { EventService } from './events/event.service';
|
||||
import { GlobalConfig } from '@n8n/config';
|
||||
import { SubworkflowPolicyChecker } from './subworkflows/subworkflow-policy-checker.service';
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ import { generateFailedExecutionFromError } from '@/WorkflowHelpers';
|
|||
import { PermissionChecker } from '@/UserManagement/PermissionChecker';
|
||||
import { Logger } from '@/Logger';
|
||||
import { WorkflowStaticDataService } from '@/workflows/workflowStaticData.service';
|
||||
import { EventService } from './eventbus/event.service';
|
||||
import { EventService } from './events/event.service';
|
||||
|
||||
@Service()
|
||||
export class WorkflowRunner {
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Container } from 'typedi';
|
|||
import { isLdapLoginEnabled } from '@/Ldap/helpers.ee';
|
||||
import { UserRepository } from '@db/repositories/user.repository';
|
||||
import { AuthError } from '@/errors/response-errors/auth.error';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
export const handleEmailLogin = async (
|
||||
email: string,
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
updateLdapUserOnLocalDb,
|
||||
} from '@/Ldap/helpers.ee';
|
||||
import type { User } from '@db/entities/User';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
export const handleLdapLogin = async (
|
||||
loginId: string,
|
||||
|
|
|
@ -23,7 +23,7 @@ import { initExpressionEvaluator } from '@/ExpressionEvaluator';
|
|||
import { generateHostInstanceId } from '@db/utils/generators';
|
||||
import { WorkflowHistoryManager } from '@/workflows/workflowHistory/workflowHistoryManager.ee';
|
||||
import { ShutdownService } from '@/shutdown/Shutdown.service';
|
||||
import { TelemetryEventRelay } from '@/telemetry/telemetry-event-relay.service';
|
||||
import { TelemetryEventRelay } from '@/events/telemetry-event-relay';
|
||||
|
||||
export abstract class BaseCommand extends Command {
|
||||
protected logger = Container.get(Logger);
|
||||
|
|
|
@ -33,7 +33,7 @@ import { ExecutionService } from '@/executions/execution.service';
|
|||
import { OwnershipService } from '@/services/ownership.service';
|
||||
import { WorkflowRunner } from '@/WorkflowRunner';
|
||||
import { ExecutionRecoveryService } from '@/executions/execution-recovery.service';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-var-requires
|
||||
const open = require('open');
|
||||
|
|
|
@ -30,7 +30,7 @@ import type { WorkerJobStatusSummary } from '@/services/orchestration/worker/typ
|
|||
import { ServiceUnavailableError } from '@/errors/response-errors/service-unavailable.error';
|
||||
import { BaseCommand } from './BaseCommand';
|
||||
import { MaxStalledCountError } from '@/errors/max-stalled-count.error';
|
||||
import { AuditEventRelay } from '@/eventbus/audit-event-relay.service';
|
||||
import { LogStreamingEventRelay } from '@/events/log-streaming-event-relay';
|
||||
|
||||
export class Worker extends BaseCommand {
|
||||
static description = '\nStarts a n8n worker';
|
||||
|
@ -286,7 +286,7 @@ export class Worker extends BaseCommand {
|
|||
await Container.get(MessageEventBus).initialize({
|
||||
workerId: this.queueModeId,
|
||||
});
|
||||
Container.get(AuditEventRelay).init();
|
||||
Container.get(LogStreamingEventRelay).init();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -12,7 +12,7 @@ import type { WorkflowExecuteMode as ExecutionMode } from 'n8n-workflow';
|
|||
import type { ExecutionRepository } from '@/databases/repositories/execution.repository';
|
||||
import type { IExecutingWorkflowData } from '@/Interfaces';
|
||||
import type { Telemetry } from '@/telemetry';
|
||||
import type { EventService } from '@/eventbus/event.service';
|
||||
import type { EventService } from '@/events/event.service';
|
||||
|
||||
describe('ConcurrencyControlService', () => {
|
||||
const logger = mock<Logger>();
|
||||
|
|
|
@ -8,7 +8,7 @@ import { ExecutionRepository } from '@/databases/repositories/execution.reposito
|
|||
import type { WorkflowExecuteMode as ExecutionMode } from 'n8n-workflow';
|
||||
import type { IExecutingWorkflowData } from '@/Interfaces';
|
||||
import { Telemetry } from '@/telemetry';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
export const CLOUD_TEMP_PRODUCTION_LIMIT = 999;
|
||||
export const CLOUD_TEMP_REPORTABLE_THRESHOLDS = [5, 10, 20, 50, 100, 200];
|
||||
|
|
|
@ -24,7 +24,7 @@ import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
|||
import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
|
||||
import { ApplicationError } from 'n8n-workflow';
|
||||
import { UserRepository } from '@/databases/repositories/user.repository';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
@RestController()
|
||||
export class AuthController {
|
||||
|
|
|
@ -13,7 +13,7 @@ import { Push } from '@/push';
|
|||
import { CommunityPackagesService } from '@/services/communityPackages.service';
|
||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
const {
|
||||
PACKAGE_NOT_INSTALLED,
|
||||
|
|
|
@ -18,7 +18,7 @@ import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
|||
import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
|
||||
import { InternalHooks } from '@/InternalHooks';
|
||||
import { ExternalHooks } from '@/ExternalHooks';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
@RestController('/invitations')
|
||||
export class InvitationController {
|
||||
|
|
|
@ -23,7 +23,7 @@ import { InternalHooks } from '@/InternalHooks';
|
|||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||
import { UserRepository } from '@/databases/repositories/user.repository';
|
||||
import { isApiEnabled } from '@/PublicApi';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
export const API_KEY_PREFIX = 'n8n_api_';
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import { ForbiddenError } from '@/errors/response-errors/forbidden.error';
|
|||
import { NotFoundError } from '@/errors/response-errors/not-found.error';
|
||||
import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error';
|
||||
import { UserRepository } from '@/databases/repositories/user.repository';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
@RestController()
|
||||
export class PasswordResetController {
|
||||
|
|
|
@ -23,7 +23,7 @@ import { ProjectRepository } from '@/databases/repositories/project.repository';
|
|||
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
|
||||
import { In, Not } from '@n8n/typeorm';
|
||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
@RestController('/projects')
|
||||
export class ProjectController {
|
||||
|
|
|
@ -28,7 +28,7 @@ import { Project } from '@/databases/entities/Project';
|
|||
import { WorkflowService } from '@/workflows/workflow.service';
|
||||
import { CredentialsService } from '@/credentials/credentials.service';
|
||||
import { ProjectService } from '@/services/project.service';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
@RestController('/users')
|
||||
export class UsersController {
|
||||
|
|
|
@ -30,7 +30,7 @@ import { SharedCredentialsRepository } from '@/databases/repositories/sharedCred
|
|||
import { SharedCredentials } from '@/databases/entities/SharedCredentials';
|
||||
import { ProjectRelationRepository } from '@/databases/repositories/projectRelation.repository';
|
||||
import { z } from 'zod';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
@RestController('/credentials')
|
||||
export class CredentialsController {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { RedactableError } from '@/errors/redactable.error';
|
||||
import type { UserLike } from '@/eventbus/event.types';
|
||||
import type { UserLike } from '@/events/relay-event-map';
|
||||
|
||||
function toRedactable(userLike: UserLike) {
|
||||
return {
|
||||
|
@ -14,7 +14,7 @@ function toRedactable(userLike: UserLike) {
|
|||
type FieldName = 'user' | 'inviter' | 'invitee';
|
||||
|
||||
/**
|
||||
* Mark redactable properties in a `{ user: UserLike }` field in an `AuditEventRelay`
|
||||
* Mark redactable properties in a `{ user: UserLike }` field in an `LogStreamingEventRelay`
|
||||
* method arg. These properties will be later redacted by the log streaming
|
||||
* destination based on user prefs. Only for `n8n.audit.*` logs.
|
||||
*
|
||||
|
|
|
@ -12,7 +12,7 @@ import type { SourceControlPreferences } from './types/sourceControlPreferences'
|
|||
import type { SourceControlledFile } from './types/sourceControlledFile';
|
||||
import { SOURCE_CONTROL_DEFAULT_BRANCH } from './constants';
|
||||
import type { ImportResult } from './types/importResult';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
import { getRepoType } from './sourceControlHelper.ee';
|
||||
import { SourceControlGetStatus } from './types/sourceControlGetStatus';
|
||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||
|
|
|
@ -30,7 +30,7 @@ import type { TagEntity } from '@db/entities/TagEntity';
|
|||
import type { Variables } from '@db/entities/Variables';
|
||||
import type { SourceControlWorkflowVersionId } from './types/sourceControlWorkflowVersionId';
|
||||
import type { ExportableCredential } from './types/exportableCredential';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
import { TagRepository } from '@db/repositories/tag.repository';
|
||||
import { Logger } from '@/Logger';
|
||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||
|
|
|
@ -6,7 +6,7 @@ import { CacheService } from '@/services/cache/cache.service';
|
|||
import { VariablesRepository } from '@db/repositories/variables.repository';
|
||||
import { VariableCountLimitReachedError } from '@/errors/variable-count-limit-reached.error';
|
||||
import { VariableValidationError } from '@/errors/variable-validation.error';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
@Service()
|
||||
export class VariablesService {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { ApplicationError } from 'n8n-workflow';
|
|||
export class RedactableError extends ApplicationError {
|
||||
constructor(fieldName: string, args: string) {
|
||||
super(
|
||||
`Failed to find "${fieldName}" property in argument "${args.toString()}". Please set the decorator \`@Redactable()\` only on \`AuditEventRelay\` methods where the argument contains a "${fieldName}" property.`,
|
||||
`Failed to find "${fieldName}" property in argument "${args.toString()}". Please set the decorator \`@Redactable()\` only on \`LogStreamingEventRelay\` methods where the argument contains a "${fieldName}" property.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
import { Service } from 'typedi';
|
||||
import { TypedEmitter } from '@/TypedEmitter';
|
||||
import type { Event } from './event.types';
|
||||
|
||||
@Service()
|
||||
export class EventService extends TypedEmitter<Event> {}
|
|
@ -1,16 +1,15 @@
|
|||
import { mock } from 'jest-mock-extended';
|
||||
import { AuditEventRelay } from '../audit-event-relay.service';
|
||||
import type { MessageEventBus } from '../MessageEventBus/MessageEventBus';
|
||||
import type { Event } from '../event.types';
|
||||
import { EventService } from '../event.service';
|
||||
import { LogStreamingEventRelay } from '@/events/log-streaming-event-relay';
|
||||
import { EventService } from '@/events/event.service';
|
||||
import type { INode, IRun, IWorkflowBase } from 'n8n-workflow';
|
||||
import type { IWorkflowDb } from '@/Interfaces';
|
||||
import type { MessageEventBus } from '@/eventbus/MessageEventBus/MessageEventBus';
|
||||
import type { RelayEventMap } from '@/events/relay-event-map';
|
||||
|
||||
describe('AuditEventRelay', () => {
|
||||
describe('LogStreamingEventRelay', () => {
|
||||
const eventBus = mock<MessageEventBus>();
|
||||
const eventService = new EventService();
|
||||
const auditor = new AuditEventRelay(eventService, eventBus);
|
||||
auditor.init();
|
||||
new LogStreamingEventRelay(eventService, eventBus).init();
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
@ -18,7 +17,7 @@ describe('AuditEventRelay', () => {
|
|||
|
||||
describe('workflow events', () => {
|
||||
it('should log on `workflow-created` event', () => {
|
||||
const event: Event['workflow-created'] = {
|
||||
const event: RelayEventMap['workflow-created'] = {
|
||||
user: {
|
||||
id: '123',
|
||||
email: 'john@n8n.io',
|
||||
|
@ -52,7 +51,7 @@ describe('AuditEventRelay', () => {
|
|||
});
|
||||
|
||||
it('should log on `workflow-deleted` event', () => {
|
||||
const event: Event['workflow-deleted'] = {
|
||||
const event: RelayEventMap['workflow-deleted'] = {
|
||||
user: {
|
||||
id: '456',
|
||||
email: 'jane@n8n.io',
|
||||
|
@ -80,7 +79,7 @@ describe('AuditEventRelay', () => {
|
|||
});
|
||||
|
||||
it('should log on `workflow-saved` event', () => {
|
||||
const event: Event['workflow-saved'] = {
|
||||
const event: RelayEventMap['workflow-saved'] = {
|
||||
user: {
|
||||
id: '789',
|
||||
email: 'alex@n8n.io',
|
||||
|
@ -119,7 +118,7 @@ describe('AuditEventRelay', () => {
|
|||
settings: {},
|
||||
});
|
||||
|
||||
const event: Event['workflow-pre-execute'] = {
|
||||
const event: RelayEventMap['workflow-pre-execute'] = {
|
||||
executionId: 'exec123',
|
||||
data: workflow,
|
||||
};
|
||||
|
@ -139,7 +138,7 @@ describe('AuditEventRelay', () => {
|
|||
});
|
||||
|
||||
it('should log on `workflow-post-execute` for successful execution', () => {
|
||||
const payload = mock<Event['workflow-post-execute']>({
|
||||
const payload = mock<RelayEventMap['workflow-post-execute']>({
|
||||
executionId: 'some-id',
|
||||
userId: 'some-id',
|
||||
workflow: mock<IWorkflowBase>({ id: 'some-id', name: 'some-name' }),
|
||||
|
@ -208,7 +207,7 @@ describe('AuditEventRelay', () => {
|
|||
|
||||
describe('user events', () => {
|
||||
it('should log on `user-updated` event', () => {
|
||||
const event: Event['user-updated'] = {
|
||||
const event: RelayEventMap['user-updated'] = {
|
||||
user: {
|
||||
id: 'user456',
|
||||
email: 'updated@example.com',
|
||||
|
@ -235,7 +234,7 @@ describe('AuditEventRelay', () => {
|
|||
});
|
||||
|
||||
it('should log on `user-deleted` event', () => {
|
||||
const event: Event['user-deleted'] = {
|
||||
const event: RelayEventMap['user-deleted'] = {
|
||||
user: {
|
||||
id: '123',
|
||||
email: 'john@n8n.io',
|
||||
|
@ -262,7 +261,7 @@ describe('AuditEventRelay', () => {
|
|||
|
||||
describe('click events', () => {
|
||||
it('should log on `user-password-reset-request-click` event', () => {
|
||||
const event: Event['user-password-reset-request-click'] = {
|
||||
const event: RelayEventMap['user-password-reset-request-click'] = {
|
||||
user: {
|
||||
id: 'user101',
|
||||
email: 'user101@example.com',
|
||||
|
@ -287,7 +286,7 @@ describe('AuditEventRelay', () => {
|
|||
});
|
||||
|
||||
it('should log on `user-invite-email-click` event', () => {
|
||||
const event: Event['user-invite-email-click'] = {
|
||||
const event: RelayEventMap['user-invite-email-click'] = {
|
||||
inviter: {
|
||||
id: '123',
|
||||
email: 'john@n8n.io',
|
||||
|
@ -354,7 +353,7 @@ describe('AuditEventRelay', () => {
|
|||
settings: {},
|
||||
});
|
||||
|
||||
const event: Event['node-pre-execute'] = {
|
||||
const event: RelayEventMap['node-pre-execute'] = {
|
||||
executionId: 'exec456',
|
||||
nodeName: 'HTTP Request',
|
||||
workflow,
|
||||
|
@ -399,7 +398,7 @@ describe('AuditEventRelay', () => {
|
|||
settings: {},
|
||||
});
|
||||
|
||||
const event: Event['node-post-execute'] = {
|
||||
const event: RelayEventMap['node-post-execute'] = {
|
||||
executionId: 'exec789',
|
||||
nodeName: 'HTTP Response',
|
||||
workflow,
|
||||
|
@ -422,7 +421,7 @@ describe('AuditEventRelay', () => {
|
|||
|
||||
describe('credentials events', () => {
|
||||
it('should log on `credentials-shared` event', () => {
|
||||
const event: Event['credentials-shared'] = {
|
||||
const event: RelayEventMap['credentials-shared'] = {
|
||||
user: {
|
||||
id: 'user123',
|
||||
email: 'sharer@example.com',
|
||||
|
@ -457,7 +456,7 @@ describe('AuditEventRelay', () => {
|
|||
});
|
||||
|
||||
it('should log on `credentials-created` event', () => {
|
||||
const event: Event['credentials-created'] = {
|
||||
const event: RelayEventMap['credentials-created'] = {
|
||||
user: {
|
||||
id: 'user123',
|
||||
email: 'user@example.com',
|
||||
|
@ -494,7 +493,7 @@ describe('AuditEventRelay', () => {
|
|||
|
||||
describe('auth events', () => {
|
||||
it('should log on `user-login-failed` event', () => {
|
||||
const event: Event['user-login-failed'] = {
|
||||
const event: RelayEventMap['user-login-failed'] = {
|
||||
userEmail: 'user@example.com',
|
||||
authenticationMethod: 'email',
|
||||
reason: 'Invalid password',
|
||||
|
@ -515,7 +514,7 @@ describe('AuditEventRelay', () => {
|
|||
|
||||
describe('community package events', () => {
|
||||
it('should log on `community-package-updated` event', () => {
|
||||
const event: Event['community-package-updated'] = {
|
||||
const event: RelayEventMap['community-package-updated'] = {
|
||||
user: {
|
||||
id: 'user202',
|
||||
email: 'packageupdater@example.com',
|
||||
|
@ -552,7 +551,7 @@ describe('AuditEventRelay', () => {
|
|||
});
|
||||
|
||||
it('should log on `community-package-installed` event', () => {
|
||||
const event: Event['community-package-installed'] = {
|
||||
const event: RelayEventMap['community-package-installed'] = {
|
||||
user: {
|
||||
id: 'user789',
|
||||
email: 'admin@example.com',
|
||||
|
@ -593,7 +592,7 @@ describe('AuditEventRelay', () => {
|
|||
|
||||
describe('email events', () => {
|
||||
it('should log on `email-failed` event', () => {
|
||||
const event: Event['email-failed'] = {
|
||||
const event: RelayEventMap['email-failed'] = {
|
||||
user: {
|
||||
id: 'user789',
|
||||
email: 'recipient@example.com',
|
||||
|
@ -622,7 +621,7 @@ describe('AuditEventRelay', () => {
|
|||
|
||||
describe('public API events', () => {
|
||||
it('should log on `public-api-key-created` event', () => {
|
||||
const event: Event['public-api-key-created'] = {
|
||||
const event: RelayEventMap['public-api-key-created'] = {
|
||||
user: {
|
||||
id: 'user101',
|
||||
email: 'apiuser@example.com',
|
||||
|
@ -650,7 +649,7 @@ describe('AuditEventRelay', () => {
|
|||
|
||||
describe('execution events', () => {
|
||||
it('should log on `execution-throttled` event', () => {
|
||||
const event: Event['execution-throttled'] = {
|
||||
const event: RelayEventMap['execution-throttled'] = {
|
||||
executionId: 'exec123456',
|
||||
};
|
||||
|
20
packages/cli/src/events/event-relay.ts
Normal file
20
packages/cli/src/events/event-relay.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { EventService } from './event.service';
|
||||
import { Service } from 'typedi';
|
||||
import type { RelayEventMap } from '@/events/relay-event-map';
|
||||
|
||||
@Service()
|
||||
export class EventRelay {
|
||||
constructor(readonly eventService: EventService) {}
|
||||
|
||||
protected setupListeners<EventNames extends keyof RelayEventMap>(map: {
|
||||
[EventName in EventNames]?: (event: RelayEventMap[EventName]) => void | Promise<void>;
|
||||
}) {
|
||||
for (const [eventName, handler] of Object.entries(map) as Array<
|
||||
[EventNames, (event: RelayEventMap[EventNames]) => void | Promise<void>]
|
||||
>) {
|
||||
this.eventService.on(eventName, async (event) => {
|
||||
await handler(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
6
packages/cli/src/events/event.service.ts
Normal file
6
packages/cli/src/events/event.service.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { Service } from 'typedi';
|
||||
import { TypedEmitter } from '@/TypedEmitter';
|
||||
import type { RelayEventMap } from './relay-event-map';
|
||||
|
||||
@Service()
|
||||
export class EventService extends TypedEmitter<RelayEventMap> {}
|
|
@ -1,72 +1,58 @@
|
|||
import { Service } from 'typedi';
|
||||
import { MessageEventBus } from './MessageEventBus/MessageEventBus';
|
||||
import { MessageEventBus } from '@/eventbus/MessageEventBus/MessageEventBus';
|
||||
import { Redactable } from '@/decorators/Redactable';
|
||||
import { EventService } from './event.service';
|
||||
import type { Event } from './event.types';
|
||||
import { EventRelay } from '@/events/event-relay';
|
||||
import type { RelayEventMap } from '@/events/relay-event-map';
|
||||
import type { IWorkflowBase } from 'n8n-workflow';
|
||||
import { EventService } from './event.service';
|
||||
|
||||
@Service()
|
||||
export class AuditEventRelay {
|
||||
export class LogStreamingEventRelay extends EventRelay {
|
||||
constructor(
|
||||
private readonly eventService: EventService,
|
||||
readonly eventService: EventService,
|
||||
private readonly eventBus: MessageEventBus,
|
||||
) {}
|
||||
) {
|
||||
super(eventService);
|
||||
}
|
||||
|
||||
init() {
|
||||
this.setupHandlers();
|
||||
this.setupListeners({
|
||||
'workflow-created': (event) => this.workflowCreated(event),
|
||||
'workflow-deleted': (event) => this.workflowDeleted(event),
|
||||
'workflow-saved': (event) => this.workflowSaved(event),
|
||||
'workflow-pre-execute': (event) => this.workflowPreExecute(event),
|
||||
'workflow-post-execute': (event) => this.workflowPostExecute(event),
|
||||
'node-pre-execute': (event) => this.nodePreExecute(event),
|
||||
'node-post-execute': (event) => this.nodePostExecute(event),
|
||||
'user-deleted': (event) => this.userDeleted(event),
|
||||
'user-invited': (event) => this.userInvited(event),
|
||||
'user-reinvited': (event) => this.userReinvited(event),
|
||||
'user-updated': (event) => this.userUpdated(event),
|
||||
'user-signed-up': (event) => this.userSignedUp(event),
|
||||
'user-logged-in': (event) => this.userLoggedIn(event),
|
||||
'user-login-failed': (event) => this.userLoginFailed(event),
|
||||
'user-invite-email-click': (event) => this.userInviteEmailClick(event),
|
||||
'user-password-reset-email-click': (event) => this.userPasswordResetEmailClick(event),
|
||||
'user-password-reset-request-click': (event) => this.userPasswordResetRequestClick(event),
|
||||
'public-api-key-created': (event) => this.publicApiKeyCreated(event),
|
||||
'public-api-key-deleted': (event) => this.publicApiKeyDeleted(event),
|
||||
'email-failed': (event) => this.emailFailed(event),
|
||||
'credentials-created': (event) => this.credentialsCreated(event),
|
||||
'credentials-deleted': (event) => this.credentialsDeleted(event),
|
||||
'credentials-shared': (event) => this.credentialsShared(event),
|
||||
'credentials-updated': (event) => this.credentialsUpdated(event),
|
||||
'community-package-installed': (event) => this.communityPackageInstalled(event),
|
||||
'community-package-updated': (event) => this.communityPackageUpdated(event),
|
||||
'community-package-deleted': (event) => this.communityPackageDeleted(event),
|
||||
'execution-throttled': (event) => this.executionThrottled(event),
|
||||
'execution-started-during-bootup': (event) => this.executionStartedDuringBootup(event),
|
||||
});
|
||||
}
|
||||
|
||||
private setupHandlers() {
|
||||
this.eventService.on('workflow-created', (event) => this.workflowCreated(event));
|
||||
this.eventService.on('workflow-deleted', (event) => this.workflowDeleted(event));
|
||||
this.eventService.on('workflow-saved', (event) => this.workflowSaved(event));
|
||||
this.eventService.on('workflow-pre-execute', (event) => this.workflowPreExecute(event));
|
||||
this.eventService.on('workflow-post-execute', (event) => this.workflowPostExecute(event));
|
||||
this.eventService.on('node-pre-execute', (event) => this.nodePreExecute(event));
|
||||
this.eventService.on('node-post-execute', (event) => this.nodePostExecute(event));
|
||||
this.eventService.on('user-deleted', (event) => this.userDeleted(event));
|
||||
this.eventService.on('user-invited', (event) => this.userInvited(event));
|
||||
this.eventService.on('user-reinvited', (event) => this.userReinvited(event));
|
||||
this.eventService.on('user-updated', (event) => this.userUpdated(event));
|
||||
this.eventService.on('user-signed-up', (event) => this.userSignedUp(event));
|
||||
this.eventService.on('user-logged-in', (event) => this.userLoggedIn(event));
|
||||
this.eventService.on('user-login-failed', (event) => this.userLoginFailed(event));
|
||||
this.eventService.on('user-invite-email-click', (event) => this.userInviteEmailClick(event));
|
||||
this.eventService.on('user-password-reset-email-click', (event) =>
|
||||
this.userPasswordResetEmailClick(event),
|
||||
);
|
||||
this.eventService.on('user-password-reset-request-click', (event) =>
|
||||
this.userPasswordResetRequestClick(event),
|
||||
);
|
||||
this.eventService.on('public-api-key-created', (event) => this.publicApiKeyCreated(event));
|
||||
this.eventService.on('public-api-key-deleted', (event) => this.publicApiKeyDeleted(event));
|
||||
this.eventService.on('email-failed', (event) => this.emailFailed(event));
|
||||
this.eventService.on('credentials-created', (event) => this.credentialsCreated(event));
|
||||
this.eventService.on('credentials-deleted', (event) => this.credentialsDeleted(event));
|
||||
this.eventService.on('credentials-shared', (event) => this.credentialsShared(event));
|
||||
this.eventService.on('credentials-updated', (event) => this.credentialsUpdated(event));
|
||||
this.eventService.on('credentials-deleted', (event) => this.credentialsDeleted(event));
|
||||
this.eventService.on('community-package-installed', (event) =>
|
||||
this.communityPackageInstalled(event),
|
||||
);
|
||||
this.eventService.on('community-package-updated', (event) =>
|
||||
this.communityPackageUpdated(event),
|
||||
);
|
||||
this.eventService.on('community-package-deleted', (event) =>
|
||||
this.communityPackageDeleted(event),
|
||||
);
|
||||
this.eventService.on('execution-throttled', (event) => this.executionThrottled(event));
|
||||
this.eventService.on('execution-started-during-bootup', (event) =>
|
||||
this.executionStartedDuringBootup(event),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Workflow
|
||||
*/
|
||||
// #region Workflow
|
||||
|
||||
@Redactable()
|
||||
private workflowCreated({ user, workflow }: Event['workflow-created']) {
|
||||
private workflowCreated({ user, workflow }: RelayEventMap['workflow-created']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.workflow.created',
|
||||
payload: {
|
||||
|
@ -78,7 +64,7 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
@Redactable()
|
||||
private workflowDeleted({ user, workflowId }: Event['workflow-deleted']) {
|
||||
private workflowDeleted({ user, workflowId }: RelayEventMap['workflow-deleted']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.workflow.deleted',
|
||||
payload: { ...user, workflowId },
|
||||
|
@ -86,7 +72,7 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
@Redactable()
|
||||
private workflowSaved({ user, workflow }: Event['workflow-saved']) {
|
||||
private workflowSaved({ user, workflow }: RelayEventMap['workflow-saved']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.workflow.updated',
|
||||
payload: {
|
||||
|
@ -97,7 +83,7 @@ export class AuditEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
private workflowPreExecute({ data, executionId }: Event['workflow-pre-execute']) {
|
||||
private workflowPreExecute({ data, executionId }: RelayEventMap['workflow-pre-execute']) {
|
||||
const payload =
|
||||
'executionData' in data
|
||||
? {
|
||||
|
@ -121,7 +107,7 @@ export class AuditEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
private workflowPostExecute(event: Event['workflow-post-execute']) {
|
||||
private workflowPostExecute(event: RelayEventMap['workflow-post-execute']) {
|
||||
const { runData, workflow, ...rest } = event;
|
||||
|
||||
const payload = {
|
||||
|
@ -155,11 +141,11 @@ export class AuditEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Node
|
||||
*/
|
||||
// #endregion
|
||||
|
||||
private nodePreExecute({ workflow, executionId, nodeName }: Event['node-pre-execute']) {
|
||||
// #region Node
|
||||
|
||||
private nodePreExecute({ workflow, executionId, nodeName }: RelayEventMap['node-pre-execute']) {
|
||||
void this.eventBus.sendNodeEvent({
|
||||
eventName: 'n8n.node.started',
|
||||
payload: {
|
||||
|
@ -172,7 +158,7 @@ export class AuditEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
private nodePostExecute({ workflow, executionId, nodeName }: Event['node-post-execute']) {
|
||||
private nodePostExecute({ workflow, executionId, nodeName }: RelayEventMap['node-post-execute']) {
|
||||
void this.eventBus.sendNodeEvent({
|
||||
eventName: 'n8n.node.finished',
|
||||
payload: {
|
||||
|
@ -185,12 +171,12 @@ export class AuditEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* User
|
||||
*/
|
||||
// #endregion
|
||||
|
||||
// #region User
|
||||
|
||||
@Redactable()
|
||||
private userDeleted({ user }: Event['user-deleted']) {
|
||||
private userDeleted({ user }: RelayEventMap['user-deleted']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.deleted',
|
||||
payload: user,
|
||||
|
@ -198,7 +184,7 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
@Redactable()
|
||||
private userInvited({ user, targetUserId }: Event['user-invited']) {
|
||||
private userInvited({ user, targetUserId }: RelayEventMap['user-invited']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.invited',
|
||||
payload: { ...user, targetUserId },
|
||||
|
@ -206,7 +192,7 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
@Redactable()
|
||||
private userReinvited({ user, targetUserId }: Event['user-reinvited']) {
|
||||
private userReinvited({ user, targetUserId }: RelayEventMap['user-reinvited']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.reinvited',
|
||||
payload: { ...user, targetUserId },
|
||||
|
@ -214,19 +200,19 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
@Redactable()
|
||||
private userUpdated({ user, fieldsChanged }: Event['user-updated']) {
|
||||
private userUpdated({ user, fieldsChanged }: RelayEventMap['user-updated']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.updated',
|
||||
payload: { ...user, fieldsChanged },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth
|
||||
*/
|
||||
// #endregion
|
||||
|
||||
// #region Auth
|
||||
|
||||
@Redactable()
|
||||
private userSignedUp({ user }: Event['user-signed-up']) {
|
||||
private userSignedUp({ user }: RelayEventMap['user-signed-up']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.signedup',
|
||||
payload: user,
|
||||
|
@ -234,7 +220,7 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
@Redactable()
|
||||
private userLoggedIn({ user, authenticationMethod }: Event['user-logged-in']) {
|
||||
private userLoggedIn({ user, authenticationMethod }: RelayEventMap['user-logged-in']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.login.success',
|
||||
payload: { ...user, authenticationMethod },
|
||||
|
@ -242,7 +228,7 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
private userLoginFailed(
|
||||
event: Event['user-login-failed'] /* exception: no `UserLike` to redact */,
|
||||
event: RelayEventMap['user-login-failed'] /* exception: no `UserLike` to redact */,
|
||||
) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.login.failed',
|
||||
|
@ -250,13 +236,13 @@ export class AuditEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Click
|
||||
*/
|
||||
// #endregion
|
||||
|
||||
// #region Click
|
||||
|
||||
@Redactable('inviter')
|
||||
@Redactable('invitee')
|
||||
private userInviteEmailClick(event: Event['user-invite-email-click']) {
|
||||
private userInviteEmailClick(event: RelayEventMap['user-invite-email-click']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.invitation.accepted',
|
||||
payload: event,
|
||||
|
@ -264,7 +250,7 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
@Redactable()
|
||||
private userPasswordResetEmailClick({ user }: Event['user-password-reset-email-click']) {
|
||||
private userPasswordResetEmailClick({ user }: RelayEventMap['user-password-reset-email-click']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.reset',
|
||||
payload: user,
|
||||
|
@ -272,19 +258,21 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
@Redactable()
|
||||
private userPasswordResetRequestClick({ user }: Event['user-password-reset-request-click']) {
|
||||
private userPasswordResetRequestClick({
|
||||
user,
|
||||
}: RelayEventMap['user-password-reset-request-click']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.reset.requested',
|
||||
payload: user,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Public API
|
||||
*/
|
||||
// #endregion
|
||||
|
||||
// #region Public API
|
||||
|
||||
@Redactable()
|
||||
private publicApiKeyCreated({ user }: Event['public-api-key-created']) {
|
||||
private publicApiKeyCreated({ user }: RelayEventMap['public-api-key-created']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.api.created',
|
||||
payload: user,
|
||||
|
@ -292,31 +280,31 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
@Redactable()
|
||||
private publicApiKeyDeleted({ user }: Event['public-api-key-deleted']) {
|
||||
private publicApiKeyDeleted({ user }: RelayEventMap['public-api-key-deleted']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.api.deleted',
|
||||
payload: user,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Emailing
|
||||
*/
|
||||
// #endregion
|
||||
|
||||
// #region Email
|
||||
|
||||
@Redactable()
|
||||
private emailFailed({ user, messageType }: Event['email-failed']) {
|
||||
private emailFailed({ user, messageType }: RelayEventMap['email-failed']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.email.failed',
|
||||
payload: { ...user, messageType },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Credentials
|
||||
*/
|
||||
// #endregion
|
||||
|
||||
// #region Credentials
|
||||
|
||||
@Redactable()
|
||||
private credentialsCreated({ user, ...rest }: Event['credentials-created']) {
|
||||
private credentialsCreated({ user, ...rest }: RelayEventMap['credentials-created']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.credentials.created',
|
||||
payload: { ...user, ...rest },
|
||||
|
@ -324,7 +312,7 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
@Redactable()
|
||||
private credentialsDeleted({ user, ...rest }: Event['credentials-deleted']) {
|
||||
private credentialsDeleted({ user, ...rest }: RelayEventMap['credentials-deleted']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.credentials.deleted',
|
||||
payload: { ...user, ...rest },
|
||||
|
@ -332,7 +320,7 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
@Redactable()
|
||||
private credentialsShared({ user, ...rest }: Event['credentials-shared']) {
|
||||
private credentialsShared({ user, ...rest }: RelayEventMap['credentials-shared']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.credentials.shared',
|
||||
payload: { ...user, ...rest },
|
||||
|
@ -340,19 +328,22 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
@Redactable()
|
||||
private credentialsUpdated({ user, ...rest }: Event['credentials-updated']) {
|
||||
private credentialsUpdated({ user, ...rest }: RelayEventMap['credentials-updated']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.credentials.updated',
|
||||
payload: { ...user, ...rest },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Community package
|
||||
*/
|
||||
// #endregion
|
||||
|
||||
// #region Community package
|
||||
|
||||
@Redactable()
|
||||
private communityPackageInstalled({ user, ...rest }: Event['community-package-installed']) {
|
||||
private communityPackageInstalled({
|
||||
user,
|
||||
...rest
|
||||
}: RelayEventMap['community-package-installed']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.package.installed',
|
||||
payload: { ...user, ...rest },
|
||||
|
@ -360,7 +351,7 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
@Redactable()
|
||||
private communityPackageUpdated({ user, ...rest }: Event['community-package-updated']) {
|
||||
private communityPackageUpdated({ user, ...rest }: RelayEventMap['community-package-updated']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.package.updated',
|
||||
payload: { ...user, ...rest },
|
||||
|
@ -368,28 +359,32 @@ export class AuditEventRelay {
|
|||
}
|
||||
|
||||
@Redactable()
|
||||
private communityPackageDeleted({ user, ...rest }: Event['community-package-deleted']) {
|
||||
private communityPackageDeleted({ user, ...rest }: RelayEventMap['community-package-deleted']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.package.deleted',
|
||||
payload: { ...user, ...rest },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution
|
||||
*/
|
||||
// #endregion
|
||||
|
||||
private executionThrottled({ executionId }: Event['execution-throttled']) {
|
||||
// #region Execution
|
||||
|
||||
private executionThrottled({ executionId }: RelayEventMap['execution-throttled']) {
|
||||
void this.eventBus.sendExecutionEvent({
|
||||
eventName: 'n8n.execution.throttled',
|
||||
payload: { executionId },
|
||||
});
|
||||
}
|
||||
|
||||
private executionStartedDuringBootup({ executionId }: Event['execution-started-during-bootup']) {
|
||||
private executionStartedDuringBootup({
|
||||
executionId,
|
||||
}: RelayEventMap['execution-started-during-bootup']) {
|
||||
void this.eventBus.sendExecutionEvent({
|
||||
eventName: 'n8n.execution.started-during-bootup',
|
||||
payload: { executionId },
|
||||
});
|
||||
}
|
||||
|
||||
// #endregion
|
||||
}
|
|
@ -11,12 +11,15 @@ export type UserLike = {
|
|||
role: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Events sent by `EventService` and forwarded by relays, e.g. `AuditEventRelay` and `TelemetryEventRelay`.
|
||||
*/
|
||||
export type Event = {
|
||||
export type RelayEventMap = {
|
||||
// #region Server
|
||||
|
||||
'server-started': {};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Workflow
|
||||
|
||||
'workflow-created': {
|
||||
user: UserLike;
|
||||
workflow: IWorkflowBase;
|
||||
|
@ -49,6 +52,10 @@ export type Event = {
|
|||
runData?: IRun;
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Node
|
||||
|
||||
'node-pre-execute': {
|
||||
executionId: string;
|
||||
workflow: IWorkflowBase;
|
||||
|
@ -61,6 +68,10 @@ export type Event = {
|
|||
nodeName: string;
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region User
|
||||
|
||||
'user-deleted': {
|
||||
user: UserLike;
|
||||
};
|
||||
|
@ -95,6 +106,10 @@ export type Event = {
|
|||
reason?: string;
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Click
|
||||
|
||||
'user-invite-email-click': {
|
||||
inviter: UserLike;
|
||||
invitee: UserLike;
|
||||
|
@ -108,6 +123,20 @@ export type Event = {
|
|||
user: UserLike;
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Public API
|
||||
|
||||
'public-api-key-created': {
|
||||
user: UserLike;
|
||||
publicApi: boolean;
|
||||
};
|
||||
|
||||
'public-api-key-deleted': {
|
||||
user: UserLike;
|
||||
publicApi: boolean;
|
||||
};
|
||||
|
||||
'public-api-invoked': {
|
||||
userId: string;
|
||||
path: string;
|
||||
|
@ -115,6 +144,10 @@ export type Event = {
|
|||
apiVersion: string;
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Email
|
||||
|
||||
'email-failed': {
|
||||
user: UserLike;
|
||||
messageType:
|
||||
|
@ -125,6 +158,10 @@ export type Event = {
|
|||
| 'Credentials shared';
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Credentials
|
||||
|
||||
'credentials-created': {
|
||||
user: UserLike;
|
||||
credentialType: string;
|
||||
|
@ -155,6 +192,10 @@ export type Event = {
|
|||
credentialId: string;
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Community package
|
||||
|
||||
'community-package-installed': {
|
||||
user: UserLike;
|
||||
inputString: string;
|
||||
|
@ -186,6 +227,10 @@ export type Event = {
|
|||
packageAuthorEmail?: string;
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Execution
|
||||
|
||||
'execution-throttled': {
|
||||
executionId: string;
|
||||
};
|
||||
|
@ -194,6 +239,10 @@ export type Event = {
|
|||
executionId: string;
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Project
|
||||
|
||||
'team-project-updated': {
|
||||
userId: string;
|
||||
role: GlobalRole;
|
||||
|
@ -217,6 +266,10 @@ export type Event = {
|
|||
role: GlobalRole;
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Source control
|
||||
|
||||
'source-control-settings-updated': {
|
||||
branchName: string;
|
||||
readOnlyInstance: boolean;
|
||||
|
@ -254,12 +307,24 @@ export type Event = {
|
|||
variablesPushed: number;
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region License
|
||||
|
||||
'license-renewal-attempted': {
|
||||
success: boolean;
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Variable
|
||||
|
||||
'variable-created': {};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region External secrets
|
||||
|
||||
'external-secrets-provider-settings-saved': {
|
||||
userId?: string;
|
||||
vaultType: string;
|
||||
|
@ -268,6 +333,10 @@ export type Event = {
|
|||
errorMessage?: string;
|
||||
};
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region LDAP
|
||||
|
||||
'ldap-general-sync-finished': {
|
||||
type: string;
|
||||
succeeded: boolean;
|
||||
|
@ -298,17 +367,5 @@ export type Event = {
|
|||
userId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Events listened to by more than one relay
|
||||
*/
|
||||
|
||||
'public-api-key-created': {
|
||||
user: UserLike; // audit and telemetry
|
||||
publicApi: boolean; // telemetry only
|
||||
};
|
||||
|
||||
'public-api-key-deleted': {
|
||||
user: UserLike; // audit and telemetry
|
||||
publicApi: boolean; // telemetry only
|
||||
};
|
||||
// #endregion
|
||||
};
|
|
@ -1,7 +1,7 @@
|
|||
import { Service } from 'typedi';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import type { Event } from '@/eventbus/event.types';
|
||||
import { Telemetry } from '.';
|
||||
import { EventService } from '@/events/event.service';
|
||||
import type { RelayEventMap } from '@/events/relay-event-map';
|
||||
import { Telemetry } from '../telemetry';
|
||||
import config from '@/config';
|
||||
import os from 'node:os';
|
||||
import { License } from '@/License';
|
||||
|
@ -16,11 +16,12 @@ import { SharedWorkflowRepository } from '@/databases/repositories/sharedWorkflo
|
|||
import { ProjectRelationRepository } from '@/databases/repositories/projectRelation.repository';
|
||||
import type { IExecutionTrackProperties } from '@/Interfaces';
|
||||
import { determineFinalExecutionStatus } from '@/executionLifecycleHooks/shared/sharedHookFunctions';
|
||||
import { EventRelay } from './event-relay';
|
||||
|
||||
@Service()
|
||||
export class TelemetryEventRelay {
|
||||
export class TelemetryEventRelay extends EventRelay {
|
||||
constructor(
|
||||
private readonly eventService: EventService,
|
||||
readonly eventService: EventService,
|
||||
private readonly telemetry: Telemetry,
|
||||
private readonly license: License,
|
||||
private readonly globalConfig: GlobalConfig,
|
||||
|
@ -28,106 +29,63 @@ export class TelemetryEventRelay {
|
|||
private readonly nodeTypes: NodeTypes,
|
||||
private readonly sharedWorkflowRepository: SharedWorkflowRepository,
|
||||
private readonly projectRelationRepository: ProjectRelationRepository,
|
||||
) {}
|
||||
) {
|
||||
super(eventService);
|
||||
}
|
||||
|
||||
async init() {
|
||||
if (!config.getEnv('diagnostics.enabled')) return;
|
||||
|
||||
await this.telemetry.init();
|
||||
|
||||
this.setupHandlers();
|
||||
}
|
||||
|
||||
private setupHandlers() {
|
||||
this.eventService.on('server-started', async () => await this.serverStarted());
|
||||
|
||||
this.eventService.on('team-project-updated', (event) => this.teamProjectUpdated(event));
|
||||
this.eventService.on('team-project-deleted', (event) => this.teamProjectDeleted(event));
|
||||
this.eventService.on('team-project-created', (event) => this.teamProjectCreated(event));
|
||||
this.eventService.on('source-control-settings-updated', (event) =>
|
||||
this.sourceControlSettingsUpdated(event),
|
||||
);
|
||||
this.eventService.on('source-control-user-started-pull-ui', (event) =>
|
||||
this.sourceControlUserStartedPullUi(event),
|
||||
);
|
||||
this.eventService.on('source-control-user-finished-pull-ui', (event) =>
|
||||
this.sourceControlUserFinishedPullUi(event),
|
||||
);
|
||||
this.eventService.on('source-control-user-pulled-api', (event) =>
|
||||
this.sourceControlUserPulledApi(event),
|
||||
);
|
||||
this.eventService.on('source-control-user-started-push-ui', (event) =>
|
||||
this.sourceControlUserStartedPushUi(event),
|
||||
);
|
||||
this.eventService.on('source-control-user-finished-push-ui', (event) =>
|
||||
this.sourceControlUserFinishedPushUi(event),
|
||||
);
|
||||
this.eventService.on('license-renewal-attempted', (event) => {
|
||||
this.licenseRenewalAttempted(event);
|
||||
});
|
||||
this.eventService.on('variable-created', () => this.variableCreated());
|
||||
this.eventService.on('external-secrets-provider-settings-saved', (event) => {
|
||||
this.externalSecretsProviderSettingsSaved(event);
|
||||
});
|
||||
this.eventService.on('public-api-invoked', (event) => {
|
||||
this.publicApiInvoked(event);
|
||||
});
|
||||
this.eventService.on('public-api-key-created', (event) => {
|
||||
this.publicApiKeyCreated(event);
|
||||
});
|
||||
this.eventService.on('public-api-key-deleted', (event) => {
|
||||
this.publicApiKeyDeleted(event);
|
||||
});
|
||||
this.eventService.on('community-package-installed', (event) => {
|
||||
this.communityPackageInstalled(event);
|
||||
});
|
||||
this.eventService.on('community-package-updated', (event) => {
|
||||
this.communityPackageUpdated(event);
|
||||
});
|
||||
this.eventService.on('community-package-deleted', (event) => {
|
||||
this.communityPackageDeleted(event);
|
||||
});
|
||||
|
||||
this.eventService.on('credentials-created', (event) => {
|
||||
this.credentialsCreated(event);
|
||||
});
|
||||
this.eventService.on('credentials-shared', (event) => {
|
||||
this.credentialsShared(event);
|
||||
});
|
||||
this.eventService.on('credentials-updated', (event) => {
|
||||
this.credentialsUpdated(event);
|
||||
});
|
||||
this.eventService.on('credentials-deleted', (event) => {
|
||||
this.credentialsDeleted(event);
|
||||
});
|
||||
this.eventService.on('ldap-general-sync-finished', (event) => {
|
||||
this.ldapGeneralSyncFinished(event);
|
||||
});
|
||||
this.eventService.on('ldap-settings-updated', (event) => {
|
||||
this.ldapSettingsUpdated(event);
|
||||
});
|
||||
this.eventService.on('ldap-login-sync-failed', (event) => {
|
||||
this.ldapLoginSyncFailed(event);
|
||||
});
|
||||
this.eventService.on('login-failed-due-to-ldap-disabled', (event) => {
|
||||
this.loginFailedDueToLdapDisabled(event);
|
||||
});
|
||||
|
||||
this.eventService.on('workflow-created', (event) => {
|
||||
this.workflowCreated(event);
|
||||
});
|
||||
this.eventService.on('workflow-deleted', (event) => {
|
||||
this.workflowDeleted(event);
|
||||
});
|
||||
this.eventService.on('workflow-saved', async (event) => {
|
||||
await this.workflowSaved(event);
|
||||
});
|
||||
this.eventService.on('workflow-post-execute', async (event) => {
|
||||
await this.workflowPostExecute(event);
|
||||
this.setupListeners({
|
||||
'team-project-updated': (event) => this.teamProjectUpdated(event),
|
||||
'team-project-deleted': (event) => this.teamProjectDeleted(event),
|
||||
'team-project-created': (event) => this.teamProjectCreated(event),
|
||||
'source-control-settings-updated': (event) => this.sourceControlSettingsUpdated(event),
|
||||
'source-control-user-started-pull-ui': (event) => this.sourceControlUserStartedPullUi(event),
|
||||
'source-control-user-finished-pull-ui': (event) =>
|
||||
this.sourceControlUserFinishedPullUi(event),
|
||||
'source-control-user-pulled-api': (event) => this.sourceControlUserPulledApi(event),
|
||||
'source-control-user-started-push-ui': (event) => this.sourceControlUserStartedPushUi(event),
|
||||
'source-control-user-finished-push-ui': (event) =>
|
||||
this.sourceControlUserFinishedPushUi(event),
|
||||
'license-renewal-attempted': (event) => this.licenseRenewalAttempted(event),
|
||||
'variable-created': () => this.variableCreated(),
|
||||
'external-secrets-provider-settings-saved': (event) =>
|
||||
this.externalSecretsProviderSettingsSaved(event),
|
||||
'public-api-invoked': (event) => this.publicApiInvoked(event),
|
||||
'public-api-key-created': (event) => this.publicApiKeyCreated(event),
|
||||
'public-api-key-deleted': (event) => this.publicApiKeyDeleted(event),
|
||||
'community-package-installed': (event) => this.communityPackageInstalled(event),
|
||||
'community-package-updated': (event) => this.communityPackageUpdated(event),
|
||||
'community-package-deleted': (event) => this.communityPackageDeleted(event),
|
||||
'credentials-created': (event) => this.credentialsCreated(event),
|
||||
'credentials-shared': (event) => this.credentialsShared(event),
|
||||
'credentials-updated': (event) => this.credentialsUpdated(event),
|
||||
'credentials-deleted': (event) => this.credentialsDeleted(event),
|
||||
'ldap-general-sync-finished': (event) => this.ldapGeneralSyncFinished(event),
|
||||
'ldap-settings-updated': (event) => this.ldapSettingsUpdated(event),
|
||||
'ldap-login-sync-failed': (event) => this.ldapLoginSyncFailed(event),
|
||||
'login-failed-due-to-ldap-disabled': (event) => this.loginFailedDueToLdapDisabled(event),
|
||||
'workflow-created': (event) => this.workflowCreated(event),
|
||||
'workflow-deleted': (event) => this.workflowDeleted(event),
|
||||
'workflow-saved': async (event) => await this.workflowSaved(event),
|
||||
'server-started': async () => await this.serverStarted(),
|
||||
'workflow-post-execute': async (event) => await this.workflowPostExecute(event),
|
||||
});
|
||||
}
|
||||
|
||||
private teamProjectUpdated({ userId, role, members, projectId }: Event['team-project-updated']) {
|
||||
// #endregion
|
||||
|
||||
// #region Team
|
||||
|
||||
private teamProjectUpdated({
|
||||
userId,
|
||||
role,
|
||||
members,
|
||||
projectId,
|
||||
}: RelayEventMap['team-project-updated']) {
|
||||
this.telemetry.track('Project settings updated', {
|
||||
user_id: userId,
|
||||
role,
|
||||
|
@ -143,7 +101,7 @@ export class TelemetryEventRelay {
|
|||
projectId,
|
||||
removalType,
|
||||
targetProjectId,
|
||||
}: Event['team-project-deleted']) {
|
||||
}: RelayEventMap['team-project-deleted']) {
|
||||
this.telemetry.track('User deleted project', {
|
||||
user_id: userId,
|
||||
role,
|
||||
|
@ -153,19 +111,23 @@ export class TelemetryEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
private teamProjectCreated({ userId, role }: Event['team-project-created']) {
|
||||
private teamProjectCreated({ userId, role }: RelayEventMap['team-project-created']) {
|
||||
this.telemetry.track('User created project', {
|
||||
user_id: userId,
|
||||
role,
|
||||
});
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Source control
|
||||
|
||||
private sourceControlSettingsUpdated({
|
||||
branchName,
|
||||
readOnlyInstance,
|
||||
repoType,
|
||||
connected,
|
||||
}: Event['source-control-settings-updated']) {
|
||||
}: RelayEventMap['source-control-settings-updated']) {
|
||||
this.telemetry.track('User updated source control settings', {
|
||||
branch_name: branchName,
|
||||
read_only_instance: readOnlyInstance,
|
||||
|
@ -178,7 +140,7 @@ export class TelemetryEventRelay {
|
|||
workflowUpdates,
|
||||
workflowConflicts,
|
||||
credConflicts,
|
||||
}: Event['source-control-user-started-pull-ui']) {
|
||||
}: RelayEventMap['source-control-user-started-pull-ui']) {
|
||||
this.telemetry.track('User started pull via UI', {
|
||||
workflow_updates: workflowUpdates,
|
||||
workflow_conflicts: workflowConflicts,
|
||||
|
@ -188,7 +150,7 @@ export class TelemetryEventRelay {
|
|||
|
||||
private sourceControlUserFinishedPullUi({
|
||||
workflowUpdates,
|
||||
}: Event['source-control-user-finished-pull-ui']) {
|
||||
}: RelayEventMap['source-control-user-finished-pull-ui']) {
|
||||
this.telemetry.track('User finished pull via UI', {
|
||||
workflow_updates: workflowUpdates,
|
||||
});
|
||||
|
@ -197,7 +159,7 @@ export class TelemetryEventRelay {
|
|||
private sourceControlUserPulledApi({
|
||||
workflowUpdates,
|
||||
forced,
|
||||
}: Event['source-control-user-pulled-api']) {
|
||||
}: RelayEventMap['source-control-user-pulled-api']) {
|
||||
console.log('source-control-user-pulled-api', {
|
||||
workflow_updates: workflowUpdates,
|
||||
forced,
|
||||
|
@ -214,7 +176,7 @@ export class TelemetryEventRelay {
|
|||
credsEligible,
|
||||
credsEligibleWithConflicts,
|
||||
variablesEligible,
|
||||
}: Event['source-control-user-started-push-ui']) {
|
||||
}: RelayEventMap['source-control-user-started-push-ui']) {
|
||||
this.telemetry.track('User started push via UI', {
|
||||
workflows_eligible: workflowsEligible,
|
||||
workflows_eligible_with_conflicts: workflowsEligibleWithConflicts,
|
||||
|
@ -229,7 +191,7 @@ export class TelemetryEventRelay {
|
|||
workflowsPushed,
|
||||
credsPushed,
|
||||
variablesPushed,
|
||||
}: Event['source-control-user-finished-push-ui']) {
|
||||
}: RelayEventMap['source-control-user-finished-push-ui']) {
|
||||
this.telemetry.track('User finished push via UI', {
|
||||
workflows_eligible: workflowsEligible,
|
||||
workflows_pushed: workflowsPushed,
|
||||
|
@ -238,23 +200,35 @@ export class TelemetryEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
private licenseRenewalAttempted({ success }: Event['license-renewal-attempted']) {
|
||||
// #endregion
|
||||
|
||||
// #region License
|
||||
|
||||
private licenseRenewalAttempted({ success }: RelayEventMap['license-renewal-attempted']) {
|
||||
this.telemetry.track('Instance attempted to refresh license', {
|
||||
success,
|
||||
});
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Variable
|
||||
|
||||
private variableCreated() {
|
||||
this.telemetry.track('User created variable');
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region External secrets
|
||||
|
||||
private externalSecretsProviderSettingsSaved({
|
||||
userId,
|
||||
vaultType,
|
||||
isValid,
|
||||
isNew,
|
||||
errorMessage,
|
||||
}: Event['external-secrets-provider-settings-saved']) {
|
||||
}: RelayEventMap['external-secrets-provider-settings-saved']) {
|
||||
this.telemetry.track('User updated external secrets settings', {
|
||||
user_id: userId,
|
||||
vault_type: vaultType,
|
||||
|
@ -264,7 +238,16 @@ export class TelemetryEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
private publicApiInvoked({ userId, path, method, apiVersion }: Event['public-api-invoked']) {
|
||||
// #endregion
|
||||
|
||||
// #region Public API
|
||||
|
||||
private publicApiInvoked({
|
||||
userId,
|
||||
path,
|
||||
method,
|
||||
apiVersion,
|
||||
}: RelayEventMap['public-api-invoked']) {
|
||||
this.telemetry.track('User invoked API', {
|
||||
user_id: userId,
|
||||
path,
|
||||
|
@ -273,7 +256,7 @@ export class TelemetryEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
private publicApiKeyCreated(event: Event['public-api-key-created']) {
|
||||
private publicApiKeyCreated(event: RelayEventMap['public-api-key-created']) {
|
||||
const { user, publicApi } = event;
|
||||
|
||||
this.telemetry.track('API key created', {
|
||||
|
@ -282,7 +265,7 @@ export class TelemetryEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
private publicApiKeyDeleted(event: Event['public-api-key-deleted']) {
|
||||
private publicApiKeyDeleted(event: RelayEventMap['public-api-key-deleted']) {
|
||||
const { user, publicApi } = event;
|
||||
|
||||
this.telemetry.track('API key deleted', {
|
||||
|
@ -291,6 +274,10 @@ export class TelemetryEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Community package
|
||||
|
||||
private communityPackageInstalled({
|
||||
user,
|
||||
inputString,
|
||||
|
@ -301,7 +288,7 @@ export class TelemetryEventRelay {
|
|||
packageAuthor,
|
||||
packageAuthorEmail,
|
||||
failureReason,
|
||||
}: Event['community-package-installed']) {
|
||||
}: RelayEventMap['community-package-installed']) {
|
||||
this.telemetry.track('cnr package install finished', {
|
||||
user_id: user.id,
|
||||
input_string: inputString,
|
||||
|
@ -323,7 +310,7 @@ export class TelemetryEventRelay {
|
|||
packageNodeNames,
|
||||
packageAuthor,
|
||||
packageAuthorEmail,
|
||||
}: Event['community-package-updated']) {
|
||||
}: RelayEventMap['community-package-updated']) {
|
||||
this.telemetry.track('cnr package updated', {
|
||||
user_id: user.id,
|
||||
package_name: packageName,
|
||||
|
@ -342,7 +329,7 @@ export class TelemetryEventRelay {
|
|||
packageNodeNames,
|
||||
packageAuthor,
|
||||
packageAuthorEmail,
|
||||
}: Event['community-package-deleted']) {
|
||||
}: RelayEventMap['community-package-deleted']) {
|
||||
this.telemetry.track('cnr package deleted', {
|
||||
user_id: user.id,
|
||||
package_name: packageName,
|
||||
|
@ -353,13 +340,17 @@ export class TelemetryEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Credentials
|
||||
|
||||
private credentialsCreated({
|
||||
user,
|
||||
credentialType,
|
||||
credentialId,
|
||||
projectId,
|
||||
projectType,
|
||||
}: Event['credentials-created']) {
|
||||
}: RelayEventMap['credentials-created']) {
|
||||
this.telemetry.track('User created credentials', {
|
||||
user_id: user.id,
|
||||
credential_type: credentialType,
|
||||
|
@ -376,7 +367,7 @@ export class TelemetryEventRelay {
|
|||
userIdSharer,
|
||||
userIdsShareesAdded,
|
||||
shareesRemoved,
|
||||
}: Event['credentials-shared']) {
|
||||
}: RelayEventMap['credentials-shared']) {
|
||||
this.telemetry.track('User updated cred sharing', {
|
||||
user_id: user.id,
|
||||
credential_type: credentialType,
|
||||
|
@ -387,7 +378,11 @@ export class TelemetryEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
private credentialsUpdated({ user, credentialId, credentialType }: Event['credentials-updated']) {
|
||||
private credentialsUpdated({
|
||||
user,
|
||||
credentialId,
|
||||
credentialType,
|
||||
}: RelayEventMap['credentials-updated']) {
|
||||
this.telemetry.track('User updated credentials', {
|
||||
user_id: user.id,
|
||||
credential_type: credentialType,
|
||||
|
@ -395,7 +390,11 @@ export class TelemetryEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
private credentialsDeleted({ user, credentialId, credentialType }: Event['credentials-deleted']) {
|
||||
private credentialsDeleted({
|
||||
user,
|
||||
credentialId,
|
||||
credentialType,
|
||||
}: RelayEventMap['credentials-deleted']) {
|
||||
this.telemetry.track('User deleted credentials', {
|
||||
user_id: user.id,
|
||||
credential_type: credentialType,
|
||||
|
@ -403,12 +402,16 @@ export class TelemetryEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region LDAP
|
||||
|
||||
private ldapGeneralSyncFinished({
|
||||
type,
|
||||
succeeded,
|
||||
usersSynced,
|
||||
error,
|
||||
}: Event['ldap-general-sync-finished']) {
|
||||
}: RelayEventMap['ldap-general-sync-finished']) {
|
||||
this.telemetry.track('Ldap general sync finished', {
|
||||
type,
|
||||
succeeded,
|
||||
|
@ -430,7 +433,7 @@ export class TelemetryEventRelay {
|
|||
synchronizationInterval,
|
||||
loginLabel,
|
||||
loginEnabled,
|
||||
}: Event['ldap-settings-updated']) {
|
||||
}: RelayEventMap['ldap-settings-updated']) {
|
||||
this.telemetry.track('User updated Ldap settings', {
|
||||
user_id: userId,
|
||||
loginIdAttribute,
|
||||
|
@ -447,21 +450,27 @@ export class TelemetryEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
private ldapLoginSyncFailed({ error }: Event['ldap-login-sync-failed']) {
|
||||
private ldapLoginSyncFailed({ error }: RelayEventMap['ldap-login-sync-failed']) {
|
||||
this.telemetry.track('Ldap login sync failed', { error });
|
||||
}
|
||||
|
||||
private loginFailedDueToLdapDisabled({ userId }: Event['login-failed-due-to-ldap-disabled']) {
|
||||
private loginFailedDueToLdapDisabled({
|
||||
userId,
|
||||
}: RelayEventMap['login-failed-due-to-ldap-disabled']) {
|
||||
this.telemetry.track('User login failed since ldap disabled', { user_ud: userId });
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Workflow
|
||||
|
||||
private workflowCreated({
|
||||
user,
|
||||
workflow,
|
||||
publicApi,
|
||||
projectId,
|
||||
projectType,
|
||||
}: Event['workflow-created']) {
|
||||
}: RelayEventMap['workflow-created']) {
|
||||
const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes);
|
||||
|
||||
this.telemetry.track('User created workflow', {
|
||||
|
@ -474,7 +483,7 @@ export class TelemetryEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
private workflowDeleted({ user, workflowId, publicApi }: Event['workflow-deleted']) {
|
||||
private workflowDeleted({ user, workflowId, publicApi }: RelayEventMap['workflow-deleted']) {
|
||||
this.telemetry.track('User deleted workflow', {
|
||||
user_id: user.id,
|
||||
workflow_id: workflowId,
|
||||
|
@ -482,7 +491,7 @@ export class TelemetryEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
private async workflowSaved({ user, workflow, publicApi }: Event['workflow-saved']) {
|
||||
private async workflowSaved({ user, workflow, publicApi }: RelayEventMap['workflow-saved']) {
|
||||
const isCloudDeployment = config.getEnv('deployment.type') === 'cloud';
|
||||
|
||||
const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes, {
|
||||
|
@ -528,72 +537,12 @@ export class TelemetryEventRelay {
|
|||
});
|
||||
}
|
||||
|
||||
private async serverStarted() {
|
||||
const cpus = os.cpus();
|
||||
const binaryDataConfig = config.getEnv('binaryDataManager');
|
||||
|
||||
const isS3Selected = config.getEnv('binaryDataManager.mode') === 's3';
|
||||
const isS3Available = config.getEnv('binaryDataManager.availableModes').includes('s3');
|
||||
const isS3Licensed = this.license.isBinaryDataS3Licensed();
|
||||
const authenticationMethod = config.getEnv('userManagement.authenticationMethod');
|
||||
|
||||
const info = {
|
||||
version_cli: N8N_VERSION,
|
||||
db_type: this.globalConfig.database.type,
|
||||
n8n_version_notifications_enabled: this.globalConfig.versionNotifications.enabled,
|
||||
n8n_disable_production_main_process:
|
||||
this.globalConfig.endpoints.disableProductionWebhooksOnMainProcess,
|
||||
system_info: {
|
||||
os: {
|
||||
type: os.type(),
|
||||
version: os.version(),
|
||||
},
|
||||
memory: os.totalmem() / 1024,
|
||||
cpus: {
|
||||
count: cpus.length,
|
||||
model: cpus[0].model,
|
||||
speed: cpus[0].speed,
|
||||
},
|
||||
},
|
||||
execution_variables: {
|
||||
executions_mode: config.getEnv('executions.mode'),
|
||||
executions_timeout: config.getEnv('executions.timeout'),
|
||||
executions_timeout_max: config.getEnv('executions.maxTimeout'),
|
||||
executions_data_save_on_error: config.getEnv('executions.saveDataOnError'),
|
||||
executions_data_save_on_success: config.getEnv('executions.saveDataOnSuccess'),
|
||||
executions_data_save_on_progress: config.getEnv('executions.saveExecutionProgress'),
|
||||
executions_data_save_manual_executions: config.getEnv(
|
||||
'executions.saveDataManualExecutions',
|
||||
),
|
||||
executions_data_prune: config.getEnv('executions.pruneData'),
|
||||
executions_data_max_age: config.getEnv('executions.pruneDataMaxAge'),
|
||||
},
|
||||
n8n_deployment_type: config.getEnv('deployment.type'),
|
||||
n8n_binary_data_mode: binaryDataConfig.mode,
|
||||
smtp_set_up: this.globalConfig.userManagement.emails.mode === 'smtp',
|
||||
ldap_allowed: authenticationMethod === 'ldap',
|
||||
saml_enabled: authenticationMethod === 'saml',
|
||||
license_plan_name: this.license.getPlanName(),
|
||||
license_tenant_id: config.getEnv('license.tenantId'),
|
||||
binary_data_s3: isS3Available && isS3Selected && isS3Licensed,
|
||||
multi_main_setup_enabled: config.getEnv('multiMainSetup.enabled'),
|
||||
};
|
||||
|
||||
const firstWorkflow = await this.workflowRepository.findOne({
|
||||
select: ['createdAt'],
|
||||
order: { createdAt: 'ASC' },
|
||||
where: {},
|
||||
});
|
||||
|
||||
this.telemetry.identify(info);
|
||||
this.telemetry.track('Instance started', {
|
||||
...info,
|
||||
earliest_workflow_created: firstWorkflow?.createdAt,
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
private async workflowPostExecute({ workflow, runData, userId }: Event['workflow-post-execute']) {
|
||||
private async workflowPostExecute({
|
||||
workflow,
|
||||
runData,
|
||||
userId,
|
||||
}: RelayEventMap['workflow-post-execute']) {
|
||||
if (!workflow.id) {
|
||||
return;
|
||||
}
|
||||
|
@ -725,4 +674,74 @@ export class TelemetryEventRelay {
|
|||
|
||||
this.telemetry.trackWorkflowExecution(telemetryProperties);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Server
|
||||
|
||||
private async serverStarted() {
|
||||
const cpus = os.cpus();
|
||||
const binaryDataConfig = config.getEnv('binaryDataManager');
|
||||
|
||||
const isS3Selected = config.getEnv('binaryDataManager.mode') === 's3';
|
||||
const isS3Available = config.getEnv('binaryDataManager.availableModes').includes('s3');
|
||||
const isS3Licensed = this.license.isBinaryDataS3Licensed();
|
||||
const authenticationMethod = config.getEnv('userManagement.authenticationMethod');
|
||||
|
||||
const info = {
|
||||
version_cli: N8N_VERSION,
|
||||
db_type: this.globalConfig.database.type,
|
||||
n8n_version_notifications_enabled: this.globalConfig.versionNotifications.enabled,
|
||||
n8n_disable_production_main_process:
|
||||
this.globalConfig.endpoints.disableProductionWebhooksOnMainProcess,
|
||||
system_info: {
|
||||
os: {
|
||||
type: os.type(),
|
||||
version: os.version(),
|
||||
},
|
||||
memory: os.totalmem() / 1024,
|
||||
cpus: {
|
||||
count: cpus.length,
|
||||
model: cpus[0].model,
|
||||
speed: cpus[0].speed,
|
||||
},
|
||||
},
|
||||
execution_variables: {
|
||||
executions_mode: config.getEnv('executions.mode'),
|
||||
executions_timeout: config.getEnv('executions.timeout'),
|
||||
executions_timeout_max: config.getEnv('executions.maxTimeout'),
|
||||
executions_data_save_on_error: config.getEnv('executions.saveDataOnError'),
|
||||
executions_data_save_on_success: config.getEnv('executions.saveDataOnSuccess'),
|
||||
executions_data_save_on_progress: config.getEnv('executions.saveExecutionProgress'),
|
||||
executions_data_save_manual_executions: config.getEnv(
|
||||
'executions.saveDataManualExecutions',
|
||||
),
|
||||
executions_data_prune: config.getEnv('executions.pruneData'),
|
||||
executions_data_max_age: config.getEnv('executions.pruneDataMaxAge'),
|
||||
},
|
||||
n8n_deployment_type: config.getEnv('deployment.type'),
|
||||
n8n_binary_data_mode: binaryDataConfig.mode,
|
||||
smtp_set_up: this.globalConfig.userManagement.emails.mode === 'smtp',
|
||||
ldap_allowed: authenticationMethod === 'ldap',
|
||||
saml_enabled: authenticationMethod === 'saml',
|
||||
license_plan_name: this.license.getPlanName(),
|
||||
license_tenant_id: config.getEnv('license.tenantId'),
|
||||
binary_data_s3: isS3Available && isS3Selected && isS3Licensed,
|
||||
multi_main_setup_enabled: config.getEnv('multiMainSetup.enabled'),
|
||||
};
|
||||
|
||||
const firstWorkflow = await this.workflowRepository.findOne({
|
||||
select: ['createdAt'],
|
||||
order: { createdAt: 'ASC' },
|
||||
where: {},
|
||||
});
|
||||
|
||||
this.telemetry.identify(info);
|
||||
this.telemetry.track('Instance started', {
|
||||
...info,
|
||||
earliest_workflow_created: firstWorkflow?.createdAt,
|
||||
});
|
||||
}
|
||||
|
||||
// #endregion
|
||||
}
|
|
@ -16,7 +16,7 @@ import config from '@/config';
|
|||
import { OnShutdown } from '@/decorators/OnShutdown';
|
||||
import type { QueueRecoverySettings } from './execution.types';
|
||||
import { OrchestrationService } from '@/services/orchestration.service';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
/**
|
||||
* Service for recovering key properties in executions.
|
||||
|
|
|
@ -3,7 +3,7 @@ import axios from 'axios';
|
|||
|
||||
import { Logger } from '@/Logger';
|
||||
import { License } from '@/License';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
import type { User } from '@db/entities/User';
|
||||
import { WorkflowRepository } from '@db/repositories/workflow.repository';
|
||||
import { BadRequestError } from '@/errors/response-errors/bad-request.error';
|
||||
|
|
|
@ -12,7 +12,7 @@ import { InternalHooks } from '@/InternalHooks';
|
|||
import { UrlService } from '@/services/url.service';
|
||||
import type { UserRequest } from '@/requests';
|
||||
import { InternalServerError } from '@/errors/response-errors/internal-server.error';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
@Service()
|
||||
export class UserService {
|
||||
|
|
|
@ -27,7 +27,7 @@ import {
|
|||
import { SamlService } from '../saml.service.ee';
|
||||
import { SamlConfiguration } from '../types/requests';
|
||||
import { getInitSSOFormView } from '../views/initSsoPost';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
@RestController('/sso/saml')
|
||||
export class SamlController {
|
||||
|
|
|
@ -33,7 +33,7 @@ import type { EntityManager } from '@n8n/typeorm';
|
|||
// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import
|
||||
import { In } from '@n8n/typeorm';
|
||||
import { SharedWorkflow } from '@/databases/entities/SharedWorkflow';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
|
||||
@Service()
|
||||
export class WorkflowService {
|
||||
|
|
|
@ -42,7 +42,7 @@ import { In, type FindOptionsRelations } from '@n8n/typeorm';
|
|||
import type { Project } from '@/databases/entities/Project';
|
||||
import { ProjectRelationRepository } from '@/databases/repositories/projectRelation.repository';
|
||||
import { z } from 'zod';
|
||||
import { EventService } from '@/eventbus/event.service';
|
||||
import { EventService } from '@/events/event.service';
|
||||
import { GlobalConfig } from '@n8n/config';
|
||||
|
||||
@RestController('/workflows')
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
TestFailProvider,
|
||||
} from '../../shared/ExternalSecrets/utils';
|
||||
import type { SuperAgentTest } from '../shared/types';
|
||||
import type { EventService } from '@/eventbus/event.service';
|
||||
import type { EventService } from '@/events/event.service';
|
||||
|
||||
let authOwnerAgent: SuperAgentTest;
|
||||
let authMemberAgent: SuperAgentTest;
|
||||
|
|
|
@ -15,7 +15,7 @@ import { type JobQueue, Queue } from '@/Queue';
|
|||
|
||||
import { setupTestCommand } from '@test-integration/utils/testCommand';
|
||||
import { mockInstance } from '../../shared/mocking';
|
||||
import { AuditEventRelay } from '@/eventbus/audit-event-relay.service';
|
||||
import { LogStreamingEventRelay } from '@/events/log-streaming-event-relay';
|
||||
|
||||
config.set('executions.mode', 'queue');
|
||||
config.set('binaryDataManager.availableModes', 'filesystem');
|
||||
|
@ -26,7 +26,7 @@ const externalHooks = mockInstance(ExternalHooks);
|
|||
const externalSecretsManager = mockInstance(ExternalSecretsManager);
|
||||
const license = mockInstance(License);
|
||||
const messageEventBus = mockInstance(MessageEventBus);
|
||||
const auditEventRelay = mockInstance(AuditEventRelay);
|
||||
const logStreamingEventRelay = mockInstance(LogStreamingEventRelay);
|
||||
const orchestrationHandlerWorkerService = mockInstance(OrchestrationHandlerWorkerService);
|
||||
const queue = mockInstance(Queue);
|
||||
const orchestrationWorkerService = mockInstance(OrchestrationWorkerService);
|
||||
|
@ -45,7 +45,7 @@ test('worker initializes all its components', async () => {
|
|||
expect(externalHooks.init).toHaveBeenCalledTimes(1);
|
||||
expect(externalSecretsManager.init).toHaveBeenCalledTimes(1);
|
||||
expect(messageEventBus.initialize).toHaveBeenCalledTimes(1);
|
||||
expect(auditEventRelay.init).toHaveBeenCalledTimes(1);
|
||||
expect(logStreamingEventRelay.init).toHaveBeenCalledTimes(1);
|
||||
expect(queue.init).toHaveBeenCalledTimes(1);
|
||||
expect(queue.process).toHaveBeenCalledTimes(1);
|
||||
expect(orchestrationWorkerService.init).toHaveBeenCalledTimes(1);
|
||||
|
|
|
@ -4,7 +4,7 @@ import { mock } from 'jest-mock-extended';
|
|||
|
||||
import type { BaseCommand } from '@/commands/BaseCommand';
|
||||
import * as testDb from '../testDb';
|
||||
import { TelemetryEventRelay } from '@/telemetry/telemetry-event-relay.service';
|
||||
import { TelemetryEventRelay } from '@/events/telemetry-event-relay';
|
||||
import { mockInstance } from '@test/mocking';
|
||||
|
||||
export const setupTestCommand = <T extends BaseCommand>(Command: Class<T>) => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { LicenseErrors, LicenseService } from '@/license/license.service';
|
||||
import type { License } from '@/License';
|
||||
import type { EventService } from '@/eventbus/event.service';
|
||||
import type { EventService } from '@/events/event.service';
|
||||
import type { WorkflowRepository } from '@db/repositories/workflow.repository';
|
||||
import type { TEntitlement } from '@n8n_io/license-sdk';
|
||||
import { mock } from 'jest-mock-extended';
|
||||
|
|
Loading…
Reference in a new issue