@oneuptime/common 12.0.2 → 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/DatabaseBaseModel/DatabaseBaseModel.ts +14 -0
- package/Models/DatabaseModels/DockerHost.ts +14 -0
- package/Models/DatabaseModels/GlobalOidcProject.ts +18 -0
- package/Models/DatabaseModels/GlobalSsoProject.ts +18 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/IoTFleet.ts +14 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +14 -0
- package/Models/DatabaseModels/LogDropFilter.ts +9 -20
- 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/StatusPageMonitorRule.ts +911 -0
- package/Models/DatabaseModels/StatusPagePrivateUser.ts +30 -0
- package/Models/DatabaseModels/StatusPageResource.ts +73 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +9 -20
- package/Models/DatabaseModels/User.ts +30 -0
- package/Server/API/AIAgentDataAPI.ts +31 -0
- package/Server/API/BaseAPI.ts +13 -2
- package/Server/API/DashboardAPI.ts +458 -12
- package/Server/API/GitHubAPI.ts +119 -283
- package/Server/API/UserAPI.ts +263 -1
- package/Server/EnvironmentConfig.ts +38 -4
- package/Server/Infrastructure/GlobalCache.ts +152 -0
- package/Server/Infrastructure/Postgres/DataSourceOptions.ts +16 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.ts +123 -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 +12 -0
- package/Server/Services/AlertStateTimelineService.ts +20 -0
- package/Server/Services/AnalyticsDatabaseService.ts +72 -1
- package/Server/Services/CephClusterService.ts +26 -86
- package/Server/Services/CloudResourceService.ts +23 -83
- package/Server/Services/CodeRepositoryService.ts +105 -2
- package/Server/Services/DatabaseService.ts +296 -2
- package/Server/Services/DockerHostService.ts +19 -79
- package/Server/Services/DockerSwarmClusterService.ts +27 -88
- package/Server/Services/HostService.ts +46 -99
- package/Server/Services/IncidentStateTimelineService.ts +20 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/IoTFleetService.ts +19 -79
- package/Server/Services/KubernetesClusterService.ts +17 -78
- package/Server/Services/LabelService.ts +57 -10
- package/Server/Services/MetricTypeService.ts +84 -0
- package/Server/Services/MonitorService.ts +89 -14
- package/Server/Services/MonitorStatusTimelineService.ts +29 -0
- package/Server/Services/MonitorTemplateService.ts +65 -0
- package/Server/Services/OpenTelemetryIngestService.ts +230 -26
- package/Server/Services/PodmanHostService.ts +19 -79
- package/Server/Services/ProxmoxClusterService.ts +23 -83
- package/Server/Services/RumApplicationService.ts +19 -80
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +21 -0
- package/Server/Services/ServerlessFunctionService.ts +24 -85
- package/Server/Services/ServiceService.ts +299 -73
- package/Server/Services/StatusPageMonitorRuleEngineService.ts +752 -0
- package/Server/Services/StatusPageMonitorRuleService.ts +471 -0
- package/Server/Services/TelemetryEntityRelationshipService.ts +1 -0
- package/Server/Services/TelemetryEntityService.ts +1 -0
- 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/Database/ProjectScopedReferenceValidator.ts +170 -39
- package/Server/Utils/Monitor/MonitorResource.ts +63 -36
- package/Server/Utils/Monitor/MonitorStatusTimeline.ts +36 -0
- package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +148 -127
- package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +434 -0
- package/Server/Utils/PasswordHash.ts +306 -0
- package/Server/Utils/SingleFlight.ts +83 -0
- package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +46 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +107 -26
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +314 -0
- package/Server/Utils/Telemetry/Telemetry.ts +351 -122
- package/Tests/App/Dashboard/OverviewCustomFields.test.tsx +333 -0
- package/Tests/App/Dashboard/UserCustomFields.test.tsx +392 -0
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +2 -0
- package/Tests/Models/DatabaseModels/ColumnTransformerInvariants.test.ts +306 -0
- package/Tests/Models/DatabaseModels/NumberColumnDeserialization.test.ts +333 -0
- package/Tests/Models/DatabaseModels/StatusPageMonitorRuleModel.test.ts +190 -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/DashboardPublicResourceListAPI.test.ts +26 -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 +354 -0
- package/Tests/Server/Infrastructure/PostgresLockTimeoutOptions.test.ts +181 -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/DatabaseServiceUpdateColumnsIfUnlocked.test.ts +342 -0
- package/Tests/Server/Services/DropFilterSaveValidation.test.ts +245 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +163 -15
- package/Tests/Server/Services/MetricTypeAttachServices.test.ts +222 -0
- package/Tests/Server/Services/MissingReferenceWriteGuard.test.ts +614 -0
- package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +23 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +568 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +305 -56
- package/Tests/Server/Services/RestoreDroppedUniqueIndexesMigration.test.ts +386 -0
- package/Tests/Server/Services/RestoreServiceLowerNameIndexMigration.test.ts +240 -0
- package/Tests/Server/Services/ServiceUpdateLastSeenSplitGates.test.ts +705 -0
- package/Tests/Server/Services/StatusPageMonitorRuleEngineService.test.ts +1719 -0
- package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +1212 -0
- package/Tests/Server/Services/StatusPageMonitorRuleMonitorCreateHook.test.ts +228 -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/APIKey/AccessPermission.test.ts +267 -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/Database/ProjectScopedReferenceValidator.test.ts +260 -13
- package/Tests/Server/Utils/EntityRegistryRowFence.test.ts +302 -0
- package/Tests/Server/Utils/GitHubInstallationOwnershipVerification.test.ts +340 -0
- package/Tests/Server/Utils/GitHubWebhookAndTreeCacheIsolation.test.ts +250 -0
- package/Tests/Server/Utils/MetricTypeIndexConvergence.test.ts +598 -0
- package/Tests/Server/Utils/Monitor/MonitorStatusTimelineUnusableStatus.test.ts +154 -0
- package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +638 -92
- package/Tests/Server/Utils/Monitor/MonitorStepsReferenceExtractor.test.ts +724 -0
- package/Tests/Server/Utils/PasswordHash.test.ts +555 -0
- package/Tests/Server/Utils/SingleFlight.test.ts +221 -0
- package/Tests/Types/Dashboard/NetworkDashboardTemplate.test.ts +793 -0
- package/Tests/Types/Database/BigIntColumnTransformer.test.ts +147 -0
- package/Tests/Types/Database/ColumnLength.test.ts +7 -1
- package/Tests/Types/Database/DatabasePropertyTransformer.test.ts +140 -0
- package/Tests/Types/Database/NumericColumnValue.test.ts +127 -0
- package/Tests/Types/Decimal.test.ts +37 -0
- 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/Types/Monitor/SnmpVersionParsing.test.ts +152 -0
- package/Tests/Types/Port.test.ts +38 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +696 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +276 -0
- package/Tests/UI/Components/UseLiveLogsRefresh.test.tsx +489 -0
- package/Tests/UI/Components/UseLogsHistogram.test.tsx +500 -0
- package/Tests/UI/Utils/UserProjectsModelAPI.test.ts +714 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentsUtil.test.ts +120 -0
- package/Tests/Utils/Dashboard/DashboardNetworkMapComponent.test.ts +327 -0
- package/Tests/Utils/TeamMembersByProject.test.ts +655 -0
- package/Types/Dashboard/DashboardComponentType.ts +1 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.ts +40 -0
- package/Types/Dashboard/DashboardTemplates.ts +397 -0
- package/Types/Database/BigIntColumnTransformer.ts +74 -0
- package/Types/Database/ColumnLength.ts +9 -1
- package/Types/Database/DatabaseProperty.ts +26 -0
- package/Types/Database/NumericColumnValue.ts +128 -0
- package/Types/Database/TableColumn.ts +11 -0
- package/Types/Database/UnsynchronizedIndex.ts +48 -0
- package/Types/Decimal.ts +16 -4
- package/Types/HashedString.ts +139 -4
- package/Types/Permission.ts +48 -0
- package/Types/Port.ts +16 -4
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +85 -11
- package/UI/Components/LogsViewer/useLiveLogsRefresh.ts +86 -0
- package/UI/Components/LogsViewer/useLogsHistogram.ts +113 -0
- 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/Dashboard/Components/DashboardNetworkMapComponent.ts +136 -0
- package/Utils/Dashboard/Components/Index.ts +7 -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/DatabaseBaseModel/DatabaseBaseModel.js +12 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.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/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.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/LogDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/LogDropFilter.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/StatusPageMonitorRule.js +925 -0
- package/build/dist/Models/DatabaseModels/StatusPageMonitorRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js +32 -0
- package/build/dist/Models/DatabaseModels/StatusPagePrivateUser.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageResource.js +72 -0
- package/build/dist/Models/DatabaseModels/StatusPageResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js +9 -20
- package/build/dist/Models/DatabaseModels/TraceDropFilter.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/BaseAPI.js +11 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +343 -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 +34 -4
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +120 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js +5 -3
- package/build/dist/Server/Infrastructure/Postgres/DataSourceOptions.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js +48 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786005052769-AddStatusPageMonitorRule.js.map +1 -0
- 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 +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertStateTimelineService.js +19 -0
- package/build/dist/Server/Services/AlertStateTimelineService.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/CephClusterService.js +26 -76
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +23 -73
- package/build/dist/Server/Services/CloudResourceService.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 +246 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +19 -69
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +27 -78
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +58 -102
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +19 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetService.js +19 -69
- package/build/dist/Server/Services/IoTFleetService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +17 -68
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +42 -7
- package/build/dist/Server/Services/LabelService.js.map +1 -1
- package/build/dist/Server/Services/MetricTypeService.js +74 -0
- package/build/dist/Server/Services/MetricTypeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +81 -9
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusTimelineService.js +28 -0
- package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +60 -0
- package/build/dist/Server/Services/MonitorTemplateService.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/PodmanHostService.js +19 -69
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +23 -73
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +19 -70
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +20 -0
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +24 -75
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/Server/Services/ServiceService.js +236 -61
- package/build/dist/Server/Services/ServiceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js +603 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js +401 -0
- package/build/dist/Server/Services/StatusPageMonitorRuleService.js.map +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityRelationshipService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryEntityService.js +1 -0
- package/build/dist/Server/Services/TelemetryEntityService.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/Database/ProjectScopedReferenceValidator.js +105 -29
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +56 -32
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +30 -3
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +110 -98
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +312 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.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/Server/Utils/SingleFlight.js +76 -0
- package/build/dist/Server/Utils/SingleFlight.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +29 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +78 -19
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +160 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +279 -104
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js +368 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Database/BigIntColumnTransformer.js +66 -0
- package/build/dist/Types/Database/BigIntColumnTransformer.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/DatabaseProperty.js +25 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/NumericColumnValue.js +97 -0
- package/build/dist/Types/Database/NumericColumnValue.js.map +1 -0
- 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/Decimal.js +14 -3
- package/build/dist/Types/Decimal.js.map +1 -1
- package/build/dist/Types/HashedString.js +95 -5
- package/build/dist/Types/HashedString.js.map +1 -1
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Port.js +15 -5
- package/build/dist/Types/Port.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/LogsViewer/useLiveLogsRefresh.js +44 -0
- package/build/dist/UI/Components/LogsViewer/useLiveLogsRefresh.js.map +1 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js +75 -0
- package/build/dist/UI/Components/LogsViewer/useLogsHistogram.js.map +1 -0
- 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/Dashboard/Components/DashboardNetworkMapComponent.js +114 -0
- package/build/dist/Utils/Dashboard/Components/DashboardNetworkMapComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/TeamMembersByProject.js +145 -0
- package/build/dist/Utils/TeamMembersByProject.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import React, { useEffect, useRef } from "react";
|
|
2
|
+
|
|
3
|
+
export interface LiveLogsRefreshOptions {
|
|
4
|
+
/** Whether the user has switched live mode on. */
|
|
5
|
+
isLive: boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Whether the current view can be refreshed in place. Live tailing only
|
|
8
|
+
* makes sense on the first page sorted newest-first — on page 3, or sorted
|
|
9
|
+
* by severity, "the newest logs" is not what the viewer is showing.
|
|
10
|
+
*/
|
|
11
|
+
isEligible: boolean;
|
|
12
|
+
/** How often to poll while live mode is on. */
|
|
13
|
+
intervalInMs: number;
|
|
14
|
+
/** Pull the newest page of logs. */
|
|
15
|
+
refreshLogs: () => void;
|
|
16
|
+
/**
|
|
17
|
+
* Re-run the histogram query. The chart is driven by its own aggregation
|
|
18
|
+
* request rather than by the rows in the table, so it goes stale unless it
|
|
19
|
+
* is refreshed on the same beat as the list.
|
|
20
|
+
*/
|
|
21
|
+
refreshHistogram: () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type UseLiveLogsRefreshFunction = (
|
|
25
|
+
options: LiveLogsRefreshOptions,
|
|
26
|
+
) => void;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Drives the polling loop behind the log viewer's live mode.
|
|
30
|
+
*
|
|
31
|
+
* Every tick refreshes the log list *and* the histogram together. That
|
|
32
|
+
* pairing is the point of this hook: the two come from different endpoints,
|
|
33
|
+
* and refreshing only the list leaves a chart that stops moving while rows
|
|
34
|
+
* keep arriving underneath it.
|
|
35
|
+
*
|
|
36
|
+
* The callbacks are read through refs, so a parent that re-renders (which it
|
|
37
|
+
* does on every poll, as results land) does not tear down and re-arm the
|
|
38
|
+
* timer — that would reset the cadence and fire an extra immediate poll each
|
|
39
|
+
* time. Only the gating flags and the interval restart the loop.
|
|
40
|
+
*/
|
|
41
|
+
const useLiveLogsRefresh: UseLiveLogsRefreshFunction = (
|
|
42
|
+
options: LiveLogsRefreshOptions,
|
|
43
|
+
): void => {
|
|
44
|
+
const { isLive, isEligible, intervalInMs } = options;
|
|
45
|
+
|
|
46
|
+
const refreshLogsRef: React.MutableRefObject<() => void> = useRef<() => void>(
|
|
47
|
+
options.refreshLogs,
|
|
48
|
+
);
|
|
49
|
+
const refreshHistogramRef: React.MutableRefObject<() => void> = useRef<
|
|
50
|
+
() => void
|
|
51
|
+
>(options.refreshHistogram);
|
|
52
|
+
|
|
53
|
+
/*
|
|
54
|
+
* Declared before the polling effect so the refs are already current by the
|
|
55
|
+
* time that effect (re-)runs — effects fire in declaration order.
|
|
56
|
+
*/
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
refreshLogsRef.current = options.refreshLogs;
|
|
59
|
+
refreshHistogramRef.current = options.refreshHistogram;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (!isLive || !isEligible) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const poll: () => void = (): void => {
|
|
68
|
+
refreshLogsRef.current();
|
|
69
|
+
refreshHistogramRef.current();
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Switching live mode on should show fresh data immediately, not one tick later.
|
|
73
|
+
poll();
|
|
74
|
+
|
|
75
|
+
const intervalId: ReturnType<typeof setInterval> = setInterval(
|
|
76
|
+
poll,
|
|
77
|
+
intervalInMs,
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
return () => {
|
|
81
|
+
clearInterval(intervalId);
|
|
82
|
+
};
|
|
83
|
+
}, [isLive, isEligible, intervalInMs]);
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export default useLiveLogsRefresh;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { HistogramBucket } from "./types";
|
|
3
|
+
|
|
4
|
+
export interface LogsHistogramRefreshOptions {
|
|
5
|
+
/**
|
|
6
|
+
* A background refresh: no loader, no blanking on failure, and no stacking
|
|
7
|
+
* if the previous one has not come back yet. This is what live mode uses.
|
|
8
|
+
*/
|
|
9
|
+
silent?: boolean | undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface LogsHistogramState {
|
|
13
|
+
buckets: Array<HistogramBucket>;
|
|
14
|
+
isLoading: boolean;
|
|
15
|
+
refresh: (options?: LogsHistogramRefreshOptions) => Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type FetchHistogramBucketsFunction = () => Promise<
|
|
19
|
+
Array<HistogramBucket>
|
|
20
|
+
>;
|
|
21
|
+
|
|
22
|
+
export type UseLogsHistogramFunction = (
|
|
23
|
+
fetchBuckets: FetchHistogramBucketsFunction,
|
|
24
|
+
) => LogsHistogramState;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Owns the log histogram's data, loading flag and refresh loop.
|
|
28
|
+
*
|
|
29
|
+
* The chart is fed by its own aggregation query rather than by the rows in
|
|
30
|
+
* the table, so it needs refreshing in its own right — including on every
|
|
31
|
+
* live-mode tick. A poll is a *silent* refresh, which is what keeps live mode
|
|
32
|
+
* from looking broken:
|
|
33
|
+
*
|
|
34
|
+
* - it does not raise the loading flag, so the chart does not flash a loader
|
|
35
|
+
* every few seconds;
|
|
36
|
+
* - it keeps the buckets already on screen if the request fails, so one bad
|
|
37
|
+
* response does not blank the chart;
|
|
38
|
+
* - it is dropped while an earlier poll is still running, so a slow
|
|
39
|
+
* aggregation cannot pile requests up behind it.
|
|
40
|
+
*
|
|
41
|
+
* `fetchBuckets` doubles as the query identity: hand over a new callback (a
|
|
42
|
+
* new time range, filter or scope) and the histogram reloads.
|
|
43
|
+
*/
|
|
44
|
+
const useLogsHistogram: UseLogsHistogramFunction = (
|
|
45
|
+
fetchBuckets: FetchHistogramBucketsFunction,
|
|
46
|
+
): LogsHistogramState => {
|
|
47
|
+
const [buckets, setBuckets] = useState<Array<HistogramBucket>>([]);
|
|
48
|
+
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
49
|
+
|
|
50
|
+
const silentRequestInFlight: React.MutableRefObject<boolean> =
|
|
51
|
+
useRef<boolean>(false);
|
|
52
|
+
|
|
53
|
+
/*
|
|
54
|
+
* The query the viewer is on right now. A request that comes back after the
|
|
55
|
+
* reader has moved to another time range or filter is dropped rather than
|
|
56
|
+
* painted over the window they are actually looking at.
|
|
57
|
+
*/
|
|
58
|
+
const currentQuery: React.MutableRefObject<FetchHistogramBucketsFunction> =
|
|
59
|
+
useRef<FetchHistogramBucketsFunction>(fetchBuckets);
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
currentQuery.current = fetchBuckets;
|
|
63
|
+
}, [fetchBuckets]);
|
|
64
|
+
|
|
65
|
+
const refresh: (options?: LogsHistogramRefreshOptions) => Promise<void> =
|
|
66
|
+
useCallback(
|
|
67
|
+
async (options: LogsHistogramRefreshOptions = {}): Promise<void> => {
|
|
68
|
+
const isSilent: boolean = options.silent === true;
|
|
69
|
+
|
|
70
|
+
if (isSilent) {
|
|
71
|
+
if (silentRequestInFlight.current) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
silentRequestInFlight.current = true;
|
|
76
|
+
} else {
|
|
77
|
+
setIsLoading(true);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const nextBuckets: Array<HistogramBucket> = await fetchBuckets();
|
|
82
|
+
|
|
83
|
+
if (currentQuery.current === fetchBuckets) {
|
|
84
|
+
setBuckets(nextBuckets);
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
// The histogram is non-critical; degrade rather than fail the page.
|
|
88
|
+
if (!isSilent && currentQuery.current === fetchBuckets) {
|
|
89
|
+
setBuckets([]);
|
|
90
|
+
}
|
|
91
|
+
} finally {
|
|
92
|
+
if (isSilent) {
|
|
93
|
+
silentRequestInFlight.current = false;
|
|
94
|
+
} else if (currentQuery.current === fetchBuckets) {
|
|
95
|
+
setIsLoading(false);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
[fetchBuckets],
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
void refresh();
|
|
104
|
+
}, [refresh]);
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
buckets: buckets,
|
|
108
|
+
isLoading: isLoading,
|
|
109
|
+
refresh: refresh,
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
export default useLogsHistogram;
|
|
@@ -13,6 +13,7 @@ import NavBarMenuModal from "./NavBarMenuModal";
|
|
|
13
13
|
import Button, { ButtonStyleType } from "../Button/Button";
|
|
14
14
|
import Navigation from "../../Utils/Navigation";
|
|
15
15
|
import useComponentOutsideClick from "../../Types/UseComponentOutsideClick";
|
|
16
|
+
import useTranslateValue from "../../Utils/Translation";
|
|
16
17
|
import Icon, { ThickProp } from "../Icon/Icon";
|
|
17
18
|
|
|
18
19
|
export interface NavItem {
|
|
@@ -54,7 +55,7 @@ export interface ComponentProps {
|
|
|
54
55
|
items?: NavItem[];
|
|
55
56
|
rightElement?: NavItem;
|
|
56
57
|
moreMenuItems?: MoreMenuItem[];
|
|
57
|
-
moreMenuTitle?: string; // Title for the more menu (default: "
|
|
58
|
+
moreMenuTitle?: string; // Title for the more menu (default: "Products")
|
|
58
59
|
moreMenuSearchPlaceholder?: string; // Placeholder for the menu search box
|
|
59
60
|
moreMenuNoResultsText?: string; // Empty-state text when search matches nothing
|
|
60
61
|
moreMenuKeyboardHint?: string; // Keyboard hint shown in the menu footer
|
|
@@ -79,6 +80,7 @@ const MOBILE_MENU_MIN_HEIGHT_IN_PX: number = 160;
|
|
|
79
80
|
const Navbar: FunctionComponent<ComponentProps> = (
|
|
80
81
|
props: ComponentProps,
|
|
81
82
|
): ReactElement => {
|
|
83
|
+
const { translateString } = useTranslateValue();
|
|
82
84
|
const [isMobile, setIsMobile] = useState<boolean>(false);
|
|
83
85
|
const [isMobileMenuVisible, setIsMobileMenuVisible] =
|
|
84
86
|
useState<boolean>(false);
|
|
@@ -408,7 +410,15 @@ const Navbar: FunctionComponent<ComponentProps> = (
|
|
|
408
410
|
className="mr-1.5 h-4 w-4 transition-transform duration-150 group-hover:scale-110 group-hover:text-indigo-600"
|
|
409
411
|
thick={ThickProp.Thick}
|
|
410
412
|
/>
|
|
411
|
-
|
|
413
|
+
{/*
|
|
414
|
+
* Callers pass a plain English literal here (or nothing, and
|
|
415
|
+
* take the default) — run it through the same flat-key lookup
|
|
416
|
+
* the side menu uses so the button is localised like every
|
|
417
|
+
* other label in the nav.
|
|
418
|
+
*/}
|
|
419
|
+
<span>
|
|
420
|
+
{translateString(props.moreMenuTitle || "Products")}
|
|
421
|
+
</span>
|
|
412
422
|
<Icon
|
|
413
423
|
icon={IconProp.ChevronDown}
|
|
414
424
|
className={`ml-1.5 h-3 w-3 text-gray-400 transition-transform duration-200 ${
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Icon from "../Icon/Icon";
|
|
2
2
|
import Link from "../Link/Link";
|
|
3
3
|
import Navigation from "../../Utils/Navigation";
|
|
4
|
+
import useTranslateValue from "../../Utils/Translation";
|
|
4
5
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
5
6
|
import URL from "../../../Types/API/URL";
|
|
6
7
|
import { isMoreMenuItemActive, type MoreMenuItem } from "./NavBar";
|
|
@@ -163,7 +164,18 @@ const NavBarMenuModal: FunctionComponent<ComponentProps> = (
|
|
|
163
164
|
Array<HTMLDivElement | null>
|
|
164
165
|
>([]);
|
|
165
166
|
|
|
166
|
-
|
|
167
|
+
/*
|
|
168
|
+
* Callers hand us plain English literals (or omit them and take the
|
|
169
|
+
* defaults). Run every user-facing string through the same flat-key lookup
|
|
170
|
+
* the rest of the nav uses, so a caller that has not been wired up to i18n
|
|
171
|
+
* still renders localised text instead of English.
|
|
172
|
+
*/
|
|
173
|
+
const { translateString } = useTranslateValue();
|
|
174
|
+
const tx: (value: string) => string = (value: string): string => {
|
|
175
|
+
return translateString(value) ?? value;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const recentLabel: string = tx(props.recentLabel || "Recent");
|
|
167
179
|
|
|
168
180
|
// Show the OS-appropriate shortcut hint (⌘K on macOS, Ctrl K elsewhere).
|
|
169
181
|
const shortcutLabel: string = useMemo(() => {
|
|
@@ -482,7 +494,7 @@ const NavBarMenuModal: FunctionComponent<ComponentProps> = (
|
|
|
482
494
|
? `navbar-menu-option-${activeIndex}`
|
|
483
495
|
: undefined
|
|
484
496
|
}
|
|
485
|
-
aria-label={props.searchPlaceholder || "Search products"}
|
|
497
|
+
aria-label={tx(props.searchPlaceholder || "Search products")}
|
|
486
498
|
autoComplete="off"
|
|
487
499
|
autoCorrect="off"
|
|
488
500
|
autoCapitalize="off"
|
|
@@ -492,7 +504,7 @@ const NavBarMenuModal: FunctionComponent<ComponentProps> = (
|
|
|
492
504
|
setQuery(event.target.value);
|
|
493
505
|
}}
|
|
494
506
|
onKeyDown={handleKeyDown}
|
|
495
|
-
placeholder={props.searchPlaceholder || "Search…"}
|
|
507
|
+
placeholder={tx(props.searchPlaceholder || "Search…")}
|
|
496
508
|
className="flex-1 border-0 bg-transparent p-0 text-base text-gray-900 placeholder-gray-400 focus:outline-none focus:ring-0"
|
|
497
509
|
/>
|
|
498
510
|
{query ? (
|
|
@@ -530,7 +542,7 @@ const NavBarMenuModal: FunctionComponent<ComponentProps> = (
|
|
|
530
542
|
/>
|
|
531
543
|
</div>
|
|
532
544
|
<p className="text-sm font-medium text-gray-900">
|
|
533
|
-
{props.noResultsText || "No results found."}
|
|
545
|
+
{tx(props.noResultsText || "No results found.")}
|
|
534
546
|
</p>
|
|
535
547
|
{query && (
|
|
536
548
|
<p className="mt-1 text-xs text-gray-500">
|
|
@@ -640,20 +652,20 @@ const NavBarMenuModal: FunctionComponent<ComponentProps> = (
|
|
|
640
652
|
</div>
|
|
641
653
|
<div className="min-w-0 text-left">
|
|
642
654
|
<p className="flex items-center gap-1 text-sm font-medium text-gray-900">
|
|
643
|
-
<span className="truncate">{props.footer.title}</span>
|
|
655
|
+
<span className="truncate">{tx(props.footer.title)}</span>
|
|
644
656
|
<Icon
|
|
645
657
|
icon={IconProp.ExternalLink}
|
|
646
658
|
className="h-3 w-3 flex-shrink-0 text-gray-400 opacity-0 transition-opacity duration-150 group-hover:opacity-100 group-focus-visible:opacity-100"
|
|
647
659
|
/>
|
|
648
660
|
</p>
|
|
649
661
|
<p className="truncate text-xs text-gray-500">
|
|
650
|
-
{props.footer.description}
|
|
662
|
+
{tx(props.footer.description)}
|
|
651
663
|
</p>
|
|
652
664
|
</div>
|
|
653
665
|
</Link>
|
|
654
666
|
{props.keyboardHint && (
|
|
655
667
|
<div
|
|
656
|
-
aria-label={props.keyboardHint}
|
|
668
|
+
aria-label={tx(props.keyboardHint)}
|
|
657
669
|
className="hidden flex-shrink-0 items-center gap-2 text-xs text-gray-400 md:flex"
|
|
658
670
|
>
|
|
659
671
|
<span className="flex items-center gap-1">
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import ModelAPI, { ListResult, RequestOptions } from "./ModelAPI";
|
|
2
|
+
import API from "../API/API";
|
|
3
|
+
import { APP_API_URL } from "../../Config";
|
|
4
|
+
import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
5
|
+
import Team from "../../../Models/DatabaseModels/Team";
|
|
6
|
+
import TeamMember from "../../../Models/DatabaseModels/TeamMember";
|
|
7
|
+
import User from "../../../Models/DatabaseModels/User";
|
|
8
|
+
import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
|
|
9
|
+
import HTTPMethod from "../../../Types/API/HTTPMethod";
|
|
10
|
+
import HTTPResponse from "../../../Types/API/HTTPResponse";
|
|
11
|
+
import URL from "../../../Types/API/URL";
|
|
12
|
+
import Query from "../../../Types/BaseDatabase/Query";
|
|
13
|
+
import Select from "../../../Types/BaseDatabase/Select";
|
|
14
|
+
import Sort from "../../../Types/BaseDatabase/Sort";
|
|
15
|
+
import GroupBy from "../../../Types/BaseDatabase/GroupBy";
|
|
16
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
17
|
+
import { JSONArray, JSONObject } from "../../../Types/JSON";
|
|
18
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* A drop-in ModelAPI for tables that list TeamMember but mean to list the
|
|
22
|
+
* PROJECTS one user belongs to - the Admin Dashboard's User > Projects page
|
|
23
|
+
* being the one that does.
|
|
24
|
+
*
|
|
25
|
+
* TeamMember rows are (user, team) pairs, so the plain API pages over
|
|
26
|
+
* memberships: a user on three teams of one project occupies three rows and
|
|
27
|
+
* reads as three projects. This API pages over projects instead, by calling the
|
|
28
|
+
* master-admin endpoint that does the grouping server-side
|
|
29
|
+
* (POST /user/:userId/projects). Grouping there rather than in the browser is
|
|
30
|
+
* what makes `count` a number of projects and every page boundary fall between
|
|
31
|
+
* two projects rather than between two of one project's teams.
|
|
32
|
+
*
|
|
33
|
+
* The endpoint owns the shape of a row, so the caller's `select` and `sort` are
|
|
34
|
+
* NOT forwarded - a row is always a project with its teams, always ordered by
|
|
35
|
+
* project name. Adding a column to the table therefore means adding the field
|
|
36
|
+
* to the endpoint's select and to the row it serializes, not just to the
|
|
37
|
+
* column list. Only `skip`/`limit` and the user in the query are the caller's.
|
|
38
|
+
*
|
|
39
|
+
* Deleting is redefined to match what a row now means - see deleteItem.
|
|
40
|
+
*/
|
|
41
|
+
|
|
42
|
+
export interface UserProjectExtras {
|
|
43
|
+
// Every team this user belongs to in this project, sorted by name.
|
|
44
|
+
teamsForProject: Array<Team>;
|
|
45
|
+
teamCountForProject: number;
|
|
46
|
+
/*
|
|
47
|
+
* How many of those memberships are still unaccepted invitations. A user can
|
|
48
|
+
* be a fully accepted member of one team of a project and a pending invite on
|
|
49
|
+
* another.
|
|
50
|
+
*/
|
|
51
|
+
pendingTeamCountForProject: number;
|
|
52
|
+
// The ids of every membership this row was folded from.
|
|
53
|
+
teamMemberIdsForProject: Array<string>;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
* The row is still a TeamMember - the table, its columns and its delete action
|
|
58
|
+
* are all typed against the model - it just stands for a project the user is in
|
|
59
|
+
* rather than for one of their memberships.
|
|
60
|
+
*/
|
|
61
|
+
export type UserProjectRow = TeamMember & UserProjectExtras;
|
|
62
|
+
|
|
63
|
+
export default class UserProjectsModelAPI extends ModelAPI {
|
|
64
|
+
public static override async getList<TBaseModel extends BaseModel>(data: {
|
|
65
|
+
modelType: { new (): TBaseModel };
|
|
66
|
+
query: Query<TBaseModel>;
|
|
67
|
+
groupBy?: GroupBy<TBaseModel> | undefined;
|
|
68
|
+
limit: number;
|
|
69
|
+
skip: number;
|
|
70
|
+
select: Select<TBaseModel>;
|
|
71
|
+
sort: Sort<TBaseModel>;
|
|
72
|
+
requestOptions?: RequestOptions | undefined;
|
|
73
|
+
}): Promise<ListResult<TBaseModel>> {
|
|
74
|
+
if (!(new data.modelType() instanceof TeamMember)) {
|
|
75
|
+
/*
|
|
76
|
+
* Not the model this API exists for - a table's entity filter fetching
|
|
77
|
+
* its dropdown options, say. Behave like the plain one, but through
|
|
78
|
+
* `super` so a subclass's getCommonHeaders still applies.
|
|
79
|
+
*/
|
|
80
|
+
return super.getList<TBaseModel>(data);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const userId: ObjectID | null = this.getUserIdFromQuery(
|
|
84
|
+
data.query as Query<TeamMember>,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (!userId) {
|
|
88
|
+
/*
|
|
89
|
+
* Without a user there is no list to fetch - the endpoint is
|
|
90
|
+
* "the projects of THIS user". Failing loudly beats returning an empty
|
|
91
|
+
* list, which would render as "this user is in no projects".
|
|
92
|
+
*/
|
|
93
|
+
throw new BadDataException(
|
|
94
|
+
"A user id is required to list the projects a user belongs to.",
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const url: URL = URL.fromString(APP_API_URL.toString())
|
|
99
|
+
.addRoute(new User().getCrudApiPath()!)
|
|
100
|
+
.addRoute(`/${userId.toString()}/projects`);
|
|
101
|
+
|
|
102
|
+
const result: HTTPResponse<JSONArray> | HTTPErrorResponse =
|
|
103
|
+
await API.fetch<JSONArray>({
|
|
104
|
+
method: HTTPMethod.POST,
|
|
105
|
+
url: url,
|
|
106
|
+
data: {},
|
|
107
|
+
headers: this.getCommonHeaders(data.requestOptions),
|
|
108
|
+
params: {
|
|
109
|
+
limit: data.limit.toString(),
|
|
110
|
+
skip: data.skip.toString(),
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
if (result instanceof HTTPErrorResponse) {
|
|
115
|
+
throw result;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const rows: Array<UserProjectRow> = (result.data as JSONArray).map(
|
|
119
|
+
(item: JSONObject) => {
|
|
120
|
+
return this.toRow(item);
|
|
121
|
+
},
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
data: rows as unknown as Array<TBaseModel>,
|
|
126
|
+
count: result.count,
|
|
127
|
+
skip: result.skip,
|
|
128
|
+
limit: result.limit,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Removes the user from the project on this row - every one of their team
|
|
134
|
+
* memberships in it, not just the one whose id the row happens to carry.
|
|
135
|
+
*
|
|
136
|
+
* A grouped row stands for a project, so deleting one membership would leave
|
|
137
|
+
* the row on screen (still in the project, via their other teams) and look
|
|
138
|
+
* like the delete silently failed. Removing the user from a single team is
|
|
139
|
+
* done from the memberships table on the same page, where a row is a
|
|
140
|
+
* membership again.
|
|
141
|
+
*
|
|
142
|
+
* This goes through one `remove-from-project` request rather than a DELETE
|
|
143
|
+
* per membership, and that is load-bearing rather than an optimisation. The
|
|
144
|
+
* server refuses to remove the last accepted member of the Owners team, and
|
|
145
|
+
* it refuses per request - so a loop deletes every other membership first and
|
|
146
|
+
* only then reports a failure, leaving the person stripped of the teams the
|
|
147
|
+
* caller was just told they had not lost. The endpoint runs that check across
|
|
148
|
+
* the whole set before deleting anything.
|
|
149
|
+
*/
|
|
150
|
+
public static override async deleteItem<TBaseModel extends BaseModel>(data: {
|
|
151
|
+
modelType: { new (): TBaseModel };
|
|
152
|
+
id: ObjectID;
|
|
153
|
+
requestOptions?: RequestOptions | undefined;
|
|
154
|
+
}): Promise<void> {
|
|
155
|
+
if (!(new data.modelType() instanceof TeamMember)) {
|
|
156
|
+
return super.deleteItem<TBaseModel>(data);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const rowMembership: TeamMember | null = await this.getItem<TeamMember>({
|
|
160
|
+
modelType: TeamMember,
|
|
161
|
+
id: data.id,
|
|
162
|
+
select: {
|
|
163
|
+
_id: true,
|
|
164
|
+
userId: true,
|
|
165
|
+
projectId: true,
|
|
166
|
+
},
|
|
167
|
+
requestOptions: data.requestOptions,
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
const userId: ObjectID | undefined = rowMembership?.userId;
|
|
171
|
+
const projectId: ObjectID | undefined = rowMembership?.projectId;
|
|
172
|
+
|
|
173
|
+
if (!userId || !projectId) {
|
|
174
|
+
/*
|
|
175
|
+
* The row is gone, or came back without the user or project it stands
|
|
176
|
+
* for. Fall back to the single delete the table asked for rather than
|
|
177
|
+
* guessing which project to empty.
|
|
178
|
+
*/
|
|
179
|
+
return super.deleteItem<TBaseModel>(data);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const url: URL = URL.fromString(APP_API_URL.toString())
|
|
183
|
+
.addRoute(new User().getCrudApiPath()!)
|
|
184
|
+
.addRoute(`/${userId.toString()}/remove-from-project`);
|
|
185
|
+
|
|
186
|
+
const result: HTTPResponse<JSONObject> | HTTPErrorResponse =
|
|
187
|
+
await API.post<JSONObject>({
|
|
188
|
+
url: url,
|
|
189
|
+
data: {
|
|
190
|
+
projectId: projectId.toString(),
|
|
191
|
+
},
|
|
192
|
+
headers: this.getCommonHeaders(data.requestOptions),
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
if (result instanceof HTTPErrorResponse) {
|
|
196
|
+
throw result;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* The user the table is listing projects for.
|
|
202
|
+
*
|
|
203
|
+
* Accepts the id in the shapes a table query can carry it in: an ObjectID
|
|
204
|
+
* (what the page passes) or a plain string.
|
|
205
|
+
*/
|
|
206
|
+
private static getUserIdFromQuery(query: Query<TeamMember>): ObjectID | null {
|
|
207
|
+
const value: unknown = (query as Record<string, unknown>)["userId"];
|
|
208
|
+
|
|
209
|
+
if (value instanceof ObjectID) {
|
|
210
|
+
return value;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (typeof value === "string" && value) {
|
|
214
|
+
return new ObjectID(value);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* One row of the response.
|
|
222
|
+
*
|
|
223
|
+
* The base of the row is a TeamMember and hydrates as one. The aggregate
|
|
224
|
+
* fields are not columns of TeamMember, so BaseModel.fromJSON drops them -
|
|
225
|
+
* they are read off the raw JSON and attached here.
|
|
226
|
+
*/
|
|
227
|
+
private static toRow(item: JSONObject): UserProjectRow {
|
|
228
|
+
const row: UserProjectRow = BaseModel.fromJSON(
|
|
229
|
+
item,
|
|
230
|
+
TeamMember,
|
|
231
|
+
) as UserProjectRow;
|
|
232
|
+
|
|
233
|
+
row.teamsForProject = BaseModel.fromJSONArray(
|
|
234
|
+
(item["teams"] as Array<JSONObject>) || [],
|
|
235
|
+
Team,
|
|
236
|
+
);
|
|
237
|
+
row.teamCountForProject = row.teamsForProject.length;
|
|
238
|
+
row.pendingTeamCountForProject = this.toCount(item["pendingTeamCount"]);
|
|
239
|
+
row.teamMemberIdsForProject = (
|
|
240
|
+
(item["teamMemberIds"] as Array<unknown>) || []
|
|
241
|
+
)
|
|
242
|
+
.map((id: unknown) => {
|
|
243
|
+
return id?.toString() || "";
|
|
244
|
+
})
|
|
245
|
+
.filter((id: string) => {
|
|
246
|
+
return Boolean(id);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return row;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private static toCount(value: unknown): number {
|
|
253
|
+
const count: number = Number(value);
|
|
254
|
+
|
|
255
|
+
return Number.isFinite(count) && count > 0 ? count : 0;
|
|
256
|
+
}
|
|
257
|
+
}
|