Develop #41

Merged
tao.chen merged 96 commits from develop into main 2026-09-02 10:15:06 +08:00
3 changed files with 24 additions and 8 deletions
Showing only changes of commit a9b682c1a1 - Show all commits
+1 -1
View File
@@ -28,6 +28,7 @@ from backend.dependencies import (
database_session, database_session,
request_context, request_context,
) )
from backend.scripts import _escape_like_pattern
from backend.schemas import ( from backend.schemas import (
CompleteResourceUploadRequest, CompleteResourceUploadRequest,
CreateResourceUploadRequest, CreateResourceUploadRequest,
@@ -402,7 +403,6 @@ async def list_resources(
if keyword: if keyword:
# Escape LIKE metacharacters so a search like "100%" or "my_file" # Escape LIKE metacharacters so a search like "100%" or "my_file"
# doesn't act as a wildcard. The outer "%...%" wildcards stay raw. # doesn't act as a wildcard. The outer "%...%" wildcards stay raw.
from backend.scripts import _escape_like_pattern # local import: avoid cycle
escaped = _escape_like_pattern(keyword.strip()) escaped = _escape_like_pattern(keyword.strip())
statement = statement.where( statement = statement.where(
DataResources.resource_name.like(f"%{escaped}%", escape="\\") DataResources.resource_name.like(f"%{escaped}%", escape="\\")
+12 -5
View File
@@ -1138,11 +1138,18 @@ async def list_scripts(
# 避免被 listScripts 的懒加载语义污染。该路由必须在 /scripts/{script_id} 之前声明 # 避免被 listScripts 的懒加载语义污染。该路由必须在 /scripts/{script_id} 之前声明
# ——FastAPI 按声明顺序匹配,否则 `count` 会被当作 script_id 命中 get_script。 # ——FastAPI 按声明顺序匹配,否则 `count` 会被当作 script_id 命中 get_script。
# #
# 范围与 list_scripts(parent_path="") 对齐:INNER JOIN 到 StorageObjects 以排除 # Scope: equals the UNION of list_scripts across every parent_path
# 孤儿脚本(其 current_object_id 没有 joinable row),按用户子树 # within the user's subtree, NOT just the root-level call. We
# (workspace/{user_id}/) 过滤。否则: # intentionally include deeper descendants because the dashboard's
# - 含孤儿 → 数字虚高 # "全部脚本" / "工作副本" counts reflect the whole workspace, not
# - workspace 范围 → 多成员工作区里 dashboard 会显示用户看不见的脚本 # only the top level.
#
# Implementation choices and why:
# - INNER JOIN to StorageObjects so orphans (current_object_id has no
# joinable row) are excluded.
# - User subtree filter so multi-member workspaces don't show counts
# the requester can't see.
# - No NOT-LIKE filter because the count wants descendants too.
@router.get("/api/v1/scripts/count") @router.get("/api/v1/scripts/count")
async def count_scripts( async def count_scripts(
context: RequestContext = Depends(request_context), context: RequestContext = Depends(request_context),
@@ -40,6 +40,7 @@ let _api: WorkspaceBoundApi | null = null;
let _previewController: AbortController | null = null; let _previewController: AbortController | null = null;
let _previewRequest = 0; let _previewRequest = 0;
let _pythonEditorOpeningIds = new Set<string>(); let _pythonEditorOpeningIds = new Set<string>();
let _scriptCountSeq = 0;
export const bindScriptWorkspaceApi = (api: WorkspaceBoundApi | null) => { export const bindScriptWorkspaceApi = (api: WorkspaceBoundApi | null) => {
_api = api; _api = api;
@@ -397,17 +398,25 @@ export const useScriptWorkspaceStore = create<State>((set, get) => {
loadScriptCount: async () => { loadScriptCount: async () => {
const api = requireApi(); const api = requireApi();
if (get().scriptCountLoading) return; // Always fire — don't dedupe via the loading flag. Rapid workspace
// switches would otherwise drop the new fetch and leave the
// dashboard showing the previous workspace's count. The sequence
// counter below discards stale responses instead.
const seq = ++_scriptCountSeq;
set({ scriptCountLoading: true, scriptCount: null }); set({ scriptCountLoading: true, scriptCount: null });
try { try {
const total = await api.countScripts(); const total = await api.countScripts();
if (_scriptCountSeq !== seq) return; // a newer fetch superseded us
set({ scriptCount: total }); set({ scriptCount: total });
} catch { } catch {
if (_scriptCountSeq !== seq) return;
// Leave previous value in place; the dashboard already tolerates // Leave previous value in place; the dashboard already tolerates
// a stale count by rendering `scriptCount ?? 0`. Don't toast — // a stale count by rendering `scriptCount ?? 0`. Don't toast —
// the dashboard's other metrics are best-effort. // the dashboard's other metrics are best-effort.
} finally { } finally {
set({ scriptCountLoading: false }); if (_scriptCountSeq === seq) {
set({ scriptCountLoading: false });
}
} }
}, },