@oneuptime/common 12.0.18 → 12.0.20

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 (262) hide show
  1. package/Models/DatabaseModels/EnterpriseLicense.ts +23 -0
  2. package/Models/DatabaseModels/Index.ts +4 -2
  3. package/Models/DatabaseModels/Monitor.ts +39 -9
  4. package/Models/DatabaseModels/UserMicrosoftTeams.ts +344 -0
  5. package/Models/DatabaseModels/UserNotificationRule.ts +144 -8
  6. package/Models/DatabaseModels/UserNotificationSetting.ts +34 -0
  7. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +95 -0
  8. package/Models/DatabaseModels/UserSlack.ts +341 -0
  9. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +9 -0
  10. package/Server/API/EnterpriseLicenseAPI.ts +94 -19
  11. package/Server/API/UserMicrosoftTeamsAPI.ts +127 -0
  12. package/Server/API/UserSlackAPI.ts +125 -0
  13. package/Server/EnvironmentConfig.ts +29 -107
  14. package/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.ts +33 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.ts +51 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.ts +117 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.ts +197 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
  19. package/Server/Infrastructure/Queue.ts +7 -0
  20. package/Server/Services/EnterpriseLicenseService.ts +60 -0
  21. package/Server/Services/Index.ts +6 -2
  22. package/Server/Services/LlmLogService.ts +25 -9
  23. package/Server/Services/OnCallReadinessService.ts +86 -2
  24. package/Server/Services/ProjectService.ts +171 -0
  25. package/Server/Services/UserMicrosoftTeamsService.ts +208 -0
  26. package/Server/Services/UserNotificationMethodAdminService.ts +167 -5
  27. package/Server/Services/UserNotificationRuleAdminService.ts +40 -6
  28. package/Server/Services/UserNotificationRuleService.ts +589 -11
  29. package/Server/Services/UserNotificationSettingService.ts +138 -0
  30. package/Server/Services/UserService.ts +28 -0
  31. package/Server/Services/UserSlackService.ts +218 -0
  32. package/Server/Services/WorkspaceProjectAuthTokenService.ts +67 -1
  33. package/Server/Services/WorkspaceUserAuthTokenService.ts +73 -0
  34. package/Server/Services/WorkspaceUserNotificationService.ts +190 -0
  35. package/Server/Utils/Marketing/MarketingEventUtil.ts +145 -0
  36. package/Server/Utils/Marketing/MarketingEventWebhook.ts +114 -0
  37. package/Server/Utils/Monitor/MonitorLogUtil.ts +14 -1
  38. package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +321 -0
  39. package/Server/Utils/Response.ts +2 -2
  40. package/Server/Utils/StartServer.ts +2 -2
  41. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +191 -4
  42. package/Server/Views/Partials/AnalyticsConsent.ejs +533 -0
  43. package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +22 -14
  44. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +22 -11
  45. package/Tests/App/Dashboard/AlertEpisodeViewFields.test.tsx +465 -0
  46. package/Tests/App/Dashboard/DashboardChartWidgetZoom.test.tsx +302 -0
  47. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +408 -0
  48. package/Tests/App/Dashboard/IncidentEpisodeViewFields.test.tsx +474 -0
  49. package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +368 -0
  50. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +37 -1
  51. package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +18 -10
  52. package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +27 -1
  53. package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +214 -0
  54. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +763 -0
  55. package/Tests/Server/API/OnCallReadinessAPI.test.ts +145 -26
  56. package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +2 -2
  57. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +11 -1
  58. package/Tests/Server/Services/AdminRuleEditGuards.test.ts +22 -8
  59. package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +18 -2
  60. package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +5 -1
  61. package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +640 -0
  62. package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +246 -22
  63. package/Tests/Server/Services/NotificationDeletionImpact.test.ts +136 -8
  64. package/Tests/Server/Services/OnCallNotificationFallback.test.ts +191 -8
  65. package/Tests/Server/Services/OnCallReadinessService.test.ts +343 -18
  66. package/Tests/Server/Services/ProjectServiceChangePlan.test.ts +138 -0
  67. package/Tests/Server/Services/ProjectServiceCreateSubscriptionStarted.test.ts +516 -0
  68. package/Tests/Server/Services/SeverityRuleBackfill.test.ts +5 -1
  69. package/Tests/Server/Services/UserMicrosoftTeamsService.test.ts +343 -0
  70. package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +209 -8
  71. package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +22 -6
  72. package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +12 -6
  73. package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +37 -3
  74. package/Tests/Server/Services/UserNotificationRuleWorkspaceDelivery.test.ts +669 -0
  75. package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +410 -0
  76. package/Tests/Server/Services/UserSlackService.test.ts +407 -0
  77. package/Tests/Server/Services/WorkspaceProjectAuthTokenDisconnectCascade.test.ts +224 -0
  78. package/Tests/Server/Services/WorkspaceUserAuthTokenNotificationMethodCascade.test.ts +189 -0
  79. package/Tests/Server/Services/WorkspaceUserNotificationService.test.ts +496 -0
  80. package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +30 -17
  81. package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +8 -3
  82. package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +72 -3
  83. package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +22 -18
  84. package/Tests/Server/Types/Database/Permissions/WorkspaceMethodStampedColumnCreate.test.ts +178 -0
  85. package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +259 -0
  86. package/Tests/Server/Utils/Marketing/MarketingEventWebhook.test.ts +257 -0
  87. package/Tests/Server/Utils/Monitor/MonitorLogUtilRedaction.test.ts +300 -0
  88. package/Tests/Server/Utils/Monitor/MonitorPayloadRedaction.test.ts +381 -0
  89. package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +53 -0
  90. package/Tests/Server/Utils/Runbook/RunbookExecutePermission.test.ts +228 -0
  91. package/Tests/Server/Utils/SessionReplay/SessionReplayErasureTombstone.test.ts +189 -0
  92. package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +205 -0
  93. package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +275 -0
  94. package/Tests/Server/Utils/Telemetry/TelemetryContext.test.ts +279 -0
  95. package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +454 -0
  96. package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +241 -0
  97. package/Tests/UI/Components/Charts/ChartTooltipSorted.test.tsx +129 -0
  98. package/Tests/UI/Components/Charts/TooltipEntries.test.ts +155 -0
  99. package/Tests/UI/Components/Detail/DetailFieldErrors.test.tsx +347 -0
  100. package/Tests/UI/Components/Forms/BasicFormStepValidation.test.tsx +392 -0
  101. package/Tests/UI/Components/Forms/ValidationFormSteps.test.ts +549 -0
  102. package/Tests/UI/Utils/NotificationMethodUtil.test.ts +137 -9
  103. package/Tests/UI/Utils/PermissionGate.test.ts +105 -0
  104. package/Tests/Utils/EnterpriseLicenseSync.test.ts +562 -0
  105. package/Types/Analytics/RevenueEvent.ts +1 -0
  106. package/Types/Marketing/MarketingEvent.ts +140 -0
  107. package/UI/Components/Charts/Area/AreaChart.tsx +8 -1
  108. package/UI/Components/Charts/Bar/BarChart.tsx +8 -1
  109. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +19 -4
  110. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +20 -2
  111. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +19 -4
  112. package/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.ts +105 -0
  113. package/UI/Components/Charts/Line/LineChart.tsx +8 -1
  114. package/UI/Components/Detail/Detail.tsx +18 -1
  115. package/UI/Utils/NotificationMethodUtil.ts +41 -1
  116. package/UI/Utils/PermissionGate.ts +56 -0
  117. package/Utils/EnterpriseLicense/EnterpriseLicenseSync.ts +244 -0
  118. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +24 -0
  119. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
  120. package/build/dist/Models/DatabaseModels/Index.js +4 -2
  121. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  122. package/build/dist/Models/DatabaseModels/Monitor.js +39 -9
  123. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  124. package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js +363 -0
  125. package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js.map +1 -0
  126. package/build/dist/Models/DatabaseModels/UserNotificationRule.js +145 -8
  127. package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
  128. package/build/dist/Models/DatabaseModels/UserNotificationSetting.js +38 -0
  129. package/build/dist/Models/DatabaseModels/UserNotificationSetting.js.map +1 -1
  130. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +96 -0
  131. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  132. package/build/dist/Models/DatabaseModels/UserSlack.js +361 -0
  133. package/build/dist/Models/DatabaseModels/UserSlack.js.map +1 -0
  134. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  135. package/build/dist/Server/API/EnterpriseLicenseAPI.js +66 -12
  136. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
  137. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +82 -0
  138. package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -0
  139. package/build/dist/Server/API/UserSlackAPI.js +81 -0
  140. package/build/dist/Server/API/UserSlackAPI.js.map +1 -0
  141. package/build/dist/Server/EnvironmentConfig.js +25 -70
  142. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  143. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js +26 -0
  144. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js.map +1 -0
  145. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js +37 -0
  146. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js.map +1 -0
  147. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js +106 -0
  148. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js.map +1 -0
  149. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js +78 -0
  150. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js.map +1 -0
  151. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
  152. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  153. package/build/dist/Server/Infrastructure/Queue.js +7 -0
  154. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  155. package/build/dist/Server/Services/EnterpriseLicenseService.js +65 -0
  156. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  157. package/build/dist/Server/Services/Index.js +6 -2
  158. package/build/dist/Server/Services/Index.js.map +1 -1
  159. package/build/dist/Server/Services/LlmLogService.js +25 -9
  160. package/build/dist/Server/Services/LlmLogService.js.map +1 -1
  161. package/build/dist/Server/Services/OnCallReadinessService.js +74 -2
  162. package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -1
  163. package/build/dist/Server/Services/ProjectService.js +144 -0
  164. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  165. package/build/dist/Server/Services/UserMicrosoftTeamsService.js +183 -0
  166. package/build/dist/Server/Services/UserMicrosoftTeamsService.js.map +1 -0
  167. package/build/dist/Server/Services/UserNotificationMethodAdminService.js +138 -2
  168. package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
  169. package/build/dist/Server/Services/UserNotificationRuleAdminService.js +32 -4
  170. package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
  171. package/build/dist/Server/Services/UserNotificationRuleService.js +499 -67
  172. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  173. package/build/dist/Server/Services/UserNotificationSettingService.js +118 -0
  174. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  175. package/build/dist/Server/Services/UserService.js +26 -1
  176. package/build/dist/Server/Services/UserService.js.map +1 -1
  177. package/build/dist/Server/Services/UserSlackService.js +195 -0
  178. package/build/dist/Server/Services/UserSlackService.js.map +1 -0
  179. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +64 -1
  180. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
  181. package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js +70 -0
  182. package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js.map +1 -1
  183. package/build/dist/Server/Services/WorkspaceUserNotificationService.js +129 -0
  184. package/build/dist/Server/Services/WorkspaceUserNotificationService.js.map +1 -0
  185. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +94 -0
  186. package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -0
  187. package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js +93 -0
  188. package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js.map +1 -0
  189. package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js +12 -1
  190. package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js.map +1 -1
  191. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +233 -0
  192. package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -0
  193. package/build/dist/Server/Utils/Response.js +2 -2
  194. package/build/dist/Server/Utils/Response.js.map +1 -1
  195. package/build/dist/Server/Utils/StartServer.js +2 -2
  196. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  197. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +141 -4
  198. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  199. package/build/dist/Types/Analytics/RevenueEvent.js +1 -0
  200. package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -1
  201. package/build/dist/Types/Marketing/MarketingEvent.js +82 -0
  202. package/build/dist/Types/Marketing/MarketingEvent.js.map +1 -0
  203. package/build/dist/UI/Components/Charts/Area/AreaChart.js +9 -1
  204. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  205. package/build/dist/UI/Components/Charts/Bar/BarChart.js +9 -1
  206. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  207. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +22 -13
  208. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  209. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +27 -15
  210. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  211. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +27 -18
  212. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  213. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.js +71 -0
  214. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/TooltipEntries.js.map +1 -0
  215. package/build/dist/UI/Components/Charts/Line/LineChart.js +9 -1
  216. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  217. package/build/dist/UI/Components/Detail/Detail.js +17 -1
  218. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  219. package/build/dist/UI/Utils/NotificationMethodUtil.js +33 -1
  220. package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -1
  221. package/build/dist/UI/Utils/PermissionGate.js +32 -0
  222. package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
  223. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js +174 -0
  224. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSync.js.map +1 -0
  225. package/package.json +1 -1
  226. package/Models/DatabaseModels/MarketingConversion.ts +0 -410
  227. package/Server/Services/MarketingConversionService.ts +0 -10
  228. package/Server/Utils/Marketing/ConversionUploadProvider.ts +0 -215
  229. package/Server/Utils/Marketing/ConversionUploadProviders.ts +0 -22
  230. package/Server/Utils/Marketing/Providers/GoogleAds.ts +0 -333
  231. package/Server/Utils/Marketing/Providers/LinkedIn.ts +0 -185
  232. package/Server/Utils/Marketing/Providers/Meta.ts +0 -182
  233. package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +0 -223
  234. package/Server/Utils/Marketing/Providers/Reddit.ts +0 -208
  235. package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +0 -315
  236. package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +0 -300
  237. package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +0 -592
  238. package/Tests/Server/Utils/Marketing/LinkedIn.test.ts +0 -282
  239. package/Tests/Server/Utils/Marketing/Meta.test.ts +0 -304
  240. package/Tests/Server/Utils/Marketing/MicrosoftAds.test.ts +0 -263
  241. package/Tests/Server/Utils/Marketing/Reddit.test.ts +0 -259
  242. package/Types/Marketing/MarketingConversion.ts +0 -53
  243. package/build/dist/Models/DatabaseModels/MarketingConversion.js +0 -445
  244. package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +0 -1
  245. package/build/dist/Server/Services/MarketingConversionService.js +0 -9
  246. package/build/dist/Server/Services/MarketingConversionService.js.map +0 -1
  247. package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +0 -93
  248. package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +0 -1
  249. package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js +0 -20
  250. package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js.map +0 -1
  251. package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +0 -221
  252. package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +0 -1
  253. package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +0 -137
  254. package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +0 -1
  255. package/build/dist/Server/Utils/Marketing/Providers/Meta.js +0 -133
  256. package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +0 -1
  257. package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +0 -143
  258. package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +0 -1
  259. package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +0 -145
  260. package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +0 -1
  261. package/build/dist/Types/Marketing/MarketingConversion.js +0 -52
  262. package/build/dist/Types/Marketing/MarketingConversion.js.map +0 -1
@@ -314,6 +314,40 @@ class UserNotificationSetting extends BaseModel {
314
314
  })
315
315
  public alertByTelegram?: boolean = undefined;
316
316
 
317
+ @ColumnAccessControl({
318
+ create: [Permission.CurrentUser],
319
+ read: [Permission.CurrentUser],
320
+ update: [Permission.CurrentUser],
321
+ })
322
+ @TableColumn({
323
+ isDefaultValueColumn: true,
324
+ type: TableColumnType.Boolean,
325
+ defaultValue: false,
326
+ example: false,
327
+ })
328
+ @Column({
329
+ type: ColumnType.Boolean,
330
+ default: false,
331
+ })
332
+ public alertBySlack?: boolean = undefined;
333
+
334
+ @ColumnAccessControl({
335
+ create: [Permission.CurrentUser],
336
+ read: [Permission.CurrentUser],
337
+ update: [Permission.CurrentUser],
338
+ })
339
+ @TableColumn({
340
+ isDefaultValueColumn: true,
341
+ type: TableColumnType.Boolean,
342
+ defaultValue: false,
343
+ example: false,
344
+ })
345
+ @Column({
346
+ type: ColumnType.Boolean,
347
+ default: false,
348
+ })
349
+ public alertByMicrosoftTeams?: boolean = undefined;
350
+
317
351
  @ColumnAccessControl({
318
352
  create: [Permission.CurrentUser],
319
353
  read: [Permission.CurrentUser],
@@ -16,6 +16,8 @@ import UserPush from "./UserPush";
16
16
  import UserOnCallLog from "./UserOnCallLog";
17
17
  import UserSMS from "./UserSMS";
18
18
  import UserTelegram from "./UserTelegram";
19
+ import UserSlack from "./UserSlack";
20
+ import UserMicrosoftTeams from "./UserMicrosoftTeams";
19
21
  import UserWebhook from "./UserWebhook";
20
22
  import UserWhatsApp from "./UserWhatsApp";
21
23
  import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
@@ -1057,6 +1059,99 @@ export default class UserOnCallLogTimeline extends BaseModel {
1057
1059
  })
1058
1060
  public userTelegramId?: ObjectID = undefined;
1059
1061
 
1062
+ @ColumnAccessControl({
1063
+ create: [],
1064
+ read: [Permission.CurrentUser],
1065
+ update: [],
1066
+ })
1067
+ @TableColumn({
1068
+ manyToOneRelationColumn: "userSlackId",
1069
+ type: TableColumnType.Entity,
1070
+ modelType: UserSlack,
1071
+ title: "User Slack",
1072
+ description: "Relation to User Slack Resource in which this object belongs",
1073
+ })
1074
+ @ManyToOne(
1075
+ () => {
1076
+ return UserSlack;
1077
+ },
1078
+ {
1079
+ eager: false,
1080
+ nullable: true,
1081
+ onDelete: "CASCADE",
1082
+ orphanedRowAction: "nullify",
1083
+ },
1084
+ )
1085
+ @JoinColumn({ name: "userSlackId" })
1086
+ public userSlack?: UserSlack = undefined;
1087
+
1088
+ @ColumnAccessControl({
1089
+ create: [],
1090
+ read: [Permission.CurrentUser],
1091
+ update: [],
1092
+ })
1093
+ @Index()
1094
+ @TableColumn({
1095
+ type: TableColumnType.ObjectID,
1096
+ required: false,
1097
+ canReadOnRelationQuery: true,
1098
+ title: "User Slack ID",
1099
+ description: "ID of User Slack in which this object belongs",
1100
+ })
1101
+ @Column({
1102
+ type: ColumnType.ObjectID,
1103
+ nullable: true,
1104
+ transformer: ObjectID.getDatabaseTransformer(),
1105
+ })
1106
+ public userSlackId?: ObjectID = undefined;
1107
+
1108
+ @ColumnAccessControl({
1109
+ create: [],
1110
+ read: [Permission.CurrentUser],
1111
+ update: [],
1112
+ })
1113
+ @TableColumn({
1114
+ manyToOneRelationColumn: "userMicrosoftTeamsId",
1115
+ type: TableColumnType.Entity,
1116
+ modelType: UserMicrosoftTeams,
1117
+ title: "User Microsoft Teams",
1118
+ description:
1119
+ "Relation to User Microsoft Teams Resource in which this object belongs",
1120
+ })
1121
+ @ManyToOne(
1122
+ () => {
1123
+ return UserMicrosoftTeams;
1124
+ },
1125
+ {
1126
+ eager: false,
1127
+ nullable: true,
1128
+ onDelete: "CASCADE",
1129
+ orphanedRowAction: "nullify",
1130
+ },
1131
+ )
1132
+ @JoinColumn({ name: "userMicrosoftTeamsId" })
1133
+ public userMicrosoftTeams?: UserMicrosoftTeams = undefined;
1134
+
1135
+ @ColumnAccessControl({
1136
+ create: [],
1137
+ read: [Permission.CurrentUser],
1138
+ update: [],
1139
+ })
1140
+ @Index()
1141
+ @TableColumn({
1142
+ type: TableColumnType.ObjectID,
1143
+ required: false,
1144
+ canReadOnRelationQuery: true,
1145
+ title: "User Microsoft Teams ID",
1146
+ description: "ID of User Microsoft Teams in which this object belongs",
1147
+ })
1148
+ @Column({
1149
+ type: ColumnType.ObjectID,
1150
+ nullable: true,
1151
+ transformer: ObjectID.getDatabaseTransformer(),
1152
+ })
1153
+ public userMicrosoftTeamsId?: ObjectID = undefined;
1154
+
1060
1155
  @ColumnAccessControl({
1061
1156
  create: [],
1062
1157
  read: [Permission.CurrentUser],
@@ -0,0 +1,341 @@
1
+ import Project from "./Project";
2
+ import User from "./User";
3
+ import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
4
+ import Route from "../../Types/API/Route";
5
+ import AllowAccessIfSubscriptionIsUnpaid from "../../Types/Database/AccessControl/AllowAccessIfSubscriptionIsUnpaid";
6
+ import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
7
+ import OwnerOnlyColumn from "../../Types/Database/AccessControl/OwnerOnlyColumn";
8
+ import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
9
+ import ColumnLength from "../../Types/Database/ColumnLength";
10
+ import ColumnType from "../../Types/Database/ColumnType";
11
+ import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
12
+ import CurrentUserCanAccessRecordBy from "../../Types/Database/CurrentUserCanAccessRecordBy";
13
+ import TableColumn from "../../Types/Database/TableColumn";
14
+ import TableColumnType from "../../Types/Database/TableColumnType";
15
+ import TableMetadata from "../../Types/Database/TableMetadata";
16
+ import TenantColumn from "../../Types/Database/TenantColumn";
17
+ import IconProp from "../../Types/Icon/IconProp";
18
+ import ObjectID from "../../Types/ObjectID";
19
+ import Permission from "../../Types/Permission";
20
+ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
21
+
22
+ /*
23
+ * `read` names Permission.CurrentUser and nothing else, exactly like the other
24
+ * notification-method models (UserTelegram has the full essay). That single
25
+ * entry is what keeps every row owner-only: row scoping comes from the TABLE
26
+ * list alone, and widening it would expose the Slack member id every project
27
+ * member's pages are delivered to. Do not add administrator permissions here -
28
+ * OnCallReadinessService returns the masked, admin-sanctioned view instead.
29
+ *
30
+ * Unlike Telegram/SMS/WhatsApp there is no verification code: a row is only
31
+ * ever created from an existing, OAuth-established workspace link
32
+ * (WorkspaceUserAuthToken), so creation IS verification. UserSlackService
33
+ * enforces that and stamps the Slack identifiers server-side.
34
+ */
35
+ @TenantColumn("projectId")
36
+ @AllowAccessIfSubscriptionIsUnpaid()
37
+ @TableAccessControl({
38
+ create: [Permission.CurrentUser],
39
+ read: [Permission.CurrentUser],
40
+ delete: [Permission.CurrentUser],
41
+ update: [Permission.CurrentUser],
42
+ })
43
+ @CrudApiEndpoint(new Route("/user-slack"))
44
+ @Entity({
45
+ name: "UserSlack",
46
+ })
47
+ @TableMetadata({
48
+ tableName: "UserSlack",
49
+ singularName: "Slack Account",
50
+ pluralName: "Slack Accounts",
51
+ icon: IconProp.Slack,
52
+ tableDescription: "Slack accounts used for Slack notifications.",
53
+ })
54
+ @CurrentUserCanAccessRecordBy("userId")
55
+ class UserSlack extends BaseModel {
56
+ @ColumnAccessControl({
57
+ create: [Permission.CurrentUser],
58
+ read: [Permission.CurrentUser],
59
+ update: [],
60
+ })
61
+ @TableColumn({
62
+ manyToOneRelationColumn: "projectId",
63
+ type: TableColumnType.Entity,
64
+ modelType: Project,
65
+ title: "Project",
66
+ description: "Relation to Project Resource in which this object belongs",
67
+ })
68
+ @ManyToOne(
69
+ () => {
70
+ return Project;
71
+ },
72
+ {
73
+ eager: false,
74
+ nullable: true,
75
+ onDelete: "CASCADE",
76
+ orphanedRowAction: "nullify",
77
+ },
78
+ )
79
+ @JoinColumn({ name: "projectId" })
80
+ public project?: Project = undefined;
81
+
82
+ @ColumnAccessControl({
83
+ create: [Permission.CurrentUser],
84
+ read: [Permission.CurrentUser],
85
+ update: [],
86
+ })
87
+ @Index()
88
+ @TableColumn({
89
+ type: TableColumnType.ObjectID,
90
+ required: true,
91
+ canReadOnRelationQuery: true,
92
+ title: "Project ID",
93
+ description: "ID of your OneUptime Project in which this object belongs",
94
+ })
95
+ @Column({
96
+ type: ColumnType.ObjectID,
97
+ nullable: false,
98
+ transformer: ObjectID.getDatabaseTransformer(),
99
+ })
100
+ public projectId?: ObjectID = undefined;
101
+
102
+ @ColumnAccessControl({
103
+ /*
104
+ * CurrentUser here does NOT mean the client may choose the value: the
105
+ * service hook refuses any non-root payload that carries these columns
106
+ * and then stamps them itself, and hooks run BEFORE the column
107
+ * permission check (DatabaseService.create). The entry exists because
108
+ * that same check runs on the STAMPED data - with an empty list the
109
+ * server's own write is refused and no user can add this method at all.
110
+ * Same shape as UserWebAuthn's server-stamped isVerified.
111
+ */
112
+ create: [Permission.CurrentUser],
113
+ read: [Permission.CurrentUser],
114
+ update: [],
115
+ })
116
+ @Index()
117
+ /*
118
+ * The Slack member id (e.g. U0123ABCD) the project's bot delivers direct
119
+ * messages to. Holding it is enough to address bot messages at that person,
120
+ * so it is a delivery target and not a label. Captured server-side from the
121
+ * user's own WorkspaceUserAuthToken - never writable by the client.
122
+ */
123
+ @OwnerOnlyColumn()
124
+ @TableColumn({
125
+ title: "Slack User ID",
126
+ required: false,
127
+ unique: false,
128
+ type: TableColumnType.ShortText,
129
+ canReadOnRelationQuery: true,
130
+ description:
131
+ "Slack member ID captured from your connected Slack account. Populated automatically.",
132
+ })
133
+ @Column({
134
+ type: ColumnType.ShortText,
135
+ length: ColumnLength.ShortText,
136
+ unique: false,
137
+ nullable: true,
138
+ })
139
+ public slackUserId?: string = undefined;
140
+
141
+ @ColumnAccessControl({
142
+ /*
143
+ * CurrentUser here does NOT mean the client may choose the value: the
144
+ * service hook refuses any non-root payload that carries these columns
145
+ * and then stamps them itself, and hooks run BEFORE the column
146
+ * permission check (DatabaseService.create). The entry exists because
147
+ * that same check runs on the STAMPED data - with an empty list the
148
+ * server's own write is refused and no user can add this method at all.
149
+ * Same shape as UserWebAuthn's server-stamped isVerified.
150
+ */
151
+ create: [Permission.CurrentUser],
152
+ read: [Permission.CurrentUser],
153
+ update: [],
154
+ })
155
+ /*
156
+ * The person's Slack display name at the time the method was added. Only a
157
+ * label for the row, but it is still their identity in an outside workspace,
158
+ * so it stays owner-only like the Telegram handle.
159
+ */
160
+ @OwnerOnlyColumn()
161
+ @TableColumn({
162
+ title: "Slack Username",
163
+ required: false,
164
+ unique: false,
165
+ type: TableColumnType.ShortText,
166
+ canReadOnRelationQuery: true,
167
+ description:
168
+ "Slack username captured from your connected Slack account. Populated automatically.",
169
+ })
170
+ @Column({
171
+ type: ColumnType.ShortText,
172
+ length: ColumnLength.ShortText,
173
+ unique: false,
174
+ nullable: true,
175
+ })
176
+ public slackUserName?: string = undefined;
177
+
178
+ @ColumnAccessControl({
179
+ create: [Permission.CurrentUser],
180
+ read: [Permission.CurrentUser],
181
+ update: [],
182
+ })
183
+ @TableColumn({
184
+ manyToOneRelationColumn: "user",
185
+ type: TableColumnType.Entity,
186
+ modelType: User,
187
+ title: "User",
188
+ description: "Relation to User who this Slack account belongs to",
189
+ })
190
+ @ManyToOne(
191
+ () => {
192
+ return User;
193
+ },
194
+ {
195
+ eager: false,
196
+ nullable: true,
197
+ onDelete: "CASCADE",
198
+ orphanedRowAction: "nullify",
199
+ },
200
+ )
201
+ @JoinColumn({ name: "userId" })
202
+ public user?: User = undefined;
203
+
204
+ @ColumnAccessControl({
205
+ create: [Permission.CurrentUser],
206
+ read: [Permission.CurrentUser],
207
+ update: [],
208
+ })
209
+ @TableColumn({
210
+ type: TableColumnType.ObjectID,
211
+ title: "User ID",
212
+ description: "User ID who this Slack account belongs to",
213
+ })
214
+ @Column({
215
+ type: ColumnType.ObjectID,
216
+ nullable: true,
217
+ transformer: ObjectID.getDatabaseTransformer(),
218
+ })
219
+ @Index()
220
+ public userId?: ObjectID = undefined;
221
+
222
+ @ColumnAccessControl({
223
+ create: [Permission.CurrentUser],
224
+ read: [Permission.CurrentUser],
225
+ update: [],
226
+ })
227
+ @TableColumn({
228
+ manyToOneRelationColumn: "createdByUserId",
229
+ type: TableColumnType.Entity,
230
+ modelType: User,
231
+ title: "Created by User",
232
+ description:
233
+ "Relation to User who created this object (if this object was created by a User)",
234
+ })
235
+ @ManyToOne(
236
+ () => {
237
+ return User;
238
+ },
239
+ {
240
+ eager: false,
241
+ nullable: true,
242
+ onDelete: "SET NULL",
243
+ orphanedRowAction: "nullify",
244
+ },
245
+ )
246
+ @JoinColumn({ name: "createdByUserId" })
247
+ public createdByUser?: User = undefined;
248
+
249
+ @ColumnAccessControl({
250
+ create: [Permission.CurrentUser],
251
+ read: [Permission.CurrentUser],
252
+ update: [],
253
+ })
254
+ @TableColumn({
255
+ type: TableColumnType.ObjectID,
256
+ title: "Created by User ID",
257
+ description:
258
+ "User ID who created this object (if this object was created by a User)",
259
+ })
260
+ @Column({
261
+ type: ColumnType.ObjectID,
262
+ nullable: true,
263
+ transformer: ObjectID.getDatabaseTransformer(),
264
+ })
265
+ public createdByUserId?: ObjectID = undefined;
266
+
267
+ @ColumnAccessControl({
268
+ create: [],
269
+ read: [],
270
+ update: [],
271
+ })
272
+ @TableColumn({
273
+ manyToOneRelationColumn: "deletedByUserId",
274
+ type: TableColumnType.Entity,
275
+ title: "Deleted by User",
276
+ modelType: User,
277
+ description:
278
+ "Relation to User who deleted this object (if this object was deleted by a User)",
279
+ })
280
+ @ManyToOne(
281
+ () => {
282
+ return User;
283
+ },
284
+ {
285
+ cascade: false,
286
+ eager: false,
287
+ nullable: true,
288
+ onDelete: "SET NULL",
289
+ orphanedRowAction: "nullify",
290
+ },
291
+ )
292
+ @JoinColumn({ name: "deletedByUserId" })
293
+ public deletedByUser?: User = undefined;
294
+
295
+ @ColumnAccessControl({
296
+ create: [],
297
+ read: [],
298
+ update: [],
299
+ })
300
+ @TableColumn({
301
+ type: TableColumnType.ObjectID,
302
+ title: "Deleted by User ID",
303
+ description:
304
+ "User ID who deleted this object (if this object was deleted by a User)",
305
+ })
306
+ @Column({
307
+ type: ColumnType.ObjectID,
308
+ nullable: true,
309
+ transformer: ObjectID.getDatabaseTransformer(),
310
+ })
311
+ public deletedByUserId?: ObjectID = undefined;
312
+
313
+ @ColumnAccessControl({
314
+ /*
315
+ * CurrentUser here does NOT mean the client may choose the value: the
316
+ * service hook refuses any non-root payload that carries these columns
317
+ * and then stamps them itself, and hooks run BEFORE the column
318
+ * permission check (DatabaseService.create). The entry exists because
319
+ * that same check runs on the STAMPED data - with an empty list the
320
+ * server's own write is refused and no user can add this method at all.
321
+ * Same shape as UserWebAuthn's server-stamped isVerified.
322
+ */
323
+ create: [Permission.CurrentUser],
324
+ read: [Permission.CurrentUser],
325
+ update: [],
326
+ })
327
+ @TableColumn({
328
+ title: "Is Verified",
329
+ description: "Is this Slack account verified?",
330
+ isDefaultValueColumn: true,
331
+ type: TableColumnType.Boolean,
332
+ defaultValue: false,
333
+ })
334
+ @Column({
335
+ type: ColumnType.Boolean,
336
+ default: false,
337
+ })
338
+ public isVerified?: boolean = undefined;
339
+ }
340
+
341
+ export default UserSlack;
@@ -34,6 +34,15 @@ export interface MicrosoftTeamsChat {
34
34
  chatType: MicrosoftTeamsChatType;
35
35
  serviceUrl?: string | undefined; // Bot Framework service URL captured when the bot was added.
36
36
  addedAt?: string | undefined;
37
+ /*
38
+ * Microsoft Entra object ids of the human members, captured from the chat
39
+ * roster. What lets a personal chat be matched back to the OneUptime user
40
+ * it belongs to (WorkspaceUserAuthToken.workspaceUserId is the same id), so
41
+ * user direct-message notifications can reuse the existing chat instead of
42
+ * creating a new conversation. Absent on records captured before this
43
+ * shipped.
44
+ */
45
+ memberAadObjectIds?: Array<string> | undefined;
37
46
  }
38
47
 
39
48
  /*
@@ -1,7 +1,6 @@
1
1
  import EnterpriseLicense from "../../Models/DatabaseModels/EnterpriseLicense";
2
2
  import EnterpriseLicenseInstance from "../../Models/DatabaseModels/EnterpriseLicenseInstance";
3
3
  import BadDataException from "../../Types/Exception/BadDataException";
4
- import { JSONObject } from "../../Types/JSON";
5
4
  import PartialEntity from "../../Types/Database/PartialEntity";
6
5
  import EnterpriseLicenseInstanceSummary from "../../Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary";
7
6
  import EnterpriseLicenseUsageUtil from "../../Utils/EnterpriseLicense/EnterpriseLicenseUsage";
@@ -26,6 +25,20 @@ import {
26
25
  import BaseAPI from "./BaseAPI";
27
26
  // import { Host } from "../EnvironmentConfig";
28
27
 
28
+ /*
29
+ * The license state every installation of a license key mirrors locally.
30
+ * Returned by both /validate and /report-user-count so that the daily report
31
+ * keeps a self-hosted installation current on its own.
32
+ */
33
+ export interface LicenseTerms {
34
+ companyName: string;
35
+ // ISO 8601, or null for a license with no expiry set.
36
+ expiresAt: string | null;
37
+ licenseKey: string;
38
+ // Null means the license carries no seat limit.
39
+ userLimit: number | null;
40
+ }
41
+
29
42
  export interface LicenseInstanceUpsert {
30
43
  licenseId: ObjectID;
31
44
  instanceId: string;
@@ -137,24 +150,15 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
137
150
  const instances: Array<EnterpriseLicenseInstance> =
138
151
  await this.findLicenseInstances(license.id!);
139
152
 
140
- const payload: JSONObject = {
141
- companyName: license.companyName || "",
142
- expiresAt: license.expiresAt.toISOString(),
143
- licenseKey: license.licenseKey || "",
144
- userLimit:
145
- typeof license.userLimit === "number" ? license.userLimit : null,
146
- };
147
-
148
- const token: string = JSONWebToken.signJsonPayload(
149
- payload,
150
- Math.max(secondsUntilExpiry, 1),
151
- );
153
+ const terms: LicenseTerms = this.getLicenseTerms(license);
154
+
155
+ const token: string | null = this.signLicenseToken(license);
152
156
 
153
157
  return Response.sendJsonObjectResponse(req, res, {
154
- companyName: payload["companyName"] as string,
155
- expiresAt: payload["expiresAt"] as string,
156
- licenseKey: payload["licenseKey"] as string,
157
- userLimit: payload["userLimit"],
158
+ companyName: terms.companyName,
159
+ expiresAt: terms.expiresAt,
160
+ licenseKey: terms.licenseKey,
161
+ userLimit: terms.userLimit,
158
162
  currentUserCount:
159
163
  typeof license.currentUserCount === "number"
160
164
  ? license.currentUserCount
@@ -202,8 +206,19 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
202
206
  query: {
203
207
  licenseKey: licenseKey,
204
208
  },
209
+ /*
210
+ * The full set of terms, not just the usage columns: this
211
+ * response is what a self-hosted installation mirrors into its
212
+ * own GlobalConfig every day, so anything it is missing here is
213
+ * a field the customer keeps the stale value of until somebody
214
+ * re-types the license key by hand. Same indexed lookup either
215
+ * way.
216
+ */
205
217
  select: {
206
218
  _id: true,
219
+ companyName: true,
220
+ expiresAt: true,
221
+ licenseKey: true,
207
222
  userLimit: true,
208
223
  isEvaluationLicense: true,
209
224
  },
@@ -282,13 +297,25 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
282
297
  });
283
298
  }
284
299
 
300
+ const terms: LicenseTerms = this.getLicenseTerms(license);
301
+
285
302
  return Response.sendJsonObjectResponse(req, res, {
303
+ companyName: terms.companyName,
304
+ expiresAt: terms.expiresAt,
305
+ licenseKey: terms.licenseKey,
306
+ userLimit: terms.userLimit,
286
307
  currentUserCount: currentUserCount,
287
308
  userCountUpdatedAt: reportedAt.toISOString(),
288
- userLimit:
289
- typeof license.userLimit === "number" ? license.userLimit : null,
290
309
  isEvaluationLicense: Boolean(license.isEvaluationLicense),
291
310
  instances: this.getInstanceSummaries(instances),
311
+ /*
312
+ * Null once the license has expired. Unlike /validate this route
313
+ * does not reject an expired license - the instance is still
314
+ * expected to report, and the expiry notification emails are built
315
+ * from what it reports - but it must not keep being handed a fresh
316
+ * token, and the expiresAt above is what tells it the truth.
317
+ */
318
+ token: this.signLicenseToken(license),
292
319
  });
293
320
  } catch (err) {
294
321
  next(err);
@@ -297,6 +324,54 @@ export default class EnterpriseLicenseAPI extends BaseAPI<
297
324
  );
298
325
  }
299
326
 
327
+ /*
328
+ * The license terms a self-hosted installation mirrors locally. Built in one
329
+ * place so /validate and /report-user-count cannot drift: the seat limit was
330
+ * missing from what an instance could refresh for exactly as long as the two
331
+ * responses were written out by hand, separately.
332
+ */
333
+ private getLicenseTerms(license: EnterpriseLicense): LicenseTerms {
334
+ return {
335
+ companyName: license.companyName || "",
336
+ expiresAt: license.expiresAt ? license.expiresAt.toISOString() : null,
337
+ licenseKey: license.licenseKey || "",
338
+ userLimit:
339
+ typeof license.userLimit === "number" ? license.userLimit : null,
340
+ };
341
+ }
342
+
343
+ /*
344
+ * Signed with the license server's own secret, so it is opaque to the
345
+ * installation holding it - it is the proof that oneuptime.com recognized
346
+ * the key, and the instance only checks that it exists. An expired license
347
+ * gets none, which is why this returns null rather than a short-lived token.
348
+ */
349
+ private signLicenseToken(license: EnterpriseLicense): string | null {
350
+ if (!license.expiresAt) {
351
+ return null;
352
+ }
353
+
354
+ const secondsUntilExpiry: number = Math.floor(
355
+ (license.expiresAt.getTime() - Date.now()) / 1000,
356
+ );
357
+
358
+ if (secondsUntilExpiry <= 0) {
359
+ return null;
360
+ }
361
+
362
+ const terms: LicenseTerms = this.getLicenseTerms(license);
363
+
364
+ return JSONWebToken.signJsonPayload(
365
+ {
366
+ companyName: terms.companyName,
367
+ expiresAt: terms.expiresAt,
368
+ licenseKey: terms.licenseKey,
369
+ userLimit: terms.userLimit,
370
+ },
371
+ Math.max(secondsUntilExpiry, 1),
372
+ );
373
+ }
374
+
300
375
  private parseShortText(value: unknown): string {
301
376
  if (typeof value !== "string") {
302
377
  return "";