#!/usr/bin/env python3 """修复 Redis URL 解析问题""" config_path = "/app/app/config.py" with open(config_path, "r", encoding="utf-8") as f: content = f.read() # 找到并替换 create_redis_client 方法 old = ''' def create_redis_client(self) -> aioredis.Redis: """创建 Redis 异步客户端实例。 使用单独的 host/port/password 参数,避免 URL 解析问题 (特别是密码中包含特殊字符 ! @ # 时)。 自动附加 protocol=2 参数,强制使用 RESP2 协议。 原因:Windows 版 Redis 3.x 不支持 RESP3 协议(HELLO 命令), 而 redis-py 8.0+ 默认使用 RESP3,会导致连接失败。 全项目统一使用此方法创建 Redis 客户端,避免协议不匹配。 Returns: aioredis.Redis: 配置好的 Redis 异步客户端 """ # 如果 redis_url 为空,使用默认值 if not self.redis_url: # 默认值:本地 Redis return aioredis.Redis( host="localhost", port=6379, protocol=2, decode_responses=True ) # 解析 REDIS_URL 提取连接参数 # 格式: redis://:password@host:port/db from urllib.parse import urlparse parsed = urlparse(self.redis_url) # 提取密码(去掉用户名部分,如果存在的话) password = parsed.password if not password: # 尝试从 netloc 中提取(格式 :password@host) netloc = parsed.netloc if "@" in netloc: password = netloc.split("@")[0].split(":")[-1] return aioredis.Redis( host=parsed.hostname or "localhost", port=parsed.port or 6379, password=password, db=parsed.path and int(parsed.path.lstrip("/")) or 0, protocol=2, decode_responses=True )''' new = ''' def create_redis_client(self) -> aioredis.Redis: """创建 Redis 异步客户端实例。 使用单独的 host/port/password 参数,避免 URL 解析问题 (特别是密码中包含特殊字符 ! @ # 时)。 自动附加 protocol=2 参数,强制使用 RESP2 协议。 原因:Windows 版 Redis 3.x 不支持 RESP3 协议(HELLO 命令), 而 redis-py 8.0+ 默认使用 RESP3,会导致连接失败。 全项目统一使用此方法创建 Redis 客户端,避免协议不匹配。 Returns: aioredis.Redis: 配置好的 Redis 异步客户端 """ # 如果 redis_url 为空,使用默认值 if not self.redis_url: # 默认值:本地 Redis return aioredis.Redis( host="localhost", port=6379, protocol=2, decode_responses=True ) # 解析 REDIS_URL 提取连接参数 # 格式: redis://:password@host:port/db # 注意:密码中可能包含 @ 字符,需要特殊处理 # 例如:redis://:R3d!s@2026#Secure@redis:6379/0 # 其中 :R3d!s@2026#Secure 是密码,redis 是主机名 url = self.redis_url # 找到最后一个 @ 之前的所有内容作为密码 # 格式: redis://:password@host:port/db scheme_prefix = "redis://:" if url.startswith(scheme_prefix): # 提取 @ 之后的部分(主机和端口) rest = url[len(scheme_prefix):] at_pos = rest.rfind("@") if at_pos > 0: password = rest[:at_pos] host_part = rest[at_pos+1:] # 解析主机部分 if "/" in host_part: host_port, db = host_part.split("/", 1) db = int(db) if db.isdigit() else 0 else: host_port = host_part db = 0 if ":" in host_port: host, port = host_port.split(":", 1) port = int(port) else: host = host_port port = 6379 return aioredis.Redis( host=host, port=port, password=password, db=db, protocol=2, decode_responses=True ) # 回退:使用 from_url return aioredis.from_url(url, protocol=2)''' if old not in content: print("ERROR: Could not find old method") print("Looking for method...") import re match = re.search(r'def create_redis_client.*?(?=\n def |\nclass |\Z)', content, re.DOTALL) if match: print(f"Found: {match.group(0)[:200]}") sys.exit(1) new_content = content.replace(old, new) with open(config_path, "w", encoding="utf-8") as f: f.write(new_content) print("Fixed!")