소스 수정
This commit is contained in:
@@ -87,8 +87,8 @@ async function detectAnomalies(targetId: number, serverName: string) {
|
||||
const cpuChange = prevCpuAvg > 1 ? ((currCpuAvg - prevCpuAvg) / prevCpuAvg) * 100 : currCpuAvg - prevCpuAvg
|
||||
const memChange = prevMemAvg > 1 ? ((currMemAvg - prevMemAvg) / prevMemAvg) * 100 : currMemAvg - prevMemAvg
|
||||
|
||||
// CPU 단기 변화율 체크
|
||||
if (Math.abs(cpuChange) >= SHORT_TERM_THRESHOLD) {
|
||||
// CPU 단기 변화율 체크 (증가만 감지)
|
||||
if (cpuChange >= SHORT_TERM_THRESHOLD) {
|
||||
const recentExists = await queryOne(`
|
||||
SELECT 1 FROM anomaly_logs
|
||||
WHERE target_id = $1 AND detect_type = 'short-term' AND metric = 'CPU'
|
||||
@@ -97,20 +97,19 @@ async function detectAnomalies(targetId: number, serverName: string) {
|
||||
`, [targetId])
|
||||
|
||||
if (!recentExists) {
|
||||
const level = Math.abs(cpuChange) >= 100 ? 'danger' : 'warning'
|
||||
const direction = cpuChange >= 0 ? '증가' : '감소'
|
||||
const message = `CPU ${direction} 감지 (${prevCpuAvg.toFixed(1)}% → ${currCpuAvg.toFixed(1)}%)`
|
||||
const level = cpuChange >= 100 ? 'danger' : 'warning'
|
||||
const message = `CPU 급증 감지 (${prevCpuAvg.toFixed(1)}% → ${currCpuAvg.toFixed(1)}%)`
|
||||
|
||||
await execute(`
|
||||
INSERT INTO anomaly_logs (target_id, server_name, detect_type, metric, level, current_value, threshold_value, message)
|
||||
VALUES ($1, $2, 'short-term', 'CPU', $3, $4, $5, $6)
|
||||
`, [targetId, serverName, level, currCpuAvg, cpuChange, message])
|
||||
console.log(`[${now}] 🚨 [${serverName}] 단기변화율 이상감지: CPU ${cpuChange.toFixed(1)}% (${level})`)
|
||||
console.log(`[${now}] 🚨 [${serverName}] 단기변화율 이상감지: CPU +${cpuChange.toFixed(1)}% (${level})`)
|
||||
}
|
||||
}
|
||||
|
||||
// Memory 단기 변화율 체크
|
||||
if (Math.abs(memChange) >= SHORT_TERM_THRESHOLD) {
|
||||
// Memory 단기 변화율 체크 (증가만 감지)
|
||||
if (memChange >= SHORT_TERM_THRESHOLD) {
|
||||
const recentExists = await queryOne(`
|
||||
SELECT 1 FROM anomaly_logs
|
||||
WHERE target_id = $1 AND detect_type = 'short-term' AND metric = 'Memory'
|
||||
@@ -119,15 +118,14 @@ async function detectAnomalies(targetId: number, serverName: string) {
|
||||
`, [targetId])
|
||||
|
||||
if (!recentExists) {
|
||||
const level = Math.abs(memChange) >= 100 ? 'danger' : 'warning'
|
||||
const direction = memChange >= 0 ? '증가' : '감소'
|
||||
const message = `Memory ${direction} 감지 (${prevMemAvg.toFixed(1)}% → ${currMemAvg.toFixed(1)}%)`
|
||||
const level = memChange >= 100 ? 'danger' : 'warning'
|
||||
const message = `Memory 급증 감지 (${prevMemAvg.toFixed(1)}% → ${currMemAvg.toFixed(1)}%)`
|
||||
|
||||
await execute(`
|
||||
INSERT INTO anomaly_logs (target_id, server_name, detect_type, metric, level, current_value, threshold_value, message)
|
||||
VALUES ($1, $2, 'short-term', 'Memory', $3, $4, $5, $6)
|
||||
`, [targetId, serverName, level, currMemAvg, memChange, message])
|
||||
console.log(`[${now}] 🚨 [${serverName}] 단기변화율 이상감지: Memory ${memChange.toFixed(1)}% (${level})`)
|
||||
console.log(`[${now}] 🚨 [${serverName}] 단기변화율 이상감지: Memory +${memChange.toFixed(1)}% (${level})`)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -163,8 +161,8 @@ async function detectAnomalies(targetId: number, serverName: string) {
|
||||
const cpuZscore = cpuStd > 0.1 ? (currCpu - cpuAvg) / cpuStd : 0
|
||||
const memZscore = memStd > 0.1 ? (currMem - memAvg) / memStd : 0
|
||||
|
||||
// CPU Z-Score 체크
|
||||
if (Math.abs(cpuZscore) >= WARNING_Z) {
|
||||
// CPU Z-Score 체크 (높은 경우만 감지)
|
||||
if (cpuZscore >= WARNING_Z) {
|
||||
const recentExists = await queryOne(`
|
||||
SELECT 1 FROM anomaly_logs
|
||||
WHERE target_id = $1 AND detect_type = 'zscore' AND metric = 'CPU'
|
||||
@@ -173,9 +171,8 @@ async function detectAnomalies(targetId: number, serverName: string) {
|
||||
`, [targetId])
|
||||
|
||||
if (!recentExists) {
|
||||
const level = Math.abs(cpuZscore) >= DANGER_Z ? 'danger' : 'warning'
|
||||
const direction = cpuZscore >= 0 ? '높음' : '낮음'
|
||||
const message = `CPU 평균 대비 ${Math.abs(cpuZscore).toFixed(1)}σ ${direction} (평균: ${cpuAvg.toFixed(1)}%, 현재: ${currCpu.toFixed(1)}%)`
|
||||
const level = cpuZscore >= DANGER_Z ? 'danger' : 'warning'
|
||||
const message = `CPU 평균 대비 ${cpuZscore.toFixed(1)}σ 높음 (평균: ${cpuAvg.toFixed(1)}%, 현재: ${currCpu.toFixed(1)}%)`
|
||||
|
||||
await execute(`
|
||||
INSERT INTO anomaly_logs (target_id, server_name, detect_type, metric, level, current_value, threshold_value, message)
|
||||
@@ -185,8 +182,8 @@ async function detectAnomalies(targetId: number, serverName: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Memory Z-Score 체크
|
||||
if (Math.abs(memZscore) >= WARNING_Z) {
|
||||
// Memory Z-Score 체크 (높은 경우만 감지)
|
||||
if (memZscore >= WARNING_Z) {
|
||||
const recentExists = await queryOne(`
|
||||
SELECT 1 FROM anomaly_logs
|
||||
WHERE target_id = $1 AND detect_type = 'zscore' AND metric = 'Memory'
|
||||
@@ -195,9 +192,8 @@ async function detectAnomalies(targetId: number, serverName: string) {
|
||||
`, [targetId])
|
||||
|
||||
if (!recentExists) {
|
||||
const level = Math.abs(memZscore) >= DANGER_Z ? 'danger' : 'warning'
|
||||
const direction = memZscore >= 0 ? '높음' : '낮음'
|
||||
const message = `Memory 평균 대비 ${Math.abs(memZscore).toFixed(1)}σ ${direction} (평균: ${memAvg.toFixed(1)}%, 현재: ${currMem.toFixed(1)}%)`
|
||||
const level = memZscore >= DANGER_Z ? 'danger' : 'warning'
|
||||
const message = `Memory 평균 대비 ${memZscore.toFixed(1)}σ 높음 (평균: ${memAvg.toFixed(1)}%, 현재: ${currMem.toFixed(1)}%)`
|
||||
|
||||
await execute(`
|
||||
INSERT INTO anomaly_logs (target_id, server_name, detect_type, metric, level, current_value, threshold_value, message)
|
||||
@@ -253,7 +249,8 @@ async function detectAnomalies(targetId: number, serverName: string) {
|
||||
const cpuDeviation = cpuStd > 0.1 ? (currCpu - cpuAvg) / cpuStd : 0
|
||||
const memDeviation = memStd > 0.1 ? (currMem - memAvg) / memStd : 0
|
||||
|
||||
if (Math.abs(cpuDeviation) >= DEVIATION_THRESHOLD) {
|
||||
// CPU 베이스라인 체크 (높은 경우만 감지)
|
||||
if (cpuDeviation >= DEVIATION_THRESHOLD) {
|
||||
const recentExists = await queryOne(`
|
||||
SELECT 1 FROM anomaly_logs
|
||||
WHERE target_id = $1 AND detect_type = 'baseline' AND metric = 'CPU'
|
||||
@@ -262,10 +259,9 @@ async function detectAnomalies(targetId: number, serverName: string) {
|
||||
`, [targetId])
|
||||
|
||||
if (!recentExists) {
|
||||
const level = Math.abs(cpuDeviation) >= 3.0 ? 'danger' : 'warning'
|
||||
const direction = cpuDeviation >= 0 ? '높음' : '낮음'
|
||||
const level = cpuDeviation >= 3.0 ? 'danger' : 'warning'
|
||||
const dayLabel = isWeekend ? '주말' : '평일'
|
||||
const message = `CPU ${dayLabel} ${currentHour}시 베이스라인 대비 ${Math.abs(cpuDeviation).toFixed(1)}σ ${direction}`
|
||||
const message = `CPU ${dayLabel} ${currentHour}시 베이스라인 대비 ${cpuDeviation.toFixed(1)}σ 높음`
|
||||
|
||||
await execute(`
|
||||
INSERT INTO anomaly_logs (target_id, server_name, detect_type, metric, level, current_value, threshold_value, message)
|
||||
@@ -275,7 +271,8 @@ async function detectAnomalies(targetId: number, serverName: string) {
|
||||
}
|
||||
}
|
||||
|
||||
if (Math.abs(memDeviation) >= DEVIATION_THRESHOLD) {
|
||||
// Memory 베이스라인 체크 (높은 경우만 감지)
|
||||
if (memDeviation >= DEVIATION_THRESHOLD) {
|
||||
const recentExists = await queryOne(`
|
||||
SELECT 1 FROM anomaly_logs
|
||||
WHERE target_id = $1 AND detect_type = 'baseline' AND metric = 'Memory'
|
||||
@@ -284,10 +281,9 @@ async function detectAnomalies(targetId: number, serverName: string) {
|
||||
`, [targetId])
|
||||
|
||||
if (!recentExists) {
|
||||
const level = Math.abs(memDeviation) >= 3.0 ? 'danger' : 'warning'
|
||||
const direction = memDeviation >= 0 ? '높음' : '낮음'
|
||||
const level = memDeviation >= 3.0 ? 'danger' : 'warning'
|
||||
const dayLabel = isWeekend ? '주말' : '평일'
|
||||
const message = `Memory ${dayLabel} ${currentHour}시 베이스라인 대비 ${Math.abs(memDeviation).toFixed(1)}σ ${direction}`
|
||||
const message = `Memory ${dayLabel} ${currentHour}시 베이스라인 대비 ${memDeviation.toFixed(1)}σ 높음`
|
||||
|
||||
await execute(`
|
||||
INSERT INTO anomaly_logs (target_id, server_name, detect_type, metric, level, current_value, threshold_value, message)
|
||||
|
||||
@@ -263,8 +263,8 @@ onUnmounted(() => { if (chart) chart.destroy() })
|
||||
|
||||
.pros-cons { display: flex; gap: 16px; }
|
||||
.pros, .cons { flex: 1; padding: 12px; border-radius: 8px; }
|
||||
.pros { background: #f0fdf4; border: 1px solid #86efac; }
|
||||
.cons { background: #fef2f2; border: 1px solid #fca5a5; }
|
||||
.pros { background: var(--container-normal-bg, #f0fdf4); border: 1px solid var(--container-normal-border, #86efac); }
|
||||
.cons { background: var(--container-danger-bg, #fef2f2); border: 1px solid var(--container-danger-border, #fca5a5); }
|
||||
.pros h4, .cons h4 { margin: 0 0 8px 0; font-size: 13px; }
|
||||
.pros ul, .cons ul { margin: 0; padding-left: 18px; font-size: 12px; color: var(--text-secondary); }
|
||||
|
||||
@@ -304,14 +304,14 @@ onUnmounted(() => { if (chart) chart.destroy() })
|
||||
|
||||
.log-list { max-height: 300px; overflow-y: auto; }
|
||||
.log-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 6px; margin-bottom: 4px; font-size: 12px; }
|
||||
.log-item.warning { background: #fefce8; }
|
||||
.log-item.danger { background: #fef2f2; }
|
||||
.log-item.warning { background: var(--log-warning-bg, #fefce8); }
|
||||
.log-item.danger { background: var(--log-danger-bg, #fef2f2); }
|
||||
.log-time { color: var(--text-muted); font-family: monospace; min-width: 70px; }
|
||||
.log-level { font-size: 14px; }
|
||||
.log-server { font-weight: 600; color: var(--text-primary); min-width: 80px; }
|
||||
.log-metric { color: var(--text-secondary); min-width: 50px; }
|
||||
.log-value { font-weight: 600; min-width: 50px; }
|
||||
.log-item.warning .log-value { color: #ca8a04; }
|
||||
.log-item.danger .log-value { color: #dc2626; }
|
||||
.log-item.warning .log-value { color: var(--log-warning-text, #ca8a04); }
|
||||
.log-item.danger .log-value { color: var(--log-danger-text, #dc2626); }
|
||||
.log-msg { color: var(--text-muted); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
</style>
|
||||
|
||||
@@ -293,8 +293,8 @@ onUnmounted(() => {
|
||||
|
||||
.pros-cons { display: flex; gap: 16px; }
|
||||
.pros, .cons { flex: 1; padding: 12px; border-radius: 8px; }
|
||||
.pros { background: #f0fdf4; border: 1px solid #86efac; }
|
||||
.cons { background: #fef2f2; border: 1px solid #fca5a5; }
|
||||
.pros { background: var(--container-normal-bg, #f0fdf4); border: 1px solid var(--container-normal-border, #86efac); }
|
||||
.cons { background: var(--container-danger-bg, #fef2f2); border: 1px solid var(--container-danger-border, #fca5a5); }
|
||||
.pros h4, .cons h4 { margin: 0 0 8px 0; font-size: 13px; }
|
||||
.pros ul, .cons ul { margin: 0; padding-left: 18px; font-size: 12px; color: var(--text-secondary); }
|
||||
.pros li, .cons li { margin-bottom: 2px; }
|
||||
@@ -338,14 +338,14 @@ onUnmounted(() => {
|
||||
|
||||
.log-list { max-height: 300px; overflow-y: auto; }
|
||||
.log-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 6px; margin-bottom: 4px; font-size: 12px; }
|
||||
.log-item.warning { background: #fefce8; }
|
||||
.log-item.danger { background: #fef2f2; }
|
||||
.log-item.warning { background: var(--log-warning-bg, #fefce8); }
|
||||
.log-item.danger { background: var(--log-danger-bg, #fef2f2); }
|
||||
.log-time { color: var(--text-muted); font-family: monospace; min-width: 70px; }
|
||||
.log-level { font-size: 14px; }
|
||||
.log-server { font-weight: 600; color: var(--text-primary); min-width: 80px; }
|
||||
.log-metric { color: var(--text-secondary); min-width: 50px; }
|
||||
.log-value { font-weight: 600; min-width: 50px; }
|
||||
.log-item.warning .log-value { color: #ca8a04; }
|
||||
.log-item.danger .log-value { color: #dc2626; }
|
||||
.log-item.warning .log-value { color: var(--log-warning-text, #ca8a04); }
|
||||
.log-item.danger .log-value { color: var(--log-danger-text, #dc2626); }
|
||||
.log-msg { color: var(--text-muted); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
</style>
|
||||
|
||||
@@ -263,8 +263,8 @@ onUnmounted(() => { if (chart) chart.destroy() })
|
||||
|
||||
.pros-cons { display: flex; gap: 16px; }
|
||||
.pros, .cons { flex: 1; padding: 12px; border-radius: 8px; }
|
||||
.pros { background: #f0fdf4; border: 1px solid #86efac; }
|
||||
.cons { background: #fef2f2; border: 1px solid #fca5a5; }
|
||||
.pros { background: var(--container-normal-bg, #f0fdf4); border: 1px solid var(--container-normal-border, #86efac); }
|
||||
.cons { background: var(--container-danger-bg, #fef2f2); border: 1px solid var(--container-danger-border, #fca5a5); }
|
||||
.pros h4, .cons h4 { margin: 0 0 8px 0; font-size: 13px; }
|
||||
.pros ul, .cons ul { margin: 0; padding-left: 18px; font-size: 12px; color: var(--text-secondary); }
|
||||
|
||||
@@ -305,14 +305,14 @@ onUnmounted(() => { if (chart) chart.destroy() })
|
||||
|
||||
.log-list { max-height: 300px; overflow-y: auto; }
|
||||
.log-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 6px; margin-bottom: 4px; font-size: 12px; }
|
||||
.log-item.warning { background: #fefce8; }
|
||||
.log-item.danger { background: #fef2f2; }
|
||||
.log-item.warning { background: var(--log-warning-bg, #fefce8); }
|
||||
.log-item.danger { background: var(--log-danger-bg, #fef2f2); }
|
||||
.log-time { color: var(--text-muted); font-family: monospace; min-width: 70px; }
|
||||
.log-level { font-size: 14px; }
|
||||
.log-server { font-weight: 600; color: var(--text-primary); min-width: 80px; }
|
||||
.log-metric { color: var(--text-secondary); min-width: 50px; }
|
||||
.log-value { font-weight: 600; min-width: 70px; }
|
||||
.log-item.warning .log-value { color: #ca8a04; }
|
||||
.log-item.danger .log-value { color: #dc2626; }
|
||||
.log-item.warning .log-value { color: var(--log-warning-text, #ca8a04); }
|
||||
.log-item.danger .log-value { color: var(--log-danger-text, #dc2626); }
|
||||
.log-msg { color: var(--text-muted); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
</style>
|
||||
|
||||
@@ -258,8 +258,8 @@ onUnmounted(() => { if (chart) chart.destroy() })
|
||||
|
||||
.pros-cons { display: flex; gap: 16px; }
|
||||
.pros, .cons { flex: 1; padding: 12px; border-radius: 8px; }
|
||||
.pros { background: #f0fdf4; border: 1px solid #86efac; }
|
||||
.cons { background: #fef2f2; border: 1px solid #fca5a5; }
|
||||
.pros { background: var(--container-normal-bg, #f0fdf4); border: 1px solid var(--container-normal-border, #86efac); }
|
||||
.cons { background: var(--container-danger-bg, #fef2f2); border: 1px solid var(--container-danger-border, #fca5a5); }
|
||||
.pros h4, .cons h4 { margin: 0 0 8px 0; font-size: 13px; }
|
||||
.pros ul, .cons ul { margin: 0; padding-left: 18px; font-size: 12px; color: var(--text-secondary); }
|
||||
|
||||
@@ -299,14 +299,14 @@ onUnmounted(() => { if (chart) chart.destroy() })
|
||||
|
||||
.log-list { max-height: 300px; overflow-y: auto; }
|
||||
.log-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 6px; margin-bottom: 4px; font-size: 12px; }
|
||||
.log-item.warning { background: #fefce8; }
|
||||
.log-item.danger { background: #fef2f2; }
|
||||
.log-item.warning { background: var(--log-warning-bg, #fefce8); }
|
||||
.log-item.danger { background: var(--log-danger-bg, #fef2f2); }
|
||||
.log-time { color: var(--text-muted); font-family: monospace; min-width: 70px; }
|
||||
.log-level { font-size: 14px; }
|
||||
.log-server { font-weight: 600; color: var(--text-primary); min-width: 80px; }
|
||||
.log-metric { color: var(--text-secondary); min-width: 50px; }
|
||||
.log-value { font-weight: 600; min-width: 60px; }
|
||||
.log-item.warning .log-value { color: #ca8a04; }
|
||||
.log-item.danger .log-value { color: #dc2626; }
|
||||
.log-item.warning .log-value { color: var(--log-warning-text, #ca8a04); }
|
||||
.log-item.danger .log-value { color: var(--log-danger-text, #dc2626); }
|
||||
.log-msg { color: var(--text-muted); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
</style>
|
||||
|
||||
@@ -73,6 +73,28 @@
|
||||
--fail-border: #f87171;
|
||||
--fail-text: #fca5a5;
|
||||
|
||||
/* 컨테이너 카드 색상 */
|
||||
--container-normal-bg: #1a3d1a;
|
||||
--container-normal-border: #2d5a2d;
|
||||
--container-warning-bg: #3d3a1a;
|
||||
--container-warning-border: #5a5a2d;
|
||||
--container-critical-bg: #3d2a1a;
|
||||
--container-critical-border: #5a3d2d;
|
||||
--container-danger-bg: #3d1a1a;
|
||||
--container-danger-border: #5a2d2d;
|
||||
|
||||
/* 로그 색상 */
|
||||
--log-warning-bg: #3d3a1a;
|
||||
--log-warning-text: #fbbf24;
|
||||
--log-danger-bg: #3d1a1a;
|
||||
--log-danger-text: #f87171;
|
||||
|
||||
/* 컨테이너 상태 텍스트 */
|
||||
--container-status-running: #86efac;
|
||||
--container-status-exited: #fca5a5;
|
||||
--container-status-paused: #fcd34d;
|
||||
--container-status-restarting: #fdba74;
|
||||
|
||||
/* 버튼 */
|
||||
--btn-active-bg: #555;
|
||||
--btn-active-border: #666;
|
||||
|
||||
@@ -22,12 +22,8 @@
|
||||
{{ autoRefresh ? '⏸ 자동갱신 ON' : '▶ 자동갱신 OFF' }}
|
||||
</button>
|
||||
|
||||
<div class="last-fetch-info">
|
||||
마지막 조회: <span class="last-fetch-time">{{ relativeTime }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-divider"></div>
|
||||
|
||||
<div class="control-row">
|
||||
<span class="control-label">특정시간:</span>
|
||||
<input
|
||||
type="datetime-local"
|
||||
@@ -49,7 +45,10 @@
|
||||
<span v-if="fetchState === 'loading'" class="loading-spinner"></span>
|
||||
{{ buttonText }}
|
||||
</button>
|
||||
<span v-if="autoRefresh" class="hint-text">자동갱신 OFF 시 특정시간 조회 가능</span>
|
||||
|
||||
<div class="last-fetch-info">
|
||||
마지막 조회: <span class="last-fetch-time">{{ relativeTime }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -290,11 +290,11 @@ function formatTimeAgo(datetime: string | null): string {
|
||||
/* 컨테이너 카드 */
|
||||
.container-card { width: 200px; padding: 10px; border-radius: 8px; border: 1px solid var(--border-color); background: var(--bg-secondary); cursor: pointer; transition: all 0.15s; overflow: hidden; }
|
||||
.container-card:hover { transform: translateY(-1px); box-shadow: 0 2px 8px rgba(0,0,0,0.08); }
|
||||
.container-card.normal { background: #f0fdf4; border-color: #86efac; }
|
||||
.container-card.warning { background: #fefce8; border-color: #fde047; }
|
||||
.container-card.critical { background: #fff7ed; border-color: #fdba74; }
|
||||
.container-card.danger { background: #fef2f2; border-color: #fca5a5; }
|
||||
.container-card.stopped { background: #fef2f2; border-color: #fca5a5; }
|
||||
.container-card.normal { background: var(--container-normal-bg, #f0fdf4); border-color: var(--container-normal-border, #86efac); }
|
||||
.container-card.warning { background: var(--container-warning-bg, #fefce8); border-color: var(--container-warning-border, #fde047); }
|
||||
.container-card.critical { background: var(--container-critical-bg, #fff7ed); border-color: var(--container-critical-border, #fdba74); }
|
||||
.container-card.danger { background: var(--container-danger-bg, #fef2f2); border-color: var(--container-danger-border, #fca5a5); }
|
||||
.container-card.stopped { background: var(--container-danger-bg, #fef2f2); border-color: var(--container-danger-border, #fca5a5); }
|
||||
|
||||
.card-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; overflow: hidden; }
|
||||
.card-name { display: flex; align-items: center; gap: 4px; flex: 1; min-width: 0; overflow: hidden; }
|
||||
|
||||
@@ -755,32 +755,32 @@ onUnmounted(() => {
|
||||
.section-title { margin: 0 0 16px 0; font-size: 16px; font-weight: 700; color: var(--text-primary); }
|
||||
.container-cards { display: flex; flex-direction: column; gap: 16px; }
|
||||
.container-card { background: var(--bg-secondary); border: 1px solid var(--border-color); border-radius: 10px; padding: 14px; }
|
||||
.container-card.running { background: #f0fdf4; border-color: #86efac; }
|
||||
.container-card.exited { background: #fef2f2; border-color: #fca5a5; }
|
||||
.container-card.paused { background: #fffbeb; border-color: #fcd34d; }
|
||||
.container-card.restarting { background: #fef3c7; border-color: #f59e0b; }
|
||||
.container-card.running { background: var(--container-normal-bg, #f0fdf4); border-color: var(--container-normal-border, #86efac); }
|
||||
.container-card.exited { background: var(--container-danger-bg, #fef2f2); border-color: var(--container-danger-border, #fca5a5); }
|
||||
.container-card.paused { background: var(--container-warning-bg, #fffbeb); border-color: var(--container-warning-border, #fcd34d); }
|
||||
.container-card.restarting { background: var(--container-critical-bg, #fef3c7); border-color: var(--container-critical-border, #f59e0b); }
|
||||
.container-header { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; padding-bottom: 10px; border-bottom: 1px solid var(--border-color); }
|
||||
.container-card.running .container-header { border-color: #86efac; }
|
||||
.container-card.exited .container-header { border-color: #fca5a5; }
|
||||
.container-card.paused .container-header { border-color: #fcd34d; }
|
||||
.container-card.restarting .container-header { border-color: #f59e0b; }
|
||||
.container-card.running .container-header { border-color: var(--container-normal-border, #86efac); }
|
||||
.container-card.exited .container-header { border-color: var(--container-danger-border, #fca5a5); }
|
||||
.container-card.paused .container-header { border-color: var(--container-warning-border, #fcd34d); }
|
||||
.container-card.restarting .container-header { border-color: var(--container-critical-border, #f59e0b); }
|
||||
.container-name { font-size: 16px; font-weight: 700; color: var(--btn-primary-bg); background: linear-gradient(135deg, #3b82f6 0%, #8b5cf6 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; }
|
||||
.container-card.exited .container-name { background: linear-gradient(135deg, #dc2626 0%, #f97316 100%); -webkit-background-clip: text; background-clip: text; }
|
||||
.container-card.paused .container-name { background: linear-gradient(135deg, #d97706 0%, #eab308 100%); -webkit-background-clip: text; background-clip: text; }
|
||||
.container-card.restarting .container-name { background: linear-gradient(135deg, #ea580c 0%, #facc15 100%); -webkit-background-clip: text; background-clip: text; }
|
||||
.container-status { font-size: 12px; padding: 3px 10px; border-radius: 12px; font-weight: 500; }
|
||||
.container-status.running { background: #dcfce7; color: #166534; }
|
||||
.container-status.exited { background: #fee2e2; color: #991b1b; }
|
||||
.container-status.paused { background: #fef3c7; color: #92400e; }
|
||||
.container-status.restarting { background: #ffedd5; color: #c2410c; }
|
||||
.container-status.unknown { background: #e2e8f0; color: #475569; }
|
||||
.container-status.running { background: var(--container-normal-bg, #dcfce7); color: var(--container-status-running, #166534); }
|
||||
.container-status.exited { background: var(--container-danger-bg, #fee2e2); color: var(--container-status-exited, #991b1b); }
|
||||
.container-status.paused { background: var(--container-warning-bg, #fef3c7); color: var(--container-status-paused, #92400e); }
|
||||
.container-status.restarting { background: var(--container-critical-bg, #ffedd5); color: var(--container-status-restarting, #c2410c); }
|
||||
.container-status.unknown { background: var(--bg-tertiary, #e2e8f0); color: var(--text-muted); }
|
||||
.container-uptime { font-size: 12px; color: var(--text-muted); margin-left: auto; }
|
||||
.container-charts { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
|
||||
.container-chart-box { background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 8px; padding: 10px; }
|
||||
.container-card.running .container-chart-box { background: #ffffff; border-color: #bbf7d0; }
|
||||
.container-card.exited .container-chart-box { background: #ffffff; border-color: #fecaca; }
|
||||
.container-card.paused .container-chart-box { background: #ffffff; border-color: #fde68a; }
|
||||
.container-card.restarting .container-chart-box { background: #ffffff; border-color: #fed7aa; }
|
||||
.container-card.running .container-chart-box { background: var(--bg-primary); border-color: var(--container-normal-border, #bbf7d0); }
|
||||
.container-card.exited .container-chart-box { background: var(--bg-primary); border-color: var(--container-danger-border, #fecaca); }
|
||||
.container-card.paused .container-chart-box { background: var(--bg-primary); border-color: var(--container-warning-border, #fde68a); }
|
||||
.container-card.restarting .container-chart-box { background: var(--bg-primary); border-color: var(--container-critical-border, #fed7aa); }
|
||||
.container-chart-box .chart-header { margin-bottom: 6px; }
|
||||
.container-chart-box .chart-title { font-size: 12px; font-weight: 600; color: var(--text-primary); }
|
||||
.container-chart-box .chart-avg { font-size: 11px; padding: 2px 8px; }
|
||||
|
||||
Reference in New Issue
Block a user