fix log in

This commit is contained in:
Simon Larsen 2022-11-26 21:02:15 +00:00
parent 8be5b7ea87
commit 4c0db69279
No known key found for this signature in database
GPG Key ID: AB45983AA9C81CDE
7 changed files with 699 additions and 10 deletions

View File

@ -11,11 +11,9 @@ import NotFound from './Pages/NotFound';
import SsoLoginPage from './Pages/SsoLogin';
import ForgotPasswordPage from './Pages/ForgotPassword';
import RegisterPage from './Pages/Register';
import { DASHBOARD_URL } from 'CommonUI/src/Config';
import 'CommonUI/src/Styles/theme.scss';
import Navigation from 'CommonUI/src/Utils/Navigation';
import VerifyEmail from './Pages/VerifyEmail';
import User from 'CommonUI/src/Utils/User';
import ResetPasswordPage from './Pages/ResetPassword';
function App(): ReactElement {
@ -23,10 +21,6 @@ function App(): ReactElement {
Navigation.setLocation(useLocation());
Navigation.setParams(useParams());
if (User.isLoggedIn()) {
Navigation.navigate(DASHBOARD_URL);
}
return (
<div className="App">
<Routes>

View File

@ -9,10 +9,17 @@ import { LOGIN_API_URL } from '../Utils/ApiPaths';
import URL from 'Common/Types/API/URL';
import { JSONObject } from 'Common/Types/JSON';
import LoginUtil from '../Utils/Login';
import UserUtil from 'CommonUI/src/Utils/User';
import Navigation from 'CommonUI/src/Utils/Navigation';
import { DASHBOARD_URL } from 'CommonUI/src/Config';
const LoginPage: FunctionComponent = () => {
const apiUrl: URL = LOGIN_API_URL;
if (UserUtil.isLoggedIn()) {
Navigation.navigate(DASHBOARD_URL);
}
return (
<div className="auth-page">
<div className="container-fluid p-0">

View File

@ -7,13 +7,19 @@ import FormFieldSchemaType from 'CommonUI/src/Components/Forms/Types/FormFieldSc
import OneUptimeLogo from 'CommonUI/src/Images/logos/OneUptimePNG/7.png';
import LoginUtil from '../Utils/Login';
import { JSONObject } from 'Common/Types/JSON';
import UserUtil from 'CommonUI/src/Utils/User';
import Navigation from 'CommonUI/src/Utils/Navigation';
import { DASHBOARD_URL } from 'CommonUI/src/Config';
import URL from 'Common/Types/API/URL';
import { SIGNUP_API_URL } from '../Utils/ApiPaths';
const RegisterPage: FunctionComponent = () => {
const apiUrl: URL = SIGNUP_API_URL;
if (UserUtil.isLoggedIn()) {
Navigation.navigate(DASHBOARD_URL);
}
return (
<div className="auth-page">
<div className="container-fluid p-0">

View File

@ -4,6 +4,7 @@ enum EmailTemplateType {
EmailVerified = 'EmailVerified.hbs',
PasswordChanged = 'PasswordChanged.hbs',
InviteMember = 'InviteMember.hbs',
EmailChanged = 'EmailChanged.hbs'
}
export default EmailTemplateType;

View File

@ -8,9 +8,13 @@ import MailService from './MailService';
import UpdateBy from '../Types/Database/UpdateBy';
import LIMIT_MAX from 'Common/Types/Database/LimitMax';
import EmailTemplateType from 'Common/Types/Email/EmailTemplateType';
import { Domain, HttpProtocol } from '../Config';
import { AccountsRoute, Domain, HttpProtocol } from '../Config';
import logger from '../Utils/Logger';
import URL from 'Common/Types/API/URL';
import EmailVerificationToken from 'Model/Models/EmailVerificationToken';
import OneUptimeDate from 'Common/Types/Date';
import EmailVerificationTokenService from './EmailVerificationTokenService';
import Route from 'Common/Types/API/Route';
export class Service extends DatabaseService<Model> {
public constructor(postgresDatabase?: PostgresDatabase) {
@ -33,10 +37,14 @@ export class Service extends DatabaseService<Model> {
}
protected override async onBeforeUpdate(updateBy: UpdateBy<Model>): Promise<OnUpdate<Model>> {
if (updateBy.data.password) {
if (updateBy.data.password || updateBy.data.email) {
const users = await this.findBy({
query: updateBy.query,
select: {
_id: true,
email: true,
},
props: {
@ -46,6 +54,7 @@ export class Service extends DatabaseService<Model> {
skip: 0
})
return { updateBy, carryForward: users };
}
return { updateBy, carryForward: [] };
@ -68,6 +77,82 @@ export class Service extends DatabaseService<Model> {
}
}
if (onUpdate && onUpdate.updateBy.data.email) {
const newUsers = await this.findBy({
query: onUpdate.updateBy.query,
select: {
_id: true,
email: true,
name: true
},
props: {
isRoot: true,
},
limit: LIMIT_MAX,
skip: 0
})
for (const user of onUpdate.carryForward) {
const newUser = newUsers.find((u) => u._id?.toString() === user._id.toString());
if (newUser && newUser.email?.toString() !== user.email.toString()) {
// password changed, send password changed mail
const generatedToken: ObjectID = ObjectID.generate();
const emailVerificationToken: EmailVerificationToken =
new EmailVerificationToken();
emailVerificationToken.userId = user?.id!;
emailVerificationToken.email = newUser?.email!;
emailVerificationToken.token = generatedToken;
emailVerificationToken.expires = OneUptimeDate.getOneDayAfter();
await EmailVerificationTokenService.create({
data: emailVerificationToken,
props: {
isRoot: true,
},
});
MailService.sendMail({
toEmail: newUser.email!,
subject: 'You have changed your email. Please verify your email.',
templateType: EmailTemplateType.EmailChanged,
vars: {
name: newUser.name!.toString(),
tokenVerifyUrl: new URL(
HttpProtocol,
Domain,
new Route(AccountsRoute.toString()).addRoute(
'/verify-email/' + generatedToken.toString()
)
).toString(),
homeUrl: new URL(HttpProtocol, Domain).toString(),
},
}).catch((err: Error) => {
logger.error(err);
});
await this.updateBy({
query: {
_id: user.id.toString()
},
data: {
isEmailVerified: false
},
props: {
isRoot: true,
ignoreHooks: true
}
});
}
}
}
return onUpdate;
}

View File

@ -47,6 +47,7 @@ const UserProfileModal: FunctionComponent<ComponentProps> = (
'jeff@example.com',
required: true,
title: 'Email',
description: 'You will have to verify your email again if you change it'
},
{
field: {

View File

@ -0,0 +1,595 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=3Dutf-8">
<meta name="viewport" content="width=3Ddevice-width">
<title>You have changed your email.</title>
<style>
/**
* IMPORTANT:
* Please read before changing anything, CSS involved in our HTML emails is
* extremely specific and written a certain way for a reason. It might not make
* sense in a normal setting but Outlook loves it this way.
*
* !!! [override] prevents Yahoo Mail breaking media queries. It must be used
* !!! at the beginning of every line of CSS inside a media query.
* !!! Do not remove.
*
* !!! div[style*="margin: 16px 0"] allows us to target a weird margin
* !!! bug in Android's email client.
* !!! Do not remove.
*
* Also, the img files are hosted on S3. Please don't break these URLs!
* The images are also versioned by date, so please update the URLs accordingly
* if you create new versions
*
***/
/**
* # Root
* - CSS resets and general styles go here.
**/
html,
body,
a,
span,
div[style*="margin: 16px 0"] {
border: 0 !important;
margin: 0 !important;
outline: 0 !important;
padding: 0 !important;
text-decoration: none !important;
}
a,
span,
td,
th {
-webkit-font-smoothing: antialiased !important;
-moz-osx-font-smoothing: grayscale !important;
}
/**
* # Delink
* - Classes for overriding clients which creates links out of things like
* emails, addresses, phone numbers, etc.
**/
span.st-Delink a {
color: #000000 !important;
text-decoration: none !important;
}
/** Modifier: preheader */
span.st-Delink.st-Delink--preheader a {
color: white !important;
text-decoration: none !important;
}
/** */
/** Modifier: title */
span.st-Delink.st-Delink--title a {
color: #000000 !important;
text-decoration: none !important;
}
/** */
/** Modifier: footer */
span.st-Delink.st-Delink--footer a {
color: #8898aa !important;
text-decoration: none !important;
}
/** */
.ii a[href] {
color: #000000;
}
/**
* # Mobile
* - This affects emails views in clients less than 600px wide.
**/
@media all and (max-width: 600px) {
/**
* # Wrapper
**/
body[override] table.st-Wrapper,
body[override] table.st-Width.st-Width--mobile {
min-width: 100% !important;
width: 100% !important;
}
/**
* # Spacer
**/
/** Modifier: gutter */
body[override] td.st-Spacer.st-Spacer--gutter {
width: 32px !important;
}
/** */
/** Modifier: kill */
body[override] td.st-Spacer.st-Spacer--kill {
width: 0 !important;
}
/** */
/** Modifier: emailEnd */
body[override] td.st-Spacer.st-Spacer--emailEnd {
height: 32px !important;
}
/** */
/**
* # Font
**/
/** Modifier: title */
body[override] td.st-Font.st-Font--title,
body[override] td.st-Font.st-Font--title span,
body[override] td.st-Font.st-Font--title a {
font-size: 28px !important;
line-height: 36px !important;
}
/** */
/** Modifier: header */
body[override] td.st-Font.st-Font--header,
body[override] td.st-Font.st-Font--header span,
body[override] td.st-Font.st-Font--header a {
font-size: 24px !important;
line-height: 32px !important;
}
/** */
/** Modifier: body */
body[override] td.st-Font.st-Font--body,
body[override] td.st-Font.st-Font--body span,
body[override] td.st-Font.st-Font--body a {
font-size: 18px !important;
line-height: 28px !important;
}
/** */
/** Modifier: caption */
body[override] td.st-Font.st-Font--caption,
body[override] td.st-Font.st-Font--caption span,
body[override] td.st-Font.st-Font--caption a {
font-size: 14px !important;
line-height: 20px !important;
}
/** */
/** Modifier: simplified */
body[override] table.st-Header.st-Header--simplified td.st-Header-logo {
width: auto !important;
}
body[override] table.st-Header.st-Header--simplified td.st-Header-spacing {
width: 0 !important;
}
/**
* # Divider
**/
body[override] table.st-Divider td.st-Spacer.st-Spacer--gutter,
body[override] tr.st-Divider td.st-Spacer.st-Spacer--gutter {
background-color: #000000;
}
/**
* # Blocks
**/
body[override] table.st-Blocks table.st-Blocks-inner {
border-radius: 0 !important;
}
body[override] table.st-Blocks table.st-Blocks-inner table.st-Blocks-item td.st-Blocks-item-cell {
display: block !important;
}
/**
* # Button
**/
body[override] table.st-Button {
margin: 0 auto !important;
width: 100% !important;
}
body[override] table.st-Button td.st-Button-area,
body[override] table.st-Button td.st-Button-area a.st-Button-link,
body[override] table.st-Button td.st-Button-area span.st-Button-internal {
height: 44px !important;
line-height: 44px !important;
font-size: 18px !important;
vertical-align: middle !important;
}
}
</style>
</head>
<body class="st-Email" bgcolor="f7f7f7"
style="border: 0; margin: 0; padding: 0; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; min-width: 100%; width: 100%;"
override="fix">
<!-- Background -->
<table class="st-Background" bgcolor="f7f7f7" border="0" cellpadding="0" cellspacing="0" width="100%"
style="border: 0; margin: 0; padding: 0;">
<tbody>
<tr>
<td style="border: 0; margin: 0; padding: 0;">
<!-- Wrapper -->
<table class="st-Wrapper" align="center" bgcolor="ffffff" border="0" cellpadding="0" cellspacing="0"
width="600"
style="border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; margin: 0 auto; min-width: 600px;">
<tbody>
<tr>
<td style="border: 0; margin: 0; padding: 0;">
<table class="st-Header st-Header--simplified st-Width st-Width--mobile" border="0" cellpadding="0"
cellspacing="0" width="600" style="min-width: 600px;">
<tbody>
<tr>
<td class="st-Spacer st-Spacer--divider" colspan="4" height="19"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td align="left" style="height:80px; border: 0; margin: 0; padding: 0;">
<div>
<a style="border: 0; margin: 0; padding: 0; text-decoration: none;" href={{homeURL}}>
<img alt="OneUptime" border="0"
style="height:70px; width:70px; border: 0; margin: 0; padding: 0; color: #000000; display: block; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Ubuntu, sans-serif; font-size: 12px; font-weight: normal;"
src="https://res.cloudinary.com/deityhub/image/upload/v1637736803/1png.png">
</a>
</div>
</td>
<td class="st-Header-spacing" width="423" style="border: 0; margin: 0; padding: 0;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr>
<td class="st-Spacer st-Spacer--divider" colspan="4" height="19"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr class="st-Divider">
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; max-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td bgcolor="#fdfdfd" colspan="2" height="1"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; max-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; max-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
</tbody>
</table>
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0"
width="600" style="min-width: 600px;">
<tbody>
<tr>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td class="st-Font st-Font--body"
style="color: #000000 !important; border:0;margin:0;padding:0; font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Ubuntu,sans-serif;font-size:16px;line-height:24px">
<h3>You have changed your email. Please verify your email.</h3>
</td>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr>
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="12"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
</tbody>
</table>
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0"
width="600" style="min-width: 600px;">
<tbody>
<tr>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td class="st-Font st-Font--body"
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;">
Next step would be to verify your email account. Can you please click on 'Verify Email' button which will help us to get your email verified.
</td>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr>
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="12"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
</tbody>
</table>
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0"
width="600" style="min-width: 600px;">
<tbody>
<tr>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td class="st-Font st-Font--body"
style="border: 0; margin: 0; padding: 0; color: #000000; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Ubuntu, sans-serif; font-size: 16px; line-height: 24px;">
<!-- Button & Modifier: fullWidth -->
<table class="st-Button st-Button--fullWidth" border="0" cellpadding="0" cellspacing="0"
width="100%">
<tbody>
<tr>
<td align="center" class="st-Button-area" height="38" valign="middle"
style="border: 0; margin: 0; padding: 0; background-color: #000000; border-radius: 5px; text-align: center;">
<a class="st-Button-link"
style="border: 0; margin: 0; padding: 0; color: #ffffff; display: block; height: 38px; text-align: center; text-decoration: none;"
href={{tokenVerifyUrl}}>
<span class="st-Button-internal"
style="border: 0; margin: 0; padding: 0; color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Ubuntu, sans-serif; font-size: 16px; font-weight: bold; height: 38px; line-height: 38px; mso-line-height-rule: exactly; text-decoration: none; vertical-align: middle; white-space: nowrap; width: 100%;">Verify Email</span>
</a>
</td>
</tr>
</tbody>
</table>
<!-- /Button & Modifier: fullWidth -->
</td>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr>
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="12"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
</tbody>
</table>
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0"
width="600" style="min-width: 600px;">
<tbody>
<tr>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td class="st-Font st-Font--body link-content"
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;">
You can also copy and paste this link: {{tokenVerifyUrl}} on any browser of your choice if the button above does not work.<br/><br/><br/>If you have not signed up for the account, 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.
</td>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr>
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="12"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
</tbody>
</table>
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0"
width="600" style="min-width: 600px;">
<tbody>
<tr>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td class="st-Font st-Font--body"
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;">
Thanks, have a great day.
</td>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr>
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="12"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
</tbody>
</table>
<table class="st-Copy st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0"
width="600" style="min-width: 600px;">
<tbody>
<tr>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td class="st-Font st-Font--body"
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;">
OneUptime Team.
</td>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr>
<td class="st-Spacer st-Spacer--stacked" colspan="3" height="12"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
</tbody>
</table>
<table class="st-Footer st-Width st-Width--mobile" border="0" cellpadding="0" cellspacing="0"
width="600" style="min-width: 600px;">
<tbody>
<tr>
<td class="st-Spacer st-Spacer--divider" colspan="3" height="20"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; max-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr class="st-Divider">
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; max-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td bgcolor="#fdfdfd" colspan="2" height="1"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; max-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; max-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr>
<td class="st-Spacer st-Spacer--divider" colspan="3" height="31"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
<td class="st-Font st-Font--caption"
style="border: 0; margin: 0;padding: 0; color: #8898aa; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Ubuntu, sans-serif; font-size: 12px; line-height: 16px;">
<span class="st-Delink st-Delink--footer"
style="border: 0; margin: 0; padding: 0; color: #8898aa; text-decoration: none;">
© {{year}} OneUptime Inc.
</span>
</td>
<td class="st-Spacer st-Spacer--gutter"
style="border: 0; margin:0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;"
width="64">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
<tr>
<td class="st-Spacer st-Spacer--emailEnd" colspan="3" height="64"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler"></div>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
<!-- /Wrapper -->
</td>
</tr>
<tr>
<td class="st-Spacer st-Spacer--emailEnd" height="64"
style="border: 0; margin: 0; padding: 0; font-size: 1px; line-height: 1px; mso-line-height-rule: exactly;">
<div class="st-Spacer st-Spacer--filler">&nbsp;</div>
</td>
</tr>
</tbody>
</table>
<!-- /Background -->
</body>
</html>