diff --git a/backend/api/server/history/containers.get.ts b/backend/api/server/history/containers.get.ts
index 8332829..e7cb705 100644
--- a/backend/api/server/history/containers.get.ts
+++ b/backend/api/server/history/containers.get.ts
@@ -45,7 +45,7 @@ export default defineEventHandler(async (event) => {
collected_at
FROM server_containers
WHERE target_id = $1
- AND collected_at >= NOW() - INTERVAL '${interval}'
+ AND collected_at::timestamp >= NOW() - INTERVAL '${interval}'
ORDER BY collected_at ASC, container_name ASC
`, [targetId])
} catch (e) {
diff --git a/backend/api/server/history/disks.get.ts b/backend/api/server/history/disks.get.ts
index 029541e..da7a6b7 100644
--- a/backend/api/server/history/disks.get.ts
+++ b/backend/api/server/history/disks.get.ts
@@ -42,7 +42,7 @@ export default defineEventHandler(async (event) => {
collected_at
FROM server_disks
WHERE target_id = $1
- AND collected_at >= NOW() - INTERVAL '${interval}'
+ AND collected_at::timestamp >= NOW() - INTERVAL '${interval}'
ORDER BY collected_at ASC, mount_point ASC
`, [targetId])
} catch (e) {
diff --git a/backend/api/server/history/networks.get.ts b/backend/api/server/history/networks.get.ts
index c47f7c0..7bd2ea6 100644
--- a/backend/api/server/history/networks.get.ts
+++ b/backend/api/server/history/networks.get.ts
@@ -42,7 +42,7 @@ export default defineEventHandler(async (event) => {
collected_at
FROM server_networks
WHERE target_id = $1
- AND collected_at >= NOW() - INTERVAL '${interval}'
+ AND collected_at::timestamp >= NOW() - INTERVAL '${interval}'
ORDER BY collected_at ASC, interface_name ASC
`, [targetId])
} catch (e) {
diff --git a/backend/routes/_ws.ts b/backend/routes/_ws.ts
index 1a7710f..66b277b 100644
--- a/backend/routes/_ws.ts
+++ b/backend/routes/_ws.ts
@@ -171,9 +171,10 @@ async function getServerDashboard() {
const serverStatuses: any[] = []
const summaryServers = { total: servers.length, normal: 0, warning: 0, critical: 0, danger: 0, offline: 0 }
+ const summaryContainers = { total: 0, normal: 0, warning: 0, critical: 0, danger: 0, stopped: 0 }
for (const server of servers) {
- // 최신 로그
+ // 최신 스냅샷
const snapshot = await queryOne(`
SELECT cpu_percent, memory_percent, collected_at
FROM server_snapshots
@@ -209,6 +210,68 @@ async function getServerDashboard() {
else if (serverLevel === 'warning') summaryServers.warning++
else summaryServers.normal++
+ // 컨테이너 조회 (최신 데이터만)
+ const containers: any[] = []
+ const containerSummary = { total: 0, normal: 0, warning: 0, critical: 0, stopped: 0 }
+
+ if (!isOffline) {
+ // 서버별 최신 컨테이너 (container_name별 최신 1건)
+ const latestContainers = await query(`
+ SELECT DISTINCT ON (container_name)
+ container_name as name,
+ container_status as status,
+ cpu_percent,
+ memory_usage,
+ memory_limit,
+ uptime,
+ network_rx,
+ network_tx
+ FROM server_containers
+ WHERE target_id = $1
+ ORDER BY container_name, collected_at DESC
+ `, [server.target_id])
+
+ for (const c of latestContainers) {
+ let containerLevel = 'normal'
+
+ if (c.status !== 'running') {
+ containerLevel = 'stopped'
+ containerSummary.stopped++
+ } else {
+ const cCpuLevel = getLevel(Number(c.cpu_percent), thresholds.container?.cpu || { warning: 80, critical: 90, danger: 95 })
+ const memPct = c.memory_limit ? (Number(c.memory_usage) / Number(c.memory_limit)) * 100 : 0
+ const cMemLevel = getLevel(memPct, thresholds.container?.memory || { warning: 80, critical: 90, danger: 95 })
+ containerLevel = getHighestLevel([cCpuLevel, cMemLevel])
+
+ if (containerLevel === 'danger') containerSummary.critical++
+ else if (containerLevel === 'critical') containerSummary.critical++
+ else if (containerLevel === 'warning') containerSummary.warning++
+ else containerSummary.normal++
+ }
+
+ containers.push({
+ name: c.name,
+ status: c.status || 'unknown',
+ level: containerLevel,
+ cpu_percent: c.cpu_percent,
+ memory_usage: c.memory_usage,
+ memory_limit: c.memory_limit,
+ uptime: c.uptime,
+ network_rx: c.network_rx,
+ network_tx: c.network_tx
+ })
+
+ containerSummary.total++
+ summaryContainers.total++
+ }
+
+ // 전체 컨테이너 요약 집계
+ summaryContainers.normal += containerSummary.normal
+ summaryContainers.warning += containerSummary.warning
+ summaryContainers.critical += containerSummary.critical
+ summaryContainers.stopped += containerSummary.stopped
+ }
+
serverStatuses.push({
target_id: server.target_id,
server_name: server.server_name,
@@ -219,7 +282,9 @@ async function getServerDashboard() {
memory_level: memLevel,
disk_percent: snapshot?.disk_percent ?? null,
disk_level: diskLevel,
- last_collected: lastCollected
+ last_collected: lastCollected,
+ containers: containers,
+ container_summary: containerSummary
})
}
@@ -227,7 +292,7 @@ async function getServerDashboard() {
serverStatuses.sort((a, b) => a.server_name.localeCompare(b.server_name))
return {
- summary: { servers: summaryServers },
+ summary: { servers: summaryServers, containers: summaryContainers },
servers: serverStatuses,
timestamp: new Date().toISOString()
}
diff --git a/frontend/components/SidebarNav.vue b/frontend/components/SidebarNav.vue
index d2806e6..016a9ea 100644
--- a/frontend/components/SidebarNav.vue
+++ b/frontend/components/SidebarNav.vue
@@ -37,6 +37,11 @@
Server Status
+