This commit is contained in:
snltty
2024-05-20 14:29:14 +08:00
parent 921b9639a5
commit 2bc25d408f
27 changed files with 560 additions and 113 deletions

View File

@@ -3,3 +3,10 @@ import { sendWebsocketMsg } from './request'
export const updateRelayConnect = (machineName) => {
return sendWebsocketMsg('relay/Connect', machineName);
}
export const getRelayTypes = () => {
return sendWebsocketMsg('relay/gettypes');
}
export const updateRelaySetServers = (servers) => {
return sendWebsocketMsg('relay/SetServers', servers);
}

View File

@@ -3,3 +3,10 @@ import { sendWebsocketMsg } from './request'
export const updateTunnelConnect = (machineName) => {
return sendWebsocketMsg('tunnel/Connect', machineName);
}
export const getTunnelTypes = () => {
return sendWebsocketMsg('tunnel/gettypes');
}
export const updateTunnelSetServers = (servers) => {
return sendWebsocketMsg('tunnel/SetServers', servers);
}

View File

@@ -84,6 +84,10 @@ a.a-line {
padding-left: .6rem;
}
.pdl-10 {
padding-left: 1rem;
}
.pdt-10 {
padding-top: 1rem;
}

View File

@@ -0,0 +1,154 @@
<template>
<div>将按顺序使用主机直到其中一个成功中继秘钥用以确定服务器是否允许你进行中继</div>
<el-table :data="state.list" border size="small" width="100%" height="300" @cell-dblclick="handleCellClick">
<el-table-column prop="Name" label="名称">
<template #default="scope">
<template v-if="scope.row.NameEditing">
<el-input autofocus size="small" v-model="scope.row.Name"
@blur="handleEditBlur(scope.row, 'Name')"></el-input>
</template>
<template v-else>
{{ scope.row.Name }}
</template>
</template>
</el-table-column>
<el-table-column prop="Type" label="类别" >
<template #default="scope">
<el-select v-model="scope.row.Type" placeholder="Select" size="small" @change="handleEditBlur(scope.row, 'Type')">
<el-option v-for="item in state.types" :key="item.Value" :label="item.Name" :value="item.Value"/>
</el-select>
</template>
</el-table-column>
<el-table-column prop="Host" label="地址" width="120">
<template #default="scope">
<template v-if="scope.row.HostEditing">
<el-input autofocus size="small" v-model="scope.row.Host" @blur="handleEditBlur(scope.row, 'Host')"></el-input>
</template>
<template v-else>
{{ scope.row.Host }}
</template>
</template>
</el-table-column>
<el-table-column prop="SecretKey" label="秘钥" >
<template #default="scope">
<template v-if="scope.row.SecretKeyEditing">
<el-input type="password" show-password size="small" v-model="scope.row.SecretKey" @blur="handleEditBlur(scope.row, 'SecretKey')"></el-input>
</template>
<template v-else></template>
</template>
</el-table-column>
<el-table-column property="Disabled" label="禁用" width="60">
<template #default="scope">
<el-switch v-model="scope.row.Disabled" @change="handleEditBlur(scope.row, 'Disabled')" inline-prompt active-text="是" inactive-text="否" style="--el-switch-on-color: red; --el-switch-off-color: #ddd" />
</template>
</el-table-column>
<el-table-column prop="Sort" label="调序" width="104" fixed="right">
<template #default="scope">
<div>
<el-button size="small" @click="handleSort(scope.$index,-1)">
<el-icon><Top /></el-icon>
</el-button>
<el-button size="small" @click="handleSort(scope.$index,1)">
<el-icon><Bottom /></el-icon>
</el-button>
</div>
</template>
</el-table-column>
<el-table-column prop="Oper" label="操作" width="104" fixed="right">
<template #default="scope">
<div>
<el-popconfirm title="删除不可逆,是否确认?" @confirm="handleDel(scope.$index)">
<template #reference>
<el-button type="danger" size="small">
<el-icon><Delete /></el-icon>
</el-button>
</template>
</el-popconfirm>
<el-button type="primary" size="small" @click="handleAdd(scope.$index)">
<el-icon><Plus /></el-icon>
</el-button>
</div>
</template>
</el-table-column>
</el-table>
</template>
<script>
import { updateRelaySetServers,getRelayTypes } from '@/apis/relay';
import { injectGlobalData } from '@/provide';
import { computed, onMounted, reactive } from 'vue'
export default {
props:{
data:{
type:Array,
default:[]
}
},
setup(props) {
const globalData = injectGlobalData();
const state = reactive({
list:props.data.sort((a,b)=>a.Disabled - b.Disabled),
types:[]
});
const _getRelayTypes = ()=>{
getRelayTypes().then((res)=>{
state.types = res;
});
}
const handleCellClick = (row, column) => {
handleEdit(row, column.property);
}
const handleEdit = (row, p) => {
state.list.forEach(c => {
c[`NameEditing`] = false;
c[`TypeEditing`] = false;
c[`HostEditing`] = false;
c[`SecretKeyEditing`] = false;
})
row[`${p}Editing`] = true;
}
const handleEditBlur = (row, p) => {
row[`${p}Editing`] = false;
handleSave();
}
const handleDel = (index)=>{
state.list.splice(index,1);
handleSave();
}
const handleAdd = (index)=>{
if(state.list.filter(c=>c.Host == '' || c.Name == '').length > 0){
return;
}
state.list.splice(index+1,0,{Name:'',Host:'',Type:0,SecretKey:'snltty',Disabled:false});
handleSave();
}
const handleSort = (index,oper)=>{
const current = state.list[index];
const outher = state.list[index+oper];
if(current && outher){
state.list[index+oper] = current;
state.list[index] = outher;
}
handleSave(state.list);
}
const handleSave = ()=>{
state.list = state.list.slice().sort((a,b)=>a.Disabled - b.Disabled);
updateRelaySetServers(state.list);
}
onMounted(()=>{
_getRelayTypes();
});
return {state,handleCellClick,handleEditBlur,handleDel,handleAdd,handleSort}
}
}
</script>
<style lang="stylus" scoped>
</style>

View File

@@ -3,7 +3,7 @@
<a href="javascript:;" @click="handleConfig">服务器 {{state.server}}</a>
<span class="num">{{state.serverLength}}</span>
</div>
<el-dialog v-model="state.show" title="登入设置" width="600">
<el-dialog v-model="state.show" title="登入设置" width="700">
<div>
<el-form :model="state.form" :rules="state.rules" label-width="6rem">
<el-form-item label="" label-width="0">
@@ -18,16 +18,16 @@
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="服务器" prop="servers">
<el-form-item label-width="0">
<el-tabs type="border-card" style="width:100%" v-model="state.tab">
<el-tab-pane label="登入服务器" name="login">
<Servers :data="state.servers"></Servers>
</el-tab-pane>
<el-tab-pane label="中继服务器" name="relay">
<Servers :data="state.relayServers"></Servers>
<RelayServers :data="state.relayServers"></RelayServers>
</el-tab-pane>
<el-tab-pane label="打洞服务器" name="hole">
<Servers :data="state.holeServers"></Servers>
<el-tab-pane label="公网端口服务器" name="hole">
<TunnelServers :data="state.holeServers"></TunnelServers>
</el-tab-pane>
</el-tabs>
</el-form-item>
@@ -49,8 +49,10 @@ import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus';
import { computed, reactive } from 'vue';
import Servers from './Servers.vue'
import RelayServers from './RelayServers.vue'
import TunnelServers from './TunnelServers.vue'
export default {
components:{Servers},
components:{Servers,RelayServers,TunnelServers},
setup(props) {
const globalData = injectGlobalData();

View File

@@ -1,29 +1,55 @@
<template>
<el-table :data="state.list" border size="small" width="100%" height="300">
<el-table-column prop="Name" label="名称"></el-table-column>
<el-table-column prop="Host" label="地址" ></el-table-column>
<div>使用其中一条作为主机</div>
<el-table :data="state.list" border size="small" width="100%" height="300" @cell-dblclick="handleCellClick">
<el-table-column prop="Name" label="名称">
<template #default="scope">
<template v-if="scope.row.NameEditing">
<el-input autofocus size="small" v-model="scope.row.Name"
@blur="handleEditBlur(scope.row, 'Name')"></el-input>
</template>
<template v-else>
{{ scope.row.Name }}
</template>
</template>
</el-table-column>
<el-table-column prop="Host" label="地址" >
<template #default="scope">
<template v-if="scope.row.HostEditing">
<el-input autofocus size="small" v-model="scope.row.Host"
@blur="handleEditBlur(scope.row, 'Host')"></el-input>
</template>
<template v-else>
{{ scope.row.Host }}
</template>
</template>
</el-table-column>
<el-table-column prop="Oper" label="操作" width="150">
<template #default="scope">
<div>
<el-popconfirm title="删除不可逆,是否确认?" @confirm="handleDel(scope.$index)">
<template #reference>
<el-button type="danger" size="small">
<el-icon><Delete /></el-icon>
</el-button>
</template>
</el-popconfirm>
<el-button type="primary" size="small" @click="handleAdd(scope.$index)">
<el-icon><Plus /></el-icon>
</el-button>
<template v-if="state.server != scope.row.Host">
<el-button size="small" @click="handleUse(scope.$index)">
<el-icon><Select /></el-icon>
</el-button>
</template>
</div>
</template>
</el-table-column>
</el-table>
<el-dialog v-model="state.showAdd" title="添加服务器" width="300" >
<div>
<el-form :model="state.formAdd" :rules="state.rulesAdd" label-width="6rem">
<el-form-item label="名称" prop="name">
<el-input v-model="state.formAdd.name" maxlength="12" show-word-limit />
</el-form-item>
<el-form-item label="地址" prop="host">
<el-input v-model="state.formAdd.host" placeholder="ip/域名:端口" />
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer t-c">
<el-button @click="state.showAdd = false" :loading="state.loading">取消</el-button>
<el-button type="primary" @click="handleSaveAdd" :loading="state.loading">确定保存</el-button>
</div>
</template>
</el-dialog>
</template>
<script>
import { reactive } from 'vue'
import { updateConfigSetServers } from '@/apis/signin';
import { injectGlobalData } from '@/provide';
import { computed, reactive } from 'vue'
export default {
props:{
data:{
@@ -32,44 +58,49 @@ export default {
}
},
setup(props) {
const globalData = injectGlobalData();
const state = reactive({
list:props.data,
showAdd:false,
formAdd:{
name:'',
host:''
},
rulesAdd:{
name:[
{ required: true, message: '听填写', trigger: 'blur' },
],
host:[
{ required: true, message: '听填写', trigger: 'blur' },
]
},
server:computed(()=>globalData.value.config.Client.Server)
});
const handleDel = (item)=>{
const servers = state.list.filter(c=>c.Host != item.Host || c.Name != item.Name);
const handleCellClick = (row, column) => {
handleEdit(row, column.property);
}
const handleAdd = ()=>{
state.showAdd = true;
state.formAdd.name = '';
state.formAdd.host = '';
const handleEdit = (row, p) => {
state.list.forEach(c => {
c[`NameEditing`] = false;
c[`HostEditing`] = false;
})
row[`${p}Editing`] = true;
}
const handleSaveAdd = ()=>{
const servers = state.list || [];
const name = state.formAdd.name.replace(/^\s|\s$/g,'');
const host = state.formAdd.host.replace(/^\s|\s$/g,'');
if(servers.filter(c=>c.Host == host).length > 0 || servers.filter(c=>c.Name == name).length > 0){
ElMessage.error('已存在差不多相同的记录!');
return;
}
servers.push({Name:name,Host:host});
const handleEditBlur = (row, p) => {
row[`${p}Editing`] = false;
handleSave();
}
return {state,handleDel,handleAdd,handleSaveAdd}
const handleDel = (index)=>{
state.list.splice(index,1);
handleSave();
}
const handleAdd = (index)=>{
if(state.list.filter(c=>c.Host == '' || c.Name == '').length > 0){
return;
}
state.list.splice(index+1,0,{Name:'',Host:''});
handleSave();
}
const handleUse = (index)=>{
const temp = state.list[index];
state.list[index] = state.list[0];
state.list[0] = temp;
handleSave();
}
const handleSave = ()=>{
updateConfigSetServers(state.list);
}
return {state,handleCellClick,handleEditBlur,handleDel,handleAdd,handleUse}
}
}
</script>

View File

@@ -0,0 +1,145 @@
<template>
<div>将按顺序通过所有主机获取外网端口如何使用交由打洞决定默认打洞仅使用第一个</div>
<el-table :data="state.list" border size="small" width="100%" height="300" @cell-dblclick="handleCellClick">
<el-table-column prop="Name" label="名称">
<template #default="scope">
<template v-if="scope.row.NameEditing">
<el-input autofocus size="small" v-model="scope.row.Name"
@blur="handleEditBlur(scope.row, 'Name')"></el-input>
</template>
<template v-else>
{{ scope.row.Name }}
</template>
</template>
</el-table-column>
<el-table-column prop="Type" label="类别" >
<template #default="scope">
<el-select v-model="scope.row.Type" placeholder="Select" size="small" @change="handleEditBlur(scope.row, 'Type')">
<el-option v-for="item in state.types" :key="item.Value" :label="item.Name" :value="item.Value"/>
</el-select>
</template>
</el-table-column>
<el-table-column prop="Host" label="地址" width="120">
<template #default="scope">
<template v-if="scope.row.HostEditing">
<el-input autofocus size="small" v-model="scope.row.Host" @blur="handleEditBlur(scope.row, 'Host')"></el-input>
</template>
<template v-else>
{{ scope.row.Host }}
</template>
</template>
</el-table-column>
<el-table-column property="Disabled" label="禁用" width="60">
<template #default="scope">
<el-switch v-model="scope.row.Disabled" @change="handleEditBlur(scope.row, 'Disabled')" inline-prompt active-text="是" inactive-text="否" style="--el-switch-on-color: red; --el-switch-off-color: #ddd" />
</template>
</el-table-column>
<el-table-column prop="Sort" label="调序" width="104" fixed="right">
<template #default="scope">
<div>
<el-button size="small" @click="handleSort(scope.$index,-1)">
<el-icon><Top /></el-icon>
</el-button>
<el-button size="small" @click="handleSort(scope.$index,1)">
<el-icon><Bottom /></el-icon>
</el-button>
</div>
</template>
</el-table-column>
<el-table-column prop="Oper" label="操作" width="104" fixed="right">
<template #default="scope">
<div>
<el-popconfirm title="删除不可逆,是否确认?" @confirm="handleDel(scope.$index)">
<template #reference>
<el-button type="danger" size="small">
<el-icon><Delete /></el-icon>
</el-button>
</template>
</el-popconfirm>
<el-button type="primary" size="small" @click="handleAdd(scope.$index)">
<el-icon><Plus /></el-icon>
</el-button>
</div>
</template>
</el-table-column>
</el-table>
</template>
<script>
import { updateTunnelSetServers,getTunnelTypes } from '@/apis/tunnel';
import { injectGlobalData } from '@/provide';
import { computed, onMounted, reactive } from 'vue'
export default {
props:{
data:{
type:Array,
default:[]
}
},
setup(props) {
const globalData = injectGlobalData();
const state = reactive({
list:props.data.sort((a,b)=>a.Disabled - b.Disabled),
types:[]
});
const _getTunnelTypes = ()=>{
getTunnelTypes().then((res)=>{
state.types = res;
});
}
const handleCellClick = (row, column) => {
handleEdit(row, column.property);
}
const handleEdit = (row, p) => {
state.list.forEach(c => {
c[`NameEditing`] = false;
c[`TypeEditing`] = false;
c[`HostEditing`] = false;
})
row[`${p}Editing`] = true;
}
const handleEditBlur = (row, p) => {
row[`${p}Editing`] = false;
handleSave();
}
const handleDel = (index)=>{
state.list.splice(index,1);
handleSave();
}
const handleAdd = (index)=>{
if(state.list.filter(c=>c.Host == '' || c.Name == '').length > 0){
return;
}
state.list.splice(index+1,0,{Name:'',Host:'',Type:0,Disabled:false});
handleSave();
}
const handleSort = (index,oper)=>{
const current = state.list[index];
const outher = state.list[index+oper];
if(current && outher){
state.list[index+oper] = current;
state.list[index] = outher;
}
handleSave(state.list);
}
const handleSave = ()=>{
state.list = state.list.slice().sort((a,b)=>a.Disabled - b.Disabled);
updateTunnelSetServers(state.list);
}
onMounted(()=>{
_getTunnelTypes();
});
return {state,handleCellClick,handleEditBlur,handleDel,handleAdd,handleSort}
}
}
</script>
<style lang="stylus" scoped>
</style>

View File

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

View File

@@ -59,19 +59,18 @@
</template>
</template>
</el-table-column>
<el-table-column property="Started" label="启动状态" width="80">
<el-table-column property="Started" label="状态" width="60">
<template #default="scope">
<el-switch v-model="scope.row.Started" @change="handleStartChange(scope.row)" inline-prompt
active-text="" inactive-text="" />
</template>
</el-table-column>
<el-table-column label="操作" width="66">
<el-table-column label="操作" width="54">
<template #default="scope">
<el-popconfirm confirm-button-text="确认" cancel-button-text="取消" title="删除不可逆,是否确认?"
@confirm="handleDel(scope.row.ID)">
<template #reference>
<el-button type="danger" size="small">删除</el-button>
<el-button type="danger" size="small"><el-icon><Delete /></el-icon></el-button>
</template>
</el-popconfirm>
</template>

View File

@@ -50,7 +50,7 @@
<el-popconfirm v-if="scope.row.showDel" confirm-button-text="确认"
cancel-button-text="取消" title="删除不可逆,是否确认?" @confirm="handleDel(scope.row.MachineName)">
<template #reference>
<el-button type="danger" size="small">删除</el-button>
<el-button type="danger" size="small"><el-icon><Delete /></el-icon></el-button>
</template>
</el-popconfirm>
</template>
@@ -153,6 +153,8 @@ export default {
item.Version = res.List[j].Version;
item.LastSignIn = res.List[j].LastSignIn;
item.Args = res.List[j].Args;
item.showTunnel = machineName.value != res.List[j].MachineName;
item.showForward = machineName.value != res.List[j].MachineName;
item.showDel = machineName.value != res.List[j].MachineName && res.List[j].Connected == false;
}
}

View File

@@ -30,9 +30,9 @@
<div class="flex-1">
<el-input v-model="state.ruleForm.LanIPs[index]" />
</div>
<div>
<el-button type="danger" @click="handleDel(index)">删除</el-button>
<el-button type="primary" @click="handleAdd(index)">添加</el-button>
<div class="pdl-10">
<el-button type="danger" @click="handleDel(index)"><el-icon><Delete /></el-icon></el-button>
<el-button type="primary" @click="handleAdd(index)"><el-icon><Plus /></el-icon></el-button>
</div>
</div>
</template>

View File

@@ -10,9 +10,17 @@ namespace cmonitor.config
public sealed partial class ConfigClientInfo
{
public ClientServerInfo[] Servers { get; set; } = new ClientServerInfo[] {
private ClientServerInfo[] servers = new ClientServerInfo[] {
new ClientServerInfo{ Name="默认", Host=new IPEndPoint(IPAddress.Loopback, 1802).ToString() }
};
public ClientServerInfo[] Servers
{
get => servers; set
{
servers = value;
if (value.Length > 0) Server = value.FirstOrDefault().Host;
}
}
private string name = Dns.GetHostName().SubStr(0, 12);
@@ -36,7 +44,7 @@ namespace cmonitor.config
public sealed class ClientServerInfo
{
public string Name { get; set; }
public string Host { get; set; }
public string Name { get; set; } = string.Empty;
public string Host { get; set; } = string.Empty;
}
}

View File

@@ -21,6 +21,11 @@ namespace cmonitor.plugins.relay
RelayTest();
}
public List<RelayCompactTypeInfo> GetTypes(ApiControllerParamsInfo param)
{
return relayTransfer.GetTypes();
}
public bool SetServers(ApiControllerParamsInfo param)
{
config.Data.Client.Relay.Servers = param.Content.DeJson<RelayCompactInfo[]>();

View File

@@ -32,6 +32,11 @@ namespace cmonitor.plugins.relay
Logger.Instance.Warning($"load relay transport:{string.Join(",", transports.Select(c => c.Name))}");
}
public List<RelayCompactTypeInfo> GetTypes()
{
return transports.Select(c => new RelayCompactTypeInfo { Value = c.Type, Name = c.Type.ToString() }).Distinct(new RelayCompactTypeInfoEqualityComparer()).ToList();
}
public void SetConnectedCallback(string transactionId, Action<ITunnelConnection> callback)
{
if (OnConnected.TryGetValue(transactionId, out List<Action<ITunnelConnection>> callbacks) == false)
@@ -52,7 +57,7 @@ namespace cmonitor.plugins.relay
public async Task<ITunnelConnection> ConnectAsync(string remoteMachineName, string transactionId)
{
IEnumerable<ITransport> _transports = transports.OrderBy(c => c.Type);
foreach (RelayCompactInfo item in config.Data.Client.Relay.Servers.Where(c => c.Disabled == false))
foreach (RelayCompactInfo item in config.Data.Client.Relay.Servers.Where(c => c.Disabled == false && string.IsNullOrWhiteSpace(c.Host) == false))
{
ITransport transport = _transports.FirstOrDefault(c => c.Type == item.Type);
if (transport == null)

View File

@@ -1,4 +1,7 @@
namespace cmonitor.config
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace cmonitor.config
{
public partial class ConfigClientInfo
{
@@ -21,10 +24,10 @@
public sealed class RelayCompactInfo
{
public string Name { get; set; }
public string Name { get; set; } = string.Empty;
public RelayCompactType Type { get; set; } = RelayCompactType.Self;
public string SecretKey { get; set; } = "snltty";
public string Host { get; set; }
public string Host { get; set; } = string.Empty;
public bool Disabled { get; set; }
}
@@ -32,4 +35,23 @@
{
Self = 0,
}
public sealed class RelayCompactTypeInfo
{
public RelayCompactType Value { get; set; }
public string Name { get; set; }
}
public sealed class RelayCompactTypeInfoEqualityComparer : IEqualityComparer<RelayCompactTypeInfo>
{
public bool Equals(RelayCompactTypeInfo x, RelayCompactTypeInfo y)
{
return x.Value == y.Value;
}
public int GetHashCode([DisallowNull] RelayCompactTypeInfo obj)
{
return obj.Value.GetHashCode();
}
}
}

View File

@@ -31,7 +31,7 @@ namespace cmonitor.plugins.relay.transport
Socket socket = new Socket(relayInfo.Server.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
socket.Reuse(true);
socket.IPv6Only(relayInfo.Server.AddressFamily, false);
await socket.ConnectAsync(relayInfo.Server).WaitAsync(TimeSpan.FromSeconds(2));
await socket.ConnectAsync(relayInfo.Server).WaitAsync(TimeSpan.FromMilliseconds(500));
IConnection connection = tcpServer.BindReceive(socket);
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
@@ -65,7 +65,7 @@ namespace cmonitor.plugins.relay.transport
Socket socket = new Socket(relayInfo.Server.AddressFamily, SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
socket.Reuse(true);
socket.IPv6Only(relayInfo.Server.AddressFamily, false);
await socket.ConnectAsync(relayInfo.Server).WaitAsync(TimeSpan.FromSeconds(2));
await socket.ConnectAsync(relayInfo.Server).WaitAsync(TimeSpan.FromMilliseconds(500));
IConnection connection = tcpServer.BindReceive(socket);
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap

View File

@@ -58,17 +58,33 @@ namespace cmonitor.plugins.signin
}
public void Set(ApiControllerParamsInfo param)
{
string name = config.Data.Client.Name;
string gid = config.Data.Client.GroupId;
ConfigSetInfo info = param.Content.DeJson<ConfigSetInfo>();
config.Data.Client.Name = info.Name;
config.Data.Client.GroupId = info.GroupId;
config.Save();
clientSignInTransfer.SignOut();
_ = clientSignInTransfer.SignIn();
if(name != config.Data.Client.Name || gid != config.Data.Client.GroupId)
{
clientSignInTransfer.SignOut();
_ = clientSignInTransfer.SignIn();
}
}
public bool SetServers(ApiControllerParamsInfo param)
{
string server = config.Data.Client.Server;
config.Data.Client.Servers = param.Content.DeJson<ClientServerInfo[]>();
config.Save();
if(server != config.Data.Client.Server)
{
clientSignInTransfer.SignOut();
_ = clientSignInTransfer.SignIn();
}
return true;
}

View File

@@ -1,6 +1,7 @@
using cmonitor.client.capi;
using cmonitor.client.tunnel;
using cmonitor.config;
using cmonitor.plugins.tunnel.compact;
using common.libs;
using common.libs.api;
using common.libs.extends;
@@ -12,13 +13,22 @@ namespace cmonitor.plugins.tunnel
{
private readonly TunnelTransfer tunnelTransfer;
private readonly Config config;
public TunnelApiController(TunnelTransfer tunnelTransfer, Config config)
private readonly CompactTransfer compactTransfer;
public TunnelApiController(TunnelTransfer tunnelTransfer, Config config, CompactTransfer compactTransfer)
{
this.tunnelTransfer = tunnelTransfer;
this.config = config;
this.compactTransfer = compactTransfer;
TunnelTest();
}
public List<TunnelCompactTypeInfo> GetTypes(ApiControllerParamsInfo param)
{
return compactTransfer.GetTypes();
}
public bool SetServers(ApiControllerParamsInfo param)
{
config.Data.Client.Tunnel.Servers = param.Content.DeJson<TunnelCompactInfo[]>();

View File

@@ -49,6 +49,23 @@ namespace cmonitor.plugins.tunnel
Logger.Instance.Warning($"load tunnel transport:{string.Join(",", transports.Select(c => c.Name))}");
}
public void SetConnectedCallback(string transactionId, Action<ITunnelConnection> callback)
{
if (OnConnected.TryGetValue(transactionId, out List<Action<ITunnelConnection>> callbacks) == false)
{
callbacks = new List<Action<ITunnelConnection>>();
OnConnected[transactionId] = callbacks;
}
callbacks.Add(callback);
}
public void RemoveConnectedCallback(string transactionId, Action<ITunnelConnection> callback)
{
if (OnConnected.TryGetValue(transactionId, out List<Action<ITunnelConnection>> callbacks))
{
callbacks.Remove(callback);
}
}
public async Task<ITunnelConnection> ConnectAsync(string remoteMachineName, string transactionId)
{
IEnumerable<ITransport> _transports = transports.OrderBy(c => c.ProtocolType);
@@ -187,22 +204,7 @@ namespace cmonitor.plugins.tunnel
}
public void SetConnectedCallback(string transactionId, Action<ITunnelConnection> callback)
{
if (OnConnected.TryGetValue(transactionId, out List<Action<ITunnelConnection>> callbacks) == false)
{
callbacks = new List<Action<ITunnelConnection>>();
OnConnected[transactionId] = callbacks;
}
callbacks.Add(callback);
}
public void RemoveConnectedCallback(string transactionId, Action<ITunnelConnection> callback)
{
if (OnConnected.TryGetValue(transactionId, out List<Action<ITunnelConnection>> callbacks))
{
callbacks.Remove(callback);
}
}
private void OnConnecting(TunnelTransportInfo tunnelTransportInfo)
{

View File

@@ -29,7 +29,7 @@ namespace cmonitor.plugins.tunnel.compact
Socket socket = new Socket(server.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Reuse(true);
socket.IPv6Only(server.AddressFamily, false);
await socket.ConnectAsync(server).WaitAsync(TimeSpan.FromSeconds(2));
await socket.ConnectAsync(server).WaitAsync(TimeSpan.FromMilliseconds(500));
IConnection connection = tcpServer.BindReceive(socket);
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap { Connection = connection, MessengerId = (ushort)TunnelMessengerIds.ExternalIP });
@@ -53,7 +53,7 @@ namespace cmonitor.plugins.tunnel.compact
try
{
await udpClient.SendAsync(new byte[1] { 0 }, server);
UdpReceiveResult result = await udpClient.ReceiveAsync().WaitAsync(TimeSpan.FromSeconds(500));
UdpReceiveResult result = await udpClient.ReceiveAsync().WaitAsync(TimeSpan.FromMilliseconds(500));
if (result.Buffer.Length == 0)
{
return null;

View File

@@ -28,6 +28,12 @@ namespace cmonitor.plugins.tunnel.compact
Logger.Instance.Warning($"load tunnel compacts:{string.Join(",", compacts.Select(c => c.Name))}");
}
public List<TunnelCompactTypeInfo> GetTypes()
{
return compacts.Select(c => new TunnelCompactTypeInfo { Value = c.Type, Name = c.Type.ToString() }).Distinct(new TunnelCompactTypeInfoEqualityComparer()).ToList();
}
public async Task<TunnelCompactIPEndPoint[]> GetExternalIPAsync(TunnelProtocolType protocolType)
{
TunnelCompactIPEndPoint[] endpoints = new TunnelCompactIPEndPoint[config.Data.Client.Tunnel.Servers.Length];
@@ -35,7 +41,7 @@ namespace cmonitor.plugins.tunnel.compact
for (int i = 0; i < config.Data.Client.Tunnel.Servers.Length; i++)
{
TunnelCompactInfo item = config.Data.Client.Tunnel.Servers[i];
if (item.Disabled) continue;
if (item.Disabled || string.IsNullOrWhiteSpace(item.Host)) continue;
ICompact compact = compacts.FirstOrDefault(c => c.Type == item.Type);
if (compact == null) continue;

View File

@@ -1,4 +1,5 @@
using System.Net;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Text.Json.Serialization;
namespace cmonitor.config
@@ -20,9 +21,9 @@ namespace cmonitor.config
public sealed class TunnelCompactInfo
{
public string Name { get; set; }
public string Name { get; set; } = string.Empty;
public TunnelCompactType Type { get; set; }
public string Host { get; set; }
public string Host { get; set; } = string.Empty;
public bool Disabled { get; set; }
}
@@ -30,4 +31,23 @@ namespace cmonitor.config
{
Self = 0
}
public sealed class TunnelCompactTypeInfo
{
public TunnelCompactType Value { get; set; }
public string Name { get; set; }
}
public sealed class TunnelCompactTypeInfoEqualityComparer : IEqualityComparer<TunnelCompactTypeInfo>
{
public bool Equals(TunnelCompactTypeInfo x, TunnelCompactTypeInfo y)
{
return x.Value == y.Value;
}
public int GetHashCode([DisallowNull] TunnelCompactTypeInfo obj)
{
return obj.Value.GetHashCode();
}
}
}

View File

@@ -6,7 +6,6 @@ using cmonitor.plugins.tuntap.vea;
using cmonitor.server;
using common.libs;
using MemoryPack;
using SharpDX;
using System.Buffers.Binary;
using System.Collections.Concurrent;
using System.Net;
@@ -64,11 +63,7 @@ namespace cmonitor.plugins.tuntap
OnChange();
try
{
bool result = await tuntapVea.Run(tuntapProxy.LocalEndpoint.Port);
if (result)
{
await tuntapVea.SetIp(config.Data.Client.Tuntap.IP);
}
bool result = await tuntapVea.Run(tuntapProxy.LocalEndpoint.Port, config.Data.Client.Tuntap.IP);
config.Data.Client.Tuntap.Running = Status == TuntapStatus.Running;
config.Save();
}

View File

@@ -9,7 +9,7 @@ namespace cmonitor.plugins.tuntap.vea
{
public bool Running { get; }
public Task<bool> Run(int proxyPort);
public Task<bool> Run(int proxyPort,IPAddress ip);
public Task<bool> SetIp(IPAddress ip);
public void Kill();

View File

@@ -19,10 +19,10 @@ namespace cmonitor.plugins.tuntap.vea
{
}
public async Task<bool> Run(int proxyPort)
public async Task<bool> Run(int proxyPort, IPAddress ip)
{
CommandHelper.Linux(string.Empty, new string[] { $"ip tuntap add mode tun dev {veaName}" });
await SetIp(ip);
string str = CommandHelper.Linux(string.Empty, new string[] { $"ifconfig" });
if (str.Contains(veaName) == false)
{

View File

@@ -19,7 +19,7 @@ namespace cmonitor.plugins.tuntap.vea
{
}
public async Task<bool> Run(int proxyPort)
public async Task<bool> Run(int proxyPort, IPAddress ip)
{
interfaceOsx = GetOsxInterfaceNum();
try
@@ -42,6 +42,8 @@ namespace cmonitor.plugins.tuntap.vea
await Task.Delay(1000);
}
await SetIp(ip);
return string.IsNullOrWhiteSpace(interfaceOsx) == false;
}
public async Task<bool> SetIp(IPAddress ip)

View File

@@ -18,7 +18,7 @@ namespace cmonitor.plugins.tuntap.vea
{
}
public async Task<bool> Run(int proxyPort)
public async Task<bool> Run(int proxyPort, IPAddress ip)
{
string command = $" -device {veaName} -proxy socks5://127.0.0.1:{proxyPort} -loglevel silent";
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
@@ -52,7 +52,7 @@ namespace cmonitor.plugins.tuntap.vea
}
continue;
}
await SetIp(ip);
return true;
}
}