MarkBase架构升级:Multi-Volume Virtual Tree + Dual-View Management + Git Remote修正
核心功能: - ✅ Categories/Series双视图管理(category_view.rs + import_markdown.rs) - ✅ FUSE Multi-Volume支持(tree_type参数) - ✅ SSH/SFTP/SCP/rsync协议完整实现(4042行) - ✅ NFS/SMB Module Phase 1-3完成 - ✅ Archive Module Phase 1-4完成(2916行) - ✅ Download Center API完整实现 - ✅ S3兼容API实现(560行) Git配置修正: - ✅ 删除错误origin(gitea.momentry.ddns.net) - ✅ 删除m5max128(指向机器名) - ✅ 设置origin = m5max128gitea.momentry.ddns.net/admin/markbase - ✅ 设置m4minigitea = m4minigitea.momentry.ddns.net/warren/markbase 数据清理: - ✅ 删除38个临时SQLite(保留accusys.sqlite、demo.sqlite) - ✅ 删除.bak、test_*.bin、调试脚本等临时文件 - ✅ 删除临时目录(build/、download files/、raid_test/等) - ✅ 更新.gitignore排除临时文件 架构优化: - 52个文件修改,2434行新增,4739行删除 - Workspace成员整合(16个crate) - 数据库状态:accusys.sqlite保留(主demo测试) 远程同步: - ✅ 准备推送到m5max128gitea(远程Gitea) - ✅ 准备推送到m4minigitea(本地Gitea)
This commit is contained in:
@@ -105,6 +105,7 @@ color:#64748b;font-size:18px;cursor:pointer}
|
||||
<div id=mb-detail><button id=mb-detail-close onclick="closeDetail()">✕</button><div id=mb-detail-body></div></div>
|
||||
<div id=mb-tree-panel><div id=mb-tree-body></div></div>
|
||||
<div id=mb-settings-panel><div id=mb-settings-body></div></div>
|
||||
<div id=mb-s3-panel style="display:none;position:fixed;top:0;left:0;right:0;bottom:52px;background:#0f172a;z-index:9998;overflow-y:auto;padding:16px 24px"><div id=mb-s3-body></div></div>
|
||||
|
||||
<div id=mb-bar style="position:fixed;bottom:0;left:0;right:0;background:#1e293b;border-top:1px solid #334155;display:flex;justify-content:center;align-items:center;gap:5px;padding:5px 10px;z-index:9999;font-size:12px">
|
||||
<button onclick="fetch('/command',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({cmd:'restart'})})" title="Restart">⏮</button>
|
||||
@@ -117,6 +118,8 @@ color:#64748b;font-size:18px;cursor:pointer}
|
||||
<span style=color:#475569;font-size:10px>|</span>
|
||||
<button onclick="toggleTree()" title="File Tree" style="background:none;border:none;color:#60a5fa;cursor:pointer;font-size:16px">🗂</button>
|
||||
<span style=color:#475569;font-size:10px>|</span>
|
||||
<button onclick="toggleS3()" title="S3 Service" style="background:none;border:none;color:#60a5fa;cursor:pointer;font-size:16px">☁️</button>
|
||||
<span style=color:#475569;font-size:10px>|</span>
|
||||
<button onclick="toggleSettings()" title="Settings" style="background:none;border:none;color:#60a5fa;cursor:pointer;font-size:16px">⚙️</button>
|
||||
<span style=color:#475569;font-size:10px>|</span>
|
||||
<button onclick="var t=this.textContent;this.textContent=t===String.fromCodePoint(0x1F50A)?String.fromCodePoint(0x1F507):String.fromCodePoint(0x1F50A)" id=mbvb title=Voice style=font-size:16px>🔊</button>
|
||||
@@ -426,37 +429,52 @@ d.forEach(function(x){var o=document.createElement("option");o.value=x.num;o.tex
|
||||
var _tv=false, _tm="tree", _td=null, _tree_user=null;
|
||||
|
||||
function toggleTree(){
|
||||
var token=localStorage.getItem('tree_token');
|
||||
var savedUser=localStorage.getItem('tree_user');
|
||||
var savedMode=localStorage.getItem('display_mode')||'categories';
|
||||
|
||||
if(token && savedUser){
|
||||
// Verify token validity
|
||||
fetch('/api/v2/auth/verify',{
|
||||
headers:{'Authorization':'Bearer '+token}
|
||||
})
|
||||
.then(function(r){return r.json()})
|
||||
.then(function(d){
|
||||
if(d.ok && d.user_id===savedUser){
|
||||
// Token valid, open tree
|
||||
_tree_user=savedUser;
|
||||
_tv=!_tv;
|
||||
document.getElementById("mb-tree-panel").classList.toggle("active",_tv);
|
||||
if(_tv)loadTree();
|
||||
}else{
|
||||
// Token invalid or user mismatch, clear and show login
|
||||
if(savedMode==='categories' || savedMode==='series'){
|
||||
_tm=savedMode;
|
||||
_tv=!_tv;
|
||||
if(!_tv){
|
||||
localStorage.setItem('display_mode','categories');
|
||||
}
|
||||
document.getElementById("mb-tree-panel").classList.toggle("active",_tv);
|
||||
if(_tv)loadTree();
|
||||
}else{
|
||||
var token=localStorage.getItem('tree_token');
|
||||
var savedUser=localStorage.getItem('tree_user');
|
||||
|
||||
if(token && savedUser){
|
||||
fetch('/api/v2/auth/verify',{
|
||||
headers:{'Authorization':'Bearer '+token}
|
||||
})
|
||||
.then(function(r){return r.json()})
|
||||
.then(function(d){
|
||||
if(d.ok && d.user_id===savedUser){
|
||||
_tree_user=savedUser;
|
||||
_tm=savedMode;
|
||||
_tv=!_tv;
|
||||
if(!_tv){
|
||||
localStorage.setItem('display_mode','categories');
|
||||
}
|
||||
document.getElementById("mb-tree-panel").classList.toggle("active",_tv);
|
||||
if(_tv)loadTree();
|
||||
}else{
|
||||
localStorage.removeItem('tree_token');
|
||||
localStorage.removeItem('tree_user');
|
||||
localStorage.setItem('display_mode','categories');
|
||||
showTreeLoginModal();
|
||||
}
|
||||
})
|
||||
.catch(function(e){
|
||||
localStorage.removeItem('tree_token');
|
||||
localStorage.removeItem('tree_user');
|
||||
localStorage.setItem('display_mode','categories');
|
||||
showTreeLoginModal();
|
||||
}
|
||||
})
|
||||
.catch(function(e){
|
||||
localStorage.removeItem('tree_token');
|
||||
localStorage.removeItem('tree_user');
|
||||
});
|
||||
}else{
|
||||
localStorage.setItem('display_mode','categories');
|
||||
showTreeLoginModal();
|
||||
});
|
||||
}else{
|
||||
// No token, show login
|
||||
showTreeLoginModal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,9 +597,24 @@ function loadTree(searchQuery){
|
||||
var token=localStorage.getItem('tree_token');
|
||||
var user=_tree_user||localStorage.getItem('tree_user')||'demo';
|
||||
|
||||
var url="/api/v2/tree/"+user+"?mode="+_tm;
|
||||
if(searchQuery && searchQuery.trim()){
|
||||
url="/api/v2/tree/"+user+"/search?q="+encodeURIComponent(searchQuery.trim())+"&mode="+_tm;
|
||||
var url;
|
||||
if(_tm==="categories"){
|
||||
if(searchQuery && searchQuery.trim()){
|
||||
url="/api/v2/files/search?q="+encodeURIComponent(searchQuery.trim())+"&view=category";
|
||||
}else{
|
||||
url="/api/v2/categories";
|
||||
}
|
||||
}else if(_tm==="series"){
|
||||
if(searchQuery && searchQuery.trim()){
|
||||
url="/api/v2/files/search?q="+encodeURIComponent(searchQuery.trim())+"&view=series";
|
||||
}else{
|
||||
url="/api/v2/series";
|
||||
}
|
||||
}else{
|
||||
url="/api/v2/tree/"+user+"?mode="+_tm;
|
||||
if(searchQuery && searchQuery.trim()){
|
||||
url="/api/v2/tree/"+user+"/search?q="+encodeURIComponent(searchQuery.trim())+"&mode="+_tm;
|
||||
}
|
||||
}
|
||||
|
||||
fetch(url,{
|
||||
@@ -597,7 +630,7 @@ function loadTree(searchQuery){
|
||||
h+="</div>";
|
||||
|
||||
// Mode buttons
|
||||
var modes=[{k:"tree",i:"🌳",l:"Tree"},{k:"list",i:"📋",l:"List"},{k:"grid_sm",i:"🟦",l:"Icons"},{k:"grid_lg",i:"🔲",l:"Large"}];
|
||||
var modes=[{k:"tree",i:"🌳",l:"All Files"},{k:"categories",i:"📁",l:"Categories"},{k:"series",i:"📦",l:"Series"},{k:"grid_sm",i:"🟦",l:"Icons"},{k:"grid_lg",i:"🔲",l:"Large"}];
|
||||
h+="<div class=mb-mode-bar>";
|
||||
modes.forEach(function(m){
|
||||
h+="<button class=mb-mode-btn"+(_tm==m.k?" active":"")+" onclick='changeMode(\""+m.k+"\")'>";
|
||||
@@ -605,28 +638,41 @@ function loadTree(searchQuery){
|
||||
});
|
||||
h+="<span style=flex:1></span>";
|
||||
h+="<button class=mb-lock-btn id=mb-lock-icon onclick=toggleLock() title='Toggle edit lock'>"+(_locked?"🔒":"🔓")+"</button>";
|
||||
if(!_locked){
|
||||
if(!_locked && _tm==="tree"){
|
||||
h+="<button class=mb-new-folder-btn onclick='document.getElementById(\"mb-file-input\").click()' style='background:#064e3b;border-color:#4ade80;color:#4ade80'>📤 Upload</button>";
|
||||
h+="<input type=file id=mb-file-input style=display:none onchange=uploadFile(this)>";
|
||||
}
|
||||
if(!_locked){
|
||||
if(!_locked && _tm==="tree"){
|
||||
h+="<button class=mb-new-folder-btn onclick=organizeTree() style='background:#0c4a6e;border-color:#38bdf8;color:#38bdf8'>⚡ Agent</button>";
|
||||
}
|
||||
h+="<button class=mb-new-folder-btn onclick=newFolder()>+ Folder</button>";
|
||||
if(!_locked){
|
||||
if(_tm==="tree"){
|
||||
h+="<button class=mb-new-folder-btn onclick=newFolder()>+ Folder</button>";
|
||||
}
|
||||
if(!_locked && _tm==="tree"){
|
||||
h+="<button class=mb-new-folder-btn onclick=restoreTree() style='background:#1e3a5f;border-color:#3b82f6;color:#93c5fd'>↻ Restore</button>";
|
||||
h+="<button class=mb-new-folder-btn onclick=findDupes() style='background:#451a03;color:#fbbf24;border-color:#b45309'>🔍 Dupes</button>";
|
||||
h+="<button class=mb-new-folder-btn onclick=deleteAll() style='background:#451a03;color:#fbbf24;border-color:#b45309'>✕ All</button>";
|
||||
h+="<button class=mb-new-folder-btn onclick=logoutTree() style='background:#7f1d1d;color:#fca5a5;border-color:#dc2626'>🚪 Logout</button>";
|
||||
}
|
||||
h+="<span style=color:#64748b;font-size:12px;align-self:center>"+d.nodes.length+" nodes</span></div>";
|
||||
|
||||
var nodeCount=0;
|
||||
if(_tm==="categories" && d.categories){
|
||||
nodeCount=d.total_categories||d.categories.length||0;
|
||||
}else if(_tm==="series" && d.series){
|
||||
nodeCount=d.total_series||d.series.length||0;
|
||||
}else if(d.nodes){
|
||||
nodeCount=d.nodes.length||0;
|
||||
}
|
||||
h+="<span style=color:#64748b;font-size:12px;align-self:center>"+nodeCount+(_tm==="categories"||_tm==="series"?" items":" nodes")+"</span></div>";
|
||||
|
||||
if(_tm=="tree")h+=renderTree(d);
|
||||
if(_tm==="categories")h+=renderCategories(d);
|
||||
else if(_tm==="series")h+=renderSeries(d);
|
||||
else if(_tm=="tree")h+=renderTree(d);
|
||||
else if(_tm=="list")h+=renderList(d);
|
||||
else h+=renderGrid(d,_tm);
|
||||
b.innerHTML=h;
|
||||
}).catch(function(e){
|
||||
b.innerHTML="<div style=padding:20px;color:#ef4444>Failed to load tree: "+e+"</div>";
|
||||
b.innerHTML="<div style=padding:20px;color:#ef4444>Failed to load: "+e+"</div>";
|
||||
});
|
||||
}
|
||||
|
||||
@@ -645,9 +691,14 @@ function clearSearch(){
|
||||
|
||||
function changeMode(m){
|
||||
_tm=m;localStorage.setItem("display_mode",m);
|
||||
var searchInput=document.getElementById('mb-search-input');
|
||||
var q=searchInput?searchInput.value:'';
|
||||
loadTree(q);
|
||||
|
||||
if(m==="categories" || m==="series"){
|
||||
loadTree();
|
||||
}else{
|
||||
var searchInput=document.getElementById('mb-search-input');
|
||||
var q=searchInput?searchInput.value:'';
|
||||
loadTree(q);
|
||||
}
|
||||
}
|
||||
|
||||
function dname(n){
|
||||
@@ -740,6 +791,114 @@ function renderGrid(d,mode){
|
||||
h+="</div>";return h;
|
||||
}
|
||||
|
||||
function renderCategories(d){
|
||||
var h="<div style='padding:20px'>";
|
||||
h+="<h2 style='border:none;margin-bottom:20px;color:#60a5fa'>📁 Categories ("+(d.total_categories||0)+") - "+(d.total_files||0)+" files</h2>";
|
||||
h+="<div style='display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px'>";
|
||||
if(d.categories && d.categories.length){
|
||||
d.categories.forEach(function(cat){
|
||||
h+="<div onclick='loadCategoryDetail(\""+cat.name+"\")' style='background:#1e293b;border:1px solid #334155;border-radius:8px;padding:16px;cursor:pointer;transition:all 0.2s' onmouseover='this.style.borderColor=\"#3b82f6\";this.style.background=\"#1e3a5f\"' onmouseout='this.style.borderColor=\"#334155\";this.style.background=\"#1e293b\"'>";
|
||||
h+="<div style='font-size:18px;font-weight:600;color:#e2e8f0;margin-bottom:8px'>📁 "+(cat.display_name||cat.name)+"</div>";
|
||||
h+="<div style='font-size:13px;color:#94a3b8'>"+(cat.file_count||0)+" files</div>";
|
||||
if(cat.description){
|
||||
h+="<div style='font-size:12px;color:#64748b;margin-top:8px'>"+cat.description+"</div>";
|
||||
}
|
||||
h+="</div>";
|
||||
});
|
||||
}else{
|
||||
h+="<div style='color:#64748b'>No categories found</div>";
|
||||
}
|
||||
h+="</div></div>";
|
||||
return h;
|
||||
}
|
||||
|
||||
function renderSeries(d){
|
||||
var h="<div style='padding:20px'>";
|
||||
h+="<h2 style='border:none;margin-bottom:20px;color:#60a5fa'>📦 Product Series ("+(d.total_series||0)+") - "+(d.total_files||0)+" files</h2>";
|
||||
h+="<div style='display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px'>";
|
||||
if(d.series && d.series.length){
|
||||
d.series.forEach(function(ser){
|
||||
h+="<div onclick='loadSeriesDetail(\""+ser.name+"\")' style='background:#1e293b;border:1px solid #334155;border-radius:8px;padding:16px;cursor:pointer;transition:all 0.2s' onmouseover='this.style.borderColor=\"#3b82f6\";this.style.background=\"#1e3a5f\"' onmouseout='this.style.borderColor=\"#334155\";this.style.background=\"#1e293b\"'>";
|
||||
h+="<div style='font-size:18px;font-weight:600;color:#e2e8f0;margin-bottom:8px'>📦 "+(ser.display_name||ser.name)+"</div>";
|
||||
h+="<div style='font-size:13px;color:#94a3b8'>"+(ser.file_count||0)+" files</div>";
|
||||
if(ser.description){
|
||||
h+="<div style='font-size:12px;color:#64748b;margin-top:8px'>"+ser.description+"</div>";
|
||||
}
|
||||
h+="</div>";
|
||||
});
|
||||
}else{
|
||||
h+="<div style='color:#64748b'>No series found</div>";
|
||||
}
|
||||
h+="</div></div>";
|
||||
return h;
|
||||
}
|
||||
|
||||
function loadCategoryDetail(name){
|
||||
var b=document.getElementById("mb-tree-body");
|
||||
if(!b)return;
|
||||
b.innerHTML="<div style=text-align:center;padding:40px;color:#64748b>Loading category...</div>";
|
||||
|
||||
fetch("/api/v2/categories/"+encodeURIComponent(name)).then(function(r){return r.json()}).then(function(d){
|
||||
var h="<div style='padding:20px'>";
|
||||
h+="<button onclick='loadTree()' style='background:#1e293b;border:1px solid #334155;color:#94a3b8;padding:8px 16px;border-radius:6px;cursor:pointer;margin-bottom:16px'>← Back to Categories</button>";
|
||||
h+="<h2 style='border:none;margin:20px 0;color:#60a5fa'>📁 "+(d.category.display_name||d.category.name)+" - "+d.category.file_count+" files</h2>";
|
||||
|
||||
if(d.series_groups && d.series_groups.length){
|
||||
d.series_groups.forEach(function(group){
|
||||
h+="<h3 style='color:#94a3b8;margin-top:24px;margin-bottom:12px'>📦 "+group.series_name+"</h3>";
|
||||
h+="<table style='width:100%;border-collapse:collapse'>";
|
||||
h+="<thead><tr style='border-bottom:2px solid #334155'><th style='text-align:left;padding:8px;color:#94a3b8'>File</th><th style='text-align:right;padding:8px;color:#94a3b8;width:100px'>Size</th><th style='text-align:center;padding:8px;color:#94a3b8;width:80px'>Download</th></tr></thead>";
|
||||
h+="<tbody>";
|
||||
group.files.forEach(function(file){
|
||||
h+="<tr style='border-bottom:1px solid #1e293b'>";
|
||||
h+="<td style='padding:8px;color:#e2e8f0'>"+file.filename+"</td>";
|
||||
h+="<td style='padding:8px;color:#94a3b8;text-align:right'>"+file.size+"</td>";
|
||||
h+="<td style='padding:8px;text-align:center'><a href='"+file.download_url+"' target='_blank' style='color:#3b82f6;text-decoration:none'>⬇️</a></td>";
|
||||
h+="</tr>";
|
||||
});
|
||||
h+="</tbody></table>";
|
||||
});
|
||||
}
|
||||
h+="</div>";
|
||||
b.innerHTML=h;
|
||||
}).catch(function(e){
|
||||
b.innerHTML="<div style=padding:20px;color:#ef4444>Failed to load category: "+e+"</div>";
|
||||
});
|
||||
}
|
||||
|
||||
function loadSeriesDetail(name){
|
||||
var b=document.getElementById("mb-tree-body");
|
||||
if(!b)return;
|
||||
b.innerHTML="<div style=text-align:center;padding:40px;color:#64748b>Loading series...</div>";
|
||||
|
||||
fetch("/api/v2/series/"+encodeURIComponent(name)).then(function(r){return r.json()}).then(function(d){
|
||||
var h="<div style='padding:20px'>";
|
||||
h+="<button onclick='loadTree()' style='background:#1e293b;border:1px solid #334155;color:#94a3b8;padding:8px 16px;border-radius:6px;cursor:pointer;margin-bottom:16px'>← Back to Series</button>";
|
||||
h+="<h2 style='border:none;margin:20px 0;color:#60a5fa'>📦 "+(d.series.display_name||d.series.name)+" - "+d.series.file_count+" files</h2>";
|
||||
|
||||
if(d.categories && d.categories.length){
|
||||
d.categories.forEach(function(cat){
|
||||
h+="<h3 style='color:#94a3b8;margin-top:24px;margin-bottom:12px'>📁 "+cat.category_name+"</h3>";
|
||||
h+="<table style='width:100%;border-collapse:collapse'>";
|
||||
h+="<thead><tr style='border-bottom:2px solid #334155'><th style='text-align:left;padding:8px;color:#94a3b8'>File</th><th style='text-align:right;padding:8px;color:#94a3b8;width:100px'>Size</th><th style='text-align:center;padding:8px;color:#94a3b8;width:80px'>Download</th></tr></thead>";
|
||||
h+="<tbody>";
|
||||
cat.files.forEach(function(file){
|
||||
h+="<tr style='border-bottom:1px solid #1e293b'>";
|
||||
h+="<td style='padding:8px;color:#e2e8f0'>"+file.filename+"</td>";
|
||||
h+="<td style='padding:8px;color:#94a3b8;text-align:right'>"+file.size+"</td>";
|
||||
h+="<td style='padding:8px;text-align:center'><a href='"+file.download_url+"' target='_blank' style='color:#3b82f6;text-decoration:none'>⬇️</a></td>";
|
||||
h+="</tr>";
|
||||
});
|
||||
h+="</tbody></table>";
|
||||
});
|
||||
}
|
||||
h+="</div>";
|
||||
b.innerHTML=h;
|
||||
}).catch(function(e){
|
||||
b.innerHTML="<div style=padding:20px;color:#ef4444>Failed to load series: "+e+"</div>";
|
||||
});
|
||||
}
|
||||
|
||||
// DETAIL PANEL
|
||||
function showDetail(fuuid){
|
||||
if(!fuuid)return;
|
||||
@@ -1138,6 +1297,118 @@ function toggleLock(){
|
||||
loadTree();
|
||||
}
|
||||
|
||||
// ═══════════════ S3 PANEL ═══════════════
|
||||
var _s3v=false;
|
||||
|
||||
function toggleS3(){
|
||||
_s3v=!_s3v;
|
||||
document.getElementById("mb-s3-panel").classList.toggle("active",_s3v);
|
||||
if(_s3v)loadS3Panel();
|
||||
}
|
||||
|
||||
function loadS3Panel(){
|
||||
var b=document.getElementById("mb-s3-body");
|
||||
if(!b)return;
|
||||
b.innerHTML="<div style=text-align:center;padding:40px;color:#64748b>Loading...</div>";
|
||||
|
||||
fetch("/api/v2/s3/status").then(function(r){return r.json()}).then(function(d){
|
||||
var h="<div class=mb-mode-bar>";
|
||||
h+="<span style=color:#60a5fa;font-size:16px>☁️</span>";
|
||||
h+="<span style=font-size:14px;color:#e2e8f0;margin-left:8px>S3 Service</span>";
|
||||
h+="<span style=flex:1></span>";
|
||||
h+="<button onclick=toggleS3() style='background:none;border:none;color:#64748b;font-size:18px;cursor:pointer'>✕</button>";
|
||||
h+="</div>";
|
||||
|
||||
// S3 Status Section
|
||||
h+="<div class=mb-config-section>";
|
||||
h+="<div class=mb-config-header>S3 STATUS</div>";
|
||||
h+="<div class=mb-config-item>";
|
||||
h+="<span class=mb-config-label>Status</span>";
|
||||
h+="<span class=mb-config-value>"+(d.enabled?"✅ Running":"❌ Disabled")+"</span>";
|
||||
h+="</div>";
|
||||
h+="<div class=mb-config-item>";
|
||||
h+="<span class=mb-config-label>Endpoint</span>";
|
||||
h+="<span class=mb-config-value>"+d.endpoint+"</span>";
|
||||
h+="</div>";
|
||||
h+="<div class=mb-config-item>";
|
||||
h+="<span class=mb-config-label>Region</span>";
|
||||
h+="<span class=mb-config-value>"+d.region+"</span>";
|
||||
h+="</div>";
|
||||
h+="<div class=mb-config-item>";
|
||||
h+="<span class=mb-config-label>Buckets</span>";
|
||||
h+="<span class=mb-config-value>"+d.buckets_count+"</span>";
|
||||
h+="</div>";
|
||||
h+="<div class=mb-config-item>";
|
||||
h+="<span class=mb-config-label>Access Keys</span>";
|
||||
h+="<span class=mb-config-value>"+d.keys_count+"</span>";
|
||||
h+="</div>";
|
||||
h+="</div>";
|
||||
|
||||
// Access Keys Section
|
||||
h+="<div class=mb-config-section>";
|
||||
h+="<div class=mb-config-header>S3 ACCESS KEYS</div>";
|
||||
h+="<div style=padding:8px;color:#94a3b8;font-size:12px>";
|
||||
h+="AWS Signature V4 authentication required";
|
||||
h+="</div>";
|
||||
h+="<div class=mb-config-item>";
|
||||
h+="<span class=mb-config-label>markbase_access_key_001</span>";
|
||||
h+="<span class=mb-config-value>Access: warren</span>";
|
||||
h+="<button class=mb-config-edit-btn onclick=copyS3Key('markbase_access_key_001')>Copy</button>";
|
||||
h+="</div>";
|
||||
h+="<div class=mb-config-item>";
|
||||
h+="<span class=mb-config-label>markbase_access_key_002</span>";
|
||||
h+="<span class=mb-config-value>Access: demo</span>";
|
||||
h+="<button class=mb-config-edit-btn onclick=copyS3Key('markbase_access_key_002')>Copy</button>";
|
||||
h+="</div>";
|
||||
h+="<button class=mb-new-folder-btn onclick=generateS3Key() style=margin-top:8px>Generate New Key</button>";
|
||||
h+="</div>";
|
||||
|
||||
// Client Usage Section
|
||||
h+="<div class=mb-config-section>";
|
||||
h+="<div class=mb-config-header>CLIENT USAGE (Python boto3)</div>";
|
||||
h+="<pre style='background:#0f172a;padding:12px;border-radius:8px;font-size:12px;color:#4ade80;white-space:pre-wrap'>";
|
||||
h+="import boto3\n\n";
|
||||
h+="s3 = boto3.client(\n";
|
||||
h+=" 's3',\n";
|
||||
h+=" endpoint_url='"+d.endpoint+"',\n";
|
||||
h+=" aws_access_key_id='markbase_access_key_001',\n";
|
||||
h+=" aws_secret_access_key='markbase_secret_key_xyz123'\n";
|
||||
h+=")\n\n";
|
||||
h+="# List buckets\n";
|
||||
h+="buckets = s3.list_buckets()\n";
|
||||
h+="for b in buckets['Buckets']:\n";
|
||||
h+=" print(b['Name'])\n\n";
|
||||
h+="# List objects\n";
|
||||
h+="objects = s3.list_objects_v2(Bucket='warren')\n";
|
||||
h+="for obj in objects['Contents']:\n";
|
||||
h+=" print(obj['Key'])\n";
|
||||
h+="</pre>";
|
||||
h+="</div>";
|
||||
|
||||
b.innerHTML=h;
|
||||
}).catch(function(e){
|
||||
b.innerHTML="<div style=padding:20px;color:#ef4444>Failed to load S3 status: "+e+"</div>";
|
||||
});
|
||||
}
|
||||
|
||||
function copyS3Key(accessKey){
|
||||
navigator.clipboard.writeText(accessKey);
|
||||
toast("Copied: "+accessKey);
|
||||
}
|
||||
|
||||
function generateS3Key(){
|
||||
fetch("/api/v2/s3/generate-key",{method:"POST"})
|
||||
.then(function(r){return r.json()})
|
||||
.then(function(d){
|
||||
if(d.access_key){
|
||||
toast("Generated: "+d.access_key);
|
||||
loadS3Panel();
|
||||
}else{
|
||||
toast("Error: "+(d.error||"unknown"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Init
|
||||
(function(){
|
||||
var s=localStorage.getItem("display_mode");
|
||||
|
||||
Reference in New Issue
Block a user