qwen_agent/routes/webdav.py
朱潮 38c5d1e622 修复方案:在 routes/webdav.py 中添加了一个 WSGI 中间件包装函数,在请求到达 WsgiDAV 前:
1. 读取 X-Forwarded-Proto 设置正确的 wsgi.url_scheme
  2. 将 Destination 头中的外部 scheme/host 重写为内部实际值
2026-03-09 18:51:08 +08:00

93 lines
3.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
WebDAV 文件管理路由
使用 WsgiDAV 开源库提供完整的 WebDAV 协议支持
为 projects/{resource_type}/ 目录提供 WebDAV 协议支持
支持的资源类型: robot, docs
"""
from pathlib import Path
from urllib.parse import urlparse, urlunparse
from wsgidav.wsgidav_app import WsgiDAVApp
from wsgidav.fs_dav_provider import FilesystemProvider
from utils.settings import WEBDAV_USERNAME, WEBDAV_PASSWORD
PROJECTS_BASE_DIR = Path("projects")
ALLOWED_RESOURCE_TYPES = {"robot", "docs"}
# 确保基础目录存在
for resource_type in ALLOWED_RESOURCE_TYPES:
(PROJECTS_BASE_DIR / resource_type).mkdir(parents=True, exist_ok=True)
# 构建 provider_mapping每个资源类型映射到对应目录
provider_mapping = {}
for resource_type in ALLOWED_RESOURCE_TYPES:
abs_path = str((PROJECTS_BASE_DIR / resource_type).resolve())
provider_mapping[f"/{resource_type}"] = FilesystemProvider(abs_path)
config = {
"mount_path": "/webdav",
"provider_mapping": provider_mapping,
"http_authenticator": {
"domain_controller": "wsgidav.dc.simple_dc.SimpleDomainController",
"accept_basic": True,
"accept_digest": False,
"default_to_digest": False,
},
"simple_dc": {
"user_mapping": {
"*": {
WEBDAV_USERNAME: {
"password": WEBDAV_PASSWORD,
},
},
},
},
"verbose": 1,
"lock_storage": True,
"property_manager": True,
"dir_browser": {
"enable": True,
"icon": True,
"response_trailer": "",
},
"hotfixes": {
"re_encode_path_info": True,
},
}
_wsgidav_app = WsgiDAVApp(config)
def wsgidav_app(environ, start_response):
"""WSGI 中间件:处理反向代理场景下 Destination 头的 scheme/host 不匹配问题。
当服务运行在 HTTPS 反向代理后面时,客户端发送的 Destination 头包含外部 URL
(如 https://example.com/webdav/docs/new-name但 WsgiDAV 内部看到的是 HTTP。
这个中间件将 Destination 头重写为与内部请求一致的 scheme 和 host。
"""
# 从 X-Forwarded 头获取外部信息,并确保 wsgi.url_scheme 正确
forwarded_proto = environ.get("HTTP_X_FORWARDED_PROTO")
if forwarded_proto:
environ["wsgi.url_scheme"] = forwarded_proto
# 重写 Destination 头:将外部 scheme/host 替换为内部实际值
destination = environ.get("HTTP_DESTINATION")
if destination:
parsed = urlparse(destination)
# 用内部实际的 scheme 和 host 替换
internal_scheme = environ.get("wsgi.url_scheme", "http")
internal_host = environ.get("HTTP_HOST", environ.get("SERVER_NAME", "localhost"))
rewritten = urlunparse((
internal_scheme,
internal_host,
parsed.path,
parsed.params,
parsed.query,
parsed.fragment,
))
environ["HTTP_DESTINATION"] = rewritten
return _wsgidav_app(environ, start_response)