修复bug

This commit is contained in:
zhinianboke 2025-07-26 15:16:14 +08:00
parent cea4e04bf0
commit cbd3ee64c0
4 changed files with 127 additions and 101 deletions

View File

@ -195,31 +195,30 @@ xianyu-auto-reply/
## ⚙️ 配置说明
### 环境变量配置
系统支持通过环境变量或 `.env` 文件进行配置:
```bash
# 基础配置
WEB_PORT=8080
ADMIN_USERNAME=admin
ADMIN_PASSWORD=admin123
JWT_SECRET_KEY=your-secret-key
### 管理员密码配置
# 多用户系统
MULTIUSER_ENABLED=true
USER_REGISTRATION_ENABLED=true
EMAIL_VERIFICATION_ENABLED=true
CAPTCHA_ENABLED=true
**重要**:为了系统安全,强烈建议修改默认管理员密码!
# AI回复配置
AI_REPLY_ENABLED=false
DEFAULT_AI_MODEL=qwen-plus
DEFAULT_AI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
#### 默认密码
- **用户名**`admin`
- **默认密码**`admin123`
- **初始化机制**首次创建数据库时自动创建admin用户
# 自动发货配置
AUTO_DELIVERY_ENABLED=true
AUTO_DELIVERY_TIMEOUT=30
```
#### 修改密码方式
**方式一Web界面修改推荐**
1. 使用默认密码登录系统
2. 进入系统设置页面
3. 在"修改密码"区域输入当前密码和新密码
4. 点击"修改密码"按钮完成修改
**密码管理机制**
- 数据库初始化时创建admin用户密码为 `admin123`
- 重启时如果用户表已存在,不重新初始化
- 所有用户包括admin统一使用用户表验证
- 密码修改后立即生效,无需重启
### 全局配置文件
`global_config.yml` 包含详细的系统配置,支持:

View File

@ -298,18 +298,24 @@ class DBManager:
)
''')
# 插入默认系统设置
# 插入默认系统设置不包括管理员密码由reply_server.py初始化
cursor.execute('''
INSERT OR IGNORE INTO system_settings (key, value, description) VALUES
('admin_password_hash', ?, '管理员密码哈希'),
('theme_color', 'blue', '主题颜色')
''', (hashlib.sha256("admin123".encode()).hexdigest(),))
''')
# 创建默认admin用户
# 创建默认admin用户只在首次初始化时创建
cursor.execute('SELECT COUNT(*) FROM users WHERE username = ?', ('admin',))
admin_exists = cursor.fetchone()[0] > 0
if not admin_exists:
# 首次创建admin用户设置默认密码
default_password_hash = hashlib.sha256("admin123".encode()).hexdigest()
cursor.execute('''
INSERT OR IGNORE INTO users (username, email, password_hash) VALUES
INSERT INTO users (username, email, password_hash) VALUES
('admin', 'admin@localhost', ?)
''', (hashlib.sha256("admin123".encode()).hexdigest(),))
''', (default_password_hash,))
logger.info("创建默认admin用户密码: admin123")
# 获取admin用户ID用于历史数据绑定
cursor.execute("SELECT id FROM users WHERE username = 'admin'")
@ -1214,19 +1220,7 @@ class DBManager:
logger.error(f"获取所有系统设置失败: {e}")
return {}
def verify_admin_password(self, password: str) -> bool:
"""验证管理员密码"""
stored_hash = self.get_system_setting('admin_password_hash')
if not stored_hash:
return False
password_hash = hashlib.sha256(password.encode()).hexdigest()
return password_hash == stored_hash
def update_admin_password(self, new_password: str) -> bool:
"""更新管理员密码"""
password_hash = hashlib.sha256(new_password.encode()).hexdigest()
return self.set_system_setting('admin_password_hash', password_hash, '管理员密码哈希')
# 管理员密码现在统一使用用户表管理,不再需要单独的方法
# ==================== 用户管理方法 ====================
@ -1315,6 +1309,31 @@ class DBManager:
password_hash = hashlib.sha256(password.encode()).hexdigest()
return user['password_hash'] == password_hash and user['is_active']
def update_user_password(self, username: str, new_password: str) -> bool:
"""更新用户密码"""
with self.lock:
try:
cursor = self.conn.cursor()
password_hash = hashlib.sha256(new_password.encode()).hexdigest()
cursor.execute('''
UPDATE users SET password_hash = ?, updated_at = CURRENT_TIMESTAMP
WHERE username = ?
''', (password_hash, username))
if cursor.rowcount > 0:
self.conn.commit()
logger.info(f"用户 {username} 密码更新成功")
return True
else:
logger.warning(f"用户 {username} 不存在,密码更新失败")
return False
except Exception as e:
logger.error(f"更新用户密码失败: {e}")
self.conn.rollback()
return False
def generate_verification_code(self) -> str:
"""生成6位数字验证码"""
return ''.join(random.choices(string.digits, k=6))

View File

@ -17,19 +17,22 @@ import cookie_manager
from db_manager import db_manager
from file_log_collector import setup_file_logging, get_file_log_collector
from ai_reply_engine import ai_reply_engine
from loguru import logger
# 关键字文件路径
KEYWORDS_FILE = Path(__file__).parent / "回复关键字.txt"
# 简单的用户认证配置
ADMIN_USERNAME = "admin"
ADMIN_PASSWORD_HASH = hashlib.sha256("admin123".encode()).hexdigest() # 默认密码: admin123
DEFAULT_ADMIN_PASSWORD = "admin123" # 系统初始化时的默认密码
SESSION_TOKENS = {} # 存储会话token: {token: {'user_id': int, 'username': str, 'timestamp': float}}
TOKEN_EXPIRE_TIME = 24 * 60 * 60 # token过期时间24小时
# HTTP Bearer认证
security = HTTPBearer(auto_error=False)
# 不再需要单独的密码初始化,由数据库初始化时处理
def load_keywords() -> List[Tuple[str, str]]:
"""读取关键字→回复映射表
@ -79,6 +82,11 @@ class LoginResponse(BaseModel):
user_id: Optional[int] = None
class ChangePasswordRequest(BaseModel):
current_password: str
new_password: str
class RegisterRequest(BaseModel):
username: str
email: str
@ -147,6 +155,19 @@ def verify_token(credentials: Optional[HTTPAuthorizationCredentials] = Depends(s
return token_data
def verify_admin_token(credentials: Optional[HTTPAuthorizationCredentials] = Depends(security)) -> Dict[str, Any]:
"""验证管理员token"""
user_info = verify_token(credentials)
if not user_info:
raise HTTPException(status_code=401, detail="未授权访问")
# 检查是否是管理员
if user_info['username'] != ADMIN_USERNAME:
raise HTTPException(status_code=403, detail="需要管理员权限")
return user_info
def require_auth(user_info: Optional[Dict[str, Any]] = Depends(verify_token)):
"""需要认证的依赖,返回用户信息"""
if not user_info:
@ -422,33 +443,7 @@ async def login(request: LoginRequest):
# 用户名/密码登录
logger.info(f"{request.username}】尝试用户名登录")
# 首先检查是否是admin用户向后兼容
if request.username == ADMIN_USERNAME and db_manager.verify_admin_password(request.password):
# 获取admin用户信息
admin_user = db_manager.get_user_by_username('admin')
if admin_user:
user_id = admin_user['id']
else:
user_id = 1 # 默认admin用户ID
# 生成token
token = generate_token()
SESSION_TOKENS[token] = {
'user_id': user_id,
'username': 'admin',
'timestamp': time.time()
}
logger.info(f"【admin#{user_id}】登录成功(管理员)")
return LoginResponse(
success=True,
token=token,
message="登录成功",
user_id=user_id
)
# 检查普通用户
# 统一使用用户表验证包括admin用户
if db_manager.verify_user_password(request.username, request.password):
user = db_manager.get_user_by_username(request.username)
if user:
@ -460,6 +455,10 @@ async def login(request: LoginRequest):
'timestamp': time.time()
}
# 区分管理员和普通用户的日志
if user['username'] == ADMIN_USERNAME:
logger.info(f"{user['username']}#{user['id']}】登录成功(管理员)")
else:
logger.info(f"{user['username']}#{user['id']}】登录成功")
return LoginResponse(
@ -569,6 +568,30 @@ async def logout(credentials: Optional[HTTPAuthorizationCredentials] = Depends(s
return {"message": "已登出"}
# 修改管理员密码接口
@app.post('/change-admin-password')
async def change_admin_password(request: ChangePasswordRequest, admin_user: Dict[str, Any] = Depends(verify_admin_token)):
from db_manager import db_manager
try:
# 验证当前密码(使用用户表验证)
if not db_manager.verify_user_password('admin', request.current_password):
return {"success": False, "message": "当前密码错误"}
# 更新密码(使用用户表更新)
success = db_manager.update_user_password('admin', request.new_password)
if success:
logger.info(f"【admin#{admin_user['user_id']}】管理员密码修改成功")
return {"success": True, "message": "密码修改成功"}
else:
return {"success": False, "message": "密码修改失败"}
except Exception as e:
logger.error(f"修改管理员密码异常: {e}")
return {"success": False, "message": "系统错误"}
# 生成图形验证码接口
@app.post('/generate-captcha')
async def generate_captcha(request: CaptchaRequest):
@ -829,9 +852,7 @@ class SystemSettingIn(BaseModel):
description: Optional[str] = None
class PasswordUpdateIn(BaseModel):
current_password: str
new_password: str
@app.get("/cookies")
@ -1228,25 +1249,7 @@ def get_system_settings(_: None = Depends(require_auth)):
raise HTTPException(status_code=500, detail=str(e))
@app.put('/system-settings/password')
def update_admin_password(password_data: PasswordUpdateIn, _: None = Depends(require_auth)):
"""更新管理员密码"""
from db_manager import db_manager
try:
# 验证当前密码
if not db_manager.verify_admin_password(password_data.current_password):
raise HTTPException(status_code=400, detail='当前密码错误')
# 更新密码
success = db_manager.update_admin_password(password_data.new_password)
if success:
return {'msg': 'password updated'}
else:
raise HTTPException(status_code=400, detail='密码更新失败')
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
@app.put('/system-settings/{key}')

View File

@ -5797,8 +5797,8 @@
}
try {
const response = await fetch(`${apiBase}/system-settings/password`, {
method: 'PUT',
const response = await fetch(`${apiBase}/change-admin-password`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${authToken}`,
'Content-Type': 'application/json'
@ -5810,6 +5810,8 @@
});
if (response.ok) {
const result = await response.json();
if (result.success) {
showToast('密码更新成功,请重新登录', 'success');
passwordForm.reset();
// 3秒后跳转到登录页面
@ -5817,6 +5819,9 @@
localStorage.removeItem('auth_token');
window.location.href = '/login.html';
}, 3000);
} else {
showToast(`密码更新失败: ${result.message}`, 'danger');
}
} else {
const error = await response.text();
showToast(`密码更新失败: ${error}`, 'danger');