Files
3x-ui/frontend/src/schemas/protocols/stream/external-proxy.ts
MHSanaei a8d5d0dfab fix(external-proxy): relabel "Host" as "Address", add per-entry ECH (#4935)
The external proxy "Host" field was bound to dest (the connection address that becomes the link host) but labeled "Host", misleading users into thinking it set a transport host header. Relabel it to "Address" to match what it actually controls.

Add per-entry ECH (echConfigList) to the external proxy schema, form (shown under Force TLS = TLS), the TS link generator, and the Go sub services: ech is emitted on share links and vmess objects, and written into the stream so the JSON subscription picks it up via the existing tlsData reader.
2026-06-05 10:40:11 +02:00

29 lines
1.1 KiB
TypeScript

import { z } from 'zod';
import { PortSchema } from '@/schemas/primitives';
import { AlpnSchema, UtlsFingerprintSchema } from '@/schemas/protocols/security/tls';
export const ExternalProxyForceTlsSchema = z.enum(['same', 'tls', 'none']);
export type ExternalProxyForceTls = z.infer<typeof ExternalProxyForceTlsSchema>;
// An inbound can advertise external proxy fronts (CDN edges, mirror nodes)
// that share its config but vary the dest+port+SNI for the share link. The
// panel form ships rows of this shape; link generators iterate them when
// stream.externalProxy is non-empty.
export const ExternalProxyEntrySchema = z.object({
forceTls: ExternalProxyForceTlsSchema.default('same'),
dest: z.string().default(''),
port: PortSchema.default(443),
remark: z.string().default(''),
sni: z.string().optional(),
fingerprint: z.preprocess(
(val) => (val === '' ? undefined : val),
UtlsFingerprintSchema.optional(),
),
alpn: z.array(AlpnSchema).optional(),
pinnedPeerCertSha256: z.array(z.string()).optional(),
echConfigList: z.string().optional(),
});
export type ExternalProxyEntry = z.infer<typeof ExternalProxyEntrySchema>;