This commit is contained in:
courtmanr@gmail.com
2025-02-28 20:39:40 +00:00
parent 0cefe252ca
commit 7e780e1792
17 changed files with 2147 additions and 1717 deletions
+3
View File
@@ -17,6 +17,9 @@ build/
logs/
*.log
# Reports
reports/
# IDE and editor files
.idea/
.vscode/
+22 -20
View File
@@ -1,28 +1,30 @@
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/dist ./dist
# Copy package files
COPY package*.json ./
COPY frontend/package*.json ./frontend/
RUN npm ci --only=production
# Install dependencies
RUN npm ci
RUN cd frontend && npm ci
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD wget -qO- http://localhost:${PORT:-3000}/api/health || exit 1
# Copy application files
COPY . .
# Expose the port
EXPOSE ${PORT:-3000}
# Make the startup script executable
RUN chmod +x start-pulse.sh
# Start the application
CMD ["node", "dist/server.js"]
# Set environment variables
ENV NODE_ENV=development
ENV LOG_LEVEL=info
ENV ENABLE_DEV_TOOLS=true
ENV PORT=3000
ENV DOCKER_CONTAINER=true
# Expose the backend and frontend ports
EXPOSE 3000 5173
# Start both the backend and frontend (using the start-pulse.sh script)
CMD ["./start-pulse.sh"]
+51 -103
View File
@@ -7,9 +7,7 @@ A lightweight, responsive ProxMox monitoring application that displays real-time
- Real-time monitoring of ProxMox nodes, VMs, and containers
- Dashboard with summary cards for nodes, guests, and resources
- Responsive design that works on desktop and mobile
- WebSocket connection for real-time updates
- Development tools for easier debugging
- Configurable polling intervals for optimal performance
- WebSocket connection for live updates
## Installation
@@ -18,15 +16,50 @@ A lightweight, responsive ProxMox monitoring application that displays real-time
```
npm install
```
3. Create a `.env` file based on the `.env.example` file
4. Build the application:
3. Install frontend dependencies:
```
npm run build
```
5. Start the application:
```
npm start
cd frontend && npm install && cd ..
```
4. Create a `.env` file based on the `.env.example` file
## Running the Application
The simplest way to run the application is to use one of these methods:
### Using the startup script directly
```bash
# Make the script executable (only needed once)
chmod +x start-pulse.sh
# Run the application
./start-pulse.sh
```
### Using npm
```bash
# Run the application
npm run pulse
```
### Using Docker
```bash
# Build the Docker image
docker build -t proxmox-pulse .
# Run the Docker container
docker run -p 3000:3000 -p 5173:5173 --env-file .env proxmox-pulse
```
This will:
1. Kill any existing server processes
2. Start the backend server in development mode
3. Start the frontend Vite server
4. Host the application at http://192.168.0.130:5173/
The application will show real-time metrics from your ProxMox servers configured in the .env file.
## Configuration
@@ -37,120 +70,35 @@ The application uses environment variables for configuration. You can create a `
PROXMOX_NODE_1_NAME=Proxmox Node 1
PROXMOX_NODE_1_HOST=https://192.168.0.132:8006
PROXMOX_NODE_1_TOKEN_ID=root@pam!pulse
PROXMOX_NODE_1_TOKEN_SECRET=e1850350-6afc-4b8e-ae28-472152af84f9
PROXMOX_NODE_1_TOKEN_SECRET=your-token-secret
# Node 2
PROXMOX_NODE_2_NAME=Proxmox Node 2
PROXMOX_NODE_2_HOST=https://192.168.0.141:8006
PROXMOX_NODE_2_TOKEN_ID=root@pam!pulse
PROXMOX_NODE_2_TOKEN_SECRET=63e21f63-4cfd-4ba0-9b14-fc681c59d932
PROXMOX_NODE_2_TOKEN_SECRET=your-token-secret
# App Configuration
PORT=3000
NODE_ENV=development
LOG_LEVEL=debug
LOG_LEVEL=info
ENABLE_DEV_TOOLS=true
METRICS_HISTORY_MINUTES=60
IGNORE_SSL_ERRORS=true
# Polling Intervals (in milliseconds)
NODE_POLLING_INTERVAL_MS=10000
EVENT_POLLING_INTERVAL_MS=2000
```
### Polling Intervals
The application uses polling to fetch data from ProxMox nodes:
- `NODE_POLLING_INTERVAL_MS`: How often to poll for node status, VMs, and containers (default: 10000ms)
- `EVENT_POLLING_INTERVAL_MS`: How often to poll for events (default: 2000ms)
The application also implements adaptive polling for events:
- When events are detected, polling frequency temporarily increases
- Quick follow-up polls occur 500ms after receiving events
- Polling frequency remains higher for 10 seconds after activity is detected
Decreasing these values will make the application more responsive but may increase server load. Increasing them will reduce server load but make updates less frequent.
### Important Note on API Tokens
If your ProxMox API token ID contains special characters (like `!`, `?`, `&`, etc.), you may encounter issues when using it in shell commands or other contexts. The application handles this internally, but if you're testing the connection manually, make sure to properly escape or quote the token ID.
For example, when using curl:
```bash
# INCORRECT - will cause issues with the ! character
curl -k -v -H "Authorization: PVEAPIToken=root@pam!pulse=your-token-secret" https://your-proxmox-host:8006/api2/json/version
# CORRECT - use single quotes around the token ID
curl -k -v -H "Authorization: PVEAPIToken='root@pam!pulse'=your-token-secret" https://your-proxmox-host:8006/api2/json/version
```
## Testing the Connection
The application includes a script to test the connection to your ProxMox nodes. This is useful for troubleshooting connection issues.
```bash
npm run test:connection
```
This script will:
1. Validate your configuration
2. Test the connection to each configured ProxMox node
3. Check for potential issues with your API tokens
4. Provide helpful error messages and suggestions if the connection fails
If you encounter connection issues, the script will provide detailed information about what went wrong and how to fix it.
## Development
To run the application in development mode:
```bash
npm run dev
```
This will start the application with hot reloading enabled.
### Development Tools
When `ENABLE_DEV_TOOLS` is set to `true`, the application provides additional endpoints for debugging:
- `/dev/api-reference`: API reference documentation
- `/dev/config`: Current configuration (with sensitive information redacted)
- `/dev/state`: Current application state
- `/dev/refresh/:nodeId`: Manually refresh data for a node
If your ProxMox API token ID contains special characters (like `!`, `?`, `&`, etc.), make sure your `.env` file has the correct format. The application handles this internally, but it's important for the configuration to be correct.
## Troubleshooting
### Connection Issues
If you're having trouble connecting to your ProxMox nodes, check the following:
1. **Network Connectivity**: Make sure you can reach the ProxMox host from the machine running the application.
```bash
ping your-proxmox-host
```
2. **API Access**: Verify that the ProxMox API is accessible on the specified port.
```bash
curl -k https://your-proxmox-host:8006/api2/json/version
```
3. **API Token**: Ensure that your API token has the correct permissions and is properly formatted.
- If your token ID contains special characters, make sure to properly escape or quote it.
- Run the connection test script to check for issues with your token:
```bash
npm run test:connection
```
4. **SSL Certificates**: If you're using self-signed certificates, make sure `IGNORE_SSL_ERRORS` is set to `true`.
### Common Errors
- **Timeout**: The connection to the ProxMox API is timing out. Check your network configuration and firewall settings.
- **401 Unauthorized**: The API token is invalid or doesn't have the necessary permissions.
- **SSL Certificate Error**: The SSL certificate is not trusted. Set `IGNORE_SSL_ERRORS` to `true` or install a valid certificate.
1. **Network Connectivity**: Make sure you can reach the ProxMox host from your machine
2. **API Access**: Verify that the ProxMox API is accessible on the specified port
3. **API Token**: Ensure that your API token has the correct permissions and is properly formatted
4. **SSL Certificates**: If you're using self-signed certificates, make sure `IGNORE_SSL_ERRORS` is set to `true`
## License
-114
View File
@@ -1,114 +0,0 @@
> pulse_basic@1.0.0 test:connection
> ts-node src/scripts/test-connection.ts
2025-02-27 14:52:10 info [TestConnection]: Running comprehensive connection tests...
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 info [StartupCheck]: Running startup checks...
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 info [StartupCheck]: Validating configuration...
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 info [ConfigValidator]: Validating application configuration...
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 info [ConfigValidator]: Validating node configuration: Proxmox Node 1 (node-1)
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 warn [ConfigValidator]: Node node-1: Token ID contains special characters that may cause issues with shell commands: !
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 warn [ConfigValidator]: When using in curl or similar commands, make sure to properly escape or quote the token ID.
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 info [ConfigValidator]: Node node-1: Successfully connected to ProxMox API version 8.3.4
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 info [ConfigValidator]: Validating node configuration: Proxmox Node 2 (node-2)
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 warn [ConfigValidator]: Node node-2: Token ID contains special characters that may cause issues with shell commands: !
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 warn [ConfigValidator]: When using in curl or similar commands, make sure to properly escape or quote the token ID.
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 info [ConfigValidator]: Node node-2: Successfully connected to ProxMox API version 8.3.4
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 info [StartupCheck]: ✅ Configuration validation passed.
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 info [StartupCheck]: Testing connections to all ProxMox nodes...
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 warn [ProxmoxClient][node-1]: SSL certificate validation is disabled. This is not recommended for production.
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 warn [ProxmoxClient][node-2]: SSL certificate validation is disabled. This is not recommended for production.
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 debug [ProxmoxClient][node-1]: API Request: GET /version
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:10 debug [ProxmoxClient][node-2]: API Request: GET /version
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:11 debug [ProxmoxClient][node-2]: API Response: 200 GET /version
{
"service": "proxmox-monitor",
"data": {
"data": {
"release": "8.3",
"repoid": "65224a0f9cd294a3",
"version": "8.3.4"
}
}
}
2025-02-27 14:52:11 info [ProxmoxClient][node-2]: Connected to ProxMox API version 8.3.4
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:11 debug [ProxmoxClient][node-1]: API Response: 200 GET /version
{
"service": "proxmox-monitor",
"data": {
"data": {
"version": "8.3.4",
"repoid": "65224a0f9cd294a3",
"release": "8.3"
}
}
}
2025-02-27 14:52:11 info [ProxmoxClient][node-1]: Connected to ProxMox API version 8.3.4
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:11 info [StartupCheck]: ✅ Successfully connected to all 2 ProxMox nodes.
{
"service": "proxmox-monitor"
}
2025-02-27 14:52:11 info [TestConnection]: ✅ All checks passed successfully!
{
"service": "proxmox-monitor"
}
+338 -25
View File
@@ -13,8 +13,6 @@
"@emotion/styled": "^11.14.0",
"@mui/icons-material": "^6.4.6",
"@mui/material": "^6.4.6",
"@types/react": "^19.0.10",
"@types/react-dom": "^19.0.4",
"@vitejs/plugin-react": "^4.3.4",
"axios": "^1.8.1",
"react": "^19.0.0",
@@ -22,8 +20,11 @@
"react-router-dom": "^7.2.0",
"recharts": "^2.15.1",
"socket.io-client": "^4.8.1",
"typescript": "^5.7.3",
"vite": "^6.2.0"
},
"devDependencies": {
"concurrently": "^9.1.2",
"kill-port": "^2.0.1"
}
},
"node_modules/@ampproject/remapping": {
@@ -1531,19 +1532,11 @@
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz",
"integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==",
"license": "MIT",
"peer": true,
"dependencies": {
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-dom": {
"version": "19.0.4",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz",
"integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==",
"license": "MIT",
"peerDependencies": {
"@types/react": "^19.0.0"
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.12",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
@@ -1572,6 +1565,32 @@
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"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/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -1678,6 +1697,51 @@
],
"license": "CC-BY-4.0"
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?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",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@@ -1687,6 +1751,26 @@
"node": ">=6"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"license": "MIT"
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -1699,6 +1783,32 @@
"node": ">= 0.8"
}
},
"node_modules/concurrently": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.2.tgz",
"integrity": "sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^4.1.2",
"lodash": "^4.17.21",
"rxjs": "^7.8.1",
"shell-quote": "^1.8.1",
"supports-color": "^8.1.1",
"tree-kill": "^1.2.2",
"yargs": "^17.7.2"
},
"bin": {
"conc": "dist/bin/concurrently.js",
"concurrently": "dist/bin/concurrently.js"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
}
},
"node_modules/convert-source-map": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
@@ -1928,6 +2038,13 @@
"integrity": "sha512-dJr1o6yCntRkXElnhsHh1bAV19bo/hKyFf7tCcWgpXbuFIF0Lakjgqv5LRfSDaNzAII8Fnxg2tqgHkgCvxdbxw==",
"license": "ISC"
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/engine.io-client": {
"version": "6.6.3",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.3.tgz",
@@ -2170,6 +2287,16 @@
"node": ">=6.9.0"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -2207,6 +2334,13 @@
"node": ">= 0.4"
}
},
"node_modules/get-them-args": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/get-them-args/-/get-them-args-1.3.2.tgz",
"integrity": "sha512-LRn8Jlk+DwZE4GTlDbT3Hikd1wSHgLMme/+7ddlqKd7ldwR6LjJgTVWzBnR01wnYGe4KgrXjg287RaI22UHmAw==",
"dev": true,
"license": "MIT"
},
"node_modules/globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@@ -2228,6 +2362,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -2328,6 +2472,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -2364,6 +2518,20 @@
"node": ">=6"
}
},
"node_modules/kill-port": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/kill-port/-/kill-port-2.0.1.tgz",
"integrity": "sha512-e0SVOV5jFo0mx8r7bS29maVWp17qGqLBZ5ricNSajON6//kmb7qqqNnml4twNE8Dtj97UQD+gNFOaipS/q1zzQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"get-them-args": "1.3.2",
"shell-exec": "1.0.2"
},
"bin": {
"kill-port": "cli.js"
}
},
"node_modules/lines-and-columns": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
@@ -2719,6 +2887,16 @@
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
"license": "MIT"
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@@ -2786,6 +2964,16 @@
"fsevents": "~2.3.2"
}
},
"node_modules/rxjs": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/scheduler": {
"version": "0.25.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz",
@@ -2807,6 +2995,26 @@
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
"license": "MIT"
},
"node_modules/shell-exec": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/shell-exec/-/shell-exec-1.0.2.tgz",
"integrity": "sha512-jyVd+kU2X+mWKMmGhx4fpWbPsjvD53k9ivqetutVW/BQ+WIZoDoP4d8vUMGezV6saZsiNoW2f9GIhg9Dondohg==",
"dev": true,
"license": "MIT"
},
"node_modules/shell-quote": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz",
"integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/socket.io-client": {
"version": "4.8.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.1.tgz",
@@ -2887,12 +3095,56 @@
"node": ">=0.10.0"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/stylis": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
"license": "MIT"
},
"node_modules/supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
@@ -2911,25 +3163,29 @@
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
"license": "MIT"
},
"node_modules/tree-kill": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
"integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
"dev": true,
"license": "MIT",
"bin": {
"tree-kill": "cli.js"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
},
"node_modules/turbo-stream": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",
"integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==",
"license": "ISC"
},
"node_modules/typescript": {
"version": "5.7.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/update-browserslist-db": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
@@ -3053,6 +3309,24 @@
}
}
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/ws": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
@@ -3082,6 +3356,16 @@
"node": ">=0.4.0"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
@@ -3101,6 +3385,35 @@
"engines": {
"node": ">= 14"
}
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dev": true,
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=12"
}
}
}
}
+4
View File
@@ -25,5 +25,9 @@
"recharts": "^2.15.1",
"socket.io-client": "^4.8.1",
"vite": "^6.2.0"
},
"devDependencies": {
"concurrently": "^9.1.2",
"kill-port": "^2.0.1"
}
}
+167 -20
View File
@@ -3,21 +3,117 @@ import { ThemeProvider, createTheme } from '@mui/material/styles';
import CssBaseline from '@mui/material/CssBaseline';
import { Container, Box, Typography, AppBar, Toolbar, Paper } from '@mui/material';
import NetworkDisplay from './components/NetworkDisplay';
// Import connection test utility
import './utils/testConnection';
// Create a theme instance
// Create a theme instance with improved colors and styling
const theme = createTheme({
palette: {
mode: 'light',
primary: {
main: '#1976d2',
main: '#3a7bd5',
light: '#5e9cf5',
dark: '#2c5ea3',
contrastText: '#ffffff',
},
secondary: {
main: '#dc004e',
main: '#00b8d4',
light: '#62ebff',
dark: '#0088a3',
contrastText: '#ffffff',
},
background: {
default: '#f5f5f5',
default: '#f8f9fa',
paper: '#ffffff',
},
success: {
main: '#4caf50',
light: '#80e27e',
dark: '#087f23',
},
warning: {
main: '#ff9800',
light: '#ffc947',
dark: '#c66900',
},
error: {
main: '#f44336',
light: '#ff7961',
dark: '#ba000d',
},
info: {
main: '#2196f3',
light: '#64b5f6',
dark: '#0069c0',
},
grey: {
50: '#fafafa',
100: '#f5f5f5',
200: '#eeeeee',
300: '#e0e0e0',
400: '#bdbdbd',
500: '#9e9e9e',
600: '#757575',
700: '#616161',
800: '#424242',
900: '#212121',
},
},
typography: {
fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif',
h6: {
fontWeight: 500,
letterSpacing: '0.0075em',
},
body1: {
fontSize: '0.9rem',
},
body2: {
fontSize: '0.8rem',
},
},
shape: {
borderRadius: 8,
},
components: {
MuiAppBar: {
styleOverrides: {
root: {
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.05)',
},
},
},
MuiPaper: {
styleOverrides: {
root: {
boxShadow: '0 2px 10px rgba(0, 0, 0, 0.05)',
},
},
},
MuiTableCell: {
styleOverrides: {
root: {
padding: '12px 16px',
},
head: {
fontWeight: 600,
color: '#424242',
},
},
},
MuiTableRow: {
styleOverrides: {
root: {
'&:last-child td, &:last-child th': {
border: 0,
},
},
},
},
MuiChip: {
styleOverrides: {
root: {
fontWeight: 500,
},
},
},
},
});
@@ -26,30 +122,81 @@ function App() {
return (
<ThemeProvider theme={theme}>
<CssBaseline />
<Box sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
<AppBar position="static">
<Toolbar>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
<Box sx={{
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
background: 'linear-gradient(to bottom, #f8f9fa, #e8eaf6)',
}}>
<AppBar position="static" color="primary" elevation={0}>
<Toolbar sx={{ px: { xs: 2, sm: 3 } }}>
<Typography
variant="h6"
component="div"
sx={{
flexGrow: 1,
display: 'flex',
alignItems: 'center',
fontWeight: 600,
letterSpacing: '0.02em',
}}
>
<Box
component="span"
sx={{
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
mr: 1.5,
width: 32,
height: 32,
borderRadius: '50%',
bgcolor: 'rgba(255, 255, 255, 0.2)',
}}
>
<Box
component="span"
sx={{
width: 20,
height: 20,
borderRadius: '50%',
bgcolor: 'white',
boxShadow: '0 0 0 2px rgba(255, 255, 255, 0.5)',
}}
/>
</Box>
System Monitor
</Typography>
</Toolbar>
</AppBar>
<Container maxWidth="lg" sx={{ mt: 4, mb: 4, flexGrow: 1 }}>
<Box sx={{ my: 4 }}>
<Typography variant="h4" component="h1" gutterBottom>
Live System Metrics
</Typography>
<Typography variant="subtitle1" color="text.secondary" paragraph>
Real-time monitoring of system resources and network activity
</Typography>
<Container
maxWidth="lg"
sx={{
mt: { xs: 2, sm: 4 },
mb: { xs: 2, sm: 4 },
flexGrow: 1,
px: { xs: 2, sm: 3 },
}}
>
<Box sx={{ my: { xs: 2, sm: 4 } }}>
{/* Network Display Component */}
<NetworkDisplay />
</Box>
</Container>
<Paper sx={{ padding: 2, marginTop: 'auto' }} component="footer">
<Paper
sx={{
padding: 2,
marginTop: 'auto',
borderRadius: 0,
bgcolor: 'background.paper',
borderTop: '1px solid',
borderColor: 'grey.200',
}}
component="footer"
elevation={0}
>
<Typography variant="body2" color="text.secondary" align="center">
System Monitor © {new Date().getFullYear()}
</Typography>
File diff suppressed because it is too large Load Diff
-37
View File
@@ -1,37 +0,0 @@
import { io } from 'socket.io-client';
// This function can be called from the browser console to help debug WebSocket connections
const testWebSocketConnection = (url = 'http://localhost:3000') => {
console.log(`Testing connection to: ${url}`);
const socket = io(url, {
transports: ['websocket', 'polling'],
reconnectionAttempts: 5,
reconnectionDelay: 500,
timeout: 3000
});
socket.on('connect', () => {
console.log('✅ CONNECTED SUCCESSFULLY!');
console.log('Socket ID:', socket.id);
});
socket.on('connect_error', (err) => {
console.error('❌ CONNECTION ERROR:', err);
console.error('Message:', err.message);
});
socket.on('message', (msg) => {
console.log('📩 RECEIVED MESSAGE:', msg);
});
socket.on('disconnect', () => {
console.log('❌ DISCONNECTED');
});
return socket;
};
window.testConnection = testWebSocketConnection;
export default testWebSocketConnection;
+4 -3
View File
@@ -5,19 +5,20 @@ import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
host: '192.168.0.130',
port: 5173,
proxy: {
// Proxy WebSocket connections to the backend
// Proxy WebSocket connections to the real backend
'/socket.io': {
target: 'http://localhost:3000',
ws: true,
},
// Proxy API requests to the backend
// Proxy API requests to the real backend
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
}
},
host: true
},
build: {
outDir: 'dist',
+323
View File
@@ -24,6 +24,7 @@
},
"devDependencies": {
"@types/cors": "^2.8.17",
"concurrently": "^9.1.2",
"nodemon": "^3.1.9",
"ts-node-dev": "^2.0.0"
}
@@ -287,6 +288,52 @@
"node": ">=0.4.0"
}
},
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"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/ansi-styles/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
"engines": {
"node": ">=7.0.0"
}
},
"node_modules/ansi-styles/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true,
"license": "MIT"
},
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -458,6 +505,46 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/chalk/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/chalk/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/chokidar": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
@@ -483,6 +570,21 @@
"fsevents": "~2.3.2"
}
},
"node_modules/cliui": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
"dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.1",
"wrap-ansi": "^7.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/color": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz",
@@ -547,6 +649,58 @@
"dev": true,
"license": "MIT"
},
"node_modules/concurrently": {
"version": "9.1.2",
"resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.1.2.tgz",
"integrity": "sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"chalk": "^4.1.2",
"lodash": "^4.17.21",
"rxjs": "^7.8.1",
"shell-quote": "^1.8.1",
"supports-color": "^8.1.1",
"tree-kill": "^1.2.2",
"yargs": "^17.7.2"
},
"bin": {
"conc": "dist/bin/concurrently.js",
"concurrently": "dist/bin/concurrently.js"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
}
},
"node_modules/concurrently/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/concurrently/node_modules/supports-color": {
"version": "8.1.1",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@@ -690,6 +844,13 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
"license": "MIT"
},
"node_modules/emoji-regex": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
"license": "MIT"
},
"node_modules/enabled": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz",
@@ -811,6 +972,16 @@
"node": ">= 0.4"
}
},
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/escape-html": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
@@ -999,6 +1170,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
@@ -1239,6 +1420,16 @@
"node": ">=0.10.0"
}
},
"node_modules/is-fullwidth-code-point": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -1280,6 +1471,13 @@
"integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==",
"license": "MIT"
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true,
"license": "MIT"
},
"node_modules/logform": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz",
@@ -1702,6 +1900,16 @@
"node": ">=8.10.0"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/resolve": {
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
@@ -1737,6 +1945,16 @@
"rimraf": "bin.js"
}
},
"node_modules/rxjs": {
"version": "7.8.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
"integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"tslib": "^2.1.0"
}
},
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -1845,6 +2063,19 @@
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"license": "ISC"
},
"node_modules/shell-quote": {
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz",
"integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/side-channel": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
@@ -2097,6 +2328,34 @@
"safe-buffer": "~5.2.0"
}
},
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
"engines": {
"node": ">=8"
}
},
"node_modules/strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
@@ -2297,6 +2556,13 @@
"strip-json-comments": "^2.0.0"
}
},
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"dev": true,
"license": "0BSD"
},
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -2427,6 +2693,24 @@
"node": ">= 12.0.0"
}
},
"node_modules/wrap-ansi": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
"strip-ansi": "^6.0.0"
},
"engines": {
"node": ">=10"
},
"funding": {
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
@@ -2465,6 +2749,45 @@
"node": ">=0.4"
}
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=10"
}
},
"node_modules/yargs": {
"version": "17.7.2",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
"dev": true,
"license": "MIT",
"dependencies": {
"cliui": "^8.0.1",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.3",
"y18n": "^5.0.5",
"yargs-parser": "^21.1.1"
},
"engines": {
"node": ">=12"
}
},
"node_modules/yargs-parser": {
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+7 -1
View File
@@ -9,7 +9,12 @@
"lint": "eslint . --ext .ts",
"test": "echo \"Error: no test specified\" && exit 1",
"test:startup": "ts-node src/scripts/startup-check.ts",
"test:api": "ts-node src/scripts/proxmox-api-test.ts"
"test:api": "ts-node src/scripts/proxmox-api-test.ts",
"kill:backend": "npx kill-port 3000",
"kill:frontend": "npx kill-port 5173 5174 5175 5176 5177 5178 5179 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 5199 5200",
"kill:all": "npm run kill:backend && npm run kill:frontend",
"dev:frontend": "cd frontend && npm run dev",
"pulse": "./start-pulse.sh"
},
"keywords": [],
"author": "",
@@ -31,6 +36,7 @@
},
"devDependencies": {
"@types/cors": "^2.8.17",
"concurrently": "^9.1.2",
"nodemon": "^3.1.9",
"ts-node-dev": "^2.0.0"
}
-285
View File
@@ -1,285 +0,0 @@
# ProxMox Monitoring App Implementation Plan
## Project Overview
A lightweight, responsive ProxMox monitoring application that displays real-time metrics for CPU, memory, network, and disk usage across multiple nodes. The application will use the ProxMox API and event subscription for real-time updates.
## Environment Configuration
The application will use environment variables for configuration with the following structure:
```
# Node 1
PROXMOX_NODE_1_NAME=Proxmox Node 1
PROXMOX_NODE_1_HOST=https://192.168.0.132:8006
PROXMOX_NODE_1_TOKEN_ID=root@pam!pulse
PROXMOX_NODE_1_TOKEN_SECRET=e1850350-6afc-4b8e-ae28-472152af84f9
# Node 2
PROXMOX_NODE_2_NAME=Proxmox Node 2
PROXMOX_NODE_2_HOST=https://192.168.0.141:8006
PROXMOX_NODE_2_TOKEN_ID=root@pam!pulse
PROXMOX_NODE_2_TOKEN_SECRET=63e21f63-4cfd-4ba0-9b14-fc681c59d932
# App Configuration
PORT=3000
NODE_ENV=development
LOG_LEVEL=debug
ENABLE_DEV_TOOLS=true
METRICS_HISTORY_MINUTES=60
```
## Technology Stack
- **Backend**: Node.js with Express, TypeScript, Socket.io for real-time updates
- **Frontend**: Simple HTML/CSS/TypeScript with grid layout
- **Deployment**: Docker container
## Project Structure
```
proxmox-monitor/
├── src/
│ ├── config/
│ ├── types/
│ ├── api/
│ ├── services/
│ ├── utils/
│ ├── routes/
│ ├── websocket/
│ ├── public/
│ └── server.ts
├── .env
├── .env.example
├── .gitignore
├── tsconfig.json
├── package.json
├── Dockerfile
└── README.md
```
## Core Components
### 1. Configuration Module
- **Purpose**: Load and validate environment variables
- **Features**:
- Parse environment variables for multiple ProxMox nodes
- Support dynamic number of nodes (PROXMOX_NODE_1, PROXMOX_NODE_2, etc.)
- Validate configuration during startup
- Keep credentials secure
### 2. ProxMox API Client
- **Purpose**: Interface with ProxMox API
- **Features**:
- Authentication with API tokens
- Fetch node status information
- Get virtual machines and containers
- Get detailed metrics
- Handle API errors with proper logging
- Support HTTPS with option to ignore certificate validation for lab environments
### 3. Event Subscription Service
- **Purpose**: Receive real-time updates from ProxMox
- **Features**:
- Connect to ProxMox event stream
- Process event data
- Emit events to internal event system
- Handle connection errors and auto-reconnect
- Correlate events with specific nodes
### 4. Node Management Service
- **Purpose**: Manage multiple ProxMox nodes
- **Features**:
- Create and maintain API clients for each node
- Manage event subscriptions
- Aggregate data from multiple nodes
- Track node connectivity status
- Support dynamic node addition/removal
### 5. Metrics Collection Service
- **Purpose**: Collect and process metrics
- **Features**:
- Combine event-based and polling data
- Normalize metrics across different sources
- Calculate derived metrics (percentages, rates)
- Store short-term history (configurable, default 60 minutes)
- Support for different metric types (CPU, memory, network, disk)
### 6. WebSocket Server
- **Purpose**: Real-time communication with frontend
- **Features**:
- Push metrics updates to clients
- Handle client connections/disconnections
- Support filtering data sent to clients
- Optimize data transfer (send only changed data)
### 7. REST API
- **Purpose**: Provide data to frontend
- **Endpoints**:
- `/api/nodes` - List all nodes
- `/api/nodes/:nodeId` - Get node details
- `/api/nodes/:nodeId/vms` - Get all VMs for a node
- `/api/nodes/:nodeId/containers` - Get all containers for a node
- `/api/status` - Get system status
- `/api/metrics` - Get current metrics
- `/api/metrics/history` - Get historical metrics
### 8. Development Tools
- **Purpose**: Assist in development with AI
- **Features**:
- Enhanced logging with context
- API call visualization
- State inspection
- Error handling with suggestions
- Development dashboard
- API response recording/playback
### 9. Frontend Components
- **Purpose**: Display monitoring data
- **Features**:
- Grid layout for VMs/containers
- Real-time metric updates
- Node selector
- Responsive design
- Minimal but functional UI
## Implementation Phases
### Phase 1: Project Setup
- Initialize TypeScript project
- Configure environment
- Set up basic project structure
- Install core dependencies
### Phase 2: Core API Implementation
- Implement ProxMox API client
- Create type definitions for API responses
- Build node configuration loader
- Implement enhanced logging system
### Phase 3: Event System
- Create event subscription service
- Implement event processing
- Set up WebSocket server
- Build real-time data pipeline
### Phase 4: Metrics Collection
- Implement metrics normalization
- Create polling fallback for missing metrics
- Build short-term history storage
- Implement derived metrics calculation
### Phase 5: Node Management
- Create multi-node support
- Implement node discovery (if in cluster)
- Build node status tracking
- Create aggregated metrics view
### Phase 6: Frontend Development
- Create simple grid layout
- Implement WebSocket client
- Build real-time updates
- Create node selector
### Phase 7: Development Tools
- Implement enhanced logging
- Create development dashboard
- Add API explorer
- Build state visualization
### Phase 8: Containerization
- Create Docker configuration
- Set up environment variable passing
- Test container deployment
## API Type Definitions
The application should define TypeScript interfaces for:
1. **ProxmoxNodeStatus** - Node status information
2. **ProxmoxVM** - Virtual machine information
3. **ProxmoxContainer** - Container information
4. **ProxmoxGuest** - Union type of VM and Container
5. **ProxmoxEvent** - Event data structure
6. **MetricsData** - Normalized metrics format
7. **NodeConfig** - Node configuration structure
## Security Considerations
1. Keep API tokens in environment variables
2. Don't commit .env files with credentials
3. Use HTTPS for API communication
4. Consider read-only API tokens with minimal permissions
5. Validate all user inputs
## Development Context Helpers
1. **Configuration Validator and API Reference**:
- **Purpose**: Provide a reliable reference for ProxMox API interactions
- **Features**:
- Validate environment configurations during startup
- Test connectivity to all configured nodes
- Sample and store actual API response structures for reference
- Document endpoint patterns with examples
- Create schema validators for request/response bodies
- Generate TypeScript interfaces from actual API responses
- Expose an endpoint that shows API structure for reference
- Store successful API call patterns for the AI to reference
- Cache sample responses for offline development
- Provide helper functions to validate API calls before sending
- Create a "cheat sheet" of common API patterns
- **Implementation**:
- Create a startup validation process that tests all endpoints
- Build an in-memory reference database of API interactions
- Expose `/dev/api-reference` endpoint with interactive documentation
- Log all successful API interactions for pattern recognition
- Create a validation library the AI can use to check its work
2. **Enhanced Logging**:
- Include node ID, component, operation in logs
- Different log levels (debug, info, warn, error)
- Contextual error information
- Log reference to successful patterns when errors occur
3. **State Inspection**:
- Endpoint to view current system state
- Connection status visualization
- Event subscription status
- Configuration status and validation results
4. **API Explorer**:
- Test API calls directly
- View recent API calls and responses
- Response visualization
- Template library of working API calls
- Side-by-side comparison of expected vs. actual responses
5. **Performance Monitoring**:
- Track memory and CPU usage
- Monitor event processing time
- Connection statistics
## Docker Configuration
- Use Node.js Alpine image as base
- Multi-stage build for smaller image
- Expose configuration via environment variables
- Volume mounting for persistent data (if needed)
- Health check endpoint
-19
View File
@@ -1,19 +0,0 @@
#!/bin/bash
# Kill any existing test servers
pkill -f "node test-server.js" || true
# Start the test server
echo "Starting test server..."
node test-server.js &
TEST_SERVER_PID=$!
# Wait a moment for the server to start
sleep 2
# Start the frontend
echo "Starting frontend..."
cd frontend && npm run dev
# When the frontend exits, also kill the test server
kill $TEST_SERVER_PID
Executable
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
# Make script executable if it isn't already
chmod +x "$0"
# Kill any existing servers
echo "Killing any existing servers..."
pkill -f "node dist/server.js" || true
npx kill-port 5173 3000
# Start the real backend server
echo "Starting real backend server..."
NODE_ENV=development LOG_LEVEL=info ENABLE_DEV_TOOLS=true npm run dev &
BACKEND_PID=$!
# Wait a moment for the server to start
sleep 3
# Get the host IP (use 0.0.0.0 in Docker, otherwise use 192.168.0.130 or the local IP)
HOST_IP="0.0.0.0"
if [[ -z "${DOCKER_CONTAINER}" ]]; then
# Not in Docker, use the specific IP
HOST_IP="192.168.0.130"
fi
# Start the frontend Vite dev server
echo "Starting Pulse interface..."
cd frontend && npm run dev -- --host "${HOST_IP}"
# When the frontend exits, also kill the backend server
kill $BACKEND_PID
-110
View File
@@ -1,110 +0,0 @@
> pulse_basic@1.0.0 test:startup
> ts-node src/scripts/startup-check.ts
2025-02-27 14:51:58 info [StartupCheck]: Running startup checks...
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 info [StartupCheck]: Validating configuration...
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 info [ConfigValidator]: Validating application configuration...
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 info [ConfigValidator]: Validating node configuration: Proxmox Node 1 (node-1)
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 warn [ConfigValidator]: Node node-1: Token ID contains special characters that may cause issues with shell commands: !
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 warn [ConfigValidator]: When using in curl or similar commands, make sure to properly escape or quote the token ID.
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 info [ConfigValidator]: Node node-1: Successfully connected to ProxMox API version 8.3.4
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 info [ConfigValidator]: Validating node configuration: Proxmox Node 2 (node-2)
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 warn [ConfigValidator]: Node node-2: Token ID contains special characters that may cause issues with shell commands: !
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 warn [ConfigValidator]: When using in curl or similar commands, make sure to properly escape or quote the token ID.
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 info [ConfigValidator]: Node node-2: Successfully connected to ProxMox API version 8.3.4
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 info [StartupCheck]: ✅ Configuration validation passed.
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 info [StartupCheck]: Testing connections to all ProxMox nodes...
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 warn [ProxmoxClient][node-1]: SSL certificate validation is disabled. This is not recommended for production.
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 warn [ProxmoxClient][node-2]: SSL certificate validation is disabled. This is not recommended for production.
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 debug [ProxmoxClient][node-1]: API Request: GET /version
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 debug [ProxmoxClient][node-2]: API Request: GET /version
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 debug [ProxmoxClient][node-2]: API Response: 200 GET /version
{
"service": "proxmox-monitor",
"data": {
"data": {
"version": "8.3.4",
"release": "8.3",
"repoid": "65224a0f9cd294a3"
}
}
}
2025-02-27 14:51:58 info [ProxmoxClient][node-2]: Connected to ProxMox API version 8.3.4
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 debug [ProxmoxClient][node-1]: API Response: 200 GET /version
{
"service": "proxmox-monitor",
"data": {
"data": {
"repoid": "65224a0f9cd294a3",
"release": "8.3",
"version": "8.3.4"
}
}
}
2025-02-27 14:51:58 info [ProxmoxClient][node-1]: Connected to ProxMox API version 8.3.4
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 info [StartupCheck]: ✅ Successfully connected to all 2 ProxMox nodes.
{
"service": "proxmox-monitor"
}
2025-02-27 14:51:58 info [StartupCheck]: ✅ All startup checks passed.
{
"service": "proxmox-monitor"
}
-186
View File
@@ -1,186 +0,0 @@
const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const app = express();
const server = http.createServer(app);
const io = new Server(server, {
cors: {
origin: '*',
methods: ['GET', 'POST']
}
});
const PORT = 3001;
// Create some sample nodes data
const nodes = [
{
id: 'node-1',
name: 'Primary Server',
status: 'online',
ip: '192.168.1.100',
uptime: 86400 * 5 // 5 days in seconds
},
{
id: 'node-2',
name: 'Secondary Server',
status: 'online',
ip: '192.168.1.101',
uptime: 86400 * 3 // 3 days in seconds
}
];
// Create some sample guests data
const guests = [
{
id: 'guest-101',
name: 'Web Server',
status: 'running',
type: 'lxc',
node: 'node-1'
},
{
id: 'guest-102',
name: 'Database',
status: 'running',
type: 'lxc',
node: 'node-1'
},
{
id: 'guest-103',
name: 'File Storage',
status: 'stopped',
type: 'qemu',
node: 'node-2'
}
];
// Serve a simple message
app.get('/', (req, res) => {
res.send('Socket.IO Test Server');
});
// Socket.IO connection handler
io.on('connection', (socket) => {
console.log('Client connected:', socket.id);
// Create consistent metrics for each guest to ensure stable data
// This is more reliable than generating random values each time
const initialMetrics = {
'guest-101': { // Web Server
inRate: 850000, // ~850 KB/s
outRate: 420000 // ~420 KB/s
},
'guest-102': { // Database
inRate: 650000, // ~650 KB/s
outRate: 320000 // ~320 KB/s
}
};
// Send connection confirmation
socket.emit('message', {
type: 'CONNECTED',
payload: {
timestamp: Date.now(),
message: 'Connected to Test Socket.IO Server'
}
});
// Send initial node data
socket.emit('message', {
type: 'NODE_STATUS_UPDATE',
payload: nodes
});
console.log('Sent initial node data to', socket.id);
// Send initial guest data
socket.emit('message', {
type: 'GUEST_STATUS_UPDATE',
payload: guests
});
console.log('Sent initial guest data to', socket.id);
// Send initial metrics data for running guests
guests.forEach(guest => {
if (guest.status === 'running') {
// Use the consistent initial metrics for this guest
const baseMetrics = initialMetrics[guest.id] || {
inRate: 500000, // Default if not specified
outRate: 250000
};
// Add some randomness (±10%)
const randomFactor = 0.9 + Math.random() * 0.2;
socket.emit('message', {
type: 'METRICS_UPDATE',
payload: {
guestId: guest.id,
nodeId: guest.node,
timestamp: Date.now(),
metrics: {
network: {
inRate: baseMetrics.inRate * randomFactor,
outRate: baseMetrics.outRate * randomFactor
},
uptime: Math.floor(Math.random() * 86400)
}
}
});
console.log(`Sent initial metrics for ${guest.id} (${guest.name})`);
}
});
console.log('Sent initial metrics data for running guests to', socket.id);
// Send test metrics data every 3 seconds
const interval = setInterval(() => {
// Create metrics for each guest
guests.forEach(guest => {
// Only send metrics for running guests
if (guest.status === 'running') {
// Use the consistent initial metrics with some random variation
const baseMetrics = initialMetrics[guest.id] || {
inRate: 500000,
outRate: 250000
};
// Add larger random variation (±30%)
const randomFactor = 0.7 + Math.random() * 0.6;
const metrics = {
type: 'METRICS_UPDATE',
payload: {
guestId: guest.id,
nodeId: guest.node,
timestamp: Date.now(),
metrics: {
network: {
inRate: baseMetrics.inRate * randomFactor,
outRate: baseMetrics.outRate * randomFactor
},
uptime: Math.floor(Math.random() * 86400)
}
}
};
socket.emit('message', metrics);
console.log(`Sent updated metrics for ${guest.id} (${guest.name})`);
}
});
console.log('Sent metrics data to', socket.id);
}, 3000);
// Clean up on disconnect
socket.on('disconnect', () => {
console.log('Client disconnected:', socket.id);
clearInterval(interval);
});
});
// Start the server
server.listen(PORT, () => {
console.log(`Test server running on http://localhost:${PORT}`);
});