fixed reminder encryption
All checks were successful
ci/woodpecker/manual/woodpecker Pipeline was successful

This commit is contained in:
Josh 2025-09-18 16:08:34 +00:00
parent 46b66df823
commit 5536bb3bc9
2 changed files with 25 additions and 7 deletions

View File

@ -3439,7 +3439,7 @@ app.post('/api/premium/financial-profile', authenticatePremiumUser, async (req,
if (!req.body || Object.keys(req.body).length === 0) { if (!req.body || Object.keys(req.body).length === 0) {
return res.json({ message: 'No changes' }); return res.json({ message: 'No changes' });
} }
// ---- Normalize split: numbers, clamp, complement, 50/50 fallback ---- // ---- Normalize split: numbers, clamp, complement, 50/50 fallback ----
function normalizeSplit(eIn, rIn) { function normalizeSplit(eIn, rIn) {
let e = Number(eIn), r = Number(rIn); let e = Number(eIn), r = Number(rIn);
@ -4075,11 +4075,17 @@ app.post('/api/premium/tasks', authenticatePremiumUser, async (req, res) => {
[req.id] [req.id]
); );
if (profile?.sms_reminders_opt_in && profile.phone_verified_at && profile.phone_e164) { if (profile?.sms_reminders_opt_in && profile.phone_verified_at && profile.phone_e164) {
await createReminder({ // If due_date is just YYYY-MM-DD, schedule at 14:00:00Z that day
const isoSend =
/^\d{4}-\d{2}-\d{2}$/.test(String(finalDue))
? `${finalDue}T14:00:00.000Z`
: new Date(finalDue).toISOString();
await createReminder({
userId : req.id, userId : req.id,
phone : profile.phone_e164, phone : profile.phone_e164,
body : `🔔 AptivaAI: “${title}” is due ${due_date.slice(0,10)}`, body : `🔔 AptivaAI: “${title}” is due ${due_date.slice(0,10)}`,
sendAtUtc: new Date(due_date).toISOString() // UTC ISO sendAtUtc: isoSend // UTC ISO
}); });
console.log('[reminder] queued for task', title); console.log('[reminder] queued for task', title);
} }

View File

@ -4,6 +4,7 @@
// cronjob doesnt need its own UPDATE logic. // cronjob doesnt need its own UPDATE logic.
import twilio from 'twilio'; import twilio from 'twilio';
import { decrypt } from '../shared/crypto/encryption.js';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import db from '../config/mysqlPool.js'; import db from '../config/mysqlPool.js';
@ -24,9 +25,19 @@ const client = twilio(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN);
------------------------------------------------------------------ */ ------------------------------------------------------------------ */
export async function sendSMS ({ reminderId = null, to, body }) { export async function sendSMS ({ reminderId = null, to, body }) {
try { 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({ const msg = await client.messages.create({
to, to: toE164,
body, body: bodyPlain,
messagingServiceSid: TWILIO_MESSAGING_SERVICE_SID messagingServiceSid: TWILIO_MESSAGING_SERVICE_SID
}); });
@ -50,9 +61,10 @@ export async function sendSMS ({ reminderId = null, to, body }) {
`UPDATE reminders `UPDATE reminders
SET status = 'failed', SET status = 'failed',
sent_at = UTC_TIMESTAMP(), sent_at = UTC_TIMESTAMP(),
error_code = ? error_code = ?,
error_message = ?
WHERE id = ?`, WHERE id = ?`,
[err.code || null, reminderId] [err.code || null, err.message || null, reminderId]
); );
} }
throw err; // propagate so cron can log throw err; // propagate so cron can log