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

feat(frontend): OutboundFormModal.new.tsx socks/http/hysteria/loopback/blackhole/wireguard sections

- SOCKS / HTTP: user + pass at settings root.
- Hysteria: read-only version=2 (the actual transport knobs live on
  stream.hysteria, added with the stream tab).
- Loopback: inboundTag.
- Blackhole: response type Select with empty/none/http options.
- Wireguard: address (csv) + secretKey (with regenerate icon) + derived
  pubKey + domain strategy + MTU + workers + no-kernel-tun + reserved
  (csv) + peers Form.List with nested allowedIPs sub-list.

Wireguard regenerate icon uses Wireguard.generateKeypair() and writes
both keys to the form via setFieldValue — preserves the legacy UX of
the SyncOutlined inline-icon next to the privateKey label.
MHSanaei 9 цаг өмнө
parent
commit
b6d996d1b1

+ 185 - 1
frontend/src/pages/xray/OutboundFormModal.new.tsx

@@ -1,8 +1,22 @@
 import { useEffect, useMemo, useState } from 'react';
 import { useTranslation } from 'react-i18next';
-import { Form, Input, InputNumber, Modal, Select, Space, Switch, Tabs, message } from 'antd';
+import {
+  Button,
+  Form,
+  Input,
+  InputNumber,
+  Modal,
+  Select,
+  Space,
+  Switch,
+  Tabs,
+  message,
+} from 'antd';
+import { DeleteOutlined, MinusOutlined, PlusOutlined, SyncOutlined } from '@ant-design/icons';
 
+import InputAddon from '@/components/InputAddon';
 import JsonEditor from '@/components/JsonEditor';
+import { Wireguard } from '@/utils';
 import {
   formValuesToWirePayload,
   rawOutboundToFormValues,
@@ -19,6 +33,7 @@ import {
   OutboundProtocols as Protocols,
   TLS_FLOW_CONTROL,
   USERS_SECURITY,
+  WireguardDomainStrategy,
 } from '@/schemas/primitives';
 import { SSMethodSchema } from '@/schemas/protocols/inbound/shadowsocks';
 import { antdRule } from '@/utils/zodForm';
@@ -315,6 +330,175 @@ export default function OutboundFormModalNew({
                         </Form.Item>
                       </>
                     )}
+
+                    {(protocol === 'socks' || protocol === 'http') && (
+                      <>
+                        <Form.Item label={t('username')} name={['settings', 'user']}>
+                          <Input />
+                        </Form.Item>
+                        <Form.Item label={t('password')} name={['settings', 'pass']}>
+                          <Input />
+                        </Form.Item>
+                      </>
+                    )}
+
+                    {protocol === 'hysteria' && (
+                      <Form.Item label="Version" name={['settings', 'version']}>
+                        <InputNumber min={2} max={2} disabled />
+                      </Form.Item>
+                    )}
+
+                    {protocol === 'loopback' && (
+                      <Form.Item label="Inbound tag" name={['settings', 'inboundTag']}>
+                        <Input placeholder="inbound tag used in routing rules" />
+                      </Form.Item>
+                    )}
+
+                    {protocol === 'blackhole' && (
+                      <Form.Item label="Response type" name={['settings', 'type']}>
+                        <Select
+                          options={[
+                            { value: '', label: '(empty)' },
+                            { value: 'none', label: 'none' },
+                            { value: 'http', label: 'http' },
+                          ]}
+                        />
+                      </Form.Item>
+                    )}
+
+                    {protocol === 'wireguard' && (
+                      <>
+                        <Form.Item label={t('pages.inbounds.address')} name={['settings', 'address']}>
+                          <Input placeholder="comma-separated, e.g. 10.0.0.1,fd00::1" />
+                        </Form.Item>
+                        <Form.Item
+                          label={
+                            <>
+                              {t('pages.inbounds.privatekey')}
+                              <SyncOutlined
+                                className="random-icon"
+                                onClick={() => {
+                                  const pair = Wireguard.generateKeypair();
+                                  form.setFieldValue(['settings', 'secretKey'], pair.privateKey);
+                                  form.setFieldValue(['settings', 'pubKey'], pair.publicKey);
+                                }}
+                              />
+                            </>
+                          }
+                          name={['settings', 'secretKey']}
+                        >
+                          <Input />
+                        </Form.Item>
+                        <Form.Item label={t('pages.inbounds.publicKey')} name={['settings', 'pubKey']}>
+                          <Input disabled />
+                        </Form.Item>
+                        <Form.Item label="Domain strategy" name={['settings', 'domainStrategy']}>
+                          <Select
+                            options={[
+                              { value: '', label: `(${t('none')})` },
+                              ...WireguardDomainStrategy.map((s) => ({ value: s, label: s })),
+                            ]}
+                          />
+                        </Form.Item>
+                        <Form.Item label="MTU" name={['settings', 'mtu']}>
+                          <InputNumber min={0} />
+                        </Form.Item>
+                        <Form.Item label="Workers" name={['settings', 'workers']}>
+                          <InputNumber min={0} />
+                        </Form.Item>
+                        <Form.Item
+                          label="No-kernel TUN"
+                          name={['settings', 'noKernelTun']}
+                          valuePropName="checked"
+                        >
+                          <Switch />
+                        </Form.Item>
+                        <Form.Item label="Reserved" name={['settings', 'reserved']}>
+                          <Input placeholder="comma-separated bytes, e.g. 1,2,3" />
+                        </Form.Item>
+                        <Form.List name={['settings', 'peers']}>
+                          {(fields, { add, remove }) => (
+                            <>
+                              <Form.Item label="Peers">
+                                <Button
+                                  size="small"
+                                  type="primary"
+                                  icon={<PlusOutlined />}
+                                  onClick={() =>
+                                    add({
+                                      publicKey: '',
+                                      psk: '',
+                                      allowedIPs: ['0.0.0.0/0', '::/0'],
+                                      endpoint: '',
+                                      keepAlive: 0,
+                                    })
+                                  }
+                                />
+                              </Form.Item>
+                              {fields.map((field, index) => (
+                                <div key={field.key}>
+                                  <Form.Item wrapperCol={{ md: { span: 14, offset: 8 } }}>
+                                    <div className="item-heading">
+                                      <span>Peer {index + 1}</span>
+                                      {fields.length > 1 && (
+                                        <DeleteOutlined
+                                          className="danger-icon"
+                                          onClick={() => remove(field.name)}
+                                        />
+                                      )}
+                                    </div>
+                                  </Form.Item>
+                                  <Form.Item label="Endpoint" name={[field.name, 'endpoint']}>
+                                    <Input />
+                                  </Form.Item>
+                                  <Form.Item
+                                    label={t('pages.inbounds.publicKey')}
+                                    name={[field.name, 'publicKey']}
+                                  >
+                                    <Input />
+                                  </Form.Item>
+                                  <Form.Item label="PSK" name={[field.name, 'psk']}>
+                                    <Input />
+                                  </Form.Item>
+                                  <Form.Item label="Allowed IPs">
+                                    <Form.List name={[field.name, 'allowedIPs']}>
+                                      {(ipFields, { add: addIp, remove: removeIp }) => (
+                                        <>
+                                          {ipFields.map((ipField, ipIdx) => (
+                                            <Space.Compact
+                                              key={ipField.key}
+                                              block
+                                              style={{ marginBottom: 4 }}
+                                            >
+                                              <Form.Item noStyle name={ipField.name}>
+                                                <Input />
+                                              </Form.Item>
+                                              {ipFields.length > 1 && (
+                                                <InputAddon onClick={() => removeIp(ipIdx)}>
+                                                  <MinusOutlined />
+                                                </InputAddon>
+                                              )}
+                                            </Space.Compact>
+                                          ))}
+                                          <Button
+                                            size="small"
+                                            icon={<PlusOutlined />}
+                                            onClick={() => addIp('')}
+                                          />
+                                        </>
+                                      )}
+                                    </Form.List>
+                                  </Form.Item>
+                                  <Form.Item label="Keep alive" name={[field.name, 'keepAlive']}>
+                                    <InputNumber min={0} />
+                                  </Form.Item>
+                                </div>
+                              ))}
+                            </>
+                          )}
+                        </Form.List>
+                      </>
+                    )}
                   </>
                 ),
               },