131 lines
5.7 KiB
JavaScript
131 lines
5.7 KiB
JavaScript
/* Mail and password-reset administration view. */
|
|
|
|
class MailAdmin {
|
|
/**
|
|
* goal : Connect mail settings, test delivery and TLS controls to the API.
|
|
* pre : The mail administration form exists in the document.
|
|
* post : Save, test and TLS-change events are handled by this controller.
|
|
* result : A directly usable mail administration controller.
|
|
*/
|
|
constructor(api, translate) {
|
|
this.api = api;
|
|
this.translate = translate;
|
|
this.form = document.getElementById("mail-settings-form");
|
|
this.status = document.getElementById("mail-settings-status");
|
|
this.testButton = document.getElementById("mail-test-button");
|
|
this.tls = document.getElementById("mail-smtp-tls");
|
|
this.acceptUntrusted = document.getElementById(
|
|
"mail-smtp-accept-untrusted-certificates");
|
|
|
|
this.form.addEventListener("submit", (event) => this.#save(event));
|
|
this.testButton.addEventListener("click", () => this.#test());
|
|
this.tls.addEventListener("change", () => this.#syncTlsControls());
|
|
}
|
|
|
|
/**
|
|
* goal : Load stored mail settings and initialize the test recipient.
|
|
* pre : The current session has administrator permission.
|
|
* post : All form controls represent the stored server configuration.
|
|
* result : A promise that resolves after the form has been initialized.
|
|
*/
|
|
async load(currentUserEmail = "") {
|
|
const settings = await this.api("/api/admin/mail-settings");
|
|
document.getElementById("mail-public-url").value =
|
|
settings.publicUrl || window.location.origin;
|
|
document.getElementById("mail-smtp-host").value = settings.smtpHost || "";
|
|
document.getElementById("mail-smtp-port").value = settings.smtpPort || "587";
|
|
document.getElementById("mail-smtp-from").value = settings.smtpFrom || "";
|
|
document.getElementById("mail-smtp-user").value = settings.smtpUser || "";
|
|
document.getElementById("mail-smtp-password").value = "";
|
|
this.tls.checked = settings.smtpTls !== false;
|
|
this.acceptUntrusted.checked = settings.smtpAcceptUntrustedCertificates === true;
|
|
document.getElementById("mail-reset-limit").value = settings.resetLimit || "2";
|
|
document.getElementById("mail-test-recipient").value = currentUserEmail;
|
|
document.getElementById("mail-password-help").textContent = settings.hasPassword
|
|
? this.translate(
|
|
"smtp-password-kept", "A password is stored; leave empty to keep it.")
|
|
: "";
|
|
this.status.textContent = "";
|
|
this.acceptUntrusted.disabled = !this.tls.checked;
|
|
}
|
|
|
|
/** Read the mail form using the public server API field names. */
|
|
#formData() {
|
|
return {
|
|
publicUrl: document.getElementById("mail-public-url").value,
|
|
smtpHost: document.getElementById("mail-smtp-host").value,
|
|
smtpPort: document.getElementById("mail-smtp-port").value,
|
|
smtpFrom: document.getElementById("mail-smtp-from").value,
|
|
smtpUser: document.getElementById("mail-smtp-user").value,
|
|
smtpPassword: document.getElementById("mail-smtp-password").value,
|
|
smtpTls: this.tls.checked,
|
|
smtpAcceptUntrustedCertificates: this.acceptUntrusted.checked,
|
|
resetLimit: document.getElementById("mail-reset-limit").value
|
|
};
|
|
}
|
|
|
|
/** Store the current mail settings and retain an omitted password. */
|
|
async #save(event) {
|
|
event.preventDefault();
|
|
try {
|
|
await this.api("/api/admin/mail-settings", {
|
|
method: "PUT",
|
|
body: JSON.stringify(this.#formData())
|
|
});
|
|
document.getElementById("mail-smtp-password").value = "";
|
|
document.getElementById("mail-password-help").textContent = this.translate(
|
|
"smtp-password-kept", "A password is stored; leave empty to keep it.");
|
|
this.status.textContent = this.translate("saved", "Saved");
|
|
} catch (error) {
|
|
this.status.textContent = error.message;
|
|
}
|
|
}
|
|
|
|
/** Send one test message using the unsaved values currently in the form. */
|
|
async #test() {
|
|
const recipient = document.getElementById("mail-test-recipient").value.trim();
|
|
this.testButton.disabled = true;
|
|
this.status.textContent = this.translate(
|
|
"sending-test-mail", "Sending test email…");
|
|
try {
|
|
await this.api("/api/admin/mail-settings/test", {
|
|
method: "POST",
|
|
body: JSON.stringify({ ...this.#formData(), recipient })
|
|
});
|
|
this.status.textContent = this.translate(
|
|
"test-mail-sent", "Test email accepted by SMTP server");
|
|
} catch (error) {
|
|
console.error("SMTP test failed", error);
|
|
this.status.textContent = this.#smtpErrorMessage(error);
|
|
} finally {
|
|
this.testButton.disabled = false;
|
|
}
|
|
}
|
|
|
|
/** Translate known TLS failures and retain other server error messages. */
|
|
#smtpErrorMessage(error) {
|
|
const message = error && error.message ? error.message : String(error);
|
|
const certificateFailure = message.includes("certificate verify failed") ||
|
|
message.includes("TLS certificate verification failed");
|
|
if (certificateFailure) {
|
|
return this.translate(
|
|
"smtp-certificate-verification-failed",
|
|
"The SMTP server certificate could not be verified. Install a valid certificate, or select the local-server exception if this is a trusted local SMTP server.");
|
|
}
|
|
if (message.includes("no protocols available")) {
|
|
return this.translate(
|
|
"smtp-no-modern-tls",
|
|
"The SMTP connection attempted an obsolete TLS protocol. Install the current Racket Wiki version, which negotiates modern TLS automatically.");
|
|
}
|
|
return message;
|
|
}
|
|
|
|
/** Disable the certificate exception whenever STARTTLS is disabled. */
|
|
#syncTlsControls() {
|
|
this.acceptUntrusted.disabled = !this.tls.checked;
|
|
if (this.acceptUntrusted.disabled) this.acceptUntrusted.checked = false;
|
|
}
|
|
}
|
|
|
|
export { MailAdmin };
|