koishi-plugin-docker-control 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commands/control.d.ts +9 -0
- package/lib/commands/control.js +202 -0
- package/lib/commands/index.d.ts +14 -0
- package/lib/commands/index.js +224 -0
- package/lib/commands/list.d.ts +6 -0
- package/lib/commands/list.js +269 -0
- package/lib/commands/logs.d.ts +10 -0
- package/lib/commands/logs.js +85 -0
- package/lib/config.d.ts +55 -0
- package/lib/config.js +90 -0
- package/lib/constants.d.ts +26 -0
- package/lib/constants.js +37 -0
- package/lib/index.d.ts +125 -0
- package/lib/index.js +312 -0
- package/lib/service/agent.d.ts +28 -0
- package/lib/service/agent.js +64 -0
- package/lib/service/connector.d.ts +67 -0
- package/lib/service/connector.js +267 -0
- package/lib/service/index.d.ts +65 -0
- package/lib/service/index.js +202 -0
- package/lib/service/monitor.d.ts +38 -0
- package/lib/service/monitor.js +139 -0
- package/lib/service/node.d.ts +119 -0
- package/lib/service/node.js +509 -0
- package/lib/service/notifier.d.ts +33 -0
- package/lib/service/notifier.js +189 -0
- package/lib/types.d.ts +100 -0
- package/lib/types.js +5 -0
- package/lib/utils/format.d.ts +39 -0
- package/lib/utils/format.js +142 -0
- package/lib/utils/logger.d.ts +65 -0
- package/lib/utils/logger.js +89 -0
- package/lib/utils/stream.d.ts +28 -0
- package/lib/utils/stream.js +156 -0
- package/package.json +38 -0
- package/readme.md +96 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerControlCommands = registerControlCommands;
|
|
4
|
+
const logger_1 = require("../utils/logger");
|
|
5
|
+
/**
|
|
6
|
+
* 格式化容器搜索结果
|
|
7
|
+
*/
|
|
8
|
+
function formatSearchResults(results, operation) {
|
|
9
|
+
const lines = [];
|
|
10
|
+
let successCount = 0;
|
|
11
|
+
let failCount = 0;
|
|
12
|
+
for (const r of results) {
|
|
13
|
+
const status = r.success ? '✅' : '❌';
|
|
14
|
+
const name = r.container.Names?.[0]?.replace('/', '') || r.container.Id?.slice(0, 8) || '?';
|
|
15
|
+
if (r.success) {
|
|
16
|
+
successCount++;
|
|
17
|
+
lines.push(`${status} ${r.node.name}: ${name}`);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
failCount++;
|
|
21
|
+
lines.push(`${status} ${r.node.name}: ${name} - ${r.error}`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const summary = `共 ${results.length} 个,成功 ${successCount},失败 ${failCount}`;
|
|
25
|
+
return [summary, ...lines].join('\n');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 注册控制指令
|
|
29
|
+
*/
|
|
30
|
+
function registerControlCommands(ctx, getService) {
|
|
31
|
+
/**
|
|
32
|
+
* 启动容器
|
|
33
|
+
*/
|
|
34
|
+
ctx
|
|
35
|
+
.command('docker.start <selector> <container>', '启动容器')
|
|
36
|
+
.alias('docker启动', '容器启动', 'docker开启', '容器开启')
|
|
37
|
+
.option('async', '-a 异步执行,不等待结果', { fallback: false })
|
|
38
|
+
.action(async ({ options }, selector, container) => {
|
|
39
|
+
logger_1.commandLogger.debug(`docker.start 被调用: selector=${selector}, container=${container}`);
|
|
40
|
+
const service = getService();
|
|
41
|
+
if (!service) {
|
|
42
|
+
logger_1.commandLogger.debug('服务未初始化');
|
|
43
|
+
return 'Docker 服务未初始化';
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
if (container === '*') {
|
|
47
|
+
logger_1.commandLogger.debug('批量启动容器');
|
|
48
|
+
// 批量操作
|
|
49
|
+
const results = await service.operateContainers(selector, container, 'start');
|
|
50
|
+
return formatSearchResults(results, '启动');
|
|
51
|
+
}
|
|
52
|
+
// 单个容器
|
|
53
|
+
logger_1.commandLogger.debug(`查找容器: ${container} 在 ${selector}`);
|
|
54
|
+
const { node, container: found } = await service.findContainer(selector, container);
|
|
55
|
+
logger_1.commandLogger.debug(`找到容器: ${found.Names[0]} 在节点 ${node.name}`);
|
|
56
|
+
await node.startContainer(found.Id);
|
|
57
|
+
logger_1.commandLogger.debug(`容器已启动: ${found.Id}`);
|
|
58
|
+
const name = found.Names[0]?.replace('/', '') || found.Id.slice(0, 8);
|
|
59
|
+
return `✅ ${node.name}: ${name} 已启动`;
|
|
60
|
+
}
|
|
61
|
+
catch (e) {
|
|
62
|
+
logger_1.commandLogger.error(`启动容器失败: ${e.message}`);
|
|
63
|
+
return `❌ 启动失败: ${e.message}`;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* 停止容器
|
|
68
|
+
*/
|
|
69
|
+
ctx
|
|
70
|
+
.command('docker.stop <selector> <container>', '停止容器')
|
|
71
|
+
.alias('docker停止', '容器停止', 'docker关闭', '容器关闭')
|
|
72
|
+
.option('async', '-a 异步执行,不等待结果', { fallback: false })
|
|
73
|
+
.action(async ({ options }, selector, container) => {
|
|
74
|
+
logger_1.commandLogger.debug(`docker.stop 被调用: selector=${selector}, container=${container}`);
|
|
75
|
+
const service = getService();
|
|
76
|
+
if (!service) {
|
|
77
|
+
logger_1.commandLogger.debug('服务未初始化');
|
|
78
|
+
return 'Docker 服务未初始化';
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
if (container === '*') {
|
|
82
|
+
logger_1.commandLogger.debug('批量停止容器');
|
|
83
|
+
// 批量操作
|
|
84
|
+
const results = await service.operateContainers(selector, container, 'stop');
|
|
85
|
+
return formatSearchResults(results, '停止');
|
|
86
|
+
}
|
|
87
|
+
logger_1.commandLogger.debug(`查找容器: ${container} 在 ${selector}`);
|
|
88
|
+
const { node, container: found } = await service.findContainer(selector, container);
|
|
89
|
+
logger_1.commandLogger.debug(`找到容器: ${found.Names[0]} 在节点 ${node.name}`);
|
|
90
|
+
await node.stopContainer(found.Id);
|
|
91
|
+
logger_1.commandLogger.debug(`容器已停止: ${found.Id}`);
|
|
92
|
+
const name = found.Names[0]?.replace('/', '') || found.Id.slice(0, 8);
|
|
93
|
+
return `✅ ${node.name}: ${name} 已停止`;
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
logger_1.commandLogger.error(`停止容器失败: ${e.message}`);
|
|
97
|
+
return `❌ 停止失败: ${e.message}`;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
/**
|
|
101
|
+
* 重启容器
|
|
102
|
+
*/
|
|
103
|
+
ctx
|
|
104
|
+
.command('docker.restart <selector> <container>', '重启容器')
|
|
105
|
+
.alias('docker重启', '容器重启')
|
|
106
|
+
.option('async', '-a 异步执行,不等待结果', { fallback: false })
|
|
107
|
+
.action(async ({ options }, selector, container) => {
|
|
108
|
+
logger_1.commandLogger.debug(`docker.restart 被调用: selector=${selector}, container=${container}`);
|
|
109
|
+
const service = getService();
|
|
110
|
+
if (!service) {
|
|
111
|
+
logger_1.commandLogger.debug('服务未初始化');
|
|
112
|
+
return 'Docker 服务未初始化';
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
if (container === '*') {
|
|
116
|
+
logger_1.commandLogger.debug('批量重启容器');
|
|
117
|
+
// 批量操作
|
|
118
|
+
const results = await service.operateContainers(selector, container, 'restart');
|
|
119
|
+
return formatSearchResults(results, '重启');
|
|
120
|
+
}
|
|
121
|
+
logger_1.commandLogger.debug(`查找容器: ${container} 在 ${selector}`);
|
|
122
|
+
const { node, container: found } = await service.findContainer(selector, container);
|
|
123
|
+
logger_1.commandLogger.debug(`找到容器: ${found.Names[0]} 在节点 ${node.name}`);
|
|
124
|
+
await node.restartContainer(found.Id);
|
|
125
|
+
logger_1.commandLogger.debug(`容器已重启: ${found.Id}`);
|
|
126
|
+
const name = found.Names[0]?.replace('/', '') || found.Id.slice(0, 8);
|
|
127
|
+
return `✅ ${node.name}: ${name} 已重启`;
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
logger_1.commandLogger.error(`重启容器失败: ${e.message}`);
|
|
131
|
+
return `❌ 重启失败: ${e.message}`;
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
/**
|
|
135
|
+
* 查看容器详情
|
|
136
|
+
*/
|
|
137
|
+
ctx
|
|
138
|
+
.command('docker.inspect <selector> <container>', '查看容器详情')
|
|
139
|
+
.alias('docker详情', '容器详情', 'docker检查', '容器检查')
|
|
140
|
+
.action(async (_, selector, container) => {
|
|
141
|
+
const service = getService();
|
|
142
|
+
if (!service) {
|
|
143
|
+
return 'Docker 服务未初始化';
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
const { node, container: found } = await service.findContainer(selector, container);
|
|
147
|
+
const info = await node.getContainer(found.Id);
|
|
148
|
+
const lines = [
|
|
149
|
+
`名称: ${info.Name.replace('/', '')}`,
|
|
150
|
+
`ID: ${info.Id.slice(0, 12)}`,
|
|
151
|
+
`镜像: ${info.Config.Image}`,
|
|
152
|
+
`状态: ${info.State.Status} (运行中: ${info.State.Running})`,
|
|
153
|
+
`创建时间: ${new Date(info.Created).toLocaleString()}`,
|
|
154
|
+
`启动时间: ${info.State.StartedAt}`,
|
|
155
|
+
`重启次数: ${info.RestartCount || 0}`,
|
|
156
|
+
];
|
|
157
|
+
if (info.State.Health) {
|
|
158
|
+
lines.push(`健康状态: ${info.State.Health.Status}`);
|
|
159
|
+
}
|
|
160
|
+
return lines.join('\n');
|
|
161
|
+
}
|
|
162
|
+
catch (e) {
|
|
163
|
+
logger_1.commandLogger.error(`查看详情失败: ${e.message}`);
|
|
164
|
+
return `❌ 查看失败: ${e.message}`;
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
/**
|
|
168
|
+
* 执行命令
|
|
169
|
+
*/
|
|
170
|
+
ctx
|
|
171
|
+
.command('docker.exec <selector> <container> <cmd>', '在容器内执行命令')
|
|
172
|
+
.option('timeout', '-t <seconds> 超时时间(秒)', { fallback: 30 })
|
|
173
|
+
.action(async ({ options }, selector, container, cmd) => {
|
|
174
|
+
const service = getService();
|
|
175
|
+
if (!service) {
|
|
176
|
+
return 'Docker 服务未初始化';
|
|
177
|
+
}
|
|
178
|
+
if (!cmd) {
|
|
179
|
+
return '请输入要执行的命令';
|
|
180
|
+
}
|
|
181
|
+
logger_1.commandLogger.info(`[${selector}] 执行命令: "${cmd}"`);
|
|
182
|
+
try {
|
|
183
|
+
const { node, container: found } = await service.findContainer(selector, container);
|
|
184
|
+
const result = await node.execContainer(found.Id, [
|
|
185
|
+
'/bin/sh',
|
|
186
|
+
'-c',
|
|
187
|
+
cmd,
|
|
188
|
+
]);
|
|
189
|
+
const name = found.Names[0]?.replace('/', '') || found.Id.slice(0, 8);
|
|
190
|
+
if (result.output.trim()) {
|
|
191
|
+
return `=== ${node.name}: ${name} ===\n${result.output}`;
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
return `✅ ${node.name}: ${name} - 命令执行完成(无输出)`;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
logger_1.commandLogger.error(`执行命令失败: ${e.message}`);
|
|
199
|
+
return `❌ 执行失败: ${e.message}`;
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 指令注册入口
|
|
3
|
+
*/
|
|
4
|
+
import { Context } from 'koishi';
|
|
5
|
+
import type { DockerControlConfig } from '../types';
|
|
6
|
+
/**
|
|
7
|
+
* 获取服务的回调类型
|
|
8
|
+
*/
|
|
9
|
+
type GetService = () => any;
|
|
10
|
+
/**
|
|
11
|
+
* 注册所有指令
|
|
12
|
+
*/
|
|
13
|
+
export declare function registerCommands(ctx: Context, getService: GetService, config?: DockerControlConfig): void;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerCommands = registerCommands;
|
|
4
|
+
const list_1 = require("./list");
|
|
5
|
+
const control_1 = require("./control");
|
|
6
|
+
const logs_1 = require("./logs");
|
|
7
|
+
/**
|
|
8
|
+
* 注册所有指令
|
|
9
|
+
*/
|
|
10
|
+
function registerCommands(ctx, getService, config) {
|
|
11
|
+
// 注册各模块指令
|
|
12
|
+
(0, list_1.registerListCommand)(ctx, getService, config);
|
|
13
|
+
(0, control_1.registerControlCommands)(ctx, getService);
|
|
14
|
+
(0, logs_1.registerLogsCommand)(ctx, getService, config);
|
|
15
|
+
// 注册辅助指令
|
|
16
|
+
registerHelperCommands(ctx, getService);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* 注册辅助指令
|
|
20
|
+
*/
|
|
21
|
+
function registerHelperCommands(ctx, getService) {
|
|
22
|
+
/**
|
|
23
|
+
* 查看节点列表
|
|
24
|
+
*/
|
|
25
|
+
ctx.command('docker.nodes', '查看节点').alias('docker节点', '容器节点').action(async () => {
|
|
26
|
+
const service = getService();
|
|
27
|
+
if (!service) {
|
|
28
|
+
return 'Docker 服务未初始化';
|
|
29
|
+
}
|
|
30
|
+
const nodes = service.getAllNodes();
|
|
31
|
+
if (nodes.length === 0) {
|
|
32
|
+
return '未配置任何节点';
|
|
33
|
+
}
|
|
34
|
+
const lines = ['=== Docker 节点 ==='];
|
|
35
|
+
for (const node of nodes) {
|
|
36
|
+
const statusIcon = node.status === 'connected'
|
|
37
|
+
? '🟢'
|
|
38
|
+
: node.status === 'connecting'
|
|
39
|
+
? '🟡'
|
|
40
|
+
: '🔴';
|
|
41
|
+
const tags = node.tags.length > 0 ? ` [@${node.tags.join(' @')}]` : '';
|
|
42
|
+
lines.push(`${statusIcon} ${node.name} (${node.id})${tags} - ${node.status}`);
|
|
43
|
+
}
|
|
44
|
+
const online = nodes.filter((n) => n.status === 'connected').length;
|
|
45
|
+
lines.push(`\n总计: ${nodes.length} 个节点,${online} 个在线`);
|
|
46
|
+
return lines.join('\n');
|
|
47
|
+
});
|
|
48
|
+
/**
|
|
49
|
+
* 查看节点详情
|
|
50
|
+
*/
|
|
51
|
+
ctx
|
|
52
|
+
.command('docker.node <selector>', '查看节点详情')
|
|
53
|
+
.alias('docker节点详情', '容器节点详情')
|
|
54
|
+
.action(async (_, selector) => {
|
|
55
|
+
const service = getService();
|
|
56
|
+
if (!service) {
|
|
57
|
+
return 'Docker 服务未初始化';
|
|
58
|
+
}
|
|
59
|
+
const nodes = service.getNodesBySelector(selector);
|
|
60
|
+
if (nodes.length === 0) {
|
|
61
|
+
return `未找到节点: ${selector}`;
|
|
62
|
+
}
|
|
63
|
+
const node = nodes[0];
|
|
64
|
+
try {
|
|
65
|
+
const version = await node.getVersion();
|
|
66
|
+
const lines = [
|
|
67
|
+
`=== ${node.name} ===`,
|
|
68
|
+
`ID: ${node.id}`,
|
|
69
|
+
`状态: ${node.status}`,
|
|
70
|
+
`标签: ${node.tags.join(', ') || '无'}`,
|
|
71
|
+
`Docker 版本: ${version.Version}`,
|
|
72
|
+
`API 版本: ${version.ApiVersion}`,
|
|
73
|
+
`操作系统: ${version.Os} (${version.Arch})`,
|
|
74
|
+
`内核: ${version.KernelVersion}`,
|
|
75
|
+
];
|
|
76
|
+
return lines.join('\n');
|
|
77
|
+
}
|
|
78
|
+
catch (e) {
|
|
79
|
+
return `获取节点信息失败: ${e.message}`;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
/**
|
|
83
|
+
* 搜索容器
|
|
84
|
+
*/
|
|
85
|
+
ctx
|
|
86
|
+
.command('docker.find <container>', '搜索容器')
|
|
87
|
+
.alias('docker查找', '容器查找', 'docker搜索', '容器搜索')
|
|
88
|
+
.option('all', '-a 包含已停止的容器', { fallback: false })
|
|
89
|
+
.action(async ({ options }, container) => {
|
|
90
|
+
const service = getService();
|
|
91
|
+
if (!service) {
|
|
92
|
+
return 'Docker 服务未初始化';
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
const results = await service.findContainerGlobal(container);
|
|
96
|
+
if (results.length === 0) {
|
|
97
|
+
return `未在任何节点找到容器: ${container}`;
|
|
98
|
+
}
|
|
99
|
+
const lines = [`找到 ${results.length} 个匹配:`];
|
|
100
|
+
for (const { node, container: c } of results) {
|
|
101
|
+
const status = c.State === 'running' ? '🟢' : '🔴';
|
|
102
|
+
const name = c.Names[0]?.replace('/', '') || c.Id.slice(0, 8);
|
|
103
|
+
lines.push(`${status} ${node.name}: ${name} (${c.Id.slice(0, 12)})`);
|
|
104
|
+
}
|
|
105
|
+
return lines.join('\n');
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
return `搜索失败: ${e.message}`;
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
/**
|
|
112
|
+
* 执行命令 (一次性)
|
|
113
|
+
*/
|
|
114
|
+
ctx
|
|
115
|
+
.command('docker.exec <container> <cmd>', '在容器中执行命令')
|
|
116
|
+
.alias('docker执行', '容器执行', 'dockerexec', 'dockercmd', 'docker命令', '容器命令')
|
|
117
|
+
.option('node', '-n <node> 指定节点', { fallback: '' })
|
|
118
|
+
.action(async ({ options }, container, cmd) => {
|
|
119
|
+
const service = getService();
|
|
120
|
+
if (!service) {
|
|
121
|
+
return 'Docker 服务未初始化';
|
|
122
|
+
}
|
|
123
|
+
const nodeSelector = options.node || 'all';
|
|
124
|
+
try {
|
|
125
|
+
const nodes = service.getNodesBySelector(nodeSelector);
|
|
126
|
+
if (nodes.length === 0) {
|
|
127
|
+
return `未找到节点: ${nodeSelector}`;
|
|
128
|
+
}
|
|
129
|
+
// 在匹配的节点上搜索容器
|
|
130
|
+
const results = await service.findContainerGlobal(container);
|
|
131
|
+
if (results.length === 0) {
|
|
132
|
+
return `未找到容器: ${container}`;
|
|
133
|
+
}
|
|
134
|
+
// 在第一个匹配的节点和容器上执行
|
|
135
|
+
const { node, container: c } = results[0];
|
|
136
|
+
if (c.State !== 'running') {
|
|
137
|
+
return `容器 ${container} 未运行`;
|
|
138
|
+
}
|
|
139
|
+
const result = await node.execContainer(c.Id, cmd.split(' '));
|
|
140
|
+
return [
|
|
141
|
+
`=== 执行结果 ===`,
|
|
142
|
+
`退出码: ${result.exitCode}`,
|
|
143
|
+
'',
|
|
144
|
+
result.output || '(无输出)',
|
|
145
|
+
].join('\n');
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
return `执行失败: ${e.message}`;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
/**
|
|
152
|
+
* 交互式执行 (返回结果,不支持实时交互)
|
|
153
|
+
*/
|
|
154
|
+
ctx
|
|
155
|
+
.command('docker.shell <container> <cmd>', '在容器中执行命令(交互式)')
|
|
156
|
+
.alias('dockershell', '容器shell')
|
|
157
|
+
.option('node', '-n <node> 指定节点', { fallback: '' })
|
|
158
|
+
.option('timeout', '-t <seconds> 超时时间', { fallback: 30 })
|
|
159
|
+
.action(async ({ options }, container, cmd) => {
|
|
160
|
+
const service = getService();
|
|
161
|
+
if (!service) {
|
|
162
|
+
return 'Docker 服务未初始化';
|
|
163
|
+
}
|
|
164
|
+
const nodeSelector = options.node || 'all';
|
|
165
|
+
try {
|
|
166
|
+
const nodes = service.getNodesBySelector(nodeSelector);
|
|
167
|
+
if (nodes.length === 0) {
|
|
168
|
+
return `未找到节点: ${nodeSelector}`;
|
|
169
|
+
}
|
|
170
|
+
const results = await service.findContainerGlobal(container);
|
|
171
|
+
if (results.length === 0) {
|
|
172
|
+
return `未找到容器: ${container}`;
|
|
173
|
+
}
|
|
174
|
+
const { node, container: c } = results[0];
|
|
175
|
+
if (c.State !== 'running') {
|
|
176
|
+
return `容器 ${container} 未运行`;
|
|
177
|
+
}
|
|
178
|
+
const result = await node.execContainer(c.Id, cmd.split(' '));
|
|
179
|
+
return [
|
|
180
|
+
`=== ${node.name}/${c.Names[0]?.replace('/', '') || c.Id.slice(0, 8)} ===`,
|
|
181
|
+
`> ${cmd}`,
|
|
182
|
+
``,
|
|
183
|
+
result.output || '(无输出)',
|
|
184
|
+
``,
|
|
185
|
+
`[退出码: ${result.exitCode}]`,
|
|
186
|
+
].join('\n');
|
|
187
|
+
}
|
|
188
|
+
catch (e) {
|
|
189
|
+
return `执行失败: ${e.message}`;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
/**
|
|
193
|
+
* 查看帮助
|
|
194
|
+
*/
|
|
195
|
+
ctx.command('docker.help', '查看帮助').alias('docker帮助', 'docker帮助', '容器帮助').action(async () => {
|
|
196
|
+
return [
|
|
197
|
+
'=== Docker Control 帮助 ===',
|
|
198
|
+
'',
|
|
199
|
+
'【节点操作】',
|
|
200
|
+
' docker.nodes - 查看节点列表',
|
|
201
|
+
' docker.node <节点> - 查看节点详情',
|
|
202
|
+
'',
|
|
203
|
+
'【容器操作】',
|
|
204
|
+
' docker.ls [节点] - 列出容器',
|
|
205
|
+
' docker.start <容器> - 启动容器',
|
|
206
|
+
' docker.stop <容器> - 停止容器',
|
|
207
|
+
' docker.restart <容器> - 重启容器',
|
|
208
|
+
' docker.logs <容器> [-t 行数] - 查看日志',
|
|
209
|
+
' docker.find <容器> - 搜索容器',
|
|
210
|
+
' docker.exec <容器> <命令> - 执行命令',
|
|
211
|
+
' docker.shell <容器> <命令> - 交互式执行',
|
|
212
|
+
'',
|
|
213
|
+
'【节点选择器】',
|
|
214
|
+
' all - 所有节点',
|
|
215
|
+
' @标签 - 指定标签的节点',
|
|
216
|
+
' 节点ID/名称 - 指定单个节点',
|
|
217
|
+
'',
|
|
218
|
+
'【通知事件类型】',
|
|
219
|
+
' container.start/stop/restart/die',
|
|
220
|
+
' container.health_status',
|
|
221
|
+
' node.online/offline/error',
|
|
222
|
+
].join('\n');
|
|
223
|
+
});
|
|
224
|
+
}
|