skill-base 2.0.1
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 +141 -0
- package/bin/skill-base.js +53 -0
- package/data/.gitkeep +0 -0
- package/package.json +36 -0
- package/src/database.js +119 -0
- package/src/index.js +88 -0
- package/src/middleware/.gitkeep +0 -0
- package/src/middleware/admin.js +23 -0
- package/src/middleware/auth.js +96 -0
- package/src/middleware/error.js +23 -0
- package/src/models/.gitkeep +0 -0
- package/src/models/skill.js +57 -0
- package/src/models/user.js +130 -0
- package/src/models/version.js +57 -0
- package/src/routes/.gitkeep +0 -0
- package/src/routes/auth.js +173 -0
- package/src/routes/collaborators.js +260 -0
- package/src/routes/init.js +86 -0
- package/src/routes/publish.js +108 -0
- package/src/routes/skills.js +119 -0
- package/src/routes/users.js +169 -0
- package/src/utils/.gitkeep +0 -0
- package/src/utils/crypto.js +35 -0
- package/src/utils/permission.js +45 -0
- package/src/utils/zip.js +35 -0
- package/static/admin/users.html +593 -0
- package/static/cli-code.html +203 -0
- package/static/css/.gitkeep +0 -0
- package/static/css/style.css +1567 -0
- package/static/diff.html +466 -0
- package/static/file.html +443 -0
- package/static/index.html +251 -0
- package/static/js/.gitkeep +0 -0
- package/static/js/admin/users.js +346 -0
- package/static/js/app.js +508 -0
- package/static/js/auth.js +151 -0
- package/static/js/cli-code.js +184 -0
- package/static/js/collaborators.js +283 -0
- package/static/js/diff.js +540 -0
- package/static/js/file.js +619 -0
- package/static/js/i18n.js +739 -0
- package/static/js/index.js +168 -0
- package/static/js/publish.js +718 -0
- package/static/js/settings.js +124 -0
- package/static/js/setup.js +157 -0
- package/static/js/skill.js +808 -0
- package/static/login.html +82 -0
- package/static/publish.html +459 -0
- package/static/settings.html +163 -0
- package/static/setup.html +101 -0
- package/static/skill.html +851 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 账户设置页面脚本
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// 页面初始化
|
|
6
|
+
document.addEventListener('DOMContentLoaded', async () => {
|
|
7
|
+
const user = await getCurrentUser();
|
|
8
|
+
if (!user) {
|
|
9
|
+
// 未登录,checkAuth 会自动跳转
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
renderNavbar(user);
|
|
14
|
+
loadUserProfile();
|
|
15
|
+
bindEvents();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
// 绑定事件
|
|
19
|
+
function bindEvents() {
|
|
20
|
+
// 基本信息表单提交
|
|
21
|
+
const profileForm = document.getElementById('profileForm');
|
|
22
|
+
profileForm.addEventListener('submit', handleProfileUpdate);
|
|
23
|
+
|
|
24
|
+
// 修改密码表单提交
|
|
25
|
+
const passwordForm = document.getElementById('passwordForm');
|
|
26
|
+
passwordForm.addEventListener('submit', handlePasswordChange);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// 加载用户资料
|
|
30
|
+
async function loadUserProfile() {
|
|
31
|
+
try {
|
|
32
|
+
const user = await getCurrentUser(true); // 强制刷新
|
|
33
|
+
|
|
34
|
+
if (user) {
|
|
35
|
+
document.getElementById('username').value = user.username || '';
|
|
36
|
+
document.getElementById('name').value = user.name || '';
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
showToast(error.message || t('settings.loadFailed'), 'error');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// 处理基本信息更新
|
|
44
|
+
async function handleProfileUpdate(e) {
|
|
45
|
+
e.preventDefault();
|
|
46
|
+
|
|
47
|
+
const username = document.getElementById('username').value.trim();
|
|
48
|
+
const name = document.getElementById('name').value.trim();
|
|
49
|
+
|
|
50
|
+
if (!username || username.length < 1 || username.length > 50) {
|
|
51
|
+
showToast(t('settings.usernameLenError'), 'warning');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const submitBtn = document.getElementById('saveProfileBtn');
|
|
56
|
+
submitBtn.disabled = true;
|
|
57
|
+
submitBtn.innerHTML = '<span class="spinner spinner-sm"></span> ' + t('settings.saving');
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
const result = await api('/auth/me', {
|
|
61
|
+
method: 'PATCH',
|
|
62
|
+
body: JSON.stringify({ username, name })
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
if (result.ok) {
|
|
66
|
+
showToast(t('settings.updateSuccess'), 'success');
|
|
67
|
+
// 刷新当前用户缓存
|
|
68
|
+
await getCurrentUser(true);
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
showToast(error.message || t('settings.updateFailed'), 'error');
|
|
72
|
+
} finally {
|
|
73
|
+
submitBtn.disabled = false;
|
|
74
|
+
submitBtn.textContent = t('settings.saveBtn');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 处理密码修改
|
|
79
|
+
async function handlePasswordChange(e) {
|
|
80
|
+
e.preventDefault();
|
|
81
|
+
|
|
82
|
+
const oldPassword = document.getElementById('oldPassword').value;
|
|
83
|
+
const newPassword = document.getElementById('newPassword').value;
|
|
84
|
+
const confirmPassword = document.getElementById('confirmPassword').value;
|
|
85
|
+
|
|
86
|
+
if (!oldPassword) {
|
|
87
|
+
showToast(t('settings.noOldPassword'), 'warning');
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (newPassword.length < 6) {
|
|
92
|
+
showToast(t('settings.newPassTooShort'), 'warning');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (newPassword !== confirmPassword) {
|
|
97
|
+
showToast(t('settings.passMismatch'), 'warning');
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const submitBtn = document.getElementById('changePasswordBtn');
|
|
102
|
+
submitBtn.disabled = true;
|
|
103
|
+
submitBtn.innerHTML = '<span class="spinner spinner-sm"></span> ' + t('settings.changing');
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const result = await apiPost('/auth/me/change-password', {
|
|
107
|
+
old_password: oldPassword,
|
|
108
|
+
new_password: newPassword
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (result.ok) {
|
|
112
|
+
showToast(t('settings.changeSuccess'), 'success');
|
|
113
|
+
// 清空密码表单
|
|
114
|
+
document.getElementById('oldPassword').value = '';
|
|
115
|
+
document.getElementById('newPassword').value = '';
|
|
116
|
+
document.getElementById('confirmPassword').value = '';
|
|
117
|
+
}
|
|
118
|
+
} catch (error) {
|
|
119
|
+
showToast(error.message || t('settings.changeFailed'), 'error');
|
|
120
|
+
} finally {
|
|
121
|
+
submitBtn.disabled = false;
|
|
122
|
+
submitBtn.textContent = t('settings.changePasswordBtn');
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Base - Initial Setup Page
|
|
3
|
+
* Handles first-time admin account creation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
(function() {
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const API_BASE = '/api/v1';
|
|
10
|
+
|
|
11
|
+
// DOM elements
|
|
12
|
+
const form = document.getElementById('setupForm');
|
|
13
|
+
const errorEl = document.getElementById('setupError');
|
|
14
|
+
const successEl = document.getElementById('setupSuccess');
|
|
15
|
+
const submitBtn = document.getElementById('setupButton');
|
|
16
|
+
const usernameInput = document.getElementById('username');
|
|
17
|
+
const passwordInput = document.getElementById('password');
|
|
18
|
+
const confirmPasswordInput = document.getElementById('confirmPassword');
|
|
19
|
+
|
|
20
|
+
// Check if system is already initialized
|
|
21
|
+
async function checkInitStatus() {
|
|
22
|
+
try {
|
|
23
|
+
const res = await fetch(`${API_BASE}/init/status`);
|
|
24
|
+
const data = await res.json();
|
|
25
|
+
|
|
26
|
+
if (data.initialized) {
|
|
27
|
+
// Already initialized, redirect to home
|
|
28
|
+
window.location.href = '/';
|
|
29
|
+
}
|
|
30
|
+
} catch (err) {
|
|
31
|
+
console.error('Failed to check init status:', err);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Show error message
|
|
36
|
+
function showError(message) {
|
|
37
|
+
errorEl.textContent = message;
|
|
38
|
+
errorEl.style.display = 'block';
|
|
39
|
+
successEl.style.display = 'none';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Show success message
|
|
43
|
+
function showSuccess(message) {
|
|
44
|
+
successEl.textContent = message;
|
|
45
|
+
successEl.style.display = 'block';
|
|
46
|
+
errorEl.style.display = 'none';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Clear messages
|
|
50
|
+
function clearMessages() {
|
|
51
|
+
errorEl.style.display = 'none';
|
|
52
|
+
successEl.style.display = 'none';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Set button loading state
|
|
56
|
+
function setLoading(loading) {
|
|
57
|
+
submitBtn.disabled = loading;
|
|
58
|
+
const btnText = submitBtn.querySelector('span');
|
|
59
|
+
if (loading) {
|
|
60
|
+
btnText.textContent = window._t ? window._t('setup.loading') : '创建中...';
|
|
61
|
+
} else {
|
|
62
|
+
btnText.textContent = window._t ? window._t('setup.submit') : '创建管理员账号';
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Handle form submit
|
|
67
|
+
async function handleSubmit(e) {
|
|
68
|
+
e.preventDefault();
|
|
69
|
+
clearMessages();
|
|
70
|
+
|
|
71
|
+
const username = usernameInput.value.trim();
|
|
72
|
+
const password = passwordInput.value;
|
|
73
|
+
const confirmPassword = confirmPasswordInput.value;
|
|
74
|
+
|
|
75
|
+
// Validate
|
|
76
|
+
if (!username) {
|
|
77
|
+
showError(window._t ? window._t('setup.errUsername') : '请输入用户名');
|
|
78
|
+
usernameInput.focus();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (username.length < 3 || username.length > 50) {
|
|
83
|
+
showError(window._t ? window._t('setup.errUsernameLength') : '用户名需要 3-50 个字符');
|
|
84
|
+
usernameInput.focus();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!password) {
|
|
89
|
+
showError(window._t ? window._t('setup.errPassword') : '请输入密码');
|
|
90
|
+
passwordInput.focus();
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (password.length < 6) {
|
|
95
|
+
showError(window._t ? window._t('setup.errPasswordLength') : '密码至少需要 6 个字符');
|
|
96
|
+
passwordInput.focus();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (password !== confirmPassword) {
|
|
101
|
+
showError(window._t ? window._t('setup.errPasswordMismatch') : '两次输入的密码不一致');
|
|
102
|
+
confirmPasswordInput.focus();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
setLoading(true);
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
const res = await fetch(`${API_BASE}/init/setup`, {
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: { 'Content-Type': 'application/json' },
|
|
112
|
+
body: JSON.stringify({ username, password })
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
const data = await res.json();
|
|
116
|
+
|
|
117
|
+
if (!res.ok) {
|
|
118
|
+
throw new Error(data.error || 'Setup failed');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Success
|
|
122
|
+
showSuccess(window._t ? window._t('setup.success') : '管理员账号创建成功,即将跳转到登录页...');
|
|
123
|
+
form.reset();
|
|
124
|
+
|
|
125
|
+
// Redirect to login after 2 seconds
|
|
126
|
+
setTimeout(() => {
|
|
127
|
+
window.location.href = '/login';
|
|
128
|
+
}, 2000);
|
|
129
|
+
|
|
130
|
+
} catch (err) {
|
|
131
|
+
showError(err.message || (window._t ? window._t('setup.errFailed') : '创建失败,请重试'));
|
|
132
|
+
} finally {
|
|
133
|
+
setLoading(false);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Initialize
|
|
138
|
+
function init() {
|
|
139
|
+
// Apply i18n if available
|
|
140
|
+
if (window.applyI18n) {
|
|
141
|
+
window.applyI18n();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Check init status
|
|
145
|
+
checkInitStatus();
|
|
146
|
+
|
|
147
|
+
// Bind form submit
|
|
148
|
+
form.addEventListener('submit', handleSubmit);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Run on DOM ready
|
|
152
|
+
if (document.readyState === 'loading') {
|
|
153
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
154
|
+
} else {
|
|
155
|
+
init();
|
|
156
|
+
}
|
|
157
|
+
})();
|