You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
169 lines
4.1 KiB
169 lines
4.1 KiB
2 years ago
|
import nodemailer from "nodemailer";
|
||
|
import fs from 'fs/promises';
|
||
|
import _ from 'lodash';
|
||
|
import logging from "./logging.js";
|
||
|
import { promises } from "dns";
|
||
|
|
||
|
const log = logging.create("lib/email.js");
|
||
|
|
||
|
// TODO: move this out to an easy to access/change location
|
||
|
let configuration = {};
|
||
|
|
||
|
const configure_transporter = async () => {
|
||
|
if(process.env.DANGER_ADMIN || process.env.DEBUG) {
|
||
|
configuration = {
|
||
|
streamTransport: true,
|
||
|
debug: true,
|
||
|
logger: log,
|
||
|
newline: 'windows'
|
||
|
}
|
||
|
} else {
|
||
|
configuration = JSON.parse(await fs.readFile("./secrets/email.json"));
|
||
|
console.log("EMAIL CONFIG", configuration);
|
||
|
}
|
||
|
|
||
|
return nodemailer.createTransport(configuration);
|
||
|
}
|
||
|
|
||
|
// TODO: trash hacks to get the config out for the mail testing tool
|
||
|
export const get_config = () => configuration;
|
||
|
|
||
|
export const transporter = await configure_transporter();
|
||
|
|
||
|
export const load_templates = async (name) => {
|
||
|
const html = await fs.readFile(`emails/${name}.html`);
|
||
|
const txt = await fs.readFile(`emails/${name}.txt`);
|
||
|
|
||
|
return {
|
||
|
html: _.template(html),
|
||
|
txt: _.template(txt)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export const debug_templates = async (templates, name) => {
|
||
|
if(process.env.DEBUG) {
|
||
|
log.debug(`Writing debug email for ${name} to debug/emails/${name}.(html|txt)`);
|
||
|
await fs.mkdir("debug/emails", { recursive: true});
|
||
|
log.debug(`Emails written to debug/emails/${name}.(html|txt)`);
|
||
|
await fs.writeFile(`debug/emails/${name}.html`, templates.html);
|
||
|
await fs.writeFile(`debug/emails/${name}.txt`, templates.text);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* This never rejects the promise it makes but instead returns
|
||
|
* any errors it receives or undefined if none.
|
||
|
*/
|
||
|
export const send_email = async (data) => {
|
||
|
const result = new Promise((resolve, reject) => {
|
||
|
transporter.sendMail(data, (err, info) => {
|
||
|
try {
|
||
|
if(err) {
|
||
|
log.error(err);
|
||
|
resolve(err);
|
||
|
} else {
|
||
|
if(process.env.DEBUG) {
|
||
|
log.debug(info.envelope);
|
||
|
log.debug(info.messageId);
|
||
|
// I only do this when I'm lazy
|
||
|
// info.message.pipe(process.stdout);
|
||
|
}
|
||
|
|
||
|
resolve(undefined);
|
||
|
}
|
||
|
} catch(error) {
|
||
|
resolve(error);
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
const add_reverse_error = (result, error) => {
|
||
|
if(result.reverse_errors === undefined) {
|
||
|
result.reverse_errors = [];
|
||
|
}
|
||
|
|
||
|
let res_error = { error: {...error} };
|
||
|
|
||
|
res_error.message = error.message;
|
||
|
|
||
|
result.reverse_errors.push(res_error);
|
||
|
}
|
||
|
|
||
|
export const dns_check = async (hostname) => {
|
||
|
let result = {
|
||
|
ip4: {},
|
||
|
ip6: {}
|
||
|
};
|
||
|
|
||
|
const res = new promises.Resolver();
|
||
|
res.setServers(['8.8.8.8']);
|
||
|
|
||
|
try {
|
||
|
result.ip4.host = await res.resolve4(hostname);
|
||
|
} catch(error) {
|
||
|
// the errors in dns are stupid. You only get .message if you call it.
|
||
|
result.ip4.error = {...error};
|
||
|
result.ip4.error.message = error.message;
|
||
|
}
|
||
|
|
||
|
// no point doing reverse DNS if no IP4 address
|
||
|
if(result.ip4.host) {
|
||
|
result.ip4.reverse = [];
|
||
|
|
||
|
for(let ip4 of result.ip4.host) {
|
||
|
try {
|
||
|
let host = await res.reverse(ip4);
|
||
|
result.ip4.reverse.push(host);
|
||
|
} catch(error) {
|
||
|
add_reverse_error(res.ip4, error);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
result.ip6.host = await res.resolve6(hostname);
|
||
|
} catch(error) {
|
||
|
result.ip6.error = {...error};
|
||
|
result.ip6.error.message = error.message;
|
||
|
}
|
||
|
|
||
|
// no point in doing reverse DNS if no address
|
||
|
if(result.ip6.host) {
|
||
|
result.ip6.reverse = [];
|
||
|
|
||
|
for(let ip6 of result.ip6.host) {
|
||
|
try {
|
||
|
let host = await res.reverse(ip6);
|
||
|
result.ip6.reverse.push(host);
|
||
|
} catch(error) {
|
||
|
add_reverse_error(result.ip6, error);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
result.mx = await res.resolveMx(hostname);
|
||
|
} catch(error) {
|
||
|
result.mx_error = {...error};
|
||
|
result.mx_error.message = error.message;
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
result.spf = await res.resolveTxt(hostname);
|
||
|
} catch(error) {
|
||
|
result.spf_error = {...error};
|
||
|
result.spf_error.message = error.message;
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
result.dmarc = await res.resolveTxt(`_dmarc.${hostname}`);
|
||
|
} catch(error) {
|
||
|
result.dmarc_error = {...error};
|
||
|
result.dmarc_error.message = error.message;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|