Files
markdown_editor/server_webdav.py
2025-10-26 07:49:26 +04:00

199 lines
6.2 KiB
Python
Executable File

#!/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()
server.wait()
except KeyboardInterrupt:
print("\n\nShutting down...")
server.stop()
if __name__ == '__main__':
main()