@pengzi/kms 1.1.0 → 1.2.0
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/README.md +64 -5
- package/dist/cli/cli/kms.js +1050 -0
- package/dist/cli/kms.js +1050 -0
- package/dist/cli/src/client.js +254 -0
- package/dist/cli/src/core/asymmetric-crypto.js +170 -0
- package/dist/cli/src/core/crypto.js +99 -0
- package/dist/cli/src/core/crypto.service.js +66 -0
- package/dist/cli/src/core/key-derivation.js +95 -0
- package/dist/cli/src/index.js +50 -0
- package/dist/cli/src/models/audit.model.js +82 -0
- package/dist/cli/src/models/key.model.js +119 -0
- package/dist/cli/src/models/project.model.js +53 -0
- package/dist/cli/src/models/user.model.js +140 -0
- package/dist/cli/src/repositories/audit.repository.js +115 -0
- package/dist/cli/src/repositories/base.repository.js +94 -0
- package/dist/cli/src/repositories/key.repository.js +125 -0
- package/dist/cli/src/repositories/project.repository.js +81 -0
- package/dist/cli/src/repositories/user.repository.js +101 -0
- package/dist/cli/src/services/audit.service.js +111 -0
- package/dist/cli/src/services/auth.service.js +176 -0
- package/dist/cli/src/services/key.service.js +137 -0
- package/dist/cli/src/services/permission.service.js +142 -0
- package/dist/cli/src/services/project.service.js +102 -0
- package/dist/cli/src/types/audit.types.js +54 -0
- package/dist/cli/src/types/crypto.types.js +5 -0
- package/dist/cli/src/types/index.js +90 -0
- package/dist/cli/src/types/key.types.js +27 -0
- package/dist/cli/src/types/project.types.js +15 -0
- package/dist/cli/src/types/user.types.js +48 -0
- package/dist/cli/src/utils/config-loader.js +125 -0
- package/dist/cli/src/utils/constants.js +118 -0
- package/dist/cli/src/utils/error-handler.js +108 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +19 -2
- package/dist/client.js.map +1 -1
- package/dist/models/key.model.js +1 -1
- package/dist/models/key.model.js.map +1 -1
- package/dist/services/key.service.d.ts +5 -0
- package/dist/services/key.service.d.ts.map +1 -1
- package/dist/services/key.service.js +12 -4
- package/dist/services/key.service.js.map +1 -1
- package/dist/src/client.js +269 -0
- package/dist/src/core/asymmetric-crypto.js +170 -0
- package/dist/src/core/crypto.js +99 -0
- package/dist/src/core/crypto.service.js +66 -0
- package/dist/src/core/key-derivation.js +95 -0
- package/dist/src/index.js +50 -0
- package/dist/src/models/audit.model.js +82 -0
- package/dist/src/models/key.model.js +119 -0
- package/dist/src/models/project.model.js +53 -0
- package/dist/src/models/user.model.js +140 -0
- package/dist/src/repositories/audit.repository.js +115 -0
- package/dist/src/repositories/base.repository.js +94 -0
- package/dist/src/repositories/key.repository.js +125 -0
- package/dist/src/repositories/project.repository.js +81 -0
- package/dist/src/repositories/user.repository.js +101 -0
- package/dist/src/services/audit.service.js +111 -0
- package/dist/src/services/auth.service.js +176 -0
- package/dist/src/services/key.service.js +137 -0
- package/dist/src/services/permission.service.js +142 -0
- package/dist/src/services/project.service.js +102 -0
- package/dist/src/types/audit.types.js +54 -0
- package/dist/src/types/crypto.types.js +5 -0
- package/dist/src/types/index.js +90 -0
- package/dist/src/types/key.types.js +27 -0
- package/dist/src/types/project.types.js +15 -0
- package/dist/src/types/user.types.js +48 -0
- package/dist/src/utils/config-loader.js +125 -0
- package/dist/src/utils/constants.js +118 -0
- package/dist/src/utils/error-handler.js +108 -0
- package/dist/types/client.types.d.ts +20 -0
- package/dist/types/client.types.d.ts.map +1 -1
- package/package.json +7 -2
package/dist/cli/kms.js
ADDED
|
@@ -0,0 +1,1050 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* KMS CLI - 交互式命令行工具
|
|
5
|
+
*
|
|
6
|
+
* 用于管理密钥、项目、用户等
|
|
7
|
+
*
|
|
8
|
+
* 使用方法:
|
|
9
|
+
* node cli/kms.ts
|
|
10
|
+
* 或
|
|
11
|
+
* npm link 后直接使用 kms 命令
|
|
12
|
+
*/
|
|
13
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
16
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
17
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
18
|
+
}
|
|
19
|
+
Object.defineProperty(o, k2, desc);
|
|
20
|
+
}) : (function(o, m, k, k2) {
|
|
21
|
+
if (k2 === undefined) k2 = k;
|
|
22
|
+
o[k2] = m[k];
|
|
23
|
+
}));
|
|
24
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
+
}) : function(o, v) {
|
|
27
|
+
o["default"] = v;
|
|
28
|
+
});
|
|
29
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
+
var ownKeys = function(o) {
|
|
31
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
+
var ar = [];
|
|
33
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
+
return ar;
|
|
35
|
+
};
|
|
36
|
+
return ownKeys(o);
|
|
37
|
+
};
|
|
38
|
+
return function (mod) {
|
|
39
|
+
if (mod && mod.__esModule) return mod;
|
|
40
|
+
var result = {};
|
|
41
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
+
__setModuleDefault(result, mod);
|
|
43
|
+
return result;
|
|
44
|
+
};
|
|
45
|
+
})();
|
|
46
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
const readline = __importStar(require("readline"));
|
|
48
|
+
const mongodb_1 = require("mongodb");
|
|
49
|
+
const src_1 = require("../src");
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
const path = __importStar(require("path"));
|
|
52
|
+
// 创建 readline 接口
|
|
53
|
+
const rl = readline.createInterface({
|
|
54
|
+
input: process.stdin,
|
|
55
|
+
output: process.stdout
|
|
56
|
+
});
|
|
57
|
+
class KMSCLI {
|
|
58
|
+
constructor(options) {
|
|
59
|
+
this.kms = null;
|
|
60
|
+
this.currentUserId = 'cli_user';
|
|
61
|
+
this.options = options;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 显示主菜单
|
|
65
|
+
*/
|
|
66
|
+
async showMainMenu() {
|
|
67
|
+
console.log('\n' + '='.repeat(60));
|
|
68
|
+
console.log('🔐 KMS - 密钥管理系统 CLI');
|
|
69
|
+
console.log('='.repeat(60));
|
|
70
|
+
console.log('\n请选择操作:');
|
|
71
|
+
console.log(' 1. 项目管理');
|
|
72
|
+
console.log(' 2. 密钥管理');
|
|
73
|
+
console.log(' 3. 用户管理');
|
|
74
|
+
console.log(' 4. 审计日志');
|
|
75
|
+
console.log(' 5. 系统设置');
|
|
76
|
+
console.log(' 0. 退出');
|
|
77
|
+
console.log('');
|
|
78
|
+
const answer = await this.question('请输入选项 (0-5): ');
|
|
79
|
+
switch (answer.trim()) {
|
|
80
|
+
case '1':
|
|
81
|
+
await this.projectMenu();
|
|
82
|
+
break;
|
|
83
|
+
case '2':
|
|
84
|
+
await this.keyMenu();
|
|
85
|
+
break;
|
|
86
|
+
case '3':
|
|
87
|
+
await this.userMenu();
|
|
88
|
+
break;
|
|
89
|
+
case '4':
|
|
90
|
+
await this.auditMenu();
|
|
91
|
+
break;
|
|
92
|
+
case '5':
|
|
93
|
+
await this.settingsMenu();
|
|
94
|
+
break;
|
|
95
|
+
case '0':
|
|
96
|
+
console.log('\n👋 再见!');
|
|
97
|
+
process.exit(0);
|
|
98
|
+
default:
|
|
99
|
+
console.log('\n❌ 无效选项,请重新选择');
|
|
100
|
+
await this.showMainMenu();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* 项目管理菜单
|
|
105
|
+
*/
|
|
106
|
+
async projectMenu() {
|
|
107
|
+
console.log('\n📁 项目管理');
|
|
108
|
+
console.log('-'.repeat(40));
|
|
109
|
+
console.log(' 1. 创建新项目');
|
|
110
|
+
console.log(' 2. 查看所有项目');
|
|
111
|
+
console.log(' 3. 删除项目');
|
|
112
|
+
console.log(' 0. 返回主菜单');
|
|
113
|
+
console.log('');
|
|
114
|
+
const answer = await this.question('请选择操作 (0-3): ');
|
|
115
|
+
switch (answer.trim()) {
|
|
116
|
+
case '1':
|
|
117
|
+
await this.createProject();
|
|
118
|
+
break;
|
|
119
|
+
case '2':
|
|
120
|
+
await this.listProjects();
|
|
121
|
+
break;
|
|
122
|
+
case '3':
|
|
123
|
+
await this.deleteProject();
|
|
124
|
+
break;
|
|
125
|
+
case '0':
|
|
126
|
+
await this.showMainMenu();
|
|
127
|
+
break;
|
|
128
|
+
default:
|
|
129
|
+
console.log('❌ 无效选项');
|
|
130
|
+
await this.projectMenu();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* 密钥管理菜单
|
|
135
|
+
*/
|
|
136
|
+
async keyMenu() {
|
|
137
|
+
console.log('\n🔑 密钥管理');
|
|
138
|
+
console.log('-'.repeat(40));
|
|
139
|
+
console.log(' 1. 创建密钥');
|
|
140
|
+
console.log(' 2. 查看所有密钥');
|
|
141
|
+
console.log(' 3. 获取密钥值');
|
|
142
|
+
console.log(' 4. 更新密钥');
|
|
143
|
+
console.log(' 5. 删除密钥');
|
|
144
|
+
console.log(' 6. 按标签搜索');
|
|
145
|
+
console.log(' 0. 返回主菜单');
|
|
146
|
+
console.log('');
|
|
147
|
+
const answer = await this.question('请选择操作 (0-6): ');
|
|
148
|
+
switch (answer.trim()) {
|
|
149
|
+
case '1':
|
|
150
|
+
await this.createKey();
|
|
151
|
+
break;
|
|
152
|
+
case '2':
|
|
153
|
+
await this.listKeys();
|
|
154
|
+
break;
|
|
155
|
+
case '3':
|
|
156
|
+
await this.getKey();
|
|
157
|
+
break;
|
|
158
|
+
case '4':
|
|
159
|
+
await this.updateKey();
|
|
160
|
+
break;
|
|
161
|
+
case '5':
|
|
162
|
+
await this.deleteKey();
|
|
163
|
+
break;
|
|
164
|
+
case '6':
|
|
165
|
+
await this.searchKeys();
|
|
166
|
+
break;
|
|
167
|
+
case '0':
|
|
168
|
+
await this.showMainMenu();
|
|
169
|
+
break;
|
|
170
|
+
default:
|
|
171
|
+
console.log('❌ 无效选项');
|
|
172
|
+
await this.keyMenu();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 用户管理菜单
|
|
177
|
+
*/
|
|
178
|
+
async userMenu() {
|
|
179
|
+
console.log('\n👥 用户管理');
|
|
180
|
+
console.log('-'.repeat(40));
|
|
181
|
+
console.log(' 1. 创建用户');
|
|
182
|
+
console.log(' 2. 查看用户列表');
|
|
183
|
+
console.log(' 3. 删除用户');
|
|
184
|
+
console.log(' 0. 返回主菜单');
|
|
185
|
+
console.log('');
|
|
186
|
+
const answer = await this.question('请选择操作 (0-3): ');
|
|
187
|
+
switch (answer.trim()) {
|
|
188
|
+
case '1':
|
|
189
|
+
await this.createUser();
|
|
190
|
+
break;
|
|
191
|
+
case '2':
|
|
192
|
+
await this.listUsers();
|
|
193
|
+
break;
|
|
194
|
+
case '3':
|
|
195
|
+
await this.deleteUser();
|
|
196
|
+
break;
|
|
197
|
+
case '0':
|
|
198
|
+
await this.showMainMenu();
|
|
199
|
+
break;
|
|
200
|
+
default:
|
|
201
|
+
console.log('❌ 无效选项');
|
|
202
|
+
await this.userMenu();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* 审计日志菜单
|
|
207
|
+
*/
|
|
208
|
+
async auditMenu() {
|
|
209
|
+
console.log('\n📝 审计日志');
|
|
210
|
+
console.log('-'.repeat(40));
|
|
211
|
+
console.log(' 1. 查看最近日志');
|
|
212
|
+
console.log(' 2. 搜索日志');
|
|
213
|
+
console.log(' 0. 返回主菜单');
|
|
214
|
+
console.log('');
|
|
215
|
+
const answer = await this.question('请选择操作 (0-2): ');
|
|
216
|
+
switch (answer.trim()) {
|
|
217
|
+
case '1':
|
|
218
|
+
await this.getRecentLogs();
|
|
219
|
+
break;
|
|
220
|
+
case '2':
|
|
221
|
+
await this.searchAuditLogs();
|
|
222
|
+
break;
|
|
223
|
+
case '0':
|
|
224
|
+
await this.showMainMenu();
|
|
225
|
+
break;
|
|
226
|
+
default:
|
|
227
|
+
console.log('❌ 无效选项');
|
|
228
|
+
await this.auditMenu();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* 系统设置菜单
|
|
233
|
+
*/
|
|
234
|
+
async settingsMenu() {
|
|
235
|
+
console.log('\n⚙️ 系统设置');
|
|
236
|
+
console.log('-'.repeat(40));
|
|
237
|
+
console.log(' 1. 切换数据库连接');
|
|
238
|
+
console.log(' 2. 生成加密密钥对');
|
|
239
|
+
console.log(' 3. 查看当前配置');
|
|
240
|
+
console.log(' 0. 返回主菜单');
|
|
241
|
+
console.log('');
|
|
242
|
+
const answer = await this.question('请选择操作 (0-3): ');
|
|
243
|
+
switch (answer.trim()) {
|
|
244
|
+
case '1':
|
|
245
|
+
await this.switchConnection();
|
|
246
|
+
break;
|
|
247
|
+
case '2':
|
|
248
|
+
await this.generateKeyPair();
|
|
249
|
+
break;
|
|
250
|
+
case '3':
|
|
251
|
+
this.showCurrentConfig();
|
|
252
|
+
await this.pressEnter();
|
|
253
|
+
await this.settingsMenu();
|
|
254
|
+
break;
|
|
255
|
+
case '0':
|
|
256
|
+
await this.showMainMenu();
|
|
257
|
+
break;
|
|
258
|
+
default:
|
|
259
|
+
console.log('❌ 无效选项');
|
|
260
|
+
await this.settingsMenu();
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
// ============ 项目管理功能 ============
|
|
264
|
+
/**
|
|
265
|
+
* 创建项目
|
|
266
|
+
*/
|
|
267
|
+
async createProject() {
|
|
268
|
+
console.log('\n➕ 创建新项目');
|
|
269
|
+
console.log('-'.repeat(40));
|
|
270
|
+
const projectName = await this.question('项目名称: ', true);
|
|
271
|
+
if (!projectName) {
|
|
272
|
+
console.log('❌ 项目名称不能为空');
|
|
273
|
+
return await this.projectMenu();
|
|
274
|
+
}
|
|
275
|
+
const masterPassword = await this.question('主密码 (用于加密项目密钥): ', true);
|
|
276
|
+
if (!masterPassword || masterPassword.length < 8) {
|
|
277
|
+
console.log('❌ 主密码至少需要 8 个字符');
|
|
278
|
+
return await this.projectMenu();
|
|
279
|
+
}
|
|
280
|
+
const confirm = await this.question('确认创建? (y/n): ');
|
|
281
|
+
if (confirm.toLowerCase() !== 'y') {
|
|
282
|
+
console.log('❌ 已取消');
|
|
283
|
+
return await this.projectMenu();
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
await this.ensureConnected();
|
|
287
|
+
this.kms.setCurrentUser(this.currentUserId);
|
|
288
|
+
const metadata = {};
|
|
289
|
+
const env = await this.question('环境 (dev/staging/prod): ');
|
|
290
|
+
if (env)
|
|
291
|
+
metadata.environment = env;
|
|
292
|
+
const project = await this.kms.createProject(projectName, masterPassword, metadata);
|
|
293
|
+
console.log('\n✅ 项目创建成功!');
|
|
294
|
+
console.log(` 项目 ID: ${project.projectId}`);
|
|
295
|
+
console.log(` 项目名称: ${project.projectName}`);
|
|
296
|
+
await this.pressEnter();
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
console.log(`\n❌ 创建失败: ${error.message}`);
|
|
300
|
+
}
|
|
301
|
+
await this.projectMenu();
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* 查看所有项目
|
|
305
|
+
*/
|
|
306
|
+
async listProjects() {
|
|
307
|
+
console.log('\n📋 项目列表');
|
|
308
|
+
console.log('-'.repeat(40));
|
|
309
|
+
try {
|
|
310
|
+
await this.ensureConnected();
|
|
311
|
+
const projects = await this.kms.listProjects();
|
|
312
|
+
if (projects.length === 0) {
|
|
313
|
+
console.log(' 暂无项目');
|
|
314
|
+
await this.pressEnter();
|
|
315
|
+
return await this.projectMenu();
|
|
316
|
+
}
|
|
317
|
+
console.log(`\n共 ${projects.length} 个项目:\n`);
|
|
318
|
+
projects.forEach((project, index) => {
|
|
319
|
+
console.log(`${index + 1}. ${project.projectName}`);
|
|
320
|
+
console.log(` ID: ${project.projectId}`);
|
|
321
|
+
console.log(` 状态: ${project.status}`);
|
|
322
|
+
if (project.metadata) {
|
|
323
|
+
console.log(` 元数据: ${JSON.stringify(project.metadata)}`);
|
|
324
|
+
}
|
|
325
|
+
console.log('');
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
console.log(`\n❌ 查询失败: ${error.message}`);
|
|
330
|
+
}
|
|
331
|
+
await this.pressEnter();
|
|
332
|
+
await this.projectMenu();
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* 删除项目
|
|
336
|
+
*/
|
|
337
|
+
async deleteProject() {
|
|
338
|
+
console.log('\n🗑️ 删除项目');
|
|
339
|
+
console.log('-'.repeat(40));
|
|
340
|
+
try {
|
|
341
|
+
await this.ensureConnected();
|
|
342
|
+
const projects = await this.kms.listProjects();
|
|
343
|
+
if (projects.length === 0) {
|
|
344
|
+
console.log(' 暂无项目');
|
|
345
|
+
await this.pressEnter();
|
|
346
|
+
return await this.projectMenu();
|
|
347
|
+
}
|
|
348
|
+
// 显示项目列表
|
|
349
|
+
console.log('\n选择要删除的项目:\n');
|
|
350
|
+
projects.forEach((project, index) => {
|
|
351
|
+
console.log(`${index + 1}. ${project.projectName} (${project.projectId})`);
|
|
352
|
+
});
|
|
353
|
+
const index = await this.question('\n请输入项目编号: ');
|
|
354
|
+
const project = projects[parseInt(index) - 1];
|
|
355
|
+
if (!project) {
|
|
356
|
+
console.log('❌ 无效选择');
|
|
357
|
+
await this.pressEnter();
|
|
358
|
+
return await this.projectMenu();
|
|
359
|
+
}
|
|
360
|
+
const confirm = await this.question(`\n确认删除项目 "${project.projectName}"? 此操作不可恢复! (yes/no): `);
|
|
361
|
+
if (confirm !== 'yes') {
|
|
362
|
+
console.log('❌ 已取消');
|
|
363
|
+
return await this.projectMenu();
|
|
364
|
+
}
|
|
365
|
+
await this.kms.setCurrentUser(this.currentUserId);
|
|
366
|
+
await this.kms.deleteProject(project.projectId);
|
|
367
|
+
console.log('\n✅ 项目已删除');
|
|
368
|
+
}
|
|
369
|
+
catch (error) {
|
|
370
|
+
console.log(`\n❌ 删除失败: ${error.message}`);
|
|
371
|
+
}
|
|
372
|
+
await this.pressEnter();
|
|
373
|
+
await this.projectMenu();
|
|
374
|
+
}
|
|
375
|
+
// ============ 密钥管理功能 ============
|
|
376
|
+
/**
|
|
377
|
+
* 创建密钥
|
|
378
|
+
*/
|
|
379
|
+
async createKey() {
|
|
380
|
+
console.log('\n🔑 创建密钥');
|
|
381
|
+
console.log('-'.repeat(40));
|
|
382
|
+
try {
|
|
383
|
+
await this.ensureConnected();
|
|
384
|
+
// 选择项目
|
|
385
|
+
const projects = await this.kms.listProjects();
|
|
386
|
+
if (projects.length === 0) {
|
|
387
|
+
console.log('❌ 请先创建项目');
|
|
388
|
+
await this.pressEnter();
|
|
389
|
+
return await this.keyMenu();
|
|
390
|
+
}
|
|
391
|
+
console.log('\n选择项目:\n');
|
|
392
|
+
projects.forEach((project, index) => {
|
|
393
|
+
console.log(`${index + 1}. ${project.projectName}`);
|
|
394
|
+
});
|
|
395
|
+
const projectIndex = await this.question('\n请输入项目编号: ');
|
|
396
|
+
const project = projects[parseInt(projectIndex) - 1];
|
|
397
|
+
if (!project) {
|
|
398
|
+
console.log('❌ 无效选择');
|
|
399
|
+
await this.pressEnter();
|
|
400
|
+
return await this.keyMenu();
|
|
401
|
+
}
|
|
402
|
+
// 输入密钥信息
|
|
403
|
+
const keyName = await this.question('密钥名称: ', true);
|
|
404
|
+
const keyType = await this.selectKeyType();
|
|
405
|
+
const value = await this.question('密钥值 (连接字符串): ', true);
|
|
406
|
+
const description = await this.question('描述: ');
|
|
407
|
+
const tags = await this.question('标签 (逗号分隔,可选): ');
|
|
408
|
+
const tagArray = tags ? tags.split(',').map(t => t.trim()) : [];
|
|
409
|
+
const masterPassword = await this.question('项目主密码: ', true);
|
|
410
|
+
const confirm = await this.question('\n确认创建? (y/n): ');
|
|
411
|
+
if (confirm.toLowerCase() !== 'y') {
|
|
412
|
+
console.log('❌ 已取消');
|
|
413
|
+
return await this.keyMenu();
|
|
414
|
+
}
|
|
415
|
+
this.kms.setCurrentUser(this.currentUserId);
|
|
416
|
+
const key = await this.kms.createKey(project.projectId, masterPassword, {
|
|
417
|
+
keyName,
|
|
418
|
+
keyType,
|
|
419
|
+
value,
|
|
420
|
+
description: description || undefined,
|
|
421
|
+
tags: tagArray
|
|
422
|
+
});
|
|
423
|
+
console.log('\n✅ 密钥创建成功!');
|
|
424
|
+
console.log(` 密钥 ID: ${key.keyId}`);
|
|
425
|
+
console.log(` 密钥类型: ${key.keyType}`);
|
|
426
|
+
}
|
|
427
|
+
catch (error) {
|
|
428
|
+
console.log(`\n❌ 创建失败: ${error.message}`);
|
|
429
|
+
}
|
|
430
|
+
await this.pressEnter();
|
|
431
|
+
await this.keyMenu();
|
|
432
|
+
}
|
|
433
|
+
/**
|
|
434
|
+
* 查看所有密钥
|
|
435
|
+
*/
|
|
436
|
+
async listKeys() {
|
|
437
|
+
console.log('\n📋 密钥列表');
|
|
438
|
+
console.log('-'.repeat(40));
|
|
439
|
+
try {
|
|
440
|
+
const projectId = await this.selectProject();
|
|
441
|
+
if (!projectId)
|
|
442
|
+
return await this.keyMenu();
|
|
443
|
+
this.kms.setCurrentUser(this.currentUserId);
|
|
444
|
+
const { keys, total } = await this.kms.listKeys(projectId);
|
|
445
|
+
if (total === 0) {
|
|
446
|
+
console.log(' 暂无密钥');
|
|
447
|
+
await this.pressEnter();
|
|
448
|
+
return await this.keyMenu();
|
|
449
|
+
}
|
|
450
|
+
console.log(`\n共 ${total} 个密钥:\n`);
|
|
451
|
+
keys.forEach((key, index) => {
|
|
452
|
+
console.log(`${index + 1}. ${key.keyName}`);
|
|
453
|
+
console.log(` ID: ${key.keyId}`);
|
|
454
|
+
console.log(` 类型: ${key.keyType}`);
|
|
455
|
+
console.log(` 状态: ${key.status}`);
|
|
456
|
+
console.log(` 标签: ${key.tags.join(', ') || '无'}`);
|
|
457
|
+
console.log(` 描述: ${key.description || '无'}`);
|
|
458
|
+
console.log('');
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
catch (error) {
|
|
462
|
+
console.log(`\n❌ 查询失败: ${error.message}`);
|
|
463
|
+
}
|
|
464
|
+
await this.pressEnter();
|
|
465
|
+
await this.keyMenu();
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* 获取密钥值
|
|
469
|
+
*/
|
|
470
|
+
async getKey() {
|
|
471
|
+
console.log('\n🔓 获取密钥值');
|
|
472
|
+
console.log('-'.repeat(40));
|
|
473
|
+
try {
|
|
474
|
+
const projectId = await this.selectProject();
|
|
475
|
+
if (!projectId)
|
|
476
|
+
return await this.keyMenu();
|
|
477
|
+
const { keys, total } = await this.kms.listKeys(projectId);
|
|
478
|
+
if (total === 0) {
|
|
479
|
+
console.log(' 暂无密钥');
|
|
480
|
+
await this.pressEnter();
|
|
481
|
+
return await this.keyMenu();
|
|
482
|
+
}
|
|
483
|
+
console.log('\n选择密钥:\n');
|
|
484
|
+
keys.forEach((key, index) => {
|
|
485
|
+
console.log(`${index + 1}. ${key.keyName} (${key.keyType})`);
|
|
486
|
+
});
|
|
487
|
+
const index = await this.question('\n请输入密钥编号: ');
|
|
488
|
+
const key = keys[parseInt(index) - 1];
|
|
489
|
+
if (!key) {
|
|
490
|
+
console.log('❌ 无效选择');
|
|
491
|
+
await this.pressEnter();
|
|
492
|
+
return await this.keyMenu();
|
|
493
|
+
}
|
|
494
|
+
const masterPassword = await this.question('\n项目主密码: ', true);
|
|
495
|
+
this.kms.setCurrentUser(this.currentUserId);
|
|
496
|
+
const keyValue = await this.kms.getKey(projectId, masterPassword, key.keyId);
|
|
497
|
+
console.log('\n✅ 密钥值:');
|
|
498
|
+
console.log(' ' + keyValue.value);
|
|
499
|
+
console.log('\n⚠️ 请妥善保管密钥值,不要分享给他人!');
|
|
500
|
+
}
|
|
501
|
+
catch (error) {
|
|
502
|
+
console.log(`\n❌ 获取失败: ${error.message}`);
|
|
503
|
+
}
|
|
504
|
+
await this.pressEnter();
|
|
505
|
+
await this.keyMenu();
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* 更新密钥
|
|
509
|
+
*/
|
|
510
|
+
async updateKey() {
|
|
511
|
+
console.log('\n✏️ 更新密钥');
|
|
512
|
+
console.log('-'.repeat(40));
|
|
513
|
+
try {
|
|
514
|
+
const projectId = await this.selectProject();
|
|
515
|
+
if (!projectId)
|
|
516
|
+
return await this.keyMenu();
|
|
517
|
+
const { keys, total } = await this.kms.listKeys(projectId);
|
|
518
|
+
if (total === 0) {
|
|
519
|
+
console.log(' 暂无密钥');
|
|
520
|
+
await this.pressEnter();
|
|
521
|
+
return await this.keyMenu();
|
|
522
|
+
}
|
|
523
|
+
console.log('\n选择要更新的密钥:\n');
|
|
524
|
+
keys.forEach((key, index) => {
|
|
525
|
+
console.log(`${index + 1}. ${key.keyName}`);
|
|
526
|
+
});
|
|
527
|
+
const index = await this.question('\n请输入密钥编号: ');
|
|
528
|
+
const key = keys[parseInt(index) - 1];
|
|
529
|
+
if (!key) {
|
|
530
|
+
console.log('❌ 无效选择');
|
|
531
|
+
await this.pressEnter();
|
|
532
|
+
return await this.keyMenu();
|
|
533
|
+
}
|
|
534
|
+
console.log('\n输入新信息 (留空保持不变):');
|
|
535
|
+
const description = await this.question('新描述: ');
|
|
536
|
+
const tags = await this.question('新标签 (逗号分隔): ');
|
|
537
|
+
this.kms.setCurrentUser(this.currentUserId);
|
|
538
|
+
const updates = {};
|
|
539
|
+
if (description)
|
|
540
|
+
updates.description = description;
|
|
541
|
+
if (tags)
|
|
542
|
+
updates.tags = tags.split(',').map(t => t.trim());
|
|
543
|
+
const masterPassword = await this.question('\n项目主密码: ', true);
|
|
544
|
+
const updatedKey = await this.kms.updateKey(projectId, masterPassword, key.keyId, updates);
|
|
545
|
+
console.log('\n✅ 密钥更新成功!');
|
|
546
|
+
}
|
|
547
|
+
catch (error) {
|
|
548
|
+
console.log(`\n❌ 更新失败: ${error.message}`);
|
|
549
|
+
}
|
|
550
|
+
await this.pressEnter();
|
|
551
|
+
await this.keyMenu();
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* 删除密钥
|
|
555
|
+
*/
|
|
556
|
+
async deleteKey() {
|
|
557
|
+
console.log('\n🗑️ 删除密钥');
|
|
558
|
+
console.log('-'.repeat(40));
|
|
559
|
+
try {
|
|
560
|
+
const projectId = await this.selectProject();
|
|
561
|
+
if (!projectId)
|
|
562
|
+
return await this.keyMenu();
|
|
563
|
+
const { keys, total } = await this.kms.listKeys(projectId);
|
|
564
|
+
if (total === 0) {
|
|
565
|
+
console.log(' 暂无密钥');
|
|
566
|
+
await this.pressEnter();
|
|
567
|
+
return await this.keyMenu();
|
|
568
|
+
}
|
|
569
|
+
console.log('\n选择要删除的密钥:\n');
|
|
570
|
+
keys.forEach((key, index) => {
|
|
571
|
+
console.log(`${index + 1}. ${key.keyName}`);
|
|
572
|
+
});
|
|
573
|
+
const index = await this.question('\n请输入密钥编号: ');
|
|
574
|
+
const key = keys[parseInt(index) - 1];
|
|
575
|
+
if (!key) {
|
|
576
|
+
console.log('❌ 无效选择');
|
|
577
|
+
await this.pressEnter();
|
|
578
|
+
return await this.keyMenu();
|
|
579
|
+
}
|
|
580
|
+
const confirm = await this.question(`\n确认删除密钥 "${key.keyName}"? (yes/no): `);
|
|
581
|
+
if (confirm !== 'yes') {
|
|
582
|
+
console.log('❌ 已取消');
|
|
583
|
+
return await this.keyMenu();
|
|
584
|
+
}
|
|
585
|
+
this.kms.setCurrentUser(this.currentUserId);
|
|
586
|
+
await this.kms.deleteKey(projectId, key.keyId);
|
|
587
|
+
console.log('\n✅ 密钥已删除');
|
|
588
|
+
}
|
|
589
|
+
catch (error) {
|
|
590
|
+
console.log(`\n❌ 删除失败: ${error.message}`);
|
|
591
|
+
}
|
|
592
|
+
await this.pressEnter();
|
|
593
|
+
await this.keyMenu();
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* 搜索密钥
|
|
597
|
+
*/
|
|
598
|
+
async searchKeys() {
|
|
599
|
+
console.log('\n🔍 搜索密钥');
|
|
600
|
+
console.log('-'.repeat(40));
|
|
601
|
+
try {
|
|
602
|
+
const projectId = await this.selectProject();
|
|
603
|
+
if (!projectId)
|
|
604
|
+
return await this.keyMenu();
|
|
605
|
+
const search = await this.question('搜索关键词: ');
|
|
606
|
+
const tagFilter = await this.question('过滤标签 (可选): ');
|
|
607
|
+
this.kms.setCurrentUser(this.currentUserId);
|
|
608
|
+
const filters = {};
|
|
609
|
+
if (search)
|
|
610
|
+
filters.search = search;
|
|
611
|
+
if (tagFilter) {
|
|
612
|
+
filters.tags = tagFilter.split(',').map(t => t.trim());
|
|
613
|
+
}
|
|
614
|
+
const { keys, total } = await this.kms.listKeys(projectId, filters);
|
|
615
|
+
console.log(`\n找到 ${total} 个匹配的密钥:\n`);
|
|
616
|
+
keys.forEach((key, index) => {
|
|
617
|
+
console.log(`${index + 1}. ${key.keyName}`);
|
|
618
|
+
console.log(` 类型: ${key.keyType}`);
|
|
619
|
+
console.log(` 标签: ${key.tags.join(', ') || '无'}`);
|
|
620
|
+
console.log('');
|
|
621
|
+
});
|
|
622
|
+
}
|
|
623
|
+
catch (error) {
|
|
624
|
+
console.log(`\n❌ 搜索失败: ${error.message}`);
|
|
625
|
+
}
|
|
626
|
+
await this.pressEnter();
|
|
627
|
+
await this.keyMenu();
|
|
628
|
+
}
|
|
629
|
+
// ============ 用户管理功能 ============
|
|
630
|
+
/**
|
|
631
|
+
* 创建用户
|
|
632
|
+
*/
|
|
633
|
+
async createUser() {
|
|
634
|
+
console.log('\n👤 创建用户');
|
|
635
|
+
console.log('-'.repeat(40));
|
|
636
|
+
try {
|
|
637
|
+
await this.ensureConnected();
|
|
638
|
+
const projectId = await this.selectProject();
|
|
639
|
+
if (!projectId)
|
|
640
|
+
return await this.userMenu();
|
|
641
|
+
const username = await this.question('用户名: ', true);
|
|
642
|
+
const password = await this.question('密码: ', true);
|
|
643
|
+
console.log('\n选择角色 (可多选,用逗号分隔):');
|
|
644
|
+
console.log(' 1. admin - 管理员 (所有权限)');
|
|
645
|
+
console.log(' 2. operator - 运维人员 (读取、更新密钥)');
|
|
646
|
+
console.log(' 3. developer - 开发人员 (读取密钥)');
|
|
647
|
+
console.log(' 4. readonly - 只读用户 (列出密钥)');
|
|
648
|
+
console.log(' 5. auditor - 审计员 (查看日志)');
|
|
649
|
+
const rolesAnswer = await this.question('\n输入角色编号 (如 1,2,3): ');
|
|
650
|
+
const roleMap = {
|
|
651
|
+
'1': 'admin',
|
|
652
|
+
'2': 'operator',
|
|
653
|
+
'3': 'developer',
|
|
654
|
+
'4': 'readonly',
|
|
655
|
+
'5': 'auditor'
|
|
656
|
+
};
|
|
657
|
+
const selectedRoles = rolesAnswer.split(',')
|
|
658
|
+
.map(n => roleMap[n.trim()])
|
|
659
|
+
.filter(r => r);
|
|
660
|
+
this.kms.setCurrentUser(this.currentUserId);
|
|
661
|
+
const user = await this.kms.createUser(projectId, {
|
|
662
|
+
username,
|
|
663
|
+
password,
|
|
664
|
+
roles: selectedRoles
|
|
665
|
+
});
|
|
666
|
+
console.log('\n✅ 用户创建成功!');
|
|
667
|
+
console.log(` 用户 ID: ${user.userId}`);
|
|
668
|
+
console.log(` 用户名: ${user.username}`);
|
|
669
|
+
console.log(` 角色: ${user.roles.join(', ')}`);
|
|
670
|
+
if (user.apiKey) {
|
|
671
|
+
console.log(` API 密钥: ${user.apiKey} (仅显示一次,请妥善保管)`);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
catch (error) {
|
|
675
|
+
console.log(`\n❌ 创建失败: ${error.message}`);
|
|
676
|
+
}
|
|
677
|
+
await this.pressEnter();
|
|
678
|
+
await this.userMenu();
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* 查看用户列表
|
|
682
|
+
*/
|
|
683
|
+
async listUsers() {
|
|
684
|
+
console.log('\n👥 用户列表');
|
|
685
|
+
console.log('-'.repeat(40));
|
|
686
|
+
try {
|
|
687
|
+
const projectId = await this.selectProject();
|
|
688
|
+
if (!projectId)
|
|
689
|
+
return await this.userMenu();
|
|
690
|
+
this.kms.setCurrentUser(this.currentUserId);
|
|
691
|
+
// 通过审计日志获取用户信息
|
|
692
|
+
const logs = await this.kms.getAuditLogs(projectId, { limit: 100 });
|
|
693
|
+
const userActions = new Set();
|
|
694
|
+
logs.logs.forEach(log => {
|
|
695
|
+
if (log.action === 'CREATE_USER' && log.details.success) {
|
|
696
|
+
const userId = log.resourceId;
|
|
697
|
+
// 从日志中获取更多信息
|
|
698
|
+
userActions.add(userId);
|
|
699
|
+
}
|
|
700
|
+
});
|
|
701
|
+
// 简化显示 - 实际应用中需要查询用户表
|
|
702
|
+
console.log(`\n📊 项目用户统计: ${userActions.size} 个用户`);
|
|
703
|
+
console.log('\n提示: 详细用户列表请使用 MongoDB 客户端查看');
|
|
704
|
+
}
|
|
705
|
+
catch (error) {
|
|
706
|
+
console.log(`\n❌ 查询失败: ${error.message}`);
|
|
707
|
+
}
|
|
708
|
+
await this.pressEnter();
|
|
709
|
+
await this.userMenu();
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* 删除用户
|
|
713
|
+
*/
|
|
714
|
+
async deleteUser() {
|
|
715
|
+
console.log('\n🗑️ 删除用户');
|
|
716
|
+
console.log('-'.repeat(40));
|
|
717
|
+
console.log('⚠️ 此功能需要直接在 MongoDB 中操作');
|
|
718
|
+
console.log(' 使用: db.users.deleteMany({ projectId: "your-project-id" })');
|
|
719
|
+
console.log(' 然后运行: db.users.deleteMany({ projectId: "your-project-id" })');
|
|
720
|
+
await this.pressEnter();
|
|
721
|
+
await this.userMenu();
|
|
722
|
+
}
|
|
723
|
+
// ============ 审计日志功能 ============
|
|
724
|
+
/**
|
|
725
|
+
* 获取最近日志
|
|
726
|
+
*/
|
|
727
|
+
async getRecentLogs() {
|
|
728
|
+
console.log('\n📝 最近日志');
|
|
729
|
+
console.log('-'.repeat(40));
|
|
730
|
+
try {
|
|
731
|
+
const projectId = await this.selectProject();
|
|
732
|
+
if (!projectId)
|
|
733
|
+
return await this.auditMenu();
|
|
734
|
+
const limit = 20;
|
|
735
|
+
const logs = await this.kms.getRecentLogs(projectId, limit);
|
|
736
|
+
console.log(`\n最近 ${logs.length} 条操作:\n`);
|
|
737
|
+
logs.forEach((log, index) => {
|
|
738
|
+
const time = new Date(log.timestamp).toLocaleString();
|
|
739
|
+
const status = log.details.success ? '✅' : '❌';
|
|
740
|
+
console.log(`${index + 1}. ${status} [${time}] ${log.action}`);
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
catch (error) {
|
|
744
|
+
console.log(`\n❌ 查询失败: ${error.message}`);
|
|
745
|
+
}
|
|
746
|
+
await this.pressEnter();
|
|
747
|
+
await this.auditMenu();
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* 搜索审计日志
|
|
751
|
+
*/
|
|
752
|
+
async searchAuditLogs() {
|
|
753
|
+
console.log('\n🔍 搜索审计日志');
|
|
754
|
+
console.log('-'.repeat(40));
|
|
755
|
+
try {
|
|
756
|
+
const projectId = await this.selectProject();
|
|
757
|
+
if (!projectId)
|
|
758
|
+
return await this.auditMenu();
|
|
759
|
+
const action = await this.question('操作类型 (如 key.create, user.login): ');
|
|
760
|
+
this.kms.setCurrentUser(this.currentUserId);
|
|
761
|
+
// 简化显示 - 实际需要更多功能
|
|
762
|
+
const logs = await this.kms.getAuditLogs(projectId, { limit: 100 });
|
|
763
|
+
const filtered = logs.logs.filter(log => log.action.includes(action));
|
|
764
|
+
console.log(`\n找到 ${filtered.length} 条匹配的日志\n`);
|
|
765
|
+
filtered.forEach((log, index) => {
|
|
766
|
+
const time = new Date(log.timestamp).toLocaleString();
|
|
767
|
+
const status = log.details.success ? '✅' : '❌';
|
|
768
|
+
console.log(`${index + 1}. ${status} [${time}] ${log.action}`);
|
|
769
|
+
console.log(` 操作者: ${log.userId || 'unknown'}`);
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
catch (error) {
|
|
773
|
+
console.log(`\n❌ 搜索失败: ${error.message}`);
|
|
774
|
+
}
|
|
775
|
+
await this.pressEnter();
|
|
776
|
+
await this.auditMenu();
|
|
777
|
+
}
|
|
778
|
+
// ============ 系统设置功能 ============
|
|
779
|
+
/**
|
|
780
|
+
* 切换数据库连接
|
|
781
|
+
*/
|
|
782
|
+
async switchConnection() {
|
|
783
|
+
console.log('\n🔗 切换数据库连接');
|
|
784
|
+
console.log('-'.repeat(40));
|
|
785
|
+
const newConnectionString = await this.question('新连接字符串: ', true);
|
|
786
|
+
const newDatabaseName = await this.question('数据库名称: ', true);
|
|
787
|
+
try {
|
|
788
|
+
// 测试连接
|
|
789
|
+
const mongoClient = new mongodb_1.MongoClient(newConnectionString);
|
|
790
|
+
await mongoClient.connect();
|
|
791
|
+
const db = mongoClient.db(newDatabaseName);
|
|
792
|
+
await db.command({ ping: 1 });
|
|
793
|
+
await mongoClient.close();
|
|
794
|
+
// 更新连接
|
|
795
|
+
if (this.kms) {
|
|
796
|
+
await this.kms.disconnect();
|
|
797
|
+
}
|
|
798
|
+
this.options.connectionString = newConnectionString;
|
|
799
|
+
this.options.databaseName = newDatabaseName;
|
|
800
|
+
console.log('\n✅ 连接测试成功!');
|
|
801
|
+
console.log(` 连接字符串: ${newConnectionString}`);
|
|
802
|
+
console.log(` 数据库: ${newDatabaseName}`);
|
|
803
|
+
// 保存配置
|
|
804
|
+
await this.saveConfig();
|
|
805
|
+
}
|
|
806
|
+
catch (error) {
|
|
807
|
+
console.log(`\n❌ 连接失败: ${error.message}`);
|
|
808
|
+
}
|
|
809
|
+
await this.pressEnter();
|
|
810
|
+
await this.settingsMenu();
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* 生成加密密钥对
|
|
814
|
+
*/
|
|
815
|
+
async generateKeyPair() {
|
|
816
|
+
console.log('\n🔑 生成 RSA 加密密钥对');
|
|
817
|
+
console.log('-'.repeat(40));
|
|
818
|
+
const passphrase = await this.question('私钥密码 (可选,推荐设置): ');
|
|
819
|
+
console.log('\n正在生成密钥对...\n');
|
|
820
|
+
// 导入密钥生成函数
|
|
821
|
+
const { generateRSAKeyPair } = require('../src/core/asymmetric-crypto');
|
|
822
|
+
const keyPair = generateRSAKeyPair(passphrase || undefined);
|
|
823
|
+
console.log('✅ 密钥对生成成功!\n');
|
|
824
|
+
console.log('公钥 (可存储在代码库中):');
|
|
825
|
+
console.log('```');
|
|
826
|
+
console.log(keyPair.publicKey);
|
|
827
|
+
console.log('```\n');
|
|
828
|
+
console.log('私钥 (请妥善保管,不要提交到代码库):');
|
|
829
|
+
console.log('```');
|
|
830
|
+
console.log(keyPair.privateKey);
|
|
831
|
+
console.log('```\n');
|
|
832
|
+
console.log('💡 使用建议:');
|
|
833
|
+
console.log('1. 将公钥保存为 config/public_key.pem');
|
|
834
|
+
console.log('2. 将私钥保存到安全位置(如密钥管理服务)');
|
|
835
|
+
console.log('3. 使用私钥加密连接字符串,然后可以安全提交到代码库');
|
|
836
|
+
await this.pressEnter();
|
|
837
|
+
await this.settingsMenu();
|
|
838
|
+
}
|
|
839
|
+
/**
|
|
840
|
+
* 查看当前配置
|
|
841
|
+
*/
|
|
842
|
+
showCurrentConfig() {
|
|
843
|
+
console.log('\n⚙️ 当前配置');
|
|
844
|
+
console.log('-'.repeat(40));
|
|
845
|
+
console.log(`连接字符串: ${this.options.connectionString}`);
|
|
846
|
+
console.log(`数据库名称: ${this.options.databaseName}`);
|
|
847
|
+
console.log(`当前用户: ${this.currentUserId}`);
|
|
848
|
+
}
|
|
849
|
+
// ============ 辅助方法 ============
|
|
850
|
+
/**
|
|
851
|
+
* 确保已连接
|
|
852
|
+
*/
|
|
853
|
+
async ensureConnected() {
|
|
854
|
+
if (!this.kms) {
|
|
855
|
+
this.kms = new src_1.KMSClient(this.options);
|
|
856
|
+
}
|
|
857
|
+
// 简单检查 - 实际应该有更好的连接状态跟踪
|
|
858
|
+
try {
|
|
859
|
+
await this.kms.connect();
|
|
860
|
+
}
|
|
861
|
+
catch {
|
|
862
|
+
// 已经连接,忽略错误
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
/**
|
|
866
|
+
* 选择项目
|
|
867
|
+
*/
|
|
868
|
+
async selectProject() {
|
|
869
|
+
await this.ensureConnected();
|
|
870
|
+
const projects = await this.kms.listProjects();
|
|
871
|
+
if (projects.length === 0) {
|
|
872
|
+
console.log(' 暂无可用项目');
|
|
873
|
+
return null;
|
|
874
|
+
}
|
|
875
|
+
console.log('\n可用项目:\n');
|
|
876
|
+
projects.forEach((project, index) => {
|
|
877
|
+
console.log(`${index + 1}. ${project.projectName}`);
|
|
878
|
+
});
|
|
879
|
+
const index = await this.question('\n请选择项目编号 (0 取消): ');
|
|
880
|
+
const num = parseInt(index);
|
|
881
|
+
if (isNaN(num) || num === 0)
|
|
882
|
+
return null;
|
|
883
|
+
const project = projects[num - 1];
|
|
884
|
+
return project.projectId;
|
|
885
|
+
}
|
|
886
|
+
/**
|
|
887
|
+
* 选择密钥类型
|
|
888
|
+
*/
|
|
889
|
+
async selectKeyType() {
|
|
890
|
+
console.log('\n选择密钥类型:\n');
|
|
891
|
+
console.log('1. mongodb');
|
|
892
|
+
console.log('2. mysql');
|
|
893
|
+
console.log('3. postgresql');
|
|
894
|
+
console.log('4. redis');
|
|
895
|
+
console.log('5. custom');
|
|
896
|
+
const types = [src_1.KeyType.MONGODB, src_1.KeyType.MYSQL, src_1.KeyType.POSTGRESQL, src_1.KeyType.REDIS, src_1.KeyType.CUSTOM];
|
|
897
|
+
const index = await this.question('\n请选择密钥类型 (1-5): ');
|
|
898
|
+
const num = parseInt(index);
|
|
899
|
+
if (num < 1 || num > 5) {
|
|
900
|
+
throw new Error('无效选择');
|
|
901
|
+
}
|
|
902
|
+
return types[num - 1];
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* 保存配置
|
|
906
|
+
*/
|
|
907
|
+
async saveConfig() {
|
|
908
|
+
// TODO: 实现配置持久化
|
|
909
|
+
console.log('\n💡 提示: 可以将配置保存到 ~/.kms/config.json');
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* 等待用户按回车
|
|
913
|
+
*/
|
|
914
|
+
async pressEnter() {
|
|
915
|
+
await this.question('\n按回车继续...');
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* 提问并获取答案
|
|
919
|
+
*/
|
|
920
|
+
question(query, required = false) {
|
|
921
|
+
return new Promise((resolve) => {
|
|
922
|
+
rl.question(query, (answer) => {
|
|
923
|
+
resolve(answer.trim());
|
|
924
|
+
});
|
|
925
|
+
});
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* 从命令行参数获取数据库配置
|
|
930
|
+
*/
|
|
931
|
+
function getDatabaseConfigFromArgs(args) {
|
|
932
|
+
if (args.length === 0)
|
|
933
|
+
return null;
|
|
934
|
+
const config = {
|
|
935
|
+
connectionString: '',
|
|
936
|
+
databaseName: 'kms'
|
|
937
|
+
};
|
|
938
|
+
for (let i = 0; i < args.length; i++) {
|
|
939
|
+
const arg = args[i];
|
|
940
|
+
if (arg === '--connection' || arg === '-c') {
|
|
941
|
+
config.connectionString = args[++i];
|
|
942
|
+
}
|
|
943
|
+
else if (arg === '--database' || arg === '-d') {
|
|
944
|
+
config.databaseName = args[++i];
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
if (!config.connectionString) {
|
|
948
|
+
return null;
|
|
949
|
+
}
|
|
950
|
+
return config;
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* 创建配置文件路径
|
|
954
|
+
*/
|
|
955
|
+
function getConfigPath() {
|
|
956
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
957
|
+
if (!homeDir) {
|
|
958
|
+
throw new Error('无法确定用户主目录');
|
|
959
|
+
}
|
|
960
|
+
const configDir = path.join(homeDir, '.kms');
|
|
961
|
+
return path.join(configDir, 'config.json');
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* 从配置文件加载配置
|
|
965
|
+
*/
|
|
966
|
+
function loadConfigFromFile() {
|
|
967
|
+
const configPath = getConfigPath();
|
|
968
|
+
if (!fs.existsSync(configPath)) {
|
|
969
|
+
return null;
|
|
970
|
+
}
|
|
971
|
+
try {
|
|
972
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
973
|
+
return config;
|
|
974
|
+
}
|
|
975
|
+
catch (error) {
|
|
976
|
+
console.error(`配置文件加载失败: ${error}`);
|
|
977
|
+
return null;
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* 保存配置到文件
|
|
982
|
+
*/
|
|
983
|
+
function saveConfigToFile(config) {
|
|
984
|
+
const configPath = getConfigPath();
|
|
985
|
+
const configDir = path.dirname(configPath);
|
|
986
|
+
if (!fs.existsSync(configDir)) {
|
|
987
|
+
fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
|
|
988
|
+
}
|
|
989
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
990
|
+
}
|
|
991
|
+
// ============================================================
|
|
992
|
+
// 主程序入口
|
|
993
|
+
// ============================================================
|
|
994
|
+
async function main() {
|
|
995
|
+
console.log('\n🔐 KMS CLI - 密钥管理系统命令行工具\n');
|
|
996
|
+
// 尝试从文件加载配置
|
|
997
|
+
let options = loadConfigFromFile();
|
|
998
|
+
// 尝试从命令行参数获取配置
|
|
999
|
+
if (!options) {
|
|
1000
|
+
options = getDatabaseConfigFromArgs(process.argv.slice(2));
|
|
1001
|
+
}
|
|
1002
|
+
// 如果都没有,使用默认值并提示
|
|
1003
|
+
if (!options) {
|
|
1004
|
+
console.log('💡 提示: 可以通过以下方式配置数据库连接:');
|
|
1005
|
+
console.log(' 1. 命令行参数: kms --connection <string> --database <name>');
|
|
1006
|
+
console.log(' 2. 配置文件: ~/.kms/config.json');
|
|
1007
|
+
console.log(' 3. 交互输入: 启动后选择 "系统设置" → "切换连接"\n');
|
|
1008
|
+
// 询问配置
|
|
1009
|
+
const connectionString = await question('MongoDB 连接字符串 (mongodb://localhost:27017): ', true);
|
|
1010
|
+
if (!connectionString) {
|
|
1011
|
+
console.log('\n❌ 连接字符串不能为空');
|
|
1012
|
+
process.exit(1);
|
|
1013
|
+
}
|
|
1014
|
+
options = {
|
|
1015
|
+
connectionString,
|
|
1016
|
+
databaseName: 'kms'
|
|
1017
|
+
};
|
|
1018
|
+
// 询问是否保存配置
|
|
1019
|
+
const saveConfig = await question('\n是否保存配置到 ~/.kms/config.json? (y/n): ');
|
|
1020
|
+
if (saveConfig.toLowerCase() === 'y') {
|
|
1021
|
+
saveConfigToFile(options);
|
|
1022
|
+
console.log('✅ 配置已保存');
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
// 创建 CLI 实例
|
|
1026
|
+
const cli = new KMSCLI(options);
|
|
1027
|
+
try {
|
|
1028
|
+
await cli.ensureConnected();
|
|
1029
|
+
await cli.showMainMenu();
|
|
1030
|
+
}
|
|
1031
|
+
catch (error) {
|
|
1032
|
+
console.error('\n❌ 错误:', error.message);
|
|
1033
|
+
process.exit(1);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
function question(query, required = false) {
|
|
1037
|
+
return new Promise((resolve) => {
|
|
1038
|
+
rl.question(query, (answer) => {
|
|
1039
|
+
const result = answer.trim();
|
|
1040
|
+
if (required && !result) {
|
|
1041
|
+
console.log('此字段不能为空');
|
|
1042
|
+
resolve(question(query, required));
|
|
1043
|
+
return;
|
|
1044
|
+
}
|
|
1045
|
+
resolve(result);
|
|
1046
|
+
});
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
// 运行 CLI
|
|
1050
|
+
main();
|