mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-14 17:15:24 +03:00
ccd0853b6c
Unix Domain Socket inbounds (listen path starting with /) use port 0, which xray-core ignores. Validation was hard-locked to a minimum of 1 in three places: the shared Zod PortSchema, the AntD InputNumber, and the Go Inbound model tag. Adds an InboundPortSchema (min 0) for the inbound form/API schemas, makes the port InputNumber min UDS-aware, and relaxes the Inbound model validate tag to gte=0. PortSchema and the Node model stay min 1.
65 lines
2.7 KiB
TypeScript
65 lines
2.7 KiB
TypeScript
import { z } from 'zod';
|
|
|
|
import { InboundPortSchema, SniffingSchema } from '@/schemas/primitives';
|
|
import { InboundSettingsSchema } from '@/schemas/protocols/inbound';
|
|
import { SecuritySettingsSchema } from '@/schemas/protocols/security';
|
|
import { NetworkSettingsSchema, StreamExtrasSchema } from '@/schemas/protocols/stream';
|
|
|
|
// Top-level inbound shape on the wire. Composes:
|
|
// - Per-protocol settings via the InboundSettingsSchema discriminated
|
|
// union (10 protocols, tagged-wrapper {protocol, settings}).
|
|
// - StreamSettings as an intersection of the network DU (6 branches),
|
|
// security DU (3 branches), and the orthogonal extras (finalmask,
|
|
// sockopt, externalProxy). Zod 4 supports DU intersection — each
|
|
// branch validates its slice of the same input object.
|
|
//
|
|
// The id/up/down/total/expiryTime fields are int64 on the Go side but
|
|
// the panel ships them as JS numbers. Numbers above Number.MAX_SAFE_INTEGER
|
|
// (~9e15) lose precision; the panel works around this for the traffic
|
|
// counters by stringifying them at the API edge. Not modeled here.
|
|
|
|
export const StreamSettingsSchema = NetworkSettingsSchema
|
|
.and(SecuritySettingsSchema)
|
|
.and(StreamExtrasSchema);
|
|
export type StreamSettings = z.infer<typeof StreamSettingsSchema>;
|
|
|
|
export const InboundCoreSchema = z.object({
|
|
id: z.number().int().optional(),
|
|
up: z.number().int().min(0).default(0),
|
|
down: z.number().int().min(0).default(0),
|
|
total: z.number().int().min(0).default(0),
|
|
remark: z.string().default(''),
|
|
enable: z.boolean().default(true),
|
|
expiryTime: z.number().int().default(0),
|
|
listen: z.string().default(''),
|
|
port: InboundPortSchema,
|
|
tag: z.string().default(''),
|
|
sniffing: SniffingSchema.default({
|
|
enabled: false,
|
|
destOverride: ['http', 'tls', 'quic', 'fakedns'],
|
|
metadataOnly: false,
|
|
routeOnly: false,
|
|
ipsExcluded: [],
|
|
domainsExcluded: [],
|
|
}),
|
|
streamSettings: StreamSettingsSchema.optional(),
|
|
clientStats: z.string().optional(),
|
|
});
|
|
export type InboundCore = z.infer<typeof InboundCoreSchema>;
|
|
|
|
// Full Inbound = core fields + the protocol/settings discriminated union.
|
|
// Consumers narrow on `.protocol` to access the matching `.settings`
|
|
// branch with full type safety.
|
|
export const InboundSchema = InboundCoreSchema.and(InboundSettingsSchema);
|
|
export type Inbound = z.infer<typeof InboundSchema>;
|
|
|
|
// SlimInbound is the list-view projection — same shape minus settings
|
|
// and streamSettings (the list endpoint omits both to keep payload
|
|
// small). Used by InboundsPage list rendering.
|
|
export const SlimInboundSchema = InboundCoreSchema.omit({
|
|
streamSettings: true,
|
|
}).extend({
|
|
protocol: z.string(),
|
|
});
|
|
export type SlimInbound = z.infer<typeof SlimInboundSchema>;
|