소스 수정
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -37,6 +37,11 @@
|
||||
<span>Server Status</span>
|
||||
</NuxtLink>
|
||||
|
||||
<NuxtLink to="/settings/thresholds" class="nav-item nav-sub-item" :class="{ active: route.path === '/settings/thresholds' }">
|
||||
<span class="icon">⚙️</span>
|
||||
<span>Thresholds</span>
|
||||
</NuxtLink>
|
||||
|
||||
<div class="nav-group-title">이상감지</div>
|
||||
|
||||
<NuxtLink to="/anomaly/short-term" class="nav-item nav-sub-item" :class="{ active: route.path === '/anomaly/short-term' }">
|
||||
@@ -58,13 +63,6 @@
|
||||
<span class="icon">📉</span>
|
||||
<span>추세 분석</span>
|
||||
</NuxtLink>
|
||||
|
||||
<div class="nav-group-title">설정</div>
|
||||
|
||||
<NuxtLink to="/settings/thresholds" class="nav-item nav-sub-item" :class="{ active: route.path === '/settings/thresholds' }">
|
||||
<span class="icon">⚙️</span>
|
||||
<span>임계값 설정</span>
|
||||
</NuxtLink>
|
||||
</nav>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
@@ -138,9 +138,20 @@ const thresholds = ref(JSON.parse(JSON.stringify(defaultThresholds)))
|
||||
|
||||
async function fetchThresholds() {
|
||||
try {
|
||||
const data = await $fetch('/api/settings/thresholds')
|
||||
const data = await $fetch('/api/settings/thresholds') as any
|
||||
if (data) {
|
||||
thresholds.value = data as typeof defaultThresholds
|
||||
// 기본값과 병합 (API 응답이 불완전할 수 있음)
|
||||
thresholds.value = {
|
||||
server: {
|
||||
cpu: { ...defaultThresholds.server.cpu, ...data.server?.cpu },
|
||||
memory: { ...defaultThresholds.server.memory, ...data.server?.memory },
|
||||
disk: { ...defaultThresholds.server.disk, ...data.server?.disk }
|
||||
},
|
||||
container: {
|
||||
cpu: { ...defaultThresholds.container.cpu, ...data.container?.cpu },
|
||||
memory: { ...defaultThresholds.container.memory, ...data.container?.memory }
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch thresholds:', err)
|
||||
|
||||
Reference in New Issue
Block a user