@oneuptime/common 12.0.3 → 12.0.4
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/CephCluster.ts +14 -0
- package/Models/DatabaseModels/CodeRepository.ts +14 -6
- package/Models/DatabaseModels/DockerHost.ts +14 -0
- package/Models/DatabaseModels/GlobalOidcProject.ts +18 -0
- package/Models/DatabaseModels/GlobalSsoProject.ts +18 -0
- package/Models/DatabaseModels/IoTFleet.ts +14 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +14 -0
- package/Models/DatabaseModels/NetworkDevice.ts +14 -0
- package/Models/DatabaseModels/NetworkInterface.ts +18 -0
- package/Models/DatabaseModels/Project.ts +16 -2
- package/Models/DatabaseModels/ProxmoxCluster.ts +14 -0
- package/Models/DatabaseModels/Service.ts +30 -0
- package/Models/DatabaseModels/StatusPagePrivateUser.ts +30 -0
- package/Models/DatabaseModels/User.ts +30 -0
- package/Server/API/AIAgentDataAPI.ts +31 -0
- package/Server/API/DashboardAPI.ts +419 -12
- package/Server/API/GitHubAPI.ts +119 -283
- package/Server/API/UserAPI.ts +263 -1
- package/Server/EnvironmentConfig.ts +7 -3
- package/Server/Infrastructure/GlobalCache.ts +37 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.ts +21 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.ts +68 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.ts +100 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.ts +214 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.ts +69 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Services/AnalyticsDatabaseService.ts +72 -1
- package/Server/Services/CodeRepositoryService.ts +105 -2
- package/Server/Services/DatabaseService.ts +185 -2
- package/Server/Services/OpenTelemetryIngestService.ts +230 -26
- package/Server/Services/UserService.ts +184 -0
- package/Server/Utils/AI/Toolbox/CodeTools.ts +45 -4
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +13 -0
- package/Server/Utils/CodeRepository/GitHub/GitHub.ts +157 -4
- package/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.ts +130 -0
- package/Server/Utils/PasswordHash.ts +306 -0
- package/Tests/App/Dashboard/OverviewCustomFields.test.tsx +333 -0
- package/Tests/App/Dashboard/UserCustomFields.test.tsx +392 -0
- package/Tests/Server/API/AIAgentDataRepositoryToken.test.ts +381 -0
- package/Tests/Server/API/DashboardPublicAttributeValuesAPI.test.ts +943 -0
- package/Tests/Server/API/DashboardPublicMetricsAggregateAPI.test.ts +1296 -0
- package/Tests/Server/API/DashboardPublicTemplatePayloads.test.ts +598 -0
- package/Tests/Server/API/GitHubAppInstallationBindingAPI.test.ts +539 -0
- package/Tests/Server/API/Helpers.ts +6 -1
- package/Tests/Server/API/UserProjectsAPI.test.ts +852 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +128 -0
- package/Tests/Server/Infrastructure/SemaphoreMutex.test.ts +215 -0
- package/Tests/Server/Services/AddPerUserPasswordSaltMigration.test.ts +178 -0
- package/Tests/Server/Services/AnalyticsDatabasePaginationStability.test.ts +582 -0
- package/Tests/Server/Services/AnalyticsDatabaseService.test.ts +5 -0
- package/Tests/Server/Services/CodeRepositoryInstallationBinding.test.ts +305 -0
- package/Tests/Server/Services/CodeRepositoryResolutionBinding.test.ts +147 -0
- package/Tests/Server/Services/DatabaseServicePerUserPasswordSalt.test.ts +870 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +568 -0
- package/Tests/Server/Services/RestoreDroppedUniqueIndexesMigration.test.ts +386 -0
- package/Tests/Server/Services/RestoreServiceLowerNameIndexMigration.test.ts +240 -0
- package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +885 -0
- package/Tests/Server/Services/WidenHashedStringColumnsForScryptMigration.test.ts +220 -0
- package/Tests/Server/Utils/AI/CodeTools.test.ts +47 -1
- package/Tests/Server/Utils/AI/CodeWriteTools.test.ts +10 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +120 -0
- package/Tests/Server/Utils/CodeRepository/GitHubInstallationBinding.test.ts +292 -0
- package/Tests/Server/Utils/GitHubInstallationOwnershipVerification.test.ts +340 -0
- package/Tests/Server/Utils/GitHubWebhookAndTreeCacheIsolation.test.ts +250 -0
- package/Tests/Server/Utils/PasswordHash.test.ts +555 -0
- package/Tests/Types/Database/ColumnLength.test.ts +7 -1
- package/Tests/Types/HashedStringPerUserSalt.test.ts +476 -0
- package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +293 -0
- package/Tests/Types/Monitor/HostAlertTemplates.test.ts +249 -0
- package/Tests/Types/Monitor/IotAlertTemplates.test.ts +320 -0
- package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +271 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +696 -0
- package/Tests/UI/Utils/UserProjectsModelAPI.test.ts +714 -0
- package/Tests/Utils/TeamMembersByProject.test.ts +655 -0
- package/Types/Database/ColumnLength.ts +9 -1
- package/Types/Database/TableColumn.ts +11 -0
- package/Types/Database/UnsynchronizedIndex.ts +48 -0
- package/Types/HashedString.ts +139 -4
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +85 -11
- package/UI/Components/Navbar/NavBar.tsx +12 -2
- package/UI/Components/Navbar/NavBarMenuModal.tsx +19 -7
- package/UI/Utils/ModelAPI/UserProjectsModelAPI.ts +257 -0
- package/Utils/TeamMembersByProject.ts +237 -0
- package/build/dist/Models/DatabaseModels/CephCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/CephCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/CodeRepository.js +14 -6
- package/build/dist/Models/DatabaseModels/CodeRepository.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js +16 -1
- package/build/dist/Models/DatabaseModels/DockerHost.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js +16 -1
- package/build/dist/Models/DatabaseModels/GlobalOidcProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js +16 -1
- package/build/dist/Models/DatabaseModels/GlobalSsoProject.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js +16 -1
- package/build/dist/Models/DatabaseModels/IoTFleet.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +16 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkInterface.js +16 -1
- package/build/dist/Models/DatabaseModels/NetworkInterface.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +16 -2
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js +16 -1
- package/build/dist/Models/DatabaseModels/ProxmoxCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Service.js +28 -1
- package/build/dist/Models/DatabaseModels/Service.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +32 -0
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +32 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Server/API/AIAgentDataAPI.js +21 -2
- package/build/dist/Server/API/AIAgentDataAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +304 -10
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/GitHubAPI.js +94 -167
- package/build/dist/Server/API/GitHubAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +187 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +7 -3
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +29 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786018109307-AddPerUserPasswordSalt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js +59 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786023262402-WidenHashedStringColumnsForScrypt.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js +93 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786100000000-RestoreServiceLowerNameIndex.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js +101 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786200000000-RestoreDroppedUniqueIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js +66 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786300000000-QuarantineUnboundGitHubInstallations.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +57 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/CodeRepositoryService.js +90 -2
- package/build/dist/Server/Services/CodeRepositoryService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +160 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +155 -25
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +165 -0
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js +31 -1
- package/build/dist/Server/Utils/AI/Toolbox/CodeTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +13 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js +126 -4
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHub.js.map +1 -1
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js +136 -0
- package/build/dist/Server/Utils/CodeRepository/GitHub/GitHubInstallationBinding.js.map +1 -0
- package/build/dist/Server/Utils/PasswordHash.js +237 -0
- package/build/dist/Server/Utils/PasswordHash.js.map +1 -0
- package/build/dist/Types/Database/ColumnLength.js +9 -1
- package/build/dist/Types/Database/ColumnLength.js.map +1 -1
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/Database/UnsynchronizedIndex.js +46 -0
- package/build/dist/Types/Database/UnsynchronizedIndex.js.map +1 -0
- package/build/dist/Types/HashedString.js +95 -5
- package/build/dist/Types/HashedString.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +51 -10
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBar.js +3 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +19 -7
- package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js +156 -0
- package/build/dist/UI/Utils/ModelAPI/UserProjectsModelAPI.js.map +1 -0
- package/build/dist/Utils/TeamMembersByProject.js +145 -0
- package/build/dist/Utils/TeamMembersByProject.js.map +1 -0
- package/package.json +1 -1
|
@@ -80,6 +80,20 @@ import {
|
|
|
80
80
|
*/
|
|
81
81
|
@Index(["projectId", "name"], { unique: true })
|
|
82
82
|
@Index(["projectId", "isArchived"])
|
|
83
|
+
/*
|
|
84
|
+
* Restores a UNIQUE index that an autogenerated migration deleted.
|
|
85
|
+
*
|
|
86
|
+
* Declared natively and NAMED. TypeORM's schema builder matches database
|
|
87
|
+
* indexes to entity metadata by name and drops every one it cannot find, which
|
|
88
|
+
* is how the original was lost: it existed only in a migration, so entity
|
|
89
|
+
* metadata never mentioned it and `migration:generate` emitted a DROP. Naming
|
|
90
|
+
* it here keeps the original index name and lets the builder match, compare
|
|
91
|
+
* uniqueness and columns, and keep it.
|
|
92
|
+
*/
|
|
93
|
+
@Index("IDX_ceph_cluster_slug", ["slug"], {
|
|
94
|
+
unique: true,
|
|
95
|
+
where: '"deletedAt" IS NULL',
|
|
96
|
+
})
|
|
83
97
|
@TableMetadata({
|
|
84
98
|
tableName: "CephCluster",
|
|
85
99
|
singularName: "Ceph Cluster",
|
|
@@ -649,13 +649,21 @@ export default class CodeRepository extends BaseModel {
|
|
|
649
649
|
})
|
|
650
650
|
public repositoryUrl?: URL = undefined;
|
|
651
651
|
|
|
652
|
-
|
|
652
|
+
/*
|
|
653
|
+
* GitHub App specific fields.
|
|
654
|
+
*
|
|
655
|
+
* `create` is empty on purpose. This column decides which GitHub App
|
|
656
|
+
* installation a repository token is minted from, so a caller who could set
|
|
657
|
+
* it could name ANOTHER tenant's installation, put the row in a project they
|
|
658
|
+
* own, and pass every "is this row in your project?" check on the way to a
|
|
659
|
+
* live `ghs_` token. It is assigned server-side only — by the install
|
|
660
|
+
* callback and the installation webhooks, which write with `isRoot` after
|
|
661
|
+
* GitHub has confirmed the installation belongs to the project.
|
|
662
|
+
* CodeRepositoryService.onBeforeCreate/onBeforeUpdate enforce the same rule
|
|
663
|
+
* a second time, in case a future route writes as root.
|
|
664
|
+
*/
|
|
653
665
|
@ColumnAccessControl({
|
|
654
|
-
create: [
|
|
655
|
-
Permission.ProjectOwner,
|
|
656
|
-
Permission.ProjectAdmin,
|
|
657
|
-
Permission.CreateCodeRepository,
|
|
658
|
-
],
|
|
666
|
+
create: [],
|
|
659
667
|
read: [
|
|
660
668
|
Permission.ProjectOwner,
|
|
661
669
|
Permission.ProjectAdmin,
|
|
@@ -72,6 +72,20 @@ import {
|
|
|
72
72
|
@CrudApiEndpoint(new Route("/docker-host"))
|
|
73
73
|
@SlugifyColumn("name", "slug")
|
|
74
74
|
@Index(["projectId", "isArchived"])
|
|
75
|
+
/*
|
|
76
|
+
* Restores a UNIQUE index that an autogenerated migration deleted.
|
|
77
|
+
*
|
|
78
|
+
* Declared natively and NAMED. TypeORM's schema builder matches database
|
|
79
|
+
* indexes to entity metadata by name and drops every one it cannot find, which
|
|
80
|
+
* is how the original was lost: it existed only in a migration, so entity
|
|
81
|
+
* metadata never mentioned it and `migration:generate` emitted a DROP. Naming
|
|
82
|
+
* it here keeps the original index name and lets the builder match, compare
|
|
83
|
+
* uniqueness and columns, and keep it.
|
|
84
|
+
*/
|
|
85
|
+
@Index("IDX_docker_host_slug", ["slug"], {
|
|
86
|
+
unique: true,
|
|
87
|
+
where: '"deletedAt" IS NULL',
|
|
88
|
+
})
|
|
75
89
|
@TableMetadata({
|
|
76
90
|
tableName: "DockerHost",
|
|
77
91
|
singularName: "Docker Host",
|
|
@@ -39,6 +39,24 @@ import {
|
|
|
39
39
|
update: [],
|
|
40
40
|
})
|
|
41
41
|
@CrudApiEndpoint(new Route("/global-oidc-project"))
|
|
42
|
+
/*
|
|
43
|
+
* Restores a UNIQUE index that an autogenerated migration deleted.
|
|
44
|
+
*
|
|
45
|
+
* Declared natively and NAMED. TypeORM's schema builder matches database
|
|
46
|
+
* indexes to entity metadata by name and drops every one it cannot find, which
|
|
47
|
+
* is how the original was lost: it existed only in a migration, so entity
|
|
48
|
+
* metadata never mentioned it and `migration:generate` emitted a DROP. Naming
|
|
49
|
+
* it here keeps the original index name and lets the builder match, compare
|
|
50
|
+
* uniqueness and columns, and keep it.
|
|
51
|
+
*/
|
|
52
|
+
@Index(
|
|
53
|
+
"IDX_GlobalOIDCProject_globalOidcId_projectId",
|
|
54
|
+
["globalOidcId", "projectId"],
|
|
55
|
+
{
|
|
56
|
+
unique: true,
|
|
57
|
+
where: '"deletedAt" IS NULL',
|
|
58
|
+
},
|
|
59
|
+
)
|
|
42
60
|
@TableMetadata({
|
|
43
61
|
tableName: "GlobalOIDCProject",
|
|
44
62
|
singularName: "Global OIDC Project",
|
|
@@ -42,6 +42,24 @@ import {
|
|
|
42
42
|
update: [],
|
|
43
43
|
})
|
|
44
44
|
@CrudApiEndpoint(new Route("/global-sso-project"))
|
|
45
|
+
/*
|
|
46
|
+
* Restores a UNIQUE index that an autogenerated migration deleted.
|
|
47
|
+
*
|
|
48
|
+
* Declared natively and NAMED. TypeORM's schema builder matches database
|
|
49
|
+
* indexes to entity metadata by name and drops every one it cannot find, which
|
|
50
|
+
* is how the original was lost: it existed only in a migration, so entity
|
|
51
|
+
* metadata never mentioned it and `migration:generate` emitted a DROP. Naming
|
|
52
|
+
* it here keeps the original index name and lets the builder match, compare
|
|
53
|
+
* uniqueness and columns, and keep it.
|
|
54
|
+
*/
|
|
55
|
+
@Index(
|
|
56
|
+
"IDX_GlobalSSOProject_globalSsoId_projectId",
|
|
57
|
+
["globalSsoId", "projectId"],
|
|
58
|
+
{
|
|
59
|
+
unique: true,
|
|
60
|
+
where: '"deletedAt" IS NULL',
|
|
61
|
+
},
|
|
62
|
+
)
|
|
45
63
|
@TableMetadata({
|
|
46
64
|
tableName: "GlobalSSOProject",
|
|
47
65
|
singularName: "Global SSO Project",
|
|
@@ -80,6 +80,20 @@ import {
|
|
|
80
80
|
*/
|
|
81
81
|
@Index(["projectId", "name"], { unique: true })
|
|
82
82
|
@Index(["projectId", "isArchived"])
|
|
83
|
+
/*
|
|
84
|
+
* Restores a UNIQUE index that an autogenerated migration deleted.
|
|
85
|
+
*
|
|
86
|
+
* Declared natively and NAMED. TypeORM's schema builder matches database
|
|
87
|
+
* indexes to entity metadata by name and drops every one it cannot find, which
|
|
88
|
+
* is how the original was lost: it existed only in a migration, so entity
|
|
89
|
+
* metadata never mentioned it and `migration:generate` emitted a DROP. Naming
|
|
90
|
+
* it here keeps the original index name and lets the builder match, compare
|
|
91
|
+
* uniqueness and columns, and keep it.
|
|
92
|
+
*/
|
|
93
|
+
@Index("IDX_iot_fleet_slug", ["slug"], {
|
|
94
|
+
unique: true,
|
|
95
|
+
where: '"deletedAt" IS NULL',
|
|
96
|
+
})
|
|
83
97
|
@TableMetadata({
|
|
84
98
|
tableName: "IoTFleet",
|
|
85
99
|
singularName: "IoT Fleet",
|
|
@@ -79,6 +79,20 @@ import {
|
|
|
79
79
|
*/
|
|
80
80
|
@Index(["projectId", "clusterIdentifier"], { unique: true })
|
|
81
81
|
@Index(["projectId", "isArchived"])
|
|
82
|
+
/*
|
|
83
|
+
* Restores a UNIQUE index that an autogenerated migration deleted.
|
|
84
|
+
*
|
|
85
|
+
* Declared natively and NAMED. TypeORM's schema builder matches database
|
|
86
|
+
* indexes to entity metadata by name and drops every one it cannot find, which
|
|
87
|
+
* is how the original was lost: it existed only in a migration, so entity
|
|
88
|
+
* metadata never mentioned it and `migration:generate` emitted a DROP. Naming
|
|
89
|
+
* it here keeps the original index name and lets the builder match, compare
|
|
90
|
+
* uniqueness and columns, and keep it.
|
|
91
|
+
*/
|
|
92
|
+
@Index("IDX_kubernetes_cluster_slug", ["slug"], {
|
|
93
|
+
unique: true,
|
|
94
|
+
where: '"deletedAt" IS NULL',
|
|
95
|
+
})
|
|
82
96
|
@TableMetadata({
|
|
83
97
|
tableName: "KubernetesCluster",
|
|
84
98
|
singularName: "Kubernetes Cluster",
|
|
@@ -78,6 +78,20 @@ import {
|
|
|
78
78
|
@CrudApiEndpoint(new Route("/network-device"))
|
|
79
79
|
@SlugifyColumn("name", "slug")
|
|
80
80
|
@Index(["projectId", "isArchived"])
|
|
81
|
+
/*
|
|
82
|
+
* Restores a UNIQUE index that an autogenerated migration deleted.
|
|
83
|
+
*
|
|
84
|
+
* Declared natively and NAMED. TypeORM's schema builder matches database
|
|
85
|
+
* indexes to entity metadata by name and drops every one it cannot find, which
|
|
86
|
+
* is how the original was lost: it existed only in a migration, so entity
|
|
87
|
+
* metadata never mentioned it and `migration:generate` emitted a DROP. Naming
|
|
88
|
+
* it here keeps the original index name and lets the builder match, compare
|
|
89
|
+
* uniqueness and columns, and keep it.
|
|
90
|
+
*/
|
|
91
|
+
@Index("IDX_network_device_slug", ["slug"], {
|
|
92
|
+
unique: true,
|
|
93
|
+
where: '"deletedAt" IS NULL',
|
|
94
|
+
})
|
|
81
95
|
@TableMetadata({
|
|
82
96
|
tableName: "NetworkDevice",
|
|
83
97
|
singularName: "Network Device",
|
|
@@ -72,6 +72,24 @@ const decimalTransformer: ValueTransformer = {
|
|
|
72
72
|
})
|
|
73
73
|
@CrudApiEndpoint(new Route("/network-interface"))
|
|
74
74
|
@Index(["projectId", "networkDeviceId"])
|
|
75
|
+
/*
|
|
76
|
+
* Restores a UNIQUE index that an autogenerated migration deleted.
|
|
77
|
+
*
|
|
78
|
+
* Declared natively and NAMED. TypeORM's schema builder matches database
|
|
79
|
+
* indexes to entity metadata by name and drops every one it cannot find, which
|
|
80
|
+
* is how the original was lost: it existed only in a migration, so entity
|
|
81
|
+
* metadata never mentioned it and `migration:generate` emitted a DROP. Naming
|
|
82
|
+
* it here keeps the original index name and lets the builder match, compare
|
|
83
|
+
* uniqueness and columns, and keep it.
|
|
84
|
+
*/
|
|
85
|
+
@Index(
|
|
86
|
+
"IDX_network_interface_device_ifindex",
|
|
87
|
+
["networkDeviceId", "interfaceIndex"],
|
|
88
|
+
{
|
|
89
|
+
unique: true,
|
|
90
|
+
where: '"deletedAt" IS NULL',
|
|
91
|
+
},
|
|
92
|
+
)
|
|
75
93
|
@TableMetadata({
|
|
76
94
|
tableName: "NetworkInterface",
|
|
77
95
|
singularName: "Network Interface",
|
|
@@ -2583,7 +2583,21 @@ export default class Project extends TenantModel {
|
|
|
2583
2583
|
})
|
|
2584
2584
|
public doNotAddGlobalProbesByDefaultOnNewMonitors?: boolean = undefined;
|
|
2585
2585
|
|
|
2586
|
-
|
|
2586
|
+
/*
|
|
2587
|
+
* GitHub App Installation ID for this project.
|
|
2588
|
+
*
|
|
2589
|
+
* This column IS the authority on which GitHub App installation a project
|
|
2590
|
+
* owns — every path that mints a repository token re-derives the binding
|
|
2591
|
+
* from it (see GitHubInstallationBinding). That makes it security state, not
|
|
2592
|
+
* project configuration, so `update` is empty: a Project Owner who could PUT
|
|
2593
|
+
* an arbitrary installation ID here would simply be writing their own
|
|
2594
|
+
* permission slip to another tenant's repositories.
|
|
2595
|
+
*
|
|
2596
|
+
* It is written server-side only, with `isRoot`, by the GitHub App install
|
|
2597
|
+
* callback — and only after the OAuth user-to-server round trip has proved
|
|
2598
|
+
* to GitHub's satisfaction that the installer controls that installation —
|
|
2599
|
+
* and cleared again by the `installation.deleted` webhook.
|
|
2600
|
+
*/
|
|
2587
2601
|
@ColumnAccessControl({
|
|
2588
2602
|
create: [],
|
|
2589
2603
|
read: [
|
|
@@ -2593,7 +2607,7 @@ export default class Project extends TenantModel {
|
|
|
2593
2607
|
Permission.Viewer,
|
|
2594
2608
|
Permission.ReadProject,
|
|
2595
2609
|
],
|
|
2596
|
-
update: [
|
|
2610
|
+
update: [],
|
|
2597
2611
|
})
|
|
2598
2612
|
@TableColumn({
|
|
2599
2613
|
required: false,
|
|
@@ -81,6 +81,20 @@ import {
|
|
|
81
81
|
*/
|
|
82
82
|
@Index(["projectId", "name"], { unique: true })
|
|
83
83
|
@Index(["projectId", "isArchived"])
|
|
84
|
+
/*
|
|
85
|
+
* Restores a UNIQUE index that an autogenerated migration deleted.
|
|
86
|
+
*
|
|
87
|
+
* Declared natively and NAMED. TypeORM's schema builder matches database
|
|
88
|
+
* indexes to entity metadata by name and drops every one it cannot find, which
|
|
89
|
+
* is how the original was lost: it existed only in a migration, so entity
|
|
90
|
+
* metadata never mentioned it and `migration:generate` emitted a DROP. Naming
|
|
91
|
+
* it here keeps the original index name and lets the builder match, compare
|
|
92
|
+
* uniqueness and columns, and keep it.
|
|
93
|
+
*/
|
|
94
|
+
@Index("IDX_proxmox_cluster_slug", ["slug"], {
|
|
95
|
+
unique: true,
|
|
96
|
+
where: '"deletedAt" IS NULL',
|
|
97
|
+
})
|
|
84
98
|
@TableMetadata({
|
|
85
99
|
tableName: "ProxmoxCluster",
|
|
86
100
|
singularName: "Proxmox Cluster",
|
|
@@ -26,6 +26,7 @@ import Permission from "../../Types/Permission";
|
|
|
26
26
|
import TechStack from "../../Types/Service/TechStack";
|
|
27
27
|
import MetricDownsamplingRetentionDays from "../../Types/Metrics/MetricDownsamplingRetentionDays";
|
|
28
28
|
import TelemetryRetentionConfig from "../../Types/Telemetry/TelemetryRetentionConfig";
|
|
29
|
+
import UNSYNCHRONIZED_INDEX from "../../Types/Database/UnsynchronizedIndex";
|
|
29
30
|
import {
|
|
30
31
|
Column,
|
|
31
32
|
Entity,
|
|
@@ -101,6 +102,35 @@ import {
|
|
|
101
102
|
*/
|
|
102
103
|
@Index(["projectId", "name"], { unique: true })
|
|
103
104
|
@Index(["projectId", "isArchived"])
|
|
105
|
+
/*
|
|
106
|
+
* Serves the hottest read on the telemetry ingest path: OTLP resource
|
|
107
|
+
* resolution looks a service up by `service.name` case-insensitively, so
|
|
108
|
+
* `QueryHelper.findWithSameText` emits `LOWER("Service"."name") = $2`. The
|
|
109
|
+
* unique index above is over the RAW name column and cannot satisfy that
|
|
110
|
+
* predicate — Postgres can only use its `projectId` prefix as an index qual and
|
|
111
|
+
* must then heap-fetch and filter every service in the project. The real
|
|
112
|
+
* definition is an EXPRESSION index and lives in migration
|
|
113
|
+
* 1786100000000-RestoreServiceLowerNameIndex; `@Index()` cannot express
|
|
114
|
+
* `LOWER(...)`.
|
|
115
|
+
*
|
|
116
|
+
* `synchronize: false` is the load-bearing part of this declaration, and it is
|
|
117
|
+
* why the index is declared here at all rather than only in the migration.
|
|
118
|
+
* TypeORM's schema builder matches database indexes to entity metadata BY NAME
|
|
119
|
+
* and drops every one it cannot find (RdbmsSchemaBuilder.dropOldIndices). An
|
|
120
|
+
* index created only in a migration is therefore invisible to it and gets
|
|
121
|
+
* emitted as a DROP by the next autogenerated migration. That is not
|
|
122
|
+
* hypothetical: this exact index existed once, as
|
|
123
|
+
* IDX_SERVICE_PROJECT_NAME_UNIQUE in migration 1774559064921, and migration
|
|
124
|
+
* 1775735059360 dropped it as collateral damage — its down() even "restores"
|
|
125
|
+
* it without the LOWER(), so the round-trip silently loses the expression.
|
|
126
|
+
* Naming it here with synchronize:false makes the builder skip it in both
|
|
127
|
+
* directions: it will never drop it, and it will never try to create it.
|
|
128
|
+
*/
|
|
129
|
+
@Index(
|
|
130
|
+
"IDX_SERVICE_PROJECT_LOWER_NAME",
|
|
131
|
+
["projectId", "name"],
|
|
132
|
+
UNSYNCHRONIZED_INDEX,
|
|
133
|
+
)
|
|
104
134
|
@TableMetadata({
|
|
105
135
|
tableName: "Service",
|
|
106
136
|
singularName: "Service",
|
|
@@ -319,6 +319,7 @@ export default class StatusPagePrivateUser extends BaseModel {
|
|
|
319
319
|
@TableColumn({
|
|
320
320
|
title: "Password",
|
|
321
321
|
hashed: true,
|
|
322
|
+
hashSaltColumn: "passwordSalt",
|
|
322
323
|
type: TableColumnType.HashedString,
|
|
323
324
|
})
|
|
324
325
|
@Column({
|
|
@@ -330,6 +331,35 @@ export default class StatusPagePrivateUser extends BaseModel {
|
|
|
330
331
|
})
|
|
331
332
|
public password?: HashedString = undefined;
|
|
332
333
|
|
|
334
|
+
/*
|
|
335
|
+
* Per-user salt mixed into `password`'s hash. Written by the database
|
|
336
|
+
* write path on every password write — never by an API caller, hence the
|
|
337
|
+
* empty access control and `computed: true`.
|
|
338
|
+
*
|
|
339
|
+
* Nullable on purpose: rows that predate per-user salts have none, and are
|
|
340
|
+
* verified against the legacy unsalted scheme until the next successful
|
|
341
|
+
* login re-hashes them with a fresh salt.
|
|
342
|
+
*/
|
|
343
|
+
@ColumnAccessControl({
|
|
344
|
+
create: [],
|
|
345
|
+
read: [],
|
|
346
|
+
update: [],
|
|
347
|
+
})
|
|
348
|
+
@TableColumn({
|
|
349
|
+
title: "Password Salt",
|
|
350
|
+
description: "Per-user random salt mixed into this user's password hash.",
|
|
351
|
+
computed: true,
|
|
352
|
+
type: TableColumnType.ShortText,
|
|
353
|
+
hideColumnInDocumentation: true,
|
|
354
|
+
})
|
|
355
|
+
@Column({
|
|
356
|
+
type: ColumnType.ShortText,
|
|
357
|
+
length: ColumnLength.ShortText,
|
|
358
|
+
unique: false,
|
|
359
|
+
nullable: true,
|
|
360
|
+
})
|
|
361
|
+
public passwordSalt?: string = undefined;
|
|
362
|
+
|
|
333
363
|
@ColumnAccessControl({
|
|
334
364
|
create: [],
|
|
335
365
|
read: [],
|
|
@@ -140,6 +140,7 @@ class User extends UserModel {
|
|
|
140
140
|
@TableColumn({
|
|
141
141
|
title: "Password",
|
|
142
142
|
hashed: true,
|
|
143
|
+
hashSaltColumn: "passwordSalt",
|
|
143
144
|
type: TableColumnType.HashedString,
|
|
144
145
|
})
|
|
145
146
|
@Column({
|
|
@@ -151,6 +152,35 @@ class User extends UserModel {
|
|
|
151
152
|
})
|
|
152
153
|
public password?: HashedString = undefined;
|
|
153
154
|
|
|
155
|
+
/*
|
|
156
|
+
* Per-user salt mixed into `password`'s hash. Written by the database
|
|
157
|
+
* write path on every password write — never by an API caller, hence the
|
|
158
|
+
* empty access control and `computed: true`.
|
|
159
|
+
*
|
|
160
|
+
* Nullable on purpose: rows that predate per-user salts have none, and are
|
|
161
|
+
* verified against the legacy unsalted scheme until the next successful
|
|
162
|
+
* login re-hashes them with a fresh salt.
|
|
163
|
+
*/
|
|
164
|
+
@ColumnAccessControl({
|
|
165
|
+
create: [],
|
|
166
|
+
read: [],
|
|
167
|
+
update: [],
|
|
168
|
+
})
|
|
169
|
+
@TableColumn({
|
|
170
|
+
title: "Password Salt",
|
|
171
|
+
description: "Per-user random salt mixed into this user's password hash.",
|
|
172
|
+
computed: true,
|
|
173
|
+
type: TableColumnType.ShortText,
|
|
174
|
+
hideColumnInDocumentation: true,
|
|
175
|
+
})
|
|
176
|
+
@Column({
|
|
177
|
+
type: ColumnType.ShortText,
|
|
178
|
+
length: ColumnLength.ShortText,
|
|
179
|
+
unique: false,
|
|
180
|
+
nullable: true,
|
|
181
|
+
})
|
|
182
|
+
public passwordSalt?: string = undefined;
|
|
183
|
+
|
|
154
184
|
@ColumnAccessControl({
|
|
155
185
|
create: [],
|
|
156
186
|
read: [Permission.CurrentUser],
|
|
@@ -69,6 +69,7 @@ import ObjectID from "../../Types/ObjectID";
|
|
|
69
69
|
import GitHubUtil, {
|
|
70
70
|
GitHubInstallationToken,
|
|
71
71
|
} from "../Utils/CodeRepository/GitHub/GitHub";
|
|
72
|
+
import GitHubInstallationBinding from "../Utils/CodeRepository/GitHub/GitHubInstallationBinding";
|
|
72
73
|
import CodeRepositoryType from "../../Types/CodeRepository/CodeRepositoryType";
|
|
73
74
|
import URL from "../../Types/API/URL";
|
|
74
75
|
import PullRequestState from "../../Types/CodeRepository/PullRequestState";
|
|
@@ -1166,6 +1167,36 @@ export default class AIAgentDataAPI {
|
|
|
1166
1167
|
);
|
|
1167
1168
|
}
|
|
1168
1169
|
|
|
1170
|
+
/*
|
|
1171
|
+
* The project check above proves the ROW is in the agent's project.
|
|
1172
|
+
* It does not prove the INSTALLATION is — and those are different
|
|
1173
|
+
* questions, because the row's installation ID is just a number
|
|
1174
|
+
* stored on a record. Someone who could get an arbitrary
|
|
1175
|
+
* installation ID onto a row in a project they own would otherwise
|
|
1176
|
+
* pass every check to this point and be handed a live `ghs_` token
|
|
1177
|
+
* for another tenant's repositories. So re-derive the binding from
|
|
1178
|
+
* the project itself, right before minting.
|
|
1179
|
+
*/
|
|
1180
|
+
if (
|
|
1181
|
+
!codeRepository.projectId ||
|
|
1182
|
+
!(await GitHubInstallationBinding.isInstallationBoundToProject({
|
|
1183
|
+
projectId: codeRepository.projectId,
|
|
1184
|
+
installationId: codeRepository.gitHubAppInstallationId,
|
|
1185
|
+
}))
|
|
1186
|
+
) {
|
|
1187
|
+
logger.error(
|
|
1188
|
+
`Refusing to mint a GitHub token for code repository ${codeRepositoryId.toString()}: installation ${codeRepository.gitHubAppInstallationId} is not bound to project ${codeRepository.projectId?.toString()}.`,
|
|
1189
|
+
);
|
|
1190
|
+
|
|
1191
|
+
return Response.sendErrorResponse(
|
|
1192
|
+
req,
|
|
1193
|
+
res,
|
|
1194
|
+
new BadDataException(
|
|
1195
|
+
"This repository's GitHub App installation is not connected to its project. Please reconnect the GitHub App from Project Settings.",
|
|
1196
|
+
),
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1169
1200
|
/*
|
|
1170
1201
|
* G11 guardrail: per-repo open-PR cap, enforced HERE because the
|
|
1171
1202
|
* token is the agent's only way to clone and push — a repo at its
|