From 23f18963ef8c9331d88174b2216d16a85f35f364 Mon Sep 17 00:00:00 2001 From: chick Date: Sun, 19 Jul 2026 00:22:23 +0800 Subject: [PATCH] feat(fleet): show instance resource metrics --- .../connections/upstream-session-client.ts | 2 +- .../instance-resource-service.test.ts | 48 ++++++++++ .../resources/instance-resource-service.ts | 93 +++++++++++++++++++ apps/api/src/control-plane.ts | 7 ++ .../transport/safe-instance-transport.ts | 7 +- .../transport/safe-upstream-gateway.ts | 17 +++- .../api/src/interface/http/instance-routes.ts | 9 ++ apps/web/src/app-shell.integration.test.tsx | 10 ++ .../src/fleet/fleet-api-data-source.test.ts | 53 +++++++---- apps/web/src/fleet/fleet-api-data-source.ts | 62 +++++++++++-- apps/web/src/fleet/fleet-page.tsx | 23 +++++ apps/web/src/fleet/fleet-table-view-model.ts | 11 ++- 12 files changed, 312 insertions(+), 30 deletions(-) create mode 100644 apps/api/src/application/resources/instance-resource-service.test.ts create mode 100644 apps/api/src/application/resources/instance-resource-service.ts diff --git a/apps/api/src/application/connections/upstream-session-client.ts b/apps/api/src/application/connections/upstream-session-client.ts index 6d9bd82..396fdd1 100644 --- a/apps/api/src/application/connections/upstream-session-client.ts +++ b/apps/api/src/application/connections/upstream-session-client.ts @@ -1,6 +1,6 @@ export interface UpstreamRequest { readonly url: string; - readonly method: 'POST'; + readonly method: 'GET' | 'POST'; readonly headers: Readonly>; /** One-shot secret. Request implementations must not log or persist this field. */ readonly secret?: string; diff --git a/apps/api/src/application/resources/instance-resource-service.test.ts b/apps/api/src/application/resources/instance-resource-service.test.ts new file mode 100644 index 0000000..6c316c8 --- /dev/null +++ b/apps/api/src/application/resources/instance-resource-service.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from 'vitest'; + +import { parseSim, parseStats } from './instance-resource-service.js'; + +const response = (body: unknown) => ({ + status: 200, + headers: {}, + body: JSON.stringify(body), +}); + +describe('instance resource allowlist parsing', () => { + it('extracts CPU, memory and only the highest valid temperature', () => { + expect( + parseStats( + response({ + data: { + cpu_load: { load_percent: 23.4, secret: 'drop' }, + memory: { used_percent: 67.8, total_bytes: 123 }, + temperature: [ + { label: 'a', temperature: 41.2 }, + { label: 'b', temperature: 52.6 }, + { label: 'bad', temperature: 900 }, + ], + }, + }), + ), + ).toEqual({ cpuPercent: 23.4, memoryPercent: 67.8, maxTemperatureCelsius: 52.6 }); + }); + + it('extracts, validates, deduplicates and bounds phone numbers only', () => { + expect( + parseSim( + response({ + data: { + phone_numbers: ['+86 138-0000-0000', '+86 138-0000-0000', 'bad