refactor: remove unused monitoring and system info features
This commit is contained in:
@@ -9,8 +9,6 @@ Frontend application for Super-frpc - a frpc instance integrated management syst
|
||||
- User management (create, edit, delete users)
|
||||
- Session management (view and delete active sessions)
|
||||
- System logs viewing with filtering capabilities
|
||||
- Real-time system monitoring (CPU, memory, disk, network)
|
||||
- System information display
|
||||
- Role-based access control (superuser, admin, visitor)
|
||||
- Responsive design with modern UI
|
||||
|
||||
@@ -49,8 +47,7 @@ frontend/
|
||||
│ ├── Users.vue # User management
|
||||
│ ├── Sessions.vue # Session management
|
||||
│ ├── Logs.vue # System logs
|
||||
│ ├── Monitor.vue # System monitoring
|
||||
│ └── SystemInfo.vue # System information
|
||||
│ └── Settings.vue # Settings
|
||||
├── logger.js # Logging utility
|
||||
├── api-backend.md # Backend API documentation
|
||||
└── README.md # This file
|
||||
|
||||
@@ -112,13 +112,4 @@ export const logApi = {
|
||||
getLogs: (level) => api.post('/logMgr/list', { level })
|
||||
};
|
||||
|
||||
export const monitorApi = {
|
||||
getSystemStats: () => api.get('/monitor/stats')
|
||||
};
|
||||
|
||||
export default api;
|
||||
|
||||
export const TODO_API_NOTES = {
|
||||
monitorStats: 'TODO: /monitor/stats - 获取系统监控数据 API 未在 api-backend.md 中定义',
|
||||
systemInfo: 'TODO: /system/info - 获取系统信息 API 未在 api-backend.md 中定义'
|
||||
};
|
||||
|
||||
@@ -53,8 +53,6 @@ export default {
|
||||
{ path: '/users', title: 'User Management', icon: 'fas fa-users', permission: ['superuser'] },
|
||||
{ path: '/sessions', title: 'Session Management', icon: 'fas fa-key', permission: ['superuser', 'admin'] },
|
||||
{ path: '/logs', title: 'System Logs', icon: 'fas fa-file-alt', permission: ['superuser', 'admin'] },
|
||||
{ path: '/monitor', title: 'System Monitoring', icon: 'fas fa-chart-bar', permission: ['superuser', 'admin', 'visitor'] },
|
||||
{ path: '/system-info', title: 'System Information', icon: 'fas fa-info-circle', permission: ['superuser', 'admin', 'visitor'] },
|
||||
{ path: '/settings', title: 'Settings', icon: 'fas fa-cog', permission: ['superuser', 'admin'] }
|
||||
];
|
||||
|
||||
|
||||
@@ -43,18 +43,6 @@ const routes = [
|
||||
component: () => import('../views/Logs.vue'),
|
||||
meta: { requiresAuth: true, permission: ['superuser', 'admin'] }
|
||||
},
|
||||
{
|
||||
path: 'monitor',
|
||||
name: 'Monitor',
|
||||
component: () => import('../views/Monitor.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: 'system-info',
|
||||
name: 'SystemInfo',
|
||||
component: () => import('../views/SystemInfo.vue'),
|
||||
meta: { requiresAuth: true }
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
name: 'Settings',
|
||||
|
||||
@@ -1,317 +0,0 @@
|
||||
<template>
|
||||
<div class="monitor-page">
|
||||
<div class="page-header">
|
||||
<h2>System Monitoring</h2>
|
||||
</div>
|
||||
<div class="monitor-grid">
|
||||
<div class="monitor-card">
|
||||
<div class="card-header">
|
||||
<h3>CPU Usage</h3>
|
||||
<span class="card-value">{{ cpuUsage }}%</span>
|
||||
</div>
|
||||
<div class="chart-container">
|
||||
<canvas ref="cpuChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="monitor-card">
|
||||
<div class="card-header">
|
||||
<h3>Memory Usage</h3>
|
||||
<span class="card-value">{{ memoryUsage }}%</span>
|
||||
</div>
|
||||
<div class="chart-container">
|
||||
<canvas ref="memoryChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="monitor-card">
|
||||
<div class="card-header">
|
||||
<h3>Disk Usage</h3>
|
||||
<span class="card-value">{{ diskUsage }}%</span>
|
||||
</div>
|
||||
<div class="chart-container">
|
||||
<canvas ref="diskChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="monitor-card">
|
||||
<div class="card-header">
|
||||
<h3>Network Traffic</h3>
|
||||
</div>
|
||||
<div class="network-stats">
|
||||
<div class="network-item">
|
||||
<span class="network-label">Upload:</span>
|
||||
<span class="network-value">{{ formatBytes(networkUpload) }}/s</span>
|
||||
</div>
|
||||
<div class="network-item">
|
||||
<span class="network-label">Download:</span>
|
||||
<span class="network-value">{{ formatBytes(networkDownload) }}/s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart-container">
|
||||
<canvas ref="networkChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import { Chart, registerables } from 'chart.js';
|
||||
import { monitorApi } from '../api/index.js';
|
||||
import { showNotification, formatBytes } from '../utils/functions.js';
|
||||
|
||||
Chart.register(...registerables);
|
||||
|
||||
export default {
|
||||
name: 'Monitor',
|
||||
setup() {
|
||||
const cpuUsage = ref(0);
|
||||
const memoryUsage = ref(0);
|
||||
const diskUsage = ref(0);
|
||||
const networkUpload = ref(0);
|
||||
const networkDownload = ref(0);
|
||||
|
||||
const cpuChart = ref(null);
|
||||
const memoryChart = ref(null);
|
||||
const diskChart = ref(null);
|
||||
const networkChart = ref(null);
|
||||
|
||||
let cpuChartInstance = null;
|
||||
let memoryChartInstance = null;
|
||||
let diskChartInstance = null;
|
||||
let networkChartInstance = null;
|
||||
let updateInterval = null;
|
||||
|
||||
const createChart = (canvas, data, label, color) => {
|
||||
return new Chart(canvas, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: Array(20).fill(''),
|
||||
datasets: [{
|
||||
label: label,
|
||||
data: data,
|
||||
borderColor: color,
|
||||
backgroundColor: color + '20',
|
||||
fill: true,
|
||||
tension: 0.4,
|
||||
pointRadius: 0,
|
||||
borderWidth: 2
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
max: 100,
|
||||
ticks: {
|
||||
callback: (value) => value + '%'
|
||||
}
|
||||
},
|
||||
x: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
animation: {
|
||||
duration: 0
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const createNetworkChart = (canvas, uploadData, downloadData) => {
|
||||
return new Chart(canvas, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: Array(20).fill(''),
|
||||
datasets: [
|
||||
{
|
||||
label: '上传',
|
||||
data: uploadData,
|
||||
borderColor: '#52c41a',
|
||||
backgroundColor: '#52c41a20',
|
||||
fill: true,
|
||||
tension: 0.4,
|
||||
pointRadius: 0,
|
||||
borderWidth: 2
|
||||
},
|
||||
{
|
||||
label: '下载',
|
||||
data: downloadData,
|
||||
borderColor: '#1890ff',
|
||||
backgroundColor: '#1890ff20',
|
||||
fill: true,
|
||||
tension: 0.4,
|
||||
pointRadius: 0,
|
||||
borderWidth: 2
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: true,
|
||||
position: 'top'
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
callback: (value) => formatBytes(value) + '/s'
|
||||
}
|
||||
},
|
||||
x: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
animation: {
|
||||
duration: 0
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const updateStats = async () => {
|
||||
try {
|
||||
const result = await monitorApi.getSystemStats();
|
||||
const stats = result.data;
|
||||
|
||||
cpuUsage.value = stats.cpuUsage || 0;
|
||||
memoryUsage.value = stats.memoryUsage || 0;
|
||||
diskUsage.value = stats.diskUsage || 0;
|
||||
networkUpload.value = stats.networkUpload || 0;
|
||||
networkDownload.value = stats.networkDownload || 0;
|
||||
|
||||
if (cpuChartInstance) {
|
||||
cpuChartInstance.data.datasets[0].data.push(cpuUsage.value);
|
||||
cpuChartInstance.data.datasets[0].data.shift();
|
||||
cpuChartInstance.update('none');
|
||||
}
|
||||
|
||||
if (memoryChartInstance) {
|
||||
memoryChartInstance.data.datasets[0].data.push(memoryUsage.value);
|
||||
memoryChartInstance.data.datasets[0].data.shift();
|
||||
memoryChartInstance.update('none');
|
||||
}
|
||||
|
||||
if (diskChartInstance) {
|
||||
diskChartInstance.data.datasets[0].data.push(diskUsage.value);
|
||||
diskChartInstance.data.datasets[0].data.shift();
|
||||
diskChartInstance.update('none');
|
||||
}
|
||||
|
||||
if (networkChartInstance) {
|
||||
networkChartInstance.data.datasets[0].data.push(networkUpload.value);
|
||||
networkChartInstance.data.datasets[0].data.shift();
|
||||
networkChartInstance.data.datasets[1].data.push(networkDownload.value);
|
||||
networkChartInstance.data.datasets[1].data.shift();
|
||||
networkChartInstance.update('none');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to update stats:', error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const initialData = Array(20).fill(0);
|
||||
|
||||
cpuChartInstance = createChart(cpuChart.value, [...initialData], 'CPU', '#ff4d4f');
|
||||
memoryChartInstance = createChart(memoryChart.value, [...initialData], '内存', '#1890ff');
|
||||
diskChartInstance = createChart(diskChart.value, [...initialData], '磁盘', '#faad14');
|
||||
networkChartInstance = createNetworkChart(networkChart.value, [...initialData], [...initialData]);
|
||||
|
||||
updateStats();
|
||||
updateInterval = setInterval(updateStats, 2000);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (updateInterval) {
|
||||
clearInterval(updateInterval);
|
||||
}
|
||||
if (cpuChartInstance) cpuChartInstance.destroy();
|
||||
if (memoryChartInstance) memoryChartInstance.destroy();
|
||||
if (diskChartInstance) diskChartInstance.destroy();
|
||||
if (networkChartInstance) networkChartInstance.destroy();
|
||||
});
|
||||
|
||||
return {
|
||||
cpuUsage,
|
||||
memoryUsage,
|
||||
diskUsage,
|
||||
networkUpload,
|
||||
networkDownload,
|
||||
cpuChart,
|
||||
memoryChart,
|
||||
diskChart,
|
||||
networkChart,
|
||||
formatBytes
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import '../styles/common.css';
|
||||
|
||||
.monitor-page {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.monitor-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.monitor-card {
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.card-value {
|
||||
font-family: 'SF Pro Display', 'SF Pro Icons', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-size: 28px;
|
||||
font-weight: 400;
|
||||
line-height: 1.14;
|
||||
letter-spacing: 0.196px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
height: 200px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.network-stats {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.network-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-family: 'SF Pro Text', 'SF Pro Icons', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.29;
|
||||
letter-spacing: -0.224px;
|
||||
}
|
||||
|
||||
.network-label {
|
||||
color: var(--text-tertiary);
|
||||
}
|
||||
|
||||
.network-value {
|
||||
color: var(--text-color);
|
||||
font-weight: 500;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
</style>
|
||||
@@ -1,204 +0,0 @@
|
||||
<template>
|
||||
<div class="system-info-page">
|
||||
<div class="page-header">
|
||||
<h2>System Information</h2>
|
||||
</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon"><i class="fas fa-laptop" aria-hidden="true"></i></span>
|
||||
<h3>Operating System</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="info-item">
|
||||
<span class="info-label">Operating System:</span>
|
||||
<span class="info-value">{{ systemInfo.os || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Hostname:</span>
|
||||
<span class="info-value">{{ systemInfo.hostname || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Kernel Version:</span>
|
||||
<span class="info-value">{{ systemInfo.kernel || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon"><i class="fas fa-tools" aria-hidden="true"></i></span>
|
||||
<h3>Processor</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="info-item">
|
||||
<span class="info-label">Processor Model:</span>
|
||||
<span class="info-value">{{ systemInfo.cpuModel || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">CPU Cores:</span>
|
||||
<span class="info-value">{{ systemInfo.cpuCores || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">CPU Frequency:</span>
|
||||
<span class="info-value">{{ systemInfo.cpuFrequency || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon"><i class="fas fa-memory" aria-hidden="true"></i></span>
|
||||
<h3>Memory</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="info-item">
|
||||
<span class="info-label">Total Memory:</span>
|
||||
<span class="info-value">{{ formatBytes(systemInfo.totalMemory || 0) }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Available Memory:</span>
|
||||
<span class="info-value">{{ formatBytes(systemInfo.availableMemory || 0) }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Used Memory:</span>
|
||||
<span class="info-value">{{ formatBytes(systemInfo.usedMemory || 0) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon"><i class="fas fa-hdd" aria-hidden="true"></i></span>
|
||||
<h3>Disk</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="info-item">
|
||||
<span class="info-label">Total Disk Space:</span>
|
||||
<span class="info-value">{{ formatBytes(systemInfo.totalDisk || 0) }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Available Disk Space:</span>
|
||||
<span class="info-value">{{ formatBytes(systemInfo.availableDisk || 0) }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Used Disk Space:</span>
|
||||
<span class="info-value">{{ formatBytes(systemInfo.usedDisk || 0) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon"><i class="fas fa-globe" aria-hidden="true"></i></span>
|
||||
<h3>Network</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="info-item">
|
||||
<span class="info-label">IP Address:</span>
|
||||
<span class="info-value">{{ systemInfo.ipAddress || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">MAC Address:</span>
|
||||
<span class="info-value">{{ systemInfo.macAddress || '-' }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Network Interface:</span>
|
||||
<span class="info-value">{{ systemInfo.networkInterface || '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-card">
|
||||
<div class="card-header">
|
||||
<span class="card-icon"><i class="fas fa-clock" aria-hidden="true"></i></span>
|
||||
<h3>Uptime</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="info-item">
|
||||
<span class="info-label">System Uptime:</span>
|
||||
<span class="info-value">{{ formatUptime(systemInfo.uptime || 0) }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span class="info-label">Boot Time:</span>
|
||||
<span class="info-value">{{ formatDate(systemInfo.bootTime || 0) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { systemInfoApi } from '../api/index.js';
|
||||
import { showNotification, formatBytes, formatDate } from '../utils/functions.js';
|
||||
|
||||
export default {
|
||||
name: 'SystemInfo',
|
||||
setup() {
|
||||
const systemInfo = ref({});
|
||||
|
||||
const loadSystemInfo = async () => {
|
||||
try {
|
||||
const result = await systemInfoApi.getSystemInfo();
|
||||
systemInfo.value = result.data;
|
||||
} catch (error) {
|
||||
showNotification('Load System Info Failed', 'error');
|
||||
}
|
||||
};
|
||||
|
||||
const formatUptime = (seconds) => {
|
||||
const days = Math.floor(seconds / 86400);
|
||||
const hours = Math.floor((seconds % 86400) / 3600);
|
||||
const minutes = Math.floor((seconds % 3600) / 60);
|
||||
|
||||
const parts = [];
|
||||
if (days > 0) parts.push(`${days} day${days > 1 ? 's' : ''}`);
|
||||
if (hours > 0) parts.push(`${hours} hour${hours > 1 ? 's' : ''}`);
|
||||
if (minutes > 0) parts.push(`${minutes} minute${minutes > 1 ? 's' : ''}`);
|
||||
|
||||
return parts.length > 0 ? parts.join(' ') : '0 minute';
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadSystemInfo();
|
||||
});
|
||||
|
||||
return {
|
||||
systemInfo,
|
||||
formatBytes,
|
||||
formatDate,
|
||||
formatUptime
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import '../styles/common.css';
|
||||
|
||||
.system-info-page {
|
||||
padding: 24px;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 24px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user