From eb8f15ca18a02185736872084428f3ed148ee83b Mon Sep 17 00:00:00 2001 From: lingniu Date: Sat, 4 Jul 2026 17:53:03 +0800 Subject: [PATCH] feat(platform): expose amap server readiness --- .../apps/api/internal/app/server.go | 13 +++++++------ .../apps/api/internal/app/server_test.go | 5 +++-- .../apps/api/internal/config/config.go | 2 ++ .../apps/api/internal/config/config_test.go | 10 ++++++++++ .../apps/api/internal/platform/handler_test.go | 4 ++++ .../apps/api/internal/platform/model.go | 1 + vehicle-data-platform/apps/web/src/api/types.ts | 1 + .../apps/web/src/pages/OpsQuality.tsx | 1 + .../apps/web/src/pages/Realtime.tsx | 6 ++++++ .../apps/web/src/test/App.test.tsx | 4 ++++ 10 files changed, 39 insertions(+), 8 deletions(-) diff --git a/vehicle-data-platform/apps/api/internal/app/server.go b/vehicle-data-platform/apps/api/internal/app/server.go index 43f6dddd..57224d21 100644 --- a/vehicle-data-platform/apps/api/internal/app/server.go +++ b/vehicle-data-platform/apps/api/internal/app/server.go @@ -52,12 +52,13 @@ func NewServer(cfg config.Config) http.Handler { } } api := platform.NewHandler(platform.NewServiceWithRuntime(store, platform.RuntimeInfo{ - RequestTimeoutMs: int(cfg.RequestTimeout / time.Millisecond), - AMapWebJSConfigured: strings.TrimSpace(cfg.AMapWebJSKey) != "", - AMapSecurityProxyEnabled: strings.TrimSpace(cfg.AMapSecurityCode) != "" && strings.TrimSpace(cfg.AMapServiceHost) != "", - AMapSecurityCodeExposed: exposedAMapSecurityCode(cfg) != "", - AMapSecurityServiceHost: strings.TrimSpace(cfg.AMapServiceHost), - PlatformRelease: strings.TrimSpace(cfg.PlatformRelease), + RequestTimeoutMs: int(cfg.RequestTimeout / time.Millisecond), + AMapWebJSConfigured: strings.TrimSpace(cfg.AMapWebJSKey) != "", + AMapAPIConfigured: strings.TrimSpace(cfg.AMapAPIKey) != "", + AMapSecurityProxyEnabled: strings.TrimSpace(cfg.AMapSecurityCode) != "" && strings.TrimSpace(cfg.AMapServiceHost) != "", + AMapSecurityCodeExposed: exposedAMapSecurityCode(cfg) != "", + AMapSecurityServiceHost: strings.TrimSpace(cfg.AMapServiceHost), + PlatformRelease: strings.TrimSpace(cfg.PlatformRelease), })) handler := static.Handler(cfg.StaticDir, api) handler = withAppConfig(handler, cfg) diff --git a/vehicle-data-platform/apps/api/internal/app/server_test.go b/vehicle-data-platform/apps/api/internal/app/server_test.go index 39042922..abd3fb93 100644 --- a/vehicle-data-platform/apps/api/internal/app/server_test.go +++ b/vehicle-data-platform/apps/api/internal/app/server_test.go @@ -62,6 +62,7 @@ func TestWithRequestTimeoutReturnsEnvelopeWithTraceID(t *testing.T) { func TestAppConfigScriptIsRuntimeRendered(t *testing.T) { handler := withAppConfig(http.NotFoundHandler(), config.Config{ AMapWebJSKey: "web-key", + AMapAPIKey: "server-api-key", AMapSecurityCode: "security-code", AMapServiceHost: "/_AMapService", }) @@ -82,8 +83,8 @@ func TestAppConfigScriptIsRuntimeRendered(t *testing.T) { t.Fatalf("app config script missing %q: %s", want, body) } } - if strings.Contains(body, "security-code") || strings.Contains(body, "amapSecurityJsCode") { - t.Fatalf("app config script should not expose AMap security code: %s", body) + if strings.Contains(body, "security-code") || strings.Contains(body, "server-api-key") || strings.Contains(body, "amapSecurityJsCode") || strings.Contains(body, "amapApiKey") { + t.Fatalf("app config script should not expose server-side AMap secrets: %s", body) } } diff --git a/vehicle-data-platform/apps/api/internal/config/config.go b/vehicle-data-platform/apps/api/internal/config/config.go index 99aadfde..b385f453 100644 --- a/vehicle-data-platform/apps/api/internal/config/config.go +++ b/vehicle-data-platform/apps/api/internal/config/config.go @@ -21,6 +21,7 @@ type Config struct { AuthToken string RequestTimeout time.Duration AMapWebJSKey string + AMapAPIKey string AMapSecurityCode string AMapServiceHost string PlatformRelease string @@ -42,6 +43,7 @@ func Load() Config { AuthToken: os.Getenv("AUTH_TOKEN"), RequestTimeout: time.Duration(envInt("REQUEST_TIMEOUT_MS", 5000)) * time.Millisecond, AMapWebJSKey: os.Getenv("AMAP_WEB_JS_KEY"), + AMapAPIKey: os.Getenv("AMAP_API_KEY"), AMapSecurityCode: os.Getenv("AMAP_SECURITY_JS_CODE"), AMapServiceHost: os.Getenv("AMAP_SECURITY_SERVICE_HOST"), PlatformRelease: os.Getenv("PLATFORM_RELEASE"), diff --git a/vehicle-data-platform/apps/api/internal/config/config_test.go b/vehicle-data-platform/apps/api/internal/config/config_test.go index 070aabaf..db03fcad 100644 --- a/vehicle-data-platform/apps/api/internal/config/config_test.go +++ b/vehicle-data-platform/apps/api/internal/config/config_test.go @@ -24,3 +24,13 @@ func TestLoadReadsPlatformRelease(t *testing.T) { t.Fatalf("PlatformRelease = %q", cfg.PlatformRelease) } } + +func TestLoadReadsAMapServerAPIKey(t *testing.T) { + t.Setenv("AMAP_API_KEY", "server-map-key") + + cfg := Load() + + if cfg.AMapAPIKey != "server-map-key" { + t.Fatalf("AMapAPIKey = %q", cfg.AMapAPIKey) + } +} diff --git a/vehicle-data-platform/apps/api/internal/platform/handler_test.go b/vehicle-data-platform/apps/api/internal/platform/handler_test.go index 5685c87a..13bc8d54 100644 --- a/vehicle-data-platform/apps/api/internal/platform/handler_test.go +++ b/vehicle-data-platform/apps/api/internal/platform/handler_test.go @@ -898,6 +898,7 @@ func TestHandlerOpsHealthIncludesVehicleServiceRuntime(t *testing.T) { handler := NewHandler(NewServiceWithRuntime(NewMockStore(), RuntimeInfo{ RequestTimeoutMs: 1500, AMapWebJSConfigured: true, + AMapAPIConfigured: true, AMapSecurityProxyEnabled: true, AMapSecurityCodeExposed: false, AMapSecurityServiceHost: "/_AMapService", @@ -921,6 +922,9 @@ func TestHandlerOpsHealthIncludesVehicleServiceRuntime(t *testing.T) { if !body.Data.Runtime.AMapWebJSConfigured || !body.Data.Runtime.AMapSecurityProxyEnabled { t.Fatalf("ops health should expose AMap runtime readiness, got %+v body=%s", body.Data.Runtime, rec.Body.String()) } + if !body.Data.Runtime.AMapAPIConfigured { + t.Fatalf("ops health should expose server-side AMap API readiness, got %+v body=%s", body.Data.Runtime, rec.Body.String()) + } if body.Data.Runtime.AMapSecurityCodeExposed { t.Fatalf("ops health should show that AMap security code is not exposed when proxy is enabled: %+v", body.Data.Runtime) } diff --git a/vehicle-data-platform/apps/api/internal/platform/model.go b/vehicle-data-platform/apps/api/internal/platform/model.go index 11fa52aa..fcf1704b 100644 --- a/vehicle-data-platform/apps/api/internal/platform/model.go +++ b/vehicle-data-platform/apps/api/internal/platform/model.go @@ -372,6 +372,7 @@ type OpsHealth struct { type RuntimeInfo struct { RequestTimeoutMs int `json:"requestTimeoutMs"` AMapWebJSConfigured bool `json:"amapWebJsConfigured"` + AMapAPIConfigured bool `json:"amapApiConfigured"` AMapSecurityProxyEnabled bool `json:"amapSecurityProxyEnabled"` AMapSecurityCodeExposed bool `json:"amapSecurityCodeExposed"` AMapSecurityServiceHost string `json:"amapSecurityServiceHost"` diff --git a/vehicle-data-platform/apps/web/src/api/types.ts b/vehicle-data-platform/apps/web/src/api/types.ts index ff0840c9..4835674f 100644 --- a/vehicle-data-platform/apps/web/src/api/types.ts +++ b/vehicle-data-platform/apps/web/src/api/types.ts @@ -361,6 +361,7 @@ export interface OpsHealth { export interface RuntimeInfo { requestTimeoutMs: number; amapWebJsConfigured?: boolean; + amapApiConfigured?: boolean; amapSecurityProxyEnabled?: boolean; amapSecurityCodeExposed?: boolean; amapSecurityServiceHost?: string; diff --git a/vehicle-data-platform/apps/web/src/pages/OpsQuality.tsx b/vehicle-data-platform/apps/web/src/pages/OpsQuality.tsx index 90e30e69..e1119607 100644 --- a/vehicle-data-platform/apps/web/src/pages/OpsQuality.tsx +++ b/vehicle-data-platform/apps/web/src/pages/OpsQuality.tsx @@ -105,6 +105,7 @@ export function OpsQuality() { {runtime?.amapWebJsConfigured ? 'Web JS Key 已配置' : 'Web JS Key 未配置'} + {runtime?.amapApiConfigured ? '服务端 API Key 已配置' : '服务端 API Key 未配置'} {amapSecurityText(health)} {runtime?.amapSecurityServiceHost || '代理未启用'} diff --git a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx index 9f290a1d..adb54e32 100644 --- a/vehicle-data-platform/apps/web/src/pages/Realtime.tsx +++ b/vehicle-data-platform/apps/web/src/pages/Realtime.tsx @@ -368,6 +368,12 @@ export function Realtime({ color: amapConfigured ? 'green' as const : 'orange' as const, detail: amapConfigured ? '前端可加载高德 Web JS API。' : '缺少公开 Key,地图将使用坐标预览。' }, + { + label: '服务端 API Key', + value: runtime?.amapApiConfigured ? '已配置' : '未配置', + color: runtime?.amapApiConfigured ? 'green' as const : 'orange' as const, + detail: runtime?.amapApiConfigured ? '后端可支持地理编码、路线、围栏等服务端地图能力。' : '服务端地图能力未配置,后续地理服务会降级。' + }, { label: '安全代理', value: amapSecurityServiceHost || '未启用', diff --git a/vehicle-data-platform/apps/web/src/test/App.test.tsx b/vehicle-data-platform/apps/web/src/test/App.test.tsx index 54ff94f4..8ea51311 100644 --- a/vehicle-data-platform/apps/web/src/test/App.test.tsx +++ b/vehicle-data-platform/apps/web/src/test/App.test.tsx @@ -3351,6 +3351,7 @@ test('renders ops quality as a standalone runtime health page', async () => { requestTimeoutMs: 5000, platformRelease: 'platform-ops-test', amapWebJsConfigured: true, + amapApiConfigured: true, amapSecurityProxyEnabled: true, amapSecurityCodeExposed: false, amapSecurityServiceHost: '/_AMapService' @@ -3380,6 +3381,7 @@ test('renders ops quality as a standalone runtime health page', async () => { expect(screen.getByText('368')).toBeInTheDocument(); expect(screen.getByText('TDengine 写入')).toBeInTheDocument(); expect(screen.getByText('MySQL 写入')).toBeInTheDocument(); + expect(screen.getByText('服务端 API Key 已配置')).toBeInTheDocument(); expect(screen.getByText('/_AMapService')).toBeInTheDocument(); expect(screen.getByText('安全码未暴露')).toBeInTheDocument(); expect(screen.getByText('gb32960-gateway')).toBeInTheDocument(); @@ -8133,6 +8135,7 @@ test('shows production AMap integration status on realtime page', async () => { runtime: { requestTimeoutMs: 5000, amapWebJsConfigured: true, + amapApiConfigured: true, amapSecurityProxyEnabled: true, amapSecurityCodeExposed: false, amapSecurityServiceHost: '/_AMapService', @@ -8214,6 +8217,7 @@ test('shows production AMap integration status on realtime page', async () => { expect(await screen.findByText('地图接入状态')).toBeInTheDocument(); expect(screen.getByText('Web JS Key')).toBeInTheDocument(); + expect(screen.getByText('服务端 API Key')).toBeInTheDocument(); expect(screen.getAllByText('已配置').length).toBeGreaterThan(0); expect(screen.getByText('安全代理')).toBeInTheDocument(); expect(screen.getByText('/_AMapService')).toBeInTheDocument();