diff --git a/backend/src/services/ComposeService.ts b/backend/src/services/ComposeService.ts index 594b0cf5..89f98dcc 100644 --- a/backend/src/services/ComposeService.ts +++ b/backend/src/services/ComposeService.ts @@ -6,7 +6,7 @@ export class ComposeService { private baseDir: string; constructor() { - this.baseDir = process.env.COMPOSE_DIR || path.join(process.cwd(), '..', 'docker', 'compose'); + this.baseDir = process.env.COMPOSE_DIR || '/app/compose'; } /** @@ -45,6 +45,7 @@ export class ComposeService { }); child.on('error', (error: Error) => { + console.error(`Docker Compose Error for ${stackName}:`, error.message); ws.send(`Error: ${error.message}\n`); }); } @@ -93,6 +94,7 @@ export class ComposeService { }); pullProcess.on('error', (error: Error) => { + console.error(`Docker Compose Pull Error for ${stackName}:`, error.message); sendOutput(`Pull error: ${error.message}\n`); reject(error); }); @@ -128,6 +130,7 @@ export class ComposeService { }); upProcess.on('error', (error: Error) => { + console.error(`Docker Compose Up Error for ${stackName}:`, error.message); sendOutput(`Update error: ${error.message}\n`); reject(error); }); diff --git a/backend/src/services/DockerController.ts b/backend/src/services/DockerController.ts index 96517195..df51a99f 100644 --- a/backend/src/services/DockerController.ts +++ b/backend/src/services/DockerController.ts @@ -38,7 +38,7 @@ class DockerController { public async getContainersByStack(stackName: string) { try { const stackDir = path.join(COMPOSE_DIR, stackName); - const { stdout } = await execAsync('docker compose ps --format json -a', { + const { stdout, stderr } = await execAsync('docker compose ps --format json -a', { cwd: stackDir, env: { ...process.env, @@ -65,10 +65,16 @@ class DockerController { // Try parsing as a standard JSON array const parsed = JSON.parse(stdout); containers = Array.isArray(parsed) ? parsed : [parsed]; - } catch { + } catch (parseError) { // Fallback: parse newline-separated JSON objects, filtering out empty lines - const lines = stdout.trim().split('\n').filter(line => line.trim() !== ''); - containers = lines.map(line => JSON.parse(line) as ComposeContainer); + try { + const lines = stdout.trim().split('\n').filter(line => line.trim() !== ''); + containers = lines.map(line => JSON.parse(line) as ComposeContainer); + } catch (innerError) { + // Log parsing failure with stderr for debugging + console.error(`Docker Compose JSON Parse Error for ${stackName}:`, stderr || (parseError as Error).message); + return []; + } } // Map to frontend's expected interface @@ -81,8 +87,9 @@ class DockerController { Status: c.Status || '' })); } catch (error) { - // If command fails (e.g., stack not deployed), return empty array - console.error('Failed to get containers for stack:', stackName, error); + // If command fails (e.g., stack not deployed, invalid YAML, missing env_file) + const execError = error as { stderr?: string; message?: string }; + console.error(`Docker Compose Error for ${stackName}:`, execError.stderr || execError.message); return []; } } diff --git a/docker-compose.yml b/docker-compose.yml index a21a193f..16710e14 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,12 +9,14 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock - # DATA DIRECTORY - # Left Side: Where Sencho saves your admin login (Change this if you want it elsewhere) - # Right Side: Internal container path (DO NOT CHANGE) + # Persistent data for your admin login - ./data:/app/data - # STACKS DIRECTORY - # Left Side: The absolute path to your Docker compose stacks on your server - # Right Side: Internal container path (DO NOT CHANGE) - - /path/to/your/server/stacks:/app/compose + # ⚠️ THE 1:1 PATH RULE (MUST MATCH EXACTLY) + # Left Side (Host) === Right Side (Container) + # Change both sides to where you actually store your compose files! + - /opt/stacks:/opt/stacks + + environment: + # This MUST match the path you set in the volumes above! + - COMPOSE_DIR=/opt/stacks \ No newline at end of file