diff --git a/package-lock.json b/package-lock.json index 050445cc5..c5e911ca5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ }, "devDependencies": { "autoprefixer": "^10.4.21", + "dotenv": "^16.5.0", "postcss": "^8.5.3", "tailwindcss": "^3.4.4" } @@ -176,13 +177,15 @@ } }, "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { - "node": ">=12" + "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" @@ -377,21 +380,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/chalk/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -465,21 +453,6 @@ "node": ">=8" } }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/cliui/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -642,6 +615,19 @@ "dev": true, "license": "MIT" }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -650,9 +636,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.140", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.140.tgz", - "integrity": "sha512-o82Rj+ONp4Ip7Cl1r7lrqx/pXhbp/lh9DpKcMNscFJdh8ebyRofnc7Sh01B4jx403RI0oqTBvlZ7OBIZLMr2+Q==", + "version": "1.5.142", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.142.tgz", + "integrity": "sha512-Ah2HgkTu/9RhTDNThBtzu2Wirdy4DC9b0sMT1pUhbkZQ5U/iwmE+PHZX1MpjD5IkJCc2wSghgGG/B04szAx07w==", "dev": true, "license": "ISC" }, @@ -1881,22 +1867,6 @@ "node": ">=8" } }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1932,6 +1902,19 @@ "node": ">=8" } }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index e3c717a6c..bc9e71c22 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "pulse", - "version": "3.1.2", + "version": "3.1.3", "description": "A lightweight monitoring application for Proxmox VE.", "main": "server/index.js", "scripts": { "start": "cd server && node index.js", "dev:server": "NODE_ENV=development node -r dotenv/config --env-file=.env server/index.js", - "dev:css": "tailwindcss -i ./src/index.css -o ./src/public/output.css --watch", + "dev:css": "tailwindcss -c ./src/tailwind.config.js -i ./src/index.css -o ./src/public/output.css --watch", "build:css": "tailwindcss -c ./src/tailwind.config.js -i ./src/index.css -o ./src/public/output.css", "dev": "concurrently \"npm:dev:server\" \"npm:dev:css\"", "test": "echo \"Error: no test specified\" && exit 1" @@ -23,13 +23,11 @@ "dependencies": { "concurrently": "^8.2.2" }, - // NOTE: Using Tailwind CSS v3 due to build inconsistencies observed with v4. - // v4 (specifically 4.1.4) resulted in incorrectly purged CSS files when built - // within certain Linux environments (e.g., Proxmox LXC - Debian 12 x86_64), - // failing to detect dynamically added classes. v3.4.4 builds correctly. + "_comment_tailwind_v3_reason": "Using Tailwind CSS v3 (3.4.4) due to build inconsistencies observed with v4 (specifically 4.1.4). v4 resulted in incorrectly purged CSS files when built within certain Linux environments (e.g., Proxmox LXC - Debian 12 x86_64), failing to detect dynamically added classes. v3.4.4 builds correctly.", "devDependencies": { - "tailwindcss": "^3.4.4", "autoprefixer": "^10.4.21", - "postcss": "^8.5.3" + "dotenv": "^16.5.0", + "postcss": "^8.5.3", + "tailwindcss": "^3.4.4" } } diff --git a/server/index.js b/server/index.js index fbb807e9d..ea3cd5694 100644 --- a/server/index.js +++ b/server/index.js @@ -1,4 +1,4 @@ -// require('dotenv').config(); // Load environment variables from .env file +require('dotenv').config(); // Load environment variables from .env file // --- BEGIN Environment Variable Validation --- const primaryRequiredEnvVars = [ @@ -478,9 +478,19 @@ app.get('/api/version', (req, res) => { app.get('/api/storage', async (req, res) => { try { - // Return the current node data which includes storage information - // TODO: Consider filtering/transforming this data specifically for storage endpoint if needed - res.json({ nodes: currentNodes }); // Return current node state + // Transform currentNodes into the format expected by updateStorageInfo + const storageInfoByNode = {}; + (currentNodes || []).forEach(node => { + // Assuming storage details are fetched and stored within the node object + // during the discovery cycle. Need to find where Proxmox stores this. + // Use the `storage` property added by the updated `fetchDataForNode` function + storageInfoByNode[node.node] = node.storage || []; // Use node name as key + if (!node.storage) { + // This warning should ideally not appear now unless the storage fetch itself failed + console.warn(`[API /api/storage] No storage data found for node: ${node.node}. Sending empty array.`); + } + }); + res.json(storageInfoByNode); // Return the transformed object } catch (error) { console.error("Error in /api/storage:", error); res.status(500).json({ globalError: error.message || "Failed to fetch storage details." }); @@ -551,7 +561,8 @@ async function fetchDataForNode(apiClient, endpointId, nodeName) { vms: [], containers: [], metrics: [], - nodeStatus: null // Initialize node status object + nodeStatus: null, // Initialize node status object + storage: [] // Initialize storage array }; // Fetch node status ONLY (removed concurrent /cpu fetch) @@ -569,6 +580,22 @@ async function fetchDataForNode(apiClient, endpointId, nodeName) { nodeData.nodeStatus = {}; // Ensure nodeStatus is an object even on failure } + // ---> ADDED: Fetch Node Storage <--- + try { + const storageResult = await apiClient.get(`/nodes/${nodeName}/storage`); + if (storageResult.data && storageResult.data.data && Array.isArray(storageResult.data.data)) { + nodeData.storage = storageResult.data.data; // Store storage array + } else { + console.warn(`[Discovery] Storage data for ${nodeName} (Endpoint: ${endpointId}) was empty or malformed.`); + nodeData.storage = []; // Default to empty array on failure/malformed + } + } catch (err) { + const status = err.response?.status ? ` (Status: ${err.response.status})` : ''; + console.error(`[Discovery] Error fetching storage for node ${nodeName} (Endpoint: ${endpointId})${status}: ${err.message}`); + nodeData.storage = []; // Default to empty array on error + } + // ---> END ADDED <--- + try { // Fetch VMs const vmsResponse = await apiClient.get(`/nodes/${nodeName}/qemu`); @@ -685,7 +712,8 @@ async function fetchDataForNode(apiClient, endpointId, nodeName) { return { vms: nodeData.vms, containers: nodeData.containers, - nodeStatus: nodeData.nodeStatus // Return node status + nodeStatus: nodeData.nodeStatus, // Return node status + storage: nodeData.storage // Return storage array }; } @@ -787,6 +815,12 @@ async function fetchDiscoveryData() { endpointNodes[targetNodeIndex].status = statusData.uptime > 0 ? 'online' : endpointNodes[targetNodeIndex].status; } + // ---> ADDED: Merge node storage if available <--- + if (result.value.storage && Array.isArray(result.value.storage)) { + endpointNodes[targetNodeIndex].storage = result.value.storage; + } + // ---> END ADDED <--- + } else if (result.status === 'rejected') { // Log node-specific failure if needed, but continue processing others console.error(`[Discovery Cycle - ${endpointName}] Failed fetching guest data for a node: ${result.reason?.message || result.reason}`); diff --git a/src/public/app.js b/src/public/app.js index 8b466cc78..d2dee2d15 100644 --- a/src/public/app.js +++ b/src/public/app.js @@ -1687,6 +1687,13 @@ document.addEventListener('DOMContentLoaded', function() { loadingMessage.remove(); } + // ---> ADDED: Also remove the 'Not Configured' banner if present, as we now have data + const notConfiguredBanner = container.querySelector('.pbs-not-configured-banner'); + if (notConfiguredBanner) { + notConfiguredBanner.remove(); + } + // ---> END ADDED + // console.log('[updatePbsInfo] Processing PBS array:', pbsArray); const currentInstanceIds = new Set(); diff --git a/src/tailwind.config.js b/src/tailwind.config.js index 58022533e..33787a900 100644 --- a/src/tailwind.config.js +++ b/src/tailwind.config.js @@ -10,9 +10,6 @@ module.exports = { { pattern: /^(bg|text)-(red|yellow|green|blue)-(100|200|300|400|500|600|700|800|900)(\/50)?$/, }, - { - pattern: /^dark:(bg|text)-(red|yellow|green|blue)-(100|200|300|400|500|600|700|800|900)(\/50)?$/, - }, 'vm-icon', 'ct-icon', ],