From 7117c93f7893f681527bc44017a1ff09bd6bbe42 Mon Sep 17 00:00:00 2001 From: Rohit Date: Tue, 3 Jun 2025 18:42:11 +0530 Subject: [PATCH] feat: unified create and run robot route --- server/src/routes/storage.ts | 177 +++++++++++++++++++++-------------- 1 file changed, 105 insertions(+), 72 deletions(-) diff --git a/server/src/routes/storage.ts b/server/src/routes/storage.ts index b4e8cdfd..92807d31 100644 --- a/server/src/routes/storage.ts +++ b/server/src/routes/storage.ts @@ -1,6 +1,6 @@ import { Router } from 'express'; import logger from "../logger"; -import { createRemoteBrowserForRun, getActiveBrowserIdByState } from "../browser-management/controller"; +import { createRemoteBrowserForRun, destroyRemoteBrowser, getActiveBrowserIdByState } from "../browser-management/controller"; import { chromium } from 'playwright-extra'; import stealthPlugin from 'puppeteer-extra-plugin-stealth'; import { browserPool } from "../server"; @@ -517,98 +517,131 @@ router.put('/runs/:id', requireSignIn, async (req: AuthenticatedRequest, res) => return res.status(401).send({ error: 'Unauthorized' }); } - const proxyConfig = await getDecryptedProxyConfig(req.user.id); - let proxyOptions: any = {}; - - if (proxyConfig.proxy_url) { - proxyOptions = { - server: proxyConfig.proxy_url, - ...(proxyConfig.proxy_username && proxyConfig.proxy_password && { - username: proxyConfig.proxy_username, - password: proxyConfig.proxy_password, - }), - }; - } - - console.log(`Proxy config for run: ${JSON.stringify(proxyOptions)}`); - // Generate runId first const runId = uuid(); - // Check if user has reached browser limit - const userBrowserIds = browserPool.getAllBrowserIdsForUser(req.user.id); - const canCreateBrowser = userBrowserIds.length < 2; - - if (canCreateBrowser) { - // User has available browser slots, create it directly - const id = createRemoteBrowserForRun(req.user.id); + const canCreateBrowser = await browserPool.hasAvailableBrowserSlots(req.user.id, "run"); - const run = await Run.create({ - status: 'running', + if (canCreateBrowser) { + let browserId: string; + + try { + browserId = await createRemoteBrowserForRun(req.user.id); + + if (!browserId || browserId.trim() === '') { + throw new Error('Failed to generate valid browser ID'); + } + + logger.log('info', `Created browser ${browserId} for run ${runId}`); + + } catch (browserError: any) { + logger.log('error', `Failed to create browser: ${browserError.message}`); + return res.status(500).send({ error: 'Failed to create browser instance' }); + } + + try { + await Run.create({ + status: 'running', + name: recording.recording_meta.name, + robotId: recording.id, + robotMetaId: recording.recording_meta.id, + startedAt: new Date().toLocaleString(), + finishedAt: '', + browserId: browserId, + interpreterSettings: req.body, + log: '', + runId, + runByUserId: req.user.id, + serializableOutput: {}, + binaryOutput: {}, + }); + + logger.log('info', `Created run ${runId} with browser ${browserId}`); + + } catch (dbError: any) { + logger.log('error', `Database error creating run: ${dbError.message}`); + + try { + await destroyRemoteBrowser(browserId, req.user.id); + } catch (cleanupError: any) { + logger.log('warn', `Failed to cleanup browser after run creation failure: ${cleanupError.message}`); + } + + return res.status(500).send({ error: 'Failed to create run record' }); + } + + try { + const userQueueName = `execute-run-user-${req.user.id}`; + await pgBoss.createQueue(userQueueName); + + const jobId = await pgBoss.send(userQueueName, { + emailId: req.user.email, + userId: req.user.id, + runId: runId, + browserId: browserId, + interpreterSettings: req.body + }); + + logger.log('info', `Queued run execution job with ID: ${jobId} for run: ${runId}`); + } catch (queueError: any) { + logger.log('error', `Failed to queue run execution: ${queueError.message}`); + + try { + await Run.update({ + status: 'failed', + finishedAt: new Date().toLocaleString(), + log: 'Failed to queue execution job' + }, { where: { runId: runId } }); + + await destroyRemoteBrowser(browserId, req.user.id); + } catch (cleanupError: any) { + logger.log('warn', `Failed to cleanup after queue error: ${cleanupError.message}`); + } + + return res.status(503).send({ error: 'Unable to queue run, please try again later' }); + } + + return res.send({ + browserId: browserId, + runId: runId, + robotMetaId: recording.recording_meta.id, + queued: false + }); + } else { + const browserId = uuid(); + + await Run.create({ + status: 'queued', name: recording.recording_meta.name, robotId: recording.id, robotMetaId: recording.recording_meta.id, startedAt: new Date().toLocaleString(), finishedAt: '', - browserId: id, + browserId, interpreterSettings: req.body, - log: '', + log: 'Run queued - waiting for available browser slot', runId, runByUserId: req.user.id, serializableOutput: {}, binaryOutput: {}, }); - - const plainRun = run.toJSON(); - + return res.send({ - browserId: id, - runId: plainRun.runId, + browserId: browserId, + runId: runId, robotMetaId: recording.recording_meta.id, - queued: false + queued: true }); - } else { - const browserId = getActiveBrowserIdByState(req.user.id, "run") - - if (browserId) { - // User has reached the browser limit, queue the run - try { - // Create the run record with 'queued' status - await Run.create({ - status: 'queued', - name: recording.recording_meta.name, - robotId: recording.id, - robotMetaId: recording.recording_meta.id, - startedAt: new Date().toLocaleString(), - finishedAt: '', - browserId: browserId, // Random will be updated later - interpreterSettings: req.body, - log: 'Run queued - waiting for available browser slot', - runId, - runByUserId: req.user.id, - serializableOutput: {}, - binaryOutput: {}, - }); - - return res.send({ - browserId: browserId, - runId: runId, - robotMetaId: recording.recording_meta.id, - queued: true, - }); - } catch (queueError: any) { - logger.log('error', `Failed to queue run job: ${queueError.message}`); - return res.status(503).send({ error: 'Unable to queue run, please try again later' }); - } - } else { - logger.log('info', "Browser id does not exist"); - return res.send(''); - } } } catch (e) { const { message } = e as Error; - logger.log('info', `Error while creating a run with robot id: ${req.params.id} - ${message}`); - return res.send(''); + logger.log('error', `Error while creating a run with robot id: ${req.params.id} - ${message}`); + + if (message.includes('invalid input syntax for type uuid')) { + return res.status(400).send({ error: 'Invalid UUID format detected' }); + } + + return res.status(500).send({ error: 'Internal server error' }); } });