This commit is contained in:
snltty
2024-07-07 20:01:27 +08:00
parent 08672c3433
commit ec19ca5d8b
56 changed files with 1384 additions and 551 deletions

View File

@@ -42,6 +42,7 @@ namespace linker.tunnel
} }
var transportItems = tunnelAdapter.GetTunnelTransports(); var transportItems = tunnelAdapter.GetTunnelTransports();
var names = transportItems.Select(c => c.Name);
transportItems = transportItems.Concat(transports.Select(c => new TunnelTransportItemInfo transportItems = transportItems.Concat(transports.Select(c => new TunnelTransportItemInfo
{ {
Label = c.Label, Label = c.Label,
@@ -74,8 +75,7 @@ namespace linker.tunnel
} }
} }
tunnelAdapter.SetTunnelTransports(transportItems, names.SequenceEqual(transportItems.Select(c => c.Name)) == false);
tunnelAdapter.SetTunnelTransports(transportItems);
LoggerHelper.Instance.Warning($"load tunnel transport:{string.Join(",", transports.Select(c => c.Name))}"); LoggerHelper.Instance.Warning($"load tunnel transport:{string.Join(",", transports.Select(c => c.Name))}");
LoggerHelper.Instance.Warning($"used tunnel transport:{string.Join(",", transportItems.Where(c => c.Disabled == false).Select(c => c.Name))}"); LoggerHelper.Instance.Warning($"used tunnel transport:{string.Join(",", transportItems.Where(c => c.Disabled == false).Select(c => c.Name))}");
@@ -136,7 +136,7 @@ namespace linker.tunnel
foreach (var wanPortProtocol in tunnelAdapter.GetTunnelWanPortProtocols().Where(c => c.Disabled == false && string.IsNullOrWhiteSpace(c.Host) == false)) foreach (var wanPortProtocol in tunnelAdapter.GetTunnelWanPortProtocols().Where(c => c.Disabled == false && string.IsNullOrWhiteSpace(c.Host) == false))
{ {
//这个打洞协议不支持这个外网端口协议 //这个打洞协议不支持这个外网端口协议
if ((transport.AllowWanPortProtocolType & wanPortProtocol.ProtocolType) != wanPortProtocol.ProtocolType) if ((transport.AllowWanPortProtocolType & wanPortProtocol.ProtocolType) != wanPortProtocol.ProtocolType)
{ {

View File

@@ -26,7 +26,7 @@ namespace linker.tunnel.adapter
/// 保存外网端口协议列表 /// 保存外网端口协议列表
/// </summary> /// </summary>
/// <param name="compacts"></param> /// <param name="compacts"></param>
public void SetTunnelWanPortProtocols(List<TunnelWanPortInfo> protocols); public void SetTunnelWanPortProtocols(List<TunnelWanPortInfo> protocols,bool updateVersion);
/// <summary> /// <summary>
/// 获取打洞协议列表 /// 获取打洞协议列表
@@ -37,7 +37,7 @@ namespace linker.tunnel.adapter
/// 保存打洞协议列表 /// 保存打洞协议列表
/// </summary> /// </summary>
/// <param name="transports"></param> /// <param name="transports"></param>
public void SetTunnelTransports(List<TunnelTransportItemInfo> transports); public void SetTunnelTransports(List<TunnelTransportItemInfo> transports, bool updateVersion);
/// <summary> /// <summary>
/// 获取本地网络信息 /// 获取本地网络信息

View File

@@ -14,6 +14,7 @@ namespace linker.tunnel.wanport
public TunnelWanPortTransfer() public TunnelWanPortTransfer()
{ {
} }
/// <summary> /// <summary>

View File

@@ -10,6 +10,9 @@ export const removeForwardConnection = (id) => {
export const getForwardInfo = () => { export const getForwardInfo = () => {
return sendWebsocketMsg('forwardclient/get'); return sendWebsocketMsg('forwardclient/get');
} }
export const getForwardRemoteInfo = (data) => {
return sendWebsocketMsg('forwardclient/getremote', data);
}
export const getForwardIpv4 = () => { export const getForwardIpv4 = () => {
return sendWebsocketMsg('forwardclient/bindips'); return sendWebsocketMsg('forwardclient/bindips');
} }

View File

@@ -0,0 +1,5 @@
import { sendWebsocketMsg } from './request'
export const updateVersion = (data) => {
return sendWebsocketMsg('RunningConfig/UpdateVersion', data);
}

View File

@@ -9,6 +9,9 @@ export const setSForwardSecretKey = (data) => {
export const getSForwardInfo = () => { export const getSForwardInfo = () => {
return sendWebsocketMsg('sforwardclient/get'); return sendWebsocketMsg('sforwardclient/get');
} }
export const getSForwardRemoteInfo = (data) => {
return sendWebsocketMsg('sforwardclient/getremote', data);
}
export const removeSForwardInfo = (id) => { export const removeSForwardInfo = (id) => {
return sendWebsocketMsg('sforwardclient/remove', id); return sendWebsocketMsg('sforwardclient/remove', id);
} }

View File

@@ -16,6 +16,9 @@ export const getSignInfo = () => {
export const getSignInList = (data) => { export const getSignInList = (data) => {
return sendWebsocketMsg('signInclient/List', data); return sendWebsocketMsg('signInclient/List', data);
} }
export const getSignInIds = (data) => {
return sendWebsocketMsg('signInclient/ids', data);
}
export const signInDel = (machineId) => { export const signInDel = (machineId) => {
return sendWebsocketMsg('signInclient/del', machineId); return sendWebsocketMsg('signInclient/del', machineId);
} }

View File

@@ -304,14 +304,11 @@ span.split-pad10 {
background-color: #fafafa; background-color: #fafafa;
} }
.el-table--border, .el-table thead.is-group th.el-table__cell {
.el-table--group, background: var(--el-table-header-bg-color) !important;
.el-table-filter,
.el-table td,
.el-table th.is-leaf {
border-color: var(--main-border-color);
} }
.el-pagination.is-background .el-pager li:not(.disabled).active { .el-pagination.is-background .el-pager li:not(.disabled).active {
background-color: var(--main-color); background-color: var(--main-color);
} }

View File

@@ -20,105 +20,16 @@
</ul> </ul>
</div> </div>
</div> </div>
<el-dialog class="options-center" title="管理接口" destroy-on-close v-model="showPort" center :show-close="false"
:close-on-click-modal="false" align-center width="200">
<div class="port-wrap t-c">
<div>
接口 : <el-input v-model="state.api" style="width:70%"></el-input>
</div>
<div class="pdt-10">
秘钥 : <el-input type="password" v-model="state.psd" style="width:70%"></el-input>
</div>
</div>
<template #footer>
<el-button type="success" @click="handleConnect" plain> </el-button>
</template>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { computed, onMounted, reactive, watch } from 'vue';
import { initWebsocket, subWebsocketState,closeWebsocket } from '../apis/request'
import { getConfig,getSignInfo } from '../apis/signin'
import { useRoute, useRouter } from 'vue-router';
import { injectGlobalData } from '../provide';
import {Tools,StarFilled,WarnTriangleFilled} from '@element-plus/icons-vue' import {Tools,StarFilled,WarnTriangleFilled} from '@element-plus/icons-vue'
export default { export default {
components:{Tools,StarFilled,WarnTriangleFilled}, components:{Tools,StarFilled,WarnTriangleFilled},
setup() { setup() {
const globalData = injectGlobalData();
const route = useRoute();
const router = useRouter();
const queryCache = JSON.parse(localStorage.getItem('api-cache') || JSON.stringify({api:`${window.location.hostname}:1803`,psd:'snltty',groupid:'snltty'}));
const state = reactive({
api:queryCache.api,
psd:queryCache.psd,
groupid:globalData.value.groupid || queryCache.groupid,
showPort: false
});
const showPort = computed(() => globalData.value.connected == false && state.showPort);
const handleConnect = () => {
globalData.value.groupid = state.groupid;
queryCache.api = state.api;
queryCache.psd = state.psd;
queryCache.groupid = state.groupid;
localStorage.setItem('api-cache',JSON.stringify(queryCache));
closeWebsocket();
//initWebsocket(`ws://192.168.1.18:1803`,state.psd);
initWebsocket(`ws://${state.api}`,state.psd);
}
const _getConfig = ()=>{
getConfig().then((res)=>{
globalData.value.config.Common = res.Common;
globalData.value.config.Client = res.Client;
globalData.value.config.Running = res.Running;
globalData.value.configed = true;
setTimeout(()=>{
_getConfig();
},1000);
}).catch((err)=>{
setTimeout(()=>{
_getConfig();
},1000);
});
}
const _getSignInfoInfo = ()=>{
getSignInfo().then((res)=>{
globalData.value.signin.Connected = res.Connected;
globalData.value.signin.Connecting = res.Connecting;
globalData.value.signin.Version = res.Version;
setTimeout(()=>{
_getSignInfoInfo();
},1000);
}).catch((err)=>{
setTimeout(()=>{
_getSignInfoInfo();
},1000);
});
}
onMounted(() => {
setTimeout(() => { state.showPort = true; }, 100);
subWebsocketState((state) => { if (state) {
_getConfig();
_getSignInfoInfo();
}});
router.isReady().then(()=>{
state.api = route.query.api ?`${window.location.hostname}:${route.query.api}` : state.api;
state.psd = route.query.psd || state.psd;
state.groupid = route.query.groupid || state.groupid;
handleConnect();
});
});
return { return {
state, showPort, handleConnect
} }
} }
} }

View File

@@ -1,20 +1,35 @@
<template> <template>
<div class="status-api-wrap" :class="{connected:connected}"> <div class="status-api-wrap" :class="{connected:connected}">
<el-popconfirm confirm-button-text="" cancel-button-text="" title="确定清楚连接信息并刷新吗?" @confirm="handleResetConnect" > <el-popconfirm confirm-button-text="清除" cancel-button-text="更改" title="确定你的惭怍?" @cancel="handleShow" @confirm="handleResetConnect" >
<template #reference> <template #reference>
<a href="javascript:;" > <a href="javascript:;" >
<el-icon size="16"><Tools /></el-icon> <el-icon size="16"><Tools /></el-icon>
<template v-if="connected">管理接口</template> 管理接口
<template v-else>管理接口</template>
</a> </a>
</template> </template>
</el-popconfirm> </el-popconfirm>
<el-dialog class="options-center" title="管理接口" destroy-on-close v-model="showPort" center :show-close="false"
:close-on-click-modal="false" align-center width="200">
<div class="port-wrap t-c">
<div>
接口 : <el-input v-model="state.api" style="width:70%"></el-input>
</div>
<div class="pdt-10">
秘钥 : <el-input type="password" v-model="state.psd" style="width:70%"></el-input>
</div>
</div>
<template #footer>
<el-button type="success" @click="handleConnect1" plain> </el-button>
</template>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
import {computed} from 'vue'
import {useRoute,useRouter} from 'vue-router' import {useRoute,useRouter} from 'vue-router'
import {injectGlobalData} from '../../provide' import {injectGlobalData} from '../../provide'
import { computed, onMounted, reactive } from 'vue';
import { initWebsocket, subWebsocketState,closeWebsocket } from '../../apis/request'
import { getConfig,getSignInfo } from '../../apis/signin'
import {Tools} from '@element-plus/icons-vue' import {Tools} from '@element-plus/icons-vue'
export default { export default {
components:{Tools}, components:{Tools},
@@ -24,13 +39,87 @@ export default {
const router = useRouter(); const router = useRouter();
const route = useRoute(); const route = useRoute();
const defaultInfo = {api:`${window.location.hostname}:1803`,psd:'snltty'};
const handleResetConnect = () => { const handleResetConnect = () => {
localStorage.setItem('api-cache', ''); localStorage.setItem('api-cache', '');
router.push({name:route.name}); router.push({name:route.name});
window.location.reload(); window.location.reload();
} }
const queryCache = JSON.parse(localStorage.getItem('api-cache') || JSON.stringify(defaultInfo));
const state = reactive({
api:queryCache.api,
psd:queryCache.psd,
groupid:globalData.value.groupid || queryCache.groupid,
showPort: false
});
const showPort = computed(() => globalData.value.connected == false && state.showPort);
return {connected,handleResetConnect}; const handleConnect = () => {
globalData.value.groupid = state.groupid;
queryCache.api = state.api;
queryCache.psd = state.psd;
queryCache.groupid = state.groupid;
localStorage.setItem('api-cache',JSON.stringify(queryCache));
closeWebsocket();
initWebsocket(`ws://${state.api}`,state.psd);
}
const handleConnect1 = ()=>{
handleConnect();
window.location.reload();
}
const handleShow = ()=>{
//state.showPort = true;
closeWebsocket();
initWebsocket(`ws://${window.location.hostname}:12345`,state.psd);
}
const _getConfig = ()=>{
getConfig().then((res)=>{
globalData.value.config.Common = res.Common;
globalData.value.config.Client = res.Client;
globalData.value.config.Running = res.Running;
globalData.value.configed = true;
setTimeout(()=>{
_getConfig();
},1000);
}).catch((err)=>{
setTimeout(()=>{
_getConfig();
},1000);
});
}
const _getSignInfoInfo = ()=>{
getSignInfo().then((res)=>{
globalData.value.signin.Connected = res.Connected;
globalData.value.signin.Connecting = res.Connecting;
globalData.value.signin.Version = res.Version;
setTimeout(()=>{
_getSignInfoInfo();
},1000);
}).catch((err)=>{
setTimeout(()=>{
_getSignInfoInfo();
},1000);
});
}
onMounted(() => {
setTimeout(() => { state.showPort = true; }, 100);
subWebsocketState((state) => { if (state) {
_getConfig();
_getSignInfoInfo();
}});
router.isReady().then(()=>{
state.api = route.query.api ?`${window.location.hostname}:${route.query.api}` : state.api;
state.psd = route.query.psd || state.psd;
state.groupid = route.query.groupid || state.groupid;
handleConnect();
});
});
return { state, showPort, handleConnect1,connected,handleShow,handleResetConnect};
} }
} }
</script> </script>

View File

@@ -15,7 +15,7 @@ import 'element-plus/theme-chalk/display.css'
import 'element-plus/theme-chalk/dark/css-vars.css' import 'element-plus/theme-chalk/dark/css-vars.css'
import { import {
ArrowDown, Top, Bottom, Delete, Plus, Select, Refresh ArrowDown, Top, Bottom, Delete, Plus, Select, Refresh, Search
} from '@element-plus/icons-vue' } from '@element-plus/icons-vue'
app.component(ArrowDown.name, ArrowDown); app.component(ArrowDown.name, ArrowDown);
app.component(Top.name, Top); app.component(Top.name, Top);
@@ -24,6 +24,7 @@ app.component(Delete.name, Delete);
app.component(Plus.name, Plus); app.component(Plus.name, Plus);
app.component(Select.name, Select); app.component(Select.name, Select);
app.component(Refresh.name, Refresh); app.component(Refresh.name, Refresh);
app.component(Search.name, Search);
app.use(ElementPlus, { size: 'default' }).use(router).mount('#app'); app.use(ElementPlus, { size: 'default' }).use(router).mount('#app');

View File

@@ -51,16 +51,17 @@
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
import { onMounted, reactive, watch,inject,computed } from 'vue'; import { reactive, watch,computed } from 'vue';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { useConnections, useForwardConnections, useTuntapConnections } from './connections';
export default { export default {
props: ['modelValue'], props: ['modelValue'],
emits: ['change','update:modelValue'], emits: ['change','update:modelValue'],
setup(props, { emit }) { setup(props, { emit }) {
const connections = inject('connections'); const connections = useConnections();
const forwardConnections = inject('forward-connections'); const forwardConnections =useForwardConnections();
const tuntapConnections = inject('tuntap-connections'); const tuntapConnections = useTuntapConnections();
const state = reactive({ const state = reactive({
show: true, show: true,
protocolTypes:{1:'tcp',2:'udp',4:'msquic'}, protocolTypes:{1:'tcp',2:'udp',4:'msquic'},

View File

@@ -3,12 +3,10 @@
<template #header> <template #header>
<div class="flex"> <div class="flex">
<span class="flex-1">设备</span> <span class="flex-1">设备</span>
<span> <el-input size="small" v-model="name" clearable @change="handleRefresh" > <span> <el-input size="small" v-model="name" clearable @input="handleRefresh" placeholder="设备/虚拟网卡/端口转发" ></el-input> </span>
<template #prefix> <span>
<el-icon><Search /></el-icon> <el-button size="small" @click="handleRefresh"><el-icon><Search /></el-icon></el-button>
</template> </span>
</el-input> </span>
<el-button size="small" @click="handleRefresh"><el-icon><Refresh /></el-icon></el-button>
</div> </div>
</template> </template>
<template #default="scope"> <template #default="scope">
@@ -71,7 +69,7 @@ a.green{color:green}
.el-icon{vertical-align:middle} .el-icon{vertical-align:middle}
} }
.el-input{ .el-input{
width:8rem; width:15rem;
margin-right:.6rem margin-right:.6rem
} }
</style> </style>

View File

@@ -53,23 +53,27 @@
</el-table-column> </el-table-column>
</template> </template>
<script> <script>
import { inject } from 'vue'; import { useForward } from './forward';
import { useSforward } from './sforward';
export default { export default {
emits: ['edit','sedit'], emits: ['edit','sedit'],
setup(props, { emit }) { setup(props, { emit }) {
const forward = inject('forward') const forward = useForward()
const sforward = inject('sforward') const sforward = useSforward()
const handleEdit = (machineId)=>{ const handleEdit = (machineId)=>{
emit('edit',machineId) emit('edit',machineId)
} }
const handleSEdit = ()=>{ const handleSEdit = ()=>{
emit('sedit') emit('sedit')
} }
const handleForwardRefresh = ()=>{
emit('refresh');
}
return { return {
forward,sforward, handleEdit,handleSEdit forward,sforward, handleEdit,handleSEdit,handleForwardRefresh
} }
} }
} }

View File

@@ -0,0 +1,154 @@
<template>
<el-dialog v-model="state.show" @open="handleOnShowList" append-to=".app-wrap" title="复制端口转发" top="1vh" width="500">
<div>
<div class="t-c head">
<span>复制</span>
<el-select v-model="state.machineId" @change="handleMachineChange"
filterable remote :loading="state.loading" :remote-method="handleSearch">
<template #header>
<div class="t-c">
<div class="page-wrap">
<el-pagination small background layout="prev, pager, next"
:page-size="state.machineIds.Request.Size"
:total="state.machineIds.Count"
:pager-count="5"
:current-page="state.machineIds.Request.Page" @current-change="handlePageChange" />
</div>
</div>
</template>
<el-option v-for="(item, index) in state.machineIds.List" :key="index" :label="item.MachineName" :value="item.MachineId">
</el-option>
</el-select>
<span>{{ state.toMachineName }}的端口转发记录</span>
</div>
<el-table :data="state.forwards" size="small" border>
<el-table-column property="Name" label="名称"></el-table-column>
<el-table-column prop="BufferSize" label="缓冲区" width="60">
<template #default="scope">
{{ (1<<scope.row.BufferSize) }}KB
</template>
</el-table-column>
<el-table-column property="Port" label="监听端口" width="80"></el-table-column>
<el-table-column property="TargetEP" label="目标服务" width="140"></el-table-column>
<el-table-column label="操作" width="80">
<template #default="scope">
<el-checkbox v-model="scope.row.use">使用</el-checkbox>
</template>
</el-table-column>
</el-table>
<div class="foot t-c">
<el-button type="primary" @click="handleConfirm">确定复制</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import { onMounted, onUnmounted, reactive, watch } from 'vue';
import { getForwardRemoteInfo,addForwardInfo } from '@/apis/forward'
import { ElMessage } from 'element-plus';
import {WarnTriangleFilled} from '@element-plus/icons-vue'
import { injectGlobalData } from '@/provide';
import { useForward } from './forward';
import { getSignInIds } from '@/apis/signin';
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
components:{WarnTriangleFilled},
setup(props, { emit }) {
const globalData = injectGlobalData();
const forward = useForward();
const state = reactive({
show: true,
loading:false,
machineId: '',
toMachineId: forward.value.current,
toMachineName: forward.value.machineName,
machineIds:{
Request: {
Page: 1, Size:10, Name: ''
},
Count: 0,
List: []
},
forwards: []
});
watch(() => state.show, (val) => {
if (!val) {
setTimeout(() => {
emit('update:modelValue', val);
}, 300);
}
});
const handleOnShowList = () => {
_getMachineIds();
}
const _getMachineIds = ()=>{
state.loading = true;
getSignInIds(state.machineIds.Request).then((res)=>{
state.loading = false;
state.machineIds.Request = res.Request;
state.machineIds.Count = res.Count;
state.machineIds.List = res.List;
if(!state.machineId && state.machineIds.List.length > 0){
state.machineId = state.machineIds.List[0].MachineId;
_getForwardRemoteInfo();
}
}).catch((e)=>{
state.loading = false;
});
}
const handlePageChange = (page)=>{
state.machineIds.Request.Page = page;
_getMachineIds();
}
const handleSearch = (name)=>{
state.machineIds.Request.Name = name;
_getMachineIds();
}
const _getForwardRemoteInfo = ()=>{
getForwardRemoteInfo({
MachineId: state.machineId,
ToMachineId: state.toMachineId
}).then((res)=>{
res.forEach(c=>{
c.use = true;
});
state.forwards = res;
}).catch((e)=>{
console.log(e);
});
}
const handleMachineChange = ()=>{
_getForwardRemoteInfo();
}
const handleConfirm = ()=>{
const tasks = state.forwards.filter(c=>c.use)
.map(c=>addForwardInfo({Name:c.Name,Port:c.Port,TargetEP:c.TargetEP,BufferSize:c.BufferSize,MachineId:state.toMachineId}));
Promise.all(tasks).then(()=>{
ElMessage.success('已操作!');
state.show = false;
}).catch(()=>{
ElMessage.success('操作失败!');
});
}
onMounted(()=>{
_getMachineIds();
});
onUnmounted(()=>{
});
return {
state,handleSearch, handleOnShowList,handleMachineChange,handleConfirm,handlePageChange
}
}
}
</script>
<style lang="stylus" scoped>
.el-select{width : 12rem}
.head{padding-bottom:1rem}
.foot{padding-top:1rem}
.page-wrap{display:inline-block}
</style>

View File

@@ -4,6 +4,7 @@
<div class="t-c head"> <div class="t-c head">
<el-button type="success" size="small" @click="handleAdd">添加</el-button> <el-button type="success" size="small" @click="handleAdd">添加</el-button>
<el-button size="small" @click="handleRefresh">刷新</el-button> <el-button size="small" @click="handleRefresh">刷新</el-button>
<el-button size="small" @click="handleCopy">复制转发配置</el-button>
</div> </div>
<el-table :data="state.data" size="small" border height="500" @cell-dblclick="handleCellClick"> <el-table :data="state.data" size="small" border height="500" @cell-dblclick="handleCellClick">
<el-table-column property="Name" label="名称"> <el-table-column property="Name" label="名称">
@@ -96,11 +97,12 @@
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
import { inject, onMounted, onUnmounted, reactive, watch } from 'vue'; import { onMounted, onUnmounted, reactive, watch } from 'vue';
import { getForwardInfo, removeForwardInfo, addForwardInfo ,getForwardIpv4,testTargetForwardInfo,testListenForwardInfo } from '@/apis/forward' import { getForwardInfo, removeForwardInfo, addForwardInfo ,getForwardIpv4,testTargetForwardInfo,testListenForwardInfo } from '@/apis/forward'
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import {WarnTriangleFilled} from '@element-plus/icons-vue' import {WarnTriangleFilled} from '@element-plus/icons-vue'
import { injectGlobalData } from '@/provide'; import { injectGlobalData } from '@/provide';
import { useForward } from './forward';
export default { export default {
props: ['data','modelValue'], props: ['data','modelValue'],
emits: ['update:modelValue'], emits: ['update:modelValue'],
@@ -108,7 +110,7 @@ export default {
setup(props, { emit }) { setup(props, { emit }) {
const globalData = injectGlobalData(); const globalData = injectGlobalData();
const forward = inject('forward'); const forward = useForward();
const state = reactive({ const state = reactive({
show: true, show: true,
machineId: forward.value.current, machineId: forward.value.current,
@@ -210,11 +212,16 @@ export default {
}); });
} }
const handleCopy = ()=>{
forward.value.showCopy = true;
}
onMounted(()=>{ onMounted(()=>{
_getForwardInfo(); _getForwardInfo();
_getForwardIpv4(); _getForwardIpv4();
_testTargetForwardInfo(); _testTargetForwardInfo();
_testListenForwardInfo(); _testListenForwardInfo();
}); });
onUnmounted(()=>{ onUnmounted(()=>{
clearTimeout(state.timerTestTarget); clearTimeout(state.timerTestTarget);
@@ -222,7 +229,7 @@ export default {
}); });
return { return {
state, handleOnShowList, handleCellClick, handleRefresh, handleAdd, handleEdit, handleEditBlur, handleDel, handleStartChange state, handleOnShowList, handleCellClick, handleRefresh, handleAdd, handleEdit, handleEditBlur, handleDel, handleStartChange,handleCopy
} }
} }
} }

View File

@@ -1,14 +1,13 @@
<template> <template>
<div class="home-list-wrap absolute" > <div class="home-list-wrap absolute" >
<el-table :data="state.page.List" stripe border style="width: 100%" :height="`${state.height}px`" size="small"> <el-table :data="devices.page.List" stripe border style="width: 100%" :height="`${state.height}px`" size="small">
<Device @edit="handleDeviceEdit" @refresh="handlePageRefresh"></Device> <Device @edit="handleDeviceEdit" @refresh="handlePageRefresh"></Device>
<Tunnel @edit="handleTunnelEdit" @refresh="handleTunnelRefresh" @connections="handleTunnelConnections"></Tunnel> <Tunnel @edit="handleTunnelEdit" @refresh="handleTunnelRefresh" @connections="handleTunnelConnections"></Tunnel>
<Tuntap @edit="handleTuntapEdit" @refresh="handleTuntapRefresh"></Tuntap> <Tuntap @edit="handleTuntapEdit" @refresh="handleTuntapRefresh"></Tuntap>
<Forward @edit="handleForwardEdit" @sedit="handleSForwardEdit" @refresh="handleForwardRefresh"></Forward> <Forward @edit="_handleForwardEdit" @sedit="handleSForwardEdit"></Forward>
<el-table-column label="操作" width="54" fixed="right"> <el-table-column label="操作" width="54" fixed="right">
<template #default="scope"> <template #default="scope">
<el-popconfirm v-if="scope.row.showDel" confirm-button-text="确认" <el-popconfirm v-if="scope.row.showDel" confirm-button-text="确认" cancel-button-text="取消" title="删除不可逆,是否确认?" @confirm="handleDel(scope.row.MachineId)">
cancel-button-text="取消" title="删除不可逆,是否确认?" @confirm="handleDel(scope.row.MachineId)">
<template #reference> <template #reference>
<el-button type="danger" size="small"><el-icon><Delete /></el-icon></el-button> <el-button type="danger" size="small"><el-icon><Delete /></el-icon></el-button>
</template> </template>
@@ -18,28 +17,25 @@
</el-table> </el-table>
<div class="page t-c"> <div class="page t-c">
<div class="page-wrap"> <div class="page-wrap">
<el-pagination small background layout="total,prev, pager, next" :total="state.page.Count" <el-pagination small background layout="total,sizes,prev,pager, next" :total="devices.page.Count"
:page-size="state.page.Request.Size" :current-page="state.page.Request.Page" :page-size="devices.page.Request.Size" :current-page="devices.page.Request.Page"
@current-change="handlePageChange" /> @current-change="handlePageChange" @size-change="handlePageSizeChange" :page-sizes="[10, 20, 50, 100,255]" />
</div> </div>
</div> </div>
<DeviceEdit v-if="state.showDeviceEdit" v-model="state.showDeviceEdit" @change="handlePageChange" :data="state.deviceInfo"></DeviceEdit> <DeviceEdit v-if="devices.showDeviceEdit" v-model="devices.showDeviceEdit" @change="handlePageChange" :data="devices.deviceInfo"></DeviceEdit>
<TunnelEdit v-if="tunnel.showEdit" v-model="tunnel.showEdit" @change="handleTunnelChange"></TunnelEdit> <TunnelEdit v-if="tunnel.showEdit" v-model="tunnel.showEdit" @change="handleTunnelRefresh"></TunnelEdit>
<ConnectionsEdit v-if="connections.showEdit" v-model="connections.showEdit" ></ConnectionsEdit> <ConnectionsEdit v-if="connections.showEdit" v-model="connections.showEdit" ></ConnectionsEdit>
<TuntapEdit v-if="tuntap.showEdit" v-model="tuntap.showEdit" @change="handleTuntapChange"></TuntapEdit> <TuntapEdit v-if="tuntap.showEdit" v-model="tuntap.showEdit" @change="handleTuntapRefresh"></TuntapEdit>
<ForwardEdit v-if="forward.showEdit" v-model="forward.showEdit" @change="handleForwardChange"></ForwardEdit> <ForwardEdit v-if="forward.showEdit" v-model="forward.showEdit" ></ForwardEdit>
<SForwardEdit v-if="sforward.showEdit" v-model="sforward.showEdit" @change="handleSForwardChange"></SForwardEdit> <ForwardCopy v-if="forward.showCopy" v-model="forward.showCopy" ></ForwardCopy>
<SForwardEdit v-if="sforward.showEdit" v-model="sforward.showEdit" ></SForwardEdit>
<SForwardCopy v-if="sforward.showCopy" v-model="sforward.showCopy" ></SForwardCopy>
</div> </div>
</template> </template>
<script> <script>
import { getSignInList, signInDel } from '@/apis/signin.js'
import { subWebsocketState } from '@/apis/request.js' import { subWebsocketState } from '@/apis/request.js'
import { getTuntapInfo,refreshTuntap,getTuntapConnections,removeTuntapConnection } from '@/apis/tuntap'
import { getForwardInfo ,testTargetForwardInfo,testListenForwardInfo,getForwardConnections,removeForwardConnection} from '@/apis/forward'
import { getSForwardInfo,testLocalSForwardInfo} from '@/apis/sforward'
import { getTunnelInfo ,refreshTunnel} from '@/apis/tunnel'
import { injectGlobalData } from '@/provide.js' import { injectGlobalData } from '@/provide.js'
import { reactive, onMounted, ref, nextTick, onUnmounted, computed, provide } from 'vue' import { reactive, onMounted, onUnmounted, computed } from 'vue'
import Device from './Device.vue' import Device from './Device.vue'
import DeviceEdit from './DeviceEdit.vue' import DeviceEdit from './DeviceEdit.vue'
import Tuntap from './Tuntap.vue' import Tuntap from './Tuntap.vue'
@@ -48,334 +44,69 @@ import Tunnel from './Tunnel.vue'
import TunnelEdit from './TunnelEdit.vue' import TunnelEdit from './TunnelEdit.vue'
import Forward from './Forward.vue' import Forward from './Forward.vue'
import ForwardEdit from './ForwardEdit.vue' import ForwardEdit from './ForwardEdit.vue'
import ForwardCopy from './ForwardCopy.vue'
import SForwardEdit from './SForwardEdit.vue' import SForwardEdit from './SForwardEdit.vue'
import SForwardCopy from './SForwardCopy.vue'
import ConnectionsEdit from './ConnectionsEdit.vue' import ConnectionsEdit from './ConnectionsEdit.vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { provideTuntap } from './tuntap'
import { provideTunnel } from './tunnel'
import { provideForward } from './forward'
import { provideConnections } from './connections'
import { provideSforward } from './sforward'
import { provideDevices } from './devices'
export default { export default {
components: {Device,DeviceEdit,Tunnel,TunnelEdit,ConnectionsEdit, Tuntap,TuntapEdit, Forward,ForwardEdit,SForwardEdit }, components: {Device,DeviceEdit,Tunnel,TunnelEdit,ConnectionsEdit, Tuntap,TuntapEdit, Forward,ForwardEdit,ForwardCopy,SForwardEdit,SForwardCopy },
setup(props) { setup(props) {
const globalData = injectGlobalData(); const globalData = injectGlobalData();
const machineId = computed(() => globalData.value.config.Client.Id);
const state = reactive({ const state = reactive({
timer:0,
page: {
Request: { Page: 1, Size: 10, GroupId: globalData.value.groupid,Name:'' },
Count: 0,
List: []
},
showDeviceEdit:false,
deviceInfo: null,
height: computed(()=>globalData.value.height-60), height: computed(()=>globalData.value.height-60),
}); });
const tuntap = ref({ const {devices, machineId, _getSignList, _getSignList1,
timer:0, handleDeviceEdit, handlePageChange, handlePageSizeChange, handleDel,clearDevicesTimeout} = provideDevices();
showEdit:false,
current: null,
list: {},
hashcode: 0,
});
provide('tuntap',tuntap);
const _getTuntapInfo = () => {
if (globalData.value.connected) {
getTuntapInfo(tuntap.value.hashcode.toString()).then((res) => {
tuntap.value.hashcode = res.HashCode;
if (res.List) {
for (let j in res.List) {
res.List[j].running = res.List[j].Status == 2;
res.List[j].loading = res.List[j].Status == 1;
}
tuntap.value.list = res.List;
}
tuntap.value.timer = setTimeout(_getTuntapInfo, 200);
}).catch(() => {
tuntap.value.timer = setTimeout(_getTuntapInfo, 200);
});
} else {
tuntap.value.timer = setTimeout(_getTuntapInfo, 1000);
}
}
const handleTuntapEdit = (_tuntap)=>{
tuntap.value.current = _tuntap;
tuntap.value.showEdit = true;
} const {tuntap,_getTuntapInfo,handleTuntapEdit,handleTuntapRefresh,clearTuntapTimeout,getTuntapMachines} = provideTuntap();
const handleTuntapChange = (page) => { const {tunnel,_getTunnelInfo,handleTunnelEdit,handleTunnelRefresh,clearTunnelTimeout} = provideTunnel();
if(page){ const {forward,_getForwardInfo,handleForwardEdit,_testTargetForwardInfo,_testListenForwardInfo,clearForwardTimeout,getForwardMachines} = provideForward();
state.page.Request.Page = page; const {sforward,_getSForwardInfo,handleSForwardEdit,_testLocalSForwardInfo,clearSForwardTimeout,getSForwardMachines} = provideSforward();
} const {connections,
_getSignList(); forwardConnections,_getForwardConnections,
} tuntapConnections,_getTuntapConnections,
const handleTuntapRefresh = ()=>{ handleTunnelConnections,clearConnectionsTimeout
refreshTuntap(); } = provideConnections();
ElMessage.success('刷新成功');
}
const tunnel = ref({ const _handleForwardEdit = (machineId) => {
timer:0, handleForwardEdit(machineId,devices.page.List.filter(c => c.MachineId == machineId)[0].MachineName);
showEdit:false,
current: null,
list: {},
hashcode: 0,
});
provide('tunnel',tunnel);
const _getTunnelInfo = () => {
if (globalData.value.connected) {
getTunnelInfo(tunnel.value.hashcode.toString()).then((res) => {
tunnel.value.hashcode = res.HashCode;
if (res.List) {
tunnel.value.list = res.List;
}
tunnel.value.timer = setTimeout(_getTunnelInfo, 1000);
}).catch(() => {
tunnel.value.timer = setTimeout(_getTunnelInfo, 1000);
});
} else {
tunnel.value.timer = setTimeout(_getTunnelInfo, 1000);
}
}
const handleTunnelEdit = (_tunnel)=>{
tunnel.value.current = _tunnel;
tunnel.value.showEdit = true;
}
const handleTunnelChange = () => {
_getSignList();
}
const handleTunnelRefresh = ()=>{
refreshTunnel();
ElMessage.success('刷新成功');
} }
const connections = ref({
showEdit:false,
speedCache: {},
current:''
});
provide('connections',connections);
const forwardConnections = ref({
timer:0,
list: {},
});
provide('forward-connections',forwardConnections);
const _getForwardConnections = ()=>{
if (globalData.value.connected) {
getForwardConnections().then((res)=>{
parseConnections(res,removeForwardConnection);
forwardConnections.value.list = res;
forwardConnections.value.timer = setTimeout(_getForwardConnections, 1000);
}).catch((e)=>{
forwardConnections.value.timer = setTimeout(_getForwardConnections, 1000);
})
}else {
forwardConnections.value.timer = setTimeout(_getForwardConnections, 1000);
}
}
const tuntapConnections = ref({
timer:0,
list: {},
});
provide('tuntap-connections',tuntapConnections);
const _getTuntapConnections = ()=>{
if (globalData.value.connected) {
getTuntapConnections().then((res)=>{
parseConnections(res,removeTuntapConnection);
tuntapConnections.value.list = res;
tuntapConnections.value.timer = setTimeout(_getTuntapConnections, 1000);
}).catch((e)=>{
tuntapConnections.value.timer = setTimeout(_getTuntapConnections, 1000);
})
}else {
tuntapConnections.value.timer = setTimeout(_getTuntapConnections, 1000);
}
}
const parseConnections = (_connections,removeFunc)=>{
const caches = connections.value.speedCache;
for(let machineId in _connections){
const connection = _connections[machineId];
connection.removeFunc = removeFunc;
const key = `${connection.RemoteMachineId}-${connection.TransactionId}`;
const cache = caches[key] || {SendBytes:0,ReceiveBytes:0};
connection.SendBytesText = parseSpeed(connection.SendBytes - cache.SendBytes);
connection.ReceiveBytesText = parseSpeed(connection.ReceiveBytes - cache.ReceiveBytes);
cache.SendBytes = connection.SendBytes;
cache.ReceiveBytes = connection.ReceiveBytes;
caches[key] = cache;
}
}
const parseSpeed = (num)=>{
let index = 0;
while(num >= 1024){
num/=1024;
index++;
}
return `${num.toFixed(2)}${['B/s','KB/s','MB/s','GB/s','TB/s'][index]}`;
}
const handleTunnelConnections = (machineId)=>{
connections.value.current = machineId;
connections.value.showEdit = true;
}
const forward = ref({
timer:0,
showEdit:false,
current: null,
list: {}
});
provide('forward',forward);
const _getForwardInfo = () => {
if (globalData.value.connected) {
getForwardInfo().then((res) => {
forward.value.list = res;
forward.value.timer = setTimeout(_getForwardInfo, 1000);
}).catch(() => {
forward.value.timer = setTimeout(_getForwardInfo, 1000);
});
}else {
forward.value.timer = setTimeout(_getForwardInfo, 1000);
}
}
const handleForwardEdit = (machineId)=>{
forward.value.current = machineId;
forward.value.machineName =state.page.List.filter(c=>c.MachineId == machineId)[0].MachineName ;
forward.value.showEdit = true;
}
const handleForwardChange = () => {
_getSignList();
}
const handleForwardRefresh = ()=>{
_getSignList();
ElMessage.success('刷新成功');
}
const sforward = ref({
timer:0,
showEdit:false,
list: []
});
provide('sforward',sforward);
const _getSForwardInfo = () => {
if (globalData.value.connected) {
getSForwardInfo().then((res) => {
sforward.value.list = res;
sforward.value.timer = setTimeout(_getSForwardInfo, 1000);
}).catch(() => {
sforward.value.timer = setTimeout(_getSForwardInfo, 1000);
});
}else {
sforward.value.timer = setTimeout(_getSForwardInfo, 1000);
}
}
const handleSForwardEdit = ()=>{
sforward.value.showEdit = true;
}
const handleSForwardChange = () => {
_getSignList();
}
const _getSignList = () => {
state.page.Request.GroupId = globalData.value.groupid;
getSignInList(state.page.Request).then((res) => {
state.page.Request = res.Request;
state.page.Count = res.Count;
for (let j in res.List) {
res.List[j].showTunnel = machineId.value != res.List[j].MachineId;
res.List[j].showForward = machineId.value != res.List[j].MachineId;
res.List[j].showSForward = machineId.value == res.List[j].MachineId;
res.List[j].showDel = machineId.value != res.List[j].MachineId && res.List[j].Connected == false;
res.List[j].isSelf = machineId.value == res.List[j].MachineId;
}
state.page.List = res.List.sort((a,b)=>b.Connected - a.Connected);
}).catch((err) => { });
}
const _getSignList1 = () => {
if (globalData.value.connected) {
state.page.Request.GroupId = globalData.value.groupid;
getSignInList(state.page.Request).then((res) => {
for (let j in res.List) {
const item = state.page.List.filter(c=>c.MachineId == res.List[j].MachineId)[0];
if(item){
item.Connected = res.List[j].Connected;
item.Version = res.List[j].Version;
item.LastSignIn = res.List[j].LastSignIn;
item.Args = res.List[j].Args;
item.showTunnel = machineId.value != res.List[j].MachineId;
item.showForward = machineId.value != res.List[j].MachineId;
item.showSForward = machineId.value == res.List[j].MachineId;
item.showDel = machineId.value != res.List[j].MachineId && res.List[j].Connected == false;
item.isSelf = machineId.value == res.List[j].MachineId;
}
}
state.timer = setTimeout(_getSignList1, 5000);
}).catch((err) => {
state.timer = setTimeout(_getSignList1, 5000);
});
}else{
state.timer = setTimeout(_getSignList1, 5000);
}
}
const handleDeviceEdit = (row)=>{
state.deviceInfo = row;
state.showDeviceEdit = true;
}
const handlePageRefresh = (name)=>{ const handlePageRefresh = (name)=>{
state.page.Request.Name = name; devices.page.Request.Name = name || '';
handlePageChange(); if(devices.page.Request.Name){
refreshTunnel(); devices.page.Request.Ids = getTuntapMachines(devices.page.Request.Name)
refreshTuntap(); .concat(getForwardMachines(devices.page.Request.Name))
ElMessage.success('刷新成功'); .concat(getSForwardMachines(devices.page.Request.Name))
} .reduce((arr,id)=>{
const handlePageChange = (page) => { if(arr.indexOf(id) == -1){
if(page){ arr.push(id);
state.page.Request.Page = page; }
_getSignList(); return arr;
},[]);
}else{
devices.page.Request.Ids = [];
} }
handlePageChange();
handleTunnelRefresh();
handleTuntapRefresh();
ElMessage.success({message:'刷新成功',grouping:true});
} }
const handleDel = (name) => { const handlePageSearch = ()=>{
signInDel(name).then(() => { handlePageChange();
_getSignList(); handleTunnelRefresh();
}); handleTuntapRefresh();
} ElMessage.success({message:'刷新成功',grouping:true});
const timerState = reactive({
timerTestTarget:0,
timerTestListen:0,
timerTestLocal:0,
})
const _testTargetForwardInfo = ()=>{
testTargetForwardInfo(forward.value.current).then((res)=>{
timerState.timerTestTarget = setTimeout(_testTargetForwardInfo,5000);
}).catch(()=>{
timerState.timerTestTarget = setTimeout(_testTargetForwardInfo,5000);
});
}
const _testListenForwardInfo = ()=>{
testListenForwardInfo(forward.value.current).then((res)=>{
timerState.timerTestListen = setTimeout(_testListenForwardInfo,5000);
}).catch(()=>{
timerState.timerTestListen = setTimeout(_testListenForwardInfo,5000);
});
}
const _testLocalSForwardInfo = ()=>{
testLocalSForwardInfo().then((res)=>{
timerState.timerTestLocal = setTimeout(_testLocalSForwardInfo,5000);
}).catch(()=>{
timerState.timerTestLocal = setTimeout(_testLocalSForwardInfo,5000);
});
} }
onMounted(() => { onMounted(() => {
@@ -399,23 +130,21 @@ export default {
_testLocalSForwardInfo(); _testLocalSForwardInfo();
}); });
onUnmounted(() => { onUnmounted(() => {
clearTimeout( state.timer); clearDevicesTimeout();
clearTimeout(tuntap.value.timer); clearConnectionsTimeout();
clearTimeout(tunnel.value.timer); clearTuntapTimeout();
clearTimeout(forward.value.timer); clearTunnelTimeout();
clearTimeout(sforward.value.timer); clearForwardTimeout();
clearSForwardTimeout();
clearTimeout(timerState.timerTestTarget);
clearTimeout(timerState.timerTestListen);
clearTimeout(timerState.timerTestLocal);
}); });
return { return {
machineId, state, state,devices, machineId,
handleDeviceEdit,handlePageRefresh, handlePageChange, handleDel, handleDeviceEdit,handlePageRefresh,handlePageSearch, handlePageChange,handlePageSizeChange, handleDel,
tuntap, handleTuntapEdit, handleTuntapChange, handleTuntapRefresh, tuntap, handleTuntapEdit, handleTuntapRefresh,
tunnel,connections, handleTunnelEdit, handleTunnelChange, handleTunnelRefresh,handleTunnelConnections, tunnel,connections, handleTunnelEdit, handleTunnelRefresh,handleTunnelConnections,
forward,sforward, handleForwardEdit,handleForwardChange,handleForwardRefresh,handleSForwardEdit,handleSForwardChange forward,_handleForwardEdit,
sforward,handleSForwardEdit
} }
} }
} }

View File

@@ -0,0 +1,146 @@
<template>
<el-dialog v-model="state.show" @open="handleOnShowList" append-to=".app-wrap" title="复制服务器代理穿透" top="1vh" width="500">
<div>
<div class="t-c head">
<span>复制</span>
<el-select v-model="state.machineId" @change="handleMachineChange"
filterable remote :loading="state.loading" :remote-method="handleSearch">
<template #header>
<div class="t-c">
<div class="page-wrap">
<el-pagination small background layout="prev, pager, next"
:page-size="state.machineIds.Request.Size"
:total="state.machineIds.Count"
:pager-count="5"
:current-page="state.machineIds.Request.Page" @current-change="handlePageChange" />
</div>
</div>
</template>
<el-option v-for="(item, index) in state.machineIds.List" :key="index" :label="item.MachineName" :value="item.MachineId">
</el-option>
</el-select>
<span>的服务器代理穿透记录</span>
</div>
<el-table :data="state.forwards" size="small" border>
<el-table-column property="Name" label="名称"></el-table-column>
<el-table-column property="Domain" label="域名/端口">
<template #default="scope">
<span>{{ scope.row.Domain || scope.row.RemotePort }}</span>
</template>
</el-table-column>
<el-table-column property="LocalEP" label="本机服务" width="140"></el-table-column>
<el-table-column label="操作" width="80">
<template #default="scope">
<el-checkbox v-model="scope.row.use">使用</el-checkbox>
</template>
</el-table-column>
</el-table>
<div class="foot t-c">
<el-button type="primary" @click="handleConfirm">确定复制</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import { onMounted, onUnmounted, reactive, watch } from 'vue';
import { getSForwardRemoteInfo,addSForwardInfo } from '@/apis/sforward'
import { ElMessage } from 'element-plus';
import {WarnTriangleFilled} from '@element-plus/icons-vue'
import { injectGlobalData } from '@/provide';
import { getSignInIds } from '@/apis/signin';
export default {
props: ['modelValue'],
emits: ['update:modelValue'],
components:{WarnTriangleFilled},
setup(props, { emit }) {
const globalData = injectGlobalData();
const state = reactive({
show: true,
loading:false,
machineId: '',
machineIds:{
Request: {
Page: 1, Size:10, Name: ''
},
Count: 0,
List: []
},
forwards: []
});
watch(() => state.show, (val) => {
if (!val) {
setTimeout(() => {
emit('update:modelValue', val);
}, 300);
}
});
const handleOnShowList = () => {
_getMachineIds();
}
const _getMachineIds = ()=>{
state.loading = true;
getSignInIds(state.machineIds.Request).then((res)=>{
state.loading = false;
state.machineIds.Request = res.Request;
state.machineIds.Count = res.Count;
state.machineIds.List = res.List;
if(!state.machineId && state.machineIds.List.length > 0){
state.machineId = state.machineIds.List[0].MachineId;
_getForwardRemoteInfo();
}
}).catch((e)=>{
state.loading = false;
});
}
const handlePageChange = (page)=>{
state.machineIds.Request.Page = page;
_getMachineIds();
}
const handleSearch = (name)=>{
state.machineIds.Request.Name = name;
_getMachineIds();
}
const _getForwardRemoteInfo = ()=>{
getSForwardRemoteInfo(state.machineId).then((res)=>{
res.forEach(c=>{
c.use = true;
});
state.forwards = res;
}).catch((e)=>{
console.log(e);
});
}
const handleMachineChange = ()=>{
_getForwardRemoteInfo();
}
const handleConfirm = ()=>{
const tasks = state.forwards.filter(c=>c.use)
.map(c=>addSForwardInfo({Name:c.Name,Domain:c.Domain,RemotePort:c.RemotePort,LocalEP:c.LocalEP}));
Promise.all(tasks).then(()=>{
ElMessage.success('已操作!');
state.show = false;
}).catch(()=>{
ElMessage.success('操作失败!');
});
}
onMounted(()=>{
_getMachineIds();
});
onUnmounted(()=>{
});
return {
state,handleSearch, handleOnShowList,handleMachineChange,handleConfirm,handlePageChange
}
}
}
</script>
<style lang="stylus" scoped>
.el-select{width : 12rem}
.head{padding-bottom:1rem}
.foot{padding-top:1rem}
.page-wrap{display:inline-block}
</style>

View File

@@ -1,9 +1,10 @@
<template> <template>
<el-dialog v-model="state.show" @open="handleOnShowList" append-to=".app-wrap" title="服务器端口转发到本机" top="1vh" width="700"> <el-dialog v-model="state.show" @open="handleOnShowList" append-to=".app-wrap" title="服务器代理穿透" top="1vh" width="700">
<div> <div>
<div class="t-c head"> <div class="t-c head">
<el-button type="success" size="small" @click="handleAdd">添加</el-button> <el-button type="success" size="small" @click="handleAdd">添加</el-button>
<el-button size="small" @click="handleRefresh">刷新</el-button> <el-button size="small" @click="handleRefresh">刷新</el-button>
<el-button size="small" @click="handleCopy">复制穿透配置</el-button>
</div> </div>
<el-table :data="state.data" size="small" border height="500" @cell-dblclick="handleCellClick"> <el-table :data="state.data" size="small" border height="500" @cell-dblclick="handleCellClick">
<el-table-column property="Name" label="名称"> <el-table-column property="Name" label="名称">
@@ -85,11 +86,12 @@
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
import { inject, onMounted, onUnmounted, reactive, watch } from 'vue'; import { onMounted, onUnmounted, reactive, watch } from 'vue';
import { getSForwardInfo, removeSForwardInfo, addSForwardInfo,testLocalSForwardInfo } from '@/apis/sforward' import { getSForwardInfo, removeSForwardInfo, addSForwardInfo,testLocalSForwardInfo } from '@/apis/sforward'
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import {WarnTriangleFilled} from '@element-plus/icons-vue' import {WarnTriangleFilled} from '@element-plus/icons-vue'
import { injectGlobalData } from '@/provide'; import { injectGlobalData } from '@/provide';
import { useSforward } from './sforward';
export default { export default {
props: ['data','modelValue'], props: ['data','modelValue'],
emits: ['update:modelValue'], emits: ['update:modelValue'],
@@ -97,7 +99,7 @@ export default {
setup(props, { emit }) { setup(props, { emit }) {
const globalData = injectGlobalData(); const globalData = injectGlobalData();
const sforward = inject('sforward'); const sforward = useSforward();
const state = reactive({ const state = reactive({
bufferSize:globalData.value.bufferSize, bufferSize:globalData.value.bufferSize,
show: true, show: true,
@@ -197,6 +199,9 @@ export default {
ElMessage.error(err); ElMessage.error(err);
}); });
} }
const handleCopy = ()=>{
sforward.value.showCopy = true;
}
onMounted(()=>{ onMounted(()=>{
_getSForwardInfo(); _getSForwardInfo();
@@ -207,7 +212,7 @@ export default {
}) })
return { return {
state, handleOnShowList, handleCellClick, handleRefresh, handleAdd, handleEdit, handleEditBlur, handleDel, handleStartChange state, handleOnShowList, handleCellClick, handleRefresh, handleAdd, handleEdit, handleEditBlur, handleDel, handleStartChange,handleCopy
} }
} }
} }

View File

@@ -1,11 +1,5 @@
<template> <template>
<el-table-column prop="tuntap" label="隧道" width="90"> <el-table-column prop="tunnel" label="隧道" width="90">
<template #header>
<div class="flex">
<span class="flex-1">隧道</span>
<el-button size="small" @click="handleTunnelRefresh"><el-icon><Refresh /></el-icon></el-button>
</div>
</template>
<template #default="scope"> <template #default="scope">
<div v-if="tunnel.list[scope.row.MachineId]"> <div v-if="tunnel.list[scope.row.MachineId]">
<p> <p>
@@ -23,16 +17,17 @@
</el-table-column> </el-table-column>
</template> </template>
<script> <script>
import { computed, inject, reactive } from 'vue'; import { useTunnel } from './tunnel';
import { useConnections,useForwardConnections,useTuntapConnections } from './connections';
export default { export default {
emits: ['edit','refresh'], emits: ['edit','refresh'],
setup(props, { emit }) { setup(props, { emit }) {
const tunnel = inject('tunnel'); const tunnel = useTunnel();
const connections = useConnections();
const forwardConnections = inject('forward-connections'); const forwardConnections =useForwardConnections();
const tuntapConnections = inject('tuntap-connections'); const tuntapConnections = useTuntapConnections();
const connectionCount = (machineId)=>{ const connectionCount = (machineId)=>{
return [ return [

View File

@@ -36,14 +36,15 @@
<script> <script>
import {setTunnelRouteLevel } from '@/apis/tunnel'; import {setTunnelRouteLevel } from '@/apis/tunnel';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { inject, reactive, ref, watch } from 'vue'; import { reactive, ref, watch } from 'vue';
import { useTunnel } from './tunnel';
export default { export default {
props: ['modelValue'], props: ['modelValue'],
emits: ['change','update:modelValue'], emits: ['change','update:modelValue'],
setup(props, { emit }) { setup(props, { emit }) {
const tunnel = inject('tunnel'); const tunnel = useTunnel();
const ruleFormRef = ref(null); const ruleFormRef = ref(null);
const state = reactive({ const state = reactive({
show: true, show: true,

View File

@@ -1,11 +1,5 @@
<template> <template>
<el-table-column prop="tuntap" label="虚拟网卡" width="150"> <el-table-column prop="tuntap" label="虚拟网卡" width="150">
<template #header>
<div class="flex">
<span class="flex-1">虚拟网卡</span>
<el-button size="small" @click="handleRuntapRefresh"><el-icon><Refresh /></el-icon></el-button>
</div>
</template>
<template #default="scope"> <template #default="scope">
<div v-if="tuntap.list[scope.row.MachineId]"> <div v-if="tuntap.list[scope.row.MachineId]">
<div class="flex"> <div class="flex">
@@ -40,13 +34,13 @@
<script> <script>
import { stopTuntap, runTuntap } from '@/apis/tuntap'; import { stopTuntap, runTuntap } from '@/apis/tuntap';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { inject, reactive } from 'vue'; import { useTuntap } from './tuntap';
export default { export default {
emits: ['edit','refresh'], emits: ['edit','refresh'],
setup(props, { emit }) { setup(props, { emit }) {
const tuntap = inject('tuntap'); const tuntap = useTuntap();
const handleTuntap = (tuntap) => { const handleTuntap = (tuntap) => {
const fn = tuntap.running ? stopTuntap (tuntap.MachineId) : runTuntap(tuntap.MachineId); const fn = tuntap.running ? stopTuntap (tuntap.MachineId) : runTuntap(tuntap.MachineId);
fn.then(() => { fn.then(() => {
@@ -58,12 +52,12 @@ export default {
const handleTuntapIP = (tuntap) => { const handleTuntapIP = (tuntap) => {
emit('edit',tuntap); emit('edit',tuntap);
} }
const handleRuntapRefresh = ()=>{ const handleTuntapRefresh = ()=>{
emit('refresh'); emit('refresh');
} }
return { return {
tuntap, handleTuntap, handleTuntapIP,handleRuntapRefresh tuntap, handleTuntap, handleTuntapIP,handleTuntapRefresh
} }
} }
} }
@@ -72,4 +66,7 @@ export default {
.green{color:green;} .green{color:green;}
.error{color:red;} .error{color:red;}
.el-switch.is-disabled{opacity :1;} .el-switch.is-disabled{opacity :1;}
.el-input{
width:8rem;
}
</style> </style>

View File

@@ -37,7 +37,8 @@
import {updateTuntap } from '@/apis/tuntap'; import {updateTuntap } from '@/apis/tuntap';
import { injectGlobalData } from '@/provide'; import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { inject, reactive, ref, watch } from 'vue'; import { reactive, ref, watch } from 'vue';
import { useTuntap } from './tuntap';
export default { export default {
props: ['modelValue'], props: ['modelValue'],
@@ -45,8 +46,7 @@ export default {
setup(props, { emit }) { setup(props, { emit }) {
const globalData = injectGlobalData(); const globalData = injectGlobalData();
const tuntap = useTuntap();
const tuntap = inject('tuntap');
const ruleFormRef = ref(null); const ruleFormRef = ref(null);
const state = reactive({ const state = reactive({
show: true, show: true,

View File

@@ -0,0 +1,102 @@
import { getForwardConnections } from "@/apis/forward";
import { getTuntapConnections } from "@/apis/tuntap";
import { injectGlobalData } from "@/provide";
import { inject, provide, ref } from "vue";
const connectionsSymbol = Symbol();
const forwardConnectionsSymbol = Symbol();
const tuntapConnectionsSymbol = Symbol();
export const provideConnections = () => {
const globalData = injectGlobalData();
const connections = ref({
showEdit: false,
speedCache: {},
current: ''
});
provide(connectionsSymbol, connections);
const forwardConnections = ref({
timer: 0,
list: {},
});
provide(forwardConnectionsSymbol, forwardConnections);
const _getForwardConnections = () => {
if (globalData.value.connected) {
getForwardConnections().then((res) => {
parseConnections(res, removeForwardConnection);
forwardConnections.value.list = res;
forwardConnections.value.timer = setTimeout(_getForwardConnections, 1000);
}).catch((e) => {
forwardConnections.value.timer = setTimeout(_getForwardConnections, 1000);
})
} else {
forwardConnections.value.timer = setTimeout(_getForwardConnections, 1000);
}
}
const tuntapConnections = ref({
timer: 0,
list: {},
});
provide(tuntapConnectionsSymbol, tuntapConnections);
const _getTuntapConnections = () => {
if (globalData.value.connected) {
getTuntapConnections().then((res) => {
parseConnections(res, removeTuntapConnection);
tuntapConnections.value.list = res;
tuntapConnections.value.timer = setTimeout(_getTuntapConnections, 1000);
}).catch((e) => {
tuntapConnections.value.timer = setTimeout(_getTuntapConnections, 1000);
})
} else {
tuntapConnections.value.timer = setTimeout(_getTuntapConnections, 1000);
}
}
const parseConnections = (_connections, removeFunc) => {
const caches = connections.value.speedCache;
for (let machineId in _connections) {
const connection = _connections[machineId];
connection.removeFunc = removeFunc;
const key = `${connection.RemoteMachineId}-${connection.TransactionId}`;
const cache = caches[key] || { SendBytes: 0, ReceiveBytes: 0 };
connection.SendBytesText = parseSpeed(connection.SendBytes - cache.SendBytes);
connection.ReceiveBytesText = parseSpeed(connection.ReceiveBytes - cache.ReceiveBytes);
cache.SendBytes = connection.SendBytes;
cache.ReceiveBytes = connection.ReceiveBytes;
caches[key] = cache;
}
}
const parseSpeed = (num) => {
let index = 0;
while (num >= 1024) {
num /= 1024;
index++;
}
return `${num.toFixed(2)}${['B/s', 'KB/s', 'MB/s', 'GB/s', 'TB/s'][index]}`;
}
const handleTunnelConnections = (machineId) => {
connections.value.current = machineId;
connections.value.showEdit = true;
}
const clearConnectionsTimeout = () => {
clearTimeout(forwardConnections.value.timer);
clearTimeout(tuntapConnections.value.timer);
}
return {
connections,
forwardConnections, _getForwardConnections,
tuntapConnections, _getTuntapConnections,
handleTunnelConnections, clearConnectionsTimeout
}
}
export const useConnections = () => {
return inject(connectionsSymbol);
}
export const useForwardConnections = () => {
return inject(forwardConnectionsSymbol);
}
export const useTuntapConnections = () => {
return inject(tuntapConnectionsSymbol);
}

View File

@@ -0,0 +1,95 @@
import { getSignInList, signInDel } from "@/apis/signin";
import { injectGlobalData } from "@/provide";
import { computed, reactive } from "vue";
export const provideDevices = () => {
const globalData = injectGlobalData();
const machineId = computed(() => globalData.value.config.Client.Id);
const devices = reactive({
timer: 0,
page: {
Request: {
Page: 1, Size: +(localStorage.getItem('ps') || '10'),
GroupId: globalData.value.groupid, Name: '', Ids: []
},
Count: 0,
List: []
},
showDeviceEdit: false,
deviceInfo: null
});
const _getSignList = () => {
devices.page.Request.GroupId = globalData.value.groupid;
getSignInList(devices.page.Request).then((res) => {
devices.page.Request = res.Request;
devices.page.Count = res.Count;
for (let j in res.List) {
res.List[j].showTunnel = machineId.value != res.List[j].MachineId;
res.List[j].showForward = machineId.value != res.List[j].MachineId;
res.List[j].showSForward = machineId.value == res.List[j].MachineId;
res.List[j].showDel = machineId.value != res.List[j].MachineId && res.List[j].Connected == false;
res.List[j].isSelf = machineId.value == res.List[j].MachineId;
}
devices.page.List = res.List.sort((a, b) => b.Connected - a.Connected);
}).catch((err) => { });
}
const _getSignList1 = () => {
if (globalData.value.connected) {
devices.page.Request.GroupId = globalData.value.groupid;
getSignInList(devices.page.Request).then((res) => {
for (let j in res.List) {
const item = devices.page.List.filter(c => c.MachineId == res.List[j].MachineId)[0];
if (item) {
item.Connected = res.List[j].Connected;
item.Version = res.List[j].Version;
item.LastSignIn = res.List[j].LastSignIn;
item.Args = res.List[j].Args;
item.showTunnel = machineId.value != res.List[j].MachineId;
item.showForward = machineId.value != res.List[j].MachineId;
item.showSForward = machineId.value == res.List[j].MachineId;
item.showDel = machineId.value != res.List[j].MachineId && res.List[j].Connected == false;
item.isSelf = machineId.value == res.List[j].MachineId;
}
}
devices.timer = setTimeout(_getSignList1, 5000);
}).catch((err) => {
devices.timer = setTimeout(_getSignList1, 5000);
});
} else {
devices.timer = setTimeout(_getSignList1, 5000);
}
}
const handleDeviceEdit = (row) => {
devices.deviceInfo = row;
devices.showDeviceEdit = true;
}
const handlePageChange = (page) => {
if (page) {
devices.page.Request.Page = page;
}
_getSignList();
}
const handlePageSizeChange = (size) => {
if (size) {
devices.page.Request.Size = size;
localStorage.setItem('ps', size);
}
_getSignList();
}
const handleDel = (name) => {
signInDel(name).then(() => {
_getSignList();
});
}
const clearDevicesTimeout = () => {
clearTimeout(devices.timer);
devices.timer = 0;
}
return {
devices, machineId, _getSignList, _getSignList1, handleDeviceEdit, handlePageChange, handlePageSizeChange, handleDel, clearDevicesTimeout
}
}

View File

@@ -0,0 +1,69 @@
import { getForwardInfo, testListenForwardInfo, testTargetForwardInfo } from "@/apis/forward";
import { injectGlobalData } from "@/provide";
import { inject, provide, ref } from "vue";
const forwardSymbol = Symbol();
export const provideForward = () => {
const globalData = injectGlobalData();
const forward = ref({
timer: 0,
showEdit: false,
showCopy: false,
current: null,
list: {},
testTimer: 0,
testTargetTimer: 0,
});
provide(forwardSymbol, forward);
const _getForwardInfo = () => {
if (globalData.value.connected) {
getForwardInfo().then((res) => {
forward.value.list = res;
forward.value.timer = setTimeout(_getForwardInfo, 1000);
}).catch(() => {
forward.value.timer = setTimeout(_getForwardInfo, 1000);
});
} else {
forward.value.timer = setTimeout(_getForwardInfo, 1000);
}
}
const handleForwardEdit = (machineId, machineName) => {
forward.value.current = machineId;
forward.value.machineName = machineName;
forward.value.showEdit = true;
}
const _testTargetForwardInfo = () => {
clearTimeout(forward.value.testTargetTimer)
testTargetForwardInfo(forward.value.current).then((res) => {
forward.value.testTargetTimer = setTimeout(_testTargetForwardInfo, 5000);
}).catch(() => {
forward.value.testTargetTimer = setTimeout(_testTargetForwardInfo, 5000);
});
}
const _testListenForwardInfo = () => {
clearTimeout(forward.value.testTimer)
testListenForwardInfo(forward.value.current).then((res) => {
forward.value.testTimer = setTimeout(_testListenForwardInfo, 5000);
}).catch(() => {
forward.value.testTimer = setTimeout(_testListenForwardInfo, 5000);
});
}
const clearForwardTimeout = () => {
clearTimeout(forward.value.timer);
clearTimeout(forward.value.testTimer);
clearTimeout(forward.value.testTargetTimer);
}
const getForwardMachines = (name) => {
return Object.values(forward.value.list)
.filter(c => (c.Name || '').indexOf(name) >= 0 || (c.BindIPAddress || '').indexOf(name) >= 0 || (c.Port.toString()).indexOf(name) >= 0 || (c.TargetEP || '').indexOf(name) >= 0)
.map(c => c.MachineId);
}
return {
forward, _getForwardInfo, handleForwardEdit, _testTargetForwardInfo, _testListenForwardInfo, clearForwardTimeout, getForwardMachines
}
}
export const useForward = () => {
return inject(forwardSymbol);
}

View File

@@ -0,0 +1,61 @@
import { getSForwardInfo, testLocalSForwardInfo } from '@/apis/sforward';
import { injectGlobalData } from '@/provide';
import { ref, provide, inject, computed } from 'vue';
const sforwardSymbol = Symbol();
export const provideSforward = () => {
const globalData = injectGlobalData();
const machineId = computed(() => globalData.value.config.Client.Id);
const sforward = ref({
timer: 0,
showEdit: false,
showCopy: false,
list: [],
testTimer: 0
});
provide(sforwardSymbol, sforward);
const _getSForwardInfo = () => {
if (globalData.value.connected) {
getSForwardInfo().then((res) => {
sforward.value.list = res;
sforward.value.timer = setTimeout(_getSForwardInfo, 1000);
}).catch(() => {
sforward.value.timer = setTimeout(_getSForwardInfo, 1000);
});
} else {
sforward.value.timer = setTimeout(_getSForwardInfo, 1000);
}
}
const handleSForwardEdit = () => {
sforward.value.showEdit = true;
}
const _testLocalSForwardInfo = () => {
clearTimeout(sforward.value.testTimer)
testLocalSForwardInfo().then((res) => {
sforward.value.testTimer = setTimeout(_testLocalSForwardInfo, 5000);
}).catch(() => {
sforward.value.testTimer = setTimeout(_testLocalSForwardInfo, 5000);
});
}
const clearSForwardTimeout = () => {
clearTimeout(sforward.value.timer);
clearTimeout(sforward.value.testTimer);
}
const getSForwardMachines = (name) => {
const sfs = sforward.value.list
.filter(c => (c.Name || '').indexOf(name) >= 0 || (c.Domain || '').indexOf(name) >= 0 || (c.RemotePort.toString()).indexOf(name) >= 0 || c.LocalEP.indexOf(name) >= 0);
if (sfs.length > 0) {
return [machineId.value];
}
return [];
}
return {
sforward, _getSForwardInfo, handleSForwardEdit, _testLocalSForwardInfo, clearSForwardTimeout, getSForwardMachines
}
}
export const useSforward = () => {
return inject(sforwardSymbol);
}

View File

@@ -0,0 +1,49 @@
import { getTunnelInfo, refreshTunnel } from "@/apis/tunnel";
import { injectGlobalData } from "@/provide";
import { ElMessage } from "element-plus";
import { inject, provide, ref } from "vue";
const tunnelSymbol = Symbol();
export const provideTunnel = () => {
const globalData = injectGlobalData();
const tunnel = ref({
timer: 0,
showEdit: false,
current: null,
list: {},
hashcode: 0,
});
provide(tunnelSymbol, tunnel);
const _getTunnelInfo = () => {
if (globalData.value.connected) {
getTunnelInfo(tunnel.value.hashcode.toString()).then((res) => {
tunnel.value.hashcode = res.HashCode;
if (res.List) {
tunnel.value.list = res.List;
}
tunnel.value.timer = setTimeout(_getTunnelInfo, 1000);
}).catch(() => {
tunnel.value.timer = setTimeout(_getTunnelInfo, 1000);
});
} else {
tunnel.value.timer = setTimeout(_getTunnelInfo, 1000);
}
}
const handleTunnelEdit = (_tunnel) => {
tunnel.value.current = _tunnel;
tunnel.value.showEdit = true;
}
const handleTunnelRefresh = () => {
refreshTunnel();
ElMessage.success({ message: '刷新成功', grouping: true });
}
const clearTunnelTimeout = () => {
clearTimeout(tunnel.value.timer);
}
return {
tunnel, _getTunnelInfo, handleTunnelEdit, handleTunnelRefresh, clearTunnelTimeout
}
}
export const useTunnel = () => {
return inject(tunnelSymbol);
}

View File

@@ -0,0 +1,62 @@
import { injectGlobalData } from "@/provide";
import { ElMessage } from "element-plus";
import { inject, provide, ref } from "vue"
import { getTuntapInfo, refreshTuntap } from "@/apis/tuntap";
const tuntapSymbol = Symbol();
export const provideTuntap = () => {
const globalData = injectGlobalData();
const tuntap = ref({
timer: 0,
showEdit: false,
current: null,
list: {},
hashcode: 0
});
provide(tuntapSymbol, tuntap);
const _getTuntapInfo = () => {
clearTimeout(tuntap.value.timer);
if (globalData.value.connected) {
getTuntapInfo(tuntap.value.hashcode.toString()).then((res) => {
tuntap.value.hashcode = res.HashCode;
if (res.List) {
for (let j in res.List) {
res.List[j].running = res.List[j].Status == 2;
res.List[j].loading = res.List[j].Status == 1;
}
tuntap.value.list = res.List;
}
tuntap.value.timer = setTimeout(_getTuntapInfo, 200);
}).catch(() => {
tuntap.value.timer = setTimeout(_getTuntapInfo, 200);
});
} else {
tuntap.value.timer = setTimeout(_getTuntapInfo, 1000);
}
}
const handleTuntapEdit = (_tuntap) => {
tuntap.value.current = _tuntap;
tuntap.value.showEdit = true;
}
const handleTuntapRefresh = () => {
refreshTuntap();
ElMessage.success({ message: '刷新成功', grouping: true });
}
const clearTuntapTimeout = () => {
clearTimeout(tuntap.value.timer);
tuntap.value.timer = 0;
}
const getTuntapMachines = (name) => {
return Object.values(tuntap.value.list)
.filter(c => c.IP.indexOf(name) >= 0 || (c.LanIPs.filter(d => d.indexOf(name) >= 0).length > 0))
.map(c => c.MachineId);
}
return {
tuntap, _getTuntapInfo, handleTuntapEdit, handleTuntapRefresh, clearTuntapTimeout, getTuntapMachines
}
}
export const useTuntap = () => {
return inject(tuntapSymbol);
}

View File

@@ -1,4 +1,5 @@
<template> <template>
<Version ckey="excludeIPConfig"/>
<el-table :data="state.list" border size="small" width="100%" :height="`${state.height}px`" @cell-dblclick="handleCellClick"> <el-table :data="state.list" border size="small" width="100%" :height="`${state.height}px`" @cell-dblclick="handleCellClick">
<el-table-column prop="IPAddress" label="IP"> <el-table-column prop="IPAddress" label="IP">
<template #default="scope"> <template #default="scope">
@@ -43,16 +44,18 @@ import { setTunnelExcludeIPs } from '@/apis/tunnel';
import { injectGlobalData } from '@/provide'; import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { computed, inject, onMounted, reactive } from 'vue' import { computed, inject, onMounted, reactive } from 'vue'
import Version from './Version.vue';
export default { export default {
label:'打洞排除IP', label:'打洞排除IP',
name:'excludeIP', name:'excludeIP',
order:3, order:3,
components:{Version},
setup(props) { setup(props) {
const globalData = injectGlobalData(); const globalData = injectGlobalData();
const state = reactive({ const state = reactive({
list:((globalData.value.config.Running.Tunnel || {ExcludeIPs:[]}).ExcludeIPs || [{IPAddress:'0.0.0.0',Mask:32}]), list:((globalData.value.config.Running.Tunnel || {ExcludeIPs:[]}).ExcludeIPs || [{IPAddress:'0.0.0.0',Mask:32}]),
types:[], types:[],
height: computed(()=>globalData.value.height-92) height: computed(()=>globalData.value.height-127)
}); });
const handleCellClick = (row, column) => { const handleCellClick = (row, column) => {

View File

@@ -1,4 +1,5 @@
<template> <template>
<Version ckey="relayServers"/>
<el-table :data="state.list" border size="small" width="100%" :height="`${state.height}px`" @cell-dblclick="handleCellClick"> <el-table :data="state.list" border size="small" width="100%" :height="`${state.height}px`" @cell-dblclick="handleCellClick">
<el-table-column prop="Name" label="名称" width="100"> <el-table-column prop="Name" label="名称" width="100">
<template #default="scope"> <template #default="scope">
@@ -81,16 +82,18 @@ import { setRelayServers,getRelayTypes } from '@/apis/relay';
import { injectGlobalData } from '@/provide'; import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { computed, inject, onMounted, reactive } from 'vue' import { computed, inject, onMounted, reactive } from 'vue'
import Version from './Version.vue';
export default { export default {
label:'中继服务器', label:'中继服务器',
name:'relayServers', name:'relayServers',
order:4, order:4,
components:{Version},
setup(props) { setup(props) {
const globalData = injectGlobalData(); const globalData = injectGlobalData();
const state = reactive({ const state = reactive({
list:((globalData.value.config.Running.Relay || {Servers:[]}).Servers || []).sort((a,b)=>a.Disabled - b.Disabled), list:((globalData.value.config.Running.Relay || {Servers:[]}).Servers || []).sort((a,b)=>a.Disabled - b.Disabled),
types:[], types:[],
height: computed(()=>globalData.value.height-92) height: computed(()=>globalData.value.height-127)
}); });
const _getRelayTypes = ()=>{ const _getRelayTypes = ()=>{

View File

@@ -1,4 +1,5 @@
<template> <template>
<Version ckey="sforwardKey"/>
<div style="width: 30rem;padding: 5rem 0; margin: 0 auto;"> <div style="width: 30rem;padding: 5rem 0; margin: 0 auto;">
<p class="t-c"> <p class="t-c">
服务器代理穿透密钥 服务器代理穿透密钥
@@ -13,10 +14,12 @@ import { getSForwardSecretKey,setSForwardSecretKey } from '@/apis/sforward';
import { injectGlobalData } from '@/provide'; import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { computed, inject, onMounted, reactive } from 'vue' import { computed, inject, onMounted, reactive } from 'vue'
import Version from './Version.vue';
export default { export default {
label:'服务器代理穿透', label:'服务器代理穿透',
name:'sforward', name:'sforward',
order:5, order:5,
components:{Version},
setup(props) { setup(props) {
const globalData = injectGlobalData(); const globalData = injectGlobalData();
const state = reactive({ const state = reactive({

View File

@@ -1,4 +1,5 @@
<template> <template>
<Version ckey="signServers"/>
<el-table :data="state.list" border size="small" width="100%" :height="`${state.height}px`" @cell-dblclick="handleCellClick"> <el-table :data="state.list" border size="small" width="100%" :height="`${state.height}px`" @cell-dblclick="handleCellClick">
<el-table-column prop="Name" label="名称"> <el-table-column prop="Name" label="名称">
<template #default="scope"> <template #default="scope">
@@ -50,16 +51,18 @@ import { setSignInServers } from '@/apis/signin';
import { injectGlobalData } from '@/provide'; import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { computed, inject, reactive } from 'vue' import { computed, inject, reactive } from 'vue'
import Version from './Version.vue';
export default { export default {
label:'信标服务器', label:'信标服务器',
name:'signInServers', name:'signInServers',
order:0, order:0,
components:{Version},
setup(props) { setup(props) {
const globalData = injectGlobalData(); const globalData = injectGlobalData();
const state = reactive({ const state = reactive({
list:globalData.value.config.Running.Client.Servers || [], list:globalData.value.config.Running.Client.Servers || [],
server:computed(()=>globalData.value.config.Client.Server), server:computed(()=>globalData.value.config.Client.Server),
height: computed(()=>globalData.value.height-92), height: computed(()=>globalData.value.height-127),
}); });
const handleCellClick = (row, column) => { const handleCellClick = (row, column) => {

View File

@@ -1,4 +1,5 @@
<template> <template>
<Version ckey="tunnelTransports"/>
<el-table :data="state.list" border size="small" width="100%" :height="`${state.height}px`" > <el-table :data="state.list" border size="small" width="100%" :height="`${state.height}px`" >
<el-table-column prop="Name" label="名称" width="120"></el-table-column> <el-table-column prop="Name" label="名称" width="120"></el-table-column>
<el-table-column prop="Label" label="说明"></el-table-column> <el-table-column prop="Label" label="说明"></el-table-column>
@@ -44,15 +45,17 @@ import { getTunnelTransports,setTunnelTransports } from '@/apis/tunnel';
import { injectGlobalData } from '@/provide'; import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { computed, inject, onMounted, reactive } from 'vue' import { computed, inject, onMounted, reactive } from 'vue'
import Version from './Version.vue';
export default { export default {
label:'打洞协议', label:'打洞协议',
name:'transports', name:'transports',
order:2, order:2,
components:{Version},
setup(props) { setup(props) {
const globalData = injectGlobalData(); const globalData = injectGlobalData();
const state = reactive({ const state = reactive({
list:[], list:[],
height: computed(()=>globalData.value.height-92), height: computed(()=>globalData.value.height-127),
bufferSize:globalData.value.bufferSize bufferSize:globalData.value.bufferSize
}); });

View File

@@ -1,4 +1,5 @@
<template> <template>
<Version ckey="tunnelWanPortProtocols"/>
<el-table :data="state.list" border size="small" width="100%" :height="`${state.height}px`" @cell-dblclick="handleCellClick"> <el-table :data="state.list" border size="small" width="100%" :height="`${state.height}px`" @cell-dblclick="handleCellClick">
<el-table-column prop="Name" label="名称"> <el-table-column prop="Name" label="名称">
<template #default="scope"> <template #default="scope">
@@ -78,17 +79,19 @@ import { setTunnelServers,getTunnelTypes } from '@/apis/tunnel';
import { injectGlobalData } from '@/provide'; import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { computed, inject, onMounted, reactive } from 'vue' import { computed, inject, onMounted, reactive } from 'vue'
import Version from './Version.vue';
export default { export default {
label:'外网端口服务器', label:'外网端口服务器',
name:'tunnelServers', name:'tunnelServers',
order:1, order:1,
components:{Version},
setup(props) { setup(props) {
const globalData = injectGlobalData(); const globalData = injectGlobalData();
const list = ((globalData.value.config.Running.Tunnel || {Servers:[]}).Servers || []).sort((a,b)=>a.Disabled - b.Disabled); const list = ((globalData.value.config.Running.Tunnel || {Servers:[]}).Servers || []).sort((a,b)=>a.Disabled - b.Disabled);
const state = reactive({ const state = reactive({
list:list, list:list,
types:[], types:[],
height: computed(()=>globalData.value.height-92) height: computed(()=>globalData.value.height-127)
}); });
const _getTunnelTypes = ()=>{ const _getTunnelTypes = ()=>{

View File

@@ -0,0 +1,50 @@
<template>
<div class="running-version-wrap">
<span>配置版本 : {{version}}</span>
<el-button size="small" @click=handleEdit>手动修改版本</el-button>
<span>高版本一端自动同步到低版本一端</span>
</div>
</template>
<script>
import {updateVersion} from '@/apis/running'
import { injectGlobalData } from '@/provide';
import { ElMessageBox } from 'element-plus';
import { computed } from 'vue'
export default {
props:['ckey'],
setup(props) {
const globalData = injectGlobalData();
const version = computed(()=>globalData.value.config.Running.Versions[props.ckey]);
const handleEdit = () => {
ElMessageBox.prompt('输入你要修改到的版本', '修改版本', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputValue:version.value,
inputPattern:/\d+/,
inputErrorMessage: 'Invalid Number',
}).then(({ value }) => {
value = +value;
if(isNaN(value)) return ;
updateVersion({key:props.ckey,version:value});
}).catch(()=>{
});
}
return {
version,handleEdit
}
}
}
</script>
<style lang="stylus" scoped>
.running-version-wrap{
span{
vertical-align:middle
}
padding:0 0 1rem 0;
line-height:2.4rem
}
</style>

View File

@@ -176,6 +176,7 @@ namespace linker.client
public async Task UpdateServers(ClientServerInfo[] servers) public async Task UpdateServers(ClientServerInfo[] servers)
{ {
await SetServers(servers); await SetServers(servers);
runningConfigTransfer.IncrementVersion(configKey);
SyncServers(); SyncServers();
} }
private void SetServers(Memory<byte> data) private void SetServers(Memory<byte> data)

View File

@@ -5,6 +5,7 @@ using System.Reflection;
using linker.client.args; using linker.client.args;
using linker.client.config; using linker.client.config;
using linker.config; using linker.config;
using linker.client.config.messenger;
namespace linker.client namespace linker.client
{ {
@@ -23,6 +24,8 @@ namespace linker.client
{ {
serviceCollection.AddSingleton<RunningConfig>(); serviceCollection.AddSingleton<RunningConfig>();
serviceCollection.AddSingleton<RunningConfigTransfer>(); serviceCollection.AddSingleton<RunningConfigTransfer>();
serviceCollection.AddSingleton<ConfigClientMessenger>();
serviceCollection.AddSingleton<RunningConfigApiController>();
serviceCollection.AddSingleton<SignInArgsTransfer>(); serviceCollection.AddSingleton<SignInArgsTransfer>();
@@ -43,7 +46,7 @@ namespace linker.client
public void AddServer(ServiceCollection serviceCollection, ConfigWrap config, Assembly[] assemblies) public void AddServer(ServiceCollection serviceCollection, ConfigWrap config, Assembly[] assemblies)
{ {
serviceCollection.AddSingleton<ConfigServerMessenger>();
} }
public void UseServer(ServiceProvider serviceProvider, ConfigWrap config, Assembly[] assemblies) public void UseServer(ServiceProvider serviceProvider, ConfigWrap config, Assembly[] assemblies)
{ {

View File

@@ -0,0 +1,28 @@
using linker.libs.api;
using linker.client.capi;
using linker.libs.extends;
namespace linker.client.config
{
public sealed class RunningConfigApiController : IApiClientController
{
private readonly RunningConfigTransfer runningConfigTransfer;
public RunningConfigApiController(RunningConfigTransfer runningConfigTransfer)
{
this.runningConfigTransfer = runningConfigTransfer;
}
public void UpdateVersion(ApiControllerParamsInfo param)
{
UpdateVersionInfo info = param.Content.DeJson<UpdateVersionInfo>();
runningConfigTransfer.UpdateVersion(info.Key, info.Version);
}
public sealed class UpdateVersionInfo
{
public string Key { get; set; }
public ulong Version { get; set; }
}
}
}

View File

@@ -2,7 +2,6 @@
using linker.libs; using linker.libs;
using linker.server; using linker.server;
using MemoryPack; using MemoryPack;
using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace linker.client.config namespace linker.client.config
@@ -45,7 +44,7 @@ namespace linker.client.config
public Memory<byte> InputConfig(ConfigVersionInfo info) public Memory<byte> InputConfig(ConfigVersionInfo info)
{ {
CheckVersion(info.Key, out ulong version); ulong version = GetVersion(info.Key);
if (setters.TryGetValue(info.Key, out Action<Memory<byte>> setter) && info.Version > version) if (setters.TryGetValue(info.Key, out Action<Memory<byte>> setter) && info.Version > version)
{ {
@@ -54,7 +53,12 @@ namespace linker.client.config
} }
else if (getters.TryGetValue(info.Key, out Func<Memory<byte>> getter) && version > info.Version) else if (getters.TryGetValue(info.Key, out Func<Memory<byte>> getter) && version > info.Version)
{ {
return getter(); return MemoryPackSerializer.Serialize(new ConfigVersionInfo
{
Data = getter(),
Key = info.Key,
Version = version
});
} }
return Helper.EmptyArray; return Helper.EmptyArray;
@@ -64,8 +68,7 @@ namespace linker.client.config
private object syncLockObj = new(); private object syncLockObj = new();
public void Sync(string key, Memory<byte> data) public void Sync(string key, Memory<byte> data)
{ {
CheckVersion(key, out ulong version); ulong version = GetVersion(key);
sender.SendReply(new MessageRequestWrap sender.SendReply(new MessageRequestWrap
{ {
Connection = clientSignInState.Connection, Connection = clientSignInState.Connection,
@@ -93,31 +96,25 @@ namespace linker.client.config
}); });
} }
private void CheckVersion(string key, out ulong version)
{
if (runningConfig.Data.Versions.TryGetValue(key, out version) == false)
{
version = 1;
}
else
{
version++;
}
runningConfig.Data.Versions[key] = version;
runningConfig.Data.Update();
}
private ulong GetVersion(string key) private ulong GetVersion(string key)
{ {
if (runningConfig.Data.Versions.TryGetValue(key, out ulong version) == false) if (runningConfig.Data.Versions.TryGetValue(key, out ulong version) == false)
{ {
return 0; version = 1;
runningConfig.Data.Versions[key] = version;
runningConfig.Data.Update();
} }
return version; return version;
} }
private void UpdateVersion(string key, ulong version) public void UpdateVersion(string key, ulong version)
{ {
runningConfig.Data.Versions[key] = version; runningConfig.Data.Versions[key] = version;
runningConfig.Data.Update(); runningConfig.Data.Update();
} }
public void IncrementVersion(string key)
{
ulong version = GetVersion(key);
UpdateVersion(key, version + 1);
}
} }
} }

View File

@@ -7,6 +7,10 @@ using linker.libs;
using linker.plugins.forward.proxy; using linker.plugins.forward.proxy;
using linker.tunnel.connection; using linker.tunnel.connection;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using linker.plugins.forward.messenger;
using linker.server;
using linker.client;
using MemoryPack;
namespace linker.plugins.forward namespace linker.plugins.forward
{ {
@@ -14,11 +18,15 @@ namespace linker.plugins.forward
{ {
private readonly ForwardTransfer forwardTransfer; private readonly ForwardTransfer forwardTransfer;
private readonly ForwardProxy forwardProxy; private readonly ForwardProxy forwardProxy;
private readonly MessengerSender messengerSender;
private readonly ClientSignInState clientSignInState;
public ForwardClientApiController(ForwardTransfer forwardTransfer, ForwardProxy forwardProxy) public ForwardClientApiController(ForwardTransfer forwardTransfer, ForwardProxy forwardProxy, MessengerSender messengerSender, ClientSignInState clientSignInState)
{ {
this.forwardTransfer = forwardTransfer; this.forwardTransfer = forwardTransfer;
this.forwardProxy = forwardProxy; this.forwardProxy = forwardProxy;
this.messengerSender = messengerSender;
this.clientSignInState = clientSignInState;
} }
public ConcurrentDictionary<string, ITunnelConnection> Connections(ApiControllerParamsInfo param) public ConcurrentDictionary<string, ITunnelConnection> Connections(ApiControllerParamsInfo param)
@@ -51,6 +59,21 @@ namespace linker.plugins.forward
{ {
return forwardTransfer.Get(); return forwardTransfer.Get();
} }
public async Task<List<ForwardRemoteInfo>> GetRemote(ApiControllerParamsInfo param)
{
GetForwardInfo request = param.Content.DeJson<GetForwardInfo>();
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)ForwardMessengerIds.GetForward,
Payload = MemoryPackSerializer.Serialize(request)
}).ConfigureAwait(false);
if (resp.Code == MessageResponeCodes.OK)
{
return MemoryPackSerializer.Deserialize<List<ForwardRemoteInfo>>(resp.Data.Span);
}
return new List<ForwardRemoteInfo>();
}
public IPAddress[] BindIPs(ApiControllerParamsInfo param) public IPAddress[] BindIPs(ApiControllerParamsInfo param)
{ {
@@ -62,7 +85,6 @@ namespace linker.plugins.forward
ForwardInfo info = param.Content.DeJson<ForwardInfo>(); ForwardInfo info = param.Content.DeJson<ForwardInfo>();
return forwardTransfer.Add(info); return forwardTransfer.Add(info);
} }
public bool Remove(ApiControllerParamsInfo param) public bool Remove(ApiControllerParamsInfo param)
{ {
if (uint.TryParse(param.Content, out uint id)) if (uint.TryParse(param.Content, out uint id))

View File

@@ -40,4 +40,10 @@ namespace linker.client.config
public List<IPEndPoint> EndPoints { get; set; } public List<IPEndPoint> EndPoints { get; set; }
} }
[MemoryPackable]
public sealed partial class GetForwardInfo
{
public string MachineId { get; set; }
public string ToMachineId { get; set; }
}
} }

View File

@@ -1,7 +1,9 @@
using linker.client.config; using linker.client.config;
using linker.plugins.signin.messenger; using linker.plugins.signin.messenger;
using linker.server; using linker.server;
using LiteDB;
using MemoryPack; using MemoryPack;
using System.Net;
namespace linker.plugins.forward.messenger namespace linker.plugins.forward.messenger
{ {
@@ -45,6 +47,34 @@ namespace linker.plugins.forward.messenger
}); });
} }
} }
[MessengerId((ushort)ForwardMessengerIds.GetForward)]
public void GetForward(IConnection connection)
{
GetForwardInfo info = MemoryPackSerializer.Deserialize<GetForwardInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(info.MachineId, out SignCacheInfo cache) && signCaching.TryGet(connection.Id, out SignCacheInfo cache1) && cache1.GroupId == cache.GroupId)
{
uint requestid = connection.ReceiveRequestWrap.RequestId;
sender.SendReply(new MessageRequestWrap
{
Connection = cache.Connection,
MessengerId = (ushort)ForwardMessengerIds.Get,
Payload = connection.ReceiveRequestWrap.Payload
}).ContinueWith(async (result) =>
{
if (result.Result.Code == MessageResponeCodes.OK)
{
await sender.ReplyOnly(new MessageResponseWrap
{
Connection = connection,
Code = MessageResponeCodes.OK,
Payload = result.Result.Data,
RequestId = requestid
}).ConfigureAwait(false);
}
});
}
}
} }
public sealed class ForwardClientMessenger : IMessenger public sealed class ForwardClientMessenger : IMessenger
@@ -75,7 +105,31 @@ namespace linker.plugins.forward.messenger
}).ConfigureAwait(false); }).ConfigureAwait(false);
}); });
} }
[MessengerId((ushort)ForwardMessengerIds.Get)]
public void Get(IConnection connection)
{
GetForwardInfo info = MemoryPackSerializer.Deserialize<GetForwardInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (forwardTransfer.Get().TryGetValue(info.ToMachineId, out List<ForwardInfo> list))
{
var result = list.Select(c => new ForwardRemoteInfo { BufferSize = c.BufferSize, Name = c.Name, Port = c.Port, TargetEP = c.TargetEP }).ToList();
connection.Write(MemoryPackSerializer.Serialize(result));
return;
}
connection.Write(MemoryPackSerializer.Serialize(new List<ForwardRemoteInfo>()));
}
} }
[MemoryPackable]
public sealed partial class ForwardRemoteInfo
{
public string Name { get; set; }
public int Port { get; set; }
[MemoryPackAllowSerialize]
public IPEndPoint TargetEP { get; set; }
public byte BufferSize { get; set; } = 3;
}
} }

View File

@@ -7,6 +7,9 @@
TestForward = 2401, TestForward = 2401,
Test = 2402, Test = 2402,
GetForward = 2403,
Get = 2404,
Max = 2499 Max = 2499
} }
} }

View File

@@ -85,6 +85,7 @@ namespace linker.plugins.relay
{ {
running.Data.Relay.Servers = servers; running.Data.Relay.Servers = servers;
running.Data.Update(); running.Data.Update();
runningConfigTransfer.IncrementVersion(configKey);
SyncServers(); SyncServers();
} }
private void SetServers(Memory<byte> data) private void SetServers(Memory<byte> data)

View File

@@ -2,24 +2,22 @@
using linker.libs.extends; using linker.libs.extends;
using linker.client.capi; using linker.client.capi;
using linker.client.config; using linker.client.config;
using linker.server;
using linker.client; using linker.client;
using linker.plugins.sforward.messenger; using linker.server;
using MemoryPack; using MemoryPack;
using linker.plugins.sforward.messenger;
namespace linker.plugins.sforward namespace linker.plugins.sforward
{ {
public sealed class SForwardClientApiController : IApiClientController public sealed class SForwardClientApiController : IApiClientController
{ {
private readonly SForwardTransfer forwardTransfer; private readonly SForwardTransfer forwardTransfer;
private readonly RunningConfig runningConfig;
private readonly MessengerSender messengerSender; private readonly MessengerSender messengerSender;
private readonly ClientSignInState clientSignInState; private readonly ClientSignInState clientSignInState;
public SForwardClientApiController(SForwardTransfer forwardTransfer, RunningConfig runningConfig, MessengerSender messengerSender, ClientSignInState clientSignInState) public SForwardClientApiController(SForwardTransfer forwardTransfer, MessengerSender messengerSender, ClientSignInState clientSignInState)
{ {
this.forwardTransfer = forwardTransfer; this.forwardTransfer = forwardTransfer;
this.runningConfig = runningConfig;
this.messengerSender = messengerSender; this.messengerSender = messengerSender;
this.clientSignInState = clientSignInState; this.clientSignInState = clientSignInState;
} }
@@ -38,6 +36,20 @@ namespace linker.plugins.sforward
{ {
return forwardTransfer.Get(); return forwardTransfer.Get();
} }
public async Task<List<SForwardRemoteInfo>> GetRemote(ApiControllerParamsInfo param)
{
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)SForwardMessengerIds.GetForward,
Payload = MemoryPackSerializer.Serialize(param.Content)
}).ConfigureAwait(false);
if (resp.Code == MessageResponeCodes.OK)
{
return MemoryPackSerializer.Deserialize<List<SForwardRemoteInfo>>(resp.Data.Span);
}
return new List<SForwardRemoteInfo>();
}
public bool Add(ApiControllerParamsInfo param) public bool Add(ApiControllerParamsInfo param)
{ {

View File

@@ -46,6 +46,7 @@ namespace linker.plugins.sforward
{ {
running.Data.SForwardSecretKey = key; running.Data.SForwardSecretKey = key;
running.Data.Update(); running.Data.Update();
runningConfigTransfer.IncrementVersion(configKey);
SyncKey(); SyncKey();
} }
private void SetSecretKey(Memory<byte> data) private void SetSecretKey(Memory<byte> data)
@@ -237,7 +238,6 @@ namespace linker.plugins.sforward
Start(); Start();
running.Data.SForwards.Remove(old); running.Data.SForwards.Remove(old);
running.Data.Update(); running.Data.Update();
return true; return true;
} }
} }

View File

@@ -6,6 +6,9 @@ using linker.server;
using MemoryPack; using MemoryPack;
using linker.plugins.sforward.proxy; using linker.plugins.sforward.proxy;
using linker.config; using linker.config;
using LiteDB;
using System.Net;
using linker.plugins.forward.messenger;
namespace linker.plugins.sforward.messenger namespace linker.plugins.sforward.messenger
{ {
@@ -151,6 +154,34 @@ namespace linker.plugins.sforward.messenger
} }
} }
[MessengerId((ushort)SForwardMessengerIds.GetForward)]
public void GetForward(IConnection connection)
{
string machineId = MemoryPackSerializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(machineId, out SignCacheInfo cache) && signCaching.TryGet(connection.Id, out SignCacheInfo cache1) && cache1.GroupId == cache.GroupId)
{
uint requestid = connection.ReceiveRequestWrap.RequestId;
sender.SendReply(new MessageRequestWrap
{
Connection = cache.Connection,
MessengerId = (ushort)SForwardMessengerIds.Get,
Payload = connection.ReceiveRequestWrap.Payload
}).ContinueWith(async (result) =>
{
if (result.Result.Code == MessageResponeCodes.OK)
{
await sender.ReplyOnly(new MessageResponseWrap
{
Connection = connection,
Code = MessageResponeCodes.OK,
Payload = result.Result.Data,
RequestId = requestid
}).ConfigureAwait(false);
}
});
}
}
private async Task<bool> WebConnect(string host, int port, ulong id) private async Task<bool> WebConnect(string host, int port, ulong id)
{ {
if (sForwardServerCahing.TryGet(host, out string machineId) && signCaching.TryGet(machineId, out SignCacheInfo sign) && sign.Connected) if (sForwardServerCahing.TryGet(host, out string machineId) && signCaching.TryGet(machineId, out SignCacheInfo sign) && sign.Connected)
@@ -159,7 +190,7 @@ namespace linker.plugins.sforward.messenger
{ {
Connection = sign.Connection, Connection = sign.Connection,
MessengerId = (ushort)SForwardMessengerIds.Proxy, MessengerId = (ushort)SForwardMessengerIds.Proxy,
Payload = MemoryPackSerializer.Serialize(new SForwardProxyInfo { Domain = host, RemotePort = port, Id = id, BufferSize= configWrap.Data.Server.SForward.BufferSize }) Payload = MemoryPackSerializer.Serialize(new SForwardProxyInfo { Domain = host, RemotePort = port, Id = id, BufferSize = configWrap.Data.Server.SForward.BufferSize })
}).ConfigureAwait(false); }).ConfigureAwait(false);
} }
return false; return false;
@@ -196,11 +227,13 @@ namespace linker.plugins.sforward.messenger
{ {
private readonly SForwardProxy proxy; private readonly SForwardProxy proxy;
private readonly RunningConfig runningConfig; private readonly RunningConfig runningConfig;
private readonly SForwardTransfer sForwardTransfer;
public SForwardClientMessenger(SForwardProxy proxy, RunningConfig runningConfig) public SForwardClientMessenger(SForwardProxy proxy, RunningConfig runningConfig, SForwardTransfer sForwardTransfer)
{ {
this.proxy = proxy; this.proxy = proxy;
this.runningConfig = runningConfig; this.runningConfig = runningConfig;
this.sForwardTransfer = sForwardTransfer;
} }
[MessengerId((ushort)SForwardMessengerIds.Proxy)] [MessengerId((ushort)SForwardMessengerIds.Proxy)]
@@ -213,7 +246,7 @@ namespace linker.plugins.sforward.messenger
SForwardInfo sForwardInfo = runningConfig.Data.SForwards.FirstOrDefault(c => c.Domain == sForwardProxyInfo.Domain); SForwardInfo sForwardInfo = runningConfig.Data.SForwards.FirstOrDefault(c => c.Domain == sForwardProxyInfo.Domain);
if (sForwardInfo != null) if (sForwardInfo != null)
{ {
_ = proxy.OnConnectTcp(sForwardProxyInfo.BufferSize,sForwardProxyInfo.Id, new System.Net.IPEndPoint(connection.Address.Address, sForwardProxyInfo.RemotePort), sForwardInfo.LocalEP); _ = proxy.OnConnectTcp(sForwardProxyInfo.BufferSize, sForwardProxyInfo.Id, new System.Net.IPEndPoint(connection.Address.Address, sForwardProxyInfo.RemotePort), sForwardInfo.LocalEP);
} }
} }
else if (sForwardProxyInfo.RemotePort > 0) else if (sForwardProxyInfo.RemotePort > 0)
@@ -225,6 +258,7 @@ namespace linker.plugins.sforward.messenger
} }
} }
} }
[MessengerId((ushort)SForwardMessengerIds.ProxyUdp)] [MessengerId((ushort)SForwardMessengerIds.ProxyUdp)]
public void ProxyUdp(IConnection connection) public void ProxyUdp(IConnection connection)
{ {
@@ -238,5 +272,33 @@ namespace linker.plugins.sforward.messenger
} }
} }
} }
[MessengerId((ushort)SForwardMessengerIds.Get)]
public void Get(IConnection connection)
{
List<SForwardRemoteInfo> result = sForwardTransfer.Get().Select(c => new SForwardRemoteInfo
{
BufferSize = c.BufferSize,
Domain = c.Domain,
LocalEP = c.LocalEP,
Name = c.Name,
RemotePort = c.RemotePort,
}).ToList();
connection.Write(MemoryPackSerializer.Serialize(result));
}
}
[MemoryPackable]
public sealed partial class SForwardRemoteInfo
{
public string Name { get; set; }
public string Domain { get; set; }
public int RemotePort { get; set; }
public byte BufferSize { get; set; } = 3;
[MemoryPackAllowSerialize]
public IPEndPoint LocalEP { get; set; }
} }
} }

View File

@@ -10,6 +10,9 @@
Proxy = 2303, Proxy = 2303,
ProxyUdp = 2304, ProxyUdp = 2304,
GetForward = 2305,
Get = 2306,
Max = 2399 Max = 2399
} }
} }

View File

@@ -74,6 +74,21 @@ namespace linker.plugins.signin
} }
return new SignInListResponseInfo { }; return new SignInListResponseInfo { };
} }
public async Task<SignInIdsResponseInfo> Ids(ApiControllerParamsInfo param)
{
SignInIdsRequestInfo request = param.Content.DeJson<SignInIdsRequestInfo>();
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)SignInMessengerIds.Ids,
Payload = MemoryPackSerializer.Serialize(request)
}).ConfigureAwait(false);
if (resp.Code == MessageResponeCodes.OK)
{
return MemoryPackSerializer.Deserialize<SignInIdsResponseInfo>(resp.Data.Span);
}
return new SignInIdsResponseInfo { };
}
public async Task<bool> SetName(ApiControllerParamsInfo param) public async Task<bool> SetName(ApiControllerParamsInfo param)
{ {

View File

@@ -8,11 +8,9 @@ namespace linker.plugins.signin.messenger
{ {
public sealed class SignInClientMessenger : IMessenger public sealed class SignInClientMessenger : IMessenger
{ {
private readonly ConfigWrap config;
private readonly ClientSignInTransfer clientSignInTransfer; private readonly ClientSignInTransfer clientSignInTransfer;
public SignInClientMessenger(ConfigWrap config, ClientSignInTransfer clientSignInTransfer) public SignInClientMessenger(ConfigWrap config, ClientSignInTransfer clientSignInTransfer)
{ {
this.config = config;
this.clientSignInTransfer = clientSignInTransfer; this.clientSignInTransfer = clientSignInTransfer;
} }
@@ -58,13 +56,12 @@ namespace linker.plugins.signin.messenger
public void List(IConnection connection) public void List(IConnection connection)
{ {
SignInListRequestInfo request = MemoryPackSerializer.Deserialize<SignInListRequestInfo>(connection.ReceiveRequestWrap.Payload.Span); SignInListRequestInfo request = MemoryPackSerializer.Deserialize<SignInListRequestInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache)) if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
{ {
IEnumerable<SignCacheInfo> list = signCaching.Get(cache.GroupId).OrderByDescending(c => c.MachineName).OrderByDescending(c => c.LastSignIn).OrderByDescending(c => c.Version).ToList(); IEnumerable<SignCacheInfo> list = signCaching.Get(cache.GroupId).OrderByDescending(c => c.MachineName).OrderByDescending(c => c.LastSignIn).OrderByDescending(c => c.Version).ToList();
if (string.IsNullOrWhiteSpace(request.Name) == false) if (string.IsNullOrWhiteSpace(request.Name) == false)
{ {
list = list.Where(c => c.MachineName.Contains(request.Name)); list = list.Where(c => c.Version.Contains(request.Name) || c.IP.ToString().Contains(request.Name) || c.MachineName.Contains(request.Name) || request.Ids.Contains(c.MachineId));
} }
int count = list.Count(); int count = list.Count();
list = list.Skip((request.Page - 1) * request.Size).Take(request.Size); list = list.Skip((request.Page - 1) * request.Size).Take(request.Size);
@@ -109,6 +106,31 @@ namespace linker.plugins.signin.messenger
connection.Write(MemoryPackSerializer.Serialize(config.Data.Version)); connection.Write(MemoryPackSerializer.Serialize(config.Data.Version));
} }
[MessengerId((ushort)SignInMessengerIds.Ids)]
public void Ids(IConnection connection)
{
SignInIdsRequestInfo request = MemoryPackSerializer.Deserialize<SignInIdsRequestInfo>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
{
IEnumerable<SignCacheInfo> list = signCaching.Get(cache.GroupId).OrderByDescending(c => c.MachineName).OrderByDescending(c => c.LastSignIn).OrderByDescending(c => c.Version).ToList();
if (string.IsNullOrWhiteSpace(request.Name) == false)
{
list = list.Where(c => c.MachineName.Contains(request.Name));
}
int count = list.Count();
list = list.Skip((request.Page - 1) * request.Size).Take(request.Size);
SignInIdsResponseInfo response = new SignInIdsResponseInfo
{
Request = request,
Count = count,
List = list.Select(c => new SignInIdsResponseItemInfo { MachineId = c.MachineId, MachineName = c.MachineName }).ToList()
};
connection.Write(MemoryPackSerializer.Serialize(response));
}
}
} }
[MemoryPackable] [MemoryPackable]
@@ -130,6 +152,10 @@ namespace linker.plugins.signin.messenger
/// 按名称搜索 /// 按名称搜索
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <summary>
/// 按id获取
/// </summary>
public string[] Ids { get; set; }
} }
[MemoryPackable] [MemoryPackable]
@@ -139,4 +165,37 @@ namespace linker.plugins.signin.messenger
public int Count { get; set; } public int Count { get; set; }
public List<SignCacheInfo> List { get; set; } = new List<SignCacheInfo>(); public List<SignCacheInfo> List { get; set; } = new List<SignCacheInfo>();
} }
[MemoryPackable]
public sealed partial class SignInIdsRequestInfo
{
/// <summary>
/// 当前页
/// </summary>
public int Page { get; set; } = 1;
/// <summary>
/// 每页大小
/// </summary>
public int Size { get; set; } = 10;
/// <summary>
/// 按名称搜索
/// </summary>
public string Name { get; set; }
}
[MemoryPackable]
public sealed partial class SignInIdsResponseInfo
{
public SignInIdsRequestInfo Request { get; set; } = new SignInIdsRequestInfo();
public int Count { get; set; }
public List<SignInIdsResponseItemInfo> List { get; set; } = new List<SignInIdsResponseItemInfo>();
}
[MemoryPackable]
public sealed partial class SignInIdsResponseItemInfo
{
public string MachineId { get; set; }
public string MachineName { get; set; }
}
} }

View File

@@ -11,6 +11,8 @@
Version = 7, Version = 7,
Ids = 8,
None = 99 None = 99
} }
} }

View File

@@ -68,10 +68,12 @@ namespace linker.plugins.tunnel
{ {
return running.Data.Tunnel.Servers; return running.Data.Tunnel.Servers;
} }
public void SetTunnelWanPortProtocols(List<TunnelWanPortInfo> compacts) public void SetTunnelWanPortProtocols(List<TunnelWanPortInfo> compacts, bool updateVersion)
{ {
running.Data.Tunnel.Servers = compacts; running.Data.Tunnel.Servers = compacts;
running.Data.Update(); running.Data.Update();
if (updateVersion)
runningConfigTransfer.IncrementVersion(wanPortConfigKey);
SyncWanPort(); SyncWanPort();
} }
private void SetTunnelWanPortProtocols(Memory<byte> data) private void SetTunnelWanPortProtocols(Memory<byte> data)
@@ -84,10 +86,12 @@ namespace linker.plugins.tunnel
{ {
return running.Data.Tunnel.Transports; return running.Data.Tunnel.Transports;
} }
public void SetTunnelTransports(List<TunnelTransportItemInfo> transports) public void SetTunnelTransports(List<TunnelTransportItemInfo> transports, bool updateVersion)
{ {
running.Data.Tunnel.Transports = transports; running.Data.Tunnel.Transports = transports;
running.Data.Update(); running.Data.Update();
if (updateVersion)
runningConfigTransfer.IncrementVersion(transportConfigKey);
SyncTransport(); SyncTransport();
} }
private void SetTunnelTransports(Memory<byte> data) private void SetTunnelTransports(Memory<byte> data)

View File

@@ -81,7 +81,7 @@ namespace linker.plugins.tunnel
public bool SetServers(ApiControllerParamsInfo param) public bool SetServers(ApiControllerParamsInfo param)
{ {
List<TunnelWanPortInfo> info = param.Content.DeJson<List<TunnelWanPortInfo>>(); List<TunnelWanPortInfo> info = param.Content.DeJson<List<TunnelWanPortInfo>>();
tunnelMessengerAdapter.SetTunnelWanPortProtocols(info); tunnelMessengerAdapter.SetTunnelWanPortProtocols(info,true);
return true; return true;
} }
@@ -127,7 +127,7 @@ namespace linker.plugins.tunnel
public bool SetTransports(ApiControllerParamsInfo param) public bool SetTransports(ApiControllerParamsInfo param)
{ {
List<TunnelTransportItemInfo> info = param.Content.DeJson<List<TunnelTransportItemInfo>>(); List<TunnelTransportItemInfo> info = param.Content.DeJson<List<TunnelTransportItemInfo>>();
tunnelMessengerAdapter.SetTunnelTransports(info); tunnelMessengerAdapter.SetTunnelTransports(info, true);
return true; return true;
} }

View File

@@ -3,6 +3,7 @@ using linker.client.config;
using linker.config; using linker.config;
using linker.plugins.tunnel.messenger; using linker.plugins.tunnel.messenger;
using linker.server; using linker.server;
using linker.tunnel.adapter;
using linker.tunnel.wanport; using linker.tunnel.wanport;
using MemoryPack; using MemoryPack;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@@ -16,6 +17,7 @@ namespace linker.plugins.tunnel
private readonly ClientSignInState clientSignInState; private readonly ClientSignInState clientSignInState;
private readonly MessengerSender messengerSender; private readonly MessengerSender messengerSender;
private readonly RunningConfigTransfer runningConfigTransfer; private readonly RunningConfigTransfer runningConfigTransfer;
private readonly ITunnelAdapter tunnelAdapter;
private string exipConfigKey = "excludeIPConfig"; private string exipConfigKey = "excludeIPConfig";
@@ -24,13 +26,14 @@ namespace linker.plugins.tunnel
private ConcurrentDictionary<string, TunnelTransportRouteLevelInfo> configs = new ConcurrentDictionary<string, TunnelTransportRouteLevelInfo>(); private ConcurrentDictionary<string, TunnelTransportRouteLevelInfo> configs = new ConcurrentDictionary<string, TunnelTransportRouteLevelInfo>();
public ConcurrentDictionary<string, TunnelTransportRouteLevelInfo> Config => configs; public ConcurrentDictionary<string, TunnelTransportRouteLevelInfo> Config => configs;
public TunnelConfigTransfer(ConfigWrap config, RunningConfig running, ClientSignInState clientSignInState, MessengerSender messengerSender, RunningConfigTransfer runningConfigTransfer) public TunnelConfigTransfer(ConfigWrap config, RunningConfig running, ClientSignInState clientSignInState, MessengerSender messengerSender, RunningConfigTransfer runningConfigTransfer, ITunnelAdapter tunnelAdapter)
{ {
this.config = config; this.config = config;
this.running = running; this.running = running;
this.clientSignInState = clientSignInState; this.clientSignInState = clientSignInState;
this.messengerSender = messengerSender; this.messengerSender = messengerSender;
this.runningConfigTransfer = runningConfigTransfer; this.runningConfigTransfer = runningConfigTransfer;
this.tunnelAdapter = tunnelAdapter;
clientSignInState.NetworkEnabledHandle += (times) => clientSignInState.NetworkEnabledHandle += (times) =>
{ {
@@ -48,9 +51,11 @@ namespace linker.plugins.tunnel
} }
private void InitConfig() private void InitConfig()
{ {
if (running.Data.Tunnel.Servers.FirstOrDefault(c => c.Type == TunnelWanPortType.Linker && c.ProtocolType == TunnelWanPortProtocolType.Udp) == null) bool updateVersion = false;
List<TunnelWanPortInfo> server = running.Data.Tunnel.Servers;
if (server.FirstOrDefault(c => c.Type == TunnelWanPortType.Linker && c.ProtocolType == TunnelWanPortProtocolType.Udp) == null)
{ {
running.Data.Tunnel.Servers.Add(new TunnelWanPortInfo server.Add(new TunnelWanPortInfo
{ {
Name = "Linker Udp", Name = "Linker Udp",
Type = TunnelWanPortType.Linker, Type = TunnelWanPortType.Linker,
@@ -58,10 +63,11 @@ namespace linker.plugins.tunnel
Disabled = false, Disabled = false,
Host = running.Data.Client.Servers.FirstOrDefault().Host, Host = running.Data.Client.Servers.FirstOrDefault().Host,
}); });
updateVersion = true;
} }
if (running.Data.Tunnel.Servers.FirstOrDefault(c => c.Type == TunnelWanPortType.Linker && c.ProtocolType == TunnelWanPortProtocolType.Tcp) == null) if (server.FirstOrDefault(c => c.Type == TunnelWanPortType.Linker && c.ProtocolType == TunnelWanPortProtocolType.Tcp) == null)
{ {
running.Data.Tunnel.Servers.Add(new TunnelWanPortInfo server.Add(new TunnelWanPortInfo
{ {
Name = "Linker Tcp", Name = "Linker Tcp",
Type = TunnelWanPortType.Linker, Type = TunnelWanPortType.Linker,
@@ -69,7 +75,9 @@ namespace linker.plugins.tunnel
Disabled = false, Disabled = false,
Host = running.Data.Client.Servers.FirstOrDefault().Host, Host = running.Data.Client.Servers.FirstOrDefault().Host,
}); });
updateVersion = true;
} }
tunnelAdapter.SetTunnelWanPortProtocols(server, updateVersion);
} }
/// <summary> /// <summary>
@@ -147,6 +155,7 @@ namespace linker.plugins.tunnel
{ {
running.Data.Tunnel.ExcludeIPs = ips; running.Data.Tunnel.ExcludeIPs = ips;
running.Data.Update(); running.Data.Update();
runningConfigTransfer.IncrementVersion(exipConfigKey);
SyncExcludeIP(); SyncExcludeIP();
} }
private void SettExcludeIPs(Memory<byte> data) private void SettExcludeIPs(Memory<byte> data)

View File

@@ -6,8 +6,6 @@ using linker.tunnel.adapter;
using linker.tunnel.transport; using linker.tunnel.transport;
using linker.libs; using linker.libs;
using MemoryPack; using MemoryPack;
using linker.tunnel.wanport;
using linker.client.config;
namespace linker.plugins.tunnel.messenger namespace linker.plugins.tunnel.messenger
{ {
@@ -15,13 +13,11 @@ namespace linker.plugins.tunnel.messenger
{ {
private readonly TunnelTransfer tunnel; private readonly TunnelTransfer tunnel;
private readonly TunnelConfigTransfer tunnelConfigTransfer; private readonly TunnelConfigTransfer tunnelConfigTransfer;
private readonly ITunnelAdapter tunnelMessengerAdapter;
public TunnelClientMessenger(TunnelTransfer tunnel, TunnelConfigTransfer tunnelConfigTransfer, ITunnelAdapter tunnelMessengerAdapter) public TunnelClientMessenger(TunnelTransfer tunnel, TunnelConfigTransfer tunnelConfigTransfer)
{ {
this.tunnel = tunnel; this.tunnel = tunnel;
this.tunnelConfigTransfer = tunnelConfigTransfer; this.tunnelConfigTransfer = tunnelConfigTransfer;
this.tunnelMessengerAdapter = tunnelMessengerAdapter;
} }
[MessengerId((ushort)TunnelMessengerIds.Begin)] [MessengerId((ushort)TunnelMessengerIds.Begin)]