"""Minimal WebDAV server. Serves /data for uploads. Auth at reverse tunnel; app allows anonymous.""" import os import sys import traceback from wsgidav.fs_dav_provider import FilesystemProvider from wsgidav.wsgidav_app import WsgiDAVApp from wsgidav.dir_browser import WsgiDavDirBrowser from wsgidav.error_printer import ErrorPrinter from wsgidav.request_resolver import RequestResolver from wsgidav.http_authenticator import HTTPAuthenticator from wsgidav.mw.cors import Cors from cheroot.wsgi import Server as WSGIServer ROOT = "/data" PORT = 80 def ensure_data_dir_writable() -> None: os.makedirs(ROOT, exist_ok=True) probe = os.path.join(ROOT, ".write_probe") try: with open(probe, "w") as f: f.write("") os.remove(probe) except OSError as e: print(f"FATAL: cannot write to {ROOT}: {e}", file=sys.stderr) sys.exit(1) def catch_all(app): """Catch uncaught exceptions so one bad request doesn't kill the server.""" def wrapper(environ, start_response): try: return app(environ, start_response) except Exception: traceback.print_exc() start_response("500 Internal Server Error", [("Content-Type", "text/plain")]) return [b"Internal Server Error"] return wrapper ensure_data_dir_writable() config = { "host": "0.0.0.0", "port": PORT, # Mount at /dav so root / is not special-cased (Finder PROPFIND needs real XML) "provider_mapping": {"/dav": FilesystemProvider(ROOT, readonly=False)}, "middleware_stack": [ Cors, ErrorPrinter, HTTPAuthenticator, WsgiDavDirBrowser, RequestResolver, ], "http_authenticator": { "domain_controller": None, "accept_basic": True, "accept_digest": True, "default_to_digest": False, }, "simple_dc": { "user_mapping": {"*": True}, }, "hotfixes": { "emulate_win32_lastmod": True, "treat_root_options_as_asterisk": True, }, "add_header_MS_Author_Via": True, "property_manager": True, # Required for PROPFIND to return XML (Finder mount) "verbose": 3, } app = WsgiDAVApp(config) app = catch_all(app) if __name__ == "__main__": server = WSGIServer((config["host"], config["port"]), app) # Avoid Finder/hung clients holding connections forever if hasattr(server, "connection_limit"): server.connection_limit = 20 if hasattr(server, "timeout"): server.timeout = 30 server.start()