feat: expose accessible storage locations

This commit is contained in:
OpenClaw Agent
2026-09-22 02:05:06 +08:00
parent d738fa41a3
commit 901321e329
3 changed files with 50 additions and 1 deletions
+3 -1
View File
@@ -25,7 +25,7 @@ table{width:100%;border-collapse:collapse}th,td{text-align:left;padding:13px 15p
<main>
<div id="serverNotice">__CHFS_NOTICE__</div><div id="notice"></div>
<div class="toolbar">
<button onclick="goUp()">上一级</button><button onclick="refresh()">刷新</button>
<button onclick="goUp()">上一级</button><button onclick="refresh()">刷新</button><button class="ghost" onclick="showRoots()">位置</button>
<button id="newFolderBtn" onclick="newFolder()">新建文件夹</button><button id="newTextBtn" onclick="newText()">新建文本</button>
<label><button id="uploadBtn">上传</button><input id="upload" type="file" multiple hidden></label>
<input id="search" placeholder="搜索文件名,回车执行">
@@ -43,6 +43,8 @@ async function init(){try{me=await api("/chfs/me")}catch{me={username:"",home:""
function updateUser(){$("who").textContent=me.username?me.username: "访客";$("loginBtn").hidden=!!me.username;$("logoutBtn").hidden=!me.username;$("usersBtn").hidden=!me.admin;$("pwdBtn").hidden=!me.username||!me.managed}
async function refresh(){try{const q=encodeURIComponent(current);const data=await api("/chfs/files?filepath="+q);currentMask=data.currentMask||"";const writable=currentMask.includes("W");$("newFolderBtn").disabled=!writable;$("newTextBtn").disabled=!writable;$("uploadBtn").disabled=!writable;$("search").disabled=!data.currentMask?.includes("R");render(data.files||[]);$("crumb").textContent="/"+current;$("notice").innerHTML=""}catch(e){$("notice").innerHTML='<div class="notice">'+esc(e.message)+"</div>"}}
function render(items){const body=$("files");body.innerHTML="";if(!items.length){body.innerHTML='<tr><td colspan="4" class="empty">这里还没有文件</td></tr>';return}for(const f of items){const p=current?current+"/"+f.name:f.name;const row=document.createElement("tr");const icon=f.dir?"📁":"📄";const share=f.guestMask&&f.guestMask.includes("R")?'<button class="ghost" onclick="shareItem('+JSON.stringify(p)+')">分享</button>':"";const canRead=(f.mask||"").includes("R"),canWrite=(f.mask||"").includes("W"),canDelete=(f.mask||"").includes("D");const actions=(canRead?(f.dir?'<button class="ghost" onclick="downloadDir('+JSON.stringify(p)+')">ZIP</button>':'<button class="ghost" onclick="downloadFile('+JSON.stringify(p)+')">下载</button>'):"")+(canRead?share:"")+(canWrite?'<button class="ghost" onclick="renameItem('+JSON.stringify(p)+')">改名</button>':"")+(canDelete?'<button class="danger" onclick="removeItem('+JSON.stringify(p)+')">删除</button>':"");row.innerHTML='<td><div class="name"><span>'+icon+'</span>'+ (f.dir&&canRead?'<a href="#" onclick="openDir('+JSON.stringify(p)+');return false">'+esc(f.name)+'</a>':'<span>'+esc(f.name)+'</span>')+'</div></td><td class="meta">'+(f.dir?"—":fmt(f.size))+'</td><td class="meta">'+esc(f.modified||"")+'</td><td><div class="actions">'+actions+'</div></td>';body.appendChild(row)}}
async function showRoots(){try{const d=await api("/chfs/roots");const roots=d.roots||[];let html='<h2>选择位置</h2><div>';for(const r of roots)html+='<button class="ghost" style="display:block;width:100%;text-align:left;margin:8px 0" onclick="jumpRoot('+JSON.stringify(r.path)+')"><b>'+esc(r.name)+'</b><span class="meta"> · '+esc(r.path||"/")+' · '+esc(r.mask)+'</span></button>';if(!roots.length)html+='<div class="empty">没有可进入的共享位置</div>';html+='</div><div class="row"><button class="ghost" onclick="closeModal()">关闭</button></div>';show(html)}catch(e){alert(e.message)}}
function jumpRoot(p){current=p;closeModal();refresh()}
function openDir(p){current=p;refresh()}function goUp(){if(!current)return;const root=me.home||"";if(current===root)return;let p=current.split("/");p.pop();let n=p.join("/");if(root&&!(n===root||n.startsWith(root+"/")))n=root;current=n;refresh()}
function downloadFile(p){location.href="/chfs/shared/"+encodeURIComponent(p).replace(/%2F/g,"/")}
function downloadDir(p){location.href="/chfs/downloaddir/"+encodeURI(p)}