Merge pull request #485 from getmaxun/edit-rob-url
feat: edit robot target url
This commit is contained in:
@@ -259,11 +259,11 @@ function handleWorkflowActions(workflow: any[], credentials: Credentials) {
|
||||
router.put('/recordings/:id', requireSignIn, async (req: AuthenticatedRequest, res) => {
|
||||
try {
|
||||
const { id } = req.params;
|
||||
const { name, limit, credentials } = req.body;
|
||||
const { name, limit, credentials, targetUrl } = req.body;
|
||||
|
||||
// Validate input
|
||||
if (!name && limit === undefined) {
|
||||
return res.status(400).json({ error: 'Either "name" or "limit" must be provided.' });
|
||||
if (!name && limit === undefined && !targetUrl) {
|
||||
return res.status(400).json({ error: 'Either "name", "limit" or "target_url" must be provided.' });
|
||||
}
|
||||
|
||||
// Fetch the robot by ID
|
||||
@@ -278,6 +278,27 @@ router.put('/recordings/:id', requireSignIn, async (req: AuthenticatedRequest, r
|
||||
robot.set('recording_meta', { ...robot.recording_meta, name });
|
||||
}
|
||||
|
||||
if (targetUrl) {
|
||||
const updatedWorkflow = robot.recording.workflow.map((step) => {
|
||||
if (step.where?.url && step.where.url !== "about:blank") {
|
||||
step.where.url = targetUrl;
|
||||
}
|
||||
|
||||
step.what.forEach((action) => {
|
||||
if (action.action === "goto" && action.args?.length) {
|
||||
action.args[0] = targetUrl;
|
||||
}
|
||||
});
|
||||
|
||||
return step;
|
||||
});
|
||||
|
||||
robot.set('recording', { ...robot.recording, workflow: updatedWorkflow });
|
||||
robot.changed('recording', true);
|
||||
}
|
||||
|
||||
await robot.save();
|
||||
|
||||
let workflow = [...robot.recording.workflow]; // Create a copy of the workflow
|
||||
|
||||
if (credentials) {
|
||||
|
||||
@@ -28,7 +28,7 @@ export const getStoredRecordings = async (): Promise<string[] | null> => {
|
||||
}
|
||||
};
|
||||
|
||||
export const updateRecording = async (id: string, data: { name?: string; limit?: number, credentials?: Credentials }): Promise<boolean> => {
|
||||
export const updateRecording = async (id: string, data: { name?: string; limit?: number, credentials?: Credentials, targetUrl?: string }): Promise<boolean> => {
|
||||
try {
|
||||
const response = await axios.put(`${apiUrl}/storage/recordings/${id}`, data);
|
||||
if (response.status === 200) {
|
||||
|
||||
@@ -7,7 +7,6 @@ import { modalStyle } from "../recorder/AddWhereCondModal";
|
||||
import { useGlobalInfoStore } from '../../context/globalInfo';
|
||||
import { getStoredRecording, updateRecording } from '../../api/storage';
|
||||
import { WhereWhatPair } from 'maxun-core';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
interface RobotMeta {
|
||||
name: string;
|
||||
@@ -126,35 +125,35 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
|
||||
|
||||
function extractInitialCredentials(workflow: any[]): Credentials {
|
||||
const credentials: Credentials = {};
|
||||
|
||||
|
||||
const isPrintableCharacter = (char: string): boolean => {
|
||||
return char.length === 1 && !!char.match(/^[\x20-\x7E]$/);
|
||||
};
|
||||
|
||||
|
||||
workflow.forEach(step => {
|
||||
if (!step.what) return;
|
||||
|
||||
|
||||
let currentSelector = '';
|
||||
let currentValue = '';
|
||||
let currentType = '';
|
||||
let i = 0;
|
||||
|
||||
|
||||
while (i < step.what.length) {
|
||||
const action = step.what[i];
|
||||
|
||||
|
||||
if (!action.action || !action.args?.[0]) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
const selector = action.args[0];
|
||||
|
||||
|
||||
// Handle full word type actions first
|
||||
if (action.action === 'type' &&
|
||||
action.args?.length >= 2 &&
|
||||
if (action.action === 'type' &&
|
||||
action.args?.length >= 2 &&
|
||||
typeof action.args[1] === 'string' &&
|
||||
action.args[1].length > 1) {
|
||||
|
||||
action.args[1].length > 1) {
|
||||
|
||||
if (!credentials[selector]) {
|
||||
credentials[selector] = {
|
||||
value: action.args[1],
|
||||
@@ -164,12 +163,12 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Handle character-by-character sequences (both type and press)
|
||||
if ((action.action === 'type' || action.action === 'press') &&
|
||||
action.args?.length >= 2 &&
|
||||
typeof action.args[1] === 'string') {
|
||||
|
||||
|
||||
if (selector !== currentSelector) {
|
||||
if (currentSelector && currentValue) {
|
||||
credentials[currentSelector] = {
|
||||
@@ -181,23 +180,23 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
|
||||
currentValue = credentials[selector]?.value || '';
|
||||
currentType = action.args[2] || credentials[selector]?.type || 'text';
|
||||
}
|
||||
|
||||
|
||||
const character = action.args[1];
|
||||
|
||||
|
||||
if (isPrintableCharacter(character)) {
|
||||
currentValue += character;
|
||||
} else if (character === 'Backspace') {
|
||||
currentValue = currentValue.slice(0, -1);
|
||||
}
|
||||
|
||||
|
||||
if (!currentType && action.args[2]?.toLowerCase() === 'password') {
|
||||
currentType = 'password';
|
||||
}
|
||||
|
||||
|
||||
let j = i + 1;
|
||||
while (j < step.what.length) {
|
||||
const nextAction = step.what[j];
|
||||
if (!nextAction.action || !nextAction.args?.[0] ||
|
||||
if (!nextAction.action || !nextAction.args?.[0] ||
|
||||
nextAction.args[0] !== selector ||
|
||||
(nextAction.action !== 'type' && nextAction.action !== 'press')) {
|
||||
break;
|
||||
@@ -209,18 +208,18 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
||||
|
||||
credentials[currentSelector] = {
|
||||
value: currentValue,
|
||||
type: currentType
|
||||
};
|
||||
|
||||
i = j;
|
||||
|
||||
i = j;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (currentSelector && currentValue) {
|
||||
credentials[currentSelector] = {
|
||||
value: currentValue,
|
||||
@@ -228,7 +227,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return credentials;
|
||||
}
|
||||
|
||||
@@ -306,6 +305,24 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
|
||||
});
|
||||
};
|
||||
|
||||
const handleTargetUrlChange = (newUrl: string) => {
|
||||
setRobot((prev) => {
|
||||
if (!prev) return prev;
|
||||
|
||||
const updatedWorkflow = [...prev.recording.workflow];
|
||||
const lastPairIndex = updatedWorkflow.length - 1;
|
||||
|
||||
if (lastPairIndex >= 0) {
|
||||
const gotoAction = updatedWorkflow[lastPairIndex]?.what?.find(action => action.action === "goto");
|
||||
if (gotoAction && gotoAction.args && gotoAction.args.length > 0) {
|
||||
gotoAction.args[0] = newUrl;
|
||||
}
|
||||
}
|
||||
|
||||
return { ...prev, recording: { ...prev.recording, workflow: updatedWorkflow } };
|
||||
});
|
||||
};
|
||||
|
||||
const renderAllCredentialFields = () => {
|
||||
return (
|
||||
<>
|
||||
@@ -390,10 +407,14 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
|
||||
return acc;
|
||||
}, {} as Record<string, CredentialInfo>);
|
||||
|
||||
const lastPair = robot.recording.workflow[robot.recording.workflow.length - 1];
|
||||
const targetUrl = lastPair?.what.find(action => action.action === "goto")?.args?.[0];
|
||||
|
||||
const payload = {
|
||||
name: robot.recording_meta.name,
|
||||
limit: robot.recording.workflow[0]?.what[0]?.args?.[0]?.limit,
|
||||
credentials: credentialsForPayload,
|
||||
targetUrl: targetUrl,
|
||||
};
|
||||
|
||||
const success = await updateRecording(robot.recording_meta.id, payload);
|
||||
@@ -413,6 +434,9 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
|
||||
}
|
||||
};
|
||||
|
||||
const lastPair = robot?.recording.workflow[robot?.recording.workflow.length - 1];
|
||||
const targetUrl = lastPair?.what.find(action => action.action === "goto")?.args?.[0];
|
||||
|
||||
return (
|
||||
<GenericModal
|
||||
isOpen={isOpen}
|
||||
@@ -435,6 +459,15 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
|
||||
style={{ marginBottom: '20px' }}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
label="Robot Target URL"
|
||||
key="Robot Target URL"
|
||||
type='text'
|
||||
value={targetUrl || ''}
|
||||
onChange={(e) => handleTargetUrlChange(e.target.value)}
|
||||
style={{ marginBottom: '20px', marginTop: '15px' }}
|
||||
/>
|
||||
|
||||
{robot.recording.workflow?.[0]?.what?.[0]?.args?.[0]?.limit !== undefined && (
|
||||
<TextField
|
||||
label={t('robot_edit.robot_limit')}
|
||||
|
||||
Reference in New Issue
Block a user