@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
|
@@ -450,6 +450,56 @@ export default class RumSessionChunk extends AnalyticsBaseModel {
|
|
|
450
450
|
});
|
|
451
451
|
});
|
|
452
452
|
|
|
453
|
+
/*
|
|
454
|
+
* WHERE the page was while this chunk was open.
|
|
455
|
+
*
|
|
456
|
+
* The chunk table used to carry routeCount but not the routes, so the
|
|
457
|
+
* session header's entryUrl / exitUrl / routes[] could only ever be
|
|
458
|
+
* whatever chunk 0 happened to know - which for a single-page app is the
|
|
459
|
+
* landing page, forever. pageCount said "7 pages" on the same row where
|
|
460
|
+
* routes[] held one element, and the "Exit page URL (exact)" filter
|
|
461
|
+
* could not match a page the user demonstrably reached.
|
|
462
|
+
*
|
|
463
|
+
* Both are already scrubbed on the client AND re-scrubbed at ingest:
|
|
464
|
+
* these render under the wider session-metadata ACL, so an unscrubbed
|
|
465
|
+
* `/reset-password?token=...` here reaches more readers than the payload
|
|
466
|
+
* itself does.
|
|
467
|
+
*/
|
|
468
|
+
const urlColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
|
469
|
+
key: "url",
|
|
470
|
+
title: "URL",
|
|
471
|
+
description:
|
|
472
|
+
"Scrubbed URL the page was on when this chunk was flushed. The session's exit URL is the latest of these.",
|
|
473
|
+
/*
|
|
474
|
+
* Required with an empty default rather than nullable. Chunks written
|
|
475
|
+
* before this column existed read back as "", which is what the
|
|
476
|
+
* finalizer's argMinIf/argMaxIf skip over so a pre-migration session
|
|
477
|
+
* falls back to its provisional header instead of losing its URLs.
|
|
478
|
+
*/
|
|
479
|
+
required: true,
|
|
480
|
+
defaultValue: "",
|
|
481
|
+
type: TableColumnType.Text,
|
|
482
|
+
codec: [{ codec: "ZSTD", level: 1 }],
|
|
483
|
+
accessControl: chunkAccessControl,
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
const routesColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
|
487
|
+
key: "routes",
|
|
488
|
+
title: "Routes",
|
|
489
|
+
description:
|
|
490
|
+
"Distinct scrubbed URLs visited while this chunk was open, in order. The session's routes[] is the union of these.",
|
|
491
|
+
/*
|
|
492
|
+
* Required, like RumSession.routes. ClickHouse refuses
|
|
493
|
+
* Nullable(Array(String)) outright, so an array column can never be
|
|
494
|
+
* optional - it is empty or it is absent.
|
|
495
|
+
*/
|
|
496
|
+
required: true,
|
|
497
|
+
defaultValue: [],
|
|
498
|
+
type: TableColumnType.ArrayText,
|
|
499
|
+
codec: [{ codec: "ZSTD", level: 1 }],
|
|
500
|
+
accessControl: chunkAccessControl,
|
|
501
|
+
});
|
|
502
|
+
|
|
453
503
|
const retentionDateColumn: AnalyticsTableColumn = new AnalyticsTableColumn({
|
|
454
504
|
key: "retentionDate",
|
|
455
505
|
title: "Retention Date",
|
|
@@ -510,6 +560,8 @@ export default class RumSessionChunk extends AnalyticsBaseModel {
|
|
|
510
560
|
payloadColumn,
|
|
511
561
|
payloadBytesColumn,
|
|
512
562
|
...counterColumns,
|
|
563
|
+
urlColumn,
|
|
564
|
+
routesColumn,
|
|
513
565
|
retentionDateColumn,
|
|
514
566
|
],
|
|
515
567
|
sortKeys: ["projectId", "sessionId", "tabId", "chunkIndex"],
|
|
@@ -283,8 +283,22 @@ export default class CloudResource extends BaseModel {
|
|
|
283
283
|
})
|
|
284
284
|
public description?: string = undefined;
|
|
285
285
|
|
|
286
|
+
/*
|
|
287
|
+
* Creatable, never updatable - see the identical note on
|
|
288
|
+
* RumApplication.appIdentifier. `create: []` on a required column that the
|
|
289
|
+
* Cloud Resources create form declares made manual creation impossible:
|
|
290
|
+
* the field was stripped from the form and the server then rejected the
|
|
291
|
+
* POST for the very value it had just removed.
|
|
292
|
+
*/
|
|
286
293
|
@ColumnAccessControl({
|
|
287
|
-
create: [
|
|
294
|
+
create: [
|
|
295
|
+
Permission.ProjectOwner,
|
|
296
|
+
Permission.ProjectAdmin,
|
|
297
|
+
Permission.ProjectMember,
|
|
298
|
+
Permission.SettingsAdmin,
|
|
299
|
+
Permission.SettingsMember,
|
|
300
|
+
Permission.CreateCloudResource,
|
|
301
|
+
],
|
|
288
302
|
read: [
|
|
289
303
|
Permission.ProjectOwner,
|
|
290
304
|
Permission.ProjectAdmin,
|
|
@@ -305,6 +319,8 @@ export default class CloudResource extends BaseModel {
|
|
|
305
319
|
description:
|
|
306
320
|
"Stable identifier for this managed-compute workload (service.name, falling back to host.name). Identity key for this resource.",
|
|
307
321
|
})
|
|
322
|
+
/* Case-insensitive uniqueness; see RumApplication.appIdentifier. */
|
|
323
|
+
@UniqueColumnBy("projectId")
|
|
308
324
|
@Column({
|
|
309
325
|
nullable: false,
|
|
310
326
|
type: ColumnType.ShortText,
|
|
@@ -754,4 +754,47 @@ export default class DashboardDomain extends BaseModel {
|
|
|
754
754
|
default: false,
|
|
755
755
|
})
|
|
756
756
|
public isCustomCertificate?: boolean = undefined;
|
|
757
|
+
|
|
758
|
+
/*
|
|
759
|
+
* When a reissue of this domain's Let's Encrypt certificate was last
|
|
760
|
+
* REQUESTED from the dashboard - not when one last succeeded.
|
|
761
|
+
*
|
|
762
|
+
* This is the whole state behind the reissue cooldown. Certificates come
|
|
763
|
+
* from a Let's Encrypt account shared by every customer on the
|
|
764
|
+
* installation, and a failed order still spends a validation attempt
|
|
765
|
+
* against that account, so the stamp is written before the order is
|
|
766
|
+
* attempted and is left in place whether the order succeeds or fails.
|
|
767
|
+
*
|
|
768
|
+
* Null means this domain has never had a reissue requested; automated
|
|
769
|
+
* renewal never writes here, so the cron keeping a certificate fresh can
|
|
770
|
+
* never be the reason a customer's button is refused.
|
|
771
|
+
*/
|
|
772
|
+
@ColumnAccessControl({
|
|
773
|
+
create: [],
|
|
774
|
+
read: [
|
|
775
|
+
Permission.ProjectOwner,
|
|
776
|
+
Permission.ProjectAdmin,
|
|
777
|
+
Permission.ProjectMember,
|
|
778
|
+
Permission.Viewer,
|
|
779
|
+
Permission.SettingsAdmin,
|
|
780
|
+
Permission.SettingsMember,
|
|
781
|
+
Permission.SettingsViewer,
|
|
782
|
+
Permission.ReadDashboardDomain,
|
|
783
|
+
],
|
|
784
|
+
update: [],
|
|
785
|
+
})
|
|
786
|
+
@TableColumn({
|
|
787
|
+
required: false,
|
|
788
|
+
computed: true,
|
|
789
|
+
type: TableColumnType.Date,
|
|
790
|
+
title: "Certificate Reissue Requested At",
|
|
791
|
+
description:
|
|
792
|
+
"When a reissue of the SSL certificate for this domain was last requested. Used to rate limit how often a certificate can be reissued.",
|
|
793
|
+
})
|
|
794
|
+
@Column({
|
|
795
|
+
type: ColumnType.Date,
|
|
796
|
+
nullable: true,
|
|
797
|
+
unique: false,
|
|
798
|
+
})
|
|
799
|
+
public certificateReissueRequestedAt?: Date = undefined;
|
|
757
800
|
}
|
|
@@ -313,15 +313,27 @@ export default class MonitorTemplate extends BaseModel {
|
|
|
313
313
|
Permission.EditMonitorTemplate,
|
|
314
314
|
],
|
|
315
315
|
})
|
|
316
|
+
/*
|
|
317
|
+
* OPTIONAL, and the reason it is optional is the whole of issue #3486.
|
|
318
|
+
*
|
|
319
|
+
* A Network Device auto-import rule names every monitor it provisions
|
|
320
|
+
* "<device> - <this>". While this column was required, an operator who only
|
|
321
|
+
* wanted the device's own name had nothing to type that would not become a
|
|
322
|
+
* suffix on every imported router in the estate. Left blank, the monitor is
|
|
323
|
+
* named after the resource alone.
|
|
324
|
+
*
|
|
325
|
+
* Nullable rather than defaulted: a default would put the same invented
|
|
326
|
+
* suffix back on every template that never asked for one.
|
|
327
|
+
*/
|
|
316
328
|
@TableColumn({
|
|
317
|
-
required:
|
|
329
|
+
required: false,
|
|
318
330
|
type: TableColumnType.ShortText,
|
|
319
331
|
title: "Monitor Name",
|
|
320
332
|
description:
|
|
321
|
-
"Default name applied to monitors created from this template. Users can override on creation.",
|
|
333
|
+
"Default name applied to monitors created from this template. Users can override on creation. Leave it blank to name each monitor after the resource it watches.",
|
|
322
334
|
})
|
|
323
335
|
@Column({
|
|
324
|
-
nullable:
|
|
336
|
+
nullable: true,
|
|
325
337
|
type: ColumnType.ShortText,
|
|
326
338
|
length: ColumnLength.ShortText,
|
|
327
339
|
})
|
|
@@ -286,8 +286,31 @@ export default class RumApplication extends BaseModel {
|
|
|
286
286
|
})
|
|
287
287
|
public description?: string = undefined;
|
|
288
288
|
|
|
289
|
+
/*
|
|
290
|
+
* Creatable, never updatable.
|
|
291
|
+
*
|
|
292
|
+
* `create: []` here made the documented "create an application by hand"
|
|
293
|
+
* flow (docs/rum/applications.md) impossible: ModelForm drops any field
|
|
294
|
+
* the caller has no create permission on, so the Dashboard's required
|
|
295
|
+
* "App Identifier" input never rendered, and the POST that followed was
|
|
296
|
+
* rejected by the server with a raw `appIdentifier is required`. The
|
|
297
|
+
* column is required and has no default, so nothing could supply it.
|
|
298
|
+
*
|
|
299
|
+
* `update` stays empty on purpose. This value is the application's
|
|
300
|
+
* identity - telemetry is filed under it and the (projectId,
|
|
301
|
+
* appIdentifier) index is unique - so re-pointing it after the fact would
|
|
302
|
+
* orphan every session, trace and recording already keyed on the old one.
|
|
303
|
+
* Same shape as ServerlessFunction.functionIdentifier.
|
|
304
|
+
*/
|
|
289
305
|
@ColumnAccessControl({
|
|
290
|
-
create: [
|
|
306
|
+
create: [
|
|
307
|
+
Permission.ProjectOwner,
|
|
308
|
+
Permission.ProjectAdmin,
|
|
309
|
+
Permission.ProjectMember,
|
|
310
|
+
Permission.SettingsAdmin,
|
|
311
|
+
Permission.SettingsMember,
|
|
312
|
+
Permission.CreateRumApplication,
|
|
313
|
+
],
|
|
291
314
|
read: [
|
|
292
315
|
Permission.ProjectOwner,
|
|
293
316
|
Permission.ProjectAdmin,
|
|
@@ -308,6 +331,16 @@ export default class RumApplication extends BaseModel {
|
|
|
308
331
|
description:
|
|
309
332
|
"Stable identifier for this application from the service.name OpenTelemetry resource attribute. Identity key for this RUM application.",
|
|
310
333
|
})
|
|
334
|
+
/*
|
|
335
|
+
* Case-INSENSITIVE uniqueness, which the unique index on
|
|
336
|
+
* (projectId, appIdentifier) does not give: Postgres compares it byte for
|
|
337
|
+
* byte, while RumApplicationService.findOrCreateByAppIdentifier resolves
|
|
338
|
+
* an incoming service.name with QueryHelper.findWithSameText, which
|
|
339
|
+
* lowercases. Without this, someone hand-creating "Storefront-Web" beside
|
|
340
|
+
* an auto-discovered "storefront-web" passes the index and leaves two rows
|
|
341
|
+
* that the case-insensitive lookup resolves arbitrarily.
|
|
342
|
+
*/
|
|
343
|
+
@UniqueColumnBy("projectId")
|
|
311
344
|
@Column({
|
|
312
345
|
nullable: false,
|
|
313
346
|
type: ColumnType.ShortText,
|
|
@@ -1245,7 +1278,7 @@ export default class RumApplication extends BaseModel {
|
|
|
1245
1278
|
type: TableColumnType.Boolean,
|
|
1246
1279
|
title: "Capture Session Replay User Identity",
|
|
1247
1280
|
description:
|
|
1248
|
-
"When enabled, the
|
|
1281
|
+
"When enabled, the end-user reference supplied by the host page is stored alongside the recording - as a one-way per-project HMAC for lookup and erasure, plus the raw reference behind its own narrower column ACL - so a support engineer can find the session a named customer is complaining about. When off, the reference is never attached to a recording and neither column is stored. (It is still sent once on the policy request, which is how targeted capture matches a named user; it is not persisted.) The reference must be supplied at load time - identify() called later reaches the server only on the session's final chunk, which the header is not rebuilt from. On by default. Narrower create/update ACL than the other replay settings: this is the switch that turns a pseudonymous recording into an identified one.",
|
|
1249
1282
|
defaultValue: true,
|
|
1250
1283
|
})
|
|
1251
1284
|
@Column({
|
|
@@ -761,4 +761,47 @@ export default class StatusPageDomain extends BaseModel {
|
|
|
761
761
|
default: false, // default is false
|
|
762
762
|
})
|
|
763
763
|
public isCustomCertificate?: boolean = undefined;
|
|
764
|
+
|
|
765
|
+
/*
|
|
766
|
+
* When a reissue of this domain's Let's Encrypt certificate was last
|
|
767
|
+
* REQUESTED from the dashboard - not when one last succeeded.
|
|
768
|
+
*
|
|
769
|
+
* This is the whole state behind the reissue cooldown. Certificates come
|
|
770
|
+
* from a Let's Encrypt account shared by every customer on the
|
|
771
|
+
* installation, and a failed order still spends a validation attempt
|
|
772
|
+
* against that account, so the stamp is written before the order is
|
|
773
|
+
* attempted and is left in place whether the order succeeds or fails.
|
|
774
|
+
*
|
|
775
|
+
* Null means this domain has never had a reissue requested; automated
|
|
776
|
+
* renewal never writes here, so the cron keeping a certificate fresh can
|
|
777
|
+
* never be the reason a customer's button is refused.
|
|
778
|
+
*/
|
|
779
|
+
@ColumnAccessControl({
|
|
780
|
+
create: [],
|
|
781
|
+
read: [
|
|
782
|
+
Permission.ProjectOwner,
|
|
783
|
+
Permission.ProjectAdmin,
|
|
784
|
+
Permission.ProjectMember,
|
|
785
|
+
Permission.Viewer,
|
|
786
|
+
Permission.StatusPageAdmin,
|
|
787
|
+
Permission.StatusPageMember,
|
|
788
|
+
Permission.StatusPageViewer,
|
|
789
|
+
Permission.ReadStatusPageDomain,
|
|
790
|
+
],
|
|
791
|
+
update: [],
|
|
792
|
+
})
|
|
793
|
+
@TableColumn({
|
|
794
|
+
required: false,
|
|
795
|
+
computed: true,
|
|
796
|
+
type: TableColumnType.Date,
|
|
797
|
+
title: "Certificate Reissue Requested At",
|
|
798
|
+
description:
|
|
799
|
+
"When a reissue of the SSL certificate for this domain was last requested. Used to rate limit how often a certificate can be reissued.",
|
|
800
|
+
})
|
|
801
|
+
@Column({
|
|
802
|
+
type: ColumnType.Date,
|
|
803
|
+
nullable: true,
|
|
804
|
+
unique: false,
|
|
805
|
+
})
|
|
806
|
+
public certificateReissueRequestedAt?: Date = undefined;
|
|
764
807
|
}
|
|
@@ -238,5 +238,79 @@ export default class DashboardDomainAPI extends BaseAPI<
|
|
|
238
238
|
}
|
|
239
239
|
},
|
|
240
240
|
);
|
|
241
|
+
|
|
242
|
+
/*
|
|
243
|
+
* Reissue SSL API. Backs the "Reissue SSL Certificate" button in the
|
|
244
|
+
* dashboard, for a customer who wants a fresh certificate now rather than
|
|
245
|
+
* whenever the renewal cron next gets to this domain.
|
|
246
|
+
*
|
|
247
|
+
* The throttle that keeps this from becoming a way to spend the shared
|
|
248
|
+
* Let's Encrypt allowance lives in the service, alongside the write that
|
|
249
|
+
* claims it - a check here would be a check the automated callers do not
|
|
250
|
+
* share, and one an extra caller could forget.
|
|
251
|
+
*/
|
|
252
|
+
this.router.get(
|
|
253
|
+
`${new this.entityType().getCrudApiPath()?.toString()}/reissue-ssl/:id`,
|
|
254
|
+
UserMiddleware.getUserMiddleware,
|
|
255
|
+
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
256
|
+
try {
|
|
257
|
+
if (!DashboardCNameRecord) {
|
|
258
|
+
return Response.sendErrorResponse(
|
|
259
|
+
req,
|
|
260
|
+
res,
|
|
261
|
+
new BadDataException(
|
|
262
|
+
`Custom Domains not enabled for this
|
|
263
|
+
OneUptime installation. Please contact
|
|
264
|
+
your server admin to enable this
|
|
265
|
+
feature.`,
|
|
266
|
+
),
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const databaseProps: DatabaseCommonInteractionProps =
|
|
271
|
+
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
|
272
|
+
|
|
273
|
+
const id: ObjectID = new ObjectID(req.params["id"] as string);
|
|
274
|
+
|
|
275
|
+
/*
|
|
276
|
+
* Scoped by the caller's own props, so a domain in someone else's
|
|
277
|
+
* project is indistinguishable from one that does not exist.
|
|
278
|
+
*/
|
|
279
|
+
const domainCount: PositiveNumber =
|
|
280
|
+
await DashboardDomainService.countBy({
|
|
281
|
+
query: {
|
|
282
|
+
_id: id.toString(),
|
|
283
|
+
},
|
|
284
|
+
props: databaseProps,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
if (domainCount.toNumber() === 0) {
|
|
288
|
+
return Response.sendErrorResponse(
|
|
289
|
+
req,
|
|
290
|
+
res,
|
|
291
|
+
new BadDataException(
|
|
292
|
+
"The domain does not exist or user does not have access to it.",
|
|
293
|
+
),
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
logger.debug(
|
|
298
|
+
"Reissuing SSL",
|
|
299
|
+
getLogAttributesFromRequest(req as OneUptimeRequest),
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
await DashboardDomainService.reissueCert(id);
|
|
303
|
+
|
|
304
|
+
logger.debug(
|
|
305
|
+
"SSL reissued for domain id - " + id.toString(),
|
|
306
|
+
getLogAttributesFromRequest(req as OneUptimeRequest),
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
return Response.sendEmptySuccessResponse(req, res);
|
|
310
|
+
} catch (e) {
|
|
311
|
+
next(e);
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
);
|
|
241
315
|
}
|
|
242
316
|
}
|
|
@@ -32,14 +32,6 @@ import ObjectID from "../../Types/ObjectID";
|
|
|
32
32
|
import WorkspaceUserAuthTokenService from "../Services/WorkspaceUserAuthTokenService";
|
|
33
33
|
import WorkspaceUserAuthToken from "../../Models/DatabaseModels/WorkspaceUserAuthToken";
|
|
34
34
|
import WorkspaceType from "../../Types/Workspace/WorkspaceType";
|
|
35
|
-
import MicrosoftTeamsAuthAction, {
|
|
36
|
-
MicrosoftTeamsRequest,
|
|
37
|
-
} from "../Utils/Workspace/MicrosoftTeams/Actions/Auth";
|
|
38
|
-
import MicrosoftTeamsIncidentActions from "../Utils/Workspace/MicrosoftTeams/Actions/Incident";
|
|
39
|
-
import MicrosoftTeamsAlertActions from "../Utils/Workspace/MicrosoftTeams/Actions/Alert";
|
|
40
|
-
import MicrosoftTeamsScheduledMaintenanceActions from "../Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance";
|
|
41
|
-
import MicrosoftTeamsMonitorActions from "../Utils/Workspace/MicrosoftTeams/Actions/Monitor";
|
|
42
|
-
import MicrosoftTeamsOnCallDutyActions from "../Utils/Workspace/MicrosoftTeams/Actions/OnCallDutyPolicy";
|
|
43
35
|
import MicrosoftTeamsUtil from "../Utils/Workspace/MicrosoftTeams/MicrosoftTeams";
|
|
44
36
|
import archiver, { Archiver } from "archiver";
|
|
45
37
|
import LocalFile from "../Utils/LocalFile";
|
|
@@ -927,63 +919,78 @@ export default class MicrosoftTeamsAPI {
|
|
|
927
919
|
},
|
|
928
920
|
);
|
|
929
921
|
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
922
|
+
/*
|
|
923
|
+
* The same path, answering the browser.
|
|
924
|
+
*
|
|
925
|
+
* Azure Bot Service only ever POSTs here, so for a long time GET fell
|
|
926
|
+
* through to the catch-all 404 in StartServer and answered
|
|
927
|
+
* "Page not found - /api/microsoft-bot/messages". That is the single
|
|
928
|
+
* cheapest thing an admin can do to check their messaging endpoint, and
|
|
929
|
+
* OneUptime replied with the words for "this route does not exist" — so
|
|
930
|
+
* admins concluded the route had been dropped from the build and went
|
|
931
|
+
* looking for a regression, while the actual fault (Azure cannot reach
|
|
932
|
+
* this deployment) sat untouched. The 404 was true of the method and false
|
|
933
|
+
* of the endpoint, and nothing in the response said which.
|
|
934
|
+
*
|
|
935
|
+
* 405 is the honest answer, and it is honest in a way a human reads at a
|
|
936
|
+
* glance: the route is here, the deployment is reachable from wherever the
|
|
937
|
+
* request came from, and POST is what it wants. Everything else in the
|
|
938
|
+
* body exists to redirect the next hour of debugging away from OneUptime's
|
|
939
|
+
* routing table and towards the network path Azure has to traverse.
|
|
940
|
+
*/
|
|
941
|
+
router.get(
|
|
942
|
+
"/microsoft-bot/messages",
|
|
943
|
+
(_req: ExpressRequest, res: ExpressResponse) => {
|
|
944
|
+
res.setHeader("Allow", "POST");
|
|
945
|
+
|
|
946
|
+
/*
|
|
947
|
+
* Written straight onto the response rather than through
|
|
948
|
+
* Response.sendJsonObjectResponse, whose ?output-type=csv branch
|
|
949
|
+
* answers 200 regardless of the status code it was handed. The status
|
|
950
|
+
* code is the entire point of this route — it is what distinguishes
|
|
951
|
+
* "wrong method" from "no such route" — so no query parameter gets to
|
|
952
|
+
* rewrite it.
|
|
953
|
+
*/
|
|
954
|
+
res.status(405).json({
|
|
955
|
+
status:
|
|
956
|
+
"This is the OneUptime Microsoft Teams bot messaging endpoint. It exists, and it accepts POST only.",
|
|
957
|
+
allow: ["POST"],
|
|
958
|
+
messagingEndpoint: `${AppApiClientUrl.toString()}/microsoft-bot/messages`,
|
|
959
|
+
/*
|
|
960
|
+
* Stated as a fact about this response rather than as advice,
|
|
961
|
+
* because it is the one thing the admin has actually proven by
|
|
962
|
+
* getting here and it is easy to under-read.
|
|
963
|
+
*/
|
|
964
|
+
whatThisProves:
|
|
965
|
+
"You reached OneUptime. This endpoint is registered and this deployment served your request, so the messaging endpoint is not missing.",
|
|
966
|
+
whatThisDoesNotProve:
|
|
967
|
+
"That Azure Bot Service can reach this URL. Azure calls it from the public internet, and your browser or terminal may not be on the same path.",
|
|
968
|
+
/*
|
|
969
|
+
* A 404 on this path does NOT mean the request failed to arrive, and
|
|
970
|
+
* saying so would repeat the mistake this endpoint exists to end.
|
|
971
|
+
* OneUptime's own catch-all (StartServer's app.get("*")) answers an
|
|
972
|
+
* unmatched GET with a 404 whose body is
|
|
973
|
+
* {"message":"Page not found - <path>"} — 58 bytes for this path,
|
|
974
|
+
* which is exactly the `404 58` an admin sees in their access log.
|
|
975
|
+
* That 404 is proof the request arrived, not proof it did not. Only
|
|
976
|
+
* the body separates it from a proxy's own 404, so point at the body.
|
|
977
|
+
*/
|
|
978
|
+
ifYouGetA404InsteadOfThis:
|
|
979
|
+
'Read the body, not just the status code. A body of {"message":"Page not found - /api/microsoft-bot/messages"} comes from OneUptime itself, which means the request DID arrive: either this deployment predates this 405 response (the path was POST-only, so a GET fell through to the generic not-found handler), or something in front of OneUptime rewrote the path and stripped the /api prefix before the app saw it. An HTML error page from nginx, your ingress or a load balancer means the opposite — the request never reached OneUptime.',
|
|
980
|
+
/*
|
|
981
|
+
* Ordered by what actually goes wrong on self-hosted deployments.
|
|
982
|
+
* Outbound-works-inbound-fails leads because it is the state that
|
|
983
|
+
* makes people doubt the route: alerts arrive in Teams, so the
|
|
984
|
+
* integration looks live, and only the interactive half is dead.
|
|
985
|
+
*/
|
|
986
|
+
ifTeamsSaysUnableToReachApp: [
|
|
987
|
+
"Working outbound alerts prove nothing here. OneUptime posts cards by calling Microsoft, which needs no inbound access. Card buttons, bot commands and chat registration all travel the other way — Azure Bot Service POSTs to this URL — and that is the direction that is failing.",
|
|
988
|
+
"Confirm this exact URL is set as the messaging endpoint on the Azure Bot resource, then check that it resolves publicly. A private DNS name or an internal-only ingress is the most common cause.",
|
|
989
|
+
"Azure requires HTTPS with a publicly trusted certificate served with its full chain. A self-signed certificate, an internal CA, or a missing intermediate fails the TLS handshake before OneUptime ever sees the request, so nothing appears in your access log.",
|
|
990
|
+
"Then look for the POST, not the 404, in your access log: `grep 'POST /api/microsoft-bot/messages' <access log>`. No POST lines at all means Azure never got through, and the fault is the network path rather than OneUptime.",
|
|
991
|
+
],
|
|
992
|
+
nextStep: `GET ${AppApiClientUrl.toString()}/microsoft-bot/test to see this deployment's bot id, and compare it with the bot id of the OneUptime app package installed in Microsoft Teams.`,
|
|
993
|
+
});
|
|
987
994
|
},
|
|
988
995
|
);
|
|
989
996
|
|
|
@@ -1031,6 +1038,15 @@ export default class MicrosoftTeamsAPI {
|
|
|
1031
1038
|
notVerified: [
|
|
1032
1039
|
"That an Azure Bot resource exists for this client id",
|
|
1033
1040
|
"That the Azure Bot's messaging endpoint points at this deployment",
|
|
1041
|
+
/*
|
|
1042
|
+
* Reachability is listed separately from the endpoint being
|
|
1043
|
+
* configured, because they fail separately and look identical from
|
|
1044
|
+
* in here. A correctly configured endpoint on a deployment Azure
|
|
1045
|
+
* cannot dial produces working outbound alerts and a completely
|
|
1046
|
+
* dead bot, which reads as a half-broken integration rather than a
|
|
1047
|
+
* network problem.
|
|
1048
|
+
*/
|
|
1049
|
+
"That Azure Bot Service can actually reach this deployment over the public internet — outbound notifications work without it, so a working alert does not test this",
|
|
1034
1050
|
"That the Azure Bot has the Microsoft Teams channel enabled",
|
|
1035
1051
|
"That the client secret is valid and has not expired",
|
|
1036
1052
|
"That the Teams app package installed in your teams was built from this deployment",
|
|
@@ -1238,119 +1254,4 @@ export default class MicrosoftTeamsAPI {
|
|
|
1238
1254
|
|
|
1239
1255
|
return router;
|
|
1240
1256
|
}
|
|
1241
|
-
|
|
1242
|
-
private static async handleMessageActivity(
|
|
1243
|
-
_req: ExpressRequest,
|
|
1244
|
-
res: ExpressResponse,
|
|
1245
|
-
authResult: MicrosoftTeamsRequest,
|
|
1246
|
-
activity: JSONObject,
|
|
1247
|
-
): Promise<void> {
|
|
1248
|
-
// Handle direct messages to bot or @mentions
|
|
1249
|
-
const messageText: string = (activity["text"] as string) || "";
|
|
1250
|
-
const from: JSONObject = activity["from"] as JSONObject;
|
|
1251
|
-
|
|
1252
|
-
if (messageText.toLowerCase().includes("help")) {
|
|
1253
|
-
// Send help message
|
|
1254
|
-
const helpMessage: any = {
|
|
1255
|
-
_type: "WorkspacePayloadText",
|
|
1256
|
-
text: "Hello! I'm the OneUptime bot. I can help you:\n\n• Get notifications about incidents\n• Acknowledge alerts\n• View system status\n\nType 'status' to see current system status.",
|
|
1257
|
-
};
|
|
1258
|
-
|
|
1259
|
-
await MicrosoftTeamsUtil.sendDirectMessageToUser({
|
|
1260
|
-
authToken: authResult.authToken,
|
|
1261
|
-
workspaceUserId: from["id"] as string,
|
|
1262
|
-
messageBlocks: [helpMessage],
|
|
1263
|
-
});
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
Response.sendTextResponse(_req, res, "");
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
private static async handleInvokeActivity(
|
|
1270
|
-
req: ExpressRequest,
|
|
1271
|
-
res: ExpressResponse,
|
|
1272
|
-
authResult: MicrosoftTeamsRequest,
|
|
1273
|
-
_activity: JSONObject,
|
|
1274
|
-
): Promise<void> {
|
|
1275
|
-
/*
|
|
1276
|
-
* Handle adaptive card button clicks
|
|
1277
|
-
* const value: JSONObject = activity["value"] as JSONObject;
|
|
1278
|
-
* const actionType: string = value["action"] as string;
|
|
1279
|
-
*/
|
|
1280
|
-
|
|
1281
|
-
for (const action of authResult.actions || []) {
|
|
1282
|
-
if (!action.actionType) {
|
|
1283
|
-
continue;
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
if (
|
|
1287
|
-
MicrosoftTeamsIncidentActions.isIncidentAction({
|
|
1288
|
-
actionType: action.actionType,
|
|
1289
|
-
})
|
|
1290
|
-
) {
|
|
1291
|
-
return MicrosoftTeamsIncidentActions.handleIncidentAction({
|
|
1292
|
-
teamsRequest: authResult,
|
|
1293
|
-
action: action,
|
|
1294
|
-
req: req,
|
|
1295
|
-
res: res,
|
|
1296
|
-
});
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
if (
|
|
1300
|
-
MicrosoftTeamsAlertActions.isAlertAction({
|
|
1301
|
-
actionType: action.actionType,
|
|
1302
|
-
})
|
|
1303
|
-
) {
|
|
1304
|
-
return MicrosoftTeamsAlertActions.handleAlertAction({
|
|
1305
|
-
teamsRequest: authResult,
|
|
1306
|
-
action: action,
|
|
1307
|
-
req: req,
|
|
1308
|
-
res: res,
|
|
1309
|
-
});
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
if (
|
|
1313
|
-
MicrosoftTeamsScheduledMaintenanceActions.isScheduledMaintenanceAction({
|
|
1314
|
-
actionType: action.actionType,
|
|
1315
|
-
})
|
|
1316
|
-
) {
|
|
1317
|
-
return MicrosoftTeamsScheduledMaintenanceActions.handleScheduledMaintenanceAction(
|
|
1318
|
-
{
|
|
1319
|
-
teamsRequest: authResult,
|
|
1320
|
-
action: action,
|
|
1321
|
-
req: req,
|
|
1322
|
-
res: res,
|
|
1323
|
-
},
|
|
1324
|
-
);
|
|
1325
|
-
}
|
|
1326
|
-
|
|
1327
|
-
if (
|
|
1328
|
-
MicrosoftTeamsMonitorActions.isMonitorAction({
|
|
1329
|
-
actionType: action.actionType,
|
|
1330
|
-
})
|
|
1331
|
-
) {
|
|
1332
|
-
return MicrosoftTeamsMonitorActions.handleMonitorAction({
|
|
1333
|
-
teamsRequest: authResult,
|
|
1334
|
-
action: action,
|
|
1335
|
-
req: req,
|
|
1336
|
-
res: res,
|
|
1337
|
-
});
|
|
1338
|
-
}
|
|
1339
|
-
|
|
1340
|
-
if (
|
|
1341
|
-
MicrosoftTeamsOnCallDutyActions.isOnCallDutyAction({
|
|
1342
|
-
actionType: action.actionType,
|
|
1343
|
-
})
|
|
1344
|
-
) {
|
|
1345
|
-
return MicrosoftTeamsOnCallDutyActions.handleOnCallDutyAction({
|
|
1346
|
-
teamsRequest: authResult,
|
|
1347
|
-
action: action,
|
|
1348
|
-
req: req,
|
|
1349
|
-
res: res,
|
|
1350
|
-
});
|
|
1351
|
-
}
|
|
1352
|
-
}
|
|
1353
|
-
|
|
1354
|
-
Response.sendTextResponse(req, res, "");
|
|
1355
|
-
}
|
|
1356
1257
|
}
|