From b1d15531e4fc2bcf10c02b58f6b8a7244bf8abef Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 22 Jan 2025 18:58:49 +0530 Subject: [PATCH 01/74] feat: set alter true while db sync --- server/src/storage/db.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/storage/db.ts b/server/src/storage/db.ts index 2f0fcde4..81c9c039 100644 --- a/server/src/storage/db.ts +++ b/server/src/storage/db.ts @@ -29,7 +29,7 @@ export const connectDB = async () => { export const syncDB = async () => { try { //setupAssociations(); - await sequelize.sync({ force: false }); // force: true will drop and recreate tables on every run + await sequelize.sync({ force: false, alter: true }); // force: true will drop and recreate tables on every run console.log('Database synced successfully!'); } catch (error) { console.error('Failed to sync database:', error); From 75d65578cd54a9ab4afd637bf8dc2e537e32ed30 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 22 Jan 2025 19:00:15 +0530 Subject: [PATCH 02/74] feat: check node env --- server/src/storage/db.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/storage/db.ts b/server/src/storage/db.ts index 81c9c039..b6854c55 100644 --- a/server/src/storage/db.ts +++ b/server/src/storage/db.ts @@ -29,6 +29,7 @@ export const connectDB = async () => { export const syncDB = async () => { try { //setupAssociations(); + const isDevelopment = process.env.NODE_ENV === 'development'; await sequelize.sync({ force: false, alter: true }); // force: true will drop and recreate tables on every run console.log('Database synced successfully!'); } catch (error) { From 76ab46fc879e9486e053362e514b9e9a92d88a64 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 22 Jan 2025 19:01:00 +0530 Subject: [PATCH 03/74] chore: re-arrange --- server/src/storage/db.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/src/storage/db.ts b/server/src/storage/db.ts index b6854c55..40968df8 100644 --- a/server/src/storage/db.ts +++ b/server/src/storage/db.ts @@ -30,7 +30,8 @@ export const syncDB = async () => { try { //setupAssociations(); const isDevelopment = process.env.NODE_ENV === 'development'; - await sequelize.sync({ force: false, alter: true }); // force: true will drop and recreate tables on every run + // force: true will drop and recreate tables on every run + await sequelize.sync({ force: false, alter: true }); console.log('Database synced successfully!'); } catch (error) { console.error('Failed to sync database:', error); From e58197b91b477163e3b3414a0c239e483596e88e Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 22 Jan 2025 19:01:16 +0530 Subject: [PATCH 04/74] chore: format --- server/src/storage/db.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/src/storage/db.ts b/server/src/storage/db.ts index 40968df8..bc31439c 100644 --- a/server/src/storage/db.ts +++ b/server/src/storage/db.ts @@ -31,7 +31,10 @@ export const syncDB = async () => { //setupAssociations(); const isDevelopment = process.env.NODE_ENV === 'development'; // force: true will drop and recreate tables on every run - await sequelize.sync({ force: false, alter: true }); + await sequelize.sync({ + force: false, + alter: true + }); console.log('Database synced successfully!'); } catch (error) { console.error('Failed to sync database:', error); From aaf13b47004529bfab5ba902e21d2392a0395950 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 22 Jan 2025 19:01:53 +0530 Subject: [PATCH 05/74] feat: alter:true only in development mode --- server/src/storage/db.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/storage/db.ts b/server/src/storage/db.ts index bc31439c..0aa0dad5 100644 --- a/server/src/storage/db.ts +++ b/server/src/storage/db.ts @@ -33,7 +33,7 @@ export const syncDB = async () => { // force: true will drop and recreate tables on every run await sequelize.sync({ force: false, - alter: true + alter: isDevelopment }); console.log('Database synced successfully!'); } catch (error) { From a4c072b7d56b2f920ef32eebd5dd9799dd09aad9 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 22 Jan 2025 19:02:22 +0530 Subject: [PATCH 06/74] chore: notes on db sync in dev mode --- server/src/storage/db.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/storage/db.ts b/server/src/storage/db.ts index 0aa0dad5..cdd84655 100644 --- a/server/src/storage/db.ts +++ b/server/src/storage/db.ts @@ -31,6 +31,7 @@ export const syncDB = async () => { //setupAssociations(); const isDevelopment = process.env.NODE_ENV === 'development'; // force: true will drop and recreate tables on every run + // Use `alter: true` only in development mode await sequelize.sync({ force: false, alter: isDevelopment From 28568957dc05b30f9b11daeb1107d0e68955ebcd Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 22 Jan 2025 19:05:41 +0530 Subject: [PATCH 07/74] fix: format --- src/components/robot/RecordingsTable.tsx | 27 ++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/components/robot/RecordingsTable.tsx b/src/components/robot/RecordingsTable.tsx index e7409b89..81e44136 100644 --- a/src/components/robot/RecordingsTable.tsx +++ b/src/components/robot/RecordingsTable.tsx @@ -11,8 +11,31 @@ import TableRow from '@mui/material/TableRow'; import { useEffect } from "react"; import { WorkflowFile } from "maxun-core"; import SearchIcon from '@mui/icons-material/Search'; -import { IconButton, Button, Box, Typography, TextField, MenuItem, Menu, ListItemIcon, ListItemText, CircularProgress, RadioGroup, FormControlLabel, Radio } from "@mui/material"; -import { Schedule, DeleteForever, Edit, PlayCircle, Settings, Power, ContentCopy, MoreHoriz } from "@mui/icons-material"; +import { + IconButton, + Button, + Box, + Typography, + TextField, + MenuItem, + Menu, + ListItemIcon, + ListItemText, + CircularProgress, + RadioGroup, + FormControlLabel, + Radio +} from "@mui/material"; +import { + Schedule, + DeleteForever, + Edit, + PlayCircle, + Settings, + Power, + ContentCopy, + MoreHoriz +} from "@mui/icons-material"; import { useGlobalInfoStore } from "../../context/globalInfo"; import { checkRunsForRecording, deleteRecordingFromStorage, getStoredRecordings } from "../../api/storage"; import { Add } from "@mui/icons-material"; From 57860f17a87f60fd5fc36108f878cacbffd9376e Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 22 Jan 2025 19:07:20 +0530 Subject: [PATCH 08/74] fix: missing Checkbox import --- src/components/robot/RecordingsTable.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/robot/RecordingsTable.tsx b/src/components/robot/RecordingsTable.tsx index 81e44136..bcfaaad4 100644 --- a/src/components/robot/RecordingsTable.tsx +++ b/src/components/robot/RecordingsTable.tsx @@ -24,7 +24,8 @@ import { CircularProgress, RadioGroup, FormControlLabel, - Radio + Radio, + Checkbox, } from "@mui/material"; import { Schedule, From d3f9829643650d7daf918b335e712570244de29f Mon Sep 17 00:00:00 2001 From: amhsirak Date: Wed, 22 Jan 2025 19:08:51 +0530 Subject: [PATCH 09/74] feat: use checkbox instead of radio group --- src/components/robot/RecordingsTable.tsx | 25 +++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/components/robot/RecordingsTable.tsx b/src/components/robot/RecordingsTable.tsx index bcfaaad4..7b5f7c84 100644 --- a/src/components/robot/RecordingsTable.tsx +++ b/src/components/robot/RecordingsTable.tsx @@ -346,17 +346,20 @@ export const RecordingsTable = ({ handleEditRecording, handleRunRecording, handl style={{ marginBottom: '20px', marginTop: '20px' }} /> - {t('recordingtable.modal.login_title')} - setIsLogin(e.target.value === 'yes')} - style={{ marginBottom: '20px' }} - > - } label="Yes" /> - } label="No" /> - + + {t('recordingtable.modal.login_title')} + + setIsLogin(e.target.checked)} + color="primary" + /> + } + label={t('recordingtable.modal.login_title')} + style={{ marginBottom: '20px' }} +/>; - - - - ) - } + + + + + + )} ); -}; +}; \ No newline at end of file From ef3ec295d7ee24eebd49be8a496d9b7193598818 Mon Sep 17 00:00:00 2001 From: Rohit Date: Thu, 23 Jan 2025 01:17:38 +0530 Subject: [PATCH 20/74] feat: add types for credentials --- src/api/storage.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/api/storage.ts b/src/api/storage.ts index c7901f22..290f6e7f 100644 --- a/src/api/storage.ts +++ b/src/api/storage.ts @@ -5,8 +5,13 @@ import { ScheduleSettings } from "../components/robot/ScheduleSettings"; import { CreateRunResponse, ScheduleRunResponse } from "../pages/MainPage"; import { apiUrl } from "../apiConfig"; +interface CredentialInfo { + value: string; + type: string; +} + interface Credentials { - [key: string]: string; + [key: string]: CredentialInfo; } export const getStoredRecordings = async (): Promise => { From ee65ea84061f19e48929f69962a70d75077e5ba9 Mon Sep 17 00:00:00 2001 From: Rohit Date: Thu, 23 Jan 2025 01:18:47 +0530 Subject: [PATCH 21/74] feat: add input type for workflow type actions --- server/src/routes/storage.ts | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/server/src/routes/storage.ts b/server/src/routes/storage.ts index 007ac40c..fb587d17 100644 --- a/server/src/routes/storage.ts +++ b/server/src/routes/storage.ts @@ -154,48 +154,52 @@ function formatRunResponse(run: any) { return formattedRun; } -interface CredentialUpdate { - [selector: string]: string; +interface CredentialInfo { + value: string; + type: string; } -function updateTypeActionsInWorkflow(workflow: any[], credentials: CredentialUpdate) { +interface Credentials { + [key: string]: CredentialInfo; +} + +function updateTypeActionsInWorkflow(workflow: any[], credentials: Credentials) { return workflow.map(step => { if (!step.what) return step; - // First pass: mark indices to remove const indicesToRemove = new Set(); - step.what.forEach((action: any, index: any) => { + step.what.forEach((action: any, index: number) => { if (!action.action || !action.args?.[0]) return; - // If it's a type/press action for a credential if ((action.action === 'type' || action.action === 'press') && credentials[action.args[0]]) { indicesToRemove.add(index); - // Check if next action is waitForLoadState + if (step.what[index + 1]?.action === 'waitForLoadState') { indicesToRemove.add(index + 1); } } }); - // Filter out marked indices and create new what array - const filteredWhat = step.what.filter((_: any, index: any) => !indicesToRemove.has(index)); + const filteredWhat = step.what.filter((_: any, index: number) => !indicesToRemove.has(index)); - // Add new type actions after click actions - Object.entries(credentials).forEach(([selector, credential]) => { + Object.entries(credentials).forEach(([selector, credentialInfo]) => { const clickIndex = filteredWhat.findIndex((action: any) => action.action === 'click' && action.args?.[0] === selector ); if (clickIndex !== -1) { - const chars = credential.split(''); + const chars = credentialInfo.value.split(''); + chars.forEach((char, i) => { - // Add type action filteredWhat.splice(clickIndex + 1 + (i * 2), 0, { action: 'type', - args: [selector, encrypt(char)] + args: [ + selector, + encrypt(char), + credentialInfo.type + ] }); - // Add waitForLoadState filteredWhat.splice(clickIndex + 2 + (i * 2), 0, { action: 'waitForLoadState', args: ['networkidle'] From 1950cc582b4df49b550e4e128196657f8608576c Mon Sep 17 00:00:00 2001 From: Rohit Date: Thu, 23 Jan 2025 01:53:04 +0530 Subject: [PATCH 22/74] feat: rm letter on backspace --- src/components/robot/RobotEdit.tsx | 62 +++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index c4e75286..8d611a68 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -135,38 +135,72 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin const extractInitialCredentials = (workflow: any[]): Credentials => { const credentials: Credentials = {}; - + + // Helper function to check if a character is printable const isPrintableCharacter = (char: string): boolean => { return char.length === 1 && !!char.match(/^[\x20-\x7E]$/); }; - + + // Process each step in the workflow workflow.forEach(step => { if (!step.what) return; - + + // Keep track of the current input field being processed + let currentSelector = ''; + let currentValue = ''; + let currentType = ''; + + // Process actions in sequence to maintain correct text state step.what.forEach((action: any) => { if ( (action.action === 'type' || action.action === 'press') && action.args?.length >= 2 && typeof action.args[1] === 'string' ) { - const currentSelector: string = action.args[0]; + const selector: string = action.args[0]; const character: string = action.args[1]; const inputType: string = action.args[2] || ''; - - if (!credentials.hasOwnProperty(currentSelector)) { - credentials[currentSelector] = { - value: '', - type: inputType - }; + + // If we're dealing with a new selector, store the previous one + if (currentSelector && selector !== currentSelector) { + if (!credentials[currentSelector]) { + credentials[currentSelector] = { + value: currentValue, + type: currentType + }; + } else { + credentials[currentSelector].value = currentValue; + } } - - if (isPrintableCharacter(character)) { - credentials[currentSelector].value += character; + + // Update current tracking variables + if (selector !== currentSelector) { + currentSelector = selector; + currentValue = credentials[selector]?.value || ''; + currentType = inputType || credentials[selector]?.type || ''; } + + // Handle different types of key actions + if (character === 'Backspace') { + // Remove the last character when backspace is pressed + currentValue = currentValue.slice(0, -1); + } else if (isPrintableCharacter(character)) { + // Add the character to the current value + currentValue += character; + } + // Note: We ignore other special keys like 'Shift', 'Enter', etc. } }); + + // Store the final state of the last processed selector + if (currentSelector) { + credentials[currentSelector] = { + value: currentValue, + type: currentType + }; + } }); - + return credentials; }; From d56623f97a5ef7207d49a661c23888268ef8189d Mon Sep 17 00:00:00 2001 From: Rohit Date: Thu, 23 Jan 2025 15:45:44 +0530 Subject: [PATCH 23/74] feat: add position of text for input click event --- .../workflow-management/classes/Generator.ts | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/server/src/workflow-management/classes/Generator.ts b/server/src/workflow-management/classes/Generator.ts index 47e6f377..528532d8 100644 --- a/server/src/workflow-management/classes/Generator.ts +++ b/server/src/workflow-management/classes/Generator.ts @@ -354,6 +354,40 @@ export class WorkflowGenerator { const elementInfo = await getElementInformation(page, coordinates, '', false); console.log("Element info: ", elementInfo); + if ((elementInfo?.tagName === 'INPUT' || elementInfo?.tagName === 'TEXTAREA') && selector) { + // Calculate the exact position within the element + const elementPos = await page.evaluate((selector) => { + const element = document.querySelector(selector); + if (!element) return null; + const rect = element.getBoundingClientRect(); + return { + x: rect.left, + y: rect.top + }; + }, selector); + + if (elementPos) { + const relativeX = coordinates.x - elementPos.x; + const relativeY = coordinates.y - elementPos.y; + + const pair: WhereWhatPair = { + where, + what: [{ + action: 'click', + args: [selector, { position: { x: relativeX, y: relativeY } }] + }] + }; + + if (selector) { + this.generatedData.lastUsedSelector = selector; + this.generatedData.lastAction = 'click'; + } + + await this.addPairToWorkflowAndNotifyClient(pair, page); + return; + } + } + // Check if clicked element is a select dropdown const isDropdown = elementInfo?.tagName === 'SELECT'; From 2abf0f0d7bdd3991168b7c73663b9a338d61e889 Mon Sep 17 00:00:00 2001 From: Rohit Date: Thu, 23 Jan 2025 20:23:53 +0530 Subject: [PATCH 24/74] feat: check visibility --- src/components/robot/RobotEdit.tsx | 55 +++++++++++++++++------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index 8d611a68..e96fc858 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -320,30 +320,37 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin {headerText} - {selectors.map((selector) => ( - handleCredentialChange(selector, e.target.value)} - style={{ marginBottom: '20px' }} - InputProps={{ - // Only show visibility toggle for password fields - endAdornment: defaultType === 'password' ? ( - - handleClickShowPassword(selector)} - edge="end" - > - {showPasswords[selector] ? : } - - - ) : undefined, - }} - /> - ))} + {selectors.map((selector) => { + const isVisible = showPasswords[selector]; + + return ( + handleCredentialChange(selector, e.target.value)} + style={{ marginBottom: '20px' }} + InputProps={{ + // Now showing visibility toggle for all fields + endAdornment: ( + + handleClickShowPassword(selector)} + edge="end" + // Optional: disable if field is empty + disabled={!credentials[selector]?.value} + > + {isVisible ? : } + + + ), + }} + /> + ); + })} ); }; From 58ed90b2e61c59b2f51f146a45a439d8eefa61fc Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 23 Jan 2025 20:49:30 +0530 Subject: [PATCH 25/74] fix: format --- src/components/robot/RecordingsTable.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/robot/RecordingsTable.tsx b/src/components/robot/RecordingsTable.tsx index 6297c7aa..a36707e7 100644 --- a/src/components/robot/RecordingsTable.tsx +++ b/src/components/robot/RecordingsTable.tsx @@ -76,7 +76,14 @@ interface RecordingsTableProps { handleDuplicateRobot: (id: string, name: string, params: string[]) => void; } -export const RecordingsTable = ({ handleEditRecording, handleRunRecording, handleScheduleRecording, handleIntegrateRecording, handleSettingsRecording, handleEditRobot, handleDuplicateRobot }: RecordingsTableProps) => { +export const RecordingsTable = ({ + handleEditRecording, + handleRunRecording, + handleScheduleRecording, + handleIntegrateRecording, + handleSettingsRecording, + handleEditRobot, + handleDuplicateRobot }: RecordingsTableProps) => { const { t } = useTranslation(); const [page, setPage] = React.useState(0); const [rowsPerPage, setRowsPerPage] = React.useState(10); From e0f58bb3d098d72a106ddc286d18ec0106aafb3c Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 23 Jan 2025 20:50:16 +0530 Subject: [PATCH 26/74] fix: format --- src/components/robot/RecordingsTable.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/robot/RecordingsTable.tsx b/src/components/robot/RecordingsTable.tsx index a36707e7..a7594b08 100644 --- a/src/components/robot/RecordingsTable.tsx +++ b/src/components/robot/RecordingsTable.tsx @@ -116,7 +116,20 @@ export const RecordingsTable = ({ }, ]; - const { notify, setRecordings, browserId, setBrowserId, setInitialUrl, recordingUrl, setRecordingUrl, isLogin, setIsLogin, recordingName, setRecordingName, recordingId, setRecordingId } = useGlobalInfoStore(); + const { + notify, + setRecordings, + browserId, + setBrowserId, + setInitialUrl, + recordingUrl, + setRecordingUrl, + isLogin, + setIsLogin, + recordingName, + setRecordingName, + recordingId, + setRecordingId } = useGlobalInfoStore(); const navigate = useNavigate(); const handleChangePage = (event: unknown, newPage: number) => { From 76a785b4604f0dec980ad26b7d66dd70b1e03b23 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 23 Jan 2025 20:54:23 +0530 Subject: [PATCH 27/74] chore: cleanup --- src/components/robot/RobotEdit.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index e96fc858..f8e85a77 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -8,7 +8,6 @@ import { useGlobalInfoStore } from '../../context/globalInfo'; import { getStoredRecording, updateRecording } from '../../api/storage'; import { WhereWhatPair } from 'maxun-core'; -// Base interfaces for robot data structure interface RobotMeta { name: string; id: string; From 011a6edf05ff23f379a751349746b6e1f407c5b1 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 23 Jan 2025 20:54:34 +0530 Subject: [PATCH 28/74] chore: cleanup --- src/components/robot/RobotEdit.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index f8e85a77..c9b7bbd6 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -54,7 +54,6 @@ interface RobotSettingsProps { initialSettings?: RobotSettings | null; } -// Enhanced interfaces for credential handling interface CredentialInfo { value: string; type: string; From 3cc23f9854b235aace43d3e65161d6de6dfeb844 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Thu, 23 Jan 2025 20:55:10 +0530 Subject: [PATCH 29/74] chore: cleanup --- src/components/robot/RobotEdit.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index c9b7bbd6..035165ba 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -113,7 +113,6 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin return 'username'; } - // If no specific pattern is matched, classify as other return 'other'; }; From 357488c801b442eb8c38d3cfcf18210d1cd602d4 Mon Sep 17 00:00:00 2001 From: Rohit Date: Fri, 24 Jan 2025 12:15:24 +0530 Subject: [PATCH 30/74] feat: add check to see if robot is running --- .../browser-management/classes/BrowserPool.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/server/src/browser-management/classes/BrowserPool.ts b/server/src/browser-management/classes/BrowserPool.ts index cd4962a1..ecd3c9dc 100644 --- a/server/src/browser-management/classes/BrowserPool.ts +++ b/server/src/browser-management/classes/BrowserPool.ts @@ -15,6 +15,8 @@ interface BrowserPoolInfo { * @default false */ active: boolean, + + isRobotRun?: boolean; } /** @@ -46,17 +48,29 @@ export class BrowserPool { * @param browser remote browser instance * @param active states if the browser's instance is being actively used */ - public addRemoteBrowser = (id: string, browser: RemoteBrowser, active: boolean = false): void => { + public addRemoteBrowser = (id: string, browser: RemoteBrowser, active: boolean = false, isRobotRun: boolean = false): void => { this.pool = { ...this.pool, [id]: { browser, active, + isRobotRun }, } logger.log('debug', `Remote browser with id: ${id} added to the pool`); }; + public hasActiveRobotRun(): boolean { + return Object.values(this.pool).some(info => info.isRobotRun); + } + + public clearRobotRunState(id: string): void { + if (this.pool[id]) { + this.pool[id].isRobotRun = false; + logger.log('debug', `Robot run state cleared for browser ${id}`); + } + } + /** * Removes the remote browser instance from the pool. * @param id remote browser instance's id @@ -67,6 +81,8 @@ export class BrowserPool { logger.log('warn', `Remote browser with id: ${id} does not exist in the pool`); return false; } + + this.clearRobotRunState(id); delete (this.pool[id]); logger.log('debug', `Remote browser with id: ${id} deleted from the pool`); return true; From 1ec5b3bf7d3f4bbd5b3adb21a08ded50b50c4c81 Mon Sep 17 00:00:00 2001 From: Rohit Date: Fri, 24 Jan 2025 12:16:44 +0530 Subject: [PATCH 31/74] feat: add route to verify is robot is running --- server/src/routes/record.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/server/src/routes/record.ts b/server/src/routes/record.ts index 51d3ff92..17146173 100644 --- a/server/src/routes/record.ts +++ b/server/src/routes/record.ts @@ -16,6 +16,7 @@ import stealthPlugin from 'puppeteer-extra-plugin-stealth'; import logger from "../logger"; import { getDecryptedProxyConfig } from './proxy'; import { requireSignIn } from '../middlewares/auth'; +import { browserPool } from '../server'; export const router = Router(); chromium.use(stealthPlugin()); @@ -33,6 +34,17 @@ router.all('/', requireSignIn, (req, res, next) => { next() // pass control to the next handler }) +router.use('/', requireSignIn, (req: AuthenticatedRequest, res: Response, next) => { + if (browserPool.hasActiveRobotRun()) { + logger.log('debug', 'Preventing browser initialization - robot run in progress'); + return res.status(403).json({ + error: 'Cannot initialize recording browser while a robot run is in progress' + }); + } + next(); +}); + + /** * GET endpoint for starting the remote browser recording session. * returns session's id From 067627bf396829c29abe2305f0461b4cb6906ccd Mon Sep 17 00:00:00 2001 From: Rohit Date: Fri, 24 Jan 2025 13:47:17 +0530 Subject: [PATCH 32/74] feat: notify on robot run after page reload --- src/pages/MainPage.tsx | 77 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 70 insertions(+), 7 deletions(-) diff --git a/src/pages/MainPage.tsx b/src/pages/MainPage.tsx index b9a4f24f..c8066951 100644 --- a/src/pages/MainPage.tsx +++ b/src/pages/MainPage.tsx @@ -68,13 +68,14 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps) const readyForRunHandler = useCallback((browserId: string, runId: string) => { interpretStoredRecording(runId).then(async (interpretation: boolean) => { if (!aborted) { - if (interpretation) { - notify('success', t('main_page.notifications.interpretation_success', { name: runningRecordingName })); - } else { - notify('success', t('main_page.notifications.interpretation_failed', { name: runningRecordingName })); - // destroy the created browser - await stopRecording(browserId); - } + // if (interpretation) { + // notify('success', t('main_page.notifications.interpretation_success', { name: runningRecordingName })); + // } else { + // notify('success', t('main_page.notifications.interpretation_failed', { name: runningRecordingName })); + // // destroy the created browser + // await stopRecording(browserId); + // } + if (!interpretation) await stopRecording(browserId); } setRunningRecordingName(''); setCurrentInterpretationLog(''); @@ -89,6 +90,12 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps) const handleRunRecording = useCallback((settings: RunSettings) => { createRunForStoredRecording(runningRecordingId, settings).then(({ browserId, runId }: CreateRunResponse) => { + localStorage.setItem('runInfo', JSON.stringify({ + browserId, + runId, + recordingName: runningRecordingName + })); + setIds({ browserId, runId }); const socket = io(`${apiUrl}/${browserId}`, { @@ -98,6 +105,18 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps) setSockets(sockets => [...sockets, socket]); socket.on('ready-for-run', () => readyForRunHandler(browserId, runId)); socket.on('debugMessage', debugMessageHandler); + + socket.on('run-completed', (status) => { + if (status === 'success') { + notify('success', t('main_page.notifications.interpretation_success', { name: runningRecordingName })); + } else { + notify('error', t('main_page.notifications.interpretation_failed', { name: runningRecordingName })); + } + setRunningRecordingName(''); + setCurrentInterpretationLog(''); + setRerenderRuns(true); + }); + setContent('runs'); if (browserId) { notify('info', t('main_page.notifications.run_started', { name: runningRecordingName })); @@ -108,6 +127,7 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps) return (socket: Socket, browserId: string, runId: string) => { socket.off('ready-for-run', () => readyForRunHandler(browserId, runId)); socket.off('debugMessage', debugMessageHandler); + socket.off('run-completed'); } }, [runningRecordingName, sockets, ids, readyForRunHandler, debugMessageHandler]) @@ -122,6 +142,49 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps) }); } + useEffect(() => { + const storedRunInfo = localStorage.getItem('runInfo'); + console.log('storedRunInfo', storedRunInfo); + + if (storedRunInfo) { + // Parse the stored info + const { browserId, runId, recordingName } = JSON.parse(storedRunInfo); + + // Reconnect to the specific browser's namespace + setIds({ browserId, runId }); + const socket = io(`${apiUrl}/${browserId}`, { + transports: ["websocket"], + rejectUnauthorized: false + }); + + // Update component state with stored info + setRunningRecordingName(recordingName); + setSockets(sockets => [...sockets, socket]); + + // Set up event listeners + socket.on('ready-for-run', () => readyForRunHandler(browserId, runId)); + socket.on('debugMessage', debugMessageHandler); + socket.on('run-completed', (status) => { + if (status === 'success') { + notify('success', t('main_page.notifications.interpretation_success', { name: recordingName })); + } else { + notify('error', t('main_page.notifications.interpretation_failed', { name: recordingName })); + } + setRunningRecordingName(''); + setCurrentInterpretationLog(''); + setRerenderRuns(true); + localStorage.removeItem('runInfo'); // Clean up stored info + }); + + // Cleanup function + return () => { + socket.off('ready-for-run', () => readyForRunHandler(browserId, runId)); + socket.off('debugMessage', debugMessageHandler); + socket.off('run-completed'); + }; + } + }, []); + const DisplayContent = () => { switch (content) { case 'robots': From 8619435f2dfb38e1622f10efd1e50543071ba33b Mon Sep 17 00:00:00 2001 From: Rohit Date: Fri, 24 Jan 2025 13:48:49 +0530 Subject: [PATCH 33/74] feat: update state as true on run creation --- server/src/browser-management/controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/browser-management/controller.ts b/server/src/browser-management/controller.ts index 24a677ce..f589ce3f 100644 --- a/server/src/browser-management/controller.ts +++ b/server/src/browser-management/controller.ts @@ -59,7 +59,7 @@ export const createRemoteBrowserForRun = (userId: string): string => { async (socket: Socket) => { const browserSession = new RemoteBrowser(socket); await browserSession.initialize(userId); - browserPool.addRemoteBrowser(id, browserSession, true); + browserPool.addRemoteBrowser(id, browserSession, true, true); socket.emit('ready-for-run'); }); return id; From 102c72afa239f2e998db3741caf78d94ee658cbb Mon Sep 17 00:00:00 2001 From: Rohit Date: Fri, 24 Jan 2025 13:49:31 +0530 Subject: [PATCH 34/74] feat: emit run completed socket event --- server/src/workflow-management/classes/Interpreter.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/src/workflow-management/classes/Interpreter.ts b/server/src/workflow-management/classes/Interpreter.ts index c8aec13c..661f58db 100644 --- a/server/src/workflow-management/classes/Interpreter.ts +++ b/server/src/workflow-management/classes/Interpreter.ts @@ -332,6 +332,8 @@ export class WorkflowInterpreter { }, {}) } + this.socket.emit('run-completed', "success"); + logger.log('debug', `Interpretation finished`); this.clearState(); return result; From afc1155354565568a3a1b5ccf873341710fff63a Mon Sep 17 00:00:00 2001 From: Rohit Date: Fri, 24 Jan 2025 14:08:36 +0530 Subject: [PATCH 35/74] feat: add translation for label field --- public/locales/de.json | 3 ++- public/locales/en.json | 3 ++- public/locales/es.json | 3 ++- public/locales/ja.json | 3 ++- public/locales/zh.json | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/public/locales/de.json b/public/locales/de.json index 21b71313..d14f36f6 100644 --- a/public/locales/de.json +++ b/public/locales/de.json @@ -205,7 +205,8 @@ "confirm_text_fields": "Bitte bestätigen Sie alle Textfelder", "unable_create_settings": "Listeneinstellungen können nicht erstellt werden. Stellen Sie sicher, dass Sie ein Feld für die Liste definiert haben.", "capture_text_discarded": "Texterfassung verworfen", - "capture_list_discarded": "Listenerfassung verworfen" + "capture_list_discarded": "Listenerfassung verworfen", + "label_required": "Beschriftung darf nicht leer sein" } }, "save_recording": { diff --git a/public/locales/en.json b/public/locales/en.json index a51f6c65..64ac4177 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -207,7 +207,8 @@ "confirm_text_fields": "Please confirm all text fields", "unable_create_settings": "Unable to create list settings. Make sure you have defined a field for the list.", "capture_text_discarded": "Capture Text Discarded", - "capture_list_discarded": "Capture List Discarded" + "capture_list_discarded": "Capture List Discarded", + "label_required": "Label cannot be empty" } }, "save_recording": { diff --git a/public/locales/es.json b/public/locales/es.json index 8ef9b8bf..c0a39216 100644 --- a/public/locales/es.json +++ b/public/locales/es.json @@ -206,7 +206,8 @@ "confirm_text_fields": "Por favor confirme todos los campos de texto", "unable_create_settings": "No se pueden crear las configuraciones de la lista. Asegúrese de haber definido un campo para la lista.", "capture_text_discarded": "Captura de texto descartada", - "capture_list_discarded": "Captura de lista descartada" + "capture_list_discarded": "Captura de lista descartada", + "label_required": "La etiqueta no puede estar vacía" } }, "save_recording": { diff --git a/public/locales/ja.json b/public/locales/ja.json index 03f91cf6..676b2149 100644 --- a/public/locales/ja.json +++ b/public/locales/ja.json @@ -206,7 +206,8 @@ "confirm_text_fields": "すべてのテキストフィールドを確認してください", "unable_create_settings": "リスト設定を作成できません。リストのフィールドを定義したことを確認してください。", "capture_text_discarded": "テキスト取得が破棄されました", - "capture_list_discarded": "リスト取得が破棄されました" + "capture_list_discarded": "リスト取得が破棄されました", + "label_required": "ラベルは空にできません" } }, "save_recording": { diff --git a/public/locales/zh.json b/public/locales/zh.json index 41e3a762..d7f7a9d2 100644 --- a/public/locales/zh.json +++ b/public/locales/zh.json @@ -206,7 +206,8 @@ "confirm_text_fields": "请确认所有文本字段", "unable_create_settings": "无法创建列表设置。请确保您已为列表定义了字段。", "capture_text_discarded": "文本捕获已放弃", - "capture_list_discarded": "列表捕获已放弃" + "capture_list_discarded": "列表捕获已放弃", + "label_required": "标签不能为空" } }, "save_recording": { From 153cf81edc4d113c83135b895168e7202b67123b Mon Sep 17 00:00:00 2001 From: Karishma Shukla Date: Fri, 24 Jan 2025 19:28:04 +0530 Subject: [PATCH 36/74] Revert "fix: prevent page reload on run trigger to open remote browser" --- .../browser-management/classes/BrowserPool.ts | 18 +---- server/src/browser-management/controller.ts | 2 +- server/src/routes/record.ts | 12 --- .../classes/Interpreter.ts | 2 - src/pages/MainPage.tsx | 77 ++----------------- 5 files changed, 9 insertions(+), 102 deletions(-) diff --git a/server/src/browser-management/classes/BrowserPool.ts b/server/src/browser-management/classes/BrowserPool.ts index ecd3c9dc..cd4962a1 100644 --- a/server/src/browser-management/classes/BrowserPool.ts +++ b/server/src/browser-management/classes/BrowserPool.ts @@ -15,8 +15,6 @@ interface BrowserPoolInfo { * @default false */ active: boolean, - - isRobotRun?: boolean; } /** @@ -48,29 +46,17 @@ export class BrowserPool { * @param browser remote browser instance * @param active states if the browser's instance is being actively used */ - public addRemoteBrowser = (id: string, browser: RemoteBrowser, active: boolean = false, isRobotRun: boolean = false): void => { + public addRemoteBrowser = (id: string, browser: RemoteBrowser, active: boolean = false): void => { this.pool = { ...this.pool, [id]: { browser, active, - isRobotRun }, } logger.log('debug', `Remote browser with id: ${id} added to the pool`); }; - public hasActiveRobotRun(): boolean { - return Object.values(this.pool).some(info => info.isRobotRun); - } - - public clearRobotRunState(id: string): void { - if (this.pool[id]) { - this.pool[id].isRobotRun = false; - logger.log('debug', `Robot run state cleared for browser ${id}`); - } - } - /** * Removes the remote browser instance from the pool. * @param id remote browser instance's id @@ -81,8 +67,6 @@ export class BrowserPool { logger.log('warn', `Remote browser with id: ${id} does not exist in the pool`); return false; } - - this.clearRobotRunState(id); delete (this.pool[id]); logger.log('debug', `Remote browser with id: ${id} deleted from the pool`); return true; diff --git a/server/src/browser-management/controller.ts b/server/src/browser-management/controller.ts index f589ce3f..24a677ce 100644 --- a/server/src/browser-management/controller.ts +++ b/server/src/browser-management/controller.ts @@ -59,7 +59,7 @@ export const createRemoteBrowserForRun = (userId: string): string => { async (socket: Socket) => { const browserSession = new RemoteBrowser(socket); await browserSession.initialize(userId); - browserPool.addRemoteBrowser(id, browserSession, true, true); + browserPool.addRemoteBrowser(id, browserSession, true); socket.emit('ready-for-run'); }); return id; diff --git a/server/src/routes/record.ts b/server/src/routes/record.ts index 17146173..51d3ff92 100644 --- a/server/src/routes/record.ts +++ b/server/src/routes/record.ts @@ -16,7 +16,6 @@ import stealthPlugin from 'puppeteer-extra-plugin-stealth'; import logger from "../logger"; import { getDecryptedProxyConfig } from './proxy'; import { requireSignIn } from '../middlewares/auth'; -import { browserPool } from '../server'; export const router = Router(); chromium.use(stealthPlugin()); @@ -34,17 +33,6 @@ router.all('/', requireSignIn, (req, res, next) => { next() // pass control to the next handler }) -router.use('/', requireSignIn, (req: AuthenticatedRequest, res: Response, next) => { - if (browserPool.hasActiveRobotRun()) { - logger.log('debug', 'Preventing browser initialization - robot run in progress'); - return res.status(403).json({ - error: 'Cannot initialize recording browser while a robot run is in progress' - }); - } - next(); -}); - - /** * GET endpoint for starting the remote browser recording session. * returns session's id diff --git a/server/src/workflow-management/classes/Interpreter.ts b/server/src/workflow-management/classes/Interpreter.ts index 661f58db..c8aec13c 100644 --- a/server/src/workflow-management/classes/Interpreter.ts +++ b/server/src/workflow-management/classes/Interpreter.ts @@ -332,8 +332,6 @@ export class WorkflowInterpreter { }, {}) } - this.socket.emit('run-completed', "success"); - logger.log('debug', `Interpretation finished`); this.clearState(); return result; diff --git a/src/pages/MainPage.tsx b/src/pages/MainPage.tsx index c8066951..b9a4f24f 100644 --- a/src/pages/MainPage.tsx +++ b/src/pages/MainPage.tsx @@ -68,14 +68,13 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps) const readyForRunHandler = useCallback((browserId: string, runId: string) => { interpretStoredRecording(runId).then(async (interpretation: boolean) => { if (!aborted) { - // if (interpretation) { - // notify('success', t('main_page.notifications.interpretation_success', { name: runningRecordingName })); - // } else { - // notify('success', t('main_page.notifications.interpretation_failed', { name: runningRecordingName })); - // // destroy the created browser - // await stopRecording(browserId); - // } - if (!interpretation) await stopRecording(browserId); + if (interpretation) { + notify('success', t('main_page.notifications.interpretation_success', { name: runningRecordingName })); + } else { + notify('success', t('main_page.notifications.interpretation_failed', { name: runningRecordingName })); + // destroy the created browser + await stopRecording(browserId); + } } setRunningRecordingName(''); setCurrentInterpretationLog(''); @@ -90,12 +89,6 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps) const handleRunRecording = useCallback((settings: RunSettings) => { createRunForStoredRecording(runningRecordingId, settings).then(({ browserId, runId }: CreateRunResponse) => { - localStorage.setItem('runInfo', JSON.stringify({ - browserId, - runId, - recordingName: runningRecordingName - })); - setIds({ browserId, runId }); const socket = io(`${apiUrl}/${browserId}`, { @@ -105,18 +98,6 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps) setSockets(sockets => [...sockets, socket]); socket.on('ready-for-run', () => readyForRunHandler(browserId, runId)); socket.on('debugMessage', debugMessageHandler); - - socket.on('run-completed', (status) => { - if (status === 'success') { - notify('success', t('main_page.notifications.interpretation_success', { name: runningRecordingName })); - } else { - notify('error', t('main_page.notifications.interpretation_failed', { name: runningRecordingName })); - } - setRunningRecordingName(''); - setCurrentInterpretationLog(''); - setRerenderRuns(true); - }); - setContent('runs'); if (browserId) { notify('info', t('main_page.notifications.run_started', { name: runningRecordingName })); @@ -127,7 +108,6 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps) return (socket: Socket, browserId: string, runId: string) => { socket.off('ready-for-run', () => readyForRunHandler(browserId, runId)); socket.off('debugMessage', debugMessageHandler); - socket.off('run-completed'); } }, [runningRecordingName, sockets, ids, readyForRunHandler, debugMessageHandler]) @@ -142,49 +122,6 @@ export const MainPage = ({ handleEditRecording, initialContent }: MainPageProps) }); } - useEffect(() => { - const storedRunInfo = localStorage.getItem('runInfo'); - console.log('storedRunInfo', storedRunInfo); - - if (storedRunInfo) { - // Parse the stored info - const { browserId, runId, recordingName } = JSON.parse(storedRunInfo); - - // Reconnect to the specific browser's namespace - setIds({ browserId, runId }); - const socket = io(`${apiUrl}/${browserId}`, { - transports: ["websocket"], - rejectUnauthorized: false - }); - - // Update component state with stored info - setRunningRecordingName(recordingName); - setSockets(sockets => [...sockets, socket]); - - // Set up event listeners - socket.on('ready-for-run', () => readyForRunHandler(browserId, runId)); - socket.on('debugMessage', debugMessageHandler); - socket.on('run-completed', (status) => { - if (status === 'success') { - notify('success', t('main_page.notifications.interpretation_success', { name: recordingName })); - } else { - notify('error', t('main_page.notifications.interpretation_failed', { name: recordingName })); - } - setRunningRecordingName(''); - setCurrentInterpretationLog(''); - setRerenderRuns(true); - localStorage.removeItem('runInfo'); // Clean up stored info - }); - - // Cleanup function - return () => { - socket.off('ready-for-run', () => readyForRunHandler(browserId, runId)); - socket.off('debugMessage', debugMessageHandler); - socket.off('run-completed'); - }; - } - }, []); - const DisplayContent = () => { switch (content) { case 'robots': From e0deccafde1bb6a5608364158c21240b86c3cf7c Mon Sep 17 00:00:00 2001 From: Rohit Date: Sat, 25 Jan 2025 12:43:06 +0530 Subject: [PATCH 37/74] feat: add robotId cookie and redirect --- server/src/routes/auth.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server/src/routes/auth.ts b/server/src/routes/auth.ts index 837fcd8a..49af7090 100644 --- a/server/src/routes/auth.ts +++ b/server/src/routes/auth.ts @@ -380,11 +380,19 @@ router.get( httpOnly: false, maxAge: 60000, }); // 1-minute expiration - res.cookie("robot_auth_message", "Robot successfully authenticated", { + // res.cookie("robot_auth_message", "Robot successfully authenticated", { + // httpOnly: false, + // maxAge: 60000, + // }); + res.cookie('robot_auth_robotId', robotId, { httpOnly: false, maxAge: 60000, }); - res.redirect(`${process.env.PUBLIC_URL}/robots/${robotId}/integrate` as string || `http://localhost:5173/robots/${robotId}/integrate`); + + const baseUrl = process.env.PUBLIC_URL || "http://localhost:5173"; + const redirectUrl = `${baseUrl}/robots/`; + + res.redirect(redirectUrl); } catch (error: any) { res.status(500).json({ message: `Google OAuth error: ${error.message}` }); } From 160777926870ffea362fa54081ae83ada265f9fc Mon Sep 17 00:00:00 2001 From: Rohit Date: Sat, 25 Jan 2025 12:43:57 +0530 Subject: [PATCH 38/74] feat: check for cookie and open modal --- src/components/robot/Recordings.tsx | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/components/robot/Recordings.tsx b/src/components/robot/Recordings.tsx index 9ddfccc7..89fc3411 100644 --- a/src/components/robot/Recordings.tsx +++ b/src/components/robot/Recordings.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { RecordingsTable } from "./RecordingsTable"; import { Grid } from "@mui/material"; import { RunSettings, RunSettingsModal } from "../run/RunSettings"; @@ -8,6 +8,7 @@ import { RobotSettingsModal } from "./RobotSettings"; import { RobotEditModal } from "./RobotEdit"; import { RobotDuplicationModal } from "./RobotDuplicate"; import { useNavigate, useLocation, useParams } from "react-router-dom"; +import { useGlobalInfoStore } from "../../context/globalInfo"; interface RecordingsProps { handleEditRecording: (id: string, fileName: string) => void; @@ -26,6 +27,7 @@ export const Recordings = ({ const location = useLocation(); const { selectedRecordingId } = useParams(); const [params, setParams] = useState([]); + const { notify } = useGlobalInfoStore(); const handleNavigate = (path: string, id: string, name: string, params: string[]) => { setParams(params); @@ -39,6 +41,31 @@ export const Recordings = ({ navigate("/robots"); // Navigate back to the main robots page }; + useEffect(() => { + // Helper function to get and clear a cookie + const getAndClearCookie = (name: string) => { + const value = document.cookie + .split('; ') + .find(row => row.startsWith(`${name}=`)) + ?.split('=')[1]; + + if (value) { + document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`; + } + + return value; + }; + + const authStatus = getAndClearCookie('robot_auth_status'); + const robotId = getAndClearCookie('robot_auth_robotId'); + + if (authStatus === 'success' && robotId) { + notify(authStatus, "Robot successfully authenticated"); + + handleNavigate(`/robots/${robotId}/integrate`, robotId, "", []);'' + } + }, []); + // Determine which modal to open based on the current route const getCurrentModal = () => { const currentPath = location.pathname; From 1d4dbf8a45c0d4bd8f2ec4d5eadc58910186b32b Mon Sep 17 00:00:00 2001 From: Rohit Date: Sat, 25 Jan 2025 12:44:51 +0530 Subject: [PATCH 39/74] feat: rm cookie logic --- .../integration/IntegrationSettings.tsx | 25 ------------------- 1 file changed, 25 deletions(-) diff --git a/src/components/integration/IntegrationSettings.tsx b/src/components/integration/IntegrationSettings.tsx index 835221ca..31c93368 100644 --- a/src/components/integration/IntegrationSettings.tsx +++ b/src/components/integration/IntegrationSettings.tsx @@ -29,20 +29,6 @@ export interface IntegrationSettings { data: string; } -// Helper functions to replace js-cookie functionality -const getCookie = (name: string): string | null => { - const value = `; ${document.cookie}`; - const parts = value.split(`; ${name}=`); - if (parts.length === 2) { - return parts.pop()?.split(';').shift() || null; - } - return null; -}; - -const removeCookie = (name: string): void => { - document.cookie = `${name}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`; -}; - export const IntegrationSettingsModal = ({ isOpen, handleStart, @@ -154,17 +140,6 @@ export const IntegrationSettingsModal = ({ }; useEffect(() => { - // Check if there is a success message in cookies - const status = getCookie("robot_auth_status"); - const message = getCookie("robot_auth_message"); - - if (status === "success" && message) { - notify("success", message); - // Clear the cookies after reading - removeCookie("robot_auth_status"); - removeCookie("robot_auth_message"); - } - // Check if we're on the callback URL const urlParams = new URLSearchParams(window.location.search); const code = urlParams.get("code"); From 9f406c79a7675de1de812826c5cf8d7a0ea5eec5 Mon Sep 17 00:00:00 2001 From: Rohit Date: Sat, 25 Jan 2025 12:56:19 +0530 Subject: [PATCH 40/74] feat: add translation for recording --- src/components/robot/Recordings.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/robot/Recordings.tsx b/src/components/robot/Recordings.tsx index 89fc3411..e63f5e1c 100644 --- a/src/components/robot/Recordings.tsx +++ b/src/components/robot/Recordings.tsx @@ -9,6 +9,7 @@ import { RobotEditModal } from "./RobotEdit"; import { RobotDuplicationModal } from "./RobotDuplicate"; import { useNavigate, useLocation, useParams } from "react-router-dom"; import { useGlobalInfoStore } from "../../context/globalInfo"; +import { useTranslation } from "react-i18next"; interface RecordingsProps { handleEditRecording: (id: string, fileName: string) => void; @@ -28,6 +29,7 @@ export const Recordings = ({ const { selectedRecordingId } = useParams(); const [params, setParams] = useState([]); const { notify } = useGlobalInfoStore(); + const { t } = useTranslation(); const handleNavigate = (path: string, id: string, name: string, params: string[]) => { setParams(params); @@ -60,7 +62,7 @@ export const Recordings = ({ const robotId = getAndClearCookie('robot_auth_robotId'); if (authStatus === 'success' && robotId) { - notify(authStatus, "Robot successfully authenticated"); + notify(authStatus, t("recordingtable.notifications.auth_success")); handleNavigate(`/robots/${robotId}/integrate`, robotId, "", []);'' } From 335bb379b6329b65b773bc9f5bcbfe1d5ad20a31 Mon Sep 17 00:00:00 2001 From: Rohit Date: Sat, 25 Jan 2025 12:56:58 +0530 Subject: [PATCH 41/74] feat: add translation for integrate notification --- public/locales/de.json | 3 ++- public/locales/en.json | 3 ++- public/locales/es.json | 3 ++- public/locales/ja.json | 3 ++- public/locales/zh.json | 3 ++- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/public/locales/de.json b/public/locales/de.json index 21b71313..48422b0a 100644 --- a/public/locales/de.json +++ b/public/locales/de.json @@ -41,7 +41,8 @@ "duplicate": "Duplizieren", "notifications": { "delete_warning": "Roboter kann nicht gelöscht werden, da zugehörige Ausführungen vorhanden sind", - "delete_success": "Roboter erfolgreich gelöscht" + "delete_success": "Roboter erfolgreich gelöscht", + "auth_success": "Roboter erfolgreich authentifiziert" } }, "mainmenu": { diff --git a/public/locales/en.json b/public/locales/en.json index a51f6c65..cd63195e 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -42,7 +42,8 @@ "search":"Search Robots...", "notifications": { "delete_warning": "Cannot delete robot as it has associated runs", - "delete_success": "Robot deleted successfully" + "delete_success": "Robot deleted successfully", + "auth_success": "Robot successfully authenticated" } }, "mainmenu":{ diff --git a/public/locales/es.json b/public/locales/es.json index 8ef9b8bf..63d6dd02 100644 --- a/public/locales/es.json +++ b/public/locales/es.json @@ -42,7 +42,8 @@ "search": "Buscar robots...", "notifications": { "delete_warning": "No se puede eliminar el robot ya que tiene ejecuciones asociadas", - "delete_success": "Robot eliminado exitosamente" + "delete_success": "Robot eliminado exitosamente", + "auth_success": "Robot autenticado exitosamente" } }, "mainmenu": { diff --git a/public/locales/ja.json b/public/locales/ja.json index 03f91cf6..f2489ff0 100644 --- a/public/locales/ja.json +++ b/public/locales/ja.json @@ -42,7 +42,8 @@ "search": "ロボットを検索...", "notifications": { "delete_warning": "関連する実行があるため、ロボットを削除できません", - "delete_success": "ロボットが正常に削除されました" + "delete_success": "ロボットが正常に削除されました", + "auth_success": "ロボットの認証に成功しました" } }, "mainmenu": { diff --git a/public/locales/zh.json b/public/locales/zh.json index 41e3a762..de7e655a 100644 --- a/public/locales/zh.json +++ b/public/locales/zh.json @@ -42,7 +42,8 @@ "search": "搜索机器人...", "notifications": { "delete_warning": "无法删除机器人,因为它有关联的运行记录", - "delete_success": "机器人删除成功" + "delete_success": "机器人删除成功", + "auth_success": "机器人认证成功" } }, "mainmenu": { From ddcbff75bcc78d616d6ccb34a5a7ff885274f297 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 15:42:33 +0530 Subject: [PATCH 42/74] feat: username --- src/components/robot/RobotEdit.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index 035165ba..9e60202c 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -281,7 +281,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin {/* Render username credentials */} {renderCredentialFields( credentialGroups.usernames, - t('Username Credentials'), + t('Username'), 'text' // Always show usernames as text )} From 9a7b5d0a8b7f96414dfef2a87ab5da5cdd1cdaaf Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 15:43:05 +0530 Subject: [PATCH 43/74] feat: email --- src/components/robot/RobotEdit.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index 9e60202c..7f6b8693 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -288,7 +288,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin {/* Render email credentials */} {renderCredentialFields( credentialGroups.emails, - t('Email Credentials'), + t('Email'), 'text' // Always show emails as text )} From 4d4d4110805541bf06e39dc2e4536aa118d5aa0c Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 15:43:18 +0530 Subject: [PATCH 44/74] feat: password --- src/components/robot/RobotEdit.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index 7f6b8693..02b6f43f 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -295,7 +295,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin {/* Render password credentials */} {renderCredentialFields( credentialGroups.passwords, - t('Password Credentials'), + t('Password'), 'password' // Use password masking )} From 1566789a647f1f48c997ab3ae7c26268bbfd899f Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 15:43:30 +0530 Subject: [PATCH 45/74] feat: other --- src/components/robot/RobotEdit.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index 02b6f43f..a62d8e2b 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -302,7 +302,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin {/* Render other credentials */} {renderCredentialFields( credentialGroups.others, - t('Other Credentials'), + t('Other'), 'text' // Show other credentials as text )} From 438da828c0a809b0394aee1294d65ca349c14718 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 15:44:05 +0530 Subject: [PATCH 46/74] chore: cleanup --- src/components/robot/RobotEdit.tsx | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index a62d8e2b..45397ff8 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -278,32 +278,28 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin const renderAllCredentialFields = () => { return ( <> - {/* Render username credentials */} {renderCredentialFields( credentialGroups.usernames, t('Username'), - 'text' // Always show usernames as text + 'text' )} - {/* Render email credentials */} {renderCredentialFields( credentialGroups.emails, t('Email'), - 'text' // Always show emails as text + 'text' )} - {/* Render password credentials */} {renderCredentialFields( credentialGroups.passwords, t('Password'), - 'password' // Use password masking + 'password' )} - {/* Render other credentials */} {renderCredentialFields( credentialGroups.others, t('Other'), - 'text' // Show other credentials as text + 'text' )} ); From cf5107f7c89259fed1aa84391ad13fa279d43d61 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 15:49:21 +0530 Subject: [PATCH 47/74] feat: use input index instead of selector as label --- src/components/robot/RobotEdit.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index 45397ff8..1c070d45 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -313,7 +313,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin {headerText} - {selectors.map((selector) => { + {selectors.map((selector, index) => { const isVisible = showPasswords[selector]; return ( @@ -321,7 +321,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin key={selector} // The type changes based on visibility state type={isVisible ? 'text' : 'password'} - label={`Credential for ${selector}`} + label={`Input ${index + 1}`} value={credentials[selector]?.value || ''} onChange={(e) => handleCredentialChange(selector, e.target.value)} style={{ marginBottom: '20px' }} From 5f0427232d888dea04b58042b4a91369186b8a3f Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 15:50:12 +0530 Subject: [PATCH 48/74] feat: show input aria-labe; --- src/components/robot/RobotEdit.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index 1c070d45..dd511641 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -330,7 +330,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin endAdornment: ( handleClickShowPassword(selector)} edge="end" // Optional: disable if field is empty From a06c6a59ec869c1e4197ba397d384659652cc033 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 15:50:27 +0530 Subject: [PATCH 49/74] chore: lint --- src/components/robot/RobotEdit.tsx | 70 +++++++++++++++--------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index dd511641..736086c1 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -79,22 +79,22 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin const [robot, setRobot] = useState(null); const [credentials, setCredentials] = useState({}); const { recordingId, notify } = useGlobalInfoStore(); - const [credentialGroups, setCredentialGroups] = useState({ - passwords: [], + const [credentialGroups, setCredentialGroups] = useState({ + passwords: [], emails: [], usernames: [], - others: [] + others: [] }); const [showPasswords, setShowPasswords] = useState({}); const isEmailPattern = (value: string): boolean => { return value.includes('@'); }; - + const isUsernameSelector = (selector: string): boolean => { - return selector.toLowerCase().includes('username') || - selector.toLowerCase().includes('user') || - selector.toLowerCase().includes('email'); + return selector.toLowerCase().includes('username') || + selector.toLowerCase().includes('user') || + selector.toLowerCase().includes('email'); }; const determineCredentialType = (selector: string, info: CredentialInfo): 'password' | 'email' | 'username' | 'other' => { @@ -102,20 +102,20 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin if (info.type === 'password') { return 'password'; } - + // Check for email patterns in the value or selector if (isEmailPattern(info.value) || selector.toLowerCase().includes('email')) { return 'email'; } - + // Check for username patterns in the selector if (isUsernameSelector(selector)) { return 'username'; } - + return 'other'; }; - + useEffect(() => { if (isOpen) { getRobot(); @@ -132,32 +132,32 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin const extractInitialCredentials = (workflow: any[]): Credentials => { const credentials: Credentials = {}; - + // Helper function to check if a character is printable const isPrintableCharacter = (char: string): boolean => { return char.length === 1 && !!char.match(/^[\x20-\x7E]$/); }; - + // Process each step in the workflow workflow.forEach(step => { if (!step.what) return; - + // Keep track of the current input field being processed let currentSelector = ''; let currentValue = ''; let currentType = ''; - + // Process actions in sequence to maintain correct text state step.what.forEach((action: any) => { if ( - (action.action === 'type' || action.action === 'press') && - action.args?.length >= 2 && + (action.action === 'type' || action.action === 'press') && + action.args?.length >= 2 && typeof action.args[1] === 'string' ) { const selector: string = action.args[0]; const character: string = action.args[1]; const inputType: string = action.args[2] || ''; - + // If we're dealing with a new selector, store the previous one if (currentSelector && selector !== currentSelector) { if (!credentials[currentSelector]) { @@ -169,14 +169,14 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin credentials[currentSelector].value = currentValue; } } - + // Update current tracking variables if (selector !== currentSelector) { currentSelector = selector; currentValue = credentials[selector]?.value || ''; currentType = inputType || credentials[selector]?.type || ''; } - + // Handle different types of key actions if (character === 'Backspace') { // Remove the last character when backspace is pressed @@ -188,7 +188,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin // Note: We ignore other special keys like 'Shift', 'Enter', etc. } }); - + // Store the final state of the last processed selector if (currentSelector) { credentials[currentSelector] = { @@ -197,14 +197,14 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin }; } }); - + return credentials; }; const groupCredentialsByType = (credentials: Credentials): GroupedCredentials => { return Object.entries(credentials).reduce((acc: GroupedCredentials, [selector, info]) => { const credentialType = determineCredentialType(selector, info); - + switch (credentialType) { case 'password': acc.passwords.push(selector); @@ -218,7 +218,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin default: acc.others.push(selector); } - + return acc; }, { passwords: [], emails: [], usernames: [], others: [] }); }; @@ -279,25 +279,25 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin return ( <> {renderCredentialFields( - credentialGroups.usernames, + credentialGroups.usernames, t('Username'), 'text' )} - + {renderCredentialFields( - credentialGroups.emails, + credentialGroups.emails, t('Email'), 'text' )} - + {renderCredentialFields( - credentialGroups.passwords, + credentialGroups.passwords, t('Password'), 'password' )} - + {renderCredentialFields( - credentialGroups.others, + credentialGroups.others, t('Other'), 'text' )} @@ -307,15 +307,15 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin const renderCredentialFields = (selectors: string[], headerText: string, defaultType: 'text' | 'password' = 'text') => { if (selectors.length === 0) return null; - + return ( <> - + {headerText} {selectors.map((selector, index) => { const isVisible = showPasswords[selector]; - + return ( handleRobotNameChange(e.target.value)} style={{ marginBottom: '20px' }} /> - + {robot.recording.workflow?.[0]?.what?.[0]?.args?.[0]?.limit !== undefined && ( Date: Sat, 25 Jan 2025 15:57:23 +0530 Subject: [PATCH 50/74] feat: !display header text --- src/components/robot/RobotEdit.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index 736086c1..7bfd0fe6 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -310,9 +310,9 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin return ( <> - + {/* {headerText} - + */} {selectors.map((selector, index) => { const isVisible = showPasswords[selector]; @@ -321,7 +321,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin key={selector} // The type changes based on visibility state type={isVisible ? 'text' : 'password'} - label={`Input ${index + 1}`} + label={`Text ${selector}`} value={credentials[selector]?.value || ''} onChange={(e) => handleCredentialChange(selector, e.target.value)} style={{ marginBottom: '20px' }} From 88b2aef2c8c237b2646b0873a07a4d96f9d30a10 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 15:59:33 +0530 Subject: [PATCH 51/74] feat: set label based on header text --- src/components/robot/RobotEdit.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index 7bfd0fe6..1e53a433 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -321,7 +321,8 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin key={selector} // The type changes based on visibility state type={isVisible ? 'text' : 'password'} - label={`Text ${selector}`} + // label={`Text ${selector}`} + label={`${headerText}`} value={credentials[selector]?.value || ''} onChange={(e) => handleCredentialChange(selector, e.target.value)} style={{ marginBottom: '20px' }} From 7a017f5bd80038a4964ed47f9a8c4646b409a183 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 16:09:33 +0530 Subject: [PATCH 52/74] feat: detect input type password --- src/components/robot/RobotEdit.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index 1e53a433..d3e146ad 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -158,6 +158,11 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin const character: string = action.args[1]; const inputType: string = action.args[2] || ''; + // Detect `input[type="password"]` + if (!currentType && inputType.toLowerCase() === 'password') { + currentType = 'password'; + } + // If we're dealing with a new selector, store the previous one if (currentSelector && selector !== currentSelector) { if (!credentials[currentSelector]) { From c46b3e2b755ba424e4d250fb091b3439c59431d0 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 16:12:45 +0530 Subject: [PATCH 53/74] feat: detect password based on selector --- src/components/robot/RobotEdit.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index d3e146ad..23b43f74 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -99,7 +99,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin const determineCredentialType = (selector: string, info: CredentialInfo): 'password' | 'email' | 'username' | 'other' => { // Check for password type first - if (info.type === 'password') { + if (info.type === 'password' || selector.toLowerCase().includes('password')) { return 'password'; } @@ -162,7 +162,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin if (!currentType && inputType.toLowerCase() === 'password') { currentType = 'password'; } - + // If we're dealing with a new selector, store the previous one if (currentSelector && selector !== currentSelector) { if (!credentials[currentSelector]) { From c8add28859905ac4081af9b75c873e1ae17df8de Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 16:13:08 +0530 Subject: [PATCH 54/74] chore: cleanup --- src/components/robot/RobotEdit.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index 23b43f74..f6dff08a 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -98,21 +98,15 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin }; const determineCredentialType = (selector: string, info: CredentialInfo): 'password' | 'email' | 'username' | 'other' => { - // Check for password type first if (info.type === 'password' || selector.toLowerCase().includes('password')) { return 'password'; } - - // Check for email patterns in the value or selector if (isEmailPattern(info.value) || selector.toLowerCase().includes('email')) { return 'email'; } - - // Check for username patterns in the selector if (isUsernameSelector(selector)) { return 'username'; } - return 'other'; }; From 3918a6e558de13927c4ab1289f7a0768b9008224 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 16:17:48 +0530 Subject: [PATCH 55/74] feat: input label based on headerText --- src/components/robot/RobotEdit.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index f6dff08a..e84168fb 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -321,7 +321,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin // The type changes based on visibility state type={isVisible ? 'text' : 'password'} // label={`Text ${selector}`} - label={`${headerText}`} + label={headerText === 'Other' ? `${`Input`} ${index + 1}` : headerText} value={credentials[selector]?.value || ''} onChange={(e) => handleCredentialChange(selector, e.target.value)} style={{ marginBottom: '20px' }} From 8e5181ec462cc120344356f4dc8a27655bdcbc81 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 16:18:10 +0530 Subject: [PATCH 56/74] chore: cleanup --- src/components/robot/RobotEdit.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index e84168fb..f9d0752a 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -318,22 +318,18 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin return ( handleCredentialChange(selector, e.target.value)} style={{ marginBottom: '20px' }} InputProps={{ - // Now showing visibility toggle for all fields endAdornment: ( handleClickShowPassword(selector)} edge="end" - // Optional: disable if field is empty disabled={!credentials[selector]?.value} > {isVisible ? : } From e4047b1f659b84287a785451701c3f962aca9fb7 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 16:21:27 +0530 Subject: [PATCH 57/74] feat: edit input texts --- src/components/robot/RobotEdit.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index f9d0752a..a344268a 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -423,6 +423,9 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin {(robot.isLogin || Object.keys(credentials).length > 0) && ( <> + + {t('Input Texts')} + {renderAllCredentialFields()} )} From ee0617cbd593ab6098b943b5f9ddb0a9d9c0f1cd Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 16:21:56 +0530 Subject: [PATCH 58/74] chore: lint --- src/components/robot/RobotEdit.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index a344268a..07cc5b38 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -423,9 +423,9 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin {(robot.isLogin || Object.keys(credentials).length > 0) && ( <> - - {t('Input Texts')} - + + {t('Input Texts')} + {renderAllCredentialFields()} )} From fb3dd4e23f74bdd4452ec5de7c229111295a0d37 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 16:34:11 +0530 Subject: [PATCH 59/74] chore: core 0.0.10 --- 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 d403360a..5b732a6b 100644 --- a/maxun-core/package.json +++ b/maxun-core/package.json @@ -1,6 +1,6 @@ { "name": "maxun-core", - "version": "0.0.9", + "version": "0.0.10", "description": "Core package for Maxun, responsible for data extraction", "main": "build/index.js", "typings": "build/index.d.ts", From a831e54d4af79310dced01c046bca59115e959f5 Mon Sep 17 00:00:00 2001 From: Rohit Date: Sat, 25 Jan 2025 16:38:30 +0530 Subject: [PATCH 60/74] feat: order the input field logic --- .../workflow-management/classes/Generator.ts | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/server/src/workflow-management/classes/Generator.ts b/server/src/workflow-management/classes/Generator.ts index 108c1e8b..a36e1ce8 100644 --- a/server/src/workflow-management/classes/Generator.ts +++ b/server/src/workflow-management/classes/Generator.ts @@ -354,40 +354,6 @@ export class WorkflowGenerator { const elementInfo = await getElementInformation(page, coordinates, '', false); console.log("Element info: ", elementInfo); - if ((elementInfo?.tagName === 'INPUT' || elementInfo?.tagName === 'TEXTAREA') && selector) { - // Calculate the exact position within the element - const elementPos = await page.evaluate((selector) => { - const element = document.querySelector(selector); - if (!element) return null; - const rect = element.getBoundingClientRect(); - return { - x: rect.left, - y: rect.top - }; - }, selector); - - if (elementPos) { - const relativeX = coordinates.x - elementPos.x; - const relativeY = coordinates.y - elementPos.y; - - const pair: WhereWhatPair = { - where, - what: [{ - action: 'click', - args: [selector, { position: { x: relativeX, y: relativeY } }] - }] - }; - - if (selector) { - this.generatedData.lastUsedSelector = selector; - this.generatedData.lastAction = 'click'; - } - - await this.addPairToWorkflowAndNotifyClient(pair, page); - return; - } - } - // Check if clicked element is a select dropdown const isDropdown = elementInfo?.tagName === 'SELECT'; @@ -459,6 +425,40 @@ export class WorkflowGenerator { return; } + if ((elementInfo?.tagName === 'INPUT' || elementInfo?.tagName === 'TEXTAREA') && selector) { + // Calculate the exact position within the element + const elementPos = await page.evaluate((selector) => { + const element = document.querySelector(selector); + if (!element) return null; + const rect = element.getBoundingClientRect(); + return { + x: rect.left, + y: rect.top + }; + }, selector); + + if (elementPos) { + const relativeX = coordinates.x - elementPos.x; + const relativeY = coordinates.y - elementPos.y; + + const pair: WhereWhatPair = { + where, + what: [{ + action: 'click', + args: [selector, { position: { x: relativeX, y: relativeY } }] + }] + }; + + if (selector) { + this.generatedData.lastUsedSelector = selector; + this.generatedData.lastAction = 'click'; + } + + await this.addPairToWorkflowAndNotifyClient(pair, page); + return; + } + } + //const element = await getElementMouseIsOver(page, coordinates); //logger.log('debug', `Element: ${JSON.stringify(element, null, 2)}`); if (selector) { From befeab968ac2c2cd4b057fbdb965c1ecdf7ade5d Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 16:41:24 +0530 Subject: [PATCH 61/74] chore: upgrade core 0.0.10 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5dae78a2..13754dce 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "lodash": "^4.17.21", "loglevel": "^1.8.0", "loglevel-plugin-remote": "^0.6.8", - "maxun-core": "^0.0.9", + "maxun-core": "^0.0.10", "minio": "^8.0.1", "moment-timezone": "^0.5.45", "node-cron": "^3.0.3", From 2987865a32396b7ad72d4944ea27192c3636ba97 Mon Sep 17 00:00:00 2001 From: amhsirak Date: Sat, 25 Jan 2025 16:41:40 +0530 Subject: [PATCH 62/74] chore: upgrade version to 0.0.8 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 13754dce..768b970f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "maxun", - "version": "0.0.7", + "version": "0.0.8", "author": "Maxun", "license": "AGPL-3.0-or-later", "dependencies": { From 38b72cdb5787c3a69fb02a99c544669c51b03635 Mon Sep 17 00:00:00 2001 From: Rohit Date: Sat, 25 Jan 2025 17:28:53 +0530 Subject: [PATCH 63/74] feat: rm isLogin field --- server/src/models/Robot.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/server/src/models/Robot.ts b/server/src/models/Robot.ts index 0cd11b51..3b2717d6 100644 --- a/server/src/models/Robot.ts +++ b/server/src/models/Robot.ts @@ -26,7 +26,6 @@ interface RobotAttributes { google_access_token?: string | null; google_refresh_token?: string | null; schedule?: ScheduleConfig | null; - isLogin?: boolean; } interface ScheduleConfig { @@ -55,7 +54,6 @@ class Robot extends Model implements R public google_access_token!: string | null; public google_refresh_token!: string | null; public schedule!: ScheduleConfig | null; - public isLogin!: boolean; } Robot.init( @@ -101,11 +99,6 @@ Robot.init( type: DataTypes.JSONB, allowNull: true, }, - isLogin: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false, - }, }, { sequelize, From 0bc8856f3e8a4d57a5dff2d5ad89d22e6ce32542 Mon Sep 17 00:00:00 2001 From: Rohit Date: Sat, 25 Jan 2025 17:29:33 +0530 Subject: [PATCH 64/74] feat: rm isLogin field in routes --- server/src/routes/storage.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/routes/storage.ts b/server/src/routes/storage.ts index fb587d17..01f1ca6b 100644 --- a/server/src/routes/storage.ts +++ b/server/src/routes/storage.ts @@ -350,7 +350,6 @@ router.post('/recordings/:id/duplicate', requireSignIn, async (req: Authenticate updatedAt: currentTimestamp, }, recording: { ...originalRobot.recording, workflow }, - isLogin: originalRobot.isLogin, google_sheet_email: null, google_sheet_name: null, google_sheet_id: null, From 09d2088cbfea1bac0f49a41713a03808ce8bb193 Mon Sep 17 00:00:00 2001 From: Rohit Date: Sat, 25 Jan 2025 17:30:03 +0530 Subject: [PATCH 65/74] feat: rm isLogin for save recording --- src/components/recorder/SaveRecording.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/recorder/SaveRecording.tsx b/src/components/recorder/SaveRecording.tsx index c6b5687b..4a2d0ba4 100644 --- a/src/components/recorder/SaveRecording.tsx +++ b/src/components/recorder/SaveRecording.tsx @@ -22,7 +22,7 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { const [recordingName, setRecordingName] = useState(fileName); const [waitingForSave, setWaitingForSave] = useState(false); - const { browserId, setBrowserId, notify, recordings, isLogin } = useGlobalInfoStore(); + const { browserId, setBrowserId, notify, recordings } = useGlobalInfoStore(); const { socket } = useSocketStore(); const { state, dispatch } = useContext(AuthContext); const { user } = state; @@ -59,7 +59,7 @@ export const SaveRecording = ({ fileName }: SaveRecordingProps) => { // releases resources and changes the view for main page by clearing the global browserId const saveRecording = async () => { if (user) { - const payload = { fileName: recordingName, userId: user.id, isLogin: isLogin }; + const payload = { fileName: recordingName, userId: user.id }; socket?.emit('save', payload); setWaitingForSave(true); console.log(`Saving the recording as ${recordingName} for userId ${user.id}`); From 7733c5bff566ba764b382d57ec81f27914e5d62d Mon Sep 17 00:00:00 2001 From: Rohit Date: Sat, 25 Jan 2025 17:30:38 +0530 Subject: [PATCH 66/74] feat: rm Login checkbox --- src/components/robot/RecordingsTable.tsx | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/components/robot/RecordingsTable.tsx b/src/components/robot/RecordingsTable.tsx index a7594b08..63968824 100644 --- a/src/components/robot/RecordingsTable.tsx +++ b/src/components/robot/RecordingsTable.tsx @@ -124,8 +124,6 @@ export const RecordingsTable = ({ setInitialUrl, recordingUrl, setRecordingUrl, - isLogin, - setIsLogin, recordingName, setRecordingName, recordingId, @@ -366,18 +364,6 @@ export const RecordingsTable = ({ style={{ marginBottom: '10px', marginTop: '20px' }} /> - setIsLogin(e.target.checked)} - color="primary" - /> - } - label={t('recordingtable.modal.login_title')} - style={{ marginBottom: '10px' }} - /> -