mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-26 11:49:16 +00:00
feat: await command execution in stack management endpoints and update container stats display based on state
This commit is contained in:
+8
-10
@@ -492,7 +492,7 @@ app.post('/api/stacks/:stackName/deploy', async (req: Request, res: Response) =>
|
||||
app.post('/api/stacks/:stackName/down', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const stackName = req.params.stackName as string;
|
||||
composeService.runCommand(stackName, 'down', terminalWs || undefined);
|
||||
await composeService.runCommand(stackName, 'down', terminalWs || undefined);
|
||||
res.json({ status: 'Command started' });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to start command' });
|
||||
@@ -502,7 +502,7 @@ app.post('/api/stacks/:stackName/down', async (req: Request, res: Response) => {
|
||||
app.post('/api/stacks/:stackName/restart', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const stackName = req.params.stackName as string;
|
||||
composeService.runCommand(stackName, 'restart', terminalWs || undefined);
|
||||
await composeService.runCommand(stackName, 'restart', terminalWs || undefined);
|
||||
res.json({ status: 'Command started' });
|
||||
} catch (error) {
|
||||
console.error('Failed to restart containers:', error);
|
||||
@@ -513,7 +513,7 @@ app.post('/api/stacks/:stackName/restart', async (req: Request, res: Response) =
|
||||
app.post('/api/stacks/:stackName/stop', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const stackName = req.params.stackName as string;
|
||||
composeService.runCommand(stackName, 'stop', terminalWs || undefined);
|
||||
await composeService.runCommand(stackName, 'stop', terminalWs || undefined);
|
||||
res.json({ status: 'Command started' });
|
||||
} catch (error) {
|
||||
console.error('Failed to stop containers:', error);
|
||||
@@ -524,7 +524,7 @@ app.post('/api/stacks/:stackName/stop', async (req: Request, res: Response) => {
|
||||
app.post('/api/stacks/:stackName/start', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const stackName = req.params.stackName as string;
|
||||
composeService.runCommand(stackName, 'start', terminalWs || undefined);
|
||||
await composeService.runCommand(stackName, 'start', terminalWs || undefined);
|
||||
res.json({ status: 'Command started' });
|
||||
} catch (error) {
|
||||
console.error('Failed to start containers:', error);
|
||||
@@ -536,13 +536,11 @@ app.post('/api/stacks/:stackName/start', async (req: Request, res: Response) =>
|
||||
app.post('/api/stacks/:stackName/update', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const stackName = req.params.stackName as string;
|
||||
// Run update asynchronously, don't wait for completion
|
||||
composeService.updateStack(stackName, terminalWs || undefined).catch(error => {
|
||||
console.error('Update stack error:', error);
|
||||
});
|
||||
res.json({ status: 'Update started' });
|
||||
// Await update completion
|
||||
await composeService.updateStack(stackName, terminalWs || undefined);
|
||||
res.json({ status: 'Update completed' });
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: 'Failed to start update' });
|
||||
res.status(500).json({ error: 'Failed to update' });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -16,39 +16,58 @@ export class ComposeService {
|
||||
* CRITICAL: cwd is set to the stack directory so relative paths in compose files
|
||||
* resolve correctly inside the isolated stack folder
|
||||
*/
|
||||
runCommand(stackName: string, action: 'down' | 'start' | 'stop' | 'restart', ws?: WebSocket) {
|
||||
async runCommand(stackName: string, action: 'down' | 'start' | 'stop' | 'restart', ws?: WebSocket): Promise<void> {
|
||||
const stackDir = path.join(this.baseDir, stackName);
|
||||
|
||||
// Run docker compose from within the stack directory
|
||||
// This ensures relative paths (e.g., ./data:/config) resolve correctly
|
||||
const args = ['compose', '-p', stackName, action];
|
||||
|
||||
const child = spawn('docker', args, {
|
||||
cwd: stackDir, // CRITICAL: Set working directory to stack folder
|
||||
env: {
|
||||
...process.env,
|
||||
PATH: process.env.PATH || '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = spawn('docker', args, {
|
||||
cwd: stackDir, // CRITICAL: Set working directory to stack folder
|
||||
env: {
|
||||
...process.env,
|
||||
PATH: process.env.PATH || '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
|
||||
}
|
||||
});
|
||||
|
||||
if (ws) {
|
||||
child.stdout.on('data', (data: Buffer) => {
|
||||
ws.send(data.toString());
|
||||
});
|
||||
|
||||
child.stderr.on('data', (data: Buffer) => {
|
||||
ws.send(data.toString());
|
||||
});
|
||||
|
||||
child.on('close', (code: number | null) => {
|
||||
ws.send(`Command exited with code ${code}\n`);
|
||||
if (code === 0) resolve();
|
||||
else reject(new Error(`Command exited with code ${code}`));
|
||||
});
|
||||
|
||||
child.on('error', (error: Error) => {
|
||||
console.error(`Docker Compose Error for ${stackName}:`, error.message);
|
||||
ws.send(`Error: ${error.message}\n`);
|
||||
reject(error);
|
||||
});
|
||||
} else {
|
||||
// Without WS, just wait for resolution
|
||||
let stderr = '';
|
||||
child.stderr.on('data', (data: Buffer) => { stderr += data.toString(); });
|
||||
|
||||
child.on('close', (code: number | null) => {
|
||||
if (code === 0) resolve();
|
||||
else reject(new Error(`Command failed with code ${code}. Stderr: ${stderr}`));
|
||||
});
|
||||
|
||||
child.on('error', (error: Error) => {
|
||||
console.error(`Docker Compose Error for ${stackName}:`, error.message);
|
||||
reject(error);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (ws) {
|
||||
child.stdout.on('data', (data: Buffer) => {
|
||||
ws.send(data.toString());
|
||||
});
|
||||
|
||||
child.stderr.on('data', (data: Buffer) => {
|
||||
ws.send(data.toString());
|
||||
});
|
||||
|
||||
child.on('close', (code: number | null) => {
|
||||
ws.send(`Command exited with code ${code}\n`);
|
||||
});
|
||||
|
||||
child.on('error', (error: Error) => {
|
||||
console.error(`Docker Compose Error for ${stackName}:`, error.message);
|
||||
ws.send(`Error: ${error.message}\n`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -899,7 +899,7 @@ export default function EditorLayout() {
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
<span className="text-xs text-muted-foreground whitespace-nowrap">
|
||||
CPU: {containerStats[container?.Id]?.cpu || 'N/A'} | RAM: {containerStats[container?.Id]?.ram || 'N/A'} | NET: {containerStats[container?.Id]?.net || '0 B ↓ / 0 B ↑'}
|
||||
CPU: {container.State === 'running' ? (containerStats[container?.Id]?.cpu || 'N/A') : '0.00%'} | RAM: {container.State === 'running' ? (containerStats[container?.Id]?.ram || 'N/A') : '0.00 MB'} | NET: {container.State === 'running' ? (containerStats[container?.Id]?.net || '0 B ↓ / 0 B ↑') : '0 B/s ↓ / 0 B/s ↑'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user