// Leest een .env-bestand in voor lokale ontwikkeling; in productie komen de // env vars van Coolify. quiet houdt de opstartlogging schoon. require('dotenv').config({ quiet: true }); const path = require('node:path'); const { openDb, listProducts, addProduct, deleteProduct, recordCheckSuccess, recordCheckFailure, } = require('./src/db'); const { fetchAndExtractPrice } = require('./src/scraper'); const { launchBrowser, fetchAndExtractPriceViaBrowser } = require('./src/browserScraper'); const { createTransport, sendPriceChangeEmail } = require('./src/mailer'); const { checkAllProducts } = require('./src/checkProducts'); const { start: startScheduler } = require('./src/scheduler'); const { createApp } = require('./src/app'); const REQUIRED_ENV = ['BASIC_AUTH_USER', 'BASIC_AUTH_PASS', 'SMTP_FROM', 'NOTIFY_EMAIL']; const missing = REQUIRED_ENV.filter((name) => !process.env[name]); if (missing.length > 0) { console.error(`Ontbrekende verplichte environment variables: ${missing.join(', ')}`); console.error('Zet ze (bijvoorbeeld in .env, zie .env.example) en start opnieuw.'); process.exit(1); } const PORT = process.env.PORT || 3000; const DB_PATH = process.env.DB_PATH || path.join(__dirname, 'data', 'korting.db'); const db = openDb(DB_PATH); const transport = createTransport(process.env); function summarize(results) { const counts = {}; for (const result of results) { counts[result.status] = (counts[result.status] || 0) + 1; } const parts = Object.entries(counts).map(([status, count]) => `${status}=${count}`); return parts.length > 0 ? parts.join(' ') : 'geen producten'; } async function runCheck() { const products = listProducts(db); let browserPromise = null; function getBrowser() { if (!browserPromise) { browserPromise = launchBrowser(); } return browserPromise; } try { const deps = { fetchAndExtractPrice, fetchAndExtractPriceViaBrowser: async (url) => { const browser = await getBrowser(); return fetchAndExtractPriceViaBrowser(browser, url); }, sendMail: (payload) => sendPriceChangeEmail(transport, { from: process.env.SMTP_FROM, to: process.env.NOTIFY_EMAIL, ...payload, }), recordCheckFailure: (id, status) => recordCheckFailure(db, id, status), recordCheckSuccess: (id, data) => recordCheckSuccess(db, id, data), now: () => new Date().toISOString(), }; const results = await checkAllProducts(products, deps); console.log(`[check] ${products.length} product(en) gecontroleerd: ${summarize(results)}`); return results; } finally { if (browserPromise) { const browser = await browserPromise; await browser.close().catch((err) => { console.error(`[check] kon de browser niet netjes sluiten: ${err && err.message}`); }); } } } startScheduler(runCheck); const app = createApp({ listProducts: () => listProducts(db), addProduct: (data) => addProduct(db, data), deleteProduct: (id) => deleteProduct(db, id), checkAllProducts: runCheck, authUser: process.env.BASIC_AUTH_USER, authPass: process.env.BASIC_AUTH_PASS, }); app.listen(PORT, () => { console.log(`Korting draait op poort ${PORT}`); });