skill-base 2.0.4 → 2.0.7

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.
Files changed (46) hide show
  1. package/README.md +177 -115
  2. package/bin/skill-base.js +29 -3
  3. package/package.json +4 -1
  4. package/src/cappy.js +416 -0
  5. package/src/database.js +11 -0
  6. package/src/index.js +125 -25
  7. package/src/middleware/auth.js +96 -32
  8. package/src/routes/auth.js +1 -1
  9. package/src/routes/skills.js +10 -5
  10. package/src/utils/zip.js +15 -4
  11. package/static/android-chrome-192x192.png +0 -0
  12. package/static/android-chrome-512x512.png +0 -0
  13. package/static/apple-touch-icon.png +0 -0
  14. package/static/assets/index-BkwByEEp.css +1 -0
  15. package/static/assets/index-CB4Diul3.js +209 -0
  16. package/static/favicon-16x16.png +0 -0
  17. package/static/favicon-32x32.png +0 -0
  18. package/static/favicon.ico +0 -0
  19. package/static/favicon.svg +14 -0
  20. package/static/index.html +18 -248
  21. package/static/site.webmanifest +1 -0
  22. package/static/admin/users.html +0 -593
  23. package/static/cli-code.html +0 -203
  24. package/static/css/.gitkeep +0 -0
  25. package/static/css/style.css +0 -1567
  26. package/static/diff.html +0 -466
  27. package/static/file.html +0 -443
  28. package/static/js/.gitkeep +0 -0
  29. package/static/js/admin/users.js +0 -346
  30. package/static/js/app.js +0 -508
  31. package/static/js/auth.js +0 -151
  32. package/static/js/cli-code.js +0 -184
  33. package/static/js/collaborators.js +0 -283
  34. package/static/js/diff.js +0 -540
  35. package/static/js/file.js +0 -619
  36. package/static/js/i18n.js +0 -739
  37. package/static/js/index.js +0 -168
  38. package/static/js/publish.js +0 -718
  39. package/static/js/settings.js +0 -124
  40. package/static/js/setup.js +0 -157
  41. package/static/js/skill.js +0 -808
  42. package/static/login.html +0 -82
  43. package/static/publish.html +0 -459
  44. package/static/settings.html +0 -163
  45. package/static/setup.html +0 -101
  46. package/static/skill.html +0 -851
@@ -1,346 +0,0 @@
1
- /**
2
- * Skill Base - 用户管理页面 JavaScript
3
- */
4
-
5
- // 全局状态
6
- let currentPage = 1;
7
- let currentLimit = 20;
8
- let currentQuery = '';
9
- let currentStatusFilter = '';
10
- let currentEditUserId = null;
11
-
12
- // 页面初始化
13
- document.addEventListener('DOMContentLoaded', async () => {
14
- const user = await getCurrentUser();
15
- if (!user) return; // checkAuth 会自动跳转
16
-
17
- // 检查管理员权限
18
- if (user.role !== 'admin') {
19
- window.location.href = '/';
20
- return;
21
- }
22
-
23
- renderNavbar(user);
24
- loadUsers();
25
- bindEvents();
26
- });
27
-
28
- // 绑定事件
29
- function bindEvents() {
30
- // 搜索输入(防抖)
31
- const searchInput = document.getElementById('searchInput');
32
- const debouncedSearch = debounce(() => {
33
- currentQuery = searchInput.value.trim();
34
- currentPage = 1;
35
- loadUsers();
36
- }, 300);
37
- searchInput.addEventListener('input', debouncedSearch);
38
-
39
- // 清除搜索
40
- const clearSearch = document.getElementById('clearSearch');
41
- clearSearch.addEventListener('click', () => {
42
- searchInput.value = '';
43
- currentQuery = '';
44
- currentPage = 1;
45
- loadUsers();
46
- });
47
-
48
- // 状态筛选
49
- const statusFilter = document.getElementById('statusFilter');
50
- statusFilter.addEventListener('change', () => {
51
- currentStatusFilter = statusFilter.value;
52
- currentPage = 1;
53
- loadUsers();
54
- });
55
-
56
- // 分页按钮
57
- document.getElementById('prevPage').addEventListener('click', () => {
58
- if (currentPage > 1) {
59
- currentPage--;
60
- loadUsers();
61
- }
62
- });
63
-
64
- document.getElementById('nextPage').addEventListener('click', () => {
65
- currentPage++;
66
- loadUsers();
67
- });
68
-
69
- // 编辑用户状态切换
70
- const editStatus = document.getElementById('editStatus');
71
- editStatus.addEventListener('change', () => {
72
- document.getElementById('editStatusLabel').textContent = editStatus.checked ? t('admin.active') : t('admin.disabled');
73
- });
74
-
75
- // 重置密码按钮
76
- document.getElementById('resetPasswordBtn').addEventListener('click', () => {
77
- const userId = document.getElementById('editUserId').value;
78
- const username = document.getElementById('editUsername').value;
79
- closeModal();
80
- showResetPasswordModal(userId, username);
81
- });
82
-
83
- // 点击弹窗遮罩关闭
84
- document.querySelectorAll('.modal-overlay').forEach(overlay => {
85
- overlay.addEventListener('click', (e) => {
86
- if (e.target === overlay) {
87
- closeModal();
88
- }
89
- });
90
- });
91
-
92
- // ESC 键关闭弹窗
93
- document.addEventListener('keydown', (e) => {
94
- if (e.key === 'Escape') {
95
- closeModal();
96
- }
97
- });
98
- }
99
-
100
- // 加载用户列表
101
- async function loadUsers() {
102
- const tbody = document.getElementById('usersTableBody');
103
-
104
- // 显示 loading
105
- tbody.innerHTML = `
106
- <tr>
107
- <td colspan="5">
108
- <div class="table-loading">
109
- <div class="spinner"></div>
110
- </div>
111
- </td>
112
- </tr>
113
- `;
114
-
115
- try {
116
- // 构建查询参数
117
- const params = new URLSearchParams();
118
- if (currentQuery) params.append('q', currentQuery);
119
- if (currentStatusFilter) params.append('status', currentStatusFilter);
120
- params.append('page', currentPage);
121
- params.append('limit', currentLimit);
122
-
123
- const data = await apiGet(`/users?${params.toString()}`);
124
-
125
- if (data.users && data.users.length > 0) {
126
- renderUsersTable(data.users);
127
- renderPagination(data.total, data.page, data.limit);
128
- } else {
129
- tbody.innerHTML = `
130
- <tr>
131
- <td colspan="5">
132
- <div class="empty-state">
133
- <div class="empty-state-icon">👤</div>
134
- <div class="empty-state-text">${t('admin.noUsers')}</div>
135
- </div>
136
- </td>
137
- </tr>
138
- `;
139
- document.getElementById('pagination').classList.add('hidden');
140
- }
141
- } catch (error) {
142
- showToast(error.message || t('admin.loadUsersFailed'), 'error');
143
- tbody.innerHTML = `
144
- <tr>
145
- <td colspan="5">
146
- <div class="empty-state">
147
- <div class="empty-state-icon">❌</div>
148
- <div class="empty-state-text">${t('admin.loadFailed')}</div>
149
- </div>
150
- </td>
151
- </tr>
152
- `;
153
- document.getElementById('pagination').classList.add('hidden');
154
- }
155
- }
156
-
157
- // 渲染用户表格
158
- function renderUsersTable(users) {
159
- const tbody = document.getElementById('usersTableBody');
160
-
161
- tbody.innerHTML = users.map(user => {
162
- const roleLabel = user.role === 'admin' ? t('admin.roleAdmin') : t('admin.roleDeveloper');
163
- const roleClass = user.role === 'admin' ? 'admin' : 'developer';
164
- const statusLabel = user.status === 'active' ? t('admin.statusActive') : t('admin.statusDisabled');
165
- const statusClass = user.status === 'active' ? 'active' : 'disabled';
166
- const nameDisplay = user.name ? escapeHtml(user.name) : '<span style="color: var(--text-muted);">-</span>';
167
-
168
- return `
169
- <tr>
170
- <td><strong>${escapeHtml(user.username)}</strong></td>
171
- <td>${nameDisplay}</td>
172
- <td><span class="role-badge ${roleClass}">${roleLabel}</span></td>
173
- <td><span class="status-badge ${statusClass}">${statusLabel}</span></td>
174
- <td>${formatDate(user.created_at)}</td>
175
- <td>
176
- <button class="btn btn-secondary btn-sm" onclick="showEditUserModal(${user.id})">
177
- ${t('admin.editBtn')}
178
- </button>
179
- </td>
180
- </tr>
181
- `;
182
- }).join('');
183
- }
184
-
185
- // 渲染分页
186
- function renderPagination(total, page, limit) {
187
- const pagination = document.getElementById('pagination');
188
- const pageInfo = document.getElementById('pageInfo');
189
- const prevBtn = document.getElementById('prevPage');
190
- const nextBtn = document.getElementById('nextPage');
191
-
192
- const totalPages = Math.ceil(total / limit);
193
-
194
- if (totalPages <= 1) {
195
- pagination.classList.add('hidden');
196
- return;
197
- }
198
-
199
- pagination.classList.remove('hidden');
200
- pageInfo.textContent = `${page} / ${totalPages}`;
201
-
202
- prevBtn.disabled = page <= 1;
203
- nextBtn.disabled = page >= totalPages;
204
- }
205
-
206
- // 显示添加用户弹窗
207
- function showAddUserModal() {
208
- // 重置表单
209
- document.getElementById('addUsername').value = '';
210
- document.getElementById('addPassword').value = '';
211
- document.querySelector('input[name="addRole"][value="developer"]').checked = true;
212
-
213
- document.getElementById('addUserModal').classList.add('visible');
214
- document.getElementById('addUsername').focus();
215
- }
216
-
217
- // 提交添加用户
218
- async function submitAddUser() {
219
- const username = document.getElementById('addUsername').value.trim();
220
- const password = document.getElementById('addPassword').value;
221
- const role = document.querySelector('input[name="addRole"]:checked').value;
222
-
223
- if (!username) {
224
- showToast(t('admin.enterUsername'), 'warning');
225
- return;
226
- }
227
-
228
- if (!password) {
229
- showToast(t('admin.enterPassword'), 'warning');
230
- return;
231
- }
232
-
233
- const submitBtn = document.getElementById('submitAddUser');
234
- submitBtn.disabled = true;
235
- submitBtn.innerHTML = `<div class="spinner spinner-sm"></div> ${t('admin.adding')}`;
236
-
237
- try {
238
- await apiPost('/users', { username, password, role });
239
- showToast(t('admin.addSuccess'), 'success');
240
- closeModal();
241
- loadUsers();
242
- } catch (error) {
243
- showToast(error.message || t('admin.addFailed'), 'error');
244
- } finally {
245
- submitBtn.disabled = false;
246
- submitBtn.textContent = t('btn.add');
247
- }
248
- }
249
-
250
- // 显示编辑用户弹窗
251
- async function showEditUserModal(userId) {
252
- currentEditUserId = userId;
253
-
254
- try {
255
- const user = await apiGet(`/users/${userId}`);
256
-
257
- document.getElementById('editUserId').value = user.id;
258
- document.getElementById('editUsername').value = user.username;
259
- document.getElementById('editName').value = user.name || '';
260
-
261
- // 设置角色
262
- const roleRadio = document.querySelector(`input[name="editRole"][value="${user.role}"]`);
263
- if (roleRadio) roleRadio.checked = true;
264
-
265
- // 设置状态
266
- const isActive = user.status === 'active';
267
- document.getElementById('editStatus').checked = isActive;
268
- document.getElementById('editStatusLabel').textContent = isActive ? t('admin.active') : t('admin.disabled');
269
-
270
- document.getElementById('editUserModal').classList.add('visible');
271
- } catch (error) {
272
- showToast(error.message || t('admin.getUserFailed'), 'error');
273
- }
274
- }
275
-
276
- // 提交编辑用户
277
- async function submitEditUser() {
278
- const userId = document.getElementById('editUserId').value;
279
- const role = document.querySelector('input[name="editRole"]:checked').value;
280
- const status = document.getElementById('editStatus').checked ? 'active' : 'disabled';
281
- const name = document.getElementById('editName').value.trim();
282
-
283
- const submitBtn = document.getElementById('submitEditUser');
284
- submitBtn.disabled = true;
285
- submitBtn.innerHTML = `<div class="spinner spinner-sm"></div> ${t('admin.saving')}`;
286
-
287
- try {
288
- // 使用 api 函数发送 PATCH 请求
289
- await api(`/users/${userId}`, {
290
- method: 'PATCH',
291
- body: JSON.stringify({ role, status, name })
292
- });
293
- showToast(t('admin.saveSuccess'), 'success');
294
- closeModal();
295
- loadUsers();
296
- } catch (error) {
297
- showToast(error.message || t('admin.saveFailed'), 'error');
298
- } finally {
299
- submitBtn.disabled = false;
300
- submitBtn.textContent = t('btn.save');
301
- }
302
- }
303
-
304
- // 显示重置密码弹窗
305
- function showResetPasswordModal(userId, username) {
306
- document.getElementById('resetPasswordUserId').value = userId;
307
- document.getElementById('resetPasswordUsername').textContent = username;
308
- document.getElementById('newPassword').value = '';
309
-
310
- document.getElementById('resetPasswordModal').classList.add('visible');
311
- document.getElementById('newPassword').focus();
312
- }
313
-
314
- // 提交重置密码
315
- async function submitResetPassword() {
316
- const userId = document.getElementById('resetPasswordUserId').value;
317
- const newPassword = document.getElementById('newPassword').value;
318
-
319
- if (!newPassword) {
320
- showToast(t('admin.enterNewPassword'), 'warning');
321
- return;
322
- }
323
-
324
- const submitBtn = document.getElementById('submitResetPassword');
325
- submitBtn.disabled = true;
326
- submitBtn.innerHTML = `<div class="spinner spinner-sm"></div> ${t('admin.resetting')}`;
327
-
328
- try {
329
- await apiPost(`/users/${userId}/reset-password`, { new_password: newPassword });
330
- showToast(t('admin.resetSuccess'), 'success');
331
- closeModal();
332
- } catch (error) {
333
- showToast(error.message || t('admin.resetFailed'), 'error');
334
- } finally {
335
- submitBtn.disabled = false;
336
- submitBtn.textContent = t('btn.reset');
337
- }
338
- }
339
-
340
- // 关闭所有弹窗
341
- function closeModal() {
342
- document.querySelectorAll('.modal-overlay').forEach(modal => {
343
- modal.classList.remove('visible');
344
- });
345
- currentEditUserId = null;
346
- }