From 25a86b9ee2a28fa5d768e7a2180e73fb7d17dbe0 Mon Sep 17 00:00:00 2001 From: nima1024m <114405577+nima1024m@users.noreply.github.com> Date: Sun, 28 Jun 2026 15:02:18 +0200 Subject: [PATCH] feat(balancers): tabbed Observatory/Burst Observatory form (#5627) * feat(balancers): tabbed Observatory/Burst form replacing raw JSON Replace the raw JSON editor for the Observatory / Burst Observatory sections with a proper Ant Design form, and split the Balancers page into two sub-tabs: "Balancer Settings" (the existing table) and "Observatory". Observers stay fully auto-managed by balancer strategy through the existing syncObservatories logic: users edit only the tunable probe fields, the subjectSelector is shown read-only since it is derived from the balancers, and deleting the last balancer that needs an observer now warns in the confirm dialog that the observer will be removed too. Overlapping selectors keep an observer alive while any balancer still references it. Also add the previously missing pingConfig.httpMethod field (HEAD/GET) and translations for the new strings across all 13 locales. * refactor(balancers): tighten httpMethod typing and align connectivity default Address automated review feedback on the Observatory form: - Use the ObservatoryHttpMethodSchema enum for pingConfig.httpMethod instead of a free-form z.string(), and drive the HTTP method Select from its options. Removes the previously dead enum export and the duplicate local list, and types the field as 'HEAD' | 'GET'. - Align the schema's connectivity default with DEFAULT_BURST_OBSERVATORY (the hicloud URL) so it matches what burst observers are actually created with. No behavior change. --- .../src/pages/xray/balancers/BalancersTab.tsx | 147 +++++------ .../xray/balancers/ObservatorySettingsTab.tsx | 238 ++++++++++++++++++ .../pages/xray/balancers/balancer-helpers.ts | 17 ++ frontend/src/schemas/observatory.ts | 34 +++ .../test/balancer-observatory-sync.test.ts | 67 ++++- internal/web/translation/ar-EG.json | 30 +++ internal/web/translation/en-US.json | 30 +++ internal/web/translation/es-ES.json | 30 +++ internal/web/translation/fa-IR.json | 30 +++ internal/web/translation/id-ID.json | 30 +++ internal/web/translation/ja-JP.json | 30 +++ internal/web/translation/pt-BR.json | 30 +++ internal/web/translation/ru-RU.json | 30 +++ internal/web/translation/tr-TR.json | 30 +++ internal/web/translation/uk-UA.json | 30 +++ internal/web/translation/vi-VN.json | 30 +++ internal/web/translation/zh-CN.json | 30 +++ internal/web/translation/zh-TW.json | 30 +++ 18 files changed, 807 insertions(+), 86 deletions(-) create mode 100644 frontend/src/pages/xray/balancers/ObservatorySettingsTab.tsx create mode 100644 frontend/src/schemas/observatory.ts diff --git a/frontend/src/pages/xray/balancers/BalancersTab.tsx b/frontend/src/pages/xray/balancers/BalancersTab.tsx index dffc1f719..001e986ea 100644 --- a/frontend/src/pages/xray/balancers/BalancersTab.tsx +++ b/frontend/src/pages/xray/balancers/BalancersTab.tsx @@ -1,13 +1,14 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { Button, Divider, Dropdown, Empty, Modal, Radio, Select, Space, Table, Tag, Tooltip } from 'antd'; -import { PlusOutlined, MoreOutlined, EditOutlined, DeleteOutlined, SyncOutlined } from '@ant-design/icons'; +import { Button, Dropdown, Empty, Modal, Select, Space, Table, Tabs, Tag, Tooltip } from 'antd'; +import { PlusOutlined, MoreOutlined, EditOutlined, DeleteOutlined, SyncOutlined, DeploymentUnitOutlined, RadarChartOutlined } from '@ant-design/icons'; import type { ColumnsType } from 'antd/es/table'; import BalancerFormModal from './BalancerFormModal'; import type { BalancerFormValue } from './BalancerFormModal'; -import { syncObservatories } from './balancer-helpers'; -import { JsonEditor } from '@/components/form'; +import { syncObservatories, observersRemovedByDeletingBalancer } from './balancer-helpers'; +import ObservatorySettingsTab from './ObservatorySettingsTab'; +import { catTabLabel } from '@/pages/settings/catTabLabel'; import { HttpUtil } from '@/utils'; import type { XraySettingsValue, SetTemplate } from '@/hooks/useXraySetting'; import type { @@ -184,8 +185,15 @@ export default function BalancersTab({ } function confirmDelete(idx: number) { + const removed = templateSettings + ? observersRemovedByDeletingBalancer(templateSettings, idx) + : { observatory: false, burst: false }; + const warnings: string[] = []; + if (removed.observatory) warnings.push(t('pages.xray.observatory.deleteAlsoObservatory')); + if (removed.burst) warnings.push(t('pages.xray.observatory.deleteAlsoBurst')); modal.confirm({ title: `${t('delete')} ${t('pages.xray.Balancers')} #${idx + 1}?`, + content: warnings.length ? warnings.join(' ') : undefined, okText: t('delete'), okType: 'danger', cancelText: t('cancel'), @@ -316,92 +324,61 @@ export default function BalancersTab({ }, ]; - const hasObservatory = !!templateSettings?.observatory; - const hasBurstObservatory = !!templateSettings?.burstObservatory; - const showObsEditor = hasObservatory || hasBurstObservatory; + const balancerSettingsTab = ( + + {rows.length === 0 ? ( + + + + ) : ( + <> + + + +