@oneuptime/common 12.0.5 → 12.0.6
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/AIRun.ts +33 -0
- package/Models/DatabaseModels/DataSource.ts +744 -0
- package/Models/DatabaseModels/Index.ts +3 -0
- package/Models/DatabaseModels/ProjectCallSMSConfig.ts +10 -2
- package/Models/DatabaseModels/User.ts +2 -0
- package/Models/DatabaseModels/UserOnCallLog.ts +2 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +2 -0
- package/Models/DatabaseModels/UserSession.ts +2 -0
- package/Models/DatabaseModels/UserTotpAuth.ts +6 -4
- package/Models/DatabaseModels/UserWebAuthn.ts +5 -3
- package/Server/API/AIInsightAPI.ts +47 -4
- package/Server/API/AIInvestigationAPI.ts +25 -0
- package/Server/API/DashboardAPI.ts +176 -492
- package/Server/API/DataSourceAPI.ts +268 -0
- package/Server/API/StatusPageAPI.ts +11 -1
- package/Server/API/UserAPI.ts +119 -0
- package/Server/Infrastructure/ClickhouseConfig.ts +59 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.ts +39 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.ts +24 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.ts +76 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Infrastructure/Queue.ts +26 -0
- package/Server/Infrastructure/QueueWorker.ts +17 -0
- package/Server/Middleware/TelemetryIngest.ts +8 -3
- package/Server/Services/AIInsightService.ts +132 -12
- package/Server/Services/AIRunService.ts +34 -0
- package/Server/Services/AIService.ts +17 -2
- package/Server/Services/AlertService.ts +38 -32
- package/Server/Services/AnalyticsDatabaseService.ts +8 -4
- package/Server/Services/DataSourceService.ts +200 -0
- package/Server/Services/HostService.ts +92 -0
- package/Server/Services/IncidentService.ts +45 -0
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/MonitorService.ts +49 -0
- package/Server/Services/MonitorStatusService.ts +166 -0
- package/Server/Services/OpenTelemetryIngestService.ts +255 -184
- package/Server/Services/RumSessionPinService.ts +7 -21
- package/Server/Services/StatusPageSubscriberService.ts +13 -0
- package/Server/Services/UserService.ts +280 -9
- package/Server/Static/Vendor/README.md +62 -0
- package/Server/Static/Vendor/fonts/InterVariable.woff2 +0 -0
- package/Server/Static/Vendor/highlight/highlight.min.js +1244 -0
- package/Server/Static/Vendor/highlight/languages/apache.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/bash.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/c.min.js +41 -0
- package/Server/Static/Vendor/highlight/languages/cpp.min.js +47 -0
- package/Server/Static/Vendor/highlight/languages/csharp.min.js +49 -0
- package/Server/Static/Vendor/highlight/languages/css.min.js +31 -0
- package/Server/Static/Vendor/highlight/languages/diff.min.js +9 -0
- package/Server/Static/Vendor/highlight/languages/dns.min.js +11 -0
- package/Server/Static/Vendor/highlight/languages/dockerfile.min.js +8 -0
- package/Server/Static/Vendor/highlight/languages/go.min.js +20 -0
- package/Server/Static/Vendor/highlight/languages/graphql.min.js +12 -0
- package/Server/Static/Vendor/highlight/languages/http.min.js +14 -0
- package/Server/Static/Vendor/highlight/languages/ini.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/java.min.js +38 -0
- package/Server/Static/Vendor/highlight/languages/javascript.min.js +81 -0
- package/Server/Static/Vendor/highlight/languages/json.min.js +8 -0
- package/Server/Static/Vendor/highlight/languages/kotlin.min.js +46 -0
- package/Server/Static/Vendor/highlight/languages/lua.min.js +15 -0
- package/Server/Static/Vendor/highlight/languages/makefile.min.js +14 -0
- package/Server/Static/Vendor/highlight/languages/nginx.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/perl.min.js +41 -0
- package/Server/Static/Vendor/highlight/languages/php.min.js +59 -0
- package/Server/Static/Vendor/highlight/languages/powershell.min.js +39 -0
- package/Server/Static/Vendor/highlight/languages/python.min.js +42 -0
- package/Server/Static/Vendor/highlight/languages/r.min.js +26 -0
- package/Server/Static/Vendor/highlight/languages/ruby.min.js +54 -0
- package/Server/Static/Vendor/highlight/languages/rust.min.js +27 -0
- package/Server/Static/Vendor/highlight/languages/scala.min.js +28 -0
- package/Server/Static/Vendor/highlight/languages/sql.min.js +21 -0
- package/Server/Static/Vendor/highlight/languages/swift.min.js +68 -0
- package/Server/Static/Vendor/highlight/languages/typescript.min.js +99 -0
- package/Server/Static/Vendor/highlight/languages/xml.min.js +29 -0
- package/Server/Static/Vendor/highlight/languages/yaml.min.js +28 -0
- package/Server/Static/Vendor/highlight/styles/github-dark.min.css +10 -0
- package/Server/Static/Vendor/highlight/styles/vs2015.min.css +1 -0
- package/Server/Static/Vendor/tailwind/tailwind-3.4.5.js +21028 -0
- package/Server/Types/Database/Permissions/BasePermission.ts +7 -0
- package/Server/Types/Database/Permissions/DeletePermission.ts +2 -0
- package/Server/Types/Database/Permissions/ReadPermission.ts +3 -0
- package/Server/Types/Database/Permissions/TenantPermission.ts +101 -16
- package/Server/Utils/AI/CodeFix/FixRunBudget.ts +2 -2
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +90 -11
- package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +1 -0
- package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +1 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.ts +125 -0
- package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +25 -15
- package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +256 -57
- package/Server/Utils/AI/SRE/Insights/InsightStore.ts +76 -18
- package/Server/Utils/AI/SRE/InvestigationTldr.ts +352 -0
- package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +7 -2
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +1793 -0
- package/Server/Utils/Dashboard/PublicDashboardViewConfig.ts +65 -0
- package/Server/Utils/DataSource/ConnectorRegistry.ts +53 -0
- package/Server/Utils/DataSource/Connectors/ClickHouseConnector.ts +338 -0
- package/Server/Utils/DataSource/Connectors/ElasticsearchConnector.ts +717 -0
- package/Server/Utils/DataSource/Connectors/LokiConnector.ts +606 -0
- package/Server/Utils/DataSource/Connectors/MySqlConnector.ts +307 -0
- package/Server/Utils/DataSource/Connectors/PostgresConnector.ts +260 -0
- package/Server/Utils/DataSource/Connectors/PrometheusConnector.ts +555 -0
- package/Server/Utils/DataSource/Connectors/RestApiConnector.ts +597 -0
- package/Server/Utils/DataSource/Connectors/SqlServerConnector.ts +318 -0
- package/Server/Utils/DataSource/EgressGuard.ts +446 -0
- package/Server/Utils/DataSource/HttpFetch.ts +221 -0
- package/Server/Utils/DataSource/SqlQueryGuard.ts +383 -0
- package/Server/Utils/DataSource/SqlResultShaper.ts +372 -0
- package/Server/Utils/DataSource/Types.ts +93 -0
- package/Server/Utils/Database/PostgresErrorTranslator.ts +172 -17
- package/Server/Utils/InProcessMemo.ts +141 -0
- package/Server/Utils/InstanceHealth/TelemetryIngestion.ts +628 -0
- package/Server/Utils/Monitor/MonitorMetricUtil.ts +58 -0
- package/Server/Utils/Monitor/MonitorResource.ts +14 -0
- package/Server/Utils/Response.ts +12 -1
- package/Server/Utils/SSRFProtection.ts +237 -0
- package/Server/Utils/StartServer.ts +8 -0
- package/Server/Utils/StatusPageSubscriberWebhook.ts +30 -0
- package/Server/Utils/Telemetry/CaptureSpan.ts +30 -18
- package/Server/Utils/Telemetry/IngestionTimestamp.ts +75 -0
- package/Server/Utils/Telemetry/ResourceHeartbeat.ts +99 -7
- package/Server/Utils/Telemetry/Telemetry.ts +112 -1
- package/Server/Utils/Telemetry.ts +104 -3
- package/Server/Utils/VendorAssets.ts +165 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +28 -5
- package/Server/Utils/Workspace/Slack/Slack.ts +5 -0
- package/Server/Views/Partials/Head.ejs +21 -7
- package/Tests/App/AdminDashboard/AdminProjectsIdFilter.test.tsx +207 -0
- package/Tests/App/AdminDashboard/AdminUsersIdFilter.test.tsx +216 -0
- package/Tests/App/Dashboard/AIInsightViewActions.test.tsx +422 -0
- package/Tests/App/Dashboard/AddWidgetModal.test.tsx +10 -3
- package/Tests/App/Dashboard/AlertsSideMenu.test.tsx +387 -0
- package/Tests/App/Dashboard/ChatActivityFeed.test.tsx +199 -0
- package/Tests/App/Dashboard/DataSourceChartSyntheticConfig.test.tsx +736 -0
- package/Tests/App/Dashboard/DataSourceQueryEditor.test.tsx +868 -0
- package/Tests/App/Dashboard/DataSourceWidgetCatalog.test.ts +440 -0
- package/Tests/App/Dashboard/DataSourceWidgetFetching.test.tsx +2109 -0
- package/Tests/App/Dashboard/DataSourceWidgetViews.test.tsx +726 -0
- package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +447 -0
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +514 -3
- package/Tests/App/Dashboard/ProductMenuBreadcrumbs.test.tsx +391 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceSideMenu.test.tsx +326 -0
- package/Tests/App/Dashboard/SideMenuHarness.tsx +187 -0
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +6 -1
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +1858 -0
- package/Tests/Models/DatabaseModels/DataSourceModelSecrets.test.ts +70 -0
- package/Tests/Server/API/AIInvestigationAPI.test.ts +187 -0
- package/Tests/Server/API/DashboardPublicDataSourceStrip.test.ts +686 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +585 -823
- package/Tests/Server/API/DataSourceAPIRoutes.test.ts +486 -0
- package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +291 -0
- package/Tests/Server/API/UserAuthenticationAPI.test.ts +641 -0
- package/Tests/Server/Infrastructure/ClickhouseKeepAliveOptions.test.ts +441 -0
- package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +140 -0
- package/Tests/Server/Services/AIInsightService.test.ts +266 -11
- package/Tests/Server/Services/AIRunAnalysisTldrPersistence.test.ts +160 -0
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +4 -0
- package/Tests/Server/Services/AlertMetricResourceAttributes.test.ts +352 -0
- package/Tests/Server/Services/DataSourceServiceSettings.test.ts +244 -0
- package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +397 -0
- package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +41 -2
- package/Tests/Server/Services/IncidentMetricResourceAttributes.test.ts +408 -0
- package/Tests/Server/Services/MonitorStatusServiceDeleteReferenceCleanup.test.ts +351 -0
- package/Tests/Server/Services/MonitorStatusServiceValidation.test.ts +171 -0
- package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +398 -0
- package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +19 -0
- package/Tests/Server/Services/ProjectCallSMSConfigCredentialUniqueness.test.ts +305 -0
- package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +36 -1
- package/Tests/Server/Services/RumSessionPinUniqueViolationRecovery.test.ts +163 -0
- package/Tests/Server/Services/UserAuthenticationService.test.ts +973 -0
- package/Tests/Server/Types/Database/ObjectIdColumnQuery.test.ts +262 -0
- package/Tests/Server/Types/Database/Permissions/TenantPermission.test.ts +358 -0
- package/Tests/Server/Utils/AI/FixRunBudget.test.ts +2 -2
- package/Tests/Server/Utils/AI/Insights/ExceptionSpikeDetector.test.ts +6 -1
- package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +121 -4
- package/Tests/Server/Utils/AI/Insights/NewExceptionDetector.test.ts +361 -10
- package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +292 -0
- package/Tests/Server/Utils/AI/InvestigationTldr.test.ts +623 -0
- package/Tests/Server/Utils/AI/InvestigationTldrWiring.test.ts +98 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +857 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardViewConfig.test.ts +480 -0
- package/Tests/Server/Utils/DataSource/ClickHouseConnector.test.ts +759 -0
- package/Tests/Server/Utils/DataSource/ConnectorRegistry.test.ts +37 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +645 -0
- package/Tests/Server/Utils/DataSource/ElasticsearchConnector.test.ts +1206 -0
- package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +616 -0
- package/Tests/Server/Utils/DataSource/LokiConnector.test.ts +970 -0
- package/Tests/Server/Utils/DataSource/MySqlConnector.test.ts +935 -0
- package/Tests/Server/Utils/DataSource/PostgresConnector.test.ts +854 -0
- package/Tests/Server/Utils/DataSource/PrometheusConnector.test.ts +881 -0
- package/Tests/Server/Utils/DataSource/RestApiConnector.test.ts +1020 -0
- package/Tests/Server/Utils/DataSource/SqlQueryGuard.test.ts +425 -0
- package/Tests/Server/Utils/DataSource/SqlResultShaper.test.ts +555 -0
- package/Tests/Server/Utils/DataSource/SqlServerConnector.test.ts +934 -0
- package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +228 -7
- package/Tests/Server/Utils/InProcessMemo.test.ts +292 -0
- package/Tests/Server/Utils/InstanceHealth/TelemetryIngestion.test.ts +960 -0
- package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +103 -0
- package/Tests/Server/Utils/Monitor/MonitorMetricResourceAttributes.test.ts +336 -0
- package/Tests/Server/Utils/OfflineAssetHygiene.test.ts +521 -0
- package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +143 -0
- package/Tests/Server/Utils/SSRFProtection.test.ts +193 -0
- package/Tests/Server/Utils/StatusPageSubscriberWebhook.test.ts +101 -0
- package/Tests/Server/Utils/TailwindBuildCopy.test.ts +225 -0
- package/Tests/Server/Utils/Telemetry/ResourceHeartbeatL1Memo.test.ts +355 -0
- package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +128 -0
- package/Tests/Server/Utils/Telemetry/TelemetryUtilAttributeKeys.test.ts +381 -0
- package/Tests/Server/Utils/Telemetry.test.ts +87 -0
- package/Tests/Server/Utils/VendorAssetIntegrity.test.ts +343 -0
- package/Tests/Server/Utils/VendorAssets.test.ts +671 -0
- package/Tests/Server/Views/OfflineRender.test.ts +297 -0
- package/Tests/Types/Dashboard/DataSourceComponentTypes.test.ts +618 -0
- package/Tests/Types/DateClickhouseFormat.test.ts +192 -0
- package/Tests/Types/Password.test.ts +363 -0
- package/Tests/UI/Components/ConfirmModal.test.tsx +7 -0
- package/Tests/UI/Components/ErrorBoundary.test.tsx +53 -0
- package/Tests/UI/Components/Modal.test.tsx +281 -1
- package/Tests/UI/Components/ObjectIdFilterPipeline.test.tsx +396 -0
- package/Tests/UI/Components/StatusPage/ResourceGroupNavigator.test.tsx +1147 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +116 -1
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
- package/Tests/Utils/Analytics.test.ts +23 -0
- package/Tests/Utils/Dashboard/Components/DashboardDataSourceComponents.test.ts +640 -0
- package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +138 -0
- package/Tests/Utils/DataSource/DataSourceValueReducer.test.ts +526 -0
- package/Tests/Utils/Metrics/MetricResourceAttributeUtil.test.ts +691 -0
- package/Tests/Utils/StatusPage/GroupHierarchyView.test.ts +1452 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +931 -0
- package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +288 -0
- package/Tests/Utils/Telemetry/IngestionTimestamp.test.ts +170 -0
- package/Types/AI/AIInsightEvidence.ts +6 -0
- package/Types/Analytics/RevenueEvent.ts +31 -0
- package/Types/Dashboard/DashboardComponentType.ts +31 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.ts +27 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.ts +32 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.ts +30 -0
- package/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.ts +55 -0
- package/Types/DataSource/DataSourceLimits.ts +62 -0
- package/Types/DataSource/DataSourceQueryConfig.ts +41 -0
- package/Types/DataSource/DataSourceTableResult.ts +38 -0
- package/Types/DataSource/DataSourceType.ts +213 -0
- package/Types/Date.ts +43 -3
- package/Types/Password.ts +64 -0
- package/Types/Permission.ts +46 -0
- package/Types/UserAuthenticationStatus.ts +34 -0
- package/UI/Components/Button/Button.tsx +1 -1
- package/UI/Components/ErrorBoundary.tsx +459 -108
- package/UI/Components/Icon/Icon.tsx +10 -9
- package/UI/Components/Modal/ConfirmModal.tsx +6 -1
- package/UI/Components/Modal/Modal.tsx +267 -35
- package/UI/Components/Modal/ModalFooter.tsx +6 -1
- package/UI/Components/MoreMenu/MoreMenu.tsx +19 -4
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +6 -1
- package/UI/Components/StatusPage/ResourceGrid.tsx +314 -0
- package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +808 -0
- package/UI/Components/StatusPage/ResourceList.tsx +570 -0
- package/UI/Styles/Theme.css +18 -0
- package/UI/Utils/API/RequestOptions.ts +7 -0
- package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +1 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +1 -0
- package/UI/Utils/TestDataSource.ts +61 -0
- package/UI/esbuild-config.js +45 -0
- package/Utils/Analytics.ts +15 -0
- package/Utils/Dashboard/Components/DashboardDataSourceChartComponent.ts +88 -0
- package/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.ts +123 -0
- package/Utils/Dashboard/Components/DashboardDataSourceShared.ts +16 -0
- package/Utils/Dashboard/Components/DashboardDataSourceTableComponent.ts +72 -0
- package/Utils/Dashboard/Components/DashboardDataSourceValueComponent.ts +126 -0
- package/Utils/Dashboard/Components/Index.ts +28 -0
- package/Utils/DataSource/DataSourceQueryText.ts +94 -0
- package/Utils/DataSource/DataSourceValueReducer.ts +156 -0
- package/Utils/Metrics/MetricResourceAttributeUtil.ts +380 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +8 -6
- package/Utils/StatusPage/GroupHierarchyView.ts +931 -0
- package/Utils/StatusPage/ResourceExplorer.ts +853 -0
- package/build/dist/Models/DatabaseModels/AIRun.js +34 -0
- package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DataSource.js +774 -0
- package/build/dist/Models/DatabaseModels/DataSource.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js +14 -4
- package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +2 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js +2 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSession.js +2 -0
- package/build/dist/Models/DatabaseModels/UserSession.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js +6 -4
- package/build/dist/Models/DatabaseModels/UserTotpAuth.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js +5 -3
- package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -1
- package/build/dist/Server/API/AIInsightAPI.js +34 -4
- package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +23 -0
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +119 -433
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/DataSourceAPI.js +173 -0
- package/build/dist/Server/API/DataSourceAPI.js.map +1 -0
- package/build/dist/Server/API/StatusPageAPI.js +11 -1
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +94 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js +49 -0
- package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js +64 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +27 -0
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Infrastructure/QueueWorker.js +13 -0
- package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +8 -3
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIInsightService.js +110 -10
- package/build/dist/Server/Services/AIInsightService.js.map +1 -1
- package/build/dist/Server/Services/AIRunService.js +34 -0
- package/build/dist/Server/Services/AIRunService.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +16 -2
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +28 -35
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +8 -4
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DataSourceService.js +159 -0
- package/build/dist/Server/Services/DataSourceService.js.map +1 -0
- package/build/dist/Server/Services/HostService.js +74 -0
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +35 -24
- package/build/dist/Server/Services/IncidentService.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/MonitorService.js +45 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorStatusService.js +129 -0
- package/build/dist/Server/Services/MonitorStatusService.js.map +1 -1
- package/build/dist/Server/Services/OpenTelemetryIngestService.js +222 -159
- package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
- package/build/dist/Server/Services/RumSessionPinService.js +7 -15
- package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +10 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +241 -10
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js +6 -0
- package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/DeletePermission.js +1 -0
- package/build/dist/Server/Types/Database/Permissions/DeletePermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +2 -0
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +68 -12
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +2 -2
- package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +68 -8
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +1 -0
- package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +1 -0
- package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js +97 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js.map +1 -0
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +17 -15
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +178 -54
- package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js +65 -16
- package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js +303 -0
- package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js.map +1 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +1261 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js +47 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js +42 -0
- package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js +187 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js +493 -0
- package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js +399 -0
- package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js +195 -0
- package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js +149 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js +362 -0
- package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js +342 -0
- package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js +186 -0
- package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +305 -0
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/HttpFetch.js +134 -0
- package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js +331 -0
- package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/SqlResultShaper.js +280 -0
- package/build/dist/Server/Utils/DataSource/SqlResultShaper.js.map +1 -0
- package/build/dist/Server/Utils/DataSource/Types.js +2 -0
- package/build/dist/Server/Utils/DataSource/Types.js.map +1 -0
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +103 -9
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -1
- package/build/dist/Server/Utils/InProcessMemo.js +113 -0
- package/build/dist/Server/Utils/InProcessMemo.js.map +1 -0
- package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js +406 -0
- package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +34 -0
- package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +14 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Response.js +9 -1
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Server/Utils/SSRFProtection.js +192 -0
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -0
- package/build/dist/Server/Utils/StartServer.js +7 -0
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js +24 -0
- package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +25 -11
- package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js +32 -0
- package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +92 -5
- package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/Telemetry.js +92 -6
- package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry.js +93 -6
- package/build/dist/Server/Utils/Telemetry.js.map +1 -1
- package/build/dist/Server/Utils/VendorAssets.js +121 -0
- package/build/dist/Server/Utils/VendorAssets.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +5 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Types/Analytics/RevenueEvent.js +26 -0
- package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js +27 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js +16 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceLimits.js +47 -0
- package/build/dist/Types/DataSource/DataSourceLimits.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceQueryConfig.js +10 -0
- package/build/dist/Types/DataSource/DataSourceQueryConfig.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceTableResult.js +14 -0
- package/build/dist/Types/DataSource/DataSourceTableResult.js.map +1 -0
- package/build/dist/Types/DataSource/DataSourceType.js +183 -0
- package/build/dist/Types/DataSource/DataSourceType.js.map +1 -0
- package/build/dist/Types/Date.js +38 -3
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Password.js +56 -0
- package/build/dist/Types/Password.js.map +1 -0
- package/build/dist/Types/Permission.js +42 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/UserAuthenticationStatus.js +2 -0
- package/build/dist/Types/UserAuthenticationStatus.js.map +1 -0
- package/build/dist/UI/Components/ErrorBoundary.js +388 -72
- package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +10 -4
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
- package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +178 -19
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js +3 -1
- package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +9 -6
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +7 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +119 -0
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +354 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -0
- package/build/dist/UI/Components/StatusPage/ResourceList.js +203 -0
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -0
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +1 -6
- package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +3 -8
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/TestDataSource.js +39 -0
- package/build/dist/UI/Utils/TestDataSource.js.map +1 -0
- package/build/dist/Utils/Analytics.js +4 -0
- package/build/dist/Utils/Analytics.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js +79 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js +111 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js +15 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js +62 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js +112 -0
- package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +16 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/DataSource/DataSourceQueryText.js +64 -0
- package/build/dist/Utils/DataSource/DataSourceQueryText.js.map +1 -0
- package/build/dist/Utils/DataSource/DataSourceValueReducer.js +124 -0
- package/build/dist/Utils/DataSource/DataSourceValueReducer.js.map +1 -0
- package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js +281 -0
- package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +8 -6
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
- package/build/dist/Utils/StatusPage/GroupHierarchyView.js +560 -0
- package/build/dist/Utils/StatusPage/GroupHierarchyView.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js +519 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -0
- package/jest.config.json +1 -0
- package/package.json +5 -1
- package/tsconfig.json +12 -10
|
@@ -3,6 +3,7 @@ import QueryDeepPartialEntity from "../../../Types/Database/PartialEntity";
|
|
|
3
3
|
import ObjectID from "../../../Types/ObjectID";
|
|
4
4
|
import DatabaseService from "../../Services/DatabaseService";
|
|
5
5
|
import GlobalCache from "../../Infrastructure/GlobalCache";
|
|
6
|
+
import InProcessMemo from "../InProcessMemo";
|
|
6
7
|
import SingleFlight from "../SingleFlight";
|
|
7
8
|
import logger from "../Logger";
|
|
8
9
|
|
|
@@ -116,10 +117,55 @@ function writeWindowNamespace(cacheNamespace: string): string {
|
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
export default class ResourceHeartbeat {
|
|
120
|
+
/*
|
|
121
|
+
* Time-based L1 over the whole gate cascade. SingleFlight collapses
|
|
122
|
+
* CONCURRENT duplicates, but sequential batches — the next job for the
|
|
123
|
+
* same host arriving a second later — each still paid 1-3 Redis round
|
|
124
|
+
* trips (liveness SET NX, fingerprint compare-and-claim, rate-limit
|
|
125
|
+
* SET NX) to be told "nothing to do". Once THIS process has settled a
|
|
126
|
+
* heartbeat for a (resource, fingerprint), that answer holds for the
|
|
127
|
+
* rest of the window, so re-asking Redis per batch buys nothing.
|
|
128
|
+
*
|
|
129
|
+
* The memo key includes the FINGERPRINT, so a genuinely changed payload
|
|
130
|
+
* bypasses the memo and reaches the compare-and-claim gate as promptly
|
|
131
|
+
* as it did before — the L1 only ever suppresses re-asking a question
|
|
132
|
+
* whose inputs are identical.
|
|
133
|
+
*
|
|
134
|
+
* Entries live for min(60s, the caller's liveness window, pre-jitter).
|
|
135
|
+
* Within that span the Redis liveness key this process (or another pod)
|
|
136
|
+
* claimed cannot have expired yet (its jittered TTL is >= the window),
|
|
137
|
+
* so a skipped probe would have been refused anyway. The one real trade
|
|
138
|
+
* is cross-pod lastSeen PRECISION: when a DIFFERENT pod's claim expires
|
|
139
|
+
* mid-window, this pod would previously have won the reopened gate on
|
|
140
|
+
* its next batch, and now reacts up to one memo TTL later (any other pod
|
|
141
|
+
* still reacts immediately). That delay is bounded by the existing
|
|
142
|
+
* window — the same staleness the jittered TTL already permits — and
|
|
143
|
+
* sits far inside the 15-minute markDisconnected* thresholds.
|
|
144
|
+
*
|
|
145
|
+
* Outcomes that must retry are NEVER memoized: a failed or lock-skipped
|
|
146
|
+
* metadata write (those release their Redis gates precisely so the next
|
|
147
|
+
* batch retries) and any outcome where a gate probe threw (a Redis
|
|
148
|
+
* outage must keep failing open per batch, not once per memo TTL).
|
|
149
|
+
*/
|
|
150
|
+
private static readonly recentHeartbeatMemo: InProcessMemo<boolean> =
|
|
151
|
+
new InProcessMemo<boolean>({
|
|
152
|
+
ttlInMs: 60 * 1000,
|
|
153
|
+
maxEntries: 10_000,
|
|
154
|
+
});
|
|
155
|
+
|
|
119
156
|
public static async write<TBaseModel extends BaseModel>(
|
|
120
157
|
input: ResourceHeartbeatWrite<TBaseModel>,
|
|
121
158
|
): Promise<void> {
|
|
122
159
|
const cacheKey: string = input.id.toString();
|
|
160
|
+
const memoKey: string = `${input.cacheNamespace}:${cacheKey}:${input.fingerprint}`;
|
|
161
|
+
|
|
162
|
+
/*
|
|
163
|
+
* L1: this process already settled this exact heartbeat within the
|
|
164
|
+
* window. Zero network cost, no gates touched.
|
|
165
|
+
*/
|
|
166
|
+
if (this.recentHeartbeatMemo.get(memoKey)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
123
169
|
|
|
124
170
|
/*
|
|
125
171
|
* Collapse duplicate concurrent callers inside THIS process first. One
|
|
@@ -128,18 +174,41 @@ export default class ResourceHeartbeat {
|
|
|
128
174
|
* and without this every one of them independently asks Redis the same
|
|
129
175
|
* question and throws the answer away.
|
|
130
176
|
*/
|
|
131
|
-
await SingleFlight.run(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
177
|
+
await SingleFlight.run(memoKey, async () => {
|
|
178
|
+
await this.writeOnce(input, cacheKey, memoKey);
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Drop the recent-heartbeat memo. For tests only — suites that reuse one
|
|
184
|
+
* resource id across cases simulate the memo TTL expiring with this, the
|
|
185
|
+
* same way they already clear their fake Redis maps and SingleFlight.
|
|
186
|
+
*/
|
|
187
|
+
public static clearRecentHeartbeatMemo(): void {
|
|
188
|
+
this.recentHeartbeatMemo.clear();
|
|
137
189
|
}
|
|
138
190
|
|
|
139
191
|
private static async writeOnce<TBaseModel extends BaseModel>(
|
|
140
192
|
input: ResourceHeartbeatWrite<TBaseModel>,
|
|
141
193
|
cacheKey: string,
|
|
194
|
+
memoKey: string,
|
|
142
195
|
): Promise<void> {
|
|
196
|
+
/*
|
|
197
|
+
* Whether every Redis gate we consulted actually answered. A probe that
|
|
198
|
+
* THREW fell back to a hardcoded outcome (open for liveness, closed for
|
|
199
|
+
* enrichment) — that outcome is a per-batch policy, not a fact about the
|
|
200
|
+
* window, so it must never be memoized into the L1.
|
|
201
|
+
*/
|
|
202
|
+
let gatesAnswered: boolean = true;
|
|
203
|
+
|
|
204
|
+
/*
|
|
205
|
+
* The memo must expire no later than the PRE-JITTER window, because that
|
|
206
|
+
* is the earliest instant the Redis liveness key claimed alongside it
|
|
207
|
+
* could expire — and never later than the 60-second norm.
|
|
208
|
+
*/
|
|
209
|
+
const memoTtlInMs: number =
|
|
210
|
+
Math.min(60, Math.max(input.throttleInSeconds, 0)) * 1000;
|
|
211
|
+
|
|
143
212
|
/*
|
|
144
213
|
* Liveness gate. Presence-only and jittered: a fleet-wide restart would
|
|
145
214
|
* otherwise re-synchronise every resource's window and rebuild the herd on
|
|
@@ -163,6 +232,7 @@ export default class ResourceHeartbeat {
|
|
|
163
232
|
* the write below cannot queue.
|
|
164
233
|
*/
|
|
165
234
|
writeLiveness = true;
|
|
235
|
+
gatesAnswered = false;
|
|
166
236
|
}
|
|
167
237
|
|
|
168
238
|
const hasMetadata: boolean =
|
|
@@ -194,6 +264,7 @@ export default class ResourceHeartbeat {
|
|
|
194
264
|
* write storm this exists to prevent.
|
|
195
265
|
*/
|
|
196
266
|
metadataChanged = false;
|
|
267
|
+
gatesAnswered = false;
|
|
197
268
|
}
|
|
198
269
|
}
|
|
199
270
|
|
|
@@ -218,11 +289,19 @@ export default class ResourceHeartbeat {
|
|
|
218
289
|
);
|
|
219
290
|
} catch {
|
|
220
291
|
writeMetadata = false;
|
|
292
|
+
gatesAnswered = false;
|
|
221
293
|
}
|
|
222
294
|
}
|
|
223
295
|
|
|
224
296
|
if (!writeLiveness && !writeMetadata) {
|
|
225
|
-
|
|
297
|
+
/*
|
|
298
|
+
* Nothing to say. Issue no statement at all — and remember that for
|
|
299
|
+
* the memo TTL, since every gate answered and the same inputs can
|
|
300
|
+
* only produce the same silence for the rest of the window.
|
|
301
|
+
*/
|
|
302
|
+
if (gatesAnswered) {
|
|
303
|
+
this.recentHeartbeatMemo.set(memoKey, true, memoTtlInMs);
|
|
304
|
+
}
|
|
226
305
|
return;
|
|
227
306
|
}
|
|
228
307
|
|
|
@@ -283,6 +362,19 @@ export default class ResourceHeartbeat {
|
|
|
283
362
|
* statement that returns immediately when contended.
|
|
284
363
|
*/
|
|
285
364
|
await this.releaseGates(input.cacheNamespace, cacheKey, true);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/*
|
|
369
|
+
* The write LANDED: nothing is left for this exact input to do this
|
|
370
|
+
* window, so remember that. Memoized only when every gate genuinely
|
|
371
|
+
* answered (an outage outcome is per-batch policy, never a fact) and
|
|
372
|
+
* NOT for the liveness-only lock-skip above `wrote === false` — that
|
|
373
|
+
* case is rare, already free of gate churn next batch, and memoizing
|
|
374
|
+
* a write we did not perform is a subtlety not worth carrying.
|
|
375
|
+
*/
|
|
376
|
+
if (wrote && gatesAnswered) {
|
|
377
|
+
this.recentHeartbeatMemo.set(memoKey, true, memoTtlInMs);
|
|
286
378
|
}
|
|
287
379
|
}
|
|
288
380
|
|
|
@@ -451,7 +451,6 @@ export default class TelemetryUtil {
|
|
|
451
451
|
};
|
|
452
452
|
}
|
|
453
453
|
|
|
454
|
-
@CaptureSpan()
|
|
455
454
|
public static getAttributes(data: {
|
|
456
455
|
prefixKeysWithString: string;
|
|
457
456
|
items: JSONArray;
|
|
@@ -655,4 +654,116 @@ export default class TelemetryUtil {
|
|
|
655
654
|
|
|
656
655
|
return Object.keys(attributes).sort();
|
|
657
656
|
}
|
|
657
|
+
|
|
658
|
+
/*
|
|
659
|
+
* OTLP scope attributes, prefixed for row storage. The three OTel ingest
|
|
660
|
+
* services stamp every key of the scope envelope onto the row's attribute
|
|
661
|
+
* map under a `scope.` prefix. The scope object is invariant for every
|
|
662
|
+
* record/span/datapoint inside its scopeLogs/scopeSpans/scopeMetrics
|
|
663
|
+
* envelope, so callers build this map ONCE per scope and merge it per row
|
|
664
|
+
* — instead of re-deriving the prefixed key strings for every row, which
|
|
665
|
+
* was a visible per-record cost on large batches.
|
|
666
|
+
*
|
|
667
|
+
* A missing/empty scope returns an empty map, which merges to a no-op —
|
|
668
|
+
* exactly what the previous inline `if (scope && keys.length > 0)` guard
|
|
669
|
+
* produced.
|
|
670
|
+
*/
|
|
671
|
+
public static getPrefixedScopeAttributes(
|
|
672
|
+
scope: JSONObject | undefined | null,
|
|
673
|
+
): Dictionary<AttributeType | Array<AttributeType>> {
|
|
674
|
+
const prefixedScopeAttributes: Dictionary<
|
|
675
|
+
AttributeType | Array<AttributeType>
|
|
676
|
+
> = {};
|
|
677
|
+
|
|
678
|
+
if (!scope || typeof scope !== "object") {
|
|
679
|
+
return prefixedScopeAttributes;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
for (const key of Object.keys(scope)) {
|
|
683
|
+
prefixedScopeAttributes[`scope.${key}`] = scope[key] as AttributeType;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
return prefixedScopeAttributes;
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
/*
|
|
690
|
+
* Sorted attribute-key union for rows whose attribute map is "large
|
|
691
|
+
* invariant base + few per-row keys" (metric datapoints). Produces exactly
|
|
692
|
+
* `Object.keys(mergedAttributes).sort()` — same elements, same order —
|
|
693
|
+
* without re-sorting the full key set per row:
|
|
694
|
+
*
|
|
695
|
+
* - `sortedBaseAttributeKeys` is the base map's key list, sorted once by
|
|
696
|
+
* the caller (per metric, not per datapoint);
|
|
697
|
+
* - `additionalAttributeKeys` is the per-row key list (typically 0-5
|
|
698
|
+
* keys), sorted here at its own tiny cost;
|
|
699
|
+
* - a linear two-pointer merge then yields the sorted, de-duplicated
|
|
700
|
+
* union.
|
|
701
|
+
*
|
|
702
|
+
* Order equivalence holds because Array.prototype.sort's default
|
|
703
|
+
* comparator orders strings by UTF-16 code units, and the `<` / `>`
|
|
704
|
+
* relational operators used in the merge apply the same code-unit
|
|
705
|
+
* comparison — so merging two lists sorted by that comparator gives the
|
|
706
|
+
* same sequence a full sort of the union would.
|
|
707
|
+
*
|
|
708
|
+
* Precondition: each input list is duplicate-free (both come from
|
|
709
|
+
* Object.keys, whose output cannot repeat a key). Duplicates ACROSS the
|
|
710
|
+
* two lists (a per-row key overriding a base key) collapse to one entry,
|
|
711
|
+
* matching Object.keys on the merged map.
|
|
712
|
+
*/
|
|
713
|
+
public static mergeAttributeKeysWithSortedBase(data: {
|
|
714
|
+
sortedBaseAttributeKeys: Array<string>;
|
|
715
|
+
additionalAttributeKeys: Array<string>;
|
|
716
|
+
}): Array<string> {
|
|
717
|
+
const { sortedBaseAttributeKeys, additionalAttributeKeys } = data;
|
|
718
|
+
|
|
719
|
+
/*
|
|
720
|
+
* Always return a fresh array: rows must never share an attributeKeys
|
|
721
|
+
* reference, because downstream pipeline rules replace a row's key list
|
|
722
|
+
* independently of its siblings.
|
|
723
|
+
*/
|
|
724
|
+
if (additionalAttributeKeys.length === 0) {
|
|
725
|
+
return sortedBaseAttributeKeys.slice();
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
const sortedAdditionalKeys: Array<string> = additionalAttributeKeys
|
|
729
|
+
.slice()
|
|
730
|
+
.sort();
|
|
731
|
+
|
|
732
|
+
const mergedKeys: Array<string> = [];
|
|
733
|
+
let baseIndex: number = 0;
|
|
734
|
+
let additionalIndex: number = 0;
|
|
735
|
+
|
|
736
|
+
while (
|
|
737
|
+
baseIndex < sortedBaseAttributeKeys.length &&
|
|
738
|
+
additionalIndex < sortedAdditionalKeys.length
|
|
739
|
+
) {
|
|
740
|
+
const baseKey: string = sortedBaseAttributeKeys[baseIndex]!;
|
|
741
|
+
const additionalKey: string = sortedAdditionalKeys[additionalIndex]!;
|
|
742
|
+
|
|
743
|
+
if (baseKey < additionalKey) {
|
|
744
|
+
mergedKeys.push(baseKey);
|
|
745
|
+
baseIndex++;
|
|
746
|
+
} else if (baseKey > additionalKey) {
|
|
747
|
+
mergedKeys.push(additionalKey);
|
|
748
|
+
additionalIndex++;
|
|
749
|
+
} else {
|
|
750
|
+
// Same key in base and per-row attributes — the map holds it once.
|
|
751
|
+
mergedKeys.push(baseKey);
|
|
752
|
+
baseIndex++;
|
|
753
|
+
additionalIndex++;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
while (baseIndex < sortedBaseAttributeKeys.length) {
|
|
758
|
+
mergedKeys.push(sortedBaseAttributeKeys[baseIndex]!);
|
|
759
|
+
baseIndex++;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
while (additionalIndex < sortedAdditionalKeys.length) {
|
|
763
|
+
mergedKeys.push(sortedAdditionalKeys[additionalIndex]!);
|
|
764
|
+
additionalIndex++;
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
return mergedKeys;
|
|
768
|
+
}
|
|
658
769
|
}
|
|
@@ -87,6 +87,13 @@ export enum SpanStatusCode {
|
|
|
87
87
|
ERROR = 2,
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
/*
|
|
91
|
+
* Tells a descriptive error `code` ("ECONNREFUSED") from one that is really a
|
|
92
|
+
* status number in disguise (an HTTP status, a Postgres SQLSTATE). See
|
|
93
|
+
* Telemetry.getExceptionTypeName.
|
|
94
|
+
*/
|
|
95
|
+
const NUMERIC_ONLY_REGEX: RegExp = /^\d+$/;
|
|
96
|
+
|
|
90
97
|
export default class Telemetry {
|
|
91
98
|
public static sdk: opentelemetry.NodeSDK | null = null;
|
|
92
99
|
|
|
@@ -102,6 +109,20 @@ export default class Telemetry {
|
|
|
102
109
|
|
|
103
110
|
public static serviceName: string | null = null;
|
|
104
111
|
|
|
112
|
+
/*
|
|
113
|
+
* True only when init() installed a real OTLP span exporter. @CaptureSpan
|
|
114
|
+
* consults this to skip span creation entirely on deployments that never
|
|
115
|
+
* export traces — without an exporter the spans go nowhere, but the wrapper
|
|
116
|
+
* would still pay attribute flattening, span allocation, and an
|
|
117
|
+
* AsyncLocalStorage context switch on every decorated call, which includes
|
|
118
|
+
* hot ingest paths that run millions of times per minute.
|
|
119
|
+
*/
|
|
120
|
+
private static spanExportEnabled: boolean = false;
|
|
121
|
+
|
|
122
|
+
public static isSpanExportEnabled(): boolean {
|
|
123
|
+
return this.spanExportEnabled;
|
|
124
|
+
}
|
|
125
|
+
|
|
105
126
|
public static getHeaders(): Dictionary<string> {
|
|
106
127
|
if (!process.env["OPENTELEMETRY_EXPORTER_OTLP_HEADERS"]) {
|
|
107
128
|
return {};
|
|
@@ -192,6 +213,7 @@ export default class Telemetry {
|
|
|
192
213
|
headers: headers,
|
|
193
214
|
compression: CompressionAlgorithm.GZIP,
|
|
194
215
|
}) as unknown as SpanExporter;
|
|
216
|
+
this.spanExportEnabled = true;
|
|
195
217
|
}
|
|
196
218
|
|
|
197
219
|
if (this.getOltpMetricsEndpoint() && hasHeaders) {
|
|
@@ -546,7 +568,36 @@ export default class Telemetry {
|
|
|
546
568
|
* empty "Error" status.
|
|
547
569
|
*/
|
|
548
570
|
span.setAttributes(exceptionAttributes);
|
|
549
|
-
|
|
571
|
+
|
|
572
|
+
/*
|
|
573
|
+
* A NORMALIZED {name, message, stack} is handed to recordException
|
|
574
|
+
* rather than the raw thrown value, because the OTel SDK reads
|
|
575
|
+
* `exception.code` BEFORE `exception.name` when deciding
|
|
576
|
+
* `exception.type` (sdk-trace-base Span.recordException). OneUptime's
|
|
577
|
+
* Exception base class exposes an HTTP STATUS as `code`
|
|
578
|
+
* (NotAuthenticatedException = 401, BadDataException = 400,
|
|
579
|
+
* ServerException = 500 — see Types/Exception/ExceptionCode), so every
|
|
580
|
+
* exception this platform raises was being typed "401"/"400"/"500".
|
|
581
|
+
*
|
|
582
|
+
* That type is the exception event's identity downstream: it is hashed
|
|
583
|
+
* into the TelemetryException group fingerprint and it is what the
|
|
584
|
+
* Issues list and the AI insight titles show. A whole service's worth of
|
|
585
|
+
* unrelated failures collapsed into "401", telling nobody anything.
|
|
586
|
+
*
|
|
587
|
+
* The payload is built from the attributes getExceptionAttributes has
|
|
588
|
+
* already extracted, which means it inherits their truncation (4k
|
|
589
|
+
* message, 8k stack) and their crash-safety — a hostile thrown value is
|
|
590
|
+
* read exactly once, defensively, rather than twice.
|
|
591
|
+
*/
|
|
592
|
+
span.recordException({
|
|
593
|
+
name: (exceptionAttributes["exception.type"] as string) || "Error",
|
|
594
|
+
message: (exceptionAttributes["exception.message"] as string) || "",
|
|
595
|
+
...(exceptionAttributes["exception.stacktrace"]
|
|
596
|
+
? {
|
|
597
|
+
stack: exceptionAttributes["exception.stacktrace"] as string,
|
|
598
|
+
}
|
|
599
|
+
: {}),
|
|
600
|
+
} as SpanException);
|
|
550
601
|
span.setStatus({
|
|
551
602
|
code: SpanStatusCode.ERROR,
|
|
552
603
|
message:
|
|
@@ -588,8 +639,7 @@ export default class Telemetry {
|
|
|
588
639
|
}
|
|
589
640
|
|
|
590
641
|
if (exception instanceof Error) {
|
|
591
|
-
attributes["exception.type"] =
|
|
592
|
-
exception.name || exception.constructor?.name || "Error";
|
|
642
|
+
attributes["exception.type"] = this.getExceptionTypeName(exception);
|
|
593
643
|
attributes["exception.message"] = this.truncate(
|
|
594
644
|
exception.message || "",
|
|
595
645
|
4000,
|
|
@@ -661,6 +711,57 @@ export default class Telemetry {
|
|
|
661
711
|
return attributes;
|
|
662
712
|
}
|
|
663
713
|
|
|
714
|
+
/*
|
|
715
|
+
* The most specific type name available for a thrown Error, in the order a
|
|
716
|
+
* human would want to read it in the Issues list.
|
|
717
|
+
*
|
|
718
|
+
* `name` is checked first but "Error" is rejected: an Error subclass that
|
|
719
|
+
* never assigns `this.name` inherits the generic "Error" from
|
|
720
|
+
* Error.prototype, and OneUptime's own Exception base does exactly that —
|
|
721
|
+
* so `name` alone reports "Error" for NotAuthenticatedException,
|
|
722
|
+
* BadDataException and every other one of them.
|
|
723
|
+
*
|
|
724
|
+
* A `code` is only used when it is a NON-NUMERIC string. That keeps the
|
|
725
|
+
* genuinely descriptive Node system codes (ECONNREFUSED, ENOTFOUND) while
|
|
726
|
+
* rejecting numeric ones — HTTP statuses on OneUptime's Exception and
|
|
727
|
+
* Postgres SQLSTATEs ("23505"), for which the constructor name
|
|
728
|
+
* (QueryFailedError) says far more. The numeric code is not lost — it keeps
|
|
729
|
+
* its own `exception.code` attribute.
|
|
730
|
+
*
|
|
731
|
+
* Never throws: it runs on the universal error path and the thrown value
|
|
732
|
+
* can be a Proxy or carry throwing getters.
|
|
733
|
+
*/
|
|
734
|
+
private static getExceptionTypeName(exception: Error): string {
|
|
735
|
+
try {
|
|
736
|
+
const name: unknown = exception.name;
|
|
737
|
+
if (typeof name === "string" && name.trim() !== "" && name !== "Error") {
|
|
738
|
+
return name;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
const code: unknown = (exception as { code?: unknown }).code;
|
|
742
|
+
if (
|
|
743
|
+
typeof code === "string" &&
|
|
744
|
+
code.trim() !== "" &&
|
|
745
|
+
!NUMERIC_ONLY_REGEX.test(code.trim())
|
|
746
|
+
) {
|
|
747
|
+
return code.trim();
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
const constructorName: unknown = exception.constructor?.name;
|
|
751
|
+
if (
|
|
752
|
+
typeof constructorName === "string" &&
|
|
753
|
+
constructorName.trim() !== "" &&
|
|
754
|
+
constructorName !== "Object"
|
|
755
|
+
) {
|
|
756
|
+
return constructorName;
|
|
757
|
+
}
|
|
758
|
+
} catch {
|
|
759
|
+
// Fall through to the generic name below.
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
return "Error";
|
|
763
|
+
}
|
|
764
|
+
|
|
664
765
|
private static truncate(value: string, maxLength: number): string {
|
|
665
766
|
return value.length > maxLength ? value.substring(0, maxLength) : value;
|
|
666
767
|
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ExpressApplication,
|
|
3
|
+
ExpressRequest,
|
|
4
|
+
ExpressResponse,
|
|
5
|
+
ExpressStatic,
|
|
6
|
+
NextFunction,
|
|
7
|
+
RequestHandler,
|
|
8
|
+
} from "./Express";
|
|
9
|
+
import logger from "./Logger";
|
|
10
|
+
import fs from "fs";
|
|
11
|
+
import path from "path";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Serves the browser libraries our server-rendered pages need, from this
|
|
15
|
+
* install rather than from a public CDN.
|
|
16
|
+
*
|
|
17
|
+
* An air-gapped deployment has no route to cdn.tailwindcss.com or
|
|
18
|
+
* cdnjs.cloudflare.com, and a `<script src>` pointed at one does not fail
|
|
19
|
+
* fast - the browser waits out the connect timeout with the parser blocked,
|
|
20
|
+
* so the page renders unstyled or not at all. Same class of bug as the Monaco
|
|
21
|
+
* loader (see CodeEditor/MonacoLoader.ts): the fix is to stop reaching off-box
|
|
22
|
+
* for anything the page cannot render without.
|
|
23
|
+
*
|
|
24
|
+
* Mounted by every service, because there is no single service that serves
|
|
25
|
+
* every one of these pages: Docs, the API reference and the SSO/on-call
|
|
26
|
+
* message views all come out of the App container, the marketing site out of
|
|
27
|
+
* Home, and nginx sends `/` to whichever of the two the install has (App when
|
|
28
|
+
* billing is off, which is every self-hosted install). A path only one of them
|
|
29
|
+
* answers would 404 for the other.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Absolute, host-relative. Views hard-code this rather than take it as a
|
|
34
|
+
* render variable - the mount point is fixed, and threading a variable through
|
|
35
|
+
* every Response.render call site would be a lot of ceremony for a constant.
|
|
36
|
+
*/
|
|
37
|
+
export const VendorAssetsRoute: string = "/oneuptime-assets";
|
|
38
|
+
|
|
39
|
+
/** Where the committed assets live on disk. */
|
|
40
|
+
export const VendorAssetsPath: string = path.resolve(
|
|
41
|
+
__dirname,
|
|
42
|
+
"..",
|
|
43
|
+
"Static",
|
|
44
|
+
"Vendor",
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Mermaid is a dependency of Common already (Common/UI renders diagrams in
|
|
49
|
+
* markdown with it), so it is on disk in every image and there is no reason to
|
|
50
|
+
* commit a second copy. Resolved rather than hard-coded because npm may hoist
|
|
51
|
+
* it above Common's own node_modules.
|
|
52
|
+
*/
|
|
53
|
+
export function getMermaidDistPath(): string | null {
|
|
54
|
+
try {
|
|
55
|
+
return path.join(
|
|
56
|
+
path.dirname(require.resolve("mermaid/package.json")),
|
|
57
|
+
"dist",
|
|
58
|
+
);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
logger.error(
|
|
61
|
+
"mermaid could not be resolved. Diagrams in docs and blog posts will not render.",
|
|
62
|
+
);
|
|
63
|
+
logger.error(error);
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/*
|
|
69
|
+
* Mermaid's dist directory also carries type definitions, sourcemaps and its
|
|
70
|
+
* own docs. Only the code the browser actually imports gets served - the
|
|
71
|
+
* entrypoint plus the diagram chunks it lazily pulls in.
|
|
72
|
+
*/
|
|
73
|
+
const MERMAID_SERVABLE_EXTENSIONS: Array<string> = [".js", ".mjs"];
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
* A year, for paths that cannot change meaning: tailwind and highlight name
|
|
77
|
+
* their version in the filename, and mermaid's chunks are content-hashed.
|
|
78
|
+
*/
|
|
79
|
+
const IMMUTABLE_CACHE_MAX_AGE_MILLISECONDS: number = 365 * 24 * 60 * 60 * 1000;
|
|
80
|
+
|
|
81
|
+
/*
|
|
82
|
+
* An hour, for mermaid's two entrypoints. Those names are stable while
|
|
83
|
+
* everything they import is content-hashed, so a year-old cached copy of
|
|
84
|
+
* mermaid.esm.min.mjs would go on asking for chunk filenames that a mermaid
|
|
85
|
+
* upgrade has already deleted - and the diagrams would stay broken, for that
|
|
86
|
+
* one visitor, until the cache entry aged out.
|
|
87
|
+
*/
|
|
88
|
+
const REVALIDATE_CACHE_MAX_AGE_MILLISECONDS: number = 60 * 60 * 1000;
|
|
89
|
+
|
|
90
|
+
/* Where mermaid puts the hashed chunks, relative to its own mount. */
|
|
91
|
+
const MERMAID_CHUNK_PREFIX: string = "/chunks/";
|
|
92
|
+
|
|
93
|
+
export type MountVendorAssetsFunction = (app: ExpressApplication) => void;
|
|
94
|
+
|
|
95
|
+
const mountVendorAssets: MountVendorAssetsFunction = (
|
|
96
|
+
app: ExpressApplication,
|
|
97
|
+
): void => {
|
|
98
|
+
if (fs.existsSync(VendorAssetsPath)) {
|
|
99
|
+
app.use(
|
|
100
|
+
VendorAssetsRoute,
|
|
101
|
+
ExpressStatic(VendorAssetsPath, {
|
|
102
|
+
maxAge: IMMUTABLE_CACHE_MAX_AGE_MILLISECONDS,
|
|
103
|
+
index: false,
|
|
104
|
+
redirect: false,
|
|
105
|
+
}) as RequestHandler,
|
|
106
|
+
);
|
|
107
|
+
} else {
|
|
108
|
+
/*
|
|
109
|
+
* Nothing to serve is worth shouting about: every page that references
|
|
110
|
+
* these paths is about to render without its stylesheet.
|
|
111
|
+
*/
|
|
112
|
+
logger.error(
|
|
113
|
+
`Vendored browser assets are missing at ${VendorAssetsPath}. Server-rendered pages will render unstyled.`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const mermaidDistPath: string | null = getMermaidDistPath();
|
|
118
|
+
|
|
119
|
+
if (mermaidDistPath) {
|
|
120
|
+
const serveImmutable: RequestHandler = ExpressStatic(mermaidDistPath, {
|
|
121
|
+
maxAge: IMMUTABLE_CACHE_MAX_AGE_MILLISECONDS,
|
|
122
|
+
index: false,
|
|
123
|
+
redirect: false,
|
|
124
|
+
}) as RequestHandler;
|
|
125
|
+
|
|
126
|
+
const serveRevalidating: RequestHandler = ExpressStatic(mermaidDistPath, {
|
|
127
|
+
maxAge: REVALIDATE_CACHE_MAX_AGE_MILLISECONDS,
|
|
128
|
+
index: false,
|
|
129
|
+
redirect: false,
|
|
130
|
+
}) as RequestHandler;
|
|
131
|
+
|
|
132
|
+
app.use(
|
|
133
|
+
`${VendorAssetsRoute}/mermaid`,
|
|
134
|
+
(req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
135
|
+
const extension: string = path.extname(req.path).toLowerCase();
|
|
136
|
+
|
|
137
|
+
if (!MERMAID_SERVABLE_EXTENSIONS.includes(extension)) {
|
|
138
|
+
return next();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* Express has already stripped the mount path, so this is "/chunks/...". */
|
|
142
|
+
if (req.path.startsWith(MERMAID_CHUNK_PREFIX)) {
|
|
143
|
+
return serveImmutable(req, res, next);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return serveRevalidating(req, res, next);
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/*
|
|
152
|
+
* Terminates the prefix. Registered unconditionally, and last, because the
|
|
153
|
+
* alternative is worse than a 404: express static falls through on a miss,
|
|
154
|
+
* and the next thing to see the request is the frontend catch-all, which
|
|
155
|
+
* answers with the dashboard's index page - HTTP 200, Content-Type
|
|
156
|
+
* text/html. A <script> handed HTML fires onload, not onerror, so the docs'
|
|
157
|
+
* lazy grammar loader would count it as loaded and highlight nothing, with
|
|
158
|
+
* no error anywhere. Better to say plainly that the asset is not here.
|
|
159
|
+
*/
|
|
160
|
+
app.use(VendorAssetsRoute, (_req: ExpressRequest, res: ExpressResponse) => {
|
|
161
|
+
res.status(404).send("Not found");
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export default mountVendorAssets;
|
|
@@ -30,6 +30,7 @@ import CaptureSpan from "../../Telemetry/CaptureSpan";
|
|
|
30
30
|
import BadDataException from "../../../../Types/Exception/BadDataException";
|
|
31
31
|
import ObjectID from "../../../../Types/ObjectID";
|
|
32
32
|
import WorkspaceProjectAuthTokenService from "../../../Services/WorkspaceProjectAuthTokenService";
|
|
33
|
+
import SSRFProtection from "../../SSRFProtection";
|
|
33
34
|
import WorkspaceProjectAuthToken, {
|
|
34
35
|
MicrosoftTeamsChat,
|
|
35
36
|
MicrosoftTeamsChatType,
|
|
@@ -113,6 +114,16 @@ const MICROSOFT_TEAMS_APP_TYPE: string = "SingleTenant";
|
|
|
113
114
|
// Maximum number of pages to fetch when paginating teams
|
|
114
115
|
const MICROSOFT_TEAMS_MAX_PAGES: number = 500;
|
|
115
116
|
|
|
117
|
+
/*
|
|
118
|
+
* Hosts that may receive a Teams incoming webhook. Connector webhooks are
|
|
119
|
+
* issued on outlook.office.com / outlook.office365.com, and per-tenant ones on
|
|
120
|
+
* <tenant>.webhook.office.com — all covered by these two registrable domains.
|
|
121
|
+
*/
|
|
122
|
+
const MICROSOFT_TEAMS_WEBHOOK_DOMAINS: Array<string> = [
|
|
123
|
+
"office.com",
|
|
124
|
+
"office365.com",
|
|
125
|
+
];
|
|
126
|
+
|
|
116
127
|
export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
117
128
|
private static cachedAdapter: CloudAdapter | null = null;
|
|
118
129
|
private static readonly WELCOME_CARD_STATE_KEY: string =
|
|
@@ -700,6 +711,13 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
700
711
|
await API.post({
|
|
701
712
|
url: data.url,
|
|
702
713
|
data: payload,
|
|
714
|
+
options: {
|
|
715
|
+
/*
|
|
716
|
+
* The host is pinned to Microsoft, but do not let a redirect from it
|
|
717
|
+
* bounce this request to an internal address.
|
|
718
|
+
*/
|
|
719
|
+
doNotFollowRedirects: true,
|
|
720
|
+
},
|
|
703
721
|
});
|
|
704
722
|
|
|
705
723
|
if (!apiResult) {
|
|
@@ -730,11 +748,16 @@ export default class MicrosoftTeamsUtil extends WorkspaceBase {
|
|
|
730
748
|
public static isValidMicrosoftTeamsIncomingWebhookUrl(
|
|
731
749
|
incomingWebhookUrl: URL,
|
|
732
750
|
): boolean {
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
751
|
+
/*
|
|
752
|
+
* Pin on the URL's HOST, not on a substring of the whole URL. Subscribers
|
|
753
|
+
* (including unauthenticated ones on a public status page) supply this
|
|
754
|
+
* value and the server POSTs to it, so a substring check was satisfied by
|
|
755
|
+
* an attacker-controlled path or query — `http://169.254.169.254/?x=office.com`
|
|
756
|
+
* passed and turned this into an SSRF into the cloud metadata endpoint.
|
|
757
|
+
*/
|
|
758
|
+
return SSRFProtection.isUrlOnAllowedDomain(
|
|
759
|
+
incomingWebhookUrl,
|
|
760
|
+
MICROSOFT_TEAMS_WEBHOOK_DOMAINS,
|
|
738
761
|
);
|
|
739
762
|
}
|
|
740
763
|
|
|
@@ -2282,6 +2282,11 @@ export default class SlackUtil extends WorkspaceBase {
|
|
|
2282
2282
|
options: {
|
|
2283
2283
|
retries: 3,
|
|
2284
2284
|
exponentialBackoff: true,
|
|
2285
|
+
/*
|
|
2286
|
+
* The host is pinned to hooks.slack.com, but do not let a redirect
|
|
2287
|
+
* from it bounce this request to an internal address.
|
|
2288
|
+
*/
|
|
2289
|
+
doNotFollowRedirects: true,
|
|
2285
2290
|
},
|
|
2286
2291
|
});
|
|
2287
2292
|
|