@oneuptime/common 8.0.5579 → 8.0.5581

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 (181) hide show
  1. package/Models/DatabaseModels/AlertInternalNote.ts +58 -1
  2. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +1 -0
  3. package/Models/DatabaseModels/File.ts +1 -1
  4. package/Models/DatabaseModels/IncidentInternalNote.ts +58 -1
  5. package/Models/DatabaseModels/IncidentPublicNote.ts +58 -1
  6. package/Models/DatabaseModels/ScheduledMaintenanceInternalNote.ts +58 -1
  7. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +58 -1
  8. package/Models/DatabaseModels/StatusPageAnnouncement.ts +49 -0
  9. package/Server/API/AlertInternalNoteAPI.ts +96 -0
  10. package/Server/API/IncidentInternalNoteAPI.ts +96 -0
  11. package/Server/API/IncidentPublicNoteAPI.ts +96 -0
  12. package/Server/API/ScheduledMaintenanceInternalNoteAPI.ts +100 -0
  13. package/Server/API/ScheduledMaintenancePublicNoteAPI.ts +100 -0
  14. package/Server/API/StatusPageAPI.ts +585 -59
  15. package/Server/API/StatusPageAnnouncementAPI.ts +98 -0
  16. package/Server/API/UserAPI.ts +95 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1763471659817-MigrationName.ts +79 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1763477560906-MigrationName.ts +81 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1763480947474-MigrationName.ts +79 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  21. package/Server/Middleware/ProjectAuthorization.ts +3 -1
  22. package/Server/Services/AlertInternalNoteService.ts +75 -2
  23. package/Server/Services/IncidentInternalNoteService.ts +76 -2
  24. package/Server/Services/IncidentPublicNoteService.ts +76 -2
  25. package/Server/Services/ScheduledMaintenanceInternalNoteService.ts +76 -2
  26. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +76 -2
  27. package/Server/Services/ScheduledMaintenanceService.ts +10 -7
  28. package/Server/Services/StatusPagePrivateUserService.ts +10 -7
  29. package/Server/Services/StatusPageService.ts +12 -7
  30. package/Server/Services/StatusPageSubscriberService.ts +19 -13
  31. package/Server/Utils/FileAttachmentMarkdownUtil.ts +98 -0
  32. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +9 -1
  33. package/Server/Utils/Monitor/Criteria/ExceptionMonitorCriteria.ts +34 -0
  34. package/Server/Utils/Monitor/DataToProcess.ts +3 -1
  35. package/Server/Utils/Monitor/MonitorCriteriaDataExtractor.ts +13 -0
  36. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +13 -0
  37. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +20 -0
  38. package/Server/Utils/Monitor/MonitorResource.ts +18 -0
  39. package/Server/Utils/Response.ts +13 -0
  40. package/Server/Utils/Telemetry.ts +15 -0
  41. package/Types/File/MimeType.ts +18 -0
  42. package/Types/Monitor/CriteriaFilter.ts +3 -0
  43. package/Types/Monitor/ExceptionMonitor/ExceptionMonitorResponse.ts +12 -0
  44. package/Types/Monitor/MonitorCriteriaInstance.ts +67 -0
  45. package/Types/Monitor/MonitorStep.ts +30 -0
  46. package/Types/Monitor/MonitorStepExceptionMonitor.ts +94 -0
  47. package/Types/Monitor/MonitorType.ts +10 -1
  48. package/Types/Telemetry/TelemetryQuery.ts +2 -1
  49. package/Types/Telemetry/TelemetryType.ts +1 -0
  50. package/UI/Components/AttachmentList/EventAttachmentList.tsx +121 -0
  51. package/UI/Components/EventItem/EventItem.tsx +22 -0
  52. package/UI/Components/Feed/FeedItem.tsx +9 -16
  53. package/UI/Components/FilePicker/FilePicker.tsx +441 -145
  54. package/UI/Components/Forms/Fields/FormField.tsx +32 -15
  55. package/UI/Components/Forms/FormSummary.tsx +168 -1
  56. package/UI/Components/Forms/ModelForm.tsx +46 -24
  57. package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
  58. package/UI/Components/Icon/Icon.tsx +1 -1
  59. package/UI/Utils/API/RequestOptions.ts +2 -0
  60. package/UI/Utils/ModelAPI/ModelAPI.ts +18 -0
  61. package/UI/Utils/User.ts +8 -0
  62. package/Utils/API.ts +11 -1
  63. package/build/dist/Models/DatabaseModels/AlertInternalNote.js +49 -1
  64. package/build/dist/Models/DatabaseModels/AlertInternalNote.js.map +1 -1
  65. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +1 -0
  66. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  67. package/build/dist/Models/DatabaseModels/File.js +1 -1
  68. package/build/dist/Models/DatabaseModels/File.js.map +1 -1
  69. package/build/dist/Models/DatabaseModels/IncidentInternalNote.js +49 -1
  70. package/build/dist/Models/DatabaseModels/IncidentInternalNote.js.map +1 -1
  71. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +49 -1
  72. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  73. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceInternalNote.js +49 -1
  74. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceInternalNote.js.map +1 -1
  75. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +49 -1
  76. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  77. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +48 -0
  78. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  79. package/build/dist/Server/API/AlertInternalNoteAPI.js +68 -0
  80. package/build/dist/Server/API/AlertInternalNoteAPI.js.map +1 -0
  81. package/build/dist/Server/API/IncidentInternalNoteAPI.js +68 -0
  82. package/build/dist/Server/API/IncidentInternalNoteAPI.js.map +1 -0
  83. package/build/dist/Server/API/IncidentPublicNoteAPI.js +68 -0
  84. package/build/dist/Server/API/IncidentPublicNoteAPI.js.map +1 -0
  85. package/build/dist/Server/API/ScheduledMaintenanceInternalNoteAPI.js +68 -0
  86. package/build/dist/Server/API/ScheduledMaintenanceInternalNoteAPI.js.map +1 -0
  87. package/build/dist/Server/API/ScheduledMaintenancePublicNoteAPI.js +68 -0
  88. package/build/dist/Server/API/ScheduledMaintenancePublicNoteAPI.js.map +1 -0
  89. package/build/dist/Server/API/StatusPageAPI.js +488 -85
  90. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  91. package/build/dist/Server/API/StatusPageAnnouncementAPI.js +68 -0
  92. package/build/dist/Server/API/StatusPageAnnouncementAPI.js.map +1 -0
  93. package/build/dist/Server/API/UserAPI.js +66 -0
  94. package/build/dist/Server/API/UserAPI.js.map +1 -0
  95. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763471659817-MigrationName.js +34 -0
  96. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763471659817-MigrationName.js.map +1 -0
  97. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763477560906-MigrationName.js +34 -0
  98. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763477560906-MigrationName.js.map +1 -0
  99. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763480947474-MigrationName.js +34 -0
  100. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763480947474-MigrationName.js.map +1 -0
  101. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  102. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  103. package/build/dist/Server/Middleware/ProjectAuthorization.js +4 -1
  104. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  105. package/build/dist/Server/Services/AlertInternalNoteService.js +54 -2
  106. package/build/dist/Server/Services/AlertInternalNoteService.js.map +1 -1
  107. package/build/dist/Server/Services/IncidentInternalNoteService.js +54 -2
  108. package/build/dist/Server/Services/IncidentInternalNoteService.js.map +1 -1
  109. package/build/dist/Server/Services/IncidentPublicNoteService.js +54 -2
  110. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  111. package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js +54 -2
  112. package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js.map +1 -1
  113. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +54 -2
  114. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  115. package/build/dist/Server/Services/ScheduledMaintenanceService.js +6 -5
  116. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  117. package/build/dist/Server/Services/StatusPagePrivateUserService.js +6 -4
  118. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  119. package/build/dist/Server/Services/StatusPageService.js +7 -4
  120. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  121. package/build/dist/Server/Services/StatusPageSubscriberService.js +11 -7
  122. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  123. package/build/dist/Server/Utils/FileAttachmentMarkdownUtil.js +67 -0
  124. package/build/dist/Server/Utils/FileAttachmentMarkdownUtil.js.map +1 -0
  125. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +6 -1
  126. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  127. package/build/dist/Server/Utils/Monitor/Criteria/ExceptionMonitorCriteria.js +34 -0
  128. package/build/dist/Server/Utils/Monitor/Criteria/ExceptionMonitorCriteria.js.map +1 -0
  129. package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js +6 -0
  130. package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js.map +1 -1
  131. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +10 -0
  132. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  133. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
  134. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  135. package/build/dist/Server/Utils/Monitor/MonitorResource.js +10 -0
  136. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  137. package/build/dist/Server/Utils/Response.js +8 -0
  138. package/build/dist/Server/Utils/Response.js.map +1 -1
  139. package/build/dist/Server/Utils/Telemetry.js +8 -1
  140. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  141. package/build/dist/Types/File/MimeType.js +18 -0
  142. package/build/dist/Types/File/MimeType.js.map +1 -1
  143. package/build/dist/Types/Monitor/CriteriaFilter.js +2 -0
  144. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  145. package/build/dist/Types/Monitor/ExceptionMonitor/ExceptionMonitorResponse.js +2 -0
  146. package/build/dist/Types/Monitor/ExceptionMonitor/ExceptionMonitorResponse.js.map +1 -0
  147. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +62 -0
  148. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  149. package/build/dist/Types/Monitor/MonitorStep.js +20 -1
  150. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  151. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js +58 -0
  152. package/build/dist/Types/Monitor/MonitorStepExceptionMonitor.js.map +1 -0
  153. package/build/dist/Types/Monitor/MonitorType.js +9 -1
  154. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  155. package/build/dist/Types/Telemetry/TelemetryType.js +1 -0
  156. package/build/dist/Types/Telemetry/TelemetryType.js.map +1 -1
  157. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +42 -0
  158. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -0
  159. package/build/dist/UI/Components/EventItem/EventItem.js +5 -1
  160. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  161. package/build/dist/UI/Components/Feed/FeedItem.js +6 -4
  162. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  163. package/build/dist/UI/Components/FilePicker/FilePicker.js +262 -77
  164. package/build/dist/UI/Components/FilePicker/FilePicker.js.map +1 -1
  165. package/build/dist/UI/Components/Forms/Fields/FormField.js +24 -12
  166. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  167. package/build/dist/UI/Components/Forms/FormSummary.js +77 -1
  168. package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
  169. package/build/dist/UI/Components/Forms/ModelForm.js +32 -18
  170. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  171. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
  172. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
  173. package/build/dist/UI/Components/Icon/Icon.js +1 -1
  174. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  175. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +30 -45
  176. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  177. package/build/dist/UI/Utils/User.js +7 -0
  178. package/build/dist/UI/Utils/User.js.map +1 -1
  179. package/build/dist/Utils/API.js +3 -0
  180. package/build/dist/Utils/API.js.map +1 -1
  181. package/package.json +6 -6
@@ -63,6 +63,7 @@ import ScheduledMaintenanceState from "../../Models/DatabaseModels/ScheduledMain
63
63
  import ScheduledMaintenanceStateTimeline from "../../Models/DatabaseModels/ScheduledMaintenanceStateTimeline";
64
64
  import StatusPage from "../../Models/DatabaseModels/StatusPage";
65
65
  import StatusPageAnnouncement from "../../Models/DatabaseModels/StatusPageAnnouncement";
66
+ import File from "../../Models/DatabaseModels/File";
66
67
  import StatusPageDomain from "../../Models/DatabaseModels/StatusPageDomain";
67
68
  import StatusPageFooterLink from "../../Models/DatabaseModels/StatusPageFooterLink";
68
69
  import StatusPageGroup from "../../Models/DatabaseModels/StatusPageGroup";
@@ -86,11 +87,57 @@ import EmailTemplateType from "../../Types/Email/EmailTemplateType";
86
87
  import Hostname from "../../Types/API/Hostname";
87
88
  import Protocol from "../../Types/API/Protocol";
88
89
  import DatabaseConfig from "../DatabaseConfig";
89
- import { FileRoute } from "../../ServiceRoute";
90
+ import { StatusPageApiRoute } from "../../ServiceRoute";
90
91
  import ProjectSmtpConfigService from "../Services/ProjectSmtpConfigService";
91
92
  import ForbiddenException from "../../Types/Exception/ForbiddenException";
92
93
  import SlackUtil from "../Utils/Workspace/Slack/Slack";
93
94
 
95
+ type ResolveStatusPageIdOrThrowFunction = (
96
+ statusPageIdOrDomain: string,
97
+ ) => Promise<ObjectID>;
98
+
99
+ const resolveStatusPageIdOrThrow: ResolveStatusPageIdOrThrowFunction = async (
100
+ statusPageIdOrDomain: string,
101
+ ): Promise<ObjectID> => {
102
+ if (!statusPageIdOrDomain) {
103
+ throw new NotFoundException("Status Page not found");
104
+ }
105
+
106
+ if (statusPageIdOrDomain.includes(".")) {
107
+ const statusPageDomain: StatusPageDomain | null =
108
+ await StatusPageDomainService.findOneBy({
109
+ query: {
110
+ fullDomain: statusPageIdOrDomain,
111
+ domain: {
112
+ isVerified: true,
113
+ } as any,
114
+ },
115
+ select: {
116
+ statusPageId: true,
117
+ },
118
+ props: {
119
+ isRoot: true,
120
+ },
121
+ });
122
+
123
+ if (!statusPageDomain || !statusPageDomain.statusPageId) {
124
+ throw new NotFoundException("Status Page not found");
125
+ }
126
+
127
+ return statusPageDomain.statusPageId;
128
+ }
129
+
130
+ try {
131
+ return new ObjectID(statusPageIdOrDomain);
132
+ } catch (err) {
133
+ logger.error(
134
+ `Error converting statusPageIdOrDomain to ObjectID: ${statusPageIdOrDomain}`,
135
+ );
136
+ logger.error(err);
137
+ throw new NotFoundException("Status Page not found");
138
+ }
139
+ };
140
+
94
141
  export default class StatusPageAPI extends BaseAPI<
95
142
  StatusPage,
96
143
  StatusPageServiceType
@@ -190,89 +237,198 @@ export default class StatusPageAPI extends BaseAPI<
190
237
  this.router.get(
191
238
  `${new this.entityType().getCrudApiPath()?.toString()}/favicon/:statusPageIdOrDomain`,
192
239
  async (req: ExpressRequest, res: ExpressResponse) => {
193
- const statusPageIdOrDomain: string = req.params[
194
- "statusPageIdOrDomain"
195
- ] as string;
196
-
197
- let statusPageId: ObjectID | null = null;
198
-
199
- if (statusPageIdOrDomain && statusPageIdOrDomain.includes(".")) {
200
- // then this is a domain and not the status page id. We need to get the status page id from the domain.
240
+ try {
241
+ const statusPageId: ObjectID = await resolveStatusPageIdOrThrow(
242
+ req.params["statusPageIdOrDomain"] as string,
243
+ );
201
244
 
202
- const statusPageDomain: StatusPageDomain | null =
203
- await StatusPageDomainService.findOneBy({
245
+ const statusPage: StatusPage | null =
246
+ await StatusPageService.findOneBy({
204
247
  query: {
205
- fullDomain: statusPageIdOrDomain,
206
- domain: {
207
- isVerified: true,
208
- } as any,
248
+ _id: statusPageId,
209
249
  },
210
250
  select: {
211
- statusPageId: true,
251
+ faviconFile: {
252
+ file: true,
253
+ _id: true,
254
+ fileType: true,
255
+ name: true,
256
+ },
212
257
  },
213
258
  props: {
214
259
  isRoot: true,
215
260
  },
216
261
  });
217
262
 
218
- if (!statusPageDomain || !statusPageDomain.statusPageId) {
219
- return Response.sendErrorResponse(
263
+ if (!statusPage || !statusPage.faviconFile) {
264
+ logger.debug("Favicon file not found. Returning default favicon.");
265
+
266
+ return Response.sendFileByPath(
220
267
  req,
221
268
  res,
222
- new NotFoundException("Status Page not found"),
269
+ `/usr/src/Common/UI/Images/favicon/status-green.png`,
223
270
  );
224
271
  }
225
272
 
226
- statusPageId = statusPageDomain.statusPageId;
227
- } else {
228
- // then this is a status page id. We need to get the status page id from the id.
229
- try {
230
- statusPageId = new ObjectID(statusPageIdOrDomain);
231
- } catch (err) {
232
- logger.error(err);
273
+ logger.debug(
274
+ `Favicon file found. Sending file: ${statusPage.faviconFile.name}`,
275
+ );
276
+
277
+ return Response.sendFileResponse(req, res, statusPage.faviconFile);
278
+ } catch (error) {
279
+ if (error instanceof NotFoundException) {
280
+ return Response.sendErrorResponse(req, res, error);
281
+ }
282
+
283
+ logger.error(error);
284
+ return Response.sendErrorResponse(
285
+ req,
286
+ res,
287
+ new NotFoundException("Status Page not found"),
288
+ );
289
+ }
290
+ },
291
+ );
292
+
293
+ this.router.get(
294
+ `${new this.entityType().getCrudApiPath()?.toString()}/logo/:statusPageIdOrDomain`,
295
+ async (req: ExpressRequest, res: ExpressResponse) => {
296
+ try {
297
+ const statusPageId: ObjectID = await resolveStatusPageIdOrThrow(
298
+ req.params["statusPageIdOrDomain"] as string,
299
+ );
300
+
301
+ const statusPage: StatusPage | null =
302
+ await StatusPageService.findOneBy({
303
+ query: {
304
+ _id: statusPageId,
305
+ },
306
+ select: {
307
+ logoFile: {
308
+ file: true,
309
+ _id: true,
310
+ fileType: true,
311
+ name: true,
312
+ },
313
+ },
314
+ props: {
315
+ isRoot: true,
316
+ },
317
+ });
318
+
319
+ if (!statusPage || !statusPage.logoFile) {
233
320
  return Response.sendErrorResponse(
234
321
  req,
235
322
  res,
236
- new NotFoundException("Status Page not found"),
323
+ new NotFoundException("Status Page logo not found"),
237
324
  );
238
325
  }
326
+
327
+ return Response.sendFileResponse(req, res, statusPage.logoFile);
328
+ } catch (error) {
329
+ if (error instanceof NotFoundException) {
330
+ return Response.sendErrorResponse(req, res, error);
331
+ }
332
+
333
+ logger.error(error);
334
+ return Response.sendErrorResponse(
335
+ req,
336
+ res,
337
+ new NotFoundException("Status Page logo not found"),
338
+ );
239
339
  }
340
+ },
341
+ );
240
342
 
241
- const statusPage: StatusPage | null = await StatusPageService.findOneBy(
242
- {
243
- query: {
244
- _id: statusPageId,
245
- },
246
- select: {
247
- faviconFile: {
248
- file: true,
249
- _id: true,
250
- fileType: true,
251
- name: true,
343
+ this.router.get(
344
+ `${new this.entityType().getCrudApiPath()?.toString()}/cover-image/:statusPageIdOrDomain`,
345
+ async (req: ExpressRequest, res: ExpressResponse) => {
346
+ try {
347
+ const statusPageId: ObjectID = await resolveStatusPageIdOrThrow(
348
+ req.params["statusPageIdOrDomain"] as string,
349
+ );
350
+
351
+ const statusPage: StatusPage | null =
352
+ await StatusPageService.findOneBy({
353
+ query: {
354
+ _id: statusPageId,
252
355
  },
253
- },
254
- props: {
255
- isRoot: true,
256
- },
257
- },
258
- );
356
+ select: {
357
+ coverImageFile: {
358
+ file: true,
359
+ _id: true,
360
+ fileType: true,
361
+ name: true,
362
+ },
363
+ },
364
+ props: {
365
+ isRoot: true,
366
+ },
367
+ });
259
368
 
260
- if (!statusPage || !statusPage.faviconFile) {
261
- logger.debug("Favicon file not found. Returning default favicon.");
369
+ if (!statusPage || !statusPage.coverImageFile) {
370
+ return Response.sendErrorResponse(
371
+ req,
372
+ res,
373
+ new NotFoundException("Status Page cover image not found"),
374
+ );
375
+ }
262
376
 
263
- // return default favicon.
264
- return Response.sendFileByPath(
377
+ return Response.sendFileResponse(req, res, statusPage.coverImageFile);
378
+ } catch (error) {
379
+ if (error instanceof NotFoundException) {
380
+ return Response.sendErrorResponse(req, res, error);
381
+ }
382
+
383
+ logger.error(error);
384
+ return Response.sendErrorResponse(
265
385
  req,
266
386
  res,
267
- `/usr/src/Common/UI/Images/favicon/status-green.png`,
387
+ new NotFoundException("Status Page cover image not found"),
268
388
  );
269
389
  }
390
+ },
391
+ );
270
392
 
271
- logger.debug(
272
- `Favicon file found. Sending file: ${statusPage.faviconFile.name}`,
273
- );
393
+ this.router.get(
394
+ `${new this.entityType()
395
+ .getCrudApiPath()
396
+ ?.toString()}/incident-public-note/attachment/:statusPageId/:incidentId/:noteId/:fileId`,
397
+ UserMiddleware.getUserMiddleware,
398
+ async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
399
+ try {
400
+ await this.getIncidentPublicNoteAttachment(req, res);
401
+ } catch (err) {
402
+ next(err);
403
+ }
404
+ },
405
+ );
274
406
 
275
- return Response.sendFileResponse(req, res, statusPage.faviconFile!);
407
+ this.router.get(
408
+ `${new this.entityType()
409
+ .getCrudApiPath()
410
+ ?.toString()}/scheduled-maintenance-public-note/attachment/:statusPageId/:scheduledMaintenanceId/:noteId/:fileId`,
411
+ UserMiddleware.getUserMiddleware,
412
+ async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
413
+ try {
414
+ await this.getScheduledMaintenancePublicNoteAttachment(req, res);
415
+ } catch (err) {
416
+ next(err);
417
+ }
418
+ },
419
+ );
420
+
421
+ this.router.get(
422
+ `${new this.entityType()
423
+ .getCrudApiPath()
424
+ ?.toString()}/status-page-announcement/attachment/:statusPageId/:announcementId/:fileId`,
425
+ UserMiddleware.getUserMiddleware,
426
+ async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
427
+ try {
428
+ await this.getStatusPageAnnouncementAttachment(req, res);
429
+ } catch (err) {
430
+ next(err);
431
+ }
276
432
  },
277
433
  );
278
434
 
@@ -1269,6 +1425,10 @@ export default class StatusPageAPI extends BaseAPI<
1269
1425
  note: true,
1270
1426
  incidentId: true,
1271
1427
  postedAt: true,
1428
+ attachments: {
1429
+ _id: true,
1430
+ name: true,
1431
+ },
1272
1432
  },
1273
1433
  sort: {
1274
1434
  postedAt: SortOrder.Descending, // new note first
@@ -1454,6 +1614,10 @@ export default class StatusPageAPI extends BaseAPI<
1454
1614
  postedAt: true,
1455
1615
  note: true,
1456
1616
  scheduledMaintenanceId: true,
1617
+ attachments: {
1618
+ _id: true,
1619
+ name: true,
1620
+ },
1457
1621
  },
1458
1622
  sort: {
1459
1623
  postedAt: SortOrder.Ascending,
@@ -1993,6 +2157,10 @@ export default class StatusPageAPI extends BaseAPI<
1993
2157
  postedAt: true,
1994
2158
  note: true,
1995
2159
  scheduledMaintenanceId: true,
2160
+ attachments: {
2161
+ _id: true,
2162
+ name: true,
2163
+ },
1996
2164
  },
1997
2165
  sort: {
1998
2166
  postedAt: SortOrder.Ascending,
@@ -2219,6 +2387,10 @@ export default class StatusPageAPI extends BaseAPI<
2219
2387
  _id: true,
2220
2388
  name: true,
2221
2389
  },
2390
+ attachments: {
2391
+ _id: true,
2392
+ name: true,
2393
+ },
2222
2394
  },
2223
2395
  skip: 0,
2224
2396
  limit: LIMIT_PER_PROJECT,
@@ -2531,6 +2703,8 @@ export default class StatusPageAPI extends BaseAPI<
2531
2703
  if (email) {
2532
2704
  const host: Hostname = await DatabaseConfig.getHost();
2533
2705
  const httpProtocol: Protocol = await DatabaseConfig.getHttpProtocol();
2706
+ const statusPageIdString: string | null =
2707
+ statusPage.id?.toString() || statusPage._id?.toString() || null;
2534
2708
 
2535
2709
  MailService.sendMail(
2536
2710
  {
@@ -2540,12 +2714,13 @@ export default class StatusPageAPI extends BaseAPI<
2540
2714
  vars: {
2541
2715
  statusPageName: statusPage.name || "Status Page",
2542
2716
  statusPageUrl: statusPageURL,
2543
- logoUrl: statusPage.logoFileId
2544
- ? new URL(httpProtocol, host)
2545
- .addRoute(FileRoute)
2546
- .addRoute("/image/" + statusPage.logoFileId)
2547
- .toString()
2548
- : "",
2717
+ logoUrl:
2718
+ statusPage.logoFileId && statusPageIdString
2719
+ ? new URL(httpProtocol, host)
2720
+ .addRoute(StatusPageApiRoute)
2721
+ .addRoute(`/logo/${statusPageIdString}`)
2722
+ .toString()
2723
+ : "",
2549
2724
  isPublicStatusPage: statusPage.isPublicStatusPage
2550
2725
  ? "true"
2551
2726
  : "false",
@@ -3155,6 +3330,10 @@ export default class StatusPageAPI extends BaseAPI<
3155
3330
  postedAt: true,
3156
3331
  note: true,
3157
3332
  incidentId: true,
3333
+ attachments: {
3334
+ _id: true,
3335
+ name: true,
3336
+ },
3158
3337
  },
3159
3338
  sort: {
3160
3339
  postedAt: SortOrder.Descending, // new note first
@@ -3489,6 +3668,353 @@ export default class StatusPageAPI extends BaseAPI<
3489
3668
  };
3490
3669
  }
3491
3670
 
3671
+ private async getStatusPageAnnouncementAttachment(
3672
+ req: ExpressRequest,
3673
+ res: ExpressResponse,
3674
+ ): Promise<void> {
3675
+ const statusPageIdParam: string | undefined = req.params["statusPageId"];
3676
+ const announcementIdParam: string | undefined =
3677
+ req.params["announcementId"];
3678
+ const fileIdParam: string | undefined = req.params["fileId"];
3679
+
3680
+ if (!statusPageIdParam || !announcementIdParam || !fileIdParam) {
3681
+ throw new NotFoundException("Attachment not found");
3682
+ }
3683
+
3684
+ let statusPageId: ObjectID;
3685
+ let announcementId: ObjectID;
3686
+ let fileId: ObjectID;
3687
+
3688
+ try {
3689
+ statusPageId = new ObjectID(statusPageIdParam);
3690
+ announcementId = new ObjectID(announcementIdParam);
3691
+ fileId = new ObjectID(fileIdParam);
3692
+ } catch {
3693
+ throw new NotFoundException("Attachment not found");
3694
+ }
3695
+
3696
+ await this.checkHasReadAccess({
3697
+ statusPageId: statusPageId,
3698
+ req: req,
3699
+ });
3700
+
3701
+ const statusPage: StatusPage | null = await StatusPageService.findOneBy({
3702
+ query: {
3703
+ _id: statusPageId.toString(),
3704
+ },
3705
+ select: {
3706
+ _id: true,
3707
+ projectId: true,
3708
+ showAnnouncementsOnStatusPage: true,
3709
+ },
3710
+ props: {
3711
+ isRoot: true,
3712
+ },
3713
+ });
3714
+
3715
+ if (
3716
+ !statusPage ||
3717
+ !statusPage.projectId ||
3718
+ !statusPage.showAnnouncementsOnStatusPage
3719
+ ) {
3720
+ throw new NotFoundException("Attachment not found");
3721
+ }
3722
+
3723
+ const announcement: StatusPageAnnouncement | null =
3724
+ await StatusPageAnnouncementService.findOneBy({
3725
+ query: {
3726
+ _id: announcementId.toString(),
3727
+ projectId: statusPage.projectId!,
3728
+ statusPages: [statusPageId] as any,
3729
+ },
3730
+ select: {
3731
+ attachments: {
3732
+ _id: true,
3733
+ file: true,
3734
+ fileType: true,
3735
+ name: true,
3736
+ },
3737
+ },
3738
+ props: {
3739
+ isRoot: true,
3740
+ },
3741
+ });
3742
+
3743
+ if (!announcement) {
3744
+ throw new NotFoundException("Attachment not found");
3745
+ }
3746
+
3747
+ const attachment: File | undefined = announcement.attachments?.find(
3748
+ (file: File) => {
3749
+ const attachmentId: string | null = file._id
3750
+ ? file._id.toString()
3751
+ : file.id
3752
+ ? file.id.toString()
3753
+ : null;
3754
+ return attachmentId === fileId.toString();
3755
+ },
3756
+ );
3757
+
3758
+ if (!attachment || !attachment.file) {
3759
+ throw new NotFoundException("Attachment not found");
3760
+ }
3761
+
3762
+ Response.setNoCacheHeaders(res);
3763
+ return Response.sendFileResponse(req, res, attachment);
3764
+ }
3765
+
3766
+ private async getScheduledMaintenancePublicNoteAttachment(
3767
+ req: ExpressRequest,
3768
+ res: ExpressResponse,
3769
+ ): Promise<void> {
3770
+ const statusPageIdParam: string | undefined = req.params["statusPageId"];
3771
+ const scheduledMaintenanceIdParam: string | undefined =
3772
+ req.params["scheduledMaintenanceId"];
3773
+ const noteIdParam: string | undefined = req.params["noteId"];
3774
+ const fileIdParam: string | undefined = req.params["fileId"];
3775
+
3776
+ if (
3777
+ !statusPageIdParam ||
3778
+ !scheduledMaintenanceIdParam ||
3779
+ !noteIdParam ||
3780
+ !fileIdParam
3781
+ ) {
3782
+ throw new NotFoundException("Attachment not found");
3783
+ }
3784
+
3785
+ let statusPageId: ObjectID;
3786
+ let scheduledMaintenanceId: ObjectID;
3787
+ let noteId: ObjectID;
3788
+ let fileId: ObjectID;
3789
+
3790
+ try {
3791
+ statusPageId = new ObjectID(statusPageIdParam);
3792
+ scheduledMaintenanceId = new ObjectID(scheduledMaintenanceIdParam);
3793
+ noteId = new ObjectID(noteIdParam);
3794
+ fileId = new ObjectID(fileIdParam);
3795
+ } catch {
3796
+ throw new NotFoundException("Attachment not found");
3797
+ }
3798
+
3799
+ await this.checkHasReadAccess({
3800
+ statusPageId: statusPageId,
3801
+ req: req,
3802
+ });
3803
+
3804
+ const statusPage: StatusPage | null = await StatusPageService.findOneBy({
3805
+ query: {
3806
+ _id: statusPageId.toString(),
3807
+ },
3808
+ select: {
3809
+ _id: true,
3810
+ projectId: true,
3811
+ showScheduledMaintenanceEventsOnStatusPage: true,
3812
+ },
3813
+ props: {
3814
+ isRoot: true,
3815
+ },
3816
+ });
3817
+
3818
+ if (
3819
+ !statusPage ||
3820
+ !statusPage.projectId ||
3821
+ !statusPage.showScheduledMaintenanceEventsOnStatusPage
3822
+ ) {
3823
+ throw new NotFoundException("Attachment not found");
3824
+ }
3825
+
3826
+ const scheduledMaintenance: ScheduledMaintenance | null =
3827
+ await ScheduledMaintenanceService.findOneBy({
3828
+ query: {
3829
+ _id: scheduledMaintenanceId.toString(),
3830
+ projectId: statusPage.projectId!,
3831
+ isVisibleOnStatusPage: true,
3832
+ statusPages: statusPageId as any,
3833
+ },
3834
+ select: {
3835
+ _id: true,
3836
+ },
3837
+ props: {
3838
+ isRoot: true,
3839
+ },
3840
+ });
3841
+
3842
+ if (!scheduledMaintenance) {
3843
+ throw new NotFoundException("Attachment not found");
3844
+ }
3845
+
3846
+ const scheduledMaintenancePublicNote: ScheduledMaintenancePublicNote | null =
3847
+ await ScheduledMaintenancePublicNoteService.findOneBy({
3848
+ query: {
3849
+ _id: noteId.toString(),
3850
+ scheduledMaintenanceId: scheduledMaintenanceId.toString(),
3851
+ projectId: statusPage.projectId!,
3852
+ },
3853
+ select: {
3854
+ attachments: {
3855
+ _id: true,
3856
+ file: true,
3857
+ fileType: true,
3858
+ name: true,
3859
+ },
3860
+ },
3861
+ props: {
3862
+ isRoot: true,
3863
+ },
3864
+ });
3865
+
3866
+ if (!scheduledMaintenancePublicNote) {
3867
+ throw new NotFoundException("Attachment not found");
3868
+ }
3869
+
3870
+ const attachment: File | undefined =
3871
+ scheduledMaintenancePublicNote.attachments?.find((file: File) => {
3872
+ const attachmentId: string | null = file._id
3873
+ ? file._id.toString()
3874
+ : file.id
3875
+ ? file.id.toString()
3876
+ : null;
3877
+ return attachmentId === fileId.toString();
3878
+ });
3879
+
3880
+ if (!attachment || !attachment.file) {
3881
+ throw new NotFoundException("Attachment not found");
3882
+ }
3883
+
3884
+ Response.setNoCacheHeaders(res);
3885
+ return Response.sendFileResponse(req, res, attachment);
3886
+ }
3887
+
3888
+ private async getIncidentPublicNoteAttachment(
3889
+ req: ExpressRequest,
3890
+ res: ExpressResponse,
3891
+ ): Promise<void> {
3892
+ const statusPageIdParam: string | undefined = req.params["statusPageId"];
3893
+ const incidentIdParam: string | undefined = req.params["incidentId"];
3894
+ const noteIdParam: string | undefined = req.params["noteId"];
3895
+ const fileIdParam: string | undefined = req.params["fileId"];
3896
+
3897
+ if (
3898
+ !statusPageIdParam ||
3899
+ !incidentIdParam ||
3900
+ !noteIdParam ||
3901
+ !fileIdParam
3902
+ ) {
3903
+ throw new NotFoundException("Attachment not found");
3904
+ }
3905
+
3906
+ let statusPageId: ObjectID;
3907
+ let incidentId: ObjectID;
3908
+ let noteId: ObjectID;
3909
+ let fileId: ObjectID;
3910
+
3911
+ try {
3912
+ statusPageId = new ObjectID(statusPageIdParam);
3913
+ incidentId = new ObjectID(incidentIdParam);
3914
+ noteId = new ObjectID(noteIdParam);
3915
+ fileId = new ObjectID(fileIdParam);
3916
+ } catch {
3917
+ throw new NotFoundException("Attachment not found");
3918
+ }
3919
+
3920
+ await this.checkHasReadAccess({
3921
+ statusPageId: statusPageId,
3922
+ req: req,
3923
+ });
3924
+
3925
+ const statusPage: StatusPage | null = await StatusPageService.findOneBy({
3926
+ query: {
3927
+ _id: statusPageId.toString(),
3928
+ },
3929
+ select: {
3930
+ _id: true,
3931
+ projectId: true,
3932
+ showIncidentsOnStatusPage: true,
3933
+ },
3934
+ props: {
3935
+ isRoot: true,
3936
+ },
3937
+ });
3938
+
3939
+ if (!statusPage || !statusPage.projectId) {
3940
+ throw new NotFoundException("Attachment not found");
3941
+ }
3942
+
3943
+ if (!statusPage.showIncidentsOnStatusPage) {
3944
+ throw new NotFoundException("Attachment not found");
3945
+ }
3946
+
3947
+ const { monitorsOnStatusPage } =
3948
+ await StatusPageService.getMonitorIdsOnStatusPage({
3949
+ statusPageId: statusPageId,
3950
+ });
3951
+
3952
+ if (!monitorsOnStatusPage || monitorsOnStatusPage.length === 0) {
3953
+ throw new NotFoundException("Attachment not found");
3954
+ }
3955
+
3956
+ const incident: Incident | null = await IncidentService.findOneBy({
3957
+ query: {
3958
+ _id: incidentId.toString(),
3959
+ projectId: statusPage.projectId!,
3960
+ isVisibleOnStatusPage: true,
3961
+ monitors: monitorsOnStatusPage as any,
3962
+ },
3963
+ select: {
3964
+ _id: true,
3965
+ },
3966
+ props: {
3967
+ isRoot: true,
3968
+ },
3969
+ });
3970
+
3971
+ if (!incident) {
3972
+ throw new NotFoundException("Attachment not found");
3973
+ }
3974
+
3975
+ const incidentPublicNote: IncidentPublicNote | null =
3976
+ await IncidentPublicNoteService.findOneBy({
3977
+ query: {
3978
+ _id: noteId.toString(),
3979
+ incidentId: incidentId.toString(),
3980
+ projectId: statusPage.projectId!,
3981
+ },
3982
+ select: {
3983
+ attachments: {
3984
+ _id: true,
3985
+ file: true,
3986
+ fileType: true,
3987
+ name: true,
3988
+ },
3989
+ },
3990
+ props: {
3991
+ isRoot: true,
3992
+ },
3993
+ });
3994
+
3995
+ if (!incidentPublicNote) {
3996
+ throw new NotFoundException("Attachment not found");
3997
+ }
3998
+
3999
+ const attachment: File | undefined = incidentPublicNote.attachments?.find(
4000
+ (file: File) => {
4001
+ const attachmentId: string | null = file._id
4002
+ ? file._id.toString()
4003
+ : file.id
4004
+ ? file.id.toString()
4005
+ : null;
4006
+ return attachmentId === fileId.toString();
4007
+ },
4008
+ );
4009
+
4010
+ if (!attachment || !attachment.file) {
4011
+ throw new NotFoundException("Attachment not found");
4012
+ }
4013
+
4014
+ Response.setNoCacheHeaders(res);
4015
+ return Response.sendFileResponse(req, res, attachment);
4016
+ }
4017
+
3492
4018
  public async checkHasReadAccess(data: {
3493
4019
  statusPageId: ObjectID;
3494
4020
  req: ExpressRequest;