refactor: Add support for reportDataInDays and isReportEnabled in StatusPageSubscriberService

This commit is contained in:
Simon Larsen 2024-07-24 12:53:18 -06:00
parent a145b346a4
commit e654b65e71
No known key found for this signature in database
GPG Key ID: 96C5DCA24769DBCA
6 changed files with 363 additions and 224 deletions

View File

@ -1,35 +1,47 @@
<%- include('cookie-banner') -%>
<footer class="bg-white" aria-labelledby="footer-heading">
<footer class="bg-white" aria-labelledby="footer-heading">
<h2 id="footer-heading" class="sr-only">Footer</h2>
<div class="mx-auto max-w-7xl px-6 pb-8 pt-16 sm:pt-24 lg:px-8 lg:pt-32">
<div class="xl:grid xl:grid-cols-3 xl:gap-8">
<div class="space-y-8">
<img class="h-20 -ml-2 -mb-5 -mt-5" src="/img/logo-gray.svg" alt="OneUptime">
<p class="text-sm leading-6 text-gray-600">OneUptime is the complete observability platform. OneUptime is open-source and always will be.</p>
<p class="text-sm leading-6 text-gray-600">OneUptime is the complete observability platform. OneUptime is
open-source and always will be.</p>
<div class="flex space-x-6">
<a href="https://twitter.com/oneuptimehq" target="_blank" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">Twitter</span>
<svg class="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" />
<a href="https://x.com/oneuptimehq" target="_blank" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">X</span>
<svg class="h-5 w-5 mt-0.5" fill="currentColor" viewBox="0 0 220 200" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M173.263 0H206.998L133.297 84.718L220 200H152.112L98.9403 130.082L38.0995 200H4.34432L83.1743 109.385L0 0H69.6111L117.674 63.9077L173.263 0ZM161.423 179.692H180.116L59.4539 19.241H39.3946L161.423 179.692Z"/>
</svg>
</a>
<a href="https://github.com/oneuptime" target="_blank" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">GitHub</span>
<svg class="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd" />
</svg>
</a>
</a>
<!-- <a href="#" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">YouTube</span>
<a href="https://github.com/oneuptime" target="_blank" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">GitHub</span>
<svg class="h-6 w-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd"
d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"
clip-rule="evenodd" />
</svg>
</a>
<a href="https://www.reddit.com/r/oneuptimehq/" target="_blank" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">Reddit</span>
<svg class="mt-0.5 h-5 w-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M14.238 15.348c.085.084.085.221 0 .306-.465.462-1.194.687-2.231.687l-.008-.002-.008.002c-1.036 0-1.766-.225-2.231-.688-.085-.084-.085-.221 0-.305.084-.084.222-.084.307 0 .379.377 1.008.561 1.924.561l.008.002.008-.002c.915 0 1.544-.184 1.924-.561.085-.084.223-.084.307 0zm-3.44-2.418c0-.507-.414-.919-.922-.919-.509 0-.923.412-.923.919 0 .506.414.918.923.918.508.001.922-.411.922-.918zm13.202-.93c0 6.627-5.373 12-12 12s-12-5.373-12-12 5.373-12 12-12 12 5.373 12 12zm-5-.129c0-.851-.695-1.543-1.55-1.543-.417 0-.795.167-1.074.435-1.056-.695-2.485-1.137-4.066-1.194l.865-2.724 2.343.549-.003.034c0 .696.569 1.262 1.268 1.262.699 0 1.267-.566 1.267-1.262s-.568-1.262-1.267-1.262c-.537 0-.994.335-1.179.804l-2.525-.592c-.11-.027-.223.037-.257.145l-.965 3.038c-1.656.02-3.155.466-4.258 1.181-.277-.255-.644-.415-1.05-.415-.854.001-1.549.693-1.549 1.544 0 .566.311 1.056.768 1.325-.03.164-.05.331-.05.5 0 2.281 2.805 4.137 6.253 4.137s6.253-1.856 6.253-4.137c0-.16-.017-.317-.044-.472.486-.261.82-.766.82-1.353zm-4.872.141c-.509 0-.922.412-.922.919 0 .506.414.918.922.918s.922-.412.922-.918c0-.507-.413-.919-.922-.919z"/>
</svg>
</a>
<a href="https://www.youtube.com/@OneUptimeHQ" class="text-gray-400 hover:text-gray-500">
<span class="sr-only">YouTube</span>
<svg class="h-7 w-7 -mt-0.5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M19.812 5.418c.861.23 1.538.907 1.768 1.768C21.998 8.746 22 12 22 12s0 3.255-.418 4.814a2.504 2.504 0 0 1-1.768 1.768c-1.56.419-7.814.419-7.814.419s-6.255 0-7.814-.419a2.505 2.505 0 0 1-1.768-1.768C2 15.255 2 12 2 12s0-3.255.417-4.814a2.507 2.507 0 0 1 1.768-1.768C5.744 5 11.998 5 11.998 5s6.255 0 7.814.418ZM15.194 12 10 15V9l5.194 3Z" clip-rule="evenodd" />
</svg>
</a> -->
</a>
</div>
</div>
<div class="mt-16 grid grid-cols-2 gap-8 xl:col-span-2 xl:mt-0">
@ -38,19 +50,23 @@
<h3 class="text-sm font-semibold leading-6 text-gray-900">Products</h3>
<ul role="list" class="mt-6 space-y-4">
<li>
<a href="/product/status-page" class="text-sm leading-6 text-gray-600 hover:text-gray-900">Status Page</a>
</li>
<li>
<a href="/product/incident-management" class="text-sm leading-6 text-gray-600 hover:text-gray-900">Incident Management</a>
<a href="/product/status-page" class="text-sm leading-6 text-gray-600 hover:text-gray-900">Status
Page</a>
</li>
<li>
<a href="/product/monitoring" class="text-sm leading-6 text-gray-600 hover:text-gray-900">Monitoring</a>
<a href="/product/incident-management"
class="text-sm leading-6 text-gray-600 hover:text-gray-900">Incident Management</a>
</li>
<li>
<a href="/product/on-call" class="text-sm leading-6 text-gray-600 hover:text-gray-900">On-Call and Alerts</a>
<a href="/product/monitoring"
class="text-sm leading-6 text-gray-600 hover:text-gray-900">Monitoring</a>
</li>
<li>
<a href="/product/on-call" class="text-sm leading-6 text-gray-600 hover:text-gray-900">On-Call and
Alerts</a>
</li>
<li>
@ -64,13 +80,15 @@
<h3 class="text-sm font-semibold leading-6 text-gray-900">Enterprise</h3>
<ul role="list" class="mt-6 space-y-4">
<li>
<a href="/enterprise/overview" class="text-sm leading-6 text-gray-600 hover:text-gray-900">Overview</a>
<a href="/enterprise/overview"
class="text-sm leading-6 text-gray-600 hover:text-gray-900">Overview</a>
</li>
<li>
<a href="/enterprise/demo" class="text-sm leading-6 text-gray-600 hover:text-gray-900">Request Demo</a>
<a href="/enterprise/demo" class="text-sm leading-6 text-gray-600 hover:text-gray-900">Request
Demo</a>
</li>
</ul>
</div>
</div>
@ -79,10 +97,12 @@
<h3 class="text-sm font-semibold leading-6 text-gray-900">Compare</h3>
<ul role="list" class="mt-6 space-y-4">
<li>
<a href="/compare/statuspage.io" class="text-sm leading-6 text-gray-600 hover:text-gray-900">StatusPage.io</a>
<a href="/compare/statuspage.io"
class="text-sm leading-6 text-gray-600 hover:text-gray-900">StatusPage.io</a>
</li>
<li>
<a href="/compare/incident.io" class="text-sm leading-6 text-gray-600 hover:text-gray-900">Incident.io</a>
<a href="/compare/incident.io"
class="text-sm leading-6 text-gray-600 hover:text-gray-900">Incident.io</a>
</li>
<li>
<a href="/compare/pagerduty" class="text-sm leading-6 text-gray-600 hover:text-gray-900">PagerDuty</a>
@ -108,13 +128,14 @@
<li>
<a href="/support" class="text-sm leading-6 text-gray-600 hover:text-gray-900">Help and Support</a>
</li>
<li>
<a href="/legal" class="text-sm leading-6 text-gray-600 hover:text-gray-900">Legal Center</a>
</li>
<li>
<a href="https://github.com/oneuptime/oneuptime" target="_blank" class="text-sm leading-6 text-gray-600 hover:text-gray-900">GitHub</a>
<a href="https://github.com/oneuptime/oneuptime" target="_blank"
class="text-sm leading-6 text-gray-600 hover:text-gray-900">GitHub</a>
</li>
<li>
@ -122,12 +143,13 @@
</li>
<li>
<a target="_blank" href="https://github.com/OneUptime/interview" class="text-sm leading-6 text-gray-600 hover:text-gray-900">Careers</a>
<a target="_blank" href="https://github.com/OneUptime/interview"
class="text-sm leading-6 text-gray-600 hover:text-gray-900">Careers</a>
</li>
<li>
<a href="/about" class="text-sm leading-6 text-gray-600 hover:text-gray-900">About Us</a>
</li>
<a href="/about" class="text-sm leading-6 text-gray-600 hover:text-gray-900">About Us</a>
</li>
</ul>
</div>
</div>
@ -141,13 +163,15 @@
<div class="flex items-center gap-x-6 bg-gray-700 px-6 py-2.5 sm:px-3.5 sm:before:flex-1">
<p class="text-sm leading-6 text-white">
<a href="https://github.com/oneuptime/interview" target="blank">
<strong class="font-semibold">We're hiring remotely, worldwide! </strong><svg viewBox="0 0 2 2" class="mx-2 inline h-0.5 w-0.5 fill-current" aria-hidden="true"><circle cx="1" cy="1" r="1" /></svg>See open roles &nbsp;<span aria-hidden="true">&rarr;</span>
<strong class="font-semibold">We're hiring remotely, worldwide! </strong><svg viewBox="0 0 2 2"
class="mx-2 inline h-0.5 w-0.5 fill-current" aria-hidden="true">
<circle cx="1" cy="1" r="1" />
</svg>See open roles &nbsp;<span aria-hidden="true">&rarr;</span>
</a>
</p>
<div class="flex flex-1 justify-end">
</div>
</div>
</footer>
</footer>

View File

@ -7,7 +7,6 @@ import IncidentStateTimelineService from "../Services/IncidentStateTimelineServi
import MonitorGroupResourceService from "../Services/MonitorGroupResourceService";
import MonitorGroupService from "../Services/MonitorGroupService";
import MonitorStatusService from "../Services/MonitorStatusService";
import MonitorStatusTimelineService from "../Services/MonitorStatusTimelineService";
import ScheduledMaintenancePublicNoteService from "../Services/ScheduledMaintenancePublicNoteService";
import ScheduledMaintenanceService from "../Services/ScheduledMaintenanceService";
import ScheduledMaintenanceStateService from "../Services/ScheduledMaintenanceStateService";
@ -40,7 +39,7 @@ import BaseModel from "Common/Models/BaseModel";
import ArrayUtil from "Common/Types/ArrayUtil";
import DatabaseCommonInteractionProps from "Common/Types/BaseDatabase/DatabaseCommonInteractionProps";
import SortOrder from "Common/Types/BaseDatabase/SortOrder";
import LIMIT_MAX, { LIMIT_PER_PROJECT } from "Common/Types/Database/LimitMax";
import { LIMIT_PER_PROJECT } from "Common/Types/Database/LimitMax";
import OneUptimeDate from "Common/Types/Date";
import Dictionary from "Common/Types/Dictionary";
import Email from "Common/Types/Email";
@ -170,7 +169,7 @@ export default class StatusPageAPI extends BaseAPI<
);
await StatusPageService.sendEmailReport({
emails: [email],
email: email,
statusPageId,
});
@ -462,9 +461,6 @@ export default class StatusPageAPI extends BaseAPI<
?.toString()}/overview/:statusPageId`,
UserMiddleware.getUserMiddleware,
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
const startDate: Date = OneUptimeDate.getSomeDaysAgo(90);
const endDate: Date = OneUptimeDate.getCurrentDate();
try {
const objectId: ObjectID = new ObjectID(
req.params["statusPageId"] as string,
@ -697,75 +693,12 @@ export default class StatusPageAPI extends BaseAPI<
monitorsInGroup[monitorGroupId.toString()] = monitorsInGroupIds;
}
let monitorStatusTimelines: Array<MonitorStatusTimeline> = [];
if (monitorsOnStatusPageForTimeline.length > 0) {
monitorStatusTimelines = await MonitorStatusTimelineService.findBy({
query: {
monitorId: QueryHelper.any(monitorsOnStatusPageForTimeline),
endsAt: QueryHelper.inBetween(startDate, endDate),
},
select: {
monitorId: true,
createdAt: true,
endsAt: true,
startsAt: true,
monitorStatus: {
name: true,
color: true,
priority: true,
} as any,
},
sort: {
createdAt: SortOrder.Descending,
},
skip: 0,
limit: LIMIT_MAX, // This can be optimized.
props: {
isRoot: true,
},
const monitorStatusTimelines: Array<MonitorStatusTimeline> =
await StatusPageService.getMonitorStatusTimelineForStatusPage({
monitorIds: monitorsOnStatusPageForTimeline,
historyDays: 90,
});
monitorStatusTimelines = monitorStatusTimelines.concat(
await MonitorStatusTimelineService.findBy({
query: {
monitorId: QueryHelper.any(monitorsOnStatusPageForTimeline),
endsAt: QueryHelper.isNull(),
},
select: {
monitorId: true,
createdAt: true,
endsAt: true,
startsAt: true,
monitorStatus: {
name: true,
color: true,
priority: true,
} as any,
},
sort: {
createdAt: SortOrder.Descending,
},
skip: 0,
limit: LIMIT_MAX, // This can be optimized.
props: {
isRoot: true,
},
}),
);
// sort monitorStatusTimelines by createdAt.
monitorStatusTimelines = monitorStatusTimelines.sort(
(a: MonitorStatusTimeline, b: MonitorStatusTimeline) => {
if (!a.createdAt || !b.createdAt) {
return 0;
}
return b.createdAt!.getTime() - a.createdAt!.getTime();
},
);
}
// check if status page has active incident.
let activeIncidents: Array<Incident> = [];
if (monitorsOnStatusPage.length > 0) {
@ -1114,7 +1047,9 @@ export default class StatusPageAPI extends BaseAPI<
incidentPublicNotes,
IncidentPublicNote,
),
activeIncidents: BaseModel.toJSONArray(activeIncidents, Incident),
monitorStatusTimelines: BaseModel.toJSONArray(
monitorStatusTimelines,
MonitorStatusTimeline,
@ -1400,28 +1335,8 @@ export default class StatusPageAPI extends BaseAPI<
// get monitors on status page.
const statusPageResources: Array<StatusPageResource> =
await StatusPageResourceService.findBy({
query: {
statusPageId: statusPageId,
},
select: {
statusPageGroupId: true,
monitorId: true,
displayTooltip: true,
displayDescription: true,
displayName: true,
monitor: {
_id: true,
currentMonitorStatusId: true,
},
monitorGroupId: true,
},
skip: 0,
limit: LIMIT_PER_PROJECT,
props: {
isRoot: true,
},
await StatusPageService.getStatusPageResources({
statusPageId: statusPageId,
});
// check if status page has active scheduled events.
@ -2062,91 +1977,17 @@ export default class StatusPageAPI extends BaseAPI<
// get monitors on status page.
const statusPageResources: Array<StatusPageResource> =
await StatusPageResourceService.findBy({
query: {
statusPageId: statusPageId,
},
select: {
statusPageGroupId: true,
monitorId: true,
displayTooltip: true,
displayDescription: true,
displayName: true,
monitor: {
_id: true,
currentMonitorStatusId: true,
},
monitorGroupId: true,
},
skip: 0,
limit: LIMIT_PER_PROJECT,
props: {
isRoot: true,
},
await StatusPageService.getStatusPageResources({
statusPageId: statusPageId,
});
const monitorGroupIds: Array<ObjectID> = statusPageResources
.map((resource: StatusPageResource) => {
return resource.monitorGroupId!;
})
.filter((id: ObjectID) => {
return Boolean(id); // remove nulls
const { monitorsOnStatusPage, monitorsInGroup } =
await StatusPageService.getMonitorIdsOnStatusPage({
statusPageId: statusPageId,
});
const monitorsInGroup: Dictionary<Array<ObjectID>> = {};
// get monitor status charts.
const monitorsOnStatusPage: Array<ObjectID> = statusPageResources
.map((monitor: StatusPageResource) => {
return monitor.monitorId!;
})
.filter((id: ObjectID) => {
return Boolean(id); // remove nulls
});
for (const monitorGroupId of monitorGroupIds) {
// get current status of monitors in the group.
// get monitors in the group.
const groupResources: Array<MonitorGroupResource> =
await MonitorGroupResourceService.findBy({
query: {
monitorGroupId: monitorGroupId,
},
select: {
monitorId: true,
},
props: {
isRoot: true,
},
limit: LIMIT_PER_PROJECT,
skip: 0,
});
const monitorsInGroupIds: Array<ObjectID> = groupResources
.map((resource: MonitorGroupResource) => {
return resource.monitorId!;
})
.filter((id: ObjectID) => {
return Boolean(id); // remove nulls
});
for (const monitorId of monitorsInGroupIds) {
if (
!monitorsOnStatusPage.find((item: ObjectID) => {
return item.toString() === monitorId.toString();
})
) {
monitorsOnStatusPage.push(monitorId);
}
}
monitorsInGroup[monitorGroupId.toString()] = monitorsInGroupIds;
}
const today: Date = OneUptimeDate.getCurrentDate();
const historyDays: Date = OneUptimeDate.getSomeDaysAgo(
statusPage.showIncidentHistoryInDays || 14,
);

View File

@ -34,6 +34,7 @@ import { MigrationName1721075917289 } from "./1721075917289-MigrationName";
import { MigrationName1721152139648 } from "./1721152139648-MigrationName";
import { MigrationName1721159743714 } from "./1721159743714-MigrationName";
import { MigrationName1721754545771 } from "./1721754545771-MigrationName";
import { MigrationName1721779190475 } from "./1721779190475-MigrationName";
export default [
InitialMigration,
@ -72,5 +73,5 @@ export default [
MigrationName1721152139648,
MigrationName1721159743714,
MigrationName1721754545771,
MigrationName1717839110671,
MigrationName1721779190475,
];

View File

@ -19,7 +19,7 @@ import Protocol from "Common/Types/API/Protocol";
import URL from "Common/Types/API/URL";
import DatabaseCommonInteractionProps from "Common/Types/BaseDatabase/DatabaseCommonInteractionProps";
import { Green } from "Common/Types/BrandColors";
import { LIMIT_PER_PROJECT } from "Common/Types/Database/LimitMax";
import LIMIT_MAX, { LIMIT_PER_PROJECT } from "Common/Types/Database/LimitMax";
import BadDataException from "Common/Types/Exception/BadDataException";
import JSONWebTokenData from "Common/Types/JsonWebTokenData";
import ObjectID from "Common/Types/ObjectID";
@ -44,6 +44,32 @@ import MailService from "./MailService";
import EmailTemplateType from "Common/Types/Email/EmailTemplateType";
import { FileRoute } from "Common/ServiceRoute";
import ProjectSMTPConfigService from "./ProjectSmtpConfigService";
import StatusPageResource from "Model/Models/StatusPageResource";
import StatusPageResourceService from "./StatusPageResourceService";
import Dictionary from "Common/Types/Dictionary";
import MonitorGroupResource from "Model/Models/MonitorGroupResource";
import MonitorGroupResourceService from "./MonitorGroupResourceService";
import QueryHelper from "../Types/Database/QueryHelper";
import OneUptimeDate from "Common/Types/Date";
import IncidentService from "./IncidentService";
import MonitorStatusTimeline from "Model/Models/MonitorStatusTimeline";
import MonitorStatusTimelineService from "./MonitorStatusTimelineService";
import SortOrder from "Common/Types/BaseDatabase/SortOrder";
export interface StatusPageReportItem {
resourceName: string;
totalIncidents: number;
uptimePercent: number;
downtimeInMinutes: number;
}
export interface StatusPageReport {
totalResources: number;
totalIncidents: number;
averageUptimePercent: number;
totalDowntimeInMinutes: number;
resources: Array<StatusPageReportItem>;
}
export class Service extends DatabaseService<StatusPage> {
public constructor(postgresDatabase?: PostgresDatabase) {
@ -343,6 +369,87 @@ export class Service extends DatabaseService<StatusPage> {
return false;
}
public async getMonitorStatusTimelineForStatusPage(data: {
monitorIds: Array<ObjectID>;
historyDays: number;
}): Promise<Array<MonitorStatusTimeline>> {
const startDate: Date = OneUptimeDate.getSomeDaysAgo(
data.historyDays || 14,
);
const endDate: Date = OneUptimeDate.getCurrentDate();
let monitorStatusTimelines: Array<MonitorStatusTimeline> = [];
if (data.monitorIds.length > 0) {
monitorStatusTimelines = await MonitorStatusTimelineService.findBy({
query: {
monitorId: QueryHelper.any(data.monitorIds),
endsAt: QueryHelper.inBetween(startDate, endDate),
},
select: {
monitorId: true,
createdAt: true,
endsAt: true,
startsAt: true,
monitorStatus: {
name: true,
color: true,
priority: true,
} as any,
},
sort: {
createdAt: SortOrder.Descending,
},
skip: 0,
limit: LIMIT_MAX, // This can be optimized.
props: {
isRoot: true,
},
});
monitorStatusTimelines = monitorStatusTimelines.concat(
await MonitorStatusTimelineService.findBy({
query: {
monitorId: QueryHelper.any(data.monitorIds),
endsAt: QueryHelper.isNull(),
},
select: {
monitorId: true,
createdAt: true,
endsAt: true,
startsAt: true,
monitorStatus: {
name: true,
color: true,
priority: true,
} as any,
},
sort: {
createdAt: SortOrder.Descending,
},
skip: 0,
limit: LIMIT_MAX, // This can be optimized.
props: {
isRoot: true,
},
}),
);
// sort monitorStatusTimelines by createdAt.
monitorStatusTimelines = monitorStatusTimelines.sort(
(a: MonitorStatusTimeline, b: MonitorStatusTimeline) => {
if (!a.createdAt || !b.createdAt) {
return 0;
}
return b.createdAt!.getTime() - a.createdAt!.getTime();
},
);
}
return monitorStatusTimelines;
}
public async getStatusPageURL(statusPageId: ObjectID): Promise<string> {
const domains: Array<StatusPageDomain> =
await StatusPageDomainService.findBy({
@ -614,5 +721,169 @@ export class Service extends DatabaseService<StatusPage> {
}
}
}
public async getReportByStatusPage(_data: {
statusPageId: ObjectID;
historyDays: number;
}): Promise<StatusPageReport> {
// const statusPageResources = await this.getStatusPageResources({
// statusPageId: data.statusPageId,
// });
// const incidentCount: number = await this.getIncidentCountOnStatusPage({
// statusPageId: data.statusPageId,
// historyDays: data.historyDays,
// });
// const monitors = await this.getMonitorIdsOnStatusPage({
// statusPageId: data.statusPageId,
// });
// const _timeline: Array<MonitorStatusTimeline> = await this.getMonitorStatusTimelineForStatusPage({
// monitorIds: monitors.monitorsOnStatusPage,
// historyDays: data.historyDays,
// });
throw new BadDataException("Not implemented");
// return {
// totalResources: statusPageResources.length,
// totalIncidents: incidentCount,
// }
}
public async getIncidentCountOnStatusPage(data: {
statusPageId: ObjectID;
historyDays: number;
}): Promise<number> {
const monitorsOnStatusPage = await this.getMonitorIdsOnStatusPage({
statusPageId: data.statusPageId,
});
const today: Date = OneUptimeDate.getCurrentDate();
const historyDays: Date = OneUptimeDate.getSomeDaysAgo(
data.historyDays || 14,
);
const incidentCount: PositiveNumber = await IncidentService.countBy({
query: {
monitors: monitorsOnStatusPage.monitorsOnStatusPage as any,
createdAt: QueryHelper.inBetween(historyDays, today),
},
props: {
isRoot: true,
},
});
return incidentCount.toNumber();
}
public async getMonitorIdsOnStatusPage(data: {
statusPageId: ObjectID;
}): Promise<{
monitorsOnStatusPage: Array<ObjectID>;
monitorsInGroup: Dictionary<Array<ObjectID>>;
}> {
const statusPageResources: Array<StatusPageResource> =
await this.getStatusPageResources(data);
const monitorGroupIds: Array<ObjectID> = statusPageResources
.map((resource: StatusPageResource) => {
return resource.monitorGroupId!;
})
.filter((id: ObjectID) => {
return Boolean(id); // remove nulls
});
const monitorsInGroup: Dictionary<Array<ObjectID>> = {};
// get monitor status charts.
const monitorsOnStatusPage: Array<ObjectID> = statusPageResources
.map((monitor: StatusPageResource) => {
return monitor.monitorId!;
})
.filter((id: ObjectID) => {
return Boolean(id); // remove nulls
});
for (const monitorGroupId of monitorGroupIds) {
// get current status of monitors in the group.
// get monitors in the group.
const groupResources: Array<MonitorGroupResource> =
await MonitorGroupResourceService.findBy({
query: {
monitorGroupId: monitorGroupId,
},
select: {
monitorId: true,
},
props: {
isRoot: true,
},
limit: LIMIT_PER_PROJECT,
skip: 0,
});
const monitorsInGroupIds: Array<ObjectID> = groupResources
.map((resource: MonitorGroupResource) => {
return resource.monitorId!;
})
.filter((id: ObjectID) => {
return Boolean(id); // remove nulls
});
for (const monitorId of monitorsInGroupIds) {
if (
!monitorsOnStatusPage.find((item: ObjectID) => {
return item.toString() === monitorId.toString();
})
) {
monitorsOnStatusPage.push(monitorId);
}
}
monitorsInGroup[monitorGroupId.toString()] = monitorsInGroupIds;
}
return {
monitorsOnStatusPage: monitorsOnStatusPage,
monitorsInGroup: monitorsInGroup,
};
}
public async getStatusPageResources(data: {
statusPageId: ObjectID;
}): Promise<Array<StatusPageResource>> {
// get monitors on status page.
const statusPageResources: Array<StatusPageResource> =
await StatusPageResourceService.findBy({
query: {
statusPageId: data.statusPageId,
},
select: {
statusPageGroupId: true,
monitorId: true,
displayTooltip: true,
displayDescription: true,
displayName: true,
monitor: {
_id: true,
currentMonitorStatusId: true,
},
monitorGroupId: true,
},
skip: 0,
limit: LIMIT_PER_PROJECT,
props: {
isRoot: true,
},
});
return statusPageResources;
}
}
export default new Service();

View File

@ -393,6 +393,8 @@ export class Service extends DatabaseService<Model> {
twilioPhoneNumber: true,
},
subscriberTimezones: true,
reportDataInDays: true,
isReportEnabled: true,
},
});
}

View File

@ -52,16 +52,16 @@ spec:
path: /status/live
port: {{ $.Values.port.ingestor }}
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 30
periodSeconds: 15
timeoutSeconds: 60
# Readyness Probe
readinessProbe:
httpGet:
path: /status/ready
port: {{ $.Values.port.ingestor }}
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 30
periodSeconds: 15
timeoutSeconds: 60
{{- if $.Values.containerSecurityContext }}
securityContext: {{- $.Values.containerSecurityContext | toYaml | nindent 12 }}
{{- end }}