mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 05:29:44 +02:00
refactor(API): Migrate the log streaming destination writes to the decorator pattern (no-changelog) (#40397)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
e7d8533880
commit
741ba22943
|
|
@ -339,10 +339,12 @@ export {
|
|||
LogStreamingDestinationListPublicDto,
|
||||
LogStreamingDestinationPublicDto,
|
||||
LogStreamingEventTypesPublicDto,
|
||||
PublicCreateDestinationDto,
|
||||
CreateLogStreamingDestinationPublicDto,
|
||||
UpdateLogStreamingDestinationPublicDto,
|
||||
type LogStreamingDestinationPublic,
|
||||
type PublicCreateDestination,
|
||||
type PublicDestinationType,
|
||||
type CreateLogStreamingDestinationPublic,
|
||||
type UpdateLogStreamingDestinationPublic,
|
||||
type LogStreamingDestinationPublicType,
|
||||
} from './log-streaming/log-streaming-public.dto';
|
||||
|
||||
export { PaginationDto, MAX_ITEMS_PER_PAGE } from './pagination/pagination.dto';
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
logStreamingDestinationCommonFieldDocs,
|
||||
logStreamingDestinationDocs,
|
||||
logStreamingDestinationListFieldDocs,
|
||||
logStreamingDestinationRequestReadOnlyFieldDocs,
|
||||
logStreamingEventTypesFieldDocs,
|
||||
logStreamingParameterFieldDocs,
|
||||
logStreamingParameterListDocs,
|
||||
|
|
@ -23,6 +24,7 @@ import {
|
|||
logStreamingWebhookFieldDocs,
|
||||
logStreamingWebhookOptionsFieldDocs,
|
||||
} from './log-streaming-public.openapi';
|
||||
import { readOnlyPublicSchema } from '../../schemas/read-only-public.schema';
|
||||
import { Z } from '../../zod-class';
|
||||
|
||||
const logStreamingEventTypesPublicSchema = z
|
||||
|
|
@ -166,29 +168,34 @@ const publicSentrySchema = z.object({
|
|||
dsn: sentryShape.dsn.openapi(sentryDocs.dsn),
|
||||
});
|
||||
|
||||
export const PublicCreateDestinationDto = z.discriminatedUnion('type', [
|
||||
publicWebhookSchema,
|
||||
publicSyslogSchema,
|
||||
publicSentrySchema,
|
||||
]);
|
||||
|
||||
export type PublicCreateDestination = z.infer<typeof PublicCreateDestinationDto>;
|
||||
|
||||
export type PublicDestinationType = PublicCreateDestination['type'];
|
||||
|
||||
const idSchema = z.string().openapi(commonDocs.id);
|
||||
|
||||
export const logStreamingDestinationPublicSchema = z
|
||||
.discriminatedUnion('type', [
|
||||
publicWebhookSchema.extend({ id: idSchema }),
|
||||
publicSyslogSchema.extend({ id: idSchema }),
|
||||
publicSentrySchema.extend({ id: idSchema }),
|
||||
])
|
||||
const readOnlyIdSchema = readOnlyPublicSchema(logStreamingDestinationRequestReadOnlyFieldDocs.id);
|
||||
|
||||
const destinationUnion = <Id extends z.ZodTypeAny>(id: Id) =>
|
||||
z
|
||||
.discriminatedUnion('type', [
|
||||
publicWebhookSchema.extend({ id }),
|
||||
publicSyslogSchema.extend({ id }),
|
||||
publicSentrySchema.extend({ id }),
|
||||
])
|
||||
.openapi(logStreamingDestinationDocs);
|
||||
|
||||
export const logStreamingDestinationPublicSchema = destinationUnion(idSchema);
|
||||
const createLogStreamingDestinationPublicSchema = destinationUnion(readOnlyIdSchema);
|
||||
const updateLogStreamingDestinationPublicSchema = z
|
||||
.discriminatedUnion('type', [publicWebhookSchema, publicSyslogSchema, publicSentrySchema])
|
||||
.openapi(logStreamingDestinationDocs);
|
||||
|
||||
export type LogStreamingDestinationPublic = z.infer<typeof logStreamingDestinationPublicSchema>;
|
||||
export type CreateLogStreamingDestinationPublic = z.infer<
|
||||
typeof createLogStreamingDestinationPublicSchema
|
||||
>;
|
||||
export type UpdateLogStreamingDestinationPublic = z.infer<
|
||||
typeof updateLogStreamingDestinationPublicSchema
|
||||
>;
|
||||
export type LogStreamingDestinationPublicType = LogStreamingDestinationPublic['type'];
|
||||
|
||||
/** A discriminated union has no object shape, so this cannot extend `Z.class`. */
|
||||
export class LogStreamingDestinationPublicDto {
|
||||
static schema = logStreamingDestinationPublicSchema;
|
||||
|
||||
|
|
@ -201,6 +208,32 @@ export class LogStreamingDestinationPublicDto {
|
|||
}
|
||||
}
|
||||
|
||||
export type CreateLogStreamingDestinationPublicDto = CreateLogStreamingDestinationPublic;
|
||||
export const CreateLogStreamingDestinationPublicDto = class {
|
||||
static schema = createLogStreamingDestinationPublicSchema;
|
||||
|
||||
static safeParse(data: unknown) {
|
||||
return createLogStreamingDestinationPublicSchema.safeParse(data);
|
||||
}
|
||||
|
||||
static parse(data: unknown): CreateLogStreamingDestinationPublic {
|
||||
return createLogStreamingDestinationPublicSchema.parse(data);
|
||||
}
|
||||
};
|
||||
|
||||
export type UpdateLogStreamingDestinationPublicDto = UpdateLogStreamingDestinationPublic;
|
||||
export const UpdateLogStreamingDestinationPublicDto = class {
|
||||
static schema = updateLogStreamingDestinationPublicSchema;
|
||||
|
||||
static safeParse(data: unknown) {
|
||||
return updateLogStreamingDestinationPublicSchema.safeParse(data);
|
||||
}
|
||||
|
||||
static parse(data: unknown): UpdateLogStreamingDestinationPublic {
|
||||
return updateLogStreamingDestinationPublicSchema.parse(data);
|
||||
}
|
||||
};
|
||||
|
||||
const logStreamingDestinationListPublicSchema = z
|
||||
.object({
|
||||
data: z
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@ export const logStreamingDestinationCommonFieldDocs = {
|
|||
},
|
||||
} as const satisfies Record<string, ZodOpenAPIMetadata>;
|
||||
|
||||
export const logStreamingDestinationRequestReadOnlyFieldDocs = {
|
||||
id: { type: 'string', readOnly: true, ...logStreamingDestinationCommonFieldDocs.id },
|
||||
} as const satisfies Record<string, ZodOpenAPIMetadata>;
|
||||
|
||||
export const logStreamingCircuitBreakerFieldDocs = {
|
||||
maxFailures: {
|
||||
description: 'Maximum failures within the sliding window before the breaker opens.',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import type {
|
||||
PublicCreateDestination,
|
||||
UpdateOtelSettingsDto,
|
||||
TestOtelTraceDto,
|
||||
UpdateSamlConfigurationDto,
|
||||
|
|
@ -115,8 +114,6 @@ export declare namespace CommunityPackageRequest {
|
|||
}
|
||||
|
||||
export declare namespace LogStreamingRequest {
|
||||
type CreateDestination = AuthenticatedRequest<{}, {}, PublicCreateDestination>;
|
||||
type UpdateDestination = AuthenticatedRequest<{ id: string }, {}, PublicCreateDestination>;
|
||||
type TestDestination = AuthenticatedRequest<{ id: string }>;
|
||||
type DeleteDestination = AuthenticatedRequest<{ id: string }>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,36 @@
|
|||
import { LogStreamingDestinationPublicDto, type PublicDestinationType } from '@n8n/api-types';
|
||||
import {
|
||||
LogStreamingDestinationPublicDto,
|
||||
type UpdateLogStreamingDestinationPublic,
|
||||
type LogStreamingDestinationPublicType,
|
||||
} from '@n8n/api-types';
|
||||
import type { MessageEventBusDestinationOptions } from 'n8n-workflow';
|
||||
import { MessageEventBusDestinationTypeNames } from 'n8n-workflow';
|
||||
|
||||
const INTERNAL_TO_PUBLIC: Partial<
|
||||
Record<MessageEventBusDestinationTypeNames, PublicDestinationType>
|
||||
Record<MessageEventBusDestinationTypeNames, LogStreamingDestinationPublicType>
|
||||
> = {
|
||||
[MessageEventBusDestinationTypeNames.webhook]: 'webhook',
|
||||
[MessageEventBusDestinationTypeNames.syslog]: 'syslog',
|
||||
[MessageEventBusDestinationTypeNames.sentry]: 'sentry',
|
||||
};
|
||||
|
||||
const PUBLIC_TO_INTERNAL: Record<
|
||||
LogStreamingDestinationPublicType,
|
||||
MessageEventBusDestinationTypeNames
|
||||
> = {
|
||||
webhook: MessageEventBusDestinationTypeNames.webhook,
|
||||
syslog: MessageEventBusDestinationTypeNames.syslog,
|
||||
sentry: MessageEventBusDestinationTypeNames.sentry,
|
||||
};
|
||||
|
||||
export function toLogStreamingDestinationPublic(options: MessageEventBusDestinationOptions) {
|
||||
const type = options.__type ? INTERNAL_TO_PUBLIC[options.__type] : undefined;
|
||||
return LogStreamingDestinationPublicDto.parse({ ...options, type });
|
||||
}
|
||||
|
||||
export function toInternalDestinationOptions(
|
||||
input: UpdateLogStreamingDestinationPublic,
|
||||
): MessageEventBusDestinationOptions {
|
||||
const { type, ...rest } = input;
|
||||
return { ...rest, __type: PUBLIC_TO_INTERNAL[type] };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,16 @@
|
|||
import {
|
||||
CreateLogStreamingDestinationPublicDto,
|
||||
UpdateLogStreamingDestinationPublicDto,
|
||||
LogStreamingDestinationListPublicDto,
|
||||
LogStreamingDestinationPublicDto,
|
||||
LogStreamingEventTypesPublicDto,
|
||||
logStreamingDestinationIdParamSchema,
|
||||
} from '@n8n/api-types';
|
||||
import { OutboundHttp } from '@n8n/backend-network';
|
||||
import { CredentialsFinderService } from '@n8n/backend-services';
|
||||
import { InstanceSettingsLoaderConfig } from '@n8n/config';
|
||||
import { LICENSE_FEATURES } from '@n8n/constants';
|
||||
import type { AuthenticatedRequest } from '@n8n/db';
|
||||
import type { AuthenticatedRequest, User } from '@n8n/db';
|
||||
import {
|
||||
ApiDescription,
|
||||
ApiErrorResponse,
|
||||
|
|
@ -13,25 +18,40 @@ import {
|
|||
ApiResponse,
|
||||
ApiSummary,
|
||||
ApiTags,
|
||||
Body,
|
||||
Get,
|
||||
Licensed,
|
||||
Param,
|
||||
Post,
|
||||
PublicApiController,
|
||||
Put,
|
||||
} from '@n8n/decorators';
|
||||
import type { Response } from 'express';
|
||||
import type { MessageEventBusDestinationOptions } from 'n8n-workflow';
|
||||
|
||||
import { NotFoundError } from '@n8n/errors';
|
||||
import { ConflictError, NotFoundError } from '@n8n/errors';
|
||||
import { eventNamesAll } from '@/eventbus/event-message-classes';
|
||||
import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus';
|
||||
import { createMessageEventBusDestination } from '@/modules/log-streaming.ee/create-message-event-bus-destination';
|
||||
import { assertUserCanUseDestinationCredentials } from '@/modules/log-streaming.ee/destinations/destination-credentials-access';
|
||||
import { LogStreamingDestinationService } from '@/modules/log-streaming.ee/log-streaming-destination.service';
|
||||
|
||||
import { toLogStreamingDestinationPublic } from './log-streaming.mapper';
|
||||
import {
|
||||
toInternalDestinationOptions,
|
||||
toLogStreamingDestinationPublic,
|
||||
} from './log-streaming.mapper';
|
||||
|
||||
const tags = ['LogStreaming'];
|
||||
|
||||
@PublicApiController('/settings/log-streaming')
|
||||
export class LogStreamingPublicController {
|
||||
constructor(private readonly destinationService: LogStreamingDestinationService) {}
|
||||
constructor(
|
||||
private readonly destinationService: LogStreamingDestinationService,
|
||||
private readonly eventBus: MessageEventBus,
|
||||
private readonly instanceSettingsLoaderConfig: InstanceSettingsLoaderConfig,
|
||||
private readonly outboundHttp: OutboundHttp,
|
||||
private readonly credentialsFinderService: CredentialsFinderService,
|
||||
) {}
|
||||
|
||||
@Get('/event-types')
|
||||
@ApiKeyScope('eventBusDestination:list')
|
||||
|
|
@ -81,6 +101,70 @@ export class LogStreamingPublicController {
|
|||
return toLogStreamingDestinationPublic(destination);
|
||||
}
|
||||
|
||||
@Post('/destinations')
|
||||
@ApiKeyScope('eventBusDestination:create')
|
||||
@Licensed(LICENSE_FEATURES.LOG_STREAMING)
|
||||
@ApiSummary('Create a log streaming destination')
|
||||
@ApiDescription(
|
||||
'Create a log streaming destination. The destination takes effect exactly as it would from the UI, using the same validation. Requires the `eventBusDestination:create` scope and the Log Streaming feature to be licensed. When destinations are managed via environment variables, the write is rejected with 409 and nothing is created; reads still return the current values.',
|
||||
)
|
||||
@ApiTags(tags)
|
||||
@ApiResponse(200, LogStreamingDestinationPublicDto)
|
||||
@ApiErrorResponse(409)
|
||||
async createLogStreamingDestination(
|
||||
req: AuthenticatedRequest,
|
||||
_res: Response,
|
||||
@Body body: CreateLogStreamingDestinationPublicDto,
|
||||
): Promise<LogStreamingDestinationPublicDto> {
|
||||
this.assertNotManagedByEnv();
|
||||
|
||||
return await this.saveDestination(req.user, toInternalDestinationOptions(body));
|
||||
}
|
||||
|
||||
@Put('/destinations/:id')
|
||||
@ApiKeyScope('eventBusDestination:update')
|
||||
@Licensed(LICENSE_FEATURES.LOG_STREAMING)
|
||||
@ApiSummary('Update a log streaming destination')
|
||||
@ApiDescription(
|
||||
'Replace an existing log streaming destination. The update takes effect exactly as it would from the UI, using the same validation. Requires the `eventBusDestination:update` scope and the Log Streaming feature to be licensed. When destinations are managed via environment variables, the write is rejected with 409 and nothing is changed; reads still return the current values.',
|
||||
)
|
||||
@ApiTags(tags)
|
||||
@ApiResponse(200, LogStreamingDestinationPublicDto)
|
||||
@ApiErrorResponse(404)
|
||||
@ApiErrorResponse(409)
|
||||
async updateLogStreamingDestination(
|
||||
req: AuthenticatedRequest,
|
||||
_res: Response,
|
||||
@Param('id', logStreamingDestinationIdParamSchema) id: string,
|
||||
@Body body: UpdateLogStreamingDestinationPublicDto,
|
||||
): Promise<LogStreamingDestinationPublicDto> {
|
||||
this.assertNotManagedByEnv();
|
||||
await this.findDestinationOrFail(id);
|
||||
|
||||
// `addDestination` replaces the stored destination that has this id.
|
||||
return await this.saveDestination(req.user, { ...toInternalDestinationOptions(body), id });
|
||||
}
|
||||
|
||||
private assertNotManagedByEnv() {
|
||||
if (this.instanceSettingsLoaderConfig.logStreamingManagedByEnv) {
|
||||
throw new ConflictError(
|
||||
'Log streaming destinations are managed via environment variables and cannot be modified through the API',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private async saveDestination(
|
||||
user: User,
|
||||
options: MessageEventBusDestinationOptions,
|
||||
): Promise<LogStreamingDestinationPublicDto> {
|
||||
await assertUserCanUseDestinationCredentials(this.credentialsFinderService, user, options);
|
||||
|
||||
const destination = createMessageEventBusDestination(this.eventBus, this.outboundHttp, options);
|
||||
const result = await this.destinationService.addDestination(destination);
|
||||
|
||||
return toLogStreamingDestinationPublic(result.serialize());
|
||||
}
|
||||
|
||||
private async findDestinationOrFail(id: string): Promise<MessageEventBusDestinationOptions> {
|
||||
const [destination] = await this.destinationService.findDestination(id);
|
||||
if (!destination) {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
import { PublicCreateDestinationDto } from '@n8n/api-types';
|
||||
import { OutboundHttp } from '@n8n/backend-network';
|
||||
import { InstanceSettingsLoaderConfig } from '@n8n/config';
|
||||
import { Container } from '@n8n/di';
|
||||
import type { MessageEventBusDestinationOptions } from 'n8n-workflow';
|
||||
|
||||
import { BadRequestError, ConflictError, NotFoundError } from '@n8n/errors';
|
||||
import { MessageEventBus } from '@/eventbus/message-event-bus/message-event-bus';
|
||||
import { createMessageEventBusDestination } from '@/modules/log-streaming.ee/create-message-event-bus-destination';
|
||||
import { ConflictError, NotFoundError } from '@n8n/errors';
|
||||
import { assertUserCanUseDestinationCredentials } from '@/modules/log-streaming.ee/destinations/destination-credentials-access';
|
||||
import { LogStreamingDestinationService } from '@/modules/log-streaming.ee/log-streaming-destination.service';
|
||||
|
||||
import { toInternalDestinationOptions } from './log-streaming.mapper';
|
||||
import { toLogStreamingDestinationPublic } from '../../controllers/log-streaming.mapper';
|
||||
import type { LogStreamingRequest } from '../../../types';
|
||||
import type { PublicAPIEndpoint } from '../../shared/handler.types';
|
||||
|
|
@ -41,79 +36,11 @@ const getCredentialsFinderService = async () => {
|
|||
};
|
||||
|
||||
type LogStreamingHandlers = {
|
||||
createDestination: PublicAPIEndpoint<LogStreamingRequest.CreateDestination>;
|
||||
updateDestination: PublicAPIEndpoint<LogStreamingRequest.UpdateDestination>;
|
||||
testDestination: PublicAPIEndpoint<LogStreamingRequest.TestDestination>;
|
||||
deleteDestination: PublicAPIEndpoint<LogStreamingRequest.DeleteDestination>;
|
||||
};
|
||||
|
||||
const logStreamingHandlers: LogStreamingHandlers = {
|
||||
createDestination: [
|
||||
isLicensed('feat:logStreaming'),
|
||||
apiKeyHasScopeWithGlobalScopeFallback({ scope: 'eventBusDestination:create' }),
|
||||
async (req, res) => {
|
||||
assertNotManagedByEnv();
|
||||
|
||||
const parseResult = PublicCreateDestinationDto.safeParse(req.body);
|
||||
if (!parseResult.success) {
|
||||
throw new BadRequestError(parseResult.error.errors[0].message);
|
||||
}
|
||||
|
||||
const options = toInternalDestinationOptions(parseResult.data);
|
||||
|
||||
await assertUserCanUseDestinationCredentials(
|
||||
await getCredentialsFinderService(),
|
||||
req.user,
|
||||
options,
|
||||
);
|
||||
|
||||
const destination = createMessageEventBusDestination(
|
||||
Container.get(MessageEventBus),
|
||||
Container.get(OutboundHttp),
|
||||
options,
|
||||
);
|
||||
const result = await Container.get(LogStreamingDestinationService).addDestination(
|
||||
destination,
|
||||
);
|
||||
|
||||
return res.json(toLogStreamingDestinationPublic(result.serialize()));
|
||||
},
|
||||
],
|
||||
|
||||
updateDestination: [
|
||||
isLicensed('feat:logStreaming'),
|
||||
apiKeyHasScopeWithGlobalScopeFallback({ scope: 'eventBusDestination:update' }),
|
||||
async (req, res) => {
|
||||
assertNotManagedByEnv();
|
||||
await findDestinationOrFail(req.params.id);
|
||||
|
||||
const parseResult = PublicCreateDestinationDto.safeParse(req.body);
|
||||
if (!parseResult.success) {
|
||||
throw new BadRequestError(parseResult.error.errors[0].message);
|
||||
}
|
||||
|
||||
// the path id is the update target; addDestination upserts on it
|
||||
const options = { ...toInternalDestinationOptions(parseResult.data), id: req.params.id };
|
||||
|
||||
await assertUserCanUseDestinationCredentials(
|
||||
await getCredentialsFinderService(),
|
||||
req.user,
|
||||
options,
|
||||
);
|
||||
|
||||
const destination = createMessageEventBusDestination(
|
||||
Container.get(MessageEventBus),
|
||||
Container.get(OutboundHttp),
|
||||
options,
|
||||
);
|
||||
const result = await Container.get(LogStreamingDestinationService).addDestination(
|
||||
destination,
|
||||
);
|
||||
|
||||
return res.json(toLogStreamingDestinationPublic(result.serialize()));
|
||||
},
|
||||
],
|
||||
|
||||
testDestination: [
|
||||
isLicensed('feat:logStreaming'),
|
||||
apiKeyHasScopeWithGlobalScopeFallback({ scope: 'eventBusDestination:test' }),
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
import { type PublicCreateDestination, type PublicDestinationType } from '@n8n/api-types';
|
||||
import type { MessageEventBusDestinationOptions } from 'n8n-workflow';
|
||||
import { MessageEventBusDestinationTypeNames } from 'n8n-workflow';
|
||||
|
||||
// The public API uses a friendly `type` discriminator; the internal service uses `__type`.
|
||||
const PUBLIC_TO_INTERNAL: Record<PublicDestinationType, MessageEventBusDestinationTypeNames> = {
|
||||
webhook: MessageEventBusDestinationTypeNames.webhook,
|
||||
syslog: MessageEventBusDestinationTypeNames.syslog,
|
||||
sentry: MessageEventBusDestinationTypeNames.sentry,
|
||||
};
|
||||
|
||||
export function toInternalDestinationOptions(
|
||||
input: PublicCreateDestination,
|
||||
): MessageEventBusDestinationOptions {
|
||||
const { type, ...rest } = input;
|
||||
return { ...rest, __type: PUBLIC_TO_INTERNAL[type] };
|
||||
}
|
||||
|
|
@ -1,47 +1,3 @@
|
|||
put:
|
||||
x-eov-operation-id: updateDestination
|
||||
x-required-scope: eventBusDestination:update
|
||||
x-eov-operation-handler: v1/handlers/log-streaming/log-streaming.handler
|
||||
tags:
|
||||
- LogStreaming
|
||||
summary: Update a log streaming destination
|
||||
description: >
|
||||
Replace an existing log streaming destination. The update takes effect exactly as it would
|
||||
from the UI, using the same validation. Requires the `eventBusDestination:update` scope and
|
||||
the Log Streaming feature to be licensed. When destinations are managed via environment
|
||||
variables, the write is rejected with 409 and nothing is changed; reads still return the
|
||||
current values.
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
description: The id of the log streaming destination.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
description: The destination fields to apply.
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../schemas/destination.yml'
|
||||
responses:
|
||||
'200':
|
||||
description: Operation successful.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../schemas/destination.yml'
|
||||
'400':
|
||||
$ref: '../../../../shared/spec/responses/badRequest.yml'
|
||||
'401':
|
||||
$ref: '../../../../shared/spec/responses/unauthorized.yml'
|
||||
'403':
|
||||
$ref: '../../../../shared/spec/responses/forbidden.yml'
|
||||
'404':
|
||||
$ref: '../../../../shared/spec/responses/notFound.yml'
|
||||
'409':
|
||||
$ref: '../../../../shared/spec/responses/conflict.yml'
|
||||
delete:
|
||||
x-eov-operation-id: deleteDestination
|
||||
x-required-scope: eventBusDestination:delete
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
post:
|
||||
x-eov-operation-id: createDestination
|
||||
x-required-scope: eventBusDestination:create
|
||||
x-eov-operation-handler: v1/handlers/log-streaming/log-streaming.handler
|
||||
tags:
|
||||
- LogStreaming
|
||||
summary: Create a log streaming destination
|
||||
description: >
|
||||
Create a log streaming destination. The destination takes effect exactly as it would from
|
||||
the UI, using the same validation. Requires the `eventBusDestination:create` scope and the
|
||||
Log Streaming feature to be licensed. When destinations are managed via environment
|
||||
variables, the write is rejected with 409 and nothing is created; reads still return the
|
||||
current values.
|
||||
requestBody:
|
||||
description: The destination to create.
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../schemas/destination.yml'
|
||||
responses:
|
||||
'200':
|
||||
description: Operation successful.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '../schemas/destination.yml'
|
||||
'400':
|
||||
$ref: '../../../../shared/spec/responses/badRequest.yml'
|
||||
'401':
|
||||
$ref: '../../../../shared/spec/responses/unauthorized.yml'
|
||||
'403':
|
||||
$ref: '../../../../shared/spec/responses/forbidden.yml'
|
||||
'409':
|
||||
$ref: '../../../../shared/spec/responses/conflict.yml'
|
||||
|
|
@ -0,0 +1,385 @@
|
|||
operationId: createLogStreamingDestination
|
||||
tags:
|
||||
- LogStreaming
|
||||
summary: Create a log streaming destination
|
||||
description: Create a log streaming destination. The destination takes effect exactly as it would from the UI, using the same validation. Requires the `eventBusDestination:create` scope and the Log Streaming feature to be licensed. When destinations are managed via environment variables, the write is rejected with 409 and nothing is created; reads still return the current values.
|
||||
x-required-scope: eventBusDestination:create
|
||||
x-eov-operation-id: unreachable
|
||||
x-eov-operation-handler: v1/handlers/decorator-routed.handler
|
||||
x-decorator-routed: true
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- type: object
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Human-readable name shown in the UI.
|
||||
example: My destination
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the destination currently receives events.
|
||||
example: true
|
||||
subscribedEvents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Event names (or group prefixes, e.g. `n8n.workflow`, which matches all `n8n.workflow.*` events) this destination receives. Retrieve the full list of streamable event names from `GET /settings/log-streaming/event-types`.
|
||||
example:
|
||||
- n8n.workflow
|
||||
- n8n.audit
|
||||
anonymizeAuditMessages:
|
||||
type: boolean
|
||||
description: Whether audit message payloads are anonymized before being sent.
|
||||
example: false
|
||||
circuitBreaker:
|
||||
type: object
|
||||
properties:
|
||||
maxFailures:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum failures within the sliding window before the breaker opens.
|
||||
failureWindow:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Sliding window, in milliseconds, over which failures are counted.
|
||||
description: Circuit-breaker tuning for delivery. After repeated failures the destination stops sending for a cool-down window instead of hammering an unhealthy target. All fields are optional; sensible defaults apply when omitted.
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- webhook
|
||||
description: Sends each event as an HTTP request to a URL.
|
||||
example: webhook
|
||||
url:
|
||||
type: string
|
||||
format: uri
|
||||
description: Target URL that receives the event payload.
|
||||
example: https://example.com/n8n-events
|
||||
method:
|
||||
type: string
|
||||
description: HTTP method used for the request.
|
||||
example: POST
|
||||
sendHeaders:
|
||||
type: boolean
|
||||
description: Whether to attach custom headers to the request.
|
||||
example: false
|
||||
specifyHeaders:
|
||||
type: string
|
||||
description: How custom headers are provided — `keypair` or `json`.
|
||||
example: keypair
|
||||
headerParameters:
|
||||
type: object
|
||||
properties:
|
||||
parameters:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
example: Authorization
|
||||
value:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
- {}
|
||||
- {}
|
||||
description: Parameter value. Usually a string; numbers, booleans and null are also accepted.
|
||||
example: Bearer <token>
|
||||
required:
|
||||
- name
|
||||
- value
|
||||
required:
|
||||
- parameters
|
||||
description: Key-value pairs used when the matching `specify*` field is `keypair`.
|
||||
jsonHeaders:
|
||||
type: string
|
||||
description: Custom headers as a JSON string when `specifyHeaders` is `json`.
|
||||
sendQuery:
|
||||
type: boolean
|
||||
description: Whether to attach query parameters to the request.
|
||||
example: false
|
||||
specifyQuery:
|
||||
type: string
|
||||
description: How query parameters are provided — `keypair` or `json`.
|
||||
queryParameters:
|
||||
type: object
|
||||
properties:
|
||||
parameters:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
example: Authorization
|
||||
value:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
- {}
|
||||
- {}
|
||||
description: Parameter value. Usually a string; numbers, booleans and null are also accepted.
|
||||
example: Bearer <token>
|
||||
required:
|
||||
- name
|
||||
- value
|
||||
required:
|
||||
- parameters
|
||||
description: Key-value pairs used when the matching `specify*` field is `keypair`.
|
||||
jsonQuery:
|
||||
type: string
|
||||
description: Query parameters as a JSON string when `specifyQuery` is `json`.
|
||||
options:
|
||||
type: object
|
||||
properties:
|
||||
timeout:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Request timeout in milliseconds.
|
||||
allowUnauthorizedCerts:
|
||||
type: boolean
|
||||
description: Accept self-signed or otherwise invalid TLS certificates.
|
||||
queryParameterArrays:
|
||||
type: string
|
||||
enum:
|
||||
- indices
|
||||
- brackets
|
||||
- repeat
|
||||
description: How array-valued query parameters are serialized into the query string.
|
||||
redirect:
|
||||
type: object
|
||||
properties:
|
||||
redirect:
|
||||
type: object
|
||||
properties:
|
||||
followRedirects:
|
||||
type: boolean
|
||||
description: Whether to follow HTTP redirects.
|
||||
maxRedirects:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum number of redirects to follow.
|
||||
required:
|
||||
- redirect
|
||||
description: Redirect-following behaviour.
|
||||
proxy:
|
||||
type: object
|
||||
properties:
|
||||
proxy:
|
||||
type: object
|
||||
properties:
|
||||
protocol:
|
||||
type: string
|
||||
enum:
|
||||
- https
|
||||
- http
|
||||
description: Proxy protocol.
|
||||
host:
|
||||
type: string
|
||||
description: Proxy host, without protocol or port.
|
||||
port:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Proxy port.
|
||||
required:
|
||||
- protocol
|
||||
- host
|
||||
- port
|
||||
required:
|
||||
- proxy
|
||||
description: Outbound proxy configuration.
|
||||
socket:
|
||||
type: object
|
||||
properties:
|
||||
keepAlive:
|
||||
type: boolean
|
||||
description: Whether to keep sockets open for reuse.
|
||||
maxSockets:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum sockets per host kept open at once.
|
||||
maxFreeSockets:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum idle sockets per host kept open.
|
||||
description: Connection socket options.
|
||||
description: Additional HTTP request options.
|
||||
id:
|
||||
type: string
|
||||
description: Server-generated unique id of the destination. Returned in responses; not accepted in requests.
|
||||
readOnly: true
|
||||
example: 88be6560-bfb4-455c-8aa1-06971e9e5522
|
||||
required:
|
||||
- type
|
||||
- url
|
||||
- type: object
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Human-readable name shown in the UI.
|
||||
example: My destination
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the destination currently receives events.
|
||||
example: true
|
||||
subscribedEvents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Event names (or group prefixes, e.g. `n8n.workflow`, which matches all `n8n.workflow.*` events) this destination receives. Retrieve the full list of streamable event names from `GET /settings/log-streaming/event-types`.
|
||||
example:
|
||||
- n8n.workflow
|
||||
- n8n.audit
|
||||
anonymizeAuditMessages:
|
||||
type: boolean
|
||||
description: Whether audit message payloads are anonymized before being sent.
|
||||
example: false
|
||||
circuitBreaker:
|
||||
type: object
|
||||
properties:
|
||||
maxFailures:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum failures within the sliding window before the breaker opens.
|
||||
failureWindow:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Sliding window, in milliseconds, over which failures are counted.
|
||||
description: Circuit-breaker tuning for delivery. After repeated failures the destination stops sending for a cool-down window instead of hammering an unhealthy target. All fields are optional; sensible defaults apply when omitted.
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- syslog
|
||||
description: Sends each event to a syslog server.
|
||||
example: syslog
|
||||
host:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Syslog server host.
|
||||
example: syslog.example.com
|
||||
port:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Syslog server port.
|
||||
example: 514
|
||||
protocol:
|
||||
type: string
|
||||
enum:
|
||||
- udp
|
||||
- tcp
|
||||
- tls
|
||||
description: Transport protocol.
|
||||
example: udp
|
||||
facility:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 23
|
||||
description: 'Syslog facility code (0–23, per RFC 5424). Common values: 0 Kernel, 1 User, 3 System, 13 Audit, 14 Alert, 16 Local0, 17 Local1, 18 Local2, 19 Local3, 20 Local4, 21 Local5, 22 Local6, 23 Local7.'
|
||||
example: 16
|
||||
app_name:
|
||||
type: string
|
||||
description: Application name reported in the syslog message.
|
||||
example: n8n
|
||||
tlsCa:
|
||||
type: string
|
||||
description: PEM-encoded CA certificate used when `protocol` is `tls`.
|
||||
id:
|
||||
type: string
|
||||
description: Server-generated unique id of the destination. Returned in responses; not accepted in requests.
|
||||
readOnly: true
|
||||
example: 88be6560-bfb4-455c-8aa1-06971e9e5522
|
||||
required:
|
||||
- type
|
||||
- host
|
||||
- type: object
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Human-readable name shown in the UI.
|
||||
example: My destination
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the destination currently receives events.
|
||||
example: true
|
||||
subscribedEvents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Event names (or group prefixes, e.g. `n8n.workflow`, which matches all `n8n.workflow.*` events) this destination receives. Retrieve the full list of streamable event names from `GET /settings/log-streaming/event-types`.
|
||||
example:
|
||||
- n8n.workflow
|
||||
- n8n.audit
|
||||
anonymizeAuditMessages:
|
||||
type: boolean
|
||||
description: Whether audit message payloads are anonymized before being sent.
|
||||
example: false
|
||||
circuitBreaker:
|
||||
type: object
|
||||
properties:
|
||||
maxFailures:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum failures within the sliding window before the breaker opens.
|
||||
failureWindow:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Sliding window, in milliseconds, over which failures are counted.
|
||||
description: Circuit-breaker tuning for delivery. After repeated failures the destination stops sending for a cool-down window instead of hammering an unhealthy target. All fields are optional; sensible defaults apply when omitted.
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- sentry
|
||||
description: Sends each event to a Sentry project.
|
||||
example: sentry
|
||||
dsn:
|
||||
type: string
|
||||
format: uri
|
||||
description: Sentry DSN the events are sent to.
|
||||
example: https://examplePublicKey@o0.ingest.sentry.io/0
|
||||
id:
|
||||
type: string
|
||||
description: Server-generated unique id of the destination. Returned in responses; not accepted in requests.
|
||||
readOnly: true
|
||||
example: 88be6560-bfb4-455c-8aa1-06971e9e5522
|
||||
required:
|
||||
- type
|
||||
- dsn
|
||||
description: 'A log streaming destination. The `type` field selects the variant and its type-specific fields: `webhook`, `syslog`, or `sentry`.'
|
||||
responses:
|
||||
'200':
|
||||
description: Operation successful.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: ../../../../shared/spec/schemas/logStreamingDestinationPublicDto.generated.yml
|
||||
'400':
|
||||
$ref: ../../../../shared/spec/responses/badRequest.yml
|
||||
'401':
|
||||
$ref: ../../../../shared/spec/responses/unauthorized.yml
|
||||
'403':
|
||||
$ref: ../../../../shared/spec/responses/forbidden.yml
|
||||
'409':
|
||||
$ref: ../../../../shared/spec/responses/conflict.yml
|
||||
'415':
|
||||
$ref: ../../../../shared/spec/responses/unsupportedMediaType.yml
|
||||
|
|
@ -20,360 +20,7 @@ responses:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- type: object
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Human-readable name shown in the UI.
|
||||
example: My destination
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the destination currently receives events.
|
||||
example: true
|
||||
subscribedEvents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Event names (or group prefixes, e.g. `n8n.workflow`, which matches all `n8n.workflow.*` events) this destination receives. Retrieve the full list of streamable event names from `GET /settings/log-streaming/event-types`.
|
||||
example:
|
||||
- n8n.workflow
|
||||
- n8n.audit
|
||||
anonymizeAuditMessages:
|
||||
type: boolean
|
||||
description: Whether audit message payloads are anonymized before being sent.
|
||||
example: false
|
||||
circuitBreaker:
|
||||
type: object
|
||||
properties:
|
||||
maxFailures:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum failures within the sliding window before the breaker opens.
|
||||
failureWindow:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Sliding window, in milliseconds, over which failures are counted.
|
||||
description: Circuit-breaker tuning for delivery. After repeated failures the destination stops sending for a cool-down window instead of hammering an unhealthy target. All fields are optional; sensible defaults apply when omitted.
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- webhook
|
||||
description: Sends each event as an HTTP request to a URL.
|
||||
example: webhook
|
||||
url:
|
||||
type: string
|
||||
format: uri
|
||||
description: Target URL that receives the event payload.
|
||||
example: https://example.com/n8n-events
|
||||
method:
|
||||
type: string
|
||||
description: HTTP method used for the request.
|
||||
example: POST
|
||||
sendHeaders:
|
||||
type: boolean
|
||||
description: Whether to attach custom headers to the request.
|
||||
example: false
|
||||
specifyHeaders:
|
||||
type: string
|
||||
description: How custom headers are provided — `keypair` or `json`.
|
||||
example: keypair
|
||||
headerParameters:
|
||||
type: object
|
||||
properties:
|
||||
parameters:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
example: Authorization
|
||||
value:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
- {}
|
||||
- {}
|
||||
description: Parameter value. Usually a string; numbers, booleans and null are also accepted.
|
||||
example: Bearer <token>
|
||||
required:
|
||||
- name
|
||||
- value
|
||||
required:
|
||||
- parameters
|
||||
description: Key-value pairs used when the matching `specify*` field is `keypair`.
|
||||
jsonHeaders:
|
||||
type: string
|
||||
description: Custom headers as a JSON string when `specifyHeaders` is `json`.
|
||||
sendQuery:
|
||||
type: boolean
|
||||
description: Whether to attach query parameters to the request.
|
||||
example: false
|
||||
specifyQuery:
|
||||
type: string
|
||||
description: How query parameters are provided — `keypair` or `json`.
|
||||
queryParameters:
|
||||
type: object
|
||||
properties:
|
||||
parameters:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
example: Authorization
|
||||
value:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
- {}
|
||||
- {}
|
||||
description: Parameter value. Usually a string; numbers, booleans and null are also accepted.
|
||||
example: Bearer <token>
|
||||
required:
|
||||
- name
|
||||
- value
|
||||
required:
|
||||
- parameters
|
||||
description: Key-value pairs used when the matching `specify*` field is `keypair`.
|
||||
jsonQuery:
|
||||
type: string
|
||||
description: Query parameters as a JSON string when `specifyQuery` is `json`.
|
||||
options:
|
||||
type: object
|
||||
properties:
|
||||
timeout:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Request timeout in milliseconds.
|
||||
allowUnauthorizedCerts:
|
||||
type: boolean
|
||||
description: Accept self-signed or otherwise invalid TLS certificates.
|
||||
queryParameterArrays:
|
||||
type: string
|
||||
enum:
|
||||
- indices
|
||||
- brackets
|
||||
- repeat
|
||||
description: How array-valued query parameters are serialized into the query string.
|
||||
redirect:
|
||||
type: object
|
||||
properties:
|
||||
redirect:
|
||||
type: object
|
||||
properties:
|
||||
followRedirects:
|
||||
type: boolean
|
||||
description: Whether to follow HTTP redirects.
|
||||
maxRedirects:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum number of redirects to follow.
|
||||
required:
|
||||
- redirect
|
||||
description: Redirect-following behaviour.
|
||||
proxy:
|
||||
type: object
|
||||
properties:
|
||||
proxy:
|
||||
type: object
|
||||
properties:
|
||||
protocol:
|
||||
type: string
|
||||
enum:
|
||||
- https
|
||||
- http
|
||||
description: Proxy protocol.
|
||||
host:
|
||||
type: string
|
||||
description: Proxy host, without protocol or port.
|
||||
port:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Proxy port.
|
||||
required:
|
||||
- protocol
|
||||
- host
|
||||
- port
|
||||
required:
|
||||
- proxy
|
||||
description: Outbound proxy configuration.
|
||||
socket:
|
||||
type: object
|
||||
properties:
|
||||
keepAlive:
|
||||
type: boolean
|
||||
description: Whether to keep sockets open for reuse.
|
||||
maxSockets:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum sockets per host kept open at once.
|
||||
maxFreeSockets:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum idle sockets per host kept open.
|
||||
description: Connection socket options.
|
||||
description: Additional HTTP request options.
|
||||
id:
|
||||
type: string
|
||||
description: Server-generated unique id of the destination. Returned in responses; not accepted in requests.
|
||||
example: 88be6560-bfb4-455c-8aa1-06971e9e5522
|
||||
required:
|
||||
- type
|
||||
- url
|
||||
- id
|
||||
- type: object
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Human-readable name shown in the UI.
|
||||
example: My destination
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the destination currently receives events.
|
||||
example: true
|
||||
subscribedEvents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Event names (or group prefixes, e.g. `n8n.workflow`, which matches all `n8n.workflow.*` events) this destination receives. Retrieve the full list of streamable event names from `GET /settings/log-streaming/event-types`.
|
||||
example:
|
||||
- n8n.workflow
|
||||
- n8n.audit
|
||||
anonymizeAuditMessages:
|
||||
type: boolean
|
||||
description: Whether audit message payloads are anonymized before being sent.
|
||||
example: false
|
||||
circuitBreaker:
|
||||
type: object
|
||||
properties:
|
||||
maxFailures:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum failures within the sliding window before the breaker opens.
|
||||
failureWindow:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Sliding window, in milliseconds, over which failures are counted.
|
||||
description: Circuit-breaker tuning for delivery. After repeated failures the destination stops sending for a cool-down window instead of hammering an unhealthy target. All fields are optional; sensible defaults apply when omitted.
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- syslog
|
||||
description: Sends each event to a syslog server.
|
||||
example: syslog
|
||||
host:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Syslog server host.
|
||||
example: syslog.example.com
|
||||
port:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Syslog server port.
|
||||
example: 514
|
||||
protocol:
|
||||
type: string
|
||||
enum:
|
||||
- udp
|
||||
- tcp
|
||||
- tls
|
||||
description: Transport protocol.
|
||||
example: udp
|
||||
facility:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 23
|
||||
description: 'Syslog facility code (0–23, per RFC 5424). Common values: 0 Kernel, 1 User, 3 System, 13 Audit, 14 Alert, 16 Local0, 17 Local1, 18 Local2, 19 Local3, 20 Local4, 21 Local5, 22 Local6, 23 Local7.'
|
||||
example: 16
|
||||
app_name:
|
||||
type: string
|
||||
description: Application name reported in the syslog message.
|
||||
example: n8n
|
||||
tlsCa:
|
||||
type: string
|
||||
description: PEM-encoded CA certificate used when `protocol` is `tls`.
|
||||
id:
|
||||
type: string
|
||||
description: Server-generated unique id of the destination. Returned in responses; not accepted in requests.
|
||||
example: 88be6560-bfb4-455c-8aa1-06971e9e5522
|
||||
required:
|
||||
- type
|
||||
- host
|
||||
- id
|
||||
- type: object
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Human-readable name shown in the UI.
|
||||
example: My destination
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the destination currently receives events.
|
||||
example: true
|
||||
subscribedEvents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Event names (or group prefixes, e.g. `n8n.workflow`, which matches all `n8n.workflow.*` events) this destination receives. Retrieve the full list of streamable event names from `GET /settings/log-streaming/event-types`.
|
||||
example:
|
||||
- n8n.workflow
|
||||
- n8n.audit
|
||||
anonymizeAuditMessages:
|
||||
type: boolean
|
||||
description: Whether audit message payloads are anonymized before being sent.
|
||||
example: false
|
||||
circuitBreaker:
|
||||
type: object
|
||||
properties:
|
||||
maxFailures:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum failures within the sliding window before the breaker opens.
|
||||
failureWindow:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Sliding window, in milliseconds, over which failures are counted.
|
||||
description: Circuit-breaker tuning for delivery. After repeated failures the destination stops sending for a cool-down window instead of hammering an unhealthy target. All fields are optional; sensible defaults apply when omitted.
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- sentry
|
||||
description: Sends each event to a Sentry project.
|
||||
example: sentry
|
||||
dsn:
|
||||
type: string
|
||||
format: uri
|
||||
description: Sentry DSN the events are sent to.
|
||||
example: https://examplePublicKey@o0.ingest.sentry.io/0
|
||||
id:
|
||||
type: string
|
||||
description: Server-generated unique id of the destination. Returned in responses; not accepted in requests.
|
||||
example: 88be6560-bfb4-455c-8aa1-06971e9e5522
|
||||
required:
|
||||
- type
|
||||
- dsn
|
||||
- id
|
||||
description: 'A log streaming destination. The `type` field selects the variant and its type-specific fields: `webhook`, `syslog`, or `sentry`.'
|
||||
$ref: ../../../../shared/spec/schemas/logStreamingDestinationPublicDto.generated.yml
|
||||
'401':
|
||||
$ref: ../../../../shared/spec/responses/unauthorized.yml
|
||||
'403':
|
||||
|
|
|
|||
|
|
@ -0,0 +1,379 @@
|
|||
operationId: updateLogStreamingDestination
|
||||
tags:
|
||||
- LogStreaming
|
||||
summary: Update a log streaming destination
|
||||
description: Replace an existing log streaming destination. The update takes effect exactly as it would from the UI, using the same validation. Requires the `eventBusDestination:update` scope and the Log Streaming feature to be licensed. When destinations are managed via environment variables, the write is rejected with 409 and nothing is changed; reads still return the current values.
|
||||
x-required-scope: eventBusDestination:update
|
||||
x-eov-operation-id: unreachable
|
||||
x-eov-operation-handler: v1/handlers/decorator-routed.handler
|
||||
x-decorator-routed: true
|
||||
parameters:
|
||||
- schema:
|
||||
type: string
|
||||
required: true
|
||||
description: The id of the log streaming destination.
|
||||
name: id
|
||||
in: path
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
oneOf:
|
||||
- type: object
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Human-readable name shown in the UI.
|
||||
example: My destination
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the destination currently receives events.
|
||||
example: true
|
||||
subscribedEvents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Event names (or group prefixes, e.g. `n8n.workflow`, which matches all `n8n.workflow.*` events) this destination receives. Retrieve the full list of streamable event names from `GET /settings/log-streaming/event-types`.
|
||||
example:
|
||||
- n8n.workflow
|
||||
- n8n.audit
|
||||
anonymizeAuditMessages:
|
||||
type: boolean
|
||||
description: Whether audit message payloads are anonymized before being sent.
|
||||
example: false
|
||||
circuitBreaker:
|
||||
type: object
|
||||
properties:
|
||||
maxFailures:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum failures within the sliding window before the breaker opens.
|
||||
failureWindow:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Sliding window, in milliseconds, over which failures are counted.
|
||||
description: Circuit-breaker tuning for delivery. After repeated failures the destination stops sending for a cool-down window instead of hammering an unhealthy target. All fields are optional; sensible defaults apply when omitted.
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- webhook
|
||||
description: Sends each event as an HTTP request to a URL.
|
||||
example: webhook
|
||||
url:
|
||||
type: string
|
||||
format: uri
|
||||
description: Target URL that receives the event payload.
|
||||
example: https://example.com/n8n-events
|
||||
method:
|
||||
type: string
|
||||
description: HTTP method used for the request.
|
||||
example: POST
|
||||
sendHeaders:
|
||||
type: boolean
|
||||
description: Whether to attach custom headers to the request.
|
||||
example: false
|
||||
specifyHeaders:
|
||||
type: string
|
||||
description: How custom headers are provided — `keypair` or `json`.
|
||||
example: keypair
|
||||
headerParameters:
|
||||
type: object
|
||||
properties:
|
||||
parameters:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
example: Authorization
|
||||
value:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
- {}
|
||||
- {}
|
||||
description: Parameter value. Usually a string; numbers, booleans and null are also accepted.
|
||||
example: Bearer <token>
|
||||
required:
|
||||
- name
|
||||
- value
|
||||
required:
|
||||
- parameters
|
||||
description: Key-value pairs used when the matching `specify*` field is `keypair`.
|
||||
jsonHeaders:
|
||||
type: string
|
||||
description: Custom headers as a JSON string when `specifyHeaders` is `json`.
|
||||
sendQuery:
|
||||
type: boolean
|
||||
description: Whether to attach query parameters to the request.
|
||||
example: false
|
||||
specifyQuery:
|
||||
type: string
|
||||
description: How query parameters are provided — `keypair` or `json`.
|
||||
queryParameters:
|
||||
type: object
|
||||
properties:
|
||||
parameters:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
example: Authorization
|
||||
value:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
- {}
|
||||
- {}
|
||||
description: Parameter value. Usually a string; numbers, booleans and null are also accepted.
|
||||
example: Bearer <token>
|
||||
required:
|
||||
- name
|
||||
- value
|
||||
required:
|
||||
- parameters
|
||||
description: Key-value pairs used when the matching `specify*` field is `keypair`.
|
||||
jsonQuery:
|
||||
type: string
|
||||
description: Query parameters as a JSON string when `specifyQuery` is `json`.
|
||||
options:
|
||||
type: object
|
||||
properties:
|
||||
timeout:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Request timeout in milliseconds.
|
||||
allowUnauthorizedCerts:
|
||||
type: boolean
|
||||
description: Accept self-signed or otherwise invalid TLS certificates.
|
||||
queryParameterArrays:
|
||||
type: string
|
||||
enum:
|
||||
- indices
|
||||
- brackets
|
||||
- repeat
|
||||
description: How array-valued query parameters are serialized into the query string.
|
||||
redirect:
|
||||
type: object
|
||||
properties:
|
||||
redirect:
|
||||
type: object
|
||||
properties:
|
||||
followRedirects:
|
||||
type: boolean
|
||||
description: Whether to follow HTTP redirects.
|
||||
maxRedirects:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum number of redirects to follow.
|
||||
required:
|
||||
- redirect
|
||||
description: Redirect-following behaviour.
|
||||
proxy:
|
||||
type: object
|
||||
properties:
|
||||
proxy:
|
||||
type: object
|
||||
properties:
|
||||
protocol:
|
||||
type: string
|
||||
enum:
|
||||
- https
|
||||
- http
|
||||
description: Proxy protocol.
|
||||
host:
|
||||
type: string
|
||||
description: Proxy host, without protocol or port.
|
||||
port:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Proxy port.
|
||||
required:
|
||||
- protocol
|
||||
- host
|
||||
- port
|
||||
required:
|
||||
- proxy
|
||||
description: Outbound proxy configuration.
|
||||
socket:
|
||||
type: object
|
||||
properties:
|
||||
keepAlive:
|
||||
type: boolean
|
||||
description: Whether to keep sockets open for reuse.
|
||||
maxSockets:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum sockets per host kept open at once.
|
||||
maxFreeSockets:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum idle sockets per host kept open.
|
||||
description: Connection socket options.
|
||||
description: Additional HTTP request options.
|
||||
required:
|
||||
- type
|
||||
- url
|
||||
- type: object
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Human-readable name shown in the UI.
|
||||
example: My destination
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the destination currently receives events.
|
||||
example: true
|
||||
subscribedEvents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Event names (or group prefixes, e.g. `n8n.workflow`, which matches all `n8n.workflow.*` events) this destination receives. Retrieve the full list of streamable event names from `GET /settings/log-streaming/event-types`.
|
||||
example:
|
||||
- n8n.workflow
|
||||
- n8n.audit
|
||||
anonymizeAuditMessages:
|
||||
type: boolean
|
||||
description: Whether audit message payloads are anonymized before being sent.
|
||||
example: false
|
||||
circuitBreaker:
|
||||
type: object
|
||||
properties:
|
||||
maxFailures:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum failures within the sliding window before the breaker opens.
|
||||
failureWindow:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Sliding window, in milliseconds, over which failures are counted.
|
||||
description: Circuit-breaker tuning for delivery. After repeated failures the destination stops sending for a cool-down window instead of hammering an unhealthy target. All fields are optional; sensible defaults apply when omitted.
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- syslog
|
||||
description: Sends each event to a syslog server.
|
||||
example: syslog
|
||||
host:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Syslog server host.
|
||||
example: syslog.example.com
|
||||
port:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Syslog server port.
|
||||
example: 514
|
||||
protocol:
|
||||
type: string
|
||||
enum:
|
||||
- udp
|
||||
- tcp
|
||||
- tls
|
||||
description: Transport protocol.
|
||||
example: udp
|
||||
facility:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 23
|
||||
description: 'Syslog facility code (0–23, per RFC 5424). Common values: 0 Kernel, 1 User, 3 System, 13 Audit, 14 Alert, 16 Local0, 17 Local1, 18 Local2, 19 Local3, 20 Local4, 21 Local5, 22 Local6, 23 Local7.'
|
||||
example: 16
|
||||
app_name:
|
||||
type: string
|
||||
description: Application name reported in the syslog message.
|
||||
example: n8n
|
||||
tlsCa:
|
||||
type: string
|
||||
description: PEM-encoded CA certificate used when `protocol` is `tls`.
|
||||
required:
|
||||
- type
|
||||
- host
|
||||
- type: object
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Human-readable name shown in the UI.
|
||||
example: My destination
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the destination currently receives events.
|
||||
example: true
|
||||
subscribedEvents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Event names (or group prefixes, e.g. `n8n.workflow`, which matches all `n8n.workflow.*` events) this destination receives. Retrieve the full list of streamable event names from `GET /settings/log-streaming/event-types`.
|
||||
example:
|
||||
- n8n.workflow
|
||||
- n8n.audit
|
||||
anonymizeAuditMessages:
|
||||
type: boolean
|
||||
description: Whether audit message payloads are anonymized before being sent.
|
||||
example: false
|
||||
circuitBreaker:
|
||||
type: object
|
||||
properties:
|
||||
maxFailures:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum failures within the sliding window before the breaker opens.
|
||||
failureWindow:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Sliding window, in milliseconds, over which failures are counted.
|
||||
description: Circuit-breaker tuning for delivery. After repeated failures the destination stops sending for a cool-down window instead of hammering an unhealthy target. All fields are optional; sensible defaults apply when omitted.
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- sentry
|
||||
description: Sends each event to a Sentry project.
|
||||
example: sentry
|
||||
dsn:
|
||||
type: string
|
||||
format: uri
|
||||
description: Sentry DSN the events are sent to.
|
||||
example: https://examplePublicKey@o0.ingest.sentry.io/0
|
||||
required:
|
||||
- type
|
||||
- dsn
|
||||
description: 'A log streaming destination. The `type` field selects the variant and its type-specific fields: `webhook`, `syslog`, or `sentry`.'
|
||||
responses:
|
||||
'200':
|
||||
description: Operation successful.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: ../../../../shared/spec/schemas/logStreamingDestinationPublicDto.generated.yml
|
||||
'400':
|
||||
$ref: ../../../../shared/spec/responses/badRequest.yml
|
||||
'401':
|
||||
$ref: ../../../../shared/spec/responses/unauthorized.yml
|
||||
'403':
|
||||
$ref: ../../../../shared/spec/responses/forbidden.yml
|
||||
'404':
|
||||
$ref: ../../../../shared/spec/responses/notFound.yml
|
||||
'409':
|
||||
$ref: ../../../../shared/spec/responses/conflict.yml
|
||||
'415':
|
||||
$ref: ../../../../shared/spec/responses/unsupportedMediaType.yml
|
||||
|
|
@ -171,9 +171,13 @@ paths:
|
|||
/settings/log-streaming/destinations:
|
||||
get:
|
||||
$ref: ./handlers/settings/spec/paths/getLogStreamingDestinations.generated.yml
|
||||
post:
|
||||
$ref: ./handlers/settings/spec/paths/createLogStreamingDestination.generated.yml
|
||||
/settings/log-streaming/destinations/{id}:
|
||||
get:
|
||||
$ref: ./handlers/settings/spec/paths/getLogStreamingDestination.generated.yml
|
||||
put:
|
||||
$ref: ./handlers/settings/spec/paths/updateLogStreamingDestination.generated.yml
|
||||
/node-type-policies/instance:
|
||||
get:
|
||||
$ref: ./handlers/node-type-policies/spec/paths/getNodeTypeInstancePolicy.generated.yml
|
||||
|
|
|
|||
|
|
@ -85,8 +85,6 @@ paths:
|
|||
$ref: './handlers/n8n-packages/spec/paths/n8n-packages.import.yml'
|
||||
/n8n-packages/import-selection:
|
||||
$ref: './handlers/n8n-packages/spec/paths/n8n-packages.import-selection.yml'
|
||||
/settings/log-streaming/destinations:
|
||||
$ref: './handlers/log-streaming/spec/paths/destinations.yml'
|
||||
/settings/log-streaming/destinations/{id}/test:
|
||||
$ref: './handlers/log-streaming/spec/paths/destinations.id.test.yml'
|
||||
/settings/log-streaming/destinations/{id}:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,354 @@
|
|||
oneOf:
|
||||
- type: object
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Human-readable name shown in the UI.
|
||||
example: My destination
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the destination currently receives events.
|
||||
example: true
|
||||
subscribedEvents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Event names (or group prefixes, e.g. `n8n.workflow`, which matches all `n8n.workflow.*` events) this destination receives. Retrieve the full list of streamable event names from `GET /settings/log-streaming/event-types`.
|
||||
example:
|
||||
- n8n.workflow
|
||||
- n8n.audit
|
||||
anonymizeAuditMessages:
|
||||
type: boolean
|
||||
description: Whether audit message payloads are anonymized before being sent.
|
||||
example: false
|
||||
circuitBreaker:
|
||||
type: object
|
||||
properties:
|
||||
maxFailures:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum failures within the sliding window before the breaker opens.
|
||||
failureWindow:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Sliding window, in milliseconds, over which failures are counted.
|
||||
description: Circuit-breaker tuning for delivery. After repeated failures the destination stops sending for a cool-down window instead of hammering an unhealthy target. All fields are optional; sensible defaults apply when omitted.
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- webhook
|
||||
description: Sends each event as an HTTP request to a URL.
|
||||
example: webhook
|
||||
url:
|
||||
type: string
|
||||
format: uri
|
||||
description: Target URL that receives the event payload.
|
||||
example: https://example.com/n8n-events
|
||||
method:
|
||||
type: string
|
||||
description: HTTP method used for the request.
|
||||
example: POST
|
||||
sendHeaders:
|
||||
type: boolean
|
||||
description: Whether to attach custom headers to the request.
|
||||
example: false
|
||||
specifyHeaders:
|
||||
type: string
|
||||
description: How custom headers are provided — `keypair` or `json`.
|
||||
example: keypair
|
||||
headerParameters:
|
||||
type: object
|
||||
properties:
|
||||
parameters:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
example: Authorization
|
||||
value:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
- {}
|
||||
- {}
|
||||
description: Parameter value. Usually a string; numbers, booleans and null are also accepted.
|
||||
example: Bearer <token>
|
||||
required:
|
||||
- name
|
||||
- value
|
||||
required:
|
||||
- parameters
|
||||
description: Key-value pairs used when the matching `specify*` field is `keypair`.
|
||||
jsonHeaders:
|
||||
type: string
|
||||
description: Custom headers as a JSON string when `specifyHeaders` is `json`.
|
||||
sendQuery:
|
||||
type: boolean
|
||||
description: Whether to attach query parameters to the request.
|
||||
example: false
|
||||
specifyQuery:
|
||||
type: string
|
||||
description: How query parameters are provided — `keypair` or `json`.
|
||||
queryParameters:
|
||||
type: object
|
||||
properties:
|
||||
parameters:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
example: Authorization
|
||||
value:
|
||||
anyOf:
|
||||
- type: string
|
||||
- type: number
|
||||
- type: boolean
|
||||
- {}
|
||||
- {}
|
||||
description: Parameter value. Usually a string; numbers, booleans and null are also accepted.
|
||||
example: Bearer <token>
|
||||
required:
|
||||
- name
|
||||
- value
|
||||
required:
|
||||
- parameters
|
||||
description: Key-value pairs used when the matching `specify*` field is `keypair`.
|
||||
jsonQuery:
|
||||
type: string
|
||||
description: Query parameters as a JSON string when `specifyQuery` is `json`.
|
||||
options:
|
||||
type: object
|
||||
properties:
|
||||
timeout:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Request timeout in milliseconds.
|
||||
allowUnauthorizedCerts:
|
||||
type: boolean
|
||||
description: Accept self-signed or otherwise invalid TLS certificates.
|
||||
queryParameterArrays:
|
||||
type: string
|
||||
enum:
|
||||
- indices
|
||||
- brackets
|
||||
- repeat
|
||||
description: How array-valued query parameters are serialized into the query string.
|
||||
redirect:
|
||||
type: object
|
||||
properties:
|
||||
redirect:
|
||||
type: object
|
||||
properties:
|
||||
followRedirects:
|
||||
type: boolean
|
||||
description: Whether to follow HTTP redirects.
|
||||
maxRedirects:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum number of redirects to follow.
|
||||
required:
|
||||
- redirect
|
||||
description: Redirect-following behaviour.
|
||||
proxy:
|
||||
type: object
|
||||
properties:
|
||||
proxy:
|
||||
type: object
|
||||
properties:
|
||||
protocol:
|
||||
type: string
|
||||
enum:
|
||||
- https
|
||||
- http
|
||||
description: Proxy protocol.
|
||||
host:
|
||||
type: string
|
||||
description: Proxy host, without protocol or port.
|
||||
port:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Proxy port.
|
||||
required:
|
||||
- protocol
|
||||
- host
|
||||
- port
|
||||
required:
|
||||
- proxy
|
||||
description: Outbound proxy configuration.
|
||||
socket:
|
||||
type: object
|
||||
properties:
|
||||
keepAlive:
|
||||
type: boolean
|
||||
description: Whether to keep sockets open for reuse.
|
||||
maxSockets:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum sockets per host kept open at once.
|
||||
maxFreeSockets:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum idle sockets per host kept open.
|
||||
description: Connection socket options.
|
||||
description: Additional HTTP request options.
|
||||
id:
|
||||
type: string
|
||||
description: Server-generated unique id of the destination. Returned in responses; not accepted in requests.
|
||||
example: 88be6560-bfb4-455c-8aa1-06971e9e5522
|
||||
required:
|
||||
- type
|
||||
- url
|
||||
- id
|
||||
- type: object
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Human-readable name shown in the UI.
|
||||
example: My destination
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the destination currently receives events.
|
||||
example: true
|
||||
subscribedEvents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Event names (or group prefixes, e.g. `n8n.workflow`, which matches all `n8n.workflow.*` events) this destination receives. Retrieve the full list of streamable event names from `GET /settings/log-streaming/event-types`.
|
||||
example:
|
||||
- n8n.workflow
|
||||
- n8n.audit
|
||||
anonymizeAuditMessages:
|
||||
type: boolean
|
||||
description: Whether audit message payloads are anonymized before being sent.
|
||||
example: false
|
||||
circuitBreaker:
|
||||
type: object
|
||||
properties:
|
||||
maxFailures:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum failures within the sliding window before the breaker opens.
|
||||
failureWindow:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Sliding window, in milliseconds, over which failures are counted.
|
||||
description: Circuit-breaker tuning for delivery. After repeated failures the destination stops sending for a cool-down window instead of hammering an unhealthy target. All fields are optional; sensible defaults apply when omitted.
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- syslog
|
||||
description: Sends each event to a syslog server.
|
||||
example: syslog
|
||||
host:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Syslog server host.
|
||||
example: syslog.example.com
|
||||
port:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Syslog server port.
|
||||
example: 514
|
||||
protocol:
|
||||
type: string
|
||||
enum:
|
||||
- udp
|
||||
- tcp
|
||||
- tls
|
||||
description: Transport protocol.
|
||||
example: udp
|
||||
facility:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 23
|
||||
description: 'Syslog facility code (0–23, per RFC 5424). Common values: 0 Kernel, 1 User, 3 System, 13 Audit, 14 Alert, 16 Local0, 17 Local1, 18 Local2, 19 Local3, 20 Local4, 21 Local5, 22 Local6, 23 Local7.'
|
||||
example: 16
|
||||
app_name:
|
||||
type: string
|
||||
description: Application name reported in the syslog message.
|
||||
example: n8n
|
||||
tlsCa:
|
||||
type: string
|
||||
description: PEM-encoded CA certificate used when `protocol` is `tls`.
|
||||
id:
|
||||
type: string
|
||||
description: Server-generated unique id of the destination. Returned in responses; not accepted in requests.
|
||||
example: 88be6560-bfb4-455c-8aa1-06971e9e5522
|
||||
required:
|
||||
- type
|
||||
- host
|
||||
- id
|
||||
- type: object
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
minLength: 1
|
||||
description: Human-readable name shown in the UI.
|
||||
example: My destination
|
||||
enabled:
|
||||
type: boolean
|
||||
description: Whether the destination currently receives events.
|
||||
example: true
|
||||
subscribedEvents:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: Event names (or group prefixes, e.g. `n8n.workflow`, which matches all `n8n.workflow.*` events) this destination receives. Retrieve the full list of streamable event names from `GET /settings/log-streaming/event-types`.
|
||||
example:
|
||||
- n8n.workflow
|
||||
- n8n.audit
|
||||
anonymizeAuditMessages:
|
||||
type: boolean
|
||||
description: Whether audit message payloads are anonymized before being sent.
|
||||
example: false
|
||||
circuitBreaker:
|
||||
type: object
|
||||
properties:
|
||||
maxFailures:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Maximum failures within the sliding window before the breaker opens.
|
||||
failureWindow:
|
||||
type: integer
|
||||
minimum: 0
|
||||
exclusiveMinimum: true
|
||||
description: Sliding window, in milliseconds, over which failures are counted.
|
||||
description: Circuit-breaker tuning for delivery. After repeated failures the destination stops sending for a cool-down window instead of hammering an unhealthy target. All fields are optional; sensible defaults apply when omitted.
|
||||
type:
|
||||
type: string
|
||||
enum:
|
||||
- sentry
|
||||
description: Sends each event to a Sentry project.
|
||||
example: sentry
|
||||
dsn:
|
||||
type: string
|
||||
format: uri
|
||||
description: Sentry DSN the events are sent to.
|
||||
example: https://examplePublicKey@o0.ingest.sentry.io/0
|
||||
id:
|
||||
type: string
|
||||
description: Server-generated unique id of the destination. Returned in responses; not accepted in requests.
|
||||
example: 88be6560-bfb4-455c-8aa1-06971e9e5522
|
||||
required:
|
||||
- type
|
||||
- dsn
|
||||
- id
|
||||
description: 'A log streaming destination. The `type` field selects the variant and its type-specific fields: `webhook`, `syslog`, or `sentry`.'
|
||||
|
|
@ -151,7 +151,8 @@ describe('GET /discover', () => {
|
|||
expect(withSchema.length).toBeGreaterThan(0);
|
||||
|
||||
for (const endpoint of withSchema) {
|
||||
expect((endpoint as Record<string, unknown>).requestSchema).toHaveProperty('type');
|
||||
const schema = (endpoint as { requestSchema: Record<string, unknown> }).requestSchema;
|
||||
expect('type' in schema || 'oneOf' in schema).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -279,6 +279,22 @@ describe('Log streaming in Public API', () => {
|
|||
expect(response.status).toBe(400);
|
||||
});
|
||||
|
||||
it('drops fields that do not belong to the destination type', async () => {
|
||||
const response = await testServer
|
||||
.publicApiAgentFor(owner)
|
||||
.post('/settings/log-streaming/destinations')
|
||||
.send({
|
||||
...webhookPayload,
|
||||
dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
|
||||
unknownField: 'value',
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.type).toBe('webhook');
|
||||
expect(response.body).not.toHaveProperty('dsn');
|
||||
expect(response.body).not.toHaveProperty('unknownField');
|
||||
});
|
||||
|
||||
it('creates a fully-populated webhook destination', async () => {
|
||||
const payload = {
|
||||
type: 'webhook',
|
||||
|
|
@ -304,12 +320,14 @@ describe('Log streaming in Public API', () => {
|
|||
.send(payload);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toHaveProperty('id');
|
||||
expect(response.body).toMatchObject(payload);
|
||||
expect(response.body).not.toHaveProperty('responseCodeMustMatch');
|
||||
expect(response.body).not.toHaveProperty('sendPayload');
|
||||
expect(response.body).not.toHaveProperty('authentication');
|
||||
expect(response.body).not.toHaveProperty('credentials');
|
||||
// Strict match: `responseCodeMustMatch`, `sendPayload`, `authentication` and
|
||||
// `credentials` must not appear.
|
||||
expect(response.body).toStrictEqual({
|
||||
id: expect.any(String),
|
||||
...payload,
|
||||
jsonHeaders: '',
|
||||
jsonQuery: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a fully-populated syslog destination', async () => {
|
||||
|
|
@ -333,8 +351,7 @@ describe('Log streaming in Public API', () => {
|
|||
.send(payload);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toHaveProperty('id');
|
||||
expect(response.body).toMatchObject(payload);
|
||||
expect(response.body).toStrictEqual({ id: expect.any(String), ...payload });
|
||||
});
|
||||
|
||||
it('creates a fully-populated sentry destination', async () => {
|
||||
|
|
@ -354,8 +371,7 @@ describe('Log streaming in Public API', () => {
|
|||
.send(payload);
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body).toHaveProperty('id');
|
||||
expect(response.body).toMatchObject(payload);
|
||||
expect(response.body).toStrictEqual({ id: expect.any(String), ...payload });
|
||||
});
|
||||
|
||||
it('rejects a malformed body with 400', async () => {
|
||||
|
|
@ -399,6 +415,22 @@ describe('Log streaming in Public API', () => {
|
|||
expect(listResponse.body.data[0].label).toBe('Renamed webhook');
|
||||
});
|
||||
|
||||
it('accepts a GET response as the body and keeps the path id', async () => {
|
||||
const created = await createDestination();
|
||||
const read = await testServer
|
||||
.publicApiAgentFor(owner)
|
||||
.get(`/settings/log-streaming/destinations/${created.id}`);
|
||||
|
||||
const response = await testServer
|
||||
.publicApiAgentFor(owner)
|
||||
.put(`/settings/log-streaming/destinations/${created.id}`)
|
||||
.send({ ...read.body, id: '99999999-9999-4999-8999-999999999999', label: 'Round trip' });
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.body.id).toBe(created.id);
|
||||
expect(response.body.label).toBe('Round trip');
|
||||
});
|
||||
|
||||
it('returns 404 for an unknown destination id', async () => {
|
||||
const response = await testServer
|
||||
.publicApiAgentFor(owner)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user