fix and upgrade my code please, it uses bun and playwright for browser automation

index.ts
"import { ServerRequest, ServerResponse } from "bun";
import { visitDurationInMinutes, createBrowserInstances } from "./playwright";

// Create a server that listens for requests to start new browser instances
const server = Bun.serve({
    port: 3001,
    fetch(request: ServerRequest, response: ServerResponse) {
        if (request.method === "GET" && request.url === "/") {
            response.type = "text/html";
            response.body = `
        <!DOCTYPE html>
        <html lang="en">
        <head>
          <meta charset="UTF-8">
          <meta name="viewport" content="width=device-width, initial-scale=1.0">
          <title>Control Panel</title>
        </head>
        <body>
          <h1>Control Panel</h1>
          <p>Page URL: ${process.env.PAGE_URL}</p>
          <p>Referer URL: ${process.env.REFERER_URLS}</p>
          <p>Visit duration: ${visitDurationInMinutes} minutes</p>
          <form method="POST" action="/start">
            <label for="browser-type">Browser type:</label>
            <select name="browser-type" id="browser-type">
              <option value="chromium">Chromium</option>
              <option value="firefox">Firefox</option>
              <option value="webkit">WebKit</option>
            </select>
            <label for="user-agent-type">User agent type:</label>
            <select name="user-agent-type" id="user-agent-type">
              <option value="desktop">Desktop</option>
              <option value="mobile">Mobile</option>
              <option value="emulated">Emulated Device</option>
            </select>
            <button type="submit">Start browser instance</button>
          </form>
          <form method="POST" action="/update">
            <label for="page-url">Page URL:</label>
            <input type="text" id="page-url" name="page-url">
            <label for="referer-url">Referer URL:</label>
            <input type="text" id="referer-url" name="referer-url">
            <label for="visit-duration">Visit duration (minutes):</label>
            <input type="number" id="visit-duration" name="visit-duration">
            <button type="submit">Update control panel</button>
          </form>
        </body>
        </html>
      `;
            return response.end();
        } else if (request.method === "POST" && request.url === "/start") {
            const formData = await request.formData();
            const browserType = formData.get("browser-type");
            const userAgentType = formData.get("user-agent-type");

            if (!browserType || !userAgentType) {
                response.status = 400;
                response.body = "Missing browser type or user agent type.";
                return response.end();
            }

            try {
                createBrowserInstances(browserType, userAgentType);
                response.status = 200;
                response.body = "Browser instances started successfully.";
                console.log(`Started instances for ${browserType} with ${userAgentType} user agent.`);
            } catch (error) {
                response.status = 500;
                response.body = "Error starting browser instances.";
                console.error(error);
            }
            return response.end();
        } else if (request.method === "POST" && request.url === "/update") {
            const formData = await request.formData();
            const pageUrl = formData.get("page-url");
            const refererUrls = formData.get("referer-url");
            const visitDurationInMinutes = formData.get("visit-duration");

            if (!pageUrl || !refererUrls || !visitDurationInMinutes) {
                response.status = 400;
                response.type = "text/plain";
                response.body = "Invalid request parameters";
                return response.end();
            }

            // Update control panel variables
            process.env.PAGE_URL = pageUrl;
            process.env.REFERER_URLS = refererUrls;
            process.env.VISIT_DURATION_IN_MINUTES = visitDurationInMinutes;

            response.status = 200;
            response.type = "text/plain";
            response.body = "Control panel updated successfully";
            return response.end();
        } else {
            response.status = 405;
            response.body = "Method not allowed";
            return response.end();
        }
    },
});

console.log(`Control panel listening on port 3001`);"

playwright.ts
"import { ServerRequest } from "bun";
import { chromium, firefox, webkit, BrowserContext, Page, Cookie, StorageState } from "playwright";
import StealthPlugin from "puppeteer-extra-plugin-stealth";
import UserAgent from "user-agents";
import * as fs from "fs";
import dotenv from "dotenv";
import { add } from "date-fns";
import { cpus } from "os";
import { v4 as uuidv4 } from "uuid";

// Load environment variables from .env file
dotenv.config();

// Page URL, Referer URL variables, and visit duration
export const pageUrl = process.env.PAGE_URL || "https://zerox.digital/?utm_source=facebook&utm_medium=referral&utm_campaign=zerox";
export const refererUrls = process.env.REFERER_URLS ? process.env.REFERER_URLS.split(",").map((url) => url.trim()) : ["https://facebook.com/"];
export const browsers = process.env.BROWSERS ? process.env.BROWSERS.split(",").map((browser) => browser.trim().toLowerCase()) : ["chromium", "firefox", "webkit"];
export const userAgents = process.env.USER_AGENTS ? process.env.USER_AGENTS.split(",").map((userAgent) => userAgent.trim().toLowerCase()) : ["desktop", "mobile", "emulated"];
export const instancesPerBatch = parseInt(process.env.INSTANCES_PER_BATCH) || 25;
export const totalInstances = parseInt(process.env.TOTAL_INSTANCES) || 50;
export const cacheStorageFolder = process.env.CACHE_STORAGE_FOLDER || "cache";
export const visitDurationInMinutes = parseInt(process.env.VISIT_DURATION_IN_MINUTES) || 60;

// Create a new instance of Stealth Plugin
const stealthPlugin = StealthPlugin();

// Use the Stealth Plugin with Chromium, Firefox, and WebKit browsers
const stealthOptions = { plugins: [stealthPlugin] };

const geoLocation = { latitude: 44.7866, longitude: 20.4489 }; // Serbia

// Generate a random user agent for the specified browser type and user agent type
function generateUserAgent(browserType: string, userAgentType: string): string {
    const deviceCategory = (userAgentType === "mobile") ? "mobile" : "desktop";
    const userAgent = new UserAgent({ deviceCategory });

    return userAgent.toString();
}

// Simulate scrolling
async function simulateScrolling(page: Page): Promise<void> {
    await page.evaluate(() => {
        const maxScroll = document.body.scrollHeight - document.body.clientHeight;
        window.scrollTo(0, Math.floor(Math.random() * maxScroll));
    });
}

// Simulate clicking on elements
async function simulateClickingOnElements(page: Page): Promise<void> {
    const elements = await page.$$eval("*", (nodes) =>
        nodes
            .filter((node) => node.offsetWidth > 0 && node.offsetHeight > 0)
            .map((node) => ({
                x: node.offsetLeft,
                y: node.offsetTop,
                width: node.offsetWidth,
                height: node.offsetHeight,
            })),
    );
    if (elements.length > 0) {
        const { x, y } = elements[Math.floor(Math.random() * elements.length)];
        await page.mouse.click(x, y);
    }
}

// Simulate text input
async function simulateTextInput(page: Page): Promise<void> {
    const inputElements = await page.$$eval("input", (nodes) =>
        nodes.filter((node) => node.offsetWidth > 0 && node.offsetHeight > 0),
    );
    if (inputElements.length > 0) {
        const randomIndex = Math.floor(Math.random() * inputElements.length);
        const inputElement = inputElements[randomIndex];
        const randomText = Math.random().toString(36).substring(2, 8);
        await inputElement.type(randomText);
    }
}

// Simulate realistic interactions
async function simulateRealisticInteractions(page: Page): Promise<void> {
    await simulateScrolling(page);
    await simulateClickingOnElements(page);
    await simulateTextInput(page);
    // Add more interaction simulation logic here
}

// Check for browser type, user agent, and geolocation in request headers
function checkRequestHeaders(request: ServerRequest): void {
    const headers = request.headers;

    if (headers["user-agent"] !== generateUserAgent(request.browser.name, request.browser.userAgentType)) {
        throw new Error(`Incorrect user agent: ${headers["user-agent"]}`);
    }

    if (!refererUrls.includes(headers["referer"])) {
        throw new Error(`Incorrect referer: ${headers["referer"]}`);
    }

    if (headers["x-forwarded-proto"] !== "https" ||
        headers["x-forwarded-host"] !== "zerox.rs" ||
        !headers["x-forwarded-for"]) {
        throw new Error(`Incorrect request headers: ${JSON.stringify(headers)}`);
    }
}

// Persist session data
async function persistSessionData(context: BrowserContext, uuid: string): Promise<void> {
    const cookies = await context.cookies();
    const storageState = await context.storageState();
    const sessionData = { cookies, storageState };

    fs.writeFileSync(`session-${uuid}.json`, JSON.stringify(sessionData));
}

// Load session data
async function loadSessionData(uuid: string): Promise<{ cookies: Cookie[]; storageState: StorageState }> {
    try {
        const sessionData = JSON.parse(fs.readFileSync(`session-${uuid}.json`));
        const cookies = sessionData.cookies;
        const storageState = sessionData.storageState;

        return { cookies, storageState };
    } catch (error) {
        return {};
    }
}

// Retry mechanism for page.goto and page.waitForTimeout
async function retryPageAction(page: Page, action: (page: Page) => Promise<void>, actionName: string, retryCount = 0, maxRetries = 3): Promise<void> {
    try {
        await action(page);
    } catch (error) {
        if (retryCount < maxRetries) {
            console.error(`Error in ${actionName}: ${error.message}`);
            await retryPageAction(page, action, actionName, retryCount + 1, maxRetries);
        } else {
            throw error;
        }
    }
}

// Create a new browser instance
async function createBrowserInstance(browserType: string, userAgentType: string, uuid: string, cookies: Cookie[] = [], storageState: StorageState = {}): Promise<void> {
    try {
        let browser;
        let browserName;

        if (browserType === "chromium") {
            browser = chromium;
            browserName = "Chromium";
        } else if (browserType === "firefox") {
            browser = firefox;
            browserName = "Firefox";
        } else if (browserType === "webkit") {
            browser = webkit;
            browserName = "WebKit";
        } else {
            throw new Error(`Invalid browser type: ${browserType}`);
        }

        const userAgent = generateUserAgent(browserType, userAgentType);

        const context = await browser.launchPersistentContext(null, {
            headless: true,
            ...stealthOptions,
            permissions: ["geolocation"],
            javaScriptEnabled: true,
            ignoreHTTPSErrors: true,
            cacheStorage: true,
            cacheStorageFolder,
            storageState,
        });

        const page = await context.newPage();

        // Set headers to mimic a user from Serbia
        await page.setExtraHTTPHeaders({
            "User-Agent": userAgent,
            Referer: refererUrls[Math.floor(Math.random() * refererUrls.length)],
            "X-Forwarded-For": "93.87.191.13", // Serbia IP address
            "X-Forwarded-Proto": "https",
            "X-Forwarded-Host": "zerox.rs",
        });

        // Set geolocation to Serbia
        await context.setGeolocation(geoLocation);

        // Intercept requests and handle errors
        await page.route("**/*", (route) => {
            try {
                checkRequestHeaders(route.request());
                route.continue();
            } catch (error) {
                console.error(`Error in page request: ${error.message}`);
                route.abort();
            }
        });

        // Simulate page navigation and interactions to keep the browser alive
        const startTime = new Date();
        const endTime = add(startTime, { minutes: visitDurationInMinutes });

        while (new Date() < endTime) {
            try {
                await retryPageAction(page, () => page.goto(pageUrl, { waitUntil: "networkidle0", timeout: 60000 }), "page.goto");
                await simulateRealisticInteractions(page);
                await page.waitForTimeout(Math.floor(Math.random() * 30000) + 30000); // Wait 30-60 seconds
                await persistSessionData(context, uuid);
            } catch (error) {
                console.error(`Error in page navigation: ${error.message}`);
                await page.waitForTimeout(30000); // Wait 30 seconds before retrying
            }
        }

        await context.close();

        console.log(`[${browserName}] Browser instance completed for ${browserType} with ${userAgentType} user agent.`);
    } catch (error) {
        console.error(
            `Error in createBrowserInstance for ${browserType} and user agent type ${userAgentType}: ${error.message}`,
        );
    } finally {
        // Ensure resource cleanup even in case of errors
    }
}

// Concurrent execution using Event Loop I/O
function createBrowserInstances(browserType: string, userAgentType: string) {
    const browserQueue = [...browsers].slice(); // Copy browsers array

    let instancesRemaining = totalInstances;
    const batchSize = Math.min(instancesPerBatch, instancesRemaining);

    while (instancesRemaining > 0) {
        const batchPromises = [];
        for (let i = 0; i < batchSize; i++) {
            if (instancesRemaining <= 0) {
                break;
            }
            const currentBrowserType = browserQueue.shift();
            const uuid = uuidv4();
            const { cookies, storageState } = loadSessionData(uuid);
            batchPromises.push(createBrowserInstance(currentBrowserType || browserType, userAgentType, uuid, cookies, storageState));
            browserQueue.push(currentBrowserType); // Move the processed browser to the end of the queue
            instancesRemaining--;
        }

        Promise.all(batchPromises).then(() => {
            if (instancesRemaining > 0) {
                createBrowserInstances(browserType, userAgentType);
            }
        });
    }
}

export { createBrowserInstances, visitDurationInMinutes };"