fix: multi issues - TUN read loop, SDWAN routing for TenantID=0, WS keepalive 10s

This commit is contained in:
2026-03-03 11:24:00 +08:00
parent 9f6e065f3a
commit 10473020d2
22 changed files with 1122 additions and 76 deletions

View File

@@ -224,6 +224,42 @@
</div>
</div>
</div>
<!-- ===== 租户管理 ===== -->
<div v-if="tab==='tenant'" class="fade-in space-y-6">
<div class="glass rounded-2xl p-5">
<div class="text-slate-300 text-sm font-bold mb-3">创建租户</div>
<div class="flex gap-3">
<input v-model="newTenant" placeholder="租户名称" class="ipt">
<button @click="createTenant" class="btn-blue">创建</button>
</div>
</div>
<div class="glass rounded-2xl p-5">
<div class="text-slate-300 text-sm font-bold mb-3">生成租户 API Key</div>
<button @click="createKey" class="btn-blue">生成 Key</button>
<div class="mt-3 text-xs text-slate-400">Key:</div>
<textarea v-model="tenantKey" class="ipt mt-1" rows="2" placeholder="生成后显示"></textarea>
</div>
<div class="glass rounded-2xl p-5">
<div class="text-slate-300 text-sm font-bold mb-3">生成 Enroll Code</div>
<div class="flex gap-3">
<button @click="createEnroll" class="btn-blue">生成</button>
<input v-model="enrollCode" placeholder="Enroll Code" class="ipt">
</div>
</div>
<div class="glass rounded-2xl p-5">
<div class="text-slate-300 text-sm font-bold mb-3">兑换 Node Secret</div>
<div class="flex gap-3">
<input v-model="enrollNode" placeholder="节点名" class="ipt">
<button @click="consumeEnroll" class="btn-blue">兑换</button>
</div>
<div class="mt-3 text-xs text-slate-400">Node Secret:</div>
<textarea v-model="enrollSecret" class="ipt mt-1" rows="2" placeholder="兑换后显示"></textarea>
</div>
</div>
</div>
</main>
@@ -279,8 +315,13 @@ createApp({setup(){
const na=reactive({appName:'',srcPort:0,peerNode:'',dstPort:0});
const addNode=ref(''),addIP=ref('');
const p2p=reactive({from:'',to:'',name:'tunnel',srcPort:18080,dstPort:22});
const tenants=ref([]),newTenant=ref('');
const tenantKey=ref('');
const enrollCode=ref('');
const enrollNode=ref('');
const enrollSecret=ref('');
const tabs=[{id:'dash',label:'仪表盘'},{id:'nodes',label:'节点管理'},{id:'sdwan',label:'SDWAN 组网'},{id:'p2p',label:'P2P 连接'}];
const tabs=[{id:'dash',label:'仪表盘'},{id:'nodes',label:'节点管理'},{id:'sdwan',label:'SDWAN 组网'},{id:'p2p',label:'P2P 连接'},{id:'tenant',label:'租户管理'}];
const show=(m,c='info',d=3000)=>{toast.value=m;toastType.value=c;setTimeout(()=>toast.value='',d)};
const log=(m,c='info')=>{logs.value.unshift({t:new Date().toLocaleTimeString(),m,c});if(logs.value.length>50)logs.value.pop()};
@@ -313,6 +354,37 @@ createApp({setup(){
busy.value=false
};
const createTenant=async()=>{
if(!newTenant.value)return;busy.value=true;
const r=await api('/api/v1/admin/tenants',{method:'POST',body:JSON.stringify({name:newTenant.value})});
busy.value=false;
if(r){show('租户已创建','ok');log('创建租户 '+newTenant.value,'ok');newTenant.value=''}
};
const createKey=async()=>{
const id=prompt('输入 tenant_id');
if(!id)return;busy.value=true;
const r=await api(`/api/v1/admin/tenants/${id}/keys`,{method:'POST',body:JSON.stringify({scope:'all',ttl:0})});
busy.value=false;
if(r){tenantKey.value=r.api_key||'';show('API Key 已生成','ok')}
};
const createEnroll=async()=>{
if(!tenantKey.value){show('先填入租户 API Key','err');return}
busy.value=true;
const r=await api('/api/v1/tenants/enroll',{method:'POST',headers:{'Authorization':'Bearer '+tenantKey.value}});
busy.value=false;
if(r){enrollCode.value=r.enroll_code||'';show('Enroll Code 已生成','ok')}
};
const consumeEnroll=async()=>{
if(!enrollCode.value||!enrollNode.value){show('需要 enroll_code 与 node 名称','err');return}
busy.value=true;
const r=await api('/api/v1/enroll/consume',{method:'POST',body:JSON.stringify({code:enrollCode.value,node:enrollNode.value})});
busy.value=false;
if(r){enrollSecret.value=r.node_secret||'';show('Node Secret 已生成','ok')}
};
const saveSD=async()=>{
busy.value=true;
const r=await api('/api/v1/sdwan/edit',{method:'POST',body:JSON.stringify(sd.value)});
@@ -375,8 +447,10 @@ createApp({setup(){
return{loggedIn,loginToken,loginErr,busy,tab,tabs,nf,toast,toastType,
st,nodes,sd,logs,tunNode,tunApps,na,addNode,addIP,p2p,
tenants,newTenant,tenantKey,enrollCode,enrollNode,enrollSecret,
login,refresh,saveSD,kickNode,openTunnel,pushTun,openConnect,doConnect,
addSDNode,autoIP,nodeOnline,uptime,fNodes,uaNodes}
addSDNode,autoIP,nodeOnline,uptime,fNodes,uaNodes,
createTenant,createKey,createEnroll,consumeEnroll}
}}).mount('#app');
</script>
</body>