| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804 |
- let sessionId = localStorage.getItem('session_id') || null;
- let autoRefreshInterval = null;
- let autoRefreshEnabled = false;
- // Restore session on page load
- if (sessionId) {
- document.getElementById('loginSection').style.display = 'none';
- document.getElementById('dashboard').style.display = 'block';
- document.getElementById('logoutBtn').style.display = 'block';
- loadDashboard();
- loadDHCPConfig();
- loadDNSConfig();
- }
- // Auto Refresh
- function toggleAutoRefresh() {
- autoRefreshEnabled = !autoRefreshEnabled;
- const btn = document.getElementById('autoRefreshBtn');
-
- if (autoRefreshEnabled) {
- btn.textContent = '▶️ 自动刷新: 开';
- autoRefreshInterval = setInterval(loadClients, 10000); // 每10秒刷新
- } else {
- btn.textContent = '⏸️ 自动刷新: 关';
- clearInterval(autoRefreshInterval);
- }
- }
- // Login
- document.getElementById('loginForm').addEventListener('submit', async (e) => {
- e.preventDefault();
-
- const username = document.getElementById('username').value;
- const password = document.getElementById('password').value;
-
- try {
- const response = await fetch('/api/login', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ username, password })
- });
-
- const data = await response.json();
-
- if (response.ok) {
- sessionId = data.session_id;
- localStorage.setItem('session_id', sessionId);
- document.getElementById('loginSection').style.display = 'none';
- document.getElementById('dashboard').style.display = 'block';
- document.getElementById('logoutBtn').style.display = 'block';
- loadDashboard();
- loadDHCPConfig();
- loadDNSConfig();
- } else {
- alert(data.error || '登录失败');
- }
- } catch (error) {
- alert('登录失败:' + error.message);
- }
- });
- // Logout
- document.getElementById('logoutBtn').addEventListener('click', () => {
- sessionId = null;
- location.reload();
- });
- // Navigation
- document.querySelectorAll('nav a').forEach(link => {
- link.addEventListener('click', (e) => {
- e.preventDefault();
- const target = e.target.getAttribute('href').substring(1);
-
- document.querySelectorAll('section').forEach(section => {
- if (section.id !== 'loginSection') {
- section.style.display = 'none';
- }
- });
-
- document.getElementById(target).style.display = 'block';
-
- if (target === 'dashboard') loadDashboard();
- if (target === 'clients') loadClients();
- if (target === 'dhcp') loadDHCPConfig();
- if (target === 'dns') loadDNSConfig();
- if (target === 'settings') loadSystemInfo();
- });
- });
- // Load Dashboard
- async function loadDashboard() {
- try {
- const response = await fetch('/api/dashboard', {
- headers: { 'X-Session-ID': sessionId }
- });
-
- const data = await response.json();
-
- document.getElementById('activeLeases').textContent = data.active_leases || 0;
- document.getElementById('staticBindings').textContent = data.static_bindings || 0;
- document.getElementById('dnsRecords').textContent = data.dns_records || 0;
- document.getElementById('onlineDevices').textContent = data.online_devices || 0;
- } catch (error) {
- console.error('Failed to load dashboard:', error);
- }
- }
- // Load DHCP Clients
- async function loadClients() {
- try {
- const response = await fetch('/api/dhcp/leases', {
- headers: { 'X-Session-ID': sessionId }
- });
-
- const data = await response.json();
- const tbody = document.querySelector('#clientsTable tbody');
- tbody.innerHTML = '';
-
- if (!data.leases || data.leases.length === 0) {
- tbody.innerHTML = '<tr><td colspan="6" style="text-align:center;color:#999;">暂无客户端</td></tr>';
- updatePoolStats(0, 0);
- return;
- }
-
- const now = Math.floor(Date.now() / 1000);
- let activeCount = 0;
-
- data.leases.forEach(lease => {
- const expiresAt = lease.ExpiresAt || 0;
- const remaining = expiresAt - now;
- const isActive = remaining > 0;
- if (isActive) activeCount++;
-
- const row = document.createElement('tr');
-
- // MAC
- const macCell = document.createElement('td');
- macCell.textContent = lease.MAC || '-';
- row.appendChild(macCell);
-
- // IP
- const ipCell = document.createElement('td');
- ipCell.textContent = lease.IP || '-';
- row.appendChild(ipCell);
-
- // Hostname
- const hostCell = document.createElement('td');
- hostCell.textContent = lease.Hostname || '-';
- row.appendChild(hostCell);
-
- // Remaining time
- const remainCell = document.createElement('td');
- remainCell.textContent = isActive ? formatTimeRemaining(remaining) : '已过期';
- remainCell.style.color = isActive ? '#27ae60' : '#e74c3c';
- row.appendChild(remainCell);
-
- // Expiry time
- const expireCell = document.createElement('td');
- expireCell.textContent = expiresAt > 0 ? new Date(expiresAt * 1000).toLocaleString() : '-';
- row.appendChild(expireCell);
-
- // Status
- const statusCell = document.createElement('td');
- statusCell.innerHTML = isActive ? '<span class="status-active">● 在线</span>' : '<span class="status-expired">● 已过期</span>';
- row.appendChild(statusCell);
-
- tbody.appendChild(row);
- });
-
- updatePoolStats(activeCount, data.leases.length);
- } catch (error) {
- console.error('Failed to load clients:', error);
- }
- }
- // Update Pool Stats
- async function updatePoolStats(activeCount, totalCount) {
- try {
- const response = await fetch('/api/dhcp/config', {
- headers: { 'X-Session-ID': sessionId }
- });
-
- const data = await response.json();
- const cfg = data.config;
-
- if (cfg) {
- const startIP = cfg.ip_pool_start || '192.168.1.100';
- const endIP = cfg.ip_pool_end || '192.168.1.200';
-
- document.getElementById('poolRange').textContent = `${startIP} - ${endIP}`;
- document.getElementById('poolUsed').textContent = activeCount;
-
- // Calculate pool size
- const startBytes = ipToBytes(startIP);
- const endBytes = ipToBytes(endIP);
- const poolSize = bytesToIP(endBytes) - bytesToIP(startBytes) + 1;
- const available = poolSize - activeCount;
- const usage = poolSize > 0 ? Math.round((activeCount / poolSize) * 100) : 0;
-
- document.getElementById('poolAvailable').textContent = available;
- document.getElementById('poolUsage').textContent = usage + '%';
-
- const barFill = document.getElementById('poolBarFill');
- barFill.style.width = usage + '%';
- barFill.textContent = usage + '%';
-
- // Color based on usage
- if (usage > 90) {
- barFill.style.background = 'linear-gradient(90deg, #e74c3c, #c0392b)';
- } else if (usage > 70) {
- barFill.style.background = 'linear-gradient(90deg, #f39c12, #e67e22)';
- } else {
- barFill.style.background = 'linear-gradient(90deg, #27ae60, #2ecc71)';
- }
- }
- } catch (error) {
- console.error('Failed to update pool stats:', error);
- }
- }
- // Helper functions
- function ipToBytes(ip) {
- return ip.split('.').map(Number);
- }
- function bytesToIP(bytes) {
- return (bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3];
- }
- function formatTimeRemaining(seconds) {
- if (seconds <= 0) return '已过期';
-
- const days = Math.floor(seconds / 86400);
- const hours = Math.floor((seconds % 86400) / 3600);
- const minutes = Math.floor((seconds % 3600) / 60);
-
- if (days > 0) return `${days}天${hours}小时`;
- if (hours > 0) return `${hours}小时${minutes}分钟`;
- return `${minutes}分钟`;
- }
- // Load DHCP Config
- async function loadDHCPConfig() {
- try {
- const response = await fetch('/api/dhcp/config', {
- headers: { 'X-Session-ID': sessionId }
- });
-
- const data = await response.json();
- const cfg = data.config;
-
- if (cfg) {
- document.getElementById('dhcpEnabled').checked = cfg.enabled;
- document.getElementById('dhcpInterface').value = cfg.interface || '';
- document.getElementById('dhcpNetwork').value = cfg.network || '';
- document.getElementById('dhcpNetmask').value = cfg.netmask || '';
- document.getElementById('dhcpGateway').value = cfg.gateway || '';
- document.getElementById('dhcpDomain').value = cfg.domain_name || '';
- document.getElementById('dhcpPoolStart').value = cfg.ip_pool_start || '';
- document.getElementById('dhcpPoolEnd').value = cfg.ip_pool_end || '';
- document.getElementById('dhcpLeaseTime').value = cfg.lease_time || 86400;
- document.getElementById('dhcpDnsServers').value = (cfg.dns_servers || []).join(',');
- document.getElementById('dhcpExcludedIps').value = (cfg.excluded_ips || []).join(',');
- }
-
- loadBindings();
- } catch (error) {
- console.error('Failed to load DHCP config:', error);
- }
- }
- // Save DHCP Basic Config
- document.getElementById('dhcpBasicForm').addEventListener('submit', async (e) => {
- e.preventDefault();
-
- const config = {
- enabled: document.getElementById('dhcpEnabled').checked,
- interface: document.getElementById('dhcpInterface').value,
- network: document.getElementById('dhcpNetwork').value,
- netmask: document.getElementById('dhcpNetmask').value,
- gateway: document.getElementById('dhcpGateway').value,
- domain_name: document.getElementById('dhcpDomain').value
- };
-
- try {
- const response = await fetch('/api/dhcp/config', {
- method: 'PUT',
- headers: {
- 'X-Session-ID': sessionId,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(config)
- });
-
- const contentType = response.headers.get('content-type');
- if (!contentType || !contentType.includes('application/json')) {
- const text = await response.text();
- console.error('Non-JSON response:', text);
- alert('服务器返回了非 JSON 格式响应,请查看控制台');
- return;
- }
-
- const data = await response.json();
-
- if (response.ok) {
- alert('基础配置已保存');
- } else {
- alert('保存失败:' + (data.error || '未知错误'));
- }
- } catch (error) {
- console.error('Save error:', error);
- alert('保存失败:' + error.message);
- }
- });
- // Save DHCP Pool Config
- document.getElementById('dhcpPoolForm').addEventListener('submit', async (e) => {
- e.preventDefault();
-
- const config = {
- ip_pool_start: document.getElementById('dhcpPoolStart').value,
- ip_pool_end: document.getElementById('dhcpPoolEnd').value,
- lease_time: parseInt(document.getElementById('dhcpLeaseTime').value)
- };
-
- try {
- const response = await fetch('/api/dhcp/config', {
- method: 'PUT',
- headers: {
- 'X-Session-ID': sessionId,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(config)
- });
-
- const contentType = response.headers.get('content-type');
- if (!contentType || !contentType.includes('application/json')) {
- const text = await response.text();
- console.error('Non-JSON response:', text);
- alert('服务器返回了非 JSON 格式响应,请查看控制台');
- return;
- }
-
- const data = await response.json();
-
- if (response.ok) {
- alert('地址池配置已保存');
- } else {
- alert('保存失败:' + (data.error || '未知错误'));
- }
- } catch (error) {
- console.error('Save error:', error);
- alert('保存失败:' + error.message);
- }
- });
- // Save DHCP DNS Config
- document.getElementById('dhcpDnsForm').addEventListener('submit', async (e) => {
- e.preventDefault();
-
- const dnsServers = document.getElementById('dhcpDnsServers').value.split(',').map(s => s.trim()).filter(s => s);
-
- try {
- const response = await fetch('/api/dhcp/config', {
- method: 'PUT',
- headers: {
- 'X-Session-ID': sessionId,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ dns_servers: dnsServers })
- });
-
- const contentType = response.headers.get('content-type');
- if (!contentType || !contentType.includes('application/json')) {
- const text = await response.text();
- console.error('Non-JSON response:', text);
- alert('服务器返回了非 JSON 格式响应,请查看控制台');
- return;
- }
-
- const data = await response.json();
-
- if (response.ok) {
- alert('DNS 配置已保存');
- } else {
- alert('保存失败:' + (data.error || '未知错误'));
- }
- } catch (error) {
- console.error('Save error:', error);
- alert('保存失败:' + error.message);
- }
- });
- // Save DHCP Excluded IPs
- document.getElementById('dhcpExcludedForm').addEventListener('submit', async (e) => {
- e.preventDefault();
-
- const excludedIps = document.getElementById('dhcpExcludedIps').value.split(',').map(s => s.trim()).filter(s => s);
-
- try {
- const response = await fetch('/api/dhcp/config', {
- method: 'PUT',
- headers: {
- 'X-Session-ID': sessionId,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ excluded_ips: excludedIps })
- });
-
- const contentType = response.headers.get('content-type');
- if (!contentType || !contentType.includes('application/json')) {
- const text = await response.text();
- console.error('Non-JSON response:', text);
- alert('服务器返回了非 JSON 格式响应,请查看控制台');
- return;
- }
-
- const data = await response.json();
-
- if (response.ok) {
- alert('排除列表已保存');
- } else {
- alert('保存失败:' + (data.error || '未知错误'));
- }
- } catch (error) {
- console.error('Save error:', error);
- alert('保存失败:' + error.message);
- }
- });
- // Load Bindings
- async function loadBindings() {
- try {
- const response = await fetch('/api/dhcp/bindings', {
- headers: { 'X-Session-ID': sessionId }
- });
-
- const data = await response.json();
- const tbody = document.querySelector('#bindingsTable tbody');
- tbody.innerHTML = '';
-
- data.bindings.forEach(binding => {
- const row = tbody.insertRow();
- row.insertCell(0).textContent = binding.MAC;
- row.insertCell(1).textContent = binding.IP;
- row.insertCell(2).textContent = binding.Hostname || '-';
- row.insertCell(3).textContent = binding.Description || '-';
-
- const actionCell = row.insertCell(4);
- const deleteBtn = document.createElement('button');
- deleteBtn.textContent = '删除';
- deleteBtn.onclick = () => deleteBinding(binding.ID);
- actionCell.appendChild(deleteBtn);
- });
- } catch (error) {
- console.error('Failed to load bindings:', error);
- }
- }
- function showAddBindingForm() {
- // TODO: Implement add binding form
- alert('添加绑定功能开发中...');
- }
- async function deleteBinding(id) {
- if (!confirm('确定要删除这个绑定吗?')) return;
-
- try {
- const response = await fetch(`/api/dhcp/bindings/${id}`, {
- method: 'DELETE',
- headers: { 'X-Session-ID': sessionId }
- });
-
- if (response.ok) {
- loadBindings();
- } else {
- alert('删除失败');
- }
- } catch (error) {
- alert('删除失败:' + error.message);
- }
- }
- // Load DNS Config
- async function loadDNSConfig() {
- try {
- const response = await fetch('/api/dns/config', {
- headers: { 'X-Session-ID': sessionId }
- });
-
- const data = await response.json();
- const cfg = data.config;
-
- if (cfg) {
- document.getElementById('dnsEnabled').checked = cfg.enabled;
- document.getElementById('dnsListenAddr').value = cfg.listen_addr || '0.0.0.0';
- document.getElementById('dnsListenPort').value = cfg.listen_port || 53;
- document.getElementById('dnsRecursion').checked = cfg.recursion !== false;
- document.getElementById('dnsUpstream').value = (cfg.upstream || []).join(',');
- }
-
- loadDNSRecords();
- loadZones();
- loadLogs();
- } catch (error) {
- console.error('Failed to load DNS config:', error);
- }
- }
- // Save DNS Basic Config
- document.getElementById('dnsBasicForm').addEventListener('submit', async (e) => {
- e.preventDefault();
-
- const config = {
- enabled: document.getElementById('dnsEnabled').checked,
- listen_addr: document.getElementById('dnsListenAddr').value,
- listen_port: parseInt(document.getElementById('dnsListenPort').value),
- recursion: document.getElementById('dnsRecursion').checked
- };
-
- try {
- const response = await fetch('/api/dns/config', {
- method: 'PUT',
- headers: {
- 'X-Session-ID': sessionId,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify(config)
- });
-
- const contentType = response.headers.get('content-type');
- if (!contentType || !contentType.includes('application/json')) {
- const text = await response.text();
- console.error('Non-JSON response:', text);
- alert('服务器返回了非 JSON 格式响应,请查看控制台');
- return;
- }
-
- const data = await response.json();
-
- if (response.ok) {
- alert('DNS 基础配置已保存');
- } else {
- alert('保存失败:' + (data.error || '未知错误'));
- }
- } catch (error) {
- console.error('Save error:', error);
- alert('保存失败:' + error.message);
- }
- });
- // Save DNS Upstream
- document.getElementById('dnsUpstreamForm').addEventListener('submit', async (e) => {
- e.preventDefault();
-
- const upstream = document.getElementById('dnsUpstream').value.split(',').map(s => s.trim()).filter(s => s);
-
- try {
- const response = await fetch('/api/dns/config', {
- method: 'PUT',
- headers: {
- 'X-Session-ID': sessionId,
- 'Content-Type': 'application/json'
- },
- body: JSON.stringify({ upstream })
- });
-
- const contentType = response.headers.get('content-type');
- if (!contentType || !contentType.includes('application/json')) {
- const text = await response.text();
- console.error('Non-JSON response:', text);
- alert('服务器返回了非 JSON 格式响应,请查看控制台');
- return;
- }
-
- const data = await response.json();
-
- if (response.ok) {
- alert('上游 DNS 已保存');
- } else {
- alert('保存失败:' + (data.error || '未知错误'));
- }
- } catch (error) {
- console.error('Save error:', error);
- alert('保存失败:' + error.message);
- }
- });
- // Load DNS Records
- async function loadDNSRecords() {
- try {
- const response = await fetch('/api/dns/records', {
- headers: { 'X-Session-ID': sessionId }
- });
-
- const data = await response.json();
- const tbody = document.querySelector('#recordsTable tbody');
- tbody.innerHTML = '';
-
- data.records.forEach(record => {
- const row = tbody.insertRow();
- row.insertCell(0).textContent = record.Name;
- row.insertCell(1).textContent = record.Type;
- row.insertCell(2).textContent = record.Value;
- row.insertCell(3).textContent = record.TTL;
-
- const actionCell = row.insertCell(4);
- const deleteBtn = document.createElement('button');
- deleteBtn.textContent = '删除';
- deleteBtn.onclick = () => deleteRecord(record.ID);
- actionCell.appendChild(deleteBtn);
- });
- } catch (error) {
- console.error('Failed to load records:', error);
- }
- }
- function showAddRecordForm() {
- // TODO: Implement add record form
- alert('添加记录功能开发中...');
- }
- async function deleteRecord(id) {
- if (!confirm('确定要删除这条记录吗?')) return;
-
- try {
- const response = await fetch(`/api/dns/records/${id}`, {
- method: 'DELETE',
- headers: { 'X-Session-ID': sessionId }
- });
-
- if (response.ok) {
- loadDNSRecords();
- } else {
- alert('删除失败');
- }
- } catch (error) {
- alert('删除失败:' + error.message);
- }
- }
- // Load Zones
- async function loadZones() {
- try {
- const response = await fetch('/api/dns/zones', {
- headers: { 'X-Session-ID': sessionId }
- });
-
- const data = await response.json();
- const tbody = document.querySelector('#zonesTable tbody');
- tbody.innerHTML = '';
-
- data.zones.forEach(zone => {
- const row = tbody.insertRow();
- row.insertCell(0).textContent = zone.name;
- row.insertCell(1).textContent = zone.type;
- row.insertCell(2).textContent = zone.record_count;
-
- const actionCell = row.insertCell(3);
- const deleteBtn = document.createElement('button');
- deleteBtn.textContent = '删除';
- deleteBtn.onclick = () => deleteZone(zone.ID);
- actionCell.appendChild(deleteBtn);
- });
- } catch (error) {
- console.error('Failed to load zones:', error);
- }
- }
- function showAddZoneForm() {
- // TODO: Implement add zone form
- alert('添加区域功能开发中...');
- }
- async function deleteZone(id) {
- if (!confirm('确定要删除这个区域吗?')) return;
-
- try {
- const response = await fetch(`/api/dns/zones/${id}`, {
- method: 'DELETE',
- headers: { 'X-Session-ID': sessionId }
- });
-
- if (response.ok) {
- loadZones();
- } else {
- alert('删除失败');
- }
- } catch (error) {
- alert('删除失败:' + error.message);
- }
- }
- // Load Logs
- async function loadLogs() {
- try {
- const response = await fetch('/api/dns/logs', {
- headers: { 'X-Session-ID': sessionId }
- });
-
- const data = await response.json();
- const tbody = document.querySelector('#logsTable tbody');
- tbody.innerHTML = '';
-
- data.logs.forEach(log => {
- const row = tbody.insertRow();
- row.insertCell(0).textContent = new Date(log.Timestamp * 1000).toLocaleString();
- row.insertCell(1).textContent = log.ClientIP;
- row.insertCell(2).textContent = log.QueryName;
- row.insertCell(3).textContent = log.QueryType;
- row.insertCell(4).textContent = log.Response || '-';
- });
- } catch (error) {
- console.error('Failed to load logs:', error);
- }
- }
- // Load System Info
- async function loadSystemInfo() {
- try {
- const response = await fetch('/api/config', {
- headers: { 'X-Session-ID': sessionId }
- });
-
- const data = await response.json();
- // TODO: Update system info display
- } catch (error) {
- console.error('Failed to load system info:', error);
- }
- }
- // Export Config
- async function exportConfig() {
- try {
- const response = await fetch('/api/config/export', {
- headers: { 'X-Session-ID': sessionId }
- });
-
- const blob = await response.blob();
- const url = window.URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = 'dhcp-dns-config.json';
- a.click();
- window.URL.revokeObjectURL(url);
- } catch (error) {
- alert('导出失败:' + error.message);
- }
- }
- // Import Config
- async function importConfig() {
- const input = document.createElement('input');
- input.type = 'file';
- input.accept = '.json';
-
- input.onchange = async (e) => {
- const file = e.target.files[0];
- if (!file) return;
-
- const formData = new FormData();
- formData.append('config', file);
-
- try {
- const response = await fetch('/api/config/import', {
- method: 'POST',
- headers: { 'X-Session-ID': sessionId },
- body: formData
- });
-
- if (response.ok) {
- alert('配置已导入');
- } else {
- const data = await response.json();
- alert('导入失败:' + data.error);
- }
- } catch (error) {
- alert('导入失败:' + error.message);
- }
- };
-
- input.click();
- }
- // Restart Service
- async function restartService() {
- if (!confirm('确定要重启服务吗?服务将短暂中断。')) return;
-
- try {
- const response = await fetch('/api/service/restart', {
- method: 'POST',
- headers: { 'X-Session-ID': sessionId }
- });
-
- if (response.ok) {
- alert('服务重启请求已发送');
- } else {
- const data = await response.json();
- alert('重启失败:' + data.error);
- }
- } catch (error) {
- alert('重启失败:' + error.message);
- }
- }
|