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';