mirror of
https://github.com/zhinianboke/xianyu-auto-reply.git
synced 2025-08-02 04:27:36 +08:00
修复bug
This commit is contained in:
parent
cea4e04bf0
commit
cbd3ee64c0
41
README.md
41
README.md
@ -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` 包含详细的系统配置,支持:
|
||||
|
@ -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用户
|
||||
cursor.execute('''
|
||||
INSERT OR IGNORE INTO users (username, email, password_hash) VALUES
|
||||
('admin', 'admin@localhost', ?)
|
||||
''', (hashlib.sha256("admin123".encode()).hexdigest(),))
|
||||
# 创建默认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 INTO users (username, email, password_hash) VALUES
|
||||
('admin', 'admin@localhost', ?)
|
||||
''', (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))
|
||||
|
103
reply_server.py
103
reply_server.py
@ -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,7 +455,11 @@ async def login(request: LoginRequest):
|
||||
'timestamp': time.time()
|
||||
}
|
||||
|
||||
logger.info(f"【{user['username']}#{user['id']}】登录成功")
|
||||
# 区分管理员和普通用户的日志
|
||||
if user['username'] == ADMIN_USERNAME:
|
||||
logger.info(f"【{user['username']}#{user['id']}】登录成功(管理员)")
|
||||
else:
|
||||
logger.info(f"【{user['username']}#{user['id']}】登录成功")
|
||||
|
||||
return LoginResponse(
|
||||
success=True,
|
||||
@ -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}')
|
||||
|
@ -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,13 +5810,18 @@
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
showToast('密码更新成功,请重新登录', 'success');
|
||||
passwordForm.reset();
|
||||
// 3秒后跳转到登录页面
|
||||
setTimeout(() => {
|
||||
localStorage.removeItem('auth_token');
|
||||
window.location.href = '/login.html';
|
||||
}, 3000);
|
||||
const result = await response.json();
|
||||
if (result.success) {
|
||||
showToast('密码更新成功,请重新登录', 'success');
|
||||
passwordForm.reset();
|
||||
// 3秒后跳转到登录页面
|
||||
setTimeout(() => {
|
||||
localStorage.removeItem('auth_token');
|
||||
window.location.href = '/login.html';
|
||||
}, 3000);
|
||||
} else {
|
||||
showToast(`密码更新失败: ${result.message}`, 'danger');
|
||||
}
|
||||
} else {
|
||||
const error = await response.text();
|
||||
showToast(`密码更新失败: ${error}`, 'danger');
|
||||
|
Loading…
x
Reference in New Issue
Block a user