Release v1.0.0: autoinstall main.py, plattformübergreifender Start, Doku komplett

This commit is contained in:
Adam Skotarczak 2025-04-21 21:32:41 +02:00
parent 999cfd2c12
commit 369bd1c536
32 changed files with 240 additions and 596 deletions

27
.gitignore vendored
View File

@ -1,37 +1,32 @@
# Build- und Cache-Ordner ignorieren
.dev/__pycache__/
.dev/upload/
.dev/logs/
.dev/build/
.dev/dist/
.app/__pycache__/
.app/upload/
.app/logs/
.app/build/
.app/dist/
# PyInstaller & kompiliertes Python
*.spec
#*.exe
*.dll
*.so
*.dylib
*.pyd
# Virtuelle Umgebung ignorieren (falls vorhanden)
.dev/.venv/
.dev/.env/
.dev/.idea/
.dev/.vscode/
.app/.venv/
.app/.env/
.app/.idea/
.app/.vscode/
# Zertifikate und sensible Dateien
.dev/*.pem
.app/*.pem
*.crt
*.key
# Sonstiges (Windows & macOS Systemdateien)
Desktop.ini
#Desktop.ini
Thumbs.db
.DS_Store
# .exe-Dateien überall ignorieren...
*.exe
# ... außer im .dev/Output/ Verzeichnis (Ausnahme!)
!./Output/
!./Output/*

24
CHANGELOG.md Normal file
View File

@ -0,0 +1,24 @@
# CHANGELOG
## pyUpload (TKInter-Version 1.0)
- 2025-04-21
- Geändert
- `main.py` übernimmt nun automatisch die Erstellung der virtuellen Umgebung `.venv` und die Installation der Abhängigkeiten aus `requirements.txt`
- Entfernt: `install.cmd` und `startUpload.cmd` wurden vollständig ersetzt durch neue Startlogik
- `start.cmd` wurde vereinfacht, prüft nun auf vorhandenes `python` und startet `main.py` über absoluten Pfad
- `main.py` setzt bei Start automatisch `os.chdir()` auf das eigene Verzeichnis, um relative Pfade sicher zu behandeln
- Hinzugefügt
- Neue plattformunabhängige `start.sh` für Linux/macOS
- Automatischer Restart nach Installation über `os.execv()` in `main.py`
- Neue Sicherheits- und Netzwerkinformationen in der `README.md`
- Erweiterung der `README.md` um Speicherort der Uploads und Projektstruktur
- Hinweis auf Projektstatus und neue Version unter <https://github.com/realAscot/pyUpload2>
- Neue LICENSE-Datei (proprietär, nicht zur Weitergabe)
- Fixes
- PowerShell-Inkompatibilitäten mit `set /p` entfernt
- `.cmd`-Startskripte reagieren jetzt korrekt auf STRG+C
- Mehrere Markdown-Korrekturen (Codeblöcke, Leerzeilen, Lesbarkeit)

17
LICENSE Normal file
View File

@ -0,0 +1,17 @@
# LICENSE
Copyright (c) 2025 Adam Skotarczak <adam@skotarczak.net>
Alle Rechte vorbehalten.
Diese Software ist urheberrechtlich geschützt. Die Verwendung, Vervielfältigung, Verbreitung oder Änderung
dieser Software oder von Teilen davon ist ohne vorherige schriftliche Genehmigung des Autors ausdrücklich untersagt.
Die Software darf ausschließlich für private, interne oder zu Prüf- und Testzwecken verwendet werden.
Eine kommerzielle Nutzung oder Weitergabe an Dritte ist nicht gestattet.
Die Bereitstellung dieser Software erfolgt OHNE JEGLICHE GARANTIE,
weder ausdrücklich noch stillschweigend, einschließlich, aber nicht beschränkt auf die Garantien
der Marktgängigkeit oder Eignung für einen bestimmten Zweck.
Durch die Nutzung dieser Software erklärst du dich mit den oben genannten Bedingungen einverstanden.

View File

@ -1,35 +0,0 @@
.PHONY: exe release clean
# Variablen
PYTHON = python3
SCRIPT = pyUpload.py
EXE_NAME = pyUpload
BUILD_DIR = build
DIST_DIR = release
REQ_FILE = requirements.txt
exe:
@echo "Erzeuge ausführbare Datei..."
@mkdir -p $(BUILD_DIR)
pyinstaller --onefile --add-data "template.html;." --add-data "success.html;." --add-data "favicon.ico;." --windowed --icon favicon.ico --name $(EXE_NAME) $(SCRIPT)
@mv dist/$(EXE_NAME) $(BUILD_DIR)/
@echo "Erstellung abgeschlossen: $(BUILD_DIR)/$(EXE_NAME)"
release: exe
@echo "Erstelle Release-Paket..."
@mkdir -p $(DIST_DIR)
@cp $(BUILD_DIR)/$(EXE_NAME) $(DIST_DIR)/
@cp $(REQ_FILE) $(DIST_DIR)/
@cp template.html success.html favicon.ico $(DIST_DIR)/
@echo "Release-Paket bereit in $(DIST_DIR)"
zip: release
@echo "Erstelle ZIP-Archiv..."
@cd $(DIST_DIR) && zip -r ../$(ZIP_NAME) $(EXE_NAME) template.html success.html favicon.ico
@echo "ZIP-Archiv erstellt: $(ZIP_NAME)"
clean:
@echo "Bereinige Projektverzeichnis..."
@rm -rf $(BUILD_DIR) $(DIST_DIR) build dist __pycache__ *.spec
@echo "Bereinigung abgeschlossen."

175
README.md
View File

@ -1,104 +1,137 @@
## pyUpload - Sicherer Datei-Upload-Server für eine einfache und sichere Dateiübertragung
# pyUpload (TKInter Version)
### Beschreibung
![pyUpload 1.0 Logo](./assets/logo-1.0-alpha.png)
pyUpload ist eine leistungsstarke und dennoch einfache Lösung für den sicheren Datei-Upload über HTTPS. Es richtet sich an Privatnutzer, die eine schnelle Möglichkeit suchen, Dateien zwischen Geräten zu übertragen, sowie an Unternehmen oder Teams, die eine sichere Lösung für den internen Datenaustausch benötigen. Dieses Programm eignet sich ideal, um Dateien schnell und unkompliziert von einem Smartphone oder einem anderen Gerät auf einen Computer zu übertragen.
## ⚠️ Projektstatus: Eingefroren Nur noch Bugfixes
Anstatt zusätzliche Apps oder USB-Kabel zu nutzen, kann der Benutzer den Server starten, den automatisch generierten QR-Code mit dem Smartphone scannen und die Dateien direkt über die Weboberfläche hochladen. Der Computer speichert die hochgeladenen Dateien strukturiert in individuellen Verzeichnissen für jedes Gerät.
Diese Version von **pyUpload** wird **nicht weiterentwickelt** und erhält nur noch Fehlerbehebungen.
Die neue Version mit Flask-Backend ist **BALD** verfügbar unter:
Zusätzlich erstellt pyUpload bei Bedarf automatisch ein selbstsigniertes SSL-Zertifikat, um eine verschlüsselte Verbindung sicherzustellen. Damit bleibt die Dateiübertragung geschützt und zuverlässig.
🔗 **<https://github.com/realAscot/pyUpload2>**
### Features Die Vorteile von pyUpload auf einen Blick
---
- **Sichere Dateiübertragung per HTTPS** Alle Daten werden verschlüsselt übertragen.
- **Automatische Erstellung eines selbstsignierten SSL-Zertifikats** Keine zusätzliche Konfiguration notwendig.
- **Intuitive, webbasierte Benutzeroberfläche** Einfach zu bedienen, keine Installation erforderlich.
- **Strukturierte Speicherung** Dateien werden in client-spezifischen Verzeichnissen gespeichert.
- **Zentralisierte und client-spezifische Logging-Funktion** Detaillierte Nachverfolgung aller Uploads.
- **Flexible Nutzung mit oder ohne GUI** Start als Desktop-Anwendung oder reine Konsolen-Version möglich.
- **Schnelle Einrichtung** Download, Entpacken und sofort loslegen!
## pyUpload Sicherer Datei-Upload-Server über HTTPS (lokal & offline)
### Installationsanleitung So startest du pyUpload
Diese Version basiert auf **Tkinter (GUI + QR)** sowie einer optionalen **reinen CLI-Nutzung**.
Sie ist vollständig lokal lauffähig ganz ohne Installation von externen Tools oder komplexen Abhängigkeiten.
Es gibt drei Möglichkeiten, pyUpload zu nutzen:
- **Manuelle Installation** in einer vorhandenen Python-Umgebung, in der alle notwendigen Bibliotheken manuell installiert werden.
- **Nutzung einer vorgefertigten, ausführbaren .exe (Windows) Version**, die mit pyinstaller kompiliert wurde.
- **Nutzung in einer virtuellen Python-Umgebung**, die automatisch durch `install.bat` eingerichtet wird. Anschließend kann das Programm mit `start.bat` gestartet werden.
---
#### 1. Manuelle Installation für Python-Nutzer
## 🛠 Features
1. Stelle sicher, dass **Python 3** auf deinem System installiert ist.
2. Installiere alle benötigten Abhängigkeiten mit folgendem Befehl:
- **HTTPS-gesicherter Datei-Upload**
- **Selbstsigniertes SSL-Zertifikat bei Bedarf**
- **QR-Code-basierte Verbindung für Smartphones**
- **Client-spezifische Verzeichnisse und Logs**
- **GUI und Konsolen-Modus verfügbar**
- **automatische Einrichtung von `.venv` und Abhängigkeiten**
- **kein Installationsskript mehr nötig alles passiert beim Start von `main.py`**
```
pip install -r requirements.txt
```
3. Starte den Server mit:
---
```
python pyUpload.py
```
4. Falls du keine grafische Benutzeroberfläche benötigst, kannst du den Server im Konsolenmodus starten:
## 🚀 Schnellstart
```
python pyUpload.py --nogui
### ▶️ Für Windows:
1. Lade das Projekt herunter oder klone es:
```sh
git clone https://github.com/realAscot/pyUpload
```
#### 2. Nutzung der fertigen .exe Download-Version
2. Starte die App mit:
Falls du keine Python-Installation benötigst, kannst du die vorgefertigte **ZIP-Version** von pyUpload herunterladen. Diese enthält bereits alle notwendigen Dateien und ist sofort einsatzbereit.
1. Lade die neueste **pyUpload.zip** von der offiziellen Website herunter, inklusive virtueller Umgebung.
2. Entpacke die ZIP-Datei in einen beliebigen Ordner.
3. Starte die enthaltene `pyUpload.exe`.
4. Falls die grafische Benutzeroberfläche nicht benötigt wird, kann die `pyUpload.exe` direkt in der Konsole mit `--nogui` gestartet werden:
```
pyUpload.exe --nogui
```
5. Eine Übersicht aller verfügbaren Befehle und Optionen erhältst du mit:
```
pyUpload.exe --help
```cmd
start.cmd
```
#### 3. Nutzung in virtueller Python-Umgebung
Alternativ in PowerShell:
Lade die ZIP-Datei mit der virtuellen Installationsumgebung herunter und starte die `install.bat` durch Doppelklick. Es öffnet sich ein Konsolenfenster (CLI) und eine virtuelle Umgebung für pyUpload wird erstellt. Anschließend kann das Programm mit der `start.bat` gestartet werden. In der Grundeinstellung wird eine GUI mit QR-Code gestartet.
```powershell
cmd /c start.cmd
```
### Zugriff auf die Weboberfläche
### 🐧 Für Linux / macOS:
- Sobald der Server läuft, kann er über die lokale IP-Adresse aufgerufen werden:
```
https://<server-ip>:4443
```
- Falls die GUI-Version gestartet wurde, erscheint ein **QR-Code**, der die Verbindungsadresse enthält. Dies ermöglicht eine einfache Verbindung mit Smartphones und Tablets.
1. Stelle sicher, dass Python 3.8+ installiert ist:
### Datei-Upload leicht gemacht So funktioniert es
```bash
python3 --version
```
1. Öffne die **Weboberfläche** im Browser.
2. Wähle die gewünschte **Datei aus** und klicke auf **„Hochladen“**.
3. Nach erfolgreichem Upload erscheint eine **Bestätigungsseite**, die den Abschluss der Übertragung bestätigt.
2. Mache das Startscript ausführbar:
### SSL-Zertifikatswarnung in Browsern umgehen
```bash
chmod +x start.sh
```
Da pyUpload ein **selbstsigniertes SSL-Zertifikat** nutzt, wird es beim ersten Zugriff zu einer Warnung des Browsers kommen. Alternativ kann ein eigenes SSL-Zertifikat hinterlegt werden, indem die Zertifikats- und Schlüsseldateien `cert.pem` und `key.pem` durch eigene, signierte Zertifikate ersetzt werden. Um die verschlüsselte Verbindung zu akzeptieren, gibt es zwei Möglichkeiten:
3. Starte die App:
- In den meisten Browsern gibt es eine Option wie **„Erweitert“** oder **„Trotzdem fortfahren“**, um die Warnung zu übergehen.
- Alternativ kann das Zertifikat **manuell importiert und als vertrauenswürdig markiert** werden, um künftige Warnmeldungen zu vermeiden.
```bash
./start.sh
```
### Logging und Fehlerbehandlung Transparenz und Kontrolle
---
- Alle **Uploads und Anfragen** werden in **zentralen sowie client-spezifischen Logdateien** gespeichert. Diese befinden sich im `logs/`-Verzeichnis.
- Falls während der Nutzung von pyUpload **Probleme auftreten**, bietet ein Blick in diese Logdateien wertvolle Hinweise zur Fehlerbehebung.
Beim ersten Start wird automatisch:
### Autoren & Mitwirkende
- eine virtuelle Umgebung `.venv/` im `app/`-Verzeichnis erzeugt
- `requirements.txt` installiert
- das Programm danach neu aus der Umgebung gestartet
Danke an alle, die zu diesem Projekt beigetragen haben!
---
- **[Adam Skotarczak](https://github.com/AJaquet)** - Projektleitung & Entwicklung
## 🧩 Kommandozeilenoptionen
### Lizenz und Autor
```sh
python app\main.py --nogui # Start ohne GUI / QR
python app\main.py --port 9999 # Custom-Port verwenden
```
- **Entwickelt von Adam Skotarczak (C) 2025**.
---
## 🌐 Zugriff im Browser
Sobald gestartet:
```https
https://<lokale-IP>:4443
```
Alternativ QR-Code scannen (GUI-Modus).
Dateien werden im `upload/<Client-IP>/` gespeichert.
---
## 📁 Logs & Uploads
- **Uploads**: im Ordner `upload/` nach Client-IP
- **Zentrale Logs**: `logs/pyupload.log`
- **Pro-Client Logs**: `logs/<Client-IP>.log`
---
## 🔐 Hinweis zur SSL-Zertifikatswarnung
Beim ersten Aufruf im Browser erscheint eine Warnung wegen des selbstsignierten Zertifikats.
Du kannst:
- auf **„Erweitert“ > „Trotzdem fortfahren“** klicken
- eigene Zertifikate in `cert.pem` und `key.pem` hinterlegen
---
## 👨‍💻 Autor
- **Adam Skotarczak**
Kontakt: [adam@skotarczak.net](mailto:adam@skotarczak.net)
GitHub: [realAscot](https://github.com/realAscot)
---
## 📝 Lizenz
- Proprietär, © 2025 Adam Skotarczak
**Keine Weitergabe ohne ausdrückliche Genehmigung**

View File

@ -1,4 +0,0 @@
# Entwicklertagebuch
## 08.03.25
Heute habe ich die mit innoSetup auf einem Rechner installiert auf dem überhaupt kein Python installiert war und dies funktionierte nicht. Klar auch, die venv Umgebung setzt eine Python Installation vorraus und arbeitet mit absoluten Pfaden. Der nächste Versuch wird mit einer portablen Python Version durchgeführt.

View File

@ -1 +1 @@
1.0.1
1.0.0

BIN
app/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View File

@ -6,6 +6,30 @@
import os
import sys
import subprocess
# Pfade definieren
os.chdir(os.path.dirname(os.path.abspath(__file__)))
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
VENV_DIR = os.path.join(BASE_DIR, ".venv")
VENV_PYTHON = os.path.join(VENV_DIR, "Scripts", "python.exe") if os.name == "nt" else os.path.join(VENV_DIR, "bin", "python")
REQUIREMENTS_FILE = os.path.join(BASE_DIR, "requirements.txt")
# Wenn wir NICHT in der venv sind → prüfen, ob venv existiert
if sys.prefix == sys.base_prefix and not os.path.exists(VENV_DIR):
print("[Setup] Virtuelle Umgebung wird erstellt...")
subprocess.run([sys.executable, "-m", "venv", "--copies", VENV_DIR], check=True)
print("[Setup] requirements.txt wird installiert...")
subprocess.run([VENV_PYTHON, "-m", "pip", "install", "--upgrade", "pip"], check=True)
subprocess.run([VENV_PYTHON, "-m", "pip", "install", "-r", REQUIREMENTS_FILE], check=True)
print("[Setup] Starte erneut mit aktivierter Umgebung...")
os.execv(VENV_PYTHON, [VENV_PYTHON] + sys.argv)
# Wir sind jetzt sicher in der richtigen Umgebung → Rest des Programms geht hier weiter:
import ssl
import logging
import socket
@ -13,43 +37,35 @@ import argparse
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler
from datetime import datetime, timedelta, timezone
from cryptography import x509
from cryptography.x509.oid import NameOID
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
# -------------------------------------------------------------------
# Virtuelle Umgebung (aktuell noch in der pyUpload.bat)
# Virtuelle Umgebung aktivieren (wenn nicht aktiv)
# -------------------------------------------------------------------
# Name der virtuellen Umgebung
VENV_DIR = ".venv"
def activate_venv():
"""Aktiviert die virtuelle Umgebung automatisch, falls sie nicht aktiv ist."""
if sys.prefix != os.path.abspath(VENV_DIR):
venv_path = os.path.join(os.path.dirname(__file__), VENV_DIR)
if os.name == "nt": # Windows
if os.name == "nt":
activate_script = os.path.join(venv_path, "Scripts", "activate.bat")
else: # Linux/macOS
else:
activate_script = os.path.join(venv_path, "bin", "activate")
if os.path.exists(activate_script):
print(f"Aktiviere virtuelle Umgebung: {VENV_DIR}")
os.system(f'"{activate_script}"')
else:
print(f"FEHLER: Virtuelle Umgebung nicht gefunden ({VENV_DIR}). Bitte zuerst install.bat ausführen.")
print(f"FEHLER: Virtuelle Umgebung nicht gefunden ({VENV_DIR}). Bitte zuerst install.cmd ausführen.")
sys.exit(1)
# Virtuelle Umgebung aktivieren, wenn sie nicht aktiv ist
activate_venv()
# -------------------------------------------------------------------
# Globale Konfigurationen & Verzeichnisse
# Globale Konfigurationen & Logging
# -------------------------------------------------------------------
UPLOAD_DIR = 'upload'
LOG_DIR = 'logs'
CERT_FILE = 'cert.pem'
@ -69,15 +85,13 @@ central_logger = logging.getLogger("central_logger")
client_loggers = {}
# -------------------------------------------------------------------
# HTTP-Request-Handler
# HTTP-Handler für Upload und Logging
# -------------------------------------------------------------------
class SecureHTTPRequestHandler(BaseHTTPRequestHandler):
def log_message(self, format, *args):
"""Sammelt Log-Einträge in Client-spezifische Dateien und die Zentrale-Logdatei."""
client_ip = self.client_address[0]
message = format % args
# Einmalig pro Client-IP einen dedizierten Logger anlegen.
if client_ip not in client_loggers:
logger = logging.getLogger(f'client_{client_ip}')
logger.setLevel(logging.INFO)
@ -86,13 +100,10 @@ class SecureHTTPRequestHandler(BaseHTTPRequestHandler):
handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))
logger.addHandler(handler)
client_loggers[client_ip] = logger
# In den Client-spezifischen Logger und in den zentralen Logger schreiben.
client_loggers[client_ip].info(message)
central_logger.info(f"{client_ip} - {message}")
def send_html_response(self, filename):
"""Liefert eine HTML-Datei als HTTP-Response zurück."""
try:
with open(filename, "r", encoding="utf-8") as f:
content = f.read()
@ -104,7 +115,6 @@ class SecureHTTPRequestHandler(BaseHTTPRequestHandler):
self.send_error(500, "HTML-Template nicht gefunden")
def do_GET(self):
"""Ausliefern der Upload-Seite (template.html)."""
if self.path == '/':
self.send_html_response("template.html")
else:
@ -112,63 +122,50 @@ class SecureHTTPRequestHandler(BaseHTTPRequestHandler):
self.log_message('404 Not Found: %s', self.path)
def do_POST(self):
"""Behandelt Datei-Uploads (multipart/form-data) direkt als Stream."""
try:
content_type = self.headers.get('Content-Type')
content_length = int(self.headers.get('Content-Length', 0))
if not content_type or 'multipart/form-data' not in content_type:
self.send_error(400, "Ungültiger Content-Type")
self.log_message('400 Bad Request: Ungültiger Content-Type')
return
if content_length == 0:
self.send_error(400, "Leere Anfrage erhalten")
self.log_message('400 Bad Request: Leere Anfrage')
return
client_ip = self.client_address[0]
client_upload_dir = os.path.join(UPLOAD_DIR, client_ip)
os.makedirs(client_upload_dir, exist_ok=True)
boundary = content_type.split("boundary=")[-1].encode()
raw_data = self.rfile.read(content_length)
parts = raw_data.split(b"--" + boundary)
found_file = False
for part in parts:
if b"Content-Disposition" in part:
headers, file_data = part.split(b"\r\n\r\n", 1)
filename_start = headers.find(b'filename="') + 10
filename_end = headers.find(b'"', filename_start)
filename = headers[filename_start:filename_end].decode()
if filename: # Falls tatsächlich ein Dateiname vorhanden ist
if filename:
file_path = os.path.join(client_upload_dir, os.path.basename(filename))
with open(file_path, "wb") as f:
# Entferne das trailing CRLF oder "--"
f.write(file_data.rstrip(b"\r\n--"))
self.log_message(f"Datei {filename} erfolgreich hochgeladen.")
found_file = True
if not found_file:
self.send_error(400, "Keine Datei im Upload enthalten")
self.log_message('400 Bad Request: Keine Datei übermittelt')
return
# Erfolgsseite senden
self.send_html_response("success.html")
except Exception as e:
self.log_message(f"Fehler: {e}")
self.send_error(500, "Interner Serverfehler")
# -------------------------------------------------------------------
# HTTPS-Server-Setup
# HTTPS-Server mit selbstsigniertem Zertifikat
# -------------------------------------------------------------------
def generate_self_signed_cert(cert_file, key_file):
"""Erzeugt ein selbstsigniertes SSL-Zertifikat, falls keines vorhanden ist."""
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
subject = issuer = x509.Name([
x509.NameAttribute(NameOID.COUNTRY_NAME, "DE"),
@ -191,7 +188,6 @@ def generate_self_signed_cert(cert_file, key_file):
)
.sign(key, hashes.SHA256())
)
with open(cert_file, "wb") as f:
f.write(cert.public_bytes(serialization.Encoding.PEM))
with open(key_file, "wb") as f:
@ -202,7 +198,6 @@ def generate_self_signed_cert(cert_file, key_file):
))
def get_server_ip():
"""Bestimmt die lokale IP-Adresse, um sie z.B. für den QR-Code zu nutzen."""
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
s.connect(("8.8.8.8", 80))
@ -214,63 +209,51 @@ def get_server_ip():
return ip
def create_https_server(port, handler_class=SecureHTTPRequestHandler):
"""Erzeugt ein HTTPS-Serverobjekt (aber startet ihn noch nicht)."""
if not os.path.exists(CERT_FILE) or not os.path.exists(KEY_FILE):
print("SSL-Zertifikat nicht gefunden. Erstelle selbstsigniertes Zertifikat...")
generate_self_signed_cert(CERT_FILE, KEY_FILE)
print("Selbstsigniertes SSL-Zertifikat erstellt.")
server_address = ('', port)
httpd = HTTPServer(server_address, handler_class)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)
httpd.socket = context.wrap_socket(httpd.socket, server_side=True)
return httpd
# -------------------------------------------------------------------
# Startfunktionen (GUI / no-GUI)
# Server starten (mit oder ohne GUI)
# -------------------------------------------------------------------
def run_server_nogui(port):
httpd = create_https_server(port)
server_ip = get_server_ip()
try:
print(f"Starte HTTPS-Server auf https://{server_ip}:{port}")
print("Drücke STRG+C, um zu beenden.")
print("warte auf Verbindungen ... \n")
print("warte auf Verbindungen ...")
print(f"Öffne im Browser: https://{server_ip}:{port}")
print("Du musst Dich im gleichen Netzwerk befinden (Lan/ Wlan)")
print("Du musst Dich im gleichen Netzwerk befinden (LAN/WLAN)")
httpd.serve_forever()
except KeyboardInterrupt:
# Nur eine kurze Meldung ausgeben und dann sauber herunterfahren
print("\nSTRG+C erkannt. Fahre Server herunter...")
except Exception as e:
print(f"Fehler aufgetreten: {e}")
logging.error("Serverfehler", exc_info=True)
finally:
httpd.server_close()
logging.shutdown()
print("Server wurde sauber beendet.")
def run_server_with_gui(port):
# Nur für QR-Code und GUI benötigt:
import qrcode
import webbrowser
from PIL import Image, ImageTk
import tkinter as tk
"""Startet den Server in einem Hintergrund-Thread und öffnet eine tkinter-GUI mit QR-Code."""
# 1) Erzeuge den Server (aber noch kein serve_forever).
httpd = create_https_server(port)
server_ip = get_server_ip()
url = f"https://{server_ip}:{port}"
# 2) Hintergrund-Thread starten
def server_thread():
try:
print(f"Starte HTTPS-Server auf {url}")
@ -281,20 +264,16 @@ def run_server_with_gui(port):
httpd.server_close()
logging.shutdown()
t = threading.Thread(target=server_thread, daemon=True)
t.start()
threading.Thread(target=server_thread, daemon=True).start()
# 3) tkinter-GUI aufbauen
root = tk.Tk()
root.title("pyUpload - Secure File Upload")
# Favicon setzen (nur unter Windows direkt mit .ico möglich)
try:
root.iconbitmap("favicon.ico")
except Exception as e:
print(f"Konnte das Icon nicht setzen: {e}")
# Labels erzeugen
labels = [
tk.Label(root, text="HTTPS-Upload-Server läuft!", font=("Arial", 14)),
tk.Label(root, text=f"IP-Adresse: {server_ip}", font=("Arial", 11)),
@ -302,70 +281,48 @@ def run_server_with_gui(port):
tk.Label(root, text="Scanne den QR-Code:", font=("Arial", 11))
]
# Alle Labels packen und größte Breite ermitteln
max_width = 0
total_height = 20 # Grundhöhe als Puffer für Abstände
max_width, total_height = 0, 20
for label in labels:
label.pack(pady=5)
label.update_idletasks() # Breite und Höhe berechnen
label.update_idletasks()
max_width = max(max_width, label.winfo_reqwidth())
total_height += label.winfo_reqheight() + 10 # Höhe sammeln
total_height += label.winfo_reqheight() + 10
# QR-Code generieren
url = f"https://{server_ip}:{port}"
qr = qrcode.QRCode(version=1, box_size=8, border=2)
qr.add_data(url)
qr.make(fit=True)
img_qr = qr.make_image(fill_color="black", back_color="white")
# QR-Code als Tkinter-Image einbinden
img_tk = ImageTk.PhotoImage(img_qr)
label_qr = tk.Label(root, image=img_tk)
label_qr.pack()
tk.Label(root, image=img_tk).pack()
total_height += img_tk.height() + 20
# Copyright-Vermerk
label_copyright = tk.Label(root, text="Adam Skotarczak (C) 2025", font=("Arial", 9), fg="gray")
label_copyright.pack(pady=5)
total_height += label_copyright.winfo_reqheight() + 10
# Funktion für klickbaren Link
def open_browser(event):
webbrowser.open("https://www.ionivation.com/pyUpload")
# Klickbarer Link unter Copyright
link_label = tk.Label(root, text="Infos: www.ionivation.com/pyUpload", font=("Arial", 10), fg="blue", cursor="hand2")
link_label.pack()
link_label.bind("<Button-1>", open_browser)
total_height += link_label.winfo_reqheight() + 10
# Funktion zum Beenden
def on_quit():
root.destroy()
# Beenden-Button
btn_quit = tk.Button(root, text="Beenden", command=on_quit, font=("Arial", 10))
btn_quit.pack(pady=10)
total_height += btn_quit.winfo_reqheight() + 20
# Endgültige Fenstergröße setzen
tk.Button(root, text="Beenden", command=root.destroy, font=("Arial", 10)).pack(pady=10)
root.geometry(f"{max_width + 40}x{total_height + 50}")
# 4) GUI-Loop starten
root.mainloop()
# -------------------------------------------------------------------
# Hauptprogramm mit CLI
# Kommandozeilenparser & Einstiegspunkt
# -------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(description="Secure file upload server with optional GUI/QR-Code.")
parser.add_argument("--port", "-p", type=int, default=4443, help="Port, auf dem der Server lauscht (Standard: 4443)")
parser.add_argument("--nogui", "-n", action="store_true", help="Ohne GUI & QR-Code im reinen CLI-Modus starten")
args = parser.parse_args()
print(f"Gestartet mit Port {args.port}, GUI: {not args.nogui}")
if args.nogui:
run_server_nogui(args.port)
else:

3
app/requirements.txt Normal file
View File

@ -0,0 +1,3 @@
cryptography==44.0.2
Pillow==11.1.0
qrcode==8.0

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

BIN
assets/logo-1.0-alpha.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
assets/logo-1.0.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

View File

@ -1,19 +0,0 @@
## exe erstellen:
### pyinstaller
pip install pyinstaller
pyinstaller --onefile --add-data "template.html;." --add-data "success.html;." --add-data "favicon.ico;." --windowed --icon favicon.ico pyUpload.py
pyinstaller --add-data "template.html;." --add-data "success.html;." --add-data "favicon.ico;." --windowed --icon favicon.ico pyUpload.py
### nuitka
python setup.py build
nuitka --standalone --onefile --enable-plugin=tk-inter --windows-console-mode=disable --windows-icon-from-ico=favicon.ico pyUpload.py
## requirements.txt:
pipreqs . --force

View File

@ -1,67 +0,0 @@
@echo off
setlocal
:: Variablen
set SCRIPT=pyUpload.py
set EXE_NAME=pyUpload.exe
set BUILD_DIR=build
set DIST_DIR=release
set ZIP_NAME=pyUpload.zip
set REQ_FILE=requirements.txt
:: Liste von Dateien/Ordnern, die beim "clean" entfernt werden sollen
set DELETE_LIST=build dist __pycache__ *.spec *.zip temp_* logs\* cache\* pyUpload.build\* pyUpload.dist\* upload\* *.pem
:: Menü anzeigen
echo Wähle eine Option:
echo 1 - Erstelle ausführbare Datei (exe 1)
echo 2 - Erstelle Release-Paket (release 1+2)
echo 3 - Erstelle ZIP-Archiv (zip 1+2+3)
echo 4 - Aufraeumen (clean)
set /p CHOICE=Eingabe (1-4):
if "%CHOICE%"=="1" goto exe
if "%CHOICE%"=="2" goto release
if "%CHOICE%"=="3" goto zip
if "%CHOICE%"=="4" goto clean
echo Ungültige Eingabe!
exit /b
:exe
echo Erzeuge ausführbare Datei...
mkdir %BUILD_DIR% 2>nul
pyinstaller --onefile --add-data "template.html;." --add-data "success.html;." --add-data "favicon.ico;." --windowed --icon favicon.ico --name %EXE_NAME% %SCRIPT%
move dist\%EXE_NAME% %BUILD_DIR%\
echo Erstellung abgeschlossen: %BUILD_DIR%\%EXE_NAME%
exit /b
:release
call :exe
echo Erstelle Release-Paket...
mkdir %DIST_DIR% 2>nul
copy %BUILD_DIR%\%EXE_NAME% %DIST_DIR%\
copy %REQ_FILE% %DIST_DIR%\
copy template.html success.html favicon.ico %DIST_DIR%\
echo Release-Paket bereit in %DIST_DIR%
exit /b
:zip
call :release
echo Erstelle ZIP-Archiv...
powershell Compress-Archive -Path "%DIST_DIR%\%EXE_NAME%", "%DIST_DIR%\template.html", "%DIST_DIR%\success.html", "%DIST_DIR%\favicon.ico" -DestinationPath "%ZIP_NAME%"
echo ZIP-Archiv erstellt: %ZIP_NAME%
exit /b
:clean
echo Bereinige Projektverzeichnis...
:: Durchläuft alle Dateien/Ordner in DELETE_LIST
for %%F in (%DELETE_LIST%) do (
if exist %%F (
echo Entferne: %%F
rmdir /s /q %%F 2>nul || del /q %%F 2>nul
)
)
echo Bereinigung abgeschlossen.
exit /b

View File

@ -1,10 +0,0 @@
MIT License
Copyright (c) 2025 Adam Scotarczak (adam@skotarczak.net) - ionivation.com
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

View File

@ -1,54 +0,0 @@
@echo off
setlocal
:: Prüfen, ob Python installiert ist
where python >nul 2>nul
if %ERRORLEVEL% NEQ 0 (
echo Fehler: Python ist nicht installiert oder nicht im PATH!
echo Bitte installiere Python und starte die Installation erneut.
pause
exit /b 1
)
:: Python-Version prüfen (Mindestversion 3.8)
for /f "tokens=2 delims= " %%v in ('python --version 2^>^&1') do set PYTHON_VERSION=%%v
for /f "tokens=1,2 delims=." %%a in ("%PYTHON_VERSION%") do (
if %%a LSS 3 (
echo Fehler: Python 3.8 oder höher ist erforderlich!
pause
exit /b 1
)
if %%a==3 if %%b LSS 8 (
echo Fehler: Python 3.8 oder höher ist erforderlich!
pause
exit /b 1
)
)
:: Virtuelle Umgebung erstellen, falls sie nicht existiert
if not exist .venv (
echo Erstelle virtuelle Server-Umgebung ...
python -m venv --copies .venv
if %ERRORLEVEL% NEQ 0 (
echo Fehler beim Erstellen der virtuellen Umgebung!
pause
exit /b 2
)
)
:: Aktivieren der virtuellen Umgebung
call .venv\Scripts\activate
:: Installieren der Abhängigkeiten
echo Installiere Abhaengigkeiten aus dem Internet ...
pip install --no-warn-script-location --disable-pip-version-check -r requirements.txt
if %ERRORLEVEL% NEQ 0 (
echo Fehler beim Installieren der Abhaengigkeiten!
pause
exit /b 3
)
:: Erfolgsmeldung
echo.
echo Installation abgeschlossen.
timeout /t 3 >nul & exit /b 0

View File

@ -1,9 +0,0 @@
# patchlog pyUpload
**1.0.0 Erste Version - 12.03.25**
**Funktionsumfang:**
- Startet standartmässig TKInter GUI
- Hilfe mit `--help` verfügbar

View File

@ -1,33 +0,0 @@
@echo off
setlocal
goto code
(C) 2025 Adam Scotarczak
options for pyUpload.py:
usage: pyUpload.py [-h] [--port PORT] [--nogui]
-h, --help show this help message and exit
--port, -p PORT Port, auf dem der Server lauscht (Standard: 4443)
--nogui, -n Ohne GUI & QR-Code im reinen CLI-Modus starten
:code
:: Prüfen, ob die virtuelle Umgebung existiert
if not exist .venv (
echo Virtuelle Umgebung nicht gefunden! Bitte zuerst install.bat ausführen.
pause
exit /b
)
:: Aktivieren der virtuellen Umgebung
call .venv\Scripts\activate
echo Virtuelle Umgebung in pyUpload.bat gestartet!
:: Starten des Upload-Servers mit Übergabe aller übergebenen Parameter
python pyUpload.py %*
:: Nach Beenden der Anwendung
deactivate
echo Virtuelle Umgebung beendet!

View File

@ -1,3 +0,0 @@
cryptography==44.0.2
Pillow==11.1.0
qrcode==8.0

View File

@ -1,7 +0,0 @@
# pyUpload Roadmap und ToDo
- ***README.md überarbeiten***
Entspricht aktuell nicht mehr dem Entwicklungsstand da auf innoSetup umgestiegen worden ist und Installationsscript eingefügt wurde.
- ***Auf pyQt6 umbauen statt tkinter?***

93
info.md
View File

@ -1,93 +0,0 @@
## pyUpload - Sicherer Datei-Upload-Server für eine einfache und sichere Dateiübertragung
### Beschreibung
pyUpload ist eine leistungsstarke und dennoch einfache Lösung für den sicheren Datei-Upload über HTTPS. Dieses Programm eignet sich ideal, um Dateien schnell und unkompliziert von einem Smartphone oder einem anderen Gerät auf einen Computer zu übertragen.
Anstatt zusätzliche Apps oder USB-Kabel zu nutzen, kann der Benutzer den Server starten, den automatisch generierten QR-Code mit dem Smartphone scannen und die Dateien direkt über die Weboberfläche hochladen. Der Computer speichert die hochgeladenen Dateien strukturiert in individuellen Verzeichnissen für jedes Gerät.
Zusätzlich erstellt pyUpload bei Bedarf automatisch ein selbstsigniertes SSL-Zertifikat, um eine verschlüsselte Verbindung sicherzustellen. Damit bleibt die Dateiübertragung geschützt und zuverlässig.
### Download
- **Portabel für Windows als .zip**
[download id="2323"]
> Aktuell ist der Download der portablen und Compilierten Version gesperrt da es von diversen Virenscannern als Bedrohung eingestuft wird. Bei Interesse ist das Programm aktuell nur auf Anfrage per eMail erhältlich. In Kürze erfolgt eine Veröffentlichung inklusive Code auf GitHub.
### Features Die Vorteile von pyUpload auf einen Blick
- **Sichere Dateiübertragung per HTTPS** Alle Daten werden verschlüsselt übertragen.
- **Automatische Erstellung eines selbstsignierten SSL-Zertifikats** Keine zusätzliche Konfiguration notwendig.
- **Intuitive, webbasierte Benutzeroberfläche** Einfach zu bedienen, keine Installation erforderlich.
- **Strukturierte Speicherung** Dateien werden in client-spezifischen Verzeichnissen gespeichert.
- **Zentralisierte und client-spezifische Logging-Funktion** Detaillierte Nachverfolgung aller Uploads.
- **Flexible Nutzung mit oder ohne GUI** Start als Desktop-Anwendung oder reine Konsolen-Version möglich.
- **Schnelle Einrichtung** Download, Entpacken und sofort loslegen!
### Installationsanleitung So startest du pyUpload
Es gibt zwei Möglichkeiten, pyUpload zu nutzen: Entweder die manuelle Installation oder die Nutzung einer vorgefertigten, ausführbaren Version.
#### 1. Nutzung der fertigen Download-Version
Falls du keine Python-Installation benötigst, kannst du die vorgefertigte **ZIP-Version** von pyUpload herunterladen. Diese enthält bereits alle notwendigen Dateien und ist sofort einsatzbereit.
1. Lade die neueste **pyUpload.zip** von der offiziellen Website herunter.
2. Entpacke die ZIP-Datei in einen beliebigen Ordner.
3. Starte die enthaltene `pyUpload.exe`.
4. Falls die grafische Benutzeroberfläche nicht benötigt wird, kann die `pyUpload.exe` direkt in der Konsole mit `--nogui` gestartet werden:
```sh
pyUpload.exe --nogui
```
5. Eine Übersicht aller verfügbaren Befehle und Optionen erhältst du mit:
```sh
pyUpload.exe --help
```
#### 2. Manuelle Installation für Python-Nutzer
1. Stelle sicher, dass **Python 3** auf deinem System installiert ist.
2. Installiere alle benötigten Abhängigkeiten mit folgendem Befehl:
```sh
pip install -r requirements.txt
```
3. Starte den Server mit:
```sh
python pyUpload.py
```
4. Falls du keine grafische Benutzeroberfläche benötigst, kannst du den Server im Konsolenmodus starten:
```sh
python pyUpload.py --nogui
```
### Zugriff auf die Weboberfläche
- Sobald der Server läuft, kann er über die lokale IP-Adresse aufgerufen werden:
```
https://<server-ip>:4443
```
- Falls die GUI-Version gestartet wurde, erscheint ein **QR-Code**, der die Verbindungsadresse enthält. Dies ermöglicht eine einfache Verbindung mit Smartphones und Tablets.
### Datei-Upload leicht gemacht So funktioniert es
1. Öffne die **Weboberfläche** im Browser.
2. Wähle die gewünschte **Datei aus** und klicke auf **„Hochladen“**.
3. Nach erfolgreichem Upload erscheint eine **Bestätigungsseite**, die den Abschluss der Übertragung bestätigt.
### SSL-Zertifikatswarnung in Browsern umgehen
Da pyUpload ein **selbstsigniertes SSL-Zertifikat** nutzt, kann es beim ersten Zugriff zu einer Warnung des Browsers kommen. Um die verschlüsselte Verbindung zu akzeptieren, gibt es zwei Möglichkeiten:
- In den meisten Browsern gibt es eine Option wie **„Erweitert“** oder **„Trotzdem fortfahren“**, um die Warnung zu übergehen.
- Alternativ kann das Zertifikat **manuell importiert und als vertrauenswürdig markiert** werden, um künftige Warnmeldungen zu vermeiden.
### Logging und Fehlerbehandlung Transparenz und Kontrolle
- Alle **Uploads und Anfragen** werden in **zentralen sowie client-spezifischen Logdateien** gespeichert. Diese befinden sich im `logs/`-Verzeichnis.
- Falls während der Nutzung von pyUpload **Probleme auftreten**, bietet ein Blick in diese Logdateien wertvolle Hinweise zur Fehlerbehebung.
### Lizenz und Autor
- **Entwickelt von Adam Skotarczak (C) 2025**.

View File

@ -1,84 +0,0 @@
; Define AppVersion as a constant
#define AppVersion "1.0.0"
#define BasePath ".\dev"
; Source: "{#BasePath}\*"; DestDir: "{app}"; Flags: ignoreversion;
[Setup]
AppId={{832B3E5D-14BC-4823-A911-00C9B79AD040}
AppName=pyUpload
AppVersion={#AppVersion}
AppVerName=pyUpload {#AppVersion} (win)
AppPublisher=Adam Skotarczak (ionivation.com)
AppPublisherURL=https://www.ionivation.com/pyupload/
AppSupportURL=https://www.ionivation.com/pyupload/
AppUpdatesURL=https://www.ionivation.com/pyupload/
DefaultDirName={userappdata}\pyUpload
DisableProgramGroupPage=yes
LicenseFile={#BasePath}\LICENSE
PrivilegesRequired=lowest
;PrivilegesRequiredOverridesAllowed=dialog
OutputBaseFilename=pyUpload-Setup-{#AppVersion}
SolidCompression=yes
WizardStyle=modern
SetupIconFile={#BasePath}\favicon.ico
DisableDirPage=yes
[Languages]
Name: "german"; MessagesFile: "compiler:Languages\German.isl"
Name: "english"; MessagesFile: "compiler:Default.isl"
[Files]
Source: "{#BasePath}\pyUpload.bat"; DestDir: "{app}"; Flags: ignoreversion
Source: "{#BasePath}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
[Dirs]
Name: "{app}\upload"; Flags: uninsalwaysuninstall
[Icons]
Name: "{autoprograms}\pyUpload"; Filename: "{app}\pyUpload.bat"; IconFilename: "{app}\favicon.ico";
Name: "{autodesktop}\pyUpload"; Filename: "{app}\pyUpload.bat"; IconFilename: "{app}\favicon.ico"; Tasks: desktopicon
Name: "{userdesktop}\pyUpload-Uploads"; Filename: "{app}\upload"; IconFilename: "{app}\favicon.ico"; Tasks: desktopicon
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Run]
Filename: "{app}\install.bat"; Parameters: ""; WorkingDir: "{app}\"; Flags: waituntilterminated
;Flags: runhidden
Filename: "{app}\pyUpload.bat"; Description: "{cm:LaunchProgram,pyUpload}"; Flags: shellexec postinstall skipifsilent
[Code]
function IsPythonInstalled(): Boolean;
var
PythonPath: String;
ResultCode: Integer;
begin
// Prüfe Registry für alle möglichen Python-Versionen (dynamisch)
if RegQueryStringValue(HKLM, 'SOFTWARE\Python\PythonCore', '', PythonPath) or
RegQueryStringValue(HKLM, 'SOFTWARE\WOW6432Node\Python\PythonCore', '', PythonPath) then
begin
Result := True;
Exit;
end;
// Prüfe mit python --version, falls kein Registry-Eintrag gefunden wurde
Result := Exec('cmd.exe', '/c python --version', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);
end;
function InitializeSetup(): Boolean;
begin
if not IsPythonInstalled() then
begin
MsgBox('Python ist nicht installiert oder nicht erreichbar! Bitte installiere Python.', mbError, MB_OK);
Result := False; // Installation abbrechen
end
else
begin
Result := True;
end;
end;

19
start.cmd Normal file
View File

@ -0,0 +1,19 @@
@echo off
setlocal
chcp 65001 >nul
:: Prüfen, ob python vorhanden ist
where python >nul 2>nul
if errorlevel 1 (
echo [Fehler] Python wurde nicht gefunden.
echo Bitte installiere Python 3.8 oder höher: https://www.python.org/downloads/windows/
pause
exit /b 1
)
:: Verzeichnis von start.cmd ermitteln (robust, egal von wo gestartet)
set SCRIPT_DIR=%~dp0
:: Starte die Anwendung direkt aus app\
python "%SCRIPT_DIR%app\main.py" %*

14
start.sh Normal file
View File

@ -0,0 +1,14 @@
#!/bin/bash
# Verzeichnis dieser Datei bestimmen
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Python-Interpreter prüfen
if ! command -v python3 &> /dev/null; then
echo "[Fehler] Python 3 ist nicht installiert."
echo "Bitte installiere es über deine Paketverwaltung (z.B. apt, dnf, brew)."
exit 1
fi
# Starte das Hauptskript
python3 "$SCRIPT_DIR/app/main.py" "$@"