@oneuptime/common 11.3.20 → 11.3.21
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/Models/DatabaseModels/EnterpriseLicenseInstance.ts +173 -0
- package/Models/DatabaseModels/GlobalConfig.ts +19 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Server/API/DashboardAPI.ts +221 -0
- package/Server/API/EnterpriseLicenseAPI.ts +279 -12
- package/Server/API/GlobalConfigAPI.ts +81 -16
- package/Server/API/StatusPageAPI.ts +894 -791
- package/Server/EnvironmentConfig.ts +9 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783226868418-AddEnterpriseLicenseInstance.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/EnterpriseLicenseInstanceService.ts +10 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/IoTDeviceService.ts +99 -4
- package/Server/Services/StatusPageService.ts +104 -45
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +6 -4
- package/Server/Utils/Monitor/MonitorCriteriaDataExtractor.ts +7 -6
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +6 -4
- package/Server/Utils/Telemetry/IoTSnapshotScan.ts +45 -7
- package/ServiceRoute.ts +4 -0
- package/Tests/Server/Services/IoTDeviceService.test.ts +280 -0
- package/Tests/Server/Utils/Monitor/Criteria/TelemetryStepMetricAlias.test.ts +390 -0
- package/Tests/Server/Utils/Telemetry/IoTSnapshotScan.test.ts +202 -0
- package/Tests/Utils/EnterpriseLicenseUsage.test.ts +165 -0
- package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +18 -0
- package/Types/Monitor/MonitorStep.ts +32 -0
- package/UI/Components/EditionLabel/EditionLabel.tsx +172 -0
- package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +125 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js +197 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js.map +1 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +18 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +166 -16
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +203 -13
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +62 -15
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +74 -31
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +6 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783226868418-AddEnterpriseLicenseInstance.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783226868418-AddEnterpriseLicenseInstance.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js +9 -0
- package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js.map +1 -0
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/IoTDeviceService.js +66 -4
- package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +91 -43
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +4 -5
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js +6 -5
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -9
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js +33 -2
- package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js.map +1 -1
- package/build/dist/ServiceRoute.js +1 -0
- package/build/dist/ServiceRoute.js.map +1 -1
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.js +2 -0
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorStep.js +25 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +92 -0
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +83 -0
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -0
- package/package.json +1 -1
|
@@ -1492,171 +1492,242 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
1492
1492
|
},
|
|
1493
1493
|
);
|
|
1494
1494
|
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1495
|
+
/*
|
|
1496
|
+
* Shared handler for the overview endpoint. Registered for both POST
|
|
1497
|
+
* (used by the status page SPA) and GET (machine-readable access for
|
|
1498
|
+
* crawlers / AI agents) so the authentication and authorization path
|
|
1499
|
+
* can never drift between the two methods. It does not read req.body.
|
|
1500
|
+
*/
|
|
1501
|
+
const overviewHandler: (
|
|
1502
|
+
req: ExpressRequest,
|
|
1503
|
+
res: ExpressResponse,
|
|
1504
|
+
next: NextFunction,
|
|
1505
|
+
) => Promise<void> = async (
|
|
1506
|
+
req: ExpressRequest,
|
|
1507
|
+
res: ExpressResponse,
|
|
1508
|
+
next: NextFunction,
|
|
1509
|
+
): Promise<void> => {
|
|
1510
|
+
try {
|
|
1511
|
+
const statusPageId: ObjectID = await resolveStatusPageIdOrThrow(
|
|
1512
|
+
req.params["statusPageIdOrDomain"] as string,
|
|
1513
|
+
);
|
|
1505
1514
|
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1515
|
+
await this.checkHasReadAccess({
|
|
1516
|
+
statusPageId: statusPageId,
|
|
1517
|
+
req: req,
|
|
1518
|
+
});
|
|
1519
|
+
|
|
1520
|
+
// First fetch the status page to get the configured uptime history days
|
|
1521
|
+
const statusPageForDays: StatusPage | null =
|
|
1522
|
+
await StatusPageService.findOneBy({
|
|
1523
|
+
query: {
|
|
1524
|
+
_id: statusPageId.toString(),
|
|
1525
|
+
},
|
|
1526
|
+
select: {
|
|
1527
|
+
showUptimeHistoryInDays: true,
|
|
1528
|
+
},
|
|
1529
|
+
props: {
|
|
1530
|
+
isRoot: true,
|
|
1531
|
+
},
|
|
1509
1532
|
});
|
|
1510
1533
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1534
|
+
let uptimeHistoryDays: number =
|
|
1535
|
+
statusPageForDays?.showUptimeHistoryInDays || 90;
|
|
1536
|
+
|
|
1537
|
+
if (uptimeHistoryDays > 90) {
|
|
1538
|
+
uptimeHistoryDays = 90;
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
if (uptimeHistoryDays < 1) {
|
|
1542
|
+
uptimeHistoryDays = 1;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
const startDate: Date = OneUptimeDate.getSomeDaysAgo(uptimeHistoryDays);
|
|
1546
|
+
const endDate: Date = OneUptimeDate.getCurrentDate();
|
|
1547
|
+
|
|
1548
|
+
const {
|
|
1549
|
+
monitorStatuses,
|
|
1550
|
+
monitorGroupCurrentStatuses,
|
|
1551
|
+
statusPageResources,
|
|
1552
|
+
statusPage,
|
|
1553
|
+
monitorsOnStatusPage,
|
|
1554
|
+
monitorStatusTimelines,
|
|
1555
|
+
statusPageGroups,
|
|
1556
|
+
monitorsInGroup,
|
|
1557
|
+
} = await this.getStatusPageResourcesAndTimelines({
|
|
1558
|
+
statusPageId: statusPageId,
|
|
1559
|
+
startDateForMonitorTimeline: startDate,
|
|
1560
|
+
endDateForMonitorTimeline: endDate,
|
|
1561
|
+
});
|
|
1562
|
+
|
|
1563
|
+
// check if status page has active incident.
|
|
1564
|
+
let activeIncidents: Array<Incident> = [];
|
|
1565
|
+
if (monitorsOnStatusPage.length > 0) {
|
|
1566
|
+
let select: Select<Incident> = {
|
|
1567
|
+
createdAt: true,
|
|
1568
|
+
declaredAt: true,
|
|
1569
|
+
updatedAt: true,
|
|
1570
|
+
title: true,
|
|
1571
|
+
description: true,
|
|
1572
|
+
_id: true,
|
|
1573
|
+
postmortemNote: true,
|
|
1574
|
+
postmortemPostedAt: true,
|
|
1575
|
+
showPostmortemOnStatusPage: true,
|
|
1576
|
+
postmortemAttachments: {
|
|
1577
|
+
_id: true,
|
|
1578
|
+
name: true,
|
|
1579
|
+
},
|
|
1580
|
+
incidentSeverity: {
|
|
1581
|
+
name: true,
|
|
1582
|
+
color: true,
|
|
1583
|
+
},
|
|
1584
|
+
currentIncidentState: {
|
|
1585
|
+
_id: true,
|
|
1586
|
+
name: true,
|
|
1587
|
+
color: true,
|
|
1588
|
+
order: true,
|
|
1589
|
+
},
|
|
1590
|
+
monitors: {
|
|
1591
|
+
_id: true,
|
|
1592
|
+
},
|
|
1593
|
+
};
|
|
1594
|
+
|
|
1595
|
+
if (statusPage.showIncidentLabelsOnStatusPage) {
|
|
1596
|
+
select = {
|
|
1597
|
+
...select,
|
|
1598
|
+
labels: {
|
|
1599
|
+
name: true,
|
|
1600
|
+
color: true,
|
|
1601
|
+
},
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
const unresolvedIncidentStates: Array<IncidentState> =
|
|
1606
|
+
await IncidentStateService.getUnresolvedIncidentStates(
|
|
1607
|
+
statusPage.projectId!,
|
|
1608
|
+
{
|
|
1609
|
+
isRoot: true,
|
|
1610
|
+
},
|
|
1611
|
+
);
|
|
1612
|
+
|
|
1613
|
+
const unresolvedIncidentStateIds: Array<ObjectID> =
|
|
1614
|
+
unresolvedIncidentStates.map((state: IncidentState) => {
|
|
1615
|
+
return state.id!;
|
|
1616
|
+
});
|
|
1617
|
+
|
|
1618
|
+
if (statusPage.showIncidentsOnStatusPage) {
|
|
1619
|
+
activeIncidents = await IncidentService.findBy({
|
|
1514
1620
|
query: {
|
|
1515
|
-
|
|
1621
|
+
monitors: monitorsOnStatusPage as any,
|
|
1622
|
+
currentIncidentStateId: QueryHelper.any(
|
|
1623
|
+
unresolvedIncidentStateIds,
|
|
1624
|
+
),
|
|
1625
|
+
isVisibleOnStatusPage: true,
|
|
1626
|
+
projectId: statusPage.projectId!,
|
|
1516
1627
|
},
|
|
1517
|
-
select:
|
|
1518
|
-
|
|
1628
|
+
select: select,
|
|
1629
|
+
sort: {
|
|
1630
|
+
declaredAt: SortOrder.Descending,
|
|
1631
|
+
createdAt: SortOrder.Descending,
|
|
1519
1632
|
},
|
|
1633
|
+
|
|
1634
|
+
skip: 0,
|
|
1635
|
+
limit: LIMIT_PER_PROJECT,
|
|
1520
1636
|
props: {
|
|
1521
1637
|
isRoot: true,
|
|
1522
1638
|
},
|
|
1523
1639
|
});
|
|
1524
|
-
|
|
1525
|
-
let uptimeHistoryDays: number =
|
|
1526
|
-
statusPageForDays?.showUptimeHistoryInDays || 90;
|
|
1527
|
-
|
|
1528
|
-
if (uptimeHistoryDays > 90) {
|
|
1529
|
-
uptimeHistoryDays = 90;
|
|
1530
|
-
}
|
|
1531
|
-
|
|
1532
|
-
if (uptimeHistoryDays < 1) {
|
|
1533
|
-
uptimeHistoryDays = 1;
|
|
1534
1640
|
}
|
|
1641
|
+
}
|
|
1535
1642
|
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1643
|
+
const incidentsOnStatusPage: Array<ObjectID> = activeIncidents.map(
|
|
1644
|
+
(incident: Incident) => {
|
|
1645
|
+
return incident.id!;
|
|
1646
|
+
},
|
|
1647
|
+
);
|
|
1539
1648
|
|
|
1540
|
-
|
|
1541
|
-
monitorStatuses,
|
|
1542
|
-
monitorGroupCurrentStatuses,
|
|
1543
|
-
statusPageResources,
|
|
1544
|
-
statusPage,
|
|
1545
|
-
monitorsOnStatusPage,
|
|
1546
|
-
monitorStatusTimelines,
|
|
1547
|
-
statusPageGroups,
|
|
1548
|
-
monitorsInGroup,
|
|
1549
|
-
} = await this.getStatusPageResourcesAndTimelines({
|
|
1550
|
-
statusPageId: statusPageId,
|
|
1551
|
-
startDateForMonitorTimeline: startDate,
|
|
1552
|
-
endDateForMonitorTimeline: endDate,
|
|
1553
|
-
});
|
|
1649
|
+
let incidentPublicNotes: Array<IncidentPublicNote> = [];
|
|
1554
1650
|
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
postmortemPostedAt: true,
|
|
1567
|
-
showPostmortemOnStatusPage: true,
|
|
1568
|
-
postmortemAttachments: {
|
|
1651
|
+
if (incidentsOnStatusPage.length > 0) {
|
|
1652
|
+
incidentPublicNotes = await IncidentPublicNoteService.findBy({
|
|
1653
|
+
query: {
|
|
1654
|
+
incidentId: QueryHelper.any(incidentsOnStatusPage),
|
|
1655
|
+
projectId: statusPage.projectId!,
|
|
1656
|
+
},
|
|
1657
|
+
select: {
|
|
1658
|
+
note: true,
|
|
1659
|
+
incidentId: true,
|
|
1660
|
+
postedAt: true,
|
|
1661
|
+
attachments: {
|
|
1569
1662
|
_id: true,
|
|
1570
1663
|
name: true,
|
|
1571
1664
|
},
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1665
|
+
},
|
|
1666
|
+
sort: {
|
|
1667
|
+
postedAt: SortOrder.Descending, // new note first
|
|
1668
|
+
},
|
|
1669
|
+
skip: 0,
|
|
1670
|
+
limit: LIMIT_PER_PROJECT,
|
|
1671
|
+
props: {
|
|
1672
|
+
isRoot: true,
|
|
1673
|
+
},
|
|
1674
|
+
});
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
let incidentStateTimelines: Array<IncidentStateTimeline> = [];
|
|
1678
|
+
|
|
1679
|
+
if (incidentsOnStatusPage.length > 0) {
|
|
1680
|
+
incidentStateTimelines = await IncidentStateTimelineService.findBy({
|
|
1681
|
+
query: {
|
|
1682
|
+
incidentId: QueryHelper.any(incidentsOnStatusPage),
|
|
1683
|
+
projectId: statusPage.projectId!,
|
|
1684
|
+
},
|
|
1685
|
+
select: {
|
|
1686
|
+
_id: true,
|
|
1687
|
+
createdAt: true,
|
|
1688
|
+
startsAt: true,
|
|
1689
|
+
incidentId: true,
|
|
1690
|
+
incidentState: {
|
|
1577
1691
|
_id: true,
|
|
1578
1692
|
name: true,
|
|
1579
1693
|
color: true,
|
|
1580
|
-
|
|
1694
|
+
isCreatedState: true,
|
|
1695
|
+
isResolvedState: true,
|
|
1696
|
+
isAcknowledgedState: true,
|
|
1581
1697
|
},
|
|
1582
|
-
|
|
1583
|
-
_id: true,
|
|
1584
|
-
},
|
|
1585
|
-
};
|
|
1586
|
-
|
|
1587
|
-
if (statusPage.showIncidentLabelsOnStatusPage) {
|
|
1588
|
-
select = {
|
|
1589
|
-
...select,
|
|
1590
|
-
labels: {
|
|
1591
|
-
name: true,
|
|
1592
|
-
color: true,
|
|
1593
|
-
},
|
|
1594
|
-
};
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
const unresolvedIncidentStates: Array<IncidentState> =
|
|
1598
|
-
await IncidentStateService.getUnresolvedIncidentStates(
|
|
1599
|
-
statusPage.projectId!,
|
|
1600
|
-
{
|
|
1601
|
-
isRoot: true,
|
|
1602
|
-
},
|
|
1603
|
-
);
|
|
1604
|
-
|
|
1605
|
-
const unresolvedIncidentStateIds: Array<ObjectID> =
|
|
1606
|
-
unresolvedIncidentStates.map((state: IncidentState) => {
|
|
1607
|
-
return state.id!;
|
|
1608
|
-
});
|
|
1609
|
-
|
|
1610
|
-
if (statusPage.showIncidentsOnStatusPage) {
|
|
1611
|
-
activeIncidents = await IncidentService.findBy({
|
|
1612
|
-
query: {
|
|
1613
|
-
monitors: monitorsOnStatusPage as any,
|
|
1614
|
-
currentIncidentStateId: QueryHelper.any(
|
|
1615
|
-
unresolvedIncidentStateIds,
|
|
1616
|
-
),
|
|
1617
|
-
isVisibleOnStatusPage: true,
|
|
1618
|
-
projectId: statusPage.projectId!,
|
|
1619
|
-
},
|
|
1620
|
-
select: select,
|
|
1621
|
-
sort: {
|
|
1622
|
-
declaredAt: SortOrder.Descending,
|
|
1623
|
-
createdAt: SortOrder.Descending,
|
|
1624
|
-
},
|
|
1625
|
-
|
|
1626
|
-
skip: 0,
|
|
1627
|
-
limit: LIMIT_PER_PROJECT,
|
|
1628
|
-
props: {
|
|
1629
|
-
isRoot: true,
|
|
1630
|
-
},
|
|
1631
|
-
});
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1698
|
+
},
|
|
1634
1699
|
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
return incident.id!;
|
|
1700
|
+
sort: {
|
|
1701
|
+
startsAt: SortOrder.Descending, // newer state changes first
|
|
1638
1702
|
},
|
|
1639
|
-
|
|
1703
|
+
skip: 0,
|
|
1704
|
+
limit: LIMIT_PER_PROJECT,
|
|
1705
|
+
props: {
|
|
1706
|
+
isRoot: true,
|
|
1707
|
+
},
|
|
1708
|
+
});
|
|
1709
|
+
}
|
|
1640
1710
|
|
|
1641
|
-
|
|
1711
|
+
// Fetch active episodes (similar to incidents)
|
|
1712
|
+
let activeEpisodes: Array<IncidentEpisode> = [];
|
|
1713
|
+
let activeEpisodesJson: JSONArray = [];
|
|
1714
|
+
let episodePublicNotes: Array<IncidentEpisodePublicNote> = [];
|
|
1715
|
+
let episodeStateTimelines: Array<IncidentEpisodeStateTimeline> = [];
|
|
1642
1716
|
|
|
1643
|
-
|
|
1644
|
-
|
|
1717
|
+
if (
|
|
1718
|
+
statusPage.showEpisodesOnStatusPage &&
|
|
1719
|
+
monitorsOnStatusPage.length > 0
|
|
1720
|
+
) {
|
|
1721
|
+
// First, get incidents that have monitors on status page
|
|
1722
|
+
const incidentsForEpisodes: Array<Incident> =
|
|
1723
|
+
await IncidentService.findBy({
|
|
1645
1724
|
query: {
|
|
1646
|
-
|
|
1725
|
+
monitors: monitorsOnStatusPage as any,
|
|
1726
|
+
isVisibleOnStatusPage: true,
|
|
1647
1727
|
projectId: statusPage.projectId!,
|
|
1648
1728
|
},
|
|
1649
1729
|
select: {
|
|
1650
|
-
|
|
1651
|
-
incidentId: true,
|
|
1652
|
-
postedAt: true,
|
|
1653
|
-
attachments: {
|
|
1654
|
-
_id: true,
|
|
1655
|
-
name: true,
|
|
1656
|
-
},
|
|
1657
|
-
},
|
|
1658
|
-
sort: {
|
|
1659
|
-
postedAt: SortOrder.Descending, // new note first
|
|
1730
|
+
_id: true,
|
|
1660
1731
|
},
|
|
1661
1732
|
skip: 0,
|
|
1662
1733
|
limit: LIMIT_PER_PROJECT,
|
|
@@ -1664,33 +1735,23 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
1664
1735
|
isRoot: true,
|
|
1665
1736
|
},
|
|
1666
1737
|
});
|
|
1667
|
-
}
|
|
1668
1738
|
|
|
1669
|
-
|
|
1739
|
+
const incidentIdsForEpisodes: Array<ObjectID> =
|
|
1740
|
+
incidentsForEpisodes.map((incident: Incident) => {
|
|
1741
|
+
return incident.id!;
|
|
1742
|
+
});
|
|
1670
1743
|
|
|
1671
|
-
|
|
1672
|
-
|
|
1744
|
+
// Get episode members for these incidents
|
|
1745
|
+
let episodeMembers: Array<IncidentEpisodeMember> = [];
|
|
1746
|
+
if (incidentIdsForEpisodes.length > 0) {
|
|
1747
|
+
episodeMembers = await IncidentEpisodeMemberService.findBy({
|
|
1673
1748
|
query: {
|
|
1674
|
-
incidentId: QueryHelper.any(
|
|
1749
|
+
incidentId: QueryHelper.any(incidentIdsForEpisodes),
|
|
1675
1750
|
projectId: statusPage.projectId!,
|
|
1676
1751
|
},
|
|
1677
1752
|
select: {
|
|
1678
|
-
|
|
1679
|
-
createdAt: true,
|
|
1680
|
-
startsAt: true,
|
|
1753
|
+
incidentEpisodeId: true,
|
|
1681
1754
|
incidentId: true,
|
|
1682
|
-
incidentState: {
|
|
1683
|
-
_id: true,
|
|
1684
|
-
name: true,
|
|
1685
|
-
color: true,
|
|
1686
|
-
isCreatedState: true,
|
|
1687
|
-
isResolvedState: true,
|
|
1688
|
-
isAcknowledgedState: true,
|
|
1689
|
-
},
|
|
1690
|
-
},
|
|
1691
|
-
|
|
1692
|
-
sort: {
|
|
1693
|
-
startsAt: SortOrder.Descending, // newer state changes first
|
|
1694
1755
|
},
|
|
1695
1756
|
skip: 0,
|
|
1696
1757
|
limit: LIMIT_PER_PROJECT,
|
|
@@ -1700,534 +1761,418 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
1700
1761
|
});
|
|
1701
1762
|
}
|
|
1702
1763
|
|
|
1703
|
-
//
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
if (
|
|
1710
|
-
statusPage.showEpisodesOnStatusPage &&
|
|
1711
|
-
monitorsOnStatusPage.length > 0
|
|
1712
|
-
) {
|
|
1713
|
-
// First, get incidents that have monitors on status page
|
|
1714
|
-
const incidentsForEpisodes: Array<Incident> =
|
|
1715
|
-
await IncidentService.findBy({
|
|
1716
|
-
query: {
|
|
1717
|
-
monitors: monitorsOnStatusPage as any,
|
|
1718
|
-
isVisibleOnStatusPage: true,
|
|
1719
|
-
projectId: statusPage.projectId!,
|
|
1720
|
-
},
|
|
1721
|
-
select: {
|
|
1722
|
-
_id: true,
|
|
1723
|
-
},
|
|
1724
|
-
skip: 0,
|
|
1725
|
-
limit: LIMIT_PER_PROJECT,
|
|
1726
|
-
props: {
|
|
1727
|
-
isRoot: true,
|
|
1728
|
-
},
|
|
1729
|
-
});
|
|
1730
|
-
|
|
1731
|
-
const incidentIdsForEpisodes: Array<ObjectID> =
|
|
1732
|
-
incidentsForEpisodes.map((incident: Incident) => {
|
|
1733
|
-
return incident.id!;
|
|
1734
|
-
});
|
|
1735
|
-
|
|
1736
|
-
// Get episode members for these incidents
|
|
1737
|
-
let episodeMembers: Array<IncidentEpisodeMember> = [];
|
|
1738
|
-
if (incidentIdsForEpisodes.length > 0) {
|
|
1739
|
-
episodeMembers = await IncidentEpisodeMemberService.findBy({
|
|
1740
|
-
query: {
|
|
1741
|
-
incidentId: QueryHelper.any(incidentIdsForEpisodes),
|
|
1742
|
-
projectId: statusPage.projectId!,
|
|
1743
|
-
},
|
|
1744
|
-
select: {
|
|
1745
|
-
incidentEpisodeId: true,
|
|
1746
|
-
incidentId: true,
|
|
1747
|
-
},
|
|
1748
|
-
skip: 0,
|
|
1749
|
-
limit: LIMIT_PER_PROJECT,
|
|
1750
|
-
props: {
|
|
1751
|
-
isRoot: true,
|
|
1752
|
-
},
|
|
1753
|
-
});
|
|
1764
|
+
// Get unique episode IDs
|
|
1765
|
+
const episodeIdsFromMembers: Set<string> = new Set();
|
|
1766
|
+
for (const member of episodeMembers) {
|
|
1767
|
+
if (member.incidentEpisodeId) {
|
|
1768
|
+
episodeIdsFromMembers.add(member.incidentEpisodeId.toString());
|
|
1754
1769
|
}
|
|
1770
|
+
}
|
|
1755
1771
|
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1772
|
+
// Fetch active (unresolved) episodes
|
|
1773
|
+
if (episodeIdsFromMembers.size > 0) {
|
|
1774
|
+
const unresolvedIncidentStates: Array<IncidentState> =
|
|
1775
|
+
await IncidentStateService.getUnresolvedIncidentStates(
|
|
1776
|
+
statusPage.projectId!,
|
|
1777
|
+
{ isRoot: true },
|
|
1778
|
+
);
|
|
1763
1779
|
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
statusPage.projectId!,
|
|
1769
|
-
{ isRoot: true },
|
|
1770
|
-
);
|
|
1771
|
-
|
|
1772
|
-
const unresolvedIncidentStateIds: Array<ObjectID> =
|
|
1773
|
-
unresolvedIncidentStates.map((state: IncidentState) => {
|
|
1774
|
-
return state.id!;
|
|
1775
|
-
});
|
|
1780
|
+
const unresolvedIncidentStateIds: Array<ObjectID> =
|
|
1781
|
+
unresolvedIncidentStates.map((state: IncidentState) => {
|
|
1782
|
+
return state.id!;
|
|
1783
|
+
});
|
|
1776
1784
|
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1785
|
+
let selectEpisodes: Select<IncidentEpisode> = {
|
|
1786
|
+
createdAt: true,
|
|
1787
|
+
declaredAt: true,
|
|
1788
|
+
updatedAt: true,
|
|
1789
|
+
title: true,
|
|
1790
|
+
description: true,
|
|
1791
|
+
_id: true,
|
|
1792
|
+
episodeNumber: true,
|
|
1793
|
+
incidentSeverity: {
|
|
1794
|
+
name: true,
|
|
1795
|
+
color: true,
|
|
1796
|
+
},
|
|
1797
|
+
currentIncidentState: {
|
|
1798
|
+
name: true,
|
|
1799
|
+
color: true,
|
|
1783
1800
|
_id: true,
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1801
|
+
order: true,
|
|
1802
|
+
isCreatedState: true,
|
|
1803
|
+
isAcknowledgedState: true,
|
|
1804
|
+
isResolvedState: true,
|
|
1805
|
+
},
|
|
1806
|
+
incidentCount: true,
|
|
1807
|
+
};
|
|
1808
|
+
|
|
1809
|
+
if (statusPage.showEpisodeLabelsOnStatusPage) {
|
|
1810
|
+
selectEpisodes = {
|
|
1811
|
+
...selectEpisodes,
|
|
1812
|
+
labels: {
|
|
1790
1813
|
name: true,
|
|
1791
1814
|
color: true,
|
|
1792
|
-
_id: true,
|
|
1793
|
-
order: true,
|
|
1794
|
-
isCreatedState: true,
|
|
1795
|
-
isAcknowledgedState: true,
|
|
1796
|
-
isResolvedState: true,
|
|
1797
1815
|
},
|
|
1798
|
-
incidentCount: true,
|
|
1799
1816
|
};
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
activeEpisodes = await IncidentEpisodeService.findBy({
|
|
1820
|
+
query: {
|
|
1821
|
+
_id: QueryHelper.any(
|
|
1822
|
+
Array.from(episodeIdsFromMembers).map((id: string) => {
|
|
1823
|
+
return new ObjectID(id);
|
|
1824
|
+
}),
|
|
1825
|
+
),
|
|
1826
|
+
currentIncidentStateId: QueryHelper.any(
|
|
1827
|
+
unresolvedIncidentStateIds,
|
|
1828
|
+
),
|
|
1829
|
+
isVisibleOnStatusPage: true,
|
|
1830
|
+
projectId: statusPage.projectId!,
|
|
1831
|
+
},
|
|
1832
|
+
select: selectEpisodes,
|
|
1833
|
+
sort: {
|
|
1834
|
+
declaredAt: SortOrder.Descending,
|
|
1835
|
+
createdAt: SortOrder.Descending,
|
|
1836
|
+
},
|
|
1837
|
+
skip: 0,
|
|
1838
|
+
limit: LIMIT_PER_PROJECT,
|
|
1839
|
+
props: {
|
|
1840
|
+
isRoot: true,
|
|
1841
|
+
},
|
|
1842
|
+
});
|
|
1843
|
+
|
|
1844
|
+
// Build episode monitors map
|
|
1845
|
+
if (activeEpisodes.length > 0) {
|
|
1846
|
+
// Collect all incident IDs from episode members for active episodes
|
|
1847
|
+
const activeEpisodeIds: Set<string> = new Set(
|
|
1848
|
+
activeEpisodes.map((e: IncidentEpisode) => {
|
|
1849
|
+
return e.id!.toString();
|
|
1850
|
+
}),
|
|
1851
|
+
);
|
|
1852
|
+
|
|
1853
|
+
const memberIncidentIds: Array<ObjectID> = [];
|
|
1854
|
+
for (const member of episodeMembers) {
|
|
1855
|
+
if (
|
|
1856
|
+
member.incidentEpisodeId &&
|
|
1857
|
+
activeEpisodeIds.has(member.incidentEpisodeId.toString()) &&
|
|
1858
|
+
member.incidentId &&
|
|
1859
|
+
!memberIncidentIds.some((id: ObjectID) => {
|
|
1860
|
+
return id.toString() === member.incidentId!.toString();
|
|
1861
|
+
})
|
|
1862
|
+
) {
|
|
1863
|
+
memberIncidentIds.push(member.incidentId);
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1800
1866
|
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1867
|
+
// Fetch incidents with monitors
|
|
1868
|
+
let memberIncidents: Array<Incident> = [];
|
|
1869
|
+
if (memberIncidentIds.length > 0) {
|
|
1870
|
+
memberIncidents = await IncidentService.findBy({
|
|
1871
|
+
query: {
|
|
1872
|
+
_id: QueryHelper.any(memberIncidentIds),
|
|
1873
|
+
isVisibleOnStatusPage: true,
|
|
1874
|
+
projectId: statusPage.projectId!,
|
|
1807
1875
|
},
|
|
1808
|
-
|
|
1876
|
+
select: {
|
|
1877
|
+
_id: true,
|
|
1878
|
+
monitors: {
|
|
1879
|
+
_id: true,
|
|
1880
|
+
},
|
|
1881
|
+
},
|
|
1882
|
+
skip: 0,
|
|
1883
|
+
limit: LIMIT_PER_PROJECT,
|
|
1884
|
+
props: {
|
|
1885
|
+
isRoot: true,
|
|
1886
|
+
},
|
|
1887
|
+
});
|
|
1809
1888
|
}
|
|
1810
1889
|
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
skip: 0,
|
|
1830
|
-
limit: LIMIT_PER_PROJECT,
|
|
1831
|
-
props: {
|
|
1832
|
-
isRoot: true,
|
|
1833
|
-
},
|
|
1834
|
-
});
|
|
1890
|
+
// Build incident -> monitors map
|
|
1891
|
+
const incidentMonitorsMap: Map<
|
|
1892
|
+
string,
|
|
1893
|
+
Array<ObjectID>
|
|
1894
|
+
> = new Map();
|
|
1895
|
+
for (const incident of memberIncidents) {
|
|
1896
|
+
const incidentIdStr: string = incident.id!.toString();
|
|
1897
|
+
const monitorIds: Array<ObjectID> = (incident.monitors || [])
|
|
1898
|
+
.map((m: Monitor) => {
|
|
1899
|
+
return new ObjectID(
|
|
1900
|
+
m._id?.toString() || m.id?.toString() || "",
|
|
1901
|
+
);
|
|
1902
|
+
})
|
|
1903
|
+
.filter((id: ObjectID) => {
|
|
1904
|
+
return id.toString() !== "";
|
|
1905
|
+
});
|
|
1906
|
+
incidentMonitorsMap.set(incidentIdStr, monitorIds);
|
|
1907
|
+
}
|
|
1835
1908
|
|
|
1836
|
-
// Build episode monitors map
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
member.incidentEpisodeId
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1909
|
+
// Build episode -> monitors map
|
|
1910
|
+
const episodeMonitorsMap: Map<
|
|
1911
|
+
string,
|
|
1912
|
+
Array<ObjectID>
|
|
1913
|
+
> = new Map();
|
|
1914
|
+
for (const member of episodeMembers) {
|
|
1915
|
+
if (
|
|
1916
|
+
member.incidentEpisodeId &&
|
|
1917
|
+
member.incidentId &&
|
|
1918
|
+
activeEpisodeIds.has(member.incidentEpisodeId.toString())
|
|
1919
|
+
) {
|
|
1920
|
+
const episodeIdStr: string =
|
|
1921
|
+
member.incidentEpisodeId.toString();
|
|
1922
|
+
const incidentIdStr: string = member.incidentId.toString();
|
|
1923
|
+
|
|
1924
|
+
if (!episodeMonitorsMap.has(episodeIdStr)) {
|
|
1925
|
+
episodeMonitorsMap.set(episodeIdStr, []);
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
const episodeMonitors: Array<ObjectID> =
|
|
1929
|
+
episodeMonitorsMap.get(episodeIdStr)!;
|
|
1930
|
+
const incidentMonitors: Array<ObjectID> =
|
|
1931
|
+
incidentMonitorsMap.get(incidentIdStr) || [];
|
|
1932
|
+
|
|
1933
|
+
for (const monitorId of incidentMonitors) {
|
|
1934
|
+
if (
|
|
1935
|
+
!episodeMonitors.some((m: ObjectID) => {
|
|
1936
|
+
return m.toString() === monitorId.toString();
|
|
1937
|
+
})
|
|
1938
|
+
) {
|
|
1939
|
+
episodeMonitors.push(monitorId);
|
|
1940
|
+
}
|
|
1856
1941
|
}
|
|
1857
1942
|
}
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
// Serialize episodes and add monitors
|
|
1946
|
+
activeEpisodesJson = BaseModel.toJSONArray(
|
|
1947
|
+
activeEpisodes,
|
|
1948
|
+
IncidentEpisode,
|
|
1949
|
+
);
|
|
1950
|
+
for (const episodeJson of activeEpisodesJson) {
|
|
1951
|
+
const episodeObj: JSONObject = episodeJson as JSONObject;
|
|
1952
|
+
const episodeId: string | undefined =
|
|
1953
|
+
episodeObj["_id"]?.toString();
|
|
1954
|
+
if (episodeId) {
|
|
1955
|
+
const monitorIds: Array<ObjectID> =
|
|
1956
|
+
episodeMonitorsMap.get(episodeId) || [];
|
|
1957
|
+
episodeObj["monitors"] = monitorIds.map((id: ObjectID) => {
|
|
1958
|
+
return { _id: id.toString() };
|
|
1959
|
+
});
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1858
1962
|
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1963
|
+
// Get episode public notes
|
|
1964
|
+
const episodesOnStatusPage: Array<ObjectID> = activeEpisodes.map(
|
|
1965
|
+
(episode: IncidentEpisode) => {
|
|
1966
|
+
return episode.id!;
|
|
1967
|
+
},
|
|
1968
|
+
);
|
|
1969
|
+
|
|
1970
|
+
if (episodesOnStatusPage.length > 0) {
|
|
1971
|
+
episodePublicNotes =
|
|
1972
|
+
await IncidentEpisodePublicNoteService.findBy({
|
|
1863
1973
|
query: {
|
|
1864
|
-
|
|
1865
|
-
isVisibleOnStatusPage: true,
|
|
1974
|
+
incidentEpisodeId: QueryHelper.any(episodesOnStatusPage),
|
|
1866
1975
|
projectId: statusPage.projectId!,
|
|
1867
1976
|
},
|
|
1868
1977
|
select: {
|
|
1869
|
-
|
|
1870
|
-
|
|
1978
|
+
postedAt: true,
|
|
1979
|
+
note: true,
|
|
1980
|
+
incidentEpisodeId: true,
|
|
1981
|
+
attachments: {
|
|
1871
1982
|
_id: true,
|
|
1983
|
+
name: true,
|
|
1872
1984
|
},
|
|
1873
1985
|
},
|
|
1986
|
+
sort: {
|
|
1987
|
+
postedAt: SortOrder.Descending,
|
|
1988
|
+
},
|
|
1874
1989
|
skip: 0,
|
|
1875
1990
|
limit: LIMIT_PER_PROJECT,
|
|
1876
1991
|
props: {
|
|
1877
1992
|
isRoot: true,
|
|
1878
1993
|
},
|
|
1879
1994
|
});
|
|
1880
|
-
}
|
|
1881
|
-
|
|
1882
|
-
// Build incident -> monitors map
|
|
1883
|
-
const incidentMonitorsMap: Map<
|
|
1884
|
-
string,
|
|
1885
|
-
Array<ObjectID>
|
|
1886
|
-
> = new Map();
|
|
1887
|
-
for (const incident of memberIncidents) {
|
|
1888
|
-
const incidentIdStr: string = incident.id!.toString();
|
|
1889
|
-
const monitorIds: Array<ObjectID> = (incident.monitors || [])
|
|
1890
|
-
.map((m: Monitor) => {
|
|
1891
|
-
return new ObjectID(
|
|
1892
|
-
m._id?.toString() || m.id?.toString() || "",
|
|
1893
|
-
);
|
|
1894
|
-
})
|
|
1895
|
-
.filter((id: ObjectID) => {
|
|
1896
|
-
return id.toString() !== "";
|
|
1897
|
-
});
|
|
1898
|
-
incidentMonitorsMap.set(incidentIdStr, monitorIds);
|
|
1899
|
-
}
|
|
1900
1995
|
|
|
1901
|
-
//
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1996
|
+
// Get episode state timelines
|
|
1997
|
+
episodeStateTimelines =
|
|
1998
|
+
await IncidentEpisodeStateTimelineService.findBy({
|
|
1999
|
+
query: {
|
|
2000
|
+
incidentEpisodeId: QueryHelper.any(episodesOnStatusPage),
|
|
2001
|
+
projectId: statusPage.projectId!,
|
|
2002
|
+
},
|
|
2003
|
+
select: {
|
|
2004
|
+
_id: true,
|
|
2005
|
+
createdAt: true,
|
|
2006
|
+
startsAt: true,
|
|
2007
|
+
incidentEpisodeId: true,
|
|
2008
|
+
incidentState: {
|
|
2009
|
+
name: true,
|
|
2010
|
+
color: true,
|
|
2011
|
+
isCreatedState: true,
|
|
2012
|
+
isAcknowledgedState: true,
|
|
2013
|
+
isResolvedState: true,
|
|
2014
|
+
},
|
|
2015
|
+
},
|
|
2016
|
+
sort: {
|
|
2017
|
+
startsAt: SortOrder.Descending,
|
|
2018
|
+
},
|
|
2019
|
+
skip: 0,
|
|
2020
|
+
limit: LIMIT_PER_PROJECT,
|
|
2021
|
+
props: {
|
|
2022
|
+
isRoot: true,
|
|
2023
|
+
},
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
}
|
|
1919
2029
|
|
|
1920
|
-
|
|
1921
|
-
episodeMonitorsMap.get(episodeIdStr)!;
|
|
1922
|
-
const incidentMonitors: Array<ObjectID> =
|
|
1923
|
-
incidentMonitorsMap.get(incidentIdStr) || [];
|
|
1924
|
-
|
|
1925
|
-
for (const monitorId of incidentMonitors) {
|
|
1926
|
-
if (
|
|
1927
|
-
!episodeMonitors.some((m: ObjectID) => {
|
|
1928
|
-
return m.toString() === monitorId.toString();
|
|
1929
|
-
})
|
|
1930
|
-
) {
|
|
1931
|
-
episodeMonitors.push(monitorId);
|
|
1932
|
-
}
|
|
1933
|
-
}
|
|
1934
|
-
}
|
|
1935
|
-
}
|
|
2030
|
+
// check if status page has active announcement.
|
|
1936
2031
|
|
|
1937
|
-
|
|
1938
|
-
activeEpisodesJson = BaseModel.toJSONArray(
|
|
1939
|
-
activeEpisodes,
|
|
1940
|
-
IncidentEpisode,
|
|
1941
|
-
);
|
|
1942
|
-
for (const episodeJson of activeEpisodesJson) {
|
|
1943
|
-
const episodeObj: JSONObject = episodeJson as JSONObject;
|
|
1944
|
-
const episodeId: string | undefined =
|
|
1945
|
-
episodeObj["_id"]?.toString();
|
|
1946
|
-
if (episodeId) {
|
|
1947
|
-
const monitorIds: Array<ObjectID> =
|
|
1948
|
-
episodeMonitorsMap.get(episodeId) || [];
|
|
1949
|
-
episodeObj["monitors"] = monitorIds.map((id: ObjectID) => {
|
|
1950
|
-
return { _id: id.toString() };
|
|
1951
|
-
});
|
|
1952
|
-
}
|
|
1953
|
-
}
|
|
2032
|
+
const today: Date = OneUptimeDate.getCurrentDate();
|
|
1954
2033
|
|
|
1955
|
-
|
|
1956
|
-
const episodesOnStatusPage: Array<ObjectID> =
|
|
1957
|
-
activeEpisodes.map((episode: IncidentEpisode) => {
|
|
1958
|
-
return episode.id!;
|
|
1959
|
-
});
|
|
2034
|
+
let activeAnnouncements: Array<StatusPageAnnouncement> = [];
|
|
1960
2035
|
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
isRoot: true,
|
|
1985
|
-
},
|
|
1986
|
-
});
|
|
2036
|
+
if (statusPage.showAnnouncementsOnStatusPage) {
|
|
2037
|
+
activeAnnouncements = await StatusPageAnnouncementService.findBy({
|
|
2038
|
+
query: {
|
|
2039
|
+
statusPages: statusPageId as any,
|
|
2040
|
+
showAnnouncementAt: QueryHelper.lessThan(today),
|
|
2041
|
+
endAnnouncementAt: QueryHelper.greaterThanOrNull(today),
|
|
2042
|
+
projectId: statusPage.projectId!,
|
|
2043
|
+
},
|
|
2044
|
+
select: {
|
|
2045
|
+
createdAt: true,
|
|
2046
|
+
title: true,
|
|
2047
|
+
description: true,
|
|
2048
|
+
_id: true,
|
|
2049
|
+
showAnnouncementAt: true,
|
|
2050
|
+
endAnnouncementAt: true,
|
|
2051
|
+
},
|
|
2052
|
+
skip: 0,
|
|
2053
|
+
limit: LIMIT_PER_PROJECT,
|
|
2054
|
+
props: {
|
|
2055
|
+
isRoot: true,
|
|
2056
|
+
},
|
|
2057
|
+
});
|
|
2058
|
+
}
|
|
1987
2059
|
|
|
1988
|
-
|
|
1989
|
-
episodeStateTimelines =
|
|
1990
|
-
await IncidentEpisodeStateTimelineService.findBy({
|
|
1991
|
-
query: {
|
|
1992
|
-
incidentEpisodeId:
|
|
1993
|
-
QueryHelper.any(episodesOnStatusPage),
|
|
1994
|
-
projectId: statusPage.projectId!,
|
|
1995
|
-
},
|
|
1996
|
-
select: {
|
|
1997
|
-
_id: true,
|
|
1998
|
-
createdAt: true,
|
|
1999
|
-
startsAt: true,
|
|
2000
|
-
incidentEpisodeId: true,
|
|
2001
|
-
incidentState: {
|
|
2002
|
-
name: true,
|
|
2003
|
-
color: true,
|
|
2004
|
-
isCreatedState: true,
|
|
2005
|
-
isAcknowledgedState: true,
|
|
2006
|
-
isResolvedState: true,
|
|
2007
|
-
},
|
|
2008
|
-
},
|
|
2009
|
-
sort: {
|
|
2010
|
-
startsAt: SortOrder.Descending,
|
|
2011
|
-
},
|
|
2012
|
-
skip: 0,
|
|
2013
|
-
limit: LIMIT_PER_PROJECT,
|
|
2014
|
-
props: {
|
|
2015
|
-
isRoot: true,
|
|
2016
|
-
},
|
|
2017
|
-
});
|
|
2018
|
-
}
|
|
2019
|
-
}
|
|
2020
|
-
}
|
|
2021
|
-
}
|
|
2060
|
+
// check if status page has active scheduled events.
|
|
2022
2061
|
|
|
2023
|
-
|
|
2062
|
+
let scheduledEventsSelect: Select<ScheduledMaintenance> = {
|
|
2063
|
+
createdAt: true,
|
|
2064
|
+
title: true,
|
|
2065
|
+
description: true,
|
|
2066
|
+
_id: true,
|
|
2067
|
+
endsAt: true,
|
|
2068
|
+
startsAt: true,
|
|
2069
|
+
currentScheduledMaintenanceState: {
|
|
2070
|
+
name: true,
|
|
2071
|
+
color: true,
|
|
2072
|
+
isScheduledState: true,
|
|
2073
|
+
isResolvedState: true,
|
|
2074
|
+
isOngoingState: true,
|
|
2075
|
+
},
|
|
2076
|
+
monitors: {
|
|
2077
|
+
_id: true,
|
|
2078
|
+
},
|
|
2079
|
+
};
|
|
2024
2080
|
|
|
2025
|
-
|
|
2081
|
+
if (statusPage.showScheduledEventLabelsOnStatusPage) {
|
|
2082
|
+
scheduledEventsSelect = {
|
|
2083
|
+
...scheduledEventsSelect,
|
|
2084
|
+
labels: {
|
|
2085
|
+
name: true,
|
|
2086
|
+
color: true,
|
|
2087
|
+
},
|
|
2088
|
+
};
|
|
2089
|
+
}
|
|
2026
2090
|
|
|
2027
|
-
|
|
2091
|
+
let scheduledMaintenanceEvents: Array<ScheduledMaintenance> = [];
|
|
2028
2092
|
|
|
2029
|
-
|
|
2030
|
-
|
|
2093
|
+
if (statusPage.showScheduledMaintenanceEventsOnStatusPage) {
|
|
2094
|
+
scheduledMaintenanceEvents = await ScheduledMaintenanceService.findBy(
|
|
2095
|
+
{
|
|
2031
2096
|
query: {
|
|
2097
|
+
currentScheduledMaintenanceState: {
|
|
2098
|
+
isOngoingState: true,
|
|
2099
|
+
} as any,
|
|
2032
2100
|
statusPages: statusPageId as any,
|
|
2033
|
-
showAnnouncementAt: QueryHelper.lessThan(today),
|
|
2034
|
-
endAnnouncementAt: QueryHelper.greaterThanOrNull(today),
|
|
2035
2101
|
projectId: statusPage.projectId!,
|
|
2102
|
+
isVisibleOnStatusPage: true,
|
|
2036
2103
|
},
|
|
2037
|
-
select:
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
description: true,
|
|
2041
|
-
_id: true,
|
|
2042
|
-
showAnnouncementAt: true,
|
|
2043
|
-
endAnnouncementAt: true,
|
|
2104
|
+
select: scheduledEventsSelect,
|
|
2105
|
+
sort: {
|
|
2106
|
+
startsAt: SortOrder.Ascending,
|
|
2044
2107
|
},
|
|
2045
2108
|
skip: 0,
|
|
2046
2109
|
limit: LIMIT_PER_PROJECT,
|
|
2047
2110
|
props: {
|
|
2048
2111
|
isRoot: true,
|
|
2049
2112
|
},
|
|
2050
|
-
});
|
|
2051
|
-
}
|
|
2052
|
-
|
|
2053
|
-
// check if status page has active scheduled events.
|
|
2054
|
-
|
|
2055
|
-
let scheduledEventsSelect: Select<ScheduledMaintenance> = {
|
|
2056
|
-
createdAt: true,
|
|
2057
|
-
title: true,
|
|
2058
|
-
description: true,
|
|
2059
|
-
_id: true,
|
|
2060
|
-
endsAt: true,
|
|
2061
|
-
startsAt: true,
|
|
2062
|
-
currentScheduledMaintenanceState: {
|
|
2063
|
-
name: true,
|
|
2064
|
-
color: true,
|
|
2065
|
-
isScheduledState: true,
|
|
2066
|
-
isResolvedState: true,
|
|
2067
|
-
isOngoingState: true,
|
|
2068
|
-
},
|
|
2069
|
-
monitors: {
|
|
2070
|
-
_id: true,
|
|
2071
|
-
},
|
|
2072
|
-
};
|
|
2073
|
-
|
|
2074
|
-
if (statusPage.showScheduledEventLabelsOnStatusPage) {
|
|
2075
|
-
scheduledEventsSelect = {
|
|
2076
|
-
...scheduledEventsSelect,
|
|
2077
|
-
labels: {
|
|
2078
|
-
name: true,
|
|
2079
|
-
color: true,
|
|
2080
|
-
},
|
|
2081
|
-
};
|
|
2082
|
-
}
|
|
2083
|
-
|
|
2084
|
-
let scheduledMaintenanceEvents: Array<ScheduledMaintenance> = [];
|
|
2085
|
-
|
|
2086
|
-
if (statusPage.showScheduledMaintenanceEventsOnStatusPage) {
|
|
2087
|
-
scheduledMaintenanceEvents =
|
|
2088
|
-
await ScheduledMaintenanceService.findBy({
|
|
2089
|
-
query: {
|
|
2090
|
-
currentScheduledMaintenanceState: {
|
|
2091
|
-
isOngoingState: true,
|
|
2092
|
-
} as any,
|
|
2093
|
-
statusPages: statusPageId as any,
|
|
2094
|
-
projectId: statusPage.projectId!,
|
|
2095
|
-
isVisibleOnStatusPage: true,
|
|
2096
|
-
},
|
|
2097
|
-
select: scheduledEventsSelect,
|
|
2098
|
-
sort: {
|
|
2099
|
-
startsAt: SortOrder.Ascending,
|
|
2100
|
-
},
|
|
2101
|
-
skip: 0,
|
|
2102
|
-
limit: LIMIT_PER_PROJECT,
|
|
2103
|
-
props: {
|
|
2104
|
-
isRoot: true,
|
|
2105
|
-
},
|
|
2106
|
-
});
|
|
2107
|
-
}
|
|
2108
|
-
|
|
2109
|
-
let futureScheduledMaintenanceEvents: Array<ScheduledMaintenance> =
|
|
2110
|
-
[];
|
|
2111
|
-
|
|
2112
|
-
if (statusPage.showScheduledMaintenanceEventsOnStatusPage) {
|
|
2113
|
-
futureScheduledMaintenanceEvents =
|
|
2114
|
-
await ScheduledMaintenanceService.findBy({
|
|
2115
|
-
query: {
|
|
2116
|
-
currentScheduledMaintenanceState: {
|
|
2117
|
-
isScheduledState: true,
|
|
2118
|
-
} as any,
|
|
2119
|
-
statusPages: statusPageId as any,
|
|
2120
|
-
projectId: statusPage.projectId!,
|
|
2121
|
-
isVisibleOnStatusPage: true,
|
|
2122
|
-
},
|
|
2123
|
-
select: scheduledEventsSelect,
|
|
2124
|
-
sort: {
|
|
2125
|
-
startsAt: SortOrder.Ascending,
|
|
2126
|
-
},
|
|
2127
|
-
skip: 0,
|
|
2128
|
-
limit: LIMIT_PER_PROJECT,
|
|
2129
|
-
props: {
|
|
2130
|
-
isRoot: true,
|
|
2131
|
-
},
|
|
2132
|
-
});
|
|
2133
|
-
}
|
|
2134
|
-
|
|
2135
|
-
futureScheduledMaintenanceEvents.forEach(
|
|
2136
|
-
(event: ScheduledMaintenance) => {
|
|
2137
|
-
scheduledMaintenanceEvents.push(event);
|
|
2138
2113
|
},
|
|
2139
2114
|
);
|
|
2115
|
+
}
|
|
2140
2116
|
|
|
2141
|
-
|
|
2142
|
-
scheduledMaintenanceEvents.map((event: ScheduledMaintenance) => {
|
|
2143
|
-
return event.id!;
|
|
2144
|
-
});
|
|
2145
|
-
|
|
2146
|
-
let scheduledMaintenanceEventsPublicNotes: Array<ScheduledMaintenancePublicNote> =
|
|
2147
|
-
[];
|
|
2117
|
+
let futureScheduledMaintenanceEvents: Array<ScheduledMaintenance> = [];
|
|
2148
2118
|
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
limit: LIMIT_PER_PROJECT,
|
|
2172
|
-
props: {
|
|
2173
|
-
isRoot: true,
|
|
2174
|
-
},
|
|
2175
|
-
});
|
|
2176
|
-
}
|
|
2119
|
+
if (statusPage.showScheduledMaintenanceEventsOnStatusPage) {
|
|
2120
|
+
futureScheduledMaintenanceEvents =
|
|
2121
|
+
await ScheduledMaintenanceService.findBy({
|
|
2122
|
+
query: {
|
|
2123
|
+
currentScheduledMaintenanceState: {
|
|
2124
|
+
isScheduledState: true,
|
|
2125
|
+
} as any,
|
|
2126
|
+
statusPages: statusPageId as any,
|
|
2127
|
+
projectId: statusPage.projectId!,
|
|
2128
|
+
isVisibleOnStatusPage: true,
|
|
2129
|
+
},
|
|
2130
|
+
select: scheduledEventsSelect,
|
|
2131
|
+
sort: {
|
|
2132
|
+
startsAt: SortOrder.Ascending,
|
|
2133
|
+
},
|
|
2134
|
+
skip: 0,
|
|
2135
|
+
limit: LIMIT_PER_PROJECT,
|
|
2136
|
+
props: {
|
|
2137
|
+
isRoot: true,
|
|
2138
|
+
},
|
|
2139
|
+
});
|
|
2140
|
+
}
|
|
2177
2141
|
|
|
2178
|
-
|
|
2179
|
-
|
|
2142
|
+
futureScheduledMaintenanceEvents.forEach(
|
|
2143
|
+
(event: ScheduledMaintenance) => {
|
|
2144
|
+
scheduledMaintenanceEvents.push(event);
|
|
2145
|
+
},
|
|
2146
|
+
);
|
|
2180
2147
|
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
scheduledMaintenanceId: QueryHelper.any(
|
|
2186
|
-
scheduledMaintenanceEventsOnStatusPage,
|
|
2187
|
-
),
|
|
2188
|
-
projectId: statusPage.projectId!,
|
|
2189
|
-
},
|
|
2190
|
-
select: {
|
|
2191
|
-
_id: true,
|
|
2192
|
-
createdAt: true,
|
|
2193
|
-
startsAt: true,
|
|
2194
|
-
scheduledMaintenanceId: true,
|
|
2195
|
-
scheduledMaintenanceState: {
|
|
2196
|
-
_id: true,
|
|
2197
|
-
color: true,
|
|
2198
|
-
name: true,
|
|
2199
|
-
isScheduledState: true,
|
|
2200
|
-
isResolvedState: true,
|
|
2201
|
-
isOngoingState: true,
|
|
2202
|
-
},
|
|
2203
|
-
},
|
|
2148
|
+
const scheduledMaintenanceEventsOnStatusPage: Array<ObjectID> =
|
|
2149
|
+
scheduledMaintenanceEvents.map((event: ScheduledMaintenance) => {
|
|
2150
|
+
return event.id!;
|
|
2151
|
+
});
|
|
2204
2152
|
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
},
|
|
2208
|
-
skip: 0,
|
|
2209
|
-
limit: LIMIT_PER_PROJECT,
|
|
2210
|
-
props: {
|
|
2211
|
-
isRoot: true,
|
|
2212
|
-
},
|
|
2213
|
-
});
|
|
2214
|
-
}
|
|
2153
|
+
let scheduledMaintenanceEventsPublicNotes: Array<ScheduledMaintenancePublicNote> =
|
|
2154
|
+
[];
|
|
2215
2155
|
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
await
|
|
2156
|
+
if (scheduledMaintenanceEventsOnStatusPage.length > 0) {
|
|
2157
|
+
scheduledMaintenanceEventsPublicNotes =
|
|
2158
|
+
await ScheduledMaintenancePublicNoteService.findBy({
|
|
2219
2159
|
query: {
|
|
2220
|
-
|
|
2160
|
+
scheduledMaintenanceId: QueryHelper.any(
|
|
2161
|
+
scheduledMaintenanceEventsOnStatusPage,
|
|
2162
|
+
),
|
|
2163
|
+
projectId: statusPage.projectId!,
|
|
2221
2164
|
},
|
|
2222
2165
|
select: {
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2166
|
+
postedAt: true,
|
|
2167
|
+
note: true,
|
|
2168
|
+
scheduledMaintenanceId: true,
|
|
2169
|
+
attachments: {
|
|
2170
|
+
_id: true,
|
|
2171
|
+
name: true,
|
|
2172
|
+
},
|
|
2228
2173
|
},
|
|
2229
2174
|
sort: {
|
|
2230
|
-
|
|
2175
|
+
postedAt: SortOrder.Ascending,
|
|
2231
2176
|
},
|
|
2232
2177
|
skip: 0,
|
|
2233
2178
|
limit: LIMIT_PER_PROJECT,
|
|
@@ -2235,42 +2180,37 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
2235
2180
|
isRoot: true,
|
|
2236
2181
|
},
|
|
2237
2182
|
});
|
|
2183
|
+
}
|
|
2238
2184
|
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
monitorsOnStatusPage.length > 0 &&
|
|
2246
|
-
statusPage.showIncidentsOnStatusPage
|
|
2247
|
-
) {
|
|
2248
|
-
timelineIncidents = await IncidentService.findBy({
|
|
2185
|
+
let scheduledMaintenanceStateTimelines: Array<ScheduledMaintenanceStateTimeline> =
|
|
2186
|
+
[];
|
|
2187
|
+
|
|
2188
|
+
if (scheduledMaintenanceEventsOnStatusPage.length > 0) {
|
|
2189
|
+
scheduledMaintenanceStateTimelines =
|
|
2190
|
+
await ScheduledMaintenanceStateTimelineService.findBy({
|
|
2249
2191
|
query: {
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2192
|
+
scheduledMaintenanceId: QueryHelper.any(
|
|
2193
|
+
scheduledMaintenanceEventsOnStatusPage,
|
|
2194
|
+
),
|
|
2253
2195
|
projectId: statusPage.projectId!,
|
|
2254
2196
|
},
|
|
2255
2197
|
select: {
|
|
2256
2198
|
_id: true,
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
color: true,
|
|
2262
|
-
},
|
|
2263
|
-
currentIncidentState: {
|
|
2199
|
+
createdAt: true,
|
|
2200
|
+
startsAt: true,
|
|
2201
|
+
scheduledMaintenanceId: true,
|
|
2202
|
+
scheduledMaintenanceState: {
|
|
2264
2203
|
_id: true,
|
|
2265
|
-
name: true,
|
|
2266
2204
|
color: true,
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2205
|
+
name: true,
|
|
2206
|
+
isScheduledState: true,
|
|
2207
|
+
isResolvedState: true,
|
|
2208
|
+
isOngoingState: true,
|
|
2270
2209
|
},
|
|
2271
2210
|
},
|
|
2211
|
+
|
|
2272
2212
|
sort: {
|
|
2273
|
-
|
|
2213
|
+
startsAt: SortOrder.Descending, // newer state changes first
|
|
2274
2214
|
},
|
|
2275
2215
|
skip: 0,
|
|
2276
2216
|
limit: LIMIT_PER_PROJECT,
|
|
@@ -2278,101 +2218,188 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
2278
2218
|
isRoot: true,
|
|
2279
2219
|
},
|
|
2280
2220
|
});
|
|
2281
|
-
|
|
2221
|
+
}
|
|
2282
2222
|
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
}
|
|
2223
|
+
// get all status page bar chart rules
|
|
2224
|
+
const statusPageHistoryChartBarColorRules: Array<StatusPageHistoryChartBarColorRule> =
|
|
2225
|
+
await StatusPageHistoryChartBarColorRuleService.findBy({
|
|
2226
|
+
query: {
|
|
2227
|
+
statusPageId: statusPageId,
|
|
2228
|
+
},
|
|
2229
|
+
select: {
|
|
2230
|
+
_id: true,
|
|
2231
|
+
barColor: true,
|
|
2232
|
+
order: true,
|
|
2233
|
+
statusPageId: true,
|
|
2234
|
+
uptimePercentGreaterThanOrEqualTo: true,
|
|
2235
|
+
},
|
|
2236
|
+
sort: {
|
|
2237
|
+
order: SortOrder.Ascending,
|
|
2238
|
+
},
|
|
2239
|
+
skip: 0,
|
|
2240
|
+
limit: LIMIT_PER_PROJECT,
|
|
2241
|
+
props: {
|
|
2242
|
+
isRoot: true,
|
|
2243
|
+
},
|
|
2244
|
+
});
|
|
2289
2245
|
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2246
|
+
/*
|
|
2247
|
+
* Fetch all incidents (active + resolved) in the timeline date range
|
|
2248
|
+
* for the uptime bar tooltip and click-through
|
|
2249
|
+
*/
|
|
2250
|
+
let timelineIncidents: Array<Incident> = [];
|
|
2251
|
+
if (
|
|
2252
|
+
monitorsOnStatusPage.length > 0 &&
|
|
2253
|
+
statusPage.showIncidentsOnStatusPage
|
|
2254
|
+
) {
|
|
2255
|
+
timelineIncidents = await IncidentService.findBy({
|
|
2256
|
+
query: {
|
|
2257
|
+
monitors: monitorsOnStatusPage as any,
|
|
2258
|
+
declaredAt: QueryHelper.inBetween(startDate, endDate),
|
|
2259
|
+
isVisibleOnStatusPage: true,
|
|
2260
|
+
projectId: statusPage.projectId!,
|
|
2261
|
+
},
|
|
2262
|
+
select: {
|
|
2263
|
+
_id: true,
|
|
2264
|
+
title: true,
|
|
2265
|
+
declaredAt: true,
|
|
2266
|
+
incidentSeverity: {
|
|
2267
|
+
name: true,
|
|
2268
|
+
color: true,
|
|
2269
|
+
},
|
|
2270
|
+
currentIncidentState: {
|
|
2271
|
+
_id: true,
|
|
2272
|
+
name: true,
|
|
2273
|
+
color: true,
|
|
2274
|
+
},
|
|
2275
|
+
monitors: {
|
|
2276
|
+
_id: true,
|
|
2277
|
+
},
|
|
2278
|
+
},
|
|
2279
|
+
sort: {
|
|
2280
|
+
declaredAt: SortOrder.Descending,
|
|
2281
|
+
},
|
|
2282
|
+
skip: 0,
|
|
2283
|
+
limit: LIMIT_PER_PROJECT,
|
|
2284
|
+
props: {
|
|
2285
|
+
isRoot: true,
|
|
2286
|
+
},
|
|
2287
|
+
});
|
|
2288
|
+
}
|
|
2294
2289
|
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
StatusPageHistoryChartBarColorRule,
|
|
2302
|
-
),
|
|
2303
|
-
scheduledMaintenanceEvents: BaseModel.toJSONArray(
|
|
2304
|
-
scheduledMaintenanceEvents,
|
|
2305
|
-
ScheduledMaintenance,
|
|
2306
|
-
),
|
|
2307
|
-
activeAnnouncements: BaseModel.toJSONArray(
|
|
2308
|
-
activeAnnouncements,
|
|
2309
|
-
StatusPageAnnouncement,
|
|
2310
|
-
),
|
|
2311
|
-
incidentPublicNotes: BaseModel.toJSONArray(
|
|
2312
|
-
incidentPublicNotes,
|
|
2313
|
-
IncidentPublicNote,
|
|
2314
|
-
),
|
|
2290
|
+
const overallStatus: MonitorStatus | null =
|
|
2291
|
+
StatusPageService.getOverallMonitorStatus({
|
|
2292
|
+
statusPageResources,
|
|
2293
|
+
monitorStatuses,
|
|
2294
|
+
monitorGroupCurrentStatuses,
|
|
2295
|
+
});
|
|
2315
2296
|
|
|
2316
|
-
|
|
2297
|
+
const response: JSONObject = {
|
|
2298
|
+
overallStatus: overallStatus
|
|
2299
|
+
? BaseModel.toJSON(overallStatus, MonitorStatus)
|
|
2300
|
+
: null,
|
|
2317
2301
|
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2302
|
+
scheduledMaintenanceEventsPublicNotes: BaseModel.toJSONArray(
|
|
2303
|
+
scheduledMaintenanceEventsPublicNotes,
|
|
2304
|
+
ScheduledMaintenancePublicNote,
|
|
2305
|
+
),
|
|
2306
|
+
statusPageHistoryChartBarColorRules: BaseModel.toJSONArray(
|
|
2307
|
+
statusPageHistoryChartBarColorRules,
|
|
2308
|
+
StatusPageHistoryChartBarColorRule,
|
|
2309
|
+
),
|
|
2310
|
+
scheduledMaintenanceEvents: BaseModel.toJSONArray(
|
|
2311
|
+
scheduledMaintenanceEvents,
|
|
2312
|
+
ScheduledMaintenance,
|
|
2313
|
+
),
|
|
2314
|
+
activeAnnouncements: BaseModel.toJSONArray(
|
|
2315
|
+
activeAnnouncements,
|
|
2316
|
+
StatusPageAnnouncement,
|
|
2317
|
+
),
|
|
2318
|
+
incidentPublicNotes: BaseModel.toJSONArray(
|
|
2319
|
+
incidentPublicNotes,
|
|
2320
|
+
IncidentPublicNote,
|
|
2321
|
+
),
|
|
2327
2322
|
|
|
2328
|
-
|
|
2329
|
-
monitorStatusTimelines,
|
|
2330
|
-
MonitorStatusTimeline,
|
|
2331
|
-
),
|
|
2332
|
-
resourceGroups: BaseModel.toJSONArray(
|
|
2333
|
-
statusPageGroups,
|
|
2334
|
-
StatusPageGroup,
|
|
2335
|
-
),
|
|
2336
|
-
monitorStatuses: BaseModel.toJSONArray(
|
|
2337
|
-
monitorStatuses,
|
|
2338
|
-
MonitorStatus,
|
|
2339
|
-
),
|
|
2340
|
-
statusPageResources: BaseModel.toJSONArray(
|
|
2341
|
-
statusPageResources,
|
|
2342
|
-
StatusPageResource,
|
|
2343
|
-
),
|
|
2344
|
-
incidentStateTimelines: BaseModel.toJSONArray(
|
|
2345
|
-
incidentStateTimelines,
|
|
2346
|
-
IncidentStateTimeline,
|
|
2347
|
-
),
|
|
2348
|
-
statusPage: (() => {
|
|
2349
|
-
const statusPageJson: JSONObject = BaseModel.toJSONObject(
|
|
2350
|
-
statusPage,
|
|
2351
|
-
StatusPage,
|
|
2352
|
-
);
|
|
2353
|
-
delete statusPageJson["projectId"];
|
|
2354
|
-
return statusPageJson;
|
|
2355
|
-
})(),
|
|
2356
|
-
scheduledMaintenanceStateTimelines: BaseModel.toJSONArray(
|
|
2357
|
-
scheduledMaintenanceStateTimelines,
|
|
2358
|
-
ScheduledMaintenanceStateTimeline,
|
|
2359
|
-
),
|
|
2323
|
+
activeIncidents: BaseModel.toJSONArray(activeIncidents, Incident),
|
|
2360
2324
|
|
|
2361
|
-
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2325
|
+
activeEpisodes: activeEpisodesJson,
|
|
2326
|
+
episodePublicNotes: BaseModel.toJSONArray(
|
|
2327
|
+
episodePublicNotes,
|
|
2328
|
+
IncidentEpisodePublicNote,
|
|
2329
|
+
),
|
|
2330
|
+
episodeStateTimelines: BaseModel.toJSONArray(
|
|
2331
|
+
episodeStateTimelines,
|
|
2332
|
+
IncidentEpisodeStateTimeline,
|
|
2333
|
+
),
|
|
2370
2334
|
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2335
|
+
monitorStatusTimelines: BaseModel.toJSONArray(
|
|
2336
|
+
monitorStatusTimelines,
|
|
2337
|
+
MonitorStatusTimeline,
|
|
2338
|
+
),
|
|
2339
|
+
resourceGroups: BaseModel.toJSONArray(
|
|
2340
|
+
statusPageGroups,
|
|
2341
|
+
StatusPageGroup,
|
|
2342
|
+
),
|
|
2343
|
+
monitorStatuses: BaseModel.toJSONArray(
|
|
2344
|
+
monitorStatuses,
|
|
2345
|
+
MonitorStatus,
|
|
2346
|
+
),
|
|
2347
|
+
statusPageResources: BaseModel.toJSONArray(
|
|
2348
|
+
statusPageResources,
|
|
2349
|
+
StatusPageResource,
|
|
2350
|
+
),
|
|
2351
|
+
incidentStateTimelines: BaseModel.toJSONArray(
|
|
2352
|
+
incidentStateTimelines,
|
|
2353
|
+
IncidentStateTimeline,
|
|
2354
|
+
),
|
|
2355
|
+
statusPage: (() => {
|
|
2356
|
+
const statusPageJson: JSONObject = BaseModel.toJSONObject(
|
|
2357
|
+
statusPage,
|
|
2358
|
+
StatusPage,
|
|
2359
|
+
);
|
|
2360
|
+
delete statusPageJson["projectId"];
|
|
2361
|
+
return statusPageJson;
|
|
2362
|
+
})(),
|
|
2363
|
+
scheduledMaintenanceStateTimelines: BaseModel.toJSONArray(
|
|
2364
|
+
scheduledMaintenanceStateTimelines,
|
|
2365
|
+
ScheduledMaintenanceStateTimeline,
|
|
2366
|
+
),
|
|
2367
|
+
|
|
2368
|
+
monitorGroupCurrentStatuses: JSONFunctions.serialize(
|
|
2369
|
+
monitorGroupCurrentStatuses,
|
|
2370
|
+
),
|
|
2371
|
+
monitorsInGroup: JSONFunctions.serialize(monitorsInGroup),
|
|
2372
|
+
timelineIncidents: BaseModel.toJSONArray(timelineIncidents, Incident),
|
|
2373
|
+
};
|
|
2374
|
+
|
|
2375
|
+
// These can serve private-page data on a GET; never let shared caches store them.
|
|
2376
|
+
Response.setNoCacheHeaders(res);
|
|
2377
|
+
|
|
2378
|
+
return Response.sendJsonObjectResponse(req, res, response);
|
|
2379
|
+
} catch (err) {
|
|
2380
|
+
next(err);
|
|
2381
|
+
}
|
|
2382
|
+
};
|
|
2383
|
+
|
|
2384
|
+
const overviewApiPath: string = `${new this.entityType()
|
|
2385
|
+
.getCrudApiPath()
|
|
2386
|
+
?.toString()}/overview/:statusPageIdOrDomain`;
|
|
2387
|
+
|
|
2388
|
+
this.router.post(
|
|
2389
|
+
overviewApiPath,
|
|
2390
|
+
UserMiddleware.getUserMiddleware,
|
|
2391
|
+
overviewHandler,
|
|
2392
|
+
);
|
|
2393
|
+
|
|
2394
|
+
/*
|
|
2395
|
+
* GET variant of the overview endpoint. Same middleware and same handler
|
|
2396
|
+
* as the POST route above — private status pages are rejected for
|
|
2397
|
+
* unauthenticated callers exactly as they are on POST.
|
|
2398
|
+
*/
|
|
2399
|
+
this.router.get(
|
|
2400
|
+
overviewApiPath,
|
|
2401
|
+
UserMiddleware.getUserMiddleware,
|
|
2402
|
+
overviewHandler,
|
|
2376
2403
|
);
|
|
2377
2404
|
|
|
2378
2405
|
this.router.put(
|
|
@@ -2444,78 +2471,154 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
2444
2471
|
},
|
|
2445
2472
|
);
|
|
2446
2473
|
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2474
|
+
/*
|
|
2475
|
+
* Shared handler for the incidents list endpoint. Registered for both
|
|
2476
|
+
* POST and GET (same auth path). It does not read req.body.
|
|
2477
|
+
*/
|
|
2478
|
+
const incidentsListHandler: (
|
|
2479
|
+
req: ExpressRequest,
|
|
2480
|
+
res: ExpressResponse,
|
|
2481
|
+
next: NextFunction,
|
|
2482
|
+
) => Promise<void> = async (
|
|
2483
|
+
req: ExpressRequest,
|
|
2484
|
+
res: ExpressResponse,
|
|
2485
|
+
next: NextFunction,
|
|
2486
|
+
): Promise<void> => {
|
|
2487
|
+
try {
|
|
2488
|
+
const objectId: ObjectID = await resolveStatusPageIdOrThrow(
|
|
2489
|
+
req.params["statusPageIdOrDomain"] as string,
|
|
2490
|
+
);
|
|
2457
2491
|
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2492
|
+
const response: JSONObject = await this.getIncidents(
|
|
2493
|
+
objectId,
|
|
2494
|
+
null,
|
|
2495
|
+
req,
|
|
2496
|
+
);
|
|
2463
2497
|
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
}
|
|
2469
|
-
|
|
2498
|
+
// These can serve private-page data on a GET; never let shared caches store them.
|
|
2499
|
+
Response.setNoCacheHeaders(res);
|
|
2500
|
+
|
|
2501
|
+
return Response.sendJsonObjectResponse(req, res, response);
|
|
2502
|
+
} catch (err) {
|
|
2503
|
+
next(err);
|
|
2504
|
+
}
|
|
2505
|
+
};
|
|
2506
|
+
|
|
2507
|
+
const incidentsListApiPath: string = `${new this.entityType()
|
|
2508
|
+
.getCrudApiPath()
|
|
2509
|
+
?.toString()}/incidents/:statusPageIdOrDomain`;
|
|
2470
2510
|
|
|
2471
2511
|
this.router.post(
|
|
2472
|
-
|
|
2473
|
-
.getCrudApiPath()
|
|
2474
|
-
?.toString()}/scheduled-maintenance-events/:statusPageIdOrDomain`,
|
|
2512
|
+
incidentsListApiPath,
|
|
2475
2513
|
UserMiddleware.getUserMiddleware,
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
const objectId: ObjectID = await resolveStatusPageIdOrThrow(
|
|
2479
|
-
req.params["statusPageIdOrDomain"] as string,
|
|
2480
|
-
);
|
|
2514
|
+
incidentsListHandler,
|
|
2515
|
+
);
|
|
2481
2516
|
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2517
|
+
this.router.get(
|
|
2518
|
+
incidentsListApiPath,
|
|
2519
|
+
UserMiddleware.getUserMiddleware,
|
|
2520
|
+
incidentsListHandler,
|
|
2521
|
+
);
|
|
2485
2522
|
|
|
2486
|
-
|
|
2487
|
-
|
|
2523
|
+
/*
|
|
2524
|
+
* Shared handler for the scheduled maintenance events list endpoint.
|
|
2525
|
+
* Registered for both POST and GET (same auth path). It does not read
|
|
2526
|
+
* req.body.
|
|
2527
|
+
*/
|
|
2528
|
+
const scheduledMaintenanceEventsListHandler: (
|
|
2529
|
+
req: ExpressRequest,
|
|
2530
|
+
res: ExpressResponse,
|
|
2531
|
+
next: NextFunction,
|
|
2532
|
+
) => Promise<void> = async (
|
|
2533
|
+
req: ExpressRequest,
|
|
2534
|
+
res: ExpressResponse,
|
|
2535
|
+
next: NextFunction,
|
|
2536
|
+
): Promise<void> => {
|
|
2537
|
+
try {
|
|
2538
|
+
const objectId: ObjectID = await resolveStatusPageIdOrThrow(
|
|
2539
|
+
req.params["statusPageIdOrDomain"] as string,
|
|
2540
|
+
);
|
|
2488
2541
|
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2542
|
+
const response: JSONObject = await this.getScheduledMaintenanceEvents(
|
|
2543
|
+
objectId,
|
|
2544
|
+
null,
|
|
2545
|
+
|
|
2546
|
+
req,
|
|
2547
|
+
);
|
|
2548
|
+
|
|
2549
|
+
// These can serve private-page data on a GET; never let shared caches store them.
|
|
2550
|
+
Response.setNoCacheHeaders(res);
|
|
2551
|
+
|
|
2552
|
+
return Response.sendJsonObjectResponse(req, res, response);
|
|
2553
|
+
} catch (err) {
|
|
2554
|
+
next(err);
|
|
2555
|
+
}
|
|
2556
|
+
};
|
|
2557
|
+
|
|
2558
|
+
const scheduledMaintenanceEventsListApiPath: string = `${new this.entityType()
|
|
2559
|
+
.getCrudApiPath()
|
|
2560
|
+
?.toString()}/scheduled-maintenance-events/:statusPageIdOrDomain`;
|
|
2495
2561
|
|
|
2496
2562
|
this.router.post(
|
|
2497
|
-
|
|
2498
|
-
.getCrudApiPath()
|
|
2499
|
-
?.toString()}/announcements/:statusPageIdOrDomain`,
|
|
2563
|
+
scheduledMaintenanceEventsListApiPath,
|
|
2500
2564
|
UserMiddleware.getUserMiddleware,
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
const objectId: ObjectID = await resolveStatusPageIdOrThrow(
|
|
2504
|
-
req.params["statusPageIdOrDomain"] as string,
|
|
2505
|
-
);
|
|
2565
|
+
scheduledMaintenanceEventsListHandler,
|
|
2566
|
+
);
|
|
2506
2567
|
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2568
|
+
this.router.get(
|
|
2569
|
+
scheduledMaintenanceEventsListApiPath,
|
|
2570
|
+
UserMiddleware.getUserMiddleware,
|
|
2571
|
+
scheduledMaintenanceEventsListHandler,
|
|
2572
|
+
);
|
|
2510
2573
|
|
|
2511
|
-
|
|
2512
|
-
|
|
2574
|
+
/*
|
|
2575
|
+
* Shared handler for the announcements list endpoint. Registered for
|
|
2576
|
+
* both POST and GET (same auth path). It does not read req.body.
|
|
2577
|
+
*/
|
|
2578
|
+
const announcementsListHandler: (
|
|
2579
|
+
req: ExpressRequest,
|
|
2580
|
+
res: ExpressResponse,
|
|
2581
|
+
next: NextFunction,
|
|
2582
|
+
) => Promise<void> = async (
|
|
2583
|
+
req: ExpressRequest,
|
|
2584
|
+
res: ExpressResponse,
|
|
2585
|
+
next: NextFunction,
|
|
2586
|
+
): Promise<void> => {
|
|
2587
|
+
try {
|
|
2588
|
+
const objectId: ObjectID = await resolveStatusPageIdOrThrow(
|
|
2589
|
+
req.params["statusPageIdOrDomain"] as string,
|
|
2590
|
+
);
|
|
2513
2591
|
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2592
|
+
const response: JSONObject = await this.getAnnouncements(
|
|
2593
|
+
objectId,
|
|
2594
|
+
null,
|
|
2595
|
+
|
|
2596
|
+
req,
|
|
2597
|
+
);
|
|
2598
|
+
|
|
2599
|
+
// These can serve private-page data on a GET; never let shared caches store them.
|
|
2600
|
+
Response.setNoCacheHeaders(res);
|
|
2601
|
+
|
|
2602
|
+
return Response.sendJsonObjectResponse(req, res, response);
|
|
2603
|
+
} catch (err) {
|
|
2604
|
+
next(err);
|
|
2605
|
+
}
|
|
2606
|
+
};
|
|
2607
|
+
|
|
2608
|
+
const announcementsListApiPath: string = `${new this.entityType()
|
|
2609
|
+
.getCrudApiPath()
|
|
2610
|
+
?.toString()}/announcements/:statusPageIdOrDomain`;
|
|
2611
|
+
|
|
2612
|
+
this.router.post(
|
|
2613
|
+
announcementsListApiPath,
|
|
2614
|
+
UserMiddleware.getUserMiddleware,
|
|
2615
|
+
announcementsListHandler,
|
|
2616
|
+
);
|
|
2617
|
+
|
|
2618
|
+
this.router.get(
|
|
2619
|
+
announcementsListApiPath,
|
|
2620
|
+
UserMiddleware.getUserMiddleware,
|
|
2621
|
+
announcementsListHandler,
|
|
2519
2622
|
);
|
|
2520
2623
|
|
|
2521
2624
|
this.router.post(
|