feat(platform): expose amap server readiness
This commit is contained in:
@@ -52,12 +52,13 @@ func NewServer(cfg config.Config) http.Handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
api := platform.NewHandler(platform.NewServiceWithRuntime(store, platform.RuntimeInfo{
|
api := platform.NewHandler(platform.NewServiceWithRuntime(store, platform.RuntimeInfo{
|
||||||
RequestTimeoutMs: int(cfg.RequestTimeout / time.Millisecond),
|
RequestTimeoutMs: int(cfg.RequestTimeout / time.Millisecond),
|
||||||
AMapWebJSConfigured: strings.TrimSpace(cfg.AMapWebJSKey) != "",
|
AMapWebJSConfigured: strings.TrimSpace(cfg.AMapWebJSKey) != "",
|
||||||
AMapSecurityProxyEnabled: strings.TrimSpace(cfg.AMapSecurityCode) != "" && strings.TrimSpace(cfg.AMapServiceHost) != "",
|
AMapAPIConfigured: strings.TrimSpace(cfg.AMapAPIKey) != "",
|
||||||
AMapSecurityCodeExposed: exposedAMapSecurityCode(cfg) != "",
|
AMapSecurityProxyEnabled: strings.TrimSpace(cfg.AMapSecurityCode) != "" && strings.TrimSpace(cfg.AMapServiceHost) != "",
|
||||||
AMapSecurityServiceHost: strings.TrimSpace(cfg.AMapServiceHost),
|
AMapSecurityCodeExposed: exposedAMapSecurityCode(cfg) != "",
|
||||||
PlatformRelease: strings.TrimSpace(cfg.PlatformRelease),
|
AMapSecurityServiceHost: strings.TrimSpace(cfg.AMapServiceHost),
|
||||||
|
PlatformRelease: strings.TrimSpace(cfg.PlatformRelease),
|
||||||
}))
|
}))
|
||||||
handler := static.Handler(cfg.StaticDir, api)
|
handler := static.Handler(cfg.StaticDir, api)
|
||||||
handler = withAppConfig(handler, cfg)
|
handler = withAppConfig(handler, cfg)
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ func TestWithRequestTimeoutReturnsEnvelopeWithTraceID(t *testing.T) {
|
|||||||
func TestAppConfigScriptIsRuntimeRendered(t *testing.T) {
|
func TestAppConfigScriptIsRuntimeRendered(t *testing.T) {
|
||||||
handler := withAppConfig(http.NotFoundHandler(), config.Config{
|
handler := withAppConfig(http.NotFoundHandler(), config.Config{
|
||||||
AMapWebJSKey: "web-key",
|
AMapWebJSKey: "web-key",
|
||||||
|
AMapAPIKey: "server-api-key",
|
||||||
AMapSecurityCode: "security-code",
|
AMapSecurityCode: "security-code",
|
||||||
AMapServiceHost: "/_AMapService",
|
AMapServiceHost: "/_AMapService",
|
||||||
})
|
})
|
||||||
@@ -82,8 +83,8 @@ func TestAppConfigScriptIsRuntimeRendered(t *testing.T) {
|
|||||||
t.Fatalf("app config script missing %q: %s", want, body)
|
t.Fatalf("app config script missing %q: %s", want, body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.Contains(body, "security-code") || strings.Contains(body, "amapSecurityJsCode") {
|
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 AMap security code: %s", body)
|
t.Fatalf("app config script should not expose server-side AMap secrets: %s", body)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ type Config struct {
|
|||||||
AuthToken string
|
AuthToken string
|
||||||
RequestTimeout time.Duration
|
RequestTimeout time.Duration
|
||||||
AMapWebJSKey string
|
AMapWebJSKey string
|
||||||
|
AMapAPIKey string
|
||||||
AMapSecurityCode string
|
AMapSecurityCode string
|
||||||
AMapServiceHost string
|
AMapServiceHost string
|
||||||
PlatformRelease string
|
PlatformRelease string
|
||||||
@@ -42,6 +43,7 @@ func Load() Config {
|
|||||||
AuthToken: os.Getenv("AUTH_TOKEN"),
|
AuthToken: os.Getenv("AUTH_TOKEN"),
|
||||||
RequestTimeout: time.Duration(envInt("REQUEST_TIMEOUT_MS", 5000)) * time.Millisecond,
|
RequestTimeout: time.Duration(envInt("REQUEST_TIMEOUT_MS", 5000)) * time.Millisecond,
|
||||||
AMapWebJSKey: os.Getenv("AMAP_WEB_JS_KEY"),
|
AMapWebJSKey: os.Getenv("AMAP_WEB_JS_KEY"),
|
||||||
|
AMapAPIKey: os.Getenv("AMAP_API_KEY"),
|
||||||
AMapSecurityCode: os.Getenv("AMAP_SECURITY_JS_CODE"),
|
AMapSecurityCode: os.Getenv("AMAP_SECURITY_JS_CODE"),
|
||||||
AMapServiceHost: os.Getenv("AMAP_SECURITY_SERVICE_HOST"),
|
AMapServiceHost: os.Getenv("AMAP_SECURITY_SERVICE_HOST"),
|
||||||
PlatformRelease: os.Getenv("PLATFORM_RELEASE"),
|
PlatformRelease: os.Getenv("PLATFORM_RELEASE"),
|
||||||
|
|||||||
@@ -24,3 +24,13 @@ func TestLoadReadsPlatformRelease(t *testing.T) {
|
|||||||
t.Fatalf("PlatformRelease = %q", cfg.PlatformRelease)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -898,6 +898,7 @@ func TestHandlerOpsHealthIncludesVehicleServiceRuntime(t *testing.T) {
|
|||||||
handler := NewHandler(NewServiceWithRuntime(NewMockStore(), RuntimeInfo{
|
handler := NewHandler(NewServiceWithRuntime(NewMockStore(), RuntimeInfo{
|
||||||
RequestTimeoutMs: 1500,
|
RequestTimeoutMs: 1500,
|
||||||
AMapWebJSConfigured: true,
|
AMapWebJSConfigured: true,
|
||||||
|
AMapAPIConfigured: true,
|
||||||
AMapSecurityProxyEnabled: true,
|
AMapSecurityProxyEnabled: true,
|
||||||
AMapSecurityCodeExposed: false,
|
AMapSecurityCodeExposed: false,
|
||||||
AMapSecurityServiceHost: "/_AMapService",
|
AMapSecurityServiceHost: "/_AMapService",
|
||||||
@@ -921,6 +922,9 @@ func TestHandlerOpsHealthIncludesVehicleServiceRuntime(t *testing.T) {
|
|||||||
if !body.Data.Runtime.AMapWebJSConfigured || !body.Data.Runtime.AMapSecurityProxyEnabled {
|
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())
|
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 {
|
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)
|
t.Fatalf("ops health should show that AMap security code is not exposed when proxy is enabled: %+v", body.Data.Runtime)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -372,6 +372,7 @@ type OpsHealth struct {
|
|||||||
type RuntimeInfo struct {
|
type RuntimeInfo struct {
|
||||||
RequestTimeoutMs int `json:"requestTimeoutMs"`
|
RequestTimeoutMs int `json:"requestTimeoutMs"`
|
||||||
AMapWebJSConfigured bool `json:"amapWebJsConfigured"`
|
AMapWebJSConfigured bool `json:"amapWebJsConfigured"`
|
||||||
|
AMapAPIConfigured bool `json:"amapApiConfigured"`
|
||||||
AMapSecurityProxyEnabled bool `json:"amapSecurityProxyEnabled"`
|
AMapSecurityProxyEnabled bool `json:"amapSecurityProxyEnabled"`
|
||||||
AMapSecurityCodeExposed bool `json:"amapSecurityCodeExposed"`
|
AMapSecurityCodeExposed bool `json:"amapSecurityCodeExposed"`
|
||||||
AMapSecurityServiceHost string `json:"amapSecurityServiceHost"`
|
AMapSecurityServiceHost string `json:"amapSecurityServiceHost"`
|
||||||
|
|||||||
@@ -361,6 +361,7 @@ export interface OpsHealth {
|
|||||||
export interface RuntimeInfo {
|
export interface RuntimeInfo {
|
||||||
requestTimeoutMs: number;
|
requestTimeoutMs: number;
|
||||||
amapWebJsConfigured?: boolean;
|
amapWebJsConfigured?: boolean;
|
||||||
|
amapApiConfigured?: boolean;
|
||||||
amapSecurityProxyEnabled?: boolean;
|
amapSecurityProxyEnabled?: boolean;
|
||||||
amapSecurityCodeExposed?: boolean;
|
amapSecurityCodeExposed?: boolean;
|
||||||
amapSecurityServiceHost?: string;
|
amapSecurityServiceHost?: string;
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ export function OpsQuality() {
|
|||||||
<Card bordered title="地图安全配置" loading={loading} style={{ marginTop: 16 }}>
|
<Card bordered title="地图安全配置" loading={loading} style={{ marginTop: 16 }}>
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
<Tag color={runtime?.amapWebJsConfigured ? 'green' : 'orange'}>{runtime?.amapWebJsConfigured ? 'Web JS Key 已配置' : 'Web JS Key 未配置'}</Tag>
|
<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={amapSecurityColor(health)}>{amapSecurityText(health)}</Tag>
|
||||||
<Tag color={runtime?.amapSecurityProxyEnabled ? 'green' : 'grey'}>{runtime?.amapSecurityServiceHost || '代理未启用'}</Tag>
|
<Tag color={runtime?.amapSecurityProxyEnabled ? 'green' : 'grey'}>{runtime?.amapSecurityServiceHost || '代理未启用'}</Tag>
|
||||||
</Space>
|
</Space>
|
||||||
|
|||||||
@@ -368,6 +368,12 @@ export function Realtime({
|
|||||||
color: amapConfigured ? 'green' as const : 'orange' as const,
|
color: amapConfigured ? 'green' as const : 'orange' as const,
|
||||||
detail: amapConfigured ? '前端可加载高德 Web JS API。' : '缺少公开 Key,地图将使用坐标预览。'
|
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: '安全代理',
|
label: '安全代理',
|
||||||
value: amapSecurityServiceHost || '未启用',
|
value: amapSecurityServiceHost || '未启用',
|
||||||
|
|||||||
@@ -3351,6 +3351,7 @@ test('renders ops quality as a standalone runtime health page', async () => {
|
|||||||
requestTimeoutMs: 5000,
|
requestTimeoutMs: 5000,
|
||||||
platformRelease: 'platform-ops-test',
|
platformRelease: 'platform-ops-test',
|
||||||
amapWebJsConfigured: true,
|
amapWebJsConfigured: true,
|
||||||
|
amapApiConfigured: true,
|
||||||
amapSecurityProxyEnabled: true,
|
amapSecurityProxyEnabled: true,
|
||||||
amapSecurityCodeExposed: false,
|
amapSecurityCodeExposed: false,
|
||||||
amapSecurityServiceHost: '/_AMapService'
|
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('368')).toBeInTheDocument();
|
||||||
expect(screen.getByText('TDengine 写入')).toBeInTheDocument();
|
expect(screen.getByText('TDengine 写入')).toBeInTheDocument();
|
||||||
expect(screen.getByText('MySQL 写入')).toBeInTheDocument();
|
expect(screen.getByText('MySQL 写入')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('服务端 API Key 已配置')).toBeInTheDocument();
|
||||||
expect(screen.getByText('/_AMapService')).toBeInTheDocument();
|
expect(screen.getByText('/_AMapService')).toBeInTheDocument();
|
||||||
expect(screen.getByText('安全码未暴露')).toBeInTheDocument();
|
expect(screen.getByText('安全码未暴露')).toBeInTheDocument();
|
||||||
expect(screen.getByText('gb32960-gateway')).toBeInTheDocument();
|
expect(screen.getByText('gb32960-gateway')).toBeInTheDocument();
|
||||||
@@ -8133,6 +8135,7 @@ test('shows production AMap integration status on realtime page', async () => {
|
|||||||
runtime: {
|
runtime: {
|
||||||
requestTimeoutMs: 5000,
|
requestTimeoutMs: 5000,
|
||||||
amapWebJsConfigured: true,
|
amapWebJsConfigured: true,
|
||||||
|
amapApiConfigured: true,
|
||||||
amapSecurityProxyEnabled: true,
|
amapSecurityProxyEnabled: true,
|
||||||
amapSecurityCodeExposed: false,
|
amapSecurityCodeExposed: false,
|
||||||
amapSecurityServiceHost: '/_AMapService',
|
amapSecurityServiceHost: '/_AMapService',
|
||||||
@@ -8214,6 +8217,7 @@ test('shows production AMap integration status on realtime page', async () => {
|
|||||||
|
|
||||||
expect(await screen.findByText('地图接入状态')).toBeInTheDocument();
|
expect(await screen.findByText('地图接入状态')).toBeInTheDocument();
|
||||||
expect(screen.getByText('Web JS Key')).toBeInTheDocument();
|
expect(screen.getByText('Web JS Key')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('服务端 API Key')).toBeInTheDocument();
|
||||||
expect(screen.getAllByText('已配置').length).toBeGreaterThan(0);
|
expect(screen.getAllByText('已配置').length).toBeGreaterThan(0);
|
||||||
expect(screen.getByText('安全代理')).toBeInTheDocument();
|
expect(screen.getByText('安全代理')).toBeInTheDocument();
|
||||||
expect(screen.getByText('/_AMapService')).toBeInTheDocument();
|
expect(screen.getByText('/_AMapService')).toBeInTheDocument();
|
||||||
|
|||||||
Reference in New Issue
Block a user