fix(library): tab Workspace affiche le contenu du workspace local actif
- Nouvel endpoint /api/library/local-workspace?workspace_id=X
interroge local_workspace_items et formate pour la Library
- Nouvel endpoint /api/library/local-workspace-children/{id}
pour l'expansion d'arborescence des items du workspace local
- Frontend: détecte workspaceId + !isGiteaActive → appelle local-workspace
- Frontend: toggleExpand route vers le bon endpoint selon source_type
This commit is contained in:
@@ -255,6 +255,69 @@ async def library_private(
|
|||||||
return {"items": items}
|
return {"items": items}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/local-workspace-children/{item_id:int}")
|
||||||
|
async def library_local_workspace_children(item_id: int, request: Request):
|
||||||
|
"""Return children of a local workspace item for tree expansion."""
|
||||||
|
uid = _get_user_id(request)
|
||||||
|
with get_conn() as conn:
|
||||||
|
# Get the item to find its workspace
|
||||||
|
item = conn.execute(
|
||||||
|
"SELECT workspace_id FROM local_workspace_items WHERE id=? AND deleted_at IS NULL",
|
||||||
|
[item_id],
|
||||||
|
).fetchone()
|
||||||
|
if not item:
|
||||||
|
return {"items": []}
|
||||||
|
|
||||||
|
rows = conn.execute(
|
||||||
|
"SELECT id, name, is_folder, parent_id, content_format, size, "
|
||||||
|
"COALESCE(updated_at, created_at) as updated_at "
|
||||||
|
"FROM local_workspace_items "
|
||||||
|
"WHERE parent_id = ? AND deleted_at IS NULL "
|
||||||
|
"ORDER BY is_folder DESC, name COLLATE NOCASE",
|
||||||
|
[item_id],
|
||||||
|
).fetchall()
|
||||||
|
|
||||||
|
items = []
|
||||||
|
for r in rows:
|
||||||
|
name = r["name"] or "Untitled"
|
||||||
|
is_folder = bool(r["is_folder"])
|
||||||
|
icon = "📁" if is_folder else "📄"
|
||||||
|
fn = name.lower()
|
||||||
|
if not is_folder:
|
||||||
|
if fn.endswith(".pdf"): icon = "📕"
|
||||||
|
elif any(fn.endswith(e) for e in [".png",".jpg",".jpeg",".gif",".webp",".svg"]): icon = "🖼️"
|
||||||
|
elif any(fn.endswith(e) for e in [".py",".js",".ts",".go",".rs"]): icon = "📜"
|
||||||
|
|
||||||
|
with get_conn() as conn:
|
||||||
|
child_count = conn.execute(
|
||||||
|
"SELECT COUNT(*) FROM local_workspace_items WHERE parent_id=? AND deleted_at IS NULL",
|
||||||
|
[r["id"]],
|
||||||
|
).fetchone()[0]
|
||||||
|
|
||||||
|
items.append({
|
||||||
|
"id": r["id"],
|
||||||
|
"title": name,
|
||||||
|
"icon": icon,
|
||||||
|
"is_folder": is_folder,
|
||||||
|
"source_type": "local-ws",
|
||||||
|
"source_label": "",
|
||||||
|
"workspace": "",
|
||||||
|
"workspace_name": "",
|
||||||
|
"author": "",
|
||||||
|
"author_initial": "?",
|
||||||
|
"updated_at": r["updated_at"] or "",
|
||||||
|
"visited_at": "",
|
||||||
|
"has_children": child_count > 0,
|
||||||
|
"children": [],
|
||||||
|
"url": f"/local-workspace?folder={r['id']}" if is_folder else f"/pages/{r['id']}",
|
||||||
|
"content_format": r["content_format"] or "file",
|
||||||
|
"favorited": False,
|
||||||
|
"size_display": _format_size(r["size"]) if r["size"] else "",
|
||||||
|
})
|
||||||
|
|
||||||
|
return {"items": items}
|
||||||
|
|
||||||
|
|
||||||
@router.get("/children/{page_id:int}")
|
@router.get("/children/{page_id:int}")
|
||||||
async def library_children(page_id: int, request: Request):
|
async def library_children(page_id: int, request: Request):
|
||||||
"""Return child pages for a given parent page (for tree expansion in Library)."""
|
"""Return child pages for a given parent page (for tree expansion in Library)."""
|
||||||
@@ -295,6 +358,82 @@ async def library_repository(
|
|||||||
return {"items": items}
|
return {"items": items}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/local-workspace")
|
||||||
|
async def library_local_workspace(
|
||||||
|
request: Request,
|
||||||
|
workspace_id: int = Query(default=0),
|
||||||
|
):
|
||||||
|
"""Return local workspace items (files/folders) formatted for Library display."""
|
||||||
|
from app.routers.dashboard import _get_active_workspace
|
||||||
|
uid = _get_user_id(request)
|
||||||
|
|
||||||
|
# Get the active workspace
|
||||||
|
ws = _get_active_workspace(request, user_id=uid)
|
||||||
|
if not ws:
|
||||||
|
return {"items": []}
|
||||||
|
|
||||||
|
ws_id = workspace_id or ws["id"]
|
||||||
|
|
||||||
|
# Query local workspace tree
|
||||||
|
with get_conn() as conn:
|
||||||
|
rows = conn.execute(
|
||||||
|
"SELECT id, name, is_folder, parent_id, content_format, size, "
|
||||||
|
"COALESCE(updated_at, created_at) as updated_at "
|
||||||
|
"FROM local_workspace_items "
|
||||||
|
"WHERE workspace_id = ? AND deleted_at IS NULL "
|
||||||
|
"ORDER BY is_folder DESC, name COLLATE NOCASE",
|
||||||
|
[ws_id],
|
||||||
|
).fetchall()
|
||||||
|
|
||||||
|
items = []
|
||||||
|
for r in rows:
|
||||||
|
name = r["name"] or "Untitled"
|
||||||
|
is_folder = bool(r["is_folder"])
|
||||||
|
icon = "📁" if is_folder else "📄"
|
||||||
|
fn = name.lower()
|
||||||
|
if not is_folder:
|
||||||
|
if fn.endswith(".pdf"): icon = "📕"
|
||||||
|
elif any(fn.endswith(e) for e in [".png",".jpg",".jpeg",".gif",".webp",".svg"]): icon = "🖼️"
|
||||||
|
elif any(fn.endswith(e) for e in [".py",".js",".ts",".go",".rs"]): icon = "📜"
|
||||||
|
|
||||||
|
# Check for children
|
||||||
|
child_count = conn.execute(
|
||||||
|
"SELECT COUNT(*) FROM local_workspace_items WHERE parent_id=? AND deleted_at IS NULL",
|
||||||
|
[r["id"]],
|
||||||
|
).fetchone()[0]
|
||||||
|
|
||||||
|
items.append({
|
||||||
|
"id": r["id"],
|
||||||
|
"title": name,
|
||||||
|
"icon": icon,
|
||||||
|
"is_folder": is_folder,
|
||||||
|
"source_type": "local-ws",
|
||||||
|
"source_label": ws.get("name", "Workspace"),
|
||||||
|
"workspace": ws.get("name", ""),
|
||||||
|
"workspace_name": ws.get("name", ""),
|
||||||
|
"author": "",
|
||||||
|
"author_initial": "?",
|
||||||
|
"updated_at": r["updated_at"] or "",
|
||||||
|
"visited_at": "",
|
||||||
|
"has_children": child_count > 0,
|
||||||
|
"children": [],
|
||||||
|
"url": f"/local-workspace?folder={r['id']}" if is_folder else f"/pages/{r['id']}",
|
||||||
|
"content_format": r["content_format"] or "file",
|
||||||
|
"favorited": False,
|
||||||
|
"size_display": _format_size(r["size"]) if r["size"] else "",
|
||||||
|
})
|
||||||
|
|
||||||
|
return {"items": items}
|
||||||
|
|
||||||
|
|
||||||
|
def _format_size(size_bytes):
|
||||||
|
if not size_bytes: return ""
|
||||||
|
if size_bytes < 1024: return f"{size_bytes} B"
|
||||||
|
if size_bytes < 1048576: return f"{size_bytes/1024:.1f} KB"
|
||||||
|
if size_bytes < 1073741824: return f"{size_bytes/1048576:.1f} MB"
|
||||||
|
return f"{size_bytes/1073741824:.1f} GB"
|
||||||
|
|
||||||
|
|
||||||
@router.get("/workspace")
|
@router.get("/workspace")
|
||||||
async def library_workspace(
|
async def library_workspace(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|||||||
@@ -464,8 +464,14 @@ function libraryPage() {
|
|||||||
try {
|
try {
|
||||||
var url = '/api/library/' + this.tab;
|
var url = '/api/library/' + this.tab;
|
||||||
var sep = '?';
|
var sep = '?';
|
||||||
if (this.workspaceId && (this.tab === 'recents')) {
|
// Workspace tab: if local workspace is active, show local workspace items
|
||||||
url += sep + 'workspace_id=' + this.workspaceId; sep = '&';
|
if (this.tab === 'workspace' && this.workspaceId && !this.isGiteaActive) {
|
||||||
|
url = '/api/library/local-workspace' + sep + 'workspace_id=' + this.workspaceId;
|
||||||
|
sep = '&';
|
||||||
|
} else {
|
||||||
|
if (this.workspaceId && (this.tab === 'recents')) {
|
||||||
|
url += sep + 'workspace_id=' + this.workspaceId; sep = '&';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (this.tab === 'repository' && this.isGiteaActive) {
|
if (this.tab === 'repository' && this.isGiteaActive) {
|
||||||
url += sep + 'gitea_owner=' + encodeURIComponent(this.giteaOwner) + '&gitea_repo=' + encodeURIComponent(this.giteaRepo);
|
url += sep + 'gitea_owner=' + encodeURIComponent(this.giteaOwner) + '&gitea_repo=' + encodeURIComponent(this.giteaRepo);
|
||||||
@@ -522,7 +528,12 @@ function libraryPage() {
|
|||||||
this.expanded = Object.assign({}, this.expanded);
|
this.expanded = Object.assign({}, this.expanded);
|
||||||
if (!item.children || !item.children.length) {
|
if (!item.children || !item.children.length) {
|
||||||
try {
|
try {
|
||||||
var r = await fetch('/api/library/children/' + item.id);
|
// Route to correct children endpoint based on source type
|
||||||
|
var childrenUrl = '/api/library/children/' + item.id;
|
||||||
|
if (item.source_type === 'local-ws') {
|
||||||
|
childrenUrl = '/api/library/local-workspace-children/' + item.id;
|
||||||
|
}
|
||||||
|
var r = await fetch(childrenUrl);
|
||||||
var d = await r.json();
|
var d = await r.json();
|
||||||
item.children = d.items || [];
|
item.children = d.items || [];
|
||||||
// Mark the tree for depth calculation
|
// Mark the tree for depth calculation
|
||||||
|
|||||||
Reference in New Issue
Block a user