// backend/utils/smsService.js // Centralised Twilio helper + DB helpers for the Reminders feature. // Now *also* writes back status → reminders.status and sent_at so the // cron‑job doesn’t need its own UPDATE logic. import twilio from 'twilio'; import { v4 as uuid } from 'uuid'; import db from '../config/mysqlPool.js'; const { TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, TWILIO_MESSAGING_SERVICE_SID } = process.env; if (!TWILIO_ACCOUNT_SID || !TWILIO_AUTH_TOKEN || !TWILIO_MESSAGING_SERVICE_SID) { throw new Error('Twilio env vars missing; check env or PM2 config'); } const client = twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN); /* ────────────────────────────────────────────────────────────── * Immediate send + status update ------------------------------------------------------------------ */ export async function sendSMS ({ reminderId = null, to, body }) { try { const msg = await client.messages.create({ to, body, messagingServiceSid: TWILIO_MESSAGING_SERVICE_SID }); // Mark success if we were called from reminderCron if (reminderId) { await db.execute( `UPDATE reminders SET status = 'sent', sent_at = UTC_TIMESTAMP(), twilio_sid = ? WHERE id = ?`, [msg.sid, reminderId] ); } return msg; } catch (err) { // Persist failure so we don’t keep retrying blindly if (reminderId) { await db.execute( `UPDATE reminders SET status = 'failed', sent_at = UTC_TIMESTAMP(), error_code = ? WHERE id = ?`, [err.code || null, reminderId] ); } throw err; // propagate so cron can log } } /* ────────────────────────────────────────────────────────────── * Persist a *future* reminder row ------------------------------------------------------------------ */ export async function createReminder ({ userId, phone, body, sendAtUtc }) { const id = uuid(); const mysqlDateTime = new Date(sendAtUtc) .toISOString() .slice(0, 19) // 2025-06-17T22:00:00 .replace('T', ' '); // 2025-06-17 22:00:00 await db.execute( `INSERT INTO reminders (id, user_id, phone_e164, message_body, send_at_utc) VALUES (?,?,?,?,?)`, [id, userId, phone, body.slice(0, 320), mysqlDateTime] ); return id; }