Commit Graph

24 Commits

Author SHA1 Message Date
Sjoerd de Vries 966a80c690 fix: don't let a failed browser launch override a successful runCheck result
Guard the finally block's re-await of browserPromise so a rejected
launch (e.g. Playwright can't start Chromium) doesn't rethrow out of
finally and override the try block's already-successful return value.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 17:44:29 +02:00
Sjoerd de Vries fc8dadc8a3 feat: launch a lazy per-run browser for the fallback scraper
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 17:39:03 +02:00
Sjoerd de Vries 358347e8ba feat: fall back to the browser scraper when the primary fetch fails
- Extract fetchAndExtractPriceViaBrowser from deps (optional)
- After primary fetch, if it errors or returns no price and fallback exists, try fallback
- Fallback result replaces primary result for further processing
- Retry-safety logic unchanged: last_price/last_checked_at only updated on success
- All 4 new tests pass, all existing tests unchanged

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 17:34:02 +02:00
Sjoerd de Vries df3d3250e5 feat: add Playwright-based fallback scraper
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 17:30:18 +02:00
Sjoerd de Vries dac7893cd2 Add implementation plan for Playwright fallback
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 17:24:06 +02:00
Sjoerd de Vries 957555bdbe Refine spec: lazy browser launch for Playwright fallback
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 17:21:30 +02:00
Sjoerd de Vries 4fee15173b chore: ignore local .claude harness state directory
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 17:18:29 +02:00
Sjoerd de Vries 1d3d1caeb1 Add design spec for Playwright fallback scraping
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 17:18:23 +02:00
Sjoerd de Vries 7f378115b5 fix: guard the async /check-now handler against crashing (finding 8)
Express 4 geeft een afgewezen promise uit een async handler niet door aan
de error-handling; met Node 22 crashte daardoor het hele proces. De
handler vangt de fout nu zelf af, logt hem en antwoordt met een 500.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 15:53:20 +02:00
Sjoerd de Vries d07cda48c6 chore: default DB_PATH to the mounted volume in Docker (finding 5)
Zonder ENV DB_PATH schreef een vergeten env var stilletjes naar de
container-filesystem, waardoor alle data bij de volgende redeploy weg
was. VOLUME /data documenteert het verwachte mountpoint.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 15:53:08 +02:00
Sjoerd de Vries fb3fc62b4b feat: log failures, validate env at startup, load .env (findings 3, 4, 7)
Finding 3: fetch_error, not_found, mail_error and de onverwachte-fout
branch loggen nu naam/url plus reden naar stderr; runCheck logt na elke
run een regel met de tellingen per status, zodat een cron-run zichtbaar
is in de container-logs. De recordCheckSuccess/recordCheckFailure-condities
en alle return-waarden zijn ongewijzigd.

Finding 4: server.js controleert bij het opstarten dat BASIC_AUTH_USER,
BASIC_AUTH_PASS, SMTP_FROM en NOTIFY_EMAIL gezet zijn en stopt anders met
exit code 1 in plaats van later opake 500's te geven.

Finding 7: dotenv toegevoegd en als eerste regel in server.js geladen,
zodat het gedocumenteerde `cp .env.example .env && npm start` echt werkt.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 15:52:57 +02:00
Sjoerd de Vries 52c9947267 fix: harden price scraping (findings 1, 2, 6)
Finding 1: parsePriceValue mishandled thousands separators — "1.299,00"
and "1,299.00" both parsed to 1.3. Both the meta-tag path and the
text-regex path now funnel through a single normalizeAmount() helper
that picks the last separator as the decimal one, and the text regex
also matches thousands-grouped amounts.

Finding 2: the text fallback read <script>/<style> contents via
$('body').text(); those elements are now stripped from a clone first.

Finding 6: the default fetch now uses a 15s AbortSignal.timeout and a
browser-like User-Agent, so one unresponsive host cannot stall the
whole sequential run. The fetchImpl injection point is unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 15:49:56 +02:00
Sjoerd de Vries 862699d35b docs: add README 2026-09-05 15:40:08 +02:00
Sjoerd de Vries 195dff6fe3 chore: add Dockerfile for deployment 2026-09-05 15:28:31 +02:00
Sjoerd de Vries 5b6d672506 feat: wire modules together into the running server
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 15:21:15 +02:00
Sjoerd de Vries f73ea41a6c feat: add web UI for managing products
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 15:17:42 +02:00
Sjoerd de Vries 3b0e824c5b feat: add daily cron scheduler for price checks
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 15:15:09 +02:00
Sjoerd de Vries 68cb8d0c08 feat: add price-check orchestration with retry-safe error handling
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 15:12:16 +02:00
Sjoerd de Vries e1f8576b95 feat: add SMTP mailer for price-change notifications
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-05 15:09:26 +02:00
Sjoerd de Vries 5e7d247a16 feat: add price scraper with JSON-LD/meta/regex fallback
- Implement extractPrice() to extract price from HTML with three fallback methods:
  1. JSON-LD Product schema (json-ld)
  2. og:price:amount meta tag (meta)
  3. Euro symbol regex pattern (regex)
- Implement fetchAndExtractPrice() for fetching URLs and extracting prices
- Includes helper functions for parsing and normalizing price values
- Add comprehensive test suite with fixtures for each extraction method
- All tests passing (14/14)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 15:06:27 +02:00
Sjoerd de Vries 492833dda4 feat: add SQLite-backed products store
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 15:03:16 +02:00
Sjoerd de Vries a5073e6e74 chore: project scaffolding
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 14:59:04 +02:00
Sjoerd de Vries 5edb1f04f6 Add implementation plan for korting prijstracker
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 14:35:57 +02:00
Sjoerd de Vries 1bfe4fd794 Add design spec for korting prijstracker
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013gwc3aCiyQxmCWQke8RvEz
2026-09-05 14:30:28 +02:00