Skip to content

Commit

Permalink
add rdp app
Browse files Browse the repository at this point in the history
  • Loading branch information
lindongchen committed Oct 23, 2024
1 parent a2875e8 commit 0701995
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 72 deletions.
127 changes: 78 additions & 49 deletions gui/src/service/AppService.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,32 +52,38 @@ export default class AppService {
method: 'GET'
})
}
getTunnel({mesh, ep}) {
let reqs = [];
const tunnelName = `${mesh?.agent?.name}To${ep?.name}`;
const outboundReq = this.getOutbound({
mesh,
ep:ep?.id,
proto: 'tcp',
name:tunnelName
}).then((res)=> {
return { data:res, ep, type:'outbound' }
}).catch((e)=>{
})
reqs.push(outboundReq);
const inboundReq = this.getInbound({
mesh,
ep:mesh?.agent?.id,
proto: 'tcp',
name:tunnelName
}).then((res)=> {
return { data:res, ep, type:'inbound' }
}).catch((e)=>{
})
reqs.push(inboundReq);
return reqs;
}
getTunnels({mesh, eps, callback, error}) {
let reqs = [];
// merge request
(eps||[]).forEach((ep)=>{

const tunnelName = `${mesh?.agent?.name}To${ep?.name}`;
const outboundReq = this.getOutbound({
mesh,
ep:ep?.id,
proto: 'TCP',
name:tunnelName
}).then((res)=> {
return { data:res, ep, type:'outbound' }
}).catch((e)=>{
})
reqs.push(outboundReq);
const inboundReq = this.getInbound({
mesh,
ep:mesh?.agent?.id,
proto: 'TCP',
name:tunnelName
}).then((res)=> {
return { data:res, ep, type:'inbound' }
}).catch((e)=>{
})
reqs.push(inboundReq)
reqs = reqs.concat(this.getTunnel({
mesh, ep
}))
})

return merge(reqs).then((allRes) => {
Expand All @@ -92,14 +98,11 @@ export default class AppService {
tunnels[_key] = {
name: res.name,
proto: res.protocol,
outbounds: [],
inbounds: [],
}
}
tunnels[_key][childres.type] = {
...res,
ep:childres.ep
}
tunnels[_key][childres.type] = res;
tunnels[_key].starting = false;
tunnels[_key].stoping = false;
}
}
})
Expand All @@ -110,35 +113,22 @@ export default class AppService {
error()
})
}
startEDPTunnel({
startRDPTunnel({
mesh,
ep,
callback
}) {
const options = {
mesh:mesh?.name,
ep:mesh?.agent?.id,
provider:'ztm',
app:'proxy',
app:'tunnel',
}
const tunnelName = `${mesh?.agent?.name}To${ep?.name}`;
const base = this.getAppUrl(options);
let reqs = [];
const createInbound = this.invokeAppApi({
base,
url:`/api/endpoints/${mesh?.agent?.id}/inbound/TCP/${tunnelName}`,
method: 'POST',
body: {
listens: [{
host:'127.0.0.1',
port:13389
}],
exits:[ep?.id]
}
});
reqs.push(createInbound);
const createOutbound = this.invokeAppApi({
this.invokeAppApi({
base,
url:`/api/endpoints/${ep?.id}/outbound/TCP/${tunnelName}`,
url:`/api/endpoints/${ep?.id}/outbound/tcp/${tunnelName}`,
method: 'POST',
body: {
targets: [{
Expand All @@ -147,9 +137,48 @@ export default class AppService {
}],
entrances:[mesh?.agent?.id]
}
});
reqs.push(createOutbound);
return merge(reqs);
}).then((resInbound)=>{
this.invokeAppApi({
base,
url:`/api/endpoints/${mesh?.agent?.id}/inbound/tcp/${tunnelName}`,
method: 'POST',
body: {
listens: [{
ip:'127.0.0.1',
port:13389
}],
exits:[ep?.id]
}
}).then((resOutbound)=>{
callback(resInbound,resOutbound)
});
})
}

stopRDPTunnel({
mesh,
ep,
}) {
const options = {
mesh:mesh?.name,
ep:mesh?.agent?.id,
provider:'ztm',
app:'tunnel',
}
const tunnelName = `${mesh?.agent?.name}To${ep?.name}`;
const base = this.getAppUrl(options);
const deleteReqs = [];
deleteReqs.push(this.invokeAppApi({
base,
url:`/api/endpoints/${ep?.id}/outbound/tcp/${tunnelName}`,
method: 'DELETE',
}))
deleteReqs.push(this.invokeAppApi({
base,
url:`/api/endpoints/${mesh?.agent?.id}/inbound/tcp/${tunnelName}`,
method: 'DELETE',
}));
return merge(deleteReqs)
}
//
// App
Expand Down
6 changes: 1 addition & 5 deletions gui/src/utils/app-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,12 @@ const mobileApp = [{
name: "rdp",
}];
const webApp = [{
name: "setting",
},{
name: "ZTMLog",
label: "ZTM Log",
url:'/#/app/ztmlog',
width:455,
height:600,
icon: ztmlog,
},{
name: "browser",
},{
name: "rdp",
}];
Expand All @@ -81,7 +77,7 @@ const appSelect = {
ios:mobileApp,
android:mobileApp,
}
const apps = !!window.__TAURI_INTERNALS__ ? (appSelect[platform()] || defaultApp) : mobileApp;
const apps = !!window.__TAURI_INTERNALS__ ? (appSelect[platform()] || defaultApp) : webApp;
const appMapping = {
"/setting": {
icon: setting,
Expand Down
9 changes: 4 additions & 5 deletions gui/src/views/apps/Apps.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ const loaddata = () => {
const appLoading = ref({})
const manage = ref(false);
const mergeApps = computed(()=>{
return (!props.noInners?innerApps.value:[]).concat(meshApps.value||[]).concat(shortcutApps.value).concat(uninstallApps.value);
return (!props.noInners?innerApps.value:innerApps.value).concat(meshApps.value||[]).concat(shortcutApps.value).concat(uninstallApps.value);
})
const installAPP = (app, options, base) => {
Expand Down Expand Up @@ -192,8 +192,7 @@ watch(()=>selectedMesh,()=>{
</AppHeader>
<div v-else class="flex actions transparent-header">
<div class="flex-item">
<Button v-if="!current" v-tooltip.left="'Close'" severity="help" text rounded aria-label="Filter" @click="hide" >
<i class="pi pi-chevron-left " />
<Button icon="pi pi-chevron-left" class="app-btn" v-if="!current" v-tooltip.left="'Close'" severity="help" text rounded aria-label="Filter" @click="hide" >
</Button>
</div>
<div v-if="!!selectedMesh" class="flex-item text-center " :class="{'text-white':!props.theme}" style="line-height: 30px;">
Expand Down Expand Up @@ -256,7 +255,7 @@ watch(()=>selectedMesh,()=>{
}
.actions{
left: 0px;
padding: 9px 10px;
padding: 7px 10px;
display: flex;
right: 0px;
:deep(.p-button){
Expand Down Expand Up @@ -300,7 +299,7 @@ watch(()=>selectedMesh,()=>{
:deep(.p-inputgroup.search-bar .p-multiselect-label){
line-height: 30px;
}
:deep(.p-button){
.app-btn{
width: 2rem;
height: 2rem;
}
Expand Down
74 changes: 61 additions & 13 deletions gui/src/views/apps/core/RDP.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script setup>
import { ref, onMounted,onActivated, computed,watch } from "vue";
import AppService from '@/service/AppService';
import { merge } from '@/service/common/request';
import { useStore } from 'vuex';
const props = defineProps(['app']);
Expand All @@ -14,9 +15,6 @@ const save = (tunnel) => {
getEndpoints(tunnel);
}
const endpoints = ref([]);
onMounted(()=>{
getEndpoints()
})
const loading = ref(false);
const loader = ref(false);
const error = ref();
Expand All @@ -29,7 +27,15 @@ const getEndpoints = () => {
loader.value = false;
},2000)
error.value = null;
endpoints.value = res || [];
endpoints.value = [];
if(res){
endpoints.value = res.filter((n)=>n.id != selectedMesh.value?.agent?.id)
}
endpoints.value.forEach((ep)=>{
tunnels.value[ep.id] = {
starting: true,
};
})
loadtunnels(res);
})
}
Expand All @@ -50,14 +56,52 @@ const loadtunnels = (eps) => {
const back = () => {
emits('close')
}
const startTunnel = (ep) => {
appService.startEDPTunnel({
const getTunnel = (ep) => {
merge(appService.getTunnel({
mesh:selectedMesh.value,
ep
}).then((res)=>{
})).then((ary)=>{
ary.forEach((res)=>{
tunnels.value[ep.id].name = res.data.name;
tunnels.value[ep.id].proto = res.data.protocol;
tunnels.value[ep.id][res.type] = res.data;
});
});
}
const stopRDPTunnel = (ep) => {
tunnels.value[ep.id].stoping = true
appService.stopRDPTunnel({
mesh:selectedMesh.value,
ep
}).then(()=>{
tunnels.value[ep.id] = {
stoping: false,
starting: false,
};
})
}
const startTunnel = (ep) => {
if(!tunnels.value[ep.id]){
tunnels.value[ep.id] = {
starting: true
}
} else {
tunnels.value[ep.id].starting = true
}
appService.startRDPTunnel({
mesh:selectedMesh.value,
ep,
callback(){
getTunnel(ep, ()=>{
tunnels.value[ep.id].starting = false;
})
}
});
}
watch(()=> selectedMesh.value,()=> getEndpoints(), {deep:true})
onMounted(()=>{
getEndpoints()
})
</script>
<template>
Expand All @@ -76,22 +120,26 @@ const startTunnel = (ep) => {
<ScrollPanel v-else-if="endpoints && endpoints.length >0">
<DataView class="message-list" :value="endpoints">
<template #list="slotProps">
<div class="flex flex-col message-item pointer" v-for="(node, index) in slotProps.items" :key="index" @click="select(node)">
<div class="flex flex-col message-item pointer" v-for="(node, index) in slotProps.items" :key="index">
<div class="flex flex-col py-3 px-3 gap-4 w-full" :class="{ 'border-t border-surface-200 dark:border-surface-700': index !== 0 }">
<div class="w-40 relative">
<Avatar icon="pi pi-user" size="small" style="background-color: #ece9fc; color: #2a1261" />
</div>
<div class="flex-item" style="min-width: 150px;">
<div >
<div class="flex">
<div class="flex-item pt-1">
<b>{{ node.name || 'Unknow EP' }}</b>
<Tag class="ml-2" v-if="node.id == selectedMesh?.agent?.id" severity="contrast" >Local</Tag>
</div>
</div>
</div>
<div class="grid align-items-center justify-content-end pr-2 gap-1 pt-3" v-if="tunnels[node.id]">
<Tag v-if="tunnels[node.id]?.outbound?.targets[0]" severity="secondary" value="Secondary">Target {{tunnels[node.id]?.outbound?.targets[0]?.port}}</Tag>
<Tag v-if="tunnels[node.id]?.inbound?.listens[0]" severity="secondary" value="Secondary">Listen {{tunnels[node.id]?.inbound?.listens[0]?.port}}</Tag>
<div class="flex-item">
<div class="w-full grid align-items-center justify-content-end pr-2 gap-2 pt-3">
<Tag v-if="tunnels[node.id]?.outbound?.targets" severity="secondary" value="Secondary">Target {{tunnels[node.id]?.outbound?.targets[0]?.port}}</Tag>
<Tag v-if="tunnels[node.id]?.inbound?.listens" severity="secondary" value="Secondary">Listen {{tunnels[node.id]?.inbound?.listens[0]?.port}}</Tag>
<Button v-tooltip="'Connect'" @click="startTunnel(node)" :loading="tunnels[node.id]?.starting" v-if="!tunnels[node.id]?.outbound?.targets" raised icon="pi pi-caret-right" size="small" />
<Button v-tooltip="'Disconnect'" @click="stopRDPTunnel(node)" :loading="tunnels[node.id]?.stoping" severity="danger" v-else raised icon="pi pi-pause" size="small" />
</div>
</div>
</div>
</div>
Expand Down

0 comments on commit 0701995

Please sign in to comment.