document.addEventListener('DOMContentLoaded', () => { const contextSelect = document.getElementById('context'); const namespaceSelect = document.getElementById('namespace'); const resourceSelect = document.getElementById('resource-type'); const resourceList = document.getElementById('resource-list'); const resourceTitle = document.getElementById('resource-title'); const searchInput = document.getElementById('search-input'); const searchBtn = document.getElementById('search-btn'); const refreshBtn = document.getElementById('refresh-btn'); const setDefaultBtn = document.getElementById('set-default'); const actionName = document.getElementById('action-name'); const actionOutput = document.getElementById('action-output'); let currentPage = 1; const pageSize = 10; function saveSession() { sessionStorage.setItem('k8s_context', contextSelect.value); sessionStorage.setItem('k8s_namespace', namespaceSelect.value); sessionStorage.setItem('k8s_resource', resourceSelect.value); } function loadSession() { const ctx = sessionStorage.getItem('k8s_context'); const ns = sessionStorage.getItem('k8s_namespace'); const res = sessionStorage.getItem('k8s_resource'); if (ctx) contextSelect.value = ctx; if (ns) namespaceSelect.value = ns; if (res) resourceSelect.value = res; } function updateActionVisibility() { const isNode = resourceSelect.value === 'nodes'; const isConfigmap = resourceSelect.value === 'configmaps'; document.getElementById('btn-delete').style.display = (isNode || isConfigmap) ? 'none' : 'inline-block'; document.getElementById('btn-logs').style.display = (isNode || isConfigmap) ? 'none' : 'inline-block'; document.getElementById('btn-exec').style.display = (isNode || isConfigmap) ? 'none' : 'inline-block'; document.getElementById('btn-cordon').style.display = isNode ? 'inline-block' : 'none'; document.getElementById('btn-uncordon').style.display = isNode ? 'inline-block' : 'none'; } async function loadContexts() { try { const res = await fetch('http://localhost:8000/contexts'); const data = await res.json(); contextSelect.innerHTML = ''; data.contexts.forEach(ctx => { const opt = document.createElement('option'); opt.value = ctx; opt.textContent = ctx; if (ctx === data.current) opt.selected = true; contextSelect.appendChild(opt); }); loadSession(); loadNamespaces(); } catch (e) { console.error('Failed to load contexts', e); } } async function loadNamespaces() { try { const ctx = contextSelect.value || null; const url = new URL('http://localhost:8000/namespaces'); if (ctx) url.searchParams.set('context', ctx); const res = await fetch(url); const data = await res.json(); namespaceSelect.innerHTML = ''; data.namespaces.forEach(ns => { const opt = document.createElement('option'); opt.value = ns; opt.textContent = ns; namespaceSelect.appendChild(opt); }); loadSession(); loadResources(); } catch (e) { console.error('Failed to load namespaces', e); } } async function loadResources(bypass = false, page = 1) { const resType = resourceSelect.value; const ns = namespaceSelect.value || 'default'; const ctx = contextSelect.value || null; const search = searchInput.value.trim(); resourceTitle.textContent = resType.charAt(0).toUpperCase() + resType.slice(1); resourceList.innerHTML = '
  • Loading...
  • '; currentPage = page; try { let url; if (resType === 'pods') { url = `http://localhost:8000/pods?namespace=${encodeURIComponent(ns)}&page=${page}&page_size=${pageSize}&bypass=${bypass}`; } else if (resType === 'nodes') { url = `http://localhost:8000/nodes?page=${page}&page_size=${pageSize}&bypass=${bypass}`; } else if (resType === 'pvcs') { url = `http://localhost:8000/pvcs?namespace=${encodeURIComponent(ns)}&page=${page}&page_size=${pageSize}&bypass=${bypass}`; } else if (resType === 'configmaps') { url = `http://localhost:8000/configmaps?namespace=${encodeURIComponent(ns)}&page=${page}&page_size=${pageSize}&bypass=${bypass}`; } else { url = `http://localhost:8000/pods?namespace=${encodeURIComponent(ns)}&page=${page}&page_size=${pageSize}&bypass=${bypass}`; } if (search) url += `&search=${encodeURIComponent(search)}`; if (ctx) url += `&context=${encodeURIComponent(ctx)}`; const res = await fetch(url); const data = await res.json(); renderList(data); updateActionVisibility(); } catch (e) { console.error('Failed to load resources', e); resourceList.innerHTML = '
  • Error loading resources
  • '; } } function renderList(data) { const items = data.items || []; const total = data.total || 0; const page = data.page || 1; const pageSize = data.page_size || 10; const totalPages = Math.max(1, Math.ceil(total / pageSize)); resourceList.innerHTML = ''; if (items.length === 0) { resourceList.innerHTML = '
  • No results
  • '; } else { items.forEach(item => { const li = document.createElement('li'); li.style.cursor = 'pointer'; li.innerHTML = `${item.name} (${item.namespace})`; li.addEventListener('click', () => { actionName.value = item.name; document.getElementById('exec-pod').value = item.name; Array.from(resourceList.children).forEach(child => { child.classList.remove('selected'); child.style.background = ''; }); li.classList.add('selected'); li.style.background = '#ebf8ff'; }); resourceList.appendChild(li); }); } const pageInfo = document.getElementById('page-info'); if (pageInfo) pageInfo.textContent = `Page ${page} of ${totalPages} (${total} total)`; const prevBtn = document.getElementById('prev-page'); const nextBtn = document.getElementById('next-page'); if (prevBtn) prevBtn.disabled = page <= 1; if (nextBtn) nextBtn.disabled = page >= totalPages; } contextSelect.addEventListener('change', () => { saveSession(); loadNamespaces(); }); namespaceSelect.addEventListener('change', () => { saveSession(); loadResources(); }); resourceSelect.addEventListener('change', () => { saveSession(); loadResources(); }); searchBtn.addEventListener('click', () => loadResources(false, 1)); refreshBtn.addEventListener('click', () => loadResources(true, 1)); setDefaultBtn.addEventListener('click', () => { saveSession(); alert('Defaults saved for session'); }); document.getElementById('prev-page').addEventListener('click', () => { if (currentPage > 1) loadResources(false, currentPage - 1); }); document.getElementById('next-page').addEventListener('click', () => { loadResources(false, currentPage + 1); }); document.getElementById('btn-describe').addEventListener('click', async () => { const name = actionName.value.trim(); if (!name) return; const resType = resourceSelect.value === 'pvcs' ? 'pvc' : (resourceSelect.value === 'nodes' ? 'node' : (resourceSelect.value === 'configmaps' ? 'configmap' : 'pod')); const ns = namespaceSelect.value || 'default'; const ctx = contextSelect.value || null; try { const url = new URL('http://localhost:8000/describe'); url.searchParams.set('resource', resType); url.searchParams.set('name', name); url.searchParams.set('namespace', ns); if (ctx) url.searchParams.set('context', ctx); const res = await fetch(url); const data = await res.json(); actionOutput.textContent = data.output || data.error || JSON.stringify(data); } catch (e) { actionOutput.textContent = 'Error: ' + e; } }); document.getElementById('btn-logs').addEventListener('click', async () => { const name = actionName.value.trim(); if (!name) return; const ns = namespaceSelect.value || 'default'; const ctx = contextSelect.value || null; try { const url = new URL('http://localhost:8000/logs'); url.searchParams.set('pod', name); url.searchParams.set('namespace', ns); if (ctx) url.searchParams.set('context', ctx); const res = await fetch(url); const data = await res.json(); actionOutput.textContent = data.output || data.error || JSON.stringify(data); } catch (e) { actionOutput.textContent = 'Error: ' + e; } }); document.getElementById('btn-delete').addEventListener('click', async () => { const name = actionName.value.trim(); if (!name || !confirm('Delete ' + name + '?')) return; const resType = resourceSelect.value === 'pvcs' ? 'pvc' : (resourceSelect.value === 'nodes' ? 'node' : (resourceSelect.value === 'configmaps' ? 'configmap' : 'pod')); const ns = namespaceSelect.value || 'default'; const ctx = contextSelect.value || null; try { const url = `http://localhost:8000/delete?resource=${encodeURIComponent(resType)}&name=${encodeURIComponent(name)}&namespace=${encodeURIComponent(ns)}${ctx ? '&context=' + encodeURIComponent(ctx) : ''}`; const res = await fetch(url, { method: 'POST' }); const data = await res.json(); actionOutput.textContent = data.output || data.error || JSON.stringify(data); loadResources(); } catch (e) { actionOutput.textContent = 'Error: ' + e; } }); document.getElementById('btn-exec').addEventListener('click', async () => { const pod = document.getElementById('exec-pod').value.trim(); if (!pod) return; const ns = namespaceSelect.value || 'default'; const ctx = contextSelect.value || null; const container = document.getElementById('exec-container').value.trim() || null; const cmd = document.getElementById('exec-cmd').value.trim() || 'ls'; try { const url = new URL('http://localhost:8000/exec'); url.searchParams.set('pod', pod); url.searchParams.set('namespace', ns); url.searchParams.set('command', cmd); if (container) url.searchParams.set('container', container); if (ctx) url.searchParams.set('context', ctx); const res = await fetch(url); const data = await res.json(); actionOutput.textContent = data.output || data.error || JSON.stringify(data); } catch (e) { actionOutput.textContent = 'Error: ' + e; } }); document.getElementById('btn-cordon').addEventListener('click', async () => { const node = actionName.value.trim(); if (!node) return; const ctx = contextSelect.value || null; try { const url = new URL('http://localhost:8000/cordon'); url.searchParams.set('node', node); if (ctx) url.searchParams.set('context', ctx); const res = await fetch(url); const data = await res.json(); actionOutput.textContent = data.output || data.error || JSON.stringify(data); } catch (e) { actionOutput.textContent = 'Error: ' + e; } }); document.getElementById('btn-uncordon').addEventListener('click', async () => { const node = actionName.value.trim(); if (!node) return; const ctx = contextSelect.value || null; try { const url = new URL('http://localhost:8000/uncordon'); url.searchParams.set('node', node); if (ctx) url.searchParams.set('context', ctx); const res = await fetch(url); const data = await res.json(); actionOutput.textContent = data.output || data.error || JSON.stringify(data); } catch (e) { actionOutput.textContent = 'Error: ' + e; } }); loadContexts(); });