import logging from '../lib/logging.js'; import { company } from '../emails/config.js'; import { send_email, load_templates, debug_templates } from '../lib/email.js'; import { User, Payment, Product, RESET_CODE_SIZE } from "../lib/models.js"; import assert from "assert"; import differenceInHours from 'date-fns/differenceInHours/index.js'; const RESET_HOURS_MAX = 10; const log = logging.create("/queues/mail.js"); const generic_emailer = async (name, user, mail_form, t_vars, send_vars) => { try { const text = mail_form.txt(t_vars); const html = mail_form.html(t_vars); await debug_templates({text, html}, name); send_vars.text = text; send_vars.html = html; // send both txt and html as attachments send_email(send_vars); } catch(err) { log.error(err); } } const list_unsub = (company, user) => { return { unsubscribe: { url: `${company.unsubscribe}${user.unsubkey}`, comment: "Unsubscribe" } } } const reset_form = await load_templates("reset_email"); export const reset = async (job) => { log.debug(["Received reset with", job.data]); try { assert(job.data.user, "job.data.user is required"); const email = job.data.user.email; const user_id = job.data.user.id; const browser = job.data.browser || "None"; const ip_address = job.data.ip_address || "None"; assert(email, "Email is required"); assert(user_id !== undefined, "User user_id is undefined."); const user = await User.first({id: user_id}); assert(user.email === email, `Email ${email} doesn't match user ${user.id}'s email: ${user.email}`); if(user.reset_sent_at && differenceInHours(Date.now(), user.reset_sent_at) < RESET_HOURS_MAX) { // prevent repeated high speed reset attempts by using the advanced technolog of...time log.warn(`User ${user.id} reset request time remaining not long enough, ignoring their request.`); // update the reset count so they can only attempt this a certain number of times await User.update({id: user.id}, { reset_count: user.reset_count + 1 }); } else { // reset is good, all variables are good, so process the request user.reset_code = User.random_hex(RESET_CODE_SIZE); await User.update({id: user.id}, {reset_code: user.reset_code, reset_sent_at: Date.now()}); const reset_data = { company, user, browser, ip_address, reset_code: user.reset_code }; const text = reset_form.txt(reset_data); const html = reset_form.html(reset_data); await debug_templates({text, html}, "reset_email"); // send both txt and html as attachments send_email({ from: company.mail, to: user.email, list: list_unsub(company, user), subject: `Reset password request for ${company.website}`, text, html }); } } catch(err) { log.error(err); } } const reset_finished_form = await load_templates("reset_finished"); export const reset_finished = async (job) => { try { const user = job.data.user; const browser = job.data.browser || "None"; const ip_address = job.data.ip_address || "None"; // update the user's password now and then send the email await User.update({id: user.id}, { password: user.password, reset_code: null, reset_sent_at: null, reset_count: 0 }); const reset_finished_data = { company, user, browser, ip_address }; const text = reset_finished_form.txt(reset_finished_data); const html = reset_finished_form.html(reset_finished_data); await debug_templates({text, html}, "reset_finished"); // send both txt and html as attachments send_email({ from: company.mail, to: user.email, list: list_unsub(company, user), subject: `Reset password completed for ${company.website}`, text, html }); } catch(err) { log.error(err); } } const receipt_form = await load_templates("receipt"); export const receipt = async (job) => { const {user, product_id, payment_id} = job.data; const payment = await Payment.first({id: payment_id}); assert(payment, `Failed to find payment ID ${payment_id}`); const product = await Product.first({id: product_id}); assert(product, `Failed to find course ID ${product_id}`); generic_emailer("receipt", user, receipt_form, { company, course, payment, user }, { from: company.mail, to: user.email, list: list_unsub(company, user), subject: `Your ${company.product} Receipt` } ); } const register_form = await load_templates("register_email"); export const registration = async (job) => { const user = job.data.user; generic_emailer("register_email", user, register_form, { company, user }, { from: company.mail, to: user.email, list: list_unsub(company, user), subject: `Registration for ${company.name}` }); } const welcome_form = await load_templates("welcome_email"); export const welcome = async (job) => { const user = job.data.user; generic_emailer("welcome_email", user, welcome_form, { company, user }, { from: company.mail, to: user.email, list: list_unsub(company, user), subject: `Welcome to ${company.name}` }); }