@oneuptime/common 8.0.5239 → 8.0.5283

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 (359) hide show
  1. package/Models/DatabaseModels/Index.ts +4 -2
  2. package/Models/DatabaseModels/OnCallDutyPolicyUserOverride.ts +5 -3
  3. package/Models/DatabaseModels/Project.ts +4 -2
  4. package/Models/DatabaseModels/ProjectSmtpConfig.ts +4 -2
  5. package/Models/DatabaseModels/StatusPageDomain.ts +6 -4
  6. package/Models/DatabaseModels/User.ts +0 -46
  7. package/Models/DatabaseModels/{UserTwoFactorAuth.ts → UserTotpAuth.ts} +16 -16
  8. package/Models/DatabaseModels/UserWebAuthn.ts +244 -0
  9. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +21 -0
  10. package/Server/API/BaseAPI.ts +4 -2
  11. package/Server/API/GlobalConfigAPI.ts +16 -12
  12. package/Server/API/MicrosoftTeamsAPI.ts +1240 -0
  13. package/Server/API/ProjectAPI.ts +4 -2
  14. package/Server/API/ResellerPlanAPI.ts +4 -2
  15. package/Server/API/SlackAPI.ts +54 -48
  16. package/Server/API/StatusPageAPI.ts +5 -3
  17. package/Server/API/UserOnCallLogTimelineAPI.ts +5 -3
  18. package/Server/API/{UserTwoFactorAuthAPI.ts → UserTotpAuthAPI.ts} +20 -20
  19. package/Server/API/UserWebAuthnAPI.ts +103 -0
  20. package/Server/EnvironmentConfig.ts +6 -0
  21. package/Server/Images/MicrosoftTeams/color.png +0 -0
  22. package/Server/Images/MicrosoftTeams/outline.png +0 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/1753131488925-AddEnableCustomSubscriberEmailNotificationFooterText.ts +4 -2
  24. package/Server/Infrastructure/Postgres/SchemaMigrations/1759175457008-MigrationName.ts +27 -0
  25. package/Server/Infrastructure/Postgres/SchemaMigrations/1759232954703-MigrationName.ts +25 -0
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1759234532998-MigrationName.ts +15 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  28. package/Server/Infrastructure/Queue.ts +4 -2
  29. package/Server/Infrastructure/SocketIO.ts +4 -2
  30. package/Server/Middleware/ProjectAuthorization.ts +5 -3
  31. package/Server/Middleware/SlackAuthorization.ts +2 -2
  32. package/Server/Middleware/TelemetryIngest.ts +12 -6
  33. package/Server/Services/AlertStateTimelineService.ts +34 -18
  34. package/Server/Services/BillingInvoiceService.ts +8 -4
  35. package/Server/Services/BillingService.ts +13 -9
  36. package/Server/Services/DatabaseService.ts +42 -30
  37. package/Server/Services/IncidentService.ts +5 -3
  38. package/Server/Services/IncidentStateTimelineService.ts +34 -18
  39. package/Server/Services/Index.ts +4 -2
  40. package/Server/Services/MonitorStatusTimelineService.ts +34 -18
  41. package/Server/Services/OnCallDutyPolicyScheduleService.ts +4 -2
  42. package/Server/Services/ProjectService.ts +6 -4
  43. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +26 -14
  44. package/Server/Services/StatusPageService.ts +4 -2
  45. package/Server/Services/UserService.ts +21 -5
  46. package/Server/Services/{UserTwoFactorAuthService.ts → UserTotpAuthService.ts} +26 -7
  47. package/Server/Services/UserWebAuthnService.ts +419 -0
  48. package/Server/Services/WorkspaceNotificationRuleService.ts +257 -77
  49. package/Server/Services/WorkspaceProjectAuthTokenService.ts +2 -2
  50. package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -5
  51. package/Server/Types/Database/Permissions/BasePermission.ts +4 -2
  52. package/Server/Types/Database/Permissions/TenantPermission.ts +5 -3
  53. package/Server/Types/Database/QueryHelper.ts +4 -2
  54. package/Server/Types/Markdown.ts +6 -4
  55. package/Server/Types/Workflow/ComponentCode.ts +4 -2
  56. package/Server/Types/Workflow/Components/Conditions/IfElse.ts +5 -3
  57. package/Server/Types/Workflow/Components/JavaScript.ts +5 -3
  58. package/Server/Types/Workflow/TriggerCode.ts +4 -2
  59. package/Server/Utils/AnalyticsDatabase/Statement.ts +4 -2
  60. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +21 -11
  61. package/Server/Utils/Browser.ts +6 -4
  62. package/Server/Utils/CodeRepository/GitHub/GitHub.ts +4 -2
  63. package/Server/Utils/LocalFile.ts +14 -0
  64. package/Server/Utils/Monitor/MonitorResource.ts +17 -9
  65. package/Server/Utils/Realtime.ts +4 -2
  66. package/Server/Utils/StartServer.ts +1 -1
  67. package/Server/Utils/Telemetry.ts +15 -9
  68. package/Server/Utils/{TwoFactorAuth.ts → TotpAuth.ts} +2 -2
  69. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.ts +75 -16
  70. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +649 -0
  71. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +237 -0
  72. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +1321 -0
  73. package/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.ts +155 -0
  74. package/Server/Utils/Workspace/MicrosoftTeams/Actions/OnCallDutyPolicy.ts +119 -0
  75. package/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.ts +959 -0
  76. package/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.ts +16 -14
  77. package/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.ts +17 -14
  78. package/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.ts +18 -13
  79. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +2547 -14
  80. package/Server/Utils/Workspace/Slack/Actions/Alert.ts +4 -2
  81. package/Server/Utils/Workspace/Slack/Actions/Auth.ts +4 -2
  82. package/Server/Utils/Workspace/Slack/Actions/Incident.ts +14 -10
  83. package/Server/Utils/Workspace/Slack/Actions/Monitor.ts +4 -2
  84. package/Server/Utils/Workspace/Slack/Actions/OnCallDutyPolicy.ts +4 -2
  85. package/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.ts +14 -10
  86. package/Server/Utils/Workspace/Slack/Messages/Alert.ts +9 -7
  87. package/Server/Utils/Workspace/Slack/Messages/Incident.ts +9 -7
  88. package/Server/Utils/Workspace/Slack/Messages/Monitor.ts +9 -7
  89. package/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.ts +9 -7
  90. package/Server/Utils/Workspace/Slack/Slack.ts +6 -0
  91. package/Server/Utils/Workspace/Workspace.ts +13 -10
  92. package/Server/Utils/Workspace/WorkspaceBase.ts +9 -0
  93. package/Tests/Server/API/BaseAPI.test.ts +64 -52
  94. package/Tests/Server/Services/BillingService.test.ts +4 -4
  95. package/Tests/Server/Services/TeamMemberService.test.ts +20 -12
  96. package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +2 -2
  97. package/Tests/Types/OnCallDutyPolicy/LayerUtil.test.ts +8 -4
  98. package/Tests/UI/Components/DictionaryOfStrings.test.tsx +4 -2
  99. package/Tests/UI/Components/FilePicker.test.tsx +2 -2
  100. package/Tests/Utils/API.test.ts +9 -8
  101. package/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.ts +5 -3
  102. package/Types/Html.ts +5 -3
  103. package/Types/JSONFunctions.ts +5 -5
  104. package/Types/Metrics/MetricsQuery.ts +6 -4
  105. package/Types/Monitor/MonitorType.ts +8 -6
  106. package/Types/OnCallDutyPolicy/Layer.ts +29 -17
  107. package/Types/Phone.ts +5 -3
  108. package/Types/Workspace/NotificationRules/BaseNotificationRule.ts +1 -0
  109. package/Types/Workspace/NotificationRules/CreateChannelNotificationRule.ts +5 -2
  110. package/Types/Workspace/WorkspaceMessagePayload.ts +1 -0
  111. package/Types/Workspace/WorkspaceType.ts +13 -0
  112. package/UI/Components/Charts/Utils/DataPoint.ts +8 -6
  113. package/UI/Components/Detail/Detail.tsx +4 -1
  114. package/UI/Components/FilePicker/FilePicker.tsx +1 -1
  115. package/UI/Components/Forms/Types/Field.ts +4 -2
  116. package/UI/Components/Image/Image.tsx +1 -1
  117. package/UI/Components/JSONTable/JSONTable.tsx +4 -2
  118. package/UI/Components/ModelTable/BaseModelTable.tsx +5 -3
  119. package/UI/Components/SideMenu/SideMenu.tsx +4 -2
  120. package/UI/Components/SideMenu/SideMenuItem.tsx +69 -45
  121. package/UI/Config.ts +3 -0
  122. package/UI/Utils/API/API.ts +5 -3
  123. package/UI/Utils/Countries.ts +5 -3
  124. package/UI/Utils/Login.ts +6 -1
  125. package/Utils/Base64.ts +13 -0
  126. package/Utils/Schema/ModelSchema.ts +4 -2
  127. package/build/dist/Models/DatabaseModels/Index.js +4 -2
  128. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  129. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js +5 -3
  130. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyUserOverride.js.map +1 -1
  131. package/build/dist/Models/DatabaseModels/Project.js +4 -2
  132. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  133. package/build/dist/Models/DatabaseModels/ProjectSmtpConfig.js +4 -2
  134. package/build/dist/Models/DatabaseModels/ProjectSmtpConfig.js.map +1 -1
  135. package/build/dist/Models/DatabaseModels/StatusPageDomain.js +6 -4
  136. package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
  137. package/build/dist/Models/DatabaseModels/User.js +0 -49
  138. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  139. package/build/dist/Models/DatabaseModels/{UserTwoFactorAuth.js → UserTotpAuth.js} +27 -27
  140. package/build/dist/Models/DatabaseModels/UserTotpAuth.js.map +1 -0
  141. package/build/dist/Models/DatabaseModels/UserWebAuthn.js +270 -0
  142. package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -0
  143. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  144. package/build/dist/Server/API/BaseAPI.js +4 -2
  145. package/build/dist/Server/API/BaseAPI.js.map +1 -1
  146. package/build/dist/Server/API/GlobalConfigAPI.js +16 -12
  147. package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
  148. package/build/dist/Server/API/MicrosoftTeamsAPI.js +771 -0
  149. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -0
  150. package/build/dist/Server/API/ProjectAPI.js +4 -2
  151. package/build/dist/Server/API/ProjectAPI.js.map +1 -1
  152. package/build/dist/Server/API/ResellerPlanAPI.js +4 -2
  153. package/build/dist/Server/API/ResellerPlanAPI.js.map +1 -1
  154. package/build/dist/Server/API/SlackAPI.js +53 -47
  155. package/build/dist/Server/API/SlackAPI.js.map +1 -1
  156. package/build/dist/Server/API/StatusPageAPI.js +5 -3
  157. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  158. package/build/dist/Server/API/UserOnCallLogTimelineAPI.js +5 -3
  159. package/build/dist/Server/API/UserOnCallLogTimelineAPI.js.map +1 -1
  160. package/build/dist/Server/API/{UserTwoFactorAuthAPI.js → UserTotpAuthAPI.js} +16 -16
  161. package/build/dist/Server/API/UserTotpAuthAPI.js.map +1 -0
  162. package/build/dist/Server/API/UserWebAuthnAPI.js +65 -0
  163. package/build/dist/Server/API/UserWebAuthnAPI.js.map +1 -0
  164. package/build/dist/Server/EnvironmentConfig.js +3 -0
  165. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  166. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1753131488925-AddEnableCustomSubscriberEmailNotificationFooterText.js +4 -2
  167. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1753131488925-AddEnableCustomSubscriberEmailNotificationFooterText.js.map +1 -1
  168. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1759175457008-MigrationName.js +16 -0
  169. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1759175457008-MigrationName.js.map +1 -0
  170. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1759232954703-MigrationName.js +16 -0
  171. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1759232954703-MigrationName.js.map +1 -0
  172. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1759234532998-MigrationName.js +12 -0
  173. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1759234532998-MigrationName.js.map +1 -0
  174. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  175. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  176. package/build/dist/Server/Infrastructure/Queue.js +4 -2
  177. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  178. package/build/dist/Server/Infrastructure/SocketIO.js +4 -2
  179. package/build/dist/Server/Infrastructure/SocketIO.js.map +1 -1
  180. package/build/dist/Server/Middleware/ProjectAuthorization.js +5 -3
  181. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  182. package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -1
  183. package/build/dist/Server/Middleware/TelemetryIngest.js +12 -6
  184. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  185. package/build/dist/Server/Services/AlertStateTimelineService.js +34 -18
  186. package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
  187. package/build/dist/Server/Services/BillingInvoiceService.js +8 -4
  188. package/build/dist/Server/Services/BillingInvoiceService.js.map +1 -1
  189. package/build/dist/Server/Services/BillingService.js +13 -9
  190. package/build/dist/Server/Services/BillingService.js.map +1 -1
  191. package/build/dist/Server/Services/DatabaseService.js +40 -28
  192. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  193. package/build/dist/Server/Services/IncidentService.js +5 -3
  194. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  195. package/build/dist/Server/Services/IncidentStateTimelineService.js +34 -18
  196. package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
  197. package/build/dist/Server/Services/Index.js +4 -2
  198. package/build/dist/Server/Services/Index.js.map +1 -1
  199. package/build/dist/Server/Services/MonitorStatusTimelineService.js +34 -18
  200. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  201. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +4 -2
  202. package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js.map +1 -1
  203. package/build/dist/Server/Services/ProjectService.js +6 -4
  204. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  205. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +26 -14
  206. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  207. package/build/dist/Server/Services/StatusPageService.js +4 -2
  208. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  209. package/build/dist/Server/Services/UserService.js +16 -3
  210. package/build/dist/Server/Services/UserService.js.map +1 -1
  211. package/build/dist/Server/Services/{UserTwoFactorAuthService.js → UserTotpAuthService.js} +22 -8
  212. package/build/dist/Server/Services/UserTotpAuthService.js.map +1 -0
  213. package/build/dist/Server/Services/UserWebAuthnService.js +365 -0
  214. package/build/dist/Server/Services/UserWebAuthnService.js.map +1 -0
  215. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +142 -51
  216. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
  217. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -5
  218. package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
  219. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +4 -2
  220. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  221. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +5 -3
  222. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
  223. package/build/dist/Server/Types/Database/QueryHelper.js +4 -2
  224. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  225. package/build/dist/Server/Types/Markdown.js +6 -4
  226. package/build/dist/Server/Types/Markdown.js.map +1 -1
  227. package/build/dist/Server/Types/Workflow/ComponentCode.js +4 -2
  228. package/build/dist/Server/Types/Workflow/ComponentCode.js.map +1 -1
  229. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +5 -3
  230. package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
  231. package/build/dist/Server/Types/Workflow/Components/JavaScript.js +5 -3
  232. package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
  233. package/build/dist/Server/Types/Workflow/TriggerCode.js.map +1 -1
  234. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +4 -2
  235. package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
  236. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +21 -11
  237. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  238. package/build/dist/Server/Utils/Browser.js +6 -4
  239. package/build/dist/Server/Utils/Browser.js.map +1 -1
  240. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +4 -2
  241. package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
  242. package/build/dist/Server/Utils/LocalFile.js +16 -0
  243. package/build/dist/Server/Utils/LocalFile.js.map +1 -1
  244. package/build/dist/Server/Utils/Monitor/MonitorResource.js +17 -9
  245. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  246. package/build/dist/Server/Utils/Realtime.js +4 -2
  247. package/build/dist/Server/Utils/Realtime.js.map +1 -1
  248. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  249. package/build/dist/Server/Utils/Telemetry.js +6 -4
  250. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  251. package/build/dist/Server/Utils/{TwoFactorAuth.js → TotpAuth.js} +8 -8
  252. package/build/dist/Server/Utils/TotpAuth.js.map +1 -0
  253. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.js +86 -36
  254. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes.js.map +1 -1
  255. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +531 -0
  256. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -0
  257. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +206 -0
  258. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -0
  259. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +1102 -0
  260. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -0
  261. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js +136 -0
  262. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Monitor.js.map +1 -0
  263. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/OnCallDutyPolicy.js +107 -0
  264. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/OnCallDutyPolicy.js.map +1 -0
  265. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js +795 -0
  266. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance.js.map +1 -0
  267. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.js +16 -14
  268. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Messages/Alert.js.map +1 -1
  269. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.js +16 -14
  270. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Messages/Incident.js.map +1 -1
  271. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.js +15 -13
  272. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Messages/ScheduledMaintenance.js.map +1 -1
  273. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +1982 -13
  274. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  275. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js +4 -2
  276. package/build/dist/Server/Utils/Workspace/Slack/Actions/Alert.js.map +1 -1
  277. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js +4 -2
  278. package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js.map +1 -1
  279. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js +14 -10
  280. package/build/dist/Server/Utils/Workspace/Slack/Actions/Incident.js.map +1 -1
  281. package/build/dist/Server/Utils/Workspace/Slack/Actions/Monitor.js +4 -2
  282. package/build/dist/Server/Utils/Workspace/Slack/Actions/Monitor.js.map +1 -1
  283. package/build/dist/Server/Utils/Workspace/Slack/Actions/OnCallDutyPolicy.js +4 -2
  284. package/build/dist/Server/Utils/Workspace/Slack/Actions/OnCallDutyPolicy.js.map +1 -1
  285. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js +14 -10
  286. package/build/dist/Server/Utils/Workspace/Slack/Actions/ScheduledMaintenance.js.map +1 -1
  287. package/build/dist/Server/Utils/Workspace/Slack/Messages/Alert.js +9 -7
  288. package/build/dist/Server/Utils/Workspace/Slack/Messages/Alert.js.map +1 -1
  289. package/build/dist/Server/Utils/Workspace/Slack/Messages/Incident.js +9 -7
  290. package/build/dist/Server/Utils/Workspace/Slack/Messages/Incident.js.map +1 -1
  291. package/build/dist/Server/Utils/Workspace/Slack/Messages/Monitor.js +9 -7
  292. package/build/dist/Server/Utils/Workspace/Slack/Messages/Monitor.js.map +1 -1
  293. package/build/dist/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.js +9 -7
  294. package/build/dist/Server/Utils/Workspace/Slack/Messages/ScheduledMaintenance.js.map +1 -1
  295. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +5 -0
  296. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  297. package/build/dist/Server/Utils/Workspace/Workspace.js +12 -10
  298. package/build/dist/Server/Utils/Workspace/Workspace.js.map +1 -1
  299. package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
  300. package/build/dist/Tests/Server/API/BaseAPI.test.js +59 -47
  301. package/build/dist/Tests/Server/API/BaseAPI.test.js.map +1 -1
  302. package/build/dist/Tests/Server/Services/BillingService.test.js +4 -4
  303. package/build/dist/Tests/Server/Services/BillingService.test.js.map +1 -1
  304. package/build/dist/Tests/Server/Services/TeamMemberService.test.js +20 -12
  305. package/build/dist/Tests/Server/Services/TeamMemberService.test.js.map +1 -1
  306. package/build/dist/Tests/Server/TestingUtils/Services/BillingServiceHelper.js +2 -2
  307. package/build/dist/Tests/Server/TestingUtils/Services/BillingServiceHelper.js.map +1 -1
  308. package/build/dist/Tests/Types/OnCallDutyPolicy/LayerUtil.test.js +8 -4
  309. package/build/dist/Tests/Types/OnCallDutyPolicy/LayerUtil.test.js.map +1 -1
  310. package/build/dist/Tests/UI/Components/DictionaryOfStrings.test.js +4 -2
  311. package/build/dist/Tests/UI/Components/DictionaryOfStrings.test.js.map +1 -1
  312. package/build/dist/Tests/UI/Components/FilePicker.test.js +2 -2
  313. package/build/dist/Tests/UI/Components/FilePicker.test.js.map +1 -1
  314. package/build/dist/Tests/Utils/API.test.js +8 -7
  315. package/build/dist/Tests/Utils/API.test.js.map +1 -1
  316. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js +5 -3
  317. package/build/dist/Types/BaseDatabase/DatabaseCommonInteractionPropsUtil.js.map +1 -1
  318. package/build/dist/Types/Html.js +5 -3
  319. package/build/dist/Types/Html.js.map +1 -1
  320. package/build/dist/Types/JSONFunctions.js +5 -5
  321. package/build/dist/Types/JSONFunctions.js.map +1 -1
  322. package/build/dist/Types/Monitor/MonitorType.js +8 -6
  323. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  324. package/build/dist/Types/OnCallDutyPolicy/Layer.js +29 -17
  325. package/build/dist/Types/OnCallDutyPolicy/Layer.js.map +1 -1
  326. package/build/dist/Types/Phone.js +5 -3
  327. package/build/dist/Types/Phone.js.map +1 -1
  328. package/build/dist/Types/Workspace/WorkspaceType.js +9 -0
  329. package/build/dist/Types/Workspace/WorkspaceType.js.map +1 -1
  330. package/build/dist/UI/Components/Charts/Utils/DataPoint.js +8 -6
  331. package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
  332. package/build/dist/UI/Components/Detail/Detail.js +4 -1
  333. package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
  334. package/build/dist/UI/Components/FilePicker/FilePicker.js +1 -1
  335. package/build/dist/UI/Components/FilePicker/FilePicker.js.map +1 -1
  336. package/build/dist/UI/Components/Image/Image.js +1 -1
  337. package/build/dist/UI/Components/Image/Image.js.map +1 -1
  338. package/build/dist/UI/Components/JSONTable/JSONTable.js.map +1 -1
  339. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  340. package/build/dist/UI/Components/SideMenu/SideMenu.js +4 -2
  341. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  342. package/build/dist/UI/Components/SideMenu/SideMenuItem.js +62 -38
  343. package/build/dist/UI/Components/SideMenu/SideMenuItem.js.map +1 -1
  344. package/build/dist/UI/Config.js +1 -0
  345. package/build/dist/UI/Config.js.map +1 -1
  346. package/build/dist/UI/Utils/API/API.js +5 -3
  347. package/build/dist/UI/Utils/API/API.js.map +1 -1
  348. package/build/dist/UI/Utils/Countries.js.map +1 -1
  349. package/build/dist/UI/Utils/Login.js +6 -1
  350. package/build/dist/UI/Utils/Login.js.map +1 -1
  351. package/build/dist/Utils/Base64.js +12 -0
  352. package/build/dist/Utils/Base64.js.map +1 -0
  353. package/build/dist/Utils/Schema/ModelSchema.js +4 -2
  354. package/build/dist/Utils/Schema/ModelSchema.js.map +1 -1
  355. package/package.json +5 -1
  356. package/build/dist/Models/DatabaseModels/UserTwoFactorAuth.js.map +0 -1
  357. package/build/dist/Server/API/UserTwoFactorAuthAPI.js.map +0 -1
  358. package/build/dist/Server/Services/UserTwoFactorAuthService.js.map +0 -1
  359. package/build/dist/Server/Utils/TwoFactorAuth.js.map +0 -1
@@ -0,0 +1,1240 @@
1
+ import Express, {
2
+ ExpressRequest,
3
+ ExpressResponse,
4
+ ExpressRouter,
5
+ } from "../Utils/Express";
6
+ import Response from "../Utils/Response";
7
+ import BadRequestException from "../../Types/Exception/BadRequestException";
8
+ import logger from "../Utils/Logger";
9
+ import { JSONObject } from "../../Types/JSON";
10
+ import BadDataException from "../../Types/Exception/BadDataException";
11
+ import Exception from "../../Types/Exception/Exception";
12
+ import {
13
+ AppApiClientUrl,
14
+ AppVersion,
15
+ DashboardClientUrl,
16
+ HomeClientUrl,
17
+ Host,
18
+ MicrosoftTeamsAppClientId,
19
+ MicrosoftTeamsAppClientSecret,
20
+ } from "../EnvironmentConfig";
21
+ import URL from "../../Types/API/URL";
22
+ import HTTPErrorResponse from "../../Types/API/HTTPErrorResponse";
23
+ import HTTPResponse from "../../Types/API/HTTPResponse";
24
+ import API from "../../Utils/API";
25
+ import WorkspaceProjectAuthTokenService from "../Services/WorkspaceProjectAuthTokenService";
26
+ import WorkspaceProjectAuthToken, {
27
+ MicrosoftTeamsMiscData,
28
+ MicrosoftTeamsTeam,
29
+ } from "../../Models/DatabaseModels/WorkspaceProjectAuthToken";
30
+ import ObjectID from "../../Types/ObjectID";
31
+ import WorkspaceUserAuthTokenService from "../Services/WorkspaceUserAuthTokenService";
32
+ import WorkspaceUserAuthToken from "../../Models/DatabaseModels/WorkspaceUserAuthToken";
33
+ import WorkspaceType from "../../Types/Workspace/WorkspaceType";
34
+ import MicrosoftTeamsAuthAction, {
35
+ MicrosoftTeamsRequest,
36
+ } from "../Utils/Workspace/MicrosoftTeams/Actions/Auth";
37
+ import MicrosoftTeamsIncidentActions from "../Utils/Workspace/MicrosoftTeams/Actions/Incident";
38
+ import MicrosoftTeamsAlertActions from "../Utils/Workspace/MicrosoftTeams/Actions/Alert";
39
+ import MicrosoftTeamsScheduledMaintenanceActions from "../Utils/Workspace/MicrosoftTeams/Actions/ScheduledMaintenance";
40
+ import MicrosoftTeamsMonitorActions from "../Utils/Workspace/MicrosoftTeams/Actions/Monitor";
41
+ import MicrosoftTeamsOnCallDutyActions from "../Utils/Workspace/MicrosoftTeams/Actions/OnCallDutyPolicy";
42
+ import MicrosoftTeamsUtil from "../Utils/Workspace/MicrosoftTeams/MicrosoftTeams";
43
+ import archiver, { Archiver } from "archiver";
44
+ import LocalFile from "../Utils/LocalFile";
45
+ import path from "path";
46
+ import UserMiddleware from "../Middleware/UserAuthorization";
47
+ import CommonAPI from "./CommonAPI";
48
+ import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
49
+
50
+ export default class MicrosoftTeamsAPI {
51
+ private static getTeamsAppManifest(): JSONObject {
52
+ if (!MicrosoftTeamsAppClientId) {
53
+ throw new BadDataException("Microsoft Teams App Client ID is not set");
54
+ }
55
+
56
+ const manifest: JSONObject = {
57
+ $schema:
58
+ "https://developer.microsoft.com/json-schemas/teams/v1.23/MicrosoftTeams.schema.json",
59
+ manifestVersion: "1.23",
60
+ version: AppVersion.toLowerCase().includes("unknown")
61
+ ? "1.1.0"
62
+ : AppVersion,
63
+ id: MicrosoftTeamsAppClientId,
64
+ developer: {
65
+ name: "OneUptime",
66
+ websiteUrl: "https://oneuptime.com",
67
+ privacyUrl: "https://oneuptime.com/legal/privacy",
68
+ termsOfUseUrl: "https://oneuptime.com/legal/terms",
69
+ },
70
+ name: {
71
+ short: "OneUptime",
72
+ full: "OneUptime - Complete Observability Platform",
73
+ },
74
+ description: {
75
+ short: "Monitor your apps, websites, APIs, and more with OneUptime",
76
+ full: "OneUptime is a complete open-source observability platform that helps you monitor your applications, websites, APIs, and infrastructure. Get alerted when things go wrong and maintain your SLAs.",
77
+ },
78
+ // Default to size-specific names; route will adjust if fallbacks are used
79
+ icons: {
80
+ outline: "outline.png",
81
+ color: "color.png",
82
+ },
83
+ accentColor: "#000000",
84
+ bots: [
85
+ {
86
+ botId: MicrosoftTeamsAppClientId,
87
+ needsChannelSelector: false,
88
+ isNotificationOnly: false,
89
+ // Include groupChat to align with latest schema capabilities
90
+ scopes: ["team", "personal", "groupChat"],
91
+ supportsFiles: false,
92
+ supportsCalling: false,
93
+ supportsVideo: false,
94
+ // Provide basic command lists to improve client compatibility (esp. mobile)
95
+ commandLists: [],
96
+ },
97
+ ],
98
+ permissions: ["identity", "messageTeamMembers"],
99
+ authorization: {
100
+ permissions: {
101
+ resourceSpecific: [
102
+ {
103
+ type: "Application",
104
+ name: "ChannelMessage.Send.Group",
105
+ },
106
+ {
107
+ type: "Application",
108
+ name: "ChannelMessage.Read.Group",
109
+ },
110
+ {
111
+ type: "Application",
112
+ name: "Channel.Create.Group",
113
+ },
114
+ ],
115
+ },
116
+ },
117
+ validDomains: [Host],
118
+ webApplicationInfo: {
119
+ id: MicrosoftTeamsAppClientId,
120
+ resource: HomeClientUrl.toString(),
121
+ },
122
+ };
123
+
124
+ return manifest;
125
+ }
126
+
127
+ public getRouter(): ExpressRouter {
128
+ const router: ExpressRouter = Express.getRouter();
129
+
130
+ // Teams app manifest ZIP endpoint
131
+ router.get(
132
+ "/microsoft-teams/app-manifest-zip",
133
+ async (req: ExpressRequest, res: ExpressResponse) => {
134
+ try {
135
+ // Validate GUID format – Teams requires GUID for id / botId
136
+ const guidRegex: RegExp =
137
+ /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$/;
138
+ if (!guidRegex.test(MicrosoftTeamsAppClientId || "")) {
139
+ return Response.sendErrorResponse(
140
+ req,
141
+ res,
142
+ new BadDataException(
143
+ "Microsoft Teams App Client ID must be a valid GUID. Update the environment variable.",
144
+ ),
145
+ );
146
+ }
147
+
148
+ // Decide icon files and names included in the package
149
+ let iconColorName: string = "icon-color.png";
150
+ let iconOutlineName: string = "icon-outline.png";
151
+
152
+ // Set response headers for zip download
153
+ res.setHeader("Content-Type", "application/zip");
154
+ res.setHeader(
155
+ "Content-Disposition",
156
+ 'attachment; filename="oneuptime-teams-app.zip"',
157
+ );
158
+
159
+ // Create archive
160
+ const archive: Archiver = archiver("zip", {
161
+ zlib: { level: 9 }, // Sets the compression level
162
+ });
163
+
164
+ // Handle archive errors
165
+ archive.on("error", (err: Error) => {
166
+ logger.error("Archive error: " + err);
167
+ throw err;
168
+ });
169
+
170
+ // Pipe archive data to the response
171
+ archive.pipe(res);
172
+
173
+ const colorPath: string = path.join(
174
+ __dirname,
175
+ "../Images/MicrosoftTeams/color.png",
176
+ );
177
+ const outlinePath: string = path.join(
178
+ __dirname,
179
+ "../Images/MicrosoftTeams/outline.png",
180
+ );
181
+
182
+ let colorIconBuffer: Buffer | null = null;
183
+ let outlineIconBuffer: Buffer | null = null;
184
+
185
+ if (
186
+ (await LocalFile.doesFileExist(colorPath)) &&
187
+ (await LocalFile.doesFileExist(outlinePath))
188
+ ) {
189
+ colorIconBuffer = await LocalFile.readAsBuffer(colorPath);
190
+ outlineIconBuffer = await LocalFile.readAsBuffer(outlinePath);
191
+ iconColorName = "color.png";
192
+ iconOutlineName = "outline.png";
193
+ } else {
194
+ throw new BadDataException(
195
+ "Microsoft Teams icons not found. Expected either pre-sized icon-color-192x192.png and icon-outline-32x32.png in Common/Server/Images/MicrosoftTeams, or fallback color.png and outline.png.",
196
+ );
197
+ }
198
+
199
+ // Build manifest now that icon names are known
200
+ const manifest: JSONObject = MicrosoftTeamsAPI.getTeamsAppManifest();
201
+ (manifest["icons"] as JSONObject)["color"] = iconColorName;
202
+ (manifest["icons"] as JSONObject)["outline"] = iconOutlineName;
203
+
204
+ // Add manifest.json to zip
205
+ archive.append(JSON.stringify(manifest, null, 2), {
206
+ name: "manifest.json",
207
+ });
208
+
209
+ // Add icons to zip under the selected names
210
+ archive.append(colorIconBuffer, { name: iconColorName });
211
+ archive.append(outlineIconBuffer, { name: iconOutlineName });
212
+
213
+ // Finalize the archive
214
+ await archive.finalize();
215
+ } catch (error) {
216
+ logger.error("Error creating Teams app manifest zip: " + error);
217
+ return Response.sendErrorResponse(
218
+ req,
219
+ res,
220
+ new BadDataException("Failed to create Teams app manifest zip"),
221
+ );
222
+ }
223
+ },
224
+ );
225
+
226
+ /*
227
+ * Microsoft Teams OAuth callback endpoint for project integration
228
+ * New (preferred) static redirect URI that uses state param to carry projectId and userId
229
+ * State format: <projectId>:<userId>
230
+ */
231
+ router.get(
232
+ "/microsoft-teams/auth",
233
+ async (req: ExpressRequest, res: ExpressResponse) => {
234
+ if (!MicrosoftTeamsAppClientId) {
235
+ return Response.sendErrorResponse(
236
+ req,
237
+ res,
238
+ new BadDataException("Microsoft Teams App Client ID is not set"),
239
+ );
240
+ }
241
+
242
+ if (!MicrosoftTeamsAppClientSecret) {
243
+ return Response.sendErrorResponse(
244
+ req,
245
+ res,
246
+ new BadDataException(
247
+ "Microsoft Teams App Client Secret is not set",
248
+ ),
249
+ );
250
+ }
251
+
252
+ const error: string | undefined = req.query["error"]?.toString();
253
+ const stateParam: string | undefined = req.query["state"]?.toString();
254
+
255
+ if (!stateParam) {
256
+ return Response.sendErrorResponse(
257
+ req,
258
+ res,
259
+ new BadRequestException(
260
+ "Invalid request - state param not present",
261
+ ),
262
+ );
263
+ }
264
+
265
+ // Expect state in format projectId:userId
266
+ const stateParts: Array<string> = stateParam.split(":");
267
+ if (stateParts.length !== 2 || !stateParts[0] || !stateParts[1]) {
268
+ return Response.sendErrorResponse(
269
+ req,
270
+ res,
271
+ new BadRequestException("Invalid state param"),
272
+ );
273
+ }
274
+
275
+ const projectId: string = stateParts[0]!;
276
+ const userId: string = stateParts[1]!;
277
+
278
+ const teamsIntegrationPageUrl: URL = URL.fromString(
279
+ DashboardClientUrl.toString() +
280
+ `/${projectId.toString()}/settings/microsoft-teams-integration`,
281
+ );
282
+
283
+ if (error) {
284
+ return Response.redirect(
285
+ req,
286
+ res,
287
+ teamsIntegrationPageUrl.addQueryParam("error", error),
288
+ );
289
+ }
290
+
291
+ const code: string | undefined = req.query["code"]?.toString();
292
+
293
+ if (!code) {
294
+ return Response.sendErrorResponse(
295
+ req,
296
+ res,
297
+ new BadRequestException("Invalid request - no authorization code"),
298
+ );
299
+ }
300
+
301
+ try {
302
+ // Exchange code for access token
303
+ const redirectUri: URL = URL.fromString(
304
+ `${AppApiClientUrl.toString()}/microsoft-teams/auth`,
305
+ );
306
+
307
+ const tokenRequestBody: JSONObject = {
308
+ grant_type: "authorization_code",
309
+ code: code,
310
+ client_id: MicrosoftTeamsAppClientId,
311
+ client_secret: MicrosoftTeamsAppClientSecret,
312
+ redirect_uri: redirectUri.toString(),
313
+ scope:
314
+ "https://graph.microsoft.com/User.Read https://graph.microsoft.com/Team.ReadBasic.All https://graph.microsoft.com/Channel.ReadBasic.All https://graph.microsoft.com/ChannelMessage.Send",
315
+ };
316
+
317
+ logger.debug(
318
+ "Microsoft Teams Token Request Body (static redirect): ",
319
+ );
320
+ logger.debug(tokenRequestBody);
321
+
322
+ const tokenResponse: HTTPErrorResponse | HTTPResponse<JSONObject> =
323
+ await API.post<JSONObject>({
324
+ url: URL.fromString(
325
+ "https://login.microsoftonline.com/common/oauth2/v2.0/token",
326
+ ),
327
+ data: tokenRequestBody,
328
+ headers: {
329
+ "Content-Type": "application/x-www-form-urlencoded",
330
+ },
331
+ });
332
+
333
+ if (tokenResponse instanceof HTTPErrorResponse) {
334
+ logger.error("Error getting Teams token:");
335
+ logger.error(tokenResponse);
336
+ throw tokenResponse;
337
+ }
338
+
339
+ const tokenData: JSONObject = tokenResponse.data;
340
+ logger.debug("Microsoft Teams Token Response (static redirect): ");
341
+ logger.debug(tokenData);
342
+
343
+ if (!tokenData["access_token"]) {
344
+ return Response.sendErrorResponse(
345
+ req,
346
+ res,
347
+ new BadRequestException(
348
+ "Failed to get access token from Microsoft Teams",
349
+ ),
350
+ );
351
+ }
352
+
353
+ const accessToken: string = tokenData["access_token"] as string;
354
+
355
+ // Get user profile and team information
356
+ const userProfileResponse:
357
+ | HTTPErrorResponse
358
+ | HTTPResponse<JSONObject> = await API.get<JSONObject>({
359
+ url: URL.fromString("https://graph.microsoft.com/v1.0/me"),
360
+ headers: {
361
+ Authorization: `Bearer ${accessToken}`,
362
+ },
363
+ });
364
+
365
+ if (userProfileResponse instanceof HTTPErrorResponse) {
366
+ logger.error("Error getting user profile:");
367
+ logger.error(userProfileResponse);
368
+ throw userProfileResponse;
369
+ }
370
+
371
+ const userProfile: JSONObject = userProfileResponse.data;
372
+ logger.debug("User Profile: ");
373
+ logger.debug(userProfile);
374
+
375
+ await WorkspaceUserAuthTokenService.refreshAuthToken({
376
+ projectId: new ObjectID(projectId),
377
+ userId: new ObjectID(userId),
378
+ workspaceType: WorkspaceType.MicrosoftTeams,
379
+ authToken: accessToken,
380
+ workspaceUserId: userProfile["id"] as string,
381
+ miscData: {
382
+ userId: userProfile["id"] as string,
383
+ displayName: userProfile["displayName"] as string,
384
+ email:
385
+ (userProfile["mail"] as string) ||
386
+ (userProfile["userPrincipalName"] as string),
387
+ },
388
+ });
389
+
390
+ // Check if admin consent is already granted
391
+ const existingProjectAuth: WorkspaceProjectAuthToken | null =
392
+ await WorkspaceProjectAuthTokenService.getProjectAuth({
393
+ projectId: new ObjectID(projectId),
394
+ workspaceType: WorkspaceType.MicrosoftTeams,
395
+ });
396
+
397
+ if (
398
+ existingProjectAuth &&
399
+ (existingProjectAuth.miscData as any)?.adminConsentGranted
400
+ ) {
401
+ // Admin consent already granted, refresh teams
402
+ await MicrosoftTeamsUtil.refreshTeams({
403
+ projectId: new ObjectID(projectId),
404
+ });
405
+
406
+ return Response.redirect(req, res, teamsIntegrationPageUrl);
407
+ }
408
+ // Need admin consent
409
+ return Response.redirect(
410
+ req,
411
+ res,
412
+ teamsIntegrationPageUrl.addQueryParam("needAdminConsent", "true"),
413
+ );
414
+ } catch (err) {
415
+ logger.error("Error in static Microsoft Teams auth callback: ");
416
+ logger.error(err);
417
+ return Response.sendErrorResponse(
418
+ req,
419
+ res,
420
+ new BadDataException("Failed to authenticate with Microsoft Teams"),
421
+ );
422
+ }
423
+ },
424
+ );
425
+
426
+ // Endpoint to finalize team selection when multiple teams are available.
427
+ router.post(
428
+ "/microsoft-teams/select-team",
429
+ async (req: ExpressRequest, res: ExpressResponse) => {
430
+ try {
431
+ const projectIdString: string | undefined = req.body["projectId"];
432
+ const userIdString: string | undefined = req.body["userId"];
433
+ const teamId: string | undefined = req.body["teamId"];
434
+
435
+ if (!projectIdString || !userIdString || !teamId) {
436
+ return Response.sendErrorResponse(
437
+ req,
438
+ res,
439
+ new BadRequestException(
440
+ "projectId, userId and teamId are required",
441
+ ),
442
+ );
443
+ }
444
+
445
+ const projectId: ObjectID = new ObjectID(projectIdString);
446
+ const userId: ObjectID = new ObjectID(userIdString);
447
+
448
+ // Fetch user auth to get access token and available teams.
449
+ const userAuth: WorkspaceUserAuthToken | null =
450
+ await WorkspaceUserAuthTokenService.getUserAuth({
451
+ projectId: projectId,
452
+ userId: userId,
453
+ workspaceType: WorkspaceType.MicrosoftTeams,
454
+ });
455
+
456
+ if (!userAuth) {
457
+ return Response.sendErrorResponse(
458
+ req,
459
+ res,
460
+ new BadRequestException(
461
+ "User Microsoft Teams auth not found. Please re-authenticate.",
462
+ ),
463
+ );
464
+ }
465
+
466
+ const accessToken: string = userAuth.authToken || "";
467
+ const miscData: any = userAuth.miscData || {};
468
+ const availableTeams: Record<string, MicrosoftTeamsTeam> =
469
+ await MicrosoftTeamsUtil.refreshTeams({
470
+ projectId: projectId,
471
+ });
472
+ const matchedTeam: MicrosoftTeamsTeam | undefined = Object.values(
473
+ availableTeams,
474
+ ).find((t: MicrosoftTeamsTeam) => {
475
+ return t.id === teamId;
476
+ });
477
+
478
+ if (!matchedTeam) {
479
+ return Response.sendErrorResponse(
480
+ req,
481
+ res,
482
+ new BadRequestException(
483
+ "Selected teamId is not in availableTeams list",
484
+ ),
485
+ );
486
+ }
487
+
488
+ // Decode JWT to get tenant ID
489
+ const tokenParts: Array<string> = accessToken.split(".");
490
+ if (tokenParts.length !== 3) {
491
+ return Response.sendErrorResponse(
492
+ req,
493
+ res,
494
+ new BadRequestException("Invalid JWT token"),
495
+ );
496
+ }
497
+ const payload: JSONObject = JSON.parse(
498
+ Buffer.from(tokenParts[1]!, "base64").toString("utf-8"),
499
+ );
500
+ const tenantId: string = payload["tid"] as string;
501
+
502
+ if (!tenantId) {
503
+ return Response.sendErrorResponse(
504
+ req,
505
+ res,
506
+ new BadRequestException("Tenant ID not found in token"),
507
+ );
508
+ }
509
+
510
+ /*
511
+ * Persist project auth now that team is selected.
512
+ * IMPORTANT: Do NOT overwrite project-level auth token (admin-consent app token)
513
+ * with the user delegated token. Preserve existing project auth token if present.
514
+ */
515
+ const existingProjectAuth: WorkspaceProjectAuthToken | null =
516
+ await WorkspaceProjectAuthTokenService.getProjectAuth({
517
+ projectId: projectId,
518
+ workspaceType: WorkspaceType.MicrosoftTeams,
519
+ });
520
+
521
+ const projectAuthTokenToPersist: string =
522
+ existingProjectAuth?.authToken || "";
523
+
524
+ // Merge miscData while updating team selection details
525
+ const mergedProjectMiscData: MicrosoftTeamsMiscData = {
526
+ ...(existingProjectAuth?.miscData as any),
527
+ tenantId: tenantId,
528
+ teamId: teamId,
529
+ teamName: matchedTeam.name,
530
+ botId: MicrosoftTeamsAppClientId || "",
531
+ };
532
+
533
+ await WorkspaceProjectAuthTokenService.refreshAuthToken({
534
+ projectId: projectId,
535
+ workspaceType: WorkspaceType.MicrosoftTeams,
536
+ authToken: projectAuthTokenToPersist,
537
+ workspaceProjectId: tenantId, // Use tenant ID as the workspace project identifier
538
+ miscData: mergedProjectMiscData,
539
+ });
540
+
541
+ // Update user token to remove availableTeams (cleanup) and store selected team info
542
+ await WorkspaceUserAuthTokenService.refreshAuthToken({
543
+ projectId: projectId,
544
+ userId: userId,
545
+ workspaceType: WorkspaceType.MicrosoftTeams,
546
+ authToken: accessToken,
547
+ workspaceUserId: miscData.userId || "",
548
+ miscData: {
549
+ userId: miscData.userId,
550
+ displayName: miscData.displayName,
551
+ email: miscData.email,
552
+ teamId: teamId,
553
+ teamName: matchedTeam.name,
554
+ },
555
+ });
556
+
557
+ return Response.sendJsonObjectResponse(req, res, {
558
+ success: true,
559
+ });
560
+ } catch (err) {
561
+ logger.error("Error selecting Microsoft Teams team: ");
562
+ logger.error(err);
563
+ return Response.sendErrorResponse(
564
+ req,
565
+ res,
566
+ new BadDataException("Failed to select Microsoft Teams team"),
567
+ );
568
+ }
569
+ },
570
+ );
571
+
572
+ /*
573
+ * Admin consent - start flow (tenant-wide admin consent)
574
+ * Uses state in the same format as OAuth: <projectId>:<userId>
575
+ */
576
+ router.get(
577
+ "/microsoft-teams/admin-consent",
578
+ async (req: ExpressRequest, res: ExpressResponse) => {
579
+ try {
580
+ if (!MicrosoftTeamsAppClientId) {
581
+ return Response.sendErrorResponse(
582
+ req,
583
+ res,
584
+ new BadDataException("Microsoft Teams App Client ID is not set"),
585
+ );
586
+ }
587
+
588
+ const stateParam: string | undefined = req.query["state"]?.toString();
589
+ if (!stateParam) {
590
+ return Response.sendErrorResponse(
591
+ req,
592
+ res,
593
+ new BadRequestException(
594
+ "Invalid request - state param not present",
595
+ ),
596
+ );
597
+ }
598
+
599
+ const stateParts: Array<string> = stateParam.split(":");
600
+ if (stateParts.length !== 2 || !stateParts[0] || !stateParts[1]) {
601
+ return Response.sendErrorResponse(
602
+ req,
603
+ res,
604
+ new BadRequestException("Invalid state param"),
605
+ );
606
+ }
607
+
608
+ const projectId: string = stateParts[0]!;
609
+ // Try to use tenant from existing project auth, otherwise default to "organizations"
610
+ let tenantForConsent: string = "organizations";
611
+ try {
612
+ const existingAuth: WorkspaceProjectAuthToken | null =
613
+ await WorkspaceProjectAuthTokenService.getProjectAuth({
614
+ projectId: new ObjectID(projectId),
615
+ workspaceType: WorkspaceType.MicrosoftTeams,
616
+ });
617
+ const existingTenant: string | undefined = (
618
+ existingAuth?.miscData as any
619
+ )?.tenantId;
620
+ if (existingTenant) {
621
+ tenantForConsent = existingTenant;
622
+ }
623
+ } catch {
624
+ // ignore and fall back to default
625
+ }
626
+
627
+ const redirectUri: URL = URL.fromString(
628
+ `${AppApiClientUrl.toString()}/microsoft-teams/admin-consent/callback`,
629
+ );
630
+
631
+ const adminConsentUrl: string = `https://login.microsoftonline.com/${encodeURIComponent(
632
+ tenantForConsent,
633
+ )}/v2.0/adminconsent?client_id=${encodeURIComponent(
634
+ MicrosoftTeamsAppClientId,
635
+ )}&scope=${encodeURIComponent(
636
+ "https://graph.microsoft.com/.default",
637
+ )}&redirect_uri=${encodeURIComponent(redirectUri.toString())}&state=${encodeURIComponent(
638
+ stateParam,
639
+ )}`;
640
+
641
+ return Response.redirect(req, res, URL.fromString(adminConsentUrl));
642
+ } catch (error) {
643
+ logger.error("Error starting Teams admin consent: ");
644
+ logger.error(error);
645
+ return Response.sendErrorResponse(
646
+ req,
647
+ res,
648
+ new BadDataException(
649
+ "Failed to start Microsoft Teams admin consent",
650
+ ),
651
+ );
652
+ }
653
+ },
654
+ );
655
+
656
+ /*
657
+ * Admin consent - callback handler
658
+ * Receives: state=<projectId>:<userId>, tenant=<tenantId>, admin_consent=True | error params
659
+ */
660
+ router.get(
661
+ "/microsoft-teams/admin-consent/callback",
662
+ async (req: ExpressRequest, res: ExpressResponse) => {
663
+ try {
664
+ const error: string | undefined = req.query["error"]?.toString();
665
+ const errorDescription: string | undefined =
666
+ req.query["error_description"]?.toString();
667
+ const stateParam: string | undefined = req.query["state"]?.toString();
668
+ const tenantId: string | undefined = req.query["tenant"]?.toString();
669
+
670
+ if (!stateParam) {
671
+ return Response.sendErrorResponse(
672
+ req,
673
+ res,
674
+ new BadRequestException(
675
+ "Invalid request - state param not present",
676
+ ),
677
+ );
678
+ }
679
+
680
+ const stateParts: Array<string> = stateParam.split(":");
681
+ if (stateParts.length !== 2 || !stateParts[0] || !stateParts[1]) {
682
+ return Response.sendErrorResponse(
683
+ req,
684
+ res,
685
+ new BadRequestException("Invalid state param"),
686
+ );
687
+ }
688
+
689
+ const projectId: string = stateParts[0]!;
690
+
691
+ const teamsIntegrationPageUrl: URL = URL.fromString(
692
+ DashboardClientUrl.toString() +
693
+ `/${projectId.toString()}/settings/microsoft-teams-integration`,
694
+ );
695
+
696
+ if (error) {
697
+ return Response.redirect(
698
+ req,
699
+ res,
700
+ teamsIntegrationPageUrl.addQueryParam(
701
+ "error",
702
+ `${error}${errorDescription ? ": " + errorDescription : ""}`,
703
+ ),
704
+ );
705
+ }
706
+
707
+ if (!tenantId) {
708
+ return Response.redirect(
709
+ req,
710
+ res,
711
+ teamsIntegrationPageUrl.addQueryParam(
712
+ "error",
713
+ "Missing tenant information from admin consent callback",
714
+ ),
715
+ );
716
+ }
717
+
718
+ if (!MicrosoftTeamsAppClientId || !MicrosoftTeamsAppClientSecret) {
719
+ return Response.redirect(
720
+ req,
721
+ res,
722
+ teamsIntegrationPageUrl.addQueryParam(
723
+ "error",
724
+ "Microsoft Teams App credentials are not configured",
725
+ ),
726
+ );
727
+ }
728
+
729
+ // Fetch any existing project auth to merge
730
+ const existingAuth: WorkspaceProjectAuthToken | null =
731
+ await WorkspaceProjectAuthTokenService.getProjectAuth({
732
+ projectId: new ObjectID(projectId),
733
+ workspaceType: WorkspaceType.MicrosoftTeams,
734
+ });
735
+
736
+ // Acquire an application token for the specific tenant using client credentials
737
+ const tokenResp: HTTPErrorResponse | HTTPResponse<JSONObject> =
738
+ await API.post<JSONObject>({
739
+ url: URL.fromString(
740
+ `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
741
+ ),
742
+ data: {
743
+ client_id: MicrosoftTeamsAppClientId,
744
+ client_secret: MicrosoftTeamsAppClientSecret,
745
+ grant_type: "client_credentials",
746
+ scope: "https://graph.microsoft.com/.default",
747
+ },
748
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
749
+ });
750
+
751
+ if (tokenResp instanceof HTTPErrorResponse) {
752
+ logger.error("Error getting app token after admin consent:");
753
+ logger.error(tokenResp);
754
+ return Response.redirect(
755
+ req,
756
+ res,
757
+ teamsIntegrationPageUrl.addQueryParam(
758
+ "error",
759
+ "Failed to get Graph app token after admin consent",
760
+ ),
761
+ );
762
+ }
763
+
764
+ const tokenData: JSONObject = tokenResp.data;
765
+ const appAccessToken: string =
766
+ (tokenData["access_token"] as string) || "";
767
+ const expiresInSec: number = Number(tokenData["expires_in"] || 0);
768
+ const expiresAtIso: string = new Date(
769
+ Date.now() + Math.max(0, (expiresInSec - 60) * 1000),
770
+ ).toISOString();
771
+
772
+ logger.debug("App Access Token acquired via admin consent: ");
773
+ logger.debug(tokenData);
774
+
775
+ // Get available teams from user auth token
776
+ const userId: string = stateParts[1]!;
777
+ const userAuth: WorkspaceUserAuthToken | null =
778
+ await WorkspaceUserAuthTokenService.getUserAuth({
779
+ projectId: new ObjectID(projectId),
780
+ userId: new ObjectID(userId),
781
+ workspaceType: WorkspaceType.MicrosoftTeams,
782
+ });
783
+
784
+ let availableTeams: Record<string, MicrosoftTeamsTeam> = {};
785
+ if (userAuth?.miscData) {
786
+ availableTeams = (userAuth.miscData as any).availableTeams || {};
787
+ }
788
+
789
+ // If no teams from user auth, try to get them using app token
790
+ if (Object.keys(availableTeams).length === 0) {
791
+ try {
792
+ const teamsResponse:
793
+ | HTTPErrorResponse
794
+ | HTTPResponse<JSONObject> = await API.get<JSONObject>({
795
+ url: URL.fromString(
796
+ "https://graph.microsoft.com/v1.0/teams?$select=id,displayName",
797
+ ),
798
+ headers: {
799
+ Authorization: `Bearer ${appAccessToken}`,
800
+ },
801
+ });
802
+
803
+ if (teamsResponse instanceof HTTPErrorResponse) {
804
+ logger.error("Failed to get teams:");
805
+ logger.error(teamsResponse);
806
+ return Response.redirect(
807
+ req,
808
+ res,
809
+ teamsIntegrationPageUrl.addQueryParam(
810
+ "error",
811
+ "Failed to retrieve teams from Microsoft Graph API after admin consent",
812
+ ),
813
+ );
814
+ }
815
+
816
+ const teamsData: JSONObject = teamsResponse.data;
817
+ const teams: Array<JSONObject> =
818
+ (teamsData["value"] as Array<JSONObject>) || [];
819
+
820
+ if (teams.length === 0) {
821
+ return Response.redirect(
822
+ req,
823
+ res,
824
+ teamsIntegrationPageUrl.addQueryParam(
825
+ "error",
826
+ "No teams available in your Microsoft 365 tenant. Please create a team first.",
827
+ ),
828
+ );
829
+ }
830
+
831
+ availableTeams = teams.reduce(
832
+ (acc: Record<string, MicrosoftTeamsTeam>, t: JSONObject) => {
833
+ const team: MicrosoftTeamsTeam = {
834
+ id: t["id"] as string,
835
+ name: (t["displayName"] as string) || "Unnamed Team",
836
+ };
837
+ acc[team.name] = team;
838
+ return acc;
839
+ },
840
+ {} as Record<string, MicrosoftTeamsTeam>,
841
+ );
842
+ } catch (error) {
843
+ logger.error("Error getting teams:");
844
+ logger.error(error);
845
+ return Response.redirect(
846
+ req,
847
+ res,
848
+ teamsIntegrationPageUrl.addQueryParam(
849
+ "error",
850
+ "Failed to retrieve teams from Microsoft Graph API",
851
+ ),
852
+ );
853
+ }
854
+ }
855
+
856
+ // Merge and persist project auth with tenantId, app token, and available teams
857
+ const mergedMiscData: MicrosoftTeamsMiscData = {
858
+ ...(existingAuth?.miscData as any),
859
+ tenantId: tenantId,
860
+ appAccessToken: appAccessToken,
861
+ appAccessTokenExpiresAt: expiresAtIso,
862
+ adminConsentGranted: true,
863
+ adminConsentGrantedAt: new Date().toISOString(),
864
+ availableTeams: availableTeams,
865
+ botId: MicrosoftTeamsAppClientId || "",
866
+ };
867
+
868
+ await WorkspaceProjectAuthTokenService.refreshAuthToken({
869
+ projectId: new ObjectID(projectId),
870
+ workspaceType: WorkspaceType.MicrosoftTeams,
871
+ authToken: appAccessToken,
872
+ workspaceProjectId: tenantId, // Use tenant ID as the workspace project identifier
873
+ miscData: mergedMiscData,
874
+ });
875
+
876
+ return Response.redirect(
877
+ req,
878
+ res,
879
+ teamsIntegrationPageUrl
880
+ .addQueryParam("adminConsent", "success")
881
+ .addQueryParam("tenantId", tenantId),
882
+ );
883
+ } catch (err) {
884
+ logger.error("Error in Microsoft Teams admin consent callback: ");
885
+ logger.error(err);
886
+ // Best-effort redirect to integration page with error
887
+ try {
888
+ const stateParam: string | undefined =
889
+ req.query["state"]?.toString();
890
+ const projectId: string | undefined = stateParam?.split(":")[0];
891
+ if (projectId) {
892
+ const teamsIntegrationPageUrl: URL = URL.fromString(
893
+ DashboardClientUrl.toString() +
894
+ `/${projectId.toString()}/settings/microsoft-teams-integration`,
895
+ );
896
+ return Response.redirect(
897
+ req,
898
+ res,
899
+ teamsIntegrationPageUrl.addQueryParam(
900
+ "error",
901
+ "Failed to finalize Microsoft Teams admin consent",
902
+ ),
903
+ );
904
+ }
905
+ } catch {
906
+ // ignore
907
+ }
908
+ return Response.sendErrorResponse(
909
+ req,
910
+ res,
911
+ new BadDataException(
912
+ "Failed to finalize Microsoft Teams admin consent",
913
+ ),
914
+ );
915
+ }
916
+ },
917
+ );
918
+
919
+ /*
920
+ * Microsoft Bot Framework endpoint - this is what Teams calls for bot messages
921
+ * Now uses the Bot Framework SDK's adapter.processActivity for proper protocol handling
922
+ */
923
+ router.post(
924
+ "/microsoft-bot/messages",
925
+ async (req: ExpressRequest, res: ExpressResponse) => {
926
+ try {
927
+ // Delegate to MicrosoftTeamsUtil which uses the Bot Framework SDK
928
+ await MicrosoftTeamsUtil.processBotActivity(req, res);
929
+ } catch (error) {
930
+ logger.error("Error in Bot Framework endpoint: " + error);
931
+ if (!res.headersSent) {
932
+ Response.sendJsonObjectResponse(req, res, {
933
+ error: "Internal server error",
934
+ });
935
+ }
936
+ }
937
+ },
938
+ );
939
+
940
+ // Microsoft Teams webhook endpoint for interactive messages (legacy)
941
+ router.post(
942
+ "/microsoft-teams/webhook",
943
+ async (req: ExpressRequest, res: ExpressResponse) => {
944
+ logger.debug("Microsoft Teams Webhook Request: ");
945
+ logger.debug(req.body);
946
+
947
+ try {
948
+ const authResult: MicrosoftTeamsRequest =
949
+ await MicrosoftTeamsAuthAction.isAuthorized({
950
+ req: req,
951
+ });
952
+
953
+ logger.debug("Microsoft Teams Auth Result: ");
954
+ logger.debug(authResult);
955
+
956
+ if (authResult.isAuthorized === false) {
957
+ return Response.sendTextResponse(req, res, "");
958
+ }
959
+
960
+ // Handle different types of Teams activities
961
+ const activity: JSONObject = req.body as JSONObject;
962
+ const activityType: string = activity["type"] as string;
963
+
964
+ if (activityType === "message") {
965
+ // Handle bot mentions or direct messages
966
+ return MicrosoftTeamsAPI.handleMessageActivity(
967
+ req,
968
+ res,
969
+ authResult,
970
+ activity,
971
+ );
972
+ } else if (activityType === "invoke") {
973
+ // Handle adaptive card actions
974
+ return MicrosoftTeamsAPI.handleInvokeActivity(
975
+ req,
976
+ res,
977
+ authResult,
978
+ activity,
979
+ );
980
+ }
981
+
982
+ return Response.sendTextResponse(req, res, "");
983
+ } catch (error) {
984
+ logger.error("Error processing Teams webhook:");
985
+ logger.error(error);
986
+ return Response.sendTextResponse(req, res, "");
987
+ }
988
+ },
989
+ );
990
+
991
+ // Test endpoint to verify Bot Framework setup
992
+ router.get(
993
+ "/microsoft-bot/test",
994
+ async (req: ExpressRequest, res: ExpressResponse) => {
995
+ if (!MicrosoftTeamsAppClientId) {
996
+ return Response.sendJsonObjectResponse(req, res, {
997
+ error: "Microsoft Teams App Client ID not configured",
998
+ });
999
+ }
1000
+
1001
+ if (!MicrosoftTeamsAppClientSecret) {
1002
+ return Response.sendJsonObjectResponse(req, res, {
1003
+ error: "Microsoft Teams App Client Secret not configured",
1004
+ });
1005
+ }
1006
+
1007
+ return Response.sendJsonObjectResponse(req, res, {
1008
+ status: "Bot Framework endpoint is configured",
1009
+ clientId: MicrosoftTeamsAppClientId,
1010
+ messagingEndpoint: `${AppApiClientUrl.toString()}/microsoft-bot/messages`,
1011
+ });
1012
+ },
1013
+ );
1014
+
1015
+ // Connector configuration endpoint
1016
+ router.get(
1017
+ "/microsoft-teams/connector-config",
1018
+ (_req: ExpressRequest, res: ExpressResponse) => {
1019
+ // This endpoint provides configuration UI for Teams connectors
1020
+ const html: string = `
1021
+ <!DOCTYPE html>
1022
+ <html>
1023
+ <head>
1024
+ <title>OneUptime Teams Connector</title>
1025
+ <script src="https://statics.teams.cdn.office.net/sdk/v1.11.0/js/MicrosoftTeams.min.js"></script>
1026
+ </head>
1027
+ <body>
1028
+ <h1>OneUptime Teams Connector Setup</h1>
1029
+ <p>Configure OneUptime notifications for your team.</p>
1030
+ <button onclick="saveConfiguration()">Save Configuration</button>
1031
+
1032
+ <script>
1033
+ microsoftTeams.initialize();
1034
+
1035
+ function saveConfiguration() {
1036
+ microsoftTeams.settings.setSettings({
1037
+ entityId: "oneuptime-connector",
1038
+ contentUrl: "https://oneuptime.com",
1039
+ suggestedDisplayName: "OneUptime Notifications"
1040
+ });
1041
+ microsoftTeams.settings.setValidityState(true);
1042
+ }
1043
+
1044
+ microsoftTeams.settings.registerOnSaveHandler((saveEvent) => {
1045
+ // Handle save configuration
1046
+ saveEvent.notifySuccess();
1047
+ });
1048
+ </script>
1049
+ </body>
1050
+ </html>`;
1051
+
1052
+ res.setHeader("Content-Type", "text/html");
1053
+ return res.send(html);
1054
+ },
1055
+ );
1056
+
1057
+ // Get available teams for a project
1058
+ router.get(
1059
+ "/microsoft-teams/teams",
1060
+ UserMiddleware.getUserMiddleware,
1061
+ async (req: ExpressRequest, res: ExpressResponse) => {
1062
+ try {
1063
+ const databaseProps: DatabaseCommonInteractionProps =
1064
+ await CommonAPI.getDatabaseCommonInteractionProps(req);
1065
+
1066
+ const projectId: ObjectID = databaseProps.tenantId!;
1067
+
1068
+ // Use the refreshTeams method to get fresh teams data
1069
+ const availableTeams: Record<string, MicrosoftTeamsTeam> =
1070
+ await MicrosoftTeamsUtil.refreshTeams({
1071
+ projectId: projectId,
1072
+ });
1073
+
1074
+ return Response.sendJsonObjectResponse(req, res, {
1075
+ teams: Object.values(availableTeams).map(
1076
+ (team: MicrosoftTeamsTeam) => {
1077
+ return {
1078
+ id: team.id,
1079
+ name: team.name,
1080
+ };
1081
+ },
1082
+ ),
1083
+ });
1084
+ } catch (err) {
1085
+ return Response.sendErrorResponse(req, res, err as Exception);
1086
+ }
1087
+ },
1088
+ );
1089
+
1090
+ // Endpoint to refresh teams list
1091
+ router.post(
1092
+ "/microsoft-teams/refresh-teams",
1093
+ UserMiddleware.getUserMiddleware,
1094
+ async (req: ExpressRequest, res: ExpressResponse) => {
1095
+ try {
1096
+ const databaseProps: DatabaseCommonInteractionProps =
1097
+ await CommonAPI.getDatabaseCommonInteractionProps(req);
1098
+
1099
+ const projectId: ObjectID = databaseProps.tenantId!;
1100
+
1101
+ // Call MicrosoftTeamsUtil to refresh teams
1102
+ const availableTeams: Record<string, MicrosoftTeamsTeam> =
1103
+ await MicrosoftTeamsUtil.refreshTeams({
1104
+ projectId: projectId,
1105
+ });
1106
+
1107
+ return Response.sendJsonObjectResponse(req, res, {
1108
+ teams: Object.values(availableTeams).map(
1109
+ (team: MicrosoftTeamsTeam) => {
1110
+ return {
1111
+ id: team.id,
1112
+ name: team.name,
1113
+ };
1114
+ },
1115
+ ),
1116
+ });
1117
+ } catch (err) {
1118
+ return Response.sendErrorResponse(req, res, err as Exception);
1119
+ }
1120
+ },
1121
+ );
1122
+
1123
+ return router;
1124
+ }
1125
+
1126
+ private static async handleMessageActivity(
1127
+ _req: ExpressRequest,
1128
+ res: ExpressResponse,
1129
+ authResult: MicrosoftTeamsRequest,
1130
+ activity: JSONObject,
1131
+ ): Promise<void> {
1132
+ // Handle direct messages to bot or @mentions
1133
+ const messageText: string = (activity["text"] as string) || "";
1134
+ const from: JSONObject = activity["from"] as JSONObject;
1135
+
1136
+ if (messageText.toLowerCase().includes("help")) {
1137
+ // Send help message
1138
+ const helpMessage: any = {
1139
+ _type: "WorkspacePayloadText",
1140
+ 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.",
1141
+ };
1142
+
1143
+ await MicrosoftTeamsUtil.sendDirectMessageToUser({
1144
+ authToken: authResult.authToken,
1145
+ workspaceUserId: from["id"] as string,
1146
+ messageBlocks: [helpMessage],
1147
+ });
1148
+ }
1149
+
1150
+ Response.sendTextResponse(_req, res, "");
1151
+ }
1152
+
1153
+ private static async handleInvokeActivity(
1154
+ req: ExpressRequest,
1155
+ res: ExpressResponse,
1156
+ authResult: MicrosoftTeamsRequest,
1157
+ _activity: JSONObject,
1158
+ ): Promise<void> {
1159
+ /*
1160
+ * Handle adaptive card button clicks
1161
+ * const value: JSONObject = activity["value"] as JSONObject;
1162
+ * const actionType: string = value["action"] as string;
1163
+ */
1164
+
1165
+ for (const action of authResult.actions || []) {
1166
+ if (!action.actionType) {
1167
+ continue;
1168
+ }
1169
+
1170
+ if (
1171
+ MicrosoftTeamsIncidentActions.isIncidentAction({
1172
+ actionType: action.actionType,
1173
+ })
1174
+ ) {
1175
+ return MicrosoftTeamsIncidentActions.handleIncidentAction({
1176
+ teamsRequest: authResult,
1177
+ action: action,
1178
+ req: req,
1179
+ res: res,
1180
+ });
1181
+ }
1182
+
1183
+ if (
1184
+ MicrosoftTeamsAlertActions.isAlertAction({
1185
+ actionType: action.actionType,
1186
+ })
1187
+ ) {
1188
+ return MicrosoftTeamsAlertActions.handleAlertAction({
1189
+ teamsRequest: authResult,
1190
+ action: action,
1191
+ req: req,
1192
+ res: res,
1193
+ });
1194
+ }
1195
+
1196
+ if (
1197
+ MicrosoftTeamsScheduledMaintenanceActions.isScheduledMaintenanceAction({
1198
+ actionType: action.actionType,
1199
+ })
1200
+ ) {
1201
+ return MicrosoftTeamsScheduledMaintenanceActions.handleScheduledMaintenanceAction(
1202
+ {
1203
+ teamsRequest: authResult,
1204
+ action: action,
1205
+ req: req,
1206
+ res: res,
1207
+ },
1208
+ );
1209
+ }
1210
+
1211
+ if (
1212
+ MicrosoftTeamsMonitorActions.isMonitorAction({
1213
+ actionType: action.actionType,
1214
+ })
1215
+ ) {
1216
+ return MicrosoftTeamsMonitorActions.handleMonitorAction({
1217
+ teamsRequest: authResult,
1218
+ action: action,
1219
+ req: req,
1220
+ res: res,
1221
+ });
1222
+ }
1223
+
1224
+ if (
1225
+ MicrosoftTeamsOnCallDutyActions.isOnCallDutyAction({
1226
+ actionType: action.actionType,
1227
+ })
1228
+ ) {
1229
+ return MicrosoftTeamsOnCallDutyActions.handleOnCallDutyAction({
1230
+ teamsRequest: authResult,
1231
+ action: action,
1232
+ req: req,
1233
+ res: res,
1234
+ });
1235
+ }
1236
+ }
1237
+
1238
+ Response.sendTextResponse(req, res, "");
1239
+ }
1240
+ }