import React, { useEffect, useState } from 'react'; import { Button, Divider, Form, Grid, Header, Icon, Label, Message, Segment, } from 'semantic-ui-react'; import { API, copy, showError, showSuccess } from '../helpers'; const OperationSetting = () => { const [inputs, setInputs] = useState({ AgentHeartbeatInterval: '30000', AgentSyncInterval: '30000', NodeOfflineThreshold: '120000', AgentUpdateRepo: 'Rain-kl/ATSFlare', ServerAddress: '', AgentDiscoveryToken: '', }); const [loading, setLoading] = useState(false); const getOptions = async () => { const res = await API.get('/api/option/'); const { success, message, data } = res.data; if (success) { let newInputs = {}; data.forEach((item) => { if (item.key in inputs) { newInputs[item.key] = item.value; } }); setInputs((prev) => ({ ...prev, ...newInputs })); } else { showError(message); } }; useEffect(() => { getOptions(); getDiscoveryToken(); }, []); const getDiscoveryToken = async () => { try { const res = await API.get('/api/nodes/bootstrap-token'); const { success, data } = res.data; if (success && data) { setInputs((prev) => ({ ...prev, AgentDiscoveryToken: data.discovery_token, })); } } catch (e) { // ignore } }; const updateOption = async (key, value) => { setLoading(true); const res = await API.put('/api/option', { key, value }); const { success, message } = res.data; if (success) { showSuccess('设置已保存'); setInputs((prev) => ({ ...prev, [key]: value })); } else { showError(message); } setLoading(false); }; const handleInputChange = (e, { name, value }) => { setInputs((prev) => ({ ...prev, [name]: value })); }; const submitAgentIntervals = async () => { const hb = parseInt(inputs.AgentHeartbeatInterval, 10); const sync = parseInt(inputs.AgentSyncInterval, 10); const offline = parseInt(inputs.NodeOfflineThreshold, 10); if (isNaN(hb) || hb < 5000) { showError('心跳间隔不能小于 5000 毫秒'); return; } if (isNaN(sync) || sync < 5000) { showError('同步间隔不能小于 5000 毫秒'); return; } if (isNaN(offline) || offline < 10000) { showError('离线阈值不能小于 10000 毫秒'); return; } await updateOption('AgentHeartbeatInterval', String(hb)); await updateOption('AgentSyncInterval', String(sync)); await updateOption('NodeOfflineThreshold', String(offline)); }; const submitUpdateRepo = async () => { await updateOption('AgentUpdateRepo', inputs.AgentUpdateRepo); }; const rotateDiscoveryToken = async () => { setLoading(true); try { const res = await API.post('/api/nodes/bootstrap-token/rotate'); const { success, message, data } = res.data; if (success) { showSuccess('Discovery Token 已重新生成'); setInputs((prev) => ({ ...prev, AgentDiscoveryToken: data.discovery_token, })); } else { showError(message); } } catch (e) { showError('操作失败'); } setLoading(false); }; const formatMs = (ms) => { const val = parseInt(ms, 10); if (isNaN(val)) return ms; if (val >= 60000) return `${val / 60000} 分钟`; return `${val / 1000} 秒`; }; const serverAddr = inputs.ServerAddress || window.location.origin; const curlCommand = inputs.AgentDiscoveryToken ? `curl -fsSL https://raw.githubusercontent.com/Rain-kl/ATSFlare/main/scripts/install-agent.sh | bash -s -- --server-url ${serverAddr} --discovery-token ${inputs.AgentDiscoveryToken}` : ''; const openSwaggerDoc = () => { window.open('/swagger/index.html', '_blank', 'noopener,noreferrer'); }; return (
接口文档

通过 Swagger UI 查看当前 Server API,并直接在浏览器中调试接口。

Agent 运行参数

这些参数通过心跳响应下发到所有 Agent,修改后下次心跳即生效。

保存运行参数
Agent 更新源

自动更新开关和手动更新由节点页单独控制,这里只配置 Agent 更新仓库。

保存更新设置
节点接入
{curlCommand && ( <>
Agent 一键部署命令
                  {curlCommand}
                
)}
); }; export default OperationSetting;