mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-06-05 20:54:14 +00:00
feat(frontend): InboundFormValues schema for Pattern A rewrite
Foundation for the InboundFormModal rewrite. Mirrors the wire Inbound shape (intersection of core fields + protocol settings DU + stream/security DUs) plus the DB-side fields (up/down/total/trafficReset/nodeId/...) that flow through DBInbound rather than the xray config slice. InboundStreamFormSchema is exported separately so individual sub-form sections can rule against just the stream portion when needed. FallbackRowSchema is co-located here even though fallbacks save via a distinct endpoint after the main POST — they belong to the same form state from the user's perspective. No modal changes in this commit. Foundation only; subsequent turns swap the modal's `inboundRef`/`dbFormRef` mutable-class state for Form.useForm<InboundFormValues>().
This commit is contained in:
parent
f79e486f9f
commit
d2f3a7baa7
1 changed files with 83 additions and 0 deletions
83
frontend/src/schemas/forms/inbound-form.ts
Normal file
83
frontend/src/schemas/forms/inbound-form.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
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<T>() 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<typeof InboundStreamFormSchema>;
|
||||||
|
|
||||||
|
export const TrafficResetSchema = z.enum(['never', 'hourly', 'daily', 'weekly', 'monthly']);
|
||||||
|
export type TrafficReset = z.infer<typeof TrafficResetSchema>;
|
||||||
|
|
||||||
|
// 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<typeof InboundDbFieldsSchema>;
|
||||||
|
|
||||||
|
// 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<typeof InboundFormBaseSchema>;
|
||||||
|
|
||||||
|
// 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<typeof InboundFormSchema>;
|
||||||
|
|
||||||
|
// 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<typeof FallbackRowSchema>;
|
||||||
Loading…
Reference in a new issue