#!/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 }}">← Zurück</a>
<a href="/download{{ path }}">↓ 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": "↑"})
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)