refactor(core): Decouple Public API events from internal hooks (no-changelog) (#10121)

This commit is contained in:
Iván Ovejero
2024-07-22 10:09:02 +02:00
committed by GitHub
parent 936cb57d79
commit a7ae23b47f
6 changed files with 68 additions and 42 deletions

View File

@@ -504,29 +504,6 @@ export class InternalHooks {
); );
} }
async onUserInvokedApi(userInvokedApiData: {
user_id: string;
path: string;
method: string;
api_version: string;
}): Promise<void> {
return await this.telemetry.track('User invoked API', userInvokedApiData);
}
async onApiKeyDeleted(apiKeyDeletedData: { user: User; public_api: boolean }): Promise<void> {
void this.telemetry.track('API key deleted', {
user_id: apiKeyDeletedData.user.id,
public_api: apiKeyDeletedData.public_api,
});
}
async onApiKeyCreated(apiKeyCreatedData: { user: User; public_api: boolean }): Promise<void> {
void this.telemetry.track('API key created', {
user_id: apiKeyCreatedData.user.id,
public_api: apiKeyCreatedData.public_api,
});
}
async onUserPasswordResetRequestClick(userPasswordResetData: { user: User }): Promise<void> { async onUserPasswordResetRequestClick(userPasswordResetData: { user: User }): Promise<void> {
void this.telemetry.track('User requested password reset while logged out', { void this.telemetry.track('User requested password reset while logged out', {
user_id: userPasswordResetData.user.id, user_id: userPasswordResetData.user.id,

View File

@@ -12,12 +12,12 @@ import type { JsonObject } from 'swagger-ui-express';
import config from '@/config'; import config from '@/config';
import { InternalHooks } from '@/InternalHooks';
import { License } from '@/License'; import { License } from '@/License';
import { UserRepository } from '@db/repositories/user.repository'; import { UserRepository } from '@db/repositories/user.repository';
import { UrlService } from '@/services/url.service'; import { UrlService } from '@/services/url.service';
import type { AuthenticatedRequest } from '@/requests'; import type { AuthenticatedRequest } from '@/requests';
import { GlobalConfig } from '@n8n/config'; import { GlobalConfig } from '@n8n/config';
import { EventService } from '@/eventbus/event.service';
async function createApiRouter( async function createApiRouter(
version: string, version: string,
@@ -100,11 +100,11 @@ async function createApiRouter(
if (!user) return false; if (!user) return false;
void Container.get(InternalHooks).onUserInvokedApi({ Container.get(EventService).emit('public-api-invoked', {
user_id: user.id, userId: user.id,
path: req.path, path: req.path,
method: req.method, method: req.method,
api_version: version, apiVersion: version,
}); });
req.user = user; req.user = user;

View File

@@ -198,8 +198,7 @@ export class MeController {
await this.userService.update(req.user.id, { apiKey }); await this.userService.update(req.user.id, { apiKey });
void this.internalHooks.onApiKeyCreated({ user: req.user, public_api: false }); this.eventService.emit('public-api-key-created', { user: req.user, publicApi: false });
this.eventService.emit('api-key-created', { user: req.user });
return { apiKey }; return { apiKey };
} }
@@ -219,8 +218,7 @@ export class MeController {
async deleteAPIKey(req: AuthenticatedRequest) { async deleteAPIKey(req: AuthenticatedRequest) {
await this.userService.update(req.user.id, { apiKey: null }); await this.userService.update(req.user.id, { apiKey: null });
void this.internalHooks.onApiKeyDeleted({ user: req.user, public_api: false }); this.eventService.emit('public-api-key-deleted', { user: req.user, publicApi: false });
this.eventService.emit('api-key-deleted', { user: req.user });
return { success: true }; return { success: true };
} }

View File

@@ -38,8 +38,8 @@ export class AuditEventRelay {
this.eventService.on('user-password-reset-request-click', (event) => this.eventService.on('user-password-reset-request-click', (event) =>
this.userPasswordResetRequestClick(event), this.userPasswordResetRequestClick(event),
); );
this.eventService.on('api-key-created', (event) => this.apiKeyCreated(event)); this.eventService.on('public-api-key-created', (event) => this.apiKeyCreated(event));
this.eventService.on('api-key-deleted', (event) => this.apiKeyDeleted(event)); this.eventService.on('public-api-key-deleted', (event) => this.apiKeyDeleted(event));
this.eventService.on('email-failed', (event) => this.emailFailed(event)); this.eventService.on('email-failed', (event) => this.emailFailed(event));
this.eventService.on('credentials-created', (event) => this.credentialsCreated(event)); this.eventService.on('credentials-created', (event) => this.credentialsCreated(event));
this.eventService.on('credentials-deleted', (event) => this.credentialsDeleted(event)); this.eventService.on('credentials-deleted', (event) => this.credentialsDeleted(event));
@@ -257,18 +257,22 @@ export class AuditEventRelay {
*/ */
@Redactable() @Redactable()
private apiKeyCreated({ user }: Event['api-key-created']) { private apiKeyCreated(event: Event['public-api-key-created']) {
if ('publicApi' in event) return;
void this.eventBus.sendAuditEvent({ void this.eventBus.sendAuditEvent({
eventName: 'n8n.audit.user.api.created', eventName: 'n8n.audit.user.api.created',
payload: user, payload: event.user,
}); });
} }
@Redactable() @Redactable()
private apiKeyDeleted({ user }: Event['api-key-deleted']) { private apiKeyDeleted(event: Event['public-api-key-deleted']) {
if ('publicApi' in event) return;
void this.eventBus.sendAuditEvent({ void this.eventBus.sendAuditEvent({
eventName: 'n8n.audit.user.api.deleted', eventName: 'n8n.audit.user.api.deleted',
payload: user, payload: event.user,
}); });
} }

View File

@@ -105,13 +105,20 @@ export type Event = {
user: UserLike; user: UserLike;
}; };
'api-key-created': { 'public-api-invoked': {
user: UserLike; userId: string;
path: string;
method: string;
apiVersion: string;
}; };
'api-key-deleted': { 'public-api-key-created':
user: UserLike; | { user: UserLike } // audit
}; | { user: UserLike; publicApi: boolean }; // telemetry
'public-api-key-deleted':
| { user: UserLike } // audit
| { user: UserLike; publicApi: boolean }; // telemetry
'email-failed': { 'email-failed': {
user: UserLike; user: UserLike;

View File

@@ -50,6 +50,15 @@ export class TelemetryEventRelay {
this.eventService.on('external-secrets-provider-settings-saved', (event) => { this.eventService.on('external-secrets-provider-settings-saved', (event) => {
this.externalSecretsProviderSettingsSaved(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);
});
} }
private teamProjectUpdated({ userId, role, members, projectId }: Event['team-project-updated']) { private teamProjectUpdated({ userId, role, members, projectId }: Event['team-project-updated']) {
@@ -190,4 +199,35 @@ export class TelemetryEventRelay {
error_message: errorMessage, error_message: errorMessage,
}); });
} }
private publicApiInvoked({ userId, path, method, apiVersion }: Event['public-api-invoked']) {
void this.telemetry.track('User invoked API', {
user_id: userId,
path,
method,
api_version: apiVersion,
});
}
private publicApiKeyCreated(event: Event['public-api-key-created']) {
if (!('publicApi' in event)) return;
const { user, publicApi } = event;
void this.telemetry.track('API key created', {
user_id: user.id,
public_api: publicApi,
});
}
private publicApiKeyDeleted(event: Event['public-api-key-deleted']) {
if (!('publicApi' in event)) return;
const { user, publicApi } = event;
void this.telemetry.track('API key deleted', {
user_id: user.id,
public_api: publicApi,
});
}
} }