1
0
Эх сурвалжийг харах

refactor(frontend): add createDefault*InboundSettings factories for all 10 protocols

Round out Step 3d's settings factory set. Ten plain-object factories
(vless / vmess / trojan / shadowsocks / hysteria / hysteria2 / http /
mixed / tunnel / wireguard) replace the legacy
`new Inbound.<X>Settings(protocol)` constructors. Each returns a Zod-
parsable wire shape with schema defaults applied — no class instance.
Forms (Step 4) and InboundsPage clone (Step 5) call these factories
directly once the swap lands.

Three factories take a seed for random fields:
  - shadowsocks: method-dependent password length via
    RandomUtil.randomShadowsocksPassword(method)
  - hysteria: explicit `version` override (defaults to 2, matching
    the legacy panel constructor — v1 is opt-in)
  - wireguard: secretKey from Wireguard.generateKeypair().privateKey

Tests double-verify each factory the same way as the client factories:
snapshot the shape, then Zod parse round-trip to confirm no missing
defaults or stray fields.

Suite: 59 tests across 6 files; typecheck + lint clean. Outbound
factories and the toShareLink extraction follow next.
MHSanaei 23 цаг өмнө
parent
commit
e79ca42407

+ 111 - 6
frontend/src/lib/xray/inbound-defaults.ts

@@ -1,10 +1,15 @@
-import { RandomUtil } from '@/utils';
+import { RandomUtil, Wireguard } from '@/utils';
 
-import type { HysteriaClient } from '@/schemas/protocols/inbound/hysteria';
-import type { ShadowsocksClient } from '@/schemas/protocols/inbound/shadowsocks';
-import type { TrojanClient } from '@/schemas/protocols/inbound/trojan';
-import type { VlessClient } from '@/schemas/protocols/inbound/vless';
-import type { VmessClient } from '@/schemas/protocols/inbound/vmess';
+import type { HttpInboundSettings } from '@/schemas/protocols/inbound/http';
+import type { Hysteria2InboundSettings } from '@/schemas/protocols/inbound/hysteria2';
+import type { HysteriaClient, HysteriaInboundSettings } from '@/schemas/protocols/inbound/hysteria';
+import type { MixedInboundSettings } from '@/schemas/protocols/inbound/mixed';
+import type { ShadowsocksClient, ShadowsocksInboundSettings } from '@/schemas/protocols/inbound/shadowsocks';
+import type { TrojanClient, TrojanInboundSettings } from '@/schemas/protocols/inbound/trojan';
+import type { TunnelInboundSettings } from '@/schemas/protocols/inbound/tunnel';
+import type { VlessClient, VlessInboundSettings } from '@/schemas/protocols/inbound/vless';
+import type { VmessClient, VmessInboundSettings } from '@/schemas/protocols/inbound/vmess';
+import type { WireguardInboundSettings } from '@/schemas/protocols/inbound/wireguard';
 
 // Plain-object factories for protocol clients. Each returns a Zod-parsable
 // object matching the wire shape. Random fields (id, password, auth,
@@ -120,3 +125,103 @@ export function createDefaultHysteriaClient(seed: HysteriaClientSeed = {}): Hyst
     ...clientBase(seed),
   };
 }
+
+// Inbound-settings factories. Each returns a Zod-parsable wire-shape with
+// schema defaults already applied — no class instance, no XrayCommonClass.
+// Callers (form modals via Step 4, InboundsPage clone via Step 5) call
+// these instead of the legacy `Inbound.Settings.getSettings(protocol)`.
+
+export function createDefaultVlessInboundSettings(): VlessInboundSettings {
+  return {
+    clients: [],
+    decryption: 'none',
+    encryption: 'none',
+    fallbacks: [],
+  };
+}
+
+export function createDefaultVmessInboundSettings(): VmessInboundSettings {
+  return { clients: [] };
+}
+
+export function createDefaultTrojanInboundSettings(): TrojanInboundSettings {
+  return { clients: [], fallbacks: [] };
+}
+
+export interface ShadowsocksInboundSeed {
+  method?: ShadowsocksInboundSettings['method'];
+  password?: string;
+  network?: ShadowsocksInboundSettings['network'];
+  ivCheck?: boolean;
+}
+
+export function createDefaultShadowsocksInboundSettings(
+  seed: ShadowsocksInboundSeed = {},
+): ShadowsocksInboundSettings {
+  const method = seed.method ?? '2022-blake3-aes-256-gcm';
+  return {
+    method,
+    password: seed.password ?? RandomUtil.randomShadowsocksPassword(method),
+    network: seed.network ?? 'tcp',
+    clients: [],
+    ivCheck: seed.ivCheck ?? false,
+  };
+}
+
+// Hysteria v1 defaults still emit `version: 2` to match the legacy panel
+// constructor — the field discriminates v1 vs v2 inside the same settings
+// shape. Callers that explicitly want v1 pass `{ version: 1 }`.
+export interface HysteriaInboundSeed {
+  version?: number;
+}
+
+export function createDefaultHysteriaInboundSettings(
+  seed: HysteriaInboundSeed = {},
+): HysteriaInboundSettings {
+  return {
+    version: seed.version ?? 2,
+    clients: [],
+  };
+}
+
+export function createDefaultHysteria2InboundSettings(): Hysteria2InboundSettings {
+  return { version: 2, clients: [] };
+}
+
+export function createDefaultHttpInboundSettings(): HttpInboundSettings {
+  return { accounts: [], allowTransparent: false };
+}
+
+export function createDefaultMixedInboundSettings(): MixedInboundSettings {
+  return {
+    auth: 'password',
+    accounts: [],
+    udp: false,
+    ip: '127.0.0.1',
+  };
+}
+
+export function createDefaultTunnelInboundSettings(): TunnelInboundSettings {
+  return {
+    portMap: {},
+    allowedNetwork: 'tcp,udp',
+    followRedirect: false,
+  };
+}
+
+export interface WireguardInboundSeed {
+  mtu?: number;
+  secretKey?: string;
+  noKernelTun?: boolean;
+}
+
+export function createDefaultWireguardInboundSettings(
+  seed: WireguardInboundSeed = {},
+): WireguardInboundSettings {
+  return {
+    mtu: seed.mtu ?? 1420,
+    secretKey: seed.secretKey ?? Wireguard.generateKeypair().privateKey,
+    peers: [],
+    noKernelTun: seed.noKernelTun ?? false,
+  };
+}

+ 79 - 0
frontend/src/test/__snapshots__/inbound-defaults.test.ts.snap

@@ -1,5 +1,84 @@
 // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
 
+exports[`createDefault*InboundSettings factories > http 1`] = `
+{
+  "accounts": [],
+  "allowTransparent": false,
+}
+`;
+
+exports[`createDefault*InboundSettings factories > hysteria (v1, defaults to v2 wire version) 1`] = `
+{
+  "clients": [],
+  "version": 2,
+}
+`;
+
+exports[`createDefault*InboundSettings factories > hysteria2 1`] = `
+{
+  "clients": [],
+  "version": 2,
+}
+`;
+
+exports[`createDefault*InboundSettings factories > mixed 1`] = `
+{
+  "accounts": [],
+  "auth": "password",
+  "ip": "127.0.0.1",
+  "udp": false,
+}
+`;
+
+exports[`createDefault*InboundSettings factories > shadowsocks 1`] = `
+{
+  "clients": [],
+  "ivCheck": false,
+  "method": "2022-blake3-aes-256-gcm",
+  "network": "tcp",
+  "password": "ZmFrZS1zcy1zZWVk",
+}
+`;
+
+exports[`createDefault*InboundSettings factories > trojan 1`] = `
+{
+  "clients": [],
+  "fallbacks": [],
+}
+`;
+
+exports[`createDefault*InboundSettings factories > tunnel 1`] = `
+{
+  "allowedNetwork": "tcp,udp",
+  "followRedirect": false,
+  "portMap": {},
+}
+`;
+
+exports[`createDefault*InboundSettings factories > vless 1`] = `
+{
+  "clients": [],
+  "decryption": "none",
+  "encryption": "none",
+  "fallbacks": [],
+}
+`;
+
+exports[`createDefault*InboundSettings factories > vmess 1`] = `
+{
+  "clients": [],
+}
+`;
+
+exports[`createDefault*InboundSettings factories > wireguard 1`] = `
+{
+  "mtu": 1420,
+  "noKernelTun": false,
+  "peers": [],
+  "secretKey": "QGVlb2dXc1ZTWGw0ZXBzZndsWmtMaUM5MUlNYjBHWFdYbz0=",
+}
+`;
+
 exports[`createDefaultHysteriaClient > produces a Zod-valid client 1`] = `
 {
   "auth": "fixed-hyst-auth",

+ 84 - 5
frontend/src/test/inbound-defaults.test.ts

@@ -1,17 +1,32 @@
 import { describe, expect, it } from 'vitest';
 
 import {
+  createDefaultHttpInboundSettings,
+  createDefaultHysteria2InboundSettings,
   createDefaultHysteriaClient,
+  createDefaultHysteriaInboundSettings,
+  createDefaultMixedInboundSettings,
   createDefaultShadowsocksClient,
+  createDefaultShadowsocksInboundSettings,
   createDefaultTrojanClient,
+  createDefaultTrojanInboundSettings,
+  createDefaultTunnelInboundSettings,
   createDefaultVlessClient,
+  createDefaultVlessInboundSettings,
   createDefaultVmessClient,
+  createDefaultVmessInboundSettings,
+  createDefaultWireguardInboundSettings,
 } from '@/lib/xray/inbound-defaults';
-import { HysteriaClientSchema } from '@/schemas/protocols/inbound/hysteria';
-import { ShadowsocksClientSchema } from '@/schemas/protocols/inbound/shadowsocks';
-import { TrojanClientSchema } from '@/schemas/protocols/inbound/trojan';
-import { VlessClientSchema } from '@/schemas/protocols/inbound/vless';
-import { VmessClientSchema } from '@/schemas/protocols/inbound/vmess';
+import { HttpInboundSettingsSchema } from '@/schemas/protocols/inbound/http';
+import { Hysteria2InboundSettingsSchema } from '@/schemas/protocols/inbound/hysteria2';
+import { HysteriaClientSchema, HysteriaInboundSettingsSchema } from '@/schemas/protocols/inbound/hysteria';
+import { MixedInboundSettingsSchema } from '@/schemas/protocols/inbound/mixed';
+import { ShadowsocksClientSchema, ShadowsocksInboundSettingsSchema } from '@/schemas/protocols/inbound/shadowsocks';
+import { TrojanClientSchema, TrojanInboundSettingsSchema } from '@/schemas/protocols/inbound/trojan';
+import { TunnelInboundSettingsSchema } from '@/schemas/protocols/inbound/tunnel';
+import { VlessClientSchema, VlessInboundSettingsSchema } from '@/schemas/protocols/inbound/vless';
+import { VmessClientSchema, VmessInboundSettingsSchema } from '@/schemas/protocols/inbound/vmess';
+import { WireguardInboundSettingsSchema } from '@/schemas/protocols/inbound/wireguard';
 
 // Tests pass explicit seeds for every random field so the assertions don't
 // depend on window.crypto (the node test env has no crypto.randomUUID).
@@ -65,3 +80,67 @@ describe('createDefaultHysteriaClient', () => {
     expect(HysteriaClientSchema.parse(c)).toEqual(c);
   });
 });
+
+describe('createDefault*InboundSettings factories', () => {
+  it('vless', () => {
+    const s = createDefaultVlessInboundSettings();
+    expect(s).toMatchSnapshot();
+    expect(VlessInboundSettingsSchema.parse(s)).toEqual(s);
+  });
+
+  it('vmess', () => {
+    const s = createDefaultVmessInboundSettings();
+    expect(s).toMatchSnapshot();
+    expect(VmessInboundSettingsSchema.parse(s)).toEqual(s);
+  });
+
+  it('trojan', () => {
+    const s = createDefaultTrojanInboundSettings();
+    expect(s).toMatchSnapshot();
+    expect(TrojanInboundSettingsSchema.parse(s)).toEqual(s);
+  });
+
+  it('shadowsocks', () => {
+    const s = createDefaultShadowsocksInboundSettings({ password: 'ZmFrZS1zcy1zZWVk' });
+    expect(s).toMatchSnapshot();
+    expect(ShadowsocksInboundSettingsSchema.parse(s)).toEqual(s);
+  });
+
+  it('hysteria (v1, defaults to v2 wire version)', () => {
+    const s = createDefaultHysteriaInboundSettings();
+    expect(s).toMatchSnapshot();
+    expect(HysteriaInboundSettingsSchema.parse(s)).toEqual(s);
+  });
+
+  it('hysteria2', () => {
+    const s = createDefaultHysteria2InboundSettings();
+    expect(s).toMatchSnapshot();
+    expect(Hysteria2InboundSettingsSchema.parse(s)).toEqual(s);
+  });
+
+  it('http', () => {
+    const s = createDefaultHttpInboundSettings();
+    expect(s).toMatchSnapshot();
+    expect(HttpInboundSettingsSchema.parse(s)).toEqual(s);
+  });
+
+  it('mixed', () => {
+    const s = createDefaultMixedInboundSettings();
+    expect(s).toMatchSnapshot();
+    expect(MixedInboundSettingsSchema.parse(s)).toEqual(s);
+  });
+
+  it('tunnel', () => {
+    const s = createDefaultTunnelInboundSettings();
+    expect(s).toMatchSnapshot();
+    expect(TunnelInboundSettingsSchema.parse(s)).toEqual(s);
+  });
+
+  it('wireguard', () => {
+    const s = createDefaultWireguardInboundSettings({
+      secretKey: 'QGVlb2dXc1ZTWGw0ZXBzZndsWmtMaUM5MUlNYjBHWFdYbz0=',
+    });
+    expect(s).toMatchSnapshot();
+    expect(WireguardInboundSettingsSchema.parse(s)).toEqual(s);
+  });
+});