Implement debug API for portal files and enhance file listing functionality: add a no-auth endpoint for troubleshooting, improve error handling, and streamline the portal files listing logic. Update HTML template to handle session expiration gracefully and provide a read-only fallback for unauthenticated users.

This commit is contained in:
nearxos
2026-02-20 09:23:51 +02:00
parent 97d55a1f90
commit ed5e1a1101
2 changed files with 68 additions and 13 deletions

View File

@@ -424,6 +424,27 @@ def serve_portal_file(filename):
return send_file(path, as_attachment=False, download_name=filename.split("/")[-1])
@app.route("/api/portal-files-debug")
def api_portal_files_debug():
"""No-auth debug: what PORTAL_FILES_DIR the process sees (for troubleshooting)."""
try:
names = []
if PORTAL_FILES_DIR.is_dir():
for p in sorted(PORTAL_FILES_DIR.iterdir(), key=lambda x: (not x.is_dir(), x.name.lower())):
if ".." in p.name or p.name.startswith("."):
continue
names.append({"name": p.name, "type": "dir" if p.is_dir() else "file"})
return jsonify({
"portal_files_dir": str(PORTAL_FILES_DIR),
"exists": PORTAL_FILES_DIR.exists(),
"is_dir": PORTAL_FILES_DIR.is_dir(),
"items": names,
"CM4_PROVISIONING_DIR": os.environ.get("CM4_PROVISIONING_DIR"),
})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route("/api/status")
def api_status():
return jsonify(read_status())
@@ -610,29 +631,26 @@ def _save_portal_descriptions(descriptions):
return False
@app.route("/api/portal-files")
@require_admin
def api_portal_files_list():
"""List one level: root or contents of path=... (folders and files)."""
subpath = request.args.get("path", "").strip().strip("/")
def _portal_files_list_impl(subpath):
"""Shared impl for listing portal files. Returns (items, descriptions, base_url, current_path)."""
base_url = request.host_url.rstrip("/") + "/files/"
empty = {"items": [], "base_url": base_url, "descriptions": {}, "current_path": subpath, "portal_files_dir": str(PORTAL_FILES_DIR)}
empty_items = []
if ".." in subpath or "\\" in subpath:
return jsonify(empty)
return empty_items, {}, base_url, subpath
if not PORTAL_FILES_DIR.is_dir():
try:
PORTAL_FILES_DIR.mkdir(parents=True, exist_ok=True)
except OSError:
pass
if not PORTAL_FILES_DIR.is_dir():
return jsonify(empty)
return empty_items, {}, base_url, subpath
list_dir = (PORTAL_FILES_DIR / subpath).resolve() if subpath else PORTAL_FILES_DIR
try:
list_dir.relative_to(PORTAL_FILES_DIR.resolve())
except ValueError:
return jsonify({**empty, "current_path": subpath})
return empty_items, {}, base_url, subpath
if not list_dir.is_dir():
return jsonify({**empty, "current_path": subpath})
return empty_items, {}, base_url, subpath
items = []
for p in sorted(list_dir.iterdir(), key=lambda x: (not x.is_dir(), x.name.lower())):
if ".." in p.name or p.name.startswith("."):
@@ -646,7 +664,28 @@ def api_portal_files_list():
except OSError:
pass
descriptions = _load_portal_descriptions()
return jsonify({"items": items, "base_url": base_url, "descriptions": descriptions, "current_path": subpath, "portal_files_dir": str(PORTAL_FILES_DIR)})
return items, descriptions, base_url, subpath
@app.route("/api/portal-files")
def api_portal_files_list():
"""List one level: root or contents of path=... (folders and files). ?debug=1 allows unauthenticated read-only list."""
subpath = request.args.get("path", "").strip().strip("/")
debug = request.args.get("debug") == "1"
if not debug and not session.get("admin_logged_in"):
if request.is_json or request.path.startswith("/api/"):
return jsonify({"ok": False, "error": "Login required"}), 401
return redirect(url_for("login", next=request.url))
items, descriptions, base_url, subpath = _portal_files_list_impl(subpath)
resp = jsonify({
"items": items,
"base_url": base_url,
"descriptions": descriptions,
"current_path": subpath,
"portal_files_dir": str(PORTAL_FILES_DIR),
})
resp.headers["Cache-Control"] = "no-store, no-cache, must-revalidate"
return resp
@app.route("/api/portal-files/descriptions", methods=["GET", "PATCH"])