@wendongfly/myhi 1.0.24 → 1.0.26
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 +60 -0
- package/dist/chat.html +12 -0
- package/dist/index.html +29 -1
- package/dist/index.js +13 -13
- package/dist/login.html +45 -6
- package/dist/terminal.html +10 -0
- package/package.json +1 -1
package/bin/myhi.js
CHANGED
|
@@ -103,6 +103,59 @@ if (cmd === 'attach') {
|
|
|
103
103
|
console.log('[myhi] 日志文件不存在');
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
} else if (cmd === 'user') {
|
|
107
|
+
const { loadUsers, listUsers, addUser, removeUser } = await import('../src/roles.js');
|
|
108
|
+
loadUsers();
|
|
109
|
+
const sub = args[args.indexOf('user') + 1] || process.argv[3];
|
|
110
|
+
if (sub === 'list' || sub === 'ls') {
|
|
111
|
+
const users = listUsers();
|
|
112
|
+
if (users.length === 0) {
|
|
113
|
+
console.log('[myhi] 还没有用户,使用 myhi user add <密码> <名称> <目录> 添加');
|
|
114
|
+
} else {
|
|
115
|
+
console.log('\n 密码\t\t名称\t\t目录');
|
|
116
|
+
console.log(' ────\t\t────\t\t────');
|
|
117
|
+
for (const u of users) {
|
|
118
|
+
console.log(` ${u.password}\t\t${u.name}\t\t${u.dir}`);
|
|
119
|
+
}
|
|
120
|
+
console.log();
|
|
121
|
+
}
|
|
122
|
+
} else if (sub === 'add') {
|
|
123
|
+
const pwd = process.argv[4];
|
|
124
|
+
const name = process.argv[5];
|
|
125
|
+
const dir = process.argv[6];
|
|
126
|
+
if (!pwd || !name || !dir) {
|
|
127
|
+
console.log('用法: myhi user add <密码> <名称> <目录>');
|
|
128
|
+
console.log('示例: myhi user add 1234 张三 D:\\project\\app1');
|
|
129
|
+
process.exit(1);
|
|
130
|
+
}
|
|
131
|
+
addUser(pwd, name, dir);
|
|
132
|
+
console.log(`[myhi] 已添加用户 "${name}" (目录: ${dir})`);
|
|
133
|
+
} else if (sub === 'remove' || sub === 'rm') {
|
|
134
|
+
const pwd = process.argv[4];
|
|
135
|
+
if (!pwd) {
|
|
136
|
+
console.log('用法: myhi user remove <密码>');
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
if (removeUser(pwd)) {
|
|
140
|
+
console.log(`[myhi] 已删除用户`);
|
|
141
|
+
} else {
|
|
142
|
+
console.log('[myhi] 未找到该密码对应的用户');
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
console.log(`
|
|
146
|
+
用户管理命令:
|
|
147
|
+
myhi user list 列出所有用户
|
|
148
|
+
myhi user add <密码> <名称> <目录> 添加用户
|
|
149
|
+
myhi user remove <密码> 删除用户
|
|
150
|
+
|
|
151
|
+
示例:
|
|
152
|
+
myhi user add 1234 张三 D:\\project\\app1
|
|
153
|
+
myhi user add 5678 李四 D:\\project\\app2
|
|
154
|
+
myhi user list
|
|
155
|
+
myhi user remove 1234
|
|
156
|
+
`);
|
|
157
|
+
}
|
|
158
|
+
|
|
106
159
|
} else if (cmd === 'help' || cmd === '--help' || cmd === '-h') {
|
|
107
160
|
console.log(`
|
|
108
161
|
myhi — 基于 Web 的终端共享工具
|
|
@@ -118,6 +171,11 @@ myhi — 基于 Web 的终端共享工具
|
|
|
118
171
|
myhi status 查看后台运行状态
|
|
119
172
|
myhi log [N] 查看后台日志(最后 N 行,默认 50)
|
|
120
173
|
|
|
174
|
+
用户管理:
|
|
175
|
+
myhi user list 列出所有用户
|
|
176
|
+
myhi user add <密码> <名称> <目录> 添加用户
|
|
177
|
+
myhi user remove <密码> 删除用户
|
|
178
|
+
|
|
121
179
|
远程连接:
|
|
122
180
|
myhi attach 列出活跃会话,交互式选择后附加
|
|
123
181
|
myhi attach <id> 直接附加到指定会话
|
|
@@ -139,6 +197,7 @@ myhi — 基于 Web 的终端共享工具
|
|
|
139
197
|
配置目录:~/.myhi/
|
|
140
198
|
token 认证令牌(自动生成)
|
|
141
199
|
password 登录密码(自动生成,可手动修改)
|
|
200
|
+
users.json 独占用户配置(myhi user 命令管理)
|
|
142
201
|
roles.json 多用户角色配置(可选)
|
|
143
202
|
sessions.json 会话持久化数据
|
|
144
203
|
daemon.pid 后台进程 PID
|
|
@@ -150,6 +209,7 @@ myhi — 基于 Web 的终端共享工具
|
|
|
150
209
|
myhi -p 8080 -d # 8080 端口后台启动
|
|
151
210
|
MYHI_CWD=/project myhi # 指定会话工作目录
|
|
152
211
|
myhi attach # 从另一台机器连接到会话
|
|
212
|
+
myhi user add 1234 张三 D:\\project # 添加用户
|
|
153
213
|
`);
|
|
154
214
|
|
|
155
215
|
} else if (cmd === '-d' || cmd === '--daemon' || cmd === 'daemon') {
|
package/dist/chat.html
CHANGED
|
@@ -215,6 +215,9 @@
|
|
|
215
215
|
<button class="top-btn" onclick="goBack()" title="返回">
|
|
216
216
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M15 18l-6-6 6-6"/></svg>
|
|
217
217
|
</button>
|
|
218
|
+
<button class="top-btn" id="logout-btn" onclick="doLogout()" title="退出" style="display:none;color:#f0883e">
|
|
219
|
+
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 01-2-2V5a2 2 0 012-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg>
|
|
220
|
+
</button>
|
|
218
221
|
</div>
|
|
219
222
|
|
|
220
223
|
<div id="status-bar">
|
|
@@ -328,6 +331,15 @@
|
|
|
328
331
|
const ansi = new AnsiUp();
|
|
329
332
|
ansi.use_classes = false;
|
|
330
333
|
|
|
334
|
+
// 加载用户信息(独占模式显示退出按钮)
|
|
335
|
+
fetch('/api/me').then(r => r.json()).then(data => {
|
|
336
|
+
if (data.exclusive) document.getElementById('logout-btn').style.display = '';
|
|
337
|
+
}).catch(() => {});
|
|
338
|
+
window.doLogout = () => {
|
|
339
|
+
if (!confirm('确定退出登录?当前所有会话将被关闭。')) return;
|
|
340
|
+
fetch('/logout', { method: 'POST' }).then(() => { location.href = '/login'; });
|
|
341
|
+
};
|
|
342
|
+
|
|
331
343
|
// ── 状态 ──────────────────────────────────────
|
|
332
344
|
const SESSION_ID = location.pathname.split('/').pop();
|
|
333
345
|
const chatArea = document.getElementById('chat-area');
|
package/dist/index.html
CHANGED
|
@@ -413,7 +413,11 @@
|
|
|
413
413
|
<div id="app">
|
|
414
414
|
<div id="header">
|
|
415
415
|
<div class="logo">my<span>hi</span></div>
|
|
416
|
-
<div
|
|
416
|
+
<div style="display:flex;align-items:center;gap:0.6rem">
|
|
417
|
+
<span id="user-name" style="font-size:0.8rem;color:var(--muted)"></span>
|
|
418
|
+
<button id="logout-btn" onclick="doLogout()" style="display:none;background:none;border:1px solid var(--border);color:var(--muted);font-size:0.75rem;padding:0.3rem 0.6rem;border-radius:6px;cursor:pointer;width:auto">退出</button>
|
|
419
|
+
<div id="conn-dot"></div>
|
|
420
|
+
</div>
|
|
417
421
|
</div>
|
|
418
422
|
|
|
419
423
|
<div id="list">
|
|
@@ -504,6 +508,30 @@
|
|
|
504
508
|
const connDot = document.getElementById('conn-dot');
|
|
505
509
|
const list = document.getElementById('list');
|
|
506
510
|
|
|
511
|
+
// 加载当前用户信息(独占模式显示用户名、退出按钮、锁定目录)
|
|
512
|
+
let _userDir = null;
|
|
513
|
+
fetch('/api/me').then(r => r.json()).then(data => {
|
|
514
|
+
if (data.exclusive && data.name) {
|
|
515
|
+
document.getElementById('user-name').textContent = data.name;
|
|
516
|
+
document.getElementById('logout-btn').style.display = '';
|
|
517
|
+
}
|
|
518
|
+
if (data.dir) {
|
|
519
|
+
_userDir = data.dir;
|
|
520
|
+
const cwdInput = document.getElementById('inp-cwd');
|
|
521
|
+
cwdInput.value = data.dir;
|
|
522
|
+
cwdInput.readOnly = true;
|
|
523
|
+
cwdInput.style.opacity = '0.6';
|
|
524
|
+
// 隐藏目录选择按钮和最近目录
|
|
525
|
+
cwdInput.parentElement.querySelector('button').style.display = 'none';
|
|
526
|
+
document.getElementById('recent-dirs').style.display = 'none';
|
|
527
|
+
}
|
|
528
|
+
}).catch(() => {});
|
|
529
|
+
|
|
530
|
+
function doLogout() {
|
|
531
|
+
if (!confirm('确定退出登录?当前所有会话将被关闭。')) return;
|
|
532
|
+
fetch('/logout', { method: 'POST' }).then(() => { location.href = '/login'; });
|
|
533
|
+
}
|
|
534
|
+
|
|
507
535
|
// HTML 转义,防止 XSS
|
|
508
536
|
function esc(s) {
|
|
509
537
|
const d = document.createElement('div');
|