refactor(gateway): share control-plane identity normalization

This commit is contained in:
Vincent Koc
2026-06-23 00:10:48 +08:00
parent 37ac0f0dd2
commit 7588bd7b75
3 changed files with 17 additions and 23 deletions
+5 -12
View File
@@ -1,5 +1,6 @@
// Gateway control-plane audit helpers.
// Extracts stable actor identity and compact changed-path summaries for audit logs.
import { normalizeControlPlaneIdentityPart } from "./control-plane-identity.js";
import type { GatewayClient } from "./server-methods/types.js";
/** Stable actor fields included in control-plane audit and rate-limit logs. */
@@ -10,21 +11,13 @@ export type ControlPlaneActor = {
connId: string;
};
function normalizePart(value: unknown, fallback: string): string {
if (typeof value !== "string") {
return fallback;
}
const normalized = value.trim();
return normalized.length > 0 ? normalized : fallback;
}
/** Extracts audit identity from a possibly missing or partially connected client. */
export function resolveControlPlaneActor(client: GatewayClient | null): ControlPlaneActor {
return {
actor: normalizePart(client?.connect?.client?.id, "unknown-actor"),
deviceId: normalizePart(client?.connect?.device?.id, "unknown-device"),
clientIp: normalizePart(client?.clientIp, "unknown-ip"),
connId: normalizePart(client?.connId, "unknown-conn"),
actor: normalizeControlPlaneIdentityPart(client?.connect?.client?.id, "unknown-actor"),
deviceId: normalizeControlPlaneIdentityPart(client?.connect?.device?.id, "unknown-device"),
clientIp: normalizeControlPlaneIdentityPart(client?.clientIp, "unknown-ip"),
connId: normalizeControlPlaneIdentityPart(client?.connId, "unknown-conn"),
};
}
+8
View File
@@ -0,0 +1,8 @@
/** Normalizes an optional control-plane identity field without creating empty keys. */
export function normalizeControlPlaneIdentityPart(value: unknown, fallback: string): string {
if (typeof value !== "string") {
return fallback;
}
const normalized = value.trim();
return normalized.length > 0 ? normalized : fallback;
}
+4 -11
View File
@@ -1,5 +1,6 @@
// Control-plane rate limiting bounds write-side RPC attempts per device/IP and
// caps bucket growth against unique-key memory pressure.
import { normalizeControlPlaneIdentityPart } from "./control-plane-identity.js";
import type { GatewayClient } from "./server-methods/types.js";
const CONTROL_PLANE_RATE_LIMIT_MAX_REQUESTS = 3;
@@ -16,21 +17,13 @@ type Bucket = {
const controlPlaneBuckets = new Map<string, Bucket>();
function normalizePart(value: unknown, fallback: string): string {
if (typeof value !== "string") {
return fallback;
}
const normalized = value.trim();
return normalized.length > 0 ? normalized : fallback;
}
/** Builds a stable throttle key while avoiding shared fallback buckets for anonymous clients. */
export function resolveControlPlaneRateLimitKey(client: GatewayClient | null): string {
const deviceId = normalizePart(client?.connect?.device?.id, "unknown-device");
const clientIp = normalizePart(client?.clientIp, "unknown-ip");
const deviceId = normalizeControlPlaneIdentityPart(client?.connect?.device?.id, "unknown-device");
const clientIp = normalizeControlPlaneIdentityPart(client?.clientIp, "unknown-ip");
if (deviceId === "unknown-device" && clientIp === "unknown-ip") {
// Last-resort fallback: avoid cross-client contention when upstream identity is missing.
const connId = normalizePart(client?.connId, "");
const connId = normalizeControlPlaneIdentityPart(client?.connId, "");
if (connId) {
return `${deviceId}|${clientIp}|conn=${connId}`;
}