zlwu 9 ay önce
ebeveyn
işleme
4950c15c3b

+ 2 - 1
backend/app/api/admin/auth.py

@@ -7,7 +7,8 @@ from pydantic import BaseModel
 from app.database import get_db
 from app.models.admin import Admin
 from app.core.security import verify_password, create_access_token
-from app.core.logger import logger
+from app.core.logger import get_admin_logger
+logger = get_admin_logger()
 from datetime import datetime
 from app.utils.timezone import beijing_now
 

+ 6 - 2
backend/app/api/client/announcement.py

@@ -20,7 +20,8 @@ from app.models.announcement import Announcement
 from app.core.encrypt import encryptor
 from app.utils.timestamp_validator import TimestampValidator
 from app.config import settings
-from app.core.logger import logger
+from app.core.logger import get_client_logger
+logger = get_client_logger()
 
 router = APIRouter()
 
@@ -198,7 +199,10 @@ async def get_announcement(
             "page_size": page_size,
         }
 
-        # 8. 使用AES加密响应数据 (CTR模式,与前端保持一致)
+        # 8. 记录明文响应数据日志
+        logger.debug(f"[公告接口响应] 明文响应数据: {json.dumps(response_data, ensure_ascii=False, indent=2)}")
+
+        # 9. 使用AES加密响应数据 (CTR模式,与前端保持一致)
         # 🔐 安全设计:使用请求中解密出的同一个AES密钥加密响应
         # 这样前端可以用本地保存的AES密钥直接解密,无需额外的RSA密钥交换
         response_json = json.dumps(response_data, ensure_ascii=False, separators=(',', ':'))

+ 2 - 1
backend/app/api/client/auth.py

@@ -12,7 +12,8 @@ from app.models.recharge_record import RechargeRecord
 from app.models.system_config import SystemConfig
 from app.core.security import verify_password, get_password_hash
 from app.core.encrypt import encryptor
-from app.core.logger import logger
+from app.core.logger import get_client_logger
+logger = get_client_logger()
 from app.config import settings
 from app.schemas.client_api import *
 from app.services.key_rotation_service import KeyRotationService

+ 2 - 1
backend/app/api/client/keys.py

@@ -17,7 +17,8 @@ from app.core.encrypt import encryptor
 from app.utils.timestamp_validator import TimestampValidator
 from app.utils.timezone import beijing_now
 from app.config import settings
-from app.core.logger import logger
+from app.core.logger import get_client_logger
+logger = get_client_logger()
 
 router = APIRouter()
 

+ 2 - 1
backend/app/api/client/steam.py

@@ -9,7 +9,8 @@ from app.database import get_db
 from app.models.steam_account import SteamAccount
 from app.models.user import User
 from app.core.encrypt import encryptor
-from app.core.logger import logger
+from app.core.logger import get_client_logger
+logger = get_client_logger()
 from app.config import settings
 from datetime import datetime
 from app.utils.timezone import beijing_now

+ 53 - 7
backend/app/core/logger.py

@@ -1,9 +1,10 @@
 """
 日志配置
+支持admin和client接口日志分离
 """
 import sys
 from pathlib import Path
-from loguru import logger
+from loguru import logger as loguru_logger
 from app.config import settings
 
 # 创建日志目录
@@ -11,19 +12,18 @@ log_dir = Path("logs")
 log_dir.mkdir(exist_ok=True)
 
 # 移除默认处理器
-logger.remove()
+loguru_logger.remove()
 
 # 控制台输出 - 优化API出入参显示
-logger.add(
+loguru_logger.add(
     sys.stderr,
     format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>",
     level=settings.LOG_LEVEL,
-    # 对于DEBUG级别,使用更清晰的格式显示API出入参
     colorize=True,
 )
 
-# 文件输出
-logger.add(
+# 默认文件输出(保留,用于其他日志)
+loguru_logger.add(
     log_dir / "app_{time:YYYY-MM-DD}.log",
     rotation="00:00",
     retention="30 days",
@@ -32,7 +32,53 @@ logger.add(
     encoding="utf-8",
 )
 
+# Admin专用文件处理器
+admin_handler_id = loguru_logger.add(
+    log_dir / "admin_{time:YYYYMMDD}.log",
+    rotation="00:00",
+    retention="30 days",
+    format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}",
+    level="DEBUG",
+    encoding="utf-8",
+    filter=lambda record: record["extra"].get("admin_interface") is True
+)
+
+# Client专用文件处理器
+client_handler_id = loguru_logger.add(
+    log_dir / "client_{time:YYYYMMDD}.log",
+    rotation="00:00",
+    retention="30 days",
+    format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {name}:{function}:{line} - {message}",
+    level="DEBUG",
+    encoding="utf-8",
+    filter=lambda record: record["extra"].get("client_interface") is True
+)
+
 def setup_logger():
     """设置日志"""
-    return logger
+    return loguru_logger
+
+def get_admin_logger():
+    """获取admin接口专用logger"""
+    return loguru_logger.bind(admin_interface=True)
+
+def get_client_logger():
+    """获取client接口专用logger"""
+    return loguru_logger.bind(client_interface=True)
+
+def get_logger_by_path(path: str):
+    """
+    根据请求路径返回对应的logger
+    /admin/* -> admin_logger
+    /api/* -> client_logger
+    其他 -> 默认logger
+    """
+    if path.startswith("/admin/"):
+        return loguru_logger.bind(admin_interface=True)
+    elif path.startswith("/api/"):
+        return loguru_logger.bind(client_interface=True)
+    else:
+        return loguru_logger
 
+# 向后兼容:保留logger名称供现有代码使用
+logger = loguru_logger

+ 3 - 3
backend/app/main.py

@@ -82,10 +82,10 @@ app.add_middleware(
 # API日志中间件(使用 ASGI 中间件以可靠捕获响应体)
 from app.middleware.auth import AuthValidationASGIMiddleware
 from app.middleware.logging import APILoggingASGIMiddleware
-# 认证校验中间件先执行,校验 token / 用户 / 卡密
-app.add_middleware(AuthValidationASGIMiddleware)
-# 然后记录 API 日志(依赖认证信息)
+# 日志中间件先执行,记录所有请求(包括未认证的)
 app.add_middleware(APILoggingASGIMiddleware)
+# 然后执行认证校验中间件,校验 token / 用户 / 卡密
+app.add_middleware(AuthValidationASGIMiddleware)
 
 # 异常处理
 from fastapi import HTTPException

+ 13 - 10
backend/app/middleware/auth.py

@@ -13,7 +13,7 @@ from app.utils.timezone import beijing_now
 from fastapi.responses import JSONResponse
 import json
 from datetime import datetime
-from app.core.logger import logger
+from app.core.logger import logger, get_logger_by_path
 
 
 EXEMPT_PATHS = [
@@ -51,6 +51,9 @@ class AuthValidationASGIMiddleware:
         path = scope.get("path", "")
         method = scope.get("method", "").upper()
 
+        # 根据路径选择对应的logger
+        auth_logger = get_logger_by_path(path)
+
         # 跳过不需要验证的路径或 CORS 预检
         if method == "OPTIONS" or any(path.startswith(p) for p in EXEMPT_PATHS):
             await self.app(scope, receive, send)
@@ -65,9 +68,9 @@ class AuthValidationASGIMiddleware:
 
         # 临时调试日志:记录客户端传来的鉴别头,帮助定位客户端是否传递了 X-User-Id/X-User-Name 或 Authorization
         try:
-            logger.debug(f"[AuthMiddleware][DEBUG HEADERS] path={path} method={method} X-User-Id={header_user_id} X-User-Name={header_user_name} AuthorizationPresent={bool(auth_header)}")
+            auth_logger.debug(f"[AuthMiddleware][DEBUG HEADERS] path={path} method={method} X-User-Id={header_user_id} X-User-Name={header_user_name} AuthorizationPresent={bool(auth_header)}")
         except Exception:
-            logger.debug("[AuthMiddleware][DEBUG HEADERS] failed to read headers")
+            auth_logger.debug("[AuthMiddleware][DEBUG HEADERS] failed to read headers")
         if not auth_header or not auth_header.startswith("Bearer "):
             resp = JSONResponse(status_code=401, content={"code": 401, "msg": "认证信息缺失或无效"})
             await resp(scope, receive, send)
@@ -86,14 +89,14 @@ class AuthValidationASGIMiddleware:
             await resp(scope, receive, send)
             return
 
-        logger.debug(f"[AuthMiddleware][DEBUG] Starting authentication for username: {username}, path: {path}")
+        auth_logger.debug(f"[AuthMiddleware][DEBUG] Starting authentication for username: {username}, path: {path}")
 
         # 查询用户或管理员
         db = SessionLocal()
         try:
             user = db.query(User).filter(User.username == username, User.is_deleted == 0).first()
             if user:
-                logger.debug(f"[AuthMiddleware][DEBUG] Found user: {user.username} (ID: {user.id})")
+                auth_logger.debug(f"[AuthMiddleware][DEBUG] Found user: {user.username} (ID: {user.id})")
                 # 检查状态与过期
                 if user.status != 1:
                     resp = JSONResponse(status_code=403, content={"code": 403, "msg": "账号已被禁用"})
@@ -123,13 +126,13 @@ class AuthValidationASGIMiddleware:
                 scope["verified_user_id"] = user.id
                 scope["verified_username"] = user.username
                 scope["verified_is_admin"] = False
-                logger.debug(f"[AuthMiddleware][DEBUG] Set verified_user_id: {user.id} for user: {user.username}")
+                auth_logger.debug(f"[AuthMiddleware][DEBUG] Set verified_user_id: {user.id} for user: {user.username}")
                 await self.app(scope, receive, send)
                 return
 
             admin = db.query(Admin).filter(Admin.username == username).first()
             if admin:
-                logger.debug(f"[AuthMiddleware][DEBUG] Found admin: {admin.username} (ID: {admin.id})")
+                auth_logger.debug(f"[AuthMiddleware][DEBUG] Found admin: {admin.username} (ID: {admin.id})")
 
                 # 验证header中的用户信息是否匹配(组合校验)
                 if header_user_id and header_user_name:
@@ -155,17 +158,17 @@ class AuthValidationASGIMiddleware:
                 scope["verified_admin_id"] = admin.id
                 scope["verified_username"] = admin.username
                 scope["verified_is_admin"] = True
-                logger.debug(f"[AuthMiddleware][DEBUG] Set verified_admin_id: {admin.id} for admin: {admin.username}")
+                auth_logger.debug(f"[AuthMiddleware][DEBUG] Set verified_admin_id: {admin.id} for admin: {admin.username}")
                 await self.app(scope, receive, send)
                 return
 
             # 未找到对应用户或管理员
-            logger.debug(f"[AuthMiddleware][DEBUG] No user or admin found for username: {username}")
+            auth_logger.debug(f"[AuthMiddleware][DEBUG] No user or admin found for username: {username}")
             resp = JSONResponse(status_code=401, content={"code": 401, "msg": "认证失败:用户不存在"})
             await resp(scope, receive, send)
             return
         except Exception as e:
-            logger.exception(f"[AuthMiddleware][ERROR] Exception during authentication: {e}")
+            auth_logger.exception(f"[AuthMiddleware][ERROR] Exception during authentication: {e}")
             resp = JSONResponse(status_code=500, content={"code": 500, "msg": "认证服务异常"})
             await resp(scope, receive, send)
             return

+ 11 - 8
backend/app/middleware/logging.py

@@ -9,7 +9,7 @@ from starlette.responses import Response
 from sqlalchemy.orm import Session
 from app.database import SessionLocal
 from app.models.api_log import APILog
-from app.core.logger import logger
+from app.core.logger import logger, get_logger_by_path
 from app.core.security import verify_token
 from app.models.user import User
 from datetime import datetime
@@ -275,14 +275,17 @@ class APILoggingASGIMiddleware:
                 # 计算执行时间
                 execution_time = int((time.time() - start_time) * 1000)
 
+                # 根据路径选择对应的logger
+                api_logger = get_logger_by_path(path)
+
                 # 在控制台打印API入参出参
                 try:
                     request_display = request_text[:1000] + ('...' if request_text and len(request_text) > 1000 else '') if request_text else 'None'
                     response_display = response_text[:1000] + ('...' if response_text and len(response_text) > 1000 else '') if response_text else 'None'
-                    logger.info(f"[API请求] {method} {url} - 请求数据: {request_display}")
-                    logger.info(f"[API响应] {method} {url} - 响应数据: {response_display} - 执行时间: {execution_time}ms")
+                    api_logger.info(f"[API请求] {method} {url} - 请求数据: {request_display}")
+                    api_logger.info(f"[API响应] {method} {url} - 响应数据: {response_display} - 执行时间: {execution_time}ms")
                 except Exception as e:
-                    logger.warning(f"[API日志] 控制台日志输出失败: {e}")
+                    api_logger.warning(f"[API日志] 控制台日志输出失败: {e}")
 
                 # 写入数据库(异步性与错误容忍)
                 try:
@@ -294,7 +297,7 @@ class APILoggingASGIMiddleware:
                             xu = headers_lower.get('x-user-id') or headers_lower.get('x-user-name') or None
                         except Exception:
                             xu = None
-                        logger.debug(f"[APILoggingASGI][DEBUG] endpoint={url} verified_user_id={scope.get('verified_user_id')} verified_admin_id={scope.get('verified_admin_id')} header_X_User_Id={xu}")
+                        api_logger.debug(f"[APILoggingASGI][DEBUG] endpoint={url} verified_user_id={scope.get('verified_user_id')} verified_admin_id={scope.get('verified_admin_id')} header_X_User_Id={xu}")
                         api_log = APILog(
                             user_id=user_id,
                             admin_id=scope.get("verified_admin_id") if scope.get("verified_admin_id") else None,
@@ -310,14 +313,14 @@ class APILoggingASGIMiddleware:
                         )
                         db.add(api_log)
                         db.commit()
-                        logger.debug(f"[APILoggingASGI] 成功记录API日志: {url} {method} {status_code} {execution_time}ms")
+                        api_logger.debug(f"[APILoggingASGI] 成功记录API日志: {url} {method} {status_code} {execution_time}ms")
                     except Exception as e:
                         db.rollback()
-                        logger.error(f"[APILoggingASGI] 记录API日志失败: {url} {method} {status_code}", exc_info=True)
+                        api_logger.error(f"[APILoggingASGI] 记录API日志失败: {url} {method} {status_code}", exc_info=True)
                     finally:
                         db.close()
                 except Exception as e:
-                    logger.error(f"[APILoggingASGI] 创建数据库连接失败: {e}")
+                    api_logger.error(f"[APILoggingASGI] 创建数据库连接失败: {e}")
             except Exception:
                 pass