Fix final-review findings for Playwright fallback
- server.js: catch browser-launch failures inside the injected fetchAndExtractPriceViaBrowser wrapper so they route through checkProduct's normal error classification (recordCheckFailure) instead of escaping as a thrown error that bypasses the DB write. - server.js: add an in-flight guard around runCheck so overlapping callers (cron + check-now) share one run instead of each launching its own Chromium. - Dockerfile: copy only package.json/package-lock.json/node_modules before installing Chromium so the expensive install layer is keyed to dependency changes, not every commit; copy the rest of the app afterward. - Dockerfile: clean up apt package lists after the Playwright install step, matching the build stage's existing cleanup. - README.md: note that exercising the fallback locally (not just running the test suite) needs `npx playwright install chromium`. - README.md: correct the fallback description — domcontentloaded navigation reads the page before most client-side JS finishes, so the fallback mainly helps with HTTP-client blocking, not JS-rendered prices; reworded to drop the inaccurate claim. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
This commit is contained in:
+4
-1
@@ -8,8 +8,11 @@ COPY . .
|
|||||||
|
|
||||||
FROM node:22-slim
|
FROM node:22-slim
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
COPY --from=build /app/package.json /app/package-lock.json ./
|
||||||
|
COPY --from=build /app/node_modules ./node_modules
|
||||||
|
RUN npx --yes playwright install --with-deps chromium \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
COPY --from=build /app .
|
COPY --from=build /app .
|
||||||
RUN npx --yes playwright install --with-deps chromium
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
# De SQLite-database hoort op het persistente volume te staan; zonder deze
|
# De SQLite-database hoort op het persistente volume te staan; zonder deze
|
||||||
# default zou een vergeten DB_PATH stilletjes naar de container-fs schrijven
|
# default zou een vergeten DB_PATH stilletjes naar de container-fs schrijven
|
||||||
|
|||||||
@@ -19,6 +19,11 @@ app bij het opstarten met een melding.
|
|||||||
De UI staat achter basic auth (`BASIC_AUTH_USER` / `BASIC_AUTH_PASS`) op
|
De UI staat achter basic auth (`BASIC_AUTH_USER` / `BASIC_AUTH_PASS`) op
|
||||||
`http://localhost:3000`.
|
`http://localhost:3000`.
|
||||||
|
|
||||||
|
Wil je de headless-browser-fallback ook echt lokaal uitproberen (de
|
||||||
|
testsuite gebruikt fakes en heeft hier geen echte browser voor nodig), run
|
||||||
|
dan eerst `npx playwright install chromium` om de Chromium-binary te
|
||||||
|
downloaden.
|
||||||
|
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
| Variabele | Omschrijving |
|
| Variabele | Omschrijving |
|
||||||
@@ -43,6 +48,6 @@ controleren"-knop in de UI kan dit ook handmatig getriggerd worden.
|
|||||||
Als het ophalen van de prijs op de gewone manier mislukt (netwerkfout, of
|
Als het ophalen van de prijs op de gewone manier mislukt (netwerkfout, of
|
||||||
geen prijs gevonden in de HTML), probeert de app het één keer opnieuw met
|
geen prijs gevonden in de HTML), probeert de app het één keer opnieuw met
|
||||||
een echte headless-browser (Playwright/Chromium) — nuttig voor pagina's
|
een echte headless-browser (Playwright/Chromium) — nuttig voor pagina's
|
||||||
die de prijs pas met JavaScript renderen, of die eenvoudige HTTP-clients
|
die eenvoudige HTTP-clients blokkeren. Geen garantie tegen elke vorm van
|
||||||
blokkeren. Geen garantie tegen elke vorm van bot-detectie, maar dekt een
|
bot-detectie, maar dekt een reëel deel van de gevallen die de gewone
|
||||||
reëel deel van de gevallen die de gewone aanpak mist.
|
aanpak mist.
|
||||||
|
|||||||
@@ -37,7 +37,18 @@ function summarize(results) {
|
|||||||
return parts.length > 0 ? parts.join(' ') : 'geen producten';
|
return parts.length > 0 ? parts.join(' ') : 'geen producten';
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runCheck() {
|
let inFlightCheck = null;
|
||||||
|
function runCheck() {
|
||||||
|
if (inFlightCheck) {
|
||||||
|
return inFlightCheck;
|
||||||
|
}
|
||||||
|
inFlightCheck = runCheckOnce().finally(() => {
|
||||||
|
inFlightCheck = null;
|
||||||
|
});
|
||||||
|
return inFlightCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runCheckOnce() {
|
||||||
const products = listProducts(db);
|
const products = listProducts(db);
|
||||||
let browserPromise = null;
|
let browserPromise = null;
|
||||||
function getBrowser() {
|
function getBrowser() {
|
||||||
@@ -50,8 +61,12 @@ async function runCheck() {
|
|||||||
const deps = {
|
const deps = {
|
||||||
fetchAndExtractPrice,
|
fetchAndExtractPrice,
|
||||||
fetchAndExtractPriceViaBrowser: async (url) => {
|
fetchAndExtractPriceViaBrowser: async (url) => {
|
||||||
const browser = await getBrowser();
|
try {
|
||||||
return fetchAndExtractPriceViaBrowser(browser, url);
|
const browser = await getBrowser();
|
||||||
|
return await fetchAndExtractPriceViaBrowser(browser, url);
|
||||||
|
} catch (err) {
|
||||||
|
return { price: null, method: null, error: err };
|
||||||
|
}
|
||||||
},
|
},
|
||||||
sendMail: (payload) => sendPriceChangeEmail(transport, {
|
sendMail: (payload) => sendPriceChangeEmail(transport, {
|
||||||
from: process.env.SMTP_FROM,
|
from: process.env.SMTP_FROM,
|
||||||
|
|||||||
Reference in New Issue
Block a user