update pwd change
This commit is contained in:
parent
b91daad8ad
commit
47b6f4903f
13 changed files with 572 additions and 90 deletions
|
|
@ -2,13 +2,12 @@
|
|||
|
||||
import { state } from './state.js';
|
||||
import { api } from './api.js';
|
||||
import { initTheme } from './theme.js';
|
||||
import { theme, toast, activateNav } from './common.js';
|
||||
import { notification } from './notifications.js';
|
||||
import { DOMElements, switchView, renderConfigList, addKeyValueInput, addSingleInput, fillForm, showRenderedConfig, updateCaddyStatusView, updateSegmentedControl, updateServiceModeView, updateMultiUpstreamView } from './ui.js';
|
||||
|
||||
const POLLING_INTERVAL = 5000;
|
||||
let caddyStatusInterval;
|
||||
import { initCaddyStatus } from './caddy.js';
|
||||
import { DOMElements, switchView, renderConfigList, addKeyValueInput, addSingleInput, fillForm, showRenderedConfig, updateSegmentedControl, updateServiceModeView, updateMultiUpstreamView } from './ui.js';
|
||||
|
||||
// --- 事件处理与逻辑流 ---
|
||||
function getFormStateAsString() {
|
||||
const formData = new FormData(DOMElements.configForm);
|
||||
const data = {};
|
||||
|
|
@ -29,36 +28,6 @@ async function attemptExitForm() {
|
|||
if (await notification.confirm('您有未保存的更改。确定要放弃吗?')) switchView(DOMElements.configListPanel);
|
||||
} else switchView(DOMElements.configListPanel);
|
||||
}
|
||||
async function checkCaddyStatus() {
|
||||
try {
|
||||
const response = await api.get('/caddy/status');
|
||||
updateCaddyStatusView(response.message === 'Caddy is running' ? 'running' : 'stopped', caddyHandlers);
|
||||
} catch (error) { console.error('Error checking Caddy status:', error); updateCaddyStatusView('error', caddyHandlers); }
|
||||
}
|
||||
async function handleStartCaddy() {
|
||||
try {
|
||||
const result = await api.post('/caddy/run');
|
||||
notification.toast(result.message || '启动命令已发送。', 'success');
|
||||
setTimeout(checkCaddyStatus, 500);
|
||||
} catch (error) { notification.toast(`启动失败: ${error.message}`, 'error'); }
|
||||
}
|
||||
async function handleStopCaddy() {
|
||||
if (!await notification.confirm('您确定要停止 Caddy 实例吗?')) return;
|
||||
try {
|
||||
const result = await api.post('/caddy/stop');
|
||||
notification.toast(result.message || '停止命令已发送。', 'info');
|
||||
setTimeout(checkCaddyStatus, 500);
|
||||
} catch(error) { notification.toast(`操作失败: ${error.message}`, 'error'); }
|
||||
}
|
||||
async function handleReloadCaddy() {
|
||||
if (!await notification.confirm('确定要重载 Caddy 配置吗?')) return;
|
||||
try {
|
||||
const result = await api.post('/caddy/restart');
|
||||
notification.toast(result.message || '重载命令已发送。', 'success');
|
||||
setTimeout(checkCaddyStatus, 500);
|
||||
} catch(error) { notification.toast(`重载失败: ${error.message}`, 'error'); }
|
||||
}
|
||||
const caddyHandlers = { handleStartCaddy, handleStopCaddy, handleReloadCaddy };
|
||||
async function handleLogout() {
|
||||
if (!await notification.confirm('您确定要退出登录吗?')) return;
|
||||
notification.toast('正在退出...', 'info');
|
||||
|
|
@ -81,11 +50,8 @@ async function handleEditConfig(originalFilename) {
|
|||
DOMElements.formTitle.textContent = '编辑配置';
|
||||
fillForm(config, originalFilename);
|
||||
showRenderedConfig(rendered, originalFilename);
|
||||
|
||||
// 关键修正: 在填充表单后, 根据数据更新服务模式的fieldset可见性
|
||||
const mode = config.upstream_config?.enable_upstream ? 'reverse_proxy' : (config.file_server_config?.enable_file_server ? 'file_server' : 'none');
|
||||
updateServiceModeView(mode);
|
||||
|
||||
state.initialFormState = getFormStateAsString();
|
||||
} catch(error) { notification.toast(`加载配置详情失败: ${error.message}`, 'error'); }
|
||||
}
|
||||
|
|
@ -150,12 +116,14 @@ async function handleSaveConfig(e) {
|
|||
} catch(error) { notification.toast(`保存失败: ${error.message}`, 'error'); }
|
||||
}
|
||||
|
||||
// --- 初始化与事件绑定 ---
|
||||
function init() {
|
||||
initTheme(DOMElements.themeToggleInput);
|
||||
theme.init(DOMElements.themeToggleInput);
|
||||
notification.init(DOMElements.toastContainer, DOMElements.dialogContainer);
|
||||
activateNav('configs');
|
||||
initCaddyStatus(); // 初始化通用Caddy状态检查
|
||||
|
||||
loadAllConfigs();
|
||||
checkCaddyStatus();
|
||||
caddyStatusInterval = setInterval(checkCaddyStatus, POLLING_INTERVAL);
|
||||
|
||||
DOMElements.menuToggleBtn.addEventListener('click', () => DOMElements.sidebar.classList.toggle('is-open'));
|
||||
DOMElements.mainContent.addEventListener('click', () => DOMElements.sidebar.classList.remove('is-open'));
|
||||
|
|
|
|||
98
frontend/js/caddy.js
Normal file
98
frontend/js/caddy.js
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
// js/caddy.js - Caddy 实例状态管理与控制
|
||||
|
||||
import { api } from './api.js';
|
||||
import { notification } from './notifications.js';
|
||||
|
||||
let caddyStatusInterval;
|
||||
const POLLING_INTERVAL = 5000;
|
||||
|
||||
const DOMElements = {
|
||||
caddyStatusIndicator: document.getElementById('caddy-status-indicator'),
|
||||
caddyActionButtonContainer: document.getElementById('caddy-action-button-container'),
|
||||
};
|
||||
|
||||
function createButton(text, className, onClick) {
|
||||
const button = document.createElement('button');
|
||||
button.className = `btn ${className}`;
|
||||
button.innerHTML = `<span>${text}</span>`;
|
||||
button.addEventListener('click', onClick);
|
||||
return button;
|
||||
}
|
||||
|
||||
function updateCaddyStatusView(status) {
|
||||
const dot = DOMElements.caddyStatusIndicator.querySelector('.status-dot');
|
||||
const text = DOMElements.caddyStatusIndicator.querySelector('.status-text');
|
||||
const buttonContainer = DOMElements.caddyActionButtonContainer;
|
||||
|
||||
if(!dot || !text || !buttonContainer) return; // 如果元素不存在,则不执行
|
||||
|
||||
dot.className = 'status-dot';
|
||||
buttonContainer.innerHTML = '';
|
||||
let statusText, dotClass;
|
||||
switch (status) {
|
||||
case 'running':
|
||||
statusText = '运行中'; dotClass = 'running';
|
||||
buttonContainer.appendChild(createButton('重载配置', 'btn-warning', handleReloadCaddy));
|
||||
buttonContainer.appendChild(createButton('停止 Caddy', 'btn-danger', handleStopCaddy));
|
||||
break;
|
||||
case 'stopped':
|
||||
statusText = '已停止'; dotClass = 'stopped';
|
||||
buttonContainer.appendChild(createButton('启动 Caddy', 'btn-success', handleStartCaddy));
|
||||
break;
|
||||
case 'checking': statusText = '检查中...'; dotClass = 'checking'; break;
|
||||
default: statusText = '状态未知'; dotClass = 'error'; break;
|
||||
}
|
||||
text.textContent = statusText;
|
||||
dot.classList.add(dotClass);
|
||||
}
|
||||
|
||||
async function checkCaddyStatus() {
|
||||
try {
|
||||
const response = await api.get('/caddy/status');
|
||||
updateCaddyStatusView(response.message === 'Caddy is running' ? 'running' : 'stopped');
|
||||
} catch (error) {
|
||||
console.error('Error checking Caddy status:', error);
|
||||
updateCaddyStatusView('error');
|
||||
}
|
||||
}
|
||||
|
||||
async function handleStartCaddy() {
|
||||
try {
|
||||
const result = await api.post('/caddy/run');
|
||||
notification.toast(result.message || '启动命令已发送。', 'success');
|
||||
setTimeout(checkCaddyStatus, 500);
|
||||
} catch (error) { notification.toast(`启动失败: ${error.message}`, 'error'); }
|
||||
}
|
||||
|
||||
async function handleStopCaddy() {
|
||||
if (!await notification.confirm('您确定要停止 Caddy 实例吗?')) return;
|
||||
try {
|
||||
const result = await api.post('/caddy/stop');
|
||||
notification.toast(result.message || '停止命令已发送。', 'info');
|
||||
setTimeout(checkCaddyStatus, 500);
|
||||
} catch(error) { notification.toast(`操作失败: ${error.message}`, 'error'); }
|
||||
}
|
||||
|
||||
async function handleReloadCaddy() {
|
||||
if (!await notification.confirm('确定要重载 Caddy 配置吗?')) return;
|
||||
try {
|
||||
const result = await api.post('/caddy/restart');
|
||||
notification.toast(result.message || '重载命令已发送。', 'success');
|
||||
setTimeout(checkCaddyStatus, 500);
|
||||
} catch(error) { notification.toast(`重载失败: ${error.message}`, 'error'); }
|
||||
}
|
||||
|
||||
export function initCaddyStatus() {
|
||||
// 确保通知模块已经初始化
|
||||
const dialogContainer = document.getElementById('dialog-container');
|
||||
const toastContainer = document.getElementById('toast-container');
|
||||
if (dialogContainer && toastContainer) {
|
||||
notification.init(toastContainer, dialogContainer);
|
||||
}
|
||||
|
||||
checkCaddyStatus();
|
||||
if (caddyStatusInterval) {
|
||||
clearInterval(caddyStatusInterval);
|
||||
}
|
||||
caddyStatusInterval = setInterval(checkCaddyStatus, POLLING_INTERVAL);
|
||||
}
|
||||
59
frontend/js/common.js
Normal file
59
frontend/js/common.js
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// js/common.js - 存放共享模块
|
||||
|
||||
const theme = {
|
||||
init: (toggleElement) => {
|
||||
const storedTheme = localStorage.getItem('theme');
|
||||
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
const currentTheme = storedTheme || (systemPrefersDark ? 'dark' : 'light');
|
||||
theme.apply(currentTheme);
|
||||
if (toggleElement) {
|
||||
toggleElement.addEventListener('change', (e) => theme.apply(e.target.checked ? 'dark' : 'light'));
|
||||
}
|
||||
},
|
||||
apply: (themeName) => {
|
||||
document.documentElement.dataset.theme = themeName;
|
||||
localStorage.setItem('theme', themeName);
|
||||
const themeToggleInput = document.getElementById('theme-toggle-input');
|
||||
if (themeToggleInput) {
|
||||
themeToggleInput.checked = themeName === 'dark';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function hideToast(toastElement) {
|
||||
if (!toastElement) return;
|
||||
toastElement.classList.remove('show');
|
||||
toastElement.addEventListener('transitionend', () => toastElement.remove(), { once: true });
|
||||
}
|
||||
|
||||
const toast = {
|
||||
show: (message, type = 'info', duration = 3000) => {
|
||||
const toastContainer = document.getElementById('toast-container');
|
||||
if (!toastContainer) return;
|
||||
const icons = { success: 'fa-check-circle', error: 'fa-times-circle', info: 'fa-info-circle' };
|
||||
const iconClass = icons[type] || 'fa-info-circle';
|
||||
const toastElement = document.createElement('div');
|
||||
toastElement.className = `toast ${type}`;
|
||||
toastElement.innerHTML = `<i class="toast-icon fa-solid ${iconClass}"></i><p class="toast-message">${message}</p><button class="toast-close" data-toast-close>×</button>`;
|
||||
toastContainer.appendChild(toastElement);
|
||||
requestAnimationFrame(() => toastElement.classList.add('show'));
|
||||
const timeoutId = setTimeout(() => hideToast(toastElement), duration);
|
||||
toastElement.querySelector('[data-toast-close]').addEventListener('click', () => {
|
||||
clearTimeout(timeoutId);
|
||||
hideToast(toastElement);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function activateNav(pageId) {
|
||||
const navLinks = document.querySelectorAll('.sidebar-nav a');
|
||||
navLinks.forEach(link => {
|
||||
link.classList.remove('active');
|
||||
if (link.dataset.navId === pageId) {
|
||||
link.classList.add('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 导出模块
|
||||
export { theme, toast, activateNav };
|
||||
100
frontend/js/settings.js
Normal file
100
frontend/js/settings.js
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// js/settings.js - 设置页面的逻辑
|
||||
|
||||
import { theme, toast, activateNav } from './common.js';
|
||||
import { initCaddyStatus } from './caddy.js'; // 导入 Caddy 状态模块
|
||||
import { notification } from './notifications.js'; // 导入通知模块
|
||||
|
||||
const RESET_PWD_API_URL = '/v0/api/auth/resetpwd';
|
||||
const LOGOUT_API_URL = '/v0/api/auth/logout';
|
||||
|
||||
const DOMElements = {
|
||||
resetForm: document.getElementById('reset-password-form'),
|
||||
themeToggleInput: document.getElementById('theme-toggle-input'),
|
||||
logoutBtn: document.getElementById('logout-btn'),
|
||||
toastContainer: document.getElementById('toast-container'),
|
||||
dialogContainer: document.getElementById('dialog-container'),
|
||||
};
|
||||
const resetButton = DOMElements.resetForm.querySelector('button[type="submit"]');
|
||||
|
||||
async function handleResetPassword(e) {
|
||||
e.preventDefault();
|
||||
const newPassword = DOMElements.resetForm.new_password.value;
|
||||
const confirmPassword = DOMElements.resetForm.confirm_new_password.value;
|
||||
|
||||
//保证字段均不为空, 用户名 密码 新密码
|
||||
const currentPassword = DOMElements.resetForm.old_password.value;
|
||||
const username = DOMElements.resetForm.username.value;
|
||||
|
||||
if (username === '') {
|
||||
toast.show('用户名不能为空', 'error');
|
||||
DOMElements.resetForm.username.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentPassword === '') {
|
||||
toast.show('当前密码不能为空', 'error');
|
||||
DOMElements.resetForm.old_password.focus();
|
||||
return;
|
||||
}
|
||||
if (newPassword === '') {
|
||||
notification.toast('新密码不能为空', 'error');
|
||||
DOMElements.resetForm.new_password.focus();
|
||||
return;
|
||||
}
|
||||
if (confirmPassword === '') {
|
||||
notification.toast('确认新密码不能为空', 'error');
|
||||
DOMElements.resetForm.confirm_new_password.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword !== confirmPassword) {
|
||||
notification.toast('新密码与确认密码不匹配', 'error');
|
||||
return;
|
||||
}
|
||||
if (newPassword.length < 8) {
|
||||
notification.toast('新密码长度至少为8位', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
resetButton.disabled = true;
|
||||
resetButton.querySelector('span').textContent = '重置中...';
|
||||
|
||||
const formData = new FormData(DOMElements.resetForm);
|
||||
|
||||
try {
|
||||
const response = await fetch(RESET_PWD_API_URL, {
|
||||
method: 'POST',
|
||||
body: new URLSearchParams(formData),
|
||||
});
|
||||
const result = await response.json();
|
||||
if (response.ok) {
|
||||
notification.toast('密码重置成功!请重新登录。', 'success');
|
||||
setTimeout(() => { window.location.href = LOGOUT_API_URL; }, 1500);
|
||||
} else {
|
||||
throw new Error(result.error || '重置密码失败');
|
||||
}
|
||||
} catch (error) {
|
||||
notification.toast(error.message, 'error');
|
||||
resetButton.disabled = false;
|
||||
resetButton.querySelector('span').textContent = '重置密码';
|
||||
}
|
||||
}
|
||||
|
||||
async function handleLogout() {
|
||||
if (await notification.confirm('您确定要退出登录吗?')) {
|
||||
notification.toast('正在退出...', 'info');
|
||||
setTimeout(() => { window.location.href = LOGOUT_API_URL; }, 500);
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
theme.init(DOMElements.themeToggleInput);
|
||||
notification.init(DOMElements.toastContainer, DOMElements.dialogContainer);
|
||||
activateNav('settings');
|
||||
initCaddyStatus(); // 初始化通用Caddy状态检查
|
||||
|
||||
DOMElements.resetForm.addEventListener('submit', handleResetPassword);
|
||||
DOMElements.logoutBtn.addEventListener('click', handleLogout);
|
||||
}
|
||||
|
||||
init();
|
||||
Loading…
Add table
Add a link
Reference in a new issue