refactor(core): Clean up event relays (no-changelog) (#10284)
This commit is contained in:
@@ -1,667 +0,0 @@
|
||||
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 type { INode, IRun, IWorkflowBase } from 'n8n-workflow';
|
||||
import type { IWorkflowDb } from '@/Interfaces';
|
||||
|
||||
describe('AuditEventRelay', () => {
|
||||
const eventBus = mock<MessageEventBus>();
|
||||
const eventService = new EventService();
|
||||
const auditor = new AuditEventRelay(eventService, eventBus);
|
||||
auditor.init();
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('workflow events', () => {
|
||||
it('should log on `workflow-created` event', () => {
|
||||
const event: Event['workflow-created'] = {
|
||||
user: {
|
||||
id: '123',
|
||||
email: 'john@n8n.io',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
role: 'owner',
|
||||
},
|
||||
workflow: mock<IWorkflowBase>({
|
||||
id: 'wf123',
|
||||
name: 'Test Workflow',
|
||||
}),
|
||||
publicApi: false,
|
||||
projectId: 'proj123',
|
||||
projectType: 'personal',
|
||||
};
|
||||
|
||||
eventService.emit('workflow-created', event);
|
||||
|
||||
expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.audit.workflow.created',
|
||||
payload: {
|
||||
userId: '123',
|
||||
_email: 'john@n8n.io',
|
||||
_firstName: 'John',
|
||||
_lastName: 'Doe',
|
||||
globalRole: 'owner',
|
||||
workflowId: 'wf123',
|
||||
workflowName: 'Test Workflow',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should log on `workflow-deleted` event', () => {
|
||||
const event: Event['workflow-deleted'] = {
|
||||
user: {
|
||||
id: '456',
|
||||
email: 'jane@n8n.io',
|
||||
firstName: 'Jane',
|
||||
lastName: 'Smith',
|
||||
role: 'user',
|
||||
},
|
||||
workflowId: 'wf789',
|
||||
publicApi: false,
|
||||
};
|
||||
|
||||
eventService.emit('workflow-deleted', event);
|
||||
|
||||
expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.audit.workflow.deleted',
|
||||
payload: {
|
||||
userId: '456',
|
||||
_email: 'jane@n8n.io',
|
||||
_firstName: 'Jane',
|
||||
_lastName: 'Smith',
|
||||
globalRole: 'user',
|
||||
workflowId: 'wf789',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should log on `workflow-saved` event', () => {
|
||||
const event: Event['workflow-saved'] = {
|
||||
user: {
|
||||
id: '789',
|
||||
email: 'alex@n8n.io',
|
||||
firstName: 'Alex',
|
||||
lastName: 'Johnson',
|
||||
role: 'editor',
|
||||
},
|
||||
workflow: mock<IWorkflowDb>({ id: 'wf101', name: 'Updated Workflow' }),
|
||||
publicApi: false,
|
||||
};
|
||||
|
||||
eventService.emit('workflow-saved', event);
|
||||
|
||||
expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.audit.workflow.updated',
|
||||
payload: {
|
||||
userId: '789',
|
||||
_email: 'alex@n8n.io',
|
||||
_firstName: 'Alex',
|
||||
_lastName: 'Johnson',
|
||||
globalRole: 'editor',
|
||||
workflowId: 'wf101',
|
||||
workflowName: 'Updated Workflow',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should log on `workflow-pre-execute` event', () => {
|
||||
const workflow = mock<IWorkflowBase>({
|
||||
id: 'wf202',
|
||||
name: 'Test Workflow',
|
||||
active: true,
|
||||
nodes: [],
|
||||
connections: {},
|
||||
staticData: undefined,
|
||||
settings: {},
|
||||
});
|
||||
|
||||
const event: Event['workflow-pre-execute'] = {
|
||||
executionId: 'exec123',
|
||||
data: workflow,
|
||||
};
|
||||
|
||||
eventService.emit('workflow-pre-execute', event);
|
||||
|
||||
expect(eventBus.sendWorkflowEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.workflow.started',
|
||||
payload: {
|
||||
executionId: 'exec123',
|
||||
userId: undefined,
|
||||
workflowId: 'wf202',
|
||||
isManual: false,
|
||||
workflowName: 'Test Workflow',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should log on `workflow-post-execute` for successful execution', () => {
|
||||
const payload = mock<Event['workflow-post-execute']>({
|
||||
executionId: 'some-id',
|
||||
userId: 'some-id',
|
||||
workflow: mock<IWorkflowBase>({ id: 'some-id', name: 'some-name' }),
|
||||
runData: mock<IRun>({ status: 'success', mode: 'manual', data: { resultData: {} } }),
|
||||
});
|
||||
|
||||
eventService.emit('workflow-post-execute', payload);
|
||||
|
||||
const { runData: _, workflow: __, ...rest } = payload;
|
||||
|
||||
expect(eventBus.sendWorkflowEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.workflow.success',
|
||||
payload: {
|
||||
...rest,
|
||||
success: true,
|
||||
isManual: true,
|
||||
workflowName: 'some-name',
|
||||
workflowId: 'some-id',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should log on `workflow-post-execute` event for unsuccessful execution', () => {
|
||||
const runData = mock<IRun>({
|
||||
status: 'error',
|
||||
mode: 'manual',
|
||||
data: {
|
||||
resultData: {
|
||||
lastNodeExecuted: 'some-node',
|
||||
// @ts-expect-error Partial mock
|
||||
error: {
|
||||
node: mock<INode>({ type: 'some-type' }),
|
||||
message: 'some-message',
|
||||
},
|
||||
errorMessage: 'some-message',
|
||||
},
|
||||
},
|
||||
}) as unknown as IRun;
|
||||
|
||||
const event = {
|
||||
executionId: 'some-id',
|
||||
userId: 'some-id',
|
||||
workflow: mock<IWorkflowBase>({ id: 'some-id', name: 'some-name' }),
|
||||
runData,
|
||||
};
|
||||
|
||||
eventService.emit('workflow-post-execute', event);
|
||||
|
||||
const { runData: _, workflow: __, ...rest } = event;
|
||||
|
||||
expect(eventBus.sendWorkflowEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.workflow.failed',
|
||||
payload: {
|
||||
...rest,
|
||||
success: false,
|
||||
isManual: true,
|
||||
workflowName: 'some-name',
|
||||
workflowId: 'some-id',
|
||||
lastNodeExecuted: 'some-node',
|
||||
errorNodeType: 'some-type',
|
||||
errorMessage: 'some-message',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('user events', () => {
|
||||
it('should log on `user-updated` event', () => {
|
||||
const event: Event['user-updated'] = {
|
||||
user: {
|
||||
id: 'user456',
|
||||
email: 'updated@example.com',
|
||||
firstName: 'Updated',
|
||||
lastName: 'User',
|
||||
role: 'global:member',
|
||||
},
|
||||
fieldsChanged: ['firstName', 'lastName', 'password'],
|
||||
};
|
||||
|
||||
eventService.emit('user-updated', event);
|
||||
|
||||
expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.audit.user.updated',
|
||||
payload: {
|
||||
userId: 'user456',
|
||||
_email: 'updated@example.com',
|
||||
_firstName: 'Updated',
|
||||
_lastName: 'User',
|
||||
globalRole: 'global:member',
|
||||
fieldsChanged: ['firstName', 'lastName', 'password'],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should log on `user-deleted` event', () => {
|
||||
const event: Event['user-deleted'] = {
|
||||
user: {
|
||||
id: '123',
|
||||
email: 'john@n8n.io',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
role: 'some-role',
|
||||
},
|
||||
};
|
||||
|
||||
eventService.emit('user-deleted', event);
|
||||
|
||||
expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.audit.user.deleted',
|
||||
payload: {
|
||||
userId: '123',
|
||||
_email: 'john@n8n.io',
|
||||
_firstName: 'John',
|
||||
_lastName: 'Doe',
|
||||
globalRole: 'some-role',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('click events', () => {
|
||||
it('should log on `user-password-reset-request-click` event', () => {
|
||||
const event: Event['user-password-reset-request-click'] = {
|
||||
user: {
|
||||
id: 'user101',
|
||||
email: 'user101@example.com',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
role: 'global:member',
|
||||
},
|
||||
};
|
||||
|
||||
eventService.emit('user-password-reset-request-click', event);
|
||||
|
||||
expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.audit.user.reset.requested',
|
||||
payload: {
|
||||
userId: 'user101',
|
||||
_email: 'user101@example.com',
|
||||
_firstName: 'John',
|
||||
_lastName: 'Doe',
|
||||
globalRole: 'global:member',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should log on `user-invite-email-click` event', () => {
|
||||
const event: Event['user-invite-email-click'] = {
|
||||
inviter: {
|
||||
id: '123',
|
||||
email: 'john@n8n.io',
|
||||
firstName: 'John',
|
||||
lastName: 'Doe',
|
||||
role: 'some-role',
|
||||
},
|
||||
invitee: {
|
||||
id: '456',
|
||||
email: 'jane@n8n.io',
|
||||
firstName: 'Jane',
|
||||
lastName: 'Doe',
|
||||
role: 'some-other-role',
|
||||
},
|
||||
};
|
||||
|
||||
eventService.emit('user-invite-email-click', event);
|
||||
|
||||
expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.audit.user.invitation.accepted',
|
||||
payload: {
|
||||
inviter: {
|
||||
userId: '123',
|
||||
_email: 'john@n8n.io',
|
||||
_firstName: 'John',
|
||||
_lastName: 'Doe',
|
||||
globalRole: 'some-role',
|
||||
},
|
||||
invitee: {
|
||||
userId: '456',
|
||||
_email: 'jane@n8n.io',
|
||||
_firstName: 'Jane',
|
||||
_lastName: 'Doe',
|
||||
globalRole: 'some-other-role',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('node events', () => {
|
||||
it('should log on `node-pre-execute` event', () => {
|
||||
const workflow = mock<IWorkflowBase>({
|
||||
id: 'wf303',
|
||||
name: 'Test Workflow with Nodes',
|
||||
active: true,
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
name: 'Start Node',
|
||||
type: 'n8n-nodes-base.start',
|
||||
typeVersion: 1,
|
||||
position: [100, 200],
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
name: 'HTTP Request',
|
||||
type: 'n8n-nodes-base.httpRequest',
|
||||
typeVersion: 1,
|
||||
position: [300, 200],
|
||||
},
|
||||
],
|
||||
connections: {},
|
||||
settings: {},
|
||||
});
|
||||
|
||||
const event: Event['node-pre-execute'] = {
|
||||
executionId: 'exec456',
|
||||
nodeName: 'HTTP Request',
|
||||
workflow,
|
||||
};
|
||||
|
||||
eventService.emit('node-pre-execute', event);
|
||||
|
||||
expect(eventBus.sendNodeEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.node.started',
|
||||
payload: {
|
||||
executionId: 'exec456',
|
||||
nodeName: 'HTTP Request',
|
||||
workflowId: 'wf303',
|
||||
workflowName: 'Test Workflow with Nodes',
|
||||
nodeType: 'n8n-nodes-base.httpRequest',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should log on `node-post-execute` event', () => {
|
||||
const workflow = mock<IWorkflowBase>({
|
||||
id: 'wf404',
|
||||
name: 'Test Workflow with Completed Node',
|
||||
active: true,
|
||||
nodes: [
|
||||
{
|
||||
id: 'node1',
|
||||
name: 'Start Node',
|
||||
type: 'n8n-nodes-base.start',
|
||||
typeVersion: 1,
|
||||
position: [100, 200],
|
||||
},
|
||||
{
|
||||
id: 'node2',
|
||||
name: 'HTTP Response',
|
||||
type: 'n8n-nodes-base.httpResponse',
|
||||
typeVersion: 1,
|
||||
position: [300, 200],
|
||||
},
|
||||
],
|
||||
connections: {},
|
||||
settings: {},
|
||||
});
|
||||
|
||||
const event: Event['node-post-execute'] = {
|
||||
executionId: 'exec789',
|
||||
nodeName: 'HTTP Response',
|
||||
workflow,
|
||||
};
|
||||
|
||||
eventService.emit('node-post-execute', event);
|
||||
|
||||
expect(eventBus.sendNodeEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.node.finished',
|
||||
payload: {
|
||||
executionId: 'exec789',
|
||||
nodeName: 'HTTP Response',
|
||||
workflowId: 'wf404',
|
||||
workflowName: 'Test Workflow with Completed Node',
|
||||
nodeType: 'n8n-nodes-base.httpResponse',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('credentials events', () => {
|
||||
it('should log on `credentials-shared` event', () => {
|
||||
const event: Event['credentials-shared'] = {
|
||||
user: {
|
||||
id: 'user123',
|
||||
email: 'sharer@example.com',
|
||||
firstName: 'Alice',
|
||||
lastName: 'Sharer',
|
||||
role: 'global:owner',
|
||||
},
|
||||
credentialId: 'cred789',
|
||||
credentialType: 'githubApi',
|
||||
userIdSharer: 'user123',
|
||||
userIdsShareesAdded: ['user456', 'user789'],
|
||||
shareesRemoved: null,
|
||||
};
|
||||
|
||||
eventService.emit('credentials-shared', event);
|
||||
|
||||
expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.audit.user.credentials.shared',
|
||||
payload: {
|
||||
userId: 'user123',
|
||||
_email: 'sharer@example.com',
|
||||
_firstName: 'Alice',
|
||||
_lastName: 'Sharer',
|
||||
globalRole: 'global:owner',
|
||||
credentialId: 'cred789',
|
||||
credentialType: 'githubApi',
|
||||
userIdSharer: 'user123',
|
||||
userIdsShareesAdded: ['user456', 'user789'],
|
||||
shareesRemoved: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should log on `credentials-created` event', () => {
|
||||
const event: Event['credentials-created'] = {
|
||||
user: {
|
||||
id: 'user123',
|
||||
email: 'user@example.com',
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
role: 'global:owner',
|
||||
},
|
||||
credentialType: 'githubApi',
|
||||
credentialId: 'cred456',
|
||||
publicApi: false,
|
||||
projectId: 'proj789',
|
||||
projectType: 'Personal',
|
||||
};
|
||||
|
||||
eventService.emit('credentials-created', event);
|
||||
|
||||
expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.audit.user.credentials.created',
|
||||
payload: {
|
||||
userId: 'user123',
|
||||
_email: 'user@example.com',
|
||||
_firstName: 'Test',
|
||||
_lastName: 'User',
|
||||
globalRole: 'global:owner',
|
||||
credentialType: 'githubApi',
|
||||
credentialId: 'cred456',
|
||||
publicApi: false,
|
||||
projectId: 'proj789',
|
||||
projectType: 'Personal',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('auth events', () => {
|
||||
it('should log on `user-login-failed` event', () => {
|
||||
const event: Event['user-login-failed'] = {
|
||||
userEmail: 'user@example.com',
|
||||
authenticationMethod: 'email',
|
||||
reason: 'Invalid password',
|
||||
};
|
||||
|
||||
eventService.emit('user-login-failed', event);
|
||||
|
||||
expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.audit.user.login.failed',
|
||||
payload: {
|
||||
userEmail: 'user@example.com',
|
||||
authenticationMethod: 'email',
|
||||
reason: 'Invalid password',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('community package events', () => {
|
||||
it('should log on `community-package-updated` event', () => {
|
||||
const event: Event['community-package-updated'] = {
|
||||
user: {
|
||||
id: 'user202',
|
||||
email: 'packageupdater@example.com',
|
||||
firstName: 'Package',
|
||||
lastName: 'Updater',
|
||||
role: 'global:admin',
|
||||
},
|
||||
packageName: 'n8n-nodes-awesome-package',
|
||||
packageVersionCurrent: '1.0.0',
|
||||
packageVersionNew: '1.1.0',
|
||||
packageNodeNames: ['AwesomeNode1', 'AwesomeNode2'],
|
||||
packageAuthor: 'Jane Doe',
|
||||
packageAuthorEmail: 'jane@example.com',
|
||||
};
|
||||
|
||||
eventService.emit('community-package-updated', event);
|
||||
|
||||
expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.audit.package.updated',
|
||||
payload: {
|
||||
userId: 'user202',
|
||||
_email: 'packageupdater@example.com',
|
||||
_firstName: 'Package',
|
||||
_lastName: 'Updater',
|
||||
globalRole: 'global:admin',
|
||||
packageName: 'n8n-nodes-awesome-package',
|
||||
packageVersionCurrent: '1.0.0',
|
||||
packageVersionNew: '1.1.0',
|
||||
packageNodeNames: ['AwesomeNode1', 'AwesomeNode2'],
|
||||
packageAuthor: 'Jane Doe',
|
||||
packageAuthorEmail: 'jane@example.com',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should log on `community-package-installed` event', () => {
|
||||
const event: Event['community-package-installed'] = {
|
||||
user: {
|
||||
id: 'user789',
|
||||
email: 'admin@example.com',
|
||||
firstName: 'Admin',
|
||||
lastName: 'User',
|
||||
role: 'global:admin',
|
||||
},
|
||||
inputString: 'n8n-nodes-custom-package',
|
||||
packageName: 'n8n-nodes-custom-package',
|
||||
success: true,
|
||||
packageVersion: '1.0.0',
|
||||
packageNodeNames: ['CustomNode1', 'CustomNode2'],
|
||||
packageAuthor: 'John Doe',
|
||||
packageAuthorEmail: 'john@example.com',
|
||||
};
|
||||
|
||||
eventService.emit('community-package-installed', event);
|
||||
|
||||
expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.audit.package.installed',
|
||||
payload: {
|
||||
userId: 'user789',
|
||||
_email: 'admin@example.com',
|
||||
_firstName: 'Admin',
|
||||
_lastName: 'User',
|
||||
globalRole: 'global:admin',
|
||||
inputString: 'n8n-nodes-custom-package',
|
||||
packageName: 'n8n-nodes-custom-package',
|
||||
success: true,
|
||||
packageVersion: '1.0.0',
|
||||
packageNodeNames: ['CustomNode1', 'CustomNode2'],
|
||||
packageAuthor: 'John Doe',
|
||||
packageAuthorEmail: 'john@example.com',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('email events', () => {
|
||||
it('should log on `email-failed` event', () => {
|
||||
const event: Event['email-failed'] = {
|
||||
user: {
|
||||
id: 'user789',
|
||||
email: 'recipient@example.com',
|
||||
firstName: 'Failed',
|
||||
lastName: 'Recipient',
|
||||
role: 'global:member',
|
||||
},
|
||||
messageType: 'New user invite',
|
||||
};
|
||||
|
||||
eventService.emit('email-failed', event);
|
||||
|
||||
expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.audit.user.email.failed',
|
||||
payload: {
|
||||
userId: 'user789',
|
||||
_email: 'recipient@example.com',
|
||||
_firstName: 'Failed',
|
||||
_lastName: 'Recipient',
|
||||
globalRole: 'global:member',
|
||||
messageType: 'New user invite',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('public API events', () => {
|
||||
it('should log on `public-api-key-created` event', () => {
|
||||
const event: Event['public-api-key-created'] = {
|
||||
user: {
|
||||
id: 'user101',
|
||||
email: 'apiuser@example.com',
|
||||
firstName: 'API',
|
||||
lastName: 'User',
|
||||
role: 'global:owner',
|
||||
},
|
||||
publicApi: true,
|
||||
};
|
||||
|
||||
eventService.emit('public-api-key-created', event);
|
||||
|
||||
expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.audit.user.api.created',
|
||||
payload: {
|
||||
userId: 'user101',
|
||||
_email: 'apiuser@example.com',
|
||||
_firstName: 'API',
|
||||
_lastName: 'User',
|
||||
globalRole: 'global:owner',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('execution events', () => {
|
||||
it('should log on `execution-throttled` event', () => {
|
||||
const event: Event['execution-throttled'] = {
|
||||
executionId: 'exec123456',
|
||||
};
|
||||
|
||||
eventService.emit('execution-throttled', event);
|
||||
|
||||
expect(eventBus.sendExecutionEvent).toHaveBeenCalledWith({
|
||||
eventName: 'n8n.execution.throttled',
|
||||
payload: {
|
||||
executionId: 'exec123456',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,395 +0,0 @@
|
||||
import { Service } from 'typedi';
|
||||
import { MessageEventBus } from './MessageEventBus/MessageEventBus';
|
||||
import { Redactable } from '@/decorators/Redactable';
|
||||
import { EventService } from './event.service';
|
||||
import type { Event } from './event.types';
|
||||
import type { IWorkflowBase } from 'n8n-workflow';
|
||||
|
||||
@Service()
|
||||
export class AuditEventRelay {
|
||||
constructor(
|
||||
private readonly eventService: EventService,
|
||||
private readonly eventBus: MessageEventBus,
|
||||
) {}
|
||||
|
||||
init() {
|
||||
this.setupHandlers();
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
@Redactable()
|
||||
private workflowCreated({ user, workflow }: Event['workflow-created']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.workflow.created',
|
||||
payload: {
|
||||
...user,
|
||||
workflowId: workflow.id,
|
||||
workflowName: workflow.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@Redactable()
|
||||
private workflowDeleted({ user, workflowId }: Event['workflow-deleted']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.workflow.deleted',
|
||||
payload: { ...user, workflowId },
|
||||
});
|
||||
}
|
||||
|
||||
@Redactable()
|
||||
private workflowSaved({ user, workflow }: Event['workflow-saved']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.workflow.updated',
|
||||
payload: {
|
||||
...user,
|
||||
workflowId: workflow.id,
|
||||
workflowName: workflow.name,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private workflowPreExecute({ data, executionId }: Event['workflow-pre-execute']) {
|
||||
const payload =
|
||||
'executionData' in data
|
||||
? {
|
||||
executionId,
|
||||
userId: data.userId,
|
||||
workflowId: data.workflowData.id,
|
||||
isManual: data.executionMode === 'manual',
|
||||
workflowName: data.workflowData.name,
|
||||
}
|
||||
: {
|
||||
executionId,
|
||||
userId: undefined,
|
||||
workflowId: (data as IWorkflowBase).id,
|
||||
isManual: false,
|
||||
workflowName: (data as IWorkflowBase).name,
|
||||
};
|
||||
|
||||
void this.eventBus.sendWorkflowEvent({
|
||||
eventName: 'n8n.workflow.started',
|
||||
payload,
|
||||
});
|
||||
}
|
||||
|
||||
private workflowPostExecute(event: Event['workflow-post-execute']) {
|
||||
const { runData, workflow, ...rest } = event;
|
||||
|
||||
const payload = {
|
||||
...rest,
|
||||
success: runData?.status === 'success',
|
||||
isManual: runData?.mode === 'manual',
|
||||
workflowId: workflow.id,
|
||||
workflowName: workflow.name,
|
||||
};
|
||||
|
||||
if (payload.success) {
|
||||
void this.eventBus.sendWorkflowEvent({
|
||||
eventName: 'n8n.workflow.success',
|
||||
payload,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void this.eventBus.sendWorkflowEvent({
|
||||
eventName: 'n8n.workflow.failed',
|
||||
payload: {
|
||||
...payload,
|
||||
lastNodeExecuted: runData?.data.resultData.lastNodeExecuted,
|
||||
errorNodeType:
|
||||
runData?.data.resultData.error && 'node' in runData?.data.resultData.error
|
||||
? runData?.data.resultData.error.node?.type
|
||||
: undefined,
|
||||
errorMessage: runData?.data.resultData.error?.message.toString(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Node
|
||||
*/
|
||||
|
||||
private nodePreExecute({ workflow, executionId, nodeName }: Event['node-pre-execute']) {
|
||||
void this.eventBus.sendNodeEvent({
|
||||
eventName: 'n8n.node.started',
|
||||
payload: {
|
||||
workflowId: workflow.id,
|
||||
workflowName: workflow.name,
|
||||
executionId,
|
||||
nodeType: workflow.nodes.find((n) => n.name === nodeName)?.type,
|
||||
nodeName,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private nodePostExecute({ workflow, executionId, nodeName }: Event['node-post-execute']) {
|
||||
void this.eventBus.sendNodeEvent({
|
||||
eventName: 'n8n.node.finished',
|
||||
payload: {
|
||||
workflowId: workflow.id,
|
||||
workflowName: workflow.name,
|
||||
executionId,
|
||||
nodeType: workflow.nodes.find((n) => n.name === nodeName)?.type,
|
||||
nodeName,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* User
|
||||
*/
|
||||
|
||||
@Redactable()
|
||||
private userDeleted({ user }: Event['user-deleted']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.deleted',
|
||||
payload: user,
|
||||
});
|
||||
}
|
||||
|
||||
@Redactable()
|
||||
private userInvited({ user, targetUserId }: Event['user-invited']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.invited',
|
||||
payload: { ...user, targetUserId },
|
||||
});
|
||||
}
|
||||
|
||||
@Redactable()
|
||||
private userReinvited({ user, targetUserId }: Event['user-reinvited']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.reinvited',
|
||||
payload: { ...user, targetUserId },
|
||||
});
|
||||
}
|
||||
|
||||
@Redactable()
|
||||
private userUpdated({ user, fieldsChanged }: Event['user-updated']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.updated',
|
||||
payload: { ...user, fieldsChanged },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth
|
||||
*/
|
||||
|
||||
@Redactable()
|
||||
private userSignedUp({ user }: Event['user-signed-up']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.signedup',
|
||||
payload: user,
|
||||
});
|
||||
}
|
||||
|
||||
@Redactable()
|
||||
private userLoggedIn({ user, authenticationMethod }: Event['user-logged-in']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.login.success',
|
||||
payload: { ...user, authenticationMethod },
|
||||
});
|
||||
}
|
||||
|
||||
private userLoginFailed(
|
||||
event: Event['user-login-failed'] /* exception: no `UserLike` to redact */,
|
||||
) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.login.failed',
|
||||
payload: event,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Click
|
||||
*/
|
||||
|
||||
@Redactable('inviter')
|
||||
@Redactable('invitee')
|
||||
private userInviteEmailClick(event: Event['user-invite-email-click']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.invitation.accepted',
|
||||
payload: event,
|
||||
});
|
||||
}
|
||||
|
||||
@Redactable()
|
||||
private userPasswordResetEmailClick({ user }: Event['user-password-reset-email-click']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.reset',
|
||||
payload: user,
|
||||
});
|
||||
}
|
||||
|
||||
@Redactable()
|
||||
private userPasswordResetRequestClick({ user }: Event['user-password-reset-request-click']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.reset.requested',
|
||||
payload: user,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Public API
|
||||
*/
|
||||
|
||||
@Redactable()
|
||||
private publicApiKeyCreated({ user }: Event['public-api-key-created']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.api.created',
|
||||
payload: user,
|
||||
});
|
||||
}
|
||||
|
||||
@Redactable()
|
||||
private publicApiKeyDeleted({ user }: Event['public-api-key-deleted']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.api.deleted',
|
||||
payload: user,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Emailing
|
||||
*/
|
||||
|
||||
@Redactable()
|
||||
private emailFailed({ user, messageType }: Event['email-failed']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.email.failed',
|
||||
payload: { ...user, messageType },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Credentials
|
||||
*/
|
||||
|
||||
@Redactable()
|
||||
private credentialsCreated({ user, ...rest }: Event['credentials-created']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.credentials.created',
|
||||
payload: { ...user, ...rest },
|
||||
});
|
||||
}
|
||||
|
||||
@Redactable()
|
||||
private credentialsDeleted({ user, ...rest }: Event['credentials-deleted']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.credentials.deleted',
|
||||
payload: { ...user, ...rest },
|
||||
});
|
||||
}
|
||||
|
||||
@Redactable()
|
||||
private credentialsShared({ user, ...rest }: Event['credentials-shared']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.credentials.shared',
|
||||
payload: { ...user, ...rest },
|
||||
});
|
||||
}
|
||||
|
||||
@Redactable()
|
||||
private credentialsUpdated({ user, ...rest }: Event['credentials-updated']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.user.credentials.updated',
|
||||
payload: { ...user, ...rest },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Community package
|
||||
*/
|
||||
|
||||
@Redactable()
|
||||
private communityPackageInstalled({ user, ...rest }: Event['community-package-installed']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.package.installed',
|
||||
payload: { ...user, ...rest },
|
||||
});
|
||||
}
|
||||
|
||||
@Redactable()
|
||||
private communityPackageUpdated({ user, ...rest }: Event['community-package-updated']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.package.updated',
|
||||
payload: { ...user, ...rest },
|
||||
});
|
||||
}
|
||||
|
||||
@Redactable()
|
||||
private communityPackageDeleted({ user, ...rest }: Event['community-package-deleted']) {
|
||||
void this.eventBus.sendAuditEvent({
|
||||
eventName: 'n8n.audit.package.deleted',
|
||||
payload: { ...user, ...rest },
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution
|
||||
*/
|
||||
|
||||
private executionThrottled({ executionId }: Event['execution-throttled']) {
|
||||
void this.eventBus.sendExecutionEvent({
|
||||
eventName: 'n8n.execution.throttled',
|
||||
payload: { executionId },
|
||||
});
|
||||
}
|
||||
|
||||
private executionStartedDuringBootup({ executionId }: Event['execution-started-during-bootup']) {
|
||||
void this.eventBus.sendExecutionEvent({
|
||||
eventName: 'n8n.execution.started-during-bootup',
|
||||
payload: { executionId },
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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,314 +0,0 @@
|
||||
import type { AuthenticationMethod, IRun, IWorkflowBase } from 'n8n-workflow';
|
||||
import type { IWorkflowDb, IWorkflowExecutionDataProcess } from '@/Interfaces';
|
||||
import type { ProjectRole } from '@/databases/entities/ProjectRelation';
|
||||
import type { GlobalRole } from '@/databases/entities/User';
|
||||
|
||||
export type UserLike = {
|
||||
id: string;
|
||||
email?: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
role: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Events sent by `EventService` and forwarded by relays, e.g. `AuditEventRelay` and `TelemetryEventRelay`.
|
||||
*/
|
||||
export type Event = {
|
||||
'server-started': {};
|
||||
|
||||
'workflow-created': {
|
||||
user: UserLike;
|
||||
workflow: IWorkflowBase;
|
||||
publicApi: boolean;
|
||||
projectId: string;
|
||||
projectType: string;
|
||||
};
|
||||
|
||||
'workflow-deleted': {
|
||||
user: UserLike;
|
||||
workflowId: string;
|
||||
publicApi: boolean;
|
||||
};
|
||||
|
||||
'workflow-saved': {
|
||||
user: UserLike;
|
||||
workflow: IWorkflowDb;
|
||||
publicApi: boolean;
|
||||
};
|
||||
|
||||
'workflow-pre-execute': {
|
||||
executionId: string;
|
||||
data: IWorkflowExecutionDataProcess /* main process */ | IWorkflowBase /* worker */;
|
||||
};
|
||||
|
||||
'workflow-post-execute': {
|
||||
executionId: string;
|
||||
userId?: string;
|
||||
workflow: IWorkflowBase;
|
||||
runData?: IRun;
|
||||
};
|
||||
|
||||
'node-pre-execute': {
|
||||
executionId: string;
|
||||
workflow: IWorkflowBase;
|
||||
nodeName: string;
|
||||
};
|
||||
|
||||
'node-post-execute': {
|
||||
executionId: string;
|
||||
workflow: IWorkflowBase;
|
||||
nodeName: string;
|
||||
};
|
||||
|
||||
'user-deleted': {
|
||||
user: UserLike;
|
||||
};
|
||||
|
||||
'user-invited': {
|
||||
user: UserLike;
|
||||
targetUserId: string[];
|
||||
};
|
||||
|
||||
'user-reinvited': {
|
||||
user: UserLike;
|
||||
targetUserId: string[];
|
||||
};
|
||||
|
||||
'user-updated': {
|
||||
user: UserLike;
|
||||
fieldsChanged: string[];
|
||||
};
|
||||
|
||||
'user-signed-up': {
|
||||
user: UserLike;
|
||||
};
|
||||
|
||||
'user-logged-in': {
|
||||
user: UserLike;
|
||||
authenticationMethod: AuthenticationMethod;
|
||||
};
|
||||
|
||||
'user-login-failed': {
|
||||
userEmail: string;
|
||||
authenticationMethod: AuthenticationMethod;
|
||||
reason?: string;
|
||||
};
|
||||
|
||||
'user-invite-email-click': {
|
||||
inviter: UserLike;
|
||||
invitee: UserLike;
|
||||
};
|
||||
|
||||
'user-password-reset-email-click': {
|
||||
user: UserLike;
|
||||
};
|
||||
|
||||
'user-password-reset-request-click': {
|
||||
user: UserLike;
|
||||
};
|
||||
|
||||
'public-api-invoked': {
|
||||
userId: string;
|
||||
path: string;
|
||||
method: string;
|
||||
apiVersion: string;
|
||||
};
|
||||
|
||||
'email-failed': {
|
||||
user: UserLike;
|
||||
messageType:
|
||||
| 'Reset password'
|
||||
| 'New user invite'
|
||||
| 'Resend invite'
|
||||
| 'Workflow shared'
|
||||
| 'Credentials shared';
|
||||
};
|
||||
|
||||
'credentials-created': {
|
||||
user: UserLike;
|
||||
credentialType: string;
|
||||
credentialId: string;
|
||||
publicApi: boolean;
|
||||
projectId?: string;
|
||||
projectType?: string;
|
||||
};
|
||||
|
||||
'credentials-shared': {
|
||||
user: UserLike;
|
||||
credentialType: string;
|
||||
credentialId: string;
|
||||
userIdSharer: string;
|
||||
userIdsShareesAdded: string[];
|
||||
shareesRemoved: number | null;
|
||||
};
|
||||
|
||||
'credentials-updated': {
|
||||
user: UserLike;
|
||||
credentialType: string;
|
||||
credentialId: string;
|
||||
};
|
||||
|
||||
'credentials-deleted': {
|
||||
user: UserLike;
|
||||
credentialType: string;
|
||||
credentialId: string;
|
||||
};
|
||||
|
||||
'community-package-installed': {
|
||||
user: UserLike;
|
||||
inputString: string;
|
||||
packageName: string;
|
||||
success: boolean;
|
||||
packageVersion?: string;
|
||||
packageNodeNames?: string[];
|
||||
packageAuthor?: string;
|
||||
packageAuthorEmail?: string;
|
||||
failureReason?: string;
|
||||
};
|
||||
|
||||
'community-package-updated': {
|
||||
user: UserLike;
|
||||
packageName: string;
|
||||
packageVersionCurrent: string;
|
||||
packageVersionNew: string;
|
||||
packageNodeNames: string[];
|
||||
packageAuthor?: string;
|
||||
packageAuthorEmail?: string;
|
||||
};
|
||||
|
||||
'community-package-deleted': {
|
||||
user: UserLike;
|
||||
packageName: string;
|
||||
packageVersion: string;
|
||||
packageNodeNames: string[];
|
||||
packageAuthor?: string;
|
||||
packageAuthorEmail?: string;
|
||||
};
|
||||
|
||||
'execution-throttled': {
|
||||
executionId: string;
|
||||
};
|
||||
|
||||
'execution-started-during-bootup': {
|
||||
executionId: string;
|
||||
};
|
||||
|
||||
'team-project-updated': {
|
||||
userId: string;
|
||||
role: GlobalRole;
|
||||
members: Array<{
|
||||
userId: string;
|
||||
role: ProjectRole;
|
||||
}>;
|
||||
projectId: string;
|
||||
};
|
||||
|
||||
'team-project-deleted': {
|
||||
userId: string;
|
||||
role: GlobalRole;
|
||||
projectId: string;
|
||||
removalType: 'transfer' | 'delete';
|
||||
targetProjectId?: string;
|
||||
};
|
||||
|
||||
'team-project-created': {
|
||||
userId: string;
|
||||
role: GlobalRole;
|
||||
};
|
||||
|
||||
'source-control-settings-updated': {
|
||||
branchName: string;
|
||||
readOnlyInstance: boolean;
|
||||
repoType: 'github' | 'gitlab' | 'other';
|
||||
connected: boolean;
|
||||
};
|
||||
|
||||
'source-control-user-started-pull-ui': {
|
||||
workflowUpdates: number;
|
||||
workflowConflicts: number;
|
||||
credConflicts: number;
|
||||
};
|
||||
|
||||
'source-control-user-finished-pull-ui': {
|
||||
workflowUpdates: number;
|
||||
};
|
||||
|
||||
'source-control-user-pulled-api': {
|
||||
workflowUpdates: number;
|
||||
forced: boolean;
|
||||
};
|
||||
|
||||
'source-control-user-started-push-ui': {
|
||||
workflowsEligible: number;
|
||||
workflowsEligibleWithConflicts: number;
|
||||
credsEligible: number;
|
||||
credsEligibleWithConflicts: number;
|
||||
variablesEligible: number;
|
||||
};
|
||||
|
||||
'source-control-user-finished-push-ui': {
|
||||
workflowsEligible: number;
|
||||
workflowsPushed: number;
|
||||
credsPushed: number;
|
||||
variablesPushed: number;
|
||||
};
|
||||
|
||||
'license-renewal-attempted': {
|
||||
success: boolean;
|
||||
};
|
||||
|
||||
'variable-created': {};
|
||||
|
||||
'external-secrets-provider-settings-saved': {
|
||||
userId?: string;
|
||||
vaultType: string;
|
||||
isValid: boolean;
|
||||
isNew: boolean;
|
||||
errorMessage?: string;
|
||||
};
|
||||
|
||||
'ldap-general-sync-finished': {
|
||||
type: string;
|
||||
succeeded: boolean;
|
||||
usersSynced: number;
|
||||
error: string;
|
||||
};
|
||||
|
||||
'ldap-settings-updated': {
|
||||
userId: string;
|
||||
loginIdAttribute: string;
|
||||
firstNameAttribute: string;
|
||||
lastNameAttribute: string;
|
||||
emailAttribute: string;
|
||||
ldapIdAttribute: string;
|
||||
searchPageSize: number;
|
||||
searchTimeout: number;
|
||||
synchronizationEnabled: boolean;
|
||||
synchronizationInterval: number;
|
||||
loginLabel: string;
|
||||
loginEnabled: boolean;
|
||||
};
|
||||
|
||||
'ldap-login-sync-failed': {
|
||||
error: string;
|
||||
};
|
||||
|
||||
'login-failed-due-to-ldap-disabled': {
|
||||
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
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user