fogact 1.1.5 → 1.1.6
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/frontend/activate.html +56 -17
- package/frontend/admin/admin-panel-v2.js +27 -21
- package/frontend/admin/index.html +21 -11
- package/frontend/assets/market-ui.css +75 -1
- package/frontend/index.html +66 -30
- package/frontend/user/index.html +4 -4
- package/lib/index.js +114 -36
- package/lib/services/cliproxy-api.js +1 -1
- package/lib/services/newapi.js +1 -1
- package/package.json +1 -1
package/frontend/activate.html
CHANGED
|
@@ -3,13 +3,22 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>激活 CDK |
|
|
7
|
-
<meta name="description" content="输入
|
|
6
|
+
<title>激活 CDK | FogAct</title>
|
|
7
|
+
<meta name="description" content="输入 FogAct 激活码,完成服务绑定并查看额度与有效期。" />
|
|
8
|
+
<script>
|
|
9
|
+
;(function () {
|
|
10
|
+
var theme = localStorage.getItem('fogact_theme') || localStorage.getItem('admin_theme') || localStorage.getItem('yunyi_user_theme') || 'system';
|
|
11
|
+
var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
12
|
+
var isDark = theme === 'dark' || (theme === 'system' && prefersDark);
|
|
13
|
+
document.documentElement.classList.toggle('dark', isDark);
|
|
14
|
+
document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
|
|
15
|
+
})();
|
|
16
|
+
</script>
|
|
8
17
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
9
18
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
10
19
|
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@600;700;800&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
|
|
11
20
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet" />
|
|
12
|
-
<link rel="stylesheet" href="/assets/market-ui.css?v=
|
|
21
|
+
<link rel="stylesheet" href="/assets/market-ui.css?v=20260619-theme1" />
|
|
13
22
|
</head>
|
|
14
23
|
<body class="market-page">
|
|
15
24
|
<span class="market-mouse-light" aria-hidden="true"></span>
|
|
@@ -19,8 +28,8 @@
|
|
|
19
28
|
<a class="market-brand" href="/">
|
|
20
29
|
<span class="market-logo">F</span>
|
|
21
30
|
<span>
|
|
22
|
-
<span class="market-brand-title">
|
|
23
|
-
<span class="market-brand-subtitle"
|
|
31
|
+
<span class="market-brand-title">FogAct</span>
|
|
32
|
+
<span class="market-brand-subtitle">输入激活码完成服务绑定</span>
|
|
24
33
|
</span>
|
|
25
34
|
</a>
|
|
26
35
|
|
|
@@ -29,14 +38,20 @@
|
|
|
29
38
|
<a href="/admin/">管理后台</a>
|
|
30
39
|
<a href="/">首页</a>
|
|
31
40
|
</nav>
|
|
41
|
+
|
|
42
|
+
<div class="market-actions">
|
|
43
|
+
<button type="button" class="market-button market-theme-toggle" data-theme-toggle aria-label="切换暗黑模式">
|
|
44
|
+
<span class="material-symbols-outlined" data-theme-icon>light_mode</span>
|
|
45
|
+
</button>
|
|
46
|
+
</div>
|
|
32
47
|
</header>
|
|
33
48
|
|
|
34
49
|
<main class="market-activation">
|
|
35
50
|
<section class="market-activation-copy" aria-labelledby="activation-title">
|
|
36
|
-
<div class="market-kicker"><span class="market-kicker-dot"></span>
|
|
51
|
+
<div class="market-kicker"><span class="market-kicker-dot"></span> 快速激活流程</div>
|
|
37
52
|
<h1 id="activation-title" class="market-title">输入 CDK,立即开通 <span>CLI 服务</span></h1>
|
|
38
53
|
<p class="market-copy">
|
|
39
|
-
|
|
54
|
+
输入 CDK 后即可绑定服务;用户名和邮箱用于后台识别,可按需填写。
|
|
40
55
|
</p>
|
|
41
56
|
|
|
42
57
|
<div class="market-flow" aria-label="激活流程">
|
|
@@ -50,7 +65,7 @@
|
|
|
50
65
|
</div>
|
|
51
66
|
<div class="market-flow-step">
|
|
52
67
|
<span class="market-flow-index">3</span>
|
|
53
|
-
<span><strong
|
|
68
|
+
<span><strong>开通服务</strong><span>写入服务、额度和有效期</span></span>
|
|
54
69
|
</div>
|
|
55
70
|
</div>
|
|
56
71
|
|
|
@@ -61,7 +76,7 @@
|
|
|
61
76
|
<p class="market-mini-title">安全校验</p>
|
|
62
77
|
<p class="market-mini-copy">激活过程通过本地 API 完成,失败会返回明确提示。</p>
|
|
63
78
|
</div>
|
|
64
|
-
<span class="market-status-pill"
|
|
79
|
+
<span class="market-status-pill">安全</span>
|
|
65
80
|
</div>
|
|
66
81
|
<div class="market-mini-card">
|
|
67
82
|
<span class="market-icon material-symbols-outlined">rocket_launch</span>
|
|
@@ -69,7 +84,7 @@
|
|
|
69
84
|
<p class="market-mini-title">多服务支持</p>
|
|
70
85
|
<p class="market-mini-copy">适配 Codex、Claude Code、OpenCode 与 OpenClaw 场景。</p>
|
|
71
86
|
</div>
|
|
72
|
-
<span class="market-status-pill"
|
|
87
|
+
<span class="market-status-pill">多平台</span>
|
|
73
88
|
</div>
|
|
74
89
|
</div>
|
|
75
90
|
</section>
|
|
@@ -84,22 +99,22 @@
|
|
|
84
99
|
<div class="market-form-header">
|
|
85
100
|
<span class="market-icon material-symbols-outlined">vpn_key</span>
|
|
86
101
|
<h1>激活 CDK</h1>
|
|
87
|
-
<p
|
|
102
|
+
<p>输入 CDK 后即可绑定服务;用户名和邮箱用于后台识别,可按需填写。</p>
|
|
88
103
|
</div>
|
|
89
104
|
|
|
90
105
|
<form id="activate-form" class="market-form">
|
|
91
106
|
<div class="market-field">
|
|
92
|
-
<label for="activation-code">激活码 <span
|
|
107
|
+
<label for="activation-code">激活码 <span>必填</span></label>
|
|
93
108
|
<input type="text" id="activation-code" placeholder="例如 XXXX-XXXX-XXXX-XXXX" required />
|
|
94
109
|
</div>
|
|
95
110
|
|
|
96
111
|
<div class="market-field">
|
|
97
|
-
<label for="username">用户名 <span
|
|
112
|
+
<label for="username">用户名 <span>选填</span></label>
|
|
98
113
|
<input type="text" id="username" placeholder="用于后台识别的用户名" />
|
|
99
114
|
</div>
|
|
100
115
|
|
|
101
116
|
<div class="market-field">
|
|
102
|
-
<label for="email">邮箱 <span
|
|
117
|
+
<label for="email">邮箱 <span>选填</span></label>
|
|
103
118
|
<input type="email" id="email" placeholder="name@example.com" />
|
|
104
119
|
</div>
|
|
105
120
|
|
|
@@ -107,7 +122,7 @@
|
|
|
107
122
|
|
|
108
123
|
<button type="submit" id="submit-btn" class="market-button market-button-primary" style="width: 100%; min-height: 50px;">
|
|
109
124
|
<span class="material-symbols-outlined">bolt</span>
|
|
110
|
-
|
|
125
|
+
立即激活
|
|
111
126
|
</button>
|
|
112
127
|
</form>
|
|
113
128
|
|
|
@@ -127,7 +142,7 @@
|
|
|
127
142
|
</div>
|
|
128
143
|
|
|
129
144
|
<h2>激活成功</h2>
|
|
130
|
-
<p
|
|
145
|
+
<p>服务已开通,以下是本次激活详情。</p>
|
|
131
146
|
|
|
132
147
|
<div class="market-result-list">
|
|
133
148
|
<div class="market-result-row"><span>激活码</span><span id="result-code"></span></div>
|
|
@@ -175,6 +190,8 @@
|
|
|
175
190
|
});
|
|
176
191
|
});
|
|
177
192
|
|
|
193
|
+
initThemeToggle();
|
|
194
|
+
|
|
178
195
|
form.addEventListener('submit', async (event) => {
|
|
179
196
|
event.preventDefault();
|
|
180
197
|
|
|
@@ -210,10 +227,32 @@
|
|
|
210
227
|
showError('网络错误,请稍后重试');
|
|
211
228
|
} finally {
|
|
212
229
|
submitBtn.disabled = false;
|
|
213
|
-
submitBtn.innerHTML = '<span class="material-symbols-outlined">bolt</span>
|
|
230
|
+
submitBtn.innerHTML = '<span class="material-symbols-outlined">bolt</span> 立即激活';
|
|
214
231
|
}
|
|
215
232
|
});
|
|
216
233
|
|
|
234
|
+
function initThemeToggle() {
|
|
235
|
+
const button = document.querySelector('[data-theme-toggle]');
|
|
236
|
+
const icon = document.querySelector('[data-theme-icon]');
|
|
237
|
+
if (!button || !icon) return;
|
|
238
|
+
|
|
239
|
+
const sync = () => {
|
|
240
|
+
const isDark = document.documentElement.classList.contains('dark');
|
|
241
|
+
icon.textContent = isDark ? 'dark_mode' : 'light_mode';
|
|
242
|
+
button.setAttribute('aria-label', isDark ? '切换浅色模式' : '切换暗黑模式');
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
button.addEventListener('click', () => {
|
|
246
|
+
const isDark = !document.documentElement.classList.contains('dark');
|
|
247
|
+
document.documentElement.classList.toggle('dark', isDark);
|
|
248
|
+
document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
|
|
249
|
+
localStorage.setItem('fogact_theme', isDark ? 'dark' : 'light');
|
|
250
|
+
sync();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
sync();
|
|
254
|
+
}
|
|
255
|
+
|
|
217
256
|
function showError(message) {
|
|
218
257
|
errorMessage.textContent = message;
|
|
219
258
|
errorMessage.classList.remove('hidden');
|
|
@@ -229,7 +229,7 @@ function getServiceOptionsForScope(currentValue = '') {
|
|
|
229
229
|
|
|
230
230
|
const DEFAULT_SETTINGS = {
|
|
231
231
|
site: {
|
|
232
|
-
siteName: '
|
|
232
|
+
siteName: 'FogAct',
|
|
233
233
|
siteDescription: '统一管理用户、激活码与订阅配置。',
|
|
234
234
|
siteUrl: 'https://example.com',
|
|
235
235
|
logoUrl: '',
|
|
@@ -259,7 +259,7 @@ const DEFAULT_SETTINGS = {
|
|
|
259
259
|
email: {
|
|
260
260
|
smtpHost: '',
|
|
261
261
|
smtpPort: 587,
|
|
262
|
-
senderName: '
|
|
262
|
+
senderName: 'FogAct',
|
|
263
263
|
senderEmail: '',
|
|
264
264
|
enableTls: true
|
|
265
265
|
},
|
|
@@ -356,18 +356,22 @@ const UI = {
|
|
|
356
356
|
initThemeToggle() {
|
|
357
357
|
const button = document.getElementById('theme-toggle');
|
|
358
358
|
const html = document.documentElement;
|
|
359
|
-
const savedTheme = localStorage.getItem('admin_theme') || '
|
|
359
|
+
const savedTheme = localStorage.getItem('fogact_theme') || localStorage.getItem('admin_theme') || 'system';
|
|
360
|
+
const prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
361
|
+
const initialDark = savedTheme === 'dark' || (savedTheme === 'system' && prefersDark);
|
|
360
362
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
363
|
+
html.classList.toggle('dark', initialDark);
|
|
364
|
+
html.style.colorScheme = initialDark ? 'dark' : 'light';
|
|
365
|
+
button.querySelector('.material-symbols-outlined').textContent = initialDark ? 'dark_mode' : 'light_mode';
|
|
366
|
+
button.setAttribute('aria-label', initialDark ? '切换浅色模式' : '切换暗黑模式');
|
|
365
367
|
|
|
366
368
|
button.addEventListener('click', () => {
|
|
367
369
|
html.classList.toggle('dark');
|
|
368
370
|
const isDark = html.classList.contains('dark');
|
|
369
371
|
button.querySelector('.material-symbols-outlined').textContent = isDark ? 'dark_mode' : 'light_mode';
|
|
370
|
-
|
|
372
|
+
button.setAttribute('aria-label', isDark ? '切换浅色模式' : '切换暗黑模式');
|
|
373
|
+
html.style.colorScheme = isDark ? 'dark' : 'light';
|
|
374
|
+
localStorage.setItem('fogact_theme', isDark ? 'dark' : 'light');
|
|
371
375
|
});
|
|
372
376
|
},
|
|
373
377
|
|
|
@@ -375,13 +379,15 @@ const UI = {
|
|
|
375
379
|
const palette = {
|
|
376
380
|
success: 'bg-green-50 border-green-500 text-green-800',
|
|
377
381
|
error: 'bg-error-container border-error text-on-error-container',
|
|
378
|
-
info: 'bg-primary-fixed border-primary text-on-primary-fixed'
|
|
382
|
+
info: 'bg-primary-fixed border-primary text-on-primary-fixed',
|
|
383
|
+
warning: 'bg-warning-container border-warning text-on-surface'
|
|
379
384
|
};
|
|
380
385
|
|
|
381
386
|
const icons = {
|
|
382
387
|
success: 'check_circle',
|
|
383
388
|
error: 'error',
|
|
384
|
-
info: 'info'
|
|
389
|
+
info: 'info',
|
|
390
|
+
warning: 'warning'
|
|
385
391
|
};
|
|
386
392
|
|
|
387
393
|
const node = document.createElement('div');
|
|
@@ -569,7 +575,7 @@ const UserManagement = {
|
|
|
569
575
|
renderTable() {
|
|
570
576
|
const container = document.getElementById('users-table-container');
|
|
571
577
|
if (!AppState.users.length) {
|
|
572
|
-
container.innerHTML = '<p class="text-on-surface-variant text-center py-8"
|
|
578
|
+
container.innerHTML = '<p class="text-on-surface-variant text-center py-8">暂无用户数据,可点击右上角添加用户。</p>';
|
|
573
579
|
return;
|
|
574
580
|
}
|
|
575
581
|
|
|
@@ -700,7 +706,7 @@ const UserManagement = {
|
|
|
700
706
|
try {
|
|
701
707
|
const result = await API.deleteUser(id);
|
|
702
708
|
if (!result.success) {
|
|
703
|
-
UI.showNotification(result.message || '
|
|
709
|
+
UI.showNotification(result.message || '用户删除失败', 'error');
|
|
704
710
|
return;
|
|
705
711
|
}
|
|
706
712
|
UI.showNotification('用户已删除', 'success');
|
|
@@ -712,7 +718,7 @@ const UserManagement = {
|
|
|
712
718
|
return;
|
|
713
719
|
}
|
|
714
720
|
console.error(error);
|
|
715
|
-
UI.showNotification('
|
|
721
|
+
UI.showNotification('用户删除失败', 'error');
|
|
716
722
|
}
|
|
717
723
|
}
|
|
718
724
|
};
|
|
@@ -761,7 +767,7 @@ const CodeManagement = {
|
|
|
761
767
|
renderTable() {
|
|
762
768
|
const container = document.getElementById('codes-table-container');
|
|
763
769
|
if (!AppState.codes.length) {
|
|
764
|
-
container.innerHTML = '<p class="text-on-surface-variant text-center py-8"
|
|
770
|
+
container.innerHTML = '<p class="text-on-surface-variant text-center py-8">暂无激活码数据,可点击右上角生成 CDK。</p>';
|
|
765
771
|
return;
|
|
766
772
|
}
|
|
767
773
|
|
|
@@ -1196,7 +1202,7 @@ const CodeManagement = {
|
|
|
1196
1202
|
try {
|
|
1197
1203
|
const result = await API.createCode(payload);
|
|
1198
1204
|
if (!result.success) {
|
|
1199
|
-
UI.showNotification(result.message || '
|
|
1205
|
+
UI.showNotification(result.message || '激活码创建失败', 'error');
|
|
1200
1206
|
return;
|
|
1201
1207
|
}
|
|
1202
1208
|
UI.hideModal();
|
|
@@ -1209,7 +1215,7 @@ const CodeManagement = {
|
|
|
1209
1215
|
return;
|
|
1210
1216
|
}
|
|
1211
1217
|
console.error(error);
|
|
1212
|
-
UI.showNotification('
|
|
1218
|
+
UI.showNotification('激活码创建失败', 'error');
|
|
1213
1219
|
}
|
|
1214
1220
|
},
|
|
1215
1221
|
|
|
@@ -1225,7 +1231,7 @@ const CodeManagement = {
|
|
|
1225
1231
|
try {
|
|
1226
1232
|
const result = await API.updateCode(id, payload);
|
|
1227
1233
|
if (!result.success) {
|
|
1228
|
-
UI.showNotification(result.message || '
|
|
1234
|
+
UI.showNotification(result.message || '激活码更新失败', 'error');
|
|
1229
1235
|
return;
|
|
1230
1236
|
}
|
|
1231
1237
|
UI.hideModal();
|
|
@@ -1238,7 +1244,7 @@ const CodeManagement = {
|
|
|
1238
1244
|
return;
|
|
1239
1245
|
}
|
|
1240
1246
|
console.error(error);
|
|
1241
|
-
UI.showNotification('
|
|
1247
|
+
UI.showNotification('激活码更新失败', 'error');
|
|
1242
1248
|
}
|
|
1243
1249
|
},
|
|
1244
1250
|
|
|
@@ -1247,7 +1253,7 @@ const CodeManagement = {
|
|
|
1247
1253
|
try {
|
|
1248
1254
|
const result = await API.deleteCode(id);
|
|
1249
1255
|
if (!result.success) {
|
|
1250
|
-
UI.showNotification(result.message || '
|
|
1256
|
+
UI.showNotification(result.message || '激活码删除失败', 'error');
|
|
1251
1257
|
return;
|
|
1252
1258
|
}
|
|
1253
1259
|
UI.showNotification('激活码已删除', 'success');
|
|
@@ -1259,14 +1265,14 @@ const CodeManagement = {
|
|
|
1259
1265
|
return;
|
|
1260
1266
|
}
|
|
1261
1267
|
console.error(error);
|
|
1262
|
-
UI.showNotification('
|
|
1268
|
+
UI.showNotification('激活码删除失败', 'error');
|
|
1263
1269
|
}
|
|
1264
1270
|
}
|
|
1265
1271
|
};
|
|
1266
1272
|
|
|
1267
1273
|
const LogsManagement = {
|
|
1268
1274
|
render() {
|
|
1269
|
-
document.getElementById('logs-container').innerHTML = '<p class="text-on-surface-variant text-center py-8"
|
|
1275
|
+
document.getElementById('logs-container').innerHTML = '<p class="text-on-surface-variant text-center py-8">暂无日志记录,后续操作会在这里展示。</p>';
|
|
1270
1276
|
}
|
|
1271
1277
|
};
|
|
1272
1278
|
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html
|
|
2
|
+
<html lang="zh-CN">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>管理中心 |
|
|
6
|
+
<title>管理中心 | FogAct</title>
|
|
7
|
+
|
|
8
|
+
<script>
|
|
9
|
+
;(function () {
|
|
10
|
+
var theme = localStorage.getItem('fogact_theme') || localStorage.getItem('admin_theme') || localStorage.getItem('yunyi_user_theme') || 'system';
|
|
11
|
+
var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
12
|
+
var isDark = theme === 'dark' || (theme === 'system' && prefersDark);
|
|
13
|
+
document.documentElement.classList.toggle('dark', isDark);
|
|
14
|
+
document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
|
|
15
|
+
})();
|
|
16
|
+
</script>
|
|
7
17
|
|
|
8
18
|
<!-- Fonts -->
|
|
9
19
|
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@600;700;800&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
|
|
@@ -381,7 +391,7 @@
|
|
|
381
391
|
height: 80%;
|
|
382
392
|
}
|
|
383
393
|
</style>
|
|
384
|
-
<link rel="stylesheet" href="/assets/market-ui.css?v=
|
|
394
|
+
<link rel="stylesheet" href="/assets/market-ui.css?v=20260619-theme1">
|
|
385
395
|
</head>
|
|
386
396
|
<body class="market-admin bg-surface text-on-surface min-h-screen flex overflow-hidden">
|
|
387
397
|
<span class="market-mouse-light" aria-hidden="true"></span>
|
|
@@ -394,8 +404,8 @@
|
|
|
394
404
|
<div class="w-16 h-16 bg-primary rounded-2xl flex items-center justify-center mx-auto mb-4">
|
|
395
405
|
<span class="material-symbols-outlined text-on-primary text-3xl">shield</span>
|
|
396
406
|
</div>
|
|
397
|
-
<h1 class="text-2xl font-bold text-on-surface mb-2"
|
|
398
|
-
<p class="text-sm text-on-surface-variant">
|
|
407
|
+
<h1 class="text-2xl font-bold text-on-surface mb-2">登录管理中心</h1>
|
|
408
|
+
<p class="text-sm text-on-surface-variant">FogAct 管理中心</p>
|
|
399
409
|
</div>
|
|
400
410
|
|
|
401
411
|
<div id="login-alert" class="hidden mb-4 p-3 rounded-xl text-sm bg-error-container border border-error text-on-error-container"></div>
|
|
@@ -421,7 +431,7 @@
|
|
|
421
431
|
</form>
|
|
422
432
|
|
|
423
433
|
<div class="text-center mt-6 text-xs text-on-surface-variant">
|
|
424
|
-
|
|
434
|
+
管理密码可通过 ADMIN_PASSWORD 环境变量配置
|
|
425
435
|
</div>
|
|
426
436
|
</div>
|
|
427
437
|
</div>
|
|
@@ -435,8 +445,8 @@
|
|
|
435
445
|
<div class="text-xl font-black text-on-surface mb-8 px-4 flex items-center gap-3">
|
|
436
446
|
<span class="market-logo">F</span>
|
|
437
447
|
<span>
|
|
438
|
-
<span class="block leading-none">
|
|
439
|
-
<span class="block text-xs font-bold text-on-surface-variant mt-1"
|
|
448
|
+
<span class="block leading-none">FogAct</span>
|
|
449
|
+
<span class="block text-xs font-bold text-on-surface-variant mt-1">管理中心</span>
|
|
440
450
|
</span>
|
|
441
451
|
</div>
|
|
442
452
|
|
|
@@ -661,7 +671,7 @@
|
|
|
661
671
|
<div class="bg-surface-container-lowest rounded-2xl p-6 border border-outline-variant/20">
|
|
662
672
|
<h3 class="text-lg font-bold text-on-surface mb-6">系统日志</h3>
|
|
663
673
|
<div id="logs-container">
|
|
664
|
-
<p class="text-on-surface-variant text-center py-8"
|
|
674
|
+
<p class="text-on-surface-variant text-center py-8">暂无日志记录,后续操作会在这里展示。</p>
|
|
665
675
|
</div>
|
|
666
676
|
</div>
|
|
667
677
|
</div>
|
|
@@ -671,7 +681,7 @@
|
|
|
671
681
|
<div class="bg-surface-container-lowest rounded-2xl p-6 border border-outline-variant/20">
|
|
672
682
|
<h3 class="text-lg font-bold text-on-surface mb-6">系统设置</h3>
|
|
673
683
|
<div id="settings-container">
|
|
674
|
-
<p class="text-on-surface-variant text-center py-8"
|
|
684
|
+
<p class="text-on-surface-variant text-center py-8">请选择需要维护的系统配置。</p>
|
|
675
685
|
</div>
|
|
676
686
|
</div>
|
|
677
687
|
</div>
|
|
@@ -684,7 +694,7 @@
|
|
|
684
694
|
<div id="modal-root"></div>
|
|
685
695
|
|
|
686
696
|
<!-- JavaScript -->
|
|
687
|
-
<script src="/admin/admin-panel-v2.js?v=
|
|
697
|
+
<script src="/admin/admin-panel-v2.js?v=20260619-theme1"></script>
|
|
688
698
|
<script>
|
|
689
699
|
let marketPointerRaf = 0;
|
|
690
700
|
window.addEventListener('load', () => {
|
|
@@ -211,6 +211,16 @@ body.market-user::after {
|
|
|
211
211
|
box-shadow: var(--market-shadow-soft);
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
+
.market-theme-toggle {
|
|
215
|
+
width: 40px;
|
|
216
|
+
min-width: 40px;
|
|
217
|
+
padding: 0;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.market-theme-toggle .material-symbols-outlined {
|
|
221
|
+
font-size: 20px;
|
|
222
|
+
}
|
|
223
|
+
|
|
214
224
|
.market-button-primary {
|
|
215
225
|
border-color: color-mix(in srgb, var(--market-primary) 70%, transparent);
|
|
216
226
|
color: #fff;
|
|
@@ -222,6 +232,11 @@ body.market-user::after {
|
|
|
222
232
|
color: #fff;
|
|
223
233
|
}
|
|
224
234
|
|
|
235
|
+
html.dark .market-button:not(.market-button-primary),
|
|
236
|
+
body.market-dark .market-button:not(.market-button-primary) {
|
|
237
|
+
box-shadow: 0 1px 0 rgba(255, 255, 255, 0.08) inset;
|
|
238
|
+
}
|
|
239
|
+
|
|
225
240
|
.market-hero {
|
|
226
241
|
display: grid;
|
|
227
242
|
grid-template-columns: minmax(0, 1.05fr) minmax(320px, 0.75fr);
|
|
@@ -263,7 +278,13 @@ body.market-user::after {
|
|
|
263
278
|
|
|
264
279
|
html.dark .market-hero-main::before,
|
|
265
280
|
html.dark .market-card::before,
|
|
266
|
-
html.dark .market-auth-card::before
|
|
281
|
+
html.dark .market-auth-card::before,
|
|
282
|
+
body.market-dark .market-hero-main::before,
|
|
283
|
+
body.market-dark .market-card::before,
|
|
284
|
+
body.market-dark .market-auth-card::before {
|
|
285
|
+
background:
|
|
286
|
+
radial-gradient(circle at 12% 0%, rgba(255, 255, 255, 0.12), transparent 25%),
|
|
287
|
+
linear-gradient(135deg, rgba(255, 255, 255, 0.08), transparent 36%);
|
|
267
288
|
opacity: 0.09;
|
|
268
289
|
}
|
|
269
290
|
|
|
@@ -1041,6 +1062,59 @@ body.market-admin {
|
|
|
1041
1062
|
background-color: color-mix(in srgb, var(--market-danger) 14%, transparent) !important;
|
|
1042
1063
|
}
|
|
1043
1064
|
|
|
1065
|
+
html.dark .market-admin .bg-green-50,
|
|
1066
|
+
body.market-dark .market-admin .bg-green-50 {
|
|
1067
|
+
background-color: color-mix(in srgb, var(--market-success) 18%, transparent) !important;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
html.dark .market-admin .text-green-700,
|
|
1071
|
+
html.dark .market-admin .text-green-800,
|
|
1072
|
+
body.market-dark .market-admin .text-green-700,
|
|
1073
|
+
body.market-dark .market-admin .text-green-800 {
|
|
1074
|
+
color: color-mix(in srgb, var(--market-success) 82%, #fff 18%) !important;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
html.dark .market-admin .bg-amber-50,
|
|
1078
|
+
body.market-dark .market-admin .bg-amber-50 {
|
|
1079
|
+
background-color: color-mix(in srgb, var(--market-warning) 18%, transparent) !important;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
html.dark .market-admin .text-amber-700,
|
|
1083
|
+
body.market-dark .market-admin .text-amber-700 {
|
|
1084
|
+
color: color-mix(in srgb, var(--market-warning) 82%, #fff 18%) !important;
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
html.dark .market-admin tbody tr,
|
|
1088
|
+
body.market-dark .market-admin tbody tr {
|
|
1089
|
+
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.24);
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
html.dark .market-admin option,
|
|
1093
|
+
body.market-dark .market-admin option {
|
|
1094
|
+
color: var(--market-ink);
|
|
1095
|
+
background: var(--market-bg-soft);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
html.dark .market-admin .modal-backdrop,
|
|
1099
|
+
body.market-dark .market-admin .modal-backdrop {
|
|
1100
|
+
background: rgba(0, 0, 0, 0.68) !important;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
html.dark .market-admin .border-b,
|
|
1104
|
+
body.market-dark .market-admin .border-b {
|
|
1105
|
+
border-color: var(--market-line) !important;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
html.dark .market-admin [class*="bg-error-container"],
|
|
1109
|
+
body.market-dark .market-admin [class*="bg-error-container"] {
|
|
1110
|
+
background-color: color-mix(in srgb, var(--market-danger) 16%, transparent) !important;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
html.dark .market-admin [class*="text-on-error-container"],
|
|
1114
|
+
body.market-dark .market-admin [class*="text-on-error-container"] {
|
|
1115
|
+
color: color-mix(in srgb, var(--market-danger) 76%, #fff 24%) !important;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1044
1118
|
/* Built user app polish */
|
|
1045
1119
|
body.market-user {
|
|
1046
1120
|
overflow-x: hidden;
|
package/frontend/index.html
CHANGED
|
@@ -3,13 +3,22 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>
|
|
7
|
-
<meta name="description" content="
|
|
6
|
+
<title>FogAct | 多平台激活与配额管理</title>
|
|
7
|
+
<meta name="description" content="FogAct 提供用户中心、激活码管理、配额查看和多平台 CLI 接入入口。" />
|
|
8
|
+
<script>
|
|
9
|
+
;(function () {
|
|
10
|
+
var theme = localStorage.getItem('fogact_theme') || localStorage.getItem('admin_theme') || localStorage.getItem('yunyi_user_theme') || 'system';
|
|
11
|
+
var prefersDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
|
12
|
+
var isDark = theme === 'dark' || (theme === 'system' && prefersDark);
|
|
13
|
+
document.documentElement.classList.toggle('dark', isDark);
|
|
14
|
+
document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
|
|
15
|
+
})();
|
|
16
|
+
</script>
|
|
8
17
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
9
18
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
10
19
|
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@600;700;800&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
|
|
11
20
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet" />
|
|
12
|
-
<link rel="stylesheet" href="/assets/market-ui.css?v=
|
|
21
|
+
<link rel="stylesheet" href="/assets/market-ui.css?v=20260619-theme1" />
|
|
13
22
|
</head>
|
|
14
23
|
<body class="market-page">
|
|
15
24
|
<span class="market-mouse-light" aria-hidden="true"></span>
|
|
@@ -19,8 +28,8 @@
|
|
|
19
28
|
<a class="market-brand" href="/">
|
|
20
29
|
<span class="market-logo">F</span>
|
|
21
30
|
<span>
|
|
22
|
-
<span class="market-brand-title">
|
|
23
|
-
<span class="market-brand-subtitle"
|
|
31
|
+
<span class="market-brand-title">FogAct</span>
|
|
32
|
+
<span class="market-brand-subtitle">多平台激活工作台</span>
|
|
24
33
|
</span>
|
|
25
34
|
</a>
|
|
26
35
|
|
|
@@ -31,6 +40,9 @@
|
|
|
31
40
|
</nav>
|
|
32
41
|
|
|
33
42
|
<div class="market-actions">
|
|
43
|
+
<button type="button" class="market-button market-theme-toggle" data-theme-toggle aria-label="切换暗黑模式">
|
|
44
|
+
<span class="material-symbols-outlined" data-theme-icon>light_mode</span>
|
|
45
|
+
</button>
|
|
34
46
|
<a class="market-button" href="/api/stats">API</a>
|
|
35
47
|
<a class="market-button market-button-primary" href="/user/">
|
|
36
48
|
<span class="material-symbols-outlined">bolt</span>
|
|
@@ -42,10 +54,10 @@
|
|
|
42
54
|
<main>
|
|
43
55
|
<section class="market-hero" aria-labelledby="hero-title">
|
|
44
56
|
<div class="market-hero-main">
|
|
45
|
-
<div class="market-kicker"><span class="market-kicker-dot"></span>
|
|
46
|
-
<h1 id="hero-title" class="market-title"
|
|
57
|
+
<div class="market-kicker"><span class="market-kicker-dot"></span> CLI 激活与配额管理</div>
|
|
58
|
+
<h1 id="hero-title" class="market-title">统一管理 Codex、Claude 与更多 <span>CLI 服务</span></h1>
|
|
47
59
|
<p class="market-copy">
|
|
48
|
-
|
|
60
|
+
在一个入口完成激活码发放、用户绑定、额度查看和服务配置,减少来回切换。
|
|
49
61
|
</p>
|
|
50
62
|
|
|
51
63
|
<label class="market-search" aria-label="搜索功能入口">
|
|
@@ -70,31 +82,31 @@
|
|
|
70
82
|
</div>
|
|
71
83
|
</div>
|
|
72
84
|
|
|
73
|
-
<aside class="market-hero-side" aria-label="
|
|
85
|
+
<aside class="market-hero-side" aria-label="服务状态概览">
|
|
74
86
|
<div class="market-preview-stack">
|
|
75
87
|
<div class="market-mini-card">
|
|
76
|
-
<span class="market-icon material-symbols-outlined">
|
|
88
|
+
<span class="market-icon material-symbols-outlined">hub</span>
|
|
77
89
|
<div>
|
|
78
|
-
<p class="market-mini-title"
|
|
79
|
-
<p class="market-mini-copy"
|
|
90
|
+
<p class="market-mini-title">服务接入</p>
|
|
91
|
+
<p class="market-mini-copy">统一承载 Codex、Claude Code、OpenCode 与 OpenClaw 等 CLI 场景。</p>
|
|
80
92
|
</div>
|
|
81
|
-
<span class="market-status-pill"
|
|
93
|
+
<span class="market-status-pill">已接入</span>
|
|
82
94
|
</div>
|
|
83
95
|
<div class="market-mini-card">
|
|
84
|
-
<span class="market-icon material-symbols-outlined">
|
|
96
|
+
<span class="market-icon material-symbols-outlined">key_vertical</span>
|
|
85
97
|
<div>
|
|
86
|
-
<p class="market-mini-title"
|
|
87
|
-
<p class="market-mini-copy"
|
|
98
|
+
<p class="market-mini-title">激活码管理</p>
|
|
99
|
+
<p class="market-mini-copy">支持生成、导出、绑定、过期状态追踪,方便批量运营。</p>
|
|
88
100
|
</div>
|
|
89
|
-
<span class="market-status-pill">
|
|
101
|
+
<span class="market-status-pill">CDK</span>
|
|
90
102
|
</div>
|
|
91
103
|
<div class="market-mini-card">
|
|
92
104
|
<span class="market-icon material-symbols-outlined">speed</span>
|
|
93
105
|
<div>
|
|
94
|
-
<p class="market-mini-title"
|
|
95
|
-
<p class="market-mini-copy"
|
|
106
|
+
<p class="market-mini-title">配额监控</p>
|
|
107
|
+
<p class="market-mini-copy">快速查看请求、额度、用户状态和服务健康度。</p>
|
|
96
108
|
</div>
|
|
97
|
-
<span class="market-status-pill"
|
|
109
|
+
<span class="market-status-pill">监控</span>
|
|
98
110
|
</div>
|
|
99
111
|
</div>
|
|
100
112
|
|
|
@@ -104,7 +116,7 @@
|
|
|
104
116
|
<span class="market-status-pill"><span class="market-kicker-dot"></span> 34020</span>
|
|
105
117
|
</div>
|
|
106
118
|
<p class="market-mini-copy" style="margin-top: 14px; font-size: 14px; line-height: 1.7;">
|
|
107
|
-
|
|
119
|
+
本地 Web 服务默认运行在 34020 端口,提供首页、用户中心、管理中心和激活入口。
|
|
108
120
|
</p>
|
|
109
121
|
</div>
|
|
110
122
|
</aside>
|
|
@@ -114,7 +126,7 @@
|
|
|
114
126
|
<div class="market-section-head">
|
|
115
127
|
<div>
|
|
116
128
|
<h2 id="section-title">工作区入口</h2>
|
|
117
|
-
<p
|
|
129
|
+
<p>按使用角色拆分入口:查看用量、管理资源、完成激活,各自保持清晰路径。</p>
|
|
118
130
|
</div>
|
|
119
131
|
<a class="market-button" href="/admin/">打开管理中心</a>
|
|
120
132
|
</div>
|
|
@@ -126,9 +138,9 @@
|
|
|
126
138
|
<span class="market-widget market-widget-graph"><i></i><i></i><i></i><i></i></span>
|
|
127
139
|
</div>
|
|
128
140
|
<div class="market-card-content">
|
|
129
|
-
<h3
|
|
130
|
-
<p
|
|
131
|
-
<div class="market-card-footer"><span class="market-tag"
|
|
141
|
+
<h3>用户中心</h3>
|
|
142
|
+
<p>查看额度、请求趋势、公告和个人配置,适合作为用户自助入口。</p>
|
|
143
|
+
<div class="market-card-footer"><span class="market-tag">用户入口</span><span class="market-arrow">→</span></div>
|
|
132
144
|
</div>
|
|
133
145
|
</a>
|
|
134
146
|
|
|
@@ -139,8 +151,8 @@
|
|
|
139
151
|
</div>
|
|
140
152
|
<div class="market-card-content">
|
|
141
153
|
<h3>管理控制台</h3>
|
|
142
|
-
<p
|
|
143
|
-
<div class="market-card-footer"><span class="market-tag"
|
|
154
|
+
<p>集中维护用户、激活码、日志和系统设置,适合管理员日常操作。</p>
|
|
155
|
+
<div class="market-card-footer"><span class="market-tag">管理入口</span><span class="market-arrow">→</span></div>
|
|
144
156
|
</div>
|
|
145
157
|
</a>
|
|
146
158
|
|
|
@@ -151,9 +163,9 @@
|
|
|
151
163
|
<span class="market-widget market-widget-badge material-symbols-outlined">bolt</span>
|
|
152
164
|
</div>
|
|
153
165
|
<div class="market-card-content">
|
|
154
|
-
<h3>CDK
|
|
155
|
-
<p
|
|
156
|
-
<div class="market-card-footer"><span class="market-tag"
|
|
166
|
+
<h3>CDK 激活</h3>
|
|
167
|
+
<p>输入激活码即可完成服务绑定,并返回额度、服务类型和有效期。</p>
|
|
168
|
+
<div class="market-card-footer"><span class="market-tag">激活入口</span><span class="market-arrow">→</span></div>
|
|
157
169
|
</div>
|
|
158
170
|
</a>
|
|
159
171
|
</div>
|
|
@@ -179,6 +191,8 @@
|
|
|
179
191
|
});
|
|
180
192
|
});
|
|
181
193
|
|
|
194
|
+
initThemeToggle();
|
|
195
|
+
|
|
182
196
|
document.querySelectorAll('.market-card-lab').forEach((card) => {
|
|
183
197
|
card.addEventListener('pointermove', (event) => {
|
|
184
198
|
const rect = card.getBoundingClientRect();
|
|
@@ -186,6 +200,28 @@
|
|
|
186
200
|
card.style.setProperty('--my', `${((event.clientY - rect.top) / rect.height) * 100}%`);
|
|
187
201
|
});
|
|
188
202
|
});
|
|
203
|
+
|
|
204
|
+
function initThemeToggle() {
|
|
205
|
+
const button = document.querySelector('[data-theme-toggle]');
|
|
206
|
+
const icon = document.querySelector('[data-theme-icon]');
|
|
207
|
+
if (!button || !icon) return;
|
|
208
|
+
|
|
209
|
+
const sync = () => {
|
|
210
|
+
const isDark = document.documentElement.classList.contains('dark');
|
|
211
|
+
icon.textContent = isDark ? 'dark_mode' : 'light_mode';
|
|
212
|
+
button.setAttribute('aria-label', isDark ? '切换浅色模式' : '切换暗黑模式');
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
button.addEventListener('click', () => {
|
|
216
|
+
const isDark = !document.documentElement.classList.contains('dark');
|
|
217
|
+
document.documentElement.classList.toggle('dark', isDark);
|
|
218
|
+
document.documentElement.style.colorScheme = isDark ? 'dark' : 'light';
|
|
219
|
+
localStorage.setItem('fogact_theme', isDark ? 'dark' : 'light');
|
|
220
|
+
sync();
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
sync();
|
|
224
|
+
}
|
|
189
225
|
</script>
|
|
190
226
|
</body>
|
|
191
227
|
</html>
|
package/frontend/user/index.html
CHANGED
|
@@ -8,15 +8,15 @@
|
|
|
8
8
|
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z'%3E%3C/path%3E%3C/svg%3E"
|
|
9
9
|
/>
|
|
10
10
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
11
|
-
<title>
|
|
12
|
-
<meta name="description" content="
|
|
11
|
+
<title>FogAct 用户中心 | 用量与配额监控</title>
|
|
12
|
+
<meta name="description" content="FogAct 用户中心用于查看 API 用量、配额状态、公告和个人配置。" />
|
|
13
13
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
14
14
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
15
15
|
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@600;700;800&family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet" />
|
|
16
16
|
<script>
|
|
17
17
|
// 阻塞式主题初始化 — 防止 FOUC + 设置内联背景色
|
|
18
18
|
;(function () {
|
|
19
|
-
var t = localStorage.getItem('yunyi_user_theme') || 'system'
|
|
19
|
+
var t = localStorage.getItem('fogact_theme') || localStorage.getItem('yunyi_user_theme') || 'system'
|
|
20
20
|
var d = t === 'dark' || (t === 'system' && matchMedia('(prefers-color-scheme: dark)').matches)
|
|
21
21
|
var el = document.documentElement
|
|
22
22
|
if (d) {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
<script type="module" crossorigin src="assets/index-Da98HOxL.js"></script>
|
|
32
32
|
<link rel="modulepreload" crossorigin href="assets/chart-vendor-CULJE59K.js">
|
|
33
33
|
<link rel="stylesheet" crossorigin href="assets/index-B8QSyYhS.css">
|
|
34
|
-
<link rel="stylesheet" href="/assets/market-ui.css?v=
|
|
34
|
+
<link rel="stylesheet" href="/assets/market-ui.css?v=20260619-theme1" />
|
|
35
35
|
</head>
|
|
36
36
|
<body class="market-user min-h-screen">
|
|
37
37
|
<span class="market-mouse-light" aria-hidden="true"></span>
|
package/lib/index.js
CHANGED
|
@@ -8,51 +8,129 @@ const { runTestCommand } = require("./commands/test");
|
|
|
8
8
|
const { runRestoreCommand } = require("./commands/restore");
|
|
9
9
|
const { runActivationWizard } = require("./services/activation-orchestrator");
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
const MENU_CHOICES = [
|
|
12
|
+
{ title: "1. 激活服务", value: "activate" },
|
|
13
|
+
{ title: "2. 测试节点", value: "test" },
|
|
14
|
+
{ title: "3. 恢复备份", value: "restore" },
|
|
15
|
+
{ title: "4. 退出", value: "exit" },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
function displayWidth(value) {
|
|
19
|
+
return Array.from(value).reduce((width, char) => {
|
|
15
20
|
return width + (char.charCodeAt(0) > 0xff ? 2 : 1);
|
|
16
21
|
}, 0);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function padLine(value, width) {
|
|
25
|
+
const padding = Math.max(0, width - displayWidth(value));
|
|
26
|
+
const left = Math.floor(padding / 2);
|
|
27
|
+
const right = padding - left;
|
|
28
|
+
return `${" ".repeat(left)}${value}${" ".repeat(right)}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function renderMenu(cursor = 0) {
|
|
32
|
+
const version = packageJson.version;
|
|
33
|
+
const title = `FogAct 激活器 v${version}`;
|
|
34
|
+
const lines = [
|
|
35
|
+
"",
|
|
36
|
+
" ╭─────────────────────────────────────╮",
|
|
37
|
+
` │${padLine(title, 37)}│`,
|
|
38
|
+
" │ Claude Code / Codex 配置工具 │",
|
|
39
|
+
" ╰─────────────────────────────────────╯",
|
|
40
|
+
"",
|
|
41
|
+
"? 请选择操作:",
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
MENU_CHOICES.forEach((choice, index) => {
|
|
45
|
+
lines.push(`${index === cursor ? "❯" : " "} ${choice.title}`);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
lines.push("");
|
|
49
|
+
lines.push("↑↓ navigate • ⏎ select");
|
|
50
|
+
return lines.join("\n");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function printBanner() {
|
|
54
|
+
console.log(renderMenu(0));
|
|
29
55
|
}
|
|
30
56
|
|
|
31
57
|
async function runInteractiveMenu() {
|
|
32
58
|
await runActivationWizard();
|
|
33
59
|
}
|
|
34
60
|
|
|
61
|
+
function selectMenuAction() {
|
|
62
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
63
|
+
printBanner();
|
|
64
|
+
return Promise.resolve(null);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return new Promise((resolve) => {
|
|
68
|
+
let cursor = 0;
|
|
69
|
+
let renderedLines = 0;
|
|
70
|
+
const stdin = process.stdin;
|
|
71
|
+
|
|
72
|
+
const render = () => {
|
|
73
|
+
if (renderedLines > 0) {
|
|
74
|
+
process.stdout.write(`\x1b[${renderedLines}A`);
|
|
75
|
+
process.stdout.write("\x1b[J");
|
|
76
|
+
}
|
|
77
|
+
const output = renderMenu(cursor);
|
|
78
|
+
renderedLines = output.split("\n").length;
|
|
79
|
+
process.stdout.write(output);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const cleanup = () => {
|
|
83
|
+
stdin.off("data", onData);
|
|
84
|
+
if (stdin.isRaw) stdin.setRawMode(false);
|
|
85
|
+
stdin.pause();
|
|
86
|
+
process.stdout.write("\x1b[?25h\n");
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const submit = () => {
|
|
90
|
+
const choice = MENU_CHOICES[cursor];
|
|
91
|
+
cleanup();
|
|
92
|
+
resolve(choice.value);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const cancel = () => {
|
|
96
|
+
cleanup();
|
|
97
|
+
resolve("exit");
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const onData = (data) => {
|
|
101
|
+
const key = data.toString("utf8");
|
|
102
|
+
if (key === "\u0003" || key === "\u001b") {
|
|
103
|
+
cancel();
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (key === "\r" || key === "\n") {
|
|
107
|
+
submit();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (key === "\u001b[A") {
|
|
111
|
+
cursor = cursor === 0 ? MENU_CHOICES.length - 1 : cursor - 1;
|
|
112
|
+
render();
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (key === "\u001b[B") {
|
|
116
|
+
cursor = cursor === MENU_CHOICES.length - 1 ? 0 : cursor + 1;
|
|
117
|
+
render();
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
process.stdout.write("\x1b[?25l");
|
|
122
|
+
render();
|
|
123
|
+
stdin.resume();
|
|
124
|
+
stdin.setEncoding("utf8");
|
|
125
|
+
stdin.setRawMode(true);
|
|
126
|
+
stdin.on("data", onData);
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
35
130
|
async function runToolsMenu() {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
{
|
|
40
|
-
type: "select",
|
|
41
|
-
name: "action",
|
|
42
|
-
message: "请选择操作:",
|
|
43
|
-
hint: "↑↓ 选择,回车确认",
|
|
44
|
-
choices: [
|
|
45
|
-
{ title: "1. 激活服务", value: "activate" },
|
|
46
|
-
{ title: "2. 测试节点", value: "test" },
|
|
47
|
-
{ title: "3. 恢复备份", value: "restore" },
|
|
48
|
-
{ title: "4. 退出", value: "exit" },
|
|
49
|
-
],
|
|
50
|
-
initial: 0,
|
|
51
|
-
},
|
|
52
|
-
{ onCancel: () => false }
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
switch (response.action) {
|
|
131
|
+
const action = await selectMenuAction();
|
|
132
|
+
|
|
133
|
+
switch (action) {
|
|
56
134
|
case "activate":
|
|
57
135
|
await runActivationWizard();
|
|
58
136
|
break;
|
package/lib/services/newapi.js
CHANGED