- 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.
83 lines
2.3 KiB
JavaScript
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 {}
|
|
});
|
|
}
|