@oneuptime/common 7.0.3691 → 7.0.3697

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.
@@ -58,9 +58,13 @@ import StatusPageHistoryChartBarColorRule from "Common/Models/DatabaseModels/Sta
58
58
  import StatusPageResource from "Common/Models/DatabaseModels/StatusPageResource";
59
59
  import StatusPageSSO from "Common/Models/DatabaseModels/StatusPageSso";
60
60
  import StatusPageSubscriber from "Common/Models/DatabaseModels/StatusPageSubscriber";
61
+ import StatusPageResourceUptimeUtil from "../../Utils/StatusPage/ResourceUptime";
62
+ import UptimePrecision from "../../Types/StatusPage/UptimePrecision";
63
+ import { Green } from "../../Types/BrandColors";
64
+ import UptimeUtil from "../../Utils/Uptime/UptimeUtil";
61
65
  export default class StatusPageAPI extends BaseAPI {
62
66
  constructor() {
63
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
67
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
64
68
  super(StatusPage, StatusPageService);
65
69
  // confirm subscription api
66
70
  this.router.get(`${(_a = new this.entityType()
@@ -232,6 +236,9 @@ export default class StatusPageAPI extends BaseAPI {
232
236
  type: true,
233
237
  name: true,
234
238
  },
239
+ showIncidentsOnStatusPage: true,
240
+ showAnnouncementsOnStatusPage: true,
241
+ showScheduledMaintenanceEventsOnStatusPage: true,
235
242
  };
236
243
  const hasEnabledSSO = await StatusPageSsoService.countBy({
237
244
  query: {
@@ -363,196 +370,196 @@ export default class StatusPageAPI extends BaseAPI {
363
370
  }
364
371
  });
365
372
  this.router.post(`${(_j = new this.entityType()
366
- .getCrudApiPath()) === null || _j === void 0 ? void 0 : _j.toString()}/overview/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
373
+ .getCrudApiPath()) === null || _j === void 0 ? void 0 : _j.toString()}/uptime/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
367
374
  try {
368
- const objectId = new ObjectID(req.params["statusPageId"]);
369
- if (!(await this.service.hasReadAccess(objectId, await CommonAPI.getDatabaseCommonInteractionProps(req), req))) {
375
+ // This reosurce ID can be of a status page resource OR a status page group.
376
+ const statusPageResourceId = new ObjectID(req.params["statusPageResourceId"]);
377
+ const statusPageId = new ObjectID(req.params["statusPageId"]);
378
+ if (!statusPageId || !statusPageResourceId) {
379
+ throw new BadDataException("Status Page or Resource not found");
380
+ }
381
+ if (!(await this.service.hasReadAccess(statusPageId, await CommonAPI.getDatabaseCommonInteractionProps(req), req))) {
370
382
  throw new NotAuthenticatedException("You are not authenticated to access this status page");
371
383
  }
372
- const statusPage = await StatusPageService.findOneBy({
373
- query: {
374
- _id: objectId.toString(),
375
- },
376
- select: {
377
- _id: true,
378
- projectId: true,
379
- isPublicStatusPage: true,
380
- overviewPageDescription: true,
381
- showIncidentLabelsOnStatusPage: true,
382
- showScheduledEventLabelsOnStatusPage: true,
383
- downtimeMonitorStatuses: {
384
- _id: true,
385
- },
386
- defaultBarColor: true,
387
- showOverallUptimePercentOnStatusPage: true,
388
- overallUptimePercentPrecision: true,
389
- },
390
- props: {
391
- isRoot: true,
392
- },
393
- });
394
- if (!statusPage) {
395
- throw new BadDataException("Status Page not found");
384
+ // get start and end date from request body.
385
+ // if no end date is provided then it will be current date.
386
+ // if no start date is provided then it will be 14 days ago from end date.
387
+ let startDate = OneUptimeDate.getSomeDaysAgo(14);
388
+ let endDate = OneUptimeDate.getCurrentDate();
389
+ if (req.body["startDate"]) {
390
+ startDate = OneUptimeDate.fromString(req.body["startDate"]);
396
391
  }
397
- //get monitor statuses
398
- const monitorStatuses = await MonitorStatusService.findBy({
399
- query: {
400
- projectId: statusPage.projectId,
401
- },
402
- select: {
403
- name: true,
404
- color: true,
405
- priority: true,
406
- isOperationalState: true,
407
- },
408
- sort: {
409
- priority: SortOrder.Ascending,
410
- },
411
- skip: 0,
412
- limit: LIMIT_PER_PROJECT,
413
- props: {
414
- isRoot: true,
415
- },
416
- });
417
- // get resource groups.
418
- const groups = await StatusPageGroupService.findBy({
419
- query: {
420
- statusPageId: objectId,
421
- },
422
- select: {
423
- name: true,
424
- order: true,
425
- description: true,
426
- isExpandedByDefault: true,
427
- showCurrentStatus: true,
428
- showUptimePercent: true,
429
- uptimePercentPrecision: true,
430
- },
431
- sort: {
432
- order: SortOrder.Ascending,
433
- },
434
- skip: 0,
435
- limit: LIMIT_PER_PROJECT,
436
- props: {
437
- isRoot: true,
438
- },
439
- });
440
- // get monitors on status page.
441
- const statusPageResources = await StatusPageResourceService.findBy({
442
- query: {
443
- statusPageId: objectId,
444
- },
445
- select: {
446
- statusPageGroupId: true,
447
- monitorId: true,
448
- displayTooltip: true,
449
- displayDescription: true,
450
- displayName: true,
451
- showStatusHistoryChart: true,
452
- showCurrentStatus: true,
453
- order: true,
454
- monitor: {
455
- _id: true,
456
- currentMonitorStatusId: true,
457
- },
458
- monitorGroupId: true,
459
- showUptimePercent: true,
460
- uptimePercentPrecision: true,
461
- },
462
- sort: {
463
- order: SortOrder.Ascending,
464
- },
465
- skip: 0,
466
- limit: LIMIT_PER_PROJECT,
467
- props: {
468
- isRoot: true,
469
- },
470
- });
471
- const monitorGroupIds = statusPageResources
472
- .map((resource) => {
473
- return resource.monitorGroupId;
474
- })
475
- .filter((id) => {
476
- return Boolean(id); // remove nulls
477
- });
478
- // get monitors in the group.
479
- const monitorGroupCurrentStatuses = {};
480
- const monitorsInGroup = {};
481
- // get monitor status charts.
482
- const monitorsOnStatusPage = statusPageResources
483
- .map((monitor) => {
484
- return monitor.monitorId;
485
- })
486
- .filter((id) => {
487
- return Boolean(id); // remove nulls
488
- });
489
- const monitorsOnStatusPageForTimeline = statusPageResources
490
- .filter((monitor) => {
491
- return (monitor.showStatusHistoryChart || monitor.showUptimePercent);
492
- })
493
- .map((monitor) => {
494
- return monitor.monitorId;
495
- })
496
- .filter((id) => {
497
- return Boolean(id); // remove nulls
392
+ if (req.body["endDate"]) {
393
+ endDate = OneUptimeDate.fromString(req.body["endDate"]);
394
+ }
395
+ if (OneUptimeDate.isAfter(startDate, endDate)) {
396
+ throw new BadDataException("Start date cannot be after end date");
397
+ }
398
+ if (OneUptimeDate.getDaysBetweenTwoDatesInclusive(startDate, endDate) >
399
+ 90) {
400
+ throw new BadDataException("You can only get uptime for 90 days. Please select a date range within 90 days.");
401
+ }
402
+ const { monitorStatuses, monitorGroupCurrentStatuses, statusPageResources, statusPage, monitorStatusTimelines, statusPageGroups, monitorsInGroup, } = await this.getStatusPageResourcesAndTimelines({
403
+ statusPageId: statusPageId,
404
+ startDateForMonitorTimeline: startDate,
405
+ endDateForMonitorTimeline: endDate,
498
406
  });
499
- for (const monitorGroupId of monitorGroupIds) {
500
- // get current status of monitors in the group.
501
- const currentStatus = await MonitorGroupService.getCurrentStatus(monitorGroupId, {
502
- isRoot: true,
503
- });
504
- monitorGroupCurrentStatuses[monitorGroupId.toString()] =
505
- currentStatus.id;
506
- // get monitors in the group.
507
- const groupResources = await MonitorGroupResourceService.findBy({
508
- query: {
509
- monitorGroupId: monitorGroupId,
510
- },
511
- select: {
512
- monitorId: true,
513
- },
514
- props: {
515
- isRoot: true,
516
- },
517
- limit: LIMIT_PER_PROJECT,
518
- skip: 0,
519
- });
520
- const monitorsInGroupIds = groupResources
521
- .map((resource) => {
522
- return resource.monitorId;
523
- })
524
- .filter((id) => {
525
- return Boolean(id); // remove nulls
526
- });
527
- const shouldShowTimelineForThisGroup = Boolean(statusPageResources.find((resource) => {
528
- var _a;
529
- return (((_a = resource.monitorGroupId) === null || _a === void 0 ? void 0 : _a.toString()) ===
530
- monitorGroupId.toString() &&
531
- (resource.showStatusHistoryChart ||
532
- resource.showUptimePercent));
533
- }));
534
- for (const monitorId of monitorsInGroupIds) {
535
- if (!monitorId) {
536
- continue;
537
- }
538
- if (!monitorsOnStatusPage.find((item) => {
539
- return item.toString() === monitorId.toString();
540
- })) {
541
- monitorsOnStatusPage.push(monitorId);
542
- }
543
- // add this to the timeline event for this group.
544
- if (shouldShowTimelineForThisGroup &&
545
- !monitorsOnStatusPageForTimeline.find((item) => {
546
- return item.toString() === monitorId.toString();
547
- })) {
548
- monitorsOnStatusPageForTimeline.push(monitorId);
407
+ const downtimeMonitorStatuses = statusPage.downtimeMonitorStatuses || [];
408
+ const getUptimeByStatusPageGroup = (data) => {
409
+ var _a, _b, _c, _d, _e;
410
+ const groupUptime = {
411
+ statusPageGroupId: data && data.statusPageGroup
412
+ ? (_a = data.statusPageGroup) === null || _a === void 0 ? void 0 : _a.id
413
+ : null,
414
+ uptimePercent: null,
415
+ statusPageResourceUptimes: [],
416
+ statusPageGroupName: ((_b = data.statusPageGroup) === null || _b === void 0 ? void 0 : _b.name) || null,
417
+ currentStatus: null,
418
+ };
419
+ const group = data.statusPageGroup;
420
+ for (const resource of statusPageResources) {
421
+ if ((resource.statusPageGroupId &&
422
+ resource.statusPageGroupId.toString() &&
423
+ group &&
424
+ ((_c = group._id) === null || _c === void 0 ? void 0 : _c.toString()) &&
425
+ ((_d = group._id) === null || _d === void 0 ? void 0 : _d.toString()) ===
426
+ resource.statusPageGroupId.toString()) ||
427
+ (!resource.statusPageGroupId && !group)) {
428
+ // if its not a monitor or a monitor group, then continue. This should ideally not happen.
429
+ if (!resource.monitor && !resource.monitorGroupId) {
430
+ continue;
431
+ }
432
+ const resourceUptime = {
433
+ statusPageResourceId: resource.id,
434
+ uptimePercent: null,
435
+ statusPageResourceName: resource.displayName || ((_e = resource.monitor) === null || _e === void 0 ? void 0 : _e.name) || "",
436
+ currentStatus: null,
437
+ };
438
+ // if its a monitor
439
+ const precision = resource.uptimePercentPrecision ||
440
+ UptimePrecision.ONE_DECIMAL;
441
+ if (resource.monitor) {
442
+ let currentStatus = monitorStatuses.find((status) => {
443
+ var _a, _b, _c;
444
+ return (((_a = status._id) === null || _a === void 0 ? void 0 : _a.toString()) ===
445
+ ((_c = (_b = resource.monitor) === null || _b === void 0 ? void 0 : _b.currentMonitorStatusId) === null || _c === void 0 ? void 0 : _c.toString()));
446
+ });
447
+ if (!currentStatus) {
448
+ currentStatus = new MonitorStatus();
449
+ currentStatus.name = "Operational";
450
+ currentStatus.color = Green;
451
+ resourceUptime.currentStatus = currentStatus;
452
+ }
453
+ else {
454
+ resourceUptime.currentStatus = currentStatus;
455
+ }
456
+ if (!resource.showCurrentStatus) {
457
+ resourceUptime.currentStatus = null;
458
+ }
459
+ const resourceStatusTimelines = StatusPageResourceUptimeUtil.getMonitorStatusTimelineForResource({
460
+ statusPageResource: resource,
461
+ monitorStatusTimelines: monitorStatusTimelines,
462
+ monitorsInGroup: monitorsInGroup,
463
+ });
464
+ if (resource.showUptimePercent) {
465
+ const uptimePercent = UptimeUtil.calculateUptimePercentage(resourceStatusTimelines, precision, downtimeMonitorStatuses);
466
+ resourceUptime.uptimePercent = uptimePercent;
467
+ }
468
+ groupUptime.statusPageResourceUptimes.push(resourceUptime);
469
+ }
470
+ // if its a monitor group, then...
471
+ if (resource.monitorGroupId) {
472
+ let currentStatus = monitorStatuses.find((status) => {
473
+ var _a, _b, _c;
474
+ return (((_a = status._id) === null || _a === void 0 ? void 0 : _a.toString()) ===
475
+ ((_c = monitorGroupCurrentStatuses[((_b = resource.monitorGroupId) === null || _b === void 0 ? void 0 : _b.toString()) || ""]) === null || _c === void 0 ? void 0 : _c.toString()));
476
+ });
477
+ if (!currentStatus) {
478
+ currentStatus = new MonitorStatus();
479
+ currentStatus.name = "Operational";
480
+ currentStatus.color = Green;
481
+ resourceUptime.currentStatus = currentStatus;
482
+ }
483
+ else {
484
+ resourceUptime.currentStatus = currentStatus;
485
+ }
486
+ if (!resource.showCurrentStatus) {
487
+ resourceUptime.currentStatus = null;
488
+ }
489
+ if (resource.showUptimePercent) {
490
+ const resourceStatusTimelines = StatusPageResourceUptimeUtil.getMonitorStatusTimelineForResource({
491
+ statusPageResource: resource,
492
+ monitorStatusTimelines: monitorStatusTimelines,
493
+ monitorsInGroup: monitorsInGroup,
494
+ });
495
+ const uptimePercent = UptimeUtil.calculateUptimePercentage(resourceStatusTimelines, precision, downtimeMonitorStatuses);
496
+ resourceUptime.uptimePercent = uptimePercent;
497
+ }
498
+ groupUptime.statusPageResourceUptimes.push(resourceUptime);
499
+ }
549
500
  }
550
501
  }
551
- monitorsInGroup[monitorGroupId.toString()] = monitorsInGroupIds;
502
+ if (group === null || group === void 0 ? void 0 : group.showUptimePercent) {
503
+ // calculate uptime percent for the group.
504
+ const avgUptimePercent = UptimeUtil.calculateAvgUptimePercentage({
505
+ uptimePercentages: groupUptime.statusPageResourceUptimes
506
+ .filter((resource) => {
507
+ return resource.uptimePercent !== null;
508
+ })
509
+ .map((resource) => {
510
+ return resource.uptimePercent || 0;
511
+ }),
512
+ precision: group.uptimePercentPrecision ||
513
+ UptimePrecision.ONE_DECIMAL,
514
+ });
515
+ groupUptime.uptimePercent = avgUptimePercent;
516
+ }
517
+ if (group === null || group === void 0 ? void 0 : group.showCurrentStatus) {
518
+ const currentStatuses = groupUptime.statusPageResourceUptimes
519
+ .filter((resourceUptime) => {
520
+ return resourceUptime.currentStatus !== null;
521
+ })
522
+ .map((resourceUptime) => {
523
+ return resourceUptime.currentStatus;
524
+ });
525
+ const worstStatus = StatusPageResourceUptimeUtil.getWorstMonitorStatus({
526
+ monitorStatuses: currentStatuses,
527
+ });
528
+ groupUptime.currentStatus = worstStatus;
529
+ }
530
+ return groupUptime;
531
+ };
532
+ const groupUptimes = [];
533
+ for (const group of statusPageGroups) {
534
+ groupUptimes.push(getUptimeByStatusPageGroup({ statusPageGroup: group }));
552
535
  }
553
- const monitorStatusTimelines = await StatusPageService.getMonitorStatusTimelineForStatusPage({
554
- monitorIds: monitorsOnStatusPageForTimeline,
555
- historyDays: 90,
536
+ return Response.sendJsonObjectResponse(req, res, {
537
+ statusPageResourceUptimes: [
538
+ ...getUptimeByStatusPageGroup({ statusPageGroup: null })
539
+ .statusPageResourceUptimes,
540
+ ],
541
+ groupUptimes: groupUptimes,
542
+ startDate: startDate,
543
+ endDate: endDate,
544
+ });
545
+ }
546
+ catch (err) {
547
+ next(err);
548
+ }
549
+ });
550
+ this.router.post(`${(_k = new this.entityType()
551
+ .getCrudApiPath()) === null || _k === void 0 ? void 0 : _k.toString()}/overview/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
552
+ try {
553
+ const objectId = new ObjectID(req.params["statusPageId"]);
554
+ if (!(await this.service.hasReadAccess(objectId, await CommonAPI.getDatabaseCommonInteractionProps(req), req))) {
555
+ throw new NotAuthenticatedException("You are not authenticated to access this status page");
556
+ }
557
+ const startDate = OneUptimeDate.getSomeDaysAgo(90);
558
+ const endDate = OneUptimeDate.getCurrentDate();
559
+ const { monitorStatuses, monitorGroupCurrentStatuses, statusPageResources, statusPage, monitorsOnStatusPage, monitorStatusTimelines, statusPageGroups, monitorsInGroup, } = await this.getStatusPageResourcesAndTimelines({
560
+ statusPageId: objectId,
561
+ startDateForMonitorTimeline: startDate,
562
+ endDateForMonitorTimeline: endDate,
556
563
  });
557
564
  // check if status page has active incident.
558
565
  let activeIncidents = [];
@@ -588,23 +595,25 @@ export default class StatusPageAPI extends BaseAPI {
588
595
  const unresolvedIncidentStateIds = unresolvedIncidentStates.map((state) => {
589
596
  return state.id;
590
597
  });
591
- activeIncidents = await IncidentService.findBy({
592
- query: {
593
- monitors: monitorsOnStatusPage,
594
- currentIncidentStateId: QueryHelper.any(unresolvedIncidentStateIds),
595
- isVisibleOnStatusPage: true,
596
- projectId: statusPage.projectId,
597
- },
598
- select: select,
599
- sort: {
600
- createdAt: SortOrder.Ascending,
601
- },
602
- skip: 0,
603
- limit: LIMIT_PER_PROJECT,
604
- props: {
605
- isRoot: true,
606
- },
607
- });
598
+ if (statusPage.showIncidentsOnStatusPage) {
599
+ activeIncidents = await IncidentService.findBy({
600
+ query: {
601
+ monitors: monitorsOnStatusPage,
602
+ currentIncidentStateId: QueryHelper.any(unresolvedIncidentStateIds),
603
+ isVisibleOnStatusPage: true,
604
+ projectId: statusPage.projectId,
605
+ },
606
+ select: select,
607
+ sort: {
608
+ createdAt: SortOrder.Ascending,
609
+ },
610
+ skip: 0,
611
+ limit: LIMIT_PER_PROJECT,
612
+ props: {
613
+ isRoot: true,
614
+ },
615
+ });
616
+ }
608
617
  }
609
618
  const incidentsOnStatusPage = activeIncidents.map((incident) => {
610
619
  return incident.id;
@@ -663,27 +672,30 @@ export default class StatusPageAPI extends BaseAPI {
663
672
  }
664
673
  // check if status page has active announcement.
665
674
  const today = OneUptimeDate.getCurrentDate();
666
- const activeAnnouncements = await StatusPageAnnouncementService.findBy({
667
- query: {
668
- statusPages: objectId,
669
- showAnnouncementAt: QueryHelper.lessThan(today),
670
- endAnnouncementAt: QueryHelper.greaterThanOrNull(today),
671
- projectId: statusPage.projectId,
672
- },
673
- select: {
674
- createdAt: true,
675
- title: true,
676
- description: true,
677
- _id: true,
678
- showAnnouncementAt: true,
679
- endAnnouncementAt: true,
680
- },
681
- skip: 0,
682
- limit: LIMIT_PER_PROJECT,
683
- props: {
684
- isRoot: true,
685
- },
686
- });
675
+ let activeAnnouncements = [];
676
+ if (statusPage.showAnnouncementsOnStatusPage) {
677
+ activeAnnouncements = await StatusPageAnnouncementService.findBy({
678
+ query: {
679
+ statusPages: objectId,
680
+ showAnnouncementAt: QueryHelper.lessThan(today),
681
+ endAnnouncementAt: QueryHelper.greaterThanOrNull(today),
682
+ projectId: statusPage.projectId,
683
+ },
684
+ select: {
685
+ createdAt: true,
686
+ title: true,
687
+ description: true,
688
+ _id: true,
689
+ showAnnouncementAt: true,
690
+ endAnnouncementAt: true,
691
+ },
692
+ skip: 0,
693
+ limit: LIMIT_PER_PROJECT,
694
+ props: {
695
+ isRoot: true,
696
+ },
697
+ });
698
+ }
687
699
  // check if status page has active scheduled events.
688
700
  let scheduledEventsSelect = {
689
701
  createdAt: true,
@@ -709,44 +721,52 @@ export default class StatusPageAPI extends BaseAPI {
709
721
  color: true,
710
722
  } });
711
723
  }
712
- const scheduledMaintenanceEvents = await ScheduledMaintenanceService.findBy({
713
- query: {
714
- currentScheduledMaintenanceState: {
715
- isOngoingState: true,
716
- },
717
- statusPages: objectId,
718
- projectId: statusPage.projectId,
719
- isVisibleOnStatusPage: true,
720
- },
721
- select: scheduledEventsSelect,
722
- sort: {
723
- startsAt: SortOrder.Ascending,
724
- },
725
- skip: 0,
726
- limit: LIMIT_PER_PROJECT,
727
- props: {
728
- isRoot: true,
729
- },
730
- });
731
- const futureScheduledMaintenanceEvents = await ScheduledMaintenanceService.findBy({
732
- query: {
733
- currentScheduledMaintenanceState: {
734
- isScheduledState: true,
735
- },
736
- statusPages: objectId,
737
- projectId: statusPage.projectId,
738
- isVisibleOnStatusPage: true,
739
- },
740
- select: scheduledEventsSelect,
741
- sort: {
742
- startsAt: SortOrder.Ascending,
743
- },
744
- skip: 0,
745
- limit: LIMIT_PER_PROJECT,
746
- props: {
747
- isRoot: true,
748
- },
749
- });
724
+ let scheduledMaintenanceEvents = [];
725
+ if (statusPage.showScheduledMaintenanceEventsOnStatusPage) {
726
+ scheduledMaintenanceEvents =
727
+ await ScheduledMaintenanceService.findBy({
728
+ query: {
729
+ currentScheduledMaintenanceState: {
730
+ isOngoingState: true,
731
+ },
732
+ statusPages: objectId,
733
+ projectId: statusPage.projectId,
734
+ isVisibleOnStatusPage: true,
735
+ },
736
+ select: scheduledEventsSelect,
737
+ sort: {
738
+ startsAt: SortOrder.Ascending,
739
+ },
740
+ skip: 0,
741
+ limit: LIMIT_PER_PROJECT,
742
+ props: {
743
+ isRoot: true,
744
+ },
745
+ });
746
+ }
747
+ let futureScheduledMaintenanceEvents = [];
748
+ if (statusPage.showScheduledMaintenanceEventsOnStatusPage) {
749
+ futureScheduledMaintenanceEvents =
750
+ await ScheduledMaintenanceService.findBy({
751
+ query: {
752
+ currentScheduledMaintenanceState: {
753
+ isScheduledState: true,
754
+ },
755
+ statusPages: objectId,
756
+ projectId: statusPage.projectId,
757
+ isVisibleOnStatusPage: true,
758
+ },
759
+ select: scheduledEventsSelect,
760
+ sort: {
761
+ startsAt: SortOrder.Ascending,
762
+ },
763
+ skip: 0,
764
+ limit: LIMIT_PER_PROJECT,
765
+ props: {
766
+ isRoot: true,
767
+ },
768
+ });
769
+ }
750
770
  futureScheduledMaintenanceEvents.forEach((event) => {
751
771
  scheduledMaintenanceEvents.push(event);
752
772
  });
@@ -840,7 +860,7 @@ export default class StatusPageAPI extends BaseAPI {
840
860
  incidentPublicNotes: BaseModel.toJSONArray(incidentPublicNotes, IncidentPublicNote),
841
861
  activeIncidents: BaseModel.toJSONArray(activeIncidents, Incident),
842
862
  monitorStatusTimelines: BaseModel.toJSONArray(monitorStatusTimelines, MonitorStatusTimeline),
843
- resourceGroups: BaseModel.toJSONArray(groups, StatusPageGroup),
863
+ resourceGroups: BaseModel.toJSONArray(statusPageGroups, StatusPageGroup),
844
864
  monitorStatuses: BaseModel.toJSONArray(monitorStatuses, MonitorStatus),
845
865
  statusPageResources: BaseModel.toJSONArray(statusPageResources, StatusPageResource),
846
866
  incidentStateTimelines: BaseModel.toJSONArray(incidentStateTimelines, IncidentStateTimeline),
@@ -855,8 +875,8 @@ export default class StatusPageAPI extends BaseAPI {
855
875
  next(err);
856
876
  }
857
877
  });
858
- this.router.put(`${(_k = new this.entityType()
859
- .getCrudApiPath()) === null || _k === void 0 ? void 0 : _k.toString()}/update-subscription/:statusPageId/:subscriberId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
878
+ this.router.put(`${(_l = new this.entityType()
879
+ .getCrudApiPath()) === null || _l === void 0 ? void 0 : _l.toString()}/update-subscription/:statusPageId/:subscriberId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
860
880
  try {
861
881
  await this.subscribeToStatusPage(req);
862
882
  return Response.sendEmptySuccessResponse(req, res);
@@ -865,8 +885,8 @@ export default class StatusPageAPI extends BaseAPI {
865
885
  next(err);
866
886
  }
867
887
  });
868
- this.router.post(`${(_l = new this.entityType()
869
- .getCrudApiPath()) === null || _l === void 0 ? void 0 : _l.toString()}/get-subscription/:statusPageId/:subscriberId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
888
+ this.router.post(`${(_m = new this.entityType()
889
+ .getCrudApiPath()) === null || _m === void 0 ? void 0 : _m.toString()}/get-subscription/:statusPageId/:subscriberId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
870
890
  try {
871
891
  const subscriber = await this.getSubscriber(req);
872
892
  return Response.sendEntityResponse(req, res, subscriber, StatusPageSubscriber);
@@ -875,8 +895,8 @@ export default class StatusPageAPI extends BaseAPI {
875
895
  next(err);
876
896
  }
877
897
  });
878
- this.router.post(`${(_m = new this.entityType()
879
- .getCrudApiPath()) === null || _m === void 0 ? void 0 : _m.toString()}/subscribe/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
898
+ this.router.post(`${(_o = new this.entityType()
899
+ .getCrudApiPath()) === null || _o === void 0 ? void 0 : _o.toString()}/subscribe/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
880
900
  try {
881
901
  await this.subscribeToStatusPage(req);
882
902
  return Response.sendEmptySuccessResponse(req, res);
@@ -885,8 +905,8 @@ export default class StatusPageAPI extends BaseAPI {
885
905
  next(err);
886
906
  }
887
907
  });
888
- this.router.post(`${(_o = new this.entityType()
889
- .getCrudApiPath()) === null || _o === void 0 ? void 0 : _o.toString()}/incidents/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
908
+ this.router.post(`${(_p = new this.entityType()
909
+ .getCrudApiPath()) === null || _p === void 0 ? void 0 : _p.toString()}/incidents/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
890
910
  try {
891
911
  const objectId = new ObjectID(req.params["statusPageId"]);
892
912
  const response = await this.getIncidents(objectId, null, await CommonAPI.getDatabaseCommonInteractionProps(req), req);
@@ -896,8 +916,8 @@ export default class StatusPageAPI extends BaseAPI {
896
916
  next(err);
897
917
  }
898
918
  });
899
- this.router.post(`${(_p = new this.entityType()
900
- .getCrudApiPath()) === null || _p === void 0 ? void 0 : _p.toString()}/scheduled-maintenance-events/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
919
+ this.router.post(`${(_q = new this.entityType()
920
+ .getCrudApiPath()) === null || _q === void 0 ? void 0 : _q.toString()}/scheduled-maintenance-events/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
901
921
  try {
902
922
  const objectId = new ObjectID(req.params["statusPageId"]);
903
923
  const response = await this.getScheduledMaintenanceEvents(objectId, null, await CommonAPI.getDatabaseCommonInteractionProps(req), req);
@@ -907,8 +927,8 @@ export default class StatusPageAPI extends BaseAPI {
907
927
  next(err);
908
928
  }
909
929
  });
910
- this.router.post(`${(_q = new this.entityType()
911
- .getCrudApiPath()) === null || _q === void 0 ? void 0 : _q.toString()}/announcements/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
930
+ this.router.post(`${(_r = new this.entityType()
931
+ .getCrudApiPath()) === null || _r === void 0 ? void 0 : _r.toString()}/announcements/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
912
932
  try {
913
933
  const objectId = new ObjectID(req.params["statusPageId"]);
914
934
  const response = await this.getAnnouncements(objectId, null, await CommonAPI.getDatabaseCommonInteractionProps(req), req);
@@ -918,8 +938,8 @@ export default class StatusPageAPI extends BaseAPI {
918
938
  next(err);
919
939
  }
920
940
  });
921
- this.router.post(`${(_r = new this.entityType()
922
- .getCrudApiPath()) === null || _r === void 0 ? void 0 : _r.toString()}/incidents/:statusPageId/:incidentId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
941
+ this.router.post(`${(_s = new this.entityType()
942
+ .getCrudApiPath()) === null || _s === void 0 ? void 0 : _s.toString()}/incidents/:statusPageId/:incidentId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
923
943
  try {
924
944
  const objectId = new ObjectID(req.params["statusPageId"]);
925
945
  const incidentId = new ObjectID(req.params["incidentId"]);
@@ -930,8 +950,8 @@ export default class StatusPageAPI extends BaseAPI {
930
950
  next(err);
931
951
  }
932
952
  });
933
- this.router.post(`${(_s = new this.entityType()
934
- .getCrudApiPath()) === null || _s === void 0 ? void 0 : _s.toString()}/scheduled-maintenance-events/:statusPageId/:scheduledMaintenanceId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
953
+ this.router.post(`${(_t = new this.entityType()
954
+ .getCrudApiPath()) === null || _t === void 0 ? void 0 : _t.toString()}/scheduled-maintenance-events/:statusPageId/:scheduledMaintenanceId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
935
955
  try {
936
956
  const objectId = new ObjectID(req.params["statusPageId"]);
937
957
  const scheduledMaintenanceId = new ObjectID(req.params["scheduledMaintenanceId"]);
@@ -942,8 +962,8 @@ export default class StatusPageAPI extends BaseAPI {
942
962
  next(err);
943
963
  }
944
964
  });
945
- this.router.post(`${(_t = new this.entityType()
946
- .getCrudApiPath()) === null || _t === void 0 ? void 0 : _t.toString()}/announcements/:statusPageId/:announcementId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
965
+ this.router.post(`${(_u = new this.entityType()
966
+ .getCrudApiPath()) === null || _u === void 0 ? void 0 : _u.toString()}/announcements/:statusPageId/:announcementId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
947
967
  try {
948
968
  const objectId = new ObjectID(req.params["statusPageId"]);
949
969
  const announcementId = new ObjectID(req.params["announcementId"]);
@@ -968,6 +988,7 @@ export default class StatusPageAPI extends BaseAPI {
968
988
  projectId: true,
969
989
  showScheduledEventHistoryInDays: true,
970
990
  showScheduledEventLabelsOnStatusPage: true,
991
+ showScheduledMaintenanceEventsOnStatusPage: true,
971
992
  },
972
993
  props: {
973
994
  isRoot: true,
@@ -976,6 +997,9 @@ export default class StatusPageAPI extends BaseAPI {
976
997
  if (!statusPage) {
977
998
  throw new BadDataException("Status Page not found");
978
999
  }
1000
+ if (!statusPage.showScheduledMaintenanceEventsOnStatusPage) {
1001
+ throw new BadDataException("Scheduled Maintenance Events are not enabled on this status page");
1002
+ }
979
1003
  // get monitors on status page.
980
1004
  const statusPageResources = await StatusPageService.getStatusPageResources({
981
1005
  statusPageId: statusPageId,
@@ -1204,6 +1228,7 @@ export default class StatusPageAPI extends BaseAPI {
1204
1228
  _id: true,
1205
1229
  projectId: true,
1206
1230
  showAnnouncementHistoryInDays: true,
1231
+ showAnnouncementsOnStatusPage: true,
1207
1232
  },
1208
1233
  props: {
1209
1234
  isRoot: true,
@@ -1212,6 +1237,9 @@ export default class StatusPageAPI extends BaseAPI {
1212
1237
  if (!statusPage) {
1213
1238
  throw new BadDataException("Status Page not found");
1214
1239
  }
1240
+ if (!statusPage.showAnnouncementsOnStatusPage) {
1241
+ throw new BadDataException("Announcements are not enabled for this status page.");
1242
+ }
1215
1243
  // check if status page has active announcement.
1216
1244
  const today = OneUptimeDate.getCurrentDate();
1217
1245
  const historyDays = OneUptimeDate.getSomeDaysAgo(statusPage.showAnnouncementHistoryInDays || 14);
@@ -1440,6 +1468,7 @@ export default class StatusPageAPI extends BaseAPI {
1440
1468
  projectId: true,
1441
1469
  showIncidentHistoryInDays: true,
1442
1470
  showIncidentLabelsOnStatusPage: true,
1471
+ showIncidentsOnStatusPage: true,
1443
1472
  },
1444
1473
  props: {
1445
1474
  isRoot: true,
@@ -1448,6 +1477,9 @@ export default class StatusPageAPI extends BaseAPI {
1448
1477
  if (!statusPage) {
1449
1478
  throw new BadDataException("Status Page not found");
1450
1479
  }
1480
+ if (!statusPage.showIncidentsOnStatusPage) {
1481
+ throw new BadDataException("Incidents are not enabled on this status page.");
1482
+ }
1451
1483
  // get monitors on status page.
1452
1484
  const statusPageResources = await StatusPageService.getStatusPageResources({
1453
1485
  statusPageId: statusPageId,
@@ -1652,5 +1684,206 @@ export default class StatusPageAPI extends BaseAPI {
1652
1684
  }
1653
1685
  return currentStatus;
1654
1686
  }
1687
+ async getStatusPageResourcesAndTimelines(data) {
1688
+ const objectId = data.statusPageId;
1689
+ const statusPage = await StatusPageService.findOneBy({
1690
+ query: {
1691
+ _id: objectId.toString(),
1692
+ },
1693
+ select: {
1694
+ _id: true,
1695
+ projectId: true,
1696
+ isPublicStatusPage: true,
1697
+ overviewPageDescription: true,
1698
+ showIncidentLabelsOnStatusPage: true,
1699
+ showScheduledEventLabelsOnStatusPage: true,
1700
+ downtimeMonitorStatuses: {
1701
+ _id: true,
1702
+ },
1703
+ defaultBarColor: true,
1704
+ showOverallUptimePercentOnStatusPage: true,
1705
+ overallUptimePercentPrecision: true,
1706
+ showAnnouncementsOnStatusPage: true,
1707
+ showIncidentsOnStatusPage: true,
1708
+ showScheduledMaintenanceEventsOnStatusPage: true,
1709
+ },
1710
+ props: {
1711
+ isRoot: true,
1712
+ },
1713
+ });
1714
+ if (!statusPage) {
1715
+ throw new BadDataException("Status Page not found");
1716
+ }
1717
+ //get monitor statuses
1718
+ const monitorStatuses = await MonitorStatusService.findBy({
1719
+ query: {
1720
+ projectId: statusPage.projectId,
1721
+ },
1722
+ select: {
1723
+ name: true,
1724
+ color: true,
1725
+ priority: true,
1726
+ isOperationalState: true,
1727
+ },
1728
+ sort: {
1729
+ priority: SortOrder.Ascending,
1730
+ },
1731
+ skip: 0,
1732
+ limit: LIMIT_PER_PROJECT,
1733
+ props: {
1734
+ isRoot: true,
1735
+ },
1736
+ });
1737
+ // get resource groups.
1738
+ const groups = await StatusPageGroupService.findBy({
1739
+ query: {
1740
+ statusPageId: objectId,
1741
+ },
1742
+ select: {
1743
+ name: true,
1744
+ order: true,
1745
+ description: true,
1746
+ isExpandedByDefault: true,
1747
+ showCurrentStatus: true,
1748
+ showUptimePercent: true,
1749
+ uptimePercentPrecision: true,
1750
+ },
1751
+ sort: {
1752
+ order: SortOrder.Ascending,
1753
+ },
1754
+ skip: 0,
1755
+ limit: LIMIT_PER_PROJECT,
1756
+ props: {
1757
+ isRoot: true,
1758
+ },
1759
+ });
1760
+ // get monitors on status page.
1761
+ const statusPageResources = await StatusPageResourceService.findBy({
1762
+ query: {
1763
+ statusPageId: objectId,
1764
+ },
1765
+ select: {
1766
+ statusPageGroupId: true,
1767
+ monitorId: true,
1768
+ displayTooltip: true,
1769
+ displayDescription: true,
1770
+ displayName: true,
1771
+ showStatusHistoryChart: true,
1772
+ showCurrentStatus: true,
1773
+ order: true,
1774
+ monitor: {
1775
+ _id: true,
1776
+ currentMonitorStatusId: true,
1777
+ },
1778
+ monitorGroupId: true,
1779
+ showUptimePercent: true,
1780
+ uptimePercentPrecision: true,
1781
+ },
1782
+ sort: {
1783
+ order: SortOrder.Ascending,
1784
+ },
1785
+ skip: 0,
1786
+ limit: LIMIT_PER_PROJECT,
1787
+ props: {
1788
+ isRoot: true,
1789
+ },
1790
+ });
1791
+ const monitorGroupIds = statusPageResources
1792
+ .map((resource) => {
1793
+ return resource.monitorGroupId;
1794
+ })
1795
+ .filter((id) => {
1796
+ return Boolean(id); // remove nulls
1797
+ });
1798
+ // get monitors in the group.
1799
+ const monitorGroupCurrentStatuses = {};
1800
+ const monitorsInGroup = {};
1801
+ // get monitor status charts.
1802
+ const monitorsOnStatusPage = statusPageResources
1803
+ .map((monitor) => {
1804
+ return monitor.monitorId;
1805
+ })
1806
+ .filter((id) => {
1807
+ return Boolean(id); // remove nulls
1808
+ });
1809
+ const monitorsOnStatusPageForTimeline = statusPageResources
1810
+ .filter((monitor) => {
1811
+ return monitor.showStatusHistoryChart || monitor.showUptimePercent;
1812
+ })
1813
+ .map((monitor) => {
1814
+ return monitor.monitorId;
1815
+ })
1816
+ .filter((id) => {
1817
+ return Boolean(id); // remove nulls
1818
+ });
1819
+ for (const monitorGroupId of monitorGroupIds) {
1820
+ // get current status of monitors in the group.
1821
+ const currentStatus = await MonitorGroupService.getCurrentStatus(monitorGroupId, {
1822
+ isRoot: true,
1823
+ });
1824
+ monitorGroupCurrentStatuses[monitorGroupId.toString()] =
1825
+ currentStatus.id;
1826
+ // get monitors in the group.
1827
+ const groupResources = await MonitorGroupResourceService.findBy({
1828
+ query: {
1829
+ monitorGroupId: monitorGroupId,
1830
+ },
1831
+ select: {
1832
+ monitorId: true,
1833
+ },
1834
+ props: {
1835
+ isRoot: true,
1836
+ },
1837
+ limit: LIMIT_PER_PROJECT,
1838
+ skip: 0,
1839
+ });
1840
+ const monitorsInGroupIds = groupResources
1841
+ .map((resource) => {
1842
+ return resource.monitorId;
1843
+ })
1844
+ .filter((id) => {
1845
+ return Boolean(id); // remove nulls
1846
+ });
1847
+ const shouldShowTimelineForThisGroup = Boolean(statusPageResources.find((resource) => {
1848
+ var _a;
1849
+ return (((_a = resource.monitorGroupId) === null || _a === void 0 ? void 0 : _a.toString()) === monitorGroupId.toString() &&
1850
+ (resource.showStatusHistoryChart || resource.showUptimePercent));
1851
+ }));
1852
+ for (const monitorId of monitorsInGroupIds) {
1853
+ if (!monitorId) {
1854
+ continue;
1855
+ }
1856
+ if (!monitorsOnStatusPage.find((item) => {
1857
+ return item.toString() === monitorId.toString();
1858
+ })) {
1859
+ monitorsOnStatusPage.push(monitorId);
1860
+ }
1861
+ // add this to the timeline event for this group.
1862
+ if (shouldShowTimelineForThisGroup &&
1863
+ !monitorsOnStatusPageForTimeline.find((item) => {
1864
+ return item.toString() === monitorId.toString();
1865
+ })) {
1866
+ monitorsOnStatusPageForTimeline.push(monitorId);
1867
+ }
1868
+ }
1869
+ monitorsInGroup[monitorGroupId.toString()] = monitorsInGroupIds;
1870
+ }
1871
+ const monitorStatusTimelines = await StatusPageService.getMonitorStatusTimelineForStatusPage({
1872
+ monitorIds: monitorsOnStatusPageForTimeline,
1873
+ startDate: data.startDateForMonitorTimeline,
1874
+ endDate: data.endDateForMonitorTimeline,
1875
+ });
1876
+ // return everything.
1877
+ return {
1878
+ statusPageResources,
1879
+ monitorStatuses,
1880
+ monitorGroupCurrentStatuses,
1881
+ statusPageGroups: groups,
1882
+ monitorStatusTimelines,
1883
+ statusPage,
1884
+ monitorsOnStatusPage,
1885
+ monitorsInGroup,
1886
+ };
1887
+ }
1655
1888
  }
1656
1889
  //# sourceMappingURL=StatusPageAPI.js.map