#!/usr/bin/env node /** * TENDER EMAIL ALERTS * * Sends email notifications for: * 1. High-value tenders (>£100k) * 2. New tenders matching keywords * 3. Daily digest of all new tenders */ import pg from 'pg'; import nodemailer from 'nodemailer'; import dotenv from 'dotenv'; dotenv.config(); const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL || 'postgresql://tenderpilot:tenderpilot123@localhost:5432/tenderpilot' }); // Email configuration const transporter = nodemailer.createTransporter({ host: process.env.SMTP_HOST || 'smtp.dynu.com', port: parseInt(process.env.SMTP_PORT || '587'), secure: false, // STARTTLS auth: { user: process.env.SMTP_USER || 'peter.foster@ukdataservices.co.uk', pass: process.env.SMTP_PASS } }); const ALERT_EMAIL = process.env.ALERT_EMAIL || 'peter.foster@ukdataservices.co.uk'; async function sendAlerts(mode = 'digest') { try { console.log(`[${new Date().toISOString()}] Starting tender alerts (mode: ${mode})...`); let tenders; let subject; let intro; if (mode === 'high-value') { // High-value tenders (>£100k or equivalent) tenders = await pool.query( `SELECT * FROM tenders WHERE status = 'open' AND (value_low > 100000 OR value_high > 100000) AND created_at > NOW() - INTERVAL '24 hours' ORDER BY value_high DESC NULLS LAST, created_at DESC LIMIT 50` ); subject = `🔔 TenderRadar: ${tenders.rows.length} High-Value Tenders (>£100k)`; intro = 'High-value tender opportunities identified in the last 24 hours:'; } else if (mode === 'digest') { // Daily digest - all new tenders tenders = await pool.query( `SELECT * FROM tenders WHERE status = 'open' AND created_at > NOW() - INTERVAL '24 hours' ORDER BY created_at DESC LIMIT 100` ); subject = `📊 TenderRadar Daily Digest: ${tenders.rows.length} New Tenders`; intro = 'New tender opportunities from the last 24 hours:'; } else { throw new Error(`Unknown mode: ${mode}`); } if (tenders.rows.length === 0) { console.log(`No tenders found for mode: ${mode}`); return; } // Build HTML email let html = `
${intro}
${tender.description.substring(0, 300)}${tender.description.length > 300 ? '...' : ''}
` : ''} View Tender →