@oneuptime/common 12.0.11 → 12.0.13
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/DatabaseBaseModel/DatabaseBaseModel.ts +32 -5
- package/Models/DatabaseModels/NetworkDevice.ts +64 -0
- package/Models/DatabaseModels/StatusPage.ts +59 -0
- package/Models/DatabaseModels/StatusPageOidc.ts +2 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -2
- package/Server/API/AIChatAPI.ts +55 -56
- package/Server/API/CommonAPI.ts +89 -0
- package/Server/API/MicrosoftTeamsAPI.ts +20 -9
- package/Server/API/SlackAPI.ts +13 -10
- package/Server/API/StatusPageAPI.ts +2197 -2128
- package/Server/API/TelemetryAPI.ts +72 -3
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.ts +30 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/UserAuthorization.ts +11 -2
- package/Server/Services/ApiKeyPermissionService.ts +116 -2
- package/Server/Services/DatabaseService.ts +17 -6
- package/Server/Services/LogAggregationService.ts +34 -0
- package/Server/Services/LogService.ts +21 -0
- package/Server/Services/NetworkSiteService.ts +12 -0
- package/Server/Services/SpanService.ts +21 -0
- package/Server/Services/TraceAggregationService.ts +15 -0
- package/Server/Services/WorkspaceNotificationRuleService.ts +172 -220
- package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
- package/Server/Types/Database/Permissions/AccessControlPermission.ts +47 -16
- package/Server/Types/Workflow/Components/Conditions/IfElse.ts +9 -0
- package/Server/Types/Workflow/Components/JavaScript.ts +9 -0
- package/Server/Utils/APIKey/AccessPermission.ts +16 -40
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +144 -0
- package/Server/Utils/LogRedaction.ts +576 -0
- package/Server/Utils/Logger.ts +123 -46
- package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +94 -23
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +12 -0
- package/Server/Utils/Monitor/MonitorResource.ts +29 -9
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +28 -7
- package/Server/Utils/SessionReplay/SessionReplayGateCacheStore.ts +56 -10
- package/Server/Utils/StartServer.ts +30 -9
- package/Server/Utils/StatusPageSearchEngineIndexing.ts +33 -0
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +441 -0
- package/Server/Utils/VM/VMRunner.ts +693 -874
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +488 -46
- package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +300 -0
- package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +130 -0
- package/Tests/Models/DatabaseModels/DatabaseBaseModelToJSONObjectCache.test.ts +180 -0
- package/Tests/Models/StatusPageEnableSearchEngineIndexing.test.ts +157 -0
- package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +23 -1
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +785 -0
- package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +21 -8
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +290 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +7 -1
- package/Tests/Server/API/StatusPageOverviewCache.test.ts +474 -0
- package/Tests/Server/API/StatusPageSeoSearchEngineIndexing.test.ts +189 -0
- package/Tests/Server/Infrastructure/InMemoryTTLCache.test.ts +158 -0
- package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +9 -60
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +24 -15
- package/Tests/Server/Middleware/UserAuthorization.test.ts +79 -0
- package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +332 -0
- package/Tests/Server/Services/ApiKeyPermissionService.test.ts +347 -0
- package/Tests/Server/Services/DatabaseServiceUpdateDebugLogging.test.ts +282 -0
- package/Tests/Server/Services/NetworkSiteService.test.ts +56 -2
- package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +267 -0
- package/Tests/Server/Services/WorkspaceNotificationRuleTestRuleChannels.test.ts +702 -0
- package/Tests/Server/Types/Database/Permissions/AccessControlPermission.test.ts +165 -0
- package/Tests/Server/Types/Workflow/Components/CustomCodeScriptError.test.ts +121 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +48 -36
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +213 -0
- package/Tests/Server/Utils/LogRedaction.test.ts +415 -0
- package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +245 -0
- package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +374 -4
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorJavascriptExpression.test.ts +92 -0
- package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +455 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceReachabilityRoundTrip.test.ts +369 -0
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +86 -8
- package/Tests/Server/Utils/PrivacyFilterUtil.test.ts +64 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCacheStore.test.ts +207 -0
- package/Tests/Server/Utils/StartServerBodyVerify.test.ts +335 -0
- package/Tests/Server/Utils/StatusPageSearchEngineIndexing.test.ts +60 -0
- package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +435 -0
- package/Tests/Server/Utils/VM/VMRunnerIsolation.test.ts +385 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +3 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +607 -62
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsInstalledTeamIdSpace.test.ts +778 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +44 -10
- package/Tests/Types/API/HostnameFromAuthority.test.ts +164 -0
- package/Tests/Types/Database/AccessControl/ColumnAccessControlCache.test.ts +344 -0
- package/Tests/Types/Database/DatabasePropertyFindOperator.test.ts +518 -0
- package/Tests/Types/Database/TableColumnMetadataCache.test.ts +286 -0
- package/Tests/Types/Monitor/MonitorStep.test.ts +100 -0
- package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +360 -0
- package/Tests/Types/StatusPage/SearchEngineIndexing.test.ts +86 -0
- package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +254 -0
- package/Tests/UI/Components/CardSelect.test.tsx +1021 -0
- package/Tests/UI/Components/MasterPage.test.tsx +36 -1
- package/Tests/Utils/Monitor/MonitorMetricType.test.ts +66 -1
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +115 -6
- package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +610 -0
- package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +114 -45
- package/Types/API/Hostname.ts +79 -0
- package/Types/Database/AccessControl/ColumnAccessControl.ts +33 -11
- package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +33 -11
- package/Types/Database/DatabaseProperty.ts +38 -0
- package/Types/Database/TableColumn.ts +34 -7
- package/Types/Events/Recurring.ts +16 -1
- package/Types/Monitor/CustomCodeMonitor/CustomCodeMonitorResponse.ts +14 -2
- package/Types/Monitor/MonitorStep.ts +13 -1
- package/Types/Monitor/MonitorType.ts +345 -40
- package/Types/Monitor/SSLMonitor/SslMonitorResponse.ts +43 -0
- package/Types/StatusPage/SearchEngineIndexing.ts +61 -0
- package/Types/Telemetry/ResourceEntityFacet.ts +211 -0
- package/UI/Components/CardSelect/CardSelect.tsx +714 -105
- package/UI/Components/EmptyState/EmptyState.tsx +8 -1
- package/UI/Components/Footer/Footer.tsx +32 -6
- package/UI/Components/Forms/Fields/FormField.tsx +3 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Loader/PageLoader.tsx +29 -8
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +26 -4
- package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +17 -1
- package/UI/Components/MasterPage/MasterPage.tsx +24 -11
- package/Utils/Monitor/MonitorMetricType.ts +16 -1
- package/Utils/Monitor/NetworkTopologyUtil.ts +60 -8
- package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +262 -0
- package/Utils/NetworkSite/SiteStatusRollupUtil.ts +41 -24
- package/Utils/Telemetry/CrossSignalScope.ts +75 -4
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +20 -4
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +66 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +60 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js +2 -0
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +48 -35
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +59 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +20 -6
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +13 -10
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +745 -673
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +34 -3
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +6 -2
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js +90 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +15 -6
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +2 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/LogService.js +16 -0
- package/build/dist/Server/Services/LogService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +12 -0
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/SpanService.js +16 -0
- package/build/dist/Server/Services/SpanService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +2 -0
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +137 -164
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js +21 -13
- package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +8 -0
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js +8 -0
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +9 -32
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +87 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/LogRedaction.js +449 -0
- package/build/dist/Server/Utils/LogRedaction.js.map +1 -0
- package/build/dist/Server/Utils/Logger.js +98 -45
- package/build/dist/Server/Utils/Logger.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +65 -13
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +12 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +20 -3
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +27 -7
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js +29 -10
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +10 -7
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js +24 -0
- package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +311 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -0
- package/build/dist/Server/Utils/VM/VMRunner.js +402 -525
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +366 -37
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/API/Hostname.js +63 -0
- package/build/dist/Types/API/Hostname.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +20 -6
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +20 -6
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
- package/build/dist/Types/Database/DatabaseProperty.js +38 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/TableColumn.js +24 -6
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/Events/Recurring.js +13 -1
- package/build/dist/Types/Events/Recurring.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +9 -1
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +330 -31
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/StatusPage/SearchEngineIndexing.js +55 -0
- package/build/dist/Types/StatusPage/SearchEngineIndexing.js.map +1 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js +156 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -0
- package/build/dist/UI/Components/CardSelect/CardSelect.js +360 -40
- package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js.map +1 -1
- package/build/dist/UI/Components/Footer/Footer.js +7 -4
- package/build/dist/UI/Components/Footer/Footer.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Loader/PageLoader.js +15 -5
- package/build/dist/UI/Components/Loader/PageLoader.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +16 -3
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js +13 -1
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js +10 -2
- package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js +13 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +34 -7
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +177 -0
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -0
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +23 -27
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/CrossSignalScope.js +44 -4
- package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
- package/package.json +1 -1
|
@@ -8,6 +8,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
10
|
import UserMiddleware from "../Middleware/UserAuthorization";
|
|
11
|
+
import InMemoryTTLCache from "../Infrastructure/InMemoryTTLCache";
|
|
11
12
|
import AcmeChallengeService from "../Services/AcmeChallengeService";
|
|
12
13
|
import IncidentEpisodeService from "../Services/IncidentEpisodeService";
|
|
13
14
|
import IncidentEpisodeMemberService from "../Services/IncidentEpisodeMemberService";
|
|
@@ -36,6 +37,7 @@ import StatusPageOidcService from "../Services/StatusPageOidcService";
|
|
|
36
37
|
import StatusPageSubscriberService from "../Services/StatusPageSubscriberService";
|
|
37
38
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
38
39
|
import logger, { getLogAttributesFromRequest } from "../Utils/Logger";
|
|
40
|
+
import { SEARCH_ENGINE_INDEXING_FLAG_NAME, isSearchEngineIndexingEnabled, } from "../../Types/StatusPage/SearchEngineIndexing";
|
|
39
41
|
import Response from "../Utils/Response";
|
|
40
42
|
import BaseAPI from "./BaseAPI";
|
|
41
43
|
import BaseModel from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
@@ -120,7 +122,11 @@ const resolveStatusPageIdOrThrow = async (statusPageIdOrDomain) => {
|
|
|
120
122
|
}
|
|
121
123
|
return statusPageId;
|
|
122
124
|
};
|
|
123
|
-
|
|
125
|
+
class StatusPageAPI extends BaseAPI {
|
|
126
|
+
static clearOverviewResponseCache() {
|
|
127
|
+
this.overviewResponseCache.clear();
|
|
128
|
+
this.overviewResponseInFlight.clear();
|
|
129
|
+
}
|
|
124
130
|
constructor() {
|
|
125
131
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9;
|
|
126
132
|
super(StatusPage, StatusPageService);
|
|
@@ -142,6 +148,7 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
142
148
|
pageDescription: true,
|
|
143
149
|
name: true,
|
|
144
150
|
defaultLanguage: true,
|
|
151
|
+
enableSearchEngineIndexing: true,
|
|
145
152
|
},
|
|
146
153
|
props: {
|
|
147
154
|
isRoot: true,
|
|
@@ -155,6 +162,12 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
155
162
|
description: statusPage.pageDescription,
|
|
156
163
|
_id: (_a = statusPage._id) === null || _a === void 0 ? void 0 : _a.toString(),
|
|
157
164
|
defaultLanguage: statusPage.defaultLanguage || null,
|
|
165
|
+
/*
|
|
166
|
+
* Drives <meta name="robots"> and X-Robots-Tag on the rendered page.
|
|
167
|
+
* Normalised here rather than shipping the raw column, so the two
|
|
168
|
+
* frontend servers never have to decide what a missing value means.
|
|
169
|
+
*/
|
|
170
|
+
[SEARCH_ENGINE_INDEXING_FLAG_NAME]: isSearchEngineIndexingEnabled(statusPage.enableSearchEngineIndexing),
|
|
158
171
|
});
|
|
159
172
|
});
|
|
160
173
|
// favicon api.
|
|
@@ -1056,686 +1069,42 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
1056
1069
|
* can never drift between the two methods. It does not read req.body.
|
|
1057
1070
|
*/
|
|
1058
1071
|
const overviewHandler = async (req, res, next) => {
|
|
1059
|
-
var _a;
|
|
1060
1072
|
try {
|
|
1061
1073
|
const statusPageId = await resolveStatusPageIdOrThrow(req.params["statusPageIdOrDomain"]);
|
|
1062
|
-
await this.checkHasReadAccess({
|
|
1063
|
-
statusPageId: statusPageId,
|
|
1064
|
-
req: req,
|
|
1065
|
-
});
|
|
1066
1074
|
/*
|
|
1067
|
-
*
|
|
1068
|
-
*
|
|
1069
|
-
*
|
|
1070
|
-
* (clamped to 1..90 days, ending now) instead of issuing a separate
|
|
1071
|
-
* StatusPage query here just to read that one column.
|
|
1075
|
+
* checkHasReadAccess MUST stay before any cache read: it is what keeps
|
|
1076
|
+
* private pages, IP whitelists, and master passwords enforced on every
|
|
1077
|
+
* request even when the payload below is served from cache.
|
|
1072
1078
|
*/
|
|
1073
|
-
|
|
1079
|
+
await this.checkHasReadAccess({
|
|
1074
1080
|
statusPageId: statusPageId,
|
|
1081
|
+
req: req,
|
|
1075
1082
|
});
|
|
1076
|
-
//
|
|
1077
|
-
|
|
1078
|
-
if (monitorsOnStatusPage.length > 0) {
|
|
1079
|
-
let select = {
|
|
1080
|
-
createdAt: true,
|
|
1081
|
-
declaredAt: true,
|
|
1082
|
-
updatedAt: true,
|
|
1083
|
-
title: true,
|
|
1084
|
-
description: true,
|
|
1085
|
-
_id: true,
|
|
1086
|
-
postmortemNote: true,
|
|
1087
|
-
postmortemPostedAt: true,
|
|
1088
|
-
showPostmortemOnStatusPage: true,
|
|
1089
|
-
postmortemAttachments: {
|
|
1090
|
-
_id: true,
|
|
1091
|
-
name: true,
|
|
1092
|
-
},
|
|
1093
|
-
incidentSeverity: {
|
|
1094
|
-
name: true,
|
|
1095
|
-
color: true,
|
|
1096
|
-
},
|
|
1097
|
-
currentIncidentState: {
|
|
1098
|
-
_id: true,
|
|
1099
|
-
name: true,
|
|
1100
|
-
color: true,
|
|
1101
|
-
order: true,
|
|
1102
|
-
},
|
|
1103
|
-
monitors: {
|
|
1104
|
-
_id: true,
|
|
1105
|
-
},
|
|
1106
|
-
};
|
|
1107
|
-
if (statusPage.showIncidentLabelsOnStatusPage) {
|
|
1108
|
-
select = Object.assign(Object.assign({}, select), { labels: {
|
|
1109
|
-
name: true,
|
|
1110
|
-
color: true,
|
|
1111
|
-
} });
|
|
1112
|
-
}
|
|
1113
|
-
const unresolvedIncidentStates = await IncidentStateService.getUnresolvedIncidentStates(statusPage.projectId, {
|
|
1114
|
-
isRoot: true,
|
|
1115
|
-
});
|
|
1116
|
-
const unresolvedIncidentStateIds = unresolvedIncidentStates.map((state) => {
|
|
1117
|
-
return state.id;
|
|
1118
|
-
});
|
|
1119
|
-
if (statusPage.showIncidentsOnStatusPage) {
|
|
1120
|
-
activeIncidents = await IncidentService.findBy({
|
|
1121
|
-
query: {
|
|
1122
|
-
monitors: monitorsOnStatusPage,
|
|
1123
|
-
currentIncidentStateId: QueryHelper.any(unresolvedIncidentStateIds),
|
|
1124
|
-
isVisibleOnStatusPage: true,
|
|
1125
|
-
projectId: statusPage.projectId,
|
|
1126
|
-
},
|
|
1127
|
-
select: select,
|
|
1128
|
-
sort: {
|
|
1129
|
-
declaredAt: SortOrder.Descending,
|
|
1130
|
-
createdAt: SortOrder.Descending,
|
|
1131
|
-
},
|
|
1132
|
-
skip: 0,
|
|
1133
|
-
limit: LIMIT_PER_PROJECT,
|
|
1134
|
-
props: {
|
|
1135
|
-
isRoot: true,
|
|
1136
|
-
},
|
|
1137
|
-
});
|
|
1138
|
-
}
|
|
1139
|
-
}
|
|
1140
|
-
const incidentsOnStatusPage = activeIncidents.map((incident) => {
|
|
1141
|
-
return incident.id;
|
|
1142
|
-
});
|
|
1143
|
-
let incidentPublicNotes = [];
|
|
1144
|
-
if (incidentsOnStatusPage.length > 0) {
|
|
1145
|
-
incidentPublicNotes = await IncidentPublicNoteService.findBy({
|
|
1146
|
-
query: {
|
|
1147
|
-
incidentId: QueryHelper.any(incidentsOnStatusPage),
|
|
1148
|
-
projectId: statusPage.projectId,
|
|
1149
|
-
},
|
|
1150
|
-
select: {
|
|
1151
|
-
note: true,
|
|
1152
|
-
incidentId: true,
|
|
1153
|
-
postedAt: true,
|
|
1154
|
-
attachments: {
|
|
1155
|
-
_id: true,
|
|
1156
|
-
name: true,
|
|
1157
|
-
},
|
|
1158
|
-
},
|
|
1159
|
-
sort: {
|
|
1160
|
-
postedAt: SortOrder.Descending, // new note first
|
|
1161
|
-
},
|
|
1162
|
-
skip: 0,
|
|
1163
|
-
limit: LIMIT_PER_PROJECT,
|
|
1164
|
-
props: {
|
|
1165
|
-
isRoot: true,
|
|
1166
|
-
},
|
|
1167
|
-
});
|
|
1168
|
-
}
|
|
1169
|
-
let incidentStateTimelines = [];
|
|
1170
|
-
if (incidentsOnStatusPage.length > 0) {
|
|
1171
|
-
incidentStateTimelines = await IncidentStateTimelineService.findBy({
|
|
1172
|
-
query: {
|
|
1173
|
-
incidentId: QueryHelper.any(incidentsOnStatusPage),
|
|
1174
|
-
projectId: statusPage.projectId,
|
|
1175
|
-
},
|
|
1176
|
-
select: {
|
|
1177
|
-
_id: true,
|
|
1178
|
-
createdAt: true,
|
|
1179
|
-
startsAt: true,
|
|
1180
|
-
incidentId: true,
|
|
1181
|
-
incidentState: {
|
|
1182
|
-
_id: true,
|
|
1183
|
-
name: true,
|
|
1184
|
-
color: true,
|
|
1185
|
-
isCreatedState: true,
|
|
1186
|
-
isResolvedState: true,
|
|
1187
|
-
isAcknowledgedState: true,
|
|
1188
|
-
},
|
|
1189
|
-
},
|
|
1190
|
-
sort: {
|
|
1191
|
-
startsAt: SortOrder.Descending, // newer state changes first
|
|
1192
|
-
},
|
|
1193
|
-
skip: 0,
|
|
1194
|
-
limit: LIMIT_PER_PROJECT,
|
|
1195
|
-
props: {
|
|
1196
|
-
isRoot: true,
|
|
1197
|
-
},
|
|
1198
|
-
});
|
|
1199
|
-
}
|
|
1200
|
-
// Fetch active episodes (similar to incidents)
|
|
1201
|
-
let activeEpisodes = [];
|
|
1202
|
-
let activeEpisodesJson = [];
|
|
1203
|
-
let episodePublicNotes = [];
|
|
1204
|
-
let episodeStateTimelines = [];
|
|
1083
|
+
// Resolved id, so domain-served and id-served views share one entry.
|
|
1084
|
+
const cacheKey = statusPageId.toString();
|
|
1205
1085
|
/*
|
|
1206
|
-
*
|
|
1207
|
-
*
|
|
1208
|
-
* join, up to LIMIT_PER_PROJECT rows) just to discover episode
|
|
1209
|
-
* membership — on every overview view, even though most pages have
|
|
1210
|
-
* zero active episodes most of the time. One indexed COUNT of the
|
|
1211
|
-
* project's unresolved, visible episodes lets us skip all of it in
|
|
1212
|
-
* the common case. Behavior-preserving: the final activeEpisodes
|
|
1213
|
-
* query applies exactly these three constraints, so count == 0
|
|
1214
|
-
* implies the block's outputs stay empty.
|
|
1086
|
+
* The cached JSONObject is shared across requests and must never be
|
|
1087
|
+
* mutated after build.
|
|
1215
1088
|
*/
|
|
1216
|
-
let
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
isRoot: true,
|
|
1232
|
-
},
|
|
1233
|
-
});
|
|
1234
|
-
hasActiveEpisodes = activeEpisodeCount.toNumber() > 0;
|
|
1235
|
-
}
|
|
1236
|
-
if (hasActiveEpisodes) {
|
|
1237
|
-
// First, get incidents that have monitors on status page
|
|
1238
|
-
const incidentsForEpisodes = await IncidentService.findBy({
|
|
1239
|
-
query: {
|
|
1240
|
-
monitors: monitorsOnStatusPage,
|
|
1241
|
-
isVisibleOnStatusPage: true,
|
|
1242
|
-
projectId: statusPage.projectId,
|
|
1243
|
-
},
|
|
1244
|
-
select: {
|
|
1245
|
-
_id: true,
|
|
1246
|
-
},
|
|
1247
|
-
skip: 0,
|
|
1248
|
-
limit: LIMIT_PER_PROJECT,
|
|
1249
|
-
props: {
|
|
1250
|
-
isRoot: true,
|
|
1251
|
-
},
|
|
1252
|
-
});
|
|
1253
|
-
const incidentIdsForEpisodes = incidentsForEpisodes.map((incident) => {
|
|
1254
|
-
return incident.id;
|
|
1255
|
-
});
|
|
1256
|
-
// Get episode members for these incidents
|
|
1257
|
-
let episodeMembers = [];
|
|
1258
|
-
if (incidentIdsForEpisodes.length > 0) {
|
|
1259
|
-
episodeMembers = await IncidentEpisodeMemberService.findBy({
|
|
1260
|
-
query: {
|
|
1261
|
-
incidentId: QueryHelper.any(incidentIdsForEpisodes),
|
|
1262
|
-
projectId: statusPage.projectId,
|
|
1263
|
-
},
|
|
1264
|
-
select: {
|
|
1265
|
-
incidentEpisodeId: true,
|
|
1266
|
-
incidentId: true,
|
|
1267
|
-
},
|
|
1268
|
-
skip: 0,
|
|
1269
|
-
limit: LIMIT_PER_PROJECT,
|
|
1270
|
-
props: {
|
|
1271
|
-
isRoot: true,
|
|
1272
|
-
},
|
|
1273
|
-
});
|
|
1274
|
-
}
|
|
1275
|
-
// Get unique episode IDs
|
|
1276
|
-
const episodeIdsFromMembers = new Set();
|
|
1277
|
-
for (const member of episodeMembers) {
|
|
1278
|
-
if (member.incidentEpisodeId) {
|
|
1279
|
-
episodeIdsFromMembers.add(member.incidentEpisodeId.toString());
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
// Fetch active (unresolved) episodes
|
|
1283
|
-
if (episodeIdsFromMembers.size > 0) {
|
|
1284
|
-
// unresolvedIncidentStateIds was fetched by the guard above.
|
|
1285
|
-
let selectEpisodes = {
|
|
1286
|
-
createdAt: true,
|
|
1287
|
-
declaredAt: true,
|
|
1288
|
-
updatedAt: true,
|
|
1289
|
-
title: true,
|
|
1290
|
-
description: true,
|
|
1291
|
-
_id: true,
|
|
1292
|
-
episodeNumber: true,
|
|
1293
|
-
incidentSeverity: {
|
|
1294
|
-
name: true,
|
|
1295
|
-
color: true,
|
|
1296
|
-
},
|
|
1297
|
-
currentIncidentState: {
|
|
1298
|
-
name: true,
|
|
1299
|
-
color: true,
|
|
1300
|
-
_id: true,
|
|
1301
|
-
order: true,
|
|
1302
|
-
isCreatedState: true,
|
|
1303
|
-
isAcknowledgedState: true,
|
|
1304
|
-
isResolvedState: true,
|
|
1305
|
-
},
|
|
1306
|
-
incidentCount: true,
|
|
1307
|
-
};
|
|
1308
|
-
if (statusPage.showEpisodeLabelsOnStatusPage) {
|
|
1309
|
-
selectEpisodes = Object.assign(Object.assign({}, selectEpisodes), { labels: {
|
|
1310
|
-
name: true,
|
|
1311
|
-
color: true,
|
|
1312
|
-
} });
|
|
1313
|
-
}
|
|
1314
|
-
activeEpisodes = await IncidentEpisodeService.findBy({
|
|
1315
|
-
query: {
|
|
1316
|
-
_id: QueryHelper.any(Array.from(episodeIdsFromMembers).map((id) => {
|
|
1317
|
-
return new ObjectID(id);
|
|
1318
|
-
})),
|
|
1319
|
-
currentIncidentStateId: QueryHelper.any(unresolvedIncidentStateIds),
|
|
1320
|
-
isVisibleOnStatusPage: true,
|
|
1321
|
-
projectId: statusPage.projectId,
|
|
1322
|
-
},
|
|
1323
|
-
select: selectEpisodes,
|
|
1324
|
-
sort: {
|
|
1325
|
-
declaredAt: SortOrder.Descending,
|
|
1326
|
-
createdAt: SortOrder.Descending,
|
|
1327
|
-
},
|
|
1328
|
-
skip: 0,
|
|
1329
|
-
limit: LIMIT_PER_PROJECT,
|
|
1330
|
-
props: {
|
|
1331
|
-
isRoot: true,
|
|
1332
|
-
},
|
|
1089
|
+
let response = StatusPageAPI.overviewResponseCache.get(cacheKey);
|
|
1090
|
+
if (!response) {
|
|
1091
|
+
let inFlight = StatusPageAPI.overviewResponseInFlight.get(cacheKey);
|
|
1092
|
+
if (!inFlight) {
|
|
1093
|
+
inFlight = this.buildOverviewResponse(statusPageId);
|
|
1094
|
+
StatusPageAPI.overviewResponseInFlight.set(cacheKey, inFlight);
|
|
1095
|
+
inFlight
|
|
1096
|
+
.then((builtResponse) => {
|
|
1097
|
+
StatusPageAPI.overviewResponseCache.set(cacheKey, builtResponse, StatusPageAPI.OVERVIEW_CACHE_TTL_MS);
|
|
1098
|
+
})
|
|
1099
|
+
.catch(() => {
|
|
1100
|
+
// Failed builds are never cached; the next request rebuilds.
|
|
1101
|
+
})
|
|
1102
|
+
.finally(() => {
|
|
1103
|
+
StatusPageAPI.overviewResponseInFlight.delete(cacheKey);
|
|
1333
1104
|
});
|
|
1334
|
-
// Build episode monitors map
|
|
1335
|
-
if (activeEpisodes.length > 0) {
|
|
1336
|
-
// Collect all incident IDs from episode members for active episodes
|
|
1337
|
-
const activeEpisodeIds = new Set(activeEpisodes.map((e) => {
|
|
1338
|
-
return e.id.toString();
|
|
1339
|
-
}));
|
|
1340
|
-
const memberIncidentIds = [];
|
|
1341
|
-
for (const member of episodeMembers) {
|
|
1342
|
-
if (member.incidentEpisodeId &&
|
|
1343
|
-
activeEpisodeIds.has(member.incidentEpisodeId.toString()) &&
|
|
1344
|
-
member.incidentId &&
|
|
1345
|
-
!memberIncidentIds.some((id) => {
|
|
1346
|
-
return id.toString() === member.incidentId.toString();
|
|
1347
|
-
})) {
|
|
1348
|
-
memberIncidentIds.push(member.incidentId);
|
|
1349
|
-
}
|
|
1350
|
-
}
|
|
1351
|
-
// Fetch incidents with monitors
|
|
1352
|
-
let memberIncidents = [];
|
|
1353
|
-
if (memberIncidentIds.length > 0) {
|
|
1354
|
-
memberIncidents = await IncidentService.findBy({
|
|
1355
|
-
query: {
|
|
1356
|
-
_id: QueryHelper.any(memberIncidentIds),
|
|
1357
|
-
isVisibleOnStatusPage: true,
|
|
1358
|
-
projectId: statusPage.projectId,
|
|
1359
|
-
},
|
|
1360
|
-
select: {
|
|
1361
|
-
_id: true,
|
|
1362
|
-
monitors: {
|
|
1363
|
-
_id: true,
|
|
1364
|
-
},
|
|
1365
|
-
},
|
|
1366
|
-
skip: 0,
|
|
1367
|
-
limit: LIMIT_PER_PROJECT,
|
|
1368
|
-
props: {
|
|
1369
|
-
isRoot: true,
|
|
1370
|
-
},
|
|
1371
|
-
});
|
|
1372
|
-
}
|
|
1373
|
-
// Build incident -> monitors map
|
|
1374
|
-
const incidentMonitorsMap = new Map();
|
|
1375
|
-
for (const incident of memberIncidents) {
|
|
1376
|
-
const incidentIdStr = incident.id.toString();
|
|
1377
|
-
const monitorIds = (incident.monitors || [])
|
|
1378
|
-
.map((m) => {
|
|
1379
|
-
var _a, _b;
|
|
1380
|
-
return new ObjectID(((_a = m._id) === null || _a === void 0 ? void 0 : _a.toString()) || ((_b = m.id) === null || _b === void 0 ? void 0 : _b.toString()) || "");
|
|
1381
|
-
})
|
|
1382
|
-
.filter((id) => {
|
|
1383
|
-
return id.toString() !== "";
|
|
1384
|
-
});
|
|
1385
|
-
incidentMonitorsMap.set(incidentIdStr, monitorIds);
|
|
1386
|
-
}
|
|
1387
|
-
// Build episode -> monitors map
|
|
1388
|
-
const episodeMonitorsMap = new Map();
|
|
1389
|
-
for (const member of episodeMembers) {
|
|
1390
|
-
if (member.incidentEpisodeId &&
|
|
1391
|
-
member.incidentId &&
|
|
1392
|
-
activeEpisodeIds.has(member.incidentEpisodeId.toString())) {
|
|
1393
|
-
const episodeIdStr = member.incidentEpisodeId.toString();
|
|
1394
|
-
const incidentIdStr = member.incidentId.toString();
|
|
1395
|
-
if (!episodeMonitorsMap.has(episodeIdStr)) {
|
|
1396
|
-
episodeMonitorsMap.set(episodeIdStr, []);
|
|
1397
|
-
}
|
|
1398
|
-
const episodeMonitors = episodeMonitorsMap.get(episodeIdStr);
|
|
1399
|
-
const incidentMonitors = incidentMonitorsMap.get(incidentIdStr) || [];
|
|
1400
|
-
for (const monitorId of incidentMonitors) {
|
|
1401
|
-
if (!episodeMonitors.some((m) => {
|
|
1402
|
-
return m.toString() === monitorId.toString();
|
|
1403
|
-
})) {
|
|
1404
|
-
episodeMonitors.push(monitorId);
|
|
1405
|
-
}
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
}
|
|
1409
|
-
// Serialize episodes and add monitors
|
|
1410
|
-
activeEpisodesJson = BaseModel.toJSONArray(activeEpisodes, IncidentEpisode);
|
|
1411
|
-
for (const episodeJson of activeEpisodesJson) {
|
|
1412
|
-
const episodeObj = episodeJson;
|
|
1413
|
-
const episodeId = (_a = episodeObj["_id"]) === null || _a === void 0 ? void 0 : _a.toString();
|
|
1414
|
-
if (episodeId) {
|
|
1415
|
-
const monitorIds = episodeMonitorsMap.get(episodeId) || [];
|
|
1416
|
-
episodeObj["monitors"] = monitorIds.map((id) => {
|
|
1417
|
-
return { _id: id.toString() };
|
|
1418
|
-
});
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
// Get episode public notes
|
|
1422
|
-
const episodesOnStatusPage = activeEpisodes.map((episode) => {
|
|
1423
|
-
return episode.id;
|
|
1424
|
-
});
|
|
1425
|
-
if (episodesOnStatusPage.length > 0) {
|
|
1426
|
-
episodePublicNotes =
|
|
1427
|
-
await IncidentEpisodePublicNoteService.findBy({
|
|
1428
|
-
query: {
|
|
1429
|
-
incidentEpisodeId: QueryHelper.any(episodesOnStatusPage),
|
|
1430
|
-
projectId: statusPage.projectId,
|
|
1431
|
-
},
|
|
1432
|
-
select: {
|
|
1433
|
-
postedAt: true,
|
|
1434
|
-
note: true,
|
|
1435
|
-
incidentEpisodeId: true,
|
|
1436
|
-
attachments: {
|
|
1437
|
-
_id: true,
|
|
1438
|
-
name: true,
|
|
1439
|
-
},
|
|
1440
|
-
},
|
|
1441
|
-
sort: {
|
|
1442
|
-
postedAt: SortOrder.Descending,
|
|
1443
|
-
},
|
|
1444
|
-
skip: 0,
|
|
1445
|
-
limit: LIMIT_PER_PROJECT,
|
|
1446
|
-
props: {
|
|
1447
|
-
isRoot: true,
|
|
1448
|
-
},
|
|
1449
|
-
});
|
|
1450
|
-
// Get episode state timelines
|
|
1451
|
-
episodeStateTimelines =
|
|
1452
|
-
await IncidentEpisodeStateTimelineService.findBy({
|
|
1453
|
-
query: {
|
|
1454
|
-
incidentEpisodeId: QueryHelper.any(episodesOnStatusPage),
|
|
1455
|
-
projectId: statusPage.projectId,
|
|
1456
|
-
},
|
|
1457
|
-
select: {
|
|
1458
|
-
_id: true,
|
|
1459
|
-
createdAt: true,
|
|
1460
|
-
startsAt: true,
|
|
1461
|
-
incidentEpisodeId: true,
|
|
1462
|
-
incidentState: {
|
|
1463
|
-
name: true,
|
|
1464
|
-
color: true,
|
|
1465
|
-
isCreatedState: true,
|
|
1466
|
-
isAcknowledgedState: true,
|
|
1467
|
-
isResolvedState: true,
|
|
1468
|
-
},
|
|
1469
|
-
},
|
|
1470
|
-
sort: {
|
|
1471
|
-
startsAt: SortOrder.Descending,
|
|
1472
|
-
},
|
|
1473
|
-
skip: 0,
|
|
1474
|
-
limit: LIMIT_PER_PROJECT,
|
|
1475
|
-
props: {
|
|
1476
|
-
isRoot: true,
|
|
1477
|
-
},
|
|
1478
|
-
});
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
1105
|
}
|
|
1106
|
+
response = await inFlight;
|
|
1482
1107
|
}
|
|
1483
|
-
// check if status page has active announcement.
|
|
1484
|
-
const today = OneUptimeDate.getCurrentDate();
|
|
1485
|
-
let activeAnnouncements = [];
|
|
1486
|
-
if (statusPage.showAnnouncementsOnStatusPage) {
|
|
1487
|
-
activeAnnouncements = await StatusPageAnnouncementService.findBy({
|
|
1488
|
-
query: {
|
|
1489
|
-
statusPages: statusPageId,
|
|
1490
|
-
showAnnouncementAt: QueryHelper.lessThan(today),
|
|
1491
|
-
endAnnouncementAt: QueryHelper.greaterThanOrNull(today),
|
|
1492
|
-
projectId: statusPage.projectId,
|
|
1493
|
-
},
|
|
1494
|
-
select: {
|
|
1495
|
-
createdAt: true,
|
|
1496
|
-
title: true,
|
|
1497
|
-
description: true,
|
|
1498
|
-
_id: true,
|
|
1499
|
-
showAnnouncementAt: true,
|
|
1500
|
-
endAnnouncementAt: true,
|
|
1501
|
-
},
|
|
1502
|
-
skip: 0,
|
|
1503
|
-
limit: LIMIT_PER_PROJECT,
|
|
1504
|
-
props: {
|
|
1505
|
-
isRoot: true,
|
|
1506
|
-
},
|
|
1507
|
-
});
|
|
1508
|
-
}
|
|
1509
|
-
// check if status page has active scheduled events.
|
|
1510
|
-
let scheduledEventsSelect = {
|
|
1511
|
-
createdAt: true,
|
|
1512
|
-
title: true,
|
|
1513
|
-
description: true,
|
|
1514
|
-
_id: true,
|
|
1515
|
-
endsAt: true,
|
|
1516
|
-
startsAt: true,
|
|
1517
|
-
currentScheduledMaintenanceState: {
|
|
1518
|
-
name: true,
|
|
1519
|
-
color: true,
|
|
1520
|
-
isScheduledState: true,
|
|
1521
|
-
isResolvedState: true,
|
|
1522
|
-
isOngoingState: true,
|
|
1523
|
-
},
|
|
1524
|
-
monitors: {
|
|
1525
|
-
_id: true,
|
|
1526
|
-
},
|
|
1527
|
-
};
|
|
1528
|
-
if (statusPage.showScheduledEventLabelsOnStatusPage) {
|
|
1529
|
-
scheduledEventsSelect = Object.assign(Object.assign({}, scheduledEventsSelect), { labels: {
|
|
1530
|
-
name: true,
|
|
1531
|
-
color: true,
|
|
1532
|
-
} });
|
|
1533
|
-
}
|
|
1534
|
-
let scheduledMaintenanceEvents = [];
|
|
1535
|
-
if (statusPage.showScheduledMaintenanceEventsOnStatusPage) {
|
|
1536
|
-
scheduledMaintenanceEvents = await ScheduledMaintenanceService.findBy({
|
|
1537
|
-
query: {
|
|
1538
|
-
currentScheduledMaintenanceState: {
|
|
1539
|
-
isOngoingState: true,
|
|
1540
|
-
},
|
|
1541
|
-
statusPages: statusPageId,
|
|
1542
|
-
projectId: statusPage.projectId,
|
|
1543
|
-
isVisibleOnStatusPage: true,
|
|
1544
|
-
},
|
|
1545
|
-
select: scheduledEventsSelect,
|
|
1546
|
-
sort: {
|
|
1547
|
-
startsAt: SortOrder.Ascending,
|
|
1548
|
-
},
|
|
1549
|
-
skip: 0,
|
|
1550
|
-
limit: LIMIT_PER_PROJECT,
|
|
1551
|
-
props: {
|
|
1552
|
-
isRoot: true,
|
|
1553
|
-
},
|
|
1554
|
-
});
|
|
1555
|
-
}
|
|
1556
|
-
let futureScheduledMaintenanceEvents = [];
|
|
1557
|
-
if (statusPage.showScheduledMaintenanceEventsOnStatusPage) {
|
|
1558
|
-
futureScheduledMaintenanceEvents =
|
|
1559
|
-
await ScheduledMaintenanceService.findBy({
|
|
1560
|
-
query: {
|
|
1561
|
-
currentScheduledMaintenanceState: {
|
|
1562
|
-
isScheduledState: true,
|
|
1563
|
-
},
|
|
1564
|
-
statusPages: statusPageId,
|
|
1565
|
-
projectId: statusPage.projectId,
|
|
1566
|
-
isVisibleOnStatusPage: true,
|
|
1567
|
-
},
|
|
1568
|
-
select: scheduledEventsSelect,
|
|
1569
|
-
sort: {
|
|
1570
|
-
startsAt: SortOrder.Ascending,
|
|
1571
|
-
},
|
|
1572
|
-
skip: 0,
|
|
1573
|
-
limit: LIMIT_PER_PROJECT,
|
|
1574
|
-
props: {
|
|
1575
|
-
isRoot: true,
|
|
1576
|
-
},
|
|
1577
|
-
});
|
|
1578
|
-
}
|
|
1579
|
-
futureScheduledMaintenanceEvents.forEach((event) => {
|
|
1580
|
-
scheduledMaintenanceEvents.push(event);
|
|
1581
|
-
});
|
|
1582
|
-
const scheduledMaintenanceEventsOnStatusPage = scheduledMaintenanceEvents.map((event) => {
|
|
1583
|
-
return event.id;
|
|
1584
|
-
});
|
|
1585
|
-
let scheduledMaintenanceEventsPublicNotes = [];
|
|
1586
|
-
if (scheduledMaintenanceEventsOnStatusPage.length > 0) {
|
|
1587
|
-
scheduledMaintenanceEventsPublicNotes =
|
|
1588
|
-
await ScheduledMaintenancePublicNoteService.findBy({
|
|
1589
|
-
query: {
|
|
1590
|
-
scheduledMaintenanceId: QueryHelper.any(scheduledMaintenanceEventsOnStatusPage),
|
|
1591
|
-
projectId: statusPage.projectId,
|
|
1592
|
-
},
|
|
1593
|
-
select: {
|
|
1594
|
-
postedAt: true,
|
|
1595
|
-
note: true,
|
|
1596
|
-
scheduledMaintenanceId: true,
|
|
1597
|
-
attachments: {
|
|
1598
|
-
_id: true,
|
|
1599
|
-
name: true,
|
|
1600
|
-
},
|
|
1601
|
-
},
|
|
1602
|
-
sort: {
|
|
1603
|
-
postedAt: SortOrder.Ascending,
|
|
1604
|
-
},
|
|
1605
|
-
skip: 0,
|
|
1606
|
-
limit: LIMIT_PER_PROJECT,
|
|
1607
|
-
props: {
|
|
1608
|
-
isRoot: true,
|
|
1609
|
-
},
|
|
1610
|
-
});
|
|
1611
|
-
}
|
|
1612
|
-
let scheduledMaintenanceStateTimelines = [];
|
|
1613
|
-
if (scheduledMaintenanceEventsOnStatusPage.length > 0) {
|
|
1614
|
-
scheduledMaintenanceStateTimelines =
|
|
1615
|
-
await ScheduledMaintenanceStateTimelineService.findBy({
|
|
1616
|
-
query: {
|
|
1617
|
-
scheduledMaintenanceId: QueryHelper.any(scheduledMaintenanceEventsOnStatusPage),
|
|
1618
|
-
projectId: statusPage.projectId,
|
|
1619
|
-
},
|
|
1620
|
-
select: {
|
|
1621
|
-
_id: true,
|
|
1622
|
-
createdAt: true,
|
|
1623
|
-
startsAt: true,
|
|
1624
|
-
scheduledMaintenanceId: true,
|
|
1625
|
-
scheduledMaintenanceState: {
|
|
1626
|
-
_id: true,
|
|
1627
|
-
color: true,
|
|
1628
|
-
name: true,
|
|
1629
|
-
isScheduledState: true,
|
|
1630
|
-
isResolvedState: true,
|
|
1631
|
-
isOngoingState: true,
|
|
1632
|
-
},
|
|
1633
|
-
},
|
|
1634
|
-
sort: {
|
|
1635
|
-
startsAt: SortOrder.Descending, // newer state changes first
|
|
1636
|
-
},
|
|
1637
|
-
skip: 0,
|
|
1638
|
-
limit: LIMIT_PER_PROJECT,
|
|
1639
|
-
props: {
|
|
1640
|
-
isRoot: true,
|
|
1641
|
-
},
|
|
1642
|
-
});
|
|
1643
|
-
}
|
|
1644
|
-
// get all status page bar chart rules
|
|
1645
|
-
const statusPageHistoryChartBarColorRules = await StatusPageHistoryChartBarColorRuleService.findBy({
|
|
1646
|
-
query: {
|
|
1647
|
-
statusPageId: statusPageId,
|
|
1648
|
-
},
|
|
1649
|
-
select: {
|
|
1650
|
-
_id: true,
|
|
1651
|
-
barColor: true,
|
|
1652
|
-
order: true,
|
|
1653
|
-
statusPageId: true,
|
|
1654
|
-
uptimePercentGreaterThanOrEqualTo: true,
|
|
1655
|
-
},
|
|
1656
|
-
sort: {
|
|
1657
|
-
order: SortOrder.Ascending,
|
|
1658
|
-
},
|
|
1659
|
-
skip: 0,
|
|
1660
|
-
limit: LIMIT_PER_PROJECT,
|
|
1661
|
-
props: {
|
|
1662
|
-
isRoot: true,
|
|
1663
|
-
},
|
|
1664
|
-
});
|
|
1665
|
-
/*
|
|
1666
|
-
* Fetch all incidents (active + resolved) in the timeline date range
|
|
1667
|
-
* for the uptime bar tooltip and click-through
|
|
1668
|
-
*/
|
|
1669
|
-
let timelineIncidents = [];
|
|
1670
|
-
if (monitorsOnStatusPage.length > 0 &&
|
|
1671
|
-
statusPage.showIncidentsOnStatusPage) {
|
|
1672
|
-
timelineIncidents = await IncidentService.findBy({
|
|
1673
|
-
query: {
|
|
1674
|
-
monitors: monitorsOnStatusPage,
|
|
1675
|
-
declaredAt: QueryHelper.inBetween(startDate, endDate),
|
|
1676
|
-
isVisibleOnStatusPage: true,
|
|
1677
|
-
projectId: statusPage.projectId,
|
|
1678
|
-
},
|
|
1679
|
-
select: {
|
|
1680
|
-
_id: true,
|
|
1681
|
-
title: true,
|
|
1682
|
-
declaredAt: true,
|
|
1683
|
-
incidentSeverity: {
|
|
1684
|
-
name: true,
|
|
1685
|
-
color: true,
|
|
1686
|
-
},
|
|
1687
|
-
currentIncidentState: {
|
|
1688
|
-
_id: true,
|
|
1689
|
-
name: true,
|
|
1690
|
-
color: true,
|
|
1691
|
-
},
|
|
1692
|
-
monitors: {
|
|
1693
|
-
_id: true,
|
|
1694
|
-
},
|
|
1695
|
-
},
|
|
1696
|
-
sort: {
|
|
1697
|
-
declaredAt: SortOrder.Descending,
|
|
1698
|
-
},
|
|
1699
|
-
skip: 0,
|
|
1700
|
-
limit: LIMIT_PER_PROJECT,
|
|
1701
|
-
props: {
|
|
1702
|
-
isRoot: true,
|
|
1703
|
-
},
|
|
1704
|
-
});
|
|
1705
|
-
}
|
|
1706
|
-
const overallStatus = StatusPageService.getOverallMonitorStatus({
|
|
1707
|
-
statusPageResources,
|
|
1708
|
-
monitorStatuses,
|
|
1709
|
-
monitorGroupCurrentStatuses,
|
|
1710
|
-
});
|
|
1711
|
-
const response = {
|
|
1712
|
-
overallStatus: overallStatus
|
|
1713
|
-
? BaseModel.toJSON(overallStatus, MonitorStatus)
|
|
1714
|
-
: null,
|
|
1715
|
-
scheduledMaintenanceEventsPublicNotes: BaseModel.toJSONArray(scheduledMaintenanceEventsPublicNotes, ScheduledMaintenancePublicNote),
|
|
1716
|
-
statusPageHistoryChartBarColorRules: BaseModel.toJSONArray(statusPageHistoryChartBarColorRules, StatusPageHistoryChartBarColorRule),
|
|
1717
|
-
scheduledMaintenanceEvents: BaseModel.toJSONArray(scheduledMaintenanceEvents, ScheduledMaintenance),
|
|
1718
|
-
activeAnnouncements: BaseModel.toJSONArray(activeAnnouncements, StatusPageAnnouncement),
|
|
1719
|
-
incidentPublicNotes: BaseModel.toJSONArray(incidentPublicNotes, IncidentPublicNote),
|
|
1720
|
-
activeIncidents: BaseModel.toJSONArray(activeIncidents, Incident),
|
|
1721
|
-
activeEpisodes: activeEpisodesJson,
|
|
1722
|
-
episodePublicNotes: BaseModel.toJSONArray(episodePublicNotes, IncidentEpisodePublicNote),
|
|
1723
|
-
episodeStateTimelines: BaseModel.toJSONArray(episodeStateTimelines, IncidentEpisodeStateTimeline),
|
|
1724
|
-
monitorStatusTimelines: BaseModel.toJSONArray(monitorStatusTimelines, MonitorStatusTimeline),
|
|
1725
|
-
resourceGroups: BaseModel.toJSONArray(statusPageGroups, StatusPageGroup),
|
|
1726
|
-
monitorStatuses: BaseModel.toJSONArray(monitorStatuses, MonitorStatus),
|
|
1727
|
-
statusPageResources: BaseModel.toJSONArray(statusPageResources, StatusPageResource),
|
|
1728
|
-
incidentStateTimelines: BaseModel.toJSONArray(incidentStateTimelines, IncidentStateTimeline),
|
|
1729
|
-
statusPage: (() => {
|
|
1730
|
-
const statusPageJson = BaseModel.toJSONObject(statusPage, StatusPage);
|
|
1731
|
-
delete statusPageJson["projectId"];
|
|
1732
|
-
return statusPageJson;
|
|
1733
|
-
})(),
|
|
1734
|
-
scheduledMaintenanceStateTimelines: BaseModel.toJSONArray(scheduledMaintenanceStateTimelines, ScheduledMaintenanceStateTimeline),
|
|
1735
|
-
monitorGroupCurrentStatuses: JSONFunctions.serialize(monitorGroupCurrentStatuses),
|
|
1736
|
-
monitorsInGroup: JSONFunctions.serialize(monitorsInGroup),
|
|
1737
|
-
timelineIncidents: BaseModel.toJSONArray(timelineIncidents, Incident),
|
|
1738
|
-
};
|
|
1739
1108
|
// These can serve private-page data on a GET; never let shared caches store them.
|
|
1740
1109
|
Response.setNoCacheHeaders(res);
|
|
1741
1110
|
return Response.sendJsonObjectResponse(req, res, response);
|
|
@@ -3609,10 +2978,693 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
3609
2978
|
endDateForMonitorTimeline,
|
|
3610
2979
|
};
|
|
3611
2980
|
}
|
|
3612
|
-
|
|
2981
|
+
/*
|
|
2982
|
+
* Builds the full overview payload for one status page. Everything below
|
|
2983
|
+
* is a pure function of the statusPageId — every query runs with isRoot
|
|
2984
|
+
* props and nothing is read from the request — which is what makes the
|
|
2985
|
+
* result safe to cache and share across requests. The returned JSONObject
|
|
2986
|
+
* must never be mutated after build.
|
|
2987
|
+
*/
|
|
2988
|
+
async buildOverviewResponse(statusPageId) {
|
|
3613
2989
|
var _a;
|
|
3614
|
-
|
|
3615
|
-
|
|
2990
|
+
/*
|
|
2991
|
+
* The timeline window comes from the status page's configured
|
|
2992
|
+
* showUptimeHistoryInDays. getStatusPageResourcesAndTimelines
|
|
2993
|
+
* fetches the status page row anyway, so let it compute the window
|
|
2994
|
+
* (clamped to 1..90 days, ending now) instead of issuing a separate
|
|
2995
|
+
* StatusPage query here just to read that one column.
|
|
2996
|
+
*/
|
|
2997
|
+
const { monitorStatuses, monitorGroupCurrentStatuses, statusPageResources, statusPage, monitorsOnStatusPage, monitorStatusTimelines, statusPageGroups, monitorsInGroup, startDateForMonitorTimeline: startDate, endDateForMonitorTimeline: endDate, } = await this.getStatusPageResourcesAndTimelines({
|
|
2998
|
+
statusPageId: statusPageId,
|
|
2999
|
+
});
|
|
3000
|
+
// check if status page has active incident.
|
|
3001
|
+
let activeIncidents = [];
|
|
3002
|
+
if (monitorsOnStatusPage.length > 0) {
|
|
3003
|
+
let select = {
|
|
3004
|
+
createdAt: true,
|
|
3005
|
+
declaredAt: true,
|
|
3006
|
+
updatedAt: true,
|
|
3007
|
+
title: true,
|
|
3008
|
+
description: true,
|
|
3009
|
+
_id: true,
|
|
3010
|
+
postmortemNote: true,
|
|
3011
|
+
postmortemPostedAt: true,
|
|
3012
|
+
showPostmortemOnStatusPage: true,
|
|
3013
|
+
postmortemAttachments: {
|
|
3014
|
+
_id: true,
|
|
3015
|
+
name: true,
|
|
3016
|
+
},
|
|
3017
|
+
incidentSeverity: {
|
|
3018
|
+
name: true,
|
|
3019
|
+
color: true,
|
|
3020
|
+
},
|
|
3021
|
+
currentIncidentState: {
|
|
3022
|
+
_id: true,
|
|
3023
|
+
name: true,
|
|
3024
|
+
color: true,
|
|
3025
|
+
order: true,
|
|
3026
|
+
},
|
|
3027
|
+
monitors: {
|
|
3028
|
+
_id: true,
|
|
3029
|
+
},
|
|
3030
|
+
};
|
|
3031
|
+
if (statusPage.showIncidentLabelsOnStatusPage) {
|
|
3032
|
+
select = Object.assign(Object.assign({}, select), { labels: {
|
|
3033
|
+
name: true,
|
|
3034
|
+
color: true,
|
|
3035
|
+
} });
|
|
3036
|
+
}
|
|
3037
|
+
const unresolvedIncidentStates = await IncidentStateService.getUnresolvedIncidentStates(statusPage.projectId, {
|
|
3038
|
+
isRoot: true,
|
|
3039
|
+
});
|
|
3040
|
+
const unresolvedIncidentStateIds = unresolvedIncidentStates.map((state) => {
|
|
3041
|
+
return state.id;
|
|
3042
|
+
});
|
|
3043
|
+
if (statusPage.showIncidentsOnStatusPage) {
|
|
3044
|
+
activeIncidents = await IncidentService.findBy({
|
|
3045
|
+
query: {
|
|
3046
|
+
monitors: monitorsOnStatusPage,
|
|
3047
|
+
currentIncidentStateId: QueryHelper.any(unresolvedIncidentStateIds),
|
|
3048
|
+
isVisibleOnStatusPage: true,
|
|
3049
|
+
projectId: statusPage.projectId,
|
|
3050
|
+
},
|
|
3051
|
+
select: select,
|
|
3052
|
+
sort: {
|
|
3053
|
+
declaredAt: SortOrder.Descending,
|
|
3054
|
+
createdAt: SortOrder.Descending,
|
|
3055
|
+
},
|
|
3056
|
+
skip: 0,
|
|
3057
|
+
limit: LIMIT_PER_PROJECT,
|
|
3058
|
+
props: {
|
|
3059
|
+
isRoot: true,
|
|
3060
|
+
},
|
|
3061
|
+
});
|
|
3062
|
+
}
|
|
3063
|
+
}
|
|
3064
|
+
const incidentsOnStatusPage = activeIncidents.map((incident) => {
|
|
3065
|
+
return incident.id;
|
|
3066
|
+
});
|
|
3067
|
+
let incidentPublicNotes = [];
|
|
3068
|
+
if (incidentsOnStatusPage.length > 0) {
|
|
3069
|
+
incidentPublicNotes = await IncidentPublicNoteService.findBy({
|
|
3070
|
+
query: {
|
|
3071
|
+
incidentId: QueryHelper.any(incidentsOnStatusPage),
|
|
3072
|
+
projectId: statusPage.projectId,
|
|
3073
|
+
},
|
|
3074
|
+
select: {
|
|
3075
|
+
note: true,
|
|
3076
|
+
incidentId: true,
|
|
3077
|
+
postedAt: true,
|
|
3078
|
+
attachments: {
|
|
3079
|
+
_id: true,
|
|
3080
|
+
name: true,
|
|
3081
|
+
},
|
|
3082
|
+
},
|
|
3083
|
+
sort: {
|
|
3084
|
+
postedAt: SortOrder.Descending, // new note first
|
|
3085
|
+
},
|
|
3086
|
+
skip: 0,
|
|
3087
|
+
limit: LIMIT_PER_PROJECT,
|
|
3088
|
+
props: {
|
|
3089
|
+
isRoot: true,
|
|
3090
|
+
},
|
|
3091
|
+
});
|
|
3092
|
+
}
|
|
3093
|
+
let incidentStateTimelines = [];
|
|
3094
|
+
if (incidentsOnStatusPage.length > 0) {
|
|
3095
|
+
incidentStateTimelines = await IncidentStateTimelineService.findBy({
|
|
3096
|
+
query: {
|
|
3097
|
+
incidentId: QueryHelper.any(incidentsOnStatusPage),
|
|
3098
|
+
projectId: statusPage.projectId,
|
|
3099
|
+
},
|
|
3100
|
+
select: {
|
|
3101
|
+
_id: true,
|
|
3102
|
+
createdAt: true,
|
|
3103
|
+
startsAt: true,
|
|
3104
|
+
incidentId: true,
|
|
3105
|
+
incidentState: {
|
|
3106
|
+
_id: true,
|
|
3107
|
+
name: true,
|
|
3108
|
+
color: true,
|
|
3109
|
+
isCreatedState: true,
|
|
3110
|
+
isResolvedState: true,
|
|
3111
|
+
isAcknowledgedState: true,
|
|
3112
|
+
},
|
|
3113
|
+
},
|
|
3114
|
+
sort: {
|
|
3115
|
+
startsAt: SortOrder.Descending, // newer state changes first
|
|
3116
|
+
},
|
|
3117
|
+
skip: 0,
|
|
3118
|
+
limit: LIMIT_PER_PROJECT,
|
|
3119
|
+
props: {
|
|
3120
|
+
isRoot: true,
|
|
3121
|
+
},
|
|
3122
|
+
});
|
|
3123
|
+
}
|
|
3124
|
+
// Fetch active episodes (similar to incidents)
|
|
3125
|
+
let activeEpisodes = [];
|
|
3126
|
+
let activeEpisodesJson = [];
|
|
3127
|
+
let episodePublicNotes = [];
|
|
3128
|
+
let episodeStateTimelines = [];
|
|
3129
|
+
/*
|
|
3130
|
+
* Cheap guard before the expensive part: the block below scans every
|
|
3131
|
+
* incident ever attached to this page's monitors (a many-to-many
|
|
3132
|
+
* join, up to LIMIT_PER_PROJECT rows) just to discover episode
|
|
3133
|
+
* membership — on every overview view, even though most pages have
|
|
3134
|
+
* zero active episodes most of the time. One indexed COUNT of the
|
|
3135
|
+
* project's unresolved, visible episodes lets us skip all of it in
|
|
3136
|
+
* the common case. Behavior-preserving: the final activeEpisodes
|
|
3137
|
+
* query applies exactly these three constraints, so count == 0
|
|
3138
|
+
* implies the block's outputs stay empty.
|
|
3139
|
+
*/
|
|
3140
|
+
let unresolvedIncidentStateIds = [];
|
|
3141
|
+
let hasActiveEpisodes = false;
|
|
3142
|
+
if (statusPage.showEpisodesOnStatusPage &&
|
|
3143
|
+
monitorsOnStatusPage.length > 0) {
|
|
3144
|
+
const unresolvedIncidentStates = await IncidentStateService.getUnresolvedIncidentStates(statusPage.projectId, { isRoot: true });
|
|
3145
|
+
unresolvedIncidentStateIds = unresolvedIncidentStates.map((state) => {
|
|
3146
|
+
return state.id;
|
|
3147
|
+
});
|
|
3148
|
+
const activeEpisodeCount = await IncidentEpisodeService.countBy({
|
|
3149
|
+
query: {
|
|
3150
|
+
projectId: statusPage.projectId,
|
|
3151
|
+
isVisibleOnStatusPage: true,
|
|
3152
|
+
currentIncidentStateId: QueryHelper.any(unresolvedIncidentStateIds),
|
|
3153
|
+
},
|
|
3154
|
+
props: {
|
|
3155
|
+
isRoot: true,
|
|
3156
|
+
},
|
|
3157
|
+
});
|
|
3158
|
+
hasActiveEpisodes = activeEpisodeCount.toNumber() > 0;
|
|
3159
|
+
}
|
|
3160
|
+
if (hasActiveEpisodes) {
|
|
3161
|
+
// First, get incidents that have monitors on status page
|
|
3162
|
+
const incidentsForEpisodes = await IncidentService.findBy({
|
|
3163
|
+
query: {
|
|
3164
|
+
monitors: monitorsOnStatusPage,
|
|
3165
|
+
isVisibleOnStatusPage: true,
|
|
3166
|
+
projectId: statusPage.projectId,
|
|
3167
|
+
},
|
|
3168
|
+
select: {
|
|
3169
|
+
_id: true,
|
|
3170
|
+
},
|
|
3171
|
+
skip: 0,
|
|
3172
|
+
limit: LIMIT_PER_PROJECT,
|
|
3173
|
+
props: {
|
|
3174
|
+
isRoot: true,
|
|
3175
|
+
},
|
|
3176
|
+
});
|
|
3177
|
+
const incidentIdsForEpisodes = incidentsForEpisodes.map((incident) => {
|
|
3178
|
+
return incident.id;
|
|
3179
|
+
});
|
|
3180
|
+
// Get episode members for these incidents
|
|
3181
|
+
let episodeMembers = [];
|
|
3182
|
+
if (incidentIdsForEpisodes.length > 0) {
|
|
3183
|
+
episodeMembers = await IncidentEpisodeMemberService.findBy({
|
|
3184
|
+
query: {
|
|
3185
|
+
incidentId: QueryHelper.any(incidentIdsForEpisodes),
|
|
3186
|
+
projectId: statusPage.projectId,
|
|
3187
|
+
},
|
|
3188
|
+
select: {
|
|
3189
|
+
incidentEpisodeId: true,
|
|
3190
|
+
incidentId: true,
|
|
3191
|
+
},
|
|
3192
|
+
skip: 0,
|
|
3193
|
+
limit: LIMIT_PER_PROJECT,
|
|
3194
|
+
props: {
|
|
3195
|
+
isRoot: true,
|
|
3196
|
+
},
|
|
3197
|
+
});
|
|
3198
|
+
}
|
|
3199
|
+
// Get unique episode IDs
|
|
3200
|
+
const episodeIdsFromMembers = new Set();
|
|
3201
|
+
for (const member of episodeMembers) {
|
|
3202
|
+
if (member.incidentEpisodeId) {
|
|
3203
|
+
episodeIdsFromMembers.add(member.incidentEpisodeId.toString());
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
// Fetch active (unresolved) episodes
|
|
3207
|
+
if (episodeIdsFromMembers.size > 0) {
|
|
3208
|
+
// unresolvedIncidentStateIds was fetched by the guard above.
|
|
3209
|
+
let selectEpisodes = {
|
|
3210
|
+
createdAt: true,
|
|
3211
|
+
declaredAt: true,
|
|
3212
|
+
updatedAt: true,
|
|
3213
|
+
title: true,
|
|
3214
|
+
description: true,
|
|
3215
|
+
_id: true,
|
|
3216
|
+
episodeNumber: true,
|
|
3217
|
+
incidentSeverity: {
|
|
3218
|
+
name: true,
|
|
3219
|
+
color: true,
|
|
3220
|
+
},
|
|
3221
|
+
currentIncidentState: {
|
|
3222
|
+
name: true,
|
|
3223
|
+
color: true,
|
|
3224
|
+
_id: true,
|
|
3225
|
+
order: true,
|
|
3226
|
+
isCreatedState: true,
|
|
3227
|
+
isAcknowledgedState: true,
|
|
3228
|
+
isResolvedState: true,
|
|
3229
|
+
},
|
|
3230
|
+
incidentCount: true,
|
|
3231
|
+
};
|
|
3232
|
+
if (statusPage.showEpisodeLabelsOnStatusPage) {
|
|
3233
|
+
selectEpisodes = Object.assign(Object.assign({}, selectEpisodes), { labels: {
|
|
3234
|
+
name: true,
|
|
3235
|
+
color: true,
|
|
3236
|
+
} });
|
|
3237
|
+
}
|
|
3238
|
+
activeEpisodes = await IncidentEpisodeService.findBy({
|
|
3239
|
+
query: {
|
|
3240
|
+
_id: QueryHelper.any(Array.from(episodeIdsFromMembers).map((id) => {
|
|
3241
|
+
return new ObjectID(id);
|
|
3242
|
+
})),
|
|
3243
|
+
currentIncidentStateId: QueryHelper.any(unresolvedIncidentStateIds),
|
|
3244
|
+
isVisibleOnStatusPage: true,
|
|
3245
|
+
projectId: statusPage.projectId,
|
|
3246
|
+
},
|
|
3247
|
+
select: selectEpisodes,
|
|
3248
|
+
sort: {
|
|
3249
|
+
declaredAt: SortOrder.Descending,
|
|
3250
|
+
createdAt: SortOrder.Descending,
|
|
3251
|
+
},
|
|
3252
|
+
skip: 0,
|
|
3253
|
+
limit: LIMIT_PER_PROJECT,
|
|
3254
|
+
props: {
|
|
3255
|
+
isRoot: true,
|
|
3256
|
+
},
|
|
3257
|
+
});
|
|
3258
|
+
// Build episode monitors map
|
|
3259
|
+
if (activeEpisodes.length > 0) {
|
|
3260
|
+
// Collect all incident IDs from episode members for active episodes
|
|
3261
|
+
const activeEpisodeIds = new Set(activeEpisodes.map((e) => {
|
|
3262
|
+
return e.id.toString();
|
|
3263
|
+
}));
|
|
3264
|
+
const memberIncidentIds = [];
|
|
3265
|
+
for (const member of episodeMembers) {
|
|
3266
|
+
if (member.incidentEpisodeId &&
|
|
3267
|
+
activeEpisodeIds.has(member.incidentEpisodeId.toString()) &&
|
|
3268
|
+
member.incidentId &&
|
|
3269
|
+
!memberIncidentIds.some((id) => {
|
|
3270
|
+
return id.toString() === member.incidentId.toString();
|
|
3271
|
+
})) {
|
|
3272
|
+
memberIncidentIds.push(member.incidentId);
|
|
3273
|
+
}
|
|
3274
|
+
}
|
|
3275
|
+
// Fetch incidents with monitors
|
|
3276
|
+
let memberIncidents = [];
|
|
3277
|
+
if (memberIncidentIds.length > 0) {
|
|
3278
|
+
memberIncidents = await IncidentService.findBy({
|
|
3279
|
+
query: {
|
|
3280
|
+
_id: QueryHelper.any(memberIncidentIds),
|
|
3281
|
+
isVisibleOnStatusPage: true,
|
|
3282
|
+
projectId: statusPage.projectId,
|
|
3283
|
+
},
|
|
3284
|
+
select: {
|
|
3285
|
+
_id: true,
|
|
3286
|
+
monitors: {
|
|
3287
|
+
_id: true,
|
|
3288
|
+
},
|
|
3289
|
+
},
|
|
3290
|
+
skip: 0,
|
|
3291
|
+
limit: LIMIT_PER_PROJECT,
|
|
3292
|
+
props: {
|
|
3293
|
+
isRoot: true,
|
|
3294
|
+
},
|
|
3295
|
+
});
|
|
3296
|
+
}
|
|
3297
|
+
// Build incident -> monitors map
|
|
3298
|
+
const incidentMonitorsMap = new Map();
|
|
3299
|
+
for (const incident of memberIncidents) {
|
|
3300
|
+
const incidentIdStr = incident.id.toString();
|
|
3301
|
+
const monitorIds = (incident.monitors || [])
|
|
3302
|
+
.map((m) => {
|
|
3303
|
+
var _a, _b;
|
|
3304
|
+
return new ObjectID(((_a = m._id) === null || _a === void 0 ? void 0 : _a.toString()) || ((_b = m.id) === null || _b === void 0 ? void 0 : _b.toString()) || "");
|
|
3305
|
+
})
|
|
3306
|
+
.filter((id) => {
|
|
3307
|
+
return id.toString() !== "";
|
|
3308
|
+
});
|
|
3309
|
+
incidentMonitorsMap.set(incidentIdStr, monitorIds);
|
|
3310
|
+
}
|
|
3311
|
+
// Build episode -> monitors map
|
|
3312
|
+
const episodeMonitorsMap = new Map();
|
|
3313
|
+
for (const member of episodeMembers) {
|
|
3314
|
+
if (member.incidentEpisodeId &&
|
|
3315
|
+
member.incidentId &&
|
|
3316
|
+
activeEpisodeIds.has(member.incidentEpisodeId.toString())) {
|
|
3317
|
+
const episodeIdStr = member.incidentEpisodeId.toString();
|
|
3318
|
+
const incidentIdStr = member.incidentId.toString();
|
|
3319
|
+
if (!episodeMonitorsMap.has(episodeIdStr)) {
|
|
3320
|
+
episodeMonitorsMap.set(episodeIdStr, []);
|
|
3321
|
+
}
|
|
3322
|
+
const episodeMonitors = episodeMonitorsMap.get(episodeIdStr);
|
|
3323
|
+
const incidentMonitors = incidentMonitorsMap.get(incidentIdStr) || [];
|
|
3324
|
+
for (const monitorId of incidentMonitors) {
|
|
3325
|
+
if (!episodeMonitors.some((m) => {
|
|
3326
|
+
return m.toString() === monitorId.toString();
|
|
3327
|
+
})) {
|
|
3328
|
+
episodeMonitors.push(monitorId);
|
|
3329
|
+
}
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
}
|
|
3333
|
+
// Serialize episodes and add monitors
|
|
3334
|
+
activeEpisodesJson = BaseModel.toJSONArray(activeEpisodes, IncidentEpisode);
|
|
3335
|
+
for (const episodeJson of activeEpisodesJson) {
|
|
3336
|
+
const episodeObj = episodeJson;
|
|
3337
|
+
const episodeId = (_a = episodeObj["_id"]) === null || _a === void 0 ? void 0 : _a.toString();
|
|
3338
|
+
if (episodeId) {
|
|
3339
|
+
const monitorIds = episodeMonitorsMap.get(episodeId) || [];
|
|
3340
|
+
episodeObj["monitors"] = monitorIds.map((id) => {
|
|
3341
|
+
return { _id: id.toString() };
|
|
3342
|
+
});
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
// Get episode public notes
|
|
3346
|
+
const episodesOnStatusPage = activeEpisodes.map((episode) => {
|
|
3347
|
+
return episode.id;
|
|
3348
|
+
});
|
|
3349
|
+
if (episodesOnStatusPage.length > 0) {
|
|
3350
|
+
episodePublicNotes = await IncidentEpisodePublicNoteService.findBy({
|
|
3351
|
+
query: {
|
|
3352
|
+
incidentEpisodeId: QueryHelper.any(episodesOnStatusPage),
|
|
3353
|
+
projectId: statusPage.projectId,
|
|
3354
|
+
},
|
|
3355
|
+
select: {
|
|
3356
|
+
postedAt: true,
|
|
3357
|
+
note: true,
|
|
3358
|
+
incidentEpisodeId: true,
|
|
3359
|
+
attachments: {
|
|
3360
|
+
_id: true,
|
|
3361
|
+
name: true,
|
|
3362
|
+
},
|
|
3363
|
+
},
|
|
3364
|
+
sort: {
|
|
3365
|
+
postedAt: SortOrder.Descending,
|
|
3366
|
+
},
|
|
3367
|
+
skip: 0,
|
|
3368
|
+
limit: LIMIT_PER_PROJECT,
|
|
3369
|
+
props: {
|
|
3370
|
+
isRoot: true,
|
|
3371
|
+
},
|
|
3372
|
+
});
|
|
3373
|
+
// Get episode state timelines
|
|
3374
|
+
episodeStateTimelines =
|
|
3375
|
+
await IncidentEpisodeStateTimelineService.findBy({
|
|
3376
|
+
query: {
|
|
3377
|
+
incidentEpisodeId: QueryHelper.any(episodesOnStatusPage),
|
|
3378
|
+
projectId: statusPage.projectId,
|
|
3379
|
+
},
|
|
3380
|
+
select: {
|
|
3381
|
+
_id: true,
|
|
3382
|
+
createdAt: true,
|
|
3383
|
+
startsAt: true,
|
|
3384
|
+
incidentEpisodeId: true,
|
|
3385
|
+
incidentState: {
|
|
3386
|
+
name: true,
|
|
3387
|
+
color: true,
|
|
3388
|
+
isCreatedState: true,
|
|
3389
|
+
isAcknowledgedState: true,
|
|
3390
|
+
isResolvedState: true,
|
|
3391
|
+
},
|
|
3392
|
+
},
|
|
3393
|
+
sort: {
|
|
3394
|
+
startsAt: SortOrder.Descending,
|
|
3395
|
+
},
|
|
3396
|
+
skip: 0,
|
|
3397
|
+
limit: LIMIT_PER_PROJECT,
|
|
3398
|
+
props: {
|
|
3399
|
+
isRoot: true,
|
|
3400
|
+
},
|
|
3401
|
+
});
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
}
|
|
3405
|
+
}
|
|
3406
|
+
// check if status page has active announcement.
|
|
3407
|
+
const today = OneUptimeDate.getCurrentDate();
|
|
3408
|
+
let activeAnnouncements = [];
|
|
3409
|
+
if (statusPage.showAnnouncementsOnStatusPage) {
|
|
3410
|
+
activeAnnouncements = await StatusPageAnnouncementService.findBy({
|
|
3411
|
+
query: {
|
|
3412
|
+
statusPages: statusPageId,
|
|
3413
|
+
showAnnouncementAt: QueryHelper.lessThan(today),
|
|
3414
|
+
endAnnouncementAt: QueryHelper.greaterThanOrNull(today),
|
|
3415
|
+
projectId: statusPage.projectId,
|
|
3416
|
+
},
|
|
3417
|
+
select: {
|
|
3418
|
+
createdAt: true,
|
|
3419
|
+
title: true,
|
|
3420
|
+
description: true,
|
|
3421
|
+
_id: true,
|
|
3422
|
+
showAnnouncementAt: true,
|
|
3423
|
+
endAnnouncementAt: true,
|
|
3424
|
+
},
|
|
3425
|
+
skip: 0,
|
|
3426
|
+
limit: LIMIT_PER_PROJECT,
|
|
3427
|
+
props: {
|
|
3428
|
+
isRoot: true,
|
|
3429
|
+
},
|
|
3430
|
+
});
|
|
3431
|
+
}
|
|
3432
|
+
// check if status page has active scheduled events.
|
|
3433
|
+
let scheduledEventsSelect = {
|
|
3434
|
+
createdAt: true,
|
|
3435
|
+
title: true,
|
|
3436
|
+
description: true,
|
|
3437
|
+
_id: true,
|
|
3438
|
+
endsAt: true,
|
|
3439
|
+
startsAt: true,
|
|
3440
|
+
currentScheduledMaintenanceState: {
|
|
3441
|
+
name: true,
|
|
3442
|
+
color: true,
|
|
3443
|
+
isScheduledState: true,
|
|
3444
|
+
isResolvedState: true,
|
|
3445
|
+
isOngoingState: true,
|
|
3446
|
+
},
|
|
3447
|
+
monitors: {
|
|
3448
|
+
_id: true,
|
|
3449
|
+
},
|
|
3450
|
+
};
|
|
3451
|
+
if (statusPage.showScheduledEventLabelsOnStatusPage) {
|
|
3452
|
+
scheduledEventsSelect = Object.assign(Object.assign({}, scheduledEventsSelect), { labels: {
|
|
3453
|
+
name: true,
|
|
3454
|
+
color: true,
|
|
3455
|
+
} });
|
|
3456
|
+
}
|
|
3457
|
+
let scheduledMaintenanceEvents = [];
|
|
3458
|
+
if (statusPage.showScheduledMaintenanceEventsOnStatusPage) {
|
|
3459
|
+
scheduledMaintenanceEvents = await ScheduledMaintenanceService.findBy({
|
|
3460
|
+
query: {
|
|
3461
|
+
currentScheduledMaintenanceState: {
|
|
3462
|
+
isOngoingState: true,
|
|
3463
|
+
},
|
|
3464
|
+
statusPages: statusPageId,
|
|
3465
|
+
projectId: statusPage.projectId,
|
|
3466
|
+
isVisibleOnStatusPage: true,
|
|
3467
|
+
},
|
|
3468
|
+
select: scheduledEventsSelect,
|
|
3469
|
+
sort: {
|
|
3470
|
+
startsAt: SortOrder.Ascending,
|
|
3471
|
+
},
|
|
3472
|
+
skip: 0,
|
|
3473
|
+
limit: LIMIT_PER_PROJECT,
|
|
3474
|
+
props: {
|
|
3475
|
+
isRoot: true,
|
|
3476
|
+
},
|
|
3477
|
+
});
|
|
3478
|
+
}
|
|
3479
|
+
let futureScheduledMaintenanceEvents = [];
|
|
3480
|
+
if (statusPage.showScheduledMaintenanceEventsOnStatusPage) {
|
|
3481
|
+
futureScheduledMaintenanceEvents =
|
|
3482
|
+
await ScheduledMaintenanceService.findBy({
|
|
3483
|
+
query: {
|
|
3484
|
+
currentScheduledMaintenanceState: {
|
|
3485
|
+
isScheduledState: true,
|
|
3486
|
+
},
|
|
3487
|
+
statusPages: statusPageId,
|
|
3488
|
+
projectId: statusPage.projectId,
|
|
3489
|
+
isVisibleOnStatusPage: true,
|
|
3490
|
+
},
|
|
3491
|
+
select: scheduledEventsSelect,
|
|
3492
|
+
sort: {
|
|
3493
|
+
startsAt: SortOrder.Ascending,
|
|
3494
|
+
},
|
|
3495
|
+
skip: 0,
|
|
3496
|
+
limit: LIMIT_PER_PROJECT,
|
|
3497
|
+
props: {
|
|
3498
|
+
isRoot: true,
|
|
3499
|
+
},
|
|
3500
|
+
});
|
|
3501
|
+
}
|
|
3502
|
+
futureScheduledMaintenanceEvents.forEach((event) => {
|
|
3503
|
+
scheduledMaintenanceEvents.push(event);
|
|
3504
|
+
});
|
|
3505
|
+
const scheduledMaintenanceEventsOnStatusPage = scheduledMaintenanceEvents.map((event) => {
|
|
3506
|
+
return event.id;
|
|
3507
|
+
});
|
|
3508
|
+
let scheduledMaintenanceEventsPublicNotes = [];
|
|
3509
|
+
if (scheduledMaintenanceEventsOnStatusPage.length > 0) {
|
|
3510
|
+
scheduledMaintenanceEventsPublicNotes =
|
|
3511
|
+
await ScheduledMaintenancePublicNoteService.findBy({
|
|
3512
|
+
query: {
|
|
3513
|
+
scheduledMaintenanceId: QueryHelper.any(scheduledMaintenanceEventsOnStatusPage),
|
|
3514
|
+
projectId: statusPage.projectId,
|
|
3515
|
+
},
|
|
3516
|
+
select: {
|
|
3517
|
+
postedAt: true,
|
|
3518
|
+
note: true,
|
|
3519
|
+
scheduledMaintenanceId: true,
|
|
3520
|
+
attachments: {
|
|
3521
|
+
_id: true,
|
|
3522
|
+
name: true,
|
|
3523
|
+
},
|
|
3524
|
+
},
|
|
3525
|
+
sort: {
|
|
3526
|
+
postedAt: SortOrder.Ascending,
|
|
3527
|
+
},
|
|
3528
|
+
skip: 0,
|
|
3529
|
+
limit: LIMIT_PER_PROJECT,
|
|
3530
|
+
props: {
|
|
3531
|
+
isRoot: true,
|
|
3532
|
+
},
|
|
3533
|
+
});
|
|
3534
|
+
}
|
|
3535
|
+
let scheduledMaintenanceStateTimelines = [];
|
|
3536
|
+
if (scheduledMaintenanceEventsOnStatusPage.length > 0) {
|
|
3537
|
+
scheduledMaintenanceStateTimelines =
|
|
3538
|
+
await ScheduledMaintenanceStateTimelineService.findBy({
|
|
3539
|
+
query: {
|
|
3540
|
+
scheduledMaintenanceId: QueryHelper.any(scheduledMaintenanceEventsOnStatusPage),
|
|
3541
|
+
projectId: statusPage.projectId,
|
|
3542
|
+
},
|
|
3543
|
+
select: {
|
|
3544
|
+
_id: true,
|
|
3545
|
+
createdAt: true,
|
|
3546
|
+
startsAt: true,
|
|
3547
|
+
scheduledMaintenanceId: true,
|
|
3548
|
+
scheduledMaintenanceState: {
|
|
3549
|
+
_id: true,
|
|
3550
|
+
color: true,
|
|
3551
|
+
name: true,
|
|
3552
|
+
isScheduledState: true,
|
|
3553
|
+
isResolvedState: true,
|
|
3554
|
+
isOngoingState: true,
|
|
3555
|
+
},
|
|
3556
|
+
},
|
|
3557
|
+
sort: {
|
|
3558
|
+
startsAt: SortOrder.Descending, // newer state changes first
|
|
3559
|
+
},
|
|
3560
|
+
skip: 0,
|
|
3561
|
+
limit: LIMIT_PER_PROJECT,
|
|
3562
|
+
props: {
|
|
3563
|
+
isRoot: true,
|
|
3564
|
+
},
|
|
3565
|
+
});
|
|
3566
|
+
}
|
|
3567
|
+
// get all status page bar chart rules
|
|
3568
|
+
const statusPageHistoryChartBarColorRules = await StatusPageHistoryChartBarColorRuleService.findBy({
|
|
3569
|
+
query: {
|
|
3570
|
+
statusPageId: statusPageId,
|
|
3571
|
+
},
|
|
3572
|
+
select: {
|
|
3573
|
+
_id: true,
|
|
3574
|
+
barColor: true,
|
|
3575
|
+
order: true,
|
|
3576
|
+
statusPageId: true,
|
|
3577
|
+
uptimePercentGreaterThanOrEqualTo: true,
|
|
3578
|
+
},
|
|
3579
|
+
sort: {
|
|
3580
|
+
order: SortOrder.Ascending,
|
|
3581
|
+
},
|
|
3582
|
+
skip: 0,
|
|
3583
|
+
limit: LIMIT_PER_PROJECT,
|
|
3584
|
+
props: {
|
|
3585
|
+
isRoot: true,
|
|
3586
|
+
},
|
|
3587
|
+
});
|
|
3588
|
+
/*
|
|
3589
|
+
* Fetch all incidents (active + resolved) in the timeline date range
|
|
3590
|
+
* for the uptime bar tooltip and click-through
|
|
3591
|
+
*/
|
|
3592
|
+
let timelineIncidents = [];
|
|
3593
|
+
if (monitorsOnStatusPage.length > 0 &&
|
|
3594
|
+
statusPage.showIncidentsOnStatusPage) {
|
|
3595
|
+
timelineIncidents = await IncidentService.findBy({
|
|
3596
|
+
query: {
|
|
3597
|
+
monitors: monitorsOnStatusPage,
|
|
3598
|
+
declaredAt: QueryHelper.inBetween(startDate, endDate),
|
|
3599
|
+
isVisibleOnStatusPage: true,
|
|
3600
|
+
projectId: statusPage.projectId,
|
|
3601
|
+
},
|
|
3602
|
+
select: {
|
|
3603
|
+
_id: true,
|
|
3604
|
+
title: true,
|
|
3605
|
+
declaredAt: true,
|
|
3606
|
+
incidentSeverity: {
|
|
3607
|
+
name: true,
|
|
3608
|
+
color: true,
|
|
3609
|
+
},
|
|
3610
|
+
currentIncidentState: {
|
|
3611
|
+
_id: true,
|
|
3612
|
+
name: true,
|
|
3613
|
+
color: true,
|
|
3614
|
+
},
|
|
3615
|
+
monitors: {
|
|
3616
|
+
_id: true,
|
|
3617
|
+
},
|
|
3618
|
+
},
|
|
3619
|
+
sort: {
|
|
3620
|
+
declaredAt: SortOrder.Descending,
|
|
3621
|
+
},
|
|
3622
|
+
skip: 0,
|
|
3623
|
+
limit: LIMIT_PER_PROJECT,
|
|
3624
|
+
props: {
|
|
3625
|
+
isRoot: true,
|
|
3626
|
+
},
|
|
3627
|
+
});
|
|
3628
|
+
}
|
|
3629
|
+
const overallStatus = StatusPageService.getOverallMonitorStatus({
|
|
3630
|
+
statusPageResources,
|
|
3631
|
+
monitorStatuses,
|
|
3632
|
+
monitorGroupCurrentStatuses,
|
|
3633
|
+
});
|
|
3634
|
+
const response = {
|
|
3635
|
+
overallStatus: overallStatus
|
|
3636
|
+
? BaseModel.toJSON(overallStatus, MonitorStatus)
|
|
3637
|
+
: null,
|
|
3638
|
+
scheduledMaintenanceEventsPublicNotes: BaseModel.toJSONArray(scheduledMaintenanceEventsPublicNotes, ScheduledMaintenancePublicNote),
|
|
3639
|
+
statusPageHistoryChartBarColorRules: BaseModel.toJSONArray(statusPageHistoryChartBarColorRules, StatusPageHistoryChartBarColorRule),
|
|
3640
|
+
scheduledMaintenanceEvents: BaseModel.toJSONArray(scheduledMaintenanceEvents, ScheduledMaintenance),
|
|
3641
|
+
activeAnnouncements: BaseModel.toJSONArray(activeAnnouncements, StatusPageAnnouncement),
|
|
3642
|
+
incidentPublicNotes: BaseModel.toJSONArray(incidentPublicNotes, IncidentPublicNote),
|
|
3643
|
+
activeIncidents: BaseModel.toJSONArray(activeIncidents, Incident),
|
|
3644
|
+
activeEpisodes: activeEpisodesJson,
|
|
3645
|
+
episodePublicNotes: BaseModel.toJSONArray(episodePublicNotes, IncidentEpisodePublicNote),
|
|
3646
|
+
episodeStateTimelines: BaseModel.toJSONArray(episodeStateTimelines, IncidentEpisodeStateTimeline),
|
|
3647
|
+
monitorStatusTimelines: BaseModel.toJSONArray(monitorStatusTimelines, MonitorStatusTimeline),
|
|
3648
|
+
resourceGroups: BaseModel.toJSONArray(statusPageGroups, StatusPageGroup),
|
|
3649
|
+
monitorStatuses: BaseModel.toJSONArray(monitorStatuses, MonitorStatus),
|
|
3650
|
+
statusPageResources: BaseModel.toJSONArray(statusPageResources, StatusPageResource),
|
|
3651
|
+
incidentStateTimelines: BaseModel.toJSONArray(incidentStateTimelines, IncidentStateTimeline),
|
|
3652
|
+
statusPage: (() => {
|
|
3653
|
+
const statusPageJson = BaseModel.toJSONObject(statusPage, StatusPage);
|
|
3654
|
+
delete statusPageJson["projectId"];
|
|
3655
|
+
return statusPageJson;
|
|
3656
|
+
})(),
|
|
3657
|
+
scheduledMaintenanceStateTimelines: BaseModel.toJSONArray(scheduledMaintenanceStateTimelines, ScheduledMaintenanceStateTimeline),
|
|
3658
|
+
monitorGroupCurrentStatuses: JSONFunctions.serialize(monitorGroupCurrentStatuses),
|
|
3659
|
+
monitorsInGroup: JSONFunctions.serialize(monitorsInGroup),
|
|
3660
|
+
timelineIncidents: BaseModel.toJSONArray(timelineIncidents, Incident),
|
|
3661
|
+
};
|
|
3662
|
+
return response;
|
|
3663
|
+
}
|
|
3664
|
+
async getStatusPageAnnouncementAttachment(req, res) {
|
|
3665
|
+
var _a;
|
|
3666
|
+
const statusPageIdParam = req.params["statusPageId"];
|
|
3667
|
+
const announcementIdParam = req.params["announcementId"];
|
|
3616
3668
|
const fileIdParam = req.params["fileId"];
|
|
3617
3669
|
if (!statusPageIdParam || !announcementIdParam || !fileIdParam) {
|
|
3618
3670
|
throw new NotFoundException("Attachment not found");
|
|
@@ -4131,6 +4183,20 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
4131
4183
|
}
|
|
4132
4184
|
}
|
|
4133
4185
|
}
|
|
4186
|
+
/*
|
|
4187
|
+
* Post-auth overview responses keyed by resolved statusPageId. The payload
|
|
4188
|
+
* is user-independent (authorization is a binary gate checked per-request,
|
|
4189
|
+
* before any cache read), so one short-TTL snapshot per page per process
|
|
4190
|
+
* serves every viewer. See buildOverviewResponse.
|
|
4191
|
+
*/
|
|
4192
|
+
StatusPageAPI.overviewResponseCache = new InMemoryTTLCache(500);
|
|
4193
|
+
/*
|
|
4194
|
+
* In-flight overview builds keyed by the same cache key, so concurrent
|
|
4195
|
+
* cold-cache requests share one build instead of stampeding the database.
|
|
4196
|
+
*/
|
|
4197
|
+
StatusPageAPI.overviewResponseInFlight = new Map();
|
|
4198
|
+
StatusPageAPI.OVERVIEW_CACHE_TTL_MS = 15000;
|
|
4199
|
+
export default StatusPageAPI;
|
|
4134
4200
|
__decorate([
|
|
4135
4201
|
CaptureSpan(),
|
|
4136
4202
|
__metadata("design:type", Function),
|
|
@@ -4179,4 +4245,10 @@ __decorate([
|
|
|
4179
4245
|
__metadata("design:paramtypes", [Object]),
|
|
4180
4246
|
__metadata("design:returntype", Promise)
|
|
4181
4247
|
], StatusPageAPI.prototype, "getStatusPageResourcesAndTimelines", null);
|
|
4248
|
+
__decorate([
|
|
4249
|
+
CaptureSpan(),
|
|
4250
|
+
__metadata("design:type", Function),
|
|
4251
|
+
__metadata("design:paramtypes", [ObjectID]),
|
|
4252
|
+
__metadata("design:returntype", Promise)
|
|
4253
|
+
], StatusPageAPI.prototype, "buildOverviewResponse", null);
|
|
4182
4254
|
//# sourceMappingURL=StatusPageAPI.js.map
|