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:
Sjoerd de Vries
2026-09-05 18:06:07 +02:00
parent 7a117e7a46
commit 74f487486f
3 changed files with 30 additions and 7 deletions
+4 -1
View File
@@ -8,8 +8,11 @@ COPY . .
FROM node:22-slim
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 .
RUN npx --yes playwright install --with-deps chromium
ENV NODE_ENV=production
# De SQLite-database hoort op het persistente volume te staan; zonder deze
# default zou een vergeten DB_PATH stilletjes naar de container-fs schrijven
+8 -3
View File
@@ -19,6 +19,11 @@ app bij het opstarten met een melding.
De UI staat achter basic auth (`BASIC_AUTH_USER` / `BASIC_AUTH_PASS`) op
`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
| 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
geen prijs gevonden in de HTML), probeert de app het één keer opnieuw met
een echte headless-browser (Playwright/Chromium) — nuttig voor pagina's
die de prijs pas met JavaScript renderen, of die eenvoudige HTTP-clients
blokkeren. Geen garantie tegen elke vorm van bot-detectie, maar dekt een
reëel deel van de gevallen die de gewone aanpak mist.
die eenvoudige HTTP-clients blokkeren. Geen garantie tegen elke vorm van
bot-detectie, maar dekt een reëel deel van de gevallen die de gewone
aanpak mist.
+18 -3
View File
@@ -37,7 +37,18 @@ function summarize(results) {
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);
let browserPromise = null;
function getBrowser() {
@@ -50,8 +61,12 @@ async function runCheck() {
const deps = {
fetchAndExtractPrice,
fetchAndExtractPriceViaBrowser: async (url) => {
const browser = await getBrowser();
return fetchAndExtractPriceViaBrowser(browser, url);
try {
const browser = await getBrowser();
return await fetchAndExtractPriceViaBrowser(browser, url);
} catch (err) {
return { price: null, method: null, error: err };
}
},
sendMail: (payload) => sendPriceChangeEmail(transport, {
from: process.env.SMTP_FROM,