add workers to queue

This commit is contained in:
Simon Larsen 2023-03-02 19:36:06 +00:00
parent db2f76e79f
commit 7151609189
No known key found for this signature in database
GPG key ID: AB45983AA9C81CDE
12 changed files with 664 additions and 614 deletions

View file

@ -4,7 +4,7 @@ import { RedisHostname, RedisPassword, RedisPort } from '../Config';
export enum QueueName {
Workflow = 'Workflow',
Worker = 'Worker'
Worker = 'Worker',
}
export type QueueJob = Job;

View file

@ -24,9 +24,9 @@ const StatusPageDelete: FunctionComponent<PageComponentProps> = (
): ReactElement => {
const modelId: ObjectID = Navigation.getLastParamAsObjectID(1);
const [cnameModalText, setCnameModalText] = useState<string>('');
const [showSslProvisioningModal, setShowSslProvisioningModal] = useState<boolean>(false);
const [showSslProvisioningModal, setShowSslProvisioningModal] =
useState<boolean>(false);
return (
<Page
@ -67,7 +67,8 @@ const StatusPageDelete: FunctionComponent<PageComponentProps> = (
<ModelTable<StatusPageDomain>
modelType={StatusPageDomain}
query={{
projectId: DashboardNavigation.getProjectId()?.toString(),
projectId:
DashboardNavigation.getProjectId()?.toString(),
statusPageId: modelId,
}}
name="Status Page > Domains"
@ -83,7 +84,9 @@ const StatusPageDelete: FunctionComponent<PageComponentProps> = (
item: StatusPageDomain
): Promise<StatusPageDomain> => {
if (!props.currentProject || !props.currentProject.id) {
throw new BadDataException('Project ID cannot be null');
throw new BadDataException(
'Project ID cannot be null'
);
}
item.statusPageId = modelId;
item.projectId = props.currentProject.id;
@ -121,7 +124,10 @@ const StatusPageDelete: FunctionComponent<PageComponentProps> = (
buttonStyleType: ButtonStyleType.SUCCESS_OUTLINE,
icon: IconProp.Check,
isVisible: (item: JSONObject): boolean => {
if (item['isCnameVerified'] && !item['isSslProvisioned']) {
if (
item['isCnameVerified'] &&
!item['isSslProvisioned']
) {
return true;
}
@ -133,7 +139,7 @@ const StatusPageDelete: FunctionComponent<PageComponentProps> = (
onError: (err: Error) => void
) => {
try {
setShowSslProvisioningModal(true)
setShowSslProvisioningModal(true);
onCompleteAction();
} catch (err) {
@ -214,49 +220,55 @@ const StatusPageDelete: FunctionComponent<PageComponentProps> = (
]}
/>
{cnameModalText && <ConfirmModal
title={`Add CNAME`}
description={ <div>
<span>
Please add CNAME record to your domain. Details of
the CNAME records are:
</span>
<br />
<br />
<span>
<b>Record Type: </b> CNAME
</span>
<br />
<span>
<b>Name: </b>
{cnameModalText}
</span>
<br />
<span>
<b>Content: </b>
{StatusPageCNameRecord}
</span>
<br />
<br />
<span>
Once you have done this, it should take 24 hours to automatically verify.
</span>
</div>}
submitButtonText={'Close'}
onSubmit={() => {
return setCnameModalText('')
}}
/>}
{showSslProvisioningModal && <ConfirmModal
title={`Provision SSL`}
description={`This is an automatic process and takes around 24 hours to complete. If you do not see your SSL provisioned in 24 hours. Please contact support@oneuptime.com`}
submitButtonText={'Close'}
onSubmit={() => {
return setShowSslProvisioningModal(false)
}}
/>}
{cnameModalText && (
<ConfirmModal
title={`Add CNAME`}
description={
<div>
<span>
Please add CNAME record to your domain.
Details of the CNAME records are:
</span>
<br />
<br />
<span>
<b>Record Type: </b> CNAME
</span>
<br />
<span>
<b>Name: </b>
{cnameModalText}
</span>
<br />
<span>
<b>Content: </b>
{StatusPageCNameRecord}
</span>
<br />
<br />
<span>
Once you have done this, it should take 24
hours to automatically verify.
</span>
</div>
}
submitButtonText={'Close'}
onSubmit={() => {
return setCnameModalText('');
}}
/>
)}
{showSslProvisioningModal && (
<ConfirmModal
title={`Provision SSL`}
description={`This is an automatic process and takes around 24 hours to complete. If you do not see your SSL provisioned in 24 hours. Please contact support@oneuptime.com`}
submitButtonText={'Close'}
onSubmit={() => {
return setShowSslProvisioningModal(false);
}}
/>
)}
</>
</Page>
);

View file

@ -43,17 +43,16 @@ const init: Function = async (): Promise<void> => {
// connect redis
await Redis.connect();
// Job process.
QueueWorker.getWorker(
// Job process.
QueueWorker.getWorker(
QueueName.Worker,
async (job: QueueJob) => {
const name: string = job.name;
const funcToRun = JobDictonary.getJobFunction(name);
const name: string = job.name;
const funcToRun: Function = JobDictonary.getJobFunction(name);
await funcToRun();
},
{ concurrency: 10 }
);
} catch (err) {
logger.error('App Init Failed:');
logger.error(err);

View file

@ -14,118 +14,123 @@ import EmailTemplateType from 'Common/Types/Email/EmailTemplateType';
import logger from 'CommonServer/Utils/Logger';
import StatusPageService from 'CommonServer/Services/StatusPageService';
RunCron('Announcement:SendEmailToSubscribers', { schedule: EVERY_MINUTE, runOnStartup: false}, async () => {
// get all scheduled events of all the projects.
const announcements: Array<StatusPageAnnouncement> =
await StatusPageAnnouncementService.findBy({
query: {
isStatusPageSubscribersNotified: false,
showAnnouncementAt: QueryHelper.lessThan(
OneUptimeDate.getCurrentDate()
),
},
props: {
isRoot: true,
},
limit: LIMIT_MAX,
skip: 0,
select: {
_id: true,
title: true,
description: true,
},
populate: {
statusPages: {
_id: true,
name: true,
pageTitle: true,
isPublicStatusPage: true,
logoFileId: true,
RunCron(
'Announcement:SendEmailToSubscribers',
{ schedule: EVERY_MINUTE, runOnStartup: false },
async () => {
// get all scheduled events of all the projects.
const announcements: Array<StatusPageAnnouncement> =
await StatusPageAnnouncementService.findBy({
query: {
isStatusPageSubscribersNotified: false,
showAnnouncementAt: QueryHelper.lessThan(
OneUptimeDate.getCurrentDate()
),
},
},
});
props: {
isRoot: true,
},
limit: LIMIT_MAX,
skip: 0,
select: {
_id: true,
title: true,
description: true,
},
populate: {
statusPages: {
_id: true,
name: true,
pageTitle: true,
isPublicStatusPage: true,
logoFileId: true,
},
},
});
// change their state to Ongoing.
// change their state to Ongoing.
for (const announcement of announcements) {
if (!announcement.statusPages) {
continue;
}
await StatusPageAnnouncementService.updateOneById({
id: announcement.id!,
data: {
isStatusPageSubscribersNotified: true,
},
props: {
isRoot: true,
ignoreHooks: true,
},
});
for (const statuspage of announcement.statusPages) {
if (!statuspage.id) {
for (const announcement of announcements) {
if (!announcement.statusPages) {
continue;
}
const subscribers: Array<StatusPageSubscriber> =
await StatusPageSubscriberService.getSubscribersByStatusPage(
statuspage.id!,
{
isRoot: true,
ignoreHooks: true,
}
);
await StatusPageAnnouncementService.updateOneById({
id: announcement.id!,
data: {
isStatusPageSubscribersNotified: true,
},
props: {
isRoot: true,
ignoreHooks: true,
},
});
const statusPageURL: string =
await StatusPageService.getStatusPageURL(statuspage.id);
const statusPageName: string =
statuspage.pageTitle || statuspage.name || 'Status Page';
// Send email to Email subscribers.
for (const subscriber of subscribers) {
if (!subscriber._id) {
for (const statuspage of announcement.statusPages) {
if (!statuspage.id) {
continue;
}
if (subscriber.subscriberEmail) {
// send email here.
const subscribers: Array<StatusPageSubscriber> =
await StatusPageSubscriberService.getSubscribersByStatusPage(
statuspage.id!,
{
isRoot: true,
ignoreHooks: true,
}
);
MailService.sendMail({
toEmail: subscriber.subscriberEmail,
templateType:
EmailTemplateType.SubscriberAnnouncementCreated,
vars: {
statusPageName: statusPageName,
statusPageUrl: statusPageURL,
logoUrl: statuspage.logoFileId
? new URL(HttpProtocol, Domain)
.addRoute(FileRoute)
.addRoute(
'/image/' + statuspage.logoFileId
)
.toString()
: '',
isPublicStatusPage: statuspage.isPublicStatusPage
? 'true'
: 'false',
announcementTitle: announcement.title || '',
announcementDescription:
announcement.description || '',
unsubscribeUrl: new URL(HttpProtocol, Domain)
.addRoute(
'/api/status-page-subscriber/unsubscribe/' +
subscriber._id.toString()
)
.toString(),
},
subject: statusPageName + ' - New Announcement',
}).catch((err: Error) => {
logger.error(err);
});
const statusPageURL: string =
await StatusPageService.getStatusPageURL(statuspage.id);
const statusPageName: string =
statuspage.pageTitle || statuspage.name || 'Status Page';
// Send email to Email subscribers.
for (const subscriber of subscribers) {
if (!subscriber._id) {
continue;
}
if (subscriber.subscriberEmail) {
// send email here.
MailService.sendMail({
toEmail: subscriber.subscriberEmail,
templateType:
EmailTemplateType.SubscriberAnnouncementCreated,
vars: {
statusPageName: statusPageName,
statusPageUrl: statusPageURL,
logoUrl: statuspage.logoFileId
? new URL(HttpProtocol, Domain)
.addRoute(FileRoute)
.addRoute(
'/image/' + statuspage.logoFileId
)
.toString()
: '',
isPublicStatusPage:
statuspage.isPublicStatusPage
? 'true'
: 'false',
announcementTitle: announcement.title || '',
announcementDescription:
announcement.description || '',
unsubscribeUrl: new URL(HttpProtocol, Domain)
.addRoute(
'/api/status-page-subscriber/unsubscribe/' +
subscriber._id.toString()
)
.toString(),
},
subject: statusPageName + ' - New Announcement',
}).catch((err: Error) => {
logger.error(err);
});
}
}
}
}
}
});
);

View file

@ -20,186 +20,196 @@ import StatusPage from 'Model/Models/StatusPage';
import ObjectID from 'Common/Types/ObjectID';
import Monitor from 'Model/Models/Monitor';
RunCron('Incident:SendEmailToSubscribers', { schedule: EVERY_MINUTE, runOnStartup: false}, async () => {
// get all scheduled events of all the projects.
const incidents: Array<Incident> = await IncidentService.findBy({
query: {
isStatusPageSubscribersNotifiedOnIncidentCreated: false,
createdAt: QueryHelper.lessThan(OneUptimeDate.getCurrentDate()),
},
props: {
isRoot: true,
},
limit: LIMIT_MAX,
skip: 0,
select: {
_id: true,
title: true,
description: true,
},
populate: {
monitors: {
_id: true,
},
incidentSeverity: {
name: true,
},
},
});
for (const incident of incidents) {
if (!incident.monitors || incident.monitors.length === 0) {
continue;
}
await IncidentService.updateOneById({
id: incident.id!,
data: {
isStatusPageSubscribersNotifiedOnIncidentCreated: true,
RunCron(
'Incident:SendEmailToSubscribers',
{ schedule: EVERY_MINUTE, runOnStartup: false },
async () => {
// get all scheduled events of all the projects.
const incidents: Array<Incident> = await IncidentService.findBy({
query: {
isStatusPageSubscribersNotifiedOnIncidentCreated: false,
createdAt: QueryHelper.lessThan(OneUptimeDate.getCurrentDate()),
},
props: {
isRoot: true,
ignoreHooks: true,
},
limit: LIMIT_MAX,
skip: 0,
select: {
_id: true,
title: true,
description: true,
},
populate: {
monitors: {
_id: true,
},
incidentSeverity: {
name: true,
},
},
});
// get status page resources from monitors.
for (const incident of incidents) {
if (!incident.monitors || incident.monitors.length === 0) {
continue;
}
const sattusPageResources: Array<StatusPageResource> =
await StatusPageResourceService.findBy({
query: {
monitorId: QueryHelper.in(
incident.monitors
.filter((m: Monitor) => {
return m._id;
})
.map((m: Monitor) => {
return new ObjectID(m._id!);
})
),
await IncidentService.updateOneById({
id: incident.id!,
data: {
isStatusPageSubscribersNotifiedOnIncidentCreated: true,
},
props: {
isRoot: true,
ignoreHooks: true,
},
skip: 0,
limit: LIMIT_PER_PROJECT,
select: {
_id: true,
displayName: true,
statusPageId: true,
},
});
const statusPageToResources: Dictionary<Array<StatusPageResource>> = {};
// get status page resources from monitors.
for (const resource of sattusPageResources) {
if (!resource.statusPageId) {
continue;
}
if (!statusPageToResources[resource.statusPageId?.toString()]) {
statusPageToResources[resource.statusPageId?.toString()] = [];
}
statusPageToResources[resource.statusPageId?.toString()]?.push(
resource
);
}
const statusPages: Array<StatusPage> = await StatusPageService.findBy({
query: {
_id: QueryHelper.in(
Object.keys(statusPageToResources).map((i: string) => {
return new ObjectID(i);
})
),
},
props: {
isRoot: true,
ignoreHooks: true,
},
skip: 0,
limit: LIMIT_PER_PROJECT,
select: {
_id: true,
name: true,
pageTitle: true,
isPublicStatusPage: true,
logoFileId: true,
},
});
for (const statuspage of statusPages) {
if (!statuspage.id) {
continue;
}
const subscribers: Array<StatusPageSubscriber> =
await StatusPageSubscriberService.getSubscribersByStatusPage(
statuspage.id!,
{
const sattusPageResources: Array<StatusPageResource> =
await StatusPageResourceService.findBy({
query: {
monitorId: QueryHelper.in(
incident.monitors
.filter((m: Monitor) => {
return m._id;
})
.map((m: Monitor) => {
return new ObjectID(m._id!);
})
),
},
props: {
isRoot: true,
ignoreHooks: true,
}
);
},
skip: 0,
limit: LIMIT_PER_PROJECT,
select: {
_id: true,
displayName: true,
statusPageId: true,
},
});
const statusPageURL: string =
await StatusPageService.getStatusPageURL(statuspage.id);
const statusPageName: string =
statuspage.pageTitle || statuspage.name || 'Status Page';
const statusPageToResources: Dictionary<Array<StatusPageResource>> =
{};
// Send email to Email subscribers.
for (const subscriber of subscribers) {
if (!subscriber._id) {
for (const resource of sattusPageResources) {
if (!resource.statusPageId) {
continue;
}
if (subscriber.subscriberEmail) {
// send email here.
if (!statusPageToResources[resource.statusPageId?.toString()]) {
statusPageToResources[resource.statusPageId?.toString()] =
[];
}
MailService.sendMail({
toEmail: subscriber.subscriberEmail,
templateType:
EmailTemplateType.SubscriberIncidentCreated,
vars: {
statusPageName: statusPageName,
statusPageUrl: statusPageURL,
logoUrl: statuspage.logoFileId
? new URL(HttpProtocol, Domain)
.addRoute(FileRoute)
.addRoute(
'/image/' + statuspage.logoFileId
)
.toString()
: '',
isPublicStatusPage: statuspage.isPublicStatusPage
? 'true'
: 'false',
resourcesAffected:
statusPageToResources[statuspage._id!]
?.map((r: StatusPageResource) => {
return r.displayName;
})
.join(', ') || 'None',
incidentSeverity:
incident.incidentSeverity?.name || ' - ',
incidentTitle: incident.title || '',
incidentDescription: incident.description || '',
unsubscribeUrl: new URL(HttpProtocol, Domain)
.addRoute(
'/api/status-page-subscriber/unsubscribe/' +
subscriber._id.toString()
)
.toString(),
},
subject: statusPageName + ' - New Incident',
}).catch((err: Error) => {
logger.error(err);
});
statusPageToResources[resource.statusPageId?.toString()]?.push(
resource
);
}
const statusPages: Array<StatusPage> =
await StatusPageService.findBy({
query: {
_id: QueryHelper.in(
Object.keys(statusPageToResources).map(
(i: string) => {
return new ObjectID(i);
}
)
),
},
props: {
isRoot: true,
ignoreHooks: true,
},
skip: 0,
limit: LIMIT_PER_PROJECT,
select: {
_id: true,
name: true,
pageTitle: true,
isPublicStatusPage: true,
logoFileId: true,
},
});
for (const statuspage of statusPages) {
if (!statuspage.id) {
continue;
}
const subscribers: Array<StatusPageSubscriber> =
await StatusPageSubscriberService.getSubscribersByStatusPage(
statuspage.id!,
{
isRoot: true,
ignoreHooks: true,
}
);
const statusPageURL: string =
await StatusPageService.getStatusPageURL(statuspage.id);
const statusPageName: string =
statuspage.pageTitle || statuspage.name || 'Status Page';
// Send email to Email subscribers.
for (const subscriber of subscribers) {
if (!subscriber._id) {
continue;
}
if (subscriber.subscriberEmail) {
// send email here.
MailService.sendMail({
toEmail: subscriber.subscriberEmail,
templateType:
EmailTemplateType.SubscriberIncidentCreated,
vars: {
statusPageName: statusPageName,
statusPageUrl: statusPageURL,
logoUrl: statuspage.logoFileId
? new URL(HttpProtocol, Domain)
.addRoute(FileRoute)
.addRoute(
'/image/' + statuspage.logoFileId
)
.toString()
: '',
isPublicStatusPage:
statuspage.isPublicStatusPage
? 'true'
: 'false',
resourcesAffected:
statusPageToResources[statuspage._id!]
?.map((r: StatusPageResource) => {
return r.displayName;
})
.join(', ') || 'None',
incidentSeverity:
incident.incidentSeverity?.name || ' - ',
incidentTitle: incident.title || '',
incidentDescription: incident.description || '',
unsubscribeUrl: new URL(HttpProtocol, Domain)
.addRoute(
'/api/status-page-subscriber/unsubscribe/' +
subscriber._id.toString()
)
.toString(),
},
subject: statusPageName + ' - New Incident',
}).catch((err: Error) => {
logger.error(err);
});
}
}
}
}
}
});
);

View file

@ -10,7 +10,7 @@ import Sleep from 'Common/Types/Sleep';
RunCron(
'PaymentProvider:CheckSubscriptionStatus',
{ schedule: IsDevelopment ? EVERY_MINUTE : EVERY_DAY, runOnStartup: true } ,
{ schedule: IsDevelopment ? EVERY_MINUTE : EVERY_DAY, runOnStartup: true },
async () => {
// get all projects.
if (!IsBillingEnabled) {

View file

@ -8,66 +8,75 @@ import ScheduledMaintenanceState from 'Model/Models/ScheduledMaintenanceState';
import ScheduledMaintenanceStateService from 'CommonServer/Services/ScheduledMaintenanceStateService';
import RunCron from '../../Utils/Cron';
RunCron('ScheduledMaintenance:ChangeStateToOngoing', { schedule: EVERY_MINUTE, runOnStartup: false}, async () => {
// get all scheduled events of all the projects.
const events: Array<ScheduledMaintenance> =
await ScheduledMaintenanceService.findBy({
query: {
currentScheduledMaintenanceState: {
isScheduledState: true,
} as any,
startsAt: QueryHelper.lessThan(OneUptimeDate.getCurrentDate()),
},
props: {
isRoot: true,
},
limit: LIMIT_MAX,
skip: 0,
select: {
_id: true,
projectId: true,
changeMonitorStatusToId: true,
},
populate: {
monitors: {
_id: true,
},
},
});
// change their state to Ongoing.
for (const event of events) {
const scheduledMaintenanceState: ScheduledMaintenanceState | null =
await ScheduledMaintenanceStateService.findOneBy({
RunCron(
'ScheduledMaintenance:ChangeStateToOngoing',
{ schedule: EVERY_MINUTE, runOnStartup: false },
async () => {
// get all scheduled events of all the projects.
const events: Array<ScheduledMaintenance> =
await ScheduledMaintenanceService.findBy({
query: {
projectId: event.projectId!,
isOngoingState: true,
},
select: {
_id: true,
currentScheduledMaintenanceState: {
isScheduledState: true,
} as any,
startsAt: QueryHelper.lessThan(
OneUptimeDate.getCurrentDate()
),
},
props: {
isRoot: true,
},
limit: LIMIT_MAX,
skip: 0,
select: {
_id: true,
projectId: true,
changeMonitorStatusToId: true,
},
populate: {
monitors: {
_id: true,
},
},
});
if (!scheduledMaintenanceState || !scheduledMaintenanceState.id) {
continue;
}
// change their state to Ongoing.
await ScheduledMaintenanceService.changeScheduledMaintenanceState(
event.projectId!,
event.id!,
scheduledMaintenanceState.id,
{
isRoot: true,
for (const event of events) {
const scheduledMaintenanceState: ScheduledMaintenanceState | null =
await ScheduledMaintenanceStateService.findOneBy({
query: {
projectId: event.projectId!,
isOngoingState: true,
},
select: {
_id: true,
},
props: {
isRoot: true,
},
});
if (!scheduledMaintenanceState || !scheduledMaintenanceState.id) {
continue;
}
);
// change attached monitor states.
await ScheduledMaintenanceService.changeAttachedMonitorStates(event, {
isRoot: true,
});
await ScheduledMaintenanceService.changeScheduledMaintenanceState(
event.projectId!,
event.id!,
scheduledMaintenanceState.id,
{
isRoot: true,
}
);
// change attached monitor states.
await ScheduledMaintenanceService.changeAttachedMonitorStates(
event,
{
isRoot: true,
}
);
}
}
});
);

View file

@ -20,249 +20,266 @@ import ScheduledMaintenance from 'Model/Models/ScheduledMaintenance';
import ScheduledMaintenanceService from 'CommonServer/Services/ScheduledMaintenanceService';
import Monitor from 'Model/Models/Monitor';
RunCron('Incident:SendEmailToSubscribers', { schedule: EVERY_MINUTE, runOnStartup: false}, async () => {
// get all scheduled events of all the projects.
const scheduledEvents: Array<ScheduledMaintenance> =
await ScheduledMaintenanceService.findBy({
query: {
isStatusPageSubscribersNotifiedOnEventScheduled: false,
createdAt: QueryHelper.lessThan(OneUptimeDate.getCurrentDate()),
},
props: {
isRoot: true,
},
limit: LIMIT_MAX,
skip: 0,
select: {
_id: true,
title: true,
description: true,
startsAt: true,
},
populate: {
monitors: {
_id: true,
},
},
});
const ongoingEvents: Array<ScheduledMaintenance> =
await ScheduledMaintenanceService.findBy({
query: {
isStatusPageSubscribersNotifiedOnEventOngoing: false,
startsAt: QueryHelper.lessThan(OneUptimeDate.getCurrentDate()),
},
props: {
isRoot: true,
},
limit: LIMIT_MAX,
skip: 0,
select: {
_id: true,
title: true,
description: true,
startsAt: true,
},
populate: {
monitors: {
_id: true,
},
},
});
const scheduledEventsIds: Array<string | undefined> = scheduledEvents.map(
(i: ScheduledMaintenance) => {
return i._id?.toString();
}
);
const ongoingEventIds: Array<string | undefined> = ongoingEvents.map(
(i: ScheduledMaintenance) => {
return i._id?.toString();
}
);
const totalEvents: Array<ScheduledMaintenance> = [
...ongoingEvents,
...scheduledEvents,
];
for (const event of totalEvents) {
if (!event.monitors || event.monitors.length === 0) {
continue;
}
let isOngoing: boolean = false;
if (ongoingEventIds.includes(event._id?.toString())) {
isOngoing = true;
await ScheduledMaintenanceService.updateOneById({
id: event.id!,
data: {
isStatusPageSubscribersNotifiedOnEventOngoing: true,
},
props: {
isRoot: true,
ignoreHooks: true,
},
});
}
if (scheduledEventsIds.includes(event._id?.toString())) {
await ScheduledMaintenanceService.updateOneById({
id: event.id!,
data: {
isStatusPageSubscribersNotifiedOnEventScheduled: true,
},
props: {
isRoot: true,
ignoreHooks: true,
},
});
}
// get status page resources from monitors.
const sattusPageResources: Array<StatusPageResource> =
await StatusPageResourceService.findBy({
RunCron(
'Incident:SendEmailToSubscribers',
{ schedule: EVERY_MINUTE, runOnStartup: false },
async () => {
// get all scheduled events of all the projects.
const scheduledEvents: Array<ScheduledMaintenance> =
await ScheduledMaintenanceService.findBy({
query: {
monitorId: QueryHelper.in(
event.monitors
.filter((m: Monitor) => {
return m._id;
})
.map((m: Monitor) => {
return new ObjectID(m._id!);
})
isStatusPageSubscribersNotifiedOnEventScheduled: false,
createdAt: QueryHelper.lessThan(
OneUptimeDate.getCurrentDate()
),
},
props: {
isRoot: true,
ignoreHooks: true,
},
limit: LIMIT_MAX,
skip: 0,
limit: LIMIT_PER_PROJECT,
select: {
_id: true,
displayName: true,
statusPageId: true,
title: true,
description: true,
startsAt: true,
},
populate: {
monitors: {
_id: true,
},
},
});
const statusPageToResources: Dictionary<Array<StatusPageResource>> = {};
const ongoingEvents: Array<ScheduledMaintenance> =
await ScheduledMaintenanceService.findBy({
query: {
isStatusPageSubscribersNotifiedOnEventOngoing: false,
startsAt: QueryHelper.lessThan(
OneUptimeDate.getCurrentDate()
),
},
props: {
isRoot: true,
},
limit: LIMIT_MAX,
skip: 0,
select: {
_id: true,
title: true,
description: true,
startsAt: true,
},
populate: {
monitors: {
_id: true,
},
},
});
for (const resource of sattusPageResources) {
if (!resource.statusPageId) {
const scheduledEventsIds: Array<string | undefined> =
scheduledEvents.map((i: ScheduledMaintenance) => {
return i._id?.toString();
});
const ongoingEventIds: Array<string | undefined> = ongoingEvents.map(
(i: ScheduledMaintenance) => {
return i._id?.toString();
}
);
const totalEvents: Array<ScheduledMaintenance> = [
...ongoingEvents,
...scheduledEvents,
];
for (const event of totalEvents) {
if (!event.monitors || event.monitors.length === 0) {
continue;
}
if (!statusPageToResources[resource.statusPageId?.toString()]) {
statusPageToResources[resource.statusPageId?.toString()] = [];
}
let isOngoing: boolean = false;
statusPageToResources[resource.statusPageId?.toString()]?.push(
resource
);
}
const statusPages: Array<StatusPage> = await StatusPageService.findBy({
query: {
_id: QueryHelper.in(
Object.keys(statusPageToResources).map((i: string) => {
return new ObjectID(i);
})
),
},
props: {
isRoot: true,
ignoreHooks: true,
},
skip: 0,
limit: LIMIT_PER_PROJECT,
select: {
_id: true,
name: true,
pageTitle: true,
isPublicStatusPage: true,
logoFileId: true,
},
});
for (const statuspage of statusPages) {
if (!statuspage.id) {
continue;
}
const subscribers: Array<StatusPageSubscriber> =
await StatusPageSubscriberService.getSubscribersByStatusPage(
statuspage.id!,
{
if (ongoingEventIds.includes(event._id?.toString())) {
isOngoing = true;
await ScheduledMaintenanceService.updateOneById({
id: event.id!,
data: {
isStatusPageSubscribersNotifiedOnEventOngoing: true,
},
props: {
isRoot: true,
ignoreHooks: true,
}
);
},
});
}
const statusPageURL: string =
await StatusPageService.getStatusPageURL(statuspage.id);
if (scheduledEventsIds.includes(event._id?.toString())) {
await ScheduledMaintenanceService.updateOneById({
id: event.id!,
data: {
isStatusPageSubscribersNotifiedOnEventScheduled: true,
},
props: {
isRoot: true,
ignoreHooks: true,
},
});
}
const statusPageName: string =
statuspage.pageTitle || statuspage.name || 'Status Page';
// get status page resources from monitors.
// Send email to Email subscribers.
const sattusPageResources: Array<StatusPageResource> =
await StatusPageResourceService.findBy({
query: {
monitorId: QueryHelper.in(
event.monitors
.filter((m: Monitor) => {
return m._id;
})
.map((m: Monitor) => {
return new ObjectID(m._id!);
})
),
},
props: {
isRoot: true,
ignoreHooks: true,
},
skip: 0,
limit: LIMIT_PER_PROJECT,
select: {
_id: true,
displayName: true,
statusPageId: true,
},
});
for (const subscriber of subscribers) {
if (!subscriber._id) {
const statusPageToResources: Dictionary<Array<StatusPageResource>> =
{};
for (const resource of sattusPageResources) {
if (!resource.statusPageId) {
continue;
}
if (subscriber.subscriberEmail) {
// send email here.
if (!statusPageToResources[resource.statusPageId?.toString()]) {
statusPageToResources[resource.statusPageId?.toString()] =
[];
}
MailService.sendMail({
toEmail: subscriber.subscriberEmail,
templateType:
EmailTemplateType.SubscriberScheduledMaintenanceEventCreated,
vars: {
statusPageName: statusPageName,
statusPageUrl: statusPageURL,
logoUrl: statuspage.logoFileId
? new URL(HttpProtocol, Domain)
.addRoute(FileRoute)
statusPageToResources[resource.statusPageId?.toString()]?.push(
resource
);
}
const statusPages: Array<StatusPage> =
await StatusPageService.findBy({
query: {
_id: QueryHelper.in(
Object.keys(statusPageToResources).map(
(i: string) => {
return new ObjectID(i);
}
)
),
},
props: {
isRoot: true,
ignoreHooks: true,
},
skip: 0,
limit: LIMIT_PER_PROJECT,
select: {
_id: true,
name: true,
pageTitle: true,
isPublicStatusPage: true,
logoFileId: true,
},
});
for (const statuspage of statusPages) {
if (!statuspage.id) {
continue;
}
const subscribers: Array<StatusPageSubscriber> =
await StatusPageSubscriberService.getSubscribersByStatusPage(
statuspage.id!,
{
isRoot: true,
ignoreHooks: true,
}
);
const statusPageURL: string =
await StatusPageService.getStatusPageURL(statuspage.id);
const statusPageName: string =
statuspage.pageTitle || statuspage.name || 'Status Page';
// Send email to Email subscribers.
for (const subscriber of subscribers) {
if (!subscriber._id) {
continue;
}
if (subscriber.subscriberEmail) {
// send email here.
MailService.sendMail({
toEmail: subscriber.subscriberEmail,
templateType:
EmailTemplateType.SubscriberScheduledMaintenanceEventCreated,
vars: {
statusPageName: statusPageName,
statusPageUrl: statusPageURL,
logoUrl: statuspage.logoFileId
? new URL(HttpProtocol, Domain)
.addRoute(FileRoute)
.addRoute(
'/image/' + statuspage.logoFileId
)
.toString()
: '',
isPublicStatusPage:
statuspage.isPublicStatusPage
? 'true'
: 'false',
resourcesAffected:
statusPageToResources[statuspage._id!]
?.map((r: StatusPageResource) => {
return r.displayName;
})
.join(', ') || 'None',
eventStatus: isOngoing
? 'Ongoing'
: 'Scheduled',
scheduledAt:
OneUptimeDate.getDateAsFormattedString(
event.startsAt!
),
eventTitle: event.title || '',
eventDescription: event.description || '',
unsubscribeUrl: new URL(HttpProtocol, Domain)
.addRoute(
'/image/' + statuspage.logoFileId
'/api/status-page-subscriber/unsubscribe/' +
subscriber._id.toString()
)
.toString()
: '',
isPublicStatusPage: statuspage.isPublicStatusPage
? 'true'
: 'false',
resourcesAffected:
statusPageToResources[statuspage._id!]
?.map((r: StatusPageResource) => {
return r.displayName;
})
.join(', ') || 'None',
eventStatus: isOngoing ? 'Ongoing' : 'Scheduled',
scheduledAt: OneUptimeDate.getDateAsFormattedString(
event.startsAt!
),
eventTitle: event.title || '',
eventDescription: event.description || '',
unsubscribeUrl: new URL(HttpProtocol, Domain)
.addRoute(
'/api/status-page-subscriber/unsubscribe/' +
subscriber._id.toString()
)
.toString(),
},
subject:
statusPageName +
` - ${isOngoing ? 'Ongoing' : 'Scheduled'
} Maintenance Event`,
}).catch((err: Error) => {
logger.error(err);
});
.toString(),
},
subject:
statusPageName +
` - ${
isOngoing ? 'Ongoing' : 'Scheduled'
} Maintenance Event`,
}).catch((err: Error) => {
logger.error(err);
});
}
}
}
}
}
});
);

View file

@ -523,19 +523,11 @@ const checkCnameValidation: Function = async (
try {
const result: AxiosResponse = await axios.get(
'http://' +
fulldomain +
'/status-page-api/cname-verification/' +
token
fulldomain +
'/status-page-api/cname-verification/' +
token
);
console.log("CNAME VALIDFATION");
console.log(result);
console.log('http://' +
fulldomain +
'/status-page-api/cname-verification/' +
token)
if (result.status === 200) {
return true;
}
@ -555,9 +547,9 @@ const isSslProvisioned: Function = async (
try {
const result: AxiosResponse = await axios.get(
'https://' +
fulldomain +
'/status-page-api/cname-verification/' +
token
fulldomain +
'/status-page-api/cname-verification/' +
token
);
if (result.status === 200) {

View file

@ -1,23 +1,35 @@
import JobDictonary from './JobDictionary';
import Queue, { QueueName } from 'CommonServer/Infrastructure/Queue';
import logger from 'CommonServer/Utils/Logger';
const RunCron: Function = (
jobName: string,
options: {
schedule: string,
runOnStartup: boolean
options: {
schedule: string;
runOnStartup: boolean;
},
runFunction: Function
): void => {
JobDictonary.setJobFunction(jobName, runFunction);
Queue.addJob(QueueName.Worker, jobName, jobName, {}, {
scheduleAt: options.schedule,
Queue.addJob(
QueueName.Worker,
jobName,
jobName,
{},
{
scheduleAt: options.schedule,
}
).catch((err: Error) => {
return logger.error(err);
});
if(options.runOnStartup){
Queue.addJob(QueueName.Worker, jobName, jobName, {});
if (options.runOnStartup) {
Queue.addJob(QueueName.Worker, jobName, jobName, {}).catch(
(err: Error) => {
return logger.error(err);
}
);
}
};

View file

@ -1,20 +1,18 @@
import Dictionary from "Common/Types/Dictionary";
import BadDataException from "Common/Types/Exception/BadDataException";
import Dictionary from 'Common/Types/Dictionary';
import BadDataException from 'Common/Types/Exception/BadDataException';
export default class JobDictonary {
private static dictionary : Dictionary<Function> = {};
private static dictionary: Dictionary<Function> = {};
public static getJobFunction(name: string): Function {
if(this.dictionary[name]){
if (this.dictionary[name]) {
return this.dictionary[name] as Function;
}
throw new BadDataException("No job found with name: "+name);
throw new BadDataException('No job found with name: ' + name);
}
public static setJobFunction(name: string, job: Function): void {
this.dictionary[name] = job;
}
}
}

View file

@ -29,14 +29,12 @@ app.get(
(req: ExpressRequest, res: ExpressResponse) => {
res.sendFile(
__dirname +
'/Docs/ComponentDocumentation/' +
req.params['componentName']
'/Docs/ComponentDocumentation/' +
req.params['componentName']
);
}
);
const init: Function = async (): Promise<void> => {
try {
// connect to the database.
@ -52,7 +50,6 @@ const init: Function = async (): Promise<void> => {
// connect redis
await Redis.connect();
// Job process.
QueueWorker.getWorker(
QueueName.Workflow,
@ -68,7 +65,6 @@ const init: Function = async (): Promise<void> => {
},
{ concurrency: 10 }
);
} catch (err) {
logger.error('App Init Failed:');
logger.error(err);