@oneuptime/common 7.0.3691 → 7.0.3695
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Server/API/StatusPageAPI.ts +562 -207
- package/Server/Services/StatusPageService.ts +8 -8
- package/Utils/StatusPage/ResourceUptime.ts +23 -0
- package/Utils/Uptime/UptimeUtil.ts +26 -0
- package/build/dist/Server/API/StatusPageAPI.js +404 -202
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +6 -5
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Utils/StatusPage/ResourceUptime.js +16 -0
- package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
- package/build/dist/Utils/Uptime/UptimeUtil.js +16 -0
- package/build/dist/Utils/Uptime/UptimeUtil.js.map +1 -1
- package/package.json +2 -2
|
@@ -74,6 +74,10 @@ import StatusPageResource from "Common/Models/DatabaseModels/StatusPageResource"
|
|
|
74
74
|
import StatusPageSSO from "Common/Models/DatabaseModels/StatusPageSso";
|
|
75
75
|
import StatusPageSubscriber from "Common/Models/DatabaseModels/StatusPageSubscriber";
|
|
76
76
|
import StatusPageEventType from "../../Types/StatusPage/StatusPageEventType";
|
|
77
|
+
import StatusPageResourceUptimeUtil from "../../Utils/StatusPage/ResourceUptime";
|
|
78
|
+
import UptimePrecision from "../../Types/StatusPage/UptimePrecision";
|
|
79
|
+
import { Green } from "../../Types/BrandColors";
|
|
80
|
+
import UptimeUtil from "../../Utils/Uptime/UptimeUtil";
|
|
77
81
|
|
|
78
82
|
export default class StatusPageAPI extends BaseAPI<
|
|
79
83
|
StatusPage,
|
|
@@ -520,17 +524,26 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
520
524
|
this.router.post(
|
|
521
525
|
`${new this.entityType()
|
|
522
526
|
.getCrudApiPath()
|
|
523
|
-
?.toString()}/
|
|
527
|
+
?.toString()}/uptime/:statusPageId`,
|
|
524
528
|
UserMiddleware.getUserMiddleware,
|
|
525
529
|
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
526
530
|
try {
|
|
527
|
-
|
|
531
|
+
// This reosurce ID can be of a status page resource OR a status page group.
|
|
532
|
+
const statusPageResourceId: ObjectID = new ObjectID(
|
|
533
|
+
req.params["statusPageResourceId"] as string,
|
|
534
|
+
);
|
|
535
|
+
|
|
536
|
+
const statusPageId: ObjectID = new ObjectID(
|
|
528
537
|
req.params["statusPageId"] as string,
|
|
529
538
|
);
|
|
530
539
|
|
|
540
|
+
if (!statusPageId || !statusPageResourceId) {
|
|
541
|
+
throw new BadDataException("Status Page or Resource not found");
|
|
542
|
+
}
|
|
543
|
+
|
|
531
544
|
if (
|
|
532
545
|
!(await this.service.hasReadAccess(
|
|
533
|
-
|
|
546
|
+
statusPageId,
|
|
534
547
|
await CommonAPI.getDatabaseCommonInteractionProps(req),
|
|
535
548
|
req,
|
|
536
549
|
))
|
|
@@ -540,231 +553,320 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
540
553
|
);
|
|
541
554
|
}
|
|
542
555
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
_id: objectId.toString(),
|
|
547
|
-
},
|
|
548
|
-
select: {
|
|
549
|
-
_id: true,
|
|
550
|
-
projectId: true,
|
|
551
|
-
isPublicStatusPage: true,
|
|
552
|
-
overviewPageDescription: true,
|
|
553
|
-
showIncidentLabelsOnStatusPage: true,
|
|
554
|
-
showScheduledEventLabelsOnStatusPage: true,
|
|
555
|
-
downtimeMonitorStatuses: {
|
|
556
|
-
_id: true,
|
|
557
|
-
},
|
|
558
|
-
defaultBarColor: true,
|
|
559
|
-
showOverallUptimePercentOnStatusPage: true,
|
|
560
|
-
overallUptimePercentPrecision: true,
|
|
561
|
-
},
|
|
562
|
-
props: {
|
|
563
|
-
isRoot: true,
|
|
564
|
-
},
|
|
565
|
-
});
|
|
556
|
+
// get start and end date from request body.
|
|
557
|
+
// if no end date is provided then it will be current date.
|
|
558
|
+
// if no start date is provided then it will be 14 days ago from end date.
|
|
566
559
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
}
|
|
560
|
+
let startDate: Date = OneUptimeDate.getSomeDaysAgo(14);
|
|
561
|
+
let endDate: Date = OneUptimeDate.getCurrentDate();
|
|
570
562
|
|
|
571
|
-
|
|
563
|
+
if (req.body["startDate"]) {
|
|
564
|
+
startDate = OneUptimeDate.fromString(
|
|
565
|
+
req.body["startDate"] as string,
|
|
566
|
+
);
|
|
567
|
+
}
|
|
572
568
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
projectId: statusPage.projectId!,
|
|
577
|
-
},
|
|
578
|
-
select: {
|
|
579
|
-
name: true,
|
|
580
|
-
color: true,
|
|
581
|
-
priority: true,
|
|
582
|
-
isOperationalState: true,
|
|
583
|
-
},
|
|
584
|
-
sort: {
|
|
585
|
-
priority: SortOrder.Ascending,
|
|
586
|
-
},
|
|
587
|
-
skip: 0,
|
|
588
|
-
limit: LIMIT_PER_PROJECT,
|
|
589
|
-
props: {
|
|
590
|
-
isRoot: true,
|
|
591
|
-
},
|
|
592
|
-
});
|
|
569
|
+
if (req.body["endDate"]) {
|
|
570
|
+
endDate = OneUptimeDate.fromString(req.body["endDate"] as string);
|
|
571
|
+
}
|
|
593
572
|
|
|
594
|
-
|
|
573
|
+
if (OneUptimeDate.isAfter(startDate, endDate)) {
|
|
574
|
+
throw new BadDataException("Start date cannot be after end date");
|
|
575
|
+
}
|
|
595
576
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
select
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
description: true,
|
|
605
|
-
isExpandedByDefault: true,
|
|
606
|
-
showCurrentStatus: true,
|
|
607
|
-
showUptimePercent: true,
|
|
608
|
-
uptimePercentPrecision: true,
|
|
609
|
-
},
|
|
610
|
-
sort: {
|
|
611
|
-
order: SortOrder.Ascending,
|
|
612
|
-
},
|
|
613
|
-
skip: 0,
|
|
614
|
-
limit: LIMIT_PER_PROJECT,
|
|
615
|
-
props: {
|
|
616
|
-
isRoot: true,
|
|
617
|
-
},
|
|
618
|
-
});
|
|
577
|
+
if (
|
|
578
|
+
OneUptimeDate.getDaysBetweenTwoDatesInclusive(startDate, endDate) >
|
|
579
|
+
90
|
|
580
|
+
) {
|
|
581
|
+
throw new BadDataException(
|
|
582
|
+
"You can only get uptime for 90 days. Please select a date range within 90 days.",
|
|
583
|
+
);
|
|
584
|
+
}
|
|
619
585
|
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
showCurrentStatus: true,
|
|
634
|
-
order: true,
|
|
635
|
-
monitor: {
|
|
636
|
-
_id: true,
|
|
637
|
-
currentMonitorStatusId: true,
|
|
638
|
-
},
|
|
639
|
-
monitorGroupId: true,
|
|
640
|
-
showUptimePercent: true,
|
|
641
|
-
uptimePercentPrecision: true,
|
|
642
|
-
},
|
|
643
|
-
sort: {
|
|
644
|
-
order: SortOrder.Ascending,
|
|
645
|
-
},
|
|
646
|
-
skip: 0,
|
|
647
|
-
limit: LIMIT_PER_PROJECT,
|
|
648
|
-
props: {
|
|
649
|
-
isRoot: true,
|
|
650
|
-
},
|
|
651
|
-
});
|
|
586
|
+
const {
|
|
587
|
+
monitorStatuses,
|
|
588
|
+
monitorGroupCurrentStatuses,
|
|
589
|
+
statusPageResources,
|
|
590
|
+
statusPage,
|
|
591
|
+
monitorStatusTimelines,
|
|
592
|
+
statusPageGroups,
|
|
593
|
+
monitorsInGroup,
|
|
594
|
+
} = await this.getStatusPageResourcesAndTimelines({
|
|
595
|
+
statusPageId: statusPageId,
|
|
596
|
+
startDateForMonitorTimeline: startDate,
|
|
597
|
+
endDateForMonitorTimeline: endDate,
|
|
598
|
+
});
|
|
652
599
|
|
|
653
|
-
const
|
|
654
|
-
.
|
|
655
|
-
return resource.monitorGroupId!;
|
|
656
|
-
})
|
|
657
|
-
.filter((id: ObjectID) => {
|
|
658
|
-
return Boolean(id); // remove nulls
|
|
659
|
-
});
|
|
600
|
+
const downtimeMonitorStatuses: Array<MonitorStatus> =
|
|
601
|
+
statusPage.downtimeMonitorStatuses || [];
|
|
660
602
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
.map((monitor: StatusPageResource) => {
|
|
668
|
-
return monitor.monitorId!;
|
|
669
|
-
})
|
|
670
|
-
.filter((id: ObjectID) => {
|
|
671
|
-
return Boolean(id); // remove nulls
|
|
672
|
-
});
|
|
603
|
+
type ResourceUptime = {
|
|
604
|
+
statusPageResourceId: ObjectID;
|
|
605
|
+
uptimePercent: number | null;
|
|
606
|
+
statusPageResourceName: string;
|
|
607
|
+
currentStatus: MonitorStatus | null;
|
|
608
|
+
};
|
|
673
609
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
.map((monitor: StatusPageResource) => {
|
|
682
|
-
return monitor.monitorId!;
|
|
683
|
-
})
|
|
684
|
-
.filter((id: ObjectID) => {
|
|
685
|
-
return Boolean(id); // remove nulls
|
|
686
|
-
});
|
|
610
|
+
type StatusPageGroupUptime = {
|
|
611
|
+
statusPageGroupId: ObjectID | null;
|
|
612
|
+
uptimePercent: number | null;
|
|
613
|
+
statusPageResourceUptimes: Array<ResourceUptime>;
|
|
614
|
+
statusPageGroupName: string | null;
|
|
615
|
+
currentStatus: MonitorStatus | null;
|
|
616
|
+
};
|
|
687
617
|
|
|
688
|
-
|
|
689
|
-
|
|
618
|
+
type GetUptimeByStatusPageGroup = (data: {
|
|
619
|
+
statusPageGroup: StatusPageGroup | null;
|
|
620
|
+
}) => StatusPageGroupUptime;
|
|
621
|
+
|
|
622
|
+
const getUptimeByStatusPageGroup: GetUptimeByStatusPageGroup =
|
|
623
|
+
(data: {
|
|
624
|
+
statusPageGroup: StatusPageGroup | null;
|
|
625
|
+
}): StatusPageGroupUptime => {
|
|
626
|
+
const groupUptime: StatusPageGroupUptime = {
|
|
627
|
+
statusPageGroupId:
|
|
628
|
+
data && data.statusPageGroup
|
|
629
|
+
? data.statusPageGroup?.id
|
|
630
|
+
: null,
|
|
631
|
+
uptimePercent: null,
|
|
632
|
+
statusPageResourceUptimes: [],
|
|
633
|
+
statusPageGroupName: data.statusPageGroup?.name || null,
|
|
634
|
+
currentStatus: null,
|
|
635
|
+
};
|
|
690
636
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
637
|
+
const group: StatusPageGroup | null = data.statusPageGroup;
|
|
638
|
+
|
|
639
|
+
for (const resource of statusPageResources) {
|
|
640
|
+
if (
|
|
641
|
+
(resource.statusPageGroupId &&
|
|
642
|
+
resource.statusPageGroupId.toString() &&
|
|
643
|
+
group &&
|
|
644
|
+
group._id?.toString() &&
|
|
645
|
+
group._id?.toString() ===
|
|
646
|
+
resource.statusPageGroupId.toString()) ||
|
|
647
|
+
(!resource.statusPageGroupId && !group)
|
|
648
|
+
) {
|
|
649
|
+
// if its not a monitor or a monitor group, then continue. This should ideally not happen.
|
|
650
|
+
|
|
651
|
+
if (!resource.monitor && !resource.monitorGroupId) {
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const resourceUptime: ResourceUptime = {
|
|
656
|
+
statusPageResourceId: resource.id!,
|
|
657
|
+
uptimePercent: null,
|
|
658
|
+
statusPageResourceName:
|
|
659
|
+
resource.displayName || resource.monitor?.name || "",
|
|
660
|
+
currentStatus: null,
|
|
661
|
+
};
|
|
662
|
+
|
|
663
|
+
// if its a monitor
|
|
664
|
+
|
|
665
|
+
const precision: UptimePrecision =
|
|
666
|
+
resource.uptimePercentPrecision ||
|
|
667
|
+
UptimePrecision.ONE_DECIMAL;
|
|
668
|
+
|
|
669
|
+
if (resource.monitor) {
|
|
670
|
+
let currentStatus: MonitorStatus | undefined =
|
|
671
|
+
monitorStatuses.find((status: MonitorStatus) => {
|
|
672
|
+
return (
|
|
673
|
+
status._id?.toString() ===
|
|
674
|
+
resource.monitor?.currentMonitorStatusId?.toString()
|
|
675
|
+
);
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
if (!currentStatus) {
|
|
679
|
+
currentStatus = new MonitorStatus();
|
|
680
|
+
currentStatus.name = "Operational";
|
|
681
|
+
currentStatus.color = Green;
|
|
682
|
+
|
|
683
|
+
resourceUptime.currentStatus = currentStatus;
|
|
684
|
+
} else {
|
|
685
|
+
resourceUptime.currentStatus = currentStatus;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
if (!resource.showCurrentStatus) {
|
|
689
|
+
resourceUptime.currentStatus = null;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
const resourceStatusTimelines: Array<MonitorStatusTimeline> =
|
|
693
|
+
StatusPageResourceUptimeUtil.getMonitorStatusTimelineForResource(
|
|
694
|
+
{
|
|
695
|
+
statusPageResource: resource,
|
|
696
|
+
monitorStatusTimelines: monitorStatusTimelines,
|
|
697
|
+
monitorsInGroup: monitorsInGroup,
|
|
698
|
+
},
|
|
699
|
+
);
|
|
700
|
+
|
|
701
|
+
if (resource.showUptimePercent) {
|
|
702
|
+
const uptimePercent: number =
|
|
703
|
+
UptimeUtil.calculateUptimePercentage(
|
|
704
|
+
resourceStatusTimelines,
|
|
705
|
+
precision,
|
|
706
|
+
downtimeMonitorStatuses,
|
|
707
|
+
);
|
|
708
|
+
|
|
709
|
+
resourceUptime.uptimePercent = uptimePercent;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
groupUptime.statusPageResourceUptimes.push(resourceUptime);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// if its a monitor group, then...
|
|
716
|
+
|
|
717
|
+
if (resource.monitorGroupId) {
|
|
718
|
+
let currentStatus: MonitorStatus | undefined =
|
|
719
|
+
monitorStatuses.find((status: MonitorStatus) => {
|
|
720
|
+
return (
|
|
721
|
+
status._id?.toString() ===
|
|
722
|
+
monitorGroupCurrentStatuses[
|
|
723
|
+
resource.monitorGroupId?.toString() || ""
|
|
724
|
+
]?.toString()
|
|
725
|
+
);
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
if (!currentStatus) {
|
|
729
|
+
currentStatus = new MonitorStatus();
|
|
730
|
+
currentStatus.name = "Operational";
|
|
731
|
+
currentStatus.color = Green;
|
|
732
|
+
|
|
733
|
+
resourceUptime.currentStatus = currentStatus;
|
|
734
|
+
} else {
|
|
735
|
+
resourceUptime.currentStatus = currentStatus;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
if (!resource.showCurrentStatus) {
|
|
739
|
+
resourceUptime.currentStatus = null;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
if (resource.showUptimePercent) {
|
|
743
|
+
const resourceStatusTimelines: Array<MonitorStatusTimeline> =
|
|
744
|
+
StatusPageResourceUptimeUtil.getMonitorStatusTimelineForResource(
|
|
745
|
+
{
|
|
746
|
+
statusPageResource: resource,
|
|
747
|
+
monitorStatusTimelines: monitorStatusTimelines,
|
|
748
|
+
monitorsInGroup: monitorsInGroup,
|
|
749
|
+
},
|
|
750
|
+
);
|
|
751
|
+
|
|
752
|
+
const uptimePercent: number =
|
|
753
|
+
UptimeUtil.calculateUptimePercentage(
|
|
754
|
+
resourceStatusTimelines,
|
|
755
|
+
precision,
|
|
756
|
+
downtimeMonitorStatuses,
|
|
757
|
+
);
|
|
758
|
+
|
|
759
|
+
resourceUptime.uptimePercent = uptimePercent;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
groupUptime.statusPageResourceUptimes.push(resourceUptime);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
}
|
|
695
766
|
|
|
696
|
-
|
|
697
|
-
|
|
767
|
+
if (group?.showUptimePercent) {
|
|
768
|
+
// calculate uptime percent for the group.
|
|
769
|
+
const avgUptimePercent: number =
|
|
770
|
+
UptimeUtil.calculateAvgUptimePercentage({
|
|
771
|
+
uptimePercentages: groupUptime.statusPageResourceUptimes
|
|
772
|
+
.filter((resource: ResourceUptime) => {
|
|
773
|
+
return resource.uptimePercent !== null;
|
|
774
|
+
})
|
|
775
|
+
.map((resource: ResourceUptime) => {
|
|
776
|
+
return resource.uptimePercent || 0;
|
|
777
|
+
}),
|
|
778
|
+
precision:
|
|
779
|
+
group.uptimePercentPrecision ||
|
|
780
|
+
UptimePrecision.ONE_DECIMAL,
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
groupUptime.uptimePercent = avgUptimePercent;
|
|
784
|
+
}
|
|
698
785
|
|
|
699
|
-
|
|
786
|
+
if (group?.showCurrentStatus) {
|
|
787
|
+
const currentStatuses: Array<MonitorStatus> =
|
|
788
|
+
groupUptime.statusPageResourceUptimes
|
|
789
|
+
.filter((resourceUptime: ResourceUptime) => {
|
|
790
|
+
return resourceUptime.currentStatus !== null;
|
|
791
|
+
})
|
|
792
|
+
.map((resourceUptime: ResourceUptime) => {
|
|
793
|
+
return resourceUptime.currentStatus!;
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
const worstStatus: MonitorStatus | null =
|
|
797
|
+
StatusPageResourceUptimeUtil.getWorstMonitorStatus({
|
|
798
|
+
monitorStatuses: currentStatuses,
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
groupUptime.currentStatus = worstStatus;
|
|
802
|
+
}
|
|
700
803
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
query: {
|
|
704
|
-
monitorGroupId: monitorGroupId,
|
|
705
|
-
},
|
|
706
|
-
select: {
|
|
707
|
-
monitorId: true,
|
|
708
|
-
},
|
|
709
|
-
props: {
|
|
710
|
-
isRoot: true,
|
|
711
|
-
},
|
|
712
|
-
limit: LIMIT_PER_PROJECT,
|
|
713
|
-
skip: 0,
|
|
714
|
-
});
|
|
804
|
+
return groupUptime;
|
|
805
|
+
};
|
|
715
806
|
|
|
716
|
-
|
|
717
|
-
.map((resource: MonitorGroupResource) => {
|
|
718
|
-
return resource.monitorId!;
|
|
719
|
-
})
|
|
720
|
-
.filter((id: ObjectID) => {
|
|
721
|
-
return Boolean(id); // remove nulls
|
|
722
|
-
});
|
|
807
|
+
const groupUptimes: Array<StatusPageGroupUptime> = [];
|
|
723
808
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
resource.monitorGroupId?.toString() ===
|
|
728
|
-
monitorGroupId.toString() &&
|
|
729
|
-
(resource.showStatusHistoryChart ||
|
|
730
|
-
resource.showUptimePercent)
|
|
731
|
-
);
|
|
732
|
-
}),
|
|
809
|
+
for (const group of statusPageGroups) {
|
|
810
|
+
groupUptimes.push(
|
|
811
|
+
getUptimeByStatusPageGroup({ statusPageGroup: group }),
|
|
733
812
|
);
|
|
813
|
+
}
|
|
734
814
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
815
|
+
return Response.sendJsonObjectResponse(req, res, {
|
|
816
|
+
statusPageResourceUptimes: [
|
|
817
|
+
...getUptimeByStatusPageGroup({ statusPageGroup: null })
|
|
818
|
+
.statusPageResourceUptimes,
|
|
819
|
+
],
|
|
820
|
+
groupUptimes: groupUptimes,
|
|
821
|
+
startDate: startDate,
|
|
822
|
+
endDate: endDate,
|
|
823
|
+
});
|
|
824
|
+
} catch (err) {
|
|
825
|
+
next(err);
|
|
826
|
+
}
|
|
827
|
+
},
|
|
828
|
+
);
|
|
749
829
|
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
830
|
+
this.router.post(
|
|
831
|
+
`${new this.entityType()
|
|
832
|
+
.getCrudApiPath()
|
|
833
|
+
?.toString()}/overview/:statusPageId`,
|
|
834
|
+
UserMiddleware.getUserMiddleware,
|
|
835
|
+
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
836
|
+
try {
|
|
837
|
+
const objectId: ObjectID = new ObjectID(
|
|
838
|
+
req.params["statusPageId"] as string,
|
|
839
|
+
);
|
|
759
840
|
|
|
760
|
-
|
|
841
|
+
if (
|
|
842
|
+
!(await this.service.hasReadAccess(
|
|
843
|
+
objectId,
|
|
844
|
+
await CommonAPI.getDatabaseCommonInteractionProps(req),
|
|
845
|
+
req,
|
|
846
|
+
))
|
|
847
|
+
) {
|
|
848
|
+
throw new NotAuthenticatedException(
|
|
849
|
+
"You are not authenticated to access this status page",
|
|
850
|
+
);
|
|
761
851
|
}
|
|
762
852
|
|
|
763
|
-
const
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
853
|
+
const startDate: Date = OneUptimeDate.getSomeDaysAgo(90);
|
|
854
|
+
const endDate: Date = OneUptimeDate.getCurrentDate();
|
|
855
|
+
|
|
856
|
+
const {
|
|
857
|
+
monitorStatuses,
|
|
858
|
+
monitorGroupCurrentStatuses,
|
|
859
|
+
statusPageResources,
|
|
860
|
+
statusPage,
|
|
861
|
+
monitorsOnStatusPage,
|
|
862
|
+
monitorStatusTimelines,
|
|
863
|
+
statusPageGroups,
|
|
864
|
+
monitorsInGroup,
|
|
865
|
+
} = await this.getStatusPageResourcesAndTimelines({
|
|
866
|
+
statusPageId: objectId,
|
|
867
|
+
startDateForMonitorTimeline: startDate,
|
|
868
|
+
endDateForMonitorTimeline: endDate,
|
|
869
|
+
});
|
|
768
870
|
|
|
769
871
|
// check if status page has active incident.
|
|
770
872
|
let activeIncidents: Array<Incident> = [];
|
|
@@ -1135,7 +1237,10 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
1135
1237
|
monitorStatusTimelines,
|
|
1136
1238
|
MonitorStatusTimeline,
|
|
1137
1239
|
),
|
|
1138
|
-
resourceGroups: BaseModel.toJSONArray(
|
|
1240
|
+
resourceGroups: BaseModel.toJSONArray(
|
|
1241
|
+
statusPageGroups,
|
|
1242
|
+
StatusPageGroup,
|
|
1243
|
+
),
|
|
1139
1244
|
monitorStatuses: BaseModel.toJSONArray(
|
|
1140
1245
|
monitorStatuses,
|
|
1141
1246
|
MonitorStatus,
|
|
@@ -2352,4 +2457,254 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
2352
2457
|
|
|
2353
2458
|
return currentStatus;
|
|
2354
2459
|
}
|
|
2460
|
+
|
|
2461
|
+
public async getStatusPageResourcesAndTimelines(data: {
|
|
2462
|
+
statusPageId: ObjectID;
|
|
2463
|
+
startDateForMonitorTimeline: Date;
|
|
2464
|
+
endDateForMonitorTimeline: Date;
|
|
2465
|
+
}): Promise<{
|
|
2466
|
+
statusPageResources: StatusPageResource[];
|
|
2467
|
+
monitorStatuses: MonitorStatus[];
|
|
2468
|
+
monitorStatusTimelines: MonitorStatusTimeline[];
|
|
2469
|
+
monitorGroupCurrentStatuses: Dictionary<ObjectID>;
|
|
2470
|
+
statusPageGroups: StatusPageGroup[];
|
|
2471
|
+
statusPage: StatusPage;
|
|
2472
|
+
monitorsOnStatusPage: ObjectID[];
|
|
2473
|
+
monitorsInGroup: Dictionary<ObjectID[]>;
|
|
2474
|
+
}> {
|
|
2475
|
+
const objectId: ObjectID = data.statusPageId;
|
|
2476
|
+
|
|
2477
|
+
const statusPage: StatusPage | null = await StatusPageService.findOneBy({
|
|
2478
|
+
query: {
|
|
2479
|
+
_id: objectId.toString(),
|
|
2480
|
+
},
|
|
2481
|
+
select: {
|
|
2482
|
+
_id: true,
|
|
2483
|
+
projectId: true,
|
|
2484
|
+
isPublicStatusPage: true,
|
|
2485
|
+
overviewPageDescription: true,
|
|
2486
|
+
showIncidentLabelsOnStatusPage: true,
|
|
2487
|
+
showScheduledEventLabelsOnStatusPage: true,
|
|
2488
|
+
downtimeMonitorStatuses: {
|
|
2489
|
+
_id: true,
|
|
2490
|
+
},
|
|
2491
|
+
defaultBarColor: true,
|
|
2492
|
+
showOverallUptimePercentOnStatusPage: true,
|
|
2493
|
+
overallUptimePercentPrecision: true,
|
|
2494
|
+
},
|
|
2495
|
+
props: {
|
|
2496
|
+
isRoot: true,
|
|
2497
|
+
},
|
|
2498
|
+
});
|
|
2499
|
+
|
|
2500
|
+
if (!statusPage) {
|
|
2501
|
+
throw new BadDataException("Status Page not found");
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
//get monitor statuses
|
|
2505
|
+
|
|
2506
|
+
const monitorStatuses: Array<MonitorStatus> =
|
|
2507
|
+
await MonitorStatusService.findBy({
|
|
2508
|
+
query: {
|
|
2509
|
+
projectId: statusPage.projectId!,
|
|
2510
|
+
},
|
|
2511
|
+
select: {
|
|
2512
|
+
name: true,
|
|
2513
|
+
color: true,
|
|
2514
|
+
priority: true,
|
|
2515
|
+
isOperationalState: true,
|
|
2516
|
+
},
|
|
2517
|
+
sort: {
|
|
2518
|
+
priority: SortOrder.Ascending,
|
|
2519
|
+
},
|
|
2520
|
+
skip: 0,
|
|
2521
|
+
limit: LIMIT_PER_PROJECT,
|
|
2522
|
+
props: {
|
|
2523
|
+
isRoot: true,
|
|
2524
|
+
},
|
|
2525
|
+
});
|
|
2526
|
+
|
|
2527
|
+
// get resource groups.
|
|
2528
|
+
|
|
2529
|
+
const groups: Array<StatusPageGroup> = await StatusPageGroupService.findBy({
|
|
2530
|
+
query: {
|
|
2531
|
+
statusPageId: objectId,
|
|
2532
|
+
},
|
|
2533
|
+
select: {
|
|
2534
|
+
name: true,
|
|
2535
|
+
order: true,
|
|
2536
|
+
description: true,
|
|
2537
|
+
isExpandedByDefault: true,
|
|
2538
|
+
showCurrentStatus: true,
|
|
2539
|
+
showUptimePercent: true,
|
|
2540
|
+
uptimePercentPrecision: true,
|
|
2541
|
+
},
|
|
2542
|
+
sort: {
|
|
2543
|
+
order: SortOrder.Ascending,
|
|
2544
|
+
},
|
|
2545
|
+
skip: 0,
|
|
2546
|
+
limit: LIMIT_PER_PROJECT,
|
|
2547
|
+
props: {
|
|
2548
|
+
isRoot: true,
|
|
2549
|
+
},
|
|
2550
|
+
});
|
|
2551
|
+
|
|
2552
|
+
// get monitors on status page.
|
|
2553
|
+
const statusPageResources: Array<StatusPageResource> =
|
|
2554
|
+
await StatusPageResourceService.findBy({
|
|
2555
|
+
query: {
|
|
2556
|
+
statusPageId: objectId,
|
|
2557
|
+
},
|
|
2558
|
+
select: {
|
|
2559
|
+
statusPageGroupId: true,
|
|
2560
|
+
monitorId: true,
|
|
2561
|
+
displayTooltip: true,
|
|
2562
|
+
displayDescription: true,
|
|
2563
|
+
displayName: true,
|
|
2564
|
+
showStatusHistoryChart: true,
|
|
2565
|
+
showCurrentStatus: true,
|
|
2566
|
+
order: true,
|
|
2567
|
+
monitor: {
|
|
2568
|
+
_id: true,
|
|
2569
|
+
currentMonitorStatusId: true,
|
|
2570
|
+
},
|
|
2571
|
+
monitorGroupId: true,
|
|
2572
|
+
showUptimePercent: true,
|
|
2573
|
+
uptimePercentPrecision: true,
|
|
2574
|
+
},
|
|
2575
|
+
sort: {
|
|
2576
|
+
order: SortOrder.Ascending,
|
|
2577
|
+
},
|
|
2578
|
+
skip: 0,
|
|
2579
|
+
limit: LIMIT_PER_PROJECT,
|
|
2580
|
+
props: {
|
|
2581
|
+
isRoot: true,
|
|
2582
|
+
},
|
|
2583
|
+
});
|
|
2584
|
+
|
|
2585
|
+
const monitorGroupIds: Array<ObjectID> = statusPageResources
|
|
2586
|
+
.map((resource: StatusPageResource) => {
|
|
2587
|
+
return resource.monitorGroupId!;
|
|
2588
|
+
})
|
|
2589
|
+
.filter((id: ObjectID) => {
|
|
2590
|
+
return Boolean(id); // remove nulls
|
|
2591
|
+
});
|
|
2592
|
+
|
|
2593
|
+
// get monitors in the group.
|
|
2594
|
+
const monitorGroupCurrentStatuses: Dictionary<ObjectID> = {};
|
|
2595
|
+
const monitorsInGroup: Dictionary<Array<ObjectID>> = {};
|
|
2596
|
+
|
|
2597
|
+
// get monitor status charts.
|
|
2598
|
+
const monitorsOnStatusPage: Array<ObjectID> = statusPageResources
|
|
2599
|
+
.map((monitor: StatusPageResource) => {
|
|
2600
|
+
return monitor.monitorId!;
|
|
2601
|
+
})
|
|
2602
|
+
.filter((id: ObjectID) => {
|
|
2603
|
+
return Boolean(id); // remove nulls
|
|
2604
|
+
});
|
|
2605
|
+
|
|
2606
|
+
const monitorsOnStatusPageForTimeline: Array<ObjectID> = statusPageResources
|
|
2607
|
+
.filter((monitor: StatusPageResource) => {
|
|
2608
|
+
return monitor.showStatusHistoryChart || monitor.showUptimePercent;
|
|
2609
|
+
})
|
|
2610
|
+
.map((monitor: StatusPageResource) => {
|
|
2611
|
+
return monitor.monitorId!;
|
|
2612
|
+
})
|
|
2613
|
+
.filter((id: ObjectID) => {
|
|
2614
|
+
return Boolean(id); // remove nulls
|
|
2615
|
+
});
|
|
2616
|
+
|
|
2617
|
+
for (const monitorGroupId of monitorGroupIds) {
|
|
2618
|
+
// get current status of monitors in the group.
|
|
2619
|
+
|
|
2620
|
+
const currentStatus: MonitorStatus =
|
|
2621
|
+
await MonitorGroupService.getCurrentStatus(monitorGroupId, {
|
|
2622
|
+
isRoot: true,
|
|
2623
|
+
});
|
|
2624
|
+
|
|
2625
|
+
monitorGroupCurrentStatuses[monitorGroupId.toString()] =
|
|
2626
|
+
currentStatus.id!;
|
|
2627
|
+
|
|
2628
|
+
// get monitors in the group.
|
|
2629
|
+
|
|
2630
|
+
const groupResources: Array<MonitorGroupResource> =
|
|
2631
|
+
await MonitorGroupResourceService.findBy({
|
|
2632
|
+
query: {
|
|
2633
|
+
monitorGroupId: monitorGroupId,
|
|
2634
|
+
},
|
|
2635
|
+
select: {
|
|
2636
|
+
monitorId: true,
|
|
2637
|
+
},
|
|
2638
|
+
props: {
|
|
2639
|
+
isRoot: true,
|
|
2640
|
+
},
|
|
2641
|
+
limit: LIMIT_PER_PROJECT,
|
|
2642
|
+
skip: 0,
|
|
2643
|
+
});
|
|
2644
|
+
|
|
2645
|
+
const monitorsInGroupIds: Array<ObjectID> = groupResources
|
|
2646
|
+
.map((resource: MonitorGroupResource) => {
|
|
2647
|
+
return resource.monitorId!;
|
|
2648
|
+
})
|
|
2649
|
+
.filter((id: ObjectID) => {
|
|
2650
|
+
return Boolean(id); // remove nulls
|
|
2651
|
+
});
|
|
2652
|
+
|
|
2653
|
+
const shouldShowTimelineForThisGroup: boolean = Boolean(
|
|
2654
|
+
statusPageResources.find((resource: StatusPageResource) => {
|
|
2655
|
+
return (
|
|
2656
|
+
resource.monitorGroupId?.toString() === monitorGroupId.toString() &&
|
|
2657
|
+
(resource.showStatusHistoryChart || resource.showUptimePercent)
|
|
2658
|
+
);
|
|
2659
|
+
}),
|
|
2660
|
+
);
|
|
2661
|
+
|
|
2662
|
+
for (const monitorId of monitorsInGroupIds) {
|
|
2663
|
+
if (!monitorId) {
|
|
2664
|
+
continue;
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
if (
|
|
2668
|
+
!monitorsOnStatusPage.find((item: ObjectID) => {
|
|
2669
|
+
return item.toString() === monitorId.toString();
|
|
2670
|
+
})
|
|
2671
|
+
) {
|
|
2672
|
+
monitorsOnStatusPage.push(monitorId);
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
// add this to the timeline event for this group.
|
|
2676
|
+
|
|
2677
|
+
if (
|
|
2678
|
+
shouldShowTimelineForThisGroup &&
|
|
2679
|
+
!monitorsOnStatusPageForTimeline.find((item: ObjectID) => {
|
|
2680
|
+
return item.toString() === monitorId.toString();
|
|
2681
|
+
})
|
|
2682
|
+
) {
|
|
2683
|
+
monitorsOnStatusPageForTimeline.push(monitorId);
|
|
2684
|
+
}
|
|
2685
|
+
}
|
|
2686
|
+
|
|
2687
|
+
monitorsInGroup[monitorGroupId.toString()] = monitorsInGroupIds;
|
|
2688
|
+
}
|
|
2689
|
+
|
|
2690
|
+
const monitorStatusTimelines: Array<MonitorStatusTimeline> =
|
|
2691
|
+
await StatusPageService.getMonitorStatusTimelineForStatusPage({
|
|
2692
|
+
monitorIds: monitorsOnStatusPageForTimeline,
|
|
2693
|
+
startDate: data.startDateForMonitorTimeline,
|
|
2694
|
+
endDate: data.endDateForMonitorTimeline,
|
|
2695
|
+
});
|
|
2696
|
+
|
|
2697
|
+
// return everything.
|
|
2698
|
+
|
|
2699
|
+
return {
|
|
2700
|
+
statusPageResources,
|
|
2701
|
+
monitorStatuses,
|
|
2702
|
+
monitorGroupCurrentStatuses,
|
|
2703
|
+
statusPageGroups: groups,
|
|
2704
|
+
monitorStatusTimelines,
|
|
2705
|
+
statusPage,
|
|
2706
|
+
monitorsOnStatusPage,
|
|
2707
|
+
monitorsInGroup,
|
|
2708
|
+
};
|
|
2709
|
+
}
|
|
2355
2710
|
}
|