189 lines
6.6 KiB
JavaScript
189 lines
6.6 KiB
JavaScript
require("dotenv").config();
|
|
|
|
const path = require("node:path");
|
|
const fs = require("node:fs/promises");
|
|
const { chromium } = require("playwright");
|
|
|
|
const DEFAULT_URL = "https://bikegear.in/acerbis";
|
|
const TEST_URL = process.argv[2] || process.env.BIKEGEAR_PROXY_TEST_URL || DEFAULT_URL;
|
|
const WARM_URL = process.env.BIKEGEAR_PROXY_TEST_WARM_URL || DEFAULT_URL;
|
|
const PROFILE_PATH = path.resolve("data/sources/bikegear/.browser-profile-test");
|
|
|
|
const STEALTH_SCRIPT = `
|
|
(() => {
|
|
Object.defineProperty(navigator, 'webdriver', { get: () => false });
|
|
const fakePlugin = (name, file, mimes) => {
|
|
const p = Object.create(Plugin.prototype);
|
|
Object.defineProperty(p, 'name', { get: () => name });
|
|
Object.defineProperty(p, 'filename', { get: () => file });
|
|
Object.defineProperty(p, 'length', { get: () => mimes.length });
|
|
return p;
|
|
};
|
|
Object.defineProperty(navigator, 'plugins', {
|
|
get: () => [
|
|
fakePlugin('Chrome PDF Plugin', 'internal-pdf-viewer', ['application/x-google-chrome-pdf']),
|
|
fakePlugin('Chrome PDF Viewer', 'mhjfbmdgcfjbbpaeojofohoefgiehjai', ['application/pdf']),
|
|
fakePlugin('Native Client', 'internal-nacl-plugin', ['application/x-nacl']),
|
|
],
|
|
});
|
|
if (!window.chrome) window.chrome = { runtime: {}, loadTimes: () => {}, csi: () => {}, app: {} };
|
|
Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });
|
|
})();
|
|
`;
|
|
|
|
function getProxyUrl() {
|
|
if (process.env.BIKEGEAR_PROXY_URL) {
|
|
return process.env.BIKEGEAR_PROXY_URL;
|
|
}
|
|
|
|
const host = process.env.BIKEGEAR_PROXY_HOST;
|
|
const port = process.env.BIKEGEAR_PROXY_PORT;
|
|
if (!host || !port) {
|
|
return null;
|
|
}
|
|
|
|
const protocol = String(process.env.BIKEGEAR_PROXY_PROTOCOL || "http").replace(/:$/, "");
|
|
const username = buildWebshareUsername(process.env.BIKEGEAR_PROXY_USERNAME || "");
|
|
const password = process.env.BIKEGEAR_PROXY_PASSWORD || "";
|
|
const auth = username
|
|
? `${encodeURIComponent(username)}:${encodeURIComponent(password)}@`
|
|
: "";
|
|
|
|
return `${protocol}://${auth}${host}:${port}`;
|
|
}
|
|
|
|
function buildWebshareUsername(baseUsername) {
|
|
const username = String(baseUsername || "").trim();
|
|
if (!username) return "";
|
|
|
|
const country = String(process.env.BIKEGEAR_PROXY_COUNTRY || "").trim().toLowerCase();
|
|
const sessionId = String(process.env.BIKEGEAR_PROXY_SESSION_ID || "").trim();
|
|
if (!country && !sessionId) return username;
|
|
|
|
const countryPart = country && !new RegExp(`-${country}(?:-|$)`, "i").test(username)
|
|
? `-${country}`
|
|
: "";
|
|
const sessionPart = sessionId && !new RegExp(`-${sessionId}$`).test(username)
|
|
? `-${sessionId}`
|
|
: "";
|
|
|
|
return `${username}${countryPart}${sessionPart}`;
|
|
}
|
|
|
|
function maskProxyUrl(proxyUrl) {
|
|
if (!proxyUrl) return "none";
|
|
try {
|
|
const u = new URL(proxyUrl);
|
|
return `${u.protocol}//${u.username ? "<redacted>@" : ""}${u.host}`;
|
|
} catch {
|
|
return "<invalid proxy url>";
|
|
}
|
|
}
|
|
|
|
function buildProxyOptions(proxyUrl) {
|
|
if (!proxyUrl) return {};
|
|
const u = new URL(proxyUrl);
|
|
return {
|
|
proxy: {
|
|
server: `${u.protocol}//${u.host}`,
|
|
...(u.username
|
|
? {
|
|
username: decodeURIComponent(u.username),
|
|
password: decodeURIComponent(u.password),
|
|
}
|
|
: {}),
|
|
},
|
|
};
|
|
}
|
|
|
|
async function main() {
|
|
const proxyUrl = getProxyUrl();
|
|
await fs.mkdir(PROFILE_PATH, { recursive: true });
|
|
|
|
console.log(`[BIKEGEAR-PROXY-TEST] url=${TEST_URL}`);
|
|
console.log(`[BIKEGEAR-PROXY-TEST] warmUrl=${TEST_URL === WARM_URL ? "-" : WARM_URL}`);
|
|
console.log(`[BIKEGEAR-PROXY-TEST] proxy=${maskProxyUrl(proxyUrl)}`);
|
|
|
|
const context = await chromium.launchPersistentContext(PROFILE_PATH, {
|
|
headless: true,
|
|
args: [
|
|
"--no-sandbox",
|
|
"--disable-setuid-sandbox",
|
|
"--disable-blink-features=AutomationControlled",
|
|
],
|
|
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
|
|
locale: "en-US",
|
|
viewport: { width: 1280, height: 800 },
|
|
extraHTTPHeaders: { "Accept-Language": "en-US,en;q=0.9" },
|
|
...buildProxyOptions(proxyUrl),
|
|
});
|
|
|
|
try {
|
|
await context.addInitScript(STEALTH_SCRIPT);
|
|
const page = await context.newPage();
|
|
|
|
if (TEST_URL !== WARM_URL) {
|
|
const warmResponse = await page.goto(WARM_URL, { waitUntil: "domcontentloaded", timeout: 45000 });
|
|
await page.waitForTimeout(8000);
|
|
console.log(`[BIKEGEAR-PROXY-TEST] warmStatus=${warmResponse?.status() || 0}`);
|
|
}
|
|
|
|
let response;
|
|
if (TEST_URL !== WARM_URL && !TEST_URL.includes("?page=")) {
|
|
response = await clickLinkForUrl(page, TEST_URL);
|
|
}
|
|
if (!response) {
|
|
response = await page.goto(TEST_URL, {
|
|
waitUntil: "domcontentloaded",
|
|
timeout: 45000,
|
|
...(TEST_URL !== WARM_URL ? { referer: WARM_URL } : {}),
|
|
});
|
|
}
|
|
await page.waitForTimeout(8000);
|
|
|
|
const status = response?.status() || 0;
|
|
const title = await page.title().catch(() => "");
|
|
const html = await page.content();
|
|
const productLinks = (html.match(/class="product-img/g) || []).length;
|
|
const hasChallengeTitle = /just a moment|attention required|access denied/i.test(title);
|
|
const hasChallengeMarkup = /cf-error|cf-browser-verification/i.test(html);
|
|
const hasCloudflare = hasChallengeTitle || hasChallengeMarkup;
|
|
|
|
console.log(`[BIKEGEAR-PROXY-TEST] status=${status}`);
|
|
console.log(`[BIKEGEAR-PROXY-TEST] title=${title || "-"}`);
|
|
console.log(`[BIKEGEAR-PROXY-TEST] productLinks=${productLinks}`);
|
|
console.log(`[BIKEGEAR-PROXY-TEST] cloudflarePage=${hasCloudflare}`);
|
|
|
|
if (status === 200 && productLinks > 0 && !hasChallengeTitle) {
|
|
console.log("[BIKEGEAR-PROXY-TEST] PASS browser can read BikeGear listing through this proxy.");
|
|
return;
|
|
}
|
|
|
|
process.exitCode = 1;
|
|
console.error("[BIKEGEAR-PROXY-TEST] FAIL proxy/browser did not reach a usable BikeGear listing.");
|
|
} finally {
|
|
await context.close().catch(() => {});
|
|
}
|
|
}
|
|
|
|
async function clickLinkForUrl(page, url) {
|
|
const navigationPromise = page.waitForNavigation({ waitUntil: "domcontentloaded", timeout: 45000 })
|
|
.catch(() => null);
|
|
const clicked = await page.evaluate((targetUrl) => {
|
|
const links = Array.from(document.querySelectorAll("a[href]"));
|
|
const link = links.find((a) => a.href === targetUrl);
|
|
if (!link) return false;
|
|
link.click();
|
|
return true;
|
|
}, url).catch(() => false);
|
|
|
|
if (!clicked) return null;
|
|
await page.waitForURL(url, { timeout: 45000 }).catch(() => {});
|
|
return navigationPromise;
|
|
}
|
|
|
|
main().catch((error) => {
|
|
process.exitCode = 1;
|
|
console.error(`[BIKEGEAR-PROXY-TEST] ERROR ${error.message}`);
|
|
});
|