...
This commit is contained in:
		
							
								
								
									
										197
									
								
								server_webdav.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										197
									
								
								server_webdav.py
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,197 @@ | ||||
| #!/usr/bin/env python3 | ||||
| """ | ||||
| WebDAV-based Markdown Editor Server | ||||
| Uses WsgiDAV for standards-compliant file operations | ||||
| """ | ||||
|  | ||||
| import os | ||||
| import sys | ||||
| import yaml | ||||
| import json | ||||
| from pathlib import Path | ||||
| from wsgidav.wsgidav_app import WsgiDAVApp | ||||
| from wsgidav.fs_dav_provider import FilesystemProvider | ||||
| from cheroot import wsgi | ||||
| from cheroot.ssl.builtin import BuiltinSSLAdapter | ||||
|  | ||||
|  | ||||
| class MarkdownEditorApp: | ||||
|     """Main application that wraps WsgiDAV and adds custom endpoints""" | ||||
|      | ||||
|     def __init__(self, config_path="config.yaml"): | ||||
|         self.config = self.load_config(config_path) | ||||
|         self.collections = self.config.get('collections', {}) | ||||
|         self.setup_collections() | ||||
|         self.webdav_app = self.create_webdav_app() | ||||
|      | ||||
|     def load_config(self, config_path): | ||||
|         """Load configuration from YAML file""" | ||||
|         with open(config_path, 'r') as f: | ||||
|             return yaml.safe_load(f) | ||||
|      | ||||
|     def setup_collections(self): | ||||
|         """Create collection directories if they don't exist""" | ||||
|         for name, config in self.collections.items(): | ||||
|             path = Path(config['path']) | ||||
|             path.mkdir(parents=True, exist_ok=True) | ||||
|              | ||||
|             # Create images subdirectory | ||||
|             images_path = path / 'images' | ||||
|             images_path.mkdir(exist_ok=True) | ||||
|              | ||||
|             print(f"Collection '{name}' -> {path.absolute()}") | ||||
|      | ||||
|     def create_webdav_app(self): | ||||
|         """Create WsgiDAV application with configured collections""" | ||||
|         provider_mapping = {} | ||||
|          | ||||
|         for name, config in self.collections.items(): | ||||
|             path = os.path.abspath(config['path']) | ||||
|             provider_mapping[f'/fs/{name}'] = FilesystemProvider(path) | ||||
|          | ||||
|         config = { | ||||
|             'host': self.config['server']['host'], | ||||
|             'port': self.config['server']['port'], | ||||
|             'provider_mapping': provider_mapping, | ||||
|             'verbose': self.config['webdav'].get('verbose', 1), | ||||
|             'logging': { | ||||
|                 'enable_loggers': [] | ||||
|             }, | ||||
|             'property_manager': True, | ||||
|             'lock_storage': True, | ||||
|             'simple_dc': { | ||||
|                 'user_mapping': { | ||||
|                     '*': True  # Allow anonymous access for development | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         return WsgiDAVApp(config) | ||||
|      | ||||
|     def __call__(self, environ, start_response): | ||||
|         """WSGI application entry point""" | ||||
|         path = environ.get('PATH_INFO', '') | ||||
|         method = environ.get('REQUEST_METHOD', '') | ||||
|          | ||||
|         # Handle collection list endpoint | ||||
|         if path == '/fs/' and method == 'GET': | ||||
|             return self.handle_collections_list(environ, start_response) | ||||
|          | ||||
|         # Handle static files | ||||
|         if path.startswith('/static/'): | ||||
|             return self.handle_static(environ, start_response) | ||||
|          | ||||
|         # Handle root - serve index.html | ||||
|         if path == '/' or path == '/index.html': | ||||
|             return self.handle_index(environ, start_response) | ||||
|          | ||||
|         # All other requests go to WebDAV | ||||
|         return self.webdav_app(environ, start_response) | ||||
|      | ||||
|     def handle_collections_list(self, environ, start_response): | ||||
|         """Return list of available collections""" | ||||
|         collections = list(self.collections.keys()) | ||||
|         response_body = json.dumps(collections).encode('utf-8') | ||||
|          | ||||
|         start_response('200 OK', [ | ||||
|             ('Content-Type', 'application/json'), | ||||
|             ('Content-Length', str(len(response_body))), | ||||
|             ('Access-Control-Allow-Origin', '*') | ||||
|         ]) | ||||
|          | ||||
|         return [response_body] | ||||
|      | ||||
|     def handle_static(self, environ, start_response): | ||||
|         """Serve static files""" | ||||
|         path = environ.get('PATH_INFO', '')[1:]  # Remove leading / | ||||
|         file_path = Path(path) | ||||
|          | ||||
|         if not file_path.exists() or not file_path.is_file(): | ||||
|             start_response('404 Not Found', [('Content-Type', 'text/plain')]) | ||||
|             return [b'File not found'] | ||||
|          | ||||
|         # Determine content type | ||||
|         content_types = { | ||||
|             '.html': 'text/html', | ||||
|             '.css': 'text/css', | ||||
|             '.js': 'application/javascript', | ||||
|             '.json': 'application/json', | ||||
|             '.png': 'image/png', | ||||
|             '.jpg': 'image/jpeg', | ||||
|             '.jpeg': 'image/jpeg', | ||||
|             '.gif': 'image/gif', | ||||
|             '.svg': 'image/svg+xml', | ||||
|             '.ico': 'image/x-icon' | ||||
|         } | ||||
|          | ||||
|         ext = file_path.suffix.lower() | ||||
|         content_type = content_types.get(ext, 'application/octet-stream') | ||||
|          | ||||
|         with open(file_path, 'rb') as f: | ||||
|             content = f.read() | ||||
|          | ||||
|         start_response('200 OK', [ | ||||
|             ('Content-Type', content_type), | ||||
|             ('Content-Length', str(len(content))) | ||||
|         ]) | ||||
|          | ||||
|         return [content] | ||||
|      | ||||
|     def handle_index(self, environ, start_response): | ||||
|         """Serve index.html""" | ||||
|         index_path = Path('templates/index.html') | ||||
|          | ||||
|         if not index_path.exists(): | ||||
|             start_response('404 Not Found', [('Content-Type', 'text/plain')]) | ||||
|             return [b'index.html not found'] | ||||
|          | ||||
|         with open(index_path, 'r', encoding='utf-8') as f: | ||||
|             content = f.read().encode('utf-8') | ||||
|          | ||||
|         start_response('200 OK', [ | ||||
|             ('Content-Type', 'text/html; charset=utf-8'), | ||||
|             ('Content-Length', str(len(content))) | ||||
|         ]) | ||||
|          | ||||
|         return [content] | ||||
|  | ||||
|  | ||||
| def main(): | ||||
|     """Start the server""" | ||||
|     print("=" * 60) | ||||
|     print("Markdown Editor with WebDAV Backend") | ||||
|     print("=" * 60) | ||||
|      | ||||
|     # Create application | ||||
|     app = MarkdownEditorApp() | ||||
|      | ||||
|     # Get server config | ||||
|     host = app.config['server']['host'] | ||||
|     port = app.config['server']['port'] | ||||
|      | ||||
|     print(f"\nServer starting on http://{host}:{port}") | ||||
|     print(f"\nAvailable collections:") | ||||
|     for name, config in app.collections.items(): | ||||
|         print(f"  - {name}: {config['description']}") | ||||
|         print(f"    WebDAV: http://{host}:{port}/fs/{name}/") | ||||
|      | ||||
|     print(f"\nWeb UI: http://{host}:{port}/") | ||||
|     print("\nPress Ctrl+C to stop the server") | ||||
|     print("=" * 60) | ||||
|      | ||||
|     # Create and start server | ||||
|     server = wsgi.Server( | ||||
|         bind_addr=(host, port), | ||||
|         wsgi_app=app | ||||
|     ) | ||||
|      | ||||
|     try: | ||||
|         server.start() | ||||
|     except KeyboardInterrupt: | ||||
|         print("\n\nShutting down...") | ||||
|         server.stop() | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     main() | ||||
|  | ||||
		Reference in New Issue
	
	Block a user