This repository has been archived on 2026-05-11. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
autodeployer-old-version/server/src/ws/terminal.js
Giuseppe Raffa 87d698bc5c feat: add Docker and Gitea services, monitoring, queue, and Telegram notification functionalities
- Implemented Docker operations including image building, container management, and resource stats.
- Added Gitea API client for repository management and webhook handling.
- Introduced monitoring service to collect and store container metrics in InfluxDB.
- Created a queue system using BullMQ for managing deployment jobs with real-time log streaming.
- Developed Telegram notification service for deployment status updates.
- Added Traefik label generation for dynamic reverse proxy configuration.
- Implemented WebSocket endpoints for log streaming and terminal access to containers.
- Created an updater sidecar for self-updating the AutoDeployer container.
2026-04-13 23:23:18 +02:00

83 lines
2.3 KiB
JavaScript

import { execInContainer, resizeExec } from '../services/docker.js';
/**
* Handle WebSocket terminal session
* Path: /ws/terminal/{containerId}
*
* Creates an interactive shell (docker exec) in the container
* and bridges it to the WebSocket connection.
*
* Client messages:
* - { type: 'input', data: '...' } — stdin data
* - { type: 'resize', cols: N, rows: N } — terminal resize
*/
export async function handleTerminal(ws, containerId) {
let execInstance = null;
let stream = null;
try {
ws.send(JSON.stringify({ type: 'info', data: `Connecting to container ${containerId.slice(0, 12)}...` }));
const result = await execInContainer(containerId, ['/bin/sh', '-c', 'if command -v bash > /dev/null; then exec bash; else exec sh; fi']);
execInstance = result.exec;
stream = result.stream;
ws.send(JSON.stringify({ type: 'connected', data: 'Terminal connected' }));
// Docker exec stream → WebSocket
stream.on('data', (chunk) => {
if (ws.readyState === 1) {
ws.send(JSON.stringify({ type: 'output', data: chunk.toString('utf8') }));
}
});
stream.on('end', () => {
if (ws.readyState === 1) {
ws.send(JSON.stringify({ type: 'info', data: 'Terminal session ended' }));
ws.close();
}
});
stream.on('error', (err) => {
if (ws.readyState === 1) {
ws.send(JSON.stringify({ type: 'error', data: `Stream error: ${err.message}` }));
}
});
// WebSocket → Docker exec stream
ws.on('message', (message) => {
try {
const msg = JSON.parse(message.toString());
if (msg.type === 'input' && stream && stream.writable) {
stream.write(msg.data);
} else if (msg.type === 'resize' && execInstance) {
resizeExec(execInstance.id, msg.cols || 80, msg.rows || 24);
}
} catch {
// If raw text, treat as stdin input
if (stream && stream.writable) {
stream.write(message.toString());
}
}
});
} catch (err) {
ws.send(JSON.stringify({ type: 'error', data: `Failed to connect: ${err.message}` }));
ws.close();
return;
}
ws.on('close', () => {
try {
if (stream) stream.end();
} catch {}
});
ws.on('error', () => {
try {
if (stream) stream.end();
} catch {}
});
}