@oneuptime/common 7.0.4349 → 7.0.4358
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/DatabaseModels/StatusPage.ts +37 -0
- package/Models/DatabaseModels/StatusPageSubscriber.ts +60 -0
- package/Server/API/StatusPageAPI.ts +104 -10
- package/Server/Infrastructure/Postgres/SchemaMigrations/1749065784320-MigrationName.ts +23 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1749133333893-MigrationName.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/ScheduledMaintenanceService.ts +21 -0
- package/Server/Services/StatusPageSubscriberService.ts +116 -1
- package/Server/Utils/OpenAPI.ts +176 -11
- package/Server/Utils/Workspace/Slack/Slack.ts +14 -0
- package/Utils/Schema/ModelSchema.ts +1303 -11
- package/build/dist/Models/DatabaseModels/StatusPage.js +39 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +62 -0
- package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +73 -10
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749065784320-MigrationName.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749065784320-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749133333893-MigrationName.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749133333893-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +19 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +98 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Utils/OpenAPI.js +135 -11
- package/build/dist/Server/Utils/OpenAPI.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +15 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Utils/Schema/ModelSchema.js +1101 -6
- package/build/dist/Utils/Schema/ModelSchema.js.map +1 -1
- package/package.json +1 -1
package/Server/Utils/OpenAPI.ts
CHANGED
|
@@ -9,8 +9,28 @@ import logger from "./Logger";
|
|
|
9
9
|
import { ModelSchema, ModelSchemaType } from "../../Utils/Schema/ModelSchema";
|
|
10
10
|
import LocalCache from "../Infrastructure/LocalCache";
|
|
11
11
|
import { Host, HttpProtocol } from "../EnvironmentConfig";
|
|
12
|
+
import Permission from "../../Types/Permission";
|
|
12
13
|
|
|
13
14
|
export default class OpenAPIUtil {
|
|
15
|
+
/**
|
|
16
|
+
* Helper method to check if permissions should exclude API generation.
|
|
17
|
+
* Returns true if:
|
|
18
|
+
* 1. The permissions array is empty or undefined, OR
|
|
19
|
+
* 2. The permissions array contains Permission.Public or Permission.CurrentUser
|
|
20
|
+
*/
|
|
21
|
+
private static shouldExcludeApiForPermissions(
|
|
22
|
+
permissions: Array<Permission> | undefined,
|
|
23
|
+
): boolean {
|
|
24
|
+
if (!permissions || permissions.length === 0) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
permissions.includes(Permission.Public) ||
|
|
30
|
+
permissions.includes(Permission.CurrentUser)
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
14
34
|
public static generateOpenAPISpec(): JSONObject {
|
|
15
35
|
// check if the cache is already in LocalCache
|
|
16
36
|
const cachedSpec: JSONValue | undefined = LocalCache.getJSON(
|
|
@@ -114,6 +134,12 @@ export default class OpenAPIUtil {
|
|
|
114
134
|
const tableName: string = model.tableName || "UnknownModel";
|
|
115
135
|
const singularModelName: string = model.singularName || tableName;
|
|
116
136
|
|
|
137
|
+
// Use schema names that are already registered
|
|
138
|
+
const querySchemaName: string = `${tableName}QuerySchema`;
|
|
139
|
+
const selectSchemaName: string = `${tableName}SelectSchema`;
|
|
140
|
+
const sortSchemaName: string = `${tableName}SortSchema`;
|
|
141
|
+
const groupBySchemaName: string = `${tableName}GroupBySchema`;
|
|
142
|
+
|
|
117
143
|
data.registry.registerPath({
|
|
118
144
|
method: "post",
|
|
119
145
|
path: `${model.crudApiPath}/get-list`,
|
|
@@ -126,10 +152,10 @@ export default class OpenAPIUtil {
|
|
|
126
152
|
schema: {
|
|
127
153
|
type: "object",
|
|
128
154
|
properties: {
|
|
129
|
-
query: {
|
|
130
|
-
select: {
|
|
131
|
-
sort: {
|
|
132
|
-
groupBy: {
|
|
155
|
+
query: { $ref: `#/components/schemas/${querySchemaName}` },
|
|
156
|
+
select: { $ref: `#/components/schemas/${selectSchemaName}` },
|
|
157
|
+
sort: { $ref: `#/components/schemas/${sortSchemaName}` },
|
|
158
|
+
groupBy: { $ref: `#/components/schemas/${groupBySchemaName}` },
|
|
133
159
|
},
|
|
134
160
|
},
|
|
135
161
|
},
|
|
@@ -169,6 +195,9 @@ export default class OpenAPIUtil {
|
|
|
169
195
|
const tableName: string = model.tableName || "UnknownModel";
|
|
170
196
|
const singularModelName: string = model.singularName || tableName;
|
|
171
197
|
|
|
198
|
+
// Use schema name that is already registered
|
|
199
|
+
const querySchemaName: string = `${tableName}QuerySchema`;
|
|
200
|
+
|
|
172
201
|
data.registry.registerPath({
|
|
173
202
|
method: "post",
|
|
174
203
|
path: `${model.crudApiPath}/count`,
|
|
@@ -181,7 +210,7 @@ export default class OpenAPIUtil {
|
|
|
181
210
|
schema: {
|
|
182
211
|
type: "object",
|
|
183
212
|
properties: {
|
|
184
|
-
query: {
|
|
213
|
+
query: { $ref: `#/components/schemas/${querySchemaName}` },
|
|
185
214
|
},
|
|
186
215
|
},
|
|
187
216
|
},
|
|
@@ -215,6 +244,16 @@ export default class OpenAPIUtil {
|
|
|
215
244
|
const tableName: string = model.tableName || "UnknownModel";
|
|
216
245
|
const singularModelName: string = model.singularName || tableName;
|
|
217
246
|
|
|
247
|
+
// Skip generating create API if model has no create permissions or contains Public/CurrentUser permissions
|
|
248
|
+
if (this.shouldExcludeApiForPermissions(model.createRecordPermissions)) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Use schema names that are already registered
|
|
253
|
+
const createSchemaName: string = `${tableName}CreateSchema`;
|
|
254
|
+
const selectSchemaName: string = `${tableName}SelectSchema`;
|
|
255
|
+
const readSchemaName: string = `${tableName}ReadSchema`;
|
|
256
|
+
|
|
218
257
|
data.registry.registerPath({
|
|
219
258
|
method: "post",
|
|
220
259
|
path: `${model.crudApiPath}`,
|
|
@@ -228,9 +267,14 @@ export default class OpenAPIUtil {
|
|
|
228
267
|
type: "object",
|
|
229
268
|
properties: {
|
|
230
269
|
data: {
|
|
231
|
-
$ref: `#/components/schemas/${
|
|
270
|
+
$ref: `#/components/schemas/${createSchemaName}`,
|
|
232
271
|
},
|
|
233
|
-
miscDataProps: {
|
|
272
|
+
miscDataProps: {
|
|
273
|
+
type: "object",
|
|
274
|
+
description: "Additional data properties for creation",
|
|
275
|
+
additionalProperties: true,
|
|
276
|
+
},
|
|
277
|
+
select: { $ref: `#/components/schemas/${selectSchemaName}` },
|
|
234
278
|
},
|
|
235
279
|
required: ["data"],
|
|
236
280
|
},
|
|
@@ -246,7 +290,7 @@ export default class OpenAPIUtil {
|
|
|
246
290
|
type: "object",
|
|
247
291
|
properties: {
|
|
248
292
|
data: {
|
|
249
|
-
$ref: `#/components/schemas/${
|
|
293
|
+
$ref: `#/components/schemas/${readSchemaName}`,
|
|
250
294
|
},
|
|
251
295
|
},
|
|
252
296
|
},
|
|
@@ -308,6 +352,14 @@ export default class OpenAPIUtil {
|
|
|
308
352
|
const tableName: string = model.tableName || "UnknownModel";
|
|
309
353
|
const singularModelName: string = model.singularName || tableName;
|
|
310
354
|
|
|
355
|
+
// Skip generating get API if model has no read permissions or contains Public/CurrentUser permissions
|
|
356
|
+
if (this.shouldExcludeApiForPermissions(model.readRecordPermissions)) {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// Use schema name that is already registered
|
|
361
|
+
const selectSchemaName: string = `${tableName}SelectSchema`;
|
|
362
|
+
|
|
311
363
|
data.registry.registerPath({
|
|
312
364
|
method: "post",
|
|
313
365
|
path: `${model.crudApiPath}/{id}`,
|
|
@@ -326,6 +378,19 @@ export default class OpenAPIUtil {
|
|
|
326
378
|
description: `ID of the ${singularModelName} to retrieve`,
|
|
327
379
|
},
|
|
328
380
|
],
|
|
381
|
+
requestBody: {
|
|
382
|
+
required: false,
|
|
383
|
+
content: {
|
|
384
|
+
"application/json": {
|
|
385
|
+
schema: {
|
|
386
|
+
type: "object",
|
|
387
|
+
properties: {
|
|
388
|
+
select: { $ref: `#/components/schemas/${selectSchemaName}` },
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
},
|
|
393
|
+
},
|
|
329
394
|
responses: {
|
|
330
395
|
"200": {
|
|
331
396
|
description: "Successful response",
|
|
@@ -369,6 +434,16 @@ export default class OpenAPIUtil {
|
|
|
369
434
|
const tableName: string = model.tableName || "UnknownModel";
|
|
370
435
|
const singularModelName: string = model.singularName || tableName;
|
|
371
436
|
|
|
437
|
+
// Skip generating update API if model has no update permissions or contains Public/CurrentUser permissions
|
|
438
|
+
if (this.shouldExcludeApiForPermissions(model.updateRecordPermissions)) {
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Use schema names that are already registered
|
|
443
|
+
const updateSchemaName: string = `${tableName}UpdateSchema`;
|
|
444
|
+
const selectSchemaName: string = `${tableName}SelectSchema`;
|
|
445
|
+
const readSchemaName: string = `${tableName}ReadSchema`;
|
|
446
|
+
|
|
372
447
|
data.registry.registerPath({
|
|
373
448
|
method: "put",
|
|
374
449
|
path: `${model.crudApiPath}/{id}`,
|
|
@@ -395,8 +470,9 @@ export default class OpenAPIUtil {
|
|
|
395
470
|
type: "object",
|
|
396
471
|
properties: {
|
|
397
472
|
data: {
|
|
398
|
-
$ref: `#/components/schemas/${
|
|
473
|
+
$ref: `#/components/schemas/${updateSchemaName}`,
|
|
399
474
|
},
|
|
475
|
+
select: { $ref: `#/components/schemas/${selectSchemaName}` },
|
|
400
476
|
},
|
|
401
477
|
required: ["data"],
|
|
402
478
|
},
|
|
@@ -412,7 +488,7 @@ export default class OpenAPIUtil {
|
|
|
412
488
|
type: "object",
|
|
413
489
|
properties: {
|
|
414
490
|
data: {
|
|
415
|
-
$ref: `#/components/schemas/${
|
|
491
|
+
$ref: `#/components/schemas/${readSchemaName}`,
|
|
416
492
|
},
|
|
417
493
|
},
|
|
418
494
|
},
|
|
@@ -432,6 +508,12 @@ export default class OpenAPIUtil {
|
|
|
432
508
|
const model: DatabaseBaseModel = new modelType();
|
|
433
509
|
const tableName: string = model.tableName || "UnknownModel";
|
|
434
510
|
const singularModelName: string = model.singularName || tableName;
|
|
511
|
+
|
|
512
|
+
// Skip generating delete API if model has no delete permissions or contains Public/CurrentUser permissions
|
|
513
|
+
if (this.shouldExcludeApiForPermissions(model.deleteRecordPermissions)) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
|
|
435
517
|
data.registry.registerPath({
|
|
436
518
|
method: "delete",
|
|
437
519
|
path: `${model.crudApiPath}/{id}`,
|
|
@@ -464,9 +546,92 @@ export default class OpenAPIUtil {
|
|
|
464
546
|
model: DatabaseBaseModel,
|
|
465
547
|
): void {
|
|
466
548
|
const tableName: string = model.tableName || "UnknownModel";
|
|
549
|
+
const modelType: new () => DatabaseBaseModel =
|
|
550
|
+
model.constructor as new () => DatabaseBaseModel;
|
|
551
|
+
|
|
552
|
+
// Register the main model schema (for backwards compatibility)
|
|
467
553
|
const modelSchema: ModelSchemaType = ModelSchema.getModelSchema({
|
|
468
|
-
modelType:
|
|
554
|
+
modelType: modelType,
|
|
469
555
|
});
|
|
470
556
|
registry.register(tableName, modelSchema);
|
|
557
|
+
|
|
558
|
+
// Register operation-specific schemas based on permissions
|
|
559
|
+
this.registerOperationSpecificSchemas(
|
|
560
|
+
registry,
|
|
561
|
+
tableName,
|
|
562
|
+
modelType,
|
|
563
|
+
model,
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
// Register query, select, and sort schemas
|
|
567
|
+
this.registerQuerySchemas(registry, tableName, modelType);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
private static registerOperationSpecificSchemas(
|
|
571
|
+
registry: OpenAPIRegistry,
|
|
572
|
+
tableName: string,
|
|
573
|
+
modelType: new () => DatabaseBaseModel,
|
|
574
|
+
model: DatabaseBaseModel,
|
|
575
|
+
): void {
|
|
576
|
+
// Check if model has create permissions and should not exclude API generation
|
|
577
|
+
if (!this.shouldExcludeApiForPermissions(model.createRecordPermissions)) {
|
|
578
|
+
const createSchema: ModelSchemaType = ModelSchema.getCreateModelSchema({
|
|
579
|
+
modelType,
|
|
580
|
+
});
|
|
581
|
+
registry.register(`${tableName}CreateSchema`, createSchema);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// Check if model has read permissions and should not exclude API generation
|
|
585
|
+
if (!this.shouldExcludeApiForPermissions(model.readRecordPermissions)) {
|
|
586
|
+
const readSchema: ModelSchemaType = ModelSchema.getReadModelSchema({
|
|
587
|
+
modelType,
|
|
588
|
+
});
|
|
589
|
+
registry.register(`${tableName}ReadSchema`, readSchema);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// Check if model has update permissions and should not exclude API generation
|
|
593
|
+
if (!this.shouldExcludeApiForPermissions(model.updateRecordPermissions)) {
|
|
594
|
+
const updateSchema: ModelSchemaType = ModelSchema.getUpdateModelSchema({
|
|
595
|
+
modelType,
|
|
596
|
+
});
|
|
597
|
+
registry.register(`${tableName}UpdateSchema`, updateSchema);
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Check if model has delete permissions and should not exclude API generation
|
|
601
|
+
if (!this.shouldExcludeApiForPermissions(model.deleteRecordPermissions)) {
|
|
602
|
+
const deleteSchema: ModelSchemaType = ModelSchema.getDeleteModelSchema({
|
|
603
|
+
modelType,
|
|
604
|
+
});
|
|
605
|
+
registry.register(`${tableName}DeleteSchema`, deleteSchema);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
private static registerQuerySchemas(
|
|
610
|
+
registry: OpenAPIRegistry,
|
|
611
|
+
tableName: string,
|
|
612
|
+
modelType: new () => DatabaseBaseModel,
|
|
613
|
+
): void {
|
|
614
|
+
const querySchemaName: string = `${tableName}QuerySchema`;
|
|
615
|
+
const selectSchemaName: string = `${tableName}SelectSchema`;
|
|
616
|
+
const sortSchemaName: string = `${tableName}SortSchema`;
|
|
617
|
+
const groupBySchemaName: string = `${tableName}GroupBySchema`;
|
|
618
|
+
|
|
619
|
+
const querySchema: ModelSchemaType = ModelSchema.getQueryModelSchema({
|
|
620
|
+
modelType: modelType,
|
|
621
|
+
});
|
|
622
|
+
const selectSchema: ModelSchemaType = ModelSchema.getSelectModelSchema({
|
|
623
|
+
modelType: modelType,
|
|
624
|
+
});
|
|
625
|
+
const sortSchema: ModelSchemaType = ModelSchema.getSortModelSchema({
|
|
626
|
+
modelType: modelType,
|
|
627
|
+
});
|
|
628
|
+
const groupBySchema: ModelSchemaType = ModelSchema.getGroupByModelSchema({
|
|
629
|
+
modelType: modelType,
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
registry.register(querySchemaName, querySchema);
|
|
633
|
+
registry.register(selectSchemaName, selectSchema);
|
|
634
|
+
registry.register(sortSchemaName, sortSchema);
|
|
635
|
+
registry.register(groupBySchemaName, groupBySchema);
|
|
471
636
|
}
|
|
472
637
|
}
|
|
@@ -33,6 +33,15 @@ import CaptureSpan from "../../Telemetry/CaptureSpan";
|
|
|
33
33
|
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
34
34
|
|
|
35
35
|
export default class SlackUtil extends WorkspaceBase {
|
|
36
|
+
public static isValidSlackIncomingWebhookUrl(
|
|
37
|
+
incomingWebhookUrl: URL,
|
|
38
|
+
): boolean {
|
|
39
|
+
// check if the URL starts with https://hooks.slack.com/services/
|
|
40
|
+
return incomingWebhookUrl
|
|
41
|
+
.toString()
|
|
42
|
+
.startsWith("https://hooks.slack.com/services/");
|
|
43
|
+
}
|
|
44
|
+
|
|
36
45
|
@CaptureSpan()
|
|
37
46
|
public static override async getUsernameFromUserId(data: {
|
|
38
47
|
authToken: string;
|
|
@@ -1380,4 +1389,9 @@ export default class SlackUtil extends WorkspaceBase {
|
|
|
1380
1389
|
logger.debug(apiResult);
|
|
1381
1390
|
return apiResult;
|
|
1382
1391
|
}
|
|
1392
|
+
|
|
1393
|
+
@CaptureSpan()
|
|
1394
|
+
public static convertMarkdownToSlackRichText(markdown: string): string {
|
|
1395
|
+
return SlackifyMarkdown(markdown);
|
|
1396
|
+
}
|
|
1383
1397
|
}
|