Skip to main content
Glama

OpenManager Vibe V4 MCP Server

by skyasu2
Dashboard.js9.63 kB
import EventBus from '../services/EventBus.js'; import ServerDataService from '../services/ServerDataService.js'; import AIService from '../services/AIService.js'; export default class Dashboard { constructor() { this.servers = []; this.selectedServer = null; this.filterStatus = 'all'; this.isLoading = false; // DOM 요소 참조 this.elements = { serverList: document.getElementById('server-list'), summarySection: document.getElementById('status-summary'), filterButtons: document.querySelectorAll('.filter-btn'), queryInput: document.getElementById('ai-query-input'), queryButton: document.getElementById('ai-query-btn'), responseArea: document.getElementById('ai-response') }; // 이벤트 리스너 등록 this.initEventListeners(); // 이벤트 구독 this.subscribeToEvents(); // 초기 데이터 로드 this.loadDashboardData(); } initEventListeners() { // 필터 버튼 이벤트 설정 this.elements.filterButtons.forEach(btn => { btn.addEventListener('click', (e) => { const status = e.target.dataset.status; this.setFilterStatus(status); }); }); // AI 쿼리 제출 이벤트 this.elements.queryButton.addEventListener('click', () => { const query = this.elements.queryInput.value.trim(); if (query) { this.submitQuery(query); } }); } subscribeToEvents() { // 서버 데이터 업데이트 이벤트 EventBus.subscribe('servers:data-updated', this.handleServersUpdate.bind(this)); // 서버 선택 이벤트 EventBus.subscribe('server:selected', this.handleServerSelection.bind(this)); // AI 응답 이벤트 EventBus.subscribe('ai:response-received', this.handleAIResponse.bind(this)); // 오류 이벤트 EventBus.subscribe('error', this.handleError.bind(this)); } async loadDashboardData() { try { this.showLoading(true); const servers = await ServerDataService.getServers(); EventBus.publish('servers:data-updated', servers); } catch (error) { EventBus.publish('error', { source: 'dashboard', message: '서버 데이터를 불러오는 중 오류가 발생했습니다.', details: error.message }); } finally { this.showLoading(false); } } handleServersUpdate(servers) { this.servers = servers; this.renderDashboard(); } handleServerSelection(server) { this.selectedServer = server; this.highlightSelectedServer(); // 서버 컨텍스트 업데이트 EventBus.publish('context:updated', { selectedServer: server }); } handleAIResponse(response) { this.renderAIResponse(response); // 응답에 관련 서버가 있다면 하이라이트 if (response.related_servers && response.related_servers.length > 0) { this.highlightRelatedServers(response.related_servers); } } setFilterStatus(status) { this.filterStatus = status; // 필터 버튼 UI 업데이트 this.elements.filterButtons.forEach(btn => { btn.classList.toggle('active', btn.dataset.status === status); }); this.renderServerList(); // 필터 변경 이벤트 발행 EventBus.publish('filter:changed', { status }); } renderDashboard() { this.renderServerList(); this.renderSummary(); } renderServerList() { const filteredServers = this.filterServers(); const list = this.elements.serverList; list.innerHTML = ''; filteredServers.forEach(server => { const card = document.createElement('div'); card.className = `server-card status-${server.status}`; card.dataset.id = server.id; if (this.selectedServer && this.selectedServer.id === server.id) { card.classList.add('selected'); } card.innerHTML = ` <div class="server-name">${server.hostname}</div> <div class="server-metrics"> <div class="metric"> <span class="label">CPU</span> <div class="progress-bar"> <div class="progress" style="width: ${server.cpu}%"></div> </div> <span class="value">${server.cpu}%</span> </div> <div class="metric"> <span class="label">Memory</span> <div class="progress-bar"> <div class="progress" style="width: ${server.memory}%"></div> </div> <span class="value">${server.memory}%</span> </div> <div class="metric"> <span class="label">Disk</span> <div class="progress-bar"> <div class="progress" style="width: ${server.disk}%"></div> </div> <span class="value">${server.disk}%</span> </div> </div> `; card.addEventListener('click', () => { EventBus.publish('server:selected', server); }); list.appendChild(card); }); } renderSummary() { const counts = { total: this.servers.length, critical: this.servers.filter(s => s.status === 'critical').length, warning: this.servers.filter(s => s.status === 'warning').length, normal: this.servers.filter(s => s.status === 'normal').length }; this.elements.summarySection.innerHTML = ` <div class="summary-item"> <span class="count">${counts.total}</span> <span class="label">Total</span> </div> <div class="summary-item status-normal"> <span class="count">${counts.normal}</span> <span class="label">Normal</span> </div> <div class="summary-item status-warning"> <span class="count">${counts.warning}</span> <span class="label">Warning</span> </div> <div class="summary-item status-critical"> <span class="count">${counts.critical}</span> <span class="label">Critical</span> </div> `; } filterServers() { if (this.filterStatus === 'all') { return this.servers; } return this.servers.filter(server => server.status === this.filterStatus); } highlightSelectedServer() { const serverCards = this.elements.serverList.querySelectorAll('.server-card'); serverCards.forEach(card => { card.classList.remove('selected'); }); if (this.selectedServer) { const selectedCard = this.elements.serverList.querySelector( `.server-card[data-id="${this.selectedServer.id}"]` ); if (selectedCard) { selectedCard.classList.add('selected'); } } } highlightRelatedServers(serverIds) { const serverCards = this.elements.serverList.querySelectorAll('.server-card'); serverCards.forEach(card => { const serverId = card.dataset.id; if (serverIds.includes(serverId)) { card.classList.add('related'); } else { card.classList.remove('related'); } }); } async submitQuery(query) { try { this.showQueryLoading(true); // AI 쿼리 실행 const context = { selectedServer: this.selectedServer, filterStatus: this.filterStatus }; const response = await AIService.submitQuery(query, context); // 응답 이벤트 발행 EventBus.publish('ai:response-received', response); } catch (error) { EventBus.publish('error', { source: 'ai-query', message: '쿼리 처리 중 오류가 발생했습니다.', details: error.message }); } finally { this.showQueryLoading(false); } } renderAIResponse(response) { const responseArea = this.elements.responseArea; // 응답 내용 설정 responseArea.innerHTML = ` <div class="response-content">${response.answer}</div> ${response.visualization_type ? `<div id="visualization" class="visualization ${response.visualization_type}"></div>` : ''} `; // 시각화 타입이 있는 경우 차트 렌더링 if (response.visualization_type) { // 여기서 차트 렌더링 로직 추가 예정 // 나중에 ChartManager 컴포넌트로 분리 } } showLoading(isLoading) { this.isLoading = isLoading; document.body.classList.toggle('loading', isLoading); } showQueryLoading(isLoading) { this.elements.queryButton.disabled = isLoading; this.elements.queryButton.innerHTML = isLoading ? '<span class="spinner"></span>' : '질문하기'; } handleError(error) { console.error(`Error (${error.source}):`, error.message); // 오류 Toast 또는 알림 표시 const toast = document.createElement('div'); toast.className = 'toast error'; toast.innerHTML = ` <div class="toast-title">오류</div> <div class="toast-message">${error.message}</div> `; document.body.appendChild(toast); // 3초 후 토스트 제거 setTimeout(() => { toast.classList.add('fade-out'); setTimeout(() => toast.remove(), 300); }, 3000); } // 주기적 데이터 새로고침 설정 startAutoRefresh(intervalMs = 30000) { this.refreshInterval = setInterval(() => { this.loadDashboardData(); }, intervalMs); } stopAutoRefresh() { if (this.refreshInterval) { clearInterval(this.refreshInterval); } } } // 대시보드 초기화 document.addEventListener('DOMContentLoaded', () => { window.dashboard = new Dashboard(); window.dashboard.startAutoRefresh(); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/skyasu2/openmanager-vibe-v4'

If you have feedback or need assistance with the MCP directory API, please join our Discord server