docker.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. /**
  2. * Docker容器管理路由
  3. */
  4. const express = require('express');
  5. const router = express.Router();
  6. const WebSocket = require('ws');
  7. const http = require('http');
  8. const dockerService = require('../services/dockerService');
  9. const logger = require('../logger');
  10. const { requireLogin } = require('../middleware/auth');
  11. // 获取Docker状态
  12. router.get('/status', requireLogin, async (req, res) => {
  13. try {
  14. const containerStatus = await dockerService.getContainersStatus();
  15. res.json(containerStatus);
  16. } catch (error) {
  17. logger.error('获取 Docker 状态时出错:', error);
  18. res.status(500).json({ error: '获取 Docker 状态失败', details: error.message });
  19. }
  20. });
  21. // 获取单个容器状态
  22. router.get('/status/:id', requireLogin, async (req, res) => {
  23. try {
  24. const containerInfo = await dockerService.getContainerStatus(req.params.id);
  25. res.json(containerInfo);
  26. } catch (error) {
  27. logger.error('获取容器状态失败:', error);
  28. res.status(500).json({ error: '获取容器状态失败', details: error.message });
  29. }
  30. });
  31. // 重启容器
  32. router.post('/restart/:id', requireLogin, async (req, res) => {
  33. try {
  34. await dockerService.restartContainer(req.params.id);
  35. res.json({ success: true });
  36. } catch (error) {
  37. logger.error('重启容器失败:', error);
  38. res.status(500).json({ error: '重启容器失败', details: error.message });
  39. }
  40. });
  41. // 停止容器
  42. router.post('/stop/:id', requireLogin, async (req, res) => {
  43. try {
  44. await dockerService.stopContainer(req.params.id);
  45. res.json({ success: true });
  46. } catch (error) {
  47. logger.error('停止容器失败:', error);
  48. res.status(500).json({ error: '停止容器失败', details: error.message });
  49. }
  50. });
  51. // 删除容器
  52. router.post('/delete/:id', requireLogin, async (req, res) => {
  53. try {
  54. await dockerService.deleteContainer(req.params.id);
  55. res.json({ success: true, message: '容器已成功删除' });
  56. } catch (error) {
  57. logger.error('删除容器失败:', error);
  58. res.status(500).json({ error: '删除容器失败', details: error.message });
  59. }
  60. });
  61. // 更新容器
  62. router.post('/update/:id', requireLogin, async (req, res) => {
  63. try {
  64. const { tag } = req.body;
  65. await dockerService.updateContainer(req.params.id, tag);
  66. res.json({ success: true, message: '容器更新成功' });
  67. } catch (error) {
  68. logger.error('更新容器失败:', error);
  69. res.status(500).json({ error: '更新容器失败', details: error.message, stack: error.stack });
  70. }
  71. });
  72. // 获取已停止容器
  73. router.get('/stopped', requireLogin, async (req, res) => {
  74. try {
  75. const stoppedContainers = await dockerService.getStoppedContainers();
  76. res.json(stoppedContainers);
  77. } catch (error) {
  78. logger.error('获取已停止容器列表失败:', error);
  79. res.status(500).json({ error: '获取已停止容器列表失败', details: error.message });
  80. }
  81. });
  82. // 获取容器日志(HTTP轮询)
  83. router.get('/logs-poll/:id', async (req, res) => {
  84. const { id } = req.params;
  85. try {
  86. const logs = await dockerService.getContainerLogs(id);
  87. res.send(logs);
  88. } catch (error) {
  89. logger.error('获取容器日志失败:', error);
  90. res.status(500).send('获取日志失败');
  91. }
  92. });
  93. // 设置WebSocket路由,用于实时日志流
  94. function setupLogWebsocket(server) {
  95. const wss = new WebSocket.Server({ server });
  96. wss.on('connection', async (ws, req) => {
  97. try {
  98. const containerId = req.url.split('/').pop();
  99. const docker = await dockerService.getDockerConnection();
  100. if (!docker) {
  101. ws.send('Error: 无法连接到 Docker 守护进程');
  102. return;
  103. }
  104. const container = docker.getContainer(containerId);
  105. const stream = await container.logs({
  106. follow: true,
  107. stdout: true,
  108. stderr: true,
  109. tail: 100
  110. });
  111. stream.on('data', (chunk) => {
  112. const cleanedChunk = chunk.toString('utf8').replace(/\x1B\[[0-9;]*[JKmsu]/g, '');
  113. // 移除不可打印字符
  114. const printableChunk = cleanedChunk.replace(/[^\x20-\x7E\x0A\x0D]/g, '');
  115. ws.send(printableChunk);
  116. });
  117. ws.on('close', () => {
  118. stream.destroy();
  119. });
  120. stream.on('error', (err) => {
  121. ws.send('Error: ' + err.message);
  122. });
  123. } catch (err) {
  124. ws.send('Error: ' + err.message);
  125. }
  126. });
  127. }
  128. // 直接导出 router 实例,并添加 setupLogWebsocket 作为静态属性
  129. router.setupLogWebsocket = setupLogWebsocket;
  130. module.exports = router;