@oneuptime/common 7.0.4066 → 7.0.4078
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.
- package/Server/API/StatusPageAPI.ts +426 -0
- package/Server/EnvironmentConfig.ts +7 -0
- package/Server/Services/OnCallDutyPolicyScheduleService.ts +1 -1
- package/Server/Utils/StartServer.ts +34 -5
- package/Types/Email/EmailTemplateType.ts +1 -0
- package/UI/Components/Button/DropdownButton.tsx +93 -0
- package/UI/Components/Forms/BasicForm.tsx +14 -2
- package/UI/Components/Forms/BasicModelForm.tsx +8 -1
- package/UI/Components/Forms/Fields/FormField.tsx +32 -18
- package/UI/Components/Forms/ModelForm.tsx +13 -3
- package/UI/Components/Forms/Types/Field.ts +7 -1
- package/UI/Images/favicon/status-green.png +0 -0
- package/build/dist/Server/API/StatusPageAPI.js +320 -36
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +2 -1
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleService.js +1 -1
- package/build/dist/Server/Utils/StartServer.js +16 -4
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Types/Email/EmailTemplateType.js +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/UI/Components/Button/DropdownButton.js +20 -0
- package/build/dist/UI/Components/Button/DropdownButton.js.map +1 -0
- package/build/dist/UI/Components/Forms/BasicForm.js +8 -2
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicModelForm.js +1 -1
- package/build/dist/UI/Components/Forms/BasicModelForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +23 -18
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +2 -2
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/package.json +2 -2
- package/UI/webpack-middleware.js +0 -65
|
@@ -72,13 +72,134 @@ import UptimePrecision from "../../Types/StatusPage/UptimePrecision";
|
|
|
72
72
|
import { Green } from "../../Types/BrandColors";
|
|
73
73
|
import UptimeUtil from "../../Utils/Uptime/UptimeUtil";
|
|
74
74
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
75
|
+
import URL from "Common/Types/API/URL";
|
|
76
|
+
import SmsService from "../Services/SmsService";
|
|
77
|
+
import ProjectCallSMSConfigService from "../Services/ProjectCallSMSConfigService";
|
|
78
|
+
import MailService from "../Services/MailService";
|
|
79
|
+
import EmailTemplateType from "../../Types/Email/EmailTemplateType";
|
|
80
|
+
import DatabaseConfig from "../DatabaseConfig";
|
|
81
|
+
import { FileRoute } from "../../ServiceRoute";
|
|
82
|
+
import ProjectSmtpConfigService from "../Services/ProjectSmtpConfigService";
|
|
75
83
|
export default class StatusPageAPI extends BaseAPI {
|
|
76
84
|
constructor() {
|
|
77
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
|
|
85
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
|
|
78
86
|
super(StatusPage, StatusPageService);
|
|
87
|
+
// get title, description of the page. This is used for SEO.
|
|
88
|
+
this.router.get(`${(_a = new this.entityType().getCrudApiPath()) === null || _a === void 0 ? void 0 : _a.toString()}/seo/:statusPageIdOrDomain`, UserMiddleware.getUserMiddleware, async (req, res) => {
|
|
89
|
+
const statusPageIdOrDomain = req.params["statusPageIdOrDomain"];
|
|
90
|
+
let statusPageId = null;
|
|
91
|
+
if (statusPageIdOrDomain && statusPageIdOrDomain.includes(".")) {
|
|
92
|
+
// then this is a domain and not the status page id. We need to get the status page id from the domain.
|
|
93
|
+
const statusPageDomain = await StatusPageDomainService.findOneBy({
|
|
94
|
+
query: {
|
|
95
|
+
fullDomain: statusPageIdOrDomain,
|
|
96
|
+
domain: {
|
|
97
|
+
isVerified: true,
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
select: {
|
|
101
|
+
statusPageId: true,
|
|
102
|
+
},
|
|
103
|
+
props: {
|
|
104
|
+
isRoot: true,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
if (!statusPageDomain || !statusPageDomain.statusPageId) {
|
|
108
|
+
return Response.sendErrorResponse(req, res, new NotFoundException("Status Page not found"));
|
|
109
|
+
}
|
|
110
|
+
statusPageId = statusPageDomain.statusPageId;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
// then this is a status page id. We need to get the status page id from the id.
|
|
114
|
+
try {
|
|
115
|
+
statusPageId = new ObjectID(statusPageIdOrDomain);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
return Response.sendErrorResponse(req, res, new NotFoundException("Status Page not found"));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const statusPage = await StatusPageService.findOneBy({
|
|
122
|
+
query: {
|
|
123
|
+
_id: statusPageId,
|
|
124
|
+
},
|
|
125
|
+
select: {
|
|
126
|
+
pageTitle: true,
|
|
127
|
+
pageDescription: true,
|
|
128
|
+
name: true,
|
|
129
|
+
},
|
|
130
|
+
props: {
|
|
131
|
+
isRoot: true,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
if (!statusPage) {
|
|
135
|
+
return Response.sendErrorResponse(req, res, new NotFoundException("Status Page not found"));
|
|
136
|
+
}
|
|
137
|
+
return Response.sendJsonObjectResponse(req, res, {
|
|
138
|
+
title: statusPage.pageTitle || statusPage.name,
|
|
139
|
+
description: statusPage.pageDescription,
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
// favicon api.
|
|
143
|
+
this.router.get(`${(_b = new this.entityType().getCrudApiPath()) === null || _b === void 0 ? void 0 : _b.toString()}/favicon/:statusPageIdOrDomain`, async (req, res) => {
|
|
144
|
+
const statusPageIdOrDomain = req.params["statusPageIdOrDomain"];
|
|
145
|
+
let statusPageId = null;
|
|
146
|
+
if (statusPageIdOrDomain && statusPageIdOrDomain.includes(".")) {
|
|
147
|
+
// then this is a domain and not the status page id. We need to get the status page id from the domain.
|
|
148
|
+
const statusPageDomain = await StatusPageDomainService.findOneBy({
|
|
149
|
+
query: {
|
|
150
|
+
fullDomain: statusPageIdOrDomain,
|
|
151
|
+
domain: {
|
|
152
|
+
isVerified: true,
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
select: {
|
|
156
|
+
statusPageId: true,
|
|
157
|
+
},
|
|
158
|
+
props: {
|
|
159
|
+
isRoot: true,
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
if (!statusPageDomain || !statusPageDomain.statusPageId) {
|
|
163
|
+
return Response.sendErrorResponse(req, res, new NotFoundException("Status Page not found"));
|
|
164
|
+
}
|
|
165
|
+
statusPageId = statusPageDomain.statusPageId;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// then this is a status page id. We need to get the status page id from the id.
|
|
169
|
+
try {
|
|
170
|
+
statusPageId = new ObjectID(statusPageIdOrDomain);
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
return Response.sendErrorResponse(req, res, new NotFoundException("Status Page not found"));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const statusPage = await StatusPageService.findOneBy({
|
|
177
|
+
query: {
|
|
178
|
+
_id: statusPageId,
|
|
179
|
+
},
|
|
180
|
+
select: {
|
|
181
|
+
faviconFile: {
|
|
182
|
+
file: true,
|
|
183
|
+
_id: true,
|
|
184
|
+
type: true,
|
|
185
|
+
name: true,
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
props: {
|
|
189
|
+
isRoot: true,
|
|
190
|
+
},
|
|
191
|
+
});
|
|
192
|
+
if (!statusPage || !statusPage.faviconFile) {
|
|
193
|
+
logger.debug("Favicon file not found. Returning default favicon.");
|
|
194
|
+
// return default favicon.
|
|
195
|
+
return Response.sendFileByPath(req, res, `/usr/src/Common/UI/Images/favicon/status-green.png`);
|
|
196
|
+
}
|
|
197
|
+
logger.debug(`Favicon file found. Sending file: ${statusPage.faviconFile.name}`);
|
|
198
|
+
return Response.sendFileResponse(req, res, statusPage.faviconFile);
|
|
199
|
+
});
|
|
79
200
|
// confirm subscription api
|
|
80
|
-
this.router.get(`${(
|
|
81
|
-
.getCrudApiPath()) === null ||
|
|
201
|
+
this.router.get(`${(_c = new this.entityType()
|
|
202
|
+
.getCrudApiPath()) === null || _c === void 0 ? void 0 : _c.toString()}/confirm-subscription/:statusPageSubscriberId`, async (req, res) => {
|
|
82
203
|
const token = req.query["verification-token"];
|
|
83
204
|
const statusPageSubscriberId = new ObjectID(req.params["statusPageSubscriberId"]);
|
|
84
205
|
const subscriber = await StatusPageSubscriberService.findOneBy({
|
|
@@ -115,8 +236,8 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
115
236
|
return Response.sendEmptySuccessResponse(req, res);
|
|
116
237
|
});
|
|
117
238
|
// CNAME verification api
|
|
118
|
-
this.router.get(`${(
|
|
119
|
-
.getCrudApiPath()) === null ||
|
|
239
|
+
this.router.get(`${(_d = new this.entityType()
|
|
240
|
+
.getCrudApiPath()) === null || _d === void 0 ? void 0 : _d.toString()}/cname-verification/:token`, async (req, res) => {
|
|
120
241
|
const host = req.get("host");
|
|
121
242
|
if (!host) {
|
|
122
243
|
throw new BadDataException("Host not found");
|
|
@@ -141,8 +262,8 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
141
262
|
return Response.sendEmptySuccessResponse(req, res);
|
|
142
263
|
});
|
|
143
264
|
// ACME Challenge Validation.
|
|
144
|
-
this.router.get(`${(
|
|
145
|
-
.getCrudApiPath()) === null ||
|
|
265
|
+
this.router.get(`${(_e = new this.entityType()
|
|
266
|
+
.getCrudApiPath()) === null || _e === void 0 ? void 0 : _e.toString()}/.well-known/acme-challenge/:token`, async (req, res) => {
|
|
146
267
|
const challenge = await AcmeChallengeService.findOneBy({
|
|
147
268
|
query: {
|
|
148
269
|
token: req.params["token"],
|
|
@@ -159,7 +280,7 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
159
280
|
}
|
|
160
281
|
return Response.sendTextResponse(req, res, challenge.challenge);
|
|
161
282
|
});
|
|
162
|
-
this.router.post(`${(
|
|
283
|
+
this.router.post(`${(_f = new this.entityType().getCrudApiPath()) === null || _f === void 0 ? void 0 : _f.toString()}/test-email-report`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
163
284
|
try {
|
|
164
285
|
const email = new Email(req.body["email"]);
|
|
165
286
|
const statusPageId = new ObjectID(req.body["statusPageId"].toString());
|
|
@@ -173,7 +294,7 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
173
294
|
next(err);
|
|
174
295
|
}
|
|
175
296
|
});
|
|
176
|
-
this.router.post(`${(
|
|
297
|
+
this.router.post(`${(_g = new this.entityType().getCrudApiPath()) === null || _g === void 0 ? void 0 : _g.toString()}/domain`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
177
298
|
try {
|
|
178
299
|
if (!req.body["domain"]) {
|
|
179
300
|
throw new BadDataException("domain is required in request body");
|
|
@@ -205,8 +326,8 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
205
326
|
next(err);
|
|
206
327
|
}
|
|
207
328
|
});
|
|
208
|
-
this.router.post(`${(
|
|
209
|
-
.getCrudApiPath()) === null ||
|
|
329
|
+
this.router.post(`${(_h = new this.entityType()
|
|
330
|
+
.getCrudApiPath()) === null || _h === void 0 ? void 0 : _h.toString()}/master-page/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
210
331
|
try {
|
|
211
332
|
const objectId = new ObjectID(req.params["statusPageId"]);
|
|
212
333
|
const select = {
|
|
@@ -320,7 +441,7 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
320
441
|
next(err);
|
|
321
442
|
}
|
|
322
443
|
});
|
|
323
|
-
this.router.post(`${(
|
|
444
|
+
this.router.post(`${(_j = new this.entityType().getCrudApiPath()) === null || _j === void 0 ? void 0 : _j.toString()}/sso/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
324
445
|
try {
|
|
325
446
|
const objectId = new ObjectID(req.params["statusPageId"]);
|
|
326
447
|
const sso = await StatusPageSsoService.findBy({
|
|
@@ -347,8 +468,8 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
347
468
|
}
|
|
348
469
|
});
|
|
349
470
|
// Get all status page resources for subscriber to subscribe to.
|
|
350
|
-
this.router.post(`${(
|
|
351
|
-
.getCrudApiPath()) === null ||
|
|
471
|
+
this.router.post(`${(_k = new this.entityType()
|
|
472
|
+
.getCrudApiPath()) === null || _k === void 0 ? void 0 : _k.toString()}/resources/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
352
473
|
try {
|
|
353
474
|
const objectId = new ObjectID(req.params["statusPageId"]);
|
|
354
475
|
if (!(await this.service.hasReadAccess(objectId, await CommonAPI.getDatabaseCommonInteractionProps(req), req))) {
|
|
@@ -380,8 +501,8 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
380
501
|
next(err);
|
|
381
502
|
}
|
|
382
503
|
});
|
|
383
|
-
this.router.post(`${(
|
|
384
|
-
.getCrudApiPath()) === null ||
|
|
504
|
+
this.router.post(`${(_l = new this.entityType()
|
|
505
|
+
.getCrudApiPath()) === null || _l === void 0 ? void 0 : _l.toString()}/uptime/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
385
506
|
try {
|
|
386
507
|
// This reosurce ID can be of a status page resource OR a status page group.
|
|
387
508
|
const statusPageResourceId = new ObjectID(req.params["statusPageResourceId"]);
|
|
@@ -558,8 +679,8 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
558
679
|
next(err);
|
|
559
680
|
}
|
|
560
681
|
});
|
|
561
|
-
this.router.post(`${(
|
|
562
|
-
.getCrudApiPath()) === null ||
|
|
682
|
+
this.router.post(`${(_m = new this.entityType()
|
|
683
|
+
.getCrudApiPath()) === null || _m === void 0 ? void 0 : _m.toString()}/overview/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
563
684
|
try {
|
|
564
685
|
const objectId = new ObjectID(req.params["statusPageId"]);
|
|
565
686
|
if (!(await this.service.hasReadAccess(objectId, await CommonAPI.getDatabaseCommonInteractionProps(req), req))) {
|
|
@@ -886,8 +1007,8 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
886
1007
|
next(err);
|
|
887
1008
|
}
|
|
888
1009
|
});
|
|
889
|
-
this.router.put(`${(
|
|
890
|
-
.getCrudApiPath()) === null ||
|
|
1010
|
+
this.router.put(`${(_o = new this.entityType()
|
|
1011
|
+
.getCrudApiPath()) === null || _o === void 0 ? void 0 : _o.toString()}/update-subscription/:statusPageId/:subscriberId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
891
1012
|
try {
|
|
892
1013
|
await this.subscribeToStatusPage(req);
|
|
893
1014
|
return Response.sendEmptySuccessResponse(req, res);
|
|
@@ -896,8 +1017,8 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
896
1017
|
next(err);
|
|
897
1018
|
}
|
|
898
1019
|
});
|
|
899
|
-
this.router.post(`${(
|
|
900
|
-
.getCrudApiPath()) === null ||
|
|
1020
|
+
this.router.post(`${(_p = new this.entityType()
|
|
1021
|
+
.getCrudApiPath()) === null || _p === void 0 ? void 0 : _p.toString()}/get-subscription/:statusPageId/:subscriberId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
901
1022
|
try {
|
|
902
1023
|
const subscriber = await this.getSubscriber(req);
|
|
903
1024
|
return Response.sendEntityResponse(req, res, subscriber, StatusPageSubscriber);
|
|
@@ -906,8 +1027,8 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
906
1027
|
next(err);
|
|
907
1028
|
}
|
|
908
1029
|
});
|
|
909
|
-
this.router.post(`${(
|
|
910
|
-
.getCrudApiPath()) === null ||
|
|
1030
|
+
this.router.post(`${(_q = new this.entityType()
|
|
1031
|
+
.getCrudApiPath()) === null || _q === void 0 ? void 0 : _q.toString()}/subscribe/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
911
1032
|
try {
|
|
912
1033
|
await this.subscribeToStatusPage(req);
|
|
913
1034
|
return Response.sendEmptySuccessResponse(req, res);
|
|
@@ -916,8 +1037,18 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
916
1037
|
next(err);
|
|
917
1038
|
}
|
|
918
1039
|
});
|
|
919
|
-
this.router.post(`${(
|
|
920
|
-
.getCrudApiPath()) === null ||
|
|
1040
|
+
this.router.post(`${(_r = new this.entityType()
|
|
1041
|
+
.getCrudApiPath()) === null || _r === void 0 ? void 0 : _r.toString()}/manage-subscription/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
1042
|
+
try {
|
|
1043
|
+
await this.manageExistingSubscription(req);
|
|
1044
|
+
return Response.sendEmptySuccessResponse(req, res);
|
|
1045
|
+
}
|
|
1046
|
+
catch (err) {
|
|
1047
|
+
next(err);
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
this.router.post(`${(_s = new this.entityType()
|
|
1051
|
+
.getCrudApiPath()) === null || _s === void 0 ? void 0 : _s.toString()}/incidents/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
921
1052
|
try {
|
|
922
1053
|
const objectId = new ObjectID(req.params["statusPageId"]);
|
|
923
1054
|
const response = await this.getIncidents(objectId, null, await CommonAPI.getDatabaseCommonInteractionProps(req), req);
|
|
@@ -927,8 +1058,8 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
927
1058
|
next(err);
|
|
928
1059
|
}
|
|
929
1060
|
});
|
|
930
|
-
this.router.post(`${(
|
|
931
|
-
.getCrudApiPath()) === null ||
|
|
1061
|
+
this.router.post(`${(_t = new this.entityType()
|
|
1062
|
+
.getCrudApiPath()) === null || _t === void 0 ? void 0 : _t.toString()}/scheduled-maintenance-events/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
932
1063
|
try {
|
|
933
1064
|
const objectId = new ObjectID(req.params["statusPageId"]);
|
|
934
1065
|
const response = await this.getScheduledMaintenanceEvents(objectId, null, await CommonAPI.getDatabaseCommonInteractionProps(req), req);
|
|
@@ -938,8 +1069,8 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
938
1069
|
next(err);
|
|
939
1070
|
}
|
|
940
1071
|
});
|
|
941
|
-
this.router.post(`${(
|
|
942
|
-
.getCrudApiPath()) === null ||
|
|
1072
|
+
this.router.post(`${(_u = new this.entityType()
|
|
1073
|
+
.getCrudApiPath()) === null || _u === void 0 ? void 0 : _u.toString()}/announcements/:statusPageId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
943
1074
|
try {
|
|
944
1075
|
const objectId = new ObjectID(req.params["statusPageId"]);
|
|
945
1076
|
const response = await this.getAnnouncements(objectId, null, await CommonAPI.getDatabaseCommonInteractionProps(req), req);
|
|
@@ -949,8 +1080,8 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
949
1080
|
next(err);
|
|
950
1081
|
}
|
|
951
1082
|
});
|
|
952
|
-
this.router.post(`${(
|
|
953
|
-
.getCrudApiPath()) === null ||
|
|
1083
|
+
this.router.post(`${(_v = new this.entityType()
|
|
1084
|
+
.getCrudApiPath()) === null || _v === void 0 ? void 0 : _v.toString()}/incidents/:statusPageId/:incidentId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
954
1085
|
try {
|
|
955
1086
|
const objectId = new ObjectID(req.params["statusPageId"]);
|
|
956
1087
|
const incidentId = new ObjectID(req.params["incidentId"]);
|
|
@@ -961,8 +1092,8 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
961
1092
|
next(err);
|
|
962
1093
|
}
|
|
963
1094
|
});
|
|
964
|
-
this.router.post(`${(
|
|
965
|
-
.getCrudApiPath()) === null ||
|
|
1095
|
+
this.router.post(`${(_w = new this.entityType()
|
|
1096
|
+
.getCrudApiPath()) === null || _w === void 0 ? void 0 : _w.toString()}/scheduled-maintenance-events/:statusPageId/:scheduledMaintenanceId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
966
1097
|
try {
|
|
967
1098
|
const objectId = new ObjectID(req.params["statusPageId"]);
|
|
968
1099
|
const scheduledMaintenanceId = new ObjectID(req.params["scheduledMaintenanceId"]);
|
|
@@ -973,8 +1104,8 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
973
1104
|
next(err);
|
|
974
1105
|
}
|
|
975
1106
|
});
|
|
976
|
-
this.router.post(`${(
|
|
977
|
-
.getCrudApiPath()) === null ||
|
|
1107
|
+
this.router.post(`${(_x = new this.entityType()
|
|
1108
|
+
.getCrudApiPath()) === null || _x === void 0 ? void 0 : _x.toString()}/announcements/:statusPageId/:announcementId`, UserMiddleware.getUserMiddleware, async (req, res, next) => {
|
|
978
1109
|
try {
|
|
979
1110
|
const objectId = new ObjectID(req.params["statusPageId"]);
|
|
980
1111
|
const announcementId = new ObjectID(req.params["announcementId"]);
|
|
@@ -1310,6 +1441,153 @@ export default class StatusPageAPI extends BaseAPI {
|
|
|
1310
1441
|
};
|
|
1311
1442
|
return response;
|
|
1312
1443
|
}
|
|
1444
|
+
async manageExistingSubscription(req) {
|
|
1445
|
+
const objectId = new ObjectID(req.params["statusPageId"]);
|
|
1446
|
+
logger.debug(`Managing Existing Subscription for Status Page: ${objectId}`);
|
|
1447
|
+
if (!(await this.service.hasReadAccess(objectId, await CommonAPI.getDatabaseCommonInteractionProps(req), req))) {
|
|
1448
|
+
logger.debug(`No read access to status page with ID: ${objectId}`);
|
|
1449
|
+
throw new NotAuthenticatedException("You are not authenticated to access this status page");
|
|
1450
|
+
}
|
|
1451
|
+
const statusPage = await StatusPageService.findOneBy({
|
|
1452
|
+
query: {
|
|
1453
|
+
_id: objectId.toString(),
|
|
1454
|
+
},
|
|
1455
|
+
select: {
|
|
1456
|
+
_id: true,
|
|
1457
|
+
projectId: true,
|
|
1458
|
+
enableEmailSubscribers: true,
|
|
1459
|
+
enableSmsSubscribers: true,
|
|
1460
|
+
allowSubscribersToChooseResources: true,
|
|
1461
|
+
allowSubscribersToChooseEventTypes: true,
|
|
1462
|
+
showSubscriberPageOnStatusPage: true,
|
|
1463
|
+
},
|
|
1464
|
+
props: {
|
|
1465
|
+
isRoot: true,
|
|
1466
|
+
},
|
|
1467
|
+
});
|
|
1468
|
+
if (!statusPage) {
|
|
1469
|
+
logger.debug(`Status page not found with ID: ${objectId}`);
|
|
1470
|
+
throw new BadDataException("Status Page not found");
|
|
1471
|
+
}
|
|
1472
|
+
if (!statusPage.showSubscriberPageOnStatusPage) {
|
|
1473
|
+
logger.debug(`Subscriber page not enabled for status page with ID: ${objectId}`);
|
|
1474
|
+
throw new BadDataException("Subscribes not enabled for this status page.");
|
|
1475
|
+
}
|
|
1476
|
+
logger.debug(`Status page found: ${JSON.stringify(statusPage)}`);
|
|
1477
|
+
if (req.body.data["subscriberEmail"] &&
|
|
1478
|
+
!statusPage.enableEmailSubscribers) {
|
|
1479
|
+
logger.debug(`Email subscribers not enabled for status page with ID: ${objectId}`);
|
|
1480
|
+
throw new BadDataException("Email subscribers not enabled for this status page.");
|
|
1481
|
+
}
|
|
1482
|
+
if (req.body.data["subscriberPhone"] && !statusPage.enableSmsSubscribers) {
|
|
1483
|
+
logger.debug(`SMS subscribers not enabled for status page with ID: ${objectId}`);
|
|
1484
|
+
throw new BadDataException("SMS subscribers not enabled for this status page.");
|
|
1485
|
+
}
|
|
1486
|
+
// if no email or phone, throw error.
|
|
1487
|
+
if (!req.body.data["subscriberEmail"] &&
|
|
1488
|
+
!req.body.data["subscriberPhone"]) {
|
|
1489
|
+
logger.debug(`No email or phone provided for subscription to status page with ID: ${objectId}`);
|
|
1490
|
+
throw new BadDataException("Email or phone is required to subscribe to this status page.");
|
|
1491
|
+
}
|
|
1492
|
+
const email = req.body.data["subscriberEmail"]
|
|
1493
|
+
? new Email(req.body.data["subscriberEmail"])
|
|
1494
|
+
: undefined;
|
|
1495
|
+
const phone = req.body.data["subscriberPhone"]
|
|
1496
|
+
? new Phone(req.body.data["subscriberPhone"])
|
|
1497
|
+
: undefined;
|
|
1498
|
+
let statusPageSubscriber = null;
|
|
1499
|
+
if (email) {
|
|
1500
|
+
logger.debug(`Setting subscriber email: ${email}`);
|
|
1501
|
+
statusPageSubscriber = await StatusPageSubscriberService.findOneBy({
|
|
1502
|
+
query: {
|
|
1503
|
+
subscriberEmail: email,
|
|
1504
|
+
statusPageId: objectId,
|
|
1505
|
+
},
|
|
1506
|
+
select: {
|
|
1507
|
+
_id: true,
|
|
1508
|
+
subscriberEmail: true,
|
|
1509
|
+
},
|
|
1510
|
+
props: {
|
|
1511
|
+
isRoot: true,
|
|
1512
|
+
},
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
if (phone) {
|
|
1516
|
+
logger.debug(`Setting subscriber phone: ${phone}`);
|
|
1517
|
+
statusPageSubscriber = await StatusPageSubscriberService.findOneBy({
|
|
1518
|
+
query: {
|
|
1519
|
+
subscriberPhone: phone,
|
|
1520
|
+
statusPageId: objectId,
|
|
1521
|
+
},
|
|
1522
|
+
select: {
|
|
1523
|
+
_id: true,
|
|
1524
|
+
subscriberPhone: true,
|
|
1525
|
+
},
|
|
1526
|
+
props: {
|
|
1527
|
+
isRoot: true,
|
|
1528
|
+
},
|
|
1529
|
+
});
|
|
1530
|
+
}
|
|
1531
|
+
if (!statusPageSubscriber) {
|
|
1532
|
+
// not found, return bad data
|
|
1533
|
+
logger.debug(`Subscriber not found for email: ${email} or phone: ${phone}`);
|
|
1534
|
+
let emailOrPhone = "email";
|
|
1535
|
+
if (phone) {
|
|
1536
|
+
emailOrPhone = "phone";
|
|
1537
|
+
}
|
|
1538
|
+
throw new BadDataException(`Subscription not found for this status page. Please make sure your ${emailOrPhone} is correct.`);
|
|
1539
|
+
}
|
|
1540
|
+
const statusPageURL = await StatusPageService.getStatusPageURL(objectId);
|
|
1541
|
+
const manageUrlink = StatusPageSubscriberService.getUnsubscribeLink(URL.fromString(statusPageURL), statusPageSubscriber.id).toString();
|
|
1542
|
+
const statusPages = await StatusPageSubscriberService.getStatusPagesToSendNotification([
|
|
1543
|
+
objectId,
|
|
1544
|
+
]);
|
|
1545
|
+
for (const statusPage of statusPages) {
|
|
1546
|
+
// send email to subscriber or sms if phone is provided.
|
|
1547
|
+
if (email) {
|
|
1548
|
+
const host = await DatabaseConfig.getHost();
|
|
1549
|
+
const httpProtocol = await DatabaseConfig.getHttpProtocol();
|
|
1550
|
+
MailService.sendMail({
|
|
1551
|
+
toEmail: email,
|
|
1552
|
+
templateType: EmailTemplateType.ManageExistingStatusPageSubscriberSubscription,
|
|
1553
|
+
vars: {
|
|
1554
|
+
statusPageName: statusPage.name || "Status Page",
|
|
1555
|
+
statusPageUrl: statusPageURL,
|
|
1556
|
+
logoUrl: statusPage.logoFileId
|
|
1557
|
+
? new URL(httpProtocol, host)
|
|
1558
|
+
.addRoute(FileRoute)
|
|
1559
|
+
.addRoute("/image/" + statusPage.logoFileId)
|
|
1560
|
+
.toString()
|
|
1561
|
+
: "",
|
|
1562
|
+
isPublicStatusPage: statusPage.isPublicStatusPage
|
|
1563
|
+
? "true"
|
|
1564
|
+
: "false",
|
|
1565
|
+
subscriberEmailNotificationFooterText: statusPage.subscriberEmailNotificationFooterText || "",
|
|
1566
|
+
manageSubscriptionUrl: manageUrlink,
|
|
1567
|
+
},
|
|
1568
|
+
subject: "Manage your Subscription for" +
|
|
1569
|
+
(statusPage.name || "Status Page"),
|
|
1570
|
+
}, {
|
|
1571
|
+
mailServer: ProjectSmtpConfigService.toEmailServer(statusPage.smtpConfig),
|
|
1572
|
+
projectId: statusPage.projectId,
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
if (phone) {
|
|
1576
|
+
const sms = {
|
|
1577
|
+
message: `You have selected to manage your subscription for the status page: ${statusPage.name}. You can manage your subscription here: ${manageUrlink}`,
|
|
1578
|
+
to: phone,
|
|
1579
|
+
};
|
|
1580
|
+
// send sms here.
|
|
1581
|
+
SmsService.sendSms(sms, {
|
|
1582
|
+
projectId: statusPage.projectId,
|
|
1583
|
+
customTwilioConfig: ProjectCallSMSConfigService.toTwilioConfig(statusPage.callSmsConfig),
|
|
1584
|
+
}).catch((err) => {
|
|
1585
|
+
logger.error(err);
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1588
|
+
logger.debug(`Subscription management link sent to subscriber with ID: ${statusPageSubscriber.id}`);
|
|
1589
|
+
}
|
|
1590
|
+
}
|
|
1313
1591
|
async subscribeToStatusPage(req) {
|
|
1314
1592
|
const objectId = new ObjectID(req.params["statusPageId"]);
|
|
1315
1593
|
logger.debug(`Subscribing to status page with ID: ${objectId}`);
|
|
@@ -1933,6 +2211,12 @@ __decorate([
|
|
|
1933
2211
|
__metadata("design:paramtypes", [ObjectID, Object, Object, Object]),
|
|
1934
2212
|
__metadata("design:returntype", Promise)
|
|
1935
2213
|
], StatusPageAPI.prototype, "getAnnouncements", null);
|
|
2214
|
+
__decorate([
|
|
2215
|
+
CaptureSpan(),
|
|
2216
|
+
__metadata("design:type", Function),
|
|
2217
|
+
__metadata("design:paramtypes", [Object]),
|
|
2218
|
+
__metadata("design:returntype", Promise)
|
|
2219
|
+
], StatusPageAPI.prototype, "manageExistingSubscription", null);
|
|
1936
2220
|
__decorate([
|
|
1937
2221
|
CaptureSpan(),
|
|
1938
2222
|
__metadata("design:type", Function),
|