221 líneas
5.6 KiB
TypeScript
221 líneas
5.6 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server'
|
|
import { exec } from 'child_process'
|
|
import { promisify } from 'util'
|
|
import fs from 'fs/promises'
|
|
|
|
const execAsync = promisify(exec)
|
|
|
|
export async function GET() {
|
|
try {
|
|
// Get server statistics
|
|
const stats = await Promise.all([
|
|
getCPUUsage(),
|
|
getMemoryUsage(),
|
|
getDiskUsage(),
|
|
getNetworkStats(),
|
|
getCSFStats(),
|
|
getSystemUptime()
|
|
])
|
|
|
|
const [cpu, memory, disk, network, csf, uptime] = stats
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
data: {
|
|
cpu_usage: cpu,
|
|
memory_usage: memory,
|
|
disk_usage: disk,
|
|
network: network,
|
|
csf_stats: csf,
|
|
uptime: uptime,
|
|
timestamp: new Date().toISOString()
|
|
}
|
|
})
|
|
|
|
} catch (error: any) {
|
|
console.error('Stats API error:', error)
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: `Failed to retrieve statistics: ${error.message}`
|
|
}, { status: 500 })
|
|
}
|
|
}
|
|
|
|
async function getCPUUsage(): Promise<number> {
|
|
try {
|
|
// Get CPU usage from /proc/loadavg
|
|
const { stdout } = await execAsync('cat /proc/loadavg')
|
|
const loadAvg = parseFloat(stdout.split(' ')[0])
|
|
|
|
// Convert to percentage (assuming single core, adjust for multi-core)
|
|
const { stdout: cpuInfo } = await execAsync('nproc')
|
|
const cores = parseInt(cpuInfo.trim())
|
|
|
|
return Math.min((loadAvg / cores) * 100, 100)
|
|
} catch {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
async function getMemoryUsage(): Promise<{ used: number; total: number; percentage: number }> {
|
|
try {
|
|
const { stdout } = await execAsync('free -b')
|
|
const lines = stdout.split('\\n')
|
|
const memLine = lines[1].split(/\\s+/)
|
|
|
|
const total = parseInt(memLine[1])
|
|
const used = parseInt(memLine[2])
|
|
const percentage = (used / total) * 100
|
|
|
|
return {
|
|
used,
|
|
total,
|
|
percentage: Math.round(percentage * 100) / 100
|
|
}
|
|
} catch {
|
|
return { used: 0, total: 0, percentage: 0 }
|
|
}
|
|
}
|
|
|
|
async function getDiskUsage(): Promise<{ used: number; total: number; percentage: number }> {
|
|
try {
|
|
const { stdout } = await execAsync('df -B1 / | tail -1')
|
|
const parts = stdout.split(/\\s+/)
|
|
|
|
const total = parseInt(parts[1])
|
|
const used = parseInt(parts[2])
|
|
const percentage = (used / total) * 100
|
|
|
|
return {
|
|
used,
|
|
total,
|
|
percentage: Math.round(percentage * 100) / 100
|
|
}
|
|
} catch {
|
|
return { used: 0, total: 0, percentage: 0 }
|
|
}
|
|
}
|
|
|
|
async function getNetworkStats(): Promise<{ in_bytes: number; out_bytes: number; connections: number }> {
|
|
try {
|
|
// Get network interface stats
|
|
const { stdout } = await execAsync('cat /proc/net/dev | grep -E "(eth0|ens|enp)" | head -1')
|
|
|
|
if (stdout) {
|
|
const parts = stdout.split(/\\s+/)
|
|
const inBytes = parseInt(parts[1]) || 0
|
|
const outBytes = parseInt(parts[9]) || 0
|
|
|
|
// Get active connections
|
|
const { stdout: connStdout } = await execAsync('netstat -tn | grep ESTABLISHED | wc -l')
|
|
const connections = parseInt(connStdout.trim()) || 0
|
|
|
|
return {
|
|
in_bytes: inBytes,
|
|
out_bytes: outBytes,
|
|
connections
|
|
}
|
|
}
|
|
|
|
return { in_bytes: 0, out_bytes: 0, connections: 0 }
|
|
} catch {
|
|
return { in_bytes: 0, out_bytes: 0, connections: 0 }
|
|
}
|
|
}
|
|
|
|
async function getCSFStats(): Promise<{
|
|
status: string;
|
|
blocked_ips: number;
|
|
allowed_ips: number;
|
|
temp_blocks: number;
|
|
rules_count: number;
|
|
}> {
|
|
try {
|
|
// Check CSF status
|
|
let status = 'unknown'
|
|
try {
|
|
await execAsync('/usr/local/csf/bin/csf --status >/dev/null 2>&1')
|
|
status = 'active'
|
|
} catch {
|
|
status = 'inactive'
|
|
}
|
|
|
|
// Count rules in files
|
|
let blockedIps = 0
|
|
let allowedIps = 0
|
|
let tempBlocks = 0
|
|
|
|
try {
|
|
const denyContent = await fs.readFile('/etc/csf/csf.deny', 'utf-8')
|
|
blockedIps = denyContent.split('\\n').filter(line =>
|
|
line.trim() && !line.trim().startsWith('#')
|
|
).length
|
|
} catch {}
|
|
|
|
try {
|
|
const allowContent = await fs.readFile('/etc/csf/csf.allow', 'utf-8')
|
|
allowedIps = allowContent.split('\\n').filter(line =>
|
|
line.trim() && !line.trim().startsWith('#')
|
|
).length
|
|
} catch {}
|
|
|
|
try {
|
|
const { stdout } = await execAsync('/usr/local/csf/bin/csf --temp 2>/dev/null | grep -c "Temporary" || echo "0"')
|
|
tempBlocks = parseInt(stdout.trim()) || 0
|
|
} catch {}
|
|
|
|
return {
|
|
status,
|
|
blocked_ips: blockedIps,
|
|
allowed_ips: allowedIps,
|
|
temp_blocks: tempBlocks,
|
|
rules_count: blockedIps + allowedIps
|
|
}
|
|
} catch {
|
|
return {
|
|
status: 'unknown',
|
|
blocked_ips: 0,
|
|
allowed_ips: 0,
|
|
temp_blocks: 0,
|
|
rules_count: 0
|
|
}
|
|
}
|
|
}
|
|
|
|
async function getSystemUptime(): Promise<number> {
|
|
try {
|
|
const { stdout } = await execAsync('cat /proc/uptime')
|
|
const uptime = parseFloat(stdout.split(' ')[0])
|
|
return Math.floor(uptime)
|
|
} catch {
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// Real-time stats endpoint
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const body = await request.json()
|
|
const { interval = 5000 } = body // Default 5 seconds
|
|
|
|
// This would typically be handled by WebSocket
|
|
// For now, return current stats
|
|
const stats = await GET()
|
|
const response = await stats.json()
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
data: {
|
|
...response.data,
|
|
interval,
|
|
next_update: Date.now() + interval
|
|
}
|
|
})
|
|
|
|
} catch (error: any) {
|
|
return NextResponse.json({
|
|
success: false,
|
|
error: `Failed to get real-time stats: ${error.message}`
|
|
}, { status: 500 })
|
|
}
|
|
} |