mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-14 00:24:07 -08:00
feat(Mailhook Node): New node
This commit is contained in:
parent
497d637fc5
commit
c28ccb3e5c
|
@ -48,6 +48,7 @@ export interface FrontendSettings {
|
||||||
};
|
};
|
||||||
timezone: string;
|
timezone: string;
|
||||||
urlBaseWebhook: string;
|
urlBaseWebhook: string;
|
||||||
|
domain: string;
|
||||||
urlBaseEditor: string;
|
urlBaseEditor: string;
|
||||||
versionCli: string;
|
versionCli: string;
|
||||||
nodeJsVersion: string;
|
nodeJsVersion: string;
|
||||||
|
|
|
@ -100,6 +100,7 @@ export class FrontendService {
|
||||||
workflowCallerPolicyDefaultOption: this.globalConfig.workflows.callerPolicyDefaultOption,
|
workflowCallerPolicyDefaultOption: this.globalConfig.workflows.callerPolicyDefaultOption,
|
||||||
timezone: this.globalConfig.generic.timezone,
|
timezone: this.globalConfig.generic.timezone,
|
||||||
urlBaseWebhook: this.urlService.getWebhookBaseUrl(),
|
urlBaseWebhook: this.urlService.getWebhookBaseUrl(),
|
||||||
|
domain: this.urlService.getDomain(),
|
||||||
urlBaseEditor: instanceBaseUrl,
|
urlBaseEditor: instanceBaseUrl,
|
||||||
binaryDataMode: config.getEnv('binaryDataManager.mode'),
|
binaryDataMode: config.getEnv('binaryDataManager.mode'),
|
||||||
nodeJsVersion: process.version.replace(/^v/, ''),
|
nodeJsVersion: process.version.replace(/^v/, ''),
|
||||||
|
@ -250,6 +251,7 @@ export class FrontendService {
|
||||||
// Update all urls, in case `WEBHOOK_URL` was updated by `--tunnel`
|
// Update all urls, in case `WEBHOOK_URL` was updated by `--tunnel`
|
||||||
const instanceBaseUrl = this.urlService.getInstanceBaseUrl();
|
const instanceBaseUrl = this.urlService.getInstanceBaseUrl();
|
||||||
this.settings.urlBaseWebhook = this.urlService.getWebhookBaseUrl();
|
this.settings.urlBaseWebhook = this.urlService.getWebhookBaseUrl();
|
||||||
|
this.settings.domain = this.urlService.getDomain();
|
||||||
this.settings.urlBaseEditor = instanceBaseUrl;
|
this.settings.urlBaseEditor = instanceBaseUrl;
|
||||||
this.settings.oauthCallbackUrls = {
|
this.settings.oauthCallbackUrls = {
|
||||||
oauth1: `${instanceBaseUrl}/${restEndpoint}/oauth1-credential/callback`,
|
oauth1: `${instanceBaseUrl}/${restEndpoint}/oauth1-credential/callback`,
|
||||||
|
|
|
@ -21,6 +21,10 @@ export class UrlService {
|
||||||
return urlBaseWebhook;
|
return urlBaseWebhook;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDomain() {
|
||||||
|
return process.env.DOMAIN ?? new URL(this.getInstanceBaseUrl()).hostname;
|
||||||
|
}
|
||||||
|
|
||||||
/** Return the n8n instance base URL without trailing slash */
|
/** Return the n8n instance base URL without trailing slash */
|
||||||
getInstanceBaseUrl(): string {
|
getInstanceBaseUrl(): string {
|
||||||
const n8nBaseUrl = config.getEnv('editorBaseUrl') || this.getWebhookBaseUrl();
|
const n8nBaseUrl = config.getEnv('editorBaseUrl') || this.getWebhookBaseUrl();
|
||||||
|
|
|
@ -202,7 +202,7 @@ export class UserManagementMailer {
|
||||||
|
|
||||||
private get basePayload() {
|
private get basePayload() {
|
||||||
const baseUrl = this.urlService.getInstanceBaseUrl();
|
const baseUrl = this.urlService.getInstanceBaseUrl();
|
||||||
const domain = new URL(baseUrl).hostname;
|
const domain = this.urlService.getDomain();
|
||||||
return { baseUrl, domain };
|
return { baseUrl, domain };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -971,7 +971,8 @@ export async function getBase(
|
||||||
currentNodeParameters?: INodeParameters,
|
currentNodeParameters?: INodeParameters,
|
||||||
executionTimeoutTimestamp?: number,
|
executionTimeoutTimestamp?: number,
|
||||||
): Promise<IWorkflowExecuteAdditionalData> {
|
): Promise<IWorkflowExecuteAdditionalData> {
|
||||||
const urlBaseWebhook = Container.get(UrlService).getWebhookBaseUrl();
|
const urlService = Container.get(UrlService);
|
||||||
|
const urlBaseWebhook = urlService.getWebhookBaseUrl();
|
||||||
|
|
||||||
const globalConfig = Container.get(GlobalConfig);
|
const globalConfig = Container.get(GlobalConfig);
|
||||||
|
|
||||||
|
@ -988,6 +989,7 @@ export async function getBase(
|
||||||
webhookBaseUrl: urlBaseWebhook + globalConfig.endpoints.webhook,
|
webhookBaseUrl: urlBaseWebhook + globalConfig.endpoints.webhook,
|
||||||
webhookWaitingBaseUrl: urlBaseWebhook + globalConfig.endpoints.webhookWaiting,
|
webhookWaitingBaseUrl: urlBaseWebhook + globalConfig.endpoints.webhookWaiting,
|
||||||
webhookTestBaseUrl: urlBaseWebhook + globalConfig.endpoints.webhookTest,
|
webhookTestBaseUrl: urlBaseWebhook + globalConfig.endpoints.webhookTest,
|
||||||
|
domain: urlService.getDomain(),
|
||||||
currentNodeParameters,
|
currentNodeParameters,
|
||||||
executionTimeoutTimestamp,
|
executionTimeoutTimestamp,
|
||||||
userId,
|
userId,
|
||||||
|
|
|
@ -4572,6 +4572,9 @@ export function getExecuteHookFunctions(
|
||||||
webhookData?.isTest,
|
webhookData?.isTest,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
getDomain(): string {
|
||||||
|
return additionalData.domain;
|
||||||
|
},
|
||||||
getWebhookName(): string {
|
getWebhookName(): string {
|
||||||
if (webhookData === undefined) {
|
if (webhookData === undefined) {
|
||||||
throw new ApplicationError('Only supported in webhook functions');
|
throw new ApplicationError('Only supported in webhook functions');
|
||||||
|
|
|
@ -64,6 +64,7 @@
|
||||||
"luxon": "catalog:",
|
"luxon": "catalog:",
|
||||||
"n8n-design-system": "workspace:*",
|
"n8n-design-system": "workspace:*",
|
||||||
"n8n-workflow": "workspace:*",
|
"n8n-workflow": "workspace:*",
|
||||||
|
"nanoid": "catalog:",
|
||||||
"pinia": "^2.2.4",
|
"pinia": "^2.2.4",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
"qrcode.vue": "^3.3.4",
|
"qrcode.vue": "^3.3.4",
|
||||||
|
|
|
@ -8,7 +8,7 @@ import type {
|
||||||
IVersionNotificationSettings,
|
IVersionNotificationSettings,
|
||||||
} from '@n8n/api-types';
|
} from '@n8n/api-types';
|
||||||
import type { Scope } from '@n8n/permissions';
|
import type { Scope } from '@n8n/permissions';
|
||||||
import type { IMenuItem, NodeCreatorTag } from 'n8n-design-system';
|
import type { NodeCreatorTag } from 'n8n-design-system';
|
||||||
import type {
|
import type {
|
||||||
GenericValue,
|
GenericValue,
|
||||||
IConnections,
|
IConnections,
|
||||||
|
@ -883,6 +883,7 @@ export interface RootState {
|
||||||
pushRef: string;
|
pushRef: string;
|
||||||
urlBaseWebhook: string;
|
urlBaseWebhook: string;
|
||||||
urlBaseEditor: string;
|
urlBaseEditor: string;
|
||||||
|
domain: string;
|
||||||
instanceId: string;
|
instanceId: string;
|
||||||
binaryDataMode: 'default' | 'filesystem' | 's3';
|
binaryDataMode: 'default' | 'filesystem' | 's3';
|
||||||
}
|
}
|
||||||
|
@ -890,51 +891,6 @@ export interface RootState {
|
||||||
export interface NodeMetadataMap {
|
export interface NodeMetadataMap {
|
||||||
[nodeName: string]: INodeMetadata;
|
[nodeName: string]: INodeMetadata;
|
||||||
}
|
}
|
||||||
export interface IRootState {
|
|
||||||
activeExecutions: IExecutionsCurrentSummaryExtended[];
|
|
||||||
activeWorkflows: string[];
|
|
||||||
activeActions: string[];
|
|
||||||
activeCredentialType: string | null;
|
|
||||||
baseUrl: string;
|
|
||||||
defaultLocale: string;
|
|
||||||
endpointForm: string;
|
|
||||||
endpointFormTest: string;
|
|
||||||
endpointFormWaiting: string;
|
|
||||||
endpointWebhook: string;
|
|
||||||
endpointWebhookTest: string;
|
|
||||||
endpointWebhookWaiting: string;
|
|
||||||
executionId: string | null;
|
|
||||||
executingNode: string[];
|
|
||||||
executionWaitingForWebhook: boolean;
|
|
||||||
pushConnectionActive: boolean;
|
|
||||||
saveDataErrorExecution: string;
|
|
||||||
saveDataSuccessExecution: string;
|
|
||||||
saveManualExecutions: boolean;
|
|
||||||
timezone: string;
|
|
||||||
stateIsDirty: boolean;
|
|
||||||
executionTimeout: number;
|
|
||||||
maxExecutionTimeout: number;
|
|
||||||
versionCli: string;
|
|
||||||
oauthCallbackUrls: object;
|
|
||||||
n8nMetadata: object;
|
|
||||||
workflowExecutionData: IExecutionResponse | null;
|
|
||||||
workflowExecutionPairedItemMappings: { [itemId: string]: Set<string> };
|
|
||||||
lastSelectedNode: string | null;
|
|
||||||
lastSelectedNodeOutputIndex: number | null;
|
|
||||||
nodeViewOffsetPosition: XYPosition;
|
|
||||||
nodeViewMoveInProgress: boolean;
|
|
||||||
selectedNodes: INodeUi[];
|
|
||||||
pushRef: string;
|
|
||||||
urlBaseEditor: string;
|
|
||||||
urlBaseWebhook: string;
|
|
||||||
workflow: IWorkflowDb;
|
|
||||||
workflowsById: IWorkflowsMap;
|
|
||||||
sidebarMenuItems: IMenuItem[];
|
|
||||||
instanceId: string;
|
|
||||||
nodeMetadata: NodeMetadataMap;
|
|
||||||
subworkflowExecutionError: Error | null;
|
|
||||||
binaryDataMode: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CommunityPackageMap {
|
export interface CommunityPackageMap {
|
||||||
[name: string]: PublicInstalledPackage;
|
[name: string]: PublicInstalledPackage;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { useToast } from '@/composables/useToast';
|
||||||
import {
|
import {
|
||||||
CHAT_TRIGGER_NODE_TYPE,
|
CHAT_TRIGGER_NODE_TYPE,
|
||||||
FORM_TRIGGER_NODE_TYPE,
|
FORM_TRIGGER_NODE_TYPE,
|
||||||
|
MAILHOOK_TRIGGER_NODE_TYPE,
|
||||||
OPEN_URL_PANEL_TRIGGER_NODE_TYPES,
|
OPEN_URL_PANEL_TRIGGER_NODE_TYPES,
|
||||||
PRODUCTION_ONLY_TRIGGER_NODE_TYPES,
|
PRODUCTION_ONLY_TRIGGER_NODE_TYPES,
|
||||||
} from '@/constants';
|
} from '@/constants';
|
||||||
|
@ -95,6 +96,18 @@ const baseText = computed(() => {
|
||||||
copyMessage: i18n.baseText('nodeWebhooks.showMessage.message.formTrigger'),
|
copyMessage: i18n.baseText('nodeWebhooks.showMessage.message.formTrigger'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
case MAILHOOK_TRIGGER_NODE_TYPE:
|
||||||
|
return {
|
||||||
|
toggleTitle: i18n.baseText('nodeWebhooks.webhookUrls.mailhookTrigger'),
|
||||||
|
clickToDisplay: i18n.baseText('nodeWebhooks.clickToDisplayWebhookUrls.mailhookTrigger'),
|
||||||
|
clickToHide: i18n.baseText('nodeWebhooks.clickToHideWebhookUrls.mailhookTrigger'),
|
||||||
|
clickToCopy: i18n.baseText('nodeWebhooks.clickToCopyWebhookUrls.mailhookTrigger'),
|
||||||
|
testUrl: i18n.baseText('nodeWebhooks.mailhook.testEmailId'),
|
||||||
|
productionUrl: i18n.baseText('nodeWebhooks.mailhook.productionEmailId'),
|
||||||
|
copyTitle: i18n.baseText('nodeWebhooks.showMessage.title.mailhookTrigger'),
|
||||||
|
copyMessage: i18n.baseText('nodeWebhooks.showMessage.message.mailhookTrigger'),
|
||||||
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
toggleTitle: i18n.baseText('nodeWebhooks.webhookUrls'),
|
toggleTitle: i18n.baseText('nodeWebhooks.webhookUrls'),
|
||||||
|
@ -127,6 +140,9 @@ function copyWebhookUrl(webhookData: IWebhookDescription): void {
|
||||||
|
|
||||||
function getWebhookUrlDisplay(webhookData: IWebhookDescription): string {
|
function getWebhookUrlDisplay(webhookData: IWebhookDescription): string {
|
||||||
if (props.node) {
|
if (props.node) {
|
||||||
|
if (props.node.type === MAILHOOK_TRIGGER_NODE_TYPE) {
|
||||||
|
return workflowHelpers.getMailhookEmailId(props.node);
|
||||||
|
}
|
||||||
return workflowHelpers.getWebhookUrl(
|
return workflowHelpers.getWebhookUrl(
|
||||||
webhookData,
|
webhookData,
|
||||||
props.node,
|
props.node,
|
||||||
|
|
|
@ -1085,7 +1085,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
|
||||||
|
|
||||||
function resolveNodeWebhook(node: INodeUi, nodeTypeDescription: INodeTypeDescription) {
|
function resolveNodeWebhook(node: INodeUi, nodeTypeDescription: INodeTypeDescription) {
|
||||||
if (nodeTypeDescription.webhooks?.length) {
|
if (nodeTypeDescription.webhooks?.length) {
|
||||||
node.webhookId = uuid();
|
nodeHelpers.assignWebhookId(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it's a webhook and the path is empty set the UUID as the default path
|
// if it's a webhook and the path is empty set the UUID as the default path
|
||||||
|
@ -1657,7 +1657,7 @@ export function useCanvasOperations({ router }: { router: ReturnType<typeof useR
|
||||||
(n) => n.webhookId === node.webhookId,
|
(n) => n.webhookId === node.webhookId,
|
||||||
);
|
);
|
||||||
if (isDuplicate) {
|
if (isDuplicate) {
|
||||||
node.webhookId = uuid();
|
nodeHelpers.assignWebhookId(node);
|
||||||
|
|
||||||
if (node.parameters.path) {
|
if (node.parameters.path) {
|
||||||
node.parameters.path = node.webhookId as string;
|
node.parameters.path = node.webhookId as string;
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import { ref, nextTick } from 'vue';
|
import { ref, nextTick } from 'vue';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
import { nanoid } from 'nanoid';
|
||||||
import { v4 as uuid } from 'uuid';
|
import { v4 as uuid } from 'uuid';
|
||||||
import type { Connection, ConnectionDetachedParams } from '@jsplumb/core';
|
import type { Connection, ConnectionDetachedParams } from '@jsplumb/core';
|
||||||
import { useHistoryStore } from '@/stores/history.store';
|
import { useHistoryStore } from '@/stores/history.store';
|
||||||
import {
|
import {
|
||||||
CUSTOM_API_CALL_KEY,
|
CUSTOM_API_CALL_KEY,
|
||||||
FORM_TRIGGER_NODE_TYPE,
|
FORM_TRIGGER_NODE_TYPE,
|
||||||
|
MAILHOOK_TRIGGER_NODE_TYPE,
|
||||||
NODE_OUTPUT_DEFAULT_KEY,
|
NODE_OUTPUT_DEFAULT_KEY,
|
||||||
PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
|
PLACEHOLDER_FILLED_AT_EXECUTION_TIME,
|
||||||
SPLIT_IN_BATCHES_NODE_TYPE,
|
SPLIT_IN_BATCHES_NODE_TYPE,
|
||||||
|
@ -1247,6 +1249,10 @@ export function useNodeHelpers() {
|
||||||
canvasStore.jsPlumbInstance?.setSuspendDrawing(false, true);
|
canvasStore.jsPlumbInstance?.setSuspendDrawing(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function assignWebhookId(node: INodeUi) {
|
||||||
|
node.webhookId = node.type === MAILHOOK_TRIGGER_NODE_TYPE ? nanoid(16).toLowerCase() : uuid();
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hasProxyAuth,
|
hasProxyAuth,
|
||||||
isCustomApiCallSelected,
|
isCustomApiCallSelected,
|
||||||
|
@ -1281,5 +1287,6 @@ export function useNodeHelpers() {
|
||||||
removeConnectionByConnectionInfo,
|
removeConnectionByConnectionInfo,
|
||||||
addPinDataConnections,
|
addPinDataConnections,
|
||||||
removePinDataConnections,
|
removePinDataConnections,
|
||||||
|
assignWebhookId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -706,6 +706,10 @@ export function useWorkflowHelpers(options: { router: ReturnType<typeof useRoute
|
||||||
return NodeHelpers.getNodeWebhookUrl(baseUrl, workflowId, node, path, isFullPath);
|
return NodeHelpers.getNodeWebhookUrl(baseUrl, workflowId, node, path, isFullPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMailhookEmailId(node: INodeUi) {
|
||||||
|
return `${node.webhookId}@${rootStore.domain}`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a copy of provided node parameters with added resolvedExpressionValue
|
* Returns a copy of provided node parameters with added resolvedExpressionValue
|
||||||
* @param nodeParameters
|
* @param nodeParameters
|
||||||
|
@ -1200,6 +1204,7 @@ export function useWorkflowHelpers(options: { router: ReturnType<typeof useRoute
|
||||||
getNodeDataToSave,
|
getNodeDataToSave,
|
||||||
getWebhookExpressionValue,
|
getWebhookExpressionValue,
|
||||||
getWebhookUrl,
|
getWebhookUrl,
|
||||||
|
getMailhookEmailId,
|
||||||
resolveExpression,
|
resolveExpression,
|
||||||
updateWorkflow,
|
updateWorkflow,
|
||||||
saveCurrentWorkflow,
|
saveCurrentWorkflow,
|
||||||
|
|
|
@ -196,6 +196,7 @@ export const SIMULATE_NODE_TYPE = 'n8n-nodes-base.simulate';
|
||||||
export const SIMULATE_TRIGGER_NODE_TYPE = 'n8n-nodes-base.simulateTrigger';
|
export const SIMULATE_TRIGGER_NODE_TYPE = 'n8n-nodes-base.simulateTrigger';
|
||||||
export const AI_TRANSFORM_NODE_TYPE = 'n8n-nodes-base.aiTransform';
|
export const AI_TRANSFORM_NODE_TYPE = 'n8n-nodes-base.aiTransform';
|
||||||
export const FORM_NODE_TYPE = 'n8n-nodes-base.form';
|
export const FORM_NODE_TYPE = 'n8n-nodes-base.form';
|
||||||
|
export const MAILHOOK_TRIGGER_NODE_TYPE = 'n8n-nodes-base.mailhook';
|
||||||
|
|
||||||
export const CREDENTIAL_ONLY_NODE_PREFIX = 'n8n-creds-base';
|
export const CREDENTIAL_ONLY_NODE_PREFIX = 'n8n-creds-base';
|
||||||
export const CREDENTIAL_ONLY_HTTP_NODE_VERSION = 4.1;
|
export const CREDENTIAL_ONLY_HTTP_NODE_VERSION = 4.1;
|
||||||
|
@ -227,11 +228,15 @@ export const OPEN_URL_PANEL_TRIGGER_NODE_TYPES = [
|
||||||
WEBHOOK_NODE_TYPE,
|
WEBHOOK_NODE_TYPE,
|
||||||
FORM_TRIGGER_NODE_TYPE,
|
FORM_TRIGGER_NODE_TYPE,
|
||||||
CHAT_TRIGGER_NODE_TYPE,
|
CHAT_TRIGGER_NODE_TYPE,
|
||||||
|
MAILHOOK_TRIGGER_NODE_TYPE,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const LIST_LIKE_NODE_OPERATIONS = ['getAll', 'getMany', 'read', 'search'];
|
export const LIST_LIKE_NODE_OPERATIONS = ['getAll', 'getMany', 'read', 'search'];
|
||||||
|
|
||||||
export const PRODUCTION_ONLY_TRIGGER_NODE_TYPES = [CHAT_TRIGGER_NODE_TYPE];
|
export const PRODUCTION_ONLY_TRIGGER_NODE_TYPES = [
|
||||||
|
CHAT_TRIGGER_NODE_TYPE,
|
||||||
|
MAILHOOK_TRIGGER_NODE_TYPE,
|
||||||
|
];
|
||||||
|
|
||||||
// Node creator
|
// Node creator
|
||||||
export const NODE_CREATOR_OPEN_SOURCES: Record<
|
export const NODE_CREATOR_OPEN_SOURCES: Record<
|
||||||
|
|
|
@ -1343,23 +1343,31 @@
|
||||||
"nodeWebhooks.clickToCopyWebhookUrls": "Click to copy webhook URLs",
|
"nodeWebhooks.clickToCopyWebhookUrls": "Click to copy webhook URLs",
|
||||||
"nodeWebhooks.clickToCopyWebhookUrls.formTrigger": "Click to copy Form URL",
|
"nodeWebhooks.clickToCopyWebhookUrls.formTrigger": "Click to copy Form URL",
|
||||||
"nodeWebhooks.clickToCopyWebhookUrls.chatTrigger": "Click to copy Chat URL",
|
"nodeWebhooks.clickToCopyWebhookUrls.chatTrigger": "Click to copy Chat URL",
|
||||||
|
"nodeWebhooks.clickToCopyWebhookUrls.mailhookTrigger": "Click to copy Email ID",
|
||||||
"nodeWebhooks.clickToDisplayWebhookUrls": "Click to display webhook URLs",
|
"nodeWebhooks.clickToDisplayWebhookUrls": "Click to display webhook URLs",
|
||||||
"nodeWebhooks.clickToDisplayWebhookUrls.formTrigger": "Click to display Form URL",
|
"nodeWebhooks.clickToDisplayWebhookUrls.formTrigger": "Click to display Form URL",
|
||||||
"nodeWebhooks.clickToDisplayWebhookUrls.chatTrigger": "Click to display Chat URL",
|
"nodeWebhooks.clickToDisplayWebhookUrls.chatTrigger": "Click to display Chat URL",
|
||||||
|
"nodeWebhooks.clickToDisplayWebhookUrls.mailhookTrigger": "Click to display Email IDs",
|
||||||
"nodeWebhooks.clickToHideWebhookUrls": "Click to hide webhook URLs",
|
"nodeWebhooks.clickToHideWebhookUrls": "Click to hide webhook URLs",
|
||||||
"nodeWebhooks.clickToHideWebhookUrls.formTrigger": "Click to hide Form URL",
|
"nodeWebhooks.clickToHideWebhookUrls.formTrigger": "Click to hide Form URL",
|
||||||
"nodeWebhooks.clickToHideWebhookUrls.chatTrigger": "Click to hide Chat URL",
|
"nodeWebhooks.clickToHideWebhookUrls.chatTrigger": "Click to hide Chat URL",
|
||||||
|
"nodeWebhooks.clickToHideWebhookUrls.mailhookTrigger": "Click to hide Mailhook Email IDs",
|
||||||
"nodeWebhooks.invalidExpression": "[INVALID EXPRESSION]",
|
"nodeWebhooks.invalidExpression": "[INVALID EXPRESSION]",
|
||||||
|
"nodeWebhooks.mailhook.testEmailId": "Test Email ID",
|
||||||
|
"nodeWebhooks.mailhook.productionEmailId": "Production Email ID",
|
||||||
"nodeWebhooks.productionUrl": "Production URL",
|
"nodeWebhooks.productionUrl": "Production URL",
|
||||||
"nodeWebhooks.showMessage.title": "URL copied",
|
"nodeWebhooks.showMessage.title": "URL copied",
|
||||||
"nodeWebhooks.showMessage.title.formTrigger": "Form URL copied",
|
"nodeWebhooks.showMessage.title.formTrigger": "Form URL copied",
|
||||||
"nodeWebhooks.showMessage.title.chatTrigger": "Chat URL copied",
|
"nodeWebhooks.showMessage.title.chatTrigger": "Chat URL copied",
|
||||||
|
"nodeWebhooks.showMessage.title.mailhookTrigger": "Mailhook Email ID copied",
|
||||||
"nodeWebhooks.showMessage.message.formTrigger": "Form submissions made via this URL will trigger the workflow when it's activated",
|
"nodeWebhooks.showMessage.message.formTrigger": "Form submissions made via this URL will trigger the workflow when it's activated",
|
||||||
"nodeWebhooks.showMessage.message.chatTrigger": "Chat submissions made via this URL will trigger the workflow when it's activated",
|
"nodeWebhooks.showMessage.message.chatTrigger": "Chat submissions made via this URL will trigger the workflow when it's activated",
|
||||||
|
"nodeWebhooks.showMessage.message.mailhookTrigger": "Incoming emails on this Email ID will trigger the workflow when it's activated",
|
||||||
"nodeWebhooks.testUrl": "Test URL",
|
"nodeWebhooks.testUrl": "Test URL",
|
||||||
"nodeWebhooks.webhookUrls": "Webhook URLs",
|
"nodeWebhooks.webhookUrls": "Webhook URLs",
|
||||||
"nodeWebhooks.webhookUrls.formTrigger": "Form URLs",
|
"nodeWebhooks.webhookUrls.formTrigger": "Form URLs",
|
||||||
"nodeWebhooks.webhookUrls.chatTrigger": "Chat URL",
|
"nodeWebhooks.webhookUrls.chatTrigger": "Chat URL",
|
||||||
|
"nodeWebhooks.webhookUrls.mailhookTrigger": "Mailhook Email IDs",
|
||||||
"onboardingWorkflow.stickyContent": "## 👇 Get started faster \nLightning tour of the key concepts [4 min] \n\n[![n8n quickstart video](/static/quickstart_thumbnail.png#full-width)](https://www.youtube.com/watch?v=1MwSoB0gnM4)",
|
"onboardingWorkflow.stickyContent": "## 👇 Get started faster \nLightning tour of the key concepts [4 min] \n\n[![n8n quickstart video](/static/quickstart_thumbnail.png#full-width)](https://www.youtube.com/watch?v=1MwSoB0gnM4)",
|
||||||
"openWorkflow.workflowImportError": "Could not import workflow",
|
"openWorkflow.workflowImportError": "Could not import workflow",
|
||||||
"openWorkflow.workflowNotFoundError": "Could not find workflow",
|
"openWorkflow.workflowNotFoundError": "Could not find workflow",
|
||||||
|
|
|
@ -30,6 +30,7 @@ export const useRootStore = defineStore(STORES.ROOT, () => {
|
||||||
pushRef: randomString(10).toLowerCase(),
|
pushRef: randomString(10).toLowerCase(),
|
||||||
urlBaseWebhook: 'http://localhost:5678/',
|
urlBaseWebhook: 'http://localhost:5678/',
|
||||||
urlBaseEditor: 'http://localhost:5678',
|
urlBaseEditor: 'http://localhost:5678',
|
||||||
|
domain: 'localhost',
|
||||||
instanceId: '',
|
instanceId: '',
|
||||||
binaryDataMode: 'default',
|
binaryDataMode: 'default',
|
||||||
});
|
});
|
||||||
|
@ -54,6 +55,8 @@ export const useRootStore = defineStore(STORES.ROOT, () => {
|
||||||
() => `${state.value.urlBaseEditor}${state.value.endpointWebhookWaiting}`,
|
() => `${state.value.urlBaseEditor}${state.value.endpointWebhookWaiting}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const domain = computed(() => state.value.domain);
|
||||||
|
|
||||||
const pushRef = computed(() => state.value.pushRef);
|
const pushRef = computed(() => state.value.pushRef);
|
||||||
|
|
||||||
const binaryDataMode = computed(() => state.value.binaryDataMode);
|
const binaryDataMode = computed(() => state.value.binaryDataMode);
|
||||||
|
@ -142,6 +145,10 @@ export const useRootStore = defineStore(STORES.ROOT, () => {
|
||||||
state.value.endpointWebhookWaiting = endpointWebhookWaiting;
|
state.value.endpointWebhookWaiting = endpointWebhookWaiting;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setDomain = (domain: string) => {
|
||||||
|
state.value.domain = domain;
|
||||||
|
};
|
||||||
|
|
||||||
const setTimezone = (timezone: string) => {
|
const setTimezone = (timezone: string) => {
|
||||||
state.value.timezone = timezone;
|
state.value.timezone = timezone;
|
||||||
setGlobalState({ defaultTimezone: timezone });
|
setGlobalState({ defaultTimezone: timezone });
|
||||||
|
@ -189,6 +196,7 @@ export const useRootStore = defineStore(STORES.ROOT, () => {
|
||||||
webhookUrl,
|
webhookUrl,
|
||||||
webhookTestUrl,
|
webhookTestUrl,
|
||||||
webhookWaitingUrl,
|
webhookWaitingUrl,
|
||||||
|
domain,
|
||||||
restUrl,
|
restUrl,
|
||||||
restCloudApiContext,
|
restCloudApiContext,
|
||||||
restApiContext,
|
restApiContext,
|
||||||
|
@ -213,6 +221,7 @@ export const useRootStore = defineStore(STORES.ROOT, () => {
|
||||||
setEndpointWebhook,
|
setEndpointWebhook,
|
||||||
setEndpointWebhookTest,
|
setEndpointWebhookTest,
|
||||||
setEndpointWebhookWaiting,
|
setEndpointWebhookWaiting,
|
||||||
|
setDomain,
|
||||||
setTimezone,
|
setTimezone,
|
||||||
setExecutionTimeout,
|
setExecutionTimeout,
|
||||||
setMaxExecutionTimeout,
|
setMaxExecutionTimeout,
|
||||||
|
|
|
@ -243,6 +243,7 @@ export const useSettingsStore = defineStore(STORES.SETTINGS, () => {
|
||||||
rootStore.setEndpointWebhook(fetchedSettings.endpointWebhook);
|
rootStore.setEndpointWebhook(fetchedSettings.endpointWebhook);
|
||||||
rootStore.setEndpointWebhookTest(fetchedSettings.endpointWebhookTest);
|
rootStore.setEndpointWebhookTest(fetchedSettings.endpointWebhookTest);
|
||||||
rootStore.setEndpointWebhookWaiting(fetchedSettings.endpointWebhookWaiting);
|
rootStore.setEndpointWebhookWaiting(fetchedSettings.endpointWebhookWaiting);
|
||||||
|
rootStore.setDomain(fetchedSettings.domain);
|
||||||
rootStore.setTimezone(fetchedSettings.timezone);
|
rootStore.setTimezone(fetchedSettings.timezone);
|
||||||
rootStore.setExecutionTimeout(fetchedSettings.executionTimeout);
|
rootStore.setExecutionTimeout(fetchedSettings.executionTimeout);
|
||||||
rootStore.setMaxExecutionTimeout(fetchedSettings.maxExecutionTimeout);
|
rootStore.setMaxExecutionTimeout(fetchedSettings.maxExecutionTimeout);
|
||||||
|
|
|
@ -1923,7 +1923,7 @@ export default defineComponent({
|
||||||
this.workflowHelpers.getCurrentWorkflow().nodes,
|
this.workflowHelpers.getCurrentWorkflow().nodes,
|
||||||
).some((n) => n.webhookId === node.webhookId);
|
).some((n) => n.webhookId === node.webhookId);
|
||||||
if (isDuplicate) {
|
if (isDuplicate) {
|
||||||
node.webhookId = uuid();
|
this.nodeHelpers.assignWebhookId(node);
|
||||||
|
|
||||||
if (node.parameters.path) {
|
if (node.parameters.path) {
|
||||||
node.parameters.path = node.webhookId as string;
|
node.parameters.path = node.webhookId as string;
|
||||||
|
@ -2389,7 +2389,7 @@ export default defineComponent({
|
||||||
newNodeData.name = this.uniqueNodeName(localizedName);
|
newNodeData.name = this.uniqueNodeName(localizedName);
|
||||||
|
|
||||||
if (nodeTypeData.webhooks?.length) {
|
if (nodeTypeData.webhooks?.length) {
|
||||||
newNodeData.webhookId = uuid();
|
this.nodeHelpers.assignWebhookId(newNodeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.nodeHelpers.addNodes([newNodeData], undefined, trackHistory);
|
await this.nodeHelpers.addNodes([newNodeData], undefined, trackHistory);
|
||||||
|
|
87
packages/nodes-base/nodes/Mailhook/Mailhook.node.ts
Normal file
87
packages/nodes-base/nodes/Mailhook/Mailhook.node.ts
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import {
|
||||||
|
IDataObject,
|
||||||
|
IWebhookFunctions,
|
||||||
|
INodeTypeDescription,
|
||||||
|
INodeType,
|
||||||
|
IWebhookResponseData,
|
||||||
|
NodeConnectionType,
|
||||||
|
IHookFunctions,
|
||||||
|
} from 'n8n-workflow';
|
||||||
|
|
||||||
|
// TODO: this should be picked up from a config object
|
||||||
|
const baseUrl = 'http://localhost:8080/rest/mailhooks';
|
||||||
|
const apiRequest = async (
|
||||||
|
context: IHookFunctions,
|
||||||
|
operation: 'checkExists' | 'create' | 'delete',
|
||||||
|
) => {
|
||||||
|
const { webhookId } = context.getNode();
|
||||||
|
const domain = context.getDomain();
|
||||||
|
const method = operation === 'checkExists' ? 'GET' : operation === 'create' ? 'POST' : 'DELETE';
|
||||||
|
let url = baseUrl;
|
||||||
|
if (operation !== 'create') {
|
||||||
|
url += `/${webhookId}@${domain}`;
|
||||||
|
}
|
||||||
|
const response = await context.helpers.httpRequest({
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
returnFullResponse: true,
|
||||||
|
ignoreHttpStatusErrors: true,
|
||||||
|
});
|
||||||
|
return response.statusCode === 204;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Mailhook implements INodeType {
|
||||||
|
description: INodeTypeDescription = {
|
||||||
|
displayName: 'Mailhook',
|
||||||
|
name: 'mailhook',
|
||||||
|
icon: 'file:mailhook.svg',
|
||||||
|
group: ['trigger'],
|
||||||
|
version: 1,
|
||||||
|
description: 'Start a workflow on a Mailhook trigger',
|
||||||
|
defaults: {
|
||||||
|
name: 'Mailhook',
|
||||||
|
color: '#4363AE',
|
||||||
|
},
|
||||||
|
inputs: [],
|
||||||
|
outputs: [NodeConnectionType.Main],
|
||||||
|
webhooks: [
|
||||||
|
{
|
||||||
|
name: 'default',
|
||||||
|
httpMethod: 'POST',
|
||||||
|
responseMode: 'onReceived',
|
||||||
|
path: 'webhook',
|
||||||
|
ndvHideMethod: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
properties: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
webhookMethods = {
|
||||||
|
default: {
|
||||||
|
async checkExists(this: IHookFunctions): Promise<boolean> {
|
||||||
|
return await apiRequest(this, 'checkExists');
|
||||||
|
},
|
||||||
|
async create(this: IHookFunctions): Promise<boolean> {
|
||||||
|
return await apiRequest(this, 'create');
|
||||||
|
},
|
||||||
|
async delete(this: IHookFunctions): Promise<boolean> {
|
||||||
|
return await apiRequest(this, 'delete');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
|
||||||
|
// The data to return and so start the workflow with
|
||||||
|
const returnData: IDataObject[] = [];
|
||||||
|
returnData.push({
|
||||||
|
headers: this.getHeaderData(),
|
||||||
|
params: this.getParamsData(),
|
||||||
|
query: this.getQueryData(),
|
||||||
|
body: this.getBodyData(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
workflowData: [this.helpers.returnJsonArray(returnData)],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
4
packages/nodes-base/nodes/Mailhook/mailhook.svg
Normal file
4
packages/nodes-base/nodes/Mailhook/mailhook.svg
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
|
||||||
|
<path d="M 7.885 9.683 L 20.885 19.683 L 33.885 9.683" stroke="#727db5" stroke-width="2" fill="transparent"/>
|
||||||
|
<path d="M 8.011 21.45 L 21.011 31.45 L 34.011 21.45" stroke="#727db5" stroke-width="2" fill="transparent"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 314 B |
|
@ -604,6 +604,7 @@
|
||||||
"dist/nodes/Mailcheck/Mailcheck.node.js",
|
"dist/nodes/Mailcheck/Mailcheck.node.js",
|
||||||
"dist/nodes/Mailchimp/Mailchimp.node.js",
|
"dist/nodes/Mailchimp/Mailchimp.node.js",
|
||||||
"dist/nodes/Mailchimp/MailchimpTrigger.node.js",
|
"dist/nodes/Mailchimp/MailchimpTrigger.node.js",
|
||||||
|
"dist/nodes/Mailhook/Mailhook.node.js",
|
||||||
"dist/nodes/MailerLite/MailerLite.node.js",
|
"dist/nodes/MailerLite/MailerLite.node.js",
|
||||||
"dist/nodes/MailerLite/MailerLiteTrigger.node.js",
|
"dist/nodes/MailerLite/MailerLiteTrigger.node.js",
|
||||||
"dist/nodes/Mailgun/Mailgun.node.js",
|
"dist/nodes/Mailgun/Mailgun.node.js",
|
||||||
|
|
|
@ -1111,6 +1111,7 @@ export interface IHookFunctions
|
||||||
getWebhookName(): string;
|
getWebhookName(): string;
|
||||||
getWebhookDescription(name: string): IWebhookDescription | undefined;
|
getWebhookDescription(name: string): IWebhookDescription | undefined;
|
||||||
getNodeWebhookUrl: (name: string) => string | undefined;
|
getNodeWebhookUrl: (name: string) => string | undefined;
|
||||||
|
getDomain(): string;
|
||||||
getNodeParameter(
|
getNodeParameter(
|
||||||
parameterName: string,
|
parameterName: string,
|
||||||
fallbackValue?: any,
|
fallbackValue?: any,
|
||||||
|
@ -2306,6 +2307,7 @@ export interface IWorkflowExecuteAdditionalData {
|
||||||
webhookBaseUrl: string;
|
webhookBaseUrl: string;
|
||||||
webhookWaitingBaseUrl: string;
|
webhookWaitingBaseUrl: string;
|
||||||
webhookTestBaseUrl: string;
|
webhookTestBaseUrl: string;
|
||||||
|
domain: string;
|
||||||
currentNodeParameters?: INodeParameters;
|
currentNodeParameters?: INodeParameters;
|
||||||
executionTimeoutTimestamp?: number;
|
executionTimeoutTimestamp?: number;
|
||||||
userId?: string;
|
userId?: string;
|
||||||
|
|
|
@ -1444,6 +1444,9 @@ importers:
|
||||||
n8n-workflow:
|
n8n-workflow:
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../workflow
|
version: link:../workflow
|
||||||
|
nanoid:
|
||||||
|
specifier: 'catalog:'
|
||||||
|
version: 3.3.6
|
||||||
pinia:
|
pinia:
|
||||||
specifier: ^2.2.4
|
specifier: ^2.2.4
|
||||||
version: 2.2.4(typescript@5.6.2)(vue@3.5.11(typescript@5.6.2))
|
version: 2.2.4(typescript@5.6.2)(vue@3.5.11(typescript@5.6.2))
|
||||||
|
|
Loading…
Reference in a new issue