make sending emails optional
This commit is contained in:
parent
ae98343cd3
commit
ea888dcb1d
@ -61,9 +61,11 @@ ADMIN_EMAILS=
|
||||
# Get it from https://developers.google.com/safe-browsing/v4/get-started
|
||||
GOOGLE_SAFE_BROWSING_KEY=
|
||||
|
||||
# Your email host details to use to send verification emails.
|
||||
# More info on http://nodemailer.com/
|
||||
# Mail from example "Kutt <support@kutt.it>". Leave empty to use MAIL_USER
|
||||
# Optional - Email is used to verify or change email address, reset password, and send reports.
|
||||
# If it's disabled, all the above functionality would be disabled as well.
|
||||
# MAIL_FROM example: "Kutt <support@kutt.it>". Leave it empty to use MAIL_USER.
|
||||
# More info on the configuration on http://nodemailer.com/.
|
||||
MAIL_ENABLED=false
|
||||
MAIL_HOST=
|
||||
MAIL_PORT=
|
||||
MAIL_SECURE=true
|
||||
|
@ -28,12 +28,13 @@ const env = cleanEnv(process.env, {
|
||||
JWT_SECRET: str(),
|
||||
ADMIN_EMAILS: str({ default: "" }),
|
||||
GOOGLE_SAFE_BROWSING_KEY: str({ default: "" }),
|
||||
MAIL_HOST: str(),
|
||||
MAIL_PORT: num(),
|
||||
MAIL_ENABLED: bool({ default: false }),
|
||||
MAIL_HOST: str({ default: "" }),
|
||||
MAIL_PORT: num({ default: 587 }),
|
||||
MAIL_SECURE: bool({ default: false }),
|
||||
MAIL_USER: str(),
|
||||
MAIL_USER: str({ default: "" }),
|
||||
MAIL_FROM: str({ default: "", example: "Kutt <support@kutt.it>" }),
|
||||
MAIL_PASSWORD: str(),
|
||||
MAIL_PASSWORD: str({ default: "" }),
|
||||
REPORT_EMAIL: str({ default: "" }),
|
||||
CONTACT_EMAIL: str({ default: "" })
|
||||
});
|
||||
|
@ -222,10 +222,11 @@ async function resetPasswordRequest(req, res) {
|
||||
reset_password_expires: addMinutes(new Date(), 30).toISOString()
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
if (user) {
|
||||
// TODO: handle error
|
||||
mail.resetPasswordToken(user).catch(() => null);
|
||||
mail.resetPasswordToken(user).catch(error => {
|
||||
console.error("Send reset-password token email error:\n", error);
|
||||
});
|
||||
}
|
||||
|
||||
if (req.isHTML) {
|
||||
@ -264,11 +265,6 @@ async function resetPassword(req, res, next) {
|
||||
next();
|
||||
}
|
||||
|
||||
function signupAccess(req, res, next) {
|
||||
if (!env.DISALLOW_REGISTRATION) return next();
|
||||
throw new CustomError("Registration is not allowed.");
|
||||
}
|
||||
|
||||
async function changeEmailRequest(req, res) {
|
||||
const { email, password } = req.body;
|
||||
|
||||
@ -352,6 +348,25 @@ async function changeEmail(req, res, next) {
|
||||
return next();
|
||||
}
|
||||
|
||||
function featureAccess(features, redirect) {
|
||||
return function(req, res, next) {
|
||||
for (let i = 0; i < features.length; ++i) {
|
||||
if (!features[i]) {
|
||||
if (redirect) {
|
||||
return res.redirect("/");
|
||||
} else {
|
||||
throw new CustomError("Request is not allowed.", 400);
|
||||
}
|
||||
}
|
||||
}
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
function featureAccessPage(features) {
|
||||
return featureAccess(features, true);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
admin,
|
||||
apikey,
|
||||
@ -359,6 +374,8 @@ module.exports = {
|
||||
changeEmailRequest,
|
||||
changePassword,
|
||||
cooldown,
|
||||
featureAccess,
|
||||
featureAccessPage,
|
||||
generateApiKey,
|
||||
jwt,
|
||||
jwtLoose,
|
||||
@ -369,6 +386,5 @@ module.exports = {
|
||||
resetPassword,
|
||||
resetPasswordRequest,
|
||||
signup,
|
||||
signupAccess,
|
||||
verify,
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ function config(req, res, next) {
|
||||
res.locals.contact_email = env.CONTACT_EMAIL;
|
||||
res.locals.server_ip_address = env.SERVER_IP_ADDRESS;
|
||||
res.locals.disallow_registration = env.DISALLOW_REGISTRATION;
|
||||
res.locals.mail_enabled = env.MAIL_ENABLED;
|
||||
next();
|
||||
}
|
||||
|
||||
|
@ -24,20 +24,33 @@ const transporter = nodemailer.createTransport(mailConfig);
|
||||
const resetEmailTemplatePath = path.join(__dirname, "template-reset.html");
|
||||
const verifyEmailTemplatePath = path.join(__dirname, "template-verify.html");
|
||||
const changeEmailTemplatePath = path.join(__dirname,"template-change-email.html");
|
||||
const resetEmailTemplate = fs
|
||||
.readFileSync(resetEmailTemplatePath, { encoding: "utf-8" })
|
||||
.replace(/{{domain}}/gm, env.DEFAULT_DOMAIN)
|
||||
.replace(/{{site_name}}/gm, env.SITE_NAME);
|
||||
const verifyEmailTemplate = fs
|
||||
.readFileSync(verifyEmailTemplatePath, { encoding: "utf-8" })
|
||||
.replace(/{{domain}}/gm, env.DEFAULT_DOMAIN)
|
||||
.replace(/{{site_name}}/gm, env.SITE_NAME);
|
||||
const changeEmailTemplate = fs
|
||||
.readFileSync(changeEmailTemplatePath, { encoding: "utf-8" })
|
||||
.replace(/{{domain}}/gm, env.DEFAULT_DOMAIN)
|
||||
.replace(/{{site_name}}/gm, env.SITE_NAME);
|
||||
|
||||
|
||||
let resetEmailTemplate,
|
||||
verifyEmailTemplate,
|
||||
changeEmailTemplate;
|
||||
|
||||
// only read email templates if email is enabled
|
||||
if (env.MAIL_ENABLED) {
|
||||
resetEmailTemplate = fs
|
||||
.readFileSync(resetEmailTemplatePath, { encoding: "utf-8" })
|
||||
.replace(/{{domain}}/gm, env.DEFAULT_DOMAIN)
|
||||
.replace(/{{site_name}}/gm, env.SITE_NAME);
|
||||
verifyEmailTemplate = fs
|
||||
.readFileSync(verifyEmailTemplatePath, { encoding: "utf-8" })
|
||||
.replace(/{{domain}}/gm, env.DEFAULT_DOMAIN)
|
||||
.replace(/{{site_name}}/gm, env.SITE_NAME);
|
||||
changeEmailTemplate = fs
|
||||
.readFileSync(changeEmailTemplatePath, { encoding: "utf-8" })
|
||||
.replace(/{{domain}}/gm, env.DEFAULT_DOMAIN)
|
||||
.replace(/{{site_name}}/gm, env.SITE_NAME);
|
||||
}
|
||||
|
||||
async function verification(user) {
|
||||
if (!env.MAIL_ENABLED) {
|
||||
throw new Error("Attempting to send verification email but email is not enabled.");
|
||||
};
|
||||
|
||||
const mail = await transporter.sendMail({
|
||||
from: env.MAIL_FROM || env.MAIL_USER,
|
||||
to: user.email,
|
||||
@ -58,6 +71,10 @@ async function verification(user) {
|
||||
}
|
||||
|
||||
async function changeEmail(user) {
|
||||
if (!env.MAIL_ENABLED) {
|
||||
throw new Error("Attempting to send change email token but email is not enabled.");
|
||||
};
|
||||
|
||||
const mail = await transporter.sendMail({
|
||||
from: env.MAIL_FROM || env.MAIL_USER,
|
||||
to: user.change_email_address,
|
||||
@ -78,6 +95,10 @@ async function changeEmail(user) {
|
||||
}
|
||||
|
||||
async function resetPasswordToken(user) {
|
||||
if (!env.MAIL_ENABLED) {
|
||||
throw new Error("Attempting to send reset password email but email is not enabled.");
|
||||
};
|
||||
|
||||
const mail = await transporter.sendMail({
|
||||
from: env.MAIL_FROM || env.MAIL_USER,
|
||||
to: user.email,
|
||||
@ -89,7 +110,7 @@ async function resetPasswordToken(user) {
|
||||
.replace(/{{resetpassword}}/gm, user.reset_password_token)
|
||||
.replace(/{{domain}}/gm, env.DEFAULT_DOMAIN)
|
||||
});
|
||||
|
||||
|
||||
if (!mail.accepted.length) {
|
||||
throw new CustomError(
|
||||
"Couldn't send reset password email. Try again later."
|
||||
@ -98,6 +119,10 @@ async function resetPasswordToken(user) {
|
||||
}
|
||||
|
||||
async function sendReportEmail(link) {
|
||||
if (!env.MAIL_ENABLED) {
|
||||
throw new Error("Attempting to send report email but email is not enabled.");
|
||||
};
|
||||
|
||||
const mail = await transporter.sendMail({
|
||||
from: env.MAIL_FROM || env.MAIL_USER,
|
||||
to: env.REPORT_EMAIL,
|
||||
|
@ -6,6 +6,7 @@ const asyncHandler = require("../utils/asyncHandler");
|
||||
const locals = require("../handlers/locals.handler");
|
||||
const auth = require("../handlers/auth.handler");
|
||||
const utils = require("../utils");
|
||||
const env = require("../env");
|
||||
|
||||
const router = Router();
|
||||
|
||||
@ -21,7 +22,7 @@ router.post(
|
||||
router.post(
|
||||
"/signup",
|
||||
locals.viewTemplate("partials/auth/form"),
|
||||
auth.signupAccess,
|
||||
auth.featureAccess([!env.DISALLOW_REGISTRATION, env.MAIL_ENABLED]),
|
||||
validators.signup,
|
||||
asyncHandler(helpers.verify),
|
||||
asyncHandler(auth.signup)
|
||||
@ -40,6 +41,7 @@ router.post(
|
||||
"/change-email",
|
||||
locals.viewTemplate("partials/settings/change_email"),
|
||||
asyncHandler(auth.jwt),
|
||||
auth.featureAccess([env.MAIL_ENABLED]),
|
||||
validators.changeEmail,
|
||||
asyncHandler(helpers.verify),
|
||||
asyncHandler(auth.changeEmailRequest)
|
||||
@ -55,6 +57,7 @@ router.post(
|
||||
router.post(
|
||||
"/reset-password",
|
||||
locals.viewTemplate("partials/reset_password/form"),
|
||||
auth.featureAccess([env.MAIL_ENABLED]),
|
||||
validators.resetPassword,
|
||||
asyncHandler(helpers.verify),
|
||||
asyncHandler(auth.resetPasswordRequest)
|
||||
|
@ -88,6 +88,7 @@ router.post(
|
||||
router.post(
|
||||
"/report",
|
||||
locals.viewTemplate("partials/report/form"),
|
||||
auth.featureAccess([env.MAIL_ENABLED]),
|
||||
validators.reportLink,
|
||||
asyncHandler(helpers.verify),
|
||||
asyncHandler(link.report)
|
||||
|
@ -5,6 +5,7 @@ const renders = require("../handlers/renders.handler");
|
||||
const asyncHandler = require("../utils/asyncHandler");
|
||||
const locals = require("../handlers/locals.handler");
|
||||
const auth = require("../handlers/auth.handler");
|
||||
const env = require("../env");
|
||||
|
||||
const router = Router();
|
||||
|
||||
@ -64,6 +65,7 @@ router.get(
|
||||
|
||||
router.get(
|
||||
"/reset-password",
|
||||
auth.featureAccessPage([env.MAIL_ENABLED]),
|
||||
asyncHandler(auth.jwtLoosePage),
|
||||
asyncHandler(locals.user),
|
||||
asyncHandler(renders.resetPassword)
|
||||
|
@ -29,25 +29,29 @@
|
||||
Log in
|
||||
</button>
|
||||
{{#unless disallow_registration}}
|
||||
<button
|
||||
type="button"
|
||||
class="secondary signup"
|
||||
hx-post="/api/auth/signup"
|
||||
hx-target="#login-signup"
|
||||
hx-trigger="click"
|
||||
hx-indicator="#login-signup"
|
||||
hx-swap="outerHTML"
|
||||
hx-sync="closest form"
|
||||
hx-on:htmx:before-request="htmx.addClass('#login-signup', 'signup')"
|
||||
hx-on:htmx:after-request="htmx.removeClass('#login-signup', 'signup')"
|
||||
>
|
||||
<span>{{> icons/new_user}}</span>
|
||||
<span>{{> icons/spinner}}</span>
|
||||
Sign up
|
||||
</button>
|
||||
{{#if mail_enabled}}
|
||||
<button
|
||||
type="button"
|
||||
class="secondary signup"
|
||||
hx-post="/api/auth/signup"
|
||||
hx-target="#login-signup"
|
||||
hx-trigger="click"
|
||||
hx-indicator="#login-signup"
|
||||
hx-swap="outerHTML"
|
||||
hx-sync="closest form"
|
||||
hx-on:htmx:before-request="htmx.addClass('#login-signup', 'signup')"
|
||||
hx-on:htmx:after-request="htmx.removeClass('#login-signup', 'signup')"
|
||||
>
|
||||
<span>{{> icons/new_user}}</span>
|
||||
<span>{{> icons/spinner}}</span>
|
||||
Sign up
|
||||
</button>
|
||||
{{/if}}
|
||||
{{/unless}}
|
||||
</div>
|
||||
<a class="forgot-password" href="/reset-password" title="Reset password">Forgot your password?</a>
|
||||
{{#if mail_enabled}}
|
||||
<a class="forgot-password" href="/reset-password" title="Reset password">Forgot your password?</a>
|
||||
{{/if}}
|
||||
{{#unless errors}}
|
||||
{{#if error}}
|
||||
<p class="error">{{error}}</p>
|
||||
|
@ -4,10 +4,12 @@
|
||||
Report abuse.
|
||||
</h2>
|
||||
<p>
|
||||
Report abuses, malware and phishing links to the email address below
|
||||
or use the form. We will review as soon as we can.
|
||||
Report abuses, malware and phishing links to the email address below {{#if mail_enabled}}or use the form{{/if}}.
|
||||
We will review as soon as we can.
|
||||
</p>
|
||||
{{> report/email}}
|
||||
{{> report/form}}
|
||||
{{#if mail_enabled}}
|
||||
{{> report/form}}
|
||||
{{/if}}
|
||||
</section>
|
||||
{{> footer}}
|
@ -10,8 +10,10 @@
|
||||
<hr />
|
||||
{{> settings/change_password}}
|
||||
<hr />
|
||||
{{> settings/change_email}}
|
||||
<hr />
|
||||
{{#if mail_enabled}}
|
||||
{{> settings/change_email}}
|
||||
<hr />
|
||||
{{/if}}
|
||||
{{> settings/delete_account}}
|
||||
</section>
|
||||
{{> footer}}
|
Loading…
x
Reference in New Issue
Block a user