Add full cross-platform path support

- Added automatic volume path translation in deployment (deploy.js)
- Updated FileBrowser template to use /opt/ instead of hard-coded E:/
- Migrated self-updater.js to use centralized platformPaths module
- Updated UI placeholders to use platform-neutral paths (/media/)
- All paths now automatically adapt to Windows or Linux at runtime via process.platform detection
This commit is contained in:
Krystie
2026-03-14 06:52:18 +01:00
parent e615f24627
commit df0daaad46
4 changed files with 17 additions and 11 deletions

View File

@@ -672,8 +672,8 @@ const APP_TEMPLATES = {
image: "filebrowser/filebrowser:latest", image: "filebrowser/filebrowser:latest",
ports: ["{{PORT}}:80"], ports: ["{{PORT}}:80"],
volumes: [ volumes: [
"E:/docker-progs/filebrowser/data:/srv", "/opt/filebrowser/data:/srv",
"E:/docker-progs/filebrowser/database:/database" "/opt/filebrowser/database:/database"
], ],
environment: {} environment: {}
}, },

View File

@@ -106,13 +106,20 @@ module.exports = function(ctx, helpers) {
throw new Error(`[DC-203] Port conflict detected: ${conflictDetails}. Please choose a different port.`); throw new Error(`[DC-203] Port conflict detected: ${conflictDetails}. Please choose a different port.`);
} }
// Translate volume paths for cross-platform compatibility
const translatedVolumes = (processedTemplate.docker.volumes || []).map(volume => {
const [hostPath, ...rest] = volume.split(':');
const translatedHost = platformPaths.toDockerMountPath(hostPath);
return rest.length > 0 ? `${translatedHost}:${rest.join(':')}` : translatedHost;
});
const containerConfig = { const containerConfig = {
Image: processedTemplate.docker.image, Image: processedTemplate.docker.image,
name: containerName, name: containerName,
ExposedPorts: {}, ExposedPorts: {},
HostConfig: { HostConfig: {
PortBindings: {}, PortBindings: {},
Binds: processedTemplate.docker.volumes || [], Binds: translatedVolumes,
RestartPolicy: { Name: 'unless-stopped' }, RestartPolicy: { Name: 'unless-stopped' },
LogConfig: DOCKER.LOG_CONFIG LogConfig: DOCKER.LOG_CONFIG
}, },

View File

@@ -16,18 +16,17 @@ const path = require('path');
const crypto = require('crypto'); const crypto = require('crypto');
const { execSync } = require('child_process'); const { execSync } = require('child_process');
const zlib = require('zlib'); const zlib = require('zlib');
const platformPaths = require('./platform-paths');
const isWindows = process.platform === 'win32';
const DEFAULTS = { const DEFAULTS = {
CHECK_INTERVAL: 30 * 60 * 1000, // 30 minutes CHECK_INTERVAL: 30 * 60 * 1000, // 30 minutes
UPDATE_URL: 'https://get.dashcaddy.net/release', UPDATE_URL: 'https://get.dashcaddy.net/release',
MIRROR_URL: 'https://get2.dashcaddy.net/release', MIRROR_URL: 'https://get2.dashcaddy.net/release',
UPDATES_DIR: isWindows ? 'C:/caddy/updates' : '/app/updates', UPDATES_DIR: platformPaths.isWindows ? path.join(platformPaths.caddyBase, 'updates') : '/app/updates',
// API_SOURCE_DIR is the HOST path — written to trigger.json for the host-side updater // API_SOURCE_DIR is the HOST path — written to trigger.json for the host-side updater
API_SOURCE_DIR: isWindows ? 'C:/caddy/sites/dashcaddy-api' : '/etc/dashcaddy/sites/dashcaddy-api', API_SOURCE_DIR: path.join(platformPaths.caddySites, 'dashcaddy-api'),
// FRONTEND_DIR is the container path — dashboard is volume-mounted at /app/dashboard // FRONTEND_DIR is the container path — dashboard is volume-mounted at /app/dashboard
FRONTEND_DIR: isWindows ? 'C:/caddy/sites/status' : '/app/dashboard', FRONTEND_DIR: platformPaths.isWindows ? path.join(platformPaths.caddySites, 'status') : '/app/dashboard',
MAX_BACKUPS: 3, MAX_BACKUPS: 3,
HEALTH_TIMEOUT: 60000, HEALTH_TIMEOUT: 60000,
DOWNLOAD_TIMEOUT: 120000, DOWNLOAD_TIMEOUT: 120000,
@@ -44,7 +43,7 @@ class SelfUpdater extends EventEmitter {
updatesDir: options.updatesDir || DEFAULTS.UPDATES_DIR, updatesDir: options.updatesDir || DEFAULTS.UPDATES_DIR,
// hostUpdatesDir is the HOST path that maps to updatesDir inside the container. // hostUpdatesDir is the HOST path that maps to updatesDir inside the container.
// Used when writing trigger.json so the host-side script can find staging files. // Used when writing trigger.json so the host-side script can find staging files.
hostUpdatesDir: options.hostUpdatesDir || (isWindows ? options.updatesDir || DEFAULTS.UPDATES_DIR : '/opt/dashcaddy/updates'), hostUpdatesDir: options.hostUpdatesDir || (platformPaths.isWindows ? options.updatesDir || DEFAULTS.UPDATES_DIR : '/opt/dashcaddy/updates'),
apiSourceDir: options.apiSourceDir || DEFAULTS.API_SOURCE_DIR, apiSourceDir: options.apiSourceDir || DEFAULTS.API_SOURCE_DIR,
frontendDir: options.frontendDir || DEFAULTS.FRONTEND_DIR, frontendDir: options.frontendDir || DEFAULTS.FRONTEND_DIR,
maxBackups: parseInt(options.maxBackups || DEFAULTS.MAX_BACKUPS, 10), maxBackups: parseInt(options.maxBackups || DEFAULTS.MAX_BACKUPS, 10),

View File

@@ -104,7 +104,7 @@
<!-- Path input with browse button --> <!-- Path input with browse button -->
<div class="flex-row-gap"> <div class="flex-row-gap">
<input type="text" id="deploy-media-path" placeholder="E:/Movies, E:/TVShows" <input type="text" id="deploy-media-path" placeholder="/media/Movies, /media/TVShows"
class="input-flex" style="font-size: 0.95rem;" /> class="input-flex" style="font-size: 0.95rem;" />
<button type="button" id="browse-media-btn" style="padding: 10px 16px; background: var(--accent); color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 500; white-space: nowrap;"> <button type="button" id="browse-media-btn" style="padding: 10px 16px; background: var(--accent); color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 500; white-space: nowrap;">
📂 Browse 📂 Browse
@@ -465,7 +465,7 @@
if (appTemplate.mediaMount) { if (appTemplate.mediaMount) {
mediaPathSection.style.display = 'block'; mediaPathSection.style.display = 'block';
mediaPathInput.value = ''; mediaPathInput.value = '';
mediaPathInput.placeholder = 'E:/Movies, E:/TVShows or click Browse'; mediaPathInput.placeholder = '/media/Movies, /media/TVShows or click Browse';
// Fetch detected mounts from existing media servers // Fetch detected mounts from existing media servers
const detectedMountsContainer = document.getElementById('detected-mounts-container'); const detectedMountsContainer = document.getElementById('detected-mounts-container');