mirror of
https://github.com/OneUptime/oneuptime
synced 2024-11-21 22:59:07 +00:00
fix email
This commit is contained in:
parent
77c22d9e04
commit
396e8f8b40
@ -35,6 +35,10 @@ function App(): ReactElement {
|
||||
path="/accounts/forgot-password"
|
||||
element={<ForgotPasswordPage />}
|
||||
/>
|
||||
<Route
|
||||
path="/accounts/reset-password/:token"
|
||||
element={<ForgotPasswordPage />}
|
||||
/>
|
||||
<Route path="/accounts/register" element={<RegisterPage />} />
|
||||
<Route path="/accounts/login/sso" element={<SsoLoginPage />} />
|
||||
<Route
|
||||
|
11
Common/Types/Email/EmailMessage.ts
Normal file
11
Common/Types/Email/EmailMessage.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import Email from '../Email';
|
||||
import Dictionary from '../Dictionary';
|
||||
import EmailTemplateType from './EmailTemplateType';
|
||||
|
||||
export default interface EmailMessage {
|
||||
toEmail: Email;
|
||||
subject: string;
|
||||
templateType?: EmailTemplateType;
|
||||
vars: Dictionary<string>;
|
||||
body?: string;
|
||||
}
|
@ -2,7 +2,7 @@ import Email from '../Email';
|
||||
import Port from '../Port';
|
||||
import Hostname from '../API/Hostname';
|
||||
|
||||
export default interface MailServer {
|
||||
export default interface EmailServer {
|
||||
host: Hostname;
|
||||
port: Port;
|
||||
username: string;
|
@ -1,26 +0,0 @@
|
||||
import Dictionary from '../Dictionary';
|
||||
import BadOperationException from '../Exception/BadOperationException';
|
||||
import EmailTemplateType from './EmailTemplateType';
|
||||
|
||||
class EmailSubjects {
|
||||
private subjectMap: Dictionary<string> = {};
|
||||
|
||||
public constructor() {
|
||||
this.subjectMap[EmailTemplateType.SIGNUP_WELCOME_EMAIL] =
|
||||
'Welcome to OneUptime.';
|
||||
this.subjectMap[EmailTemplateType.SIGNUP_VERIFICATION_EMAIL] =
|
||||
'Welcome to OneUptime. Please verify your email.';
|
||||
}
|
||||
|
||||
public getSubjectByType(emailTemplateType: EmailTemplateType): string {
|
||||
if (this.subjectMap[emailTemplateType]) {
|
||||
return this.subjectMap[emailTemplateType] as string;
|
||||
}
|
||||
|
||||
throw new BadOperationException(
|
||||
`Subject for ${emailTemplateType} not found.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default new EmailSubjects();
|
@ -1,15 +1,6 @@
|
||||
enum EmailTemplateType {
|
||||
SIGNUP_WELCOME_EMAIL = 'SignupWelcomeEmail',
|
||||
SIGNUP_VERIFICATION_EMAIL = 'SignupVerificationEmail',
|
||||
SUBSCRIBER_INCIDENT_CREATED = 'Subscriber Incident Created',
|
||||
SUBSCRIBER_INCIDENT_ACKNOWLEDGED = 'Subscriber Incident Acknowledged',
|
||||
SUBSCRIBER_INCIDENT_RESOLVED = 'Subscriber Incident Resolved',
|
||||
INVESTIGATION_NOTE_CREATED = 'Investigation Note Created',
|
||||
SUBSCRIBER_SCHEDULED_MAINTENANCE_CREATED = 'Subscriber Scheduled Maintenance Created',
|
||||
SUBSCRIBER_SCHEDULED_MAINTENANCE_NOTE_CREATED = 'Subscriber Scheduled Maintenance Note Created',
|
||||
SUBSCRIBER_SCHEDULED_MAINTENANCE_RESOLVED = 'Subscriber Scheduled Maintenance Resolved',
|
||||
SUBSCRIBER_SCHEDULED_MAINTENANCE_CANCELLED = 'Subscriber Scheduled Maintenance Cancelled',
|
||||
SUBSCRIBER_ANNOUNCEMENT_NOTIFICATION_CREATED = 'Subscriber Announcement Notification Created',
|
||||
ForgotPassword = "ForgotPassword.hbs",
|
||||
WelomeEmail="WelcomeEmail.hbs"
|
||||
}
|
||||
|
||||
export default EmailTemplateType;
|
||||
|
@ -1,11 +0,0 @@
|
||||
import Email from '../Email';
|
||||
import Dictionary from '../Dictionary';
|
||||
import EmailTemplateType from '../Email/EmailTemplateType';
|
||||
|
||||
export default interface Mail {
|
||||
toEmail: Email;
|
||||
subject: string;
|
||||
templateType: EmailTemplateType;
|
||||
vars: Dictionary<string>;
|
||||
body: string;
|
||||
}
|
@ -2,6 +2,7 @@ import Protocol from 'Common/Types/API/Protocol';
|
||||
import ObjectID from 'Common/Types/ObjectID';
|
||||
import Port from 'Common/Types/Port';
|
||||
import Hostname from 'Common/Types/API/Hostname';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
|
||||
export const DisableSignup: boolean = process.env['DISABLE_SIGNUP'] === 'true';
|
||||
|
||||
@ -33,6 +34,10 @@ export const ClusterKey: ObjectID = new ObjectID(
|
||||
process.env['ONEUPTIME_SECRET'] || ''
|
||||
);
|
||||
|
||||
export const Domain: Hostname = Hostname.fromString(
|
||||
process.env['DOMAIN'] || ''
|
||||
);
|
||||
|
||||
export const RealtimeHostname: Hostname = Hostname.fromString(
|
||||
process.env['REALTIME_HOSTNAME'] || ''
|
||||
);
|
||||
@ -79,3 +84,25 @@ export const HttpProtocol: Protocol = (
|
||||
export const RedisHostname: string = process.env['REDIS_HOST'] || '';
|
||||
export const RedisPassword: string = process.env['REDIS_PASSWORD'] || '';
|
||||
export const RedisPort: Port = new Port(process.env['REDIS_PORT'] || '');
|
||||
|
||||
|
||||
export const DashboardApiRoute: Route = new Route(process.env['DASHBOARD_API_ROUTE'] || '');
|
||||
|
||||
export const IdentityRoute: Route = new Route(process.env['IDENTITY_ROUTE'] || '');
|
||||
|
||||
export const FileRoute: Route = new Route(process.env['FILE_ROUTE'] || '');
|
||||
|
||||
export const StausPageRoute: Route = new Route(process.env['STATUS_PAGE_ROUTE'] || '');
|
||||
|
||||
export const DashboardRoute: Route = new Route(process.env['DASHBOARD_ROUTE'] || '');
|
||||
|
||||
export const IntegrationRoute: Route = new Route(process.env['INTEGRATION_ROUTE'] || '');
|
||||
|
||||
export const HelmRoute: Route = new Route(process.env['HELMCHART_ROUTE'] || '');
|
||||
export const AccountsRoute: Route = new Route(process.env['ACCOUNTS_ROUTE'] || '');
|
||||
|
||||
export const ApiDocsRoute: Route = new Route(process.env['APIDOCS_ROUTE'] || '');
|
||||
|
||||
export const AdminDashboardRoute: Route = new Route(
|
||||
process.env['ADMINDASHBOARD_ROUTE'] || ''
|
||||
);
|
||||
|
@ -5,13 +5,13 @@ import URL from 'Common/Types/API/URL';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import API from 'Common/Utils/API';
|
||||
import { ClusterKey, HttpProtocol, MailHostname } from '../Config';
|
||||
import Mail from 'Common/Types/Mail/Mail';
|
||||
import MailServer from 'Common/Types/Mail/MailServer';
|
||||
import Email from 'Common/Types/Email/EmailMessage';
|
||||
import EmailServer from 'Common/Types/Email/EmailServer';
|
||||
|
||||
export default class MailService {
|
||||
public static async sendMail(
|
||||
mail: Mail,
|
||||
mailServer?: MailServer
|
||||
mail: Email,
|
||||
mailServer?: EmailServer
|
||||
): Promise<HTTPResponse<EmptyResponseData>> {
|
||||
const body: JSONObject = {
|
||||
...mail,
|
||||
|
@ -1,11 +1,12 @@
|
||||
import {
|
||||
AccountsHostname,
|
||||
DashboardHostname,
|
||||
DisableSignup,
|
||||
HomeHostname,
|
||||
HttpProtocol,
|
||||
IsSaaSService,
|
||||
EncryptionSecret,
|
||||
Domain,
|
||||
AccountsRoute,
|
||||
DashboardRoute,
|
||||
} from 'CommonServer/Config';
|
||||
import Express, {
|
||||
ExpressRequest,
|
||||
@ -23,13 +24,13 @@ import EmailVerificationToken from 'Model/Models/EmailVerificationToken';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import MailService from 'CommonServer/Services/MailService';
|
||||
import EmailTemplateType from 'Common/Types/Email/EmailTemplateType';
|
||||
import EmailSubjects from 'Common/Types/Email/EmailSubjects';
|
||||
import URL from 'Common/Types/API/URL';
|
||||
import Response from 'CommonServer/Utils/Response';
|
||||
import JSONWebToken from 'CommonServer/Utils/JsonWebToken';
|
||||
import OneUptimeDate from 'Common/Types/Date';
|
||||
import PositiveNumber from 'Common/Types/PositiveNumber';
|
||||
import BaseModel from 'Common/Models/BaseModel';
|
||||
import Route from 'Common/Types/API/Route';
|
||||
|
||||
const router: ExpressRouter = Express.getRouter();
|
||||
|
||||
@ -85,7 +86,7 @@ router.post(
|
||||
emailVerificationToken &&
|
||||
user &&
|
||||
alreadySavedUser?.id?.toString() ===
|
||||
emailVerificationToken?.userId?.toString()
|
||||
emailVerificationToken?.userId?.toString()
|
||||
) {
|
||||
user.isEmailVerified = true;
|
||||
}
|
||||
@ -122,37 +123,31 @@ router.post(
|
||||
|
||||
if (alreadySavedUser) {
|
||||
// Send Welcome Mail
|
||||
await MailService.sendMail(
|
||||
user.email!,
|
||||
EmailSubjects.getSubjectByType(
|
||||
EmailTemplateType.SIGNUP_WELCOME_EMAIL
|
||||
),
|
||||
EmailTemplateType.SIGNUP_WELCOME_EMAIL,
|
||||
{
|
||||
await MailService.sendMail({
|
||||
toEmail: user.email!,
|
||||
subject: "Welcome to OneUptime.",
|
||||
templateType: EmailTemplateType.WelomeEmail,
|
||||
vars: {
|
||||
name: user.name!.toString(),
|
||||
dashboardUrl: new URL(
|
||||
HttpProtocol,
|
||||
DashboardHostname
|
||||
Domain, DashboardRoute
|
||||
).toString(),
|
||||
homeUrl: new URL(HttpProtocol, HomeHostname).toString(),
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Send EmailVerification Link because this is a new user.
|
||||
await MailService.sendMail(
|
||||
user.email!,
|
||||
EmailSubjects.getSubjectByType(
|
||||
EmailTemplateType.SIGNUP_WELCOME_EMAIL
|
||||
),
|
||||
EmailTemplateType.SIGNUP_WELCOME_EMAIL,
|
||||
{
|
||||
MailService.sendMail({
|
||||
toEmail: user.email!,
|
||||
subject: 'Welcome to OneUptime. Please verify your email.',
|
||||
templateType: EmailTemplateType.WelomeEmail,
|
||||
vars: {
|
||||
name: user.name!.toString(),
|
||||
emailVerificationUrl: new URL(
|
||||
HttpProtocol,
|
||||
AccountsHostname
|
||||
).toString(),
|
||||
emailVerificationUrl: new URL(HttpProtocol, Domain, new Route(AccountsRoute.toString()).addRoute("/reset-password/")).toString(),
|
||||
homeUrl: new URL(HttpProtocol, HomeHostname).toString(),
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@ -187,10 +182,8 @@ router.post(
|
||||
|
||||
const user: User = User.fromJSON(data as JSONObject, User) as User;
|
||||
|
||||
await user.password?.hashValue(EncryptionSecret);
|
||||
|
||||
const alreadySavedUser: User | null = await UserService.findOneBy({
|
||||
query: { email: user.email!, password: user.password! },
|
||||
query: { email: user.email! },
|
||||
select: {
|
||||
_id: true,
|
||||
password: true,
|
||||
@ -204,19 +197,38 @@ router.post(
|
||||
});
|
||||
|
||||
if (alreadySavedUser) {
|
||||
const token: string = JSONWebToken.sign(
|
||||
alreadySavedUser,
|
||||
OneUptimeDate.getSecondsInDays(new PositiveNumber(30))
|
||||
);
|
||||
|
||||
return Response.sendJsonObjectResponse(req, res, {
|
||||
token: token,
|
||||
user: BaseModel.toJSON(alreadySavedUser, User),
|
||||
const token = ObjectID.generate().toString();
|
||||
await UserService.updateOneBy({
|
||||
query: {
|
||||
_id: user._id!
|
||||
},
|
||||
data: {
|
||||
resetPasswordToken: token,
|
||||
resetPasswordExpires: OneUptimeDate.getOneDayAfter()
|
||||
},
|
||||
props: {
|
||||
isRoot: true
|
||||
}
|
||||
});
|
||||
|
||||
MailService.sendMail({
|
||||
toEmail: user.email!,
|
||||
subject: "Password Reset Request for OneUptime",
|
||||
templateType: EmailTemplateType.ForgotPassword,
|
||||
vars: {
|
||||
homeURL: new URL(HttpProtocol, Domain).toString(),
|
||||
tokenVerifyUrl: new URL(HttpProtocol, Domain, new Route(AccountsRoute.toString()).addRoute("/reset-password/" + token)).toString(),
|
||||
}
|
||||
});
|
||||
|
||||
return Response.sendEmptyResponse(req, res);
|
||||
}
|
||||
|
||||
throw new BadDataException(
|
||||
'Invalid login: Email or password does not match.'
|
||||
`No user is registered with ${user.email?.toString()}`
|
||||
);
|
||||
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
|
@ -7,12 +7,12 @@ const router: ExpressRouter = Express.getRouter();
|
||||
import Response from 'CommonServer/Utils/Response';
|
||||
import ClusterKeyAuthorization from 'CommonServer/Middleware/ClusterKeyAuthorization';
|
||||
import MailService from '../Services/MailService';
|
||||
import Mail from 'Common/Types/Mail/Mail';
|
||||
import EmailMessage from 'Common/Types/Email/EmailMessage';
|
||||
import EmailTemplateType from 'Common/Types/Email/EmailTemplateType';
|
||||
import { JSONObject } from 'Common/Types/JSON';
|
||||
import Email from 'Common/Types/Email';
|
||||
import Dictionary from 'Common/Types/Dictionary';
|
||||
import MailServer from 'Common/Types/Mail/MailServer';
|
||||
import EmailServer from 'Common/Types/Email/EmailServer';
|
||||
|
||||
router.post(
|
||||
'/send',
|
||||
@ -20,7 +20,7 @@ router.post(
|
||||
async (req: ExpressRequest, res: ExpressResponse) => {
|
||||
const body: JSONObject = req.body;
|
||||
|
||||
const mail: Mail = {
|
||||
const mail: EmailMessage = {
|
||||
templateType: body['template-name'] as EmailTemplateType,
|
||||
toEmail: new Email(body['to-email'] as string),
|
||||
subject: body['subject'] as string,
|
||||
@ -28,10 +28,10 @@ router.post(
|
||||
body: body['body'] as string || '',
|
||||
};
|
||||
|
||||
let mailServer: MailServer | undefined = undefined;
|
||||
let mailServer: EmailServer | undefined = undefined;
|
||||
|
||||
if (hasMailServerSettingsInBody(body)) {
|
||||
mailServer = MailService.getMailServer(req.body);
|
||||
mailServer = MailService.getEmailServer(req.body);
|
||||
}
|
||||
|
||||
await MailService.send(
|
||||
|
@ -2,11 +2,11 @@ import nodemailer, { Transporter } from 'nodemailer';
|
||||
import hbs from 'nodemailer-express-handlebars';
|
||||
import Handlebars from 'handlebars';
|
||||
import fsp from 'fs/promises';
|
||||
import Mail from 'Common/Types/Mail/Mail';
|
||||
import EmailMessage from 'Common/Types/Email/EmailMessage';
|
||||
import Path from 'path';
|
||||
import Email from 'Common/Types/Email';
|
||||
import BadDataException from 'Common/Types/Exception/BadDataException';
|
||||
import MailServer from 'Common/Types/Mail/MailServer';
|
||||
import EmailServer from 'Common/Types/Email/EmailServer';
|
||||
import LocalCache from 'CommonServer/Infrastructure/LocalCache';
|
||||
import OneUptimeDate from 'Common/Types/Date';
|
||||
import EmailTemplateType from 'Common/Types/Email/EmailTemplateType';
|
||||
@ -72,7 +72,7 @@ export default class MailService {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static getMailServer(obj : JSONObject): MailServer {
|
||||
public static getEmailServer(obj : JSONObject): EmailServer {
|
||||
if (!this.isSMTPConfigValid(obj)) {
|
||||
throw new BadDataException("SMTP Config is not valid");
|
||||
}
|
||||
@ -89,8 +89,8 @@ export default class MailService {
|
||||
};
|
||||
}
|
||||
|
||||
private static getGlobalSmtpSettings(): MailServer {
|
||||
return this.getMailServer(process.env);
|
||||
private static getGlobalSmtpSettings(): EmailServer {
|
||||
return this.getEmailServer(process.env);
|
||||
}
|
||||
|
||||
|
||||
@ -111,7 +111,7 @@ export default class MailService {
|
||||
Path.resolve(
|
||||
process.cwd(),
|
||||
'Templates',
|
||||
`${emailTemplateType}.hbs`
|
||||
`${emailTemplateType}`
|
||||
),
|
||||
{ encoding: 'utf8', flag: 'r' }
|
||||
);
|
||||
@ -136,7 +136,7 @@ export default class MailService {
|
||||
return subjectHandlebars(vars).toString();
|
||||
}
|
||||
|
||||
private static createMailer(mailServer: MailServer): Transporter {
|
||||
private static createMailer(EmailServer: EmailServer): Transporter {
|
||||
const helpers: Dictionary<string> = {
|
||||
year: OneUptimeDate.getCurrentYear().toString(),
|
||||
};
|
||||
@ -154,12 +154,12 @@ export default class MailService {
|
||||
};
|
||||
|
||||
const privateMailer: Transporter = nodemailer.createTransport({
|
||||
host: mailServer.host.toString(),
|
||||
port: mailServer.port.toNumber(),
|
||||
secure: mailServer.secure,
|
||||
host: EmailServer.host.toString(),
|
||||
port: EmailServer.port.toNumber(),
|
||||
secure: EmailServer.secure,
|
||||
auth: {
|
||||
user: mailServer.username,
|
||||
pass: mailServer.password,
|
||||
user: EmailServer.username,
|
||||
pass: EmailServer.password,
|
||||
},
|
||||
});
|
||||
|
||||
@ -169,24 +169,33 @@ export default class MailService {
|
||||
}
|
||||
|
||||
private static async transportMail(
|
||||
mail: Mail,
|
||||
mailServer: MailServer
|
||||
mail: EmailMessage,
|
||||
EmailServer: EmailServer
|
||||
): Promise<void> {
|
||||
const mailer: Transporter = this.createMailer(mailServer);
|
||||
const mailer: Transporter = this.createMailer(EmailServer);
|
||||
await mailer.sendMail(mail);
|
||||
}
|
||||
|
||||
public static async send(
|
||||
mail: Mail,
|
||||
mailServer?: MailServer
|
||||
mail: EmailMessage,
|
||||
EmailServer?: EmailServer
|
||||
): Promise<void> {
|
||||
if (!mailServer) {
|
||||
mailServer = this.getGlobalSmtpSettings();
|
||||
if (!EmailServer) {
|
||||
EmailServer = this.getGlobalSmtpSettings();
|
||||
}
|
||||
|
||||
mail.body = mail.templateType ? await this.compileEmailBody(mail.templateType, mail.vars) : this.compileText(mail.body, mail.vars);
|
||||
// default vars.
|
||||
if (!mail.vars) {
|
||||
mail.vars = {};
|
||||
}
|
||||
|
||||
if (!mail.vars['year']) {
|
||||
mail.vars['year'] = OneUptimeDate.getCurrentYear().toString();
|
||||
}
|
||||
|
||||
mail.body = mail.templateType ? await this.compileEmailBody(mail.templateType, mail.vars) : this.compileText(mail.body || '', mail.vars);
|
||||
mail.subject = this.compileText(mail.subject, mail.vars);
|
||||
|
||||
await this.transportMail(mail, mailServer);
|
||||
await this.transportMail(mail, EmailServer);
|
||||
}
|
||||
}
|
||||
|
@ -429,7 +429,7 @@
|
||||
style="border: 0; margin: 0; padding: 0; color: #000000 !important; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Ubuntu, sans-serif; font-size: 16px; line-height: 24px;">
|
||||
|
||||
|
||||
If you need any help using OneUptime, please send us an email at <a
|
||||
The password reset email expires in 24 hours. If you need any help using OneUptime, please send us an email at <a
|
||||
style="color: #000000; text-decoration: none; font-weight: bold"
|
||||
href="mailto:support@oneuptime.com" target="_blank">support@oneuptime.com</a> and let us
|
||||
know.
|
@ -5,6 +5,11 @@ APP_TAG=latest
|
||||
# This will be generated automatically during install.
|
||||
ONEUPTIME_SECRET={{ .Env.ONEUPTIME_SECRET }}
|
||||
|
||||
# Which domain is this server hosted on?
|
||||
DOMAIN=oneuptime.com
|
||||
# Is this server hosted with a TLS cert. If yes, this should be "https"
|
||||
HTTP_PROTOCOL=http
|
||||
|
||||
# This supports test | production | development | ci.
|
||||
# Development is used for local development. Test is used for insider / beta / staging builds. Production is used for production ready app. ci is for testing in the CI/CD.
|
||||
ENVIRONMENT=production
|
||||
|
Loading…
Reference in New Issue
Block a user