Time Elapsed
-
+
{fmt(elapsedSeconds) ?? '—'}
diff --git a/dashboard/src/lib/api.js b/dashboard/src/lib/api.js
index 1b13c43..e69b048 100644
--- a/dashboard/src/lib/api.js
+++ b/dashboard/src/lib/api.js
@@ -21,4 +21,3 @@ export const getTests = (params = {}) => {
}
export const getConfig = () => apiFetch('/config')
export const saveConfig = (body) => apiFetch('/config', { method: 'POST', body: JSON.stringify(body) })
-export const browse = (path) => apiFetch(`/browse${path ? `?path=${encodeURIComponent(path)}` : ''}`)
diff --git a/docker-compose.yml b/docker-compose.yml
index bee5b5b..d57de57 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -4,12 +4,10 @@ services:
container_name: dashboard-backend
ports:
- "3001:3001"
- environment:
- - BROWSE_ROOTS=/host
volumes:
- - ./server:/app
- # Host file browsing root inside container (adjust HOST_BROWSE_ROOT if needed)
- - ${HOST_BROWSE_ROOT:-C:/Users}:/host
+ - type: bind
+ source: ./server
+ target: /app
restart: unless-stopped
frontend:
diff --git a/server/app.py b/server/app.py
index a360072..d59c0c6 100644
--- a/server/app.py
+++ b/server/app.py
@@ -291,89 +291,6 @@ def rescan_results_route():
return jsonify({"ok": True, "testCount": len(tests), "completedCount": completed})
-@app.get("/api/browse")
-def browse_route():
- def _normalize_request_path(path):
- if not path:
- return path
- p = path.strip()
- if os.name == "nt":
- p = p.replace("/", "\\")
- return p
-
- def _configured_roots():
- raw = os.getenv("BROWSE_ROOTS", "").strip()
- if not raw:
- return []
- roots = []
- for part in raw.split(";"):
- p = part.strip()
- if not p:
- continue
- abs_p = os.path.abspath(p)
- if os.path.isdir(abs_p):
- roots.append(abs_p)
- return roots
-
- def _is_within_allowed_roots(path, roots):
- if not roots:
- return True
- normalized = os.path.normcase(os.path.abspath(path))
- for root in roots:
- try:
- if os.path.commonpath([normalized, root]) == root:
- return True
- except ValueError:
- continue
- return False
-
- roots = _configured_roots()
- req_path = _normalize_request_path(request.args.get("path"))
-
- if not req_path:
- if roots:
- dirs = []
- for root in roots:
- display_name = os.path.basename(root.rstrip("/\\")) or root
- dirs.append({"name": display_name, "path": root})
- return jsonify({"path": None, "parent": None, "dirs": dirs})
-
- if os.name == "nt":
- dirs = []
- for drive_idx in range(65, 91):
- drive = f"{chr(drive_idx)}:\\"
- if os.path.exists(drive):
- dirs.append({"name": drive, "path": drive})
- else:
- dirs = [{"name": "/", "path": "/"}]
-
- return jsonify({"path": None, "parent": None, "dirs": dirs})
-
- if not os.path.exists(req_path):
- return jsonify({"error": "Path does not exist"}), 400
- if not os.path.isdir(req_path):
- return jsonify({"error": "Path is not a directory"}), 400
- if not _is_within_allowed_roots(req_path, roots):
- return jsonify({"error": "Path is outside allowed browse roots"}), 403
-
- try:
- dirs = []
- for name in os.listdir(req_path):
- child = os.path.join(req_path, name)
- if os.path.isdir(child) and not name.startswith("."):
- dirs.append({"name": name, "path": child})
-
- dirs.sort(key=lambda item: item["name"].lower())
- except OSError:
- return jsonify({"error": "Cannot read directory"}), 403
-
- parent = os.path.dirname(req_path)
- at_root = os.path.normcase(parent) == os.path.normcase(req_path)
- if roots and parent and not _is_within_allowed_roots(parent, roots):
- parent = None
- return jsonify({"path": req_path, "parent": None if at_root else parent, "dirs": dirs})
-
-
@app.get("/api/events")
def events_route():
headers = {
diff --git a/server/dashboard.db-shm b/server/dashboard.db-shm
index 73f4f32..c641ec4 100644
Binary files a/server/dashboard.db-shm and b/server/dashboard.db-shm differ
diff --git a/server/dashboard.db-wal b/server/dashboard.db-wal
index fb2f5c5..fe16290 100644
Binary files a/server/dashboard.db-wal and b/server/dashboard.db-wal differ