@oneuptime/common 12.0.1 → 12.0.2

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.
Files changed (205) hide show
  1. package/Models/DatabaseModels/Host.ts +12 -4
  2. package/Models/DatabaseModels/KubernetesContainer.ts +5 -4
  3. package/Models/DatabaseModels/KubernetesResource.ts +8 -7
  4. package/Models/DatabaseModels/PodmanResource.ts +6 -6
  5. package/Models/DatabaseModels/ServiceLevelObjective.ts +91 -0
  6. package/Models/DatabaseModels/StatusPage.ts +95 -0
  7. package/Server/API/TeamMemberAPI.ts +85 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.ts +84 -0
  9. package/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.ts +40 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.ts +131 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.ts +25 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  13. package/Server/Middleware/ProjectAuthorization.ts +66 -4
  14. package/Server/Services/BillingService.ts +600 -51
  15. package/Server/Services/CephClusterService.ts +47 -5
  16. package/Server/Services/CephResourceService.ts +43 -4
  17. package/Server/Services/CloudResourceService.ts +47 -5
  18. package/Server/Services/DatabaseService.ts +87 -6
  19. package/Server/Services/DockerHostService.ts +47 -5
  20. package/Server/Services/DockerResourceService.ts +62 -8
  21. package/Server/Services/DockerSwarmClusterService.ts +48 -5
  22. package/Server/Services/DockerSwarmResourceService.ts +53 -4
  23. package/Server/Services/HostService.ts +48 -5
  24. package/Server/Services/IoTDeviceService.ts +1 -17
  25. package/Server/Services/IoTFleetService.ts +47 -5
  26. package/Server/Services/KubernetesClusterService.ts +48 -5
  27. package/Server/Services/KubernetesContainerService.ts +50 -9
  28. package/Server/Services/KubernetesResourceService.ts +53 -7
  29. package/Server/Services/LabelService.ts +106 -1
  30. package/Server/Services/MonitorService.ts +57 -0
  31. package/Server/Services/PodmanHostService.ts +47 -5
  32. package/Server/Services/PodmanResourceService.ts +62 -8
  33. package/Server/Services/ProjectService.ts +165 -35
  34. package/Server/Services/ProxmoxClusterService.ts +47 -5
  35. package/Server/Services/ProxmoxResourceService.ts +43 -4
  36. package/Server/Services/RumApplicationService.ts +48 -5
  37. package/Server/Services/ServerlessFunctionService.ts +48 -5
  38. package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +426 -0
  39. package/Server/Services/ServiceLevelObjectiveService.ts +39 -0
  40. package/Server/Services/StatusPageService.ts +95 -36
  41. package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +18 -1
  42. package/Server/Services/StatusPageSubscriberService.ts +3 -0
  43. package/Server/Types/Database/JSONColumnQuery.ts +621 -0
  44. package/Server/Types/Database/Permissions/PublicPermission.ts +9 -2
  45. package/Server/Types/Database/QueryHelper.ts +17 -32
  46. package/Server/Utils/Database/TruncateColumnValue.ts +84 -0
  47. package/Tests/App/Dashboard/UsersTableGroupsByPerson.test.tsx +448 -0
  48. package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +704 -0
  49. package/Tests/Server/API/TeamMemberRemoveUserFromProjectAPI.test.ts +303 -0
  50. package/Tests/Server/Middleware/MasterAdminAuthorization.test.ts +350 -0
  51. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyHeader.test.ts +362 -0
  52. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +687 -0
  53. package/Tests/Server/Services/BillingService.test.ts +78 -3
  54. package/Tests/Server/Services/BillingServiceChangePlanCancel.test.ts +645 -0
  55. package/Tests/Server/Services/BillingServiceTrialPlanChange.test.ts +1005 -0
  56. package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +503 -0
  57. package/Tests/Server/Services/HostIpAddressesColumnWidth.test.ts +198 -0
  58. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +510 -0
  59. package/Tests/Server/Services/InventoryResourceNameColumnWidth.test.ts +500 -0
  60. package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +586 -0
  61. package/Tests/Server/Services/InventoryUpsertTruncationGuard.test.ts +1242 -0
  62. package/Tests/Server/Services/ProjectServiceChangePlan.test.ts +650 -0
  63. package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +1 -0
  64. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +387 -0
  65. package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +770 -0
  66. package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +130 -0
  67. package/Tests/Server/Services/SloMonitorLabelRuleHooks.test.ts +352 -0
  68. package/Tests/Server/Services/StatusPageSubscriberReport.test.ts +29 -13
  69. package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +69 -8
  70. package/Tests/Server/Types/Database/JSONColumnQuery.test.ts +593 -0
  71. package/Tests/Server/Types/Database/Permissions/PublicPermission.test.ts +495 -0
  72. package/Tests/Server/Types/Database/QueryUtil.test.ts +113 -0
  73. package/Tests/Server/Utils/Database/TruncateColumnValue.test.ts +179 -0
  74. package/Tests/Types/Events/Recurring.test.ts +157 -0
  75. package/Tests/UI/Components/ModelTable/ModelTableGroupsProjectUsers.test.tsx +375 -0
  76. package/Tests/UI/Components/ModelTable/useCustomFieldColumns.test.tsx +196 -0
  77. package/Tests/UI/Utils/ProjectUsersModelAPI.test.ts +739 -0
  78. package/Tests/UI/Utils/TeamMembersByUser.test.ts +633 -0
  79. package/Tests/Utils/StatusPage/ReportPeriod.test.ts +218 -0
  80. package/Tests/Utils/Telemetry/HostIpAddresses.test.ts +291 -0
  81. package/Types/CustomField/CustomFieldDefinition.ts +24 -0
  82. package/Types/Date.ts +112 -0
  83. package/Types/Events/Recurring.ts +99 -1
  84. package/Types/StatusPage/StatusPageReport.ts +11 -0
  85. package/Types/StatusPage/StatusPageReportPeriodType.ts +21 -0
  86. package/UI/Components/ModelTable/CustomFieldColumns.tsx +2 -6
  87. package/UI/Components/ModelTable/useCustomFieldColumns.ts +21 -2
  88. package/UI/Utils/ModelAPI/ProjectUsersModelAPI.ts +305 -0
  89. package/UI/Utils/TeamMembersByUser.ts +302 -0
  90. package/Utils/StatusPage/ReportPeriod.ts +236 -0
  91. package/Utils/Telemetry/HostIpAddresses.ts +98 -0
  92. package/build/dist/Models/DatabaseModels/Host.js +12 -4
  93. package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
  94. package/build/dist/Models/DatabaseModels/KubernetesContainer.js +4 -4
  95. package/build/dist/Models/DatabaseModels/KubernetesContainer.js.map +1 -1
  96. package/build/dist/Models/DatabaseModels/KubernetesResource.js +7 -7
  97. package/build/dist/Models/DatabaseModels/KubernetesResource.js.map +1 -1
  98. package/build/dist/Models/DatabaseModels/PodmanResource.js +6 -6
  99. package/build/dist/Models/DatabaseModels/PodmanResource.js.map +1 -1
  100. package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js +87 -0
  101. package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js.map +1 -1
  102. package/build/dist/Models/DatabaseModels/StatusPage.js +98 -0
  103. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  104. package/build/dist/Server/API/TeamMemberAPI.js +57 -2
  105. package/build/dist/Server/API/TeamMemberAPI.js.map +1 -1
  106. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js +45 -0
  107. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785900000000-AddSloMonitorLabelRule.js.map +1 -0
  108. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js +31 -0
  109. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785915638551-IncreaseHostIpAddressesLength.js.map +1 -0
  110. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js +90 -0
  111. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930000000-WidenInventoryResourceNameColumns.js.map +1 -0
  112. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js +14 -0
  113. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785930709405-AddStatusPageReportPeriod.js.map +1 -0
  114. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  115. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  116. package/build/dist/Server/Middleware/ProjectAuthorization.js +59 -4
  117. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  118. package/build/dist/Server/Services/BillingService.js +466 -36
  119. package/build/dist/Server/Services/BillingService.js.map +1 -1
  120. package/build/dist/Server/Services/CephClusterService.js +42 -5
  121. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  122. package/build/dist/Server/Services/CephResourceService.js +28 -3
  123. package/build/dist/Server/Services/CephResourceService.js.map +1 -1
  124. package/build/dist/Server/Services/CloudResourceService.js +42 -5
  125. package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
  126. package/build/dist/Server/Services/DatabaseService.js +69 -2
  127. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  128. package/build/dist/Server/Services/DockerHostService.js +42 -5
  129. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  130. package/build/dist/Server/Services/DockerResourceService.js +34 -4
  131. package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
  132. package/build/dist/Server/Services/DockerSwarmClusterService.js +43 -5
  133. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  134. package/build/dist/Server/Services/DockerSwarmResourceService.js +28 -3
  135. package/build/dist/Server/Services/DockerSwarmResourceService.js.map +1 -1
  136. package/build/dist/Server/Services/HostService.js +43 -5
  137. package/build/dist/Server/Services/HostService.js.map +1 -1
  138. package/build/dist/Server/Services/IoTDeviceService.js +1 -8
  139. package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
  140. package/build/dist/Server/Services/IoTFleetService.js +42 -5
  141. package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
  142. package/build/dist/Server/Services/KubernetesClusterService.js +43 -5
  143. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  144. package/build/dist/Server/Services/KubernetesContainerService.js +29 -3
  145. package/build/dist/Server/Services/KubernetesContainerService.js.map +1 -1
  146. package/build/dist/Server/Services/KubernetesResourceService.js +32 -4
  147. package/build/dist/Server/Services/KubernetesResourceService.js.map +1 -1
  148. package/build/dist/Server/Services/LabelService.js +92 -0
  149. package/build/dist/Server/Services/LabelService.js.map +1 -1
  150. package/build/dist/Server/Services/MonitorService.js +47 -0
  151. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  152. package/build/dist/Server/Services/PodmanHostService.js +42 -5
  153. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  154. package/build/dist/Server/Services/PodmanResourceService.js +34 -4
  155. package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
  156. package/build/dist/Server/Services/ProjectService.js +135 -21
  157. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  158. package/build/dist/Server/Services/ProxmoxClusterService.js +42 -5
  159. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  160. package/build/dist/Server/Services/ProxmoxResourceService.js +28 -3
  161. package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
  162. package/build/dist/Server/Services/RumApplicationService.js +43 -5
  163. package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
  164. package/build/dist/Server/Services/ServerlessFunctionService.js +43 -5
  165. package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
  166. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +347 -0
  167. package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -0
  168. package/build/dist/Server/Services/ServiceLevelObjectiveService.js +33 -1
  169. package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
  170. package/build/dist/Server/Services/StatusPageService.js +69 -46
  171. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  172. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +17 -1
  173. package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
  174. package/build/dist/Server/Services/StatusPageSubscriberService.js +3 -0
  175. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  176. package/build/dist/Server/Types/Database/JSONColumnQuery.js +402 -0
  177. package/build/dist/Server/Types/Database/JSONColumnQuery.js.map +1 -0
  178. package/build/dist/Server/Types/Database/Permissions/PublicPermission.js +9 -2
  179. package/build/dist/Server/Types/Database/Permissions/PublicPermission.js.map +1 -1
  180. package/build/dist/Server/Types/Database/QueryHelper.js +17 -27
  181. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  182. package/build/dist/Server/Utils/Database/TruncateColumnValue.js +32 -0
  183. package/build/dist/Server/Utils/Database/TruncateColumnValue.js.map +1 -0
  184. package/build/dist/Types/CustomField/CustomFieldDefinition.js +2 -0
  185. package/build/dist/Types/CustomField/CustomFieldDefinition.js.map +1 -0
  186. package/build/dist/Types/Date.js +68 -0
  187. package/build/dist/Types/Date.js.map +1 -1
  188. package/build/dist/Types/Events/Recurring.js +63 -0
  189. package/build/dist/Types/Events/Recurring.js.map +1 -1
  190. package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js +22 -0
  191. package/build/dist/Types/StatusPage/StatusPageReportPeriodType.js.map +1 -0
  192. package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -1
  193. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +18 -2
  194. package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -1
  195. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js +213 -0
  196. package/build/dist/UI/Utils/ModelAPI/ProjectUsersModelAPI.js.map +1 -0
  197. package/build/dist/UI/Utils/TeamMembersByUser.js +195 -0
  198. package/build/dist/UI/Utils/TeamMembersByUser.js.map +1 -0
  199. package/build/dist/Utils/StatusPage/ReportPeriod.js +131 -0
  200. package/build/dist/Utils/StatusPage/ReportPeriod.js.map +1 -0
  201. package/build/dist/Utils/Telemetry/HostIpAddresses.js +82 -0
  202. package/build/dist/Utils/Telemetry/HostIpAddresses.js.map +1 -0
  203. package/jest.config.json +2 -0
  204. package/package.json +1 -1
  205. package/tsconfig.json +12 -1
@@ -89,6 +89,10 @@ import {
89
89
  StatusPageReportItem,
90
90
  StatusPageReportRow,
91
91
  } from "../../Types/StatusPage/StatusPageReport";
92
+ import StatusPageReportPeriodUtil, {
93
+ StatusPageReportPeriod,
94
+ } from "../../Utils/StatusPage/ReportPeriod";
95
+ import Timezone from "../../Types/Timezone";
92
96
 
93
97
  export {
94
98
  StatusPageReport,
@@ -995,6 +999,7 @@ export class Service extends DatabaseService<StatusPage> {
995
999
  if (
996
1000
  updateBy.data.reportStartDateTime ||
997
1001
  updateBy.data.reportRecurringInterval ||
1002
+ updateBy.data.reportTimezone ||
998
1003
  updateBy.data.sendNextReportBy
999
1004
  ) {
1000
1005
  const statusPages: Array<StatusPage> = await this.findBy({
@@ -1003,6 +1008,7 @@ export class Service extends DatabaseService<StatusPage> {
1003
1008
  _id: true,
1004
1009
  reportStartDateTime: true,
1005
1010
  reportRecurringInterval: true,
1011
+ reportTimezone: true,
1006
1012
  },
1007
1013
  props: {
1008
1014
  isRoot: true,
@@ -1012,7 +1018,7 @@ export class Service extends DatabaseService<StatusPage> {
1012
1018
  });
1013
1019
 
1014
1020
  for (const statusPage of statusPages) {
1015
- const rerportStartDate: Date | undefined =
1021
+ const reportStartDate: Date | undefined =
1016
1022
  (updateBy.data.reportStartDateTime as Date) ||
1017
1023
  statusPage.reportStartDateTime;
1018
1024
  const reportRecurringInterval: Recurring | undefined =
@@ -1021,12 +1027,23 @@ export class Service extends DatabaseService<StatusPage> {
1021
1027
  statusPage.reportRecurringInterval,
1022
1028
  );
1023
1029
 
1024
- if (rerportStartDate && reportRecurringInterval) {
1025
- const nextReportDate: Date = Recurring.getNextDate(
1026
- rerportStartDate,
1027
- reportRecurringInterval,
1028
- );
1029
- updateBy.data.sendNextReportBy = nextReportDate;
1030
+ if (reportStartDate && reportRecurringInterval) {
1031
+ /*
1032
+ * Calendar-correct rather than Recurring.getNextDate's fixed
1033
+ * millisecond approximation, which walks a monthly schedule anchored
1034
+ * on the 1st backwards to the 31st, then the 30th, and eventually
1035
+ * skips a month. Resolved in the report timezone so "the 1st at
1036
+ * 09:00" survives a DST transition.
1037
+ */
1038
+ updateBy.data.sendNextReportBy = Recurring.getNextDateAfter({
1039
+ startDate: reportStartDate,
1040
+ recurring: reportRecurringInterval,
1041
+ afterDate: OneUptimeDate.getCurrentDate(),
1042
+ timezone:
1043
+ (updateBy.data.reportTimezone as Timezone) ||
1044
+ statusPage.reportTimezone ||
1045
+ StatusPageReportPeriodUtil.DEFAULT_TIMEZONE,
1046
+ });
1030
1047
  }
1031
1048
  }
1032
1049
  }
@@ -1037,6 +1054,24 @@ export class Service extends DatabaseService<StatusPage> {
1037
1054
  };
1038
1055
  }
1039
1056
 
1057
+ /*
1058
+ * The window the next report off this status page will cover. Static because
1059
+ * the settings screen resolves the very same window from the very same
1060
+ * columns to show the user what they are about to schedule.
1061
+ */
1062
+ public static getReportPeriodForStatusPage(
1063
+ statusPage: StatusPage,
1064
+ sentAt?: Date | undefined,
1065
+ ): StatusPageReportPeriod {
1066
+ return StatusPageReportPeriodUtil.getReportPeriod({
1067
+ periodType: statusPage.reportPeriodType,
1068
+ reportRecurringInterval: statusPage.reportRecurringInterval,
1069
+ reportDataInDays: statusPage.reportDataInDays,
1070
+ timezone: statusPage.reportTimezone,
1071
+ sentAt: sentAt,
1072
+ });
1073
+ }
1074
+
1040
1075
  @CaptureSpan()
1041
1076
  public async sendEmailReport(data: {
1042
1077
  statusPageId: ObjectID;
@@ -1069,7 +1104,7 @@ export class Service extends DatabaseService<StatusPage> {
1069
1104
 
1070
1105
  const report: StatusPageReport = await this.getReportByStatusPage({
1071
1106
  statusPageId: statuspage.id!,
1072
- historyDays: statuspage.reportDataInDays || 14,
1107
+ reportPeriod: Service.getReportPeriodForStatusPage(statuspage),
1073
1108
  });
1074
1109
 
1075
1110
  /*
@@ -1219,7 +1254,12 @@ export class Service extends DatabaseService<StatusPage> {
1219
1254
  @CaptureSpan()
1220
1255
  public async getReportByStatusPage(data: {
1221
1256
  statusPageId: ObjectID;
1222
- historyDays: number;
1257
+ /*
1258
+ * The exact window to measure. Resolved by StatusPageReportPeriodUtil from
1259
+ * the status page's report settings, so a monthly report can cover July
1260
+ * rather than "the 30 days ending whenever the cron happened to fire".
1261
+ */
1262
+ reportPeriod: StatusPageReportPeriod;
1223
1263
  }): Promise<StatusPageReport> {
1224
1264
  const statusPage: StatusPage | null = await this.findOneById({
1225
1265
  id: data.statusPageId,
@@ -1240,27 +1280,48 @@ export class Service extends DatabaseService<StatusPage> {
1240
1280
  statusPageId: data.statusPageId,
1241
1281
  });
1242
1282
 
1243
- const numberOfDays: number = data.historyDays || 14;
1244
-
1245
- const endDate: Date = OneUptimeDate.getCurrentDate();
1246
- const startDate: Date = OneUptimeDate.getSomeDaysAgo(numberOfDays);
1247
- const startAndEndDate: string = `${numberOfDays} days (${OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(startDate, true)} - ${OneUptimeDate.getDateAsUserFriendlyLocalFormattedString(endDate, true)})`;
1283
+ const startDate: Date = data.reportPeriod.startDate;
1284
+ const endDate: Date = data.reportPeriod.endDate;
1248
1285
 
1249
1286
  /*
1250
- * The report is explicitly "the last N days", so this window is what every uptime number
1251
- * below has to be measured against. It used to be computed only to fetch the timeline and
1252
- * was then discarded, which let an event that started before the window contribute its
1253
- * whole duration to the downtime total, and made the denominator "first event -> now"
1254
- * instead of the window - so the reported percentage drifted upwards every day.
1287
+ * This window is what every uptime number below has to be measured against.
1288
+ * It used to be computed only to fetch the timeline and was then discarded,
1289
+ * which let an event that started before the window contribute its whole
1290
+ * duration to the downtime total, and made the denominator "first event ->
1291
+ * now" instead of the window - so the reported percentage drifted upwards
1292
+ * every day.
1255
1293
  */
1256
1294
  const reportWindow: UptimeWindow = {
1257
1295
  startDate: startDate,
1258
1296
  endDate: endDate,
1259
1297
  };
1260
1298
 
1299
+ const periodStrings: Pick<
1300
+ StatusPageReport,
1301
+ | "reportDates"
1302
+ | "reportPeriodName"
1303
+ | "reportStartDate"
1304
+ | "reportEndDate"
1305
+ | "reportTimezone"
1306
+ > = {
1307
+ reportDates: data.reportPeriod.reportDates,
1308
+ reportPeriodName: data.reportPeriod.periodName,
1309
+ reportStartDate: OneUptimeDate.getDateAsCustomFormattedStringInTimezone({
1310
+ date: startDate,
1311
+ format: "MMM D, YYYY",
1312
+ timezone: data.reportPeriod.timezone,
1313
+ }),
1314
+ reportEndDate: OneUptimeDate.getDateAsCustomFormattedStringInTimezone({
1315
+ date: endDate,
1316
+ format: "MMM D, YYYY",
1317
+ timezone: data.reportPeriod.timezone,
1318
+ }),
1319
+ reportTimezone: data.reportPeriod.timezone,
1320
+ };
1321
+
1261
1322
  if (statusPageResources.length === 0) {
1262
1323
  return {
1263
- reportDates: startAndEndDate,
1324
+ ...periodStrings,
1264
1325
  totalResources: 0,
1265
1326
  totalIncidents: 0,
1266
1327
  averageUptimePercent: "0%",
@@ -1275,7 +1336,8 @@ export class Service extends DatabaseService<StatusPage> {
1275
1336
 
1276
1337
  const incidentCount: number = await this.getIncidentCountOnStatusPage({
1277
1338
  statusPageId: data.statusPageId,
1278
- historyDays: data.historyDays,
1339
+ startDate: startDate,
1340
+ endDate: endDate,
1279
1341
  });
1280
1342
 
1281
1343
  const monitors: {
@@ -1344,7 +1406,8 @@ export class Service extends DatabaseService<StatusPage> {
1344
1406
  resourceName: resource.displayName || "",
1345
1407
  totalIncidentCount: await this.getIncidentCountByMonitorIds({
1346
1408
  monitorIds: monitorIdsForThisResource,
1347
- historyDays: data.historyDays,
1409
+ startDate: startDate,
1410
+ endDate: endDate,
1348
1411
  }),
1349
1412
  uptimePercent: uptimePercent,
1350
1413
  uptimePercentAsString: `${uptimePercent}%`,
@@ -1393,7 +1456,6 @@ export class Service extends DatabaseService<StatusPage> {
1393
1456
  timeline: timeline,
1394
1457
  downtimeMonitorStatuses: statusPage.downtimeMonitorStatuses || [],
1395
1458
  reportWindow: reportWindow,
1396
- historyDays: data.historyDays,
1397
1459
  });
1398
1460
 
1399
1461
  const structure: StatusPageReportStructure = StatusPageReportTreeUtil.build(
@@ -1405,7 +1467,7 @@ export class Service extends DatabaseService<StatusPage> {
1405
1467
  );
1406
1468
 
1407
1469
  return {
1408
- reportDates: startAndEndDate,
1470
+ ...periodStrings,
1409
1471
  totalResources: statusPageResources.length,
1410
1472
  totalIncidents: incidentCount,
1411
1473
  averageUptimePercent: avgUptimePercentString,
@@ -1434,7 +1496,6 @@ export class Service extends DatabaseService<StatusPage> {
1434
1496
  timeline: Array<MonitorStatusTimeline>;
1435
1497
  downtimeMonitorStatuses: Array<MonitorStatus>;
1436
1498
  reportWindow: UptimeWindow;
1437
- historyDays: number;
1438
1499
  }): Promise<Dictionary<StatusPageReportGroupMetrics>> {
1439
1500
  const groupMetricsByGroupId: Dictionary<StatusPageReportGroupMetrics> = {};
1440
1501
  const incidentCountByMonitorSet: Dictionary<number> = {};
@@ -1528,7 +1589,8 @@ export class Service extends DatabaseService<StatusPage> {
1528
1589
  monitorIds.length > 0
1529
1590
  ? await this.getIncidentCountByMonitorIds({
1530
1591
  monitorIds: monitorIds,
1531
- historyDays: data.historyDays,
1592
+ startDate: data.reportWindow.startDate,
1593
+ endDate: data.reportWindow.endDate,
1532
1594
  })
1533
1595
  : 0;
1534
1596
 
@@ -1577,18 +1639,13 @@ export class Service extends DatabaseService<StatusPage> {
1577
1639
  @CaptureSpan()
1578
1640
  public async getIncidentCountByMonitorIds(data: {
1579
1641
  monitorIds: Array<ObjectID>;
1580
- historyDays: number;
1642
+ startDate: Date;
1643
+ endDate: Date;
1581
1644
  }): Promise<number> {
1582
- const today: Date = OneUptimeDate.getCurrentDate();
1583
-
1584
- const historyDays: Date = OneUptimeDate.getSomeDaysAgo(
1585
- data.historyDays || 14,
1586
- );
1587
-
1588
1645
  const incidentCount: PositiveNumber = await IncidentService.countBy({
1589
1646
  query: {
1590
1647
  monitors: data.monitorIds as any,
1591
- createdAt: QueryHelper.inBetween(historyDays, today),
1648
+ createdAt: QueryHelper.inBetween(data.startDate, data.endDate),
1592
1649
  },
1593
1650
  props: {
1594
1651
  isRoot: true,
@@ -1601,7 +1658,8 @@ export class Service extends DatabaseService<StatusPage> {
1601
1658
  @CaptureSpan()
1602
1659
  public async getIncidentCountOnStatusPage(data: {
1603
1660
  statusPageId: ObjectID;
1604
- historyDays: number;
1661
+ startDate: Date;
1662
+ endDate: Date;
1605
1663
  }): Promise<number> {
1606
1664
  const monitorsOnStatusPage: {
1607
1665
  monitorsOnStatusPage: Array<ObjectID>;
@@ -1612,7 +1670,8 @@ export class Service extends DatabaseService<StatusPage> {
1612
1670
 
1613
1671
  return this.getIncidentCountByMonitorIds({
1614
1672
  monitorIds: monitorsOnStatusPage.monitorsOnStatusPage,
1615
- historyDays: data.historyDays,
1673
+ startDate: data.startDate,
1674
+ endDate: data.endDate,
1616
1675
  });
1617
1676
  }
1618
1677
 
@@ -256,7 +256,24 @@ export class Service extends DatabaseService<Model> {
256
256
  ...commonVariables,
257
257
  {
258
258
  name: "report.reportDates",
259
- description: "The reporting period",
259
+ description: "The reporting period as a range",
260
+ },
261
+ {
262
+ name: "report.reportPeriodName",
263
+ description:
264
+ 'How a sentence refers to the period - "the last 30 days", "July 2026"',
265
+ },
266
+ {
267
+ name: "report.reportStartDate",
268
+ description: "First day of the reporting period",
269
+ },
270
+ {
271
+ name: "report.reportEndDate",
272
+ description: "Last day of the reporting period",
273
+ },
274
+ {
275
+ name: "report.reportTimezone",
276
+ description: "The timezone the reporting period was resolved in",
260
277
  },
261
278
  {
262
279
  name: "report.averageUptimePercent",
@@ -1373,6 +1373,9 @@ Stay informed about service availability! 🚀`;
1373
1373
  },
1374
1374
  subscriberTimezones: true,
1375
1375
  reportDataInDays: true,
1376
+ reportPeriodType: true,
1377
+ reportRecurringInterval: true,
1378
+ reportTimezone: true,
1376
1379
  isReportEnabled: true,
1377
1380
  showAnnouncementsOnStatusPage: true,
1378
1381
  showIncidentsOnStatusPage: true,