@oneuptime/common 11.0.9 → 11.0.12

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 (125) hide show
  1. package/Models/DatabaseModels/CephCluster.ts +134 -0
  2. package/Models/DatabaseModels/CloudResource.ts +134 -0
  3. package/Models/DatabaseModels/DockerHost.ts +134 -0
  4. package/Models/DatabaseModels/DockerSwarmCluster.ts +134 -0
  5. package/Models/DatabaseModels/Host.ts +134 -0
  6. package/Models/DatabaseModels/KubernetesCluster.ts +134 -0
  7. package/Models/DatabaseModels/PodmanHost.ts +134 -0
  8. package/Models/DatabaseModels/ProxmoxCluster.ts +134 -0
  9. package/Models/DatabaseModels/RumApplication.ts +134 -0
  10. package/Models/DatabaseModels/ServerlessFunction.ts +134 -0
  11. package/Models/DatabaseModels/Service.ts +134 -0
  12. package/README.md +1 -1
  13. package/Scripts/generate-service-worker.js +46 -36
  14. package/Server/Infrastructure/Postgres/DataSourceOptions.ts +4 -2
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1782600000000-AddArchiveToResources.ts +331 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/Readme.md +3 -3
  18. package/Server/Services/AIAgentService.ts +9 -4
  19. package/Server/Services/AnalyticsDatabaseService.ts +89 -56
  20. package/Server/Services/CephClusterService.ts +6 -4
  21. package/Server/Services/CloudResourceService.ts +6 -4
  22. package/Server/Services/DatabaseService.ts +126 -0
  23. package/Server/Services/DockerHostService.ts +6 -4
  24. package/Server/Services/DockerSwarmClusterService.ts +6 -4
  25. package/Server/Services/HostService.ts +6 -4
  26. package/Server/Services/KubernetesClusterService.ts +6 -4
  27. package/Server/Services/PodmanHostService.ts +6 -4
  28. package/Server/Services/ProbeService.ts +10 -4
  29. package/Server/Services/ProxmoxClusterService.ts +6 -4
  30. package/Server/Services/RumApplicationService.ts +6 -4
  31. package/Server/Services/ServerlessFunctionService.ts +6 -4
  32. package/Server/Services/ServiceService.ts +17 -4
  33. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +1 -1
  34. package/Server/Utils/Monitor/MonitorResource.ts +24 -10
  35. package/Server/Utils/Telemetry/EntityRegistry.ts +10 -5
  36. package/Server/Utils/Workspace/Slack/app-manifest.json +86 -92
  37. package/Tests/Server/Services/UpdateColumnsByIdWithoutHooks.test.ts +154 -0
  38. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +12 -9
  39. package/Tests/Types/DiskSize.test.ts +49 -0
  40. package/Types/Dashboard/DashboardComponents/DashboardTableComponent.ts +11 -0
  41. package/Types/Icon/IconProp.ts +1 -0
  42. package/Types/Monitor/MonitorType.ts +15 -31
  43. package/Types/Monitor/schema.json +32 -35
  44. package/UI/Components/BulkUpdate/BulkArchiveActions.tsx +150 -0
  45. package/UI/Components/Icon/Icon.tsx +42 -126
  46. package/UI/Components/Navbar/NavBarMenuModal.tsx +2 -5
  47. package/UI/esbuild-config.js +136 -110
  48. package/Utils/ClientAPI/README.md +1 -1
  49. package/build/dist/Models/DatabaseModels/CephCluster.js +136 -0
  50. package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
  51. package/build/dist/Models/DatabaseModels/CloudResource.js +136 -0
  52. package/build/dist/Models/DatabaseModels/CloudResource.js.map +1 -1
  53. package/build/dist/Models/DatabaseModels/DockerHost.js +136 -0
  54. package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
  55. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js +136 -0
  56. package/build/dist/Models/DatabaseModels/DockerSwarmCluster.js.map +1 -1
  57. package/build/dist/Models/DatabaseModels/Host.js +136 -0
  58. package/build/dist/Models/DatabaseModels/Host.js.map +1 -1
  59. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +136 -0
  60. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  61. package/build/dist/Models/DatabaseModels/PodmanHost.js +136 -0
  62. package/build/dist/Models/DatabaseModels/PodmanHost.js.map +1 -1
  63. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +136 -0
  64. package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
  65. package/build/dist/Models/DatabaseModels/RumApplication.js +136 -0
  66. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  67. package/build/dist/Models/DatabaseModels/ServerlessFunction.js +136 -0
  68. package/build/dist/Models/DatabaseModels/ServerlessFunction.js.map +1 -1
  69. package/build/dist/Models/DatabaseModels/Service.js +136 -0
  70. package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
  71. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +4 -2
  72. package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
  73. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1782600000000-AddArchiveToResources.js +120 -0
  74. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1782600000000-AddArchiveToResources.js.map +1 -0
  75. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
  76. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  77. package/build/dist/Server/Services/AIAgentService.js +9 -4
  78. package/build/dist/Server/Services/AIAgentService.js.map +1 -1
  79. package/build/dist/Server/Services/AnalyticsDatabaseService.js +52 -17
  80. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  81. package/build/dist/Server/Services/CephClusterService.js +6 -4
  82. package/build/dist/Server/Services/CephClusterService.js.map +1 -1
  83. package/build/dist/Server/Services/CloudResourceService.js +6 -4
  84. package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
  85. package/build/dist/Server/Services/DatabaseService.js +101 -0
  86. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  87. package/build/dist/Server/Services/DockerHostService.js +6 -4
  88. package/build/dist/Server/Services/DockerHostService.js.map +1 -1
  89. package/build/dist/Server/Services/DockerSwarmClusterService.js +6 -4
  90. package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
  91. package/build/dist/Server/Services/HostService.js +6 -4
  92. package/build/dist/Server/Services/HostService.js.map +1 -1
  93. package/build/dist/Server/Services/KubernetesClusterService.js +6 -4
  94. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  95. package/build/dist/Server/Services/PodmanHostService.js +6 -4
  96. package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
  97. package/build/dist/Server/Services/ProbeService.js +10 -4
  98. package/build/dist/Server/Services/ProbeService.js.map +1 -1
  99. package/build/dist/Server/Services/ProxmoxClusterService.js +6 -4
  100. package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
  101. package/build/dist/Server/Services/RumApplicationService.js +6 -4
  102. package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
  103. package/build/dist/Server/Services/ServerlessFunctionService.js +6 -4
  104. package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
  105. package/build/dist/Server/Services/ServiceService.js +17 -4
  106. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  107. package/build/dist/Server/Utils/Monitor/MonitorResource.js +24 -10
  108. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  109. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +10 -5
  110. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  111. package/build/dist/Server/Utils/Workspace/Slack/app-manifest.json +3 -9
  112. package/build/dist/Types/Dashboard/DashboardComponents/DashboardTableComponent.js.map +1 -1
  113. package/build/dist/Types/Icon/IconProp.js +1 -0
  114. package/build/dist/Types/Icon/IconProp.js.map +1 -1
  115. package/build/dist/Types/Monitor/MonitorType.js +15 -31
  116. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  117. package/build/dist/UI/Components/BulkUpdate/BulkArchiveActions.js +96 -0
  118. package/build/dist/UI/Components/BulkUpdate/BulkArchiveActions.js.map +1 -0
  119. package/build/dist/UI/Components/Icon/Icon.js +17 -41
  120. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  121. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +2 -3
  122. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  123. package/jest.config.json +47 -66
  124. package/package.json +1 -1
  125. package/tsconfig.json +109 -113
@@ -1005,16 +1005,14 @@ export default class AnalyticsDatabaseService<
1005
1005
  if (countBy.groupBy && Object.keys(countBy.groupBy).length > 0) {
1006
1006
  const groupByKey: string = Object.keys(countBy.groupBy)[0] as string;
1007
1007
 
1008
- statement.append(
1009
- SQL`DISTINCT ${groupByKey}`
1010
- );
1008
+ statement.append(SQL`DISTINCT ${groupByKey}`);
1011
1009
  }
1012
1010
 
1013
1011
  statement
1014
1012
  .append(
1015
1013
  SQL`) as count
1016
1014
  FROM ${databaseName}.${this.model.tableName}
1017
- WHERE TRUE `
1015
+ WHERE TRUE `,
1018
1016
  )
1019
1017
  .append(whereStatement)
1020
1018
  .append(this.getRetentionReadFilter());
@@ -1052,8 +1050,12 @@ export default class AnalyticsDatabaseService<
1052
1050
  }),
1053
1051
  );
1054
1052
 
1055
- logger.debug(`${this.model.tableName} Count Statement`, { tableName: this.model.tableName } as LogAttributes);
1056
- logger.debug(statement, { tableName: this.model.tableName } as LogAttributes);
1053
+ logger.debug(`${this.model.tableName} Count Statement`, {
1054
+ tableName: this.model.tableName,
1055
+ } as LogAttributes);
1056
+ logger.debug(statement, {
1057
+ tableName: this.model.tableName,
1058
+ } as LogAttributes);
1057
1059
 
1058
1060
  return statement;
1059
1061
  }
@@ -1092,8 +1094,12 @@ export default class AnalyticsDatabaseService<
1092
1094
 
1093
1095
  statement.append(getQuerySettings({ maxExecutionTimeInSeconds: 45 }));
1094
1096
 
1095
- logger.debug(`${this.model.tableName} Exists Statement`, { tableName: this.model.tableName } as LogAttributes);
1096
- logger.debug(statement, { tableName: this.model.tableName } as LogAttributes);
1097
+ logger.debug(`${this.model.tableName} Exists Statement`, {
1098
+ tableName: this.model.tableName,
1099
+ } as LogAttributes);
1100
+ logger.debug(statement, {
1101
+ tableName: this.model.tableName,
1102
+ } as LogAttributes);
1097
1103
 
1098
1104
  return statement;
1099
1105
  }
@@ -1112,11 +1118,11 @@ export default class AnalyticsDatabaseService<
1112
1118
  this.statementGenerator.toAggregateSelectStatement(aggregateBy);
1113
1119
 
1114
1120
  const whereStatement: Statement = this.statementGenerator.toWhereStatement(
1115
- aggregateBy.query
1121
+ aggregateBy.query,
1116
1122
  );
1117
1123
 
1118
1124
  const sortStatement: Statement = this.statementGenerator.toSortStatement(
1119
- aggregateBy.sort!
1125
+ aggregateBy.sort!,
1120
1126
  );
1121
1127
 
1122
1128
  const statement: Statement = SQL``;
@@ -1136,7 +1142,7 @@ export default class AnalyticsDatabaseService<
1136
1142
  statement
1137
1143
  .append(SQL` , `)
1138
1144
  .append(
1139
- this.statementGenerator.toGroupByStatement(aggregateBy.groupBy)
1145
+ this.statementGenerator.toGroupByStatement(aggregateBy.groupBy),
1140
1146
  );
1141
1147
  }
1142
1148
 
@@ -1146,7 +1152,7 @@ export default class AnalyticsDatabaseService<
1146
1152
  SQL` LIMIT ${{
1147
1153
  value: Number(aggregateBy.limit),
1148
1154
  type: TableColumnType.Number,
1149
- }}`
1155
+ }}`,
1150
1156
  );
1151
1157
 
1152
1158
  statement.append(SQL` OFFSET ${{
@@ -1182,8 +1188,12 @@ export default class AnalyticsDatabaseService<
1182
1188
  }),
1183
1189
  );
1184
1190
 
1185
- logger.debug(`${this.model.tableName} Aggregate Statement`, { tableName: this.model.tableName } as LogAttributes);
1186
- logger.debug(statement, { tableName: this.model.tableName } as LogAttributes);
1191
+ logger.debug(`${this.model.tableName} Aggregate Statement`, {
1192
+ tableName: this.model.tableName,
1193
+ } as LogAttributes);
1194
+ logger.debug(statement, {
1195
+ tableName: this.model.tableName,
1196
+ } as LogAttributes);
1187
1197
 
1188
1198
  return { statement, columns: select.columns };
1189
1199
  }
@@ -1206,7 +1216,7 @@ export default class AnalyticsDatabaseService<
1206
1216
  };
1207
1217
 
1208
1218
  groupByStatement = this.statementGenerator.toGroupByStatement(
1209
- findBy.groupBy
1219
+ findBy.groupBy,
1210
1220
  );
1211
1221
  }
1212
1222
 
@@ -1214,11 +1224,11 @@ export default class AnalyticsDatabaseService<
1214
1224
  this.statementGenerator.toSelectStatement(findBy.select!);
1215
1225
 
1216
1226
  const whereStatement: Statement = this.statementGenerator.toWhereStatement(
1217
- findBy.query
1227
+ findBy.query,
1218
1228
  );
1219
1229
 
1220
1230
  const sortStatement: Statement = this.statementGenerator.toSortStatement(
1221
- findBy.sort!
1231
+ findBy.sort!,
1222
1232
  );
1223
1233
 
1224
1234
  const statement: Statement = SQL``;
@@ -1240,7 +1250,7 @@ export default class AnalyticsDatabaseService<
1240
1250
  SQL` LIMIT ${{
1241
1251
  value: Number(findBy.limit),
1242
1252
  type: TableColumnType.Number,
1243
- }}`
1253
+ }}`,
1244
1254
  );
1245
1255
 
1246
1256
  statement.append(SQL` OFFSET ${{
@@ -1263,8 +1273,12 @@ export default class AnalyticsDatabaseService<
1263
1273
  }),
1264
1274
  );
1265
1275
 
1266
- logger.debug(`${this.model.tableName} Find Statement`, { tableName: this.model.tableName } as LogAttributes);
1267
- logger.debug(statement, { tableName: this.model.tableName } as LogAttributes);
1276
+ logger.debug(`${this.model.tableName} Find Statement`, {
1277
+ tableName: this.model.tableName,
1278
+ } as LogAttributes);
1279
+ logger.debug(statement, {
1280
+ tableName: this.model.tableName,
1281
+ } as LogAttributes);
1268
1282
 
1269
1283
  return { statement, columns: select.columns };
1270
1284
  }
@@ -1276,7 +1290,7 @@ export default class AnalyticsDatabaseService<
1276
1290
 
1277
1291
  const databaseName: string = this.database.getDatasourceOptions().database!;
1278
1292
  const whereStatement: Statement = this.statementGenerator.toWhereStatement(
1279
- deleteBy.query
1293
+ deleteBy.query,
1280
1294
  );
1281
1295
 
1282
1296
  /*
@@ -1294,15 +1308,19 @@ export default class AnalyticsDatabaseService<
1294
1308
  DELETE FROM ${databaseName}.${this.model.tableName}
1295
1309
  WHERE TRUE `.append(whereStatement);
1296
1310
 
1297
- logger.debug(`${this.model.tableName} Delete Statement`, { tableName: this.model.tableName } as LogAttributes);
1298
- logger.debug(statement, { tableName: this.model.tableName } as LogAttributes);
1311
+ logger.debug(`${this.model.tableName} Delete Statement`, {
1312
+ tableName: this.model.tableName,
1313
+ } as LogAttributes);
1314
+ logger.debug(statement, {
1315
+ tableName: this.model.tableName,
1316
+ } as LogAttributes);
1299
1317
 
1300
1318
  return statement;
1301
1319
  }
1302
1320
 
1303
1321
  @CaptureSpan()
1304
1322
  public async findOneBy(
1305
- findOneBy: FindOneBy<TBaseModel>
1323
+ findOneBy: FindOneBy<TBaseModel>,
1306
1324
  ): Promise<TBaseModel | null> {
1307
1325
  const findBy: FindBy<TBaseModel> = findOneBy as FindBy<TBaseModel>;
1308
1326
  findBy.limit = new PositiveNumber(1);
@@ -1332,7 +1350,7 @@ export default class AnalyticsDatabaseService<
1332
1350
  beforeDeleteBy.query = await ModelPermission.checkDeletePermission(
1333
1351
  this.modelType,
1334
1352
  beforeDeleteBy.query,
1335
- deleteBy.props
1353
+ deleteBy.props,
1336
1354
  );
1337
1355
 
1338
1356
  const select: Select<TBaseModel> = {};
@@ -1348,8 +1366,12 @@ export default class AnalyticsDatabaseService<
1348
1366
 
1349
1367
  await this.execute(deleteStatement);
1350
1368
 
1351
- logger.debug(`${this.model.tableName} Delete Statement executed`, { tableName: this.model.tableName } as LogAttributes);
1352
- logger.debug(deleteStatement, { tableName: this.model.tableName } as LogAttributes);
1369
+ logger.debug(`${this.model.tableName} Delete Statement executed`, {
1370
+ tableName: this.model.tableName,
1371
+ } as LogAttributes);
1372
+ logger.debug(deleteStatement, {
1373
+ tableName: this.model.tableName,
1374
+ } as LogAttributes);
1353
1375
  } catch (error) {
1354
1376
  await this.onDeleteError(error as Exception);
1355
1377
  throw this.getException(error as Exception);
@@ -1358,7 +1380,7 @@ export default class AnalyticsDatabaseService<
1358
1380
 
1359
1381
  @CaptureSpan()
1360
1382
  public async findOneById(
1361
- findOneById: FindOneByID<TBaseModel>
1383
+ findOneById: FindOneByID<TBaseModel>,
1362
1384
  ): Promise<TBaseModel | null> {
1363
1385
  if (!findOneById.id) {
1364
1386
  throw new BadDataException("findOneById.id is required");
@@ -1390,7 +1412,7 @@ export default class AnalyticsDatabaseService<
1390
1412
  this.modelType,
1391
1413
  beforeUpdateBy.query,
1392
1414
  beforeUpdateBy.data,
1393
- beforeUpdateBy.props
1415
+ beforeUpdateBy.props,
1394
1416
  );
1395
1417
 
1396
1418
  const select: Select<TBaseModel> = {};
@@ -1407,8 +1429,12 @@ export default class AnalyticsDatabaseService<
1407
1429
 
1408
1430
  await this.execute(statement);
1409
1431
 
1410
- logger.debug(`${this.model.tableName} Update Statement executed`, { tableName: this.model.tableName } as LogAttributes);
1411
- logger.debug(statement, { tableName: this.model.tableName } as LogAttributes);
1432
+ logger.debug(`${this.model.tableName} Update Statement executed`, {
1433
+ tableName: this.model.tableName,
1434
+ } as LogAttributes);
1435
+ logger.debug(statement, {
1436
+ tableName: this.model.tableName,
1437
+ } as LogAttributes);
1412
1438
  } catch (error) {
1413
1439
  await this.onUpdateError(error as Exception);
1414
1440
  throw this.getException(error as Exception);
@@ -1437,7 +1463,7 @@ export default class AnalyticsDatabaseService<
1437
1463
  @CaptureSpan()
1438
1464
  public async execute(
1439
1465
  statement: Statement | string,
1440
- options?: ClickhouseExecuteOptions
1466
+ options?: ClickhouseExecuteOptions,
1441
1467
  ): Promise<ExecResult<Stream>> {
1442
1468
  const client: ClickhouseClient = this.getDatabaseClient();
1443
1469
 
@@ -1459,7 +1485,7 @@ export default class AnalyticsDatabaseService<
1459
1485
  @CaptureSpan()
1460
1486
  public async executeQuery(
1461
1487
  statement: Statement | string,
1462
- options?: ClickhouseExecuteOptions
1488
+ options?: ClickhouseExecuteOptions,
1463
1489
  ): Promise<ResultSet<"JSON">> {
1464
1490
  const client: ClickhouseClient = this.getDatabaseClient();
1465
1491
 
@@ -1523,7 +1549,7 @@ export default class AnalyticsDatabaseService<
1523
1549
 
1524
1550
  protected async onUpdateSuccess(
1525
1551
  onUpdate: OnUpdate<TBaseModel>,
1526
- _updatedItemIds: Array<ObjectID>
1552
+ _updatedItemIds: Array<ObjectID>,
1527
1553
  ): Promise<OnUpdate<TBaseModel>> {
1528
1554
  // A place holder method used for overriding.
1529
1555
  return Promise.resolve(onUpdate);
@@ -1536,7 +1562,7 @@ export default class AnalyticsDatabaseService<
1536
1562
 
1537
1563
  protected async onDeleteSuccess(
1538
1564
  onDelete: OnDelete<TBaseModel>,
1539
- _itemIdsBeforeDelete: Array<ObjectID>
1565
+ _itemIdsBeforeDelete: Array<ObjectID>,
1540
1566
  ): Promise<OnDelete<TBaseModel>> {
1541
1567
  // A place holder method used for overriding.
1542
1568
  return Promise.resolve(onDelete);
@@ -1549,7 +1575,7 @@ export default class AnalyticsDatabaseService<
1549
1575
 
1550
1576
  protected async onFindSuccess(
1551
1577
  onFind: OnFind<TBaseModel>,
1552
- items: Array<TBaseModel>
1578
+ items: Array<TBaseModel>,
1553
1579
  ): Promise<OnFind<TBaseModel>> {
1554
1580
  // A place holder method used for overriding.
1555
1581
  return Promise.resolve({ ...onFind, carryForward: items });
@@ -1561,7 +1587,7 @@ export default class AnalyticsDatabaseService<
1561
1587
  }
1562
1588
 
1563
1589
  protected async onCountSuccess(
1564
- count: PositiveNumber
1590
+ count: PositiveNumber,
1565
1591
  ): Promise<PositiveNumber> {
1566
1592
  // A place holder method used for overriding.
1567
1593
  return Promise.resolve(count);
@@ -1574,14 +1600,14 @@ export default class AnalyticsDatabaseService<
1574
1600
 
1575
1601
  protected async onCreateSuccess(
1576
1602
  _onCreate: OnCreate<TBaseModel>,
1577
- createdItem: TBaseModel
1603
+ createdItem: TBaseModel,
1578
1604
  ): Promise<TBaseModel> {
1579
1605
  // A place holder method used for overriding.
1580
1606
  return Promise.resolve(createdItem);
1581
1607
  }
1582
1608
 
1583
1609
  protected async onBeforeCreate(
1584
- createBy: CreateBy<TBaseModel>
1610
+ createBy: CreateBy<TBaseModel>,
1585
1611
  ): Promise<OnCreate<TBaseModel>> {
1586
1612
  // A place holder method used for overriding.
1587
1613
  return Promise.resolve({
@@ -1591,7 +1617,7 @@ export default class AnalyticsDatabaseService<
1591
1617
  }
1592
1618
 
1593
1619
  private async _onBeforeCreate(
1594
- createBy: CreateBy<TBaseModel>
1620
+ createBy: CreateBy<TBaseModel>,
1595
1621
  ): Promise<OnCreate<TBaseModel>> {
1596
1622
  // Private method that runs before create.
1597
1623
  const projectIdColumn: string | null =
@@ -1606,7 +1632,7 @@ export default class AnalyticsDatabaseService<
1606
1632
 
1607
1633
  @CaptureSpan()
1608
1634
  public async createMany(
1609
- createBy: CreateManyBy<TBaseModel>
1635
+ createBy: CreateManyBy<TBaseModel>,
1610
1636
  ): Promise<Array<TBaseModel>> {
1611
1637
  // add tenantId if present.
1612
1638
  const tenantColumnName: string | null =
@@ -1654,7 +1680,7 @@ export default class AnalyticsDatabaseService<
1654
1680
  ModelPermission.checkCreatePermissions(
1655
1681
  this.modelType,
1656
1682
  data,
1657
- createBy.props
1683
+ createBy.props,
1658
1684
  );
1659
1685
 
1660
1686
  items.push(data);
@@ -1662,13 +1688,17 @@ export default class AnalyticsDatabaseService<
1662
1688
 
1663
1689
  try {
1664
1690
  const insertStatement: string = this.statementGenerator.toCreateStatement(
1665
- { item: items }
1691
+ { item: items },
1666
1692
  );
1667
1693
 
1668
1694
  await this.execute(insertStatement);
1669
1695
 
1670
- logger.debug(`${this.model.tableName} Create Statement executed`, { tableName: this.model.tableName } as LogAttributes);
1671
- logger.debug(insertStatement, { tableName: this.model.tableName } as LogAttributes);
1696
+ logger.debug(`${this.model.tableName} Create Statement executed`, {
1697
+ tableName: this.model.tableName,
1698
+ } as LogAttributes);
1699
+ logger.debug(insertStatement, {
1700
+ tableName: this.model.tableName,
1701
+ } as LogAttributes);
1672
1702
 
1673
1703
  if (!createBy.props.ignoreHooks) {
1674
1704
  for (let i: number = 0; i < items.length; i++) {
@@ -1684,7 +1714,7 @@ export default class AnalyticsDatabaseService<
1684
1714
  },
1685
1715
  carryForward: carryForwards[i],
1686
1716
  },
1687
- items[i]!
1717
+ items[i]!,
1688
1718
  );
1689
1719
  }
1690
1720
  }
@@ -1696,7 +1726,7 @@ export default class AnalyticsDatabaseService<
1696
1726
  for (const item of items) {
1697
1727
  if (!tenantId && this.getModel().getTenantColumn()) {
1698
1728
  tenantId = item.getColumnValue<ObjectID>(
1699
- this.getModel().getTenantColumn()!.key
1729
+ this.getModel().getTenantColumn()!.key,
1700
1730
  );
1701
1731
  }
1702
1732
 
@@ -1727,7 +1757,7 @@ export default class AnalyticsDatabaseService<
1727
1757
  tenantId: tenantId,
1728
1758
  eventType: ModelEventType.Create,
1729
1759
  modelType: this.modelType,
1730
- })
1760
+ }),
1731
1761
  );
1732
1762
  }
1733
1763
 
@@ -1766,7 +1796,7 @@ export default class AnalyticsDatabaseService<
1766
1796
  }
1767
1797
 
1768
1798
  private sanitizeCreate<TBaseModel extends AnalyticsBaseModel>(
1769
- data: TBaseModel
1799
+ data: TBaseModel,
1770
1800
  ): TBaseModel {
1771
1801
  if (!data.id) {
1772
1802
  data.id = ObjectID.generateTimeOrdered();
@@ -1798,7 +1828,7 @@ export default class AnalyticsDatabaseService<
1798
1828
  public async onTrigger(
1799
1829
  id: ObjectID,
1800
1830
  projectId: ObjectID,
1801
- triggerType: DatabaseTriggerType
1831
+ triggerType: DatabaseTriggerType,
1802
1832
  ): Promise<void> {
1803
1833
  if (this.getModel().enableWorkflowOn) {
1804
1834
  API.post({
@@ -1807,9 +1837,9 @@ export default class AnalyticsDatabaseService<
1807
1837
  WorkflowHostname,
1808
1838
  new Route(
1809
1839
  `/${WorkflowRoute.toString()}/analytics-model/${projectId.toString()}/${Text.pascalCaseToDashes(
1810
- this.getModel().tableName!
1811
- )}/${triggerType}`
1812
- )
1840
+ this.getModel().tableName!,
1841
+ )}/${triggerType}`,
1842
+ ),
1813
1843
  ),
1814
1844
  data: {
1815
1845
  _id: id.toString(),
@@ -1818,7 +1848,10 @@ export default class AnalyticsDatabaseService<
1818
1848
  ...ClusterKeyAuthorization.getClusterKeyHeaders(),
1819
1849
  },
1820
1850
  }).catch((error: Error) => {
1821
- logger.error(error, { projectId: projectId?.toString(), tableName: this.getModel().tableName } as LogAttributes);
1851
+ logger.error(error, {
1852
+ projectId: projectId?.toString(),
1853
+ tableName: this.getModel().tableName,
1854
+ } as LogAttributes);
1822
1855
  });
1823
1856
  }
1824
1857
  }
@@ -1836,7 +1869,7 @@ export default class AnalyticsDatabaseService<
1836
1869
  ) {
1837
1870
  data.setColumnValue(
1838
1871
  requiredField,
1839
- data.getDefaultValueForColumn(requiredField)
1872
+ data.getDefaultValueForColumn(requiredField),
1840
1873
  );
1841
1874
  } else {
1842
1875
  throw new BadDataException(`${requiredField} is required`);
@@ -1849,7 +1882,7 @@ export default class AnalyticsDatabaseService<
1849
1882
  // add default value.
1850
1883
  data.setColumnValue(
1851
1884
  requiredField,
1852
- data.getDefaultValueForColumn(requiredField)
1885
+ data.getDefaultValueForColumn(requiredField),
1853
1886
  );
1854
1887
  } else if (
1855
1888
  ((data as any)[requiredField] === null ||
@@ -269,12 +269,14 @@ export class Service extends DatabaseService<Model> {
269
269
  data.capacityUsedPercent = extra.capacityUsedPercent;
270
270
  }
271
271
 
272
- await this.updateOneById({
272
+ /*
273
+ * Heartbeat write: a single-statement UPDATE with no hooks and no
274
+ * `version` bump, avoiding the hot-row Postgres lock convoy that the
275
+ * full updateOneById pipeline causes. See ServiceService.updateLastSeen.
276
+ */
277
+ await this.updateColumnsByIdWithoutHooks({
273
278
  id: clusterId,
274
279
  data: data,
275
- props: {
276
- isRoot: true,
277
- },
278
280
  });
279
281
  }
280
282
 
@@ -226,12 +226,14 @@ export class Service extends DatabaseService<Model> {
226
226
  data.runtimeVersion = extra.runtimeVersion;
227
227
  }
228
228
 
229
- await this.updateOneById({
229
+ /*
230
+ * Heartbeat write: a single-statement UPDATE with no hooks and no
231
+ * `version` bump, avoiding the hot-row Postgres lock convoy that the
232
+ * full updateOneById pipeline causes. See ServiceService.updateLastSeen.
233
+ */
234
+ await this.updateColumnsByIdWithoutHooks({
230
235
  id: cloudResourceId,
231
236
  data: data,
232
- props: {
233
- isRoot: true,
234
- },
235
237
  });
236
238
  }
237
239
 
@@ -46,6 +46,7 @@ import PartialEntity from "../../Types/Database/PartialEntity";
46
46
  import { TableColumnMetadata } from "../../Types/Database/TableColumn";
47
47
  import TableColumnType from "../../Types/Database/TableColumnType";
48
48
  import { getUniqueColumnsBy } from "../../Types/Database/UniqueColumnBy";
49
+ import OneUptimeDate from "../../Types/Date";
49
50
  import Dictionary from "../../Types/Dictionary";
50
51
  import BadDataException from "../../Types/Exception/BadDataException";
51
52
  import DatabaseNotConnectedException from "../../Types/Exception/DatabaseNotConnectedException";
@@ -62,10 +63,14 @@ import API from "../../Utils/API";
62
63
  import Slug from "../../Utils/Slug";
63
64
  import {
64
65
  DataSource,
66
+ Driver,
65
67
  EntityManager,
66
68
  Repository,
67
69
  SelectQueryBuilder,
68
70
  } from "typeorm";
71
+ import { ColumnMetadata } from "typeorm/metadata/ColumnMetadata";
72
+ import { EntityMetadata } from "typeorm/metadata/EntityMetadata";
73
+ import { ObjectLiteral } from "typeorm/common/ObjectLiteral";
69
74
  import { FindWhere } from "../../Types/BaseDatabase/Query";
70
75
  import Realtime from "../Utils/Realtime";
71
76
  import ModelEventType from "../../Types/Realtime/ModelEventType";
@@ -578,6 +583,28 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
578
583
  (data as any)["createdByUserId"] = props.userId;
579
584
  }
580
585
 
586
+ /*
587
+ * Stamp archive audit fields when a resource is being (un)archived.
588
+ * The client only ever sends `isArchived`; we stamp `archivedAt` and
589
+ * `archivedByUserId` here — which runs after column update-permission
590
+ * checks — so these audit fields are server-controlled and cannot be
591
+ * spoofed, exactly like `createdByUserId` above.
592
+ */
593
+ if (isUpdate && (data as any)["isArchived"] !== undefined) {
594
+ const isArchivedValue: boolean = Boolean((data as any)["isArchived"]);
595
+
596
+ if (this.model.hasColumn("archivedAt")) {
597
+ (data as any)["archivedAt"] = isArchivedValue
598
+ ? OneUptimeDate.getCurrentDate()
599
+ : null;
600
+ }
601
+
602
+ if (this.model.hasColumn("archivedByUserId")) {
603
+ (data as any)["archivedByUserId"] =
604
+ isArchivedValue && props.userId ? props.userId : null;
605
+ }
606
+ }
607
+
581
608
  return data;
582
609
  }
583
610
 
@@ -1968,6 +1995,105 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
1968
1995
  });
1969
1996
  }
1970
1997
 
1998
+ /*
1999
+ * Fast, side-effect-free single-statement column update by id.
2000
+ *
2001
+ * `updateOneById` is heavy: an access-control pre-check SELECT, then
2002
+ * `_updateBy` (a `_findBy` SELECT to load the row, `getRepository().save()`,
2003
+ * then workflow / realtime / audit-log hooks). That is several round
2004
+ * trips, each holding the row's write lock and pinning a pool connection.
2005
+ * On a hot row written on every ingest batch — e.g. a telemetry
2006
+ * liveness/heartbeat timestamp — that pipeline becomes the head of a
2007
+ * Postgres lock convoy: one slow writer (or a save() transaction left open
2008
+ * across other async work) blocks every other writer of the same row, and
2009
+ * the waiters each pin a pool connection until the whole pool is starved.
2010
+ *
2011
+ * This issues exactly ONE auto-committed UPDATE (raw parameterized SQL).
2012
+ * The row's write lock is held only for that statement, so the write can
2013
+ * never head a convoy and never spans other async work. It deliberately
2014
+ * runs NO hooks, workflow triggers, realtime events, audit-log writes, or
2015
+ * access-control checks, and does NOT bump the optimistic-lock `version`
2016
+ * column. (We can't use the entity-aware QueryBuilder for the
2017
+ * no-version-bump part: TypeORM resolves even a table-name string back to
2018
+ * the entity metadata, whose UpdateQueryBuilder branch always appends
2019
+ * `version = version + 1`.) `updatedAt` is refreshed to preserve the
2020
+ * behaviour of the normal update path.
2021
+ *
2022
+ * Column and table identifiers are taken from entity metadata (never from
2023
+ * the caller), and every value is bound as a parameter, so this is not an
2024
+ * injection surface. Use ONLY for trusted, internal, side-effect-free
2025
+ * column writes where the full pipeline is pure overhead.
2026
+ */
2027
+ @CaptureSpan()
2028
+ public async updateColumnsByIdWithoutHooks(input: {
2029
+ id: ObjectID;
2030
+ data: PartialEntity<TBaseModel>;
2031
+ }): Promise<void> {
2032
+ if (!input.id) {
2033
+ throw new BadDataException("id is required");
2034
+ }
2035
+
2036
+ const repository: Repository<TBaseModel> = this.getRepository();
2037
+ const metadata: EntityMetadata = repository.metadata;
2038
+ const driver: Driver = repository.manager.connection.driver;
2039
+
2040
+ const setClauses: Array<string> = [];
2041
+ const params: Array<unknown> = [];
2042
+
2043
+ for (const [propertyName, value] of Object.entries(
2044
+ input.data as ObjectLiteral,
2045
+ )) {
2046
+ const column: ColumnMetadata | undefined =
2047
+ metadata.findColumnWithPropertyName(propertyName);
2048
+ if (!column) {
2049
+ throw new BadDataException(
2050
+ `updateColumnsByIdWithoutHooks: unknown column "${propertyName}" on "${metadata.tableName}"`,
2051
+ );
2052
+ }
2053
+ /*
2054
+ * PartialEntity permits `() => string` SQL-expression values, but this
2055
+ * raw bind path can only parameterize literals (and the save()-based
2056
+ * updateOneById it replaces doesn't support expressions either). Fail
2057
+ * loudly rather than binding a function object.
2058
+ */
2059
+ if (typeof value === "function") {
2060
+ throw new BadDataException(
2061
+ `updateColumnsByIdWithoutHooks: SQL-expression values are not supported (column "${propertyName}"); pass a literal value.`,
2062
+ );
2063
+ }
2064
+ /*
2065
+ * Run the value through the driver's persist path so column
2066
+ * transformers (e.g. ObjectID <-> uuid), JSON stringification, and
2067
+ * boolean/date coercion are applied exactly as getRepository().save()
2068
+ * would. Without this, the raw bind below would write an ObjectID
2069
+ * object (or a raw JS object) straight to Postgres.
2070
+ */
2071
+ params.push(driver.preparePersistentValue(value, column));
2072
+ setClauses.push(`"${column.databaseName}" = $${params.length}`);
2073
+ }
2074
+
2075
+ if (setClauses.length === 0) {
2076
+ return;
2077
+ }
2078
+
2079
+ // The raw path has no entity machinery to touch updateDate — do it here.
2080
+ if (metadata.updateDateColumn) {
2081
+ setClauses.push(
2082
+ `"${metadata.updateDateColumn.databaseName}" = CURRENT_TIMESTAMP`,
2083
+ );
2084
+ }
2085
+
2086
+ const primaryColumnName: string =
2087
+ metadata.primaryColumns[0]?.databaseName || "_id";
2088
+ params.push(input.id.toString());
2089
+
2090
+ const sql: string = `UPDATE "${metadata.tableName}" SET ${setClauses.join(
2091
+ ", ",
2092
+ )} WHERE "${primaryColumnName}" = $${params.length}`;
2093
+
2094
+ await repository.manager.query(sql, params);
2095
+ }
2096
+
1971
2097
  @CaptureSpan()
1972
2098
  protected async atomicIncrementColumnValueByOne(data: {
1973
2099
  id: ObjectID;
@@ -236,12 +236,14 @@ export class Service extends DatabaseService<Model> {
236
236
  data.agentVersion = extra.agentVersion;
237
237
  }
238
238
 
239
- await this.updateOneById({
239
+ /*
240
+ * Heartbeat write: a single-statement UPDATE with no hooks and no
241
+ * `version` bump, avoiding the hot-row Postgres lock convoy that the
242
+ * full updateOneById pipeline causes. See ServiceService.updateLastSeen.
243
+ */
244
+ await this.updateColumnsByIdWithoutHooks({
240
245
  id: hostId,
241
246
  data: data,
242
- props: {
243
- isRoot: true,
244
- },
245
247
  });
246
248
  }
247
249
 
@@ -271,12 +271,14 @@ export class Service extends DatabaseService<Model> {
271
271
  data.networkCount = extra.networkCount;
272
272
  }
273
273
 
274
- await this.updateOneById({
274
+ /*
275
+ * Heartbeat write: a single-statement UPDATE with no hooks and no
276
+ * `version` bump, avoiding the hot-row Postgres lock convoy that the
277
+ * full updateOneById pipeline causes. See ServiceService.updateLastSeen.
278
+ */
279
+ await this.updateColumnsByIdWithoutHooks({
275
280
  id: clusterId,
276
281
  data: data,
277
- props: {
278
- isRoot: true,
279
- },
280
282
  });
281
283
  }
282
284
 
@@ -309,12 +309,14 @@ export class Service extends DatabaseService<Model> {
309
309
  data.cloudAccountId = extra.cloudAccountId;
310
310
  }
311
311
 
312
- await this.updateOneById({
312
+ /*
313
+ * Heartbeat write: a single-statement UPDATE with no hooks and no
314
+ * `version` bump, avoiding the hot-row Postgres lock convoy that the
315
+ * full updateOneById pipeline causes. See ServiceService.updateLastSeen.
316
+ */
317
+ await this.updateColumnsByIdWithoutHooks({
313
318
  id: hostId,
314
319
  data: data,
315
- props: {
316
- isRoot: true,
317
- },
318
320
  });
319
321
  }
320
322