feat(platform): expose amap server readiness

This commit is contained in:
lingniu
2026-07-04 17:53:03 +08:00
parent aa384732ef
commit eb8f15ca18
10 changed files with 39 additions and 8 deletions

View File

@@ -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)

View File

@@ -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)
}
}

View File

@@ -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"),

View File

@@ -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)
}
}

View File

@@ -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)
}

View File

@@ -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"`

View File

@@ -361,6 +361,7 @@ export interface OpsHealth {
export interface RuntimeInfo {
requestTimeoutMs: number;
amapWebJsConfigured?: boolean;
amapApiConfigured?: boolean;
amapSecurityProxyEnabled?: boolean;
amapSecurityCodeExposed?: boolean;
amapSecurityServiceHost?: string;

View File

@@ -105,6 +105,7 @@ export function OpsQuality() {
<Card bordered title="地图安全配置" loading={loading} style={{ marginTop: 16 }}>
<Space wrap>
<Tag color={runtime?.amapWebJsConfigured ? 'green' : 'orange'}>{runtime?.amapWebJsConfigured ? 'Web JS Key 已配置' : 'Web JS Key 未配置'}</Tag>
<Tag color={runtime?.amapApiConfigured ? 'green' : 'orange'}>{runtime?.amapApiConfigured ? '服务端 API Key 已配置' : '服务端 API Key 未配置'}</Tag>
<Tag color={amapSecurityColor(health)}>{amapSecurityText(health)}</Tag>
<Tag color={runtime?.amapSecurityProxyEnabled ? 'green' : 'grey'}>{runtime?.amapSecurityServiceHost || '代理未启用'}</Tag>
</Space>

View File

@@ -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 || '未启用',

View File

@@ -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();