From d4013a07a9e90f66a242b0d260510cf5a7cbc394 Mon Sep 17 00:00:00 2001 From: "tao.chen" <93983997+taochen-ct@users.noreply.github.com> Date: Tue, 11 Aug 2026 20:41:15 +0800 Subject: [PATCH] rollback --- backend/src/backend/jupyter.py | 50 ++++++++++++++-------------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/backend/src/backend/jupyter.py b/backend/src/backend/jupyter.py index a5f8094..230375c 100644 --- a/backend/src/backend/jupyter.py +++ b/backend/src/backend/jupyter.py @@ -1,19 +1,16 @@ -from __future__ import annotations - import re from typing import Optional +from common.auth.jwt import JwtError, verify_jwt_token +from common.auth.membership import MembershipError, load_active_membership +from common.db.models import Scripts from fastapi import APIRouter, Depends, HTTPException, Request, Response -from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials +from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from backend.dependencies import database_session from backend.runtime_client import RuntimeClientError -from common.auth.jwt import JwtError, verify_jwt_token -from common.auth.membership import MembershipError, load_active_membership -from common.db.models import Scripts - router = APIRouter(tags=["jupyter"]) security = HTTPBearer(auto_error=False) @@ -25,19 +22,14 @@ def extract_notebook_path( ) -> Optional[str]: """Pull the relative notebook path out of the original request URI. - Match ``/jupyter/{workspace_id}/notebooks/*.ipynb`` (the legacy - iframe flow) and ``/jupyter/{workspace_id}/api/contents/*.ipynb`` - (the Monaco-based Contents API PUT path). The caller decides - whether to actually enforce the lock — typically only on writes. + Only ``/jupyter/{workspace_id}/notebooks/*.ipynb`` requests are + subject to file-level lock checks; everything else (tree views, + ``/api/contents`` and WebSocket upgrades) bypasses the lock. """ - nb_pattern = rf"^/jupyter/{re.escape(workspace_id)}/notebooks/(.+\.ipynb)" - contents_pattern = ( - rf"^/jupyter/{re.escape(workspace_id)}/api/contents/(.+\.ipynb)" - ) - for pattern in (nb_pattern, contents_pattern): - match = re.match(pattern, uri) - if match: - return match.group(1) + pattern = rf"^/jupyter/{re.escape(workspace_id)}/notebooks/(.+\.ipynb)" + match = re.match(pattern, uri) + if match: + return match.group(1) return None @@ -131,16 +123,16 @@ async def verify_jupyter_access( await load_active_membership_or_403(session, user_id, workspace_id) notebook_path = extract_notebook_path(original_uri, workspace_id) - # Lock only on writes — reads (GET) stay open so anyone can still - # preview a locked notebook the way they could via the iframe before. - if notebook_path and request.method in {"PUT", "POST", "PATCH", "DELETE"}: - if await check_notebook_is_locked( - session, workspace_id, notebook_path, user_id, - ): - raise HTTPException( - status_code=403, - detail=f"Notebook '{notebook_path}' is currently locked", - ) + if notebook_path and await check_notebook_is_locked( + session, + workspace_id, + notebook_path, + user_id, + ): + raise HTTPException( + status_code=403, + detail=f"Notebook '{notebook_path}' is currently locked", + ) runtime_client = request.app.state.runtime_client ws_info = await runtime_client.get_workspace(workspace_id)