""" WebDAV file management routes. Uses the open-source WsgiDAV library to provide full WebDAV protocol support. Provides WebDAV access for the projects/{resource_type}/ directories. Supported resource types: 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"} # Ensure the base directories exist for resource_type in ALLOWED_RESOURCE_TYPES: (PROJECTS_BASE_DIR / resource_type).mkdir(parents=True, exist_ok=True) # Build provider_mapping: map each resource type to its corresponding directory 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 middleware that fixes Destination header scheme/host mismatches behind a reverse proxy. When the service runs behind an HTTPS reverse proxy, the client sends a Destination header with the external URL (for example https://example.com/webdav/docs/new-name), while WsgiDAV internally sees an HTTP request. This middleware rewrites the Destination header so its scheme and host match the internal request. """ # Read external request information from X-Forwarded headers and keep # wsgi.url_scheme aligned with the forwarded protocol. forwarded_proto = environ.get("HTTP_X_FORWARDED_PROTO") if forwarded_proto: environ["wsgi.url_scheme"] = forwarded_proto # Rewrite the Destination header by replacing the external scheme/host # with the internal values observed by the application. destination = environ.get("HTTP_DESTINATION") if destination: parsed = urlparse(destination) # Replace with the actual internal scheme and 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)