dev1/backend/utils/smsService.js
Josh 5536bb3bc9
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful
fixed reminder encryption
2025-09-18 16:08:34 +00:00

92 lines
3.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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
// cronjob doesnt need its own UPDATE logic.
import twilio from 'twilio';
import { decrypt } from '../shared/crypto/encryption.js';
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 {
// decrypt-at-send (DB stores encrypted)
const toPlain = typeof to === 'string' && to.startsWith('gcm:') ? decrypt(to) : to;
const bodyPlain = typeof body === 'string' && body.startsWith('gcm:') ? decrypt(body) : body;
// normalize to E.164
const toE164 = (() => {
const s = String(toPlain || '').trim();
if (!s) return s;
if (s.startsWith('+')) return s.replace(/[^\d+]/g, '');
return '+' + s.replace(/\D/g, '');
})();
const msg = await client.messages.create({
to: toE164,
body: bodyPlain,
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 dont keep retrying blindly
if (reminderId) {
await db.execute(
`UPDATE reminders
SET status = 'failed',
sent_at = UTC_TIMESTAMP(),
error_code = ?,
error_message = ?
WHERE id = ?`,
[err.code || null, err.message || 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;
}