Explorer
/opt/struktur/explorer/app.py
← Zurück ↓ Download
#!/usr/bin/env python3
from flask import Flask, render_template_string, abort, Response
from pathlib import Path
import os, mimetypes

app = Flask(__name__)
ROOT = Path("/")

TEXT_EXTENSIONS = {'.md', '.txt', '.log', '.json', '.yaml', '.yml', '.py', '.sh', '.env', '.conf', '.cfg', '.ini', '.csv'}

html_dir = """<!DOCTYPE html><html><head><meta charset="utf-8">
<title>Explorer</title>
<style>
body{font-family:Arial;margin:0;background:#f0f0f0}
.header{background:#1a1a2e;color:white;padding:15px 20px;font-size:22px;font-weight:bold}
.path{background:#16213e;color:#aaa;padding:10px 20px;font-size:13px}
.content{padding:20px;max-width:1400px}
.item{background:white;padding:12px 15px;margin:4px 0;border-radius:4px;display:flex;align-items:center;text-decoration:none;color:#333}
.item:hover{background:#e8f0fe;border-left:4px solid #1a73e8}
.icon{width:30px;margin-right:12px;font-size:18px}
.name{flex:1}
</style></head><body>
<div class="header">Explorer</div>
<div class="path">{{ path }}</div>
<div class="content">
{% for item in items %}
<a href="{{ item.link }}" class="item">
<span class="icon">{{ item.icon }}</span>
<span class="name">{{ item.name }}</span>
</a>
{% endfor %}
</div></body></html>"""

html_file = """<!DOCTYPE html><html><head><meta charset="utf-8">
<title>{{ filename }}</title>
<style>
body{font-family:Arial;margin:0;background:#f0f0f0}
.header{background:#1a1a2e;color:white;padding:15px 20px;font-size:22px;font-weight:bold}
.path{background:#16213e;color:#aaa;padding:10px 20px;font-size:13px}
.toolbar{background:#fff;padding:10px 20px;border-bottom:1px solid #ddd}
.toolbar a{color:#1a73e8;text-decoration:none;margin-right:15px}
.content{padding:20px;max-width:1400px}
pre{background:white;padding:20px;border-radius:6px;overflow-x:auto;white-space:pre-wrap;word-wrap:break-word;box-shadow:0 1px 4px rgba(0,0,0,0.1);line-height:1.6}
</style></head><body>
<div class="header">Explorer</div>
<div class="path">{{ path }}</div>
<div class="toolbar">
<a href="{{ parent }}">&#8592; Zurück</a>
<a href="/download{{ path }}">&#8595; Download</a>
</div>
<div class="content"><pre>{{ content }}</pre></div>
</body></html>"""

@app.route("/")
@app.route("/browse/<path:filepath>")
def browse(filepath=""):
    full_path = ROOT / filepath
    if not full_path.exists():
        abort(404)
    if full_path.is_file():
        ext = full_path.suffix.lower()
        if ext in TEXT_EXTENSIONS:
            try:
                content = full_path.read_text(encoding="utf-8", errors="replace")
                parent = "/browse/" + "/".join(filepath.split("/")[:-1]) if filepath else "/"
                return render_template_string(html_file, filename=full_path.name, path="/"+filepath, content=content, parent=parent)
            except:
                pass
        return Response(full_path.read_bytes(), mimetype=mimetypes.guess_type(str(full_path))[0] or "application/octet-stream")
    items = []
    if filepath:
        parent = "/browse/" + "/".join(filepath.split("/")[:-1])
        items.append({"name": "..", "link": parent, "icon": "&#8593;"})
    try:
        entries = sorted(full_path.iterdir(), key=lambda x: (x.is_file(), x.name.lower()))
        for item in entries:
            try:
                rel = str(item.relative_to(ROOT))
                link = "/browse/" + rel
                icon = "📁" if item.is_dir() else "📄"
                items.append({"name": item.name, "link": link, "icon": icon})
            except:
                pass
    except PermissionError:
        pass
    return render_template_string(html_dir, path="/"+filepath, items=items)

@app.route("/download/<path:filepath>")
def download(filepath):
    full_path = ROOT / filepath
    if not full_path.exists() or not full_path.is_file():
        abort(404)
    return Response(full_path.read_bytes(), 
        headers={"Content-Disposition": f"attachment; filename={full_path.name}"},
        mimetype="application/octet-stream")

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=5058)