diff --git a/hubcmdui/config.json b/hubcmdui/config.json
index ba7308d..5b0f17c 100644
--- a/hubcmdui/config.json
+++ b/hubcmdui/config.json
@@ -8,8 +8,8 @@
},
{
"text": "GitHub",
- "link": "https://github.com/dqzboy/Docker-Proxy",
- "newTab": true
+ "link": "",
+ "newTab": false
}
],
"adImages": [
diff --git a/hubcmdui/server.js b/hubcmdui/server.js
index 268caf2..430d29b 100644
--- a/hubcmdui/server.js
+++ b/hubcmdui/server.js
@@ -156,14 +156,14 @@ app.get('/api/config', async (req, res) => {
// API 端点:保存配置
app.post('/api/config', requireLogin, async (req, res) => {
- try {
- const currentConfig = await readConfig();
- const newConfig = { ...currentConfig, ...req.body };
- await writeConfig(newConfig);
- res.json({ success: true });
- } catch (error) {
- res.status(500).json({ error: 'Failed to save config' });
- }
+ try {
+ const currentConfig = await readConfig();
+ const newConfig = { ...currentConfig, ...req.body };
+ await writeConfig(newConfig);
+ res.json({ success: true });
+ } catch (error) {
+ res.status(500).json({ error: 'Failed to save config' });
+ }
});
// API 端点:检查会话状态
diff --git a/hubcmdui/web/admin.html b/hubcmdui/web/admin.html
index 084a205..07e74ca 100644
--- a/hubcmdui/web/admin.html
+++ b/hubcmdui/web/admin.html
@@ -132,12 +132,14 @@
background-color: rgba(0,0,0,0.4);
}
.login-content {
- background-color: #fefefe;
+ background-color: rgba(255, 255, 255, 0.8);
+ backdrop-filter: blur(10px);
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 30%;
border-radius: 8px;
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
.user-management {
position: absolute;
@@ -213,7 +215,7 @@
- 密码必须包含至少一个字母和一个数字,长度在8到16个字符之间
+ 密码必须包含至少一个字母、一个数字和一个特殊字符,长度在8到16个字符之间
@@ -248,6 +250,44 @@
return menuItems;
}
+ function setupDeleteButtons() {
+ const deleteButtons = document.querySelectorAll('.delete-btn');
+ deleteButtons.forEach((button, index) => {
+ button.addEventListener('click', () => {
+ const row = button.closest('tr');
+ const index = row.getAttribute('data-index');
+ console.log(`Deleting menu item at index: ${index}`); // 添加日志输出
+ deleteMenuItem(index);
+ });
+ });
+ }
+
+ function renderMenuItems() {
+ const tbody = document.getElementById('menuTableBody');
+ tbody.innerHTML = '';
+ menuItems.forEach((item, index) => {
+ const row = `
+
+ |
+ |
+
+
+ |
+
+
+
+ |
+
+ `;
+ tbody.innerHTML += row;
+ });
+ setupEditButtons();
+ setupDeleteButtons();
+ }
+
function setMenuItems(items) {
menuItems = items;
renderMenuItems();
@@ -284,45 +324,8 @@
});
});
}
-
- function renderMenuItems() {
- const tbody = document.getElementById('menuTableBody');
- tbody.innerHTML = '';
- menuItems.forEach((item, index) => {
- const row = `
-
- |
- |
-
-
- |
-
-
-
- |
-
- `;
- tbody.innerHTML += row;
- });
- setupEditButtons();
- setupDeleteButtons();
- }
- function setupDeleteButtons() {
- const deleteButtons = document.querySelectorAll('.delete-btn');
- deleteButtons.forEach((button, index) => {
- button.addEventListener('click', () => {
- const row = button.closest('tr');
- const index = row.getAttribute('data-index');
- deleteMenuItem(index);
- });
- });
- }
-
function showNewMenuItemRow() {
const tbody = document.getElementById('menuTableBody');
const newRow = `
@@ -388,45 +391,6 @@
tbody.insertAdjacentHTML('beforeend', newRow);
}
- function renderAdItems() {
- const tbody = document.getElementById('adTableBody');
- tbody.innerHTML = '';
- adImages.forEach((ad, index) => {
- const row = `
-
- |
- |
-
-
-
- |
-
- `;
- tbody.innerHTML += row;
- });
- setupAdEditButtons();
- setupAdDeleteButtons();
- }
-
-
- function saveNewAd() {
- const url = document.getElementById('newAdUrl').value || '';
- const link = document.getElementById('newAdLink').value || '';
-
- const newAd = { url, link };
- adImages.push(newAd);
- renderAdItems(); // 先更新页面
- saveAd(adImages.length - 1, newAd);
- cancelNewAd();
- }
-
- function cancelNewAd() {
- const newRow = document.getElementById('newAdRow');
- if (newRow) {
- newRow.remove();
- }
- }
-
function renderAdItems() {
const tbody = document.getElementById('adTableBody');
tbody.innerHTML = '';
@@ -482,6 +446,49 @@
deleteAd(index);
});
});
+ }
+
+ function saveNewAd() {
+ const url = document.getElementById('newAdUrl').value || '';
+ const link = document.getElementById('newAdLink').value || '';
+
+ if (!url) {
+ alert('广告URL为必填项');
+ return;
+ }
+
+ const newAd = { url, link };
+ adImages.push(newAd);
+ renderAdItems(); // 先更新页面
+ saveAd(adImages.length - 1, newAd);
+ cancelNewAd();
+ }
+
+ function cancelNewAd() {
+ const newRow = document.getElementById('newAdRow');
+ if (newRow) {
+ newRow.remove();
+ }
+ }
+
+ function renderAdItems() {
+ const tbody = document.getElementById('adTableBody');
+ tbody.innerHTML = '';
+ adImages.forEach((ad, index) => {
+ const row = `
+
+ |
+ |
+
+
+
+ |
+
+ `;
+ tbody.innerHTML += row;
+ });
+ setupAdEditButtons();
+ setupAdDeleteButtons();
}
async function saveLogo() {
@@ -519,9 +526,23 @@
}
async function deleteMenuItem(index) {
- menuItems.splice(index, 1);
- renderMenuItems(); // 先更新页面
- await saveConfig({ menuItems: menuItems });
+ try {
+ const response = await fetch('/api/config', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ menuItems: menuItems.filter((_, i) => i !== parseInt(index)) })
+ });
+ if (response.ok) {
+ menuItems.splice(index, 1);
+ renderMenuItems(); // 先更新页面
+ await loadConfig(); // 重新加载配置
+ } else {
+ alert('删除菜单项失败');
+ }
+ } catch (error) {
+ console.error('删除菜单项失败: ' + error.message);
+ alert('删除菜单项失败: ' + error.message);
+ }
}
async function saveAd(index, ad) {
@@ -531,9 +552,23 @@
}
async function deleteAd(index) {
- adImages.splice(index, 1);
- renderAdItems(); // 先更新页面
- await saveConfig({ adImages: adImages });
+ try {
+ const response = await fetch('/api/config', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ adImages: adImages.filter((_, i) => i !== parseInt(index)) })
+ });
+ if (response.ok) {
+ adImages.splice(index, 1);
+ renderAdItems(); // 先更新页面
+ await loadConfig(); // 重新加载配置
+ } else {
+ alert('删除广告项失败');
+ }
+ } catch (error) {
+ console.error('删除广告项失败: ' + error.message);
+ alert('删除广告项失败: ' + error.message);
+ }
}
async function saveConfig(partialConfig) {
@@ -550,7 +585,7 @@
console.error('保存失败: ' + error.message);
throw error;
}
- }
+ }
async function loadConfig() {
try {
@@ -594,12 +629,14 @@
async function changePassword() {
const currentPassword = document.getElementById('currentPassword').value;
const newPassword = document.getElementById('newPassword').value;
+ const passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,16}$/;
+
if (!currentPassword || !newPassword) {
alert('请填写当前密码和新密码');
return;
}
- if (!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,16}$/.test(newPassword)) {
- alert('密码必须包含至少一个字母和一个数字,长度在8到16个字符之间');
+ if (!passwordRegex.test(newPassword)) {
+ alert('密码必须包含至少一个字母、一个数字和一个特殊字符,长度在8到16个字符之间');
return;
}
try {
@@ -619,9 +656,12 @@
}
function checkPasswordStrength() {
- const newPassword = document.getElementById('newPassword');
+ const newPassword = document.getElementById('newPassword').value;
const passwordHint = document.getElementById('passwordHint');
- if (!/^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,16}$/.test(newPassword.value)) {
+
+ const passwordRegex = /^(?=.*[A-Za-z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!%*#?&]{8,16}$/;
+
+ if (!passwordRegex.test(newPassword)) {
passwordHint.style.display = 'block';
} else {
passwordHint.style.display = 'none';