From d883d85b7b86f8666ba911bd68a53edd04a9c488 Mon Sep 17 00:00:00 2001 From: "courtmanr@gmail.com" Date: Sat, 31 May 2025 10:30:51 +0100 Subject: [PATCH] fix: implement persistent Docker volume configuration to prevent data loss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, configuration created via the web UI was lost when containers were recreated or updated, requiring users to reconfigure each time. This was due to the .env file being stored inside the container filesystem. Changes: - Add persistent volume mount for configuration directory in docker-compose.yml - Update ConfigApi to auto-detect Docker vs development environment - Configure dotenv loading to use persistent config path when available - Update file watcher to monitor correct .env file location - Remove conflicting host .env file mount - Update README with new volume configuration instructions Configuration now persists across container updates, eliminating the need for users to reconfigure after updates. Fixes #94 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- README.md | 12 +++++++++--- docker-compose.yml | 16 +++++++++++++--- server/configApi.js | 12 +++++++++++- server/configLoader.js | 13 ++++++++++++- server/index.js | 22 ++++++++++++++++++++-- 5 files changed, 65 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 08993243f..f001cc469 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ Visit the [Community Scripts page](https://community-scripts.github.io/ProxmoxVE **Steps:** -1. **Create a Directory:** Make a directory on your Docker host where Pulse will store configuration: +1. **Create a Directory:** Make a directory for your Docker configuration files: ```bash mkdir pulse-config cd pulse-config @@ -175,8 +175,14 @@ Visit the [Community Scripts page](https://community-scripts.github.io/ProxmoxVE # Change the left side (e.g., "8081:7655") if 7655 is busy on your host - "7655:7655" volumes: - # Mount local directory for persistent configuration - - ./pulse-data:/opt/pulse-proxmox + # Persistent volume for configuration data + # Configuration persists across container updates + - pulse_config:/usr/src/app/config + + # Define persistent volumes + volumes: + pulse_config: + driver: local ``` 3. **Run:** Start the container: ```bash diff --git a/docker-compose.yml b/docker-compose.yml index adcecced1..1c75a1c43 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,13 +11,23 @@ services: # Map container port 7655 to host port 7655 # You can change the host port (left side) if 7655 is already in use on your host - "7655:7655" - env_file: - # Load environment variables from a .env file in the same directory as this compose file - - .env # Standard location + # env_file: + # NOTE: .env file is now managed by the web UI and stored in persistent volume + # No need to load from host .env file + # - .env + volumes: + # Persist configuration data to avoid losing settings on container recreation + # Mount a persistent volume for configuration files + - pulse_config:/usr/src/app/config # Optional: Define networks if needed, otherwise uses default bridge network # networks: # - pulse_network +# Define persistent volumes for configuration and data +volumes: + pulse_config: + driver: local + # Optional: Define a network # networks: # pulse_network: diff --git a/server/configApi.js b/server/configApi.js index e91268d8d..8b2558839 100644 --- a/server/configApi.js +++ b/server/configApi.js @@ -1,11 +1,21 @@ const fs = require('fs').promises; +const fsSync = require('fs'); const path = require('path'); const { loadConfiguration } = require('./configLoader'); const { initializeApiClients } = require('./apiClients'); class ConfigApi { constructor() { - this.envPath = path.join(__dirname, '../.env'); + // Use persistent config directory if it exists (for Docker), otherwise use project root + const configDir = path.join(__dirname, '../config'); + const projectRootEnv = path.join(__dirname, '../.env'); + + // Check if we're in a Docker environment with persistent config volume + if (fsSync.existsSync(configDir)) { + this.envPath = path.join(configDir, '.env'); + } else { + this.envPath = projectRootEnv; + } } /** diff --git a/server/configLoader.js b/server/configLoader.js index 5ee98297c..29d707e2d 100644 --- a/server/configLoader.js +++ b/server/configLoader.js @@ -98,7 +98,18 @@ function loadPbsConfig(index = null) { function loadConfiguration() { // Only load .env file if not in test environment if (process.env.NODE_ENV !== 'test') { - require('dotenv').config(); + const fs = require('fs'); + const path = require('path'); + + const configDir = path.join(__dirname, '../config'); + const configEnvPath = path.join(configDir, '.env'); + const projectEnvPath = path.join(__dirname, '../.env'); + + if (fs.existsSync(configEnvPath)) { + require('dotenv').config({ path: configEnvPath }); + } else { + require('dotenv').config({ path: projectEnvPath }); + } } let isConfigPlaceholder = false; // Add this flag diff --git a/server/index.js b/server/index.js index 9d81b0120..ba872d129 100644 --- a/server/index.js +++ b/server/index.js @@ -1,4 +1,17 @@ -require('dotenv').config(); // Load environment variables from .env file +// Load environment variables from .env file +// Check for persistent config directory (Docker) or use project root +const fs = require('fs'); +const path = require('path'); + +const configDir = path.join(__dirname, '../config'); +const configEnvPath = path.join(configDir, '.env'); +const projectEnvPath = path.join(__dirname, '../.env'); + +if (fs.existsSync(configEnvPath)) { + require('dotenv').config({ path: configEnvPath }); +} else { + require('dotenv').config({ path: projectEnvPath }); +} // Import the state manager FIRST const stateManager = require('./state'); @@ -998,7 +1011,12 @@ let lastReloadTime = 0; global.lastReloadTime = 0; // Make it globally accessible function setupEnvFileWatcher() { - const envPath = path.join(__dirname, '../.env'); + // Use same logic as ConfigApi to find .env file + const configDir = path.join(__dirname, '../config'); + const configEnvPath = path.join(configDir, '.env'); + const projectEnvPath = path.join(__dirname, '../.env'); + + const envPath = fs.existsSync(configEnvPath) ? configEnvPath : projectEnvPath; // Check if the file exists if (!fs.existsSync(envPath)) {