Release 3X-UI for Docker / build (push) Canceled after 0s
Release 3X-UI / build (386) (push) Canceled after 0s
Release 3X-UI / build (amd64) (push) Canceled after 0s
Release 3X-UI / build (arm64) (push) Canceled after 0s
Release 3X-UI / build (armv5) (push) Canceled after 0s
Release 3X-UI / build (armv6) (push) Canceled after 0s
Release 3X-UI / build (armv7) (push) Canceled after 0s
Release 3X-UI / build (s390x) (push) Canceled after 0s
Release 3X-UI / Build for Windows (amd64) (push) Canceled after 0s
Release 3X-UI / Publish rolling dev release (push) Canceled after 0s
236 lines
7.7 KiB
TypeScript
236 lines
7.7 KiB
TypeScript
import { useMemo } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { Alert, Empty, Input, InputNumber, Select, Space, Switch, Tag } from 'antd';
|
|
|
|
import { onNumber } from '@/utils/onNumber';
|
|
import { SettingListItem } from '@/components/ui';
|
|
import {
|
|
BurstObservatorySchema,
|
|
ObservatoryHttpMethodSchema,
|
|
ObservatorySchema,
|
|
type BurstObservatoryObject,
|
|
type ObservatoryHttpMethod,
|
|
type ObservatoryObject,
|
|
type PingConfigObject,
|
|
} from '@/schemas/observatory';
|
|
import type { XraySettingsValue } from '@/hooks/useXraySetting';
|
|
import { settingsRequireBurstObservatory } from './balancer-helpers';
|
|
|
|
interface ObservatorySettingsTabProps {
|
|
templateSettings: XraySettingsValue | null;
|
|
mutate: (mutator: (next: XraySettingsValue) => void) => void;
|
|
}
|
|
|
|
const OBSERVATORY_DEFAULTS = ObservatorySchema.parse({});
|
|
const BURST_DEFAULTS = BurstObservatorySchema.parse({});
|
|
|
|
function asObject(value: unknown): Record<string, unknown> {
|
|
return value && typeof value === 'object' ? (value as Record<string, unknown>) : {};
|
|
}
|
|
|
|
function SelectorTags({ tags }: { tags: string[] }) {
|
|
if (!tags || tags.length === 0) return <Tag>—</Tag>;
|
|
return (
|
|
<>
|
|
{tags.map((sel) => (
|
|
<Tag
|
|
key={sel}
|
|
className="info-large-tag"
|
|
style={{ margin: 0, marginRight: 4, marginBottom: 4 }}
|
|
>
|
|
{sel}
|
|
</Tag>
|
|
))}
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default function ObservatorySettingsTab({
|
|
templateSettings,
|
|
mutate,
|
|
}: ObservatorySettingsTabProps) {
|
|
const { t } = useTranslation();
|
|
|
|
const observatory = useMemo<ObservatoryObject | null>(() => {
|
|
const raw = templateSettings?.observatory;
|
|
if (raw == null) return null;
|
|
return { ...OBSERVATORY_DEFAULTS, ...asObject(raw) } as ObservatoryObject;
|
|
}, [templateSettings?.observatory]);
|
|
|
|
const burst = useMemo<BurstObservatoryObject | null>(() => {
|
|
const raw = templateSettings?.burstObservatory;
|
|
if (raw == null) return null;
|
|
const merged = { ...BURST_DEFAULTS, ...asObject(raw) } as BurstObservatoryObject;
|
|
merged.pingConfig = {
|
|
...BURST_DEFAULTS.pingConfig,
|
|
...asObject(merged.pingConfig),
|
|
} as PingConfigObject;
|
|
return merged;
|
|
}, [templateSettings?.burstObservatory]);
|
|
|
|
const hasObservatory = observatory != null;
|
|
const hasBurst = burst != null;
|
|
const hasMixedObservers = hasObservatory && hasBurst;
|
|
const activeView =
|
|
hasBurst && (!hasObservatory || settingsRequireBurstObservatory(templateSettings))
|
|
? 'burstObservatory'
|
|
: 'observatory';
|
|
|
|
function patchObservatory(patch: Partial<ObservatoryObject>) {
|
|
mutate((tt) => {
|
|
tt.observatory = { ...OBSERVATORY_DEFAULTS, ...asObject(tt.observatory), ...patch };
|
|
});
|
|
}
|
|
|
|
function patchPingConfig(patch: Partial<PingConfigObject>) {
|
|
mutate((tt) => {
|
|
const current = asObject(tt.burstObservatory);
|
|
const currentPing = asObject(current.pingConfig);
|
|
tt.burstObservatory = {
|
|
...BURST_DEFAULTS,
|
|
...current,
|
|
pingConfig: { ...BURST_DEFAULTS.pingConfig, ...currentPing, ...patch },
|
|
};
|
|
});
|
|
}
|
|
|
|
if (!hasObservatory && !hasBurst) {
|
|
return <Empty description={t('pages.xray.observatory.emptyHint')} />;
|
|
}
|
|
|
|
const observatorySection = observatory && (
|
|
<>
|
|
<SettingListItem
|
|
paddings="small"
|
|
title={t('pages.xray.observatory.subjectSelector')}
|
|
description={t('pages.xray.observatory.subjectSelectorDesc')}
|
|
>
|
|
<SelectorTags tags={observatory.subjectSelector} />
|
|
</SettingListItem>
|
|
<SettingListItem
|
|
paddings="small"
|
|
title={t('pages.xray.observatory.probeURL')}
|
|
description={t('pages.xray.observatory.probeURLDesc')}
|
|
>
|
|
<Input
|
|
value={observatory.probeURL}
|
|
onChange={(e) => patchObservatory({ probeURL: e.target.value })}
|
|
placeholder="https://www.google.com/generate_204"
|
|
/>
|
|
</SettingListItem>
|
|
<SettingListItem
|
|
paddings="small"
|
|
title={t('pages.xray.observatory.probeInterval')}
|
|
description={t('pages.xray.observatory.probeIntervalDesc')}
|
|
>
|
|
<Input
|
|
value={observatory.probeInterval}
|
|
onChange={(e) => patchObservatory({ probeInterval: e.target.value })}
|
|
placeholder="1m"
|
|
/>
|
|
</SettingListItem>
|
|
<SettingListItem
|
|
paddings="small"
|
|
title={t('pages.xray.observatory.enableConcurrency')}
|
|
description={t('pages.xray.observatory.enableConcurrencyDesc')}
|
|
>
|
|
<Switch
|
|
checked={observatory.enableConcurrency}
|
|
onChange={(v) => patchObservatory({ enableConcurrency: v })}
|
|
/>
|
|
</SettingListItem>
|
|
</>
|
|
);
|
|
|
|
const burstSection = burst && (
|
|
<>
|
|
<SettingListItem
|
|
paddings="small"
|
|
title={t('pages.xray.observatory.subjectSelector')}
|
|
description={t('pages.xray.observatory.subjectSelectorDesc')}
|
|
>
|
|
<SelectorTags tags={burst.subjectSelector} />
|
|
</SettingListItem>
|
|
<SettingListItem
|
|
paddings="small"
|
|
title={t('pages.xray.observatory.destination')}
|
|
description={t('pages.xray.observatory.destinationDesc')}
|
|
>
|
|
<Input
|
|
value={burst.pingConfig.destination}
|
|
onChange={(e) => patchPingConfig({ destination: e.target.value })}
|
|
placeholder="https://www.google.com/generate_204"
|
|
/>
|
|
</SettingListItem>
|
|
<SettingListItem
|
|
paddings="small"
|
|
title={t('pages.xray.observatory.connectivity')}
|
|
description={t('pages.xray.observatory.connectivityDesc')}
|
|
>
|
|
<Input
|
|
value={burst.pingConfig.connectivity}
|
|
allowClear
|
|
onChange={(e) => patchPingConfig({ connectivity: e.target.value })}
|
|
placeholder="http://connectivitycheck.platform.hicloud.com/generate_204"
|
|
/>
|
|
</SettingListItem>
|
|
<SettingListItem
|
|
paddings="small"
|
|
title={t('pages.xray.observatory.interval')}
|
|
description={t('pages.xray.observatory.intervalDesc')}
|
|
>
|
|
<Input
|
|
value={burst.pingConfig.interval}
|
|
onChange={(e) => patchPingConfig({ interval: e.target.value })}
|
|
placeholder="1m"
|
|
/>
|
|
</SettingListItem>
|
|
<SettingListItem
|
|
paddings="small"
|
|
title={t('pages.xray.observatory.timeout')}
|
|
description={t('pages.xray.observatory.timeoutDesc')}
|
|
>
|
|
<Input
|
|
value={burst.pingConfig.timeout}
|
|
onChange={(e) => patchPingConfig({ timeout: e.target.value })}
|
|
placeholder="5s"
|
|
/>
|
|
</SettingListItem>
|
|
<SettingListItem
|
|
paddings="small"
|
|
title={t('pages.xray.observatory.sampling')}
|
|
description={t('pages.xray.observatory.samplingDesc')}
|
|
>
|
|
<InputNumber
|
|
min={1}
|
|
value={burst.pingConfig.sampling}
|
|
onChange={onNumber((v) => patchPingConfig({ sampling: v }))}
|
|
style={{ width: '100%' }}
|
|
/>
|
|
</SettingListItem>
|
|
<SettingListItem
|
|
paddings="small"
|
|
title={t('pages.xray.observatory.httpMethod')}
|
|
description={t('pages.xray.observatory.httpMethodDesc')}
|
|
>
|
|
<Select<ObservatoryHttpMethod>
|
|
value={burst.pingConfig.httpMethod}
|
|
onChange={(v) => patchPingConfig({ httpMethod: v })}
|
|
options={ObservatoryHttpMethodSchema.options.map((m) => ({ value: m, label: m }))}
|
|
style={{ width: '100%' }}
|
|
/>
|
|
</SettingListItem>
|
|
</>
|
|
);
|
|
|
|
return (
|
|
<Space orientation="vertical" size="middle" style={{ width: '100%' }}>
|
|
<Alert type="info" showIcon title={t('pages.xray.observatory.autoManaged')} />
|
|
{hasMixedObservers && (
|
|
<Alert type="warning" showIcon title={t('pages.xray.observatory.mixedLegacy')} />
|
|
)}
|
|
<div>{activeView === 'observatory' ? observatorySection : burstSection}</div>
|
|
</Space>
|
|
);
|
|
}
|