mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-18 01:00:59 +00:00
feat(wireguard): multi-client support
WireGuard inbounds now manage per-client peers using xray-core's native WireGuard users (AddUser/RemoveUser). Each client lives in settings.clients (canonical, like every other protocol) and is projected to peers[] only when emitting the xray config, at level 0 so the dispatcher's per-user traffic/online counters work with no extra plumbing. Backend: internal/util/wireguard gains KeyToHex (base64 to hex for the gRPC path), PublicKeyFromPrivate and GenerateWireguardPSK; xray/api.go builds a wireguard account in AddUser with hex keys (RemoveUser already worked); client CRUD generates a keypair and allocates a unique tunnel address per client and never rotates keys on edit; an idempotent migration converts legacy settings.peers into managed clients; WireGuard is included in the raw subscription. Frontend: WireGuard in the add-client modal with keys on the credential tab, client schema, per-client QR/link/.conf, inbound form reduced to server settings; i18n added across 13 locales. Fix: guard the settings[clients] assertion in add/update so a legacy WireGuard inbound stored without a clients key no longer panics.
This commit is contained in:
@@ -33,10 +33,36 @@ export const WireguardInboundPeerSchema = z.object({
|
||||
});
|
||||
export type WireguardInboundPeer = z.infer<typeof WireguardInboundPeerSchema>;
|
||||
|
||||
// A WireGuard inbound client (multi-client model). Each client is one peer the
|
||||
// server accepts: the panel stores its keypair so it can render a full .conf/QR,
|
||||
// and allowedIPs is the client's unique tunnel address (allocated server-side
|
||||
// when left blank). Keys are optional on the wire — the backend generates them
|
||||
// when absent.
|
||||
export const WireguardClientSchema = z.object({
|
||||
privateKey: z.string().optional(),
|
||||
publicKey: z.string().optional(),
|
||||
preSharedKey: z.string().optional(),
|
||||
allowedIPs: z.array(z.string()).default([]),
|
||||
keepAlive: optionalClearedInt(z.number().int().min(0)),
|
||||
email: z.string().min(1),
|
||||
limitIp: z.number().int().min(0).default(0),
|
||||
totalGB: z.number().int().min(0).default(0),
|
||||
expiryTime: z.number().int().default(0),
|
||||
enable: z.boolean().default(true),
|
||||
tgId: z.union([z.number(), z.string()]).transform((v) => Number(v) || 0).default(0),
|
||||
subId: z.string().default(''),
|
||||
comment: z.string().default(''),
|
||||
reset: z.number().int().min(0).default(0),
|
||||
created_at: z.number().int().optional(),
|
||||
updated_at: z.number().int().optional(),
|
||||
});
|
||||
export type WireguardClient = z.infer<typeof WireguardClientSchema>;
|
||||
|
||||
export const WireguardInboundSettingsSchema = z.object({
|
||||
mtu: optionalClearedInt(z.number().int().min(1)),
|
||||
secretKey: z.string().min(1),
|
||||
peers: z.array(WireguardInboundPeerSchema).default([]),
|
||||
clients: z.array(WireguardClientSchema).default([]),
|
||||
noKernelTun: z.boolean().default(false),
|
||||
domainStrategy: WireguardDomainStrategySchema.optional(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user