import { CDPService } from "../../services/cdp/cdp.service.js"; import { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"; import { getErrors } from "../../utils/errors.js"; import { CreateSessionRequest, SessionDetails, SessionStreamRequest } from "./sessions.schema.js"; import { CookieData } from "../../services/context/types.js"; import { getUrl, getBaseUrl } from "../../utils/url.js"; export const handleLaunchBrowserSession = async ( server: FastifyInstance, request: CreateSessionRequest, reply: FastifyReply, ) => { try { const { sessionId, proxyUrl, userDataDir, persist, userAgent, sessionContext, extensions, logSinkUrl, timezone, dimensions, isSelenium, blockAds, optimizeBandwidth, extra, credentials, skipFingerprintInjection, userPreferences, deviceConfig, headless, } = request.body; return await server.sessionService.startSession({ sessionId, proxyUrl, userDataDir, persist, userAgent, sessionContext: sessionContext as { cookies?: CookieData[] | undefined; localStorage?: Record> | undefined; }, extensions, logSinkUrl, timezone, dimensions, isSelenium, blockAds, optimizeBandwidth, extra, credentials, skipFingerprintInjection, userPreferences, deviceConfig, headless, }); } catch (e: unknown) { server.log.error({ err: e }, "Failed lauching browser session"); const error = getErrors(e); return reply.code(500).send({ success: false, message: error }); } }; export const handleExitBrowserSession = async ( server: FastifyInstance, request: FastifyRequest, reply: FastifyReply, ) => { try { const sessionDetails = await server.sessionService.endSession(); reply.send({ success: true, ...sessionDetails }); } catch (e: any) { const error = getErrors(e); return reply.code(500).send({ success: false, message: error }); } }; export const handleGetBrowserContext = async ( browserService: CDPService, request: FastifyRequest, reply: FastifyReply, ) => { const context = await browserService.getBrowserState(); return reply.send(context); }; export const handleGetSessionDetails = async ( server: FastifyInstance, request: FastifyRequest<{ Params: { sessionId: string } }>, reply: FastifyReply, ) => { const sessionId = request.params.sessionId; if (sessionId !== server.sessionService.activeSession.id) { return reply.send({ id: sessionId, createdAt: new Date().toISOString(), status: "released", duration: 0, eventCount: 0, timeout: 0, creditsUsed: 0, websocketUrl: getBaseUrl("ws"), debugUrl: getUrl("v1/sessions/debug"), debuggerUrl: getUrl("v1/devtools/inspector.html"), sessionViewerUrl: getBaseUrl(), userAgent: "", isSelenium: false, proxy: "", proxyTxBytes: 0, proxyRxBytes: 0, solveCaptcha: false, } as SessionDetails); } const session = server.sessionService.activeSession; const duration = new Date().getTime() - new Date(session.createdAt).getTime(); console.log("duration", duration); return reply.send({ ...session, duration, }); }; export const handleGetSessions = async ( server: FastifyInstance, request: FastifyRequest, reply: FastifyReply, ) => { const currentSession = { ...server.sessionService.activeSession, duration: new Date().getTime() - new Date(server.sessionService.activeSession.createdAt).getTime(), }; const pastSessions = server.sessionService.pastSessions; return reply.send({ sessions: [currentSession, ...pastSessions] }); }; export const handleGetSessionStream = async ( server: FastifyInstance, request: SessionStreamRequest, reply: FastifyReply, ) => { const { showControls, theme, interactive, pageId, pageIndex } = request.query; const singlePageMode = !!(pageId || pageIndex); // Construct WebSocket URL with page parameters if present let wsUrl = getUrl("v1/sessions/cast", "ws"); if (pageId) { wsUrl += `?pageId=${encodeURIComponent(pageId)}`; } else if (pageIndex) { wsUrl += `?pageIndex=${encodeURIComponent(pageIndex)}`; } return reply.view("live-session-streamer.ejs", { wsUrl, showControls, theme, interactive, dimensions: server.sessionService.activeSession.dimensions, singlePageMode, }); }; export const handleGetSessionLiveDetails = async ( server: FastifyInstance, request: FastifyRequest<{ Params: { id: string } }>, reply: FastifyReply, ) => { try { const pages = await server.cdpService.getAllPages(); const pagesInfo = await Promise.all( pages.map(async (page) => { try { const pageId = page.target()._targetId; const title = await page.title(); let favicon: string | null = null; try { favicon = await page.evaluate(() => { const iconLink = document.querySelector( 'link[rel="icon"], link[rel="shortcut icon"]', ); if (iconLink) { const href = iconLink.getAttribute("href"); if (href?.startsWith("http")) return href; if (href?.startsWith("//")) return window.location.protocol + href; if (href?.startsWith("/")) return window.location.origin + href; return window.location.origin + "/" + href; } return null; }); } catch (error) {} return { id: pageId, url: page.url(), title, favicon, }; } catch (error) { console.error("Error collecting page info:", error); return null; } }), ); const validPagesInfo = pagesInfo.filter((page) => page !== null); const browserVersion = await server.cdpService.getBrowserState(); const browserState = { status: server.sessionService.activeSession.status, userAgent: server.sessionService.activeSession.userAgent, browserVersion, initialDimensions: server.sessionService.activeSession.dimensions || { width: 1920, height: 1080, }, pageCount: validPagesInfo.length, }; return reply.send({ pages: validPagesInfo, browserState, websocketUrl: server.sessionService.activeSession.websocketUrl, sessionViewerUrl: server.sessionService.activeSession.sessionViewerUrl, sessionViewerFullscreenUrl: `${server.sessionService.activeSession.sessionViewerUrl}?showControls=false`, }); } catch (error) { console.error("Error getting session state:", error); return reply.code(500).send({ message: "Failed to get session state", error: getErrors(error), }); } };