시스템 모니터
This commit is contained in:
30
backend/api/server/history/container-list.get.ts
Normal file
30
backend/api/server/history/container-list.get.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { getDb } from '../../../utils/db'
|
||||
|
||||
export default defineEventHandler((event) => {
|
||||
const query = getQuery(event)
|
||||
const targetId = query.target_id as string
|
||||
|
||||
if (!targetId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'target_id is required'
|
||||
})
|
||||
}
|
||||
|
||||
const db = getDb()
|
||||
|
||||
// 최신 수집 시간 기준 컨테이너 목록
|
||||
const containers = db.prepare(`
|
||||
SELECT DISTINCT container_name
|
||||
FROM server_containers
|
||||
WHERE target_id = ?
|
||||
AND collected_at = (
|
||||
SELECT MAX(collected_at)
|
||||
FROM server_containers
|
||||
WHERE target_id = ?
|
||||
)
|
||||
ORDER BY container_name ASC
|
||||
`).all(targetId, targetId)
|
||||
|
||||
return containers.map((c: any) => c.container_name)
|
||||
})
|
||||
57
backend/api/server/history/containers.get.ts
Normal file
57
backend/api/server/history/containers.get.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { getDb } from '../../../utils/db'
|
||||
|
||||
export default defineEventHandler((event) => {
|
||||
const query = getQuery(event)
|
||||
const targetId = query.target_id as string
|
||||
const period = (query.period as string) || '1h'
|
||||
|
||||
if (!targetId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'target_id is required'
|
||||
})
|
||||
}
|
||||
|
||||
const periodMap: Record<string, string> = {
|
||||
'1h': '-1 hour',
|
||||
'2h': '-2 hours',
|
||||
'3h': '-3 hours',
|
||||
'4h': '-4 hours',
|
||||
'5h': '-5 hours',
|
||||
'6h': '-6 hours',
|
||||
'12h': '-12 hours',
|
||||
'18h': '-18 hours',
|
||||
'24h': '-24 hours',
|
||||
'7d': '-7 days',
|
||||
'30d': '-30 days'
|
||||
}
|
||||
|
||||
const timeOffset = periodMap[period] || '-1 hour'
|
||||
|
||||
const db = getDb()
|
||||
|
||||
const containers = db.prepare(`
|
||||
SELECT
|
||||
container_id,
|
||||
container_name,
|
||||
container_status,
|
||||
cpu_percent,
|
||||
memory_usage,
|
||||
memory_limit,
|
||||
memory_percent,
|
||||
uptime,
|
||||
network_rx,
|
||||
network_tx,
|
||||
collected_at
|
||||
FROM server_containers
|
||||
WHERE target_id = ?
|
||||
AND collected_at >= datetime('now', 'localtime', ?)
|
||||
ORDER BY collected_at ASC, container_name ASC
|
||||
`).all(targetId, timeOffset)
|
||||
|
||||
return {
|
||||
target_id: targetId,
|
||||
period,
|
||||
data: containers
|
||||
}
|
||||
})
|
||||
29
backend/api/server/history/disk-list.get.ts
Normal file
29
backend/api/server/history/disk-list.get.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { getDb } from '../../../utils/db'
|
||||
|
||||
export default defineEventHandler((event) => {
|
||||
const query = getQuery(event)
|
||||
const targetId = query.target_id as string
|
||||
|
||||
if (!targetId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'target_id is required'
|
||||
})
|
||||
}
|
||||
|
||||
const db = getDb()
|
||||
|
||||
// 최신 수집 시간 기준 디스크 목록 (물리 디스크만)
|
||||
const disks = db.prepare(`
|
||||
SELECT DISTINCT device_name, mount_point, fs_type, disk_total, disk_used, disk_percent
|
||||
FROM server_disks
|
||||
WHERE target_id = ?
|
||||
AND collected_at = (SELECT MAX(collected_at) FROM server_disks WHERE target_id = ?)
|
||||
AND device_name NOT LIKE '%loop%'
|
||||
AND mount_point NOT LIKE '%/snap%'
|
||||
AND fs_type NOT IN ('tmpfs', 'squashfs', 'overlay')
|
||||
ORDER BY mount_point ASC
|
||||
`).all(targetId, targetId)
|
||||
|
||||
return disks
|
||||
})
|
||||
54
backend/api/server/history/disks.get.ts
Normal file
54
backend/api/server/history/disks.get.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { getDb } from '../../../utils/db'
|
||||
|
||||
export default defineEventHandler((event) => {
|
||||
const query = getQuery(event)
|
||||
const targetId = query.target_id as string
|
||||
const period = (query.period as string) || '1h'
|
||||
|
||||
if (!targetId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'target_id is required'
|
||||
})
|
||||
}
|
||||
|
||||
const periodMap: Record<string, string> = {
|
||||
'1h': '-1 hour',
|
||||
'2h': '-2 hours',
|
||||
'3h': '-3 hours',
|
||||
'4h': '-4 hours',
|
||||
'5h': '-5 hours',
|
||||
'6h': '-6 hours',
|
||||
'12h': '-12 hours',
|
||||
'18h': '-18 hours',
|
||||
'24h': '-24 hours',
|
||||
'7d': '-7 days',
|
||||
'30d': '-30 days'
|
||||
}
|
||||
|
||||
const timeOffset = periodMap[period] || '-1 hour'
|
||||
|
||||
const db = getDb()
|
||||
|
||||
const disks = db.prepare(`
|
||||
SELECT
|
||||
disk_id,
|
||||
device_name,
|
||||
mount_point,
|
||||
fs_type,
|
||||
disk_total,
|
||||
disk_used,
|
||||
disk_percent,
|
||||
collected_at
|
||||
FROM server_disks
|
||||
WHERE target_id = ?
|
||||
AND collected_at >= datetime('now', 'localtime', ?)
|
||||
ORDER BY collected_at ASC, mount_point ASC
|
||||
`).all(targetId, timeOffset)
|
||||
|
||||
return {
|
||||
target_id: targetId,
|
||||
period,
|
||||
data: disks
|
||||
}
|
||||
})
|
||||
32
backend/api/server/history/latest.get.ts
Normal file
32
backend/api/server/history/latest.get.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { getDb } from '../../../utils/db'
|
||||
|
||||
export default defineEventHandler((event) => {
|
||||
const query = getQuery(event)
|
||||
const targetId = query.target_id as string
|
||||
|
||||
if (!targetId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'target_id is required'
|
||||
})
|
||||
}
|
||||
|
||||
const db = getDb()
|
||||
|
||||
// 최신 스냅샷
|
||||
const snapshot = db.prepare(`
|
||||
SELECT
|
||||
s.*,
|
||||
t.server_name,
|
||||
t.server_ip,
|
||||
t.glances_url,
|
||||
t.collect_interval
|
||||
FROM server_snapshots s
|
||||
JOIN server_targets t ON s.target_id = t.target_id
|
||||
WHERE s.target_id = ?
|
||||
ORDER BY s.collected_at DESC
|
||||
LIMIT 1
|
||||
`).get(targetId)
|
||||
|
||||
return snapshot || null
|
||||
})
|
||||
54
backend/api/server/history/networks.get.ts
Normal file
54
backend/api/server/history/networks.get.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { getDb } from '../../../utils/db'
|
||||
|
||||
export default defineEventHandler((event) => {
|
||||
const query = getQuery(event)
|
||||
const targetId = query.target_id as string
|
||||
const period = (query.period as string) || '1h'
|
||||
|
||||
if (!targetId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'target_id is required'
|
||||
})
|
||||
}
|
||||
|
||||
const periodMap: Record<string, string> = {
|
||||
'1h': '-1 hour',
|
||||
'2h': '-2 hours',
|
||||
'3h': '-3 hours',
|
||||
'4h': '-4 hours',
|
||||
'5h': '-5 hours',
|
||||
'6h': '-6 hours',
|
||||
'12h': '-12 hours',
|
||||
'18h': '-18 hours',
|
||||
'24h': '-24 hours',
|
||||
'7d': '-7 days',
|
||||
'30d': '-30 days'
|
||||
}
|
||||
|
||||
const timeOffset = periodMap[period] || '-1 hour'
|
||||
|
||||
const db = getDb()
|
||||
|
||||
const networks = db.prepare(`
|
||||
SELECT
|
||||
network_id,
|
||||
interface_name,
|
||||
bytes_recv,
|
||||
bytes_sent,
|
||||
speed_recv,
|
||||
speed_sent,
|
||||
is_up,
|
||||
collected_at
|
||||
FROM server_networks
|
||||
WHERE target_id = ?
|
||||
AND collected_at >= datetime('now', 'localtime', ?)
|
||||
ORDER BY collected_at ASC, interface_name ASC
|
||||
`).all(targetId, timeOffset)
|
||||
|
||||
return {
|
||||
target_id: targetId,
|
||||
period,
|
||||
data: networks
|
||||
}
|
||||
})
|
||||
59
backend/api/server/history/snapshots.get.ts
Normal file
59
backend/api/server/history/snapshots.get.ts
Normal file
@@ -0,0 +1,59 @@
|
||||
import { getDb } from '../../../utils/db'
|
||||
|
||||
export default defineEventHandler((event) => {
|
||||
const query = getQuery(event)
|
||||
const targetId = query.target_id as string
|
||||
const period = (query.period as string) || '1h'
|
||||
|
||||
if (!targetId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'target_id is required'
|
||||
})
|
||||
}
|
||||
|
||||
// 기간별 시간 계산
|
||||
const periodMap: Record<string, string> = {
|
||||
'1h': '-1 hour',
|
||||
'2h': '-2 hours',
|
||||
'3h': '-3 hours',
|
||||
'4h': '-4 hours',
|
||||
'5h': '-5 hours',
|
||||
'6h': '-6 hours',
|
||||
'12h': '-12 hours',
|
||||
'18h': '-18 hours',
|
||||
'24h': '-24 hours',
|
||||
'7d': '-7 days',
|
||||
'30d': '-30 days'
|
||||
}
|
||||
|
||||
const timeOffset = periodMap[period] || '-1 hour'
|
||||
|
||||
const db = getDb()
|
||||
|
||||
const snapshots = db.prepare(`
|
||||
SELECT
|
||||
snapshot_id,
|
||||
cpu_percent,
|
||||
cpu_temp,
|
||||
load_percent,
|
||||
memory_percent,
|
||||
memory_used,
|
||||
memory_total,
|
||||
swap_percent,
|
||||
swap_used,
|
||||
swap_total,
|
||||
is_online,
|
||||
collected_at
|
||||
FROM server_snapshots
|
||||
WHERE target_id = ?
|
||||
AND collected_at >= datetime('now', 'localtime', ?)
|
||||
ORDER BY collected_at ASC
|
||||
`).all(targetId, timeOffset)
|
||||
|
||||
return {
|
||||
target_id: targetId,
|
||||
period,
|
||||
data: snapshots
|
||||
}
|
||||
})
|
||||
6
backend/api/server/scheduler/start.post.ts
Normal file
6
backend/api/server/scheduler/start.post.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { startServerScheduler } from '../../../utils/server-scheduler'
|
||||
|
||||
export default defineEventHandler(() => {
|
||||
startServerScheduler()
|
||||
return { success: true, message: 'Server scheduler started' }
|
||||
})
|
||||
6
backend/api/server/scheduler/stop.post.ts
Normal file
6
backend/api/server/scheduler/stop.post.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { stopServerScheduler } from '../../../utils/server-scheduler'
|
||||
|
||||
export default defineEventHandler(() => {
|
||||
stopServerScheduler()
|
||||
return { success: true, message: 'Server scheduler stopped' }
|
||||
})
|
||||
5
backend/api/server/status.get.ts
Normal file
5
backend/api/server/status.get.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { getServerSchedulerStatus } from '../../utils/server-scheduler'
|
||||
|
||||
export default defineEventHandler(() => {
|
||||
return getServerSchedulerStatus()
|
||||
})
|
||||
27
backend/api/server/targets/[id].delete.ts
Normal file
27
backend/api/server/targets/[id].delete.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { getDb } from '../../../utils/db'
|
||||
import { refreshServerTimer } from '../../../utils/server-scheduler'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const targetId = getRouterParam(event, 'id')
|
||||
|
||||
if (!targetId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'target_id is required'
|
||||
})
|
||||
}
|
||||
|
||||
// 스케줄러에서 제거
|
||||
refreshServerTimer(Number(targetId))
|
||||
|
||||
const db = getDb()
|
||||
|
||||
const result = db.prepare(`
|
||||
DELETE FROM server_targets WHERE target_id = ?
|
||||
`).run(targetId)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
changes: result.changes
|
||||
}
|
||||
})
|
||||
36
backend/api/server/targets/[id].put.ts
Normal file
36
backend/api/server/targets/[id].put.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { getDb } from '../../../utils/db'
|
||||
import { refreshServerTimer } from '../../../utils/server-scheduler'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const targetId = getRouterParam(event, 'id')
|
||||
const body = await readBody(event)
|
||||
const { server_name, server_ip, glances_url, is_active, collect_interval } = body
|
||||
|
||||
if (!targetId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'target_id is required'
|
||||
})
|
||||
}
|
||||
|
||||
const db = getDb()
|
||||
|
||||
const result = db.prepare(`
|
||||
UPDATE server_targets
|
||||
SET server_name = ?,
|
||||
server_ip = ?,
|
||||
glances_url = ?,
|
||||
is_active = ?,
|
||||
collect_interval = ?,
|
||||
updated_at = datetime('now', 'localtime')
|
||||
WHERE target_id = ?
|
||||
`).run(server_name, server_ip, glances_url, is_active ? 1 : 0, collect_interval || 60, targetId)
|
||||
|
||||
// 스케줄러에 반영
|
||||
refreshServerTimer(Number(targetId))
|
||||
|
||||
return {
|
||||
success: true,
|
||||
changes: result.changes
|
||||
}
|
||||
})
|
||||
12
backend/api/server/targets/index.get.ts
Normal file
12
backend/api/server/targets/index.get.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { getDb } from '../../../utils/db'
|
||||
|
||||
export default defineEventHandler(() => {
|
||||
const db = getDb()
|
||||
|
||||
const targets = db.prepare(`
|
||||
SELECT * FROM server_targets
|
||||
ORDER BY target_id ASC
|
||||
`).all()
|
||||
|
||||
return targets
|
||||
})
|
||||
33
backend/api/server/targets/index.post.ts
Normal file
33
backend/api/server/targets/index.post.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { getDb } from '../../../utils/db'
|
||||
import { refreshServerTimer } from '../../../utils/server-scheduler'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const body = await readBody(event)
|
||||
const { server_name, server_ip, glances_url, is_active = 1, collect_interval = 60 } = body
|
||||
|
||||
if (!server_name || !server_ip || !glances_url) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
message: 'server_name, server_ip, glances_url are required'
|
||||
})
|
||||
}
|
||||
|
||||
const db = getDb()
|
||||
|
||||
const result = db.prepare(`
|
||||
INSERT INTO server_targets (server_name, server_ip, glances_url, is_active, collect_interval)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
`).run(server_name, server_ip, glances_url, is_active ? 1 : 0, collect_interval)
|
||||
|
||||
const targetId = result.lastInsertRowid as number
|
||||
|
||||
// 스케줄러에 반영
|
||||
if (is_active) {
|
||||
refreshServerTimer(targetId)
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
target_id: targetId
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user