diff --git a/.gitignore b/.gitignore index 9c8db697a..c67589039 100644 --- a/.gitignore +++ b/.gitignore @@ -23,5 +23,6 @@ # secrets secrets.production.js secrets.development.js +get-pass-key.js .idea diff --git a/package.json b/package.json index 7535a44cc..b73f83240 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "automa", - "version": "0.16.2", + "version": "0.17.0", "description": "An extension for automating your browser by connecting blocks", "license": "MIT", "repository": { @@ -29,8 +29,9 @@ "@medv/finder": "^2.1.0", "@vuex-orm/core": "^0.36.4", "compare-versions": "^4.1.2", + "crypto-js": "^4.1.1", "dayjs": "^1.10.7", - "defu": "^5.0.0", + "defu": "^5.0.1", "drawflow": "^0.0.51", "idb": "^7.0.0", "mitt": "^3.0.0", @@ -41,7 +42,7 @@ "tippy.js": "^6.3.1", "v-remixicon": "^0.1.1", "vue": "3.2.19", - "vue-i18n": "^9.2.0-beta.20", + "vue-i18n": "^9.2.0-beta.29", "vue-router": "^4.0.11", "vue-toastification": "^2.0.0-rc.5", "vuedraggable": "^4.1.0", diff --git a/src/background/index.js b/src/background/index.js index 0b4b84b6c..56300038a 100644 --- a/src/background/index.js +++ b/src/background/index.js @@ -1,11 +1,13 @@ import browser from 'webextension-polyfill'; import { MessageListener } from '@/utils/message'; import { registerSpecificDay } from '../utils/workflow-trigger'; +import { parseJSON } from '@/utils/helper'; import WorkflowState from './workflow-state'; import CollectionEngine from './collection-engine'; import WorkflowEngine from './workflow-engine/engine'; import blocksHandler from './workflow-engine/blocks-handler'; import WorkflowLogger from './workflow-logger'; +import decryptFlow, { getWorkflowPass } from '@/utils/decrypt-flow'; const storage = { async get(key) { @@ -36,6 +38,16 @@ const workflow = { return findWorkflow; }, execute(workflowData, options) { + if (workflowData.isProtected) { + const flow = parseJSON(workflowData.drawflow, null); + + if (!flow) { + const pass = getWorkflowPass(workflowData.pass); + + workflowData.drawflow = decryptFlow(workflowData, pass); + } + } + const engine = new WorkflowEngine(workflowData, { ...options, blocksHandler, @@ -136,6 +148,23 @@ chrome.runtime.onInstalled.addListener((details) => { }); } }); +chrome.runtime.onStartup.addListener(async () => { + const { onStartupTriggers, workflows } = await browser.storage.local.get([ + 'onStartupTriggers', + 'workflows', + ]); + + (onStartupTriggers || []).forEach((workflowId, index) => { + const findWorkflow = workflows.find(({ id }) => id === workflowId); + + if (findWorkflow) { + workflow.execute(findWorkflow); + } else { + onStartupTriggers.splice(index, 1); + } + }); + await browser.storage.local.set({ onStartupTriggers }); +}); const message = new MessageListener('background'); @@ -165,9 +194,16 @@ message.on('open:dashboard', async (url) => { console.error(error); } }); +message.on('set:active-tab', (tabId) => { + return browser.tabs.update(tabId, { active: true }); +}); + message.on('get:sender', (_, sender) => { return sender; }); +message.on('get:tab-screenshot', (options) => { + return browser.tabs.captureVisibleTab(options); +}); message.on('get:file', (path) => { return new Promise((resolve, reject) => { const isFile = /\.(.*)/.test(path); @@ -193,7 +229,9 @@ message.on('get:file', (path) => { } }; xhr.onerror = function () { - reject(new Error(xhr.statusText)); + reject( + new Error(xhr.statusText || `Can't find a file with "${path}" path`) + ); }; xhr.open('GET', fileUrl); xhr.send(); @@ -208,8 +246,8 @@ message.on('collection:execute', (collection) => { engine.init(); }); -message.on('workflow:execute', (param) => { - workflow.execute(param); +message.on('workflow:execute', (workflowData) => { + workflow.execute(workflowData); }); message.on('workflow:stop', async (id) => { await workflow.states.stop(id); diff --git a/src/background/workflow-engine/blocks-handler/handler-execute-workflow.js b/src/background/workflow-engine/blocks-handler/handler-execute-workflow.js index d02c120a0..2c75b5f61 100644 --- a/src/background/workflow-engine/blocks-handler/handler-execute-workflow.js +++ b/src/background/workflow-engine/blocks-handler/handler-execute-workflow.js @@ -1,13 +1,26 @@ import browser from 'webextension-polyfill'; import WorkflowEngine from '../engine'; import { getBlockConnection } from '../helper'; -import { isWhitespace } from '@/utils/helper'; +import { isWhitespace, parseJSON } from '@/utils/helper'; +import decryptFlow, { getWorkflowPass } from '@/utils/decrypt-flow'; function workflowListener(workflow, options) { return new Promise((resolve, reject) => { + if (workflow.isProtected) { + const flow = parseJSON(workflow.drawflow, null); + + if (!flow) { + const pass = getWorkflowPass(workflow.pass); + + workflow.drawflow = decryptFlow(workflow, pass); + } + } + const engine = new WorkflowEngine(workflow, options); engine.init(); engine.on('destroyed', ({ id, status, message }) => { + options.events.onDestroyed(engine); + if (status === 'error') { const error = new Error(message); error.data = { logId: id }; @@ -23,9 +36,8 @@ function workflowListener(workflow, options) { }); } -async function executeWorkflow(block) { - const nextBlockId = getBlockConnection(block); - const { data } = block; +async function executeWorkflow({ outputs, data }) { + const nextBlockId = getBlockConnection({ outputs }); try { if (data.workflowId === '') throw new Error('empty-workflow'); @@ -48,6 +60,18 @@ async function executeWorkflow(block) { onInit: (engine) => { this.childWorkflowId = engine.id; }, + onDestroyed: (engine) => { + if (data.executeId) { + const { dataColumns, globalData, googleSheets } = + engine.referenceData; + + this.referenceData.workflow[data.executeId] = { + dataColumns, + globalData, + googleSheets, + }; + } + }, }, states: this.states, logger: this.logger, diff --git a/src/background/workflow-engine/blocks-handler/handler-interaction-block.js b/src/background/workflow-engine/blocks-handler/handler-interaction-block.js index 52a64a223..13e465608 100644 --- a/src/background/workflow-engine/blocks-handler/handler-interaction-block.js +++ b/src/background/workflow-engine/blocks-handler/handler-interaction-block.js @@ -1,7 +1,23 @@ import { objectHasKey } from '@/utils/helper'; import { getBlockConnection } from '../helper'; +async function checkAccess(blockName) { + if (blockName === 'upload-file') { + const hasFileAccess = await new Promise((resolve) => + chrome.extension.isAllowedFileSchemeAccess(resolve) + ); + + if (hasFileAccess) return true; + + throw new Error('no-file-access'); + } + + return true; +} + async function interactionHandler(block, { refData }) { + await checkAccess(block.name); + const { executedBlockOnWeb, debugMode } = this.workflow.settings; const nextBlockId = getBlockConnection(block); diff --git a/src/background/workflow-engine/blocks-handler/handler-new-tab.js b/src/background/workflow-engine/blocks-handler/handler-new-tab.js index b8fd57aa5..48c521f87 100644 --- a/src/background/workflow-engine/blocks-handler/handler-new-tab.js +++ b/src/background/workflow-engine/blocks-handler/handler-new-tab.js @@ -1,6 +1,5 @@ import browser from 'webextension-polyfill'; import { getBlockConnection } from '../helper'; -import executeContentScript from '../execute-content-script'; async function newTab(block) { if (this.windowId) { @@ -48,7 +47,6 @@ async function newTab(block) { } this.activeTab.frameId = 0; - await executeContentScript(this.activeTab.id); return { data: url, diff --git a/src/background/workflow-engine/blocks-handler/handler-take-screenshot.js b/src/background/workflow-engine/blocks-handler/handler-take-screenshot.js index 6883fc8ba..f5a629e99 100644 --- a/src/background/workflow-engine/blocks-handler/handler-take-screenshot.js +++ b/src/background/workflow-engine/blocks-handler/handler-take-screenshot.js @@ -22,8 +22,15 @@ function saveImage({ fileName, uri, ext }) { async function takeScreenshot(block) { const nextBlockId = getBlockConnection(block); - const { ext, quality, captureActiveTab, fileName, saveToColumn, dataColumn } = - block.data; + const { + ext, + quality, + captureActiveTab, + fileName, + saveToColumn, + dataColumn, + fullPage, + } = block.data; const saveToComputer = typeof block.data.saveToComputer === 'undefined' @@ -51,7 +58,13 @@ async function takeScreenshot(block) { await new Promise((resolve) => setTimeout(resolve, 500)); - const uri = await browser.tabs.captureVisibleTab(options); + const uri = await (fullPage + ? this._sendMessageToTab({ + tabId: this.activeTab.id, + options, + name: block.name, + }) + : browser.tabs.captureVisibleTab(options)); if (tab) { await browser.windows.update(tab.windowId, { focused: true }); diff --git a/src/background/workflow-engine/engine.js b/src/background/workflow-engine/engine.js index 59bb51362..b84760ce5 100644 --- a/src/background/workflow-engine/engine.js +++ b/src/background/workflow-engine/engine.js @@ -45,6 +45,7 @@ class WorkflowEngine { }; this.referenceData = { loopData: {}, + workflow: {}, dataColumns: [], googleSheets: {}, globalData: parseJSON(globalDataValue, globalDataValue), diff --git a/src/components/block/BlockBase.vue b/src/components/block/BlockBase.vue index 25fe56473..8662a4d6a 100644 --- a/src/components/block/BlockBase.vue +++ b/src/components/block/BlockBase.vue @@ -1,5 +1,6 @@ + diff --git a/src/components/block/BlockGroup.vue b/src/components/block/BlockGroup.vue index d9e8464f5..3b2d69f9b 100644 --- a/src/components/block/BlockGroup.vue +++ b/src/components/block/BlockGroup.vue @@ -39,6 +39,8 @@
{ + const blockEl = document.querySelector(`[group-item-id="${itemId}"]`); + + if (blockEl) { + const blockIndex = block.data.blocks.findIndex( + (item) => item.itemId === itemId + ); + + if (blockIndex !== -1) { + emitter.emit('editor:delete-block', { itemId, isInGroup: true }); + block.data.blocks.splice(blockIndex, 1); + } + } + }, 200); +} function handleDataChange({ detail }) { if (!detail) return; @@ -147,9 +170,9 @@ function handleDrop(event) { const droppedBlock = JSON.parse(event.dataTransfer.getData('block') || null); - if (!droppedBlock) return; + if (!droppedBlock || droppedBlock.fromGroup) return; - const { id, data } = droppedBlock; + const { id, data, blockId } = droppedBlock; if (excludeBlocks.includes(id)) { toast.error( @@ -161,6 +184,10 @@ function handleDrop(event) { return; } + if (blockId) { + props.editor.removeNodeId(`node-${blockId}`); + } + block.data.blocks.push({ id, data, itemId: nanoid(5) }); } diff --git a/src/components/newtab/workflow/WorkflowActions.vue b/src/components/newtab/workflow/WorkflowActions.vue index bdfba413c..4bf16637e 100644 --- a/src/components/newtab/workflow/WorkflowActions.vue +++ b/src/components/newtab/workflow/WorkflowActions.vue @@ -11,11 +11,20 @@ + - + +