9545d69dc4
존재하지 않는 DB 테이블을 참조하여 발생하던 대시보드 오류를 해결. K8S 환경 확인을 위한 DB 조회 로직을 제거, 일반 환경에서 항상 정상 동작하도록 수정.
931 lines
30 KiB
Plaintext
931 lines
30 KiB
Plaintext
<%@ page language="java" contentType="text/html; charset=utf-8"%>
|
|
<%@ page import="java.util.*" %>
|
|
<%@ page import="com.eactive.eai.rms.common.datasource.*" %>
|
|
<%@ page import="java.util.stream.Collectors" %>
|
|
|
|
<div class="dashboard-servers">
|
|
<div class="dashboard-header">
|
|
<h2 class="section-title">서버 상태</h2>
|
|
|
|
<div class="server-filters">
|
|
<div class="filter-group">
|
|
<button class="filter-btn active" data-type="all">전체</button>
|
|
<%
|
|
// serverTypeMap 변수를 가져오기
|
|
Map<String, List<DataSourceType>> serverTypeMap =
|
|
(Map<String, List<DataSourceType>>)request.getAttribute("serverTypeMap");
|
|
|
|
if (serverTypeMap == null) {
|
|
// application 스코프에서 가져오기 시도
|
|
serverTypeMap = (Map<String, List<DataSourceType>>)application.getAttribute("serverTypeMap");
|
|
}
|
|
|
|
Map<String, Integer> priorityMap = new HashMap<String, Integer>();
|
|
priorityMap.put("MCI", 0);
|
|
priorityMap.put("EAI", 1);
|
|
priorityMap.put("FEP", 2);
|
|
priorityMap.put("CGW", 3);
|
|
priorityMap.put("FGW", 4);
|
|
|
|
Map<String,List<DataSourceType>> sortedMap = serverTypeMap.entrySet().stream()
|
|
.sorted(Comparator.comparingInt(e -> priorityMap.getOrDefault(e.getKey(), Integer.MAX_VALUE)))
|
|
.collect(Collectors.toMap(
|
|
Map.Entry::getKey
|
|
, Map.Entry::getValue
|
|
,(e1, e2) -> e1
|
|
, LinkedHashMap::new
|
|
));
|
|
|
|
if (sortedMap != null) {
|
|
// 서버 타입 필터 생성
|
|
for (Map.Entry<String, List<DataSourceType>> entry : sortedMap.entrySet()) {
|
|
String serverType = entry.getKey();
|
|
List<DataSourceType> servers = entry.getValue();
|
|
|
|
// BAT 서버는 제외
|
|
if (DataSourceTypeManager.BAT.equals(serverType) || DataSourceTypeManager.BAP.equals(serverType)) {
|
|
continue;
|
|
}
|
|
|
|
// 서버 타입에 해당하는 텍스트 가져오기 (첫 번째 서버의 텍스트 사용)
|
|
String serverTypeText = servers.size() > 0 ? servers.get(0).getText() : serverType;
|
|
%>
|
|
<button class="filter-btn" data-type="<%= serverType.toLowerCase() %>"><%= serverTypeText %></button>
|
|
<% }
|
|
} %>
|
|
</div>
|
|
<div class="search-container">
|
|
<i class="bi bi-search"></i>
|
|
<input type="text" id="server-search" placeholder="서버 검색..." />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<%
|
|
if (sortedMap != null) {
|
|
// 서버 타입별로 섹션 생성
|
|
for (Map.Entry<String, List<DataSourceType>> entry : sortedMap.entrySet()) {
|
|
String serverType = entry.getKey();
|
|
List<DataSourceType> servers = entry.getValue();
|
|
|
|
// BAT 서버는 제외
|
|
if (DataSourceTypeManager.BAT.equals(serverType) || DataSourceTypeManager.BAP.equals(serverType)) {
|
|
continue;
|
|
}
|
|
|
|
// 서버 타입에 해당하는 텍스트 가져오기 (첫 번째 서버의 텍스트 사용)
|
|
String serverTypeText = servers.size() > 0 ? servers.get(0).getText() : serverType;
|
|
|
|
// 서버 타입별 보더 색상 클래스
|
|
String borderColorClass = "";
|
|
if (serverType.equalsIgnoreCase("FEP")) {
|
|
borderColorClass = "border-primary";
|
|
} else if (serverType.equalsIgnoreCase("EAI")) {
|
|
borderColorClass = "border-success";
|
|
} else if (serverType.equalsIgnoreCase("EMS")) {
|
|
borderColorClass = "border-purple";
|
|
} else if (serverType.equalsIgnoreCase("EDI")) {
|
|
borderColorClass = "border-orange";
|
|
}
|
|
%>
|
|
<div class="server-section" id="server-section-<%= serverType.toLowerCase() %>">
|
|
<div class="card server-card-container <%= borderColorClass %>">
|
|
<div class="server-section-header">
|
|
<h3 class="server-section-title"><%= serverTypeText %> 서버</h3>
|
|
<span class="server-status-badge badge-success <%= serverType.toLowerCase() %>-status-badge">0 / 0 정상</span>
|
|
</div>
|
|
<div class="server-grid server-grid-<%= serverType.toLowerCase() %>" id="<%= serverType.toLowerCase() %>-servers-container">
|
|
<!-- 여기에 <%= serverTypeText %> 서버 카드가 동적으로 추가됩니다 -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<% }
|
|
} %>
|
|
</div>
|
|
|
|
<!-- 서버 카드 템플릿 -->
|
|
<template id="server-card-template">
|
|
<div class="server-card">
|
|
<div class="server-header">
|
|
<h5 class="server-name">서버 이름</h5>
|
|
<span class="server-status-badge badge-success">정상</span>
|
|
</div>
|
|
<div class="server-metrics">
|
|
<!-- 메트릭 헤더 행 -->
|
|
<div class="metrics-row">
|
|
<!-- CPU 메트릭 -->
|
|
<div class="server-metric">
|
|
<div class="metric-header">
|
|
<span class="metric-name"><i class="bi bi-cpu" style="color: #0dcaf0; font-size: 1.1rem;"></i></span>
|
|
<span class="metric-value cpu-value">0%</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 메모리 메트릭 -->
|
|
<div class="server-metric">
|
|
<div class="metric-header">
|
|
<span class="metric-name"><i class="bi bi-memory" style="color: #6f42c1; font-size: 1.1rem;"></i></span>
|
|
<span class="metric-value memory-value">0%</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 디스크 메트릭 -->
|
|
<div class="server-metric">
|
|
<div class="metric-header">
|
|
<span class="metric-name"><i class="bi bi-hdd" style="color: #fd7e14; font-size: 1.1rem;"></i></span>
|
|
<span class="metric-value disk-value">0%</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 스파크라인 행 -->
|
|
<div class="sparklines-row">
|
|
<div class="sparkline cpu-sparkline">
|
|
<div class="sparkline-bar success" style="height: 10%"></div>
|
|
<div class="sparkline-bar success" style="height: 15%"></div>
|
|
<div class="sparkline-bar success" style="height: 20%"></div>
|
|
<div class="sparkline-bar success" style="height: 15%"></div>
|
|
<div class="sparkline-bar success" style="height: 25%"></div>
|
|
<div class="sparkline-bar success" style="height: 20%"></div>
|
|
<div class="sparkline-bar success" style="height: 15%"></div>
|
|
</div>
|
|
|
|
<div class="sparkline memory-sparkline">
|
|
<div class="sparkline-bar success" style="height: 20%"></div>
|
|
<div class="sparkline-bar success" style="height: 25%"></div>
|
|
<div class="sparkline-bar success" style="height: 30%"></div>
|
|
<div class="sparkline-bar success" style="height: 25%"></div>
|
|
<div class="sparkline-bar success" style="height: 35%"></div>
|
|
<div class="sparkline-bar success" style="height: 30%"></div>
|
|
<div class="sparkline-bar success" style="height: 25%"></div>
|
|
</div>
|
|
|
|
<div class="sparkline disk-sparkline">
|
|
<div class="sparkline-bar success" style="height: 30%"></div>
|
|
<div class="sparkline-bar success" style="height: 30%"></div>
|
|
<div class="sparkline-bar success" style="height: 30%"></div>
|
|
<div class="sparkline-bar success" style="height: 30%"></div>
|
|
<div class="sparkline-bar success" style="height: 30%"></div>
|
|
<div class="sparkline-bar success" style="height: 30%"></div>
|
|
<div class="sparkline-bar success" style="height: 30%"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 인스턴스 테이블 -->
|
|
<div class="server-instances">
|
|
<table class="instance-table">
|
|
<thead>
|
|
<tr>
|
|
<th>인스턴스 ID</th>
|
|
<th>상태</th>
|
|
<th class="text-right">처리량</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody class="instance-table-body">
|
|
<!-- 인스턴스 데이터가 동적으로 추가됩니다 -->
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!-- 인스턴스 행 템플릿 -->
|
|
<template id="instance-row-template">
|
|
<tr>
|
|
<td>
|
|
<span class="instance-status status-normal"></span>
|
|
<span class="instance-id">인스턴스 ID</span>
|
|
</td>
|
|
<td class="instance-status-text">정상</td>
|
|
<td class="text-right instance-throughput">0/s</td>
|
|
</tr>
|
|
</template>
|
|
|
|
<style>
|
|
/* 대시보드 헤더 스타일 */
|
|
.dashboard-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 0.5rem;
|
|
padding-bottom: 0.4rem;
|
|
border-bottom: 1px solid var(--color-gray-200);
|
|
}
|
|
|
|
.dashboard-header .section-title {
|
|
margin-bottom: 0;
|
|
padding-bottom: 0;
|
|
border-bottom: none;
|
|
flex-shrink: 0;
|
|
margin-right: 0.75rem;
|
|
}
|
|
|
|
.dashboard-header .server-filters {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 0;
|
|
flex-grow: 1;
|
|
}
|
|
|
|
.dashboard-header .filter-group {
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.dashboard-header .search-container {
|
|
margin-left: auto;
|
|
}
|
|
|
|
/* 서버 상태 섹션 */
|
|
.server-section {
|
|
margin-bottom: 0.75rem;
|
|
}
|
|
|
|
.server-section-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.server-section-title {
|
|
font-size: 0.9rem;
|
|
font-weight: 600;
|
|
color: var(--color-gray-800);
|
|
margin: 0;
|
|
}
|
|
|
|
.server-filters {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 0.5rem;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.filter-group {
|
|
display: flex;
|
|
gap: 0.4rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.server-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.server-card {
|
|
background-color: white;
|
|
border-radius: var(--rounded-md);
|
|
box-shadow: var(--shadow);
|
|
border: 1px solid var(--color-gray-200);
|
|
overflow: hidden;
|
|
transition: all 0.2s ease;
|
|
height: 100%;
|
|
padding: 0.75rem;
|
|
}
|
|
|
|
.server-card:hover {
|
|
box-shadow: var(--shadow-md);
|
|
}
|
|
|
|
.server-header {
|
|
padding: 1rem 1.25rem;
|
|
background-color: var(--color-gray-50);
|
|
border-bottom: 1px solid var(--color-gray-200);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.server-name {
|
|
font-weight: 600;
|
|
font-size: 1rem;
|
|
color: var(--color-gray-800);
|
|
margin: 0;
|
|
}
|
|
|
|
.server-metrics {
|
|
padding: 1rem 1.25rem;
|
|
display: flex;
|
|
gap: 1.5rem;
|
|
}
|
|
|
|
.server-metric {
|
|
flex: 1;
|
|
}
|
|
|
|
.metric-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.metric-name {
|
|
font-size: 0.75rem;
|
|
color: var(--color-gray-500);
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.05em;
|
|
}
|
|
|
|
.server-instances {
|
|
padding: 0 16px 16px 16px;
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
max-height: 150px;
|
|
}
|
|
|
|
/* 스파크라인 스타일 */
|
|
.sparkline {
|
|
height: 28px;
|
|
display: flex;
|
|
align-items: flex-end;
|
|
gap: 2px;
|
|
margin-top: 0.5rem;
|
|
background-color: var(--color-gray-50);
|
|
border-radius: var(--rounded-sm);
|
|
padding: 0.25rem;
|
|
}
|
|
|
|
.sparkline-bar {
|
|
flex: 1;
|
|
background-color: var(--color-primary);
|
|
border-radius: 1px 1px 0 0;
|
|
}
|
|
|
|
.sparkline-bar.success {
|
|
background-color: var(--color-success);
|
|
}
|
|
|
|
.sparkline-bar.warning {
|
|
background-color: var(--color-warning);
|
|
}
|
|
|
|
.sparkline-bar.danger {
|
|
background-color: var(--color-danger);
|
|
}
|
|
|
|
/* 요약 섹션 */
|
|
.status-cards {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
gap: 1rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.status-card {
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 1.5rem;
|
|
border-radius: var(--rounded-lg);
|
|
background-color: white;
|
|
box-shadow: var(--shadow);
|
|
transition: all 0.2s ease;
|
|
}
|
|
|
|
.status-card:hover {
|
|
box-shadow: var(--shadow-md);
|
|
}
|
|
|
|
.status-card.success {
|
|
border-left: 4px solid var(--color-success);
|
|
}
|
|
|
|
.status-card.warning {
|
|
border-left: 4px solid var(--color-warning);
|
|
}
|
|
|
|
.status-card.danger {
|
|
border-left: 4px solid var(--color-danger);
|
|
}
|
|
|
|
.status-icon {
|
|
font-size: 2rem;
|
|
margin-right: 1.25rem;
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.status-icon.success {
|
|
color: var(--color-success);
|
|
}
|
|
|
|
.status-icon.warning {
|
|
color: var(--color-warning);
|
|
}
|
|
|
|
.status-icon.danger {
|
|
color: var(--color-danger);
|
|
}
|
|
|
|
.status-content {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.status-label {
|
|
font-size: 0.875rem;
|
|
color: var(--color-gray-500);
|
|
margin-bottom: 0.25rem;
|
|
}
|
|
|
|
.status-value {
|
|
font-size: 1.5rem;
|
|
font-weight: 600;
|
|
color: var(--color-gray-900);
|
|
}
|
|
|
|
/* 서버 섹션 스타일 - 기존 스타일 유지 */
|
|
.server-section {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.server-card-container {
|
|
padding: 0.75rem;
|
|
margin-bottom: 1rem;
|
|
min-height: 296px;
|
|
}
|
|
|
|
.server-section-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
|
|
.server-section-title {
|
|
font-size: 1rem;
|
|
font-weight: 600;
|
|
color: var(--color-gray-800);
|
|
margin: 0;
|
|
}
|
|
|
|
/* 서버 타입별 보더 색상 */
|
|
.border-primary {
|
|
border-top: 2px solid #0d6efd;
|
|
}
|
|
|
|
.border-success {
|
|
border-top: 2px solid #198754;
|
|
}
|
|
|
|
.border-purple {
|
|
border-top: 2px solid #6f42c1;
|
|
}
|
|
|
|
.border-orange {
|
|
border-top: 2px solid #fd7e14;
|
|
}
|
|
|
|
/* 서버 그리드 - 한 줄에 3개씩 표시 */
|
|
.server-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
/* 반응형 처리 - 화면 크기에 따라 그리드 조정 */
|
|
@media screen and (max-width: 1200px) {
|
|
.server-grid {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
}
|
|
|
|
@media screen and (max-width: 768px) {
|
|
.server-grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
/* 서버 카드 높이 축소 */
|
|
.server-card {
|
|
padding: 0.5rem;
|
|
border: 1px solid var(--color-gray-200);
|
|
border-radius: var(--rounded-sm);
|
|
background-color: white;
|
|
}
|
|
|
|
.server-header {
|
|
padding: 0.4rem 0.5rem;
|
|
margin-bottom: 0.3rem;
|
|
border-bottom: 1px solid var(--color-gray-100);
|
|
}
|
|
|
|
.server-name {
|
|
font-size: 0.95rem;
|
|
}
|
|
|
|
/* 메트릭 스타일 조정 */
|
|
.server-metrics {
|
|
padding: 0.3rem 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.0rem;
|
|
}
|
|
|
|
.metrics-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.sparklines-row {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.server-metric {
|
|
flex: 1;
|
|
min-width: 0; /* 오버플로우 방지 */
|
|
}
|
|
|
|
.metric-header {
|
|
margin-bottom: 0.2rem;
|
|
display: flex;
|
|
justify-content: flex-start;
|
|
align-items: center;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.sparkline {
|
|
flex: 1;
|
|
min-width: 0; /* 오버플로우 방지 */
|
|
}
|
|
|
|
.metric-name {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.metric-value {
|
|
margin-left: 0.5rem;
|
|
}
|
|
|
|
/* 인스턴스 테이블 스타일 */
|
|
.server-instances {
|
|
padding: 0.3rem 0;
|
|
max-height: 120px;
|
|
}
|
|
|
|
.instance-table {
|
|
font-size: 0.8rem;
|
|
}
|
|
|
|
.instance-table th,
|
|
.instance-table td {
|
|
padding: 0.2rem 0.3rem;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
/* 인스턴스 상태 점 스타일 */
|
|
.instance-status {
|
|
display: inline-block;
|
|
width: 8px;
|
|
height: 8px;
|
|
border-radius: 50%;
|
|
margin-right: 4px;
|
|
}
|
|
|
|
/* 오류 상태일 때 인스턴스 ID 색상 변경 */
|
|
tr:has(.status-error) .instance-id,
|
|
tr:has(.status-inactive) .instance-id {
|
|
color: #dc3545;
|
|
font-weight: 600;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
// 서버 카드 생성 함수
|
|
function createServerCard(server, containerId) {
|
|
var template = document.getElementById('server-card-template');
|
|
var container = document.getElementById(containerId);
|
|
|
|
if (!container) return null;
|
|
|
|
// 템플릿 복제
|
|
var clone = document.importNode(template.content, true);
|
|
|
|
// 서버 이름 설정
|
|
clone.querySelector('.server-name').textContent = server.text || server.name;
|
|
|
|
// 컨테이너에 추가
|
|
container.appendChild(clone);
|
|
|
|
return container.lastElementChild;
|
|
}
|
|
|
|
// 인스턴스 행 생성 함수
|
|
function createInstanceRow(instanceData) {
|
|
var template = document.getElementById('instance-row-template');
|
|
var clone = document.importNode(template.content, true);
|
|
|
|
// 인스턴스 ID 설정
|
|
clone.querySelector('.instance-id').textContent = instanceData.id;
|
|
|
|
// 상태 설정
|
|
var statusClass = 'status-normal';
|
|
var statusText = '정상';
|
|
|
|
if (instanceData.status === 'warning') {
|
|
statusClass = 'status-warning';
|
|
statusText = '경고';
|
|
} else if (instanceData.status === 'error' || instanceData.status === 'inactive') {
|
|
statusClass = 'status-error';
|
|
statusText = instanceData.status === 'inactive' ? '비활성' : '오류';
|
|
}
|
|
|
|
clone.querySelector('.instance-status').className = 'instance-status ' + statusClass;
|
|
clone.querySelector('.instance-status-text').textContent = statusText;
|
|
|
|
// 처리량 설정
|
|
if (instanceData.throughput) {
|
|
clone.querySelector('.instance-throughput').textContent = instanceData.throughput;
|
|
}
|
|
|
|
return clone;
|
|
}
|
|
|
|
// 서버 상태 업데이트 함수
|
|
function updateServerStatus(data) {
|
|
if (!data || !data.servers) return;
|
|
|
|
var normalCount = 0;
|
|
var warningCount = 0;
|
|
var errorCount = 0;
|
|
|
|
// 서버 타입별 상태 카운트
|
|
var typeStatusCount = {};
|
|
|
|
// 각 서버 상태 업데이트
|
|
data.servers.forEach(function(server) {
|
|
// 서버 타입 추출 (서버 이름에서 괄호 안의 텍스트 추출)
|
|
var serverTypeMatch = server.text.match(/\(([^)]+)\)/);
|
|
var serverType = serverTypeMatch ? serverTypeMatch[1] : "unknown";
|
|
|
|
// 서버 이름에서 타입 정보 제거
|
|
var displayName = server.name;
|
|
|
|
// 서버 상태
|
|
var serverStatus = server.status;
|
|
|
|
// 서버 타입별 상태 카운트 초기화
|
|
if (!typeStatusCount[serverType]) {
|
|
typeStatusCount[serverType] = {
|
|
normal: 0,
|
|
warning: 0,
|
|
error: 0,
|
|
total: 0
|
|
};
|
|
}
|
|
|
|
typeStatusCount[serverType].total++;
|
|
|
|
// 전체 상태 카운트 업데이트
|
|
if (serverStatus === 'normal') {
|
|
normalCount++;
|
|
typeStatusCount[serverType].normal++;
|
|
} else if (serverStatus === 'warning') {
|
|
warningCount++;
|
|
typeStatusCount[serverType].warning++;
|
|
} else if (serverStatus === 'error') {
|
|
errorCount++;
|
|
typeStatusCount[serverType].error++;
|
|
}
|
|
|
|
// 서버 타입 소문자로 변환 (ID용)
|
|
var serverTypeId = serverType.toLowerCase().replace(/\s+/g, '-');
|
|
|
|
// 서버 카드 찾기 및 업데이트
|
|
var serverContainerId = serverTypeId + '-servers-container';
|
|
var serverContainer = document.getElementById(serverContainerId);
|
|
|
|
if (!serverContainer) return;
|
|
|
|
// 서버 카드가 없으면 생성
|
|
var serverCard = document.querySelector('#' + serverContainerId + ' .server-card[data-server-name="' + server.name + '"]');
|
|
if (!serverCard) {
|
|
serverCard = createServerCard(server, serverContainerId);
|
|
if (serverCard) {
|
|
serverCard.setAttribute('data-server-name', server.name);
|
|
}
|
|
}
|
|
|
|
if (!serverCard) return;
|
|
|
|
// 서버 이름 업데이트
|
|
var serverNameElement = serverCard.querySelector('.server-name');
|
|
if (serverNameElement) {
|
|
serverNameElement.textContent = displayName;
|
|
}
|
|
|
|
// 서버 상태 업데이트
|
|
var statusBadge = serverCard.querySelector('.server-status-badge');
|
|
statusBadge.className = 'server-status-badge';
|
|
|
|
if (serverStatus === 'normal') {
|
|
statusBadge.classList.add('badge-success');
|
|
statusBadge.textContent = '정상';
|
|
} else if (serverStatus === 'warning') {
|
|
statusBadge.classList.add('badge-warning');
|
|
statusBadge.textContent = '경고';
|
|
} else if (serverStatus === 'error') {
|
|
statusBadge.classList.add('badge-danger');
|
|
statusBadge.textContent = '오류';
|
|
}
|
|
|
|
// CPU, 메모리, 디스크 업데이트
|
|
updateResourceValue(serverCard, 'cpu', server.cpu);
|
|
updateResourceValue(serverCard, 'memory', server.memory);
|
|
updateResourceValue(serverCard, 'disk', server.disk);
|
|
|
|
// 인스턴스 테이블 업데이트
|
|
var instanceTableBody = serverCard.querySelector('.instance-table-body');
|
|
instanceTableBody.innerHTML = '';
|
|
|
|
if (server.instances && server.instances.length > 0) {
|
|
server.instances.forEach(function(instance) {
|
|
instanceTableBody.appendChild(createInstanceRow(instance));
|
|
});
|
|
}
|
|
});
|
|
|
|
// 요약 정보 업데이트
|
|
if (document.getElementById('normal-servers')) {
|
|
document.getElementById('normal-servers').textContent = normalCount;
|
|
}
|
|
if (document.getElementById('warning-servers')) {
|
|
document.getElementById('warning-servers').textContent = warningCount;
|
|
}
|
|
if (document.getElementById('error-servers')) {
|
|
document.getElementById('error-servers').textContent = errorCount;
|
|
}
|
|
|
|
// 서버 타입별 상태 배지 업데이트
|
|
for (var type in typeStatusCount) {
|
|
var typeId = type.toLowerCase().replace(/\s+/g, '-');
|
|
var badgeElement = document.querySelector('.' + typeId + '-status-badge');
|
|
if (badgeElement) {
|
|
var countData = typeStatusCount[type];
|
|
badgeElement.textContent = countData.normal + ' / ' + countData.total + ' 정상';
|
|
badgeElement.className = typeId + '-status-badge server-status-badge';
|
|
|
|
if (countData.error > 0) {
|
|
badgeElement.classList.add('badge-danger');
|
|
} else if (countData.warning > 0) {
|
|
badgeElement.classList.add('badge-warning');
|
|
} else {
|
|
badgeElement.classList.add('badge-success');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 리소스 값 업데이트 함수 (CPU, 메모리, 디스크)
|
|
function updateResourceValue(serverCard, resourceType, value) {
|
|
var valueElement = serverCard.querySelector('.' + resourceType + '-value');
|
|
if (!valueElement) return;
|
|
|
|
valueElement.textContent = value + '%';
|
|
valueElement.className = 'metric-value ' + resourceType + '-value';
|
|
|
|
// 값에 따라 클래스 추가
|
|
if (value >= 90) {
|
|
valueElement.classList.add('danger');
|
|
} else if (value >= 70) {
|
|
valueElement.classList.add('warning');
|
|
} else {
|
|
valueElement.classList.add('success');
|
|
}
|
|
|
|
// 스파크라인 업데이트
|
|
var sparkline = serverCard.querySelector('.' + resourceType + '-sparkline');
|
|
if (!sparkline) return;
|
|
|
|
// 서버 이름 가져오기
|
|
var serverName = serverCard.getAttribute('data-server-name');
|
|
if (!serverName) return;
|
|
|
|
// 서버 리소스 시계열 데이터 가져오기
|
|
ModernDashboardApi.getServerResourceData(serverName, function(data) {
|
|
if (data && data.data) {
|
|
var resourceData = data.data;
|
|
var resourceValues = resourceData[resourceType];
|
|
|
|
// 스파크라인 업데이트
|
|
sparkline.innerHTML = '';
|
|
|
|
if (resourceValues && resourceValues.length > 0) {
|
|
resourceValues.forEach(function(val) {
|
|
var bar = document.createElement('div');
|
|
bar.className = 'sparkline-bar';
|
|
bar.style.height = val + '%';
|
|
|
|
// 값에 따라 클래스 추가
|
|
if (val >= 90) {
|
|
bar.classList.add('danger');
|
|
} else if (val >= 70) {
|
|
bar.classList.add('warning');
|
|
} else {
|
|
bar.classList.add('success');
|
|
}
|
|
|
|
sparkline.appendChild(bar);
|
|
});
|
|
} else {
|
|
// 데이터가 없는 경우 기본 스파크라인 생성
|
|
for (var i = 0; i < 7; i++) {
|
|
var bar = document.createElement('div');
|
|
bar.className = 'sparkline-bar success';
|
|
bar.style.height = value + '%';
|
|
sparkline.appendChild(bar);
|
|
}
|
|
}
|
|
} else {
|
|
// API 응답이 없는 경우 기본 스파크라인 생성
|
|
sparkline.innerHTML = '';
|
|
for (var i = 0; i < 7; i++) {
|
|
var bar = document.createElement('div');
|
|
bar.className = 'sparkline-bar success';
|
|
bar.style.height = value + '%';
|
|
sparkline.appendChild(bar);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// 서버 데이터 로드 함수
|
|
function loadServerData() {
|
|
console.log("서버 데이터 로드 시작: " + new Date().toLocaleTimeString());
|
|
ModernDashboardApi.getServerStatus(null, function(data) {
|
|
updateServerStatus(data);
|
|
console.log("서버 데이터 로드 완료: " + new Date().toLocaleTimeString());
|
|
});
|
|
}
|
|
|
|
// 서버 필터 버튼 클릭 이벤트
|
|
$('.filter-btn').click(function() {
|
|
var type = $(this).data('type');
|
|
|
|
if (type === 'all') {
|
|
// 전체 표시
|
|
$('.server-section').show();
|
|
$('.filter-btn').removeClass('active');
|
|
$(this).addClass('active');
|
|
$(".tran-filter-btn[data-type='all']").click();
|
|
} else {
|
|
// 필터링
|
|
$('.server-section').hide();
|
|
$('#server-section-' + type).show();
|
|
$('.filter-btn').removeClass('active');
|
|
$(this).addClass('active');
|
|
$(".tran-filter-btn[data-type='" + type + "']").click();
|
|
}
|
|
|
|
|
|
});
|
|
|
|
// 서버 검색 기능
|
|
$('#server-search').on('input', function() {
|
|
var searchText = $(this).val().toLowerCase();
|
|
|
|
if (searchText.length > 0) {
|
|
$('.server-card').each(function() {
|
|
var serverName = $(this).find('.server-name').text().toLowerCase();
|
|
if (serverName.indexOf(searchText) > -1) {
|
|
$(this).show();
|
|
} else {
|
|
$(this).hide();
|
|
}
|
|
});
|
|
} else {
|
|
$('.server-card').show();
|
|
}
|
|
});
|
|
|
|
// 초기 로드
|
|
$(document).ready(function() {
|
|
// 서버 데이터 로드
|
|
loadServerData();
|
|
|
|
// 이벤트 리스너 등록 - 중앙 이벤트 관리자 사용
|
|
if (window.DashboardEventManager) {
|
|
// 초기화 이벤트 리스너
|
|
window.DashboardEventManager.on('init', function() {
|
|
console.log("서버 상태 초기화 이벤트 수신");
|
|
// 이미 초기화되었으므로 추가 작업 필요 없음
|
|
});
|
|
|
|
// 새로고침 이벤트 리스너
|
|
window.DashboardEventManager.on('refresh', function() {
|
|
console.log("서버 상태 새로고침 이벤트 수신");
|
|
loadServerData();
|
|
});
|
|
} else {
|
|
console.error("DashboardEventManager가 정의되지 않았습니다. modern-dashboard.js가 먼저 로드되었는지 확인하세요.");
|
|
}
|
|
});
|
|
</script> |