diff --git a/backend/src/api/controllers/user.ts b/backend/src/api/controllers/user.ts index 53a86fa7d..fd930eb35 100644 --- a/backend/src/api/controllers/user.ts +++ b/backend/src/api/controllers/user.ts @@ -8,7 +8,7 @@ import { MonkeyResponse } from "../../utils/monkey-response"; import * as DiscordUtils from "../../utils/discord"; import { buildAgentLog, - isDevEnvironment, + getFrontendUrl, replaceObjectId, replaceObjectIds, sanitizeString, @@ -178,11 +178,7 @@ export async function sendVerificationEmail( const { data: link, error } = await tryCatch( FirebaseAdmin() .auth() - .generateEmailVerificationLink(email, { - url: isDevEnvironment() - ? "http://localhost:3000" - : "https://monkeytype.com", - }) + .generateEmailVerificationLink(email, { url: getFrontendUrl() }) ); if (error) { diff --git a/backend/src/init/email-client.ts b/backend/src/init/email-client.ts index 7cfe09ef7..2fd2b89c5 100644 --- a/backend/src/init/email-client.ts +++ b/backend/src/init/email-client.ts @@ -28,6 +28,7 @@ const templates: Record = { let transportInitialized = false; let transporter: nodemailer.Transporter; +let emailFrom = "Monkeytype "; export function isInitialized(): boolean { return transportInitialized; @@ -38,7 +39,12 @@ export async function init(): Promise { return; } - const { EMAIL_HOST, EMAIL_USER, EMAIL_PASS, EMAIL_PORT } = process.env; + const { EMAIL_HOST, EMAIL_USER, EMAIL_PASS, EMAIL_PORT, EMAIL_FROM } = + process.env; + + if (EMAIL_FROM !== undefined) { + emailFrom = EMAIL_FROM; + } if (!(EMAIL_HOST ?? "") || !(EMAIL_USER ?? "") || !(EMAIL_PASS ?? "")) { if (isDevEnvironment()) { @@ -102,7 +108,7 @@ export async function sendEmail( const template = await fillTemplate(templateName, data); const mailOptions = { - from: "Monkeytype ", + from: emailFrom, to, subject: templates[templateName].subject, html: template, diff --git a/backend/src/utils/auth.ts b/backend/src/utils/auth.ts index 6145bcdf2..616d9aaff 100644 --- a/backend/src/utils/auth.ts +++ b/backend/src/utils/auth.ts @@ -6,7 +6,7 @@ import { setTokenCacheSize, } from "./prometheus"; import { type DecodedIdToken, UserRecord } from "firebase-admin/auth"; -import { isDevEnvironment } from "./misc"; +import { getFrontendUrl } from "./misc"; import emailQueue from "../queues/email-queue"; import * as UserDAL from "../dal/user"; import { isFirebaseError } from "./error"; @@ -98,11 +98,7 @@ export async function sendForgotPasswordEmail(email: string): Promise { const link = await FirebaseAdmin() .auth() - .generatePasswordResetLink(email, { - url: isDevEnvironment() - ? "http://localhost:3000" - : "https://monkeytype.com", - }); + .generatePasswordResetLink(email, { url: getFrontendUrl() }); await emailQueue.sendForgotPasswordEmail(email, name, link); } catch (err) { diff --git a/backend/src/utils/misc.ts b/backend/src/utils/misc.ts index 619d375c9..44101d1c6 100644 --- a/backend/src/utils/misc.ts +++ b/backend/src/utils/misc.ts @@ -193,6 +193,14 @@ export function isDevEnvironment(): boolean { return process.env["MODE"] === "dev"; } +export function getFrontendUrl(): string { + return isDevEnvironment() + ? "http://localhost:3000" + : process.env["FRONTEND_URL"] !== undefined + ? process.env["FRONTEND_URL"] + : "https://monkeytype.com"; +} + /** * convert database object into api object * @param data database object with `_id: ObjectId` diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 172119819..b91102609 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -35,6 +35,11 @@ services: - REDIS_URI=redis://monkeytype-redis:6379 - FRONTEND_URL=${MONKEYTYPE_FRONTENDURL} - RECAPTCHA_SECRET=${RECAPTCHA_SECRET:-} + - EMAIL_HOST=${EMAIL_HOST:-} + - EMAIL_PORT=${EMAIL_PORT:-} + - EMAIL_USER=${EMAIL_USER:-} + - EMAIL_PASS=${EMAIL_PASS:-} + - EMAIL_FROM=${EMAIL_FROM:-} volumes: #uncomment to enable the account system, check the SELF_HOSTING.md file #- type: bind diff --git a/docker/example.env b/docker/example.env index f12af80eb..52af38e91 100644 --- a/docker/example.env +++ b/docker/example.env @@ -4,8 +4,8 @@ MONKEYTYPE_FRONTENDURL=http://myserver:8080 #url of the backend server, this must be accessible by your clients browser MONKEYTYPE_BACKENDURL=http://myserver:5005 -# uncomment below config if you need user accounts # firebase config +# uncomment below config if you need user accounts #FIREBASE_APIKEY= #FIREBASE_AUTHDOMAIN= #FIREBASE_PROJECTID= @@ -13,6 +13,15 @@ MONKEYTYPE_BACKENDURL=http://myserver:5005 #FIREBASE_MESSAGINGSENDERID= #FIREBASE_APPID= + +# email server config +# uncomment below if you want to send emails for e.g. password reset +#EMAIL_HOST=mail.myserver +#EMAIL_USER=mailuser +#EMAIL_PASS=mailpass +#EMAIL_PORT=465 +#EMAIL_FROM="Support " + # google recaptcha # uncomment below config if you need user accounts # you can use these defaults if you host this privately diff --git a/docs/SELF_HOSTING.md b/docs/SELF_HOSTING.md index d9cc6656b..fff6d00b8 100644 --- a/docs/SELF_HOSTING.md +++ b/docs/SELF_HOSTING.md @@ -14,6 +14,7 @@ - [Setup Firebase](#setup-firebase) - [Update backend configuration](#update-backend-configuration) - [Setup Recaptcha](#setup-recaptcha) + - [Setup email optional](#setup-email-optional) - [Enable daily leaderboards](#enable-daily-leaderboards) - [Configuration files](#configuration-files) - [env file](#env-file) @@ -126,6 +127,20 @@ RECAPTCHA_SITE_KEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI RECAPTCHA_SECRET=6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe ``` +### Setup email (optional) + +To enable emails for password reset and email verification update the following config in `.env` file: + +``` +# email server config +# uncomment below if you want to send emails for e.g. password reset +EMAIL_HOST=mail.myserver # your mailserver domain +EMAIL_USER=mailuser # username to authenticate with your mailserver +EMAIL_PASS=mailpass # password for the user +EMAIL_PORT=465 # port, likely 465 or 578 +EMAIL_FROM="Support " +``` + ## Enable daily leaderboards To enable daily leaderboards update the `backend-configuration.json` file and add/modify