Răsfoiți Sursa

feat(xray): add connIdle and bufferSize policy controls

Expose level-0 connection policies in the panel's Basics tab: idle timeout (connIdle) and per-connection buffer size (bufferSize). Empty fields delete the key so Xray falls back to its own defaults. Adds en-US/fa-IR strings and types policy.levels in the Zod schema.
MHSanaei 20 ore în urmă
părinte
comite
ceef413dc4

+ 61 - 1
frontend/src/pages/xray/basics/BasicsTab.tsx

@@ -1,8 +1,9 @@
 import { useCallback } from 'react';
 import { useTranslation } from 'react-i18next';
-import { Alert, Button, Input, Modal, Select, Space, Switch, Tabs } from 'antd';
+import { Alert, Button, Input, InputNumber, Modal, Select, Space, Switch, Tabs } from 'antd';
 import {
   BarChartOutlined,
+  ClockCircleOutlined,
   FileTextOutlined,
   ReloadOutlined,
   SettingOutlined,
@@ -54,6 +55,20 @@ export default function BasicsTab({
     [setTemplateSettings],
   );
 
+  const setLevel0 = useCallback(
+    (field: string, value: number | null) => mutate((tt) => {
+      if (!tt.policy) tt.policy = {};
+      if (!tt.policy.levels) tt.policy.levels = {};
+      if (!tt.policy.levels['0']) tt.policy.levels['0'] = {};
+      if (value === null || value === undefined) {
+        delete tt.policy.levels['0'][field];
+      } else {
+        tt.policy.levels['0'][field] = value;
+      }
+    }),
+    [mutate],
+  );
+
   function confirmResetDefault() {
     modal.confirm({
       title: t('pages.settings.resetDefaultConfig'),
@@ -72,6 +87,7 @@ export default function BasicsTab({
   const routingStrategy = templateSettings?.routing?.domainStrategy ?? 'AsIs';
   const log = (templateSettings?.log || {}) as Record<string, unknown>;
   const policy = (templateSettings?.policy?.system || {}) as Record<string, boolean>;
+  const level0 = (templateSettings?.policy?.levels?.['0'] || {}) as Record<string, unknown>;
 
   const items = [
     {
@@ -168,6 +184,50 @@ export default function BasicsTab({
         </>
       ),
     },
+    {
+      key: 'connection',
+      label: catTabLabel(<ClockCircleOutlined />, t('pages.xray.connectionLimits'), isMobile),
+      children: (
+        <>
+          <Alert
+            type="warning"
+            showIcon
+            className="mb-12 hint-alert"
+            title={t('pages.xray.connectionLimitsDesc')}
+          />
+          <SettingListItem
+            title={t('pages.xray.connIdle')}
+            description={t('pages.xray.connIdleDesc')}
+            paddings="small"
+            control={
+              <InputNumber
+                value={typeof level0.connIdle === 'number' ? level0.connIdle : undefined}
+                min={0}
+                style={{ width: '100%' }}
+                placeholder="300"
+                addonAfter={t('pages.xray.seconds')}
+                onChange={(v) => setLevel0('connIdle', v as number | null)}
+              />
+            }
+          />
+          <SettingListItem
+            title={t('pages.xray.bufferSize')}
+            description={t('pages.xray.bufferSizeDesc')}
+            paddings="small"
+            control={
+              <InputNumber
+                value={typeof level0.bufferSize === 'number' ? level0.bufferSize : undefined}
+                min={0}
+                style={{ width: '100%' }}
+                placeholder={t('pages.xray.bufferSizePlaceholder')}
+                addonAfter="KB"
+                onChange={(v) => setLevel0('bufferSize', v as number | null)}
+              />
+            }
+          />
+        </>
+      ),
+    },
     {
       key: '3',
       label: catTabLabel(<FileTextOutlined />, t('pages.xray.logConfigs'), isMobile),

+ 1 - 0
frontend/src/schemas/xray.ts

@@ -28,6 +28,7 @@ export const XraySettingsValueSchema = z.object({
   log: z.record(z.string(), z.unknown()).optional(),
   policy: z.object({
     system: z.record(z.string(), z.boolean()).optional(),
+    levels: z.record(z.string(), z.record(z.string(), z.unknown())).optional(),
   }).loose().optional(),
   observatory: z.unknown().optional(),
   burstObservatory: z.unknown().optional(),

+ 8 - 0
web/translation/en-US.json

@@ -1201,6 +1201,14 @@
       "statsOutboundUplinkDesc": "Enables the statistics collection for upstream traffic of all outbound proxies.",
       "statsOutboundDownlink": "Outbound Download Statistics",
       "statsOutboundDownlinkDesc": "Enables the statistics collection for downstream traffic of all outbound proxies.",
+      "connectionLimits": "Connection Limits",
+      "connectionLimitsDesc": "Connection-level policies for user level 0. Leave a field empty to use Xray's default.",
+      "connIdle": "Idle Timeout",
+      "connIdleDesc": "Closes a connection after it stays idle for this many seconds. Lowering it frees memory and file descriptors faster on busy servers (Xray default: 300).",
+      "bufferSize": "Buffer Size",
+      "bufferSizeDesc": "Per-connection internal buffer size in KB. Set to 0 to minimize memory usage on low-RAM servers (Xray default depends on the platform).",
+      "bufferSizePlaceholder": "auto",
+      "seconds": "seconds",
       "rules": {
         "first": "First",
         "last": "Last",

+ 8 - 0
web/translation/fa-IR.json

@@ -1201,6 +1201,14 @@
       "statsOutboundUplinkDesc": "جمع‌آوری آمار برای ترافیک بالارو (آپلود) تمام پروکسی‌های خروجی را فعال می‌کند.",
       "statsOutboundDownlink": "آمار دانلود خروجی",
       "statsOutboundDownlinkDesc": "جمع‌آوری آمار برای ترافیک پایین‌رو (دانلود) تمام پروکسی‌های خروجی را فعال می‌کند.",
+      "connectionLimits": "محدودیت اتصال",
+      "connectionLimitsDesc": "سیاست‌های سطح اتصال برای کاربرانِ سطح ۰. هر فیلد را خالی بگذارید تا مقدار پیش‌فرض Xray استفاده شود.",
+      "connIdle": "مهلت بی‌کاری",
+      "connIdleDesc": "اتصال را پس از این تعداد ثانیه بی‌کار ماندن می‌بندد. کم‌کردن آن، روی سرورهای شلوغ حافظه و file descriptor را زودتر آزاد می‌کند (پیش‌فرض Xray: ۳۰۰).",
+      "bufferSize": "اندازهٔ بافر",
+      "bufferSizeDesc": "اندازهٔ بافر داخلی هر اتصال بر حسب کیلوبایت. برای کم‌کردن مصرف حافظه روی سرورهای کم‌رم روی ۰ بگذارید (پیش‌فرض Xray به پلتفرم بستگی دارد).",
+      "bufferSizePlaceholder": "خودکار",
+      "seconds": "ثانیه",
       "rules": {
         "first": "اولین",
         "last": "آخرین",