Files
csf-web/src/app/api/stats/route.ts
2025-09-20 18:42:04 +02:00

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 })
}
}