@oneuptime/common 12.0.28 → 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/DatabaseModels/DashboardDomain.ts +43 -0
- package/Models/DatabaseModels/MonitorTemplate.ts +15 -3
- 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/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/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/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/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 +62 -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/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/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/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/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
|
@@ -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
|
})
|
|
@@ -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
|
}
|
|
@@ -239,5 +239,79 @@ export default class StatusPageDomainAPI extends BaseAPI<
|
|
|
239
239
|
}
|
|
240
240
|
},
|
|
241
241
|
);
|
|
242
|
+
|
|
243
|
+
/*
|
|
244
|
+
* Reissue SSL API. Backs the "Reissue SSL Certificate" button in the
|
|
245
|
+
* dashboard, for a customer who wants a fresh certificate now rather than
|
|
246
|
+
* whenever the renewal cron next gets to this domain.
|
|
247
|
+
*
|
|
248
|
+
* The throttle that keeps this from becoming a way to spend the shared
|
|
249
|
+
* Let's Encrypt allowance lives in the service, alongside the write that
|
|
250
|
+
* claims it - a check here would be a check the automated callers do not
|
|
251
|
+
* share, and one an extra caller could forget.
|
|
252
|
+
*/
|
|
253
|
+
this.router.get(
|
|
254
|
+
`${new this.entityType().getCrudApiPath()?.toString()}/reissue-ssl/:id`,
|
|
255
|
+
UserMiddleware.getUserMiddleware,
|
|
256
|
+
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
257
|
+
try {
|
|
258
|
+
if (!StatusPageCNameRecord) {
|
|
259
|
+
return Response.sendErrorResponse(
|
|
260
|
+
req,
|
|
261
|
+
res,
|
|
262
|
+
new BadDataException(
|
|
263
|
+
`Custom Domains not enabled for this
|
|
264
|
+
OneUptime installation. Please contact
|
|
265
|
+
your server admin to enable this
|
|
266
|
+
feature.`,
|
|
267
|
+
),
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const databaseProps: DatabaseCommonInteractionProps =
|
|
272
|
+
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
|
273
|
+
|
|
274
|
+
const id: ObjectID = new ObjectID(req.params["id"] as string);
|
|
275
|
+
|
|
276
|
+
/*
|
|
277
|
+
* Scoped by the caller's own props, so a domain in someone else's
|
|
278
|
+
* project is indistinguishable from one that does not exist.
|
|
279
|
+
*/
|
|
280
|
+
const domainCount: PositiveNumber =
|
|
281
|
+
await StatusPageDomainService.countBy({
|
|
282
|
+
query: {
|
|
283
|
+
_id: id.toString(),
|
|
284
|
+
},
|
|
285
|
+
props: databaseProps,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
if (domainCount.toNumber() === 0) {
|
|
289
|
+
return Response.sendErrorResponse(
|
|
290
|
+
req,
|
|
291
|
+
res,
|
|
292
|
+
new BadDataException(
|
|
293
|
+
"The domain does not exist or user does not have access to it.",
|
|
294
|
+
),
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
logger.debug(
|
|
299
|
+
"Reissuing SSL",
|
|
300
|
+
getLogAttributesFromRequest(req as any),
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
await StatusPageDomainService.reissueCert(id);
|
|
304
|
+
|
|
305
|
+
logger.debug(
|
|
306
|
+
"SSL reissued for domain id - " + id.toString(),
|
|
307
|
+
getLogAttributesFromRequest(req as any),
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
return Response.sendEmptySuccessResponse(req, res);
|
|
311
|
+
} catch (e) {
|
|
312
|
+
next(e);
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
);
|
|
242
316
|
}
|
|
243
317
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* "Default Monitor Name" on a Monitor Template becomes optional (issue #3486).
|
|
5
|
+
*
|
|
6
|
+
* A Network Device auto-import rule names every monitor it provisions
|
|
7
|
+
* "<device> - <template's default monitor name>". While the column was NOT
|
|
8
|
+
* NULL there was nothing an operator could type that would not become a
|
|
9
|
+
* suffix on every imported device in the estate — "UN0660WANRTR01 - Unit
|
|
10
|
+
* Router" for a router whose name is already UN0660WANRTR01. Blank now means
|
|
11
|
+
* the monitor is named after the device alone.
|
|
12
|
+
*
|
|
13
|
+
* Nullable with no default and no backfill: every template that already
|
|
14
|
+
* exists keeps the name it was created with, so nothing about an existing
|
|
15
|
+
* project's monitor naming changes when this ships.
|
|
16
|
+
*/
|
|
17
|
+
export class AllowNullMonitorNameOnMonitorTemplate1790200000000
|
|
18
|
+
implements MigrationInterface
|
|
19
|
+
{
|
|
20
|
+
public name: string = "AllowNullMonitorNameOnMonitorTemplate1790200000000";
|
|
21
|
+
|
|
22
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
23
|
+
await queryRunner.query(
|
|
24
|
+
`ALTER TABLE "MonitorTemplate" ALTER COLUMN "monitorName" DROP NOT NULL`,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
29
|
+
/*
|
|
30
|
+
* Templates written while the column was nullable have no name, and
|
|
31
|
+
* restoring NOT NULL would fail on every one of them. "Monitor" is the
|
|
32
|
+
* exact string the pre-#3486 code substituted when a template's name was
|
|
33
|
+
* blank, so it is the one value that reconstructs the old behaviour
|
|
34
|
+
* rather than inventing a new one.
|
|
35
|
+
*/
|
|
36
|
+
await queryRunner.query(
|
|
37
|
+
`UPDATE "MonitorTemplate" SET "monitorName" = 'Monitor' WHERE "monitorName" IS NULL`,
|
|
38
|
+
);
|
|
39
|
+
await queryRunner.query(
|
|
40
|
+
`ALTER TABLE "MonitorTemplate" ALTER COLUMN "monitorName" SET NOT NULL`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* When a customer last asked us to reissue the Let's Encrypt certificate for a
|
|
5
|
+
* custom domain, on both the status page and the dashboard domain tables.
|
|
6
|
+
*
|
|
7
|
+
* This is the state behind the reissue cooldown: certificates are ordered
|
|
8
|
+
* against a Let's Encrypt account shared by every customer on the
|
|
9
|
+
* installation, so the dashboard button that spends that account's allowance
|
|
10
|
+
* is throttled per domain, and this column is what the throttle reads.
|
|
11
|
+
*
|
|
12
|
+
* Nullable with no default and no backfill. Null means "never reissued", which
|
|
13
|
+
* is exactly true of every domain that exists when this ships, and it is the
|
|
14
|
+
* value that lets the first press of the button through rather than making
|
|
15
|
+
* every existing customer wait out a cooldown they never triggered.
|
|
16
|
+
*/
|
|
17
|
+
export class AddCertificateReissueRequestedAtToDomains1790300000000
|
|
18
|
+
implements MigrationInterface
|
|
19
|
+
{
|
|
20
|
+
public name: string =
|
|
21
|
+
"AddCertificateReissueRequestedAtToDomains1790300000000";
|
|
22
|
+
|
|
23
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
24
|
+
await queryRunner.query(
|
|
25
|
+
`ALTER TABLE "StatusPageDomain" ADD "certificateReissueRequestedAt" TIMESTAMP WITH TIME ZONE`,
|
|
26
|
+
);
|
|
27
|
+
await queryRunner.query(
|
|
28
|
+
`ALTER TABLE "DashboardDomain" ADD "certificateReissueRequestedAt" TIMESTAMP WITH TIME ZONE`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
33
|
+
await queryRunner.query(
|
|
34
|
+
`ALTER TABLE "DashboardDomain" DROP COLUMN "certificateReissueRequestedAt"`,
|
|
35
|
+
);
|
|
36
|
+
await queryRunner.query(
|
|
37
|
+
`ALTER TABLE "StatusPageDomain" DROP COLUMN "certificateReissueRequestedAt"`,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|