@@ -18,12 +18,43 @@ import { AuthenticatedRequest } from './record';
1818import { computeNextRun } from '../utils/schedule' ;
1919import { capture } from "../utils/analytics" ;
2020import { tryCatch } from 'bullmq' ;
21+ import { encrypt , decrypt } from '../utils/auth' ;
2122import { WorkflowFile } from 'maxun-core' ;
2223import { Page } from 'playwright' ;
2324chromium . use ( stealthPlugin ( ) ) ;
2425
2526export const router = Router ( ) ;
2627
28+ export const decryptWorkflowActions = async ( workflow : any [ ] , ) : Promise < any [ ] > => {
29+ // Create a deep copy to avoid mutating the original workflow
30+ const processedWorkflow = JSON . parse ( JSON . stringify ( workflow ) ) ;
31+
32+ // Process each step in the workflow
33+ for ( const step of processedWorkflow ) {
34+ if ( ! step . what ) continue ;
35+
36+ // Process each action in the step
37+ for ( const action of step . what ) {
38+ // Only process type and press actions
39+ if ( ( action . action === 'type' || action . action === 'press' ) && Array . isArray ( action . args ) && action . args . length > 1 ) {
40+ // The second argument contains the encrypted value
41+ const encryptedValue = action . args [ 1 ] ;
42+ if ( typeof encryptedValue === 'string' ) {
43+ try {
44+ // Decrypt the value and update the args array
45+ action . args [ 1 ] = await decrypt ( encryptedValue ) ;
46+ } catch ( error ) {
47+ console . error ( 'Failed to decrypt value:' , error ) ;
48+ // Keep the encrypted value if decryption fails
49+ }
50+ }
51+ }
52+ }
53+ }
54+
55+ return processedWorkflow ;
56+ } ;
57+
2758/**
2859 * Logs information about recordings API.
2960 */
@@ -55,6 +86,13 @@ router.get('/recordings/:id', requireSignIn, async (req, res) => {
5586 raw : true
5687 }
5788 ) ;
89+
90+ if ( data ?. recording ?. workflow ) {
91+ data . recording . workflow = await decryptWorkflowActions (
92+ data . recording . workflow ,
93+ ) ;
94+ }
95+
5896 return res . send ( data ) ;
5997 } catch ( e ) {
6098 logger . log ( 'info' , 'Error while reading robots' ) ;
@@ -116,13 +154,70 @@ function formatRunResponse(run: any) {
116154 return formattedRun ;
117155}
118156
157+ interface CredentialUpdate {
158+ [ selector : string ] : string ;
159+ }
160+
161+ function updateTypeActionsInWorkflow ( workflow : any [ ] , credentials : CredentialUpdate ) {
162+ return workflow . map ( step => {
163+ if ( ! step . what ) return step ;
164+
165+ // First pass: mark indices to remove
166+ const indicesToRemove = new Set < number > ( ) ;
167+ step . what . forEach ( ( action : any , index : any ) => {
168+ if ( ! action . action || ! action . args ?. [ 0 ] ) return ;
169+
170+ // If it's a type/press action for a credential
171+ if ( ( action . action === 'type' || action . action === 'press' ) && credentials [ action . args [ 0 ] ] ) {
172+ indicesToRemove . add ( index ) ;
173+ // Check if next action is waitForLoadState
174+ if ( step . what [ index + 1 ] ?. action === 'waitForLoadState' ) {
175+ indicesToRemove . add ( index + 1 ) ;
176+ }
177+ }
178+ } ) ;
179+
180+ // Filter out marked indices and create new what array
181+ const filteredWhat = step . what . filter ( ( _ : any , index : any ) => ! indicesToRemove . has ( index ) ) ;
182+
183+ // Add new type actions after click actions
184+ Object . entries ( credentials ) . forEach ( ( [ selector , credential ] ) => {
185+ const clickIndex = filteredWhat . findIndex ( ( action : any ) =>
186+ action . action === 'click' && action . args ?. [ 0 ] === selector
187+ ) ;
188+
189+ if ( clickIndex !== - 1 ) {
190+ const chars = credential . split ( '' ) ;
191+ chars . forEach ( ( char , i ) => {
192+ // Add type action
193+ filteredWhat . splice ( clickIndex + 1 + ( i * 2 ) , 0 , {
194+ action : 'type' ,
195+ args : [ selector , encrypt ( char ) ]
196+ } ) ;
197+
198+ // Add waitForLoadState
199+ filteredWhat . splice ( clickIndex + 2 + ( i * 2 ) , 0 , {
200+ action : 'waitForLoadState' ,
201+ args : [ 'networkidle' ]
202+ } ) ;
203+ } ) ;
204+ }
205+ } ) ;
206+
207+ return {
208+ ...step ,
209+ what : filteredWhat
210+ } ;
211+ } ) ;
212+ }
213+
119214/**
120215 * PUT endpoint to update the name and limit of a robot.
121216 */
122217router . put ( '/recordings/:id' , requireSignIn , async ( req : AuthenticatedRequest , res ) => {
123218 try {
124219 const { id } = req . params ;
125- const { name, limit } = req . body ;
220+ const { name, limit, credentials } = req . body ;
126221
127222 // Validate input
128223 if ( ! name && limit === undefined ) {
@@ -141,17 +236,21 @@ router.put('/recordings/:id', requireSignIn, async (req: AuthenticatedRequest, r
141236 robot . set ( 'recording_meta' , { ...robot . recording_meta , name } ) ;
142237 }
143238
239+ let workflow = [ ...robot . recording . workflow ] ; // Create a copy of the workflow
240+
241+ if ( credentials ) {
242+ workflow = updateTypeActionsInWorkflow ( workflow , credentials ) ;
243+ }
244+
144245 // Update the limit
145246 if ( limit !== undefined ) {
146- const workflow = [ ...robot . recording . workflow ] ; // Create a copy of the workflow
147-
148247 // Ensure the workflow structure is valid before updating
149248 if (
150249 workflow . length > 0 &&
151250 workflow [ 0 ] ?. what ?. [ 0 ]
152251 ) {
153252 // Create a new workflow object with the updated limit
154- const updatedWorkflow = workflow . map ( ( step , index ) => {
253+ workflow = workflow . map ( ( step , index ) => {
155254 if ( index === 0 ) { // Assuming you want to update the first step
156255 return {
157256 ...step ,
@@ -173,14 +272,13 @@ router.put('/recordings/:id', requireSignIn, async (req: AuthenticatedRequest, r
173272 }
174273 return step ;
175274 } ) ;
176-
177- // Replace the workflow in the recording object
178- robot . set ( 'recording' , { ...robot . recording , workflow : updatedWorkflow } ) ;
179275 } else {
180276 return res . status ( 400 ) . json ( { error : 'Invalid workflow structure for updating limit.' } ) ;
181277 }
182278 }
183279
280+ robot . set ( 'recording' , { ...robot . recording , workflow } ) ;
281+
184282 await robot . save ( ) ;
185283
186284 const updatedRobot = await Robot . findOne ( { where : { 'recording_meta.id' : id } } ) ;
@@ -248,6 +346,7 @@ router.post('/recordings/:id/duplicate', requireSignIn, async (req: Authenticate
248346 updatedAt : currentTimestamp ,
249347 } ,
250348 recording : { ...originalRobot . recording , workflow } ,
349+ isLogin : originalRobot . isLogin ,
251350 google_sheet_email : null ,
252351 google_sheet_name : null ,
253352 google_sheet_id : null ,
0 commit comments