@oneuptime/common 9.2.22 → 9.2.25
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/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.ts +10 -0
- package/Models/DatabaseModels/Alert.ts +2 -0
- package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +2 -0
- package/Models/DatabaseModels/Incident.ts +2 -0
- package/Models/DatabaseModels/IncidentState.ts +2 -0
- package/Models/DatabaseModels/Monitor.ts +2 -0
- package/Models/DatabaseModels/MonitorStatus.ts +2 -0
- package/Models/DatabaseModels/OnCallDutyPolicy.ts +2 -0
- package/Models/DatabaseModels/ScheduledMaintenance.ts +2 -0
- package/Models/DatabaseModels/StatusPage.ts +2 -0
- package/Models/DatabaseModels/Team.ts +2 -0
- package/Server/API/BaseAPI.ts +9 -3
- package/Server/API/StatusPageAPI.ts +22 -21
- package/Server/Services/ScheduledMaintenanceService.ts +9 -1
- package/Server/Types/Markdown.ts +60 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +25 -36
- package/Tests/Server/API/BaseAPI.test.ts +17 -11
- package/Types/Database/EnableMCP.ts +7 -0
- package/Types/ObjectID.ts +25 -0
- package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js +8 -0
- package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Alert.js +2 -0
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +2 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentState.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentState.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +2 -0
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorStatus.js +2 -0
- package/build/dist/Models/DatabaseModels/MonitorStatus.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicy.js +2 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicy.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +2 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +2 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Team.js +2 -0
- package/build/dist/Models/DatabaseModels/Team.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +9 -3
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +15 -14
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +3 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +50 -0
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +22 -28
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Tests/Server/API/BaseAPI.test.js +16 -11
- package/build/dist/Tests/Server/API/BaseAPI.test.js.map +1 -1
- package/build/dist/Types/Database/EnableMCP.js +6 -0
- package/build/dist/Types/Database/EnableMCP.js.map +1 -0
- package/build/dist/Types/ObjectID.js +19 -0
- package/build/dist/Types/ObjectID.js.map +1 -1
- package/package.json +1 -1
|
@@ -44,6 +44,7 @@ export default class AnalyticsBaseModel extends CommonModel {
|
|
|
44
44
|
partitionKey: string;
|
|
45
45
|
projections?: Array<Projection> | undefined;
|
|
46
46
|
materializedViews?: Array<MaterializedView> | undefined;
|
|
47
|
+
enableMCP?: boolean | undefined;
|
|
47
48
|
}) {
|
|
48
49
|
super({
|
|
49
50
|
tableColumns: data.tableColumns,
|
|
@@ -146,6 +147,7 @@ export default class AnalyticsBaseModel extends CommonModel {
|
|
|
146
147
|
this.partitionKey = data.partitionKey;
|
|
147
148
|
this.projections = data.projections || [];
|
|
148
149
|
this.materializedViews = data.materializedViews || [];
|
|
150
|
+
this.enableMCP = data.enableMCP || false;
|
|
149
151
|
}
|
|
150
152
|
|
|
151
153
|
private _enableWorkflowOn: EnableWorkflowOn | undefined;
|
|
@@ -272,6 +274,14 @@ export default class AnalyticsBaseModel extends CommonModel {
|
|
|
272
274
|
this._materializedViews = v;
|
|
273
275
|
}
|
|
274
276
|
|
|
277
|
+
private _enableMCP: boolean = false;
|
|
278
|
+
public get enableMCP(): boolean {
|
|
279
|
+
return this._enableMCP;
|
|
280
|
+
}
|
|
281
|
+
public set enableMCP(v: boolean) {
|
|
282
|
+
this._enableMCP = v;
|
|
283
|
+
}
|
|
284
|
+
|
|
275
285
|
public getTenantColumn(): AnalyticsTableColumn | null {
|
|
276
286
|
const column: AnalyticsTableColumn | undefined = this.tableColumns.find(
|
|
277
287
|
(column: AnalyticsTableColumn) => {
|
|
@@ -16,6 +16,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
|
|
|
16
16
|
import ColumnType from "../../Types/Database/ColumnType";
|
|
17
17
|
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
|
18
18
|
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
|
19
|
+
import EnableMCP from "../../Types/Database/EnableMCP";
|
|
19
20
|
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
|
|
20
21
|
import MultiTenentQueryAllowed from "../../Types/Database/MultiTenentQueryAllowed";
|
|
21
22
|
import TableColumn from "../../Types/Database/TableColumn";
|
|
@@ -39,6 +40,7 @@ import { TelemetryQuery } from "../../Types/Telemetry/TelemetryQuery";
|
|
|
39
40
|
import NotificationRuleWorkspaceChannel from "../../Types/Workspace/NotificationRules/NotificationRuleWorkspaceChannel";
|
|
40
41
|
|
|
41
42
|
@EnableDocumentation()
|
|
43
|
+
@EnableMCP()
|
|
42
44
|
@AccessControlColumn("labels")
|
|
43
45
|
@MultiTenentQueryAllowed(true)
|
|
44
46
|
@TenantColumn("projectId")
|
|
@@ -124,6 +124,8 @@ export default class DatabaseBaseModel extends BaseEntity {
|
|
|
124
124
|
public enableDocumentation!: boolean;
|
|
125
125
|
public isMasterAdminApiDocs!: boolean;
|
|
126
126
|
|
|
127
|
+
public enableMCP!: boolean;
|
|
128
|
+
|
|
127
129
|
public currentUserCanAccessColumnBy!: string | null;
|
|
128
130
|
public slugifyColumn!: string | null;
|
|
129
131
|
public saveSlugToColumn!: string | null;
|
|
@@ -17,6 +17,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
|
|
|
17
17
|
import ColumnType from "../../Types/Database/ColumnType";
|
|
18
18
|
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
|
19
19
|
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
|
20
|
+
import EnableMCP from "../../Types/Database/EnableMCP";
|
|
20
21
|
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
|
|
21
22
|
import MultiTenentQueryAllowed from "../../Types/Database/MultiTenentQueryAllowed";
|
|
22
23
|
import SlugifyColumn from "../../Types/Database/SlugifyColumn";
|
|
@@ -42,6 +43,7 @@ import { TelemetryQuery } from "../../Types/Telemetry/TelemetryQuery";
|
|
|
42
43
|
import NotificationRuleWorkspaceChannel from "../../Types/Workspace/NotificationRules/NotificationRuleWorkspaceChannel";
|
|
43
44
|
|
|
44
45
|
@EnableDocumentation()
|
|
46
|
+
@EnableMCP()
|
|
45
47
|
@AccessControlColumn("labels")
|
|
46
48
|
@MultiTenentQueryAllowed(true)
|
|
47
49
|
@TenantColumn("projectId")
|
|
@@ -11,6 +11,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
|
|
|
11
11
|
import ColumnType from "../../Types/Database/ColumnType";
|
|
12
12
|
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
|
13
13
|
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
|
14
|
+
import EnableMCP from "../../Types/Database/EnableMCP";
|
|
14
15
|
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
|
|
15
16
|
import ColorField from "../../Types/Database/ColorField";
|
|
16
17
|
import SlugifyColumn from "../../Types/Database/SlugifyColumn";
|
|
@@ -31,6 +32,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
|
|
31
32
|
delete: PlanType.Free,
|
|
32
33
|
})
|
|
33
34
|
@EnableDocumentation()
|
|
35
|
+
@EnableMCP()
|
|
34
36
|
@TenantColumn("projectId")
|
|
35
37
|
@TableAccessControl({
|
|
36
38
|
create: [
|
|
@@ -11,6 +11,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
|
|
|
11
11
|
import ColumnType from "../../Types/Database/ColumnType";
|
|
12
12
|
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
|
13
13
|
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
|
14
|
+
import EnableMCP from "../../Types/Database/EnableMCP";
|
|
14
15
|
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
|
|
15
16
|
import SlugifyColumn from "../../Types/Database/SlugifyColumn";
|
|
16
17
|
import TableColumn from "../../Types/Database/TableColumn";
|
|
@@ -37,6 +38,7 @@ import {
|
|
|
37
38
|
import NotificationRuleWorkspaceChannel from "../../Types/Workspace/NotificationRules/NotificationRuleWorkspaceChannel";
|
|
38
39
|
|
|
39
40
|
@EnableDocumentation()
|
|
41
|
+
@EnableMCP()
|
|
40
42
|
@AccessControlColumn("labels")
|
|
41
43
|
@TenantColumn("projectId")
|
|
42
44
|
@TableAccessControl({
|
|
@@ -11,6 +11,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
|
|
|
11
11
|
import ColumnType from "../../Types/Database/ColumnType";
|
|
12
12
|
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
|
13
13
|
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
|
14
|
+
import EnableMCP from "../../Types/Database/EnableMCP";
|
|
14
15
|
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
|
|
15
16
|
import ColorField from "../../Types/Database/ColorField";
|
|
16
17
|
import SlugifyColumn from "../../Types/Database/SlugifyColumn";
|
|
@@ -31,6 +32,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
|
|
31
32
|
delete: PlanType.Free,
|
|
32
33
|
})
|
|
33
34
|
@EnableDocumentation()
|
|
35
|
+
@EnableMCP()
|
|
34
36
|
@TenantColumn("projectId")
|
|
35
37
|
@TableAccessControl({
|
|
36
38
|
create: [
|
|
@@ -10,6 +10,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
|
|
|
10
10
|
import ColumnType from "../../Types/Database/ColumnType";
|
|
11
11
|
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
|
12
12
|
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
|
13
|
+
import EnableMCP from "../../Types/Database/EnableMCP";
|
|
13
14
|
import SlugifyColumn from "../../Types/Database/SlugifyColumn";
|
|
14
15
|
import TableColumn from "../../Types/Database/TableColumn";
|
|
15
16
|
import TableColumnType from "../../Types/Database/TableColumnType";
|
|
@@ -32,6 +33,7 @@ import NotificationRuleWorkspaceChannel from "../../Types/Workspace/Notification
|
|
|
32
33
|
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
|
|
33
34
|
|
|
34
35
|
@EnableDocumentation()
|
|
36
|
+
@EnableMCP()
|
|
35
37
|
@AccessControlColumn("labels")
|
|
36
38
|
@TenantColumn("projectId")
|
|
37
39
|
@EnableWorkflow({
|
|
@@ -14,6 +14,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
|
|
|
14
14
|
import ColumnType from "../../Types/Database/ColumnType";
|
|
15
15
|
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
|
16
16
|
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
|
17
|
+
import EnableMCP from "../../Types/Database/EnableMCP";
|
|
17
18
|
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
|
|
18
19
|
import MultiTenentQueryAllowed from "../../Types/Database/MultiTenentQueryAllowed";
|
|
19
20
|
import SlugifyColumn from "../../Types/Database/SlugifyColumn";
|
|
@@ -39,6 +40,7 @@ import Recurring from "../../Types/Events/Recurring";
|
|
|
39
40
|
import NotificationRuleWorkspaceChannel from "../../Types/Workspace/NotificationRules/NotificationRuleWorkspaceChannel";
|
|
40
41
|
|
|
41
42
|
@EnableDocumentation()
|
|
43
|
+
@EnableMCP()
|
|
42
44
|
@AccessControlColumn("labels")
|
|
43
45
|
@MultiTenentQueryAllowed(true)
|
|
44
46
|
@TenantColumn("projectId")
|
|
@@ -17,6 +17,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
|
|
|
17
17
|
import ColumnType from "../../Types/Database/ColumnType";
|
|
18
18
|
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
|
19
19
|
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
|
20
|
+
import EnableMCP from "../../Types/Database/EnableMCP";
|
|
20
21
|
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
|
|
21
22
|
import SlugifyColumn from "../../Types/Database/SlugifyColumn";
|
|
22
23
|
import TableColumn from "../../Types/Database/TableColumn";
|
|
@@ -43,6 +44,7 @@ import {
|
|
|
43
44
|
import UptimePrecision from "../../Types/StatusPage/UptimePrecision";
|
|
44
45
|
|
|
45
46
|
@EnableDocumentation()
|
|
47
|
+
@EnableMCP()
|
|
46
48
|
@AccessControlColumn("labels")
|
|
47
49
|
@TenantColumn("projectId")
|
|
48
50
|
@TableAccessControl({
|
|
@@ -10,6 +10,7 @@ import ColumnLength from "../../Types/Database/ColumnLength";
|
|
|
10
10
|
import ColumnType from "../../Types/Database/ColumnType";
|
|
11
11
|
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
|
12
12
|
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
|
13
|
+
import EnableMCP from "../../Types/Database/EnableMCP";
|
|
13
14
|
import EnableWorkflow from "../../Types/Database/EnableWorkflow";
|
|
14
15
|
import SlugifyColumn from "../../Types/Database/SlugifyColumn";
|
|
15
16
|
import TableColumn from "../../Types/Database/TableColumn";
|
|
@@ -28,6 +29,7 @@ import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
|
|
28
29
|
delete: PlanType.Free,
|
|
29
30
|
})
|
|
30
31
|
@EnableDocumentation()
|
|
32
|
+
@EnableMCP()
|
|
31
33
|
@TenantColumn("projectId")
|
|
32
34
|
@TableAccessControl({
|
|
33
35
|
create: [
|
package/Server/API/BaseAPI.ts
CHANGED
|
@@ -347,7 +347,9 @@ export default class BaseAPI<
|
|
|
347
347
|
req: ExpressRequest,
|
|
348
348
|
res: ExpressResponse,
|
|
349
349
|
): Promise<void> {
|
|
350
|
-
const
|
|
350
|
+
const idParam: string = req.params["id"] as string;
|
|
351
|
+
ObjectID.validateUUID(idParam);
|
|
352
|
+
const objectId: ObjectID = new ObjectID(idParam);
|
|
351
353
|
await this.onBeforeGet(req, res);
|
|
352
354
|
let select: Select<BaseModel> = {};
|
|
353
355
|
|
|
@@ -372,7 +374,9 @@ export default class BaseAPI<
|
|
|
372
374
|
res: ExpressResponse,
|
|
373
375
|
): Promise<void> {
|
|
374
376
|
await this.onBeforeDelete(req, res);
|
|
375
|
-
const
|
|
377
|
+
const idParam: string = req.params["id"] as string;
|
|
378
|
+
ObjectID.validateUUID(idParam);
|
|
379
|
+
const objectId: ObjectID = new ObjectID(idParam);
|
|
376
380
|
|
|
377
381
|
await this.service.deleteOneById({
|
|
378
382
|
id: objectId,
|
|
@@ -388,7 +392,9 @@ export default class BaseAPI<
|
|
|
388
392
|
res: ExpressResponse,
|
|
389
393
|
): Promise<void> {
|
|
390
394
|
await this.onBeforeUpdate(req, res);
|
|
391
|
-
const
|
|
395
|
+
const idParam: string = req.params["id"] as string;
|
|
396
|
+
ObjectID.validateUUID(idParam);
|
|
397
|
+
const objectId: ObjectID = new ObjectID(idParam);
|
|
392
398
|
const objectIdString: string = objectId.toString();
|
|
393
399
|
const body: JSONObject = req.body;
|
|
394
400
|
|
|
@@ -132,6 +132,7 @@ const resolveStatusPageIdOrThrow: ResolveStatusPageIdOrThrowFunction = async (
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
try {
|
|
135
|
+
ObjectID.validateUUID(statusPageIdOrDomain);
|
|
135
136
|
return new ObjectID(statusPageIdOrDomain);
|
|
136
137
|
} catch (err) {
|
|
137
138
|
logger.error(
|
|
@@ -1399,12 +1400,12 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
1399
1400
|
this.router.post(
|
|
1400
1401
|
`${new this.entityType()
|
|
1401
1402
|
.getCrudApiPath()
|
|
1402
|
-
?.toString()}/overview/:
|
|
1403
|
+
?.toString()}/overview/:statusPageIdOrDomain`,
|
|
1403
1404
|
UserMiddleware.getUserMiddleware,
|
|
1404
1405
|
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
1405
1406
|
try {
|
|
1406
|
-
const statusPageId: ObjectID =
|
|
1407
|
-
req.params["
|
|
1407
|
+
const statusPageId: ObjectID = await resolveStatusPageIdOrThrow(
|
|
1408
|
+
req.params["statusPageIdOrDomain"] as string,
|
|
1408
1409
|
);
|
|
1409
1410
|
|
|
1410
1411
|
await this.checkHasReadAccess({
|
|
@@ -1940,12 +1941,12 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
1940
1941
|
this.router.post(
|
|
1941
1942
|
`${new this.entityType()
|
|
1942
1943
|
.getCrudApiPath()
|
|
1943
|
-
?.toString()}/incidents/:
|
|
1944
|
+
?.toString()}/incidents/:statusPageIdOrDomain`,
|
|
1944
1945
|
UserMiddleware.getUserMiddleware,
|
|
1945
1946
|
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
1946
1947
|
try {
|
|
1947
|
-
const objectId: ObjectID =
|
|
1948
|
-
req.params["
|
|
1948
|
+
const objectId: ObjectID = await resolveStatusPageIdOrThrow(
|
|
1949
|
+
req.params["statusPageIdOrDomain"] as string,
|
|
1949
1950
|
);
|
|
1950
1951
|
|
|
1951
1952
|
const response: JSONObject = await this.getIncidents(
|
|
@@ -1964,12 +1965,12 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
1964
1965
|
this.router.post(
|
|
1965
1966
|
`${new this.entityType()
|
|
1966
1967
|
.getCrudApiPath()
|
|
1967
|
-
?.toString()}/scheduled-maintenance-events/:
|
|
1968
|
+
?.toString()}/scheduled-maintenance-events/:statusPageIdOrDomain`,
|
|
1968
1969
|
UserMiddleware.getUserMiddleware,
|
|
1969
1970
|
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
1970
1971
|
try {
|
|
1971
|
-
const objectId: ObjectID =
|
|
1972
|
-
req.params["
|
|
1972
|
+
const objectId: ObjectID = await resolveStatusPageIdOrThrow(
|
|
1973
|
+
req.params["statusPageIdOrDomain"] as string,
|
|
1973
1974
|
);
|
|
1974
1975
|
|
|
1975
1976
|
const response: JSONObject = await this.getScheduledMaintenanceEvents(
|
|
@@ -1989,12 +1990,12 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
1989
1990
|
this.router.post(
|
|
1990
1991
|
`${new this.entityType()
|
|
1991
1992
|
.getCrudApiPath()
|
|
1992
|
-
?.toString()}/announcements/:
|
|
1993
|
+
?.toString()}/announcements/:statusPageIdOrDomain`,
|
|
1993
1994
|
UserMiddleware.getUserMiddleware,
|
|
1994
1995
|
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
1995
1996
|
try {
|
|
1996
|
-
const objectId: ObjectID =
|
|
1997
|
-
req.params["
|
|
1997
|
+
const objectId: ObjectID = await resolveStatusPageIdOrThrow(
|
|
1998
|
+
req.params["statusPageIdOrDomain"] as string,
|
|
1998
1999
|
);
|
|
1999
2000
|
|
|
2000
2001
|
const response: JSONObject = await this.getAnnouncements(
|
|
@@ -2014,12 +2015,12 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
2014
2015
|
this.router.post(
|
|
2015
2016
|
`${new this.entityType()
|
|
2016
2017
|
.getCrudApiPath()
|
|
2017
|
-
?.toString()}/incidents/:
|
|
2018
|
+
?.toString()}/incidents/:statusPageIdOrDomain/:incidentId`,
|
|
2018
2019
|
UserMiddleware.getUserMiddleware,
|
|
2019
2020
|
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
2020
2021
|
try {
|
|
2021
|
-
const objectId: ObjectID =
|
|
2022
|
-
req.params["
|
|
2022
|
+
const objectId: ObjectID = await resolveStatusPageIdOrThrow(
|
|
2023
|
+
req.params["statusPageIdOrDomain"] as string,
|
|
2023
2024
|
);
|
|
2024
2025
|
|
|
2025
2026
|
const incidentId: ObjectID = new ObjectID(
|
|
@@ -2042,12 +2043,12 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
2042
2043
|
this.router.post(
|
|
2043
2044
|
`${new this.entityType()
|
|
2044
2045
|
.getCrudApiPath()
|
|
2045
|
-
?.toString()}/scheduled-maintenance-events/:
|
|
2046
|
+
?.toString()}/scheduled-maintenance-events/:statusPageIdOrDomain/:scheduledMaintenanceId`,
|
|
2046
2047
|
UserMiddleware.getUserMiddleware,
|
|
2047
2048
|
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
2048
2049
|
try {
|
|
2049
|
-
const objectId: ObjectID =
|
|
2050
|
-
req.params["
|
|
2050
|
+
const objectId: ObjectID = await resolveStatusPageIdOrThrow(
|
|
2051
|
+
req.params["statusPageIdOrDomain"] as string,
|
|
2051
2052
|
);
|
|
2052
2053
|
|
|
2053
2054
|
const scheduledMaintenanceId: ObjectID = new ObjectID(
|
|
@@ -2071,12 +2072,12 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
2071
2072
|
this.router.post(
|
|
2072
2073
|
`${new this.entityType()
|
|
2073
2074
|
.getCrudApiPath()
|
|
2074
|
-
?.toString()}/announcements/:
|
|
2075
|
+
?.toString()}/announcements/:statusPageIdOrDomain/:announcementId`,
|
|
2075
2076
|
UserMiddleware.getUserMiddleware,
|
|
2076
2077
|
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
2077
2078
|
try {
|
|
2078
|
-
const objectId: ObjectID =
|
|
2079
|
-
req.params["
|
|
2079
|
+
const objectId: ObjectID = await resolveStatusPageIdOrThrow(
|
|
2080
|
+
req.params["statusPageIdOrDomain"] as string,
|
|
2080
2081
|
);
|
|
2081
2082
|
|
|
2082
2083
|
const announcementId: ObjectID = new ObjectID(
|
|
@@ -291,6 +291,14 @@ export class Service extends DatabaseService<Model> {
|
|
|
291
291
|
unsubscribeUrl: unsubscribeUrl,
|
|
292
292
|
};
|
|
293
293
|
|
|
294
|
+
// SMS-specific template variables with plain text (no HTML/Markdown)
|
|
295
|
+
const smsTemplateVariables: Record<string, string> = {
|
|
296
|
+
...templateVariables,
|
|
297
|
+
scheduledMaintenanceDescription: Markdown.convertToPlainText(
|
|
298
|
+
event.description || "",
|
|
299
|
+
),
|
|
300
|
+
};
|
|
301
|
+
|
|
294
302
|
if (subscriber.subscriberPhone) {
|
|
295
303
|
let smsMessage: string;
|
|
296
304
|
|
|
@@ -303,7 +311,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
303
311
|
smsMessage =
|
|
304
312
|
StatusPageSubscriberNotificationTemplateServiceClass.compileTemplate(
|
|
305
313
|
smsTemplate.templateBody,
|
|
306
|
-
|
|
314
|
+
smsTemplateVariables,
|
|
307
315
|
);
|
|
308
316
|
} else {
|
|
309
317
|
// Use default template
|
package/Server/Types/Markdown.ts
CHANGED
|
@@ -10,6 +10,66 @@ export enum MarkdownContentType {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export default class Markdown {
|
|
13
|
+
@CaptureSpan()
|
|
14
|
+
public static convertToPlainText(markdown: string): string {
|
|
15
|
+
if (!markdown) {
|
|
16
|
+
return "";
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
let text: string = markdown;
|
|
20
|
+
|
|
21
|
+
// Remove HTML tags
|
|
22
|
+
text = text.replace(/<[^>]*>/g, "");
|
|
23
|
+
|
|
24
|
+
// Convert markdown links [text](url) to just text
|
|
25
|
+
text = text.replace(/\[([^\]]+)\]\([^)]+\)/g, "$1");
|
|
26
|
+
|
|
27
|
+
// Convert markdown images  to just alt text
|
|
28
|
+
text = text.replace(/!\[([^\]]*)\]\([^)]+\)/g, "$1");
|
|
29
|
+
|
|
30
|
+
// Remove markdown bold/italic markers
|
|
31
|
+
text = text.replace(/\*\*([^*]+)\*\*/g, "$1"); // **bold**
|
|
32
|
+
text = text.replace(/\*([^*]+)\*/g, "$1"); // *italic*
|
|
33
|
+
text = text.replace(/__([^_]+)__/g, "$1"); // __bold__
|
|
34
|
+
text = text.replace(/_([^_]+)_/g, "$1"); // _italic_
|
|
35
|
+
|
|
36
|
+
// Remove markdown strikethrough
|
|
37
|
+
text = text.replace(/~~([^~]+)~~/g, "$1");
|
|
38
|
+
|
|
39
|
+
// Remove markdown code blocks
|
|
40
|
+
text = text.replace(/```[\s\S]*?```/g, "");
|
|
41
|
+
text = text.replace(/`([^`]+)`/g, "$1");
|
|
42
|
+
|
|
43
|
+
// Remove markdown headers
|
|
44
|
+
text = text.replace(/^#{1,6}\s+/gm, "");
|
|
45
|
+
|
|
46
|
+
// Remove markdown blockquotes
|
|
47
|
+
text = text.replace(/^>\s+/gm, "");
|
|
48
|
+
|
|
49
|
+
// Remove markdown horizontal rules
|
|
50
|
+
text = text.replace(/^[-*_]{3,}$/gm, "");
|
|
51
|
+
|
|
52
|
+
// Remove markdown list markers
|
|
53
|
+
text = text.replace(/^[\s]*[-*+]\s+/gm, "");
|
|
54
|
+
text = text.replace(/^[\s]*\d+\.\s+/gm, "");
|
|
55
|
+
|
|
56
|
+
// Decode HTML entities
|
|
57
|
+
text = text.replace(/</g, "<");
|
|
58
|
+
text = text.replace(/>/g, ">");
|
|
59
|
+
text = text.replace(/&/g, "&");
|
|
60
|
+
text = text.replace(/"/g, '"');
|
|
61
|
+
text = text.replace(/'/g, "'");
|
|
62
|
+
text = text.replace(/ /g, " ");
|
|
63
|
+
|
|
64
|
+
// Normalize whitespace - collapse multiple spaces/newlines
|
|
65
|
+
text = text.replace(/\n\s*\n/g, "\n");
|
|
66
|
+
text = text.replace(/[ \t]+/g, " ");
|
|
67
|
+
|
|
68
|
+
// Trim whitespace
|
|
69
|
+
text = text.trim();
|
|
70
|
+
|
|
71
|
+
return text;
|
|
72
|
+
}
|
|
13
73
|
private static blogRenderer: Renderer | null = null;
|
|
14
74
|
private static docsRenderer: Renderer | null = null;
|
|
15
75
|
private static emailRenderer: Renderer | null = null;
|
|
@@ -30,8 +30,6 @@ import CaptureSpan from "../../Telemetry/CaptureSpan";
|
|
|
30
30
|
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
31
31
|
import ObjectID from "../../../../Types/ObjectID";
|
|
32
32
|
import WorkspaceProjectAuthTokenService from "../../../Services/WorkspaceProjectAuthTokenService";
|
|
33
|
-
import WorkspaceUserAuthTokenService from "../../../Services/WorkspaceUserAuthTokenService";
|
|
34
|
-
import WorkspaceUserAuthToken from "../../../../Models/DatabaseModels/WorkspaceUserAuthToken";
|
|
35
33
|
import WorkspaceProjectAuthToken, {
|
|
36
34
|
MicrosoftTeamsMiscData,
|
|
37
35
|
MicrosoftTeamsTeam,
|
|
@@ -2931,43 +2929,23 @@ All monitoring checks are passing normally.`;
|
|
|
2931
2929
|
);
|
|
2932
2930
|
}
|
|
2933
2931
|
|
|
2934
|
-
//
|
|
2932
|
+
// Use app-scoped token to fetch user's teams
|
|
2935
2933
|
let allTeams: Array<JSONObject> = [];
|
|
2936
|
-
let usedAccessToken: string | null = null;
|
|
2937
2934
|
|
|
2938
2935
|
try {
|
|
2939
|
-
//
|
|
2940
|
-
if (data.
|
|
2941
|
-
logger.debug(
|
|
2942
|
-
"Using provided user access token to fetch joined teams",
|
|
2943
|
-
);
|
|
2944
|
-
usedAccessToken = data.userAccessToken;
|
|
2936
|
+
// Fetch joined teams using app-scoped token
|
|
2937
|
+
if (data.userId) {
|
|
2938
|
+
logger.debug("Using app-scoped token to fetch joined teams for user");
|
|
2945
2939
|
const userTeams: Record<string, { id: string; name: string }> =
|
|
2946
|
-
await this.getUserJoinedTeams(
|
|
2947
|
-
|
|
2948
|
-
} else if (data.userId) {
|
|
2949
|
-
// Try to fetch stored user auth for this project + user
|
|
2950
|
-
logger.debug("Looking up stored user auth token for provided userId");
|
|
2951
|
-
const userAuth: WorkspaceUserAuthToken | null =
|
|
2952
|
-
await WorkspaceUserAuthTokenService.getUserAuth({
|
|
2940
|
+
await this.getUserJoinedTeams({
|
|
2941
|
+
userId: data.userId.toString(),
|
|
2953
2942
|
projectId: data.projectId,
|
|
2954
|
-
userId: data.userId,
|
|
2955
|
-
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
2956
2943
|
});
|
|
2957
|
-
|
|
2958
|
-
if (userAuth && userAuth.authToken) {
|
|
2959
|
-
usedAccessToken = userAuth.authToken;
|
|
2960
|
-
logger.debug(
|
|
2961
|
-
"Found user auth token; using it to fetch joined teams",
|
|
2962
|
-
);
|
|
2963
|
-
const userTeams: Record<string, { id: string; name: string }> =
|
|
2964
|
-
await this.getUserJoinedTeams(usedAccessToken);
|
|
2965
|
-
allTeams = Object.values(userTeams) as any;
|
|
2966
|
-
}
|
|
2944
|
+
allTeams = Object.values(userTeams) as any;
|
|
2967
2945
|
}
|
|
2968
2946
|
} catch (err) {
|
|
2969
2947
|
logger.warn(
|
|
2970
|
-
"Failed to fetch teams using
|
|
2948
|
+
"Failed to fetch teams using app-scoped token, falling back to paginated fetch:",
|
|
2971
2949
|
);
|
|
2972
2950
|
logger.warn(err);
|
|
2973
2951
|
allTeams = [];
|
|
@@ -3082,19 +3060,30 @@ All monitoring checks are passing normally.`;
|
|
|
3082
3060
|
}
|
|
3083
3061
|
}
|
|
3084
3062
|
|
|
3085
|
-
// Method to get user's joined teams using
|
|
3063
|
+
// Method to get user's joined teams using app-scoped token
|
|
3086
3064
|
@CaptureSpan()
|
|
3087
|
-
public static async getUserJoinedTeams(
|
|
3088
|
-
|
|
3089
|
-
|
|
3065
|
+
public static async getUserJoinedTeams(data: {
|
|
3066
|
+
userId: string;
|
|
3067
|
+
projectId: ObjectID;
|
|
3068
|
+
}): Promise<Record<string, { id: string; name: string }>> {
|
|
3090
3069
|
logger.debug("=== getUserJoinedTeams called ===");
|
|
3070
|
+
logger.debug(`User ID: ${data.userId}`);
|
|
3071
|
+
logger.debug(`Project ID: ${data.projectId.toString()}`);
|
|
3091
3072
|
|
|
3092
3073
|
try {
|
|
3093
|
-
// Get
|
|
3074
|
+
// Get a valid app access token (refreshed if needed)
|
|
3075
|
+
logger.debug("Refreshing app access token before fetching teams");
|
|
3076
|
+
const accessToken: string = await this.getValidAccessToken({
|
|
3077
|
+
authToken: "", // Not needed for app token refresh
|
|
3078
|
+
projectId: data.projectId,
|
|
3079
|
+
});
|
|
3080
|
+
logger.debug("App access token refreshed successfully");
|
|
3081
|
+
|
|
3082
|
+
// Get user's teams using app-scoped token
|
|
3094
3083
|
const teamsResponse: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
|
3095
3084
|
await API.get<JSONObject>({
|
|
3096
3085
|
url: URL.fromString(
|
|
3097
|
-
|
|
3086
|
+
`https://graph.microsoft.com/v1.0/users/${data.userId}/joinedTeams`,
|
|
3098
3087
|
),
|
|
3099
3088
|
headers: {
|
|
3100
3089
|
Authorization: `Bearer ${accessToken}`,
|
|
@@ -116,18 +116,24 @@ const res = {
|
|
|
116
116
|
send: jest.fn().mockReturnThis(),
|
|
117
117
|
} as any as ExpressResponse;
|
|
118
118
|
|
|
119
|
+
// Valid UUIDs for test fixtures
|
|
120
|
+
const TEST_DELETE_ID: string = "550e8400-e29b-41d4-a716-446655440001";
|
|
121
|
+
const TEST_COUNT_ID: string = "550e8400-e29b-41d4-a716-446655440002";
|
|
122
|
+
const TEST_GET_ID: string = "550e8400-e29b-41d4-a716-446655440003";
|
|
123
|
+
const TEST_UPDATE_ID: string = "550e8400-e29b-41d4-a716-446655440004";
|
|
124
|
+
|
|
119
125
|
const deleteRequest: OneUptimeRequest = {
|
|
120
|
-
params: { id:
|
|
126
|
+
params: { id: TEST_DELETE_ID },
|
|
121
127
|
headers: {},
|
|
122
128
|
} as unknown as OneUptimeRequest;
|
|
123
129
|
|
|
124
130
|
const countRequest: OneUptimeRequest = {
|
|
125
|
-
body: { query: { id:
|
|
131
|
+
body: { query: { id: TEST_COUNT_ID } },
|
|
126
132
|
headers: {},
|
|
127
133
|
} as unknown as OneUptimeRequest;
|
|
128
134
|
|
|
129
135
|
const getRequest: OneUptimeRequest = {
|
|
130
|
-
params: { id:
|
|
136
|
+
params: { id: TEST_GET_ID },
|
|
131
137
|
headers: {},
|
|
132
138
|
} as unknown as OneUptimeRequest;
|
|
133
139
|
|
|
@@ -524,7 +530,7 @@ describe("BaseAPI", () => {
|
|
|
524
530
|
const countBySpy: jest.SpyInstance = jest.spyOn(TestService, "countBy");
|
|
525
531
|
await baseApiInstance.count(countRequest, res);
|
|
526
532
|
expect(countBySpy).toHaveBeenCalledWith({
|
|
527
|
-
query: { id:
|
|
533
|
+
query: { id: TEST_COUNT_ID },
|
|
528
534
|
props: emptyDatabaseCommonInteractionProps,
|
|
529
535
|
});
|
|
530
536
|
});
|
|
@@ -533,7 +539,7 @@ describe("BaseAPI", () => {
|
|
|
533
539
|
const findBySpy: jest.SpyInstance = jest.spyOn(TestService, "countBy");
|
|
534
540
|
await baseApiInstance.count(countRequest, res);
|
|
535
541
|
expect(findBySpy).toHaveBeenCalledWith({
|
|
536
|
-
query: { id:
|
|
542
|
+
query: { id: TEST_COUNT_ID },
|
|
537
543
|
props: emptyDatabaseCommonInteractionProps,
|
|
538
544
|
});
|
|
539
545
|
});
|
|
@@ -568,7 +574,7 @@ describe("BaseAPI", () => {
|
|
|
568
574
|
await baseApiInstance.getItem(getRequest, res);
|
|
569
575
|
expect(findOneByIdSpy).toHaveBeenCalledWith(
|
|
570
576
|
expect.objectContaining({
|
|
571
|
-
id: new ObjectID(
|
|
577
|
+
id: new ObjectID(TEST_GET_ID),
|
|
572
578
|
props: emptyDatabaseCommonInteractionProps,
|
|
573
579
|
select: {},
|
|
574
580
|
}),
|
|
@@ -588,7 +594,7 @@ describe("BaseAPI", () => {
|
|
|
588
594
|
await baseApiInstance.getItem(getRequestWithSelect, res);
|
|
589
595
|
expect(findOneByIdSpy).toHaveBeenCalledWith(
|
|
590
596
|
expect.objectContaining({
|
|
591
|
-
id: new ObjectID(
|
|
597
|
+
id: new ObjectID(TEST_GET_ID),
|
|
592
598
|
props: emptyDatabaseCommonInteractionProps,
|
|
593
599
|
select: { id: true },
|
|
594
600
|
}),
|
|
@@ -630,7 +636,7 @@ describe("BaseAPI", () => {
|
|
|
630
636
|
|
|
631
637
|
expect(deleteOneBySpy).toHaveBeenCalledWith(
|
|
632
638
|
expect.objectContaining({
|
|
633
|
-
id: new ObjectID(
|
|
639
|
+
id: new ObjectID(TEST_DELETE_ID),
|
|
634
640
|
props: emptyDatabaseCommonInteractionProps,
|
|
635
641
|
}),
|
|
636
642
|
);
|
|
@@ -653,7 +659,7 @@ describe("BaseAPI", () => {
|
|
|
653
659
|
|
|
654
660
|
beforeEach(() => {
|
|
655
661
|
updateRequest = {
|
|
656
|
-
params: { id:
|
|
662
|
+
params: { id: TEST_UPDATE_ID },
|
|
657
663
|
body: { data: { name: "updatedName" } },
|
|
658
664
|
headers: {},
|
|
659
665
|
} as unknown as OneUptimeRequest;
|
|
@@ -714,7 +720,7 @@ describe("BaseAPI", () => {
|
|
|
714
720
|
|
|
715
721
|
expect(updateOneBySpy).toHaveBeenCalledWith(
|
|
716
722
|
expect.objectContaining({
|
|
717
|
-
id: new ObjectID(
|
|
723
|
+
id: new ObjectID(TEST_UPDATE_ID),
|
|
718
724
|
}),
|
|
719
725
|
);
|
|
720
726
|
});
|
|
@@ -726,7 +732,7 @@ describe("BaseAPI", () => {
|
|
|
726
732
|
"updateOneById",
|
|
727
733
|
);
|
|
728
734
|
expect(updateOneBySpy).toHaveBeenCalledWith({
|
|
729
|
-
id: new ObjectID(
|
|
735
|
+
id: new ObjectID(TEST_UPDATE_ID),
|
|
730
736
|
data: { name: "updatedName" },
|
|
731
737
|
props: emptyProps,
|
|
732
738
|
});
|