Ahmad Kholid 2 anos atrás
pai
commit
7f992fc52b

+ 5 - 1
README.md

@@ -33,7 +33,11 @@ Auto-fill forms, do a repetitive task, take a screenshot, or scrape website data
 </table>
 
 ## Marketplace
-Browse the Automa marketplace where you can share and download workflows with others. [Go to the marketplace &#187;](https://automa.vercel.app/workflows)
+Browse the Automa marketplace where you can share and download workflows with others. [Go to the marketplace &#187;](https://www.automa.site/marketplace)
+
+## Automa Chrome Extension Builder
+Automa Chrome Extension Builder (Automa CEB for short) allows you to generate a standalone chrome extension based on Automa workflows. [Go to the documentation &#187;](https://docs.automa.site/extension-builder)
+
 
 ## Project setup
 Before running the `yarn dev` or `yarn build` script, you need to create the `getPassKey.js` file in the `src/utils` directory.  Inside the file write

+ 6 - 6
package.json

@@ -1,6 +1,6 @@
 {
   "name": "automa",
-  "version": "1.27.0",
+  "version": "1.27.1",
   "description": "An extension for automating your browser by connecting blocks",
   "repository": {
     "type": "git",
@@ -45,11 +45,11 @@
     "@tiptap/extension-placeholder": "^2.0.0-beta.205",
     "@tiptap/starter-kit": "^2.0.0-beta.209",
     "@tiptap/vue-3": "^2.0.0-beta.209",
-    "@viselect/vanilla": "^3.2.4",
+    "@viselect/vanilla": "^3.2.5",
     "@vue-flow/additional-components": "^1.3.3",
-    "@vue-flow/core": "^1.12.5",
-    "@vueuse/head": "^1.0.22",
-    "@vueuse/rxjs": "^9.1.1",
+    "@vue-flow/core": "^1.12.7",
+    "@vueuse/head": "^1.0.23",
+    "@vueuse/rxjs": "^9.11.1",
     "@vuex-orm/core": "^0.36.4",
     "codemirror": "^6.0.1",
     "compare-versions": "^6.0.0-rc.1",
@@ -107,7 +107,7 @@
     "babel-loader": "^9.1.2",
     "clean-webpack-plugin": "4.0.0",
     "copy-webpack-plugin": "^11.0.0",
-    "core-js": "^3.27.1",
+    "core-js": "^3.27.2",
     "cross-env": "^7.0.3",
     "css-loader": "^6.7.3",
     "eslint": "^8.31.0",

+ 9 - 2
src/components/newtab/settings/SettingsCloudBackup.vue

@@ -220,7 +220,9 @@ async function syncCloudToLocal(workflow) {
     backupState.uploading = true;
     backupState.workflowId = workflow.id;
 
-    const response = await fetchApi(`/me/workflows/${workflow.id}`);
+    const response = await fetchApi(`/me/workflows/${workflow.id}`, {
+      auth: true,
+    });
     const data = await response.json();
     if (!response.ok) throw new Error(data.message);
 
@@ -278,6 +280,7 @@ async function deleteBackup(workflowId) {
     const response = await fetchApi(
       `/me/workflows?id=${ids.join(',')}&type=backup`,
       {
+        auth: true,
         method: 'DELETE',
       }
     );
@@ -313,7 +316,9 @@ async function fetchCloudWorkflows() {
 
   try {
     const data = await cacheApi('backup-workflows', async () => {
-      const response = await fetchApi('/me/workflows?type=backup');
+      const response = await fetchApi('/me/workflows?type=backup', {
+        auth: true,
+      });
 
       if (!response.ok) throw new Error(response.statusText);
 
@@ -355,6 +360,7 @@ async function updateCloudBackup(workflow) {
     });
 
     const response = await fetchApi(`/me/workflows/${workflow.id}`, {
+      auth: true,
       method: 'PUT',
       body: JSON.stringify({ workflow: payload }),
     });
@@ -404,6 +410,7 @@ async function backupWorkflowsToCloud(workflowId) {
     }, []);
 
     const response = await fetchApi('/me/workflows/backup', {
+      auth: true,
       method: 'POST',
       body: JSON.stringify({ workflows: workflowsPayload }),
     });

+ 1 - 0
src/components/newtab/workflow/WorkflowShare.vue

@@ -132,6 +132,7 @@ async function publishWorkflow() {
     delete workflow.extVersion;
 
     const response = await fetchApi('/me/workflows/shared', {
+      auth: true,
       method: 'POST',
       body: JSON.stringify({ workflow }),
     });

+ 1 - 0
src/components/newtab/workflow/WorkflowShareTeam.vue

@@ -187,6 +187,7 @@ async function publishWorkflow() {
     delete workflow.extVersion;
 
     const response = await fetchApi(`/teams/${state.activeTeam}/workflows`, {
+      auth: true,
       method: 'POST',
       body: JSON.stringify({ workflow }),
     });

+ 1 - 1
src/components/newtab/workflow/edit/EditGoogleSheets.vue

@@ -51,7 +51,7 @@
       Automa doesn't have access to the spreadsheet
       <v-remixicon name="riInformationLine" size="18" class="inline" />
     </a>
-    <edit-autocomplete v-if="data.type !== 'create'">
+    <edit-autocomplete v-if="!['create', 'add-sheet'].includes(data.type)">
       <ui-input
         :model-value="data.range"
         class="mt-1 w-full"

+ 2 - 2
src/components/newtab/workflow/edit/EditGoogleSheetsDrive.vue

@@ -14,7 +14,7 @@
     v-else
     google-drive
     :data="data"
-    :additional-actions="['create']"
+    :additional-actions="['create', 'add-sheet']"
     @update:data="updateData"
   >
     <ui-tabs
@@ -58,7 +58,7 @@
       </ui-button>
     </div>
     <ui-input
-      v-if="data.type === 'create'"
+      v-if="['create', 'add-sheet'].includes(data.type)"
       :model-value="data.sheetName"
       label="Sheet name"
       placeholder="A Spreadsheet"

+ 5 - 2
src/components/newtab/workflow/editor/EditorLocalActions.vue

@@ -475,6 +475,7 @@ async function setAsHostWorkflow(isHost) {
 
       url += `/host`;
       payload = {
+        auth: true,
         method: 'POST',
         body: JSON.stringify({
           workflow: workflowPaylod,
@@ -544,7 +545,7 @@ function deleteFromTeam() {
       try {
         const response = await fetchApi(
           `/teams/${teamId}/workflows/${props.workflow.id}`,
-          { method: 'DELETE' }
+          { method: 'DELETE', auth: true }
         );
         const result = await response.json();
 
@@ -592,6 +593,7 @@ async function publishWorkflow() {
     const response = await fetchApi(
       `/teams/${teamId}/workflows/${props.workflow.id}`,
       {
+        auth: true,
         method: 'PATCH',
         body: JSON.stringify({ workflow: workflowPaylod }),
       }
@@ -700,7 +702,8 @@ async function retrieveTriggerText() {
 async function fetchSyncWorkflow() {
   try {
     const response = await fetchApi(
-      `/teams/${teamId}/workflows/${props.workflow.id}`
+      `/teams/${teamId}/workflows/${props.workflow.id}`,
+      { auth: true }
     );
     const result = await response.json();
 

+ 3 - 0
src/components/newtab/workflow/editor/EditorPkgActions.vue

@@ -200,6 +200,7 @@ async function toggleSharePackage() {
       });
 
       const response = await fetchApi('/packages', {
+        auth: true,
         method: 'POST',
         body: JSON.stringify({
           package: payload,
@@ -212,6 +213,7 @@ async function toggleSharePackage() {
       packageStore.insertShared(props.data.id);
     } else {
       const response = await fetchApi(`/packages/${props.data.id}`, {
+        auth: true,
         method: 'DELETE',
       });
       const result = await response.json();
@@ -248,6 +250,7 @@ async function updateSharedPackage() {
     });
 
     const response = await fetchApi(`/packages/${props.data.id}`, {
+      auth: true,
       method: 'PATCH',
       body: JSON.stringify({ package: payload }),
     });

+ 1 - 1
src/components/newtab/workflows/WorkflowsUserTeam.vue

@@ -169,7 +169,7 @@ function onMenuSelected({ id, data }) {
         try {
           const response = await fetchApi(
             `/teams/${props.teamId}/workflows/${data.id}`,
-            { method: 'DELETE' }
+            { method: 'DELETE', auth: true }
           );
           const result = await response.json();
 

+ 2 - 1
src/locales/en/blocks.json

@@ -444,7 +444,8 @@
           "update": "Update spreadsheet cell values",
           "append": "Append spreadsheet cell values",
           "clear": "Clear spreadsheet cell values",
-          "create": "Create a spreadsheet"
+          "create": "Create a spreadsheet",
+          "add-sheet": "Add sheet"
         }
       },
       "active-tab": {

+ 2 - 0
src/newtab/pages/workflows/Shared.vue

@@ -255,6 +255,7 @@ function unpublishSharedWorkflow() {
         state.isUnpublishing = true;
 
         const response = await fetchApi(`/me/workflows/shared/${workflowId}`, {
+          auth: true,
           method: 'DELETE',
         });
 
@@ -295,6 +296,7 @@ async function saveUpdatedSharedWorkflow() {
 
     const url = `/me/workflows/shared/${workflowId}`;
     const response = await fetchApi(url, {
+      auth: true,
       method: 'PUT',
       body: JSON.stringify(payload),
     });

+ 3 - 48
src/newtab/pages/workflows/[id].vue

@@ -166,7 +166,6 @@
             tabindex="0"
             @init="onEditorInit"
             @edit="initEditBlock"
-            @click="onClickEditor"
             @update:node="state.dataChanged = true"
             @delete:node="state.dataChanged = true"
             @update:settings="onUpdateBlockSettings"
@@ -617,6 +616,7 @@ const updateHostedWorkflow = throttle(async () => {
     }
 
     const response = await fetchApi(`/me/workflows/${route.params.id}`, {
+      auth: true,
       method: 'PUT',
       keepalive: true,
       body: JSON.stringify({
@@ -721,52 +721,6 @@ function startRecording({ nodeId, handleId }) {
     router.replace('/recording');
   });
 }
-let nodeTargetHandle = null;
-function onClickEditor({ target }) {
-  const targetClass = target.classList;
-  const isHandle = targetClass.contains('vue-flow__handle');
-  const clearActiveTarget = () => {
-    if (nodeTargetHandle) {
-      const targetEl = document.querySelector(
-        `.vue-flow__handle[data-handleid="${nodeTargetHandle.handleId}"]`
-      );
-      if (targetEl) targetEl.classList.remove('ring-2');
-    }
-  };
-
-  if (!isHandle) {
-    clearActiveTarget();
-    nodeTargetHandle = null;
-    return;
-  }
-
-  if (nodeTargetHandle && targetClass.contains('target')) {
-    const { handleid, nodeid } = target.dataset;
-
-    const connectionExist = document.querySelector(
-      `.vue-flow__edge.target-${handleid}.source-${nodeTargetHandle.handleId}`
-    );
-    if (!connectionExist) {
-      editor.value.addEdges([
-        {
-          target: nodeid,
-          targetHandle: handleid,
-          source: nodeTargetHandle.nodeId,
-          sourceHandle: nodeTargetHandle.handleId,
-        },
-      ]);
-    }
-
-    clearActiveTarget();
-    nodeTargetHandle = null;
-  } else {
-    clearActiveTarget();
-    target.classList.add('ring-2');
-
-    const { handleid, nodeid } = target.dataset;
-    nodeTargetHandle = { nodeId: nodeid, handleId: handleid };
-  }
-}
 function goToBlock(blockId) {
   if (!editor.value) return;
 
@@ -1548,7 +1502,8 @@ function checkWorkflowPermission() {
 function checkWorkflowUpdate() {
   const updatedAt = encodeURIComponent(workflow.value.updatedAt);
   fetchApi(
-    `/teams/${teamId}/workflows/${workflowId}/check-update?updatedAt=${updatedAt}`
+    `/teams/${teamId}/workflows/${workflowId}/check-update?updatedAt=${updatedAt}`,
+    { auth: true }
   )
     .then((response) => response.json())
     .then((result) => {

+ 1 - 0
src/newtab/pages/workflows/index.vue

@@ -502,6 +502,7 @@ function addHostedWorkflow() {
         if (isTheUserHost) throw new Error('exist');
 
         const response = await fetchApi('/workflows/hosted', {
+          auth: true,
           method: 'POST',
           body: JSON.stringify({ hostId }),
         });

+ 1 - 0
src/stores/hostedWorkflow.js

@@ -60,6 +60,7 @@ export const useHostedWorkflowStore = defineStore('hosted-workflows', {
       if (!ids || ids.length === 0) return null;
 
       const response = await fetchApi('/workflows/hosted', {
+        auth: true,
         method: 'POST',
         body: JSON.stringify({ hosts: ids }),
       });

+ 1 - 1
src/stores/package.js

@@ -92,7 +92,7 @@ export const usePackageStore = defineStore('packages', {
       try {
         if (this.sharedRetrieved) return;
 
-        const response = await fetchApi('/me/packages');
+        const response = await fetchApi('/me/packages', { auth: true });
         const result = await response.json();
 
         if (!response.ok) throw new Error(result.message);

+ 3 - 1
src/stores/sharedWorkflow.js

@@ -42,7 +42,9 @@ export const useSharedWorkflowStore = defineStore('shared-workflows', {
         'shared-workflows',
         async () => {
           try {
-            const response = await fetchApi('/me/workflows/shared?data=all');
+            const response = await fetchApi('/me/workflows/shared?data=all', {
+              auth: true,
+            });
 
             if (response.status !== 200) throw new Error(response.statusText);
 

+ 1 - 1
src/stores/user.js

@@ -30,7 +30,7 @@ export const useUserStore = defineStore('user', {
           'user-profile',
           async () => {
             try {
-              const response = await fetchApi('/me');
+              const response = await fetchApi('/me', { auth: true });
               const result = await response.json();
 
               if (!response.ok) throw new Error(response.message);

+ 1 - 0
src/stores/workflow.js

@@ -239,6 +239,7 @@ export const useWorkflowStore = defineStore('workflow', {
 
       if (hostedWorkflow || backupIndex !== -1) {
         const response = await fetchApi(`/me/workflows?id=${id}`, {
+          auth: true,
           method: 'DELETE',
         });
         const result = await response.json();

+ 3 - 1
src/utils/api.js

@@ -10,7 +10,9 @@ export async function fetchApi(path, options) {
   };
 
   const { session } = await browser.storage.local.get('session');
-  if (session) {
+  if (session && options?.auth) {
+    delete options.auth;
+
     let token = session.access_token;
 
     if (Date.now() > (session.expires_at - 2000) * 1000) {

+ 507 - 0
src/utils/firstWorkflows.js

@@ -1,6 +1,513 @@
 import { nanoid } from 'nanoid';
 
 export default [
+  {
+    id: nanoid(),
+    name: 'Twitter Trends to Google Sheets',
+    icon: 'https://upload.wikimedia.org/wikipedia/commons/4/4f/Twitter-logo.svg',
+    table: [
+      { id: '6QbkU', name: 'text', type: 'string' },
+      { id: 'Q-zII', name: 'tweets_count', type: 'integer' },
+    ],
+    drawflow: {
+      nodes: [
+        {
+          type: 'BlockBasic',
+          dimensions: { width: 192, height: 72 },
+          handleBounds: {
+            source: [
+              {
+                id: 'F_uKdk2VrnKslRle78d-C-output-1',
+                position: 'right',
+                x: 196,
+                y: 28,
+                width: 16,
+                height: 16,
+              },
+            ],
+          },
+          computedPosition: { x: 96, y: 75.5, z: 0 },
+          selected: false,
+          dragging: false,
+          resizing: false,
+          initialized: true,
+          data: {
+            disableBlock: false,
+            description: '',
+            type: 'manual',
+            interval: 60,
+            delay: 5,
+            date: '',
+            time: '00:00',
+            url: '',
+            shortcut: '',
+            activeInInput: false,
+            isUrlRegex: false,
+            days: [],
+            contextMenuName: '',
+            contextTypes: [],
+            parameters: [],
+            preferParamsInTab: false,
+            observeElement: {
+              selector: '',
+              baseSelector: '',
+              matchPattern: '',
+              targetOptions: {
+                subtree: false,
+                childList: true,
+                attributes: false,
+                attributeFilter: [],
+                characterData: false,
+              },
+              baseElOptions: {
+                subtree: false,
+                childList: true,
+                attributes: false,
+                attributeFilter: [],
+                characterData: false,
+              },
+            },
+          },
+          events: {},
+          position: { x: 96, y: 75.5 },
+          id: 'F_uKdk2VrnKslRle78d-C',
+          label: 'trigger',
+        },
+        {
+          type: 'BlockBasic',
+          dimensions: { width: 192, height: 72 },
+          handleBounds: {
+            source: [
+              {
+                id: '369o3ob-output-1',
+                position: 'right',
+                x: 196,
+                y: 28,
+                width: 16,
+                height: 16,
+              },
+            ],
+            target: [
+              {
+                id: '369o3ob-input-1',
+                position: 'left',
+                x: -20,
+                y: 28,
+                width: 16,
+                height: 16,
+              },
+            ],
+          },
+          computedPosition: { x: 388, y: 75.5, z: 0 },
+          selected: false,
+          dragging: false,
+          resizing: false,
+          initialized: true,
+          data: {
+            disableBlock: false,
+            description: '',
+            url: 'https://twitter.com/i/trends',
+            userAgent: '',
+            active: true,
+            inGroup: false,
+            waitTabLoaded: false,
+            updatePrevTab: false,
+            customUserAgent: false,
+          },
+          events: {},
+          position: { x: 388, y: 75.5 },
+          label: 'new-tab',
+          id: '369o3ob',
+        },
+        {
+          type: 'BlockBasic',
+          dimensions: { width: 192, height: 72 },
+          handleBounds: {
+            source: [
+              {
+                id: 'f0ky2jr-output-1',
+                position: 'right',
+                x: 196,
+                y: 28,
+                width: 16,
+                height: 16,
+              },
+            ],
+            target: [
+              {
+                id: 'f0ky2jr-input-1',
+                position: 'left',
+                x: -20,
+                y: 28,
+                width: 16,
+                height: 16,
+              },
+            ],
+          },
+          computedPosition: { x: 680, y: 75.5, z: 0 },
+          selected: false,
+          dragging: false,
+          resizing: false,
+          initialized: true,
+          data: {
+            disableBlock: false,
+            loopId: 'items',
+            selector: '[data-testid="trend"]',
+            maxLoop: '0',
+            description: '',
+            reverseLoop: false,
+            actionElSelector: '',
+            findBy: 'cssSelector',
+            actionElMaxWaitTime: 5,
+            actionPageMaxWaitTime: 10,
+            loadMoreAction: 'scroll',
+            scrollToBottom: true,
+            waitForSelector: true,
+            waitSelectorTimeout: 5000,
+          },
+          events: {},
+          id: 'f0ky2jr',
+          label: 'loop-elements',
+          position: { x: 680, y: 75.5 },
+        },
+        {
+          type: 'BlockBasic',
+          dimensions: { width: 192, height: 72 },
+          handleBounds: {
+            source: [
+              {
+                id: 'tpiq3ux-output-1',
+                position: 'right',
+                x: 196,
+                y: 28,
+                width: 16,
+                height: 16,
+              },
+            ],
+            target: [
+              {
+                id: 'tpiq3ux-input-1',
+                position: 'left',
+                x: -20,
+                y: 28,
+                width: 16,
+                height: 16,
+              },
+            ],
+          },
+          computedPosition: { x: 972, y: 75.5, z: 0 },
+          selected: false,
+          dragging: false,
+          resizing: false,
+          initialized: true,
+          data: {
+            disableBlock: false,
+            description: 'Trending text',
+            findBy: 'cssSelector',
+            waitForSelector: false,
+            waitSelectorTimeout: 5000,
+            selector: '{{loopData.items}} > div > div[dir="ltr"]:nth-child(2)',
+            markEl: false,
+            multiple: false,
+            regex: '',
+            prefixText: '',
+            suffixText: '',
+            regexExp: [],
+            dataColumn: '6QbkU',
+            saveData: true,
+            includeTags: false,
+            addExtraRow: false,
+            assignVariable: false,
+            useTextContent: false,
+            variableName: '',
+            extraRowValue: '',
+            extraRowDataColumn: '',
+          },
+          events: {},
+          position: { x: 972, y: 75.5 },
+          label: 'get-text',
+          id: 'tpiq3ux',
+        },
+        {
+          type: 'BlockBasic',
+          dimensions: { width: 192, height: 72 },
+          handleBounds: {
+            source: [
+              {
+                id: 'zel6am7-output-1',
+                position: 'right',
+                x: 196,
+                y: 28,
+                width: 16,
+                height: 16,
+              },
+            ],
+            target: [
+              {
+                id: 'zel6am7-input-1',
+                position: 'left',
+                x: -20,
+                y: 28,
+                width: 16,
+                height: 16,
+              },
+            ],
+          },
+          computedPosition: { x: 1264, y: 75.5, z: 0 },
+          selected: false,
+          dragging: false,
+          resizing: false,
+          initialized: true,
+          data: {
+            disableBlock: false,
+            description: 'Trending tweets count',
+            findBy: 'cssSelector',
+            waitForSelector: false,
+            waitSelectorTimeout: 5000,
+            selector: '{{loopData.items}} > div > div[dir="ltr"]:nth-child(3)',
+            markEl: false,
+            multiple: false,
+            regex: '',
+            prefixText: '',
+            suffixText: '',
+            regexExp: [],
+            dataColumn: 'Q-zII',
+            saveData: true,
+            includeTags: false,
+            addExtraRow: false,
+            assignVariable: false,
+            useTextContent: false,
+            variableName: '',
+            extraRowValue: '',
+            extraRowDataColumn: '',
+          },
+          events: {},
+          label: 'get-text',
+          id: 'zel6am7',
+          position: { x: 1264, y: 75.5 },
+        },
+        {
+          type: 'BlockLoopBreakpoint',
+          dimensions: { width: 192, height: 151 },
+          handleBounds: {
+            source: [
+              {
+                id: 'mfzs3rz-output-1',
+                position: 'right',
+                x: 196,
+                y: 67.6875,
+                width: 16,
+                height: 16,
+              },
+            ],
+            target: [
+              {
+                id: 'mfzs3rz-input-1',
+                position: 'left',
+                x: -20,
+                y: 67.6875,
+                width: 16,
+                height: 16,
+              },
+            ],
+          },
+          computedPosition: { x: 1556, y: 75.5, z: 0 },
+          selected: false,
+          dragging: false,
+          resizing: false,
+          initialized: true,
+          data: { disableBlock: false, loopId: 'items', clearLoop: false },
+          events: {},
+          position: { x: 1556, y: 75.5 },
+          label: 'loop-breakpoint',
+          id: 'mfzs3rz',
+        },
+        {
+          type: 'BlockBasic',
+          dimensions: { width: 192, height: 72 },
+          handleBounds: {
+            source: [
+              {
+                id: '8tor34x-output-1',
+                position: 'right',
+                x: 196.00022007042253,
+                y: 27.999996538337534,
+                width: 16,
+                height: 16,
+              },
+            ],
+            target: [
+              {
+                id: '8tor34x-input-1',
+                position: 'left',
+                x: -19.99993470685429,
+                y: 27.999996538337534,
+                width: 16,
+                height: 16,
+              },
+            ],
+          },
+          computedPosition: {
+            x: 1839.6148017621147,
+            y: 112.0740088105727,
+            z: 0,
+          },
+          selected: false,
+          dragging: false,
+          resizing: false,
+          initialized: true,
+          data: {
+            disableBlock: false,
+            range: '',
+            refKey: '',
+            type: 'update',
+            customData: '',
+            description: '',
+            spreadsheetId: '',
+            dataColumn: '',
+            saveData: true,
+            assignVariable: false,
+            variableName: '',
+            firstRowAsKey: false,
+            keysAsFirstRow: true,
+            valueInputOption: 'RAW',
+            InsertDataOption: 'INSERT_ROWS',
+            dataFrom: 'data-columns',
+          },
+          events: {},
+          position: { x: 1839.6148017621147, y: 112.0740088105727 },
+          label: 'google-sheets',
+          id: '8tor34x',
+        },
+        {
+          type: 'BlockNote',
+          dimensions: { width: 312, height: 251 },
+          handleBounds: {},
+          computedPosition: {
+            x: 322.1510553694428,
+            y: 207.59283268768826,
+            z: 0,
+          },
+          selected: false,
+          dragging: false,
+          resizing: false,
+          initialized: true,
+          data: {
+            disableBlock: false,
+            note: 'Read the documentation about the Google Sheets block before running this workflow',
+            drawing: false,
+            width: 280,
+            height: 168,
+            color: 'indigo',
+            fontSize: 'regular',
+          },
+          events: {},
+          position: { x: 322.1510553694428, y: 207.59283268768826 },
+          label: 'note',
+          id: '7rznotf',
+        },
+      ],
+      edges: [
+        {
+          id: 'vueflow__edge-F_uKdk2VrnKslRle78d-CF_uKdk2VrnKslRle78d-C-output-1-369o3ob369o3ob-input-1',
+          target: '369o3ob',
+          source: 'F_uKdk2VrnKslRle78d-C',
+          targetHandle: '369o3ob-input-1',
+          sourceHandle: 'F_uKdk2VrnKslRle78d-C-output-1',
+          type: 'custom',
+          updatable: true,
+          selectable: true,
+          markerEnd: 'arrowclosed',
+          data: {},
+          events: {},
+        },
+        {
+          id: 'edge-tczf2qq',
+          source: '369o3ob',
+          target: 'f0ky2jr',
+          sourceHandle: '369o3ob-output-1',
+          targetHandle: 'f0ky2jr-input-1',
+          type: 'custom',
+          updatable: true,
+          selectable: true,
+          markerEnd: 'arrowclosed',
+          data: {},
+          events: {},
+        },
+        {
+          id: 'vueflow__edge-f0ky2jrf0ky2jr-output-1-tpiq3uxtpiq3ux-input-1',
+          target: 'tpiq3ux',
+          source: 'f0ky2jr',
+          targetHandle: 'tpiq3ux-input-1',
+          sourceHandle: 'f0ky2jr-output-1',
+          type: 'custom',
+          updatable: true,
+          selectable: true,
+          markerEnd: 'arrowclosed',
+          data: {},
+          events: {},
+        },
+        {
+          id: 'vueflow__edge-tpiq3uxtpiq3ux-output-1-zel6am7zel6am7-input-1',
+          target: 'zel6am7',
+          source: 'tpiq3ux',
+          targetHandle: 'zel6am7-input-1',
+          sourceHandle: 'tpiq3ux-output-1',
+          type: 'custom',
+          updatable: true,
+          selectable: true,
+          markerEnd: 'arrowclosed',
+          data: {},
+          events: {},
+        },
+        {
+          id: 'vueflow__edge-zel6am7zel6am7-output-1-mfzs3rzmfzs3rz-input-1',
+          target: 'mfzs3rz',
+          source: 'zel6am7',
+          targetHandle: 'mfzs3rz-input-1',
+          sourceHandle: 'zel6am7-output-1',
+          type: 'custom',
+          updatable: true,
+          selectable: true,
+          markerEnd: 'arrowclosed',
+          data: {},
+          events: {},
+        },
+        {
+          id: 'vueflow__edge-mfzs3rzmfzs3rz-output-1-8tor34x8tor34x-input-1',
+          target: '8tor34x',
+          source: 'mfzs3rz',
+          targetHandle: '8tor34x-input-1',
+          sourceHandle: 'mfzs3rz-output-1',
+          type: 'custom',
+          updatable: true,
+          selectable: true,
+          markerEnd: 'arrowclosed',
+          data: {},
+          events: {},
+        },
+      ],
+      position: [-169.83007474880947, 65.03712768543713],
+      zoom: 0.9365333557481814,
+    },
+    settings: {
+      publicId: '',
+      blockDelay: 0,
+      saveLog: true,
+      debugMode: false,
+      restartTimes: 3,
+      notification: true,
+      execContext: 'popup',
+      reuseLastState: false,
+      inputAutocomplete: true,
+      onError: 'stop-workflow',
+      executedBlockOnWeb: false,
+      insertDefaultColumn: false,
+      defaultColumnName: 'column',
+    },
+    globalData: '{\n\t"key": "value"\n}',
+    description: 'Import current twitter trends to Google Sheets',
+  },
   {
     id: nanoid(),
     name: 'Google search',

+ 15 - 0
src/utils/googleSheetsApi.js

@@ -68,6 +68,21 @@ export const googleSheetNative = {
       }),
     });
   },
+  addSheet({ sheetName, spreadsheetId }) {
+    const url = googleSheetNative.getUrl(`/${spreadsheetId}:batchUpdate`);
+    return fetchGapi(url, {
+      method: 'POST',
+      body: JSON.stringify({
+        requests: [
+          {
+            addSheet: {
+              properties: { title: sheetName },
+            },
+          },
+        ],
+      }),
+    });
+  },
 };
 
 export const googleSheets = {

+ 5 - 1
src/workflowEngine/blocksHandler/handlerGoogleSheets.js

@@ -123,7 +123,7 @@ async function updateSpreadsheetValues(
 }
 
 export default async function ({ data, id }, { refData }) {
-  const isNotCreateAction = data.type !== 'create';
+  const isNotCreateAction = !['create', 'add-sheet'].includes(data.type);
 
   if (isWhitespace(data.spreadsheetId) && isNotCreateAction)
     throw new Error('empty-spreadsheet-id');
@@ -178,6 +178,10 @@ export default async function ({ data, id }, { refData }) {
         this.addDataToColumn(data.dataColumn, result);
       }
     },
+    'add-sheet': async () => {
+      result = await googleSheetsApi(true).addSheet(data);
+      result = result.replies[0].addSheet.properties;
+    },
   };
   await actionHandlers[data.type]();
 

+ 1 - 0
src/workflowEngine/index.js

@@ -116,6 +116,7 @@ export function startWorkflowExec(workflowData, options, isPopup = true) {
         }
 
         fetchApi(`/teams/${clonedWorkflowData.teamId}/workflows/logs`, {
+          auth: true,
           method: 'POST',
           body: JSON.stringify(payload),
         }).catch((error) => {

+ 53 - 48
yarn.lock

@@ -1639,40 +1639,40 @@
   dependencies:
     "@types/node" "*"
 
-"@unhead/dom@^1.0.9":
-  version "1.0.15"
-  resolved "https://registry.yarnpkg.com/@unhead/dom/-/dom-1.0.15.tgz#03d65840fa4dea279ad70241e36939aa33ee61c1"
-  integrity sha512-W3P9eGazfQPMZTG4ryb5oOA02Z4o16Jxo8DAihF/7Xmg/FVYY5Up9p9et7Nbb6AKNgt1PEz3Sp0xBaw+F6Uyjw==
+"@unhead/dom@^1.0.18":
+  version "1.0.18"
+  resolved "https://registry.yarnpkg.com/@unhead/dom/-/dom-1.0.18.tgz#bba1e160ab8058597d3f1d0d63a431e1ead9697f"
+  integrity sha512-zX7w/Z3a1/spyQ3SuxB/0s1Tjx8zu5RzYBBXTtYvGutF8g/ScXreC0c5Vm5F3x4HOPdWG+71Qr/M+k6AxPLHDA==
   dependencies:
-    "@unhead/schema" "1.0.15"
+    "@unhead/schema" "1.0.18"
 
-"@unhead/schema@1.0.15", "@unhead/schema@^1.0.9":
-  version "1.0.15"
-  resolved "https://registry.yarnpkg.com/@unhead/schema/-/schema-1.0.15.tgz#1e0ac9ff3261ce15adfcd69eb323768ce973811a"
-  integrity sha512-aWgHDHcemcx20zZun2hCFvZC6Ob4h14D4puhknuQoMkMOZcxh2ffYoJHb6mS3TeQRwAXCOsSFIHAgwIbayjk0w==
+"@unhead/schema@1.0.18", "@unhead/schema@^1.0.18":
+  version "1.0.18"
+  resolved "https://registry.yarnpkg.com/@unhead/schema/-/schema-1.0.18.tgz#c8c5bc9b43a21a85c681c337e6b038b6b5b29e85"
+  integrity sha512-LjNxwwQMZTD0b3LlB4/mmCZpO6HP7ZjK5sKuMpy7/+2O9HJO6TefxsDVrJVAitdUfm5Jej9cNEjnL2gJkc2uWg==
   dependencies:
     "@zhead/schema" "^1.0.9"
     hookable "^5.4.2"
 
-"@unhead/ssr@^1.0.9":
-  version "1.0.15"
-  resolved "https://registry.yarnpkg.com/@unhead/ssr/-/ssr-1.0.15.tgz#96a9d3cd1c2e583d86553214d4e04622e8063304"
-  integrity sha512-WNFljr+HaWdrBVYyKcgLXIk5EldPSKEVJlFbo2ixmSVGnFlqMHMCui/ZrfMLG1QvvFw0ZkXonapkEc/S6loSCQ==
+"@unhead/ssr@^1.0.18":
+  version "1.0.18"
+  resolved "https://registry.yarnpkg.com/@unhead/ssr/-/ssr-1.0.18.tgz#9bc15ef09fc195177ec9461972fd2caec27bb2fc"
+  integrity sha512-In0bJSLAyN8DdCuNJaoOIrjsK40g904ELR/0Eue9VzyO0fe147dPGfYlwwUrZOqj0JzGtndiQCF/D6bjn76ovw==
   dependencies:
-    "@unhead/schema" "1.0.15"
+    "@unhead/schema" "1.0.18"
 
-"@unhead/vue@^1.0.9":
-  version "1.0.15"
-  resolved "https://registry.yarnpkg.com/@unhead/vue/-/vue-1.0.15.tgz#b5cc91045e101e82276f9966976a608abe61f957"
-  integrity sha512-D2NQH8fBKdYgTdIDrarx24qDEblgLIwzPDeAY36PyP6ib8oG6oaI+5lrpMG+NtQ7k9LBqL5d6mQgYn/02kdjZg==
+"@unhead/vue@^1.0.18":
+  version "1.0.18"
+  resolved "https://registry.yarnpkg.com/@unhead/vue/-/vue-1.0.18.tgz#9b00e95a62966324a8a6d4749be6883c141a85c1"
+  integrity sha512-VZ61a2pRtGXI9sj1aba5Qmm35veVvRDIE0Xsog3I0TfwavlwklZcg9bF2eT+GcDnsq1NxNO7uDyrb/+xNAzSxA==
   dependencies:
-    "@unhead/schema" "1.0.15"
+    "@unhead/schema" "1.0.18"
     hookable "^5.4.2"
 
-"@viselect/vanilla@^3.2.4":
-  version "3.2.4"
-  resolved "https://registry.yarnpkg.com/@viselect/vanilla/-/vanilla-3.2.4.tgz#60896e063d393463387c3c9278b86dc3eddb2a72"
-  integrity sha512-OmyEwjxFDptc5kEaBIsueby/8s3rzLBYMZzSDChiXUHva/24eBA1b6iNsHnszIb/yLF4Zft762x536+uen/Ukw==
+"@viselect/vanilla@^3.2.5":
+  version "3.2.5"
+  resolved "https://registry.yarnpkg.com/@viselect/vanilla/-/vanilla-3.2.5.tgz#5a31feb4ee61710d6132bbcf2c7c7edff02ce9e1"
+  integrity sha512-8Ut9aAbf+BKFr4LZb+Hb+rE1XJzr3yw+CUU7PVK+rD65qveMpD11IB+E5KCp8ReuNE6spwn5qNZbBWVyZWEBlA==
 
 "@vue-flow/additional-components@^1.3.3":
   version "1.3.3"
@@ -1682,10 +1682,10 @@
     d3-selection "^3.0.0"
     d3-zoom "^3.0.0"
 
-"@vue-flow/core@^1.12.5":
-  version "1.12.5"
-  resolved "https://registry.yarnpkg.com/@vue-flow/core/-/core-1.12.5.tgz#9d486876f603117760ee177f8d9f837664732f8c"
-  integrity sha512-SYT8QSVpHvd/ByXYy/ZuDTmbzBsY8hZmldF+4VL0kCr8uksJ/i1h8E4/VVA9ffvQpboNndLdyitSj96yfVxuuw==
+"@vue-flow/core@^1.12.7":
+  version "1.12.7"
+  resolved "https://registry.yarnpkg.com/@vue-flow/core/-/core-1.12.7.tgz#fc4c91d4977ff3a8215853f5c059e102f0c6d3d3"
+  integrity sha512-ToqaiYzO1PIsv/HpejwFGHmVdPWnqi/HxWzIIEDSQ28rUdHAiEY+uA/dilY/B2JyrdQ1ZJJdbGyCKtT0RrqfLQ==
   dependencies:
     "@vueuse/core" "^9.11.0"
     d3-drag "^3.0.0"
@@ -1797,34 +1797,27 @@
     "@vueuse/shared" "9.11.0"
     vue-demi "*"
 
-"@vueuse/head@^1.0.22":
-  version "1.0.22"
-  resolved "https://registry.yarnpkg.com/@vueuse/head/-/head-1.0.22.tgz#66b591d6e4dc636c6578d1c04a96bd6c2e00f3b6"
-  integrity sha512-YmUdbzNdCnhmrAFxGnJS+Rixj+swE+TQC9OEaYDHIro6gE7W11jugcdwVP00HrA4WRQhg+TOQ4YcY2oL/PP1hw==
+"@vueuse/head@^1.0.23":
+  version "1.0.23"
+  resolved "https://registry.yarnpkg.com/@vueuse/head/-/head-1.0.23.tgz#1834f4c08fe01f970074c0af1051275473f6e696"
+  integrity sha512-CiC9VWYbvwAqjWDBJH4WfQfBk7NWMZpvmpvIUYsm3X+aa8QHMiDGzR+RFKZSUtykiCGnSZk97yIvo5eJBmSh8A==
   dependencies:
-    "@unhead/dom" "^1.0.9"
-    "@unhead/schema" "^1.0.9"
-    "@unhead/ssr" "^1.0.9"
-    "@unhead/vue" "^1.0.9"
+    "@unhead/dom" "^1.0.18"
+    "@unhead/schema" "^1.0.18"
+    "@unhead/ssr" "^1.0.18"
+    "@unhead/vue" "^1.0.18"
 
 "@vueuse/metadata@9.11.0":
   version "9.11.0"
   resolved "https://registry.yarnpkg.com/@vueuse/metadata/-/metadata-9.11.0.tgz#07133eb3d78cbea376b6ad216950b4bbfafd041c"
   integrity sha512-HhtG2SWkcfZBLbamHdvLn7jKOCFpw/ifXjVTd5ilFkj98WVUk/3UTQ03wF1XIkuhSO4+b45hD2lfG9/GdKCF7w==
 
-"@vueuse/rxjs@^9.1.1":
-  version "9.10.0"
-  resolved "https://registry.yarnpkg.com/@vueuse/rxjs/-/rxjs-9.10.0.tgz#6e61aea78cd8d2aae981deeb9f0274f8694fe819"
-  integrity sha512-IUCfXL5/1vSY07TYNua6nSaUmtQC3z1/14649U+OPNA4GA9Mk36OSKbTN41QSnB7TJBB+vJDXzSJYqWzn+4JUw==
-  dependencies:
-    "@vueuse/shared" "9.10.0"
-    vue-demi "*"
-
-"@vueuse/shared@9.10.0":
-  version "9.10.0"
-  resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-9.10.0.tgz#49874a0f9955d28689b3133de660367c63dbc030"
-  integrity sha512-vakHJ2ZRklAzqmcVBL38RS7BxdBA4+5poG9NsSyqJxrt9kz0zX3P5CXMy0Hm6LFbZXUgvKdqAS3pUH1zX/5qTQ==
+"@vueuse/rxjs@^9.11.1":
+  version "9.11.1"
+  resolved "https://registry.yarnpkg.com/@vueuse/rxjs/-/rxjs-9.11.1.tgz#8c024ddb1bf1b4ae7f6d68d7e37c520c0151cb3a"
+  integrity sha512-QlrN81HfOEmiiVDUyySuVtPHrDX/iqXLpIQe+a8ArenCsfG9lIrn/kK3xorzD56In6bprrjw3QRk1MUIpftfAg==
   dependencies:
+    "@vueuse/shared" "9.11.1"
     vue-demi "*"
 
 "@vueuse/shared@9.11.0":
@@ -1834,6 +1827,13 @@
   dependencies:
     vue-demi "*"
 
+"@vueuse/shared@9.11.1":
+  version "9.11.1"
+  resolved "https://registry.yarnpkg.com/@vueuse/shared/-/shared-9.11.1.tgz#c76805c9d86da109b132529b4745d7d706106e7f"
+  integrity sha512-UTZYGAjT96hWn4buf4wstZbeheBVNcKPQuej6qpoSkjF1atdaeCD6kqm9uGL2waHfisSgH9mq0qCRiBOk5C/2w==
+  dependencies:
+    vue-demi "*"
+
 "@vuex-orm/core@^0.36.4":
   version "0.36.4"
   resolved "https://registry.yarnpkg.com/@vuex-orm/core/-/core-0.36.4.tgz#9e2b1b8dfd74c2a508f1862ffa3e4a2c1e4cc60c"
@@ -2741,7 +2741,12 @@ core-js-compat@^3.25.1:
   dependencies:
     browserslist "^4.21.4"
 
-core-js@^3.27.1, core-js@^3.6.0, core-js@^3.8.3:
+core-js@^3.27.2:
+  version "3.27.2"
+  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.27.2.tgz#85b35453a424abdcacb97474797815f4d62ebbf7"
+  integrity sha512-9ashVQskuh5AZEZ1JdQWp1GqSoC1e1G87MzRqg2gIfVAQ7Qn9K+uFj8EcniUFA4P2NLZfV+TOlX1SzoKfo+s7w==
+
+core-js@^3.6.0, core-js@^3.8.3:
   version "3.27.1"
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.27.1.tgz#23cc909b315a6bb4e418bf40a52758af2103ba46"
   integrity sha512-GutwJLBChfGCpwwhbYoqfv03LAfmiz7e7D/BNxzeMxwQf10GRSzqiOjx7AmtEk+heiD/JWmBuyBPgFtx0Sg1ww==