@wendongfly/myhi 1.0.113 → 1.0.114
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/bin/myhi.js +190 -0
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/bin/myhi.js
CHANGED
|
@@ -244,6 +244,191 @@ if (cmd === 'attach') {
|
|
|
244
244
|
`);
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
+
} else if (cmd === 'ssh') {
|
|
248
|
+
// SSH 账号管理:为 myhi 用户创建系统 SSH 账号,隔离目录权限
|
|
249
|
+
const { existsSync } = await import('fs');
|
|
250
|
+
const { platform } = await import('os');
|
|
251
|
+
const usersFile = join(configDir, 'users.json');
|
|
252
|
+
const isWindows = platform() === 'win32';
|
|
253
|
+
|
|
254
|
+
function loadUsersFile() {
|
|
255
|
+
try { if (existsSync(usersFile)) return JSON.parse(readFileSync(usersFile, 'utf8')); } catch {}
|
|
256
|
+
return { users: {} };
|
|
257
|
+
}
|
|
258
|
+
function saveUsersFile(data) {
|
|
259
|
+
mkdirSync(configDir, { recursive: true });
|
|
260
|
+
writeFileSync(usersFile, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function run(cmd, opts = {}) {
|
|
264
|
+
try {
|
|
265
|
+
return execSync(cmd, { encoding: 'utf8', stdio: opts.silent ? 'pipe' : 'inherit', ...opts }).trim();
|
|
266
|
+
} catch (e) {
|
|
267
|
+
if (!opts.ignoreError) { console.error(`[myhi] 命令失败: ${cmd}\n${e.stderr || e.message}`); process.exit(1); }
|
|
268
|
+
return '';
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function findUserByName(name) {
|
|
273
|
+
const data = loadUsersFile();
|
|
274
|
+
for (const [pwd, info] of Object.entries(data.users || {})) {
|
|
275
|
+
if (info.name === name) return { pwd, ...info };
|
|
276
|
+
}
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const sub = args[args.indexOf('ssh') + 1] || process.argv[3];
|
|
281
|
+
|
|
282
|
+
if (sub === 'add' || sub === 'setup') {
|
|
283
|
+
const myName = process.argv[4];
|
|
284
|
+
const sshPass = process.argv[5];
|
|
285
|
+
if (!myName) {
|
|
286
|
+
console.log('用法: myhi ssh add <myhi用户名> [SSH密码]');
|
|
287
|
+
console.log('示例: myhi ssh add 张三 mypassword');
|
|
288
|
+
console.log('\n将为 myhi 用户创建系统 SSH 账号,绑定到该用户的项目目录');
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const user = findUserByName(myName);
|
|
293
|
+
if (!user) { console.log(`[myhi] 未找到 myhi 用户 "${myName}",请先 myhi user add`); process.exit(1); }
|
|
294
|
+
|
|
295
|
+
const dir = user.dir;
|
|
296
|
+
// SSH 用户名:myhi-<name> (ASCII 化)
|
|
297
|
+
const sshUser = 'myhi-' + myName.replace(/[^a-zA-Z0-9_-]/g, () => Math.random().toString(36)[2]);
|
|
298
|
+
const sshPassword = sshPass || Math.random().toString(36).slice(2, 10) + 'A1!';
|
|
299
|
+
|
|
300
|
+
console.log(`[myhi] 创建 SSH 账号...`);
|
|
301
|
+
console.log(` myhi 用户: ${myName}`);
|
|
302
|
+
console.log(` 项目目录: ${dir}`);
|
|
303
|
+
console.log(` SSH 用户: ${sshUser}`);
|
|
304
|
+
|
|
305
|
+
if (isWindows) {
|
|
306
|
+
// ── Windows ──
|
|
307
|
+
// 创建本地用户
|
|
308
|
+
run(`net user "${sshUser}" "${sshPassword}" /add /y`, { silent: true, ignoreError: true });
|
|
309
|
+
run(`net user "${sshUser}" "${sshPassword}" /y`, { silent: true, ignoreError: true });
|
|
310
|
+
|
|
311
|
+
// 设置目录权限
|
|
312
|
+
mkdirSync(dir, { recursive: true });
|
|
313
|
+
// 上级目录:允许穿越(Traverse)但不允许列目录
|
|
314
|
+
const parent = join(dir, '..');
|
|
315
|
+
run(`icacls "${parent}" /grant "${sshUser}:(RC,S,X,RA,REA)"`, { silent: true, ignoreError: true });
|
|
316
|
+
// 用户自己的目录:完全控制
|
|
317
|
+
run(`icacls "${dir}" /grant "${sshUser}:(OI)(CI)F"`, { silent: true });
|
|
318
|
+
|
|
319
|
+
// 在用户 home 下创建 project 符号链接
|
|
320
|
+
const userHome = `C:\\Users\\${sshUser}`;
|
|
321
|
+
if (!existsSync(userHome)) mkdirSync(userHome, { recursive: true });
|
|
322
|
+
const link = join(userHome, 'project');
|
|
323
|
+
if (!existsSync(link)) {
|
|
324
|
+
run(`cmd /c mklink /D "${link}" "${dir}"`, { silent: true, ignoreError: true });
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// 确保 OpenSSH Server 已启动
|
|
328
|
+
run('powershell -Command "Start-Service sshd 2>$null; Set-Service -Name sshd -StartupType Automatic 2>$null"', { silent: true, ignoreError: true });
|
|
329
|
+
|
|
330
|
+
} else {
|
|
331
|
+
// ── Linux / Ubuntu ──
|
|
332
|
+
// 创建系统用户,home 指向项目目录
|
|
333
|
+
run(`id "${sshUser}" 2>/dev/null || useradd -m -d "/home/${sshUser}" -s /bin/bash "${sshUser}"`, { silent: true, ignoreError: true });
|
|
334
|
+
run(`echo "${sshUser}:${sshPassword}" | chpasswd`, { silent: true });
|
|
335
|
+
|
|
336
|
+
mkdirSync(dir, { recursive: true });
|
|
337
|
+
|
|
338
|
+
// 在用户 home 下创建 project 符号链接
|
|
339
|
+
const homeDir = `/home/${sshUser}`;
|
|
340
|
+
const link = `${homeDir}/project`;
|
|
341
|
+
run(`ln -sfn "${dir}" "${link}"`, { silent: true, ignoreError: true });
|
|
342
|
+
run(`chown -h ${sshUser}:${sshUser} "${link}"`, { silent: true, ignoreError: true });
|
|
343
|
+
|
|
344
|
+
// 项目目录:设置为该用户所有
|
|
345
|
+
run(`chown -R ${sshUser}:${sshUser} "${dir}"`, { silent: true, ignoreError: true });
|
|
346
|
+
|
|
347
|
+
// 上级目录权限:移除 others 的读和执行
|
|
348
|
+
const parent = join(dir, '..');
|
|
349
|
+
run(`chmod 711 "${parent}"`, { silent: true, ignoreError: true });
|
|
350
|
+
// 用户自己的目录
|
|
351
|
+
run(`chmod 700 "${dir}"`, { silent: true, ignoreError: true });
|
|
352
|
+
|
|
353
|
+
// 确保 SSH 服务运行
|
|
354
|
+
run('systemctl enable ssh 2>/dev/null; systemctl start ssh 2>/dev/null || service ssh start 2>/dev/null', { silent: true, ignoreError: true });
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// 保存 SSH 映射到 users.json
|
|
358
|
+
const data = loadUsersFile();
|
|
359
|
+
if (data.users[user.pwd]) {
|
|
360
|
+
data.users[user.pwd].sshUser = sshUser;
|
|
361
|
+
saveUsersFile(data);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
console.log(`\n ✓ SSH 账号创建成功!`);
|
|
365
|
+
console.log(` ┌─────────────────────────────────`);
|
|
366
|
+
console.log(` │ SSH 用户名: ${sshUser}`);
|
|
367
|
+
console.log(` │ SSH 密码: ${sshPassword}`);
|
|
368
|
+
console.log(` │ 项目目录: ${dir}`);
|
|
369
|
+
console.log(` └─────────────────────────────────`);
|
|
370
|
+
console.log(`\n VS Code 连接方式:`);
|
|
371
|
+
console.log(` 1. 安装 Remote-SSH 扩展`);
|
|
372
|
+
console.log(` 2. 按 F1 → "Remote-SSH: Connect to Host"`);
|
|
373
|
+
console.log(` 3. 输入: ${sshUser}@<服务器IP>`);
|
|
374
|
+
console.log(` 4. 打开目录: ~/project`);
|
|
375
|
+
|
|
376
|
+
} else if (sub === 'list' || sub === 'ls') {
|
|
377
|
+
const data = loadUsersFile();
|
|
378
|
+
const entries = Object.entries(data.users || {}).filter(([, info]) => info.sshUser);
|
|
379
|
+
if (entries.length === 0) {
|
|
380
|
+
console.log('[myhi] 还没有 SSH 账号,使用 myhi ssh add <用户名> 创建');
|
|
381
|
+
} else {
|
|
382
|
+
console.log('\n myhi用户\tSSH账号\t\t\t目录');
|
|
383
|
+
console.log(' ────────\t───────\t\t\t────');
|
|
384
|
+
for (const [, info] of entries) {
|
|
385
|
+
console.log(` ${info.name}\t\t${info.sshUser}\t\t${info.dir}`);
|
|
386
|
+
}
|
|
387
|
+
console.log();
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
} else if (sub === 'remove' || sub === 'rm') {
|
|
391
|
+
const myName = process.argv[4];
|
|
392
|
+
if (!myName) { console.log('用法: myhi ssh remove <myhi用户名>'); process.exit(1); }
|
|
393
|
+
|
|
394
|
+
const user = findUserByName(myName);
|
|
395
|
+
if (!user || !user.sshUser) { console.log(`[myhi] 未找到 "${myName}" 的 SSH 账号`); process.exit(1); }
|
|
396
|
+
|
|
397
|
+
const sshUser = user.sshUser;
|
|
398
|
+
console.log(`[myhi] 删除 SSH 账号: ${sshUser}`);
|
|
399
|
+
|
|
400
|
+
if (isWindows) {
|
|
401
|
+
run(`net user "${sshUser}" /delete`, { silent: true, ignoreError: true });
|
|
402
|
+
} else {
|
|
403
|
+
run(`userdel "${sshUser}" 2>/dev/null`, { silent: true, ignoreError: true });
|
|
404
|
+
// 不删除 home,避免误删项目数据
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const data = loadUsersFile();
|
|
408
|
+
if (data.users[user.pwd]) {
|
|
409
|
+
delete data.users[user.pwd].sshUser;
|
|
410
|
+
saveUsersFile(data);
|
|
411
|
+
}
|
|
412
|
+
console.log(`[myhi] 已删除 SSH 账号 "${sshUser}"(项目目录保留)`);
|
|
413
|
+
|
|
414
|
+
} else {
|
|
415
|
+
console.log(`
|
|
416
|
+
SSH 账号管理:为 myhi 用户创建独立的 SSH 开发账号
|
|
417
|
+
|
|
418
|
+
myhi ssh add <用户名> [密码] 创建 SSH 账号(绑定到 myhi 用户的项目目录)
|
|
419
|
+
myhi ssh list 列出所有 SSH 账号
|
|
420
|
+
myhi ssh remove <用户名> 删除 SSH 账号(保留项目目录)
|
|
421
|
+
|
|
422
|
+
示例:
|
|
423
|
+
myhi user add 1234 张三 /data/projects/zhangsan # 先添加 myhi 用户
|
|
424
|
+
myhi ssh add 张三 sshpass123 # 再创建 SSH 账号
|
|
425
|
+
myhi ssh list # 查看所有 SSH 账号
|
|
426
|
+
|
|
427
|
+
员工使用 VS Code Remote-SSH 连接后打开 ~/project 即可开发。
|
|
428
|
+
每个用户只能访问自己的项目目录,无法查看其他用户的文件。
|
|
429
|
+
`);
|
|
430
|
+
}
|
|
431
|
+
|
|
247
432
|
} else if (cmd === 'kick') {
|
|
248
433
|
// 踢出会话中的用户
|
|
249
434
|
const { createRequire } = await import('module');
|
|
@@ -365,6 +550,11 @@ myhi — 基于 Web 的终端共享工具
|
|
|
365
550
|
myhi exclusive on 开启独占模式(一次只能一个账号)
|
|
366
551
|
myhi exclusive off 关闭独占模式(多人同时使用,默认)
|
|
367
552
|
|
|
553
|
+
SSH 开发:
|
|
554
|
+
myhi ssh add <用户名> [密码] 为 myhi 用户创建 SSH 账号
|
|
555
|
+
myhi ssh list 列出所有 SSH 账号
|
|
556
|
+
myhi ssh remove <用户名> 删除 SSH 账号
|
|
557
|
+
|
|
368
558
|
远程连接:
|
|
369
559
|
myhi attach 列出活跃会话,交互式选择后附加
|
|
370
560
|
myhi attach <id> 直接附加到指定会话
|
package/dist/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"1.0.
|
|
1
|
+
{"type":"module","version":"1.0.114"}
|
package/package.json
CHANGED