This is the code that runs https://bandolier.learnjsthehardway.com/ for you to review. It uses the https://git.learnjsthehardway.com/learn-javascript-the-hard-way/bandolier-template to create the documentation for the project.
https://bandolier.learnjsthehardway.com/
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.
171 lines
4.8 KiB
171 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;
|
|
|