refactor(frontend): extract fillStreamDefaults to shared helper

Move the network/security schema-default filler out of inbound-from-db.ts
into stream-defaults.ts so other consumers can reuse it without dragging
in the DBInbound-specific code path.
This commit is contained in:
MHSanaei
2026-05-26 21:30:30 +02:00
parent bb20cf506b
commit 66deec95ae
2 changed files with 71 additions and 69 deletions

View File

@@ -1,20 +1,9 @@
import type { Inbound } from '@/schemas/api/inbound';
import { InboundSettingsSchema } from '@/schemas/protocols/inbound';
import {
GrpcStreamSettingsSchema,
HttpUpgradeStreamSettingsSchema,
HysteriaStreamSettingsSchema,
KcpStreamSettingsSchema,
TcpStreamSettingsSchema,
WsStreamSettingsSchema,
XHttpStreamSettingsSchema,
} from '@/schemas/protocols/stream';
import {
RealityStreamSettingsSchema,
TlsStreamSettingsSchema,
} from '@/schemas/protocols/security';
import { coerceInboundJsonField } from '@/models/dbinbound';
import { fillStreamDefaults } from './stream-defaults';
export interface DbInboundLike {
port: number;
listen: string;
@@ -31,62 +20,6 @@ export interface DbInboundLike {
total?: number;
}
const NETWORK_KEY_MAP = {
tcp: 'tcpSettings',
kcp: 'kcpSettings',
ws: 'wsSettings',
grpc: 'grpcSettings',
httpupgrade: 'httpupgradeSettings',
xhttp: 'xhttpSettings',
hysteria: 'hysteriaSettings',
} as const;
type SchemaWithParse = { safeParse: (v: unknown) => { success: boolean; data?: unknown } };
function parseOrDefault(schema: SchemaWithParse, value: unknown): unknown {
const parsed = schema.safeParse(value ?? {});
if (parsed.success) return parsed.data;
const fallback = schema.safeParse({});
return fallback.success ? fallback.data : value;
}
function networkSchemaFor(network: string): SchemaWithParse | null {
switch (network) {
case 'tcp': return TcpStreamSettingsSchema;
case 'kcp': return KcpStreamSettingsSchema;
case 'ws': return WsStreamSettingsSchema;
case 'grpc': return GrpcStreamSettingsSchema;
case 'httpupgrade': return HttpUpgradeStreamSettingsSchema;
case 'xhttp': return XHttpStreamSettingsSchema;
case 'hysteria': return HysteriaStreamSettingsSchema;
default: return null;
}
}
function securitySchemaFor(security: string): { key: string; schema: SchemaWithParse } | null {
switch (security) {
case 'tls': return { key: 'tlsSettings', schema: TlsStreamSettingsSchema };
case 'reality': return { key: 'realitySettings', schema: RealityStreamSettingsSchema };
default: return null;
}
}
function fillStreamDefaults(stream: Record<string, unknown>): Record<string, unknown> {
const network = (stream.network as string | undefined) ?? 'tcp';
const security = (stream.security as string | undefined) ?? 'none';
const out: Record<string, unknown> = { ...stream, network, security };
const subKey = NETWORK_KEY_MAP[network as keyof typeof NETWORK_KEY_MAP];
const netSchema = networkSchemaFor(network);
if (subKey && netSchema) {
out[subKey] = parseOrDefault(netSchema, out[subKey]);
}
const sec = securitySchemaFor(security);
if (sec) {
out[sec.key] = parseOrDefault(sec.schema, out[sec.key]);
}
return out;
}
function fillProtocolSettingsDefaults(protocol: string, settings: Record<string, unknown>): Record<string, unknown> {
const parsed = InboundSettingsSchema.safeParse({ protocol, settings });
if (parsed.success) {

View File

@@ -0,0 +1,69 @@
import {
GrpcStreamSettingsSchema,
HttpUpgradeStreamSettingsSchema,
HysteriaStreamSettingsSchema,
KcpStreamSettingsSchema,
TcpStreamSettingsSchema,
WsStreamSettingsSchema,
XHttpStreamSettingsSchema,
} from '@/schemas/protocols/stream';
import {
RealityStreamSettingsSchema,
TlsStreamSettingsSchema,
} from '@/schemas/protocols/security';
const NETWORK_KEY_MAP = {
tcp: 'tcpSettings',
kcp: 'kcpSettings',
ws: 'wsSettings',
grpc: 'grpcSettings',
httpupgrade: 'httpupgradeSettings',
xhttp: 'xhttpSettings',
hysteria: 'hysteriaSettings',
} as const;
type SchemaWithParse = { safeParse: (v: unknown) => { success: boolean; data?: unknown } };
function parseOrDefault(schema: SchemaWithParse, value: unknown): unknown {
const parsed = schema.safeParse(value ?? {});
if (parsed.success) return parsed.data;
const fallback = schema.safeParse({});
return fallback.success ? fallback.data : value;
}
function networkSchemaFor(network: string): SchemaWithParse | null {
switch (network) {
case 'tcp': return TcpStreamSettingsSchema;
case 'kcp': return KcpStreamSettingsSchema;
case 'ws': return WsStreamSettingsSchema;
case 'grpc': return GrpcStreamSettingsSchema;
case 'httpupgrade': return HttpUpgradeStreamSettingsSchema;
case 'xhttp': return XHttpStreamSettingsSchema;
case 'hysteria': return HysteriaStreamSettingsSchema;
default: return null;
}
}
function securitySchemaFor(security: string): { key: string; schema: SchemaWithParse } | null {
switch (security) {
case 'tls': return { key: 'tlsSettings', schema: TlsStreamSettingsSchema };
case 'reality': return { key: 'realitySettings', schema: RealityStreamSettingsSchema };
default: return null;
}
}
export function fillStreamDefaults(stream: Record<string, unknown>): Record<string, unknown> {
const network = (stream.network as string | undefined) ?? 'tcp';
const security = (stream.security as string | undefined) ?? 'none';
const out: Record<string, unknown> = { ...stream, network, security };
const subKey = NETWORK_KEY_MAP[network as keyof typeof NETWORK_KEY_MAP];
const netSchema = networkSchemaFor(network);
if (subKey && netSchema) {
out[subKey] = parseOrDefault(netSchema, out[subKey]);
}
const sec = securitySchemaFor(security);
if (sec) {
out[sec.key] = parseOrDefault(sec.schema, out[sec.key]);
}
return out;
}