diff --git a/docs/feature_ideas/01_detailed_disk_info.md b/docs/feature_ideas/01_detailed_disk_info.md new file mode 100644 index 000000000..2690fedcd --- /dev/null +++ b/docs/feature_ideas/01_detailed_disk_info.md @@ -0,0 +1,22 @@ +# Feature Idea: Detailed Disk Information (PVE) + +**Goal:** Display more granular disk information for VMs and Containers beyond just the current total I/O. + +**Potential Data:** + +* Individual virtual disk usage/size. +* Disk throughput per virtual disk (if available separately from total I/O). +* Physical disk S.M.A.R.T. health status for the underlying host nodes. + +**Relevant API Endpoints:** + +* `/nodes/{node}/{type}/{vmid}/config`: Contains configuration details for virtual disks attached to a VM/CT (size, storage, format). +* `/nodes/{node}/disks/list`: Lists physical disks on the node. +* `/nodes/{node}/disks/smart?disk={diskname}`: Retrieves S.M.A.R.T. data for a specific physical disk on the node (requires `Disk.Audit` or higher permissions). +* `/nodes/{node}/{type}/{vmid}/status/current`: Already used, provides aggregate disk I/O (`diskread`, `diskwrite`). Check if this contains per-disk details already. + +**Implementation Notes:** + +* Requires parsing the `config` endpoint to identify virtual disks. +* Fetching S.M.A.R.T. data requires an additional API call per physical disk and potentially higher permissions than `PVEAuditor`. +* Consider how to display this: added columns, a modal/popup, or a separate "details" view. \ No newline at end of file diff --git a/docs/feature_ideas/02_network_interface_details.md b/docs/feature_ideas/02_network_interface_details.md new file mode 100644 index 000000000..f03a077d1 --- /dev/null +++ b/docs/feature_ideas/02_network_interface_details.md @@ -0,0 +1,23 @@ +# Feature Idea: Network Interface Details (PVE) + +**Goal:** Display configuration details for network interfaces attached to VMs and Containers. + +**Potential Data:** + +* MAC Address per interface. +* Connected Bridge/Network. +* VLAN Tag (if applicable). +* Model/Type of virtual NIC. +* Potentially live throughput per interface (if API provides this separately from total). + +**Relevant API Endpoints:** + +* `/nodes/{node}/{type}/{vmid}/config`: Contains configuration details for network interfaces (e.g., `net0`, `net1`) including MAC, bridge, tag, model. +* `/nodes/{node}/{type}/{vmid}/status/current`: Already used, provides aggregate network I/O (`netin`, `netout`). Check if this contains per-interface details already. + +**Implementation Notes:** + +* Requires parsing the `config` endpoint to identify and extract details for each network interface (e.g., `net0`, `net1`, ...). +* Displaying live per-interface stats might not be feasible or may already be aggregated in the `/status/current` data. +* Focus on displaying the static configuration first (MAC, bridge, VLAN). +* Consider how to display: added columns (might get wide), a modal/popup, or a details view. \ No newline at end of file diff --git a/docs/feature_ideas/03_vm_ct_backup_status.md b/docs/feature_ideas/03_vm_ct_backup_status.md new file mode 100644 index 000000000..6af58b540 --- /dev/null +++ b/docs/feature_ideas/03_vm_ct_backup_status.md @@ -0,0 +1,21 @@ +# Feature Idea: VM/CT Backup Status (PVE) + +**Goal:** Show the status or timestamp of the last backup taken *by Proxmox VE itself* (snapshot backups, not necessarily PBS) for each VM and Container. + +**Potential Data:** + +* Timestamp of the last successful PVE snapshot/backup. +* Name/description of the last snapshot. +* Indication if a backup task is currently running for the guest. + +**Relevant API Endpoints:** + +* `/nodes/{node}/{type}/{vmid}/snapshot`: Lists all snapshots for the specific VM or Container. This includes backups created via the PVE GUI/API. Need to parse the output to find the most recent one, potentially filtering by name convention if backups have specific names. +* `/nodes/{node}/tasks`: Could potentially show running backup tasks related to a specific VMID, but filtering might be complex. + +**Implementation Notes:** + +* Requires an additional API call per VM/CT (`/snapshot`). Consider performance implications if there are many guests. +* Parsing the snapshot list is needed to identify the most recent backup. +* PBS backups are handled separately; this focuses only on backups visible directly within PVE for the guest. +* Display could be a dedicated column ("Last PVE Backup") or integrated into a status indicator. \ No newline at end of file diff --git a/docs/feature_ideas/04_hardware_passthrough_info.md b/docs/feature_ideas/04_hardware_passthrough_info.md new file mode 100644 index 000000000..30771e5ad --- /dev/null +++ b/docs/feature_ideas/04_hardware_passthrough_info.md @@ -0,0 +1,21 @@ +# Feature Idea: Hardware/PCI Passthrough Info (PVE) + +**Goal:** Display information about hardware devices (PCI, USB) passed through to specific VMs. + +**Potential Data:** + +* List of PCI devices passed through (Vendor/Device ID, Name). +* List of USB devices passed through. + +**Relevant API Endpoints:** + +* `/nodes/{node}/qemu/{vmid}/config`: Contains configuration details for VMs, including `hostpci` entries for PCI passthrough and `usb` entries for USB passthrough. +* `/nodes/{node}/hardware/pci`: Lists available PCI devices on the host. +* `/nodes/{node}/hardware/usb`: Lists available USB devices on the host. + +**Implementation Notes:** + +* Requires parsing the VM's `config` endpoint. +* This information is static configuration. +* Likely only relevant for a subset of users. +* Display could be in a details view or a modal, as it's not primary monitoring data. \ No newline at end of file diff --git a/docs/feature_ideas/05_guest_ip_addresses.md b/docs/feature_ideas/05_guest_ip_addresses.md new file mode 100644 index 000000000..681ba49c0 --- /dev/null +++ b/docs/feature_ideas/05_guest_ip_addresses.md @@ -0,0 +1,22 @@ +# Feature Idea: Guest IP Addresses (PVE) + +**Goal:** Display the IP addresses assigned *within* the guest operating system for VMs. + +**Potential Data:** + +* IPv4 addresses. +* IPv6 addresses. +* Associated MAC address (to link IP to a NIC). + +**Relevant API Endpoints:** + +* `/nodes/{node}/qemu/{vmid}/agent/get-network-ifs`: Retrieves network interface details, including IP addresses, directly from the guest OS via the QEMU Guest Agent. + +**Implementation Notes:** + +* **Crucially depends on the QEMU Guest Agent being installed and running within the VM.** If the agent is not present or running, the API call will fail or return no data. +* Requires an additional API call per running VM. +* Need robust error handling for when the agent is unavailable. +* UI should clearly indicate if IPs are missing due to the agent not running. +* Consider how to display multiple IPs/interfaces (e.g., tooltip, comma-separated list in a column). +* This is likely **high-value** information for users. \ No newline at end of file diff --git a/docs/feature_ideas/06_firewall_rules.md b/docs/feature_ideas/06_firewall_rules.md new file mode 100644 index 000000000..4a9309ad2 --- /dev/null +++ b/docs/feature_ideas/06_firewall_rules.md @@ -0,0 +1,24 @@ +# Feature Idea: Firewall Rules (PVE) + +**Goal:** Provide visibility into the Proxmox VE firewall rules. + +**Potential Data:** + +* Display firewall rules configured at the Datacenter level. +* Display firewall rules configured at the Node level. +* Display firewall rules configured per VM/CT. + +**Relevant API Endpoints:** + +* `/cluster/firewall/rules`: Gets firewall rules defined at the datacenter level. +* `/nodes/{node}/firewall/rules`: Gets firewall rules defined for a specific node. +* `/nodes/{node}/{type}/{vmid}/firewall/rules`: Gets firewall rules defined for a specific VM or Container. +* Other related endpoints under `/cluster/firewall/`, `/nodes/{node}/firewall/`, `/nodes/{node}/{type}/{vmid}/firewall/` for options, groups, aliases etc. + +**Implementation Notes:** + +* Firewall rules can be numerous and complex. +* Displaying this effectively in the main dashboard is likely impractical. +* Might be better suited for a dedicated "Firewall" view or section within the application. +* Requires careful consideration of which rules (datacenter, node, guest) are most relevant to display and how. +* `PVEAuditor` role should be sufficient for reading rules. \ No newline at end of file diff --git a/docs/feature_ideas/07_ha_group_info.md b/docs/feature_ideas/07_ha_group_info.md new file mode 100644 index 000000000..f76182757 --- /dev/null +++ b/docs/feature_ideas/07_ha_group_info.md @@ -0,0 +1,23 @@ +# Feature Idea: High Availability (HA) Group Information (PVE) + +**Goal:** Display more detailed information about the High Availability configuration and status. + +**Potential Data:** + +* List of HA groups configured. +* Members (VMs/CTs) within each HA group. +* Current state of HA services/resources (e.g., master node, resource states). +* HA fencing status/configuration. + +**Relevant API Endpoints:** + +* `/cluster/ha/resources`: Currently used, lists resources managed by HA. +* `/cluster/ha/groups`: Lists the configured HA groups and their settings (nodes, priorities). +* `/cluster/ha/status/current`: Provides the current status of the HA manager, including master node and resource states. +* `/cluster/ha/status/manager_status`: Detailed status of the HA manager. + +**Implementation Notes:** + +* Requires additional API calls to `/groups` and `/status/current`. +* Could be displayed in a dedicated HA status section or integrated subtly into the existing views (e.g., indicating which guests are HA managed). +* Useful for users relying on Proxmox HA features. \ No newline at end of file diff --git a/docs/feature_ideas/08_pbs_task_log_summary.md b/docs/feature_ideas/08_pbs_task_log_summary.md new file mode 100644 index 000000000..7987b9c30 --- /dev/null +++ b/docs/feature_ideas/08_pbs_task_log_summary.md @@ -0,0 +1,21 @@ +# Feature Idea: PBS Task Log Summary + +**Goal:** Provide a quick view of the end of the task log, especially for failed Proxmox Backup Server (PBS) tasks. + +**Potential Data:** + +* Display the last N lines (e.g., 5-10) of a task log. +* Show the exit status or error message from the log. + +**Relevant API Endpoints:** + +* `/nodes/{node}/tasks/{upid}/log`: Fetches the log content for a specific task UPID. +* `/nodes/{node}/tasks/{upid}/status`: Fetches the final status of a completed task. +* `/nodes/{node}/tasks`: Currently used to list tasks. + +**Implementation Notes:** + +* Requires an additional API call per task for which logs are needed (likely only failed or recent tasks). +* The `/log` endpoint returns the full log; need to process the response to extract only the last few lines (or use API parameters if available to limit the output, e.g., `?start=X&limit=Y`). Check API docs for log limiting parameters. +* Could be displayed in a modal when clicking on a task, or inline if kept very brief. +* Helps quickly diagnose failures without needing to open the PBS UI. \ No newline at end of file diff --git a/docs/feature_ideas/09_pbs_datastore_schedule.md b/docs/feature_ideas/09_pbs_datastore_schedule.md new file mode 100644 index 000000000..e731ba5a1 --- /dev/null +++ b/docs/feature_ideas/09_pbs_datastore_schedule.md @@ -0,0 +1,21 @@ +# Feature Idea: PBS Datastore Verification/GC Schedule + +**Goal:** Display the configured schedules for maintenance jobs (Verification, Garbage Collection) on Proxmox Backup Server (PBS) datastores. + +**Potential Data:** + +* Garbage collection schedule (e.g., "daily at 02:00"). +* Verification schedule. +* Pruning schedule associated with the datastore. + +**Relevant API Endpoints:** + +* `/config/datastore`: Lists configured datastores. +* `/config/datastore/{store}`: Gets the detailed configuration for a specific datastore (`{store}`), which includes schedule properties for `gc-schedule` and `verify-schedule`. + +**Implementation Notes:** + +* Requires fetching the configuration for each datastore listed by `/config/datastore`. +* The schedule format returned by the API needs to be parsed into a human-readable string. +* Could be displayed alongside the datastore usage information already fetched. +* Provides useful context about the maintenance routines configured on the PBS. \ No newline at end of file diff --git a/docs/feature_ideas/10_pbs_tape_backup_info.md b/docs/feature_ideas/10_pbs_tape_backup_info.md new file mode 100644 index 000000000..3c2a7d8d6 --- /dev/null +++ b/docs/feature_ideas/10_pbs_tape_backup_info.md @@ -0,0 +1,24 @@ +# Feature Idea: PBS Tape Backup Information + +**Goal:** Display status and information related to tape backup operations within Proxmox Backup Server (PBS), if used. + +**Potential Data:** + +* List of configured tape drives and their status. +* Status of ongoing or recent tape backup/restore jobs. +* Information about media pools and tapes. + +**Relevant API Endpoints:** + +* `/nodes/{node}/tape/drive`: Lists tape drives and their status. +* `/nodes/{node}/tape/job`: Lists tape backup/restore jobs. +* `/nodes/{node}/tape/pool`: Lists media pools. +* `/nodes/{node}/tape/media`: Lists tapes (media). +* Various endpoints under these for specific details (e.g., job logs, drive status). + +**Implementation Notes:** + +* This is a niche feature, only relevant if the user has configured tape backups in PBS. +* Requires multiple additional API calls. +* Consider adding a dedicated section or view for Tape Backup status, only visible if tape drives are detected or configured. +* `Audit` role might be sufficient, but needs verification. \ No newline at end of file diff --git a/src/public/app.js b/src/public/app.js index fadc3190e..e9c43abc9 100644 --- a/src/public/app.js +++ b/src/public/app.js @@ -209,8 +209,8 @@ document.addEventListener('DOMContentLoaded', function() { style="width: ${numericPercent}%;" > - - + + ${text} @@ -594,7 +594,7 @@ document.addEventListener('DOMContentLoaded', function() { const diskColorClass = getUsageColor(diskPercent); // Create tooltips and bar HTML using correct fields - const cpuTooltipText = `${cpuPercent.toFixed(1)}%`; + const cpuTooltipText = `${cpuPercent.toFixed(1)}%${node.maxcpu && node.maxcpu > 0 ? ` (${(node.cpu * node.maxcpu).toFixed(1)}/${node.maxcpu} cores)` : ''}`; const memTooltipText = `${formatBytes(memUsed)} / ${formatBytes(memTotal)} (${memPercent.toFixed(1)}%)`; // ---- START DEBUG LOG ---- // console.log(`[Node: ${node.node}] diskUsed raw: ${diskUsed}, diskTotal raw: ${diskTotal}`); @@ -633,10 +633,10 @@ document.addEventListener('DOMContentLoaded', function() { ${node.node || 'N/A'} - - ${cpuBarHTML} - ${memoryBarHTML} - ${diskBarHTML} + + ${cpuBarHTML} + ${memoryBarHTML} + ${diskBarHTML} ${uptimeFormatted} ${normalizedLoadFormatted} `; @@ -889,19 +889,21 @@ document.addEventListener('DOMContentLoaded', function() { // Create ONE table for all nodes const table = document.createElement('table'); - table.className = 'w-full text-sm border-collapse table-fixed'; + // Add min-w-full and table-auto for consistency and scrolling + table.className = 'w-full text-sm border-collapse table-auto min-w-full'; const thead = document.createElement('thead'); // Define widths for most columns, leave Usage column without width + // REMOVED explicit width classes (w-X/Y, w-[...px]) thead.innerHTML = ` - Storage - Content - Type - Shared - Usage - Avail - Total + Storage + Content + Type + Shared + Usage + Avail + Total `; table.appendChild(thead); @@ -964,19 +966,24 @@ document.addEventListener('DOMContentLoaded', function() { const contentBadges = contentTypes.map(ct => { const details = getContentBadgeDetails(ct); // Use the updated helper // Re-add data-tooltip with the purpose, add trigger class and cursor - return `${ct}`; + return `${ct}`; }).join(''); const usageBarHTML = createProgressTextBarHTML(usagePercent, usageTooltipText, usageColorClass); + // Standardize padding to p-1 px-2 like other tables row.innerHTML = ` - ${store.storage || 'N/A'} - ${contentBadges || '-'} - ${store.type || 'N/A'} - ${sharedIcon} - ${usageBarHTML} - ${formatBytes(store.avail)} - ${formatBytes(store.total)} + ${store.storage || 'N/A'} + + ${contentBadges || '-'} + ${store.type || 'N/A'} + ${sharedIcon} + + + + ${usageBarHTML} + ${formatBytes(store.avail)} + ${formatBytes(store.total)} `; tbody.appendChild(row); }); @@ -1315,9 +1322,9 @@ document.addEventListener('DOMContentLoaded', function() { function createGuestRow(guest) { // console.log('[createGuestRow] Received guest data:', guest); const row = document.createElement('tr'); - // Add more prominent hover background, shadow, lift effect, and transition - // Also add opacity and grayscale for stopped guests - row.className = `transition-all duration-150 ease-out hover:bg-gray-100 dark:hover:bg-gray-700 hover:shadow-md hover:-translate-y-px ${guest.status === 'stopped' ? 'opacity-60 grayscale' : ''}`; + // ---> MODIFIED: Remove responsive classes from the row <--- + row.className = `border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/50 ${guest.status === 'stopped' ? 'opacity-60 grayscale' : ''}`; + // ---> END MODIFIED <--- row.setAttribute('data-name', guest.name.toLowerCase()); row.setAttribute('data-type', guest.type.toLowerCase()); row.setAttribute('data-node', guest.node.toLowerCase()); @@ -1334,17 +1341,18 @@ document.addEventListener('DOMContentLoaded', function() { if (guest.status === 'running') { const cpuPercent = Math.round(guest.cpu * 100); - const memoryPercent = guest.memory; + const memoryPercent = guest.memory; const diskPercent = guest.disk; + // Revert to original calculation using guest.cpus const cpuTooltipText = `${cpuPercent}% ${guest.cpus ? `(${(guest.cpu * guest.cpus).toFixed(1)}/${guest.cpus} cores)` : ''}`; const memoryTooltipText = guest.memoryTotal ? `${formatBytesInt(guest.memoryCurrent)} / ${formatBytesInt(guest.memoryTotal)} (${memoryPercent}%)` : `${memoryPercent}%`; const diskTooltipText = guest.diskTotal ? `${formatBytesInt(guest.diskCurrent)} / ${formatBytesInt(guest.diskTotal)} (${diskPercent}%)` : `${diskPercent}%`; - + const cpuColorClass = getUsageColor(cpuPercent); const memColorClass = getUsageColor(memoryPercent); const diskColorClass = getUsageColor(diskPercent); - + cpuBarHTML = createProgressTextBarHTML(cpuPercent, cpuTooltipText, cpuColorClass); memoryBarHTML = createProgressTextBarHTML(memoryPercent, memoryTooltipText, memColorClass); diskBarHTML = createProgressTextBarHTML(diskPercent, diskTooltipText, diskColorClass); @@ -1357,7 +1365,7 @@ document.addEventListener('DOMContentLoaded', function() { // --- End calculation block --- const typeIconClass = guest.type === 'VM' - ? 'vm-icon bg-blue-100 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300 px-1.5 py-0.5 font-medium' + ? 'vm-icon bg-blue-100 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300 px-1.5 py-0.5 font-medium' : 'ct-icon bg-green-100 dark:bg-green-900/50 text-green-700 dark:text-green-300 px-1.5 py-0.5 font-medium'; const typeIcon = `${guest.type}`; @@ -1374,9 +1382,47 @@ document.addEventListener('DOMContentLoaded', function() { ${netInFormatted} ${netOutFormatted} `; + + // ---> ADDED: Loop through TDs to add responsive classes <--- + // REMOVED: Responsive class and data-label logic + /* + const cells = row.querySelectorAll('td'); + cells.forEach(td => { + td.classList.add('block', 'sm:table-cell', 'px-3', 'py-2', 'align-middle', 'text-sm'); // Add base responsive and style classes + // Add a pseudo-element for the label on mobile if needed + const headerText = getHeaderTextForCell(td); // You'll need to implement getHeaderTextForCell + if (headerText) { + td.setAttribute('data-label', headerText); + } + }); + */ + // ---> END ADDED <--- + return row; } + // --- ADDED HELPER FUNCTION --- + function getHeaderTextForCell(td) { + try { + const cellIndex = Array.from(td.parentNode.children).indexOf(td); + const headerRow = document.querySelector('#main-table thead tr'); + if (!headerRow) { + console.warn("Header row not found for #main-table"); + return 'Unknown Header'; + } + const headerCell = headerRow.children[cellIndex]; + if (!headerCell) { + console.warn(`Header cell not found at index ${cellIndex}`); + return 'Unknown Header'; + } + return headerCell.textContent || headerCell.innerText || 'Unknown Header'; + } catch (error) { + console.error("Error getting header text for cell:", error, td); + return 'Error Header'; + } + } + // --- END ADDED HELPER FUNCTION --- + // --- WebSocket Message Handling --- // Add a generic listener to catch *any* events from the server socket.onAny((eventName, ...args) => { @@ -1475,12 +1521,8 @@ document.addEventListener('DOMContentLoaded', function() { // ---> END CHANGE function requestFullData() { - // console.log("Requesting full data..."); - if (socket.connected) { - socket.emit('requestData'); // Standard emit - } else { - console.warn("Socket not connected, cannot request full data."); - } + console.log('Requesting full data reload from server...'); + socket.emit('requestData'); // Ensure this uses the correct event name } // --- Function to Reset Dashboard Filters/Sort --- @@ -1854,11 +1896,11 @@ document.addEventListener('DOMContentLoaded', function() { const row = document.createElement('tr'); row.className = 'border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors duration-150 ease-in-out'; row.innerHTML = ` - ${target} - ${statusIcon} - ${startTime} - ${duration} - ${shortUpid} + ${target} + ${statusIcon} + ${startTime} + ${duration} + ${shortUpid} `; // Display shortened UPID, full UPID in title tableBody.appendChild(row); }); @@ -1885,11 +1927,11 @@ document.addEventListener('DOMContentLoaded', function() { const row = document.createElement('tr'); row.className = 'border-b border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors duration-150 ease-in-out'; row.innerHTML = ` - ${target} - ${statusIcon} - ${startTime} - ${duration} - ${shortUpid} + ${target} + ${statusIcon} + ${startTime} + ${duration} + ${shortUpid} `; // Display shortened UPID, full UPID in title tableBody.appendChild(row); }); diff --git a/src/public/index.html b/src/public/index.html index 521bf28b8..ea05d89e9 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -33,12 +33,11 @@
- -
- -
- -
+ +
+ + +
Pulse
- -
+ +
@@ -102,16 +101,16 @@