- 前端 React+TypeScript (源码+构建产物) - 后端 Python FastAPI - 数据库迁移 001-015 - Docker部署配置 - 微信ClawBot API频道绑定 - 项目文档 README.md
232 lines
7.4 KiB
Python
232 lines
7.4 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from sqlalchemy.orm import Session
|
|
from sqlalchemy import and_
|
|
from pydantic import BaseModel, Field
|
|
from typing import Optional
|
|
import uuid
|
|
from datetime import datetime
|
|
|
|
from ..database import get_db
|
|
from ..models import (
|
|
User, LoginLog, RegisterSetting,
|
|
gen_uuid
|
|
)
|
|
from ..auth import get_current_user, hash_password, verify_password, create_access_token
|
|
from ..middleware.log_service import LogService
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class LoginRequest(BaseModel):
|
|
username: str
|
|
password: str
|
|
|
|
|
|
class RegisterRequest(BaseModel):
|
|
username: str = Field(min_length=3, max_length=50)
|
|
password: str = Field(min_length=6, max_length=100)
|
|
email: Optional[str] = None
|
|
phone: Optional[str] = None
|
|
|
|
|
|
@router.post("/login")
|
|
def login(req: LoginRequest, db: Session = Depends(get_db)):
|
|
import logging
|
|
logger = logging.getLogger(__name__)
|
|
|
|
user = db.query(User).filter(User.username == req.username).first()
|
|
if not user:
|
|
logger.warning(f"登录失败: 用户 '{req.username}' 不存在")
|
|
raise HTTPException(status_code=401, detail="用户名不存在")
|
|
|
|
stored_hash = user.password_hash
|
|
logger.info(f"登录调试: username={req.username} hash_prefix={stored_hash[:7] if stored_hash else 'None'} hash_len={len(stored_hash) if stored_hash else 0}")
|
|
|
|
if not verify_password(req.password, user.password_hash):
|
|
logger.warning(f"登录失败: 用户 '{req.username}' 密码验证失败 hash_prefix={stored_hash[:7] if stored_hash else 'None'} hash_len={len(stored_hash) if stored_hash else 0}")
|
|
raise HTTPException(status_code=401, detail="密码错误")
|
|
if user.status == "banned":
|
|
raise HTTPException(status_code=403, detail="账号已被封禁")
|
|
|
|
token_data = {"sub": user.id, "username": user.username, "role": user.role}
|
|
access_token = create_access_token(token_data)
|
|
|
|
# 记录登录日志
|
|
login_log = LoginLog(
|
|
id=gen_uuid(),
|
|
user_id=user.id,
|
|
ip="",
|
|
login_at=datetime.now(),
|
|
)
|
|
db.add(login_log)
|
|
|
|
# 记录操作日志
|
|
LogService.log_operation(
|
|
user_id=user.id,
|
|
username=user.username,
|
|
action="login",
|
|
target="user",
|
|
detail=f"用户 {user.username} 登录成功",
|
|
)
|
|
|
|
db.commit()
|
|
|
|
return {
|
|
"code": 0,
|
|
"data": {
|
|
"access_token": access_token,
|
|
"token_type": "bearer",
|
|
"user": {
|
|
"id": user.id,
|
|
"username": user.username,
|
|
"email": user.email,
|
|
"phone": user.phone,
|
|
"avatar": user.avatar,
|
|
"role": user.role,
|
|
"status": user.status,
|
|
"membership_level": user.membership_level,
|
|
"membership_expire_at": str(user.membership_expire_at) if user.membership_expire_at else None,
|
|
"created_at": str(user.created_at) if user.created_at else None,
|
|
},
|
|
},
|
|
"message": "登录成功",
|
|
}
|
|
|
|
|
|
@router.post("/register")
|
|
def register(req: RegisterRequest, db: Session = Depends(get_db)):
|
|
# 检查注册设置
|
|
reg_setting = db.query(RegisterSetting).first()
|
|
if reg_setting and reg_setting.phone_required and not req.phone:
|
|
raise HTTPException(status_code=400, detail="手机号为必填项")
|
|
if reg_setting and reg_setting.email_required and not req.email:
|
|
raise HTTPException(status_code=400, detail="邮箱为必填项")
|
|
|
|
# 检查用户名是否已存在
|
|
existing = db.query(User).filter(User.username == req.username).first()
|
|
if existing:
|
|
raise HTTPException(status_code=400, detail="用户名已存在")
|
|
|
|
if req.email:
|
|
existing_email = db.query(User).filter(User.email == req.email).first()
|
|
if existing_email:
|
|
raise HTTPException(status_code=400, detail="邮箱已被注册")
|
|
|
|
if req.phone:
|
|
existing_phone = db.query(User).filter(User.phone == req.phone).first()
|
|
if existing_phone:
|
|
raise HTTPException(status_code=400, detail="手机号已被注册")
|
|
|
|
user = User(
|
|
id=gen_uuid(),
|
|
username=req.username,
|
|
password_hash=hash_password(req.password),
|
|
email=req.email,
|
|
phone=req.phone,
|
|
avatar=None,
|
|
role="user",
|
|
status="active",
|
|
real_name_status="unverified",
|
|
membership_level=0,
|
|
created_at=datetime.now(),
|
|
updated_at=datetime.now(),
|
|
)
|
|
db.add(user)
|
|
db.flush()
|
|
|
|
# 创建Token余额记录
|
|
from ..models import TokenBalance
|
|
balance = TokenBalance(
|
|
id=gen_uuid(),
|
|
user_id=user.id,
|
|
total_consumed=0,
|
|
balance=0,
|
|
daily_free_used=0,
|
|
)
|
|
db.add(balance)
|
|
|
|
token_data = {"sub": user.id, "username": user.username, "role": user.role}
|
|
access_token = create_access_token(token_data)
|
|
|
|
LogService.log_operation(
|
|
user_id=user.id,
|
|
username=user.username,
|
|
action="register",
|
|
target="user",
|
|
detail=f"新用户 {user.username} 注册成功",
|
|
)
|
|
|
|
db.commit()
|
|
|
|
return {
|
|
"code": 0,
|
|
"data": {
|
|
"access_token": access_token,
|
|
"token_type": "bearer",
|
|
"user": {
|
|
"id": user.id,
|
|
"username": user.username,
|
|
"email": user.email or "",
|
|
"phone": user.phone or "",
|
|
"avatar": user.avatar or "",
|
|
"role": user.role,
|
|
"status": user.status,
|
|
"membership_level": user.membership_level,
|
|
"created_at": str(user.created_at) if user.created_at else None,
|
|
},
|
|
},
|
|
"message": "注册成功",
|
|
}
|
|
|
|
|
|
@router.get("/me")
|
|
def get_me(current_user: User = Depends(get_current_user)):
|
|
return {
|
|
"code": 0,
|
|
"data": {
|
|
"id": current_user.id,
|
|
"username": current_user.username,
|
|
"email": current_user.email or "",
|
|
"phone": current_user.phone or "",
|
|
"avatar": current_user.avatar or "",
|
|
"role": current_user.role,
|
|
"status": current_user.status,
|
|
"real_name_status": current_user.real_name_status,
|
|
"membership_level": current_user.membership_level,
|
|
"membership_expire_at": str(current_user.membership_expire_at) if current_user.membership_expire_at else None,
|
|
"auto_renew": current_user.auto_renew,
|
|
"created_at": str(current_user.created_at) if current_user.created_at else None,
|
|
},
|
|
"message": "ok",
|
|
}
|
|
|
|
|
|
@router.post("/logout")
|
|
def logout(current_user: User = Depends(get_current_user)):
|
|
LogService.log_operation(
|
|
user_id=current_user.id,
|
|
username=current_user.username,
|
|
action="logout",
|
|
target="user",
|
|
detail=f"用户 {current_user.username} 登出",
|
|
)
|
|
return {"code": 0, "message": "登出成功"}
|
|
|
|
|
|
@router.get("/settings")
|
|
def get_settings(db: Session = Depends(get_db)):
|
|
settings = db.query(RegisterSetting).first()
|
|
if not settings:
|
|
return {"code": 0, "data": {}, "message": "ok"}
|
|
return {
|
|
"code": 0,
|
|
"data": {
|
|
"phone_required": settings.phone_required,
|
|
"phone_sms_required": settings.phone_sms_required,
|
|
"email_required": settings.email_required,
|
|
"email_verify_required": settings.email_verify_required,
|
|
"real_name_required": settings.real_name_required,
|
|
},
|
|
"message": "ok",
|
|
}
|