221
src/app/api/stats/route.ts
Archivo normal
221
src/app/api/stats/route.ts
Archivo normal
@@ -0,0 +1,221 @@
|
||||
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 })
|
||||
}
|
||||
}
|
||||
Referencia en una nueva incidencia
Block a user