Spaces:
Running
Running
Merge pull request #177 from biggraph/darabos-workspace-error-handling
Browse files- lynxkite-app/src/lynxkite_app/crdt.py +3 -3
- lynxkite-app/src/lynxkite_app/main.py +9 -7
- lynxkite-app/web/src/main.tsx +41 -10
- lynxkite-core/src/lynxkite/core/ops.py +1 -1
- lynxkite-graph-analytics/src/lynxkite_graph_analytics/ml_ops.py +1 -1
- lynxkite-lynxscribe/src/lynxkite_lynxscribe/lynxscribe_ops.py +1 -1
lynxkite-app/src/lynxkite_app/crdt.py
CHANGED
@@ -34,7 +34,7 @@ class WorkspaceWebsocketServer(pycrdt_websocket.WebsocketServer):
|
|
34 |
"""
|
35 |
crdt_path = pathlib.Path(".crdt")
|
36 |
path = crdt_path / f"{name}.crdt"
|
37 |
-
assert path.is_relative_to(crdt_path)
|
38 |
ystore = pycrdt_websocket.ystore.FileYStore(path)
|
39 |
ydoc = pycrdt.Doc()
|
40 |
ydoc["workspace"] = ws = pycrdt.Map()
|
@@ -87,7 +87,7 @@ class CodeWebsocketServer(WorkspaceWebsocketServer):
|
|
87 |
"""Initialize a room for a text document with the given name."""
|
88 |
crdt_path = pathlib.Path(".crdt")
|
89 |
path = crdt_path / f"{name}.crdt"
|
90 |
-
assert path.is_relative_to(crdt_path)
|
91 |
ystore = pycrdt_websocket.ystore.FileYStore(path)
|
92 |
ydoc = pycrdt.Doc()
|
93 |
ydoc["text"] = text = pycrdt.Text()
|
@@ -261,7 +261,7 @@ async def execute(name: str, ws_crdt: pycrdt.Map, ws_pyd: workspace.Workspace, d
|
|
261 |
print(f"Running {name} in {ws_pyd.env}...")
|
262 |
cwd = pathlib.Path()
|
263 |
path = cwd / name
|
264 |
-
assert path.is_relative_to(cwd), "
|
265 |
# Save user changes before executing, in case the execution fails.
|
266 |
ws_pyd.save(path)
|
267 |
ops.load_user_scripts(name)
|
|
|
34 |
"""
|
35 |
crdt_path = pathlib.Path(".crdt")
|
36 |
path = crdt_path / f"{name}.crdt"
|
37 |
+
assert path.is_relative_to(crdt_path), f"Path '{path}' is invalid"
|
38 |
ystore = pycrdt_websocket.ystore.FileYStore(path)
|
39 |
ydoc = pycrdt.Doc()
|
40 |
ydoc["workspace"] = ws = pycrdt.Map()
|
|
|
87 |
"""Initialize a room for a text document with the given name."""
|
88 |
crdt_path = pathlib.Path(".crdt")
|
89 |
path = crdt_path / f"{name}.crdt"
|
90 |
+
assert path.is_relative_to(crdt_path), f"Path '{path}' is invalid"
|
91 |
ystore = pycrdt_websocket.ystore.FileYStore(path)
|
92 |
ydoc = pycrdt.Doc()
|
93 |
ydoc["text"] = text = pycrdt.Text()
|
|
|
261 |
print(f"Running {name} in {ws_pyd.env}...")
|
262 |
cwd = pathlib.Path()
|
263 |
path = cwd / name
|
264 |
+
assert path.is_relative_to(cwd), f"Path '{path}' is invalid"
|
265 |
# Save user changes before executing, in case the execution fails.
|
266 |
ws_pyd.save(path)
|
267 |
ops.load_user_scripts(name)
|
lynxkite-app/src/lynxkite_app/main.py
CHANGED
@@ -49,7 +49,7 @@ data_path = pathlib.Path()
|
|
49 |
|
50 |
def save(req: SaveRequest):
|
51 |
path = data_path / req.path
|
52 |
-
assert path.is_relative_to(data_path)
|
53 |
req.ws.save(path)
|
54 |
|
55 |
|
@@ -66,7 +66,7 @@ async def save_and_execute(req: SaveRequest):
|
|
66 |
async def delete_workspace(req: dict):
|
67 |
json_path: pathlib.Path = data_path / req["path"]
|
68 |
crdt_path: pathlib.Path = data_path / ".crdt" / f"{req['path']}.crdt"
|
69 |
-
assert json_path.is_relative_to(data_path)
|
70 |
json_path.unlink()
|
71 |
crdt_path.unlink()
|
72 |
|
@@ -74,7 +74,7 @@ async def delete_workspace(req: dict):
|
|
74 |
@app.get("/api/load")
|
75 |
def load(path: str):
|
76 |
path = data_path / path
|
77 |
-
assert path.is_relative_to(data_path)
|
78 |
if not path.exists():
|
79 |
return workspace.Workspace()
|
80 |
return workspace.Workspace.load(path)
|
@@ -97,7 +97,7 @@ def _get_path_type(path: pathlib.Path) -> str:
|
|
97 |
@app.get("/api/dir/list")
|
98 |
def list_dir(path: str):
|
99 |
path = data_path / path
|
100 |
-
assert path.is_relative_to(data_path)
|
101 |
return sorted(
|
102 |
[
|
103 |
DirectoryEntry(
|
@@ -114,7 +114,7 @@ def list_dir(path: str):
|
|
114 |
@app.post("/api/dir/mkdir")
|
115 |
def make_dir(req: dict):
|
116 |
path = data_path / req["path"]
|
117 |
-
assert path.is_relative_to(data_path)
|
118 |
assert not path.exists(), f"{path} already exists"
|
119 |
path.mkdir()
|
120 |
|
@@ -122,7 +122,9 @@ def make_dir(req: dict):
|
|
122 |
@app.post("/api/dir/delete")
|
123 |
def delete_dir(req: dict):
|
124 |
path: pathlib.Path = data_path / req["path"]
|
125 |
-
assert all([path.is_relative_to(data_path), path.exists(), path.is_dir()])
|
|
|
|
|
126 |
shutil.rmtree(path)
|
127 |
|
128 |
|
@@ -146,7 +148,7 @@ async def upload(req: fastapi.Request):
|
|
146 |
form = await req.form()
|
147 |
for file in form.values():
|
148 |
file_path = data_path / "uploads" / file.filename
|
149 |
-
assert file_path.is_relative_to(data_path), "
|
150 |
with file_path.open("wb") as buffer:
|
151 |
shutil.copyfileobj(file.file, buffer)
|
152 |
return {"status": "ok"}
|
|
|
49 |
|
50 |
def save(req: SaveRequest):
|
51 |
path = data_path / req.path
|
52 |
+
assert path.is_relative_to(data_path), f"Path '{path}' is invalid"
|
53 |
req.ws.save(path)
|
54 |
|
55 |
|
|
|
66 |
async def delete_workspace(req: dict):
|
67 |
json_path: pathlib.Path = data_path / req["path"]
|
68 |
crdt_path: pathlib.Path = data_path / ".crdt" / f"{req['path']}.crdt"
|
69 |
+
assert json_path.is_relative_to(data_path), f"Path '{json_path}' is invalid"
|
70 |
json_path.unlink()
|
71 |
crdt_path.unlink()
|
72 |
|
|
|
74 |
@app.get("/api/load")
|
75 |
def load(path: str):
|
76 |
path = data_path / path
|
77 |
+
assert path.is_relative_to(data_path), f"Path '{path}' is invalid"
|
78 |
if not path.exists():
|
79 |
return workspace.Workspace()
|
80 |
return workspace.Workspace.load(path)
|
|
|
97 |
@app.get("/api/dir/list")
|
98 |
def list_dir(path: str):
|
99 |
path = data_path / path
|
100 |
+
assert path.is_relative_to(data_path), f"Path '{path}' is invalid"
|
101 |
return sorted(
|
102 |
[
|
103 |
DirectoryEntry(
|
|
|
114 |
@app.post("/api/dir/mkdir")
|
115 |
def make_dir(req: dict):
|
116 |
path = data_path / req["path"]
|
117 |
+
assert path.is_relative_to(data_path), f"Path '{path}' is invalid"
|
118 |
assert not path.exists(), f"{path} already exists"
|
119 |
path.mkdir()
|
120 |
|
|
|
122 |
@app.post("/api/dir/delete")
|
123 |
def delete_dir(req: dict):
|
124 |
path: pathlib.Path = data_path / req["path"]
|
125 |
+
assert all([path.is_relative_to(data_path), path.exists(), path.is_dir()]), (
|
126 |
+
f"Path '{path}' is invalid"
|
127 |
+
)
|
128 |
shutil.rmtree(path)
|
129 |
|
130 |
|
|
|
148 |
form = await req.form()
|
149 |
for file in form.values():
|
150 |
file_path = data_path / "uploads" / file.filename
|
151 |
+
assert file_path.is_relative_to(data_path), f"Path '{file_path}' is invalid"
|
152 |
with file_path.open("wb") as buffer:
|
153 |
shutil.copyfileobj(file.file, buffer)
|
154 |
return {"status": "ok"}
|
lynxkite-app/web/src/main.tsx
CHANGED
@@ -2,21 +2,52 @@ import { StrictMode } from "react";
|
|
2 |
import { createRoot } from "react-dom/client";
|
3 |
import "@xyflow/react/dist/style.css";
|
4 |
import "./index.css";
|
5 |
-
import {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
import Code from "./Code.tsx";
|
7 |
import Directory from "./Directory.tsx";
|
8 |
import Workspace from "./workspace/Workspace.tsx";
|
9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
createRoot(document.getElementById("root")!).render(
|
11 |
<StrictMode>
|
12 |
-
<
|
13 |
-
<Routes>
|
14 |
-
<Route path="/" element={<Directory />} />
|
15 |
-
<Route path="/dir" element={<Directory />} />
|
16 |
-
<Route path="/dir/*" element={<Directory />} />
|
17 |
-
<Route path="/edit/*" element={<Workspace />} />
|
18 |
-
<Route path="/code/*" element={<Code />} />
|
19 |
-
</Routes>
|
20 |
-
</BrowserRouter>
|
21 |
</StrictMode>,
|
22 |
);
|
|
|
2 |
import { createRoot } from "react-dom/client";
|
3 |
import "@xyflow/react/dist/style.css";
|
4 |
import "./index.css";
|
5 |
+
import {
|
6 |
+
Link,
|
7 |
+
Route,
|
8 |
+
RouterProvider,
|
9 |
+
createBrowserRouter,
|
10 |
+
createRoutesFromElements,
|
11 |
+
useRouteError,
|
12 |
+
} from "react-router";
|
13 |
import Code from "./Code.tsx";
|
14 |
import Directory from "./Directory.tsx";
|
15 |
import Workspace from "./workspace/Workspace.tsx";
|
16 |
|
17 |
+
function WorkspaceError() {
|
18 |
+
const error = useRouteError();
|
19 |
+
const stack = error instanceof Error ? error.stack : null;
|
20 |
+
return (
|
21 |
+
<div className="hero min-h-screen">
|
22 |
+
<div className="card bg-base-100 shadow-sm">
|
23 |
+
<div className="card-body">
|
24 |
+
<h2 className="card-title">Something went wrong...</h2>
|
25 |
+
<pre>{stack || "Unknown error."}</pre>
|
26 |
+
<div className="card-actions justify-end">
|
27 |
+
<Link to="/" className="btn btn-primary">
|
28 |
+
Close workspace
|
29 |
+
</Link>
|
30 |
+
</div>
|
31 |
+
</div>
|
32 |
+
</div>
|
33 |
+
</div>
|
34 |
+
);
|
35 |
+
}
|
36 |
+
|
37 |
+
const router = createBrowserRouter(
|
38 |
+
createRoutesFromElements(
|
39 |
+
<>
|
40 |
+
<Route path="/" element={<Directory />} />
|
41 |
+
<Route path="/dir" element={<Directory />} />
|
42 |
+
<Route path="/dir/*" element={<Directory />} />
|
43 |
+
<Route path="/edit/*" element={<Workspace />} errorElement={<WorkspaceError />} />
|
44 |
+
<Route path="/code/*" element={<Code />} />
|
45 |
+
</>,
|
46 |
+
),
|
47 |
+
);
|
48 |
+
|
49 |
createRoot(document.getElementById("root")!).render(
|
50 |
<StrictMode>
|
51 |
+
<RouterProvider router={router} />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
</StrictMode>,
|
53 |
);
|
lynxkite-core/src/lynxkite/core/ops.py
CHANGED
@@ -404,7 +404,7 @@ def load_user_scripts(workspace: str):
|
|
404 |
load_catalogs("plugins loaded")
|
405 |
cwd = pathlib.Path()
|
406 |
path = cwd / workspace
|
407 |
-
assert path.is_relative_to(cwd), "
|
408 |
for p in path.parents:
|
409 |
req = p / "requirements.txt"
|
410 |
if req.exists():
|
|
|
404 |
load_catalogs("plugins loaded")
|
405 |
cwd = pathlib.Path()
|
406 |
path = cwd / workspace
|
407 |
+
assert path.is_relative_to(cwd), f"Path '{path}' is invalid"
|
408 |
for p in path.parents:
|
409 |
req = p / "requirements.txt"
|
410 |
if req.exists():
|
lynxkite-graph-analytics/src/lynxkite_graph_analytics/ml_ops.py
CHANGED
@@ -20,7 +20,7 @@ op = ops.op_registration(core.ENV)
|
|
20 |
def load_ws(model_workspace: str):
|
21 |
cwd = pathlib.Path()
|
22 |
path = cwd / model_workspace
|
23 |
-
assert path.is_relative_to(cwd)
|
24 |
assert path.exists(), f"Workspace {path} does not exist"
|
25 |
ws = workspace.Workspace.load(path)
|
26 |
return ws
|
|
|
20 |
def load_ws(model_workspace: str):
|
21 |
cwd = pathlib.Path()
|
22 |
path = cwd / model_workspace
|
23 |
+
assert path.is_relative_to(cwd), f"Path '{path}' is invalid"
|
24 |
assert path.exists(), f"Workspace {path} does not exist"
|
25 |
ws = workspace.Workspace.load(path)
|
26 |
return ws
|
lynxkite-lynxscribe/src/lynxkite_lynxscribe/lynxscribe_ops.py
CHANGED
@@ -873,7 +873,7 @@ async def get_chat_api(ws: str):
|
|
873 |
|
874 |
cwd = pathlib.Path()
|
875 |
path = cwd / ws
|
876 |
-
assert path.is_relative_to(cwd)
|
877 |
assert path.exists(), f"Workspace {path} does not exist"
|
878 |
ws = workspace.Workspace.load(path)
|
879 |
contexts = await ops.EXECUTORS[ENV](ws)
|
|
|
873 |
|
874 |
cwd = pathlib.Path()
|
875 |
path = cwd / ws
|
876 |
+
assert path.is_relative_to(cwd), f"Path '{path}' is invalid"
|
877 |
assert path.exists(), f"Workspace {path} does not exist"
|
878 |
ws = workspace.Workspace.load(path)
|
879 |
contexts = await ops.EXECUTORS[ENV](ws)
|