From 873029438f0cc5d78ae8b65ab5dad65a00d9e9ef Mon Sep 17 00:00:00 2001 From: Omar Ajoue Date: Wed, 21 Jul 2021 14:30:38 +0200 Subject: [PATCH 01/40] Fixed the spreadysheet nodes for saving dates --- packages/nodes-base/nodes/SpreadsheetFile.node.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/nodes-base/nodes/SpreadsheetFile.node.ts b/packages/nodes-base/nodes/SpreadsheetFile.node.ts index b87dbde1a..fcf972de6 100644 --- a/packages/nodes-base/nodes/SpreadsheetFile.node.ts +++ b/packages/nodes-base/nodes/SpreadsheetFile.node.ts @@ -31,6 +31,10 @@ function flattenObject(data: IDataObject) { const returnData: IDataObject = {}; for (const key1 of Object.keys(data)) { if (data[key1] !== null && (typeof data[key1]) === 'object') { + if (data[key1] instanceof Date) { + returnData[key1] = data[key1]?.toString(); + continue; + } const flatObject = flattenObject(data[key1] as IDataObject); for (const key2 in flatObject) { if (flatObject[key2] === undefined) { From 073e5e24cbd64b06e77e22ff42eb27f666ff47b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 22 Jul 2021 08:10:26 +0200 Subject: [PATCH 02/40] :sparkles Add Event Filter in Taiga trigger node (#2011) * :zap: Filter events by action and type * :zap: Small change * :zap: Improvements * :zap: Minor improvements Co-authored-by: ricardo Co-authored-by: Jan Oberhauser --- .../nodes/Taiga/TaigaTrigger.node.ts | 97 ++++++++++++++++--- packages/nodes-base/nodes/Taiga/types.d.ts | 12 +++ 2 files changed, 97 insertions(+), 12 deletions(-) diff --git a/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts b/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts index a01ff66b4..0a63f51ae 100644 --- a/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts +++ b/packages/nodes-base/nodes/Taiga/TaigaTrigger.node.ts @@ -63,6 +63,70 @@ export class TaigaTrigger implements INodeType { description: 'Project ID', required: true, }, + { + displayName: 'Resources', + name: 'resources', + type: 'multiOptions', + required: true, + default: [ + 'all', + ], + options: [ + { + name: 'All', + value: 'all', + }, + { + name: 'Issue', + value: 'issue', + }, + { + name: 'Milestone (Sprint)', + value: 'milestone', + }, + { + name: 'Task', + value: 'task', + }, + { + name: 'User Story', + value: 'userstory', + }, + { + name: 'Wikipage', + value: 'wikipage', + }, + ], + description: 'Resources to listen to', + }, + { + displayName: 'Operations', + name: 'operations', + type: 'multiOptions', + required: true, + default: [ + 'all', + ], + description: 'Operations to listen to', + options: [ + { + name: 'All', + value: 'all', + }, + { + name: 'Create', + value: 'create', + }, + { + name: 'Delete', + value: 'delete', + }, + { + name: 'Update', + value: 'change', + }, + ], + }, ], }; @@ -125,7 +189,7 @@ export class TaigaTrigger implements INodeType { const body: IDataObject = { name: `n8n-webhook:${webhookUrl}`, url: webhookUrl, - key, //can't validate the secret, see: https://github.com/taigaio/taiga-back/issues/1031 + key, project: projectId, }; const { id } = await taigaApiRequest.call(this, 'POST', '/webhooks', body); @@ -150,25 +214,34 @@ export class TaigaTrigger implements INodeType { }; async webhook(this: IWebhookFunctions): Promise { - //const webhookData = this.getWorkflowStaticData('node'); - const req = this.getRequestObject(); - const bodyData = req.body; - //const headerData = this.getHeaderData(); + const body = this.getRequestObject().body as WebhookPayload; + const operations = this.getNodeParameter('operations', []) as Operations[]; + const resources = this.getNodeParameter('resources', []) as Resources[]; - // TODO - // Validate signature + if (!operations.includes('all') && !operations.includes(body.action)) { + return {}; + } + + if (!resources.includes('all') && !resources.includes(body.type)) { + return {}; + } + + // TODO: Signature does not match payload hash // https://github.com/taigaio/taiga-back/issues/1031 - // //@ts-ignore - // const requestSignature: string = headerData['x-taiga-webhook-signature']; + // const webhookData = this.getWorkflowStaticData('node'); + // const headerData = this.getHeaderData(); + + // // @ts-ignore + // const requestSignature = headerData['x-taiga-webhook-signature']; + // console.log(requestSignature); // if (requestSignature === undefined) { // return {}; // } - // //@ts-ignore - // const computedSignature = createHmac('sha1', webhookData.key as string).update(JSON.stringify(bodyData)).digest('hex'); + // const computedSignature = createHmac('sha1', webhookData.key as string).update(JSON.stringify(body)).digest('hex'); // if (requestSignature !== computedSignature) { // return {}; @@ -176,7 +249,7 @@ export class TaigaTrigger implements INodeType { return { workflowData: [ - this.helpers.returnJsonArray(bodyData), + this.helpers.returnJsonArray(body), ], }; } diff --git a/packages/nodes-base/nodes/Taiga/types.d.ts b/packages/nodes-base/nodes/Taiga/types.d.ts index 4eafdbbe9..49c5f0e0f 100644 --- a/packages/nodes-base/nodes/Taiga/types.d.ts +++ b/packages/nodes-base/nodes/Taiga/types.d.ts @@ -22,3 +22,15 @@ type LoadedEpic = LoadedUserStory; type LoadedTags = { [tagName: string]: string | null; // hex color } + +type Operations = 'all' | 'create' | 'delete' | 'change'; + +type Resources = 'all' | 'issue' | 'milestone' | 'task' | 'userstory' | 'wikipage'; + +type WebhookPayload = { + action: Operations; + type: Resources; + by: Record; + date: string; + data: Record; +} From 98ec23544b6fe57e3bf9d5b19b8f55c7432eba21 Mon Sep 17 00:00:00 2001 From: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com> Date: Thu, 22 Jul 2021 10:22:17 +0200 Subject: [PATCH 03/40] :sparkles: Add new version notification (#1977) * add menu item * implement versions modal * fix up modal * clean up badges * implement key features * fix up spacing * add error message * add notification icon * fix notification * fix bug when no updates * address lint issues * address multi line nodes * add closing animation * keep drawer open * address design feedback * address badge styling * use grid for icons * update cli to return version information * set env variables * add scss color variables * address comments * fix lint issue * handle edge cases * update scss variables, spacing * update spacing * build * override top value for theme * bolden version * update config * check endpoint exists * handle long names * set dates * set title * fix bug * update warning * remove unused component * refactor components * add fragments * inherit styles * fix icon size * fix lint issues * add cli dep * address comments * handle network error * address empty case * Revert "address comments" 480f969e07c3282c50bc326babbc5812baac5dae * remove dependency * build * update variable names * update variable names * refactor verion card * split out variables * clean up impl * clean up scss * move from nodeview * refactor out gift notification icon * fix lint issues * clean up variables * update scss variables * update info url * Add instanceId to frontendSettings * Use createHash from crypto module * Add instanceId to store & send it as http header * Fix lintings * Fix interfaces & apply review changes * Apply review changes * add console message * update text info * update endpoint * clean up interface * address comments * cleanup todo * Update packages/cli/config/index.ts Co-authored-by: Ben Hesseldieck <1849459+BHesseldieck@users.noreply.github.com> * update console message * :zap: Display node-name on hover * :zap: Formatting fix Co-authored-by: MedAliMarz Co-authored-by: Ben Hesseldieck <1849459+BHesseldieck@users.noreply.github.com> Co-authored-by: Jan Oberhauser --- packages/cli/commands/start.ts | 3 + packages/cli/config/index.ts | 21 +++ packages/cli/src/Interfaces.ts | 7 + packages/cli/src/Server.ts | 16 ++- packages/editor-ui/package.json | 4 +- packages/editor-ui/src/Interface.ts | 38 +++++ packages/editor-ui/src/api/helpers.ts | 5 +- packages/editor-ui/src/api/versions.ts | 9 ++ packages/editor-ui/src/components/Badge.vue | 51 +++++++ .../src/components/GiftNotificationIcon.vue | 36 +++++ .../editor-ui/src/components/MainSidebar.vue | 50 +++++++ packages/editor-ui/src/components/Modal.vue | 45 +++++- .../editor-ui/src/components/ModalRoot.vue | 6 +- packages/editor-ui/src/components/Modals.vue | 13 +- packages/editor-ui/src/components/Node.vue | 7 +- .../src/components/NodeCreator/NodeItem.vue | 2 +- .../editor-ui/src/components/NodeIcon.vue | 17 ++- packages/editor-ui/src/components/TimeAgo.vue | 15 ++ .../editor-ui/src/components/UpdatesPanel.vue | 122 ++++++++++++++++ .../editor-ui/src/components/VersionCard.vue | 132 ++++++++++++++++++ .../src/components/WarningTooltip.vue | 15 ++ .../src/components/mixins/newVersions.ts | 40 ++++++ .../src/components/mixins/showMessage.ts | 26 +++- packages/editor-ui/src/constants.ts | 4 + packages/editor-ui/src/main.ts | 4 + packages/editor-ui/src/modules/ui.ts | 11 +- packages/editor-ui/src/modules/versions.ts | 62 ++++++++ .../editor-ui/src/n8n-theme-variables.scss | 40 ++++++ packages/editor-ui/src/store.ts | 8 +- packages/editor-ui/src/views/NodeView.vue | 9 ++ 30 files changed, 793 insertions(+), 25 deletions(-) create mode 100644 packages/editor-ui/src/api/versions.ts create mode 100644 packages/editor-ui/src/components/Badge.vue create mode 100644 packages/editor-ui/src/components/GiftNotificationIcon.vue create mode 100644 packages/editor-ui/src/components/TimeAgo.vue create mode 100644 packages/editor-ui/src/components/UpdatesPanel.vue create mode 100644 packages/editor-ui/src/components/VersionCard.vue create mode 100644 packages/editor-ui/src/components/WarningTooltip.vue create mode 100644 packages/editor-ui/src/components/mixins/newVersions.ts create mode 100644 packages/editor-ui/src/modules/versions.ts diff --git a/packages/cli/commands/start.ts b/packages/cli/commands/start.ts index 023e3e827..bb6e64f56 100644 --- a/packages/cli/commands/start.ts +++ b/packages/cli/commands/start.ts @@ -142,6 +142,9 @@ export class Start extends Command { LoggerProxy.init(logger); logger.info('Initializing n8n process'); + // todo remove a few versions after release + logger.info('\nn8n now checks for new versions and security updates. You can turn this off using the environment variable N8N_VERSION_NOTIFICATIONS_ENABLED to "false"\nFor more information, please refer to https://docs.n8n.io/getting-started/installation/advanced/configuration.html\n'); + // Start directly with the init of the database to improve startup time const startDbInitPromise = Db.init().catch((error: Error) => { logger.error(`There was an error initializing DB: "${error.message}"`); diff --git a/packages/cli/config/index.ts b/packages/cli/config/index.ts index 0da11e786..476cbbcfa 100644 --- a/packages/cli/config/index.ts +++ b/packages/cli/config/index.ts @@ -618,6 +618,27 @@ const config = convict({ }, }, + versionNotifications: { + enabled: { + doc: 'Whether feature is enabled to request notifications about new versions and security updates.', + format: Boolean, + default: true, + env: 'N8N_VERSION_NOTIFICATIONS_ENABLED', + }, + endpoint: { + doc: 'Endpoint to retrieve version information from.', + format: String, + default: 'https://api.n8n.io/versions/', + env: 'N8N_VERSION_NOTIFICATIONS_ENDPOINT', + }, + infoUrl: { + doc: `Url in New Versions Panel with more information on updating one's instance.`, + format: String, + default: 'https://docs.n8n.io/getting-started/installation/updating.html', + env: 'N8N_VERSION_NOTIFICATIONS_INFO_URL', + }, + }, + }); // Overwrite default configuration with settings which got defined in diff --git a/packages/cli/src/Interfaces.ts b/packages/cli/src/Interfaces.ts index e1c09cf55..d2aff0eb8 100644 --- a/packages/cli/src/Interfaces.ts +++ b/packages/cli/src/Interfaces.ts @@ -312,6 +312,11 @@ export interface IN8nConfigNodes { exclude: string[]; } +export interface IVersionNotificationSettings { + enabled: boolean; + endpoint: string; + infoUrl: string; +} export interface IN8nUISettings { endpointWebhook: string; @@ -331,6 +336,8 @@ export interface IN8nUISettings { n8nMetadata?: { [key: string]: string | number | undefined; }; + versionNotifications: IVersionNotificationSettings; + instanceId: string; } export interface IPackageVersions { diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index b36a0fe0f..0abd3f60f 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -21,7 +21,7 @@ import * as clientOAuth1 from 'oauth-1.0a'; import { RequestOptions } from 'oauth-1.0a'; import * as csrf from 'csrf'; import * as requestPromise from 'request-promise-native'; -import { createHmac } from 'crypto'; +import { createHash, createHmac } from 'crypto'; // IMPORTANT! Do not switch to anther bcrypt library unless really necessary and // tested with all possible systems like Windows, Alpine on ARM, FreeBSD, ... import { compare } from 'bcryptjs'; @@ -196,6 +196,12 @@ class App { 'oauth1': urlBaseWebhook + `${this.restEndpoint}/oauth1-credential/callback`, 'oauth2': urlBaseWebhook + `${this.restEndpoint}/oauth2-credential/callback`, }, + versionNotifications: { + enabled: config.get('versionNotifications.enabled'), + endpoint: config.get('versionNotifications.endpoint'), + infoUrl: config.get('versionNotifications.infoUrl'), + }, + instanceId: '', }; } @@ -225,6 +231,7 @@ class App { this.versions = await GenericHelpers.getVersions(); this.frontendSettings.versionCli = this.versions.cli; + this.frontendSettings.instanceId = await generateInstanceId() as string; await this.externalHooks.run('frontend.settings', [this.frontendSettings]); @@ -2210,3 +2217,10 @@ async function getExecutionsCount(countFilter: IDataObject): Promise<{ count: nu const count = await Db.collections.Execution!.count(countFilter); return { count, estimate: false }; } + +async function generateInstanceId() { + const encryptionKey = await UserSettings.getEncryptionKey(); + const hash = encryptionKey ? createHash('sha256').update(encryptionKey.slice(Math.round(encryptionKey.length / 2))).digest('hex') : undefined; + + return hash; +} diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index 4486c5c02..0acc89556 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -25,7 +25,9 @@ "test:unit": "vue-cli-service test:unit" }, "dependencies": { - "v-click-outside": "^3.1.2" + "timeago.js": "^4.0.2", + "v-click-outside": "^3.1.2", + "vue-fragment": "^1.5.2" }, "devDependencies": { "@beyonk/google-fonts-webpack-plugin": "^1.5.0", diff --git a/packages/editor-ui/src/Interface.ts b/packages/editor-ui/src/Interface.ts index cc3f89b8c..6151d7e37 100644 --- a/packages/editor-ui/src/Interface.ts +++ b/packages/editor-ui/src/Interface.ts @@ -445,6 +445,12 @@ export interface IPushDataConsoleMessage { message: string; } +export interface IVersionNotificationSettings { + enabled: boolean; + endpoint: string; + infoUrl: string; +} + export interface IN8nUISettings { endpointWebhook: string; endpointWebhookTest: string; @@ -463,6 +469,8 @@ export interface IN8nUISettings { n8nMetadata?: { [key: string]: string | number | undefined; }; + versionNotifications: IVersionNotificationSettings; + instanceId: string; } export interface IWorkflowSettings extends IWorkflowSettingsWorkflow { @@ -547,6 +555,29 @@ export interface ITagRow { delete?: boolean; } +export interface IVersion { + name: string; + nodes: IVersionNode[]; + createdAt: string; + description: string; + documentationUrl: string; + hasBreakingChange: boolean; + hasSecurityFix: boolean; + hasSecurityIssue: boolean; + securityIssueFixVersion: string; +} + +export interface IVersionNode { + name: string; + displayName: string; + icon: string; + defaults: INodeParameters; + iconData: { + type: string; + icon?: string; + fileBuffer?: string; + }; +} export interface IRootState { activeExecutions: IExecutionsCurrentSummaryExtended[]; activeWorkflows: string[]; @@ -583,6 +614,7 @@ export interface IRootState { urlBaseWebhook: string; workflow: IWorkflowDb; sidebarMenuItems: IMenuItem[]; + instanceId: string; } export interface ITagsState { @@ -605,6 +637,12 @@ export interface IUiState { isPageLoading: boolean; } +export interface IVersionsState { + versionNotificationSettings: IVersionNotificationSettings; + nextVersions: IVersion[]; + currentVersion: IVersion | undefined; +} + export interface IWorkflowsState { } diff --git a/packages/editor-ui/src/api/helpers.ts b/packages/editor-ui/src/api/helpers.ts index 739242c12..1ae753db1 100644 --- a/packages/editor-ui/src/api/helpers.ts +++ b/packages/editor-ui/src/api/helpers.ts @@ -6,7 +6,6 @@ import { IRestApiContext, } from '../Interface'; - class ResponseError extends Error { // The HTTP status code of response httpStatusCode?: number; @@ -91,6 +90,6 @@ export async function makeRestApiRequest(context: IRestApiContext, method: Metho return response.data; } -export async function get(baseURL: string, endpoint: string, params?: IDataObject) { - return await request({method: 'GET', baseURL, endpoint, data: params}); +export async function get(baseURL: string, endpoint: string, params?: IDataObject, headers?: IDataObject) { + return await request({method: 'GET', baseURL, endpoint, headers, data: params}); } diff --git a/packages/editor-ui/src/api/versions.ts b/packages/editor-ui/src/api/versions.ts new file mode 100644 index 000000000..009f6bc4b --- /dev/null +++ b/packages/editor-ui/src/api/versions.ts @@ -0,0 +1,9 @@ +import { IVersion } from '@/Interface'; +import { INSTANCE_ID_HEADER } from '@/constants'; +import { IDataObject } from 'n8n-workflow'; +import { get } from './helpers'; + +export async function getNextVersions(endpoint: string, version: string, instanceId: string): Promise { + const headers = {[INSTANCE_ID_HEADER as string] : instanceId}; + return await get(endpoint, version, {}, headers); +} diff --git a/packages/editor-ui/src/components/Badge.vue b/packages/editor-ui/src/components/Badge.vue new file mode 100644 index 000000000..22fa536c7 --- /dev/null +++ b/packages/editor-ui/src/components/Badge.vue @@ -0,0 +1,51 @@ + + + + + \ No newline at end of file diff --git a/packages/editor-ui/src/components/GiftNotificationIcon.vue b/packages/editor-ui/src/components/GiftNotificationIcon.vue new file mode 100644 index 000000000..e10fa9c5a --- /dev/null +++ b/packages/editor-ui/src/components/GiftNotificationIcon.vue @@ -0,0 +1,36 @@ + + + \ No newline at end of file diff --git a/packages/editor-ui/src/components/MainSidebar.vue b/packages/editor-ui/src/components/MainSidebar.vue index cadabe788..a8d4d14fc 100644 --- a/packages/editor-ui/src/components/MainSidebar.vue +++ b/packages/editor-ui/src/components/MainSidebar.vue @@ -127,6 +127,14 @@ + @@ -149,6 +157,7 @@ import About from '@/components/About.vue'; import CredentialsEdit from '@/components/CredentialsEdit.vue'; import CredentialsList from '@/components/CredentialsList.vue'; import ExecutionsList from '@/components/ExecutionsList.vue'; +import GiftNotificationIcon from './GiftNotificationIcon.vue'; import WorkflowSettings from '@/components/WorkflowSettings.vue'; import { genericHelpers } from '@/components/mixins/genericHelpers'; @@ -212,6 +221,7 @@ export default mixins( CredentialsEdit, CredentialsList, ExecutionsList, + GiftNotificationIcon, WorkflowSettings, MenuItemsIterator, }, @@ -232,6 +242,10 @@ export default mixins( ...mapGetters('ui', { isCollapsed: 'sidebarMenuCollapsed', }), + ...mapGetters('versions', [ + 'hasVersionUpdates', + 'nextVersions', + ]), exeuctionId (): string | undefined { return this.$route.params.id; }, @@ -311,6 +325,9 @@ export default mixins( openTagManager() { this.$store.dispatch('ui/openTagsManagerModal'); }, + openUpdatesPanel() { + this.$store.dispatch('ui/openUpdatesPanel'); + }, async stopExecution () { const executionId = this.$store.getters.activeExecutionId; if (executionId === null) { @@ -574,6 +591,39 @@ a.logo { &.expanded { width: $--sidebar-expanded-width; } + + ul { + display: flex; + flex-direction: column; + } +} + +.footer-menu-items { + display: flex; + flex-grow: 1; + flex-direction: column; + justify-content: flex-end; + padding-bottom: 32px; +} + +.el-menu-item.updates { + color: $--sidebar-inactive-color; + .item-title-root { + font-size: 13px; + top: 0 !important; + } + + &:hover { + color: $--sidebar-active-color; + } + + .gift-container { + display: flex; + justify-content: flex-start; + align-items: center; + height: 100%; + width: 100%; + } } diff --git a/packages/editor-ui/src/components/Modal.vue b/packages/editor-ui/src/components/Modal.vue index 274d6fc94..e73daea11 100644 --- a/packages/editor-ui/src/components/Modal.vue +++ b/packages/editor-ui/src/components/Modal.vue @@ -1,6 +1,21 @@