diff --git a/backend/.env.example b/backend/.env.example index 0e1cf7e..d25fa44 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -13,5 +13,3 @@ REDIS_PASSWORD='' REDIS_DATABASE=0 # Token TOKEN_SECRET_KEY='1VkVF75nsNABBjK_7-qz7GtzNy3AMvktc9TCPwKczCk' -# Opera Log -OPERA_LOG_ENCRYPT_SECRET_KEY='d77b25790a804c2b4a339dd0207941e4cefa5751935a33735bc73bb7071a005b' diff --git a/backend/app/admin/model/login_log.py b/backend/app/admin/model/login_log.py index 5bb0a88..0726b39 100644 --- a/backend/app/admin/model/login_log.py +++ b/backend/app/admin/model/login_log.py @@ -21,7 +21,7 @@ class LoginLog(DataClassBase): country: Mapped[str | None] = mapped_column(sa.String(64), comment='国家') region: Mapped[str | None] = mapped_column(sa.String(64), comment='地区') city: Mapped[str | None] = mapped_column(sa.String(64), comment='城市') - user_agent: Mapped[str | None] = mapped_column(sa.String(256), comment='请求头') + user_agent: Mapped[str | None] = mapped_column(sa.String(512), comment='请求头') os: Mapped[str | None] = mapped_column(sa.String(64), comment='操作系统') browser: Mapped[str | None] = mapped_column(sa.String(64), comment='浏览器') device: Mapped[str | None] = mapped_column(sa.String(64), comment='设备') diff --git a/backend/app/admin/service/auth_service.py b/backend/app/admin/service/auth_service.py index bcef9ee..8f0aafb 100644 --- a/backend/app/admin/service/auth_service.py +++ b/backend/app/admin/service/auth_service.py @@ -224,7 +224,7 @@ async def logout(*, request: Request, response: Response) -> None: await redis_client.delete(f'{settings.TOKEN_REDIS_PREFIX}:{user_id}:{session_uuid}') await redis_client.delete(f'{settings.TOKEN_EXTRA_INFO_REDIS_PREFIX}:{user_id}:{session_uuid}') if refresh_token: - await redis_client.delete(f'{settings.TOKEN_REFRESH_REDIS_PREFIX}:{user_id}:{refresh_token}') + await redis_client.delete(f'{settings.TOKEN_REFRESH_REDIS_PREFIX}:{user_id}:{session_uuid}') auth_service: AuthService = AuthService() diff --git a/backend/cli.py b/backend/cli.py index 6f8e0d6..8ea3e98 100644 --- a/backend/cli.py +++ b/backend/cli.py @@ -65,9 +65,8 @@ def setup_env_file() -> bool: redis_password = Prompt.ask('Redis 密码(留空表示无密码)', password=True, default='') redis_db = Prompt.ask('Redis 数据库编号', default='0') - console.print('生成 Token 和操作日志密钥...', style='white') + console.print('生成 Token 密钥...', style='white') token_secret = secrets.token_urlsafe(32) - opera_log_secret = secrets.token_hex(32) console.print('写入 .env 文件...', style='white') env_content = env_content.replace("DATABASE_TYPE='postgresql'", f"DATABASE_TYPE='{db_type}'") @@ -90,10 +89,6 @@ def setup_env_file() -> bool: settings.REDIS_DATABASE = redis_db env_content = re.sub(r"TOKEN_SECRET_KEY='[^']*'", f"TOKEN_SECRET_KEY='{token_secret}'", env_content) settings.TOKEN_SECRET_KEY = token_secret - env_content = re.sub( - r"OPERA_LOG_ENCRYPT_SECRET_KEY='[^']*'", f"OPERA_LOG_ENCRYPT_SECRET_KEY='{opera_log_secret}'", env_content - ) - settings.OPERA_LOG_ENCRYPT_SECRET_KEY = opera_log_secret Path(ENV_FILE_PATH).write_text(env_content, encoding='utf-8') console.print('.env 文件创建成功', style='green') diff --git a/backend/common/queue.py b/backend/common/queue.py index 921c15a..9bc9172 100644 --- a/backend/common/queue.py +++ b/backend/common/queue.py @@ -2,6 +2,8 @@ from asyncio import Queue +from backend.common.log import log + async def batch_dequeue(queue: Queue, max_items: int, timeout: float) -> list: """ @@ -23,5 +25,7 @@ async def collector() -> None: await asyncio.wait_for(collector(), timeout=timeout) except asyncio.TimeoutError: pass + except Exception as e: + log.error(f'队列批量获取失败: {e}') return items diff --git a/backend/core/conf.py b/backend/core/conf.py index 4636b78..047e171 100644 --- a/backend/core/conf.py +++ b/backend/core/conf.py @@ -42,7 +42,7 @@ class Settings(BaseSettings): # 数据库 DATABASE_ECHO: bool | Literal['debug'] = False DATABASE_POOL_ECHO: bool | Literal['debug'] = False - DATABASE_SCHEMA: str = 'fba-simple' + DATABASE_SCHEMA: str = 'fba-slim' DATABASE_CHARSET: str = 'utf8mb4' DATABASE_PK_MODE: Literal['autoincrement', 'snowflake'] = 'autoincrement' @@ -165,9 +165,6 @@ class Settings(BaseSettings): LOG_ACCESS_FILENAME: str = 'fba_access.log' LOG_ERROR_FILENAME: str = 'fba_error.log' - # .env 操作日志 - OPERA_LOG_ENCRYPT_SECRET_KEY: str # secrets.token_hex(32) - # 操作日志 OPERA_LOG_PATH_EXCLUDE: list[str] = [ '/favicon.ico', @@ -179,8 +176,7 @@ class Settings(BaseSettings): f'{FASTAPI_API_V1_PATH}/oauth2/google/callback', f'{FASTAPI_API_V1_PATH}/oauth2/linux-do/callback', ] - OPERA_LOG_ENCRYPT_TYPE: int = 1 # 0: AES (性能损耗); 1: md5; 2: ItsDangerous; 3: 不加密, others: 替换为 ****** - OPERA_LOG_ENCRYPT_KEY_INCLUDE: list[str] = [ # 将加密接口入参参数对应的值 + OPERA_LOG_REDACT_KEYS: list[str] = [ 'password', 'old_password', 'new_password', diff --git a/backend/middleware/opera_log_middleware.py b/backend/middleware/opera_log_middleware.py index 765db86..874cce6 100644 --- a/backend/middleware/opera_log_middleware.py +++ b/backend/middleware/opera_log_middleware.py @@ -12,7 +12,7 @@ from backend.app.admin.schema.opera_log import CreateOperaLogParam from backend.app.admin.service.opera_log_service import opera_log_service from backend.common.context import ctx -from backend.common.enums import OperaLogCipherType, StatusType +from backend.common.enums import StatusType from backend.common.log import log from backend.common.prometheus.instruments import ( PROMETHEUS_EXCEPTION_COUNTER, @@ -25,7 +25,6 @@ from backend.common.response.response_code import StandardResponseCode from backend.core.conf import settings from backend.database.db import async_db_session -from backend.utils.encrypt import AESCipher, ItsDCipher, Md5Cipher from backend.utils.trace_id import get_request_trace_id @@ -208,20 +207,9 @@ def desensitization(args: dict[str, Any]) -> dict[str, Any]: :param args: 需要脱敏的参数字典 :return: """ - for key, value in args.items(): - if key in settings.OPERA_LOG_ENCRYPT_KEY_INCLUDE: - match settings.OPERA_LOG_ENCRYPT_TYPE: - case OperaLogCipherType.aes: - args[key] = (AESCipher(settings.OPERA_LOG_ENCRYPT_SECRET_KEY).encrypt(value)).hex() - case OperaLogCipherType.md5: - args[key] = Md5Cipher.encrypt(value) - case OperaLogCipherType.itsdangerous: - args[key] = ItsDCipher(settings.OPERA_LOG_ENCRYPT_SECRET_KEY).encrypt(value) - case OperaLogCipherType.plan: - pass - case _: - args[key] = '******' - + for key in args: + if key in settings.OPERA_LOG_REDACT_KEYS: + args[key] = '[REDACTED]' return args @classmethod @@ -239,6 +227,8 @@ async def consumer(cls) -> None: log.info('自动执行【操作日志批量创建】任务...') async with async_db_session.begin() as db: await opera_log_service.bulk_create(db=db, objs=logs) + except Exception as e: + log.error(f'操作日志入库失败,丢失 {len(logs)} 条日志: {e}') finally: - if not cls.opera_log_queue.empty(): + for _ in range(len(logs)): cls.opera_log_queue.task_done()