@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.
Files changed (34) hide show
  1. package/Models/DatabaseModels/StatusPage.ts +37 -0
  2. package/Models/DatabaseModels/StatusPageSubscriber.ts +60 -0
  3. package/Server/API/StatusPageAPI.ts +104 -10
  4. package/Server/Infrastructure/Postgres/SchemaMigrations/1749065784320-MigrationName.ts +23 -0
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/1749133333893-MigrationName.ts +17 -0
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  7. package/Server/Services/ScheduledMaintenanceService.ts +21 -0
  8. package/Server/Services/StatusPageSubscriberService.ts +116 -1
  9. package/Server/Utils/OpenAPI.ts +176 -11
  10. package/Server/Utils/Workspace/Slack/Slack.ts +14 -0
  11. package/Utils/Schema/ModelSchema.ts +1303 -11
  12. package/build/dist/Models/DatabaseModels/StatusPage.js +39 -0
  13. package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
  14. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js +62 -0
  15. package/build/dist/Models/DatabaseModels/StatusPageSubscriber.js.map +1 -1
  16. package/build/dist/Server/API/StatusPageAPI.js +73 -10
  17. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  18. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749065784320-MigrationName.js +14 -0
  19. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749065784320-MigrationName.js.map +1 -0
  20. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749133333893-MigrationName.js +12 -0
  21. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1749133333893-MigrationName.js.map +1 -0
  22. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  23. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  24. package/build/dist/Server/Services/ScheduledMaintenanceService.js +19 -0
  25. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  26. package/build/dist/Server/Services/StatusPageSubscriberService.js +98 -1
  27. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  28. package/build/dist/Server/Utils/OpenAPI.js +135 -11
  29. package/build/dist/Server/Utils/OpenAPI.js.map +1 -1
  30. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +15 -0
  31. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  32. package/build/dist/Utils/Schema/ModelSchema.js +1101 -6
  33. package/build/dist/Utils/Schema/ModelSchema.js.map +1 -1
  34. package/package.json +1 -1
@@ -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: { type: "object" },
130
- select: { type: "object" },
131
- sort: { type: "object" },
132
- groupBy: { type: "object" },
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: { type: "object" },
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/${tableName}`,
270
+ $ref: `#/components/schemas/${createSchemaName}`,
232
271
  },
233
- miscDataProps: { type: "object" },
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/${tableName}`,
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/${tableName}`,
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/${tableName}`,
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: model.constructor as new () => DatabaseBaseModel,
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
  }