Add initial project structure and documentation

- Created architecture documentation outlining high-level design, module organization, data flow, security model, performance considerations, testing strategy, and deployment architecture.
- Added pyproject.toml for project metadata and dependencies management.
- Introduced requirements files for development and production dependencies.
- Set up testing configuration with pytest and tox.
- Established basic directory structure for source code and tests, including __init__.py files.
- Implemented a sample web application (index.html) for drag-and-drop functionality.
- Configured VS Code workspace settings for Python development.
This commit is contained in:
claudi 2026-01-28 10:48:36 +01:00
commit 61aa33633c
34 changed files with 5342 additions and 0 deletions

291
docs/ARCHITECTURE.md Normal file
View file

@ -0,0 +1,291 @@
# Architecture Guide
## High-Level Design
```
┌──────────────────────────────────────────────────────────┐
│ Application Layer (UI) │
│ ┌────────────────────────────────────────────────────────┤
│ │ QMainWindow (main_window.py) │
│ │ ├─ QWebEngineView (web app container) │
│ │ └─ DragInterceptor (drag handling) │
│ └────────────────────────────────────────────────────────┘
│ ↓ Events (dragEnterEvent, etc.)
├──────────────────────────────────────────────────────────┤
│ Business Logic Layer (Core) │
│ ┌────────────────────────────────────────────────────────┤
│ │ PathValidator (security & validation) │
│ │ DragInterceptor (drag/drop conversion) │
│ │ Config (configuration management) │
│ └────────────────────────────────────────────────────────┘
│ ↓ Platform APIs
├──────────────────────────────────────────────────────────┤
│ Platform Layer (OS Integration) │
│ ┌────────────────────────────────────────────────────────┤
│ │ QUrl (Windows: CF_HDROP, macOS: NSFilenamesPboardType)│
│ │ QMimeData (cross-platform mime data) │
│ │ QDrag (native drag-drop implementation) │
│ └────────────────────────────────────────────────────────┘
```
## Module Organization
### `src/webdrop_bridge/core/`
**Purpose**: Core business logic, independent of UI
**Key Components:**
- `validator.py`: Path validation against whitelist
- `drag_interceptor.py`: Drag event handling and conversion
- `config.py`: Configuration management
- `errors.py`: Custom exception classes
**Dependencies**: None (only stdlib + pathlib)
### `src/webdrop_bridge/ui/`
**Purpose**: User interface components using Qt/PySide6
**Key Components:**
- `main_window.py`: Main application window
- `widgets.py`: Reusable custom widgets
- `styles.py`: UI styling and themes
**Dependencies**: PySide6, core/
### `src/webdrop_bridge/utils/`
**Purpose**: Shared utilities and helpers
**Key Components:**
- `logging.py`: Logging configuration
- `constants.py`: Application constants
- `helpers.py`: General-purpose helper functions
**Dependencies**: stdlib only
## Data Flow
### Drag-and-Drop Operation
```
User in Web App
[dragstart event] → JavaScript sets dataTransfer.text = "Z:\path\file.txt"
[dragend event] → Drag leaves WebEngine widget
DragInterceptor.dragEnterEvent() triggered
Extract text from QMimeData
PathValidator.is_valid_file(path)
├─ is_allowed(path) → Check whitelist
└─ path.exists() and path.is_file() → File system check
If valid:
→ Create QUrl.fromLocalFile(path)
→ Create new QMimeData with URLs
→ QDrag.exec() → Native file drag
If invalid:
→ event.ignore()
→ Log warning
OS receives native file drag
InDesign/Word receives file handle
```
## Security Model
### Path Validation Strategy
1. **Whitelist-based**: Only allow configured root directories
2. **Absolute path resolution**: Prevent directory traversal attacks
3. **Symlink handling**: Resolve to real path before checking
4. **File existence check**: Don't drag non-existent files
5. **Type verification**: Only allow regular files
```python
# Example allowed roots
ALLOWED_ROOTS = [
Path("Z:/"), # Network share
Path("C:/Users/Public"), # Windows public folder
]
# Example validation
path = Path(r"Z:\projects\file.psd")
→ Resolve: Z:\projects\file.psd
→ Check: starts with Z:\? YES
→ Check: file exists? YES
→ Check: is regular file? YES
→ Result: ALLOW
```
### Web Engine Security
- **LocalContentCanAccessFileUrls**: Enabled (required for drag)
- **LocalContentCanAccessRemoteUrls**: Disabled (prevent phishing)
- **Certificate validation**: Enforced for HTTPS
## Performance Considerations
### Drag Operation Timing
```
Event Time (ms) Critical Path
─────────────────────────────────────
dragenter <1 Must be fast
validation <10 Keep fast
QUrl creation <1
QDrag.exec() <50 Start quickly
─────────────────────────
TOTAL <50 Imperceptible
```
### Memory Management
- **Web engine**: ~100-150 MB baseline
- **Drag interceptor**: Minimal overhead
- **Logging**: Rotated to prevent growth
- **File cache**: None (on-demand validation)
## Testing Strategy
### Unit Testing
```
validator.py
├─ test_is_allowed()
├─ test_is_valid_file()
└─ test_symlink_handling()
config.py
├─ test_load_from_env()
├─ test_default_values()
└─ test_validation()
drag_interceptor.py
├─ test_dragEnterEvent()
├─ test_invalid_path_rejected()
└─ test_file_drag_created()
```
### Integration Testing
```
end_to_end.py
├─ test_app_startup()
├─ test_webapp_loads()
├─ test_complete_drag_workflow()
└─ test_invalid_path_handling()
```
## Extension Points
### Adding New Handlers
```python
# Future: Support for additional file types or sources
class HandlerRegistry:
def register(self, source_type: str, handler: Handler):
self.handlers[source_type] = handler
def handle(self, data: QMimeData) -> Optional[QMimeData]:
for handler in self.handlers.values():
if handler.can_handle(data):
return handler.handle(data)
return None
```
### Custom Validators
```python
# Future: Pluggable validation rules
class ValidatorPlugin(ABC):
@abstractmethod
def validate(self, path: Path) -> bool:
pass
class FileSizeValidator(ValidatorPlugin):
def __init__(self, max_size: int):
self.max_size = max_size
def validate(self, path: Path) -> bool:
return path.stat().st_size <= self.max_size
```
## Deployment Architecture
### Single-File Executable
```
WebDropBridge.exe (built with PyInstaller)
├─ Python runtime
├─ PySide6 libraries
├─ Qt libraries
├─ Embedded web app (index.html)
└─ Bundled resources (icons, etc.)
Size: ~100-120 MB
Startup: <1 second
```
### Portable Deployment
- Single executable: No installation required
- No registry entries: Clean removal
- Relative paths: Can run from any directory
- Configuration: `.env` file per instance
## Platform Differences
### Windows
- **Drag Format**: CF_HDROP (file paths)
- **File URLs**: `file:///C:/path/to/file`
- **Paths**: Backslash `\` (converted to forward `/`)
- **Permissions**: Admin privileges not required
### macOS
- **Drag Format**: NSFilenamesPboardType (same as Windows, different implementation)
- **File URLs**: `file:///Users/username/path/to/file`
- **Paths**: Forward slash `/` (native)
- **Permissions**: May require accessibility permissions
## Monitoring & Debugging
### Debug Logging
```python
# Enable debug logging
LOG_LEVEL=DEBUG
# Output
2026-01-28 14:32:15 - webdrop_bridge - DEBUG - DragInterceptor: dragEnterEvent triggered
2026-01-28 14:32:15 - webdrop_bridge - DEBUG - PathValidator: Checking Z:\file.psd
2026-01-28 14:32:15 - webdrop_bridge - INFO - File dragged: Z:\file.psd
```
### Performance Profiling
```python
import cProfile
import pstats
profiler = cProfile.Profile()
profiler.enable()
# ... drag operation ...
profiler.disable()
stats = pstats.Stats(profiler)
stats.print_stats()
```
---
For more details, see [DEVELOPMENT_PLAN.md](DEVELOPMENT_PLAN.md)