import { z } from 'zod'; import { PortSchema, SniffingSchema } from '@/schemas/primitives'; import { InboundSettingsSchema } from '@/schemas/protocols/inbound'; import { SecuritySettingsSchema } from '@/schemas/protocols/security'; import { NetworkSettingsSchema, StreamExtrasSchema } from '@/schemas/protocols/stream'; // InboundFormValues = the values shape Form.useForm() carries in // InboundFormModal. Mirrors the wire shape (so submission can hand // values straight to Schema.parse + POST) plus the DB-side fields that // the panel's /panel/api/inbounds/add endpoint expects alongside. // // Differences from schemas/api/inbound.ts InboundSchema: // - settings/streamSettings/sniffing are nested OBJECTS here, not the // JSON strings the endpoint accepts. The form holds typed data; the // submit handler stringifies right before POSTing. // - Adds DB fields not in InboundSchema: up, down, total, trafficReset, // lastTrafficResetTime, nodeId. These flow through the DBInbound row, // not the xray-config slice. export const InboundStreamFormSchema = NetworkSettingsSchema .and(SecuritySettingsSchema) .and(StreamExtrasSchema); export type InboundStreamFormValues = z.infer; export const TrafficResetSchema = z.enum(['never', 'hourly', 'daily', 'weekly', 'monthly']); export type TrafficReset = z.infer; // Db-side fields layered on top of the xray slice. These mirror the // DBInbound model — they live in the SQL row, not in xray's config. export const InboundDbFieldsSchema = z.object({ up: z.number().int().min(0).default(0), down: z.number().int().min(0).default(0), total: z.number().int().min(0).default(0), trafficReset: TrafficResetSchema.default('never'), lastTrafficResetTime: z.number().int().default(0), nodeId: z.number().int().nullable().optional(), }); export type InboundDbFields = z.infer; // Base fields that apply to every inbound regardless of protocol or // transport. The protocol-specific `settings` and the transport-specific // `streamSettings` are layered on via intersection below. export const InboundFormBaseSchema = z.object({ remark: z.string().default(''), enable: z.boolean().default(true), port: PortSchema, listen: z.string().default(''), tag: z.string().default(''), expiryTime: z.number().int().default(0), clientStats: z.string().optional(), sniffing: SniffingSchema.default({ enabled: false, destOverride: ['http', 'tls', 'quic', 'fakedns'], metadataOnly: false, routeOnly: false, ipsExcluded: [], domainsExcluded: [], }), streamSettings: InboundStreamFormSchema.optional(), }); export type InboundFormBase = z.infer; // Full form values = base + db fields + protocol-discriminated settings. // Consumers narrow on `.protocol` to access the matching settings branch. export const InboundFormSchema = InboundFormBaseSchema .and(InboundDbFieldsSchema) .and(InboundSettingsSchema); export type InboundFormValues = z.infer; // Fallback rows ride alongside the inbound submission for VLESS/Trojan // hosts. They're saved via a separate endpoint after the main inbound // POST returns, so the schema lives here but is not part of the wire // inbound payload. export const FallbackRowSchema = z.object({ rowKey: z.string(), childId: z.number().int().nullable(), name: z.string().default(''), alpn: z.string().default(''), path: z.string().default(''), xver: z.number().int().min(0).max(2).default(0), }); export type FallbackRow = z.infer;