From 4f781da136511a576570679354dcf7e450c4ad79 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 4 Dec 2024 23:53:41 +0530 Subject: [PATCH 001/102] feat: upgrade backend image to v0.0.4 --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 92b69c14c..ca64f6443 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,7 +43,7 @@ services: #build: #context: . #dockerfile: server/Dockerfile - image: getmaxun/maxun-backend:v0.0.3 + image: getmaxun/maxun-backend:v0.0.4 ports: - "${BACKEND_PORT:-8080}:${BACKEND_PORT:-8080}" env_file: .env From 1a59255c76ba5b3f7342c01f277302613f3db3f3 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 5 Dec 2024 23:06:26 +0530 Subject: [PATCH 002/102] chore: v0.0.5 maxun core --- maxun-core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maxun-core/package.json b/maxun-core/package.json index faa133ec5..90ee01b77 100644 --- a/maxun-core/package.json +++ b/maxun-core/package.json @@ -1,6 +1,6 @@ { "name": "maxun-core", - "version": "0.0.4", + "version": "0.0.5", "description": "Core package for Maxun, responsible for data extraction", "main": "build/index.js", "typings": "build/index.d.ts", From 71803055ab1589a9df044affcc21b6bccd3c7afe Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 5 Dec 2024 23:09:32 +0530 Subject: [PATCH 003/102] chore: use v0.0.5 maxun-core --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b6b735373..0269e404c 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "jwt-decode": "^4.0.0", "loglevel": "^1.8.0", "loglevel-plugin-remote": "^0.6.8", - "maxun-core": "0.0.4", + "maxun-core": "0.0.5", "minio": "^8.0.1", "moment-timezone": "^0.5.45", "node-cron": "^3.0.3", From b1e2c30d7f7cdb4b71bdb828ca1688e978b1718d Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 5 Dec 2024 23:10:03 +0530 Subject: [PATCH 004/102] chore: use v0.0.5 maxun-core --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0269e404c..006ddc8a8 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "jwt-decode": "^4.0.0", "loglevel": "^1.8.0", "loglevel-plugin-remote": "^0.6.8", - "maxun-core": "0.0.5", + "maxun-core": "^0.0.5", "minio": "^8.0.1", "moment-timezone": "^0.5.45", "node-cron": "^3.0.3", @@ -110,4 +110,4 @@ "ts-node": "^10.4.0", "vite": "^5.4.10" } -} \ No newline at end of file +} From 939e7a7bc4aea280d55e6178b4c5d28d352ce2a6 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 5 Dec 2024 23:10:32 +0530 Subject: [PATCH 005/102] chore: maxun v0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 006ddc8a8..977daada9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "maxun", - "version": "0.0.2", + "version": "0.0.4", "author": "Maxun", "license": "AGPL-3.0-or-later", "dependencies": { From 1fc7ddc363ee94409c487b90c9e9cedc9821644a Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 5 Dec 2024 23:19:55 +0530 Subject: [PATCH 006/102] chore: merge pagination hotfix --- .../workflow-management/classes/Generator.ts | 45 +------------------ 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/server/src/workflow-management/classes/Generator.ts b/server/src/workflow-management/classes/Generator.ts index cfef4a309..c9dc3385b 100644 --- a/server/src/workflow-management/classes/Generator.ts +++ b/server/src/workflow-management/classes/Generator.ts @@ -160,41 +160,6 @@ export class WorkflowGenerator { }) }; - /** - * New function to handle actionable check for scrapeList - * @param page The current Playwright Page object. - * @param config The scrapeList configuration object. - * @returns {Promise} Array of actionable selectors. - */ - private async getSelectorsForScrapeList(page: Page, config: { - listSelector: string; - fields: any; - limit?: number; - pagination: any; - }): Promise { - const { listSelector } = config; - - // Verify if the selectors are present and actionable on the current page - const actionableSelectors: string[] = []; - if (listSelector) { - const isActionable = await page.isVisible(listSelector).catch(() => false); - if (isActionable) { - actionableSelectors.push(listSelector); - logger.log('debug', `List selector ${listSelector} is actionable.`); - } else { - logger.log('warn', `List selector ${listSelector} is not visible on the page.`); - } - } - - return actionableSelectors; - } - - /** - * New function to handle actionable check for scrapeList - * @param page The current Playwright Page object. - * @param schema The scrapeSchema configuration object. - * @returns {Promise} Array of actionable selectors. - */ private async getSelectorsForSchema(page: Page, schema: Record): Promise { const selectors = Object.values(schema).map((field) => field.selector); @@ -243,14 +208,6 @@ export class WorkflowGenerator { pair.where.selectors = [...(pair.where.selectors || []), ...additionalSelectors]; } } - - if (pair.what[0].action === 'scrapeList') { - const config = pair.what[0]?.args?.[0]; - if (config) { - const actionableSelectors = await this.getSelectorsForScrapeList(page, config); - pair.where.selectors = [...(pair.where.selectors || []), ...actionableSelectors]; - } - } // Validate if the pair is already in the workflow if (pair.where.selectors && pair.where.selectors[0]) { @@ -923,4 +880,4 @@ export class WorkflowGenerator { public clearLastIndex = () => { this.generatedData.lastIndex = null; } -} +} \ No newline at end of file From 236f3edcd47b059decb6fbf9bb52caf874dfa25e Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 5 Dec 2024 23:23:30 +0530 Subject: [PATCH 007/102] chore: merge pagination hotfix --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index ca64f6443..46cc72c4e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,7 +43,7 @@ services: #build: #context: . #dockerfile: server/Dockerfile - image: getmaxun/maxun-backend:v0.0.4 + image: getmaxun/maxun-backend:v0.0.5 ports: - "${BACKEND_PORT:-8080}:${BACKEND_PORT:-8080}" env_file: .env From 5ed8e8ae427fc47e645c7b37dfb770df96f727dc Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 03:20:30 +0530 Subject: [PATCH 008/102] fix: use window.location.origin instead of base url --- maxun-core/src/browserSide/scraper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js index 369a08be8..467d0eb24 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -285,7 +285,7 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, } else if (attribute === 'src') { // Handle relative 'src' URLs const src = fieldElement.getAttribute('src'); - record[label] = src ? new URL(src, baseUrl).href : null; + record[label] = src ? new URL(src, window.location.origin).href : null; } else if (attribute === 'href') { // Handle relative 'href' URLs const href = fieldElement.getAttribute('href'); From 5985bc11f058c2e730b3daee180a6fa0ff710163 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 03:20:48 +0530 Subject: [PATCH 009/102] fix: use window.location.origin instead of base url --- maxun-core/src/browserSide/scraper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js index 467d0eb24..d3410de41 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -289,7 +289,7 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, } else if (attribute === 'href') { // Handle relative 'href' URLs const href = fieldElement.getAttribute('href'); - record[label] = href ? new URL(href, baseUrl).href : null; + record[label] = href ? new URL(href, window.location.origin).href : null; } else { record[label] = fieldElement.getAttribute(attribute); } From 429ddaa5719634b84f481c2360702c1bc59aa296 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 03:21:18 +0530 Subject: [PATCH 010/102] chore: lint --- maxun-core/src/browserSide/scraper.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/maxun-core/src/browserSide/scraper.js b/maxun-core/src/browserSide/scraper.js index d3410de41..09b6578be 100644 --- a/maxun-core/src/browserSide/scraper.js +++ b/maxun-core/src/browserSide/scraper.js @@ -283,9 +283,9 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, } else if (attribute === 'innerHTML') { record[label] = fieldElement.innerHTML.trim(); } else if (attribute === 'src') { - // Handle relative 'src' URLs - const src = fieldElement.getAttribute('src'); - record[label] = src ? new URL(src, window.location.origin).href : null; + // Handle relative 'src' URLs + const src = fieldElement.getAttribute('src'); + record[label] = src ? new URL(src, window.location.origin).href : null; } else if (attribute === 'href') { // Handle relative 'href' URLs const href = fieldElement.getAttribute('href'); @@ -346,5 +346,5 @@ function scrapableHeuristics(maxCountPerPage = 50, minArea = 20000, scrolls = 3, return results; }; - + })(window); \ No newline at end of file From 079863d015c1d3883d3d4c2154239fe419117046 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Fri, 6 Dec 2024 03:39:15 +0530 Subject: [PATCH 011/102] feat: add earliest selectors logic for page state --- maxun-core/src/interpret.ts | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index a7a5de47e..065860386 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -121,6 +121,26 @@ export default class Interpreter extends EventEmitter { } } + private getPreviousSelectors(workflow: Workflow, actionId: number): string[] { + const selectors: string[] = []; + let index = actionId - 1; + + while (index >= 0) { + const previousSelectors = workflow[index]?.where?.selectors; + if (previousSelectors && previousSelectors.length > 0) { + previousSelectors.forEach((selector) => { + if (!selectors.includes(selector)) { + selectors.push(selector); // Avoid duplicates + } + }); + break; // Exit the loop once valid selectors are found + } + index--; // Move further back in the workflow + } + + return selectors; + } + /** * Returns the context object from given Page and the current workflow.\ * \ @@ -130,11 +150,11 @@ export default class Interpreter extends EventEmitter { * @param workflow Current **initialized** workflow (array of where-what pairs). * @returns {PageState} State of the current page. */ - private async getState(page: Page, workflow: Workflow): Promise { + private async getState(page: Page, workflow: Workflow, selectors: string[]): Promise { /** * All the selectors present in the current Workflow */ - const selectors = Preprocessor.extractSelectors(workflow); + // const selectors = Preprocessor.extractSelectors(workflow); /** * Determines whether the element targetted by the selector is [actionable](https://playwright.dev/docs/actionability). @@ -365,6 +385,7 @@ export default class Interpreter extends EventEmitter { console.log("MERGED results:", mergedResult); await this.options.serializableCallback(mergedResult); + // await this.options.serializableCallback(scrapeResult); }, scrapeList: async (config: { listSelector: string, fields: any, limit?: number, pagination: any }) => { @@ -550,6 +571,7 @@ export default class Interpreter extends EventEmitter { // apply ad-blocker to the current page await this.applyAdBlocker(p); const usedActions: string[] = []; + const selectors: string[] = []; let lastAction = null; let repeatCount = 0; @@ -579,7 +601,7 @@ export default class Interpreter extends EventEmitter { let pageState = {}; try { - pageState = await this.getState(p, workflow); + pageState = await this.getState(p, workflow, selectors); } catch (e: any) { this.log('The browser has been closed.'); return; @@ -615,6 +637,9 @@ export default class Interpreter extends EventEmitter { try { await this.carryOutSteps(p, action.what); usedActions.push(action.id ?? 'undefined'); + + selectors.push(...this.getPreviousSelectors(workflow, actionId)); + console.log("SELECTORS", selectors); } catch (e) { this.log(e, Level.ERROR); } From 964913775e77b31061428432fcc2093e8f6f7206 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Fri, 6 Dec 2024 03:40:58 +0530 Subject: [PATCH 012/102] fix: add on load emit urlChanged --- .../browser-management/classes/RemoteBrowser.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index 769787da7..e30632c3f 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -402,14 +402,14 @@ export class RemoteBrowser { await this.currentPage?.close(); this.currentPage = newPage; if (this.currentPage) { - this.currentPage.on('framenavigated', (frame) => { - if (frame === this.currentPage?.mainFrame()) { - this.socket.emit('urlChanged', this.currentPage.url()); - } - }); - // this.currentPage.on('load', (page) => { - // this.socket.emit('urlChanged', page.url()); - // }) + // this.currentPage.on('framenavigated', (frame) => { + // if (frame === this.currentPage?.mainFrame()) { + // this.socket.emit('urlChanged', this.currentPage.url()); + // } + // }); + this.currentPage.on('load', (page) => { + this.socket.emit('urlChanged', page.url()); + }) this.client = await this.currentPage.context().newCDPSession(this.currentPage); await this.subscribeToScreencast(); } else { From 95b2c508a7a3e9c6c3958e9b12c1e42a2519e657 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 04:29:40 +0530 Subject: [PATCH 013/102] chore: proper spacing --- src/components/molecules/IntegrationSettings.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index c31605dea..a8c199619 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -15,11 +15,13 @@ import { useGlobalInfoStore } from "../../context/globalInfo"; import { getStoredRecording } from "../../api/storage"; import { apiUrl } from "../../apiConfig.js"; import Cookies from 'js-cookie'; + interface IntegrationProps { isOpen: boolean; handleStart: (data: IntegrationSettings) => void; handleClose: () => void; } + export interface IntegrationSettings { spreadsheetId: string; spreadsheetName: string; From 4550718e4d6079ee46c96d4c15348d20b73e9b05 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 04:37:39 +0530 Subject: [PATCH 014/102] chore: lint --- src/components/molecules/IntegrationSettings.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/molecules/IntegrationSettings.tsx b/src/components/molecules/IntegrationSettings.tsx index a8c199619..b34bc0e92 100644 --- a/src/components/molecules/IntegrationSettings.tsx +++ b/src/components/molecules/IntegrationSettings.tsx @@ -77,8 +77,7 @@ export const IntegrationSettingsModal = ({ ); notify( "error", - `Error fetching spreadsheet files: ${ - error.response?.data?.message || error.message + `Error fetching spreadsheet files: ${error.response?.data?.message || error.message }` ); } From a6f4d0436b28298cdb6b0c388470ef1b2d4a33e2 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 04:56:31 +0530 Subject: [PATCH 015/102] feat: show maxun version --- src/components/molecules/NavBar.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index ee8c80e8c..8d195ebcd 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -11,6 +11,7 @@ import { SaveRecording } from '../molecules/SaveRecording'; import DiscordIcon from '../atoms/DiscordIcon'; import { apiUrl } from '../../apiConfig'; import MaxunLogo from "../../assets/maxunlogo.png"; +import packageJson from "../../../package.json" interface NavBarProps { recordingName: string; @@ -57,7 +58,11 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => justifyContent: 'flex-start', }}> -
Maxun
+
Maxun
{ user ? ( From bbf9699dfd314021f4164794c779289d1838e470 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 04:57:09 +0530 Subject: [PATCH 016/102] chore: lint --- src/components/molecules/NavBar.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 8d195ebcd..bc373576f 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -58,11 +58,13 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => justifyContent: 'flex-start', }}> -
Maxun
+
Maxun + +
{ user ? ( From 8a0f2b6ca54705bf6168bc269e4ae09161d966f9 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 05:00:46 +0530 Subject: [PATCH 017/102] feat: format version chip --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index bc373576f..6b2bfa925 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -58,14 +58,14 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => justifyContent: 'flex-start', }}> -
Maxun +
Maxun
- { user ? (
From 76255c21eaffd2eb705f234357d0be4b4ccff29a Mon Sep 17 00:00:00 2001 From: amhsirak Date: Fri, 6 Dec 2024 05:05:25 +0530 Subject: [PATCH 018/102] chore: remove unused chip import --- src/components/molecules/Pair.tsx | 2 +- src/components/molecules/RobotDuplicate.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/Pair.tsx b/src/components/molecules/Pair.tsx index b05b912de..3c332600e 100644 --- a/src/components/molecules/Pair.tsx +++ b/src/components/molecules/Pair.tsx @@ -1,5 +1,5 @@ import React, { FC, useState } from 'react'; -import { Stack, Button, IconButton, Tooltip, Chip, Badge } from "@mui/material"; +import { Stack, Button, IconButton, Tooltip, Badge } from "@mui/material"; import { AddPair, deletePair, UpdatePair } from "../../api/workflow"; import { WorkflowFile } from "maxun-core"; import { ClearButton } from "../atoms/buttons/ClearButton"; diff --git a/src/components/molecules/RobotDuplicate.tsx b/src/components/molecules/RobotDuplicate.tsx index 850614b0f..38b7b4224 100644 --- a/src/components/molecules/RobotDuplicate.tsx +++ b/src/components/molecules/RobotDuplicate.tsx @@ -1,6 +1,6 @@ import React, { useState, useEffect } from 'react'; import { GenericModal } from "../atoms/GenericModal"; -import { TextField, Typography, Box, Button, Chip } from "@mui/material"; +import { TextField, Typography, Box, Button } from "@mui/material"; import { modalStyle } from "./AddWhereCondModal"; import { useGlobalInfoStore } from '../../context/globalInfo'; import { duplicateRecording, getStoredRecording } from '../../api/storage'; From 0d6633130596993aded87bf71420c9374e04a13c Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Fri, 6 Dec 2024 16:57:55 +0530 Subject: [PATCH 019/102] fix: add on load socket emit --- server/src/browser-management/classes/RemoteBrowser.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index e30632c3f..cfcc96f86 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -370,11 +370,11 @@ export class RemoteBrowser { await this.stopScreencast(); this.currentPage = page; - this.currentPage.on('framenavigated', (frame) => { - if (frame === this.currentPage?.mainFrame()) { - this.socket.emit('urlChanged', this.currentPage.url()); - } - }); + // this.currentPage.on('framenavigated', (frame) => { + // if (frame === this.currentPage?.mainFrame()) { + // this.socket.emit('urlChanged', this.currentPage.url()); + // } + // }); //await this.currentPage.setViewportSize({ height: 400, width: 900 }) this.client = await this.currentPage.context().newCDPSession(this.currentPage); From bffe8389889d909c4913f6d68b4085100dc3bf8c Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Fri, 6 Dec 2024 17:06:20 +0530 Subject: [PATCH 020/102] feat: add earliest selectors from workflow --- maxun-core/src/interpret.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index 065860386..5c24317c5 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -127,6 +127,7 @@ export default class Interpreter extends EventEmitter { while (index >= 0) { const previousSelectors = workflow[index]?.where?.selectors; + console.log("Previous Selectors:", previousSelectors); if (previousSelectors && previousSelectors.length > 0) { previousSelectors.forEach((selector) => { if (!selectors.includes(selector)) { @@ -156,6 +157,8 @@ export default class Interpreter extends EventEmitter { */ // const selectors = Preprocessor.extractSelectors(workflow); + console.log("All selectors:", selectors); + /** * Determines whether the element targetted by the selector is [actionable](https://playwright.dev/docs/actionability). * @param selector Selector to be queried @@ -164,8 +167,8 @@ export default class Interpreter extends EventEmitter { const actionable = async (selector: string): Promise => { try { const proms = [ - page.isEnabled(selector, { timeout: 500 }), - page.isVisible(selector, { timeout: 500 }), + page.isEnabled(selector, { timeout: 2000 }), + page.isVisible(selector, { timeout: 2000 }), ]; return await Promise.all(proms).then((bools) => bools.every((x) => x)); @@ -627,19 +630,26 @@ export default class Interpreter extends EventEmitter { if (this.options.debugChannel?.activeId) { this.options.debugChannel.activeId(actionId); } - + repeatCount = action === lastAction ? repeatCount + 1 : 0; - if (this.options.maxRepeats && repeatCount >= this.options.maxRepeats) { + + console.log("REPEAT COUNT", repeatCount); + if (this.options.maxRepeats && repeatCount > this.options.maxRepeats) { return; } lastAction = action; - + try { + console.log("Carrying out:", action.what); await this.carryOutSteps(p, action.what); usedActions.push(action.id ?? 'undefined'); - selectors.push(...this.getPreviousSelectors(workflow, actionId)); - console.log("SELECTORS", selectors); + const newSelectors = this.getPreviousSelectors(workflow, actionId); + newSelectors.forEach(selector => { + if (!selectors.includes(selector)) { + selectors.push(selector); + } + }); } catch (e) { this.log(e, Level.ERROR); } From 0ee50e1c26eaad4326135c76117ebe479e1606ed Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Fri, 6 Dec 2024 22:10:28 +0530 Subject: [PATCH 021/102] feat: add bottom up workflow traversal --- maxun-core/src/interpret.ts | 105 +++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 24 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index 5c24317c5..2457f79b4 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -121,27 +121,55 @@ export default class Interpreter extends EventEmitter { } } - private getPreviousSelectors(workflow: Workflow, actionId: number): string[] { + // private getPreviousSelectors(workflow: Workflow, actionId: number): string[] { + // const selectors: string[] = []; + // let index = actionId - 1; + + // while (index >= 0) { + // const previousSelectors = workflow[index]?.where?.selectors; + // console.log("Previous Selectors:", previousSelectors); + // if (previousSelectors && previousSelectors.length > 0) { + // previousSelectors.forEach((selector) => { + // if (!selectors.includes(selector)) { + // selectors.push(selector); // Avoid duplicates + // } + // }); + // break; // Exit the loop once valid selectors are found + // } + // index--; // Move further back in the workflow + // } + + // return selectors; + // } + + private getSelectors(workflow: Workflow, actionId: number): string[] { const selectors: string[] = []; - let index = actionId - 1; - while (index >= 0) { - const previousSelectors = workflow[index]?.where?.selectors; - console.log("Previous Selectors:", previousSelectors); - if (previousSelectors && previousSelectors.length > 0) { - previousSelectors.forEach((selector) => { + // Validate actionId + if (actionId <= 0) { + console.log("No previous selectors to collect."); + return selectors; // Empty array as there are no previous steps + } + + // Iterate from the start up to (but not including) actionId + for (let index = 0; index < actionId; index++) { + const currentSelectors = workflow[index]?.where?.selectors; + console.log(`Selectors at step ${index}:`, currentSelectors); + + if (currentSelectors && currentSelectors.length > 0) { + currentSelectors.forEach((selector) => { if (!selectors.includes(selector)) { selectors.push(selector); // Avoid duplicates } }); - break; // Exit the loop once valid selectors are found } - index--; // Move further back in the workflow } + console.log("Collected Selectors:", selectors); return selectors; } + /** * Returns the context object from given Page and the current workflow.\ * \ @@ -167,8 +195,8 @@ export default class Interpreter extends EventEmitter { const actionable = async (selector: string): Promise => { try { const proms = [ - page.isEnabled(selector, { timeout: 2000 }), - page.isVisible(selector, { timeout: 2000 }), + page.isEnabled(selector, { timeout: 500 }), + page.isVisible(selector, { timeout: 500 }), ]; return await Promise.all(proms).then((bools) => bools.every((x) => x)); @@ -198,7 +226,7 @@ export default class Interpreter extends EventEmitter { ...p, [cookie.name]: cookie.value, }), {}), - selectors: presentSelectors, + selectors: selectors, }; } @@ -570,11 +598,29 @@ export default class Interpreter extends EventEmitter { return allResults; } + private getMatchingActionId(workflow: Workflow, pageState: PageState, usedActions: string[]) { + for (let actionId = workflow.length - 1; actionId >= 0; actionId--) { + const step = workflow[actionId]; + const isApplicable = this.applicable(step.where, pageState, usedActions); + console.log("-------------------------------------------------------------"); + console.log(`Where:`, step.where); + console.log(`Page state:`, pageState); + console.log(`Match result: ${isApplicable}`); + console.log("-------------------------------------------------------------"); + + if (isApplicable) { + return actionId; + } + } + } + private async runLoop(p: Page, workflow: Workflow) { + const workflowCopy: Workflow = JSON.parse(JSON.stringify(workflow)); + // apply ad-blocker to the current page await this.applyAdBlocker(p); const usedActions: string[] = []; - const selectors: string[] = []; + let selectors: string[] = []; let lastAction = null; let repeatCount = 0; @@ -584,7 +630,7 @@ export default class Interpreter extends EventEmitter { * e.g. via `enqueueLinks`. */ p.on('popup', (popup) => { - this.concurrency.addJob(() => this.runLoop(popup, workflow)); + this.concurrency.addJob(() => this.runLoop(popup, workflowCopy)); }); /* eslint no-constant-condition: ["warn", { "checkLoops": false }] */ @@ -604,7 +650,8 @@ export default class Interpreter extends EventEmitter { let pageState = {}; try { - pageState = await this.getState(p, workflow, selectors); + pageState = await this.getState(p, workflowCopy, selectors); + selectors = []; } catch (e: any) { this.log('The browser has been closed.'); return; @@ -614,16 +661,22 @@ export default class Interpreter extends EventEmitter { this.log(`Current state is: \n${JSON.stringify(pageState, null, 2)}`, Level.WARN); } - const actionId = workflow.findIndex((step) => { - const isApplicable = this.applicable(step.where, pageState, usedActions); - console.log(`Where:`, step.where); - console.log(`Page state:`, pageState); - console.log(`Match result: ${isApplicable}`); - return isApplicable; - }); + // const actionId = workflow.findIndex((step) => { + // const isApplicable = this.applicable(step.where, pageState, usedActions); + // console.log("-------------------------------------------------------------"); + // console.log(`Where:`, step.where); + // console.log(`Page state:`, pageState); + // console.log(`Match result: ${isApplicable}`); + // console.log("-------------------------------------------------------------"); + // return isApplicable; + // }); + + const actionId = this.getMatchingActionId(workflowCopy, pageState, usedActions); - const action = workflow[actionId]; + const action = workflowCopy[actionId]; + console.log("MATCHED ACTION:", action); + console.log("MATCHED ACTION ID:", actionId); this.log(`Matched ${JSON.stringify(action?.where)}`, Level.LOG); if (action) { // action is matched @@ -643,8 +696,12 @@ export default class Interpreter extends EventEmitter { console.log("Carrying out:", action.what); await this.carryOutSteps(p, action.what); usedActions.push(action.id ?? 'undefined'); + + workflowCopy.splice(actionId, 1); + console.log(`Action with ID ${action.id} removed from the workflow copy.`); - const newSelectors = this.getPreviousSelectors(workflow, actionId); + // const newSelectors = this.getPreviousSelectors(workflow, actionId); + const newSelectors = this.getSelectors(workflowCopy, actionId); newSelectors.forEach(selector => { if (!selectors.includes(selector)) { selectors.push(selector); From d6be2683fdfc8863db235b6c679cd93eca15af45 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sat, 7 Dec 2024 21:16:58 +0530 Subject: [PATCH 022/102] feat: add check to match action url and return --- maxun-core/src/interpret.ts | 65 ++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index 2457f79b4..fce672574 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -179,54 +179,62 @@ export default class Interpreter extends EventEmitter { * @param workflow Current **initialized** workflow (array of where-what pairs). * @returns {PageState} State of the current page. */ - private async getState(page: Page, workflow: Workflow, selectors: string[]): Promise { + private async getState(page: Page, workflowCopy: Workflow, selectors: string[]): Promise { /** * All the selectors present in the current Workflow */ // const selectors = Preprocessor.extractSelectors(workflow); - - console.log("All selectors:", selectors); + // console.log("Current selectors:", selectors); /** * Determines whether the element targetted by the selector is [actionable](https://playwright.dev/docs/actionability). * @param selector Selector to be queried * @returns True if the targetted element is actionable, false otherwise. */ - const actionable = async (selector: string): Promise => { - try { - const proms = [ - page.isEnabled(selector, { timeout: 500 }), - page.isVisible(selector, { timeout: 500 }), - ]; - - return await Promise.all(proms).then((bools) => bools.every((x) => x)); - } catch (e) { - // log(e, Level.ERROR); - return false; - } - }; + // const actionable = async (selector: string): Promise => { + // try { + // const proms = [ + // page.isEnabled(selector, { timeout: 5000 }), + // page.isVisible(selector, { timeout: 5000 }), + // ]; + + // return await Promise.all(proms).then((bools) => bools.every((x) => x)); + // } catch (e) { + // // log(e, Level.ERROR); + // return false; + // } + // }; /** * Object of selectors present in the current page. */ - const presentSelectors: SelectorArray = await Promise.all( - selectors.map(async (selector) => { - if (await actionable(selector)) { - return [selector]; - } - return []; - }), - ).then((x) => x.flat()); + // const presentSelectors: SelectorArray = await Promise.all( + // selectors.map(async (selector) => { + // if (await actionable(selector)) { + // return [selector]; + // } + // return []; + // }), + // ).then((x) => x.flat()); + const action = workflowCopy[workflowCopy.length - 1]; + + console.log("Next action:", action) + + let url: any = page.url(); + + if (action && action.where.url !== url && action.where.url !== "about:blank") { + url = action.where.url; + } return { - url: page.url(), + url, cookies: (await page.context().cookies([page.url()])) .reduce((p, cookie) => ( { ...p, [cookie.name]: cookie.value, }), {}), - selectors: selectors, + selectors, }; } @@ -622,6 +630,7 @@ export default class Interpreter extends EventEmitter { const usedActions: string[] = []; let selectors: string[] = []; let lastAction = null; + let actionId = -1 let repeatCount = 0; /** @@ -649,9 +658,11 @@ export default class Interpreter extends EventEmitter { } let pageState = {}; + let getStateTest = "Hello"; try { pageState = await this.getState(p, workflowCopy, selectors); selectors = []; + console.log("Empty selectors:", selectors) } catch (e: any) { this.log('The browser has been closed.'); return; @@ -671,7 +682,7 @@ export default class Interpreter extends EventEmitter { // return isApplicable; // }); - const actionId = this.getMatchingActionId(workflowCopy, pageState, usedActions); + actionId = this.getMatchingActionId(workflowCopy, pageState, usedActions); const action = workflowCopy[actionId]; From db37c72ce5b8a97f2fa9be6cd12c3b3f0bb07d62 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sat, 7 Dec 2024 21:18:37 +0530 Subject: [PATCH 023/102] fix: add goto frame navigation --- server/src/browser-management/classes/RemoteBrowser.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index cfcc96f86..f1d18f3fc 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -402,11 +402,11 @@ export class RemoteBrowser { await this.currentPage?.close(); this.currentPage = newPage; if (this.currentPage) { - // this.currentPage.on('framenavigated', (frame) => { - // if (frame === this.currentPage?.mainFrame()) { - // this.socket.emit('urlChanged', this.currentPage.url()); - // } - // }); + this.currentPage.on('framenavigated', (frame) => { + if (frame === this.currentPage?.mainFrame()) { + this.socket.emit('urlChanged', this.currentPage.url()); + } + }); this.currentPage.on('load', (page) => { this.socket.emit('urlChanged', page.url()); }) From a8e8c1de82b0ff47add118b8ab74377d4076f42c Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sat, 7 Dec 2024 21:53:55 +0530 Subject: [PATCH 024/102] fix: rm about:blank url check for action --- maxun-core/src/interpret.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index fce672574..c2a1186c8 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -222,7 +222,7 @@ export default class Interpreter extends EventEmitter { let url: any = page.url(); - if (action && action.where.url !== url && action.where.url !== "about:blank") { + if (action && action.where.url !== url) { url = action.where.url; } From 342fd79588e14a5c9b66a11329b9a06d3c4980b6 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sat, 7 Dec 2024 22:34:02 +0530 Subject: [PATCH 025/102] feat: add bottom up get selectors logic --- maxun-core/src/interpret.ts | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index c2a1186c8..844b46c7d 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -142,31 +142,23 @@ export default class Interpreter extends EventEmitter { // return selectors; // } - private getSelectors(workflow: Workflow, actionId: number): string[] { - const selectors: string[] = []; + private getSelectors(workflow: Workflow): string[] { + const selectorsSet = new Set(); - // Validate actionId - if (actionId <= 0) { - console.log("No previous selectors to collect."); - return selectors; // Empty array as there are no previous steps + if (workflow.length === 0) { + return []; } - // Iterate from the start up to (but not including) actionId - for (let index = 0; index < actionId; index++) { + for (let index = workflow.length - 1; index >= 0; index--) { const currentSelectors = workflow[index]?.where?.selectors; - console.log(`Selectors at step ${index}:`, currentSelectors); if (currentSelectors && currentSelectors.length > 0) { - currentSelectors.forEach((selector) => { - if (!selectors.includes(selector)) { - selectors.push(selector); // Avoid duplicates - } - }); + currentSelectors.forEach((selector) => selectorsSet.add(selector)); + return Array.from(selectorsSet); } } - console.log("Collected Selectors:", selectors); - return selectors; + return []; } @@ -216,9 +208,8 @@ export default class Interpreter extends EventEmitter { // return []; // }), // ).then((x) => x.flat()); - const action = workflowCopy[workflowCopy.length - 1]; - console.log("Next action:", action) + const action = workflowCopy[workflowCopy.length - 1]; let url: any = page.url(); @@ -709,10 +700,10 @@ export default class Interpreter extends EventEmitter { usedActions.push(action.id ?? 'undefined'); workflowCopy.splice(actionId, 1); - console.log(`Action with ID ${action.id} removed from the workflow copy.`); + console.log(`Action with ID ${actionId} removed from the workflow copy.`); // const newSelectors = this.getPreviousSelectors(workflow, actionId); - const newSelectors = this.getSelectors(workflowCopy, actionId); + const newSelectors = this.getSelectors(workflowCopy); newSelectors.forEach(selector => { if (!selectors.includes(selector)) { selectors.push(selector); From ef571c4ea09ebc15b12bdbdc7398bef7c5d69d68 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 7 Dec 2024 23:28:31 +0530 Subject: [PATCH 026/102] feat: traverse dom tree for parent element selection --- server/src/workflow-management/selector.ts | 146 +++++++++++---------- 1 file changed, 79 insertions(+), 67 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 193de8910..e0cd10c57 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -20,49 +20,6 @@ type Workflow = WorkflowFile["workflow"]; * @category WorkflowManagement-Selectors * @returns {Promise} */ -export const getRect = async (page: Page, coordinates: Coordinates) => { - try { - const rect = await page.evaluate( - async ({ x, y }) => { - const el = document.elementFromPoint(x, y) as HTMLElement; - if (el) { - const { parentElement } = el; - // Match the logic in recorder.ts for link clicks - const element = parentElement?.tagName === 'A' ? parentElement : el; - const rectangle = element?.getBoundingClientRect(); - // @ts-ignore - if (rectangle) { - return { - x: rectangle.x, - y: rectangle.y, - width: rectangle.width, - height: rectangle.height, - top: rectangle.top, - right: rectangle.right, - bottom: rectangle.bottom, - left: rectangle.left, - }; - } - } - }, - { x: coordinates.x, y: coordinates.y }, - ); - return rect; - } catch (error) { - const { message, stack } = error as Error; - logger.log('error', `Error while retrieving selector: ${message}`); - logger.log('error', `Stack: ${stack}`); - } -} - -/** - * Checks the basic info about an element and returns a {@link BaseActionInfo} object. - * If the element is not found, returns undefined. - * @param page The page instance. - * @param coordinates Coordinates of an element. - * @category WorkflowManagement-Selectors - * @returns {Promise} - */ export const getElementInformation = async ( page: Page, coordinates: Coordinates @@ -70,10 +27,15 @@ export const getElementInformation = async ( try { const elementInfo = await page.evaluate( async ({ x, y }) => { - const el = document.elementFromPoint(x, y) as HTMLElement; - if (el) { - const { parentElement } = el; - const element = parentElement?.tagName === 'A' ? parentElement : el; + // Find the initial element at the point + const initialElement = document.elementFromPoint(x, y) as HTMLElement; + + if (initialElement) { + // Simply use the direct parent, no complex logic + const parentElement = initialElement.parentElement; + + // Use the parent if it exists, otherwise use the initial element + const element = parentElement || initialElement; let info: { tagName: string; @@ -84,32 +46,41 @@ export const getElementInformation = async ( attributes?: Record; innerHTML?: string; outerHTML?: string; + parentTagName?: string; + parentClasses?: string[]; } = { - tagName: element?.tagName ?? '', + tagName: element.tagName, + parentTagName: element.parentElement?.tagName, + parentClasses: element.parentElement + ? Array.from(element.parentElement.classList) + : [] }; - if (element) { - info.attributes = Array.from(element.attributes).reduce( - (acc, attr) => { - acc[attr.name] = attr.value; - return acc; - }, - {} as Record - ); - } - - // Gather specific information based on the tag - if (element?.tagName === 'A') { - info.url = (element as HTMLAnchorElement).href; - info.innerText = element.innerText ?? ''; - } else if (element?.tagName === 'IMG') { - info.imageUrl = (element as HTMLImageElement).src; + // Collect attributes + info.attributes = Array.from(element.attributes).reduce( + (acc, attr) => { + acc[attr.name] = attr.value; + return acc; + }, + {} as Record + ); + + // Specific handling for different element types + if (element.tagName === 'A') { + const anchorElement = element as HTMLAnchorElement; + info.url = anchorElement.href; + info.innerText = anchorElement.innerText ?? ''; + } else if (element.tagName === 'IMG') { + const imgElement = element as HTMLImageElement; + info.imageUrl = imgElement.src; } else { - info.hasOnlyText = element?.children?.length === 0 && - element?.innerText?.length > 0; - info.innerText = element?.innerText ?? ''; + // Check if element contains only text + info.hasOnlyText = element.children.length === 0 && + (element.innerText?.length ?? 0) > 0; + info.innerText = element.innerText ?? ''; } + // HTML content info.innerHTML = element.innerHTML; info.outerHTML = element.outerHTML; @@ -127,6 +98,47 @@ export const getElementInformation = async ( } }; +export const getRect = async (page: Page, coordinates: Coordinates) => { + try { + const rect = await page.evaluate( + async ({ x, y }) => { + // Find the initial element at the point + const initialElement = document.elementFromPoint(x, y) as HTMLElement; + + if (initialElement) { + // Simply use the direct parent, no complex logic + const parentElement = initialElement.parentElement; + + // Use the parent if it exists, otherwise use the initial element + const element = parentElement || initialElement; + + // Get bounding rectangle + const rectangle = element?.getBoundingClientRect(); + + if (rectangle) { + return { + x: rectangle.x, + y: rectangle.y, + width: rectangle.width, + height: rectangle.height, + top: rectangle.top, + right: rectangle.right, + bottom: rectangle.bottom, + left: rectangle.left, + }; + } + } + return null; + }, + { x: coordinates.x, y: coordinates.y }, + ); + return rect; + } catch (error) { + const { message, stack } = error as Error; + console.error('Error while retrieving selector:', message); + console.error('Stack:', stack); + } +}; /** * Returns the best and unique css {@link Selectors} for the element on the page. From 9f24e0018c29efa30a7a49a6cea45ea7bc7a004a Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 7 Dec 2024 23:36:01 +0530 Subject: [PATCH 027/102] feat: !return null --- server/src/workflow-management/selector.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index e0cd10c57..aaa53a5de 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -128,7 +128,6 @@ export const getRect = async (page: Page, coordinates: Coordinates) => { }; } } - return null; }, { x: coordinates.x, y: coordinates.y }, ); From 8c4c0b734d863bf1bc78f00e72fd709a89c626bc Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 7 Dec 2024 23:45:07 +0530 Subject: [PATCH 028/102] feat: handle selector generation if no parent element --- server/src/workflow-management/selector.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index aaa53a5de..7908edc22 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -753,7 +753,6 @@ interface SelectorResult { export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates): Promise => { try { const selectors = await page.evaluate(({ x, y }: { x: number, y: number }) => { - function getNonUniqueSelector(element: HTMLElement): string { let selector = element.tagName.toLowerCase(); @@ -775,18 +774,25 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates let depth = 0; const maxDepth = 2; - while (element && element !== document.body && depth < maxDepth) { - const selector = getNonUniqueSelector(element); + // Ensure we start with a valid element + let currentElement = element; + while (currentElement && currentElement !== document.body && depth < maxDepth) { + const selector = getNonUniqueSelector(currentElement); path.unshift(selector); - element = element.parentElement; + currentElement = currentElement.parentElement; depth++; } return path.join(' > '); } - const element = document.elementFromPoint(x, y) as HTMLElement | null; - if (!element) return null; + // Find the initial element at the point + const initialElement = document.elementFromPoint(x, y) as HTMLElement; + + if (!initialElement) return null; + + // Prefer parent if exists, otherwise use initial element + const element = initialElement.parentElement || initialElement; const generalSelector = getSelectorPath(element); return { From be6d8ab249d6486e7af9266dd43b3bab53d21012 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sun, 8 Dec 2024 18:04:49 +0530 Subject: [PATCH 029/102] feat: add selectors in bottom up order --- maxun-core/src/interpret.ts | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index 844b46c7d..de9a98454 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -121,24 +121,30 @@ export default class Interpreter extends EventEmitter { } } - // private getPreviousSelectors(workflow: Workflow, actionId: number): string[] { + // private getSelectors(workflow: Workflow, actionId: number): string[] { // const selectors: string[] = []; - // let index = actionId - 1; - // while (index >= 0) { - // const previousSelectors = workflow[index]?.where?.selectors; - // console.log("Previous Selectors:", previousSelectors); - // if (previousSelectors && previousSelectors.length > 0) { - // previousSelectors.forEach((selector) => { + // // Validate actionId + // if (actionId <= 0) { + // console.log("No previous selectors to collect."); + // return selectors; // Empty array as there are no previous steps + // } + + // // Iterate from the start up to (but not including) actionId + // for (let index = 0; index < actionId; index++) { + // const currentSelectors = workflow[index]?.where?.selectors; + // console.log(`Selectors at step ${index}:`, currentSelectors); + + // if (currentSelectors && currentSelectors.length > 0) { + // currentSelectors.forEach((selector) => { // if (!selectors.includes(selector)) { // selectors.push(selector); // Avoid duplicates // } // }); - // break; // Exit the loop once valid selectors are found // } - // index--; // Move further back in the workflow // } + // console.log("Collected Selectors:", selectors); // return selectors; // } @@ -208,12 +214,14 @@ export default class Interpreter extends EventEmitter { // return []; // }), // ).then((x) => x.flat()); - + const action = workflowCopy[workflowCopy.length - 1]; + // console.log("Next action:", action) + let url: any = page.url(); - if (action && action.where.url !== url) { + if (action && action.where.url !== url && action.where.url !== "about:blank") { url = action.where.url; } @@ -700,7 +708,7 @@ export default class Interpreter extends EventEmitter { usedActions.push(action.id ?? 'undefined'); workflowCopy.splice(actionId, 1); - console.log(`Action with ID ${actionId} removed from the workflow copy.`); + console.log(`Action with ID ${action.id} removed from the workflow copy.`); // const newSelectors = this.getPreviousSelectors(workflow, actionId); const newSelectors = this.getSelectors(workflowCopy); From 5259e3e386787267662755dd02ea9fc7c71d56b4 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sun, 8 Dec 2024 18:06:57 +0530 Subject: [PATCH 030/102] feat: add on flag logic for InterpretRecording --- .../classes/Interpreter.ts | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/server/src/workflow-management/classes/Interpreter.ts b/server/src/workflow-management/classes/Interpreter.ts index d53259b7d..b982b172a 100644 --- a/server/src/workflow-management/classes/Interpreter.ts +++ b/server/src/workflow-management/classes/Interpreter.ts @@ -244,7 +244,12 @@ export class WorkflowInterpreter { * @param page The page instance used to interact with the browser. * @param settings The settings to use for the interpretation. */ - public InterpretRecording = async (workflow: WorkflowFile, page: Page, settings: InterpreterSettings) => { + public InterpretRecording = async ( + workflow: WorkflowFile, + page: Page, + updatePageOnPause: (page: Page) => void, + settings: InterpreterSettings + ) => { const params = settings.params ? settings.params : null; delete settings.params; @@ -262,7 +267,7 @@ export class WorkflowInterpreter { this.socket.emit('debugMessage', msg) }, }, - serializableCallback: (data: string) => { + serializableCallback: (data: any) => { this.serializableData.push(data); this.socket.emit('serializableCallback', data); }, @@ -275,6 +280,23 @@ export class WorkflowInterpreter { const interpreter = new Interpreter(decryptedWorkflow, options); this.interpreter = interpreter; + interpreter.on('flag', async (page, resume) => { + if (this.activeId !== null && this.breakpoints[this.activeId]) { + logger.log('debug', `breakpoint hit id: ${this.activeId}`); + this.socket.emit('breakpointHit'); + this.interpretationIsPaused = true; + } + + if (this.interpretationIsPaused) { + this.interpretationResume = resume; + logger.log('debug', `Paused inside of flag: ${page.url()}`); + updatePageOnPause(page); + this.socket.emit('log', '----- The interpretation has been paused -----', false); + } else { + resume(); + } + }); + const status = await interpreter.run(page, params); const lastArray = this.serializableData.length > 1 From aec65d1b2232fef9a2c70ac3b43e0001dbbafe42 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sun, 8 Dec 2024 18:07:40 +0530 Subject: [PATCH 031/102] feat: add flag generation logic --- server/src/api/record.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/server/src/api/record.ts b/server/src/api/record.ts index 5b33b12f6..05560487d 100644 --- a/server/src/api/record.ts +++ b/server/src/api/record.ts @@ -15,6 +15,8 @@ import { io, Socket } from "socket.io-client"; import { BinaryOutputService } from "../storage/mino"; import { AuthenticatedRequest } from "../routes/record" import {capture} from "../utils/analytics"; +import { Page } from "playwright"; +import { WorkflowFile } from "maxun-core"; chromium.use(stealthPlugin()); const formatRecording = (recordingData: any) => { @@ -533,6 +535,17 @@ function resetRecordingState(browserId: string, id: string) { id = ''; } +function AddGeneratedFlags(workflow: WorkflowFile) { + const copy = JSON.parse(JSON.stringify(workflow)); + for (let i = 0; i < workflow.workflow.length; i++) { + copy.workflow[i].what.unshift({ + action: 'flag', + args: ['generated'], + }); + } + return copy; +}; + async function executeRun(id: string) { try { const run = await Run.findOne({ where: { runId: id } }); @@ -560,13 +573,14 @@ async function executeRun(id: string) { throw new Error('Could not access browser'); } - const currentPage = await browser.getCurrentPage(); + let currentPage = await browser.getCurrentPage(); if (!currentPage) { throw new Error('Could not create a new page'); } + const workflow = AddGeneratedFlags(recording.recording); const interpretationInfo = await browser.interpreter.InterpretRecording( - recording.recording, currentPage, plainRun.interpreterSettings + workflow, currentPage, (newPage: Page) => currentPage = newPage, plainRun.interpreterSettings ); const binaryOutputService = new BinaryOutputService('maxun-run-screenshots'); From 0a81292bea115446c9323bc6df5647b1b19f684d Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sun, 8 Dec 2024 18:08:05 +0530 Subject: [PATCH 032/102] feat: add flag generation logic --- server/src/routes/storage.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/server/src/routes/storage.ts b/server/src/routes/storage.ts index d1f648f86..ddadf240e 100644 --- a/server/src/routes/storage.ts +++ b/server/src/routes/storage.ts @@ -18,6 +18,8 @@ import { AuthenticatedRequest } from './record'; import { computeNextRun } from '../utils/schedule'; import { capture } from "../utils/analytics"; import { tryCatch } from 'bullmq'; +import { WorkflowFile } from 'maxun-core'; +import { Page } from 'playwright'; chromium.use(stealthPlugin()); export const router = Router(); @@ -422,6 +424,17 @@ router.get('/runs/run/:id', requireSignIn, async (req, res) => { } }); +function AddGeneratedFlags(workflow: WorkflowFile) { + const copy = JSON.parse(JSON.stringify(workflow)); + for (let i = 0; i < workflow.workflow.length; i++) { + copy.workflow[i].what.unshift({ + action: 'flag', + args: ['generated'], + }); + } + return copy; +}; + /** * PUT endpoint for finishing a run and saving it to the storage. */ @@ -443,10 +456,11 @@ router.post('/runs/run/:id', requireSignIn, async (req: AuthenticatedRequest, re // interpret the run in active browser const browser = browserPool.getRemoteBrowser(plainRun.browserId); - const currentPage = browser?.getCurrentPage(); + let currentPage = browser?.getCurrentPage(); if (browser && currentPage) { + const workflow = AddGeneratedFlags(recording.recording); const interpretationInfo = await browser.interpreter.InterpretRecording( - recording.recording, currentPage, plainRun.interpreterSettings); + workflow, currentPage, (newPage: Page) => currentPage = newPage, plainRun.interpreterSettings); const binaryOutputService = new BinaryOutputService('maxun-run-screenshots'); const uploadedBinaryOutput = await binaryOutputService.uploadAndStoreBinaryOutput(run, interpretationInfo.binaryOutput); await destroyRemoteBrowser(plainRun.browserId); From 45f0c819ea05831ccc0fe20781adfebc39b48f47 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sun, 8 Dec 2024 18:10:53 +0530 Subject: [PATCH 033/102] feat: add flag generation logic --- .../workflow-management/scheduler/index.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/server/src/workflow-management/scheduler/index.ts b/server/src/workflow-management/scheduler/index.ts index 02ca905fc..169b0061c 100644 --- a/server/src/workflow-management/scheduler/index.ts +++ b/server/src/workflow-management/scheduler/index.ts @@ -11,6 +11,8 @@ import Run from "../../models/Run"; import { getDecryptedProxyConfig } from "../../routes/proxy"; import { BinaryOutputService } from "../../storage/mino"; import { capture } from "../../utils/analytics"; +import { WorkflowFile } from "maxun-core"; +import { Page } from "playwright"; chromium.use(stealthPlugin()); async function createWorkflowAndStoreMetadata(id: string, userId: string) { @@ -79,6 +81,17 @@ async function createWorkflowAndStoreMetadata(id: string, userId: string) { } } +function AddGeneratedFlags(workflow: WorkflowFile) { + const copy = JSON.parse(JSON.stringify(workflow)); + for (let i = 0; i < workflow.workflow.length; i++) { + copy.workflow[i].what.unshift({ + action: 'flag', + args: ['generated'], + }); + } + return copy; +}; + async function executeRun(id: string) { try { const run = await Run.findOne({ where: { runId: id } }); @@ -106,13 +119,15 @@ async function executeRun(id: string) { throw new Error('Could not access browser'); } - const currentPage = await browser.getCurrentPage(); + let currentPage = await browser.getCurrentPage(); if (!currentPage) { throw new Error('Could not create a new page'); } + const workflow = AddGeneratedFlags(recording.recording); const interpretationInfo = await browser.interpreter.InterpretRecording( - recording.recording, currentPage, plainRun.interpreterSettings); + workflow, currentPage, (newPage: Page) => currentPage = newPage, plainRun.interpreterSettings + ); const binaryOutputService = new BinaryOutputService('maxun-run-screenshots'); const uploadedBinaryOutput = await binaryOutputService.uploadAndStoreBinaryOutput(run, interpretationInfo.binaryOutput); From a30211de6d3ed309a5477a44091b19a4f72a9dd0 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Sun, 8 Dec 2024 18:55:19 +0530 Subject: [PATCH 034/102] fix: add frame navigation logic in place of load --- server/src/browser-management/classes/RemoteBrowser.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index f1d18f3fc..3f5b677c7 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -407,9 +407,9 @@ export class RemoteBrowser { this.socket.emit('urlChanged', this.currentPage.url()); } }); - this.currentPage.on('load', (page) => { - this.socket.emit('urlChanged', page.url()); - }) + // this.currentPage.on('load', (page) => { + // this.socket.emit('urlChanged', page.url()); + // }) this.client = await this.currentPage.context().newCDPSession(this.currentPage); await this.subscribeToScreencast(); } else { From 2ea64385c389178972e7d2a00930f55b7dfdab1e Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 20:26:34 +0530 Subject: [PATCH 035/102] feat: youtube icon --- src/components/molecules/NavBar.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 6b2bfa925..6ecf9b618 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -4,7 +4,7 @@ import styled from "styled-components"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { IconButton, Menu, MenuItem, Typography, Avatar, Chip, } from "@mui/material"; -import { AccountCircle, Logout, Clear } from "@mui/icons-material"; +import { AccountCircle, Logout, Clear, YouTube } from "@mui/icons-material"; import { useNavigate } from 'react-router-dom'; import { AuthContext } from '../../context/auth'; import { SaveRecording } from '../molecules/SaveRecording'; @@ -114,6 +114,9 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => { handleMenuClose(); logout(); }}> Logout + { handleMenuClose(); logout(); }}> + YouTube + ) : ( From e2cb4c7613d570b0fd4cd7f8a826fe3a6e019c85 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 20:29:53 +0530 Subject: [PATCH 036/102] feat: youtube link --- src/components/molecules/NavBar.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 6ecf9b618..9e0f66426 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -114,7 +114,9 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => { handleMenuClose(); logout(); }}> Logout - { handleMenuClose(); logout(); }}> + { + window.open('https://www.youtube.com/@MaxunOSS/videos', '_blank'); + }}> YouTube From 99d62101cb512ff675b465959391c8dfe68c80ff Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 20:30:11 +0530 Subject: [PATCH 037/102] chore: lint --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 9e0f66426..d6855184a 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -114,8 +114,8 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => { handleMenuClose(); logout(); }}> Logout - { - window.open('https://www.youtube.com/@MaxunOSS/videos', '_blank'); + { + window.open('https://www.youtube.com/@MaxunOSS/videos', '_blank'); }}> YouTube From 69757050a9ac59a6ceca27a5c74a880551aba120 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 20:32:41 +0530 Subject: [PATCH 038/102] feat: x link --- src/components/molecules/NavBar.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index d6855184a..28ca959a2 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -4,7 +4,7 @@ import styled from "styled-components"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { IconButton, Menu, MenuItem, Typography, Avatar, Chip, } from "@mui/material"; -import { AccountCircle, Logout, Clear, YouTube } from "@mui/icons-material"; +import { AccountCircle, Logout, Clear, YouTube, X } from "@mui/icons-material"; import { useNavigate } from 'react-router-dom'; import { AuthContext } from '../../context/auth'; import { SaveRecording } from '../molecules/SaveRecording'; @@ -119,6 +119,11 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => }}> YouTube + { + window.open('https://x.com/maxun_io', '_blank'); + }}> + Twiiter (X) + ) : ( From 7186540ed94dbace62f45bbfd94725ffc4c1c2c2 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 20:37:48 +0530 Subject: [PATCH 039/102] feat: increase width of menu --- src/components/molecules/NavBar.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 28ca959a2..68b452db0 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -110,6 +110,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => vertical: 'top', horizontal: 'right', }} + PaperProps={{sx: {width: '180px'}}} > { handleMenuClose(); logout(); }}> Logout From a8ea05527b28a109709d71249a69977466921ead Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 20:42:24 +0530 Subject: [PATCH 040/102] feat: move discord icon to menu --- src/components/molecules/NavBar.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 68b452db0..e8d154ef7 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -115,6 +115,11 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => { handleMenuClose(); logout(); }}> Logout + { + window.open('https://discord.gg/5GbPjBUkws', '_blank'); + }}> + Discord + { window.open('https://www.youtube.com/@MaxunOSS/videos', '_blank'); }}> From 8d6b962301ae1108b409afc251bb6b61340aba72 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 20:42:54 +0530 Subject: [PATCH 041/102] feat: remove discord icon button --- src/components/molecules/NavBar.tsx | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index e8d154ef7..2d1096a57 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -71,21 +71,6 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - - - Date: Sun, 8 Dec 2024 20:55:24 +0530 Subject: [PATCH 042/102] feat: add ref to yt x links --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 2d1096a57..ef1f98782 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -106,12 +106,12 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => Discord { - window.open('https://www.youtube.com/@MaxunOSS/videos', '_blank'); + window.open('https://www.youtube.com/@MaxunOSS/videos?ref=app', '_blank'); }}> YouTube { - window.open('https://x.com/maxun_io', '_blank'); + window.open('https://x.com/maxun_io?ref=app', '_blank'); }}> Twiiter (X) From f8f1d926d99cd495e8196823e28cf5d62642ad5c Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 21:08:20 +0530 Subject: [PATCH 043/102] wip: upgrade maxun button --- src/components/molecules/NavBar.tsx | 97 ++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index ef1f98782..9b1e94727 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -3,7 +3,7 @@ import axios from 'axios'; import styled from "styled-components"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; -import { IconButton, Menu, MenuItem, Typography, Avatar, Chip, } from "@mui/material"; +import { IconButton, Menu, MenuItem, Typography, Avatar, Chip, Button, Modal, Tabs, Tab, Box } from "@mui/material"; import { AccountCircle, Logout, Clear, YouTube, X } from "@mui/icons-material"; import { useNavigate } from 'react-router-dom'; import { AuthContext } from '../../context/auth'; @@ -25,6 +25,38 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => const navigate = useNavigate(); const [anchorEl, setAnchorEl] = useState(null); + const currentVersion = "0.0.3"; // Dynamically fetch from package.json + + const [open, setOpen] = useState(false); + const [latestVersion, setLatestVersion] = useState(null); + const [tab, setTab] = useState(0); + + const fetchLatestVersion = async () => { + try { + const response = await fetch("https://api.github.com/repos/getmaxun/maxun/releases/latest"); + const data = await response.json(); + const version = data.tag_name.replace(/^v/, ""); // Remove 'v' prefix + setLatestVersion(version); + } catch (error) { + console.error("Failed to fetch latest version:", error); + setLatestVersion(null); // Handle errors gracefully + } + }; + + const handleOpen = () => { + setOpen(true); + fetchLatestVersion(); + }; + + const handleClose = () => { + setOpen(false); + setTab(0); // Reset tab to the first tab + }; + + const handleTabChange = (newValue: any) => { + setTab(newValue); + }; + const handleMenuOpen = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); @@ -71,6 +103,69 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> + + + + {latestVersion === null ? ( + Checking for updates... + ) : currentVersion === latestVersion ? ( + + 🎉 You're up to date! + + ) : ( + <> + + A new version is available: {latestVersion} + + + + + + {tab === 0 && ( + + Manual Upgrade + + git pull origin main +
+ npm install +
+ npm run start +
+
+ )} + {tab === 1 && ( + + Docker Compose Upgrade + + docker pull getmaxun/maxun:latest +
+ docker-compose up -d +
+
+ )} + + )} +
+
Date: Sun, 8 Dec 2024 21:09:42 +0530 Subject: [PATCH 044/102] refactor: rename menu & tab update functions --- src/components/molecules/NavBar.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 9b1e94727..bef197fa4 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -43,17 +43,17 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => } }; - const handleOpen = () => { + const handleUpdateOpen = () => { setOpen(true); fetchLatestVersion(); }; - const handleClose = () => { + const handleUpdateClose = () => { setOpen(false); setTab(0); // Reset tab to the first tab }; - const handleTabChange = (newValue: any) => { + const handleUpdateTabChange = (newValue: any) => { setTab(newValue); }; @@ -103,10 +103,10 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - - + = ({ recordingName, isRecording }) => From 37ed5fa555ca832f854071a885ee8fd5bf3c1454 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 21:09:58 +0530 Subject: [PATCH 045/102] chore: lint --- src/components/molecules/NavBar.tsx | 128 ++++++++++++++-------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index bef197fa4..96e1f07e4 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -103,69 +103,69 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - - - - {latestVersion === null ? ( - Checking for updates... - ) : currentVersion === latestVersion ? ( - - 🎉 You're up to date! - - ) : ( - <> - - A new version is available: {latestVersion} - - - - - - {tab === 0 && ( - - Manual Upgrade - - git pull origin main -
- npm install -
- npm run start -
-
- )} - {tab === 1 && ( - - Docker Compose Upgrade - - docker pull getmaxun/maxun:latest -
- docker-compose up -d -
-
- )} - - )} -
-
+ + + + {latestVersion === null ? ( + Checking for updates... + ) : currentVersion === latestVersion ? ( + + 🎉 You're up to date! + + ) : ( + <> + + A new version is available: {latestVersion} + + + + + + {tab === 0 && ( + + Manual Upgrade + + git pull origin main +
+ npm install +
+ npm run start +
+
+ )} + {tab === 1 && ( + + Docker Compose Upgrade + + docker pull getmaxun/maxun:latest +
+ docker-compose up -d +
+
+ )} + + )} +
+
= ({ recordingName, isRecording }) => vertical: 'top', horizontal: 'right', }} - PaperProps={{sx: {width: '180px'}}} + PaperProps={{ sx: { width: '180px' } }} > { handleMenuClose(); logout(); }}> Logout From 929bd91a7ef794ef0bbb55e4cd858d1c978374cd Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 21:11:58 +0530 Subject: [PATCH 046/102] feat: store package.json version in currentVersion --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 96e1f07e4..4ca7cc175 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -25,7 +25,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => const navigate = useNavigate(); const [anchorEl, setAnchorEl] = useState(null); - const currentVersion = "0.0.3"; // Dynamically fetch from package.json + const currentVersion = packageJson.version; const [open, setOpen] = useState(false); const [latestVersion, setLatestVersion] = useState(null); @@ -92,7 +92,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
Maxun
Date: Sun, 8 Dec 2024 21:15:21 +0530 Subject: [PATCH 047/102] feat: margin right to 30px --- src/components/molecules/NavBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 4ca7cc175..cc6d2c6bf 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -103,7 +103,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - From 6a2222e6b924697aac38c13fb6294c784870b291 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 21:21:47 +0530 Subject: [PATCH 048/102] feat: pass event prop to handle update tab change --- src/components/molecules/NavBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index cc6d2c6bf..5cdae02ed 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -53,7 +53,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => setTab(0); // Reset tab to the first tab }; - const handleUpdateTabChange = (newValue: any) => { + const handleUpdateTabChange = (event: React.SyntheticEvent, newValue: number) => { setTab(newValue); }; From 06fdfbc65a0430468d1b34e05704d7be8851e0d9 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 21:22:03 +0530 Subject: [PATCH 049/102] chore: lint --- src/components/molecules/NavBar.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 5cdae02ed..063932778 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -57,7 +57,6 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => setTab(newValue); }; - const handleMenuOpen = (event: React.MouseEvent) => { setAnchorEl(event.currentTarget); }; @@ -103,7 +102,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - From b30be4b976b478f115c0048b3f7c38ac0e4982e2 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 21:23:00 +0530 Subject: [PATCH 050/102] feat: add setup --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 063932778..94676ecd6 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -136,8 +136,8 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => sx={{ marginTop: 2, marginBottom: 2 }} centered > - - + + {tab === 0 && ( From 58aedacd4ffd99e8c8890a7ba6b63e52a69b6807 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 22:23:09 +0530 Subject: [PATCH 051/102] wip: updates ui --- src/components/molecules/NavBar.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 94676ecd6..be4818df3 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -112,7 +112,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => top: "50%", left: "50%", transform: "translate(-50%, -50%)", - width: 400, + width: 500, bgcolor: "background.paper", boxShadow: 24, p: 4, @@ -136,29 +136,27 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => sx={{ marginTop: 2, marginBottom: 2 }} centered > - - + + {tab === 0 && ( - Manual Upgrade - +
git pull origin main
npm install
npm run start - +
)} {tab === 1 && ( - Docker Compose Upgrade - +
docker pull getmaxun/maxun:latest
docker-compose up -d - +
)} From ebd866bc16c1b3d6f7a97ae303238da06160edc6 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 22:31:17 +0530 Subject: [PATCH 052/102] feat: add comments for commands --- src/components/molecules/NavBar.tsx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index be4818df3..bc4222e2a 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -141,19 +141,30 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => {tab === 0 && ( -
- git pull origin main +
+

Run the commands below

+ # pull latest changes +
+ git pull origin master +
+ # install dependencies
npm install
+ # start maxun +
npm run start
)} {tab === 1 && ( -
- docker pull getmaxun/maxun:latest +
+

Run the commands below

+ # pull latest docker images +
+ docker-compose pull + # start maxun
docker-compose up -d
From 0c66e86e28bad574460eddf622ff74e27a7f57b0 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 23:42:19 +0530 Subject: [PATCH 053/102] feat: format code blocks --- src/components/molecules/NavBar.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index bc4222e2a..99535bafb 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -140,34 +140,38 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => {tab === 0 && ( - -
+ +

Run the commands below

# pull latest changes
git pull origin master
+
# install dependencies
npm install
+
# start maxun
npm run start -
+
)} {tab === 1 && ( - -
+ +

Run the commands below

# pull latest docker images
docker-compose pull +
+
# start maxun
docker-compose up -d -
+
)} From e82863ad9c8061b072ac3bbf07ce212f983d58c6 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 23:42:29 +0530 Subject: [PATCH 054/102] chore: lint --- src/components/molecules/NavBar.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 99535bafb..b048d720e 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -141,7 +141,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => {tab === 0 && ( - +

Run the commands below

# pull latest changes
@@ -161,8 +161,8 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => )} {tab === 1 && ( - -

Run the commands below

+ +

Run the commands below

# pull latest docker images
docker-compose pull From f7eccd47cbce57d0b66eb8ed6b57bc2a60583d69 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 23:44:33 +0530 Subject: [PATCH 055/102] feat: box border radius --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index b048d720e..60b75835d 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -140,7 +140,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => {tab === 0 && ( - +

Run the commands below

# pull latest changes @@ -160,7 +160,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
)} {tab === 1 && ( - +

Run the commands below

# pull latest docker images From 885120cbb388b93e490a7479bed19c0394bb2dae Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sun, 8 Dec 2024 23:53:02 +0530 Subject: [PATCH 056/102] feat: add changelog link --- src/components/molecules/NavBar.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 60b75835d..7861340fe 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -127,8 +127,11 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => ) : ( <> - - A new version is available: {latestVersion} + + A new version is available: {latestVersion}. Upgrade to the latest version for bug fixes, enhancements and new features! +
+ View all the new updates + {' '}here.
Date: Sun, 8 Dec 2024 23:53:13 +0530 Subject: [PATCH 057/102] chore: lint --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 7861340fe..01e002ec1 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -127,11 +127,11 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
) : ( <> - + A new version is available: {latestVersion}. Upgrade to the latest version for bug fixes, enhancements and new features!
View all the new updates - {' '}here. + {' '}here.
Date: Mon, 9 Dec 2024 00:00:51 +0530 Subject: [PATCH 058/102] chore: -rm unused import --- src/components/molecules/NavBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 01e002ec1..9d5cc6232 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -3,7 +3,7 @@ import axios from 'axios'; import styled from "styled-components"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; -import { IconButton, Menu, MenuItem, Typography, Avatar, Chip, Button, Modal, Tabs, Tab, Box } from "@mui/material"; +import { IconButton, Menu, MenuItem, Typography, Chip, Button, Modal, Tabs, Tab, Box } from "@mui/material"; import { AccountCircle, Logout, Clear, YouTube, X } from "@mui/icons-material"; import { useNavigate } from 'react-router-dom'; import { AuthContext } from '../../context/auth'; From c8b95bd27c30af9133ea9e7940ceb399b15741c7 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 9 Dec 2024 00:01:26 +0530 Subject: [PATCH 059/102] chore: -rm v --- src/components/molecules/NavBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 9d5cc6232..08ab650ed 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -91,7 +91,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
Maxun
Date: Mon, 9 Dec 2024 00:25:13 +0530 Subject: [PATCH 060/102] feat: rename to upgrade maxun --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 08ab650ed..db7f72c12 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -102,8 +102,8 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - Date: Mon, 9 Dec 2024 00:32:14 +0530 Subject: [PATCH 061/102] feat: use update icon --- src/components/molecules/NavBar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index db7f72c12..fc6385036 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -4,7 +4,7 @@ import styled from "styled-components"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { IconButton, Menu, MenuItem, Typography, Chip, Button, Modal, Tabs, Tab, Box } from "@mui/material"; -import { AccountCircle, Logout, Clear, YouTube, X } from "@mui/icons-material"; +import { AccountCircle, Logout, Clear, YouTube, X, Update } from "@mui/icons-material"; import { useNavigate } from 'react-router-dom'; import { AuthContext } from '../../context/auth'; import { SaveRecording } from '../molecules/SaveRecording'; @@ -103,7 +103,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => {!isRecording ? ( <> Date: Mon, 9 Dec 2024 00:39:03 +0530 Subject: [PATCH 062/102] feat: match upgrade icon style to rest of navbar elementa --- src/components/molecules/NavBar.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index fc6385036..db29e516b 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -102,7 +102,12 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - From e5c045f0d567439d589f83f12162e60d2cf3b01b Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 9 Dec 2024 00:39:21 +0530 Subject: [PATCH 063/102] chore: lint --- src/components/molecules/NavBar.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index db29e516b..56e43c5ed 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -102,13 +102,13 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) =>
{!isRecording ? ( <> - Date: Mon, 9 Dec 2024 00:40:17 +0530 Subject: [PATCH 064/102] feat: increase margin right --- src/components/molecules/NavBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 56e43c5ed..62275078f 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -103,7 +103,7 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => {!isRecording ? ( <> + } + /> +)}
= ({ recordingName, isRecording }) => ) : "" } + ); }; From e19094185214192cc5697c97dbb5a527f92ba656 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 9 Dec 2024 00:54:55 +0530 Subject: [PATCH 066/102] chore: lint --- src/components/molecules/NavBar.tsx | 352 ++++++++++++++-------------- 1 file changed, 176 insertions(+), 176 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 1755cb20b..24fda0f0f 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -96,186 +96,186 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => return ( <> - {isUpdateAvailable && ( - - Upgrade - - } - /> -)} - -
- -
Maxun
- + Upgrade + + } /> -
- { - user ? ( -
- {!isRecording ? ( - <> - - - - {latestVersion === null ? ( - Checking for updates... - ) : currentVersion === latestVersion ? ( - - 🎉 You're up to date! - - ) : ( - <> - - A new version is available: {latestVersion}. Upgrade to the latest version for bug fixes, enhancements and new features! -
- View all the new updates - {' '}here. -
- - - - - {tab === 0 && ( - - -

Run the commands below

- # pull latest changes -
- git pull origin master -
-
- # install dependencies -
- npm install -
-
- # start maxun -
- npm run start -
-
- )} - {tab === 1 && ( - - -

Run the commands below

- # pull latest docker images -
- docker-compose pull -
-
- # start maxun -
- docker-compose up -d -
-
- )} - - )} -
-
- - - - {user.email} - - - { handleMenuClose(); logout(); }}> - Logout - - { - window.open('https://discord.gg/5GbPjBUkws', '_blank'); + )} + +
+ +
Maxun
+ +
+ { + user ? ( +
+ {!isRecording ? ( + <> + + + + {latestVersion === null ? ( + Checking for updates... + ) : currentVersion === latestVersion ? ( + + 🎉 You're up to date! + + ) : ( + <> + + A new version is available: {latestVersion}. Upgrade to the latest version for bug fixes, enhancements and new features! +
+ View all the new updates + {' '}here. +
+ + + + + {tab === 0 && ( + + +

Run the commands below

+ # pull latest changes +
+ git pull origin master +
+
+ # install dependencies +
+ npm install +
+
+ # start maxun +
+ npm run start +
+
+ )} + {tab === 1 && ( + + +

Run the commands below

+ # pull latest docker images +
+ docker-compose pull +
+
+ # start maxun +
+ docker-compose up -d +
+
+ )} + + )} +
+
+ + - YouTube - - { - window.open('https://x.com/maxun_io?ref=app', '_blank'); + + {user.email} + + + { handleMenuClose(); logout(); }}> + Logout + + { + window.open('https://discord.gg/5GbPjBUkws', '_blank'); + }}> + Discord + + { + window.open('https://www.youtube.com/@MaxunOSS/videos?ref=app', '_blank'); + }}> + YouTube + + { + window.open('https://x.com/maxun_io?ref=app', '_blank'); + }}> + Twiiter (X) + + + + ) : ( + <> + - Twiiter (X) - -
- - ) : ( - <> - - - Discard - - - - )} -
- ) : "" - } -
+ + Discard + + + + )} +
+ ) : "" + } +
); }; From 612622725d8c8542e6b4666a692d0773f3255cea Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 9 Dec 2024 01:05:39 +0530 Subject: [PATCH 067/102] feat: snackbar ui --- src/components/molecules/NavBar.tsx | 47 ++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 24fda0f0f..3f3907a9b 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -4,7 +4,7 @@ import styled from "styled-components"; import { stopRecording } from "../../api/recording"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { IconButton, Menu, MenuItem, Typography, Chip, Button, Modal, Tabs, Tab, Box, Snackbar } from "@mui/material"; -import { AccountCircle, Logout, Clear, YouTube, X, Update } from "@mui/icons-material"; +import { AccountCircle, Logout, Clear, YouTube, X, Update, Close } from "@mui/icons-material"; import { useNavigate } from 'react-router-dom'; import { AuthContext } from '../../context/auth'; import { SaveRecording } from '../molecules/SaveRecording'; @@ -98,14 +98,47 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => <> {isUpdateAvailable && ( + open={isUpdateAvailable} + onClose={() => setIsUpdateAvailable(false)} // Close when clicking the close button + message={ + + New version {latestVersion} available! Click "Upgrade Maxun" to update. + + } + action={ + <> + - } - /> + setIsUpdateAvailable(false)} // Close Snackbar + style={{ color: 'black' }} + > + + + + } + anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }} // Position of Snackbar + sx={{ + backgroundColor: 'white', + boxShadow: '0px 4px 10px rgba(0, 0, 0, 0.2)', + }} + /> + )}
Date: Mon, 9 Dec 2024 01:16:07 +0530 Subject: [PATCH 068/102] feat: snackbar ui --- src/components/molecules/NavBar.tsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/molecules/NavBar.tsx b/src/components/molecules/NavBar.tsx index 3f3907a9b..0b933b7ee 100644 --- a/src/components/molecules/NavBar.tsx +++ b/src/components/molecules/NavBar.tsx @@ -99,24 +99,23 @@ export const NavBar: React.FC = ({ recordingName, isRecording }) => {isUpdateAvailable && ( setIsUpdateAvailable(false)} // Close when clicking the close button + onClose={() => setIsUpdateAvailable(false)} message={ - - New version {latestVersion} available! Click "Upgrade Maxun" to update. - + `New version ${latestVersion} available! Click "Upgrade" to update.` } action={ <> - setIsUpdateAvailable(false)} - style={{ color: 'black' }} - > - - - - } - ContentProps={{ - sx: { - background: "white", - color: "black", } - }} - /> - + action={ + <> + + setIsUpdateAvailable(false)} + style={{ color: 'black' }} + > + + + + } + ContentProps={{ + sx: { + background: "white", + color: "black", + } + }} + /> + )}
Date: Mon, 9 Dec 2024 05:43:51 +0530 Subject: [PATCH 070/102] feat: proper rect and element info --- server/src/workflow-management/selector.ts | 168 +++++++++++++-------- 1 file changed, 104 insertions(+), 64 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 7908edc22..9587e8988 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -27,15 +27,33 @@ export const getElementInformation = async ( try { const elementInfo = await page.evaluate( async ({ x, y }) => { - // Find the initial element at the point - const initialElement = document.elementFromPoint(x, y) as HTMLElement; - - if (initialElement) { - // Simply use the direct parent, no complex logic - const parentElement = initialElement.parentElement; - - // Use the parent if it exists, otherwise use the initial element - const element = parentElement || initialElement; + const originalEl = document.elementFromPoint(x, y) as HTMLElement; + if (originalEl) { + let element = originalEl; + + // Generic parent finding logic based on visual containment + while (element.parentElement) { + const parentRect = element.parentElement.getBoundingClientRect(); + const childRect = element.getBoundingClientRect(); + + // Check if parent visually contains the child + const fullyContained = + parentRect.left <= childRect.left && + parentRect.right >= childRect.right && + parentRect.top <= childRect.top && + parentRect.bottom >= childRect.bottom; + + // Additional checks for more comprehensive containment + const significantOverlap = + (childRect.width * childRect.height) / + (parentRect.width * parentRect.height) > 0.5; + + if (fullyContained && significantOverlap) { + element = element.parentElement; + } else { + break; + } + } let info: { tagName: string; @@ -46,44 +64,34 @@ export const getElementInformation = async ( attributes?: Record; innerHTML?: string; outerHTML?: string; - parentTagName?: string; - parentClasses?: string[]; } = { - tagName: element.tagName, - parentTagName: element.parentElement?.tagName, - parentClasses: element.parentElement - ? Array.from(element.parentElement.classList) - : [] + tagName: element?.tagName ?? '', }; - // Collect attributes - info.attributes = Array.from(element.attributes).reduce( - (acc, attr) => { - acc[attr.name] = attr.value; - return acc; - }, - {} as Record - ); - - // Specific handling for different element types - if (element.tagName === 'A') { - const anchorElement = element as HTMLAnchorElement; - info.url = anchorElement.href; - info.innerText = anchorElement.innerText ?? ''; - } else if (element.tagName === 'IMG') { - const imgElement = element as HTMLImageElement; - info.imageUrl = imgElement.src; - } else { - // Check if element contains only text - info.hasOnlyText = element.children.length === 0 && - (element.innerText?.length ?? 0) > 0; + if (element) { + info.attributes = Array.from(element.attributes).reduce( + (acc, attr) => { + acc[attr.name] = attr.value; + return acc; + }, + {} as Record + ); + } + + // Existing tag-specific logic + if (element?.tagName === 'A') { + info.url = (element as HTMLAnchorElement).href; info.innerText = element.innerText ?? ''; + } else if (element?.tagName === 'IMG') { + info.imageUrl = (element as HTMLImageElement).src; + } else { + info.hasOnlyText = element?.children?.length === 0 && + element?.innerText?.length > 0; + info.innerText = element?.innerText ?? ''; } - // HTML content info.innerHTML = element.innerHTML; info.outerHTML = element.outerHTML; - return info; } return null; @@ -102,17 +110,32 @@ export const getRect = async (page: Page, coordinates: Coordinates) => { try { const rect = await page.evaluate( async ({ x, y }) => { - // Find the initial element at the point - const initialElement = document.elementFromPoint(x, y) as HTMLElement; - - if (initialElement) { - // Simply use the direct parent, no complex logic - const parentElement = initialElement.parentElement; - - // Use the parent if it exists, otherwise use the initial element - const element = parentElement || initialElement; + const originalEl = document.elementFromPoint(x, y) as HTMLElement; + if (originalEl) { + let element = originalEl; + + // Same parent-finding logic as in getElementInformation + while (element.parentElement) { + const parentRect = element.parentElement.getBoundingClientRect(); + const childRect = element.getBoundingClientRect(); + + const fullyContained = + parentRect.left <= childRect.left && + parentRect.right >= childRect.right && + parentRect.top <= childRect.top && + parentRect.bottom >= childRect.bottom; + + const significantOverlap = + (childRect.width * childRect.height) / + (parentRect.width * parentRect.height) > 0.5; + + if (fullyContained && significantOverlap) { + element = element.parentElement; + } else { + break; + } + } - // Get bounding rectangle const rectangle = element?.getBoundingClientRect(); if (rectangle) { @@ -134,10 +157,11 @@ export const getRect = async (page: Page, coordinates: Coordinates) => { return rect; } catch (error) { const { message, stack } = error as Error; - console.error('Error while retrieving selector:', message); - console.error('Stack:', stack); + logger.log('error', `Error while retrieving selector: ${message}`); + logger.log('error', `Stack: ${stack}`); } -}; +} + /** * Returns the best and unique css {@link Selectors} for the element on the page. @@ -774,25 +798,42 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates let depth = 0; const maxDepth = 2; - // Ensure we start with a valid element - let currentElement = element; - while (currentElement && currentElement !== document.body && depth < maxDepth) { - const selector = getNonUniqueSelector(currentElement); + while (element && element !== document.body && depth < maxDepth) { + const selector = getNonUniqueSelector(element); path.unshift(selector); - currentElement = currentElement.parentElement; + element = element.parentElement; depth++; } return path.join(' > '); } - // Find the initial element at the point - const initialElement = document.elementFromPoint(x, y) as HTMLElement; - - if (!initialElement) return null; + const originalEl = document.elementFromPoint(x, y) as HTMLElement; + if (!originalEl) return null; - // Prefer parent if exists, otherwise use initial element - const element = initialElement.parentElement || initialElement; + let element = originalEl; + + // Find the most appropriate parent element + while (element.parentElement) { + const parentRect = element.parentElement.getBoundingClientRect(); + const childRect = element.getBoundingClientRect(); + + const fullyContained = + parentRect.left <= childRect.left && + parentRect.right >= childRect.right && + parentRect.top <= childRect.top && + parentRect.bottom >= childRect.bottom; + + const significantOverlap = + (childRect.width * childRect.height) / + (parentRect.width * parentRect.height) > 0.5; + + if (fullyContained && significantOverlap) { + element = element.parentElement; + } else { + break; + } + } const generalSelector = getSelectorPath(element); return { @@ -807,7 +848,6 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates } }; - export const getChildSelectors = async (page: Page, parentSelector: string): Promise => { try { const childSelectors = await page.evaluate((parentSelector: string) => { From 560f0ea24f8f214e48867791b64c4b3630cb94b9 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 9 Dec 2024 14:41:59 +0530 Subject: [PATCH 071/102] fix: a tags --- server/src/workflow-management/selector.ts | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 9587e8988..6a80e7a3d 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -31,6 +31,11 @@ export const getElementInformation = async ( if (originalEl) { let element = originalEl; + if (originalEl.tagName === 'A') { + element = originalEl; + } else if (originalEl.parentElement?.tagName === 'A') { + element = originalEl.parentElement; + } else { // Generic parent finding logic based on visual containment while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); @@ -53,7 +58,7 @@ export const getElementInformation = async ( } else { break; } - } + } } let info: { tagName: string; @@ -114,7 +119,11 @@ export const getRect = async (page: Page, coordinates: Coordinates) => { if (originalEl) { let element = originalEl; - // Same parent-finding logic as in getElementInformation + if (originalEl.tagName === 'A') { + element = originalEl; + } else if (originalEl.parentElement?.tagName === 'A') { + element = originalEl.parentElement; + } else { while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); const childRect = element.getBoundingClientRect(); @@ -134,8 +143,9 @@ export const getRect = async (page: Page, coordinates: Coordinates) => { } else { break; } - } + }} + //element = element?.parentElement?.tagName === 'A' ? element?.parentElement : element; const rectangle = element?.getBoundingClientRect(); if (rectangle) { From 4da462f48bc037ee2eed4db48428b58293c44f1b Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 9 Dec 2024 15:04:45 +0530 Subject: [PATCH 072/102] fix: a tags --- server/src/workflow-management/selector.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 6a80e7a3d..f3925ec96 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -823,7 +823,11 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates let element = originalEl; - // Find the most appropriate parent element + if (originalEl.tagName === 'A') { + element = originalEl; + } else if (originalEl.parentElement?.tagName === 'A') { + element = originalEl.parentElement; + } else { while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); const childRect = element.getBoundingClientRect(); @@ -844,6 +848,7 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates break; } } + } const generalSelector = getSelectorPath(element); return { From 3dfe9117b0061d0a265f938f16a551f41ef03dae Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Mon, 9 Dec 2024 17:57:28 +0530 Subject: [PATCH 073/102] feat: inject cookie remover script --- server/src/browser-management/classes/RemoteBrowser.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index 769787da7..29beaa9b1 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -15,6 +15,7 @@ import { InterpreterSettings, RemoteBrowserOptions } from "../../types"; import { WorkflowGenerator } from "../../workflow-management/classes/Generator"; import { WorkflowInterpreter } from "../../workflow-management/classes/Interpreter"; import { getDecryptedProxyConfig } from '../../routes/proxy'; +import { getInjectableScript } from 'idcac-playwright'; chromium.use(stealthPlugin()); @@ -168,6 +169,7 @@ export class RemoteBrowser { this.currentPage.on('framenavigated', (frame) => { if (frame === this.currentPage?.mainFrame()) { + this.currentPage.evaluate(getInjectableScript()) this.socket.emit('urlChanged', this.currentPage.url()); } }); From 549a0d35fc4c7f8f7d02b25ab632f6e4f5940b0c Mon Sep 17 00:00:00 2001 From: amhsirak Date: Mon, 9 Dec 2024 18:40:00 +0530 Subject: [PATCH 074/102] chore(deps): install idcac-playwright --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 977daada9..a7d634f3c 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "fortawesome": "^0.0.1-security", "google-auth-library": "^9.14.1", "googleapis": "^144.0.0", + "idcac-playwright": "^0.1.3", "ioredis": "^5.4.1", "joi": "^17.6.0", "jsonwebtoken": "^9.0.2", From 117dddc2ff8337320a162af350632ee69633221d Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Mon, 9 Dec 2024 18:49:23 +0530 Subject: [PATCH 075/102] feat: inject cookie remover script --- server/src/browser-management/classes/RemoteBrowser.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index 29beaa9b1..0081cb5c4 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -374,6 +374,7 @@ export class RemoteBrowser { this.currentPage.on('framenavigated', (frame) => { if (frame === this.currentPage?.mainFrame()) { + this.currentPage.evaluate(getInjectableScript()); this.socket.emit('urlChanged', this.currentPage.url()); } }); @@ -406,6 +407,7 @@ export class RemoteBrowser { if (this.currentPage) { this.currentPage.on('framenavigated', (frame) => { if (frame === this.currentPage?.mainFrame()) { + this.currentPage.evaluate(getInjectableScript()); this.socket.emit('urlChanged', this.currentPage.url()); } }); From 386e7c9a98d3c519e6ad0d628318aaa355ce2134 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Mon, 9 Dec 2024 20:13:46 +0530 Subject: [PATCH 076/102] feat: add programmatic click event for clickNext --- maxun-core/src/interpret.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index a7a5de47e..f0b10936c 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -475,6 +475,8 @@ export default class Interpreter extends EventEmitter { case 'clickNext': const pageResults = await page.evaluate((cfg) => window.scrapeList(cfg), config); + // console.log("Page results:", pageResults); + // Filter out already scraped items const newResults = pageResults.filter(item => { const uniqueKey = JSON.stringify(item); @@ -482,9 +484,9 @@ export default class Interpreter extends EventEmitter { scrapedItems.add(uniqueKey); // Mark as scraped return true; }); - + allResults = allResults.concat(newResults); - + if (config.limit && allResults.length >= config.limit) { return allResults.slice(0, config.limit); } @@ -494,7 +496,7 @@ export default class Interpreter extends EventEmitter { return allResults; // No more pages to scrape } await Promise.all([ - nextButton.click(), + nextButton.dispatchEvent('click'), page.waitForNavigation({ waitUntil: 'networkidle' }) ]); From b84e9186b9ccf71cb05423b3923bb0c7c745b3d3 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Mon, 9 Dec 2024 22:05:50 +0530 Subject: [PATCH 077/102] fix: inject cookie script on page load --- .../browser-management/classes/RemoteBrowser.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index 0081cb5c4..7e0a7d1ad 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -167,13 +167,16 @@ export class RemoteBrowser { this.context = await this.browser.newContext(contextOptions); this.currentPage = await this.context.newPage(); - this.currentPage.on('framenavigated', (frame) => { + this.currentPage.on('framenavigated', (frame) => { if (frame === this.currentPage?.mainFrame()) { - this.currentPage.evaluate(getInjectableScript()) this.socket.emit('urlChanged', this.currentPage.url()); } }); + this.currentPage.on('load', (page) => { + page.evaluate(getInjectableScript()) + }) + // await this.currentPage.setExtraHTTPHeaders({ // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3' // }); @@ -374,11 +377,14 @@ export class RemoteBrowser { this.currentPage.on('framenavigated', (frame) => { if (frame === this.currentPage?.mainFrame()) { - this.currentPage.evaluate(getInjectableScript()); this.socket.emit('urlChanged', this.currentPage.url()); } }); + this.currentPage.on('load', (page) => { + page.evaluate(getInjectableScript()) + }) + //await this.currentPage.setViewportSize({ height: 400, width: 900 }) this.client = await this.currentPage.context().newCDPSession(this.currentPage); this.socket.emit('urlChanged', this.currentPage.url()); @@ -407,10 +413,13 @@ export class RemoteBrowser { if (this.currentPage) { this.currentPage.on('framenavigated', (frame) => { if (frame === this.currentPage?.mainFrame()) { - this.currentPage.evaluate(getInjectableScript()); this.socket.emit('urlChanged', this.currentPage.url()); } }); + + this.currentPage.on('load', (page) => { + page.evaluate(getInjectableScript()) + }) // this.currentPage.on('load', (page) => { // this.socket.emit('urlChanged', page.url()); // }) From f561ef7f559dac83913ca23773723088fc1d5b07 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Tue, 10 Dec 2024 04:02:28 +0530 Subject: [PATCH 078/102] feat: apply conditional visual containment --- server/src/workflow-management/selector.ts | 49 ++++++++++++++++------ 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index f3925ec96..fa58be673 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -31,16 +31,28 @@ export const getElementInformation = async ( if (originalEl) { let element = originalEl; - if (originalEl.tagName === 'A') { - element = originalEl; - } else if (originalEl.parentElement?.tagName === 'A') { - element = originalEl.parentElement; - } else { + // if (originalEl.tagName === 'A') { + // element = originalEl; + // } else if (originalEl.parentElement?.tagName === 'A') { + // element = originalEl.parentElement; + // } else { // Generic parent finding logic based on visual containment + const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', + 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', + 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', + 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', + 'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT', + 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET', + 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT', 'A' + ]; while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); const childRect = element.getBoundingClientRect(); + if (!containerTags.includes(element.parentElement.tagName)) { + break; + } + // Check if parent visually contains the child const fullyContained = parentRect.left <= childRect.left && @@ -57,7 +69,7 @@ export const getElementInformation = async ( element = element.parentElement; } else { break; - } + // } } } let info: { @@ -119,15 +131,28 @@ export const getRect = async (page: Page, coordinates: Coordinates) => { if (originalEl) { let element = originalEl; - if (originalEl.tagName === 'A') { - element = originalEl; - } else if (originalEl.parentElement?.tagName === 'A') { - element = originalEl.parentElement; - } else { + // if (originalEl.tagName === 'A') { + // element = originalEl; + // } else if (originalEl.parentElement?.tagName === 'A') { + // element = originalEl.parentElement; + // } else { + const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', + 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', + 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', + 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', + 'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT', + 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET', + 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT', 'A' + ]; while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); const childRect = element.getBoundingClientRect(); + if (!containerTags.includes(element.parentElement.tagName)) { + break; + } + + const fullyContained = parentRect.left <= childRect.left && parentRect.right >= childRect.right && @@ -142,7 +167,7 @@ export const getRect = async (page: Page, coordinates: Coordinates) => { element = element.parentElement; } else { break; - } + // } }} //element = element?.parentElement?.tagName === 'A' ? element?.parentElement : element; From 668a67057f9542027a08f37e274a0f6b78ea5e0e Mon Sep 17 00:00:00 2001 From: amhsirak Date: Tue, 10 Dec 2024 19:24:28 +0530 Subject: [PATCH 079/102] fix: include visual containment for capture list selection --- server/src/workflow-management/selector.ts | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index fa58be673..917ac561c 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -847,16 +847,24 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates if (!originalEl) return null; let element = originalEl; + + const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', + 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', + 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', + 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', + 'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT', + 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET', + 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT', 'A' + ]; - if (originalEl.tagName === 'A') { - element = originalEl; - } else if (originalEl.parentElement?.tagName === 'A') { - element = originalEl.parentElement; - } else { while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); const childRect = element.getBoundingClientRect(); + if (!containerTags.includes(element.parentElement.tagName)) { + break; + } + const fullyContained = parentRect.left <= childRect.left && parentRect.right >= childRect.right && @@ -871,7 +879,6 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates element = element.parentElement; } else { break; - } } } From 142c90ae1c0e0fc9441bcedaff1430dd3eab7abd Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Tue, 10 Dec 2024 20:32:01 +0530 Subject: [PATCH 080/102] fix: handle context destroyed, frame navigation URL --- .../classes/RemoteBrowser.ts | 94 +++++++++++++------ 1 file changed, 64 insertions(+), 30 deletions(-) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index 7e0a7d1ad..417e8964d 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -66,6 +66,8 @@ export class RemoteBrowser { maxRepeats: 1, }; + private lastEmittedUrl: string | null = null; + /** * {@link WorkflowGenerator} instance specific to the remote browser. */ @@ -88,6 +90,64 @@ export class RemoteBrowser { this.generator = new WorkflowGenerator(socket); } + /** + * Normalizes URLs to prevent navigation loops while maintaining consistent format + */ + private normalizeUrl(url: string): string { + try { + const parsedUrl = new URL(url); + // Remove trailing slashes except for root path + parsedUrl.pathname = parsedUrl.pathname.replace(/\/+$/, '') || '/'; + // Ensure consistent protocol handling + parsedUrl.protocol = parsedUrl.protocol.toLowerCase(); + return parsedUrl.toString(); + } catch { + return url; + } + } + + /** + * Determines if a URL change is significant enough to emit + */ + private shouldEmitUrlChange(newUrl: string): boolean { + if (!this.lastEmittedUrl) { + return true; + } + const normalizedNew = this.normalizeUrl(newUrl); + const normalizedLast = this.normalizeUrl(this.lastEmittedUrl); + return normalizedNew !== normalizedLast; + } + + private async setupPageEventListeners(page: Page) { + page.on('framenavigated', async (frame) => { + if (frame === page.mainFrame()) { + const currentUrl = page.url(); + if (this.shouldEmitUrlChange(currentUrl)) { + this.lastEmittedUrl = currentUrl; + this.socket.emit('urlChanged', currentUrl); + } + } + }); + + // Handle page load events with retry mechanism + page.on('load', async () => { + const injectScript = async (): Promise => { + try { + await page.waitForLoadState('networkidle', { timeout: 5000 }); + + await page.evaluate(getInjectableScript()); + return true; + } catch (error: any) { + logger.log('warn', `Script injection attempt failed: ${error.message}`); + return false; + } + }; + + const success = await injectScript(); + console.log("Script injection result:", success); + }); + } + /** * An asynchronous constructor for asynchronously initialized properties. * Must be called right after creating an instance of RemoteBrowser class. @@ -167,15 +227,7 @@ export class RemoteBrowser { this.context = await this.browser.newContext(contextOptions); this.currentPage = await this.context.newPage(); - this.currentPage.on('framenavigated', (frame) => { - if (frame === this.currentPage?.mainFrame()) { - this.socket.emit('urlChanged', this.currentPage.url()); - } - }); - - this.currentPage.on('load', (page) => { - page.evaluate(getInjectableScript()) - }) + await this.setupPageEventListeners(this.currentPage); // await this.currentPage.setExtraHTTPHeaders({ // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3' @@ -375,15 +427,7 @@ export class RemoteBrowser { await this.stopScreencast(); this.currentPage = page; - this.currentPage.on('framenavigated', (frame) => { - if (frame === this.currentPage?.mainFrame()) { - this.socket.emit('urlChanged', this.currentPage.url()); - } - }); - - this.currentPage.on('load', (page) => { - page.evaluate(getInjectableScript()) - }) + await this.setupPageEventListeners(this.currentPage); //await this.currentPage.setViewportSize({ height: 400, width: 900 }) this.client = await this.currentPage.context().newCDPSession(this.currentPage); @@ -411,18 +455,8 @@ export class RemoteBrowser { await this.currentPage?.close(); this.currentPage = newPage; if (this.currentPage) { - this.currentPage.on('framenavigated', (frame) => { - if (frame === this.currentPage?.mainFrame()) { - this.socket.emit('urlChanged', this.currentPage.url()); - } - }); - - this.currentPage.on('load', (page) => { - page.evaluate(getInjectableScript()) - }) - // this.currentPage.on('load', (page) => { - // this.socket.emit('urlChanged', page.url()); - // }) + await this.setupPageEventListeners(this.currentPage); + this.client = await this.currentPage.context().newCDPSession(this.currentPage); await this.subscribeToScreencast(); } else { From c7af54ebe061abcf1c5c5bcb26e4182fa3f8d254 Mon Sep 17 00:00:00 2001 From: RohitR311 Date: Tue, 10 Dec 2024 20:33:35 +0530 Subject: [PATCH 081/102] feat: add fallback mechanism for click and waitForLoadState action --- maxun-core/src/interpret.ts | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index bd90e40f7..afea8e476 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -469,6 +469,16 @@ export default class Interpreter extends EventEmitter { }), }; + const executeAction = async (invokee: any, methodName: string, args: any) => { + console.log("Executing action:", methodName, args); + if (!args || Array.isArray(args)) { + await (invokee[methodName])(...(args ?? [])); + } else { + await (invokee[methodName])(args); + } + }; + + for (const step of steps) { this.log(`Launching ${String(step.action)}`, Level.LOG); @@ -486,10 +496,20 @@ export default class Interpreter extends EventEmitter { invokee = invokee[level]; } - if (!step.args || Array.isArray(step.args)) { - await (invokee[methodName])(...(step.args ?? [])); + if (methodName === 'waitForLoadState') { + try { + await executeAction(invokee, methodName, step.args); + } catch (error) { + await executeAction(invokee, methodName, 'domcontentloaded'); + } + } else if (methodName === 'click') { + try { + await executeAction(invokee, methodName, step.args); + } catch (error) { + await executeAction(invokee, methodName, [step.args[0], { force: true }]); + } } else { - await (invokee[methodName])(step.args); + await executeAction(invokee, methodName, step.args); } } @@ -571,7 +591,7 @@ export default class Interpreter extends EventEmitter { return allResults; } // Click the 'Load More' button to load additional items - await loadMoreButton.click(); + await loadMoreButton.dispatchEvent('click'); await page.waitForTimeout(2000); // Wait for new items to load // After clicking 'Load More', scroll down to load more items await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); From fd16aff31c01d95b5cf8df464fba548adea55a28 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Tue, 10 Dec 2024 23:05:17 +0530 Subject: [PATCH 082/102] feat: emit setGetList:false socket event in pagination mode --- src/context/browserActions.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context/browserActions.tsx b/src/context/browserActions.tsx index ef303f822..55ca1b371 100644 --- a/src/context/browserActions.tsx +++ b/src/context/browserActions.tsx @@ -53,6 +53,7 @@ export const ActionProvider = ({ children }: { children: ReactNode }) => { const startPaginationMode = () => { setPaginationMode(true); setCaptureStage('pagination'); + socket?.emit('setGetList', { getList: false }); }; const stopPaginationMode = () => setPaginationMode(false); @@ -75,7 +76,6 @@ export const ActionProvider = ({ children }: { children: ReactNode }) => { const stopGetList = () => { setGetList(false); - socket?.emit('setGetList', { getList: false }); setPaginationType(''); setLimitType(''); setCustomLimit(''); From c994072ef70b25d516a0b9d59f832949418ab6c3 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 02:00:43 +0530 Subject: [PATCH 083/102] feat: pass listSelector getRect and getElementInfo --- server/src/workflow-management/classes/Generator.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/server/src/workflow-management/classes/Generator.ts b/server/src/workflow-management/classes/Generator.ts index c9dc3385b..1e81cd4a1 100644 --- a/server/src/workflow-management/classes/Generator.ts +++ b/server/src/workflow-management/classes/Generator.ts @@ -541,7 +541,9 @@ export class WorkflowGenerator { * @returns {Promise} */ private generateSelector = async (page: Page, coordinates: Coordinates, action: ActionType) => { - const elementInfo = await getElementInformation(page, coordinates); + const elementInfo = await getElementInformation(page, coordinates, this.listSelector); + + console.log(`List selector: ${this.listSelector}`) const selectorBasedOnCustomAction = (this.getList === true) ? await getNonUniqueSelectors(page, coordinates) @@ -570,9 +572,9 @@ export class WorkflowGenerator { * @returns {Promise} */ public generateDataForHighlighter = async (page: Page, coordinates: Coordinates) => { - const rect = await getRect(page, coordinates); + const rect = await getRect(page, coordinates, this.listSelector); const displaySelector = await this.generateSelector(page, coordinates, ActionType.Click); - const elementInfo = await getElementInformation(page, coordinates); + const elementInfo = await getElementInformation(page, coordinates, this.listSelector); if (rect) { if (this.getList === true) { if (this.listSelector !== '') { From 81bbba473fce2e37f031329305e9647c637940a6 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 02:01:55 +0530 Subject: [PATCH 084/102] feat: condtionally handle getRect & getelementInfo --- server/src/workflow-management/selector.ts | 362 ++++++++++++--------- 1 file changed, 215 insertions(+), 147 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 917ac561c..b383d6531 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -22,100 +22,144 @@ type Workflow = WorkflowFile["workflow"]; */ export const getElementInformation = async ( page: Page, - coordinates: Coordinates + coordinates: Coordinates, + listSelector: string, ) => { try { - const elementInfo = await page.evaluate( - async ({ x, y }) => { - const originalEl = document.elementFromPoint(x, y) as HTMLElement; - if (originalEl) { - let element = originalEl; - - // if (originalEl.tagName === 'A') { - // element = originalEl; - // } else if (originalEl.parentElement?.tagName === 'A') { - // element = originalEl.parentElement; - // } else { - // Generic parent finding logic based on visual containment - const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', - 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', - 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', - 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', - 'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT', - 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET', - 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT', 'A' - ]; - while (element.parentElement) { - const parentRect = element.parentElement.getBoundingClientRect(); - const childRect = element.getBoundingClientRect(); - - if (!containerTags.includes(element.parentElement.tagName)) { - break; + if (listSelector !== '') { + // Old implementation + const elementInfo = await page.evaluate( + async ({ x, y }) => { + const el = document.elementFromPoint(x, y) as HTMLElement; + if (el) { + const { parentElement } = el; + const element = parentElement?.tagName === 'A' ? parentElement : el; + let info: { + tagName: string; + hasOnlyText?: boolean; + innerText?: string; + url?: string; + imageUrl?: string; + attributes?: Record; + innerHTML?: string; + outerHTML?: string; + } = { + tagName: element?.tagName ?? '', + }; + if (element) { + info.attributes = Array.from(element.attributes).reduce( + (acc, attr) => { + acc[attr.name] = attr.value; + return acc; + }, + {} as Record + ); } + // Gather specific information based on the tag + if (element?.tagName === 'A') { + info.url = (element as HTMLAnchorElement).href; + info.innerText = element.innerText ?? ''; + } else if (element?.tagName === 'IMG') { + info.imageUrl = (element as HTMLImageElement).src; + } else { + info.hasOnlyText = element?.children?.length === 0 && + element?.innerText?.length > 0; + info.innerText = element?.innerText ?? ''; + } + info.innerHTML = element.innerHTML; + info.outerHTML = element.outerHTML; + return info; + } + return null; + }, + { x: coordinates.x, y: coordinates.y }, + ); + return elementInfo; + } else { + // New implementation + const elementInfo = await page.evaluate( + async ({ x, y }) => { + const originalEl = document.elementFromPoint(x, y) as HTMLElement; + if (originalEl) { + let element = originalEl; + + const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', + 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', + 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', + 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', + 'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT', + 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET', + 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT' + ]; + while (element.parentElement) { + const parentRect = element.parentElement.getBoundingClientRect(); + const childRect = element.getBoundingClientRect(); + + if (!containerTags.includes(element.parentElement.tagName)) { + break; + } - // Check if parent visually contains the child - const fullyContained = - parentRect.left <= childRect.left && - parentRect.right >= childRect.right && - parentRect.top <= childRect.top && - parentRect.bottom >= childRect.bottom; + const fullyContained = + parentRect.left <= childRect.left && + parentRect.right >= childRect.right && + parentRect.top <= childRect.top && + parentRect.bottom >= childRect.bottom; - // Additional checks for more comprehensive containment - const significantOverlap = - (childRect.width * childRect.height) / - (parentRect.width * parentRect.height) > 0.5; + const significantOverlap = + (childRect.width * childRect.height) / + (parentRect.width * parentRect.height) > 0.5; - if (fullyContained && significantOverlap) { - element = element.parentElement; - } else { - break; - // } - } } - - let info: { - tagName: string; - hasOnlyText?: boolean; - innerText?: string; - url?: string; - imageUrl?: string; - attributes?: Record; - innerHTML?: string; - outerHTML?: string; - } = { - tagName: element?.tagName ?? '', - }; + if (fullyContained && significantOverlap) { + element = element.parentElement; + } else { + break; + } + } - if (element) { - info.attributes = Array.from(element.attributes).reduce( - (acc, attr) => { - acc[attr.name] = attr.value; - return acc; - }, - {} as Record - ); - } + let info: { + tagName: string; + hasOnlyText?: boolean; + innerText?: string; + url?: string; + imageUrl?: string; + attributes?: Record; + innerHTML?: string; + outerHTML?: string; + } = { + tagName: element?.tagName ?? '', + }; - // Existing tag-specific logic - if (element?.tagName === 'A') { - info.url = (element as HTMLAnchorElement).href; - info.innerText = element.innerText ?? ''; - } else if (element?.tagName === 'IMG') { - info.imageUrl = (element as HTMLImageElement).src; - } else { - info.hasOnlyText = element?.children?.length === 0 && - element?.innerText?.length > 0; - info.innerText = element?.innerText ?? ''; - } + if (element) { + info.attributes = Array.from(element.attributes).reduce( + (acc, attr) => { + acc[attr.name] = attr.value; + return acc; + }, + {} as Record + ); + } - info.innerHTML = element.innerHTML; - info.outerHTML = element.outerHTML; - return info; - } - return null; - }, - { x: coordinates.x, y: coordinates.y }, - ); - return elementInfo; + if (element?.tagName === 'A') { + info.url = (element as HTMLAnchorElement).href; + info.innerText = element.innerText ?? ''; + } else if (element?.tagName === 'IMG') { + info.imageUrl = (element as HTMLImageElement).src; + } else { + info.hasOnlyText = element?.children?.length === 0 && + element?.innerText?.length > 0; + info.innerText = element?.innerText ?? ''; + } + + info.innerHTML = element.innerHTML; + info.outerHTML = element.outerHTML; + return info; + } + return null; + }, + { x: coordinates.x, y: coordinates.y }, + ); + return elementInfo; + } } catch (error) { const { message, stack } = error as Error; console.error('Error while retrieving selector:', message); @@ -123,79 +167,103 @@ export const getElementInformation = async ( } }; -export const getRect = async (page: Page, coordinates: Coordinates) => { +export const getRect = async (page: Page, coordinates: Coordinates, listSelector: string) => { try { - const rect = await page.evaluate( - async ({ x, y }) => { - const originalEl = document.elementFromPoint(x, y) as HTMLElement; - if (originalEl) { - let element = originalEl; - - // if (originalEl.tagName === 'A') { - // element = originalEl; - // } else if (originalEl.parentElement?.tagName === 'A') { - // element = originalEl.parentElement; - // } else { - const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', - 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', - 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', - 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', - 'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT', - 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET', - 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT', 'A' - ]; - while (element.parentElement) { - const parentRect = element.parentElement.getBoundingClientRect(); - const childRect = element.getBoundingClientRect(); - - if (!containerTags.includes(element.parentElement.tagName)) { - break; + if (listSelector !== '') { + // Old implementation + const rect = await page.evaluate( + async ({ x, y }) => { + const el = document.elementFromPoint(x, y) as HTMLElement; + if (el) { + const { parentElement } = el; + // Match the logic in recorder.ts for link clicks + const element = parentElement?.tagName === 'A' ? parentElement : el; + const rectangle = element?.getBoundingClientRect(); + if (rectangle) { + return { + x: rectangle.x, + y: rectangle.y, + width: rectangle.width, + height: rectangle.height, + top: rectangle.top, + right: rectangle.right, + bottom: rectangle.bottom, + left: rectangle.left, + }; } - + } + }, + { x: coordinates.x, y: coordinates.y }, + ); + return rect; + } else { + // New implementation + const rect = await page.evaluate( + async ({ x, y }) => { + const originalEl = document.elementFromPoint(x, y) as HTMLElement; + if (originalEl) { + let element = originalEl; + + const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', + 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', + 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', + 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', + 'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT', + 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET', + 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT' + ]; + while (element.parentElement) { + const parentRect = element.parentElement.getBoundingClientRect(); + const childRect = element.getBoundingClientRect(); + + if (!containerTags.includes(element.parentElement.tagName)) { + break; + } - const fullyContained = - parentRect.left <= childRect.left && - parentRect.right >= childRect.right && - parentRect.top <= childRect.top && - parentRect.bottom >= childRect.bottom; + const fullyContained = + parentRect.left <= childRect.left && + parentRect.right >= childRect.right && + parentRect.top <= childRect.top && + parentRect.bottom >= childRect.bottom; - const significantOverlap = - (childRect.width * childRect.height) / - (parentRect.width * parentRect.height) > 0.5; + const significantOverlap = + (childRect.width * childRect.height) / + (parentRect.width * parentRect.height) > 0.5; - if (fullyContained && significantOverlap) { - element = element.parentElement; - } else { - break; - // } - }} - - //element = element?.parentElement?.tagName === 'A' ? element?.parentElement : element; - const rectangle = element?.getBoundingClientRect(); - - if (rectangle) { - return { - x: rectangle.x, - y: rectangle.y, - width: rectangle.width, - height: rectangle.height, - top: rectangle.top, - right: rectangle.right, - bottom: rectangle.bottom, - left: rectangle.left, - }; + if (fullyContained && significantOverlap) { + element = element.parentElement; + } else { + break; + } + } + + const rectangle = element?.getBoundingClientRect(); + + if (rectangle) { + return { + x: rectangle.x, + y: rectangle.y, + width: rectangle.width, + height: rectangle.height, + top: rectangle.top, + right: rectangle.right, + bottom: rectangle.bottom, + left: rectangle.left, + }; + } } - } - }, - { x: coordinates.x, y: coordinates.y }, - ); - return rect; + return null; + }, + { x: coordinates.x, y: coordinates.y }, + ); + return rect; + } } catch (error) { const { message, stack } = error as Error; logger.log('error', `Error while retrieving selector: ${message}`); logger.log('error', `Stack: ${stack}`); } -} +}; /** From a34e8657735de66fdab71b08346fd1695acd1b21 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 02:02:09 +0530 Subject: [PATCH 085/102] chore: lint --- server/src/workflow-management/selector.ts | 34 +++++++++++----------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index b383d6531..66186a80f 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -82,8 +82,8 @@ export const getElementInformation = async ( const originalEl = document.elementFromPoint(x, y) as HTMLElement; if (originalEl) { let element = originalEl; - - const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', + + const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', @@ -99,14 +99,14 @@ export const getElementInformation = async ( break; } - const fullyContained = + const fullyContained = parentRect.left <= childRect.left && parentRect.right >= childRect.right && parentRect.top <= childRect.top && parentRect.bottom >= childRect.bottom; - const significantOverlap = - (childRect.width * childRect.height) / + const significantOverlap = + (childRect.width * childRect.height) / (parentRect.width * parentRect.height) > 0.5; if (fullyContained && significantOverlap) { @@ -203,8 +203,8 @@ export const getRect = async (page: Page, coordinates: Coordinates, listSelector const originalEl = document.elementFromPoint(x, y) as HTMLElement; if (originalEl) { let element = originalEl; - - const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', + + const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', @@ -220,14 +220,14 @@ export const getRect = async (page: Page, coordinates: Coordinates, listSelector break; } - const fullyContained = + const fullyContained = parentRect.left <= childRect.left && parentRect.right >= childRect.right && parentRect.top <= childRect.top && parentRect.bottom >= childRect.bottom; - const significantOverlap = - (childRect.width * childRect.height) / + const significantOverlap = + (childRect.width * childRect.height) / (parentRect.width * parentRect.height) > 0.5; if (fullyContained && significantOverlap) { @@ -238,7 +238,7 @@ export const getRect = async (page: Page, coordinates: Coordinates, listSelector } const rectangle = element?.getBoundingClientRect(); - + if (rectangle) { return { x: rectangle.x, @@ -916,7 +916,7 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates let element = originalEl; - const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', + const containerTags = ['DIV', 'SECTION', 'ARTICLE', 'MAIN', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', 'ADDRESS', 'BLOCKQUOTE', 'DETAILS', 'DIALOG', 'FIGURE', 'FIGCAPTION', 'MAIN', 'MARK', 'SUMMARY', 'TIME', 'TABLE', 'THEAD', 'TBODY', 'TFOOT', 'TR', 'TH', 'TD', 'CAPTION', 'COLGROUP', 'COL', 'FORM', 'FIELDSET', 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', @@ -924,7 +924,7 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET', 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT', 'A' ]; - + while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); const childRect = element.getBoundingClientRect(); @@ -933,22 +933,22 @@ export const getNonUniqueSelectors = async (page: Page, coordinates: Coordinates break; } - const fullyContained = + const fullyContained = parentRect.left <= childRect.left && parentRect.right >= childRect.right && parentRect.top <= childRect.top && parentRect.bottom >= childRect.bottom; - const significantOverlap = - (childRect.width * childRect.height) / + const significantOverlap = + (childRect.width * childRect.height) / (parentRect.width * parentRect.height) > 0.5; if (fullyContained && significantOverlap) { element = element.parentElement; } else { break; + } } - } const generalSelector = getSelectorPath(element); return { From 8533ea536291f4294037b5174f0471e6f03b5e0b Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 02:02:37 +0530 Subject: [PATCH 086/102] chore: remove unused imprts --- server/src/workflow-management/selector.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 66186a80f..49d56f365 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -1,8 +1,7 @@ import { Page } from "playwright"; -import { Action, ActionType, Coordinates, TagName } from "../types"; +import { Coordinates } from "../types"; import { WhereWhatPair, WorkflowFile } from "maxun-core"; import logger from "../logger"; -import { getBestSelectorForAction } from "./utils"; /*TODO: 1. Handle TS errors (here we definetly know better) From 0b1b2436834bed1c6ed67dcbf8697e7c81fcdce3 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 02:02:52 +0530 Subject: [PATCH 087/102] chore: remove todo --- server/src/workflow-management/selector.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 49d56f365..5fc621ba6 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -3,11 +3,6 @@ import { Coordinates } from "../types"; import { WhereWhatPair, WorkflowFile } from "maxun-core"; import logger from "../logger"; -/*TODO: -1. Handle TS errors (here we definetly know better) -2. Add pending function descriptions + thought process (esp. selector generation) -*/ - type Workflow = WorkflowFile["workflow"]; /** From fea0c0331b6d2f347471fd7627b1262402f12611 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 02:04:16 +0530 Subject: [PATCH 088/102] chore: cleanup --- server/src/workflow-management/selector.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 5fc621ba6..4c6d3f87d 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -21,7 +21,6 @@ export const getElementInformation = async ( ) => { try { if (listSelector !== '') { - // Old implementation const elementInfo = await page.evaluate( async ({ x, y }) => { const el = document.elementFromPoint(x, y) as HTMLElement; @@ -70,7 +69,6 @@ export const getElementInformation = async ( ); return elementInfo; } else { - // New implementation const elementInfo = await page.evaluate( async ({ x, y }) => { const originalEl = document.elementFromPoint(x, y) as HTMLElement; @@ -83,7 +81,7 @@ export const getElementInformation = async ( 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', 'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT', 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET', - 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT' + 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT', 'A' ]; while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); @@ -164,7 +162,6 @@ export const getElementInformation = async ( export const getRect = async (page: Page, coordinates: Coordinates, listSelector: string) => { try { if (listSelector !== '') { - // Old implementation const rect = await page.evaluate( async ({ x, y }) => { const el = document.elementFromPoint(x, y) as HTMLElement; @@ -191,7 +188,6 @@ export const getRect = async (page: Page, coordinates: Coordinates, listSelector ); return rect; } else { - // New implementation const rect = await page.evaluate( async ({ x, y }) => { const originalEl = document.elementFromPoint(x, y) as HTMLElement; @@ -204,7 +200,7 @@ export const getRect = async (page: Page, coordinates: Coordinates, listSelector 'LEGEND', 'LABEL', 'INPUT', 'BUTTON', 'SELECT', 'DATALIST', 'OPTGROUP', 'OPTION', 'TEXTAREA', 'OUTPUT', 'PROGRESS', 'METER', 'DETAILS', 'SUMMARY', 'MENU', 'MENUITEM', 'MENUITEM', 'APPLET', 'EMBED', 'OBJECT', 'PARAM', 'VIDEO', 'AUDIO', 'SOURCE', 'TRACK', 'CANVAS', 'MAP', 'AREA', 'SVG', 'IFRAME', 'FRAME', 'FRAMESET', - 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT' + 'LI', 'UL', 'OL', 'DL', 'DT', 'DD', 'HR', 'P', 'PRE', 'LISTING', 'PLAINTEXT', 'A' ]; while (element.parentElement) { const parentRect = element.parentElement.getBoundingClientRect(); From b72baca821ffec5ef3b31a231e356c82503fc489 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 02:08:08 +0530 Subject: [PATCH 089/102] docs: re-add jsdoc --- server/src/workflow-management/selector.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 4c6d3f87d..891c0e3ba 100644 --- a/server/src/workflow-management/selector.ts +++ b/server/src/workflow-management/selector.ts @@ -6,13 +6,12 @@ import logger from "../logger"; type Workflow = WorkflowFile["workflow"]; /** - * Returns a {@link Rectangle} object representing - * the coordinates, width, height and corner points of the element. - * If an element is not found, returns null. + * Checks the basic info about an element and returns a {@link BaseActionInfo} object. + * If the element is not found, returns undefined. * @param page The page instance. * @param coordinates Coordinates of an element. * @category WorkflowManagement-Selectors - * @returns {Promise} + * @returns {Promise} */ export const getElementInformation = async ( page: Page, @@ -159,6 +158,15 @@ export const getElementInformation = async ( } }; +/** + * Returns a {@link Rectangle} object representing + * the coordinates, width, height and corner points of the element. + * If an element is not found, returns null. + * @param page The page instance. + * @param coordinates Coordinates of an element. + * @category WorkflowManagement-Selectors + * @returns {Promise} + */ export const getRect = async (page: Page, coordinates: Coordinates, listSelector: string) => { try { if (listSelector !== '') { From d6e4b8860fd05e1abf471b6d85891d4a34e9a6bd Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 02:12:43 +0530 Subject: [PATCH 090/102] chore: remove console logs --- server/src/workflow-management/classes/Generator.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/server/src/workflow-management/classes/Generator.ts b/server/src/workflow-management/classes/Generator.ts index 1e81cd4a1..57be015ed 100644 --- a/server/src/workflow-management/classes/Generator.ts +++ b/server/src/workflow-management/classes/Generator.ts @@ -542,9 +542,6 @@ export class WorkflowGenerator { */ private generateSelector = async (page: Page, coordinates: Coordinates, action: ActionType) => { const elementInfo = await getElementInformation(page, coordinates, this.listSelector); - - console.log(`List selector: ${this.listSelector}`) - const selectorBasedOnCustomAction = (this.getList === true) ? await getNonUniqueSelectors(page, coordinates) : await getSelectors(page, coordinates); @@ -580,8 +577,6 @@ export class WorkflowGenerator { if (this.listSelector !== '') { const childSelectors = await getChildSelectors(page, this.listSelector || ''); this.socket.emit('highlighter', { rect, selector: displaySelector, elementInfo, childSelectors }) - console.log(`Child Selectors: ${childSelectors}`) - console.log(`Parent Selector: ${this.listSelector}`) } else { this.socket.emit('highlighter', { rect, selector: displaySelector, elementInfo }); } From 2135f2eb6227fcb7fd86fd32155161b6d211e438 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 13:57:09 +0530 Subject: [PATCH 091/102] chore: use node:22-slim as base --- server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Dockerfile b/server/Dockerfile index ae26e8ebc..877b781ec 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/playwright:v1.40.0-jammy +FROM node:22-slim # Set working directory WORKDIR /app From 08f8684b98dc2d1dc56c19c6805a71b4f444600b Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 14:05:14 +0530 Subject: [PATCH 092/102] chore: use BACKEND_PORT --- server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/Dockerfile b/server/Dockerfile index 877b781ec..f8853fb2b 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -50,7 +50,7 @@ RUN apt-get update && apt-get install -y \ # RUN chmod +x ./start.sh # Expose the backend port -EXPOSE 8080 +EXPOSE ${BACKEND_PORT:-8080} # Start the backend using the start script CMD ["npm", "run", "server"] \ No newline at end of file From a2b87762015eff62c439c130ce8144f2eeaf4a7a Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 14:06:08 +0530 Subject: [PATCH 093/102] chore: use BACKEND_PORT --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 719e7814c..032dc9823 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ COPY vite.config.js ./ COPY tsconfig.json ./ # Expose the frontend port -EXPOSE 5173 +EXPOSE ${FRONTEND_PORT:-5173} # Start the frontend using the client script CMD ["npm", "run", "client", "--", "--host"] \ No newline at end of file From c2ce3f3387c87191d6d940354afff716272b2083 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 14:13:12 +0530 Subject: [PATCH 094/102] feat: explicitly fetch easylist url --- server/src/browser-management/classes/RemoteBrowser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/browser-management/classes/RemoteBrowser.ts b/server/src/browser-management/classes/RemoteBrowser.ts index 7e0a7d1ad..71fdd933c 100644 --- a/server/src/browser-management/classes/RemoteBrowser.ts +++ b/server/src/browser-management/classes/RemoteBrowser.ts @@ -180,7 +180,7 @@ export class RemoteBrowser { // await this.currentPage.setExtraHTTPHeaders({ // 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3' // }); - const blocker = await PlaywrightBlocker.fromPrebuiltAdsAndTracking(fetch); + const blocker = await PlaywrightBlocker.fromLists(fetch, ['https://easylist.to/easylist/easylist.txt']); await blocker.enableBlockingInPage(this.currentPage); this.client = await this.currentPage.context().newCDPSession(this.currentPage); await blocker.disableBlockingInPage(this.currentPage); From 44880cfce0ef8030ed9915beaff5b3fb806c3770 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 14:14:23 +0530 Subject: [PATCH 095/102] feat: explicitly fetch easylist url --- maxun-core/src/interpret.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index bd90e40f7..1837dbd58 100644 --- a/maxun-core/src/interpret.ts +++ b/maxun-core/src/interpret.ts @@ -102,7 +102,7 @@ export default class Interpreter extends EventEmitter { }; } - PlaywrightBlocker.fromPrebuiltAdsAndTracking(fetch).then(blocker => { + PlaywrightBlocker.fromLists(fetch, ['https://easylist.to/easylist/easylist.txt']).then(blocker => { this.blocker = blocker; }).catch(err => { this.log(`Failed to initialize ad-blocker:`, Level.ERROR); From 53cb428715f468be6e696daa2cee12d781080a41 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 14:21:26 +0530 Subject: [PATCH 096/102] chore: core v0.0.6 --- maxun-core/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maxun-core/package.json b/maxun-core/package.json index 90ee01b77..36d06aa92 100644 --- a/maxun-core/package.json +++ b/maxun-core/package.json @@ -1,6 +1,6 @@ { "name": "maxun-core", - "version": "0.0.5", + "version": "0.0.6", "description": "Core package for Maxun, responsible for data extraction", "main": "build/index.js", "typings": "build/index.d.ts", From 63230480f3a42394f2d276ebd4044906da21ae2a Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 14:22:05 +0530 Subject: [PATCH 097/102] chore: use core v0.0.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a7d634f3c..6761d7a97 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "jwt-decode": "^4.0.0", "loglevel": "^1.8.0", "loglevel-plugin-remote": "^0.6.8", - "maxun-core": "^0.0.5", + "maxun-core": "0.0.6", "minio": "^8.0.1", "moment-timezone": "^0.5.45", "node-cron": "^3.0.3", From ab2c32c334224236243c65ee451187aaf3fea835 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 14:34:09 +0530 Subject: [PATCH 098/102] chore: use core v0.0.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6761d7a97..9fab1c559 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "jwt-decode": "^4.0.0", "loglevel": "^1.8.0", "loglevel-plugin-remote": "^0.6.8", - "maxun-core": "0.0.6", + "maxun-core": "^0.0.6", "minio": "^8.0.1", "moment-timezone": "^0.5.45", "node-cron": "^3.0.3", From 337ad21577b5ec90ffcb6bd687f82f4fde6737b4 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 14:53:18 +0530 Subject: [PATCH 099/102] feat: update description --- server/src/swagger/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/swagger/config.ts b/server/src/swagger/config.ts index c9c122104..2c7c588e3 100644 --- a/server/src/swagger/config.ts +++ b/server/src/swagger/config.ts @@ -7,7 +7,7 @@ const options = { info: { title: 'Maxun API Documentation', version: '1.0.0', - description: 'API documentation for Maxun (https://github.com/getmaxun/maxun)', + description: 'Maxun lets you get the data your robot extracted and run robots via API. All you need to do is input the Maxun API key by clicking Authorize below.', }, components: { securitySchemes: { From 2025d09e1e8240fd921e4d4dc6a722d630eeb42c Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 14:53:54 +0530 Subject: [PATCH 100/102] feat: rename to website to api --- server/src/swagger/config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/swagger/config.ts b/server/src/swagger/config.ts index 2c7c588e3..5f267053d 100644 --- a/server/src/swagger/config.ts +++ b/server/src/swagger/config.ts @@ -5,7 +5,7 @@ const options = { definition: { openapi: '3.0.0', info: { - title: 'Maxun API Documentation', + title: 'Website to API', version: '1.0.0', description: 'Maxun lets you get the data your robot extracted and run robots via API. All you need to do is input the Maxun API key by clicking Authorize below.', }, From 319f9fce24e22576b98639f91a09e7075edf0940 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 15:16:45 +0530 Subject: [PATCH 101/102] feat: ensure swagger is accessible with or without build --- server/src/swagger/config.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/server/src/swagger/config.ts b/server/src/swagger/config.ts index 5f267053d..bf115ed10 100644 --- a/server/src/swagger/config.ts +++ b/server/src/swagger/config.ts @@ -1,5 +1,16 @@ import swaggerJSDoc from 'swagger-jsdoc'; import path from 'path'; +import fs from 'fs'; + +// Dynamically determine API file paths +const jsFiles = [path.join(__dirname, '../api/*.js')] +const tsFiles = [path.join(__dirname, '../api/*.ts')] + +let apis = fs.existsSync(jsFiles[0]) ? jsFiles : tsFiles; + +if (!apis) { + throw new Error('No valid API files found! Ensure either .js or .ts files exist in the ../api/ directory.'); +} const options = { definition: { @@ -7,7 +18,8 @@ const options = { info: { title: 'Website to API', version: '1.0.0', - description: 'Maxun lets you get the data your robot extracted and run robots via API. All you need to do is input the Maxun API key by clicking Authorize below.', + description: + 'Maxun lets you get the data your robot extracted and run robots via API. All you need to do is input the Maxun API key by clicking Authorize below.', }, components: { securitySchemes: { @@ -15,7 +27,8 @@ const options = { type: 'apiKey', in: 'header', name: 'x-api-key', - description: 'API key for authorization. You can find your API key in the "API Key" section on Maxun Dashboard.', + description: + 'API key for authorization. You can find your API key in the "API Key" section on Maxun Dashboard.', }, }, }, @@ -25,7 +38,7 @@ const options = { }, ], }, - apis: process.env.NODE_ENV === 'production' ? [path.join(__dirname, '../api/*.js')] : [path.join(__dirname, '../api/*.ts')] + apis, }; const swaggerSpec = swaggerJSDoc(options); From 62198377d67cdb136d9e1695b2aed68154a5f34f Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 11 Dec 2024 15:29:05 +0530 Subject: [PATCH 102/102] chore: 0.0.6 BE img, 0.0.3 FE img --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 46cc72c4e..63e59187b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -43,7 +43,7 @@ services: #build: #context: . #dockerfile: server/Dockerfile - image: getmaxun/maxun-backend:v0.0.5 + image: getmaxun/maxun-backend:v0.0.6 ports: - "${BACKEND_PORT:-8080}:${BACKEND_PORT:-8080}" env_file: .env @@ -72,7 +72,7 @@ services: #build: #context: . #dockerfile: Dockerfile - image: getmaxun/maxun-frontend:v0.0.2 + image: getmaxun/maxun-frontend:v0.0.3 ports: - "${FRONTEND_PORT:-5173}:${FRONTEND_PORT:-5173}" env_file: .env