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 92b69c14..ca64f644 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 faa133ec..90ee01b7 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 b6b73537..0269e404 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 0269e404..006ddc8a 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 006ddc8a..977daada 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 cfef4a30..c9dc3385 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 ca64f644..46cc72c4 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 369a08be..467d0eb2 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 467d0eb2..d3410de4 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 d3410de4..09b6578b 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 a7a5de47..06586038 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 769787da..e30632c3 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 c31605de..a8c19961 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 a8c19961..b34bc0e9 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 ee8c80e8..8d195ebc 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 8d195ebc..bc373576 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 bc373576..6b2bfa92 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 b05b912d..3c332600 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 850614b0..38b7b422 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 e30632c3..cfcc96f8 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 06586038..5c24317c 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 | 107 +++++++++++++++++++++++++++--------- 1 file changed, 82 insertions(+), 25 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index 5c24317c..2457f79b 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[] { - const selectors: string[] = []; - let index = actionId - 1; + // 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) => { + // 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[] = []; + + // 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 action = workflow[actionId]; + const actionId = this.getMatchingActionId(workflowCopy, pageState, usedActions); + 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 | 63 ++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/maxun-core/src/interpret.ts b/maxun-core/src/interpret.ts index 2457f79b..fce67257 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 }), - ]; + // 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; - } - }; + // 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 cfcc96f8..f1d18f3f 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 fce67257..c2a1186c 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 c2a1186c..844b46c7 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 | 144 +++++++++++---------- 1 file changed, 78 insertions(+), 66 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 193de891..e0cd10c5 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 - ); - } + // Collect attributes + 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; + // 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 e0cd10c5..aaa53a5d 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 aaa53a5d..7908edc2 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 844b46c7..de9a9845 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 d53259b7..b982b172 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 5b33b12f..05560487 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 d1f648f8..ddadf240 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 02ca905f..169b0061 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 f1d18f3f..3f5b677c 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 6b2bfa92..6ecf9b61 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 6ecf9b61..9e0f6642 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 9e0f6642..d6855184 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 d6855184..28ca959a 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 28ca959a..68b452db 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 68b452db..e8d154ef 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 e8d154ef..2d1096a5 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 2d1096a5..ef1f9878 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 ef1f9878..9b1e9472 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 9b1e9472..bef197fa 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 bef197fa..96e1f07e 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 96e1f07e..4ca7cc17 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 4ca7cc17..cc6d2c6b 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 cc6d2c6b..5cdae02e 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 5cdae02e..06393277 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 06393277..94676ecd 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 94676ecd..be4818df 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 be4818df..bc4222e2 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 bc4222e2..99535baf 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 99535baf..b048d720 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 b048d720..60b75835 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 60b75835..7861340f 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 7861340f..01e002ec 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 01e002ec..9d5cc623 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 9d5cc623..08ab650e 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 08ab650e..db7f72c1 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 db7f72c1..fc638503 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 fc638503..db29e516 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 db29e516..56e43c5e 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 56e43c5e..62275078 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 1755cb20..24fda0f0 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. + )} + +
+ +
Maxun
+ +
+ { + user ? ( +
+ {!isRecording ? ( + <> + + + + {latestVersion === null ? ( + Checking for updates... + ) : currentVersion === latestVersion ? ( + + 🎉 You're up to date! - - - - - {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'); + ) : ( + <> + + 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 +
+
+ )} + + )} + + + + - Discord -
- { - window.open('https://www.youtube.com/@MaxunOSS/videos?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) + + + + ) : ( + <> + - YouTube - - { - window.open('https://x.com/maxun_io?ref=app', '_blank'); - }}> - 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 24fda0f0..3f3907a9 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 3f3907a9..0b933b7e 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 | 166 +++++++++++++-------- 1 file changed, 103 insertions(+), 63 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 7908edc2..9587e898 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; + 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(); - // Use the parent if it exists, otherwise use the initial element - const element = parentElement || initialElement; + // 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; - info.innerText = element.innerText ?? ''; + 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; + 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(); - // Use the parent if it exists, otherwise use the initial element - const element = parentElement || initialElement; + 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 9587e898..6a80e7a3 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 6a80e7a3..f3925ec9 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 769787da..29beaa9b 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 977daada..a7d634f3 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 29beaa9b..0081cb5c 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 a7a5de47..f0b10936 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 0081cb5c..7e0a7d1a 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 f3925ec9..fa58be67 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 fa58be67..917ac561 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 7e0a7d1a..417e8964 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 bd90e40f..afea8e47 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 ef303f82..55ca1b37 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 c9dc3385..1e81cd4a 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 | 366 ++++++++++++--------- 1 file changed, 217 insertions(+), 149 deletions(-) diff --git a/server/src/workflow-management/selector.ts b/server/src/workflow-management/selector.ts index 917ac561..b383d653 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 (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; + 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 significantOverlap = + (childRect.width * childRect.height) / + (parentRect.width * parentRect.height) > 0.5; + + if (fullyContained && significantOverlap) { + element = element.parentElement; + } else { + 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; + let info: { + tagName: string; + hasOnlyText?: boolean; + innerText?: string; + url?: string; + imageUrl?: string; + attributes?: Record; + innerHTML?: string; + outerHTML?: string; + } = { + tagName: element?.tagName ?? '', + }; - // Additional checks for more comprehensive containment - const significantOverlap = - (childRect.width * childRect.height) / - (parentRect.width * parentRect.height) > 0.5; + if (element) { + info.attributes = Array.from(element.attributes).reduce( + (acc, attr) => { + acc[attr.name] = attr.value; + return acc; + }, + {} as Record + ); + } - if (fullyContained && significantOverlap) { - element = element.parentElement; + 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 { - break; - // } - } } + info.hasOnlyText = element?.children?.length === 0 && + element?.innerText?.length > 0; + info.innerText = element?.innerText ?? ''; + } - 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 - ); + info.innerHTML = element.innerHTML; + info.outerHTML = element.outerHTML; + return info; } - - // 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 ?? ''; - } - - info.innerHTML = element.innerHTML; - info.outerHTML = element.outerHTML; - return info; - } - return null; - }, - { x: coordinates.x, y: coordinates.y }, - ); - return elementInfo; + 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, + }; } - - - 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; - // } - }} - - //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, - }; } - } - }, - { x: coordinates.x, y: coordinates.y }, - ); - return rect; + }, + { 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 significantOverlap = + (childRect.width * childRect.height) / + (parentRect.width * parentRect.height) > 0.5; + + 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, + }; + } + } + 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 b383d653..66186a80 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 66186a80..49d56f36 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 49d56f36..5fc621ba 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 5fc621ba..4c6d3f87 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 4c6d3f87..891c0e3b 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 1e81cd4a..57be015e 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 ae26e8eb..877b781e 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 877b781e..f8853fb2 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 719e7814..032dc982 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 7e0a7d1a..71fdd933 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 bd90e40f..1837dbd5 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 90ee01b7..36d06aa9 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 a7d634f3..6761d7a9 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 6761d7a9..9fab1c55 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 c9c12210..2c7c588e 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 2c7c588e..5f267053 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 5f267053..bf115ed1 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 46cc72c4..63e59187 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