File size: 4,609 Bytes
bf6b21d 3ec8db7 bf6b21d 26325c2 bf6b21d 072a0a2 bf6b21d 072a0a2 2e6fbe5 7edc106 2e6fbe5 26325c2 bf6b21d 072a0a2 bf6b21d 2e6fbe5 bf6b21d 3f46de1 7edc106 bf6b21d 58149fd 26325c2 2e6fbe5 072a0a2 bf6b21d 3f1d94d bf6b21d 7edc106 2e6fbe5 bf6b21d 2e6fbe5 58149fd 7edc106 2e6fbe5 7edc106 bf6b21d 072a0a2 2e6fbe5 26325c2 2e6fbe5 7edc106 2e6fbe5 26325c2 bf6b21d 072a0a2 26325c2 2e6fbe5 072a0a2 bf6b21d 2e6fbe5 072a0a2 bf6b21d 2e6fbe5 bf6b21d 58149fd 3f1d94d 26325c2 7edc106 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | import { Browser, BrowserContext, Page, chromium } from "playwright";
let browser: Browser | null = null;
let context: BrowserContext | null = null;
let page: Page | null = null;
function getLaunchArgs(): string[] {
return [
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-dev-shm-usage",
"--disable-gpu",
"--disable-software-rasterizer",
"--no-first-run",
"--no-default-browser-check",
"--disable-extensions",
"--disable-sync",
"--mute-audio",
"--window-size=1280,720",
];
}
function ensureNodeRuntimeEnv() {
process.env.HOME = process.env.HOME || "/home/node";
process.env.XDG_CACHE_HOME =
process.env.XDG_CACHE_HOME || "/home/node/.cache";
process.env.XDG_CONFIG_HOME =
process.env.XDG_CONFIG_HOME || "/home/node/.config";
process.env.XDG_RUNTIME_DIR =
process.env.XDG_RUNTIME_DIR || "/tmp/runtime-node";
process.env.DBUS_SESSION_BUS_ADDRESS =
process.env.DBUS_SESSION_BUS_ADDRESS || "disabled";
process.env.PLAYWRIGHT_BROWSERS_PATH =
process.env.PLAYWRIGHT_BROWSERS_PATH || "/home/node/.cache/ms-playwright";
}
export async function initBrowser(): Promise<void> {
await closeBrowser();
ensureNodeRuntimeEnv();
console.log(`[Browser] UID : ${process.getuid?.() ?? "unknown"}`);
console.log(`[Browser] HOME : ${process.env.HOME}`);
console.log(`[Browser] PW path : ${process.env.PLAYWRIGHT_BROWSERS_PATH}`);
browser = await chromium.launch({
headless: true,
args: getLaunchArgs(),
env: {
...process.env,
HOME: process.env.HOME,
XDG_CACHE_HOME: process.env.XDG_CACHE_HOME,
XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME,
XDG_RUNTIME_DIR: process.env.XDG_RUNTIME_DIR,
DBUS_SESSION_BUS_ADDRESS: process.env.DBUS_SESSION_BUS_ADDRESS,
PLAYWRIGHT_BROWSERS_PATH: process.env.PLAYWRIGHT_BROWSERS_PATH,
},
});
context = await browser.newContext({
viewport: { width: 1280, height: 720 },
locale: "en-US",
ignoreHTTPSErrors: true,
userAgent:
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
});
context.setDefaultTimeout(10_000);
context.setDefaultNavigationTimeout(15_000);
page = await context.newPage();
await page.route("**/*.{woff,woff2,ttf,otf,eot}", (route) => route.abort());
await page.route(
"**/{analytics,gtag,ga.js,fbq,hotjar,clarity,segment,mixpanel}*",
(route) => route.abort()
);
await page.setExtraHTTPHeaders({
"Accept-Language": "en-US,en;q=0.9",
});
await page.goto("about:blank", { timeout: 5_000 });
console.log("[Browser] Ready on about:blank");
}
export async function navigateTo(url: string): Promise<void> {
if (!page) {
throw new Error("No active browser page");
}
console.log(`[Browser] Navigating to ${url}`);
try {
const response = await page.goto(url, {
waitUntil: "commit",
timeout: 15_000,
});
console.log("[Browser] commit ok:", response?.status() ?? "no-response");
await page
.waitForLoadState("domcontentloaded", { timeout: 8_000 })
.catch(() => {});
await page.waitForTimeout(1200);
return;
} catch (err) {
console.warn("[Browser] commit failed, retrying with domcontentloaded", err);
}
try {
await page.goto(url, {
waitUntil: "domcontentloaded",
timeout: 15_000,
});
console.log("[Browser] Navigation OK (domcontentloaded)");
await page.waitForTimeout(1200);
return;
} catch (err) {
console.warn("[Browser] domcontentloaded failed, waiting for body", err);
}
try {
await page.waitForSelector("body", {
timeout: 8_000,
});
console.log("[Browser] Body detected");
await page.waitForTimeout(800);
return;
} catch {
throw new Error(`Navigation failed for ${url}`);
}
}
export function getPage(): Page | null {
return page;
}
export function isBrowserAlive(): boolean {
return !!browser && browser.isConnected() && !!context && !!page && !page.isClosed();
}
export async function closeBrowser(): Promise<void> {
try {
if (page && !page.isClosed()) {
await page.close().catch(() => {});
}
page = null;
if (context) {
await context.close().catch(() => {});
}
context = null;
if (browser && browser.isConnected()) {
await browser.close().catch(() => {});
}
browser = null;
} catch (e) {
console.warn("[Browser] Close warning:", e);
page = null;
context = null;
browser = null;
} finally {
console.log("[Browser] Session closed cleanly.");
}
} |