|
|
@@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next';
|
|
|
import { Alert, Button, Form, Input, Modal, Select, Space, Spin, Typography, message } from 'antd';
|
|
|
import { PlusOutlined, DeleteOutlined } from '@ant-design/icons';
|
|
|
|
|
|
+import { XrayConfigPayloadSchema } from '@/schemas/xray';
|
|
|
import { HttpUtil } from '@/utils';
|
|
|
|
|
|
interface GeodataAssetRow {
|
|
|
@@ -37,6 +38,7 @@ export default function GeodataSection({ active, onBusy, onClose }: GeodataSecti
|
|
|
const [cron, setCron] = useState(DEFAULT_CRON);
|
|
|
const [outbound, setOutbound] = useState<string | undefined>(undefined);
|
|
|
const [rows, setRows] = useState<GeodataAssetRow[]>([]);
|
|
|
+ const [standardSources, setStandardSources] = useState<GeodataAssetRow[]>([]);
|
|
|
const [outboundTags, setOutboundTags] = useState<string[]>([]);
|
|
|
const [template, setTemplate] = useState<Record<string, unknown> | null>(null);
|
|
|
const outboundTestUrlRef = useRef('');
|
|
|
@@ -45,8 +47,10 @@ export default function GeodataSection({ active, onBusy, onClose }: GeodataSecti
|
|
|
try {
|
|
|
const msg = await HttpUtil.post('/panel/api/xray/', undefined, { silent: true });
|
|
|
if (!msg?.success || typeof msg.obj !== 'string') return;
|
|
|
- const payload = JSON.parse(msg.obj) as Record<string, unknown>;
|
|
|
- const next = (payload.xraySetting || {}) as Record<string, unknown>;
|
|
|
+ const parsed = XrayConfigPayloadSchema.safeParse(JSON.parse(msg.obj));
|
|
|
+ if (!parsed.success) return;
|
|
|
+ const payload = parsed.data;
|
|
|
+ const next = payload.xraySetting as Record<string, unknown>;
|
|
|
setTemplate(next);
|
|
|
outboundTestUrlRef.current =
|
|
|
typeof payload.outboundTestUrl === 'string' ? payload.outboundTestUrl : '';
|
|
|
@@ -62,6 +66,7 @@ export default function GeodataSection({ active, onBusy, onClose }: GeodataSecti
|
|
|
setOutbound(
|
|
|
typeof geodata.outbound === 'string' && geodata.outbound ? geodata.outbound : undefined,
|
|
|
);
|
|
|
+ setStandardSources(payload.geodataSources ?? []);
|
|
|
|
|
|
// Download outbound candidates: template outbounds + subscription outbounds.
|
|
|
// Skip blackhole outbounds — routing a download through one just drops it.
|
|
|
@@ -106,6 +111,13 @@ export default function GeodataSection({ active, onBusy, onClose }: GeodataSecti
|
|
|
);
|
|
|
}
|
|
|
|
|
|
+ function addStandardSources() {
|
|
|
+ setRows((prev) => {
|
|
|
+ const files = new Set(prev.map((row) => row.file));
|
|
|
+ return [...prev, ...standardSources.filter((source) => !files.has(source.file))];
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
function save() {
|
|
|
if (!template) return;
|
|
|
const assets = rows
|
|
|
@@ -217,6 +229,9 @@ export default function GeodataSection({ active, onBusy, onClose }: GeodataSecti
|
|
|
>
|
|
|
{t('pages.index.geodataAddFile')}
|
|
|
</Button>
|
|
|
+ <Button onClick={addStandardSources} disabled={standardSources.length === 0}>
|
|
|
+ {t('pages.index.geodataUseStandardSources')}
|
|
|
+ </Button>
|
|
|
<Button type="primary" onClick={save} disabled={loading || !template}>
|
|
|
{t('pages.index.geodataSaveRestart')}
|
|
|
</Button>
|