mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 11:49:16 +00:00
refactor: update ComposeService and DockerController for improved path handling and environment variable management; enhance EditorLayout for dynamic button rendering based on stack state
This commit is contained in:
@@ -7,6 +7,7 @@ import DockerController from './services/DockerController';
|
||||
import { FileSystemService } from './services/FileSystemService';
|
||||
import { ComposeService } from './services/ComposeService';
|
||||
import { ConfigService } from './services/ConfigService';
|
||||
// @ts-ignore - composerize lacks proper type definitions
|
||||
import composerize from 'composerize';
|
||||
import si from 'systeminformation';
|
||||
import http from 'http';
|
||||
|
||||
@@ -6,7 +6,7 @@ export class ComposeService {
|
||||
private baseDir: string;
|
||||
|
||||
constructor() {
|
||||
this.baseDir = process.env.COMPOSE_DIR || path.join(process.cwd(), '..', 'mock_data', 'docker', 'compose');
|
||||
this.baseDir = process.env.COMPOSE_DIR || path.join(process.cwd(), '..', 'docker', 'compose');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -25,7 +25,10 @@ export class ComposeService {
|
||||
|
||||
const child = spawn('docker', args, {
|
||||
cwd: stackDir, // CRITICAL: Set working directory to stack folder
|
||||
shell: true
|
||||
env: {
|
||||
...process.env,
|
||||
PATH: process.env.PATH || '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
|
||||
}
|
||||
});
|
||||
|
||||
if (ws) {
|
||||
@@ -65,7 +68,10 @@ export class ComposeService {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const pullProcess = spawn('docker', ['compose', 'pull'], {
|
||||
cwd: stackDir,
|
||||
shell: true
|
||||
env: {
|
||||
...process.env,
|
||||
PATH: process.env.PATH || '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
|
||||
}
|
||||
});
|
||||
|
||||
pullProcess.stdout.on('data', (data: Buffer) => {
|
||||
@@ -97,7 +103,10 @@ export class ComposeService {
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const upProcess = spawn('docker', ['compose', 'up', '-d'], {
|
||||
cwd: stackDir,
|
||||
shell: true
|
||||
env: {
|
||||
...process.env,
|
||||
PATH: process.env.PATH || '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
|
||||
}
|
||||
});
|
||||
|
||||
upProcess.stdout.on('data', (data: Buffer) => {
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
import Docker from 'dockerode';
|
||||
import WebSocket from 'ws';
|
||||
import { Duplex } from 'stream';
|
||||
import { exec } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
import path from 'path';
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
const COMPOSE_DIR = process.env.COMPOSE_DIR || '/app/compose';
|
||||
|
||||
class DockerController {
|
||||
private static instance: DockerController;
|
||||
@@ -30,19 +36,50 @@ class DockerController {
|
||||
}
|
||||
|
||||
public async getContainersByStack(stackName: string) {
|
||||
const containers = await this.docker.listContainers({ all: true });
|
||||
// Normalize the stack name: remove all non-alphanumeric characters and lowercase
|
||||
// Docker Compose strips hyphens and underscores from project names
|
||||
const normalizedStackName = stackName.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
|
||||
|
||||
return containers.filter(container => {
|
||||
if (!container.Labels || !container.Labels['com.docker.compose.project']) {
|
||||
return false;
|
||||
try {
|
||||
const stackDir = path.join(COMPOSE_DIR, stackName);
|
||||
const { stdout } = await execAsync('docker compose ps --format json -a', {
|
||||
cwd: stackDir,
|
||||
env: {
|
||||
...process.env,
|
||||
PATH: process.env.PATH || '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
|
||||
}
|
||||
});
|
||||
|
||||
// Robust JSON parsing - handle both JSON array and newline-separated JSON objects
|
||||
// Docker Compose v2 may return either format depending on version
|
||||
interface ComposeContainer {
|
||||
ID?: string;
|
||||
Name?: string;
|
||||
State?: string;
|
||||
Status?: string;
|
||||
}
|
||||
// Normalize the Docker label for comparison
|
||||
const projectLabel = container.Labels['com.docker.compose.project'].replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
|
||||
return projectLabel === normalizedStackName;
|
||||
});
|
||||
|
||||
let containers: ComposeContainer[];
|
||||
try {
|
||||
// Try parsing as a standard JSON array
|
||||
const parsed = JSON.parse(stdout);
|
||||
containers = Array.isArray(parsed) ? parsed : [parsed];
|
||||
} catch {
|
||||
// Fallback: parse newline-separated JSON objects
|
||||
const lines = stdout.trim().split('\n');
|
||||
containers = lines.map(line => JSON.parse(line) as ComposeContainer);
|
||||
}
|
||||
|
||||
// Map to frontend's expected interface
|
||||
// Note: docker compose ps returns Name (singular), but frontend expects Names (array)
|
||||
// Dockerode returns Names with leading slash, so we add it for compatibility
|
||||
return containers.map((c) => ({
|
||||
Id: c.ID || '',
|
||||
Names: ['/' + (c.Name || '')], // Add leading slash to match Dockerode format
|
||||
State: c.State || 'unknown',
|
||||
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);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public async startContainer(containerId: string) {
|
||||
|
||||
Reference in New Issue
Block a user