@oneuptime/common 7.0.3621 → 7.0.3652

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 (152) hide show
  1. package/Models/DatabaseModels/Alert.ts +29 -0
  2. package/Models/DatabaseModels/Index.ts +10 -0
  3. package/Models/DatabaseModels/ScheduledMaintenance.ts +29 -0
  4. package/Models/DatabaseModels/WorkspaceNotificationRule.ts +461 -0
  5. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +379 -0
  6. package/Models/DatabaseModels/WorkspaceSetting.ts +230 -0
  7. package/Models/DatabaseModels/WorkspaceUserAuthToken.ts +312 -0
  8. package/Server/API/SlackAPI.ts +368 -0
  9. package/Server/EnvironmentConfig.ts +10 -0
  10. package/Server/Infrastructure/Postgres/SchemaMigrations/1739209832500-MigrationName.ts +147 -0
  11. package/Server/Infrastructure/Postgres/SchemaMigrations/1739210586538-MigrationName.ts +19 -0
  12. package/Server/Infrastructure/Postgres/SchemaMigrations/1739217257089-MigrationName.ts +23 -0
  13. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  14. package/Server/Middleware/SlackAuthorization.ts +60 -0
  15. package/Server/Services/AlertService.ts +38 -2
  16. package/Server/Services/Index.ts +9 -0
  17. package/Server/Services/ProjectService.ts +3 -1
  18. package/Server/Services/ScheduledMaintenanceService.ts +39 -2
  19. package/Server/Services/UserService.ts +1 -1
  20. package/Server/Services/WorkspaceCommunicationTools/Slack.ts +0 -0
  21. package/Server/Services/WorkspaceCommunicationTools/Teams.ts +0 -0
  22. package/Server/Services/WorkspaceCommunicationTools/WorkspaceCommunicationBaseService.ts +0 -0
  23. package/Server/Services/WorkspaceNotificationRuleService.ts +22 -0
  24. package/Server/Services/WorkspaceProjectAuthTokenService.ts +84 -0
  25. package/Server/Services/WorkspaceSettingService.ts +78 -0
  26. package/Server/Services/WorkspaceUserAuthTokenService.ts +89 -0
  27. package/Server/Types/Workflow/Components/Slack/SendMessageToChannel.ts +1 -1
  28. package/Server/Utils/Express.ts +2 -1
  29. package/Server/Utils/Monitor/MonitorResource.ts +2 -5
  30. package/Server/Utils/{Slack.ts → Slack/Slack.ts} +40 -0
  31. package/Server/Utils/Slack/app-manifest-temp.json +198 -0
  32. package/Server/Utils/Slack/app-manifest.json +67 -0
  33. package/Server/Utils/StartServer.ts +2 -1
  34. package/Types/Filter/FilterCondition.ts +2 -2
  35. package/Types/Monitor/CriteriaFilter.ts +31 -5
  36. package/Types/Monitor/MonitorCriteriaInstance.ts +40 -10
  37. package/Types/Monitor/MonitorStep.ts +1 -1
  38. package/Types/Permission.ts +34 -0
  39. package/Types/Workspace/NotificationRules/BaseNotificationRule.ts +3 -0
  40. package/Types/Workspace/NotificationRules/EventType.ts +8 -0
  41. package/Types/Workspace/NotificationRules/NotificationRuleCondition.ts +355 -0
  42. package/Types/Workspace/NotificationRules/SlackNotificationRule.ts +19 -0
  43. package/Types/Workspace/WorkspaceNotificationPayload.ts +3 -0
  44. package/Types/Workspace/WorkspaceType.ts +6 -0
  45. package/UI/Components/Forms/BasicForm.tsx +9 -0
  46. package/UI/Components/Forms/Fields/FormField.tsx +36 -1
  47. package/UI/Components/Forms/Types/Field.ts +2 -0
  48. package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -1
  49. package/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.ts +2 -2
  50. package/UI/Components/Icon/Icon.tsx +18 -5
  51. package/UI/Components/Radio/Radio.tsx +12 -10
  52. package/UI/Config.ts +3 -0
  53. package/build/dist/Models/DatabaseModels/Alert.js +31 -0
  54. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  55. package/build/dist/Models/DatabaseModels/Index.js +8 -0
  56. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  57. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +31 -0
  58. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  59. package/build/dist/Models/DatabaseModels/WorkspaceNotificationRule.js +481 -0
  60. package/build/dist/Models/DatabaseModels/WorkspaceNotificationRule.js.map +1 -0
  61. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js +390 -0
  62. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -0
  63. package/build/dist/Models/DatabaseModels/WorkspaceSetting.js +236 -0
  64. package/build/dist/Models/DatabaseModels/WorkspaceSetting.js.map +1 -0
  65. package/build/dist/Models/DatabaseModels/WorkspaceUserAuthToken.js +326 -0
  66. package/build/dist/Models/DatabaseModels/WorkspaceUserAuthToken.js.map +1 -0
  67. package/build/dist/Server/API/SlackAPI.js +237 -0
  68. package/build/dist/Server/API/SlackAPI.js.map +1 -0
  69. package/build/dist/Server/EnvironmentConfig.js +5 -1
  70. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  71. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1739209832500-MigrationName.js +58 -0
  72. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1739209832500-MigrationName.js.map +1 -0
  73. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1739210586538-MigrationName.js +14 -0
  74. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1739210586538-MigrationName.js.map +1 -0
  75. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1739217257089-MigrationName.js +14 -0
  76. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1739217257089-MigrationName.js.map +1 -0
  77. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  78. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  79. package/build/dist/Server/Middleware/SlackAuthorization.js +28 -0
  80. package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -0
  81. package/build/dist/Server/Services/AlertService.js +31 -5
  82. package/build/dist/Server/Services/AlertService.js.map +1 -1
  83. package/build/dist/Server/Services/Index.js +8 -0
  84. package/build/dist/Server/Services/Index.js.map +1 -1
  85. package/build/dist/Server/Services/ProjectService.js +2 -1
  86. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  87. package/build/dist/Server/Services/ScheduledMaintenanceService.js +30 -3
  88. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  89. package/build/dist/Server/Services/UserService.js +1 -1
  90. package/build/dist/Server/Services/UserService.js.map +1 -1
  91. package/build/dist/Server/Services/WorkspaceCommunicationTools/Slack.js +2 -0
  92. package/build/dist/Server/Services/WorkspaceCommunicationTools/Slack.js.map +1 -0
  93. package/build/dist/Server/Services/WorkspaceCommunicationTools/Teams.js +2 -0
  94. package/build/dist/Server/Services/WorkspaceCommunicationTools/Teams.js.map +1 -0
  95. package/build/dist/Server/Services/WorkspaceCommunicationTools/WorkspaceCommunicationBaseService.js +2 -0
  96. package/build/dist/Server/Services/WorkspaceCommunicationTools/WorkspaceCommunicationBaseService.js.map +1 -0
  97. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +14 -0
  98. package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -0
  99. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +63 -0
  100. package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -0
  101. package/build/dist/Server/Services/WorkspaceSettingService.js +59 -0
  102. package/build/dist/Server/Services/WorkspaceSettingService.js.map +1 -0
  103. package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js +66 -0
  104. package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js.map +1 -0
  105. package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js +1 -1
  106. package/build/dist/Server/Types/Workflow/Components/Slack/SendMessageToChannel.js.map +1 -1
  107. package/build/dist/Server/Utils/Express.js.map +1 -1
  108. package/build/dist/Server/Utils/Monitor/MonitorResource.js +2 -1
  109. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  110. package/build/dist/Server/Utils/Slack/Slack.js +49 -0
  111. package/build/dist/Server/Utils/Slack/Slack.js.map +1 -0
  112. package/build/dist/Server/Utils/Slack/app-manifest.json +67 -0
  113. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  114. package/build/dist/Types/Filter/FilterCondition.js +2 -2
  115. package/build/dist/Types/Filter/FilterCondition.js.map +1 -1
  116. package/build/dist/Types/Monitor/CriteriaFilter.js +19 -5
  117. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  118. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +33 -10
  119. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  120. package/build/dist/Types/Monitor/MonitorStep.js +1 -1
  121. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  122. package/build/dist/Types/Permission.js +32 -0
  123. package/build/dist/Types/Permission.js.map +1 -1
  124. package/build/dist/Types/Workspace/NotificationRules/BaseNotificationRule.js +2 -0
  125. package/build/dist/Types/Workspace/NotificationRules/BaseNotificationRule.js.map +1 -0
  126. package/build/dist/Types/Workspace/NotificationRules/EventType.js +9 -0
  127. package/build/dist/Types/Workspace/NotificationRules/EventType.js.map +1 -0
  128. package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js +275 -0
  129. package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js.map +1 -0
  130. package/build/dist/Types/Workspace/NotificationRules/SlackNotificationRule.js +2 -0
  131. package/build/dist/Types/Workspace/NotificationRules/SlackNotificationRule.js.map +1 -0
  132. package/build/dist/Types/Workspace/WorkspaceNotificationPayload.js +2 -0
  133. package/build/dist/Types/Workspace/WorkspaceNotificationPayload.js.map +1 -0
  134. package/build/dist/Types/Workspace/WorkspaceType.js +7 -0
  135. package/build/dist/Types/Workspace/WorkspaceType.js.map +1 -0
  136. package/build/dist/UI/Components/Forms/BasicForm.js +7 -0
  137. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  138. package/build/dist/UI/Components/Forms/Fields/FormField.js +21 -4
  139. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  140. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -1
  141. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
  142. package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js +2 -2
  143. package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js.map +1 -1
  144. package/build/dist/UI/Components/Icon/Icon.js +5 -1
  145. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  146. package/build/dist/UI/Components/Radio/Radio.js +4 -4
  147. package/build/dist/UI/Components/Radio/Radio.js.map +1 -1
  148. package/build/dist/UI/Config.js +1 -0
  149. package/build/dist/UI/Config.js.map +1 -1
  150. package/package.json +2 -2
  151. package/build/dist/Server/Utils/Slack.js +0 -20
  152. package/build/dist/Server/Utils/Slack.js.map +0 -1
@@ -0,0 +1,312 @@
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 TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
8
+ import ColumnLength from "../../Types/Database/ColumnLength";
9
+ import ColumnType from "../../Types/Database/ColumnType";
10
+ import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
11
+ import CurrentUserCanAccessRecordBy from "../../Types/Database/CurrentUserCanAccessRecordBy";
12
+ import TableColumn from "../../Types/Database/TableColumn";
13
+ import TableColumnType from "../../Types/Database/TableColumnType";
14
+ import TableMetadata from "../../Types/Database/TableMetadata";
15
+ import TenantColumn from "../../Types/Database/TenantColumn";
16
+ import IconProp from "../../Types/Icon/IconProp";
17
+ import ObjectID from "../../Types/ObjectID";
18
+ import Permission from "../../Types/Permission";
19
+ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
20
+ import WorkspaceType from "../../Types/Workspace/WorkspaceType";
21
+
22
+ export interface MiscData {
23
+ [key: string]: any;
24
+ }
25
+
26
+ export interface SlackMiscData extends MiscData {
27
+ userId: string;
28
+ }
29
+
30
+ @TenantColumn("projectId")
31
+ @AllowAccessIfSubscriptionIsUnpaid()
32
+ @TableAccessControl({
33
+ create: [Permission.CurrentUser],
34
+ read: [Permission.CurrentUser],
35
+ delete: [Permission.CurrentUser],
36
+ update: [Permission.CurrentUser],
37
+ })
38
+ @CrudApiEndpoint(new Route("/workspace-user-auth-token"))
39
+ @Entity({
40
+ name: "WorkspaceUserAuthToken",
41
+ })
42
+ @TableMetadata({
43
+ tableName: "WorkspaceUserAuthToken",
44
+ singularName: "Workspace User Auth Token",
45
+ pluralName: "Workspace User Auth Tokens",
46
+ icon: IconProp.Lock,
47
+ tableDescription: "Third Party Auth Token for the User",
48
+ })
49
+ @CurrentUserCanAccessRecordBy("userId")
50
+ class WorkspaceUserAuthToken extends BaseModel {
51
+ @ColumnAccessControl({
52
+ create: [Permission.CurrentUser],
53
+ read: [Permission.CurrentUser],
54
+ update: [],
55
+ })
56
+ @TableColumn({
57
+ manyToOneRelationColumn: "projectId",
58
+ type: TableColumnType.Entity,
59
+ modelType: Project,
60
+ title: "Project",
61
+ description: "Relation to Project Resource in which this object belongs",
62
+ })
63
+ @ManyToOne(
64
+ () => {
65
+ return Project;
66
+ },
67
+ {
68
+ eager: false,
69
+ nullable: true,
70
+ onDelete: "CASCADE",
71
+ orphanedRowAction: "nullify",
72
+ },
73
+ )
74
+ @JoinColumn({ name: "projectId" })
75
+ public project?: Project = undefined;
76
+
77
+ @ColumnAccessControl({
78
+ create: [Permission.CurrentUser],
79
+ read: [Permission.CurrentUser],
80
+ update: [],
81
+ })
82
+ @Index()
83
+ @TableColumn({
84
+ type: TableColumnType.ObjectID,
85
+ required: true,
86
+ canReadOnRelationQuery: true,
87
+ title: "Project ID",
88
+ description: "ID of your OneUptime Project in which this object belongs",
89
+ })
90
+ @Column({
91
+ type: ColumnType.ObjectID,
92
+ nullable: false,
93
+ transformer: ObjectID.getDatabaseTransformer(),
94
+ })
95
+ public projectId?: ObjectID = undefined;
96
+
97
+ @ColumnAccessControl({
98
+ create: [Permission.CurrentUser],
99
+ read: [],
100
+ update: [],
101
+ })
102
+ @TableColumn({
103
+ title: "Auth Token",
104
+ required: true,
105
+ unique: false,
106
+ type: TableColumnType.VeryLongText,
107
+ canReadOnRelationQuery: true,
108
+ })
109
+ @Column({
110
+ type: ColumnType.VeryLongText,
111
+ unique: false,
112
+ nullable: false,
113
+ })
114
+ public authToken?: string = undefined;
115
+
116
+ @ColumnAccessControl({
117
+ create: [Permission.CurrentUser],
118
+ read: [Permission.CurrentUser],
119
+ update: [],
120
+ })
121
+ @TableColumn({
122
+ title: "User ID in Service",
123
+ description: "User ID in the Workspace",
124
+ required: true,
125
+ unique: false,
126
+ type: TableColumnType.LongText,
127
+ canReadOnRelationQuery: true,
128
+ })
129
+ @Column({
130
+ type: ColumnType.LongText,
131
+ length: ColumnLength.LongText,
132
+ unique: false,
133
+ nullable: false,
134
+ })
135
+ public workspaceUserId?: string = undefined;
136
+
137
+ @ColumnAccessControl({
138
+ create: [Permission.CurrentUser],
139
+ read: [Permission.CurrentUser],
140
+ update: [],
141
+ })
142
+ @TableColumn({
143
+ title: "Workspace Type",
144
+ description: "Type of Workspace - slack, microsoft teams etc.",
145
+ required: true,
146
+ unique: false,
147
+ type: TableColumnType.LongText,
148
+ canReadOnRelationQuery: true,
149
+ })
150
+ @Column({
151
+ type: ColumnType.LongText,
152
+ length: ColumnLength.LongText,
153
+ unique: false,
154
+ nullable: false,
155
+ })
156
+ public workspaceType?: WorkspaceType = undefined;
157
+
158
+ @ColumnAccessControl({
159
+ create: [Permission.CurrentUser],
160
+ read: [Permission.CurrentUser],
161
+ update: [],
162
+ })
163
+ @TableColumn({
164
+ title: "Misc Data",
165
+ required: true,
166
+ unique: false,
167
+ type: TableColumnType.JSON,
168
+ canReadOnRelationQuery: true,
169
+ })
170
+ @Column({
171
+ type: ColumnType.JSON,
172
+ unique: false,
173
+ nullable: false,
174
+ })
175
+ public miscData?: MiscData = undefined;
176
+
177
+ @ColumnAccessControl({
178
+ create: [Permission.CurrentUser],
179
+ read: [Permission.CurrentUser],
180
+ update: [],
181
+ })
182
+ @TableColumn({
183
+ manyToOneRelationColumn: "user",
184
+ type: TableColumnType.Entity,
185
+ modelType: User,
186
+ title: "User",
187
+ description: "Relation to User who this email belongs to",
188
+ })
189
+ @ManyToOne(
190
+ () => {
191
+ return User;
192
+ },
193
+ {
194
+ eager: false,
195
+ nullable: true,
196
+ onDelete: "CASCADE",
197
+ orphanedRowAction: "nullify",
198
+ },
199
+ )
200
+ @JoinColumn({ name: "userId" })
201
+ public user?: User = undefined;
202
+
203
+ @ColumnAccessControl({
204
+ create: [Permission.CurrentUser],
205
+ read: [Permission.CurrentUser],
206
+ update: [],
207
+ })
208
+ @TableColumn({
209
+ type: TableColumnType.ObjectID,
210
+ title: "User ID",
211
+ description: "User ID who this email belongs to",
212
+ })
213
+ @Column({
214
+ type: ColumnType.ObjectID,
215
+ nullable: true,
216
+ transformer: ObjectID.getDatabaseTransformer(),
217
+ })
218
+ @Index()
219
+ public userId?: ObjectID = undefined;
220
+
221
+ @ColumnAccessControl({
222
+ create: [Permission.CurrentUser],
223
+ read: [Permission.CurrentUser],
224
+ update: [],
225
+ })
226
+ @TableColumn({
227
+ manyToOneRelationColumn: "createdByUserId",
228
+ type: TableColumnType.Entity,
229
+ modelType: User,
230
+ title: "Created by User",
231
+ description:
232
+ "Relation to User who created this object (if this object was created by a User)",
233
+ })
234
+ @ManyToOne(
235
+ () => {
236
+ return User;
237
+ },
238
+ {
239
+ eager: false,
240
+ nullable: true,
241
+ onDelete: "SET NULL",
242
+ orphanedRowAction: "nullify",
243
+ },
244
+ )
245
+ @JoinColumn({ name: "createdByUserId" })
246
+ public createdByUser?: User = undefined;
247
+
248
+ @ColumnAccessControl({
249
+ create: [Permission.CurrentUser],
250
+ read: [Permission.CurrentUser],
251
+ update: [],
252
+ })
253
+ @TableColumn({
254
+ type: TableColumnType.ObjectID,
255
+ title: "Created by User ID",
256
+ description:
257
+ "User ID who created this object (if this object was created by a User)",
258
+ })
259
+ @Column({
260
+ type: ColumnType.ObjectID,
261
+ nullable: true,
262
+ transformer: ObjectID.getDatabaseTransformer(),
263
+ })
264
+ public createdByUserId?: ObjectID = undefined;
265
+
266
+ @ColumnAccessControl({
267
+ create: [],
268
+ read: [],
269
+ update: [],
270
+ })
271
+ @TableColumn({
272
+ manyToOneRelationColumn: "deletedByUserId",
273
+ type: TableColumnType.Entity,
274
+ title: "Deleted by User",
275
+ description:
276
+ "Relation to User who deleted this object (if this object was deleted by a User)",
277
+ })
278
+ @ManyToOne(
279
+ () => {
280
+ return User;
281
+ },
282
+ {
283
+ cascade: false,
284
+ eager: false,
285
+ nullable: true,
286
+ onDelete: "SET NULL",
287
+ orphanedRowAction: "nullify",
288
+ },
289
+ )
290
+ @JoinColumn({ name: "deletedByUserId" })
291
+ public deletedByUser?: User = undefined;
292
+
293
+ @ColumnAccessControl({
294
+ create: [],
295
+ read: [],
296
+ update: [],
297
+ })
298
+ @TableColumn({
299
+ type: TableColumnType.ObjectID,
300
+ title: "Deleted by User ID",
301
+ description:
302
+ "User ID who deleted this object (if this object was deleted by a User)",
303
+ })
304
+ @Column({
305
+ type: ColumnType.ObjectID,
306
+ nullable: true,
307
+ transformer: ObjectID.getDatabaseTransformer(),
308
+ })
309
+ public deletedByUserId?: ObjectID = undefined;
310
+ }
311
+
312
+ export default WorkspaceUserAuthToken;
@@ -0,0 +1,368 @@
1
+ import Express, {
2
+ ExpressRequest,
3
+ ExpressResponse,
4
+ ExpressRouter,
5
+ } from "../Utils/Express";
6
+ import Response from "../Utils/Response";
7
+ import SlackAuthorization from "../Middleware/SlackAuthorization";
8
+ import BadRequestException from "../../Types/Exception/BadRequestException";
9
+ import logger from "../Utils/Logger";
10
+ import { JSONObject } from "../../Types/JSON";
11
+ import BadDataException from "../../Types/Exception/BadDataException";
12
+ import {
13
+ AppApiClientUrl,
14
+ DashboardClientUrl,
15
+ SlackAppClientId,
16
+ SlackAppClientSecret,
17
+ } from "../EnvironmentConfig";
18
+ import SlackAppManifest from "../Utils/Slack/app-manifest.json";
19
+ import URL from "../../Types/API/URL";
20
+ import HTTPErrorResponse from "../../Types/API/HTTPErrorResponse";
21
+ import HTTPResponse from "../../Types/API/HTTPResponse";
22
+ import API from "../../Utils/API";
23
+ import WorkspaceProjectAuthTokenService from "../Services/WorkspaceProjectAuthTokenService";
24
+ import ObjectID from "../../Types/ObjectID";
25
+ import WorkspaceUserAuthTokenService from "../Services/WorkspaceUserAuthTokenService";
26
+ import WorkspaceType from "../../Types/Workspace/WorkspaceType";
27
+
28
+ export default class SlackAPI {
29
+ public getRouter(): ExpressRouter {
30
+ const router: ExpressRouter = Express.getRouter();
31
+
32
+ router.get(
33
+ "/slack/app-manifest",
34
+ (req: ExpressRequest, res: ExpressResponse) => {
35
+ // return app manifest for slack app
36
+ return Response.sendJsonObjectResponse(req, res, SlackAppManifest);
37
+ },
38
+ );
39
+
40
+ router.get(
41
+ "/slack/auth/:projectId/:userId",
42
+ async (req: ExpressRequest, res: ExpressResponse) => {
43
+ if (!SlackAppClientId) {
44
+ return Response.sendErrorResponse(
45
+ req,
46
+ res,
47
+ new BadDataException("Slack App Client ID is not set"),
48
+ );
49
+ }
50
+
51
+ if (!SlackAppClientSecret) {
52
+ return Response.sendErrorResponse(
53
+ req,
54
+ res,
55
+ new BadDataException("Slack App Client Secret is not set"),
56
+ );
57
+ }
58
+
59
+ const projectId: string | undefined =
60
+ req.params["projectId"]?.toString();
61
+ const userId: string | undefined = req.params["userId"]?.toString();
62
+
63
+ if (!projectId) {
64
+ return Response.sendErrorResponse(
65
+ req,
66
+ res,
67
+ new BadDataException("Invalid ProjectID in request"),
68
+ );
69
+ }
70
+
71
+ if (!userId) {
72
+ return Response.sendErrorResponse(
73
+ req,
74
+ res,
75
+ new BadDataException("Invalid UserID in request"),
76
+ );
77
+ }
78
+
79
+ // if there's an error query param.
80
+ const error: string | undefined = req.query["error"]?.toString();
81
+
82
+ const slackIntegrationPageUrl: URL = URL.fromString(
83
+ DashboardClientUrl.toString() +
84
+ `/${projectId.toString()}/settings/slack-integration`,
85
+ );
86
+
87
+ if (error) {
88
+ return Response.redirect(
89
+ req,
90
+ res,
91
+ slackIntegrationPageUrl.addQueryParam("error", error),
92
+ );
93
+ }
94
+
95
+ // slack returns the code on successful auth.
96
+ const code: string | undefined = req.query["code"]?.toString();
97
+
98
+ if (!code) {
99
+ return Response.sendErrorResponse(
100
+ req,
101
+ res,
102
+ new BadRequestException("Invalid request"),
103
+ );
104
+ }
105
+
106
+ // get access token from slack api.
107
+
108
+ const redirectUri: URL = URL.fromString(
109
+ `${AppApiClientUrl.toString()}/slack/auth/${projectId}/${userId}`,
110
+ );
111
+
112
+ const requestBody: JSONObject = {
113
+ code: code,
114
+ client_id: SlackAppClientId,
115
+ client_secret: SlackAppClientSecret,
116
+ redirect_uri: redirectUri.toString(),
117
+ };
118
+
119
+ logger.debug("Slack Auth Request Body: ");
120
+ logger.debug(requestBody);
121
+
122
+ // send the request to slack api to get the access token https://slack.com/api/oauth.v2.access
123
+
124
+ const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
125
+ await API.post(
126
+ URL.fromString("https://slack.com/api/oauth.v2.access"),
127
+ requestBody,
128
+ {
129
+ "Content-Type": "application/x-www-form-urlencoded",
130
+ },
131
+ );
132
+
133
+ if (response instanceof HTTPErrorResponse) {
134
+ throw response;
135
+ }
136
+
137
+ const responseBody: JSONObject = response.data;
138
+
139
+ logger.debug("Slack Auth Request Body: ");
140
+ logger.debug(responseBody);
141
+
142
+ let slackTeamId: string | undefined = undefined;
143
+ let slackBotAccessToken: string | undefined = undefined;
144
+ let slackUserId: string | undefined = undefined;
145
+ let slackTeamName: string | undefined = undefined;
146
+ let botUserId: string | undefined = undefined;
147
+ let slackUserAccessToken: string | undefined = undefined;
148
+
149
+ // ReponseBody is in this format.
150
+ // {
151
+ // "ok": true,
152
+ // "access_token": "sample-token",
153
+ // "token_type": "bot",
154
+ // "scope": "commands,incoming-webhook",
155
+ // "bot_user_id": "U0KRQLJ9H",
156
+ // "app_id": "A0KRD7HC3",
157
+ // "team": {
158
+ // "name": "Slack Pickleball Team",
159
+ // "id": "T9TK3CUKW"
160
+ // },
161
+ // "enterprise": {
162
+ // "name": "slack-pickleball",
163
+ // "id": "E12345678"
164
+ // },
165
+ // "authed_user": {
166
+ // "id": "U1234",
167
+ // "scope": "chat:write",
168
+ // "access_token": "sample-token",
169
+ // "token_type": "user"
170
+ // }
171
+ // }
172
+
173
+ if (responseBody["ok"] !== true) {
174
+ return Response.sendErrorResponse(
175
+ req,
176
+ res,
177
+ new BadRequestException("Invalid request"),
178
+ );
179
+ }
180
+
181
+ if (
182
+ responseBody["team"] &&
183
+ (responseBody["team"] as JSONObject)["id"]
184
+ ) {
185
+ slackTeamId = (responseBody["team"] as JSONObject)["id"]?.toString();
186
+ }
187
+
188
+ if (responseBody["access_token"]) {
189
+ slackBotAccessToken = responseBody["access_token"]?.toString();
190
+ }
191
+
192
+ if (
193
+ responseBody["authed_user"] &&
194
+ (responseBody["authed_user"] as JSONObject)["id"]
195
+ ) {
196
+ slackUserId = (responseBody["authed_user"] as JSONObject)[
197
+ "id"
198
+ ]?.toString();
199
+ }
200
+
201
+ if (
202
+ responseBody["authed_user"] &&
203
+ (responseBody["authed_user"] as JSONObject)["access_token"]
204
+ ) {
205
+ slackUserAccessToken = (responseBody["authed_user"] as JSONObject)[
206
+ "access_token"
207
+ ]?.toString();
208
+ }
209
+
210
+ if (
211
+ responseBody["team"] &&
212
+ (responseBody["team"] as JSONObject)["name"]
213
+ ) {
214
+ slackTeamName = (responseBody["team"] as JSONObject)[
215
+ "name"
216
+ ]?.toString();
217
+ }
218
+
219
+ if (responseBody["bot_user_id"]) {
220
+ botUserId = responseBody["bot_user_id"]?.toString();
221
+ }
222
+
223
+ await WorkspaceProjectAuthTokenService.refreshAuthToken({
224
+ projectId: new ObjectID(projectId),
225
+ workspaceType: WorkspaceType.Slack,
226
+ authToken: slackBotAccessToken || "",
227
+ workspaceProjectId: slackTeamId || "",
228
+ miscData: {
229
+ teamId: slackTeamId || "",
230
+ teamName: slackTeamName || "",
231
+ botUserId: botUserId || "",
232
+ },
233
+ });
234
+
235
+ await WorkspaceUserAuthTokenService.refreshAuthToken({
236
+ projectId: new ObjectID(projectId),
237
+ userId: new ObjectID(userId),
238
+ workspaceType: WorkspaceType.Slack,
239
+ authToken: slackUserAccessToken || "",
240
+ workspaceUserId: slackUserId || "",
241
+ miscData: {
242
+ userId: slackUserId || "",
243
+ },
244
+ });
245
+
246
+ // return back to dashboard after successful auth.
247
+ Response.redirect(req, res, slackIntegrationPageUrl);
248
+ },
249
+ );
250
+
251
+ router.post(
252
+ "/slack/interactive",
253
+ SlackAuthorization.isAuthorizedSlackRequest,
254
+ (req: ExpressRequest, res: ExpressResponse) => {
255
+ return Response.sendJsonObjectResponse(req, res, {
256
+ response_action: "clear",
257
+ });
258
+ },
259
+ );
260
+
261
+ // options load endpoint.
262
+
263
+ router.post(
264
+ "/slack/options-load",
265
+ SlackAuthorization.isAuthorizedSlackRequest,
266
+ (req: ExpressRequest, res: ExpressResponse) => {
267
+ return Response.sendJsonObjectResponse(req, res, {
268
+ response_action: "clear",
269
+ });
270
+ },
271
+ );
272
+
273
+ router.post(
274
+ "/slack/command",
275
+ SlackAuthorization.isAuthorizedSlackRequest,
276
+ (req: ExpressRequest, res: ExpressResponse) => {
277
+ return Response.sendJsonObjectResponse(req, res, {
278
+ response_action: "clear",
279
+ });
280
+ },
281
+ );
282
+
283
+ router.post(
284
+ "/slack/events",
285
+ SlackAuthorization.isAuthorizedSlackRequest,
286
+ (req: ExpressRequest, res: ExpressResponse) => {
287
+ // respond to slack challenge
288
+
289
+ const body: any = req.body;
290
+
291
+ if (body.challenge) {
292
+ return Response.sendJsonObjectResponse(req, res, {
293
+ challenge: body.challenge,
294
+ });
295
+ }
296
+
297
+ // if event is "create-incident" then show the incident create modal with title and description and add a button to submit the form.
298
+
299
+ if (body.event && body.event.type === "create-incident") {
300
+ return Response.sendJsonObjectResponse(req, res, {
301
+ type: "modal",
302
+ title: {
303
+ type: "plain_text",
304
+ text: "Create Incident",
305
+ },
306
+ blocks: [
307
+ {
308
+ type: "input",
309
+ block_id: "title",
310
+ element: {
311
+ type: "plain_text_input",
312
+ action_id: "title",
313
+ placeholder: {
314
+ type: "plain_text",
315
+ text: "Incident Title",
316
+ },
317
+ },
318
+ label: {
319
+ type: "plain_text",
320
+ text: "Title",
321
+ },
322
+ },
323
+ {
324
+ type: "input",
325
+ block_id: "description",
326
+ element: {
327
+ type: "plain_text_input",
328
+ action_id: "description",
329
+ placeholder: {
330
+ type: "plain_text",
331
+ text: "Incident Description",
332
+ },
333
+ },
334
+ label: {
335
+ type: "plain_text",
336
+ text: "Description",
337
+ },
338
+ },
339
+ // button
340
+ {
341
+ type: "actions",
342
+ elements: [
343
+ {
344
+ type: "button",
345
+ text: {
346
+ type: "plain_text",
347
+ text: "Submit",
348
+ },
349
+ style: "primary",
350
+ value: "submit",
351
+ },
352
+ ],
353
+ },
354
+ ],
355
+ });
356
+ }
357
+
358
+ return Response.sendErrorResponse(
359
+ req,
360
+ res,
361
+ new BadRequestException("Invalid request"),
362
+ );
363
+ },
364
+ );
365
+
366
+ return router;
367
+ }
368
+ }
@@ -2,6 +2,7 @@ import {
2
2
  AccountsRoute,
3
3
  AdminDashboardRoute,
4
4
  DashboardRoute,
5
+ AppApiRoute,
5
6
  } from "Common/ServiceRoute";
6
7
  import BillingConfig from "./BillingConfig";
7
8
  import Hostname from "Common/Types/API/Hostname";
@@ -299,6 +300,8 @@ export const AdminDashboardClientURL: URL = new URL(
299
300
  AdminDashboardRoute,
300
301
  );
301
302
 
303
+ export const AppApiClientUrl: URL = new URL(HttpProtocol, Host, AppApiRoute);
304
+
302
305
  export const DashboardClientUrl: URL = new URL(
303
306
  HttpProtocol,
304
307
  Host,
@@ -313,3 +316,10 @@ export const AccountsClientUrl: URL = new URL(
313
316
 
314
317
  export const DisableTelemetry: boolean =
315
318
  process.env["DISABLE_TELEMETRY"] === "true";
319
+
320
+ export const SlackAppClientId: string | null =
321
+ process.env["SLACK_APP_CLIENT_ID"] || null;
322
+ export const SlackAppClientSecret: string | null =
323
+ process.env["SLACK_APP_CLIENT_SECRET"] || null;
324
+ export const SlackAppSigningSecret: string | null =
325
+ process.env["SLACK_APP_SIGNING_SECRET"] || null;