@oneuptime/common 12.0.27 → 12.0.29
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/AnalyticsModels/RumSessionChunk.ts +52 -0
- package/Models/DatabaseModels/CloudResource.ts +17 -1
- package/Models/DatabaseModels/DashboardDomain.ts +43 -0
- package/Models/DatabaseModels/MonitorTemplate.ts +15 -3
- package/Models/DatabaseModels/RumApplication.ts +35 -2
- package/Models/DatabaseModels/StatusPageDomain.ts +43 -0
- package/Server/API/DashboardDomainAPI.ts +74 -0
- package/Server/API/MicrosoftTeamsAPI.ts +81 -180
- package/Server/API/StatusPageDomainAPI.ts +74 -0
- package/Server/API/TelemetryAPI.ts +104 -15
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.ts +43 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/ProjectAuthorization.ts +6 -1
- package/Server/Services/DashboardDomainService.ts +131 -0
- package/Server/Services/LogPipelineProcessorService.ts +88 -0
- package/Server/Services/MonitorTemplateService.ts +70 -11
- package/Server/Services/StatusPageDomainService.ts +163 -3
- package/Server/Services/StatusPagePrivateUserSessionService.ts +197 -56
- package/Server/Types/Domain.ts +17 -0
- package/Server/Utils/APIKey/AccessPermission.ts +24 -0
- package/Server/Utils/Greenlock/Greenlock.ts +156 -58
- package/Server/Utils/LogPipelineProcessorValidation.ts +112 -0
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +1094 -45
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +153 -17
- package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +12 -5
- package/Server/Utils/SessionReplay/SessionReplayIdentity.ts +102 -0
- package/Server/Utils/SessionReplay/SessionReplayReadService.ts +11 -2
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +23 -11
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +0 -170
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.ts +158 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +23 -11
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +35 -13
- package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +505 -0
- package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +106 -0
- package/Tests/App/StatusPage/StatusPageLoginCodeUtil.test.ts +84 -0
- package/Tests/App/StatusPage/StatusPageRedirectSafety.test.ts +119 -0
- package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +81 -0
- package/Tests/Models/MonitorTemplateMonitorNameColumn.test.ts +258 -0
- package/Tests/Server/API/CustomDomainReissueSslAPI.test.ts +469 -0
- package/Tests/Server/API/MicrosoftTeamsBotMessagesEndpoint.test.ts +729 -0
- package/Tests/Server/API/MicrosoftTeamsBotTestEndpoint.test.ts +26 -0
- package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +402 -0
- package/Tests/Server/API/SessionReplayAPI.test.ts +232 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -0
- package/Tests/Server/Services/AddCertificateReissueRequestedAtToDomainsMigration.test.ts +287 -0
- package/Tests/Server/Services/CustomDomainCertificateReissue.test.ts +477 -0
- package/Tests/Server/Services/LogPipelineProcessorSaveValidation.test.ts +249 -0
- package/Tests/Server/Services/MonitorTemplateServiceBulkSync.test.ts +549 -0
- package/Tests/Server/Services/MonitorTemplateServiceNetworkDeviceSync.test.ts +16 -3
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +57 -0
- package/Tests/Server/Services/SecurityEventLastErrorColumnWidth.test.ts +151 -15
- package/Tests/Server/Services/StatusPagePrivateUserSessionService.test.ts +587 -0
- package/Tests/Server/Types/Database/Permissions/ApiKeyFileAccess.test.ts +286 -0
- package/Tests/Server/Types/Database/Permissions/MonitorTemplateColumnAccess.test.ts +328 -0
- package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAndIsNull.test.ts +83 -0
- package/Tests/Server/Utils/AI/SRE/ConfidenceSignal.test.ts +377 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +16 -1
- package/Tests/Server/Utils/Greenlock/CertificateRenewalSchedule.test.ts +250 -0
- package/Tests/Server/Utils/LogPipelineProcessorValidation.test.ts +198 -0
- package/Tests/Server/Utils/SecurityEvent/ConnectorErrorMessage.test.ts +48 -9
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsAuth.test.ts +726 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +74 -11
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsErrorHandling.test.ts +1107 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +20 -3
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +51 -11
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +1246 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsRequestContract.test.ts +961 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsResponseParsing.test.ts +653 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +880 -0
- package/Tests/Types/API/HTTPResponse.test.ts +175 -0
- package/Tests/Types/API/Route.test.ts +36 -4
- package/Tests/Types/Database/DatabaseProperty.test.ts +171 -0
- package/Tests/Types/DateLocalShortDateTimeString.test.ts +518 -0
- package/Tests/Types/DateLocalTimeString.test.ts +259 -0
- package/Tests/Types/DateUserPrefers12HourFormat.test.ts +316 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +41 -2
- package/Tests/UI/Components/LogsViewerEmptyStateTimeRange.test.tsx +212 -0
- package/Tests/UI/Components/TelemetryChartClockFormat.test.tsx +359 -0
- package/Tests/UI/Components/TimeRangePickerClockFormat.test.tsx +422 -0
- package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +146 -8
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsMessagingEndpointGuidance.test.tsx +520 -0
- package/Tests/UI/Utils/Navigation.test.ts +82 -0
- package/Tests/Utils/Array.test.ts +100 -0
- package/Tests/Utils/CertificateReissue.test.ts +225 -0
- package/Tests/Utils/Grok/Grok.test.ts +492 -0
- package/Tests/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.test.ts +181 -13
- package/Types/API/Route.ts +20 -5
- package/Types/Date.ts +143 -13
- package/Types/Rum/SessionReplay.ts +76 -1
- package/UI/Components/Date/TimeRangePickerDropdown.tsx +9 -5
- package/UI/Components/LogsViewer/LogsViewer.tsx +12 -7
- package/UI/Components/LogsViewer/components/HistogramTooltip.tsx +18 -17
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +21 -28
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +2 -4
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +2 -4
- package/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.tsx +17 -16
- package/UI/Utils/Navigation.ts +42 -0
- package/Utils/Array.ts +37 -0
- package/Utils/CertificateReissue.ts +128 -0
- package/Utils/Grok/Grok.ts +461 -0
- package/Utils/Grok/GrokPatterns.ts +118 -0
- package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +58 -6
- package/build/dist/Models/AnalyticsModels/RumSessionChunk.js +48 -0
- package/build/dist/Models/AnalyticsModels/RumSessionChunk.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CloudResource.js +19 -2
- package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DashboardDomain.js +44 -0
- package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js +17 -4
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +37 -3
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js +44 -0
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
- package/build/dist/Server/API/DashboardDomainAPI.js +43 -1
- package/build/dist/Server/API/DashboardDomainAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +74 -118
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageDomainAPI.js +43 -1
- package/build/dist/Server/API/StatusPageDomainAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +67 -14
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js +28 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.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/ProjectAuthorization.js +6 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
- package/build/dist/Server/Services/DashboardDomainService.js +104 -0
- package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
- package/build/dist/Server/Services/LogPipelineProcessorService.js +65 -0
- package/build/dist/Server/Services/LogPipelineProcessorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +57 -11
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageDomainService.js +132 -3
- package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js +112 -32
- package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js.map +1 -1
- package/build/dist/Server/Types/Domain.js +16 -0
- package/build/dist/Server/Types/Domain.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +24 -0
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/Greenlock/Greenlock.js +110 -35
- package/build/dist/Server/Utils/Greenlock/Greenlock.js.map +1 -1
- package/build/dist/Server/Utils/LogPipelineProcessorValidation.js +77 -0
- package/build/dist/Server/Utils/LogPipelineProcessorValidation.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +777 -36
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +108 -17
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayIdentity.js +59 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayIdentity.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +9 -2
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +17 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +0 -159
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js +128 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +17 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -13
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/API/Route.js +16 -5
- package/build/dist/Types/API/Route.js.map +1 -1
- package/build/dist/Types/Date.js +115 -13
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplay.js +38 -0
- package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +9 -5
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +12 -7
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js +15 -14
- package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +17 -22
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +2 -4
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +2 -4
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js +15 -14
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js +30 -0
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/build/dist/Utils/Array.js +25 -0
- package/build/dist/Utils/Array.js.map +1 -1
- package/build/dist/Utils/CertificateReissue.js +97 -0
- package/build/dist/Utils/CertificateReissue.js.map +1 -0
- package/build/dist/Utils/Grok/Grok.js +324 -0
- package/build/dist/Utils/Grok/Grok.js.map +1 -0
- package/build/dist/Utils/Grok/GrokPatterns.js +112 -0
- package/build/dist/Utils/Grok/GrokPatterns.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +63 -3
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
- package/package.json +1 -1
- package/UI/Utils/JsonWebToken.ts +0 -14
- package/build/dist/UI/Utils/JsonWebToken.js +0 -10
- package/build/dist/UI/Utils/JsonWebToken.js.map +0 -1
|
@@ -28,26 +28,27 @@ function formatTooltipTime(label: string | undefined): string {
|
|
|
28
28
|
return label;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return
|
|
31
|
+
const use12HourFormat: boolean = OneUptimeDate.getUserPrefers12HourFormat();
|
|
32
|
+
|
|
33
|
+
/*
|
|
34
|
+
* Today's buckets need no date on them - the explorer's own range picker
|
|
35
|
+
* already says which day is on screen. "Today" has to be decided in the
|
|
36
|
+
* configured timezone, or buckets either side of midnight there get the
|
|
37
|
+
* date added and dropped a few hours off.
|
|
38
|
+
*/
|
|
39
|
+
if (
|
|
40
|
+
OneUptimeDate.areOnTheSameLocalDay(date, OneUptimeDate.getCurrentDate())
|
|
41
|
+
) {
|
|
42
|
+
return OneUptimeDate.getLocalTimeString(date, {
|
|
43
|
+
includeSeconds: true,
|
|
44
|
+
use12HourFormat,
|
|
45
|
+
});
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
return OneUptimeDate.getDateAsLocalShortDateTimeString(date, {
|
|
49
|
+
includeSeconds: true,
|
|
50
|
+
use12HourFormat,
|
|
48
51
|
});
|
|
49
|
-
|
|
50
|
-
return `${dateStr}, ${time}`;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
const HistogramTooltip: FunctionComponent<HistogramTooltipProps> = (
|
|
@@ -134,17 +134,32 @@ function pivotTimeseriesData(rows: Array<AnalyticsTimeseriesRow>): {
|
|
|
134
134
|
};
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
function formatTickTime(time: string): string {
|
|
137
|
+
export function formatTickTime(time: string): string {
|
|
138
138
|
const date: Date = OneUptimeDate.fromString(time);
|
|
139
139
|
|
|
140
140
|
if (isNaN(date.getTime())) {
|
|
141
141
|
return time;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
return
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
144
|
+
return OneUptimeDate.getLocalTimeString(date, {
|
|
145
|
+
use12HourFormat: OneUptimeDate.getUserPrefers12HourFormat(),
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** The bucket a tooltip is pointing at, dated because it can be any day in the window. */
|
|
150
|
+
export function formatTooltipLabel(label: string | undefined): string {
|
|
151
|
+
if (!label) {
|
|
152
|
+
return "";
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const date: Date = OneUptimeDate.fromString(label);
|
|
156
|
+
|
|
157
|
+
if (isNaN(date.getTime())) {
|
|
158
|
+
return label;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return OneUptimeDate.getDateAsLocalShortDateTimeString(date, {
|
|
162
|
+
use12HourFormat: OneUptimeDate.getUserPrefers12HourFormat(),
|
|
148
163
|
});
|
|
149
164
|
}
|
|
150
165
|
|
|
@@ -234,32 +249,10 @@ const AnalyticsTooltip: FunctionComponent<AnalyticsTooltipProps> = (
|
|
|
234
249
|
0,
|
|
235
250
|
);
|
|
236
251
|
|
|
237
|
-
const formatTime: (label: string | undefined) => string = (
|
|
238
|
-
label: string | undefined,
|
|
239
|
-
): string => {
|
|
240
|
-
if (!label) {
|
|
241
|
-
return "";
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
const date: Date = OneUptimeDate.fromString(label);
|
|
245
|
-
|
|
246
|
-
if (isNaN(date.getTime())) {
|
|
247
|
-
return label;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
return date.toLocaleString([], {
|
|
251
|
-
month: "short",
|
|
252
|
-
day: "numeric",
|
|
253
|
-
hour: "2-digit",
|
|
254
|
-
minute: "2-digit",
|
|
255
|
-
hour12: false,
|
|
256
|
-
});
|
|
257
|
-
};
|
|
258
|
-
|
|
259
252
|
return (
|
|
260
253
|
<div className="rounded-lg border border-gray-200/80 bg-white/95 px-3.5 py-2.5 shadow-lg backdrop-blur-sm">
|
|
261
254
|
<p className="mb-2 border-b border-gray-100 pb-2 font-mono text-[11px] font-medium text-gray-400">
|
|
262
|
-
{
|
|
255
|
+
{formatTooltipLabel(props.label)}
|
|
263
256
|
</p>
|
|
264
257
|
<div className="space-y-1">
|
|
265
258
|
{entries.map((entry: { key: string; value: number; color: string }) => {
|
|
@@ -68,10 +68,8 @@ function formatTickTime(time: string): string {
|
|
|
68
68
|
return time;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
return
|
|
72
|
-
|
|
73
|
-
minute: "2-digit",
|
|
74
|
-
hour12: false,
|
|
71
|
+
return OneUptimeDate.getLocalTimeString(date, {
|
|
72
|
+
use12HourFormat: OneUptimeDate.getUserPrefers12HourFormat(),
|
|
75
73
|
});
|
|
76
74
|
}
|
|
77
75
|
|
|
@@ -73,10 +73,8 @@ function formatTickTime(time: string): string {
|
|
|
73
73
|
return time;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
return
|
|
77
|
-
|
|
78
|
-
minute: "2-digit",
|
|
79
|
-
hour12: false,
|
|
76
|
+
return OneUptimeDate.getLocalTimeString(date, {
|
|
77
|
+
use12HourFormat: OneUptimeDate.getUserPrefers12HourFormat(),
|
|
80
78
|
});
|
|
81
79
|
}
|
|
82
80
|
|
|
@@ -34,26 +34,27 @@ function formatTooltipTime(label: string | undefined): string {
|
|
|
34
34
|
return label;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
const
|
|
38
|
-
const isToday: boolean = date.toDateString() === now.toDateString();
|
|
37
|
+
const use12HourFormat: boolean = OneUptimeDate.getUserPrefers12HourFormat();
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
39
|
+
/*
|
|
40
|
+
* Today's buckets need no date on them - the explorer's own range picker
|
|
41
|
+
* already says which day is on screen. "Today" has to be decided in the
|
|
42
|
+
* configured timezone, or buckets either side of midnight there get the
|
|
43
|
+
* date added and dropped a few hours off.
|
|
44
|
+
*/
|
|
45
|
+
if (
|
|
46
|
+
OneUptimeDate.areOnTheSameLocalDay(date, OneUptimeDate.getCurrentDate())
|
|
47
|
+
) {
|
|
48
|
+
return OneUptimeDate.getLocalTimeString(date, {
|
|
49
|
+
includeSeconds: true,
|
|
50
|
+
use12HourFormat,
|
|
51
|
+
});
|
|
49
52
|
}
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
return OneUptimeDate.getDateAsLocalShortDateTimeString(date, {
|
|
55
|
+
includeSeconds: true,
|
|
56
|
+
use12HourFormat,
|
|
54
57
|
});
|
|
55
|
-
|
|
56
|
-
return `${dateStr}, ${time}`;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
const TelemetryHistogramTooltip: FunctionComponent<
|
package/UI/Utils/Navigation.ts
CHANGED
|
@@ -270,6 +270,42 @@ abstract class Navigation {
|
|
|
270
270
|
this.navigateHook(-1);
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Return whether a value is an unambiguous same-origin path. Redirect
|
|
275
|
+
* targets must be paths rather than URLs so schemes, protocol-relative
|
|
276
|
+
* values, and backslash authority tricks can never reach a browser
|
|
277
|
+
* navigation sink.
|
|
278
|
+
*/
|
|
279
|
+
public static isSafeInternalRoute(route: Route | string): boolean {
|
|
280
|
+
const routeValue: string = route.toString();
|
|
281
|
+
|
|
282
|
+
if (
|
|
283
|
+
!routeValue.startsWith("/") ||
|
|
284
|
+
routeValue.startsWith("//") ||
|
|
285
|
+
routeValue.includes("\\")
|
|
286
|
+
) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
try {
|
|
291
|
+
/*
|
|
292
|
+
* URL accepts characters that Route deliberately rejects. Validate the
|
|
293
|
+
* exact sink contract here so a value marked safe cannot crash later
|
|
294
|
+
* when a caller constructs the Route used for navigation.
|
|
295
|
+
*/
|
|
296
|
+
new Route(routeValue);
|
|
297
|
+
|
|
298
|
+
const parsedUrl: globalThis.URL = new window.URL(
|
|
299
|
+
routeValue,
|
|
300
|
+
window.location.origin,
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
return parsedUrl.origin === window.location.origin;
|
|
304
|
+
} catch {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
273
309
|
public static navigate(
|
|
274
310
|
to: Route | URL,
|
|
275
311
|
options?: {
|
|
@@ -288,6 +324,12 @@ abstract class Navigation {
|
|
|
288
324
|
}
|
|
289
325
|
|
|
290
326
|
if (options?.forceNavigate && to instanceof Route) {
|
|
327
|
+
if (!this.isSafeInternalRoute(to)) {
|
|
328
|
+
throw new BadDataException(
|
|
329
|
+
`Cannot force navigate to a non-internal route: ${finalUrl}`,
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
|
|
291
333
|
window.location.href = finalUrl;
|
|
292
334
|
return;
|
|
293
335
|
}
|
package/Utils/Array.ts
CHANGED
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
import ObjectID from "../Types/ObjectID";
|
|
2
2
|
|
|
3
3
|
export default class ArrayUtil {
|
|
4
|
+
/*
|
|
5
|
+
* Run handler over every item, at most `concurrency` of them in flight.
|
|
6
|
+
*
|
|
7
|
+
* For work that is spent waiting on the network rather than on the CPU -
|
|
8
|
+
* walking a list of customer domains and making a request against each -
|
|
9
|
+
* awaiting one at a time leaves the caller idle for almost all of its wall
|
|
10
|
+
* clock, and a single slow item delays every item behind it.
|
|
11
|
+
*
|
|
12
|
+
* Items are handed out in order, so a caller that has sorted by urgency still
|
|
13
|
+
* gets the urgent ones started first. handler is expected to deal with its
|
|
14
|
+
* own failures: as with Promise.all, the first rejection is what the caller
|
|
15
|
+
* sees, and it does not stop work already in flight.
|
|
16
|
+
*/
|
|
17
|
+
public static async forEachWithConcurrency<T>(
|
|
18
|
+
array: Array<T>,
|
|
19
|
+
concurrency: number,
|
|
20
|
+
handler: (item: T) => Promise<void>,
|
|
21
|
+
): Promise<void> {
|
|
22
|
+
let nextIndex: number = 0;
|
|
23
|
+
|
|
24
|
+
const worker: () => Promise<void> = async (): Promise<void> => {
|
|
25
|
+
while (nextIndex < array.length) {
|
|
26
|
+
const index: number = nextIndex++;
|
|
27
|
+
await handler(array[index] as T);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
await Promise.all(
|
|
32
|
+
Array.from(
|
|
33
|
+
{ length: Math.max(1, Math.min(concurrency, array.length)) },
|
|
34
|
+
(): Promise<void> => {
|
|
35
|
+
return worker();
|
|
36
|
+
},
|
|
37
|
+
),
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
4
41
|
public static mergeStringArrays(
|
|
5
42
|
array1: Array<string>,
|
|
6
43
|
array2: Array<string>,
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import OneUptimeDate from "../Types/Date";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* The cooldown that sits behind the "Reissue SSL Certificate" button on a
|
|
5
|
+
* custom domain.
|
|
6
|
+
*
|
|
7
|
+
* OneUptime orders certificates from Let's Encrypt against a single ACME
|
|
8
|
+
* account shared by every customer on the installation, so a button that any
|
|
9
|
+
* customer can press is a button that spends everybody's allowance. Two of
|
|
10
|
+
* Let's Encrypt's limits are the ones this guards:
|
|
11
|
+
*
|
|
12
|
+
* - 5 failed validations per account, per hostname, per hour. A domain whose
|
|
13
|
+
* CNAME is half-broken fails validation every single time, so an
|
|
14
|
+
* unthrottled button is a way to burn that hostname's whole hourly budget
|
|
15
|
+
* in seconds - including the budget the automated renewal cron needs.
|
|
16
|
+
* - 5 duplicate certificates per week for the same exact set of hostnames.
|
|
17
|
+
* A reissue is by definition a duplicate certificate: same hostname, no
|
|
18
|
+
* change. This is the limit a determined presser reaches second.
|
|
19
|
+
*
|
|
20
|
+
* Deliberately pure and synchronous, and deliberately in Common/Utils rather
|
|
21
|
+
* than Common/Server: the dashboard renders the very same countdown the API
|
|
22
|
+
* will enforce, so the button explains itself before it is pressed instead of
|
|
23
|
+
* only after the request comes back rejected.
|
|
24
|
+
*
|
|
25
|
+
* The cooldown is keyed off when a reissue was REQUESTED, not when one
|
|
26
|
+
* succeeded. A request that fails at the CA still cost a validation attempt,
|
|
27
|
+
* and a failing domain is exactly the domain a frustrated customer presses
|
|
28
|
+
* again - so a failed attempt must start the clock just as a successful one
|
|
29
|
+
* does.
|
|
30
|
+
*/
|
|
31
|
+
export default class CertificateReissueUtil {
|
|
32
|
+
/*
|
|
33
|
+
* Note on the value: 24 hours permits 7 reissues a week, which is above the
|
|
34
|
+
* 5-duplicate-certificates-per-week limit described above. That is a
|
|
35
|
+
* deliberate choice, not an oversight - reaching it needs a customer to
|
|
36
|
+
* press the button on six consecutive days, and the failure that follows is
|
|
37
|
+
* a clear, temporary error from the CA rather than the sustained burst the
|
|
38
|
+
* per-hour validation limit punishes. The value is a single constant so the
|
|
39
|
+
* trade can be revisited in one place.
|
|
40
|
+
*/
|
|
41
|
+
public static readonly COOLDOWN_IN_HOURS: number = 24;
|
|
42
|
+
|
|
43
|
+
/*
|
|
44
|
+
* The newest "requested at" stamp that is already out of cooldown.
|
|
45
|
+
*
|
|
46
|
+
* A row is eligible for another reissue when its stamp is null or is at or
|
|
47
|
+
* before this instant. The server turns this into the WHERE clause it
|
|
48
|
+
* claims the row with, so the comparison that decides eligibility is the
|
|
49
|
+
* same one on both sides.
|
|
50
|
+
*/
|
|
51
|
+
public static getCooldownCutoff(now: Date): Date {
|
|
52
|
+
return OneUptimeDate.addRemoveHours(
|
|
53
|
+
OneUptimeDate.fromString(now),
|
|
54
|
+
-CertificateReissueUtil.COOLDOWN_IN_HOURS,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// When the domain may be reissued again, given its last request.
|
|
59
|
+
public static getNextReissueAllowedAt(lastRequestedAt: Date): Date {
|
|
60
|
+
return OneUptimeDate.addRemoveHours(
|
|
61
|
+
OneUptimeDate.fromString(lastRequestedAt),
|
|
62
|
+
CertificateReissueUtil.COOLDOWN_IN_HOURS,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/*
|
|
67
|
+
* A domain that has never been reissued is never cooling down, which is why
|
|
68
|
+
* null is accepted here rather than being the caller's problem.
|
|
69
|
+
*/
|
|
70
|
+
public static isInCooldown(
|
|
71
|
+
lastRequestedAt: Date | null | undefined,
|
|
72
|
+
now: Date,
|
|
73
|
+
): boolean {
|
|
74
|
+
if (!lastRequestedAt) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return OneUptimeDate.isAfter(
|
|
79
|
+
CertificateReissueUtil.getNextReissueAllowedAt(lastRequestedAt),
|
|
80
|
+
OneUptimeDate.fromString(now),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/*
|
|
85
|
+
* How much longer the customer has to wait, in words. Whole minutes, since
|
|
86
|
+
* the countdown is only ever read by a person deciding whether to come back
|
|
87
|
+
* later.
|
|
88
|
+
*/
|
|
89
|
+
public static getTimeRemainingText(lastRequestedAt: Date, now: Date): string {
|
|
90
|
+
const nextAllowedAt: Date =
|
|
91
|
+
CertificateReissueUtil.getNextReissueAllowedAt(lastRequestedAt);
|
|
92
|
+
|
|
93
|
+
const totalMinutes: number = Math.max(
|
|
94
|
+
0,
|
|
95
|
+
OneUptimeDate.getMinutesBetweenTwoDates(
|
|
96
|
+
OneUptimeDate.fromString(now),
|
|
97
|
+
nextAllowedAt,
|
|
98
|
+
),
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
if (totalMinutes < 1) {
|
|
102
|
+
return "less than a minute";
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const hours: number = Math.floor(totalMinutes / 60);
|
|
106
|
+
const minutes: number = totalMinutes % 60;
|
|
107
|
+
|
|
108
|
+
const parts: Array<string> = [];
|
|
109
|
+
|
|
110
|
+
if (hours > 0) {
|
|
111
|
+
parts.push(`${hours} ${hours === 1 ? "hour" : "hours"}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (minutes > 0) {
|
|
115
|
+
parts.push(`${minutes} ${minutes === 1 ? "minute" : "minutes"}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return parts.join(" ");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/*
|
|
122
|
+
* The one sentence the API returns and the dashboard shows. Kept here so a
|
|
123
|
+
* customer reads the same explanation wherever they hit the limit.
|
|
124
|
+
*/
|
|
125
|
+
public static getCooldownMessage(lastRequestedAt: Date, now: Date): string {
|
|
126
|
+
return `A certificate reissue was already requested for this domain in the last ${CertificateReissueUtil.COOLDOWN_IN_HOURS} hours. Certificates are issued by Let's Encrypt, which rate limits how often the same domain can be issued, so a reissue can only be requested once every ${CertificateReissueUtil.COOLDOWN_IN_HOURS} hours. Please try again in ${CertificateReissueUtil.getTimeRemainingText(lastRequestedAt, now)}.`;
|
|
127
|
+
}
|
|
128
|
+
}
|