feat: 风险分定时衰减 + 密码提示修复 + 浏览器池API + next回跳

1. 风险分衰减定时任务:
   - services/scheduler.py: 每天 CST 04:00 自动执行 decay_scores()
   - 支持 RISK_SCORE_DECAY_TIME_CST 环境变量覆盖

2. 密码长度提示统一为8位:
   - app-frontend/src/pages/RegisterPage.vue
   - app-frontend/src/layouts/AppLayout.vue
   - admin-frontend/src/pages/SettingsPage.vue
   - templates/register.html

3. 浏览器池统计API:
   - GET /yuyx/api/browser_pool/stats
   - 返回 worker 状态、队列等待数等信息
   - browser_pool_worker.py: 增强 get_stats() 方法

4. 登录后支持 next 参数回跳:
   - app-frontend/src/pages/LoginPage.vue: 检查 ?next= 参数
   - 仅允许站内路径(防止开放重定向)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-27 18:28:21 +08:00
parent 3d9dba272e
commit 1b20478a08
49 changed files with 305 additions and 160 deletions

View File

@@ -2044,8 +2044,13 @@
return;
}
if (newPassword.length < 6) {
showNotification('密码至少6个字符', 'error');
if (newPassword.length < 8) {
showNotification('密码长度至少8位', 'error');
return;
}
if (!/[a-zA-Z]/.test(newPassword) || !/\d/.test(newPassword)) {
showNotification('密码必须包含字母和数字', 'error');
return;
}
@@ -2772,11 +2777,16 @@
// 管理员直接重置用户密码
async function resetUserPassword(userId) {
const newPassword = prompt('请输入新密码(至少6位:');
const newPassword = prompt('请输入新密码(至少8位且包含字母和数字:');
if (!newPassword) return;
if (newPassword.length < 6) {
showNotification('密码长度至少6位', 'error');
if (newPassword.length < 8) {
showNotification('密码长度至少8位', 'error');
return;
}
if (!/[a-zA-Z]/.test(newPassword) || !/\d/.test(newPassword)) {
showNotification('密码必须包含字母和数字', 'error');
return;
}

View File

@@ -814,7 +814,7 @@
</div>
<div class="form-group">
<label class="form-label">新密码</label>
<input type="password" class="form-input" id="newPassword" placeholder="请输入新密码(至少6位">
<input type="password" class="form-input" id="newPassword" placeholder="请输入新密码(至少8位且包含字母和数字">
</div>
<div class="form-group">
<label class="form-label">确认新密码</label>
@@ -2190,7 +2190,8 @@
const confirmPwd = document.getElementById('confirmPassword').value;
if (!currentPwd) { showToast('请输入当前密码', 'error'); return; }
if (!newPwd || newPwd.length < 6) { showToast('新密码至少6位', 'error'); return; }
if (!newPwd || newPwd.length < 8) { showToast('新密码至少8位', 'error'); return; }
if (!/[a-zA-Z]/.test(newPwd) || !/\d/.test(newPwd)) { showToast('密码必须包含字母和数字', 'error'); return; }
if (newPwd !== confirmPwd) { showToast('两次密码不一致', 'error'); return; }
fetch('/api/user/password', {

View File

@@ -163,8 +163,8 @@
<div class="form-group">
<label for="password">密码 *</label>
<input type="password" id="password" name="password" required minlength="6">
<small>至少6个字符</small>
<input type="password" id="password" name="password" required minlength="8">
<small>至少8位</small>
</div>
<div class="form-group">
@@ -239,8 +239,14 @@
return;
}
if (password.length < 6) {
errorDiv.textContent = '密码至少6个字符';
if (password.length < 8) {
errorDiv.textContent = '密码长度至少8位';
errorDiv.style.display = 'block';
return;
}
if (!/[a-zA-Z]/.test(password) || !/\d/.test(password)) {
errorDiv.textContent = '密码必须包含字母和数字';
errorDiv.style.display = 'block';
return;
}