Merge pull request 'fix(pcat-auth): two-armed post-goto poll — fail-fast on dead sites' (#75) from dev into main

Reviewed-on: #75
This commit was merged in pull request #75.
This commit is contained in:
2026-06-01 19:18:06 +00:00

View File

@@ -32,7 +32,14 @@ const REDIS_SAFETY_BUFFER_S = 60; // don't serve from Redis if <60s of life left
const TOKEN_TTL = 600; // seconds — TWS- has no built-in expiry, refresh aggressively
const REFRESH_BUFFER = 90; // Refresh 90s before expiry
const CAPTURE_POLL_INTERVAL = 500; // ms
const CAPTURE_POLL_MAX = 40; // 40 × 500ms = 20s max wait
// Two-armed post-goto poll: a healthy goto means the widget JS already loaded
// and the API call typically fires within ~1-2s — short cap is enough. A
// failed goto means we lost the page, but the widget request may still have
// gone through before the navigation timeout — give a brief grace window
// and bail. Old single-armed 20s cap was the dominant cost on failed-site
// attempts (e.g. 10s page timeout + 20s blind poll = 30s wasted).
const CAPTURE_POLL_AFTER_OK = 10; // 10 × 500ms = 5s after a healthy goto
const CAPTURE_POLL_AFTER_FAIL = 4; // 4 × 500ms = 2s grace after goto failure
const PAGE_TIMEOUT = 10_000; // 10s — healthy partner sites load in <5s through
// the proxy; a longer wait only prolongs dead-port connects in capture retries.
const CONTEXT_CLOSE_TIMEOUT = 5_000;
@@ -634,18 +641,23 @@ export class PartsCatalogsAuthService implements OnModuleInit, OnModuleDestroy {
});
// Navigate — networkidle waits for widget JS to load + make API calls
let gotoOk = true;
try {
await page.goto(siteUrl, {
timeout: PAGE_TIMEOUT,
waitUntil: "networkidle",
});
} catch (navErr) {
// Navigation may timeout but JWT could still be captured
// Navigation may timeout but JWT could still be captured if the
// widget request fired before goto gave up — see two-armed poll
// constants above.
gotoOk = false;
this.logger.debug(`Navigation ended: ${(navErr as Error).message?.slice(0, 80)}`);
}
// Poll for token
for (let i = 0; i < CAPTURE_POLL_MAX; i++) {
// Poll for token — short cap after healthy goto, brief grace after fail
const pollMax = gotoOk ? CAPTURE_POLL_AFTER_OK : CAPTURE_POLL_AFTER_FAIL;
for (let i = 0; i < pollMax; i++) {
if (capturedToken) break;
await new Promise((r) => setTimeout(r, CAPTURE_POLL_INTERVAL));
}