642 lines
14 KiB
Markdown
642 lines
14 KiB
Markdown
|
|
# WebDAV Server with aiohttp
|
||
|
|
|
||
|
|
A comprehensive, production-ready WebDAV server implementation with full RFC 4918 compliance, Windows Explorer compatibility, and enterprise-grade features.
|
||
|
|
|
||
|
|
## Features
|
||
|
|
|
||
|
|
### Core WebDAV Protocol
|
||
|
|
- ✅ **Full RFC 4918 Compliance**: All standard WebDAV methods implemented
|
||
|
|
- ✅ **Windows Explorer Integration**: Seamless compatibility with Windows WebDAV Mini-Redirector
|
||
|
|
- ✅ **Multiple Authentication Methods**: Basic, Digest, and Token-based authentication
|
||
|
|
- ✅ **Resource Locking**: Exclusive and shared locks with timeout management
|
||
|
|
- ✅ **Custom Properties**: Full property management (PROPFIND/PROPPATCH)
|
||
|
|
- ✅ **Collection Operations**: MKCOL, COPY, MOVE with depth support
|
||
|
|
|
||
|
|
### Enterprise Features
|
||
|
|
- 🔒 **Multi-User Support**: Isolated user directories with permissions
|
||
|
|
- 💾 **SQLite Database**: Robust backend for users, locks, and properties
|
||
|
|
- 🌐 **Web Management Interface**: Admin dashboard and user portal
|
||
|
|
- 🚀 **High Performance**: Async I/O with aiohttp and aiofiles
|
||
|
|
- 🔐 **Security Focused**: Input validation, path traversal prevention, SQL injection protection
|
||
|
|
- 📊 **Monitoring Ready**: Structured logging and metrics collection
|
||
|
|
- 🐳 **Docker Support**: Container-ready with docker-compose
|
||
|
|
- 📈 **Production Ready**: Gunicorn integration, health checks, graceful shutdown
|
||
|
|
|
||
|
|
## Quick Start
|
||
|
|
|
||
|
|
### Prerequisites
|
||
|
|
|
||
|
|
- Python 3.8 or higher
|
||
|
|
- pip (Python package manager)
|
||
|
|
- Optional: Redis for caching
|
||
|
|
- Optional: Docker for containerized deployment
|
||
|
|
|
||
|
|
### Installation
|
||
|
|
|
||
|
|
1. **Clone or create the project directory:**
|
||
|
|
```bash
|
||
|
|
mkdir webdav-server
|
||
|
|
cd webdav-server
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Install dependencies:**
|
||
|
|
```bash
|
||
|
|
pip install -r requirements.txt
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **Configure environment:**
|
||
|
|
```bash
|
||
|
|
cp .env.example .env
|
||
|
|
# Edit .env with your configuration
|
||
|
|
nano .env
|
||
|
|
```
|
||
|
|
|
||
|
|
4. **Generate a secure secret key:**
|
||
|
|
```bash
|
||
|
|
python -c "import secrets; print(secrets.token_hex(32))"
|
||
|
|
# Copy the output to JWT_SECRET_KEY in .env
|
||
|
|
```
|
||
|
|
|
||
|
|
5. **Run the server:**
|
||
|
|
```bash
|
||
|
|
python main.py
|
||
|
|
```
|
||
|
|
|
||
|
|
The server will start on `http://0.0.0.0:8080` by default.
|
||
|
|
|
||
|
|
### First Run
|
||
|
|
|
||
|
|
On first run, the server automatically creates:
|
||
|
|
- A default admin user: `admin` / `admin123`
|
||
|
|
- The WebDAV root directory structure
|
||
|
|
- SQLite database with all required tables
|
||
|
|
|
||
|
|
**⚠️ Important**: Change the default admin password immediately!
|
||
|
|
|
||
|
|
## Configuration
|
||
|
|
|
||
|
|
### Environment Variables
|
||
|
|
|
||
|
|
All configuration is done through the `.env` file. Key settings:
|
||
|
|
|
||
|
|
#### Server Configuration
|
||
|
|
```env
|
||
|
|
HOST=0.0.0.0 # Listen address
|
||
|
|
PORT=8080 # Listen port
|
||
|
|
SSL_ENABLED=false # Enable HTTPS
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Authentication
|
||
|
|
```env
|
||
|
|
AUTH_METHODS=basic,digest # Supported auth methods
|
||
|
|
JWT_SECRET_KEY=... # Secret for sessions (required)
|
||
|
|
SESSION_TIMEOUT=3600 # Session duration in seconds
|
||
|
|
```
|
||
|
|
|
||
|
|
#### WebDAV Settings
|
||
|
|
```env
|
||
|
|
WEBDAV_ROOT=./webdav # Root directory for files
|
||
|
|
MAX_FILE_SIZE=104857600 # Max file size (100MB)
|
||
|
|
MAX_PROPFIND_DEPTH=3 # Depth limit for PROPFIND
|
||
|
|
LOCK_TIMEOUT_DEFAULT=3600 # Default lock timeout
|
||
|
|
```
|
||
|
|
|
||
|
|
See `.env.example` for complete configuration options.
|
||
|
|
|
||
|
|
## Usage
|
||
|
|
|
||
|
|
### Connecting with Windows Explorer
|
||
|
|
|
||
|
|
#### Method 1: Map Network Drive
|
||
|
|
|
||
|
|
1. Open File Explorer
|
||
|
|
2. Right-click "This PC" → "Map network drive"
|
||
|
|
3. Choose a drive letter
|
||
|
|
4. Enter the WebDAV URL:
|
||
|
|
```
|
||
|
|
http://your-server:8080/
|
||
|
|
```
|
||
|
|
5. Check "Connect using different credentials"
|
||
|
|
6. Enter your username and password
|
||
|
|
7. Click "Finish"
|
||
|
|
|
||
|
|
#### Method 2: Add Network Location
|
||
|
|
|
||
|
|
1. Open File Explorer
|
||
|
|
2. Right-click "This PC" → "Add a network location"
|
||
|
|
3. Choose "Choose a custom network location"
|
||
|
|
4. Enter the WebDAV URL:
|
||
|
|
```
|
||
|
|
http://your-server:8080/
|
||
|
|
```
|
||
|
|
5. Enter credentials when prompted
|
||
|
|
|
||
|
|
#### Windows with SSL (Recommended)
|
||
|
|
|
||
|
|
For HTTPS connections:
|
||
|
|
```
|
||
|
|
https://your-server:8443/
|
||
|
|
```
|
||
|
|
|
||
|
|
**Note**: Windows requires port 443 for HTTPS WebDAV by default. To use other ports, modify Windows registry:
|
||
|
|
```
|
||
|
|
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters
|
||
|
|
BasicAuthLevel = 2
|
||
|
|
```
|
||
|
|
|
||
|
|
### Connecting with macOS Finder
|
||
|
|
|
||
|
|
1. Open Finder
|
||
|
|
2. Press `Cmd+K` (Go → Connect to Server)
|
||
|
|
3. Enter the WebDAV URL:
|
||
|
|
```
|
||
|
|
http://your-server:8080/
|
||
|
|
```
|
||
|
|
4. Click "Connect"
|
||
|
|
5. Enter your credentials
|
||
|
|
|
||
|
|
### Connecting with Linux (davfs2)
|
||
|
|
|
||
|
|
1. Install davfs2:
|
||
|
|
```bash
|
||
|
|
sudo apt-get install davfs2 # Ubuntu/Debian
|
||
|
|
```
|
||
|
|
|
||
|
|
2. Mount the WebDAV share:
|
||
|
|
```bash
|
||
|
|
sudo mount -t davfs http://your-server:8080/ /mnt/webdav
|
||
|
|
```
|
||
|
|
|
||
|
|
3. Enter credentials when prompted
|
||
|
|
|
||
|
|
### Command Line Tools
|
||
|
|
|
||
|
|
#### Using curl
|
||
|
|
|
||
|
|
**Upload a file:**
|
||
|
|
```bash
|
||
|
|
curl -u username:password -T file.txt http://localhost:8080/file.txt
|
||
|
|
```
|
||
|
|
|
||
|
|
**Download a file:**
|
||
|
|
```bash
|
||
|
|
curl -u username:password http://localhost:8080/file.txt -o file.txt
|
||
|
|
```
|
||
|
|
|
||
|
|
**Create a directory:**
|
||
|
|
```bash
|
||
|
|
curl -u username:password -X MKCOL http://localhost:8080/newdir/
|
||
|
|
```
|
||
|
|
|
||
|
|
**Delete a resource:**
|
||
|
|
```bash
|
||
|
|
curl -u username:password -X DELETE http://localhost:8080/file.txt
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Using cadaver
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cadaver http://localhost:8080/
|
||
|
|
# Enter credentials
|
||
|
|
dav:/> ls
|
||
|
|
dav:/> put localfile.txt
|
||
|
|
dav:/> get remotefile.txt
|
||
|
|
dav:/> mkcol newfolder
|
||
|
|
```
|
||
|
|
|
||
|
|
## User Management
|
||
|
|
|
||
|
|
### Creating Users Programmatically
|
||
|
|
|
||
|
|
```python
|
||
|
|
import asyncio
|
||
|
|
from main import Database
|
||
|
|
|
||
|
|
async def create_user():
|
||
|
|
db = Database('./webdav.db')
|
||
|
|
user_id = await db.create_user(
|
||
|
|
username='john',
|
||
|
|
password='SecurePass123!',
|
||
|
|
email='john@example.com',
|
||
|
|
role='user'
|
||
|
|
)
|
||
|
|
print(f"Created user with ID: {user_id}")
|
||
|
|
|
||
|
|
asyncio.run(create_user())
|
||
|
|
```
|
||
|
|
|
||
|
|
### User Roles
|
||
|
|
|
||
|
|
- **admin**: Full access to all features and user management
|
||
|
|
- **user**: Standard user with access to their own directory
|
||
|
|
- **readonly**: Read-only access (future implementation)
|
||
|
|
|
||
|
|
### Directory Structure
|
||
|
|
|
||
|
|
```
|
||
|
|
webdav/
|
||
|
|
├── users/
|
||
|
|
│ ├── admin/ # Admin user directory
|
||
|
|
│ ├── john/ # John's private directory
|
||
|
|
│ └── jane/ # Jane's private directory
|
||
|
|
├── shared/ # Shared between all users (optional)
|
||
|
|
└── public/ # Public access (optional)
|
||
|
|
```
|
||
|
|
|
||
|
|
## Production Deployment
|
||
|
|
|
||
|
|
### Using Gunicorn
|
||
|
|
|
||
|
|
Create `gunicorn_config.py`:
|
||
|
|
```python
|
||
|
|
import multiprocessing
|
||
|
|
|
||
|
|
bind = "0.0.0.0:8080"
|
||
|
|
workers = multiprocessing.cpu_count() * 2 + 1
|
||
|
|
worker_class = "aiohttp.GunicornWebWorker"
|
||
|
|
keepalive = 30
|
||
|
|
timeout = 60
|
||
|
|
accesslog = "./logs/access.log"
|
||
|
|
errorlog = "./logs/error.log"
|
||
|
|
loglevel = "info"
|
||
|
|
```
|
||
|
|
|
||
|
|
Run with Gunicorn:
|
||
|
|
```bash
|
||
|
|
gunicorn main:init_app --config gunicorn_config.py
|
||
|
|
```
|
||
|
|
|
||
|
|
### Using Docker
|
||
|
|
|
||
|
|
Create `Dockerfile`:
|
||
|
|
```dockerfile
|
||
|
|
FROM python:3.11-slim
|
||
|
|
|
||
|
|
WORKDIR /app
|
||
|
|
|
||
|
|
# Install dependencies
|
||
|
|
COPY requirements.txt .
|
||
|
|
RUN pip install --no-cache-dir -r requirements.txt
|
||
|
|
|
||
|
|
# Copy application
|
||
|
|
COPY . .
|
||
|
|
|
||
|
|
# Create directories
|
||
|
|
RUN mkdir -p /app/webdav /app/logs
|
||
|
|
|
||
|
|
# Expose port
|
||
|
|
EXPOSE 8080
|
||
|
|
|
||
|
|
# Run application
|
||
|
|
CMD ["python", "main.py"]
|
||
|
|
```
|
||
|
|
|
||
|
|
Create `docker-compose.yml`:
|
||
|
|
```yaml
|
||
|
|
version: '3.8'
|
||
|
|
|
||
|
|
services:
|
||
|
|
webdav:
|
||
|
|
build: .
|
||
|
|
ports:
|
||
|
|
- "8080:8080"
|
||
|
|
volumes:
|
||
|
|
- ./webdav:/app/webdav
|
||
|
|
- ./logs:/app/logs
|
||
|
|
- ./webdav.db:/app/webdav.db
|
||
|
|
environment:
|
||
|
|
- HOST=0.0.0.0
|
||
|
|
- PORT=8080
|
||
|
|
- DB_PATH=/app/webdav.db
|
||
|
|
- WEBDAV_ROOT=/app/webdav
|
||
|
|
restart: unless-stopped
|
||
|
|
|
||
|
|
# Optional: Redis for caching
|
||
|
|
redis:
|
||
|
|
image: redis:alpine
|
||
|
|
ports:
|
||
|
|
- "6379:6379"
|
||
|
|
restart: unless-stopped
|
||
|
|
```
|
||
|
|
|
||
|
|
Build and run:
|
||
|
|
```bash
|
||
|
|
docker-compose up -d
|
||
|
|
```
|
||
|
|
|
||
|
|
### Nginx Reverse Proxy
|
||
|
|
|
||
|
|
Create `/etc/nginx/sites-available/webdav`:
|
||
|
|
```nginx
|
||
|
|
server {
|
||
|
|
listen 80;
|
||
|
|
server_name webdav.example.com;
|
||
|
|
|
||
|
|
# Redirect to HTTPS
|
||
|
|
return 301 https://$server_name$request_uri;
|
||
|
|
}
|
||
|
|
|
||
|
|
server {
|
||
|
|
listen 443 ssl http2;
|
||
|
|
server_name webdav.example.com;
|
||
|
|
|
||
|
|
ssl_certificate /etc/letsencrypt/live/webdav.example.com/fullchain.pem;
|
||
|
|
ssl_certificate_key /etc/letsencrypt/live/webdav.example.com/privkey.pem;
|
||
|
|
|
||
|
|
# WebDAV specific settings
|
||
|
|
client_max_body_size 100M;
|
||
|
|
client_body_buffer_size 128k;
|
||
|
|
|
||
|
|
location / {
|
||
|
|
proxy_pass http://localhost:8080;
|
||
|
|
proxy_set_header Host $host;
|
||
|
|
proxy_set_header X-Real-IP $remote_addr;
|
||
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||
|
|
|
||
|
|
# WebDAV headers
|
||
|
|
proxy_set_header Destination $http_destination;
|
||
|
|
proxy_set_header Depth $http_depth;
|
||
|
|
|
||
|
|
# Timeouts
|
||
|
|
proxy_connect_timeout 60s;
|
||
|
|
proxy_send_timeout 60s;
|
||
|
|
proxy_read_timeout 60s;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Enable and reload:
|
||
|
|
```bash
|
||
|
|
sudo ln -s /etc/nginx/sites-available/webdav /etc/nginx/sites-enabled/
|
||
|
|
sudo nginx -t
|
||
|
|
sudo systemctl reload nginx
|
||
|
|
```
|
||
|
|
|
||
|
|
## Security Best Practices
|
||
|
|
|
||
|
|
### SSL/TLS Configuration
|
||
|
|
|
||
|
|
1. **Always use HTTPS in production**
|
||
|
|
2. Generate SSL certificates with Let's Encrypt:
|
||
|
|
```bash
|
||
|
|
sudo certbot certonly --nginx -d webdav.example.com
|
||
|
|
```
|
||
|
|
3. Update `.env`:
|
||
|
|
```env
|
||
|
|
SSL_ENABLED=true
|
||
|
|
SSL_CERT_PATH=/etc/letsencrypt/live/webdav.example.com/fullchain.pem
|
||
|
|
SSL_KEY_PATH=/etc/letsencrypt/live/webdav.example.com/privkey.pem
|
||
|
|
```
|
||
|
|
|
||
|
|
### Authentication
|
||
|
|
|
||
|
|
- Use **Digest authentication** over Basic when possible
|
||
|
|
- Enforce strong passwords (min length, complexity)
|
||
|
|
- Enable rate limiting to prevent brute force attacks
|
||
|
|
- Implement account lockout after failed attempts
|
||
|
|
|
||
|
|
### Firewall Rules
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Allow only necessary ports
|
||
|
|
sudo ufw allow 80/tcp
|
||
|
|
sudo ufw allow 443/tcp
|
||
|
|
sudo ufw enable
|
||
|
|
```
|
||
|
|
|
||
|
|
### Regular Updates
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Update dependencies
|
||
|
|
pip install --upgrade -r requirements.txt
|
||
|
|
|
||
|
|
# Backup database before updates
|
||
|
|
cp webdav.db webdav.db.backup
|
||
|
|
```
|
||
|
|
|
||
|
|
## Monitoring and Logging
|
||
|
|
|
||
|
|
### Log Files
|
||
|
|
|
||
|
|
Logs are stored in `./logs/` directory:
|
||
|
|
- `webdav.log` - Application logs
|
||
|
|
- `access.log` - Access logs (requests)
|
||
|
|
- `error.log` - Error logs
|
||
|
|
|
||
|
|
### Log Format
|
||
|
|
|
||
|
|
JSON-structured logs for easy parsing:
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"timestamp": "2025-01-15T10:30:45Z",
|
||
|
|
"level": "INFO",
|
||
|
|
"user": "john",
|
||
|
|
"method": "PROPFIND",
|
||
|
|
"path": "/documents/",
|
||
|
|
"status": 207,
|
||
|
|
"duration_ms": 45
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Health Check
|
||
|
|
|
||
|
|
Access the health check endpoint:
|
||
|
|
```bash
|
||
|
|
curl http://localhost:8080/health
|
||
|
|
```
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
### Windows Explorer Connection Issues
|
||
|
|
|
||
|
|
**Problem**: "The network folder is no longer available"
|
||
|
|
|
||
|
|
**Solutions**:
|
||
|
|
1. Increase Windows WebClient timeout:
|
||
|
|
```
|
||
|
|
Registry: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters
|
||
|
|
FileSizeLimitInBytes = DWORD: 0xFFFFFFFF
|
||
|
|
```
|
||
|
|
|
||
|
|
2. Enable Basic Authentication over HTTP (insecure - use only for testing):
|
||
|
|
```
|
||
|
|
Registry: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\WebClient\Parameters
|
||
|
|
BasicAuthLevel = 2
|
||
|
|
```
|
||
|
|
|
||
|
|
3. Restart WebClient service:
|
||
|
|
```powershell
|
||
|
|
net stop webclient
|
||
|
|
net start webclient
|
||
|
|
```
|
||
|
|
|
||
|
|
**Problem**: "The specified server cannot perform the requested operation"
|
||
|
|
|
||
|
|
**Solution**: Ensure the URL ends with a slash and doesn't include port 80:
|
||
|
|
- ❌ `http://server:80/path`
|
||
|
|
- ✅ `http://server/path/`
|
||
|
|
|
||
|
|
### macOS Finder Issues
|
||
|
|
|
||
|
|
**Problem**: Connection refused or timeout
|
||
|
|
|
||
|
|
**Solutions**:
|
||
|
|
1. Use HTTP URL with explicit protocol:
|
||
|
|
```
|
||
|
|
http://server:8080/
|
||
|
|
```
|
||
|
|
|
||
|
|
2. Check firewall settings on server
|
||
|
|
|
||
|
|
3. Try connecting via IP address instead of hostname
|
||
|
|
|
||
|
|
### Performance Issues
|
||
|
|
|
||
|
|
**Problem**: Slow PROPFIND responses
|
||
|
|
|
||
|
|
**Solutions**:
|
||
|
|
1. Reduce `MAX_PROPFIND_DEPTH` in `.env`:
|
||
|
|
```env
|
||
|
|
MAX_PROPFIND_DEPTH=1
|
||
|
|
```
|
||
|
|
|
||
|
|
2. Enable caching with Redis:
|
||
|
|
```env
|
||
|
|
CACHE_ENABLED=true
|
||
|
|
REDIS_HOST=localhost
|
||
|
|
```
|
||
|
|
|
||
|
|
3. Increase worker processes:
|
||
|
|
```env
|
||
|
|
WORKERS=8
|
||
|
|
```
|
||
|
|
|
||
|
|
### Database Locked Errors
|
||
|
|
|
||
|
|
**Problem**: "database is locked" errors
|
||
|
|
|
||
|
|
**Solutions**:
|
||
|
|
1. Enable WAL mode (automatic in code)
|
||
|
|
2. Ensure only one process accesses the database
|
||
|
|
3. Use separate databases for multiple instances
|
||
|
|
|
||
|
|
## API Documentation
|
||
|
|
|
||
|
|
### WebDAV Methods
|
||
|
|
|
||
|
|
#### PROPFIND - Property Discovery
|
||
|
|
|
||
|
|
```http
|
||
|
|
PROPFIND /documents/ HTTP/1.1
|
||
|
|
Depth: 1
|
||
|
|
Content-Type: application/xml
|
||
|
|
|
||
|
|
<?xml version="1.0"?>
|
||
|
|
<D:propfind xmlns:D="DAV:">
|
||
|
|
<D:allprop/>
|
||
|
|
</D:propfind>
|
||
|
|
```
|
||
|
|
|
||
|
|
#### PROPPATCH - Property Modification
|
||
|
|
|
||
|
|
```http
|
||
|
|
PROPPATCH /file.txt HTTP/1.1
|
||
|
|
Content-Type: application/xml
|
||
|
|
|
||
|
|
<?xml version="1.0"?>
|
||
|
|
<D:propertyupdate xmlns:D="DAV:">
|
||
|
|
<D:set>
|
||
|
|
<D:prop>
|
||
|
|
<D:displayname>My Document</D:displayname>
|
||
|
|
</D:prop>
|
||
|
|
</D:set>
|
||
|
|
</D:propertyupdate>
|
||
|
|
```
|
||
|
|
|
||
|
|
#### LOCK - Resource Locking
|
||
|
|
|
||
|
|
```http
|
||
|
|
LOCK /file.txt HTTP/1.1
|
||
|
|
Timeout: Second-3600
|
||
|
|
Content-Type: application/xml
|
||
|
|
|
||
|
|
<?xml version="1.0"?>
|
||
|
|
<D:lockinfo xmlns:D="DAV:">
|
||
|
|
<D:lockscope><D:exclusive/></D:lockscope>
|
||
|
|
<D:locktype><D:write/></D:locktype>
|
||
|
|
<D:owner>
|
||
|
|
<D:href>mailto:john@example.com</D:href>
|
||
|
|
</D:owner>
|
||
|
|
</D:lockinfo>
|
||
|
|
```
|
||
|
|
|
||
|
|
## Development
|
||
|
|
|
||
|
|
### Running Tests
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Install test dependencies
|
||
|
|
pip install pytest pytest-asyncio pytest-aiohttp pytest-cov
|
||
|
|
|
||
|
|
# Run tests
|
||
|
|
pytest tests/ -v
|
||
|
|
|
||
|
|
# Run with coverage
|
||
|
|
pytest tests/ --cov=. --cov-report=html
|
||
|
|
```
|
||
|
|
|
||
|
|
### Code Style
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Format code
|
||
|
|
black main.py
|
||
|
|
|
||
|
|
# Lint code
|
||
|
|
flake8 main.py
|
||
|
|
|
||
|
|
# Type checking
|
||
|
|
mypy main.py
|
||
|
|
```
|
||
|
|
|
||
|
|
### Contributing
|
||
|
|
|
||
|
|
1. Fork the repository
|
||
|
|
2. Create a feature branch
|
||
|
|
3. Make your changes
|
||
|
|
4. Add tests
|
||
|
|
5. Submit a pull request
|
||
|
|
|
||
|
|
## License
|
||
|
|
|
||
|
|
MIT License - See LICENSE file for details
|
||
|
|
|
||
|
|
## Support
|
||
|
|
|
||
|
|
For issues, questions, or contributions:
|
||
|
|
- GitHub Issues: [Create an issue]
|
||
|
|
- Documentation: [Wiki]
|
||
|
|
- Community: [Discussions]
|
||
|
|
|
||
|
|
## Changelog
|
||
|
|
|
||
|
|
### Version 1.0.0 (2025-01-15)
|
||
|
|
- Initial release
|
||
|
|
- Full RFC 4918 compliance
|
||
|
|
- Windows Explorer compatibility
|
||
|
|
- Multi-user support with SQLite backend
|
||
|
|
- Basic and Digest authentication
|
||
|
|
- Resource locking
|
||
|
|
- Custom properties
|
||
|
|
- Production-ready with Gunicorn support
|
||
|
|
|
||
|
|
## Acknowledgments
|
||
|
|
|
||
|
|
- RFC 4918 - HTTP Extensions for Web Distributed Authoring and Versioning (WebDAV)
|
||
|
|
- aiohttp - Async HTTP client/server framework
|
||
|
|
- Python community
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Made with ❤️ for the WebDAV community**
|