Skip to main content
Glama
main.js30.9 kB
/** * Main JavaScript for the Sectional MCP Panel UI */ // API base URL const API_BASE_URL = '/api/v1'; // DOM elements const dashboardLink = document.getElementById('dashboard-link'); const sectionsLink = document.getElementById('sections-link'); const serversLink = document.getElementById('servers-link'); const tasksLink = document.getElementById('tasks-link'); const settingsLink = document.getElementById('settings-link'); const pageTitle = document.getElementById('page-title'); const refreshBtn = document.getElementById('refresh-btn'); const addSectionBtn = document.getElementById('add-section-btn'); const dashboardContent = document.getElementById('dashboard-content'); const sectionsContent = document.getElementById('sections-content'); const serversContent = document.getElementById('servers-content'); const tasksContent = document.getElementById('tasks-content'); const settingsContent = document.getElementById('settings-content'); const sectionsContainer = document.getElementById('sections-container'); const sectionsTableBody = document.getElementById('sections-table-body'); const serversTableBody = document.getElementById('servers-table-body'); const tasksTableBody = document.getElementById('tasks-table-body'); // Modal elements const addSectionModal = new bootstrap.Modal(document.getElementById('add-section-modal')); const addServerModal = new bootstrap.Modal(document.getElementById('add-server-modal')); const addSectionSubmit = document.getElementById('add-section-submit'); const addServerSubmit = document.getElementById('add-server-submit'); // Panel settings form const panelSettingsForm = document.getElementById('panel-settings-form'); const panelNameInput = document.getElementById('panel-name'); const panelVersionInput = document.getElementById('panel-version'); const globalSettingsInput = document.getElementById('global-settings'); // Add server form const serverSectionSelect = document.getElementById('server-section'); // Event listeners document.addEventListener('DOMContentLoaded', () => { // Initialize the dashboard loadDashboard(); // Navigation event listeners dashboardLink.addEventListener('click', (e) => { e.preventDefault(); showContent('dashboard'); }); sectionsLink.addEventListener('click', (e) => { e.preventDefault(); showContent('sections'); loadSections(); }); serversLink.addEventListener('click', (e) => { e.preventDefault(); showContent('servers'); loadServers(); }); tasksLink.addEventListener('click', (e) => { e.preventDefault(); showContent('tasks'); loadTasks(); }); settingsLink.addEventListener('click', (e) => { e.preventDefault(); showContent('settings'); loadPanelSettings(); }); // Refresh button refreshBtn.addEventListener('click', () => { const currentContent = document.querySelector('main > div[style="display: block;"]') || dashboardContent; if (currentContent === dashboardContent) { loadDashboard(); } else if (currentContent === sectionsContent) { loadSections(); } else if (currentContent === serversContent) { loadServers(); } else if (currentContent === tasksContent) { loadTasks(); } else if (currentContent === settingsContent) { loadPanelSettings(); } }); // Add section button addSectionBtn.addEventListener('click', () => { addSectionModal.show(); }); // Add section form submission addSectionSubmit.addEventListener('click', () => { const sectionName = document.getElementById('section-name').value; const sectionDescription = document.getElementById('section-description').value; let sectionSettings = document.getElementById('section-settings').value; if (!sectionName) { alert('Section name is required'); return; } try { sectionSettings = sectionSettings ? JSON.parse(sectionSettings) : {}; } catch (e) { alert('Invalid JSON in section settings'); return; } createSection({ name: sectionName, description: sectionDescription, settings: sectionSettings }); }); // Add server form submission addServerSubmit.addEventListener('click', () => { const serverName = document.getElementById('server-name').value; const sectionId = parseInt(document.getElementById('server-section').value); const serverDescription = document.getElementById('server-description').value; const runtimeType = document.getElementById('server-runtime-type').value; const command = document.getElementById('server-command').value; const args = document.getElementById('server-args').value.split(' ').filter(arg => arg.trim()); const workingDir = document.getElementById('server-working-dir').value; const portsStr = document.getElementById('server-ports').value; let serverSettings = document.getElementById('server-settings').value; if (!serverName || !sectionId || !runtimeType || !command) { alert('Server name, section, runtime type, and command are required'); return; } try { serverSettings = serverSettings ? JSON.parse(serverSettings) : {}; } catch (e) { alert('Invalid JSON in server settings'); return; } // Parse ports const ports = []; if (portsStr) { const portPairs = portsStr.split(','); for (const pair of portPairs) { const [port, protocol = 'TCP'] = pair.split(':'); if (port && !isNaN(parseInt(port))) { ports.push({ containerPort: parseInt(port), protocol: protocol.toUpperCase() }); } } } // Create runtime definition const runtimeDefinition = { type: runtimeType, command: command, args: args, workingDirectory: workingDir || null, ports: ports }; createServer({ name: serverName, section_id: sectionId, description: serverDescription, runtime_definition: runtimeDefinition, settings: serverSettings }); }); // Panel settings form submission panelSettingsForm.addEventListener('submit', (e) => { e.preventDefault(); const panelName = panelNameInput.value; let globalSettings; try { globalSettings = JSON.parse(globalSettingsInput.value); } catch (e) { alert('Invalid JSON in global settings'); return; } updatePanelSettings({ name: panelName, global_settings: globalSettings }); }); }); // Show the selected content and hide others function showContent(contentType) { // Hide all content dashboardContent.style.display = 'none'; sectionsContent.style.display = 'none'; serversContent.style.display = 'none'; tasksContent.style.display = 'none'; settingsContent.style.display = 'none'; // Remove active class from all links dashboardLink.classList.remove('active'); sectionsLink.classList.remove('active'); serversLink.classList.remove('active'); tasksLink.classList.remove('active'); settingsLink.classList.remove('active'); // Show selected content and set active link switch (contentType) { case 'dashboard': dashboardContent.style.display = 'block'; dashboardLink.classList.add('active'); pageTitle.textContent = 'Dashboard'; break; case 'sections': sectionsContent.style.display = 'block'; sectionsLink.classList.add('active'); pageTitle.textContent = 'Sections'; break; case 'servers': serversContent.style.display = 'block'; serversLink.classList.add('active'); pageTitle.textContent = 'Servers'; break; case 'tasks': tasksContent.style.display = 'block'; tasksLink.classList.add('active'); pageTitle.textContent = 'Tasks'; break; case 'settings': settingsContent.style.display = 'block'; settingsLink.classList.add('active'); pageTitle.textContent = 'Settings'; break; } } // Load dashboard data async function loadDashboard() { try { // Show loading indicator sectionsContainer.innerHTML = '<div class="alert alert-info">Loading sections and servers...</div>'; // Load sections const sections = await fetchAPI('/sections'); if (sections.length === 0) { sectionsContainer.innerHTML = ` <div class="alert alert-warning"> No sections found. Click "Add Section" to create your first section. </div> `; return; } // Clear container sectionsContainer.innerHTML = ''; // Process each section for (const section of sections) { // Get servers in this section const sectionDetails = await fetchAPI(`/sections/${section.id}`); const servers = sectionDetails.servers || []; // Create section card const sectionCard = document.createElement('div'); sectionCard.className = 'card section-card mb-4'; sectionCard.innerHTML = ` <div class="card-header d-flex justify-content-between align-items-center"> <h5>${section.name}</h5> <div class="action-buttons"> <button class="btn btn-sm btn-success section-start-btn" data-section-id="${section.id}">Start All</button> <button class="btn btn-sm btn-danger section-stop-btn" data-section-id="${section.id}">Stop All</button> <button class="btn btn-sm btn-primary section-restart-btn" data-section-id="${section.id}">Restart All</button> <button class="btn btn-sm btn-outline-primary add-server-btn" data-section-id="${section.id}">Add Server</button> </div> </div> <div class="card-body"> <p>${section.description || 'No description'}</p> <div class="servers-container" id="section-${section.id}-servers"> ${servers.length === 0 ? '<div class="alert alert-warning">No servers in this section</div>' : ''} </div> </div> `; sectionsContainer.appendChild(sectionCard); // Add servers to the section const serversContainer = document.getElementById(`section-${section.id}-servers`); if (servers.length > 0) { for (const server of servers) { // Get full server details const serverDetails = await fetchAPI(`/servers/${server.id}`); // Create server card const serverCard = document.createElement('div'); serverCard.className = `card server-card server-${serverDetails.status.toLowerCase()}`; serverCard.innerHTML = ` <div class="card-body d-flex justify-content-between align-items-center"> <div> <h6>${serverDetails.name}</h6> <span class="badge status-badge-${serverDetails.status.toLowerCase()}">${serverDetails.status}</span> <small class="text-muted">${serverDetails.description || ''}</small> </div> <div class="action-buttons"> <button class="btn btn-sm btn-success server-start-btn" data-server-id="${serverDetails.id}" ${serverDetails.status === 'Running' ? 'disabled' : ''}>Start</button> <button class="btn btn-sm btn-danger server-stop-btn" data-server-id="${serverDetails.id}" ${serverDetails.status === 'Stopped' ? 'disabled' : ''}>Stop</button> <button class="btn btn-sm btn-primary server-restart-btn" data-server-id="${serverDetails.id}">Restart</button> </div> </div> `; serversContainer.appendChild(serverCard); // Add event listeners for server actions const startBtn = serverCard.querySelector('.server-start-btn'); const stopBtn = serverCard.querySelector('.server-stop-btn'); const restartBtn = serverCard.querySelector('.server-restart-btn'); startBtn.addEventListener('click', () => startServer(serverDetails.id)); stopBtn.addEventListener('click', () => stopServer(serverDetails.id)); restartBtn.addEventListener('click', () => restartServer(serverDetails.id)); } } // Add event listeners for section actions const startAllBtn = sectionCard.querySelector('.section-start-btn'); const stopAllBtn = sectionCard.querySelector('.section-stop-btn'); const restartAllBtn = sectionCard.querySelector('.section-restart-btn'); const addServerBtn = sectionCard.querySelector('.add-server-btn'); startAllBtn.addEventListener('click', () => startSection(section.id)); stopAllBtn.addEventListener('click', () => stopSection(section.id)); restartAllBtn.addEventListener('click', () => restartSection(section.id)); addServerBtn.addEventListener('click', () => showAddServerModal(section.id)); } } catch (error) { console.error('Error loading dashboard:', error); sectionsContainer.innerHTML = ` <div class="alert alert-danger"> Error loading dashboard: ${error.message} </div> `; } } // Load sections data for the sections table async function loadSections() { try { // Show loading indicator sectionsTableBody.innerHTML = '<tr><td colspan="5" class="text-center">Loading sections...</td></tr>'; // Load sections const sections = await fetchAPI('/sections'); if (sections.length === 0) { sectionsTableBody.innerHTML = '<tr><td colspan="5" class="text-center">No sections found</td></tr>'; return; } // Clear table sectionsTableBody.innerHTML = ''; // Process each section for (const section of sections) { // Get servers count const sectionDetails = await fetchAPI(`/sections/${section.id}`); const serversCount = sectionDetails.servers ? sectionDetails.servers.length : 0; // Create table row const row = document.createElement('tr'); row.innerHTML = ` <td>${section.id}</td> <td>${section.name}</td> <td>${section.description || 'No description'}</td> <td>${serversCount}</td> <td> <div class="btn-group btn-group-sm"> <button class="btn btn-outline-primary section-edit-btn" data-section-id="${section.id}">Edit</button> <button class="btn btn-outline-danger section-delete-btn" data-section-id="${section.id}" ${serversCount > 0 ? 'disabled' : ''}>Delete</button> </div> </td> `; sectionsTableBody.appendChild(row); // Add event listeners const editBtn = row.querySelector('.section-edit-btn'); const deleteBtn = row.querySelector('.section-delete-btn'); editBtn.addEventListener('click', () => editSection(section.id)); deleteBtn.addEventListener('click', () => deleteSection(section.id)); } } catch (error) { console.error('Error loading sections:', error); sectionsTableBody.innerHTML = ` <tr><td colspan="5" class="text-center text-danger">Error loading sections: ${error.message}</td></tr> `; } } // Load servers data for the servers table async function loadServers() { try { // Show loading indicator serversTableBody.innerHTML = '<tr><td colspan="6" class="text-center">Loading servers...</td></tr>'; // Load servers const servers = await fetchAPI('/servers'); if (servers.length === 0) { serversTableBody.innerHTML = '<tr><td colspan="6" class="text-center">No servers found</td></tr>'; return; } // Clear table serversTableBody.innerHTML = ''; // Process each server for (const server of servers) { // Get section name const section = await fetchAPI(`/sections/${server.section_id}`); // Create table row const row = document.createElement('tr'); row.innerHTML = ` <td>${server.id}</td> <td>${server.name}</td> <td>${section.name || 'Unknown'}</td> <td><span class="badge status-badge-${server.status.toLowerCase()}">${server.status}</span></td> <td>${server.runtime_definition.type}</td> <td> <div class="btn-group btn-group-sm"> <button class="btn btn-success server-start-btn" data-server-id="${server.id}" ${server.status === 'Running' ? 'disabled' : ''}>Start</button> <button class="btn btn-danger server-stop-btn" data-server-id="${server.id}" ${server.status === 'Stopped' ? 'disabled' : ''}>Stop</button> <button class="btn btn-primary server-restart-btn" data-server-id="${server.id}">Restart</button> <button class="btn btn-outline-primary server-edit-btn" data-server-id="${server.id}" ${server.status === 'Running' ? 'disabled' : ''}>Edit</button> <button class="btn btn-outline-danger server-delete-btn" data-server-id="${server.id}" ${server.status === 'Running' ? 'disabled' : ''}>Delete</button> </div> </td> `; serversTableBody.appendChild(row); // Add event listeners const startBtn = row.querySelector('.server-start-btn'); const stopBtn = row.querySelector('.server-stop-btn'); const restartBtn = row.querySelector('.server-restart-btn'); const editBtn = row.querySelector('.server-edit-btn'); const deleteBtn = row.querySelector('.server-delete-btn'); startBtn.addEventListener('click', () => startServer(server.id)); stopBtn.addEventListener('click', () => stopServer(server.id)); restartBtn.addEventListener('click', () => restartServer(server.id)); editBtn.addEventListener('click', () => editServer(server.id)); deleteBtn.addEventListener('click', () => deleteServer(server.id)); } } catch (error) { console.error('Error loading servers:', error); serversTableBody.innerHTML = ` <tr><td colspan="6" class="text-center text-danger">Error loading servers: ${error.message}</td></tr> `; } } // Load tasks data for the tasks table async function loadTasks() { try { // Show loading indicator tasksTableBody.innerHTML = '<tr><td colspan="6" class="text-center">Loading tasks...</td></tr>'; // Load tasks const tasks = await fetchAPI('/tasks'); if (tasks.length === 0) { tasksTableBody.innerHTML = '<tr><td colspan="6" class="text-center">No tasks found</td></tr>'; return; } // Clear table tasksTableBody.innerHTML = ''; // Process each task for (const task of tasks) { // Format dates const createdAt = new Date(task.created_at).toLocaleString(); const updatedAt = new Date(task.updated_at).toLocaleString(); // Create table row const row = document.createElement('tr'); row.innerHTML = ` <td>${task.task_id}</td> <td>${task.task_type}</td> <td>${task.status}</td> <td>${createdAt}</td> <td>${updatedAt}</td> <td> <button class="btn btn-sm btn-outline-primary task-details-btn" data-task-id="${task.task_id}">Details</button> </td> `; tasksTableBody.appendChild(row); // Add event listeners const detailsBtn = row.querySelector('.task-details-btn'); detailsBtn.addEventListener('click', () => showTaskDetails(task.task_id)); } } catch (error) { console.error('Error loading tasks:', error); tasksTableBody.innerHTML = ` <tr><td colspan="6" class="text-center text-danger">Error loading tasks: ${error.message}</td></tr> `; } } // Load panel settings async function loadPanelSettings() { try { // Load panel configuration const panel = await fetchAPI('/panel'); // Populate form panelNameInput.value = panel.name; panelVersionInput.value = panel.version; globalSettingsInput.value = JSON.stringify(panel.global_settings, null, 2); } catch (error) { console.error('Error loading panel settings:', error); alert(`Error loading panel settings: ${error.message}`); } } // Show add server modal async function showAddServerModal(sectionId = null) { try { // Load sections for dropdown const sections = await fetchAPI('/sections'); // Clear and populate dropdown serverSectionSelect.innerHTML = ''; for (const section of sections) { const option = document.createElement('option'); option.value = section.id; option.textContent = section.name; if (sectionId && section.id === sectionId) { option.selected = true; } serverSectionSelect.appendChild(option); } // Clear form fields document.getElementById('server-name').value = ''; document.getElementById('server-description').value = ''; document.getElementById('server-runtime-type').value = 'docker_image'; document.getElementById('server-command').value = ''; document.getElementById('server-args').value = ''; document.getElementById('server-working-dir').value = ''; document.getElementById('server-ports').value = ''; document.getElementById('server-settings').value = ''; // Show modal addServerModal.show(); } catch (error) { console.error('Error preparing add server modal:', error); alert(`Error: ${error.message}`); } } // API helper functions async function fetchAPI(endpoint, options = {}) { const url = `${API_BASE_URL}${endpoint}`; const defaultOptions = { headers: { 'Content-Type': 'application/json', }, }; const response = await fetch(url, { ...defaultOptions, ...options }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(errorData.detail || `API error: ${response.status}`); } return response.json(); } // Section operations async function createSection(sectionData) { try { await fetchAPI('/sections', { method: 'POST', body: JSON.stringify(sectionData), }); // Close modal and refresh addSectionModal.hide(); loadDashboard(); // Clear form document.getElementById('section-name').value = ''; document.getElementById('section-description').value = ''; document.getElementById('section-settings').value = ''; } catch (error) { console.error('Error creating section:', error); alert(`Error creating section: ${error.message}`); } } async function editSection(sectionId) { alert('Edit section functionality not implemented yet'); } async function deleteSection(sectionId) { if (!confirm('Are you sure you want to delete this section?')) { return; } try { await fetchAPI(`/sections/${sectionId}`, { method: 'DELETE', }); // Refresh sections loadSections(); } catch (error) { console.error('Error deleting section:', error); alert(`Error deleting section: ${error.message}`); } } async function startSection(sectionId) { try { const result = await fetchAPI(`/sections/${sectionId}/start`, { method: 'POST', body: JSON.stringify({ concurrency: 5 }), }); alert(`Section start operation initiated. Task ID: ${result.task_id}`); // Refresh after a delay setTimeout(() => loadDashboard(), 2000); } catch (error) { console.error('Error starting section:', error); alert(`Error starting section: ${error.message}`); } } async function stopSection(sectionId) { try { const result = await fetchAPI(`/sections/${sectionId}/stop`, { method: 'POST', body: JSON.stringify({ concurrency: 5 }), }); alert(`Section stop operation initiated. Task ID: ${result.task_id}`); // Refresh after a delay setTimeout(() => loadDashboard(), 2000); } catch (error) { console.error('Error stopping section:', error); alert(`Error stopping section: ${error.message}`); } } async function restartSection(sectionId) { try { const result = await fetchAPI(`/sections/${sectionId}/restart`, { method: 'POST', body: JSON.stringify({ concurrency: 5 }), }); alert(`Section restart operation initiated. Task ID: ${result.task_id}`); // Refresh after a delay setTimeout(() => loadDashboard(), 2000); } catch (error) { console.error('Error restarting section:', error); alert(`Error restarting section: ${error.message}`); } } // Server operations async function createServer(serverData) { try { await fetchAPI('/servers', { method: 'POST', body: JSON.stringify(serverData), }); // Close modal and refresh addServerModal.hide(); loadDashboard(); } catch (error) { console.error('Error creating server:', error); alert(`Error creating server: ${error.message}`); } } async function editServer(serverId) { alert('Edit server functionality not implemented yet'); } async function deleteServer(serverId) { if (!confirm('Are you sure you want to delete this server?')) { return; } try { await fetchAPI(`/servers/${serverId}`, { method: 'DELETE', }); // Refresh servers loadServers(); } catch (error) { console.error('Error deleting server:', error); alert(`Error deleting server: ${error.message}`); } } async function startServer(serverId) { try { const result = await fetchAPI(`/servers/${serverId}/start`, { method: 'POST', }); // Refresh the current view if (dashboardContent.style.display === 'block') { loadDashboard(); } else if (serversContent.style.display === 'block') { loadServers(); } } catch (error) { console.error('Error starting server:', error); alert(`Error starting server: ${error.message}`); } } async function stopServer(serverId) { try { const result = await fetchAPI(`/servers/${serverId}/stop`, { method: 'POST', }); // Refresh the current view if (dashboardContent.style.display === 'block') { loadDashboard(); } else if (serversContent.style.display === 'block') { loadServers(); } } catch (error) { console.error('Error stopping server:', error); alert(`Error stopping server: ${error.message}`); } } async function restartServer(serverId) { try { const result = await fetchAPI(`/servers/${serverId}/restart`, { method: 'POST', }); // Refresh the current view if (dashboardContent.style.display === 'block') { loadDashboard(); } else if (serversContent.style.display === 'block') { loadServers(); } } catch (error) { console.error('Error restarting server:', error); alert(`Error restarting server: ${error.message}`); } } // Task operations function showTaskDetails(taskId) { alert(`Task details for ${taskId} not implemented yet`); } // Panel settings operations async function updatePanelSettings(settingsData) { try { await fetchAPI('/panel', { method: 'PUT', body: JSON.stringify(settingsData), }); alert('Panel settings updated successfully'); loadPanelSettings(); } catch (error) { console.error('Error updating panel settings:', error); alert(`Error updating panel settings: ${error.message}`); } }

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/rblake2320/sectional-mcp-panel'

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