perf(proxy): capture/scrape browser'larında bant genişliği israfını kes
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
Some checks failed
QA Gate (P0/P1) / Test affected app (pull_request) Has been cancelled
DataImpulse residential kotası hızlı tükeniyordu (15 günde 58.5 GB). CSV analizi capture/scrape tarayıcılarının tam sayfa (resim/font/CSS/reklam/ CDN) yüklediğini gösterdi. - pcat + emex capture: resource-blocking'e stylesheet + üçüncü-parti çöp (yastatic, google fonts/autofill, adsco.re, displayvertising, tidio, ipify) eklendi. Widget runtime CDN'leri (jsdelivr/unpkg) bilinçli hariç. - emex: engelleme context seviyesine alındı → tüm tab'ları kapsıyor. - pcat: ipify exit-IP probe artık PCAT_EXIT_IP_PROBE ile default-off (15 günde ~54k faturalı istek + 352 MB, sadece telemetri içindi). Not: emex scraping resmi tarayıcıda yüklemiyor; URL'yi HTML'den parse edip boyutu ayrı Range GET ile alıyor → resim engellemek scraping'i etkilemez. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -260,6 +260,42 @@ export class EmexBrowserService implements OnModuleInit, OnModuleDestroy {
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
});
|
||||
|
||||
// Block heavy resources to save proxy bandwidth. Scraping reads HTML/DOM
|
||||
// only — schema-image URLs are parsed from the markup and their dims come
|
||||
// from a separate Range GET, so the browser never needs images/fonts/CSS.
|
||||
// Context-level route covers every tab (session establish + acquirePage).
|
||||
await this.context.route("**/*", (route) => {
|
||||
const url = route.request().url();
|
||||
const type = route.request().resourceType();
|
||||
|
||||
if (["image", "font", "stylesheet", "media"].includes(type)) {
|
||||
return route.abort();
|
||||
}
|
||||
|
||||
// Third-party junk seen in proxy usage: analytics, ad networks, chat
|
||||
// widgets, Google/Yandex assets, and our own ipify egress probe.
|
||||
if (
|
||||
url.includes("google-analytics.com") ||
|
||||
url.includes("googletagmanager.com") ||
|
||||
url.includes("mc.yandex.ru") ||
|
||||
url.includes("yastatic.net") ||
|
||||
url.includes("fonts.googleapis.com") ||
|
||||
url.includes("fonts.gstatic.com") ||
|
||||
url.includes("content-autofill.googleapis.com") ||
|
||||
url.includes("facebook.net") ||
|
||||
url.includes("doubleclick.net") ||
|
||||
url.includes("hotjar.com") ||
|
||||
url.includes("adsco.re") ||
|
||||
url.includes("displayvertising.com") ||
|
||||
url.includes("tidio.co") ||
|
||||
url.includes("api.ipify.org")
|
||||
) {
|
||||
return route.abort();
|
||||
}
|
||||
|
||||
return route.continue();
|
||||
});
|
||||
|
||||
this.startedAt = Date.now();
|
||||
this.sessionExpiry = 0; // force session establish on first acquirePage
|
||||
|
||||
|
||||
@@ -195,6 +195,9 @@ export class PartsCatalogsAuthService implements OnModuleInit, OnModuleDestroy {
|
||||
// the exact failure seen 2026-06-11 (Floxy 402) and 2026-07-03 (Floxy tunnel
|
||||
// down). Disable with PCAT_CAPTURE_ALLOW_DIRECT=false.
|
||||
private readonly captureAllowDirect: boolean;
|
||||
// Exit-IP telemetry probe (one ipify call per capture). Off by default: it
|
||||
// added ~54k billed proxy requests over 15 days for banned-IP telemetry only.
|
||||
private readonly exitIpProbe: boolean;
|
||||
|
||||
constructor(
|
||||
private configService: ConfigService,
|
||||
@@ -242,6 +245,7 @@ export class PartsCatalogsAuthService implements OnModuleInit, OnModuleDestroy {
|
||||
this.diHost = cfg.get<string>("PCAT_PROXY_HOST", DI_HOST);
|
||||
this.diUser = cfg.get<string>("PCAT_PROXY_USER", DI_DEFAULT_USER);
|
||||
this.diPass = cfg.get<string>("PCAT_PROXY_PASS", DI_DEFAULT_PASS);
|
||||
this.exitIpProbe = cfg.get<string>("PCAT_EXIT_IP_PROBE", "false") === "true";
|
||||
|
||||
this.captureAllowDirect =
|
||||
cfg.get<string>("PCAT_CAPTURE_ALLOW_DIRECT", "true") !== "false" &&
|
||||
@@ -806,7 +810,10 @@ export class PartsCatalogsAuthService implements OnModuleInit, OnModuleDestroy {
|
||||
// The session is sticky, so a parallel ipify probe exits from the SAME IP
|
||||
// the capture will use — that's what makes banned-IP tracking concrete.
|
||||
// Resolves in ~1-2s while the capture itself takes 7s+; never throws.
|
||||
const exitIpPromise = proxyUrl ? resolveExitIp(proxyUrl) : Promise.resolve(null);
|
||||
// Gated off by default (PCAT_EXIT_IP_PROBE): the probe is billed proxy
|
||||
// traffic and only feeds exit-IP telemetry, not the capture itself.
|
||||
const exitIpPromise =
|
||||
this.exitIpProbe && proxyUrl ? resolveExitIp(proxyUrl) : Promise.resolve(null);
|
||||
const logCapture = (ok: boolean, errorKind?: string): void => {
|
||||
void exitIpPromise.then((exitIp) =>
|
||||
this.proxyTelemetry.record({
|
||||
@@ -856,24 +863,37 @@ export class PartsCatalogsAuthService implements OnModuleInit, OnModuleDestroy {
|
||||
this.logger.debug(`Token intercepted (key=${apiKey.slice(0, 16)}...)`);
|
||||
});
|
||||
|
||||
// Block heavy resources to save proxy bandwidth
|
||||
// Block heavy resources to save proxy bandwidth. The capture only needs
|
||||
// the widget's own JS to run and fire its /v3/api/proxy XHR — images,
|
||||
// fonts, CSS and every third-party tag are pure billed residential waste.
|
||||
await page.route("**/*", (route) => {
|
||||
const url = route.request().url();
|
||||
const type = route.request().resourceType();
|
||||
|
||||
// Block images, fonts, media
|
||||
if (["image", "font", "media"].includes(type)) {
|
||||
// Block images, fonts, stylesheets, media — never gate the token XHR.
|
||||
if (["image", "font", "stylesheet", "media"].includes(type)) {
|
||||
return route.abort();
|
||||
}
|
||||
|
||||
// Block known trackers/analytics
|
||||
// Block third-party junk seen in proxy usage: analytics, ad networks,
|
||||
// chat widgets, Google/Yandex assets, and our own ipify egress probe.
|
||||
// NB: never add the widget's runtime CDNs (jsdelivr/unpkg) here — its
|
||||
// JS must load or no token fires; heavy assets are caught by type above.
|
||||
if (
|
||||
url.includes("google-analytics.com") ||
|
||||
url.includes("googletagmanager.com") ||
|
||||
url.includes("mc.yandex.ru") ||
|
||||
url.includes("yastatic.net") ||
|
||||
url.includes("fonts.googleapis.com") ||
|
||||
url.includes("fonts.gstatic.com") ||
|
||||
url.includes("content-autofill.googleapis.com") ||
|
||||
url.includes("facebook.net") ||
|
||||
url.includes("doubleclick.net") ||
|
||||
url.includes("hotjar.com")
|
||||
url.includes("hotjar.com") ||
|
||||
url.includes("adsco.re") ||
|
||||
url.includes("displayvertising.com") ||
|
||||
url.includes("tidio.co") ||
|
||||
url.includes("api.ipify.org")
|
||||
) {
|
||||
return route.abort();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user