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.
This commit is contained in:
82
server/src/ws/terminal.js
Normal file
82
server/src/ws/terminal.js
Normal file
@@ -0,0 +1,82 @@
|
||||
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 {}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user