freigabe.sh startet freigabe.py (Passwort, nur lesen, nichts mit Punkt am Anfang) und einen Cloudflare Quick Tunnel. Anleitung fuer Agents in .docs/Freigabe.md, Hinweis darauf in .claude/CLAUDE.md. Das Passwort selbst bleibt in .freigabe-passwort ausserhalb von git. Dashboard: Unter 900 px bleibt die Navigation beim Scrollen stehen, nur das Logo scrollt weg. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
97 lines
3.0 KiB
Python
97 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Freigabe des Marketing-Dashboards für Kollegen.
|
|
|
|
Derselbe Server wie serve.py, aber für den Weg nach draußen über einen
|
|
Cloudflare-Tunnel. Gestartet wird alles zusammen mit ./freigabe.sh.
|
|
|
|
- jede Anfrage braucht das Passwort, der Name ist egal
|
|
- nur lesen: kein Status setzen, kein Export, nichts öffnen
|
|
- nichts, was mit einem Punkt beginnt, also weder .git noch .DS_Store
|
|
- keine Ordnerlisten
|
|
|
|
Das Passwort steht in .freigabe-passwort. Fehlt die Datei, wird beim ersten
|
|
Start eins erzeugt. Zum Wechseln die Datei löschen und neu starten.
|
|
Anleitung zum Starten, Prüfen und Beenden: .docs/Freigabe.md
|
|
"""
|
|
|
|
import base64
|
|
import hmac
|
|
import secrets
|
|
import sys
|
|
import urllib.parse
|
|
|
|
import serve
|
|
|
|
PORT = 4330
|
|
PASSWORTDATEI = serve.HUB / ".freigabe-passwort"
|
|
|
|
|
|
def passwort():
|
|
if PASSWORTDATEI.exists():
|
|
return PASSWORTDATEI.read_text(encoding="utf-8").strip()
|
|
neu = secrets.token_urlsafe(12)
|
|
PASSWORTDATEI.write_text(neu + "\n", encoding="utf-8")
|
|
PASSWORTDATEI.chmod(0o600)
|
|
return neu
|
|
|
|
|
|
PASSWORT = passwort()
|
|
|
|
|
|
class Handler(serve.Handler):
|
|
def _erlaubt(self):
|
|
kopf = self.headers.get("Authorization", "")
|
|
if kopf.startswith("Basic "):
|
|
try:
|
|
_, _, pw = base64.b64decode(kopf[6:]).decode("utf-8").partition(":")
|
|
except Exception:
|
|
pw = ""
|
|
if hmac.compare_digest(pw.encode("utf-8"), PASSWORT.encode("utf-8")):
|
|
return True
|
|
self.send_response(401)
|
|
self.send_header("WWW-Authenticate", 'Basic realm="HiveGrid Marketing", charset="UTF-8"')
|
|
self.send_header("Content-Length", "0")
|
|
self.end_headers()
|
|
return False
|
|
|
|
def _versteckt(self):
|
|
pfad = urllib.parse.unquote(urllib.parse.urlparse(self.path).path)
|
|
if any(t.startswith(".") for t in pfad.split("/") if t):
|
|
self.send_error(404)
|
|
return True
|
|
return False
|
|
|
|
def do_GET(self):
|
|
if self._erlaubt() and not self._versteckt():
|
|
super().do_GET()
|
|
|
|
def do_HEAD(self):
|
|
if self._erlaubt() and not self._versteckt():
|
|
super().do_HEAD()
|
|
|
|
def do_POST(self):
|
|
# Das Dashboard zeigt "fehler" unten als Meldung an.
|
|
if self._erlaubt():
|
|
self._json(403, {"fehler": "In der Freigabe lässt sich nichts ändern."})
|
|
|
|
def list_directory(self, path):
|
|
# Ordner ohne index.html. Eine Liste würde auch versteckte Dateien und
|
|
# alles zeigen, was nicht für die Kollegen gedacht ist.
|
|
self.send_error(404)
|
|
return None
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
server = serve.Server(("127.0.0.1", PORT), Handler)
|
|
except OSError:
|
|
print("Port %d ist belegt. Läuft die Freigabe schon?" % PORT, flush=True)
|
|
sys.exit(1)
|
|
print("Freigabe läuft auf http://127.0.0.1:%d/" % PORT, flush=True)
|
|
print("Passwort für die Kollegen: %s (Name egal)" % PASSWORT, flush=True)
|
|
with server:
|
|
try:
|
|
server.serve_forever()
|
|
except KeyboardInterrupt:
|
|
pass
|