This is the template project that's checked out and configured when you run the bando-up command from ljsthw-bandolier. This is where the code really lives.
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.
bandolier-template/api/admin/email.js

172 lines
4.8 KiB

import logging from '../../lib/logging.js';
import { developer_admin, API } from '../../lib/api.js';
import { company } from '../../emails/config.js';
import { get_config, dns_check, send_email, load_templates } from '../../lib/email.js';
const log = logging.create(import.meta.url);
const ERRORS = {
ip4_double_reverse: {
id: 0,
title: "No IP4 Double Reverse",
active: false,
text: "IPv4 addresses need to have both a reverse DNS record and a matching DNS record for the host. You have to add a reverse DNS for the IP Host address and then make sure that exact IPv4 address is listed in your DNS with a A record."
},
ip6_double_reverse: {
id: 1,
title: "No IP6 Double Reverse",
active: false,
text: "IPv6 addresses need to have both a reverse DNS record and a matching DNS record for the host. You have to add a reverse DNS for the IP Host address and then make sure that exact IPv6 address is listed in your DNS with a AAAA record (not A)."
},
no_mx_record: {
id: 2,
title: "No MX Record",
active: false,
text: "You can run your server without an MX record but it makes it less reliable. Add a single record with your main hostname and a 10 Priority."
},
spf_invalid: {
id: 3,
title: "SPF Record Invalid",
active: false,
text: "Your SPF record needs to mention both the IPv4 address with ip4:X and IPv6 address with ip6:X."
},
no_dmarc: {
id: 4,
title: "No DMARC Record",
active: false,
text: "You need a DMARC record, and it's recommended you configure it to p=none but route the errors to an email address on this server so you can monitor failures."
},
no_ip4_address: {
id: 5,
title: "No IP4 Address",
active: false,
text: "Most email providers are now requiring an IPv4 address that is also double reverse lookup, which means you need an IPv4 A record and a reverse DNS for it that exactly matches."
},
no_ip6_address: {
id: 6,
title: "No IP6 Address",
active: false,
text: "Most email providers are now requiring an IPv6 address that is also double reverse lookup, which means you need an IPv6 AAAA record and a reverse DNS for it that exactly matches."
},
no_spf: {
id: 7,
title: "No SPF Record",
active: false,
text: "You need an SPF record. It should be a TXT record and list your IP4 and IP6 addresses in the format \"v=spf1 a mx ip4:X ip6:Y ~all\"."
},
}
const analyze_dns = (dns) => {
let r = [];
// consider using a Rools engine if this gets too insane
if(dns.ip4.error) {
r.push(ERRORS.no_ip4_address);
r.push(ERRORS.ip4_double_reverse);
}
if(dns.ip4.reverse_errors) {
r.push(ERRORS.ip4_double_reverse);
}
if(dns.ip6.error) {
r.push(ERRORS.no_ip6_address);
r.push(ERRORS.ip6_double_reverse);
}
if(dns.ip6.reverse_errors) {
r.push(ERRORS.ip6_double_reverse);
}
if(dns.mx_error) {
r.push(ERRORS.no_mx_record);
}
if(dns.spf_error) {
r.push(ERRORS.no_spf);
}
if(dns.dmarc_error) {
r.push(ERRORS.no_dmarc);
}
if(r.length > 1) r[0].active = true;
return r;
}
export const get = async (req, res) => {
const api = new API(req, res);
const rules = { domain_name: "required"}
const form = api.validate(rules);
if(!api.admin_authenticated) {
return api.error(401, "Admin rights required.");
}
try {
if(form._valid) {
const dns = await dns_check(form.domain_name);
const tests = analyze_dns(dns);
log.debug(dns);
return api.reply(200, {dns, tests});
} else {
return api.validation_error(res, form);
}
} catch (error) {
log.error(error);
return api.error(500, "Internal Server Error");
}
}
get.authenticated = !developer_admin;
const send_test = async (email) => {
try {
const test_email = await load_templates("test");
const text = test_email.txt(email);
const html = test_email.html(email);
return send_email({
from: company.mail,
to: email,
subject: `Test email for ${company.website}`,
text, html
});
} catch(error) {
log.error(error);
return error;
}
}
export const post = async (req, res) => {
const rules = { to_address: "required|email" }
const api = new API(req, res);
if(!api.admin_authenticated) {
return api.error(401, "Admin rights required.");
}
try {
const form = api.validate(rules);
if(form._valid) {
log.debug(`Sending test email to ${form.to_address}`);
const error = await send_test(form.to_address);
return api.reply(200, {
message: "Email sent. Check server logs.",
error: error === undefined ? error : error.message,
config: get_config()
});
} else {
return api.validation_error(res, form);
}
} catch (error) {
log.error(error);
return api.error(500, "Internal Server Error");
}
}
post.authenticated = !developer_admin;