From 47306699c44229524c2f746674e8d62401dcb622 Mon Sep 17 00:00:00 2001 From: DarkSky Date: Sat, 22 Aug 2026 06:01:41 +0800 Subject: [PATCH] chore(server): fix connection --- .../native/src/runtime/backend_runtime/mod.rs | 2 +- .../src/runtime/backend_runtime/search/mod.rs | 2 + .../backend_runtime/search/provider/remote.rs | 6 ++- packages/backend/native/src/runtime/http.rs | 13 ++++++ packages/backend/native/src/runtime/mod.rs | 2 + .../src/runtime/object_storage/client.rs | 20 +++------ .../native/src/runtime/object_storage/mod.rs | 2 + .../migration.sql | 38 ++++++++++------- ...0-migrate-legacy-context-blob-artifacts.ts | 42 +++++++++++-------- packages/backend/server/src/server.ts | 4 +- 10 files changed, 79 insertions(+), 52 deletions(-) create mode 100644 packages/backend/native/src/runtime/http.rs diff --git a/packages/backend/native/src/runtime/backend_runtime/mod.rs b/packages/backend/native/src/runtime/backend_runtime/mod.rs index 5b7ad24167..7b7491c282 100644 --- a/packages/backend/native/src/runtime/backend_runtime/mod.rs +++ b/packages/backend/native/src/runtime/backend_runtime/mod.rs @@ -41,7 +41,7 @@ pub(crate) use super::types; pub(super) use super::{ BackendRuntimeConfig, ConfigSource, InviteQuotaConfig, RuntimeError, RuntimeResult, migrations::{embedding_schema_health, migrate_all_tables}, - napi_error, to_napi_error, + napi_error, to_napi_error, webpki_tls_config, }; use crate::llm::{ ByokLocalLeaseOutput, ByokPolicyOutput, ByokProbeResultOutput, ByokProfileOutput, CreateByokLocalLeaseInput, diff --git a/packages/backend/native/src/runtime/backend_runtime/search/mod.rs b/packages/backend/native/src/runtime/backend_runtime/search/mod.rs index 8b781605e7..77f0707d2c 100644 --- a/packages/backend/native/src/runtime/backend_runtime/search/mod.rs +++ b/packages/backend/native/src/runtime/backend_runtime/search/mod.rs @@ -11,6 +11,8 @@ mod worker; pub(super) use runtime::SearchRuntime; pub(super) use types::{RuntimeAggregateRequest, RuntimeSearchRequest}; +pub(super) use super::webpki_tls_config; + const SCHEMA_FINGERPRINT: &str = "search-runtime-v5"; fn exact_token(value: &str) -> String { diff --git a/packages/backend/native/src/runtime/backend_runtime/search/provider/remote.rs b/packages/backend/native/src/runtime/backend_runtime/search/provider/remote.rs index 4ca7ae2a5b..1bb19e2f0f 100644 --- a/packages/backend/native/src/runtime/backend_runtime/search/provider/remote.rs +++ b/packages/backend/native/src/runtime/backend_runtime/search/provider/remote.rs @@ -5,7 +5,7 @@ use serde_json::{Value, json}; use sqlx::PgPool; use super::{ - super::{store::SearchChange, types::SearchTable}, + super::{store::SearchChange, types::SearchTable, webpki_tls_config}, manticore::{manticore_exact_tokens, manticore_fields, prepare_manticore_payload, prepare_manticore_search}, }; use crate::runtime::{RuntimeError, RuntimeResult, SearchRuntimeConfig}; @@ -33,6 +33,10 @@ impl RemoteProvider { return Err(RuntimeError::config("invalid search provider endpoint")); } let mut client = Client::builder() + .tls_backend_preconfigured( + webpki_tls_config() + .map_err(|error| RuntimeError::invalid_state(format!("search TLS config failed: {error}")))?, + ) .redirect(Policy::none()) .timeout(Duration::from_secs(30)); if config.provider == "manticoresearch" { diff --git a/packages/backend/native/src/runtime/http.rs b/packages/backend/native/src/runtime/http.rs new file mode 100644 index 0000000000..128f9d2bfc --- /dev/null +++ b/packages/backend/native/src/runtime/http.rs @@ -0,0 +1,13 @@ +use rustls::{ClientConfig, RootCertStore}; + +pub(in crate::runtime) fn webpki_tls_config() -> Result { + let roots = RootCertStore { + roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), + }; + Ok( + ClientConfig::builder_with_provider(rustls::crypto::aws_lc_rs::default_provider().into()) + .with_safe_default_protocol_versions()? + .with_root_certificates(roots) + .with_no_client_auth(), + ) +} diff --git a/packages/backend/native/src/runtime/mod.rs b/packages/backend/native/src/runtime/mod.rs index 0c102ec7e5..4911201dc5 100644 --- a/packages/backend/native/src/runtime/mod.rs +++ b/packages/backend/native/src/runtime/mod.rs @@ -4,6 +4,7 @@ pub mod storage_runtime; pub(crate) mod config; mod config_descriptor; pub(crate) mod error; +mod http; pub(crate) mod migrations; pub(crate) mod object_storage; pub(crate) mod types; @@ -15,3 +16,4 @@ pub(crate) use config::{ use config::{SUPPORTED_BYOK_PROVIDERS, validate_copilot_config}; pub use config_descriptor::{AppConfigDescriptor, app_config_descriptors, validate_app_config_value}; pub(crate) use error::{RuntimeError, RuntimeResult, napi_error, to_napi_error}; +pub(in crate::runtime) use http::webpki_tls_config; diff --git a/packages/backend/native/src/runtime/object_storage/client.rs b/packages/backend/native/src/runtime/object_storage/client.rs index 4d4c693d20..dd242caf00 100644 --- a/packages/backend/native/src/runtime/object_storage/client.rs +++ b/packages/backend/native/src/runtime/object_storage/client.rs @@ -8,7 +8,6 @@ use reqwest::{ Client as ReqwestClient, Method, StatusCode, header::{CONTENT_LENGTH, CONTENT_TYPE, ETAG, HeaderMap, HeaderName, HeaderValue, LAST_MODIFIED}, }; -use rustls::RootCertStore; use rusty_s3::{ Bucket, Credentials, actions::{ @@ -27,6 +26,7 @@ use super::{ ObjectListPage, ObjectMetadata, ObjectPrefix, ObjectPutMetadata, PresignedObjectRequest, completed_multipart_parts, trim_etag, }, + webpki_tls_config, }; const DEFAULT_REQUEST_TIMEOUT_MS: u64 = 30_000; @@ -60,7 +60,10 @@ struct ReqwestStorageHttpClient { impl ReqwestStorageHttpClient { fn new(request_timeout_ms: Option) -> ObjectStorageResult { let builder = ReqwestClient::builder() - .tls_backend_preconfigured(Self::webpki_tls_config()?) + .tls_backend_preconfigured( + webpki_tls_config() + .map_err(|err| ObjectStorageError::Config(format!("ObjectStorage TLS config failed: {err}")))?, + ) .timeout(Duration::from_millis( request_timeout_ms.unwrap_or(DEFAULT_REQUEST_TIMEOUT_MS), )); @@ -69,19 +72,6 @@ impl ReqwestStorageHttpClient { }) } - fn webpki_tls_config() -> ObjectStorageResult { - let roots = RootCertStore { - roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), - }; - Ok( - rustls::ClientConfig::builder_with_provider(rustls::crypto::aws_lc_rs::default_provider().into()) - .with_safe_default_protocol_versions() - .map_err(|err| ObjectStorageError::Config(format!("ObjectStorage TLS config failed: {err}")))? - .with_root_certificates(roots) - .with_no_client_auth(), - ) - } - async fn execute(&self, request: StorageHttpRequest) -> ObjectStorageResult { let mut builder = self.client.request(request.method, request.url); for (key, value) in request.headers { diff --git a/packages/backend/native/src/runtime/object_storage/mod.rs b/packages/backend/native/src/runtime/object_storage/mod.rs index 3c0e0a68f6..e67a16bba6 100644 --- a/packages/backend/native/src/runtime/object_storage/mod.rs +++ b/packages/backend/native/src/runtime/object_storage/mod.rs @@ -14,4 +14,6 @@ pub(in crate::runtime) use backend::{FsStorageConfig, StorageBackendConfig}; pub(in crate::runtime) use config::ObjectStorageConfig; pub(crate) use service::ObjectStorageService; +pub(super) use super::webpki_tls_config; + pub(in crate::runtime) const MAX_BLOB_SIZE: i64 = i32::MAX as i64; diff --git a/packages/backend/server/migrations/20260820120000_cleanup_legacy_copilot_runtime/migration.sql b/packages/backend/server/migrations/20260820120000_cleanup_legacy_copilot_runtime/migration.sql index febc82dba7..980cc76716 100644 --- a/packages/backend/server/migrations/20260820120000_cleanup_legacy_copilot_runtime/migration.sql +++ b/packages/backend/server/migrations/20260820120000_cleanup_legacy_copilot_runtime/migration.sql @@ -4,26 +4,32 @@ DO $$ BEGIN IF to_regclass('public.ai_contexts') IS NOT NULL AND EXISTS ( + WITH referenced_blobs AS ( + SELECT DISTINCT + session.workspace_id, + value #>> '{}' AS blob_key + FROM ai_contexts context + JOIN ai_sessions_metadata session ON session.id = context.session_id + CROSS JOIN LATERAL jsonb_path_query( + context.config::jsonb, + '$.** ? (@.type() == "string")' + ) AS referenced_value(value) + ) SELECT 1 - FROM ai_contexts context - JOIN ai_sessions_metadata session ON session.id = context.session_id + FROM referenced_blobs referenced JOIN blobs blob - ON blob.workspace_id = session.workspace_id + ON blob.workspace_id = referenced.workspace_id + AND blob.key = referenced.blob_key AND blob.deleted_at IS NULL AND blob.status = 'completed' - WHERE jsonb_path_exists( - context.config::jsonb, - '$.** ? (@ == $blobKey)', - jsonb_build_object('blobKey', to_jsonb(blob.key::text)) - ) - AND NOT EXISTS ( - SELECT 1 - FROM workspace_artifacts artifact - WHERE artifact.workspace_id = session.workspace_id - AND artifact.status = 'ready' - AND artifact.storage_scope = 'blob' - AND artifact.storage_key = concat(session.workspace_id, '/', blob.key) - ) + WHERE NOT EXISTS ( + SELECT 1 + FROM workspace_artifacts artifact + WHERE artifact.workspace_id = referenced.workspace_id + AND artifact.status = 'ready' + AND artifact.storage_scope = 'blob' + AND artifact.storage_key = concat(referenced.workspace_id, '/', blob.key) + ) ) THEN RAISE EXCEPTION 'legacy context blob artifact admission is incomplete; run the data migration before cleanup'; diff --git a/packages/backend/server/src/data/migrations/1786820000000-migrate-legacy-context-blob-artifacts.ts b/packages/backend/server/src/data/migrations/1786820000000-migrate-legacy-context-blob-artifacts.ts index c5c1e68528..4ad6afa176 100644 --- a/packages/backend/server/src/data/migrations/1786820000000-migrate-legacy-context-blob-artifacts.ts +++ b/packages/backend/server/src/data/migrations/1786820000000-migrate-legacy-context-blob-artifacts.ts @@ -39,30 +39,36 @@ export class MigrateLegacyContextBlobArtifacts1786820000000 { const runtime = injector.get(BackendRuntimeProvider, { strict: false }); const blobs = await db.$queryRaw` - SELECT DISTINCT - session.workspace_id AS "workspaceId", + WITH referenced_blobs AS ( + SELECT DISTINCT + session.workspace_id, + value #>> '{}' AS blob_key + FROM ai_contexts context + JOIN ai_sessions_metadata session ON session.id = context.session_id + CROSS JOIN LATERAL jsonb_path_query( + context.config::jsonb, + '$.** ? (@.type() == "string")' + ) AS referenced_value(value) + ) + SELECT + referenced.workspace_id AS "workspaceId", blob.key AS "blobId", blob.mime AS "mimeType" - FROM ai_contexts context - JOIN ai_sessions_metadata session ON session.id = context.session_id + FROM referenced_blobs referenced JOIN blobs blob - ON blob.workspace_id = session.workspace_id + ON blob.workspace_id = referenced.workspace_id + AND blob.key = referenced.blob_key AND blob.deleted_at IS NULL AND blob.status = 'completed' - WHERE jsonb_path_exists( - context.config::jsonb, - '$.** ? (@ == $blobKey)', - jsonb_build_object('blobKey', to_jsonb(blob.key::text)) + WHERE NOT EXISTS ( + SELECT 1 + FROM workspace_artifacts artifact + WHERE artifact.workspace_id = referenced.workspace_id + AND artifact.storage_scope = 'blob' + AND artifact.storage_key = concat(referenced.workspace_id, '/', blob.key) + AND artifact.status = 'ready' ) - AND NOT EXISTS ( - SELECT 1 - FROM workspace_artifacts artifact - WHERE artifact.workspace_id = session.workspace_id - AND artifact.storage_scope = 'blob' - AND artifact.storage_key = concat(session.workspace_id, '/', blob.key) - AND artifact.status = 'ready' - ) - ORDER BY session.workspace_id, blob.key + ORDER BY referenced.workspace_id, blob.key `; for (const blob of blobs) { diff --git a/packages/backend/server/src/server.ts b/packages/backend/server/src/server.ts index d574059f47..d34f701fbd 100644 --- a/packages/backend/server/src/server.ts +++ b/packages/backend/server/src/server.ts @@ -42,7 +42,9 @@ export async function run() { const url = app.get(URLHelper); let telemetry: TelemetryService | null = null; try { - telemetry = app.get(TelemetryService, { strict: false }); + if (env.role !== ServerRole.Worker) { + telemetry = app.get(TelemetryService, { strict: false }); + } } catch { telemetry = null; }