This commit is contained in:
snltty
2024-05-18 01:23:50 +08:00
parent 4f4921e8c4
commit f40f3565ce
30 changed files with 485 additions and 222 deletions

View File

@@ -0,0 +1,11 @@
import { sendWebsocketMsg } from './request'
export const getForwardInfo = () => {
return sendWebsocketMsg('forwardclient/get');
}
export const removeForwardInfo = (id) => {
return sendWebsocketMsg('forwardclient/remove', id);
}
export const addForwardInfo = (data) => {
return sendWebsocketMsg('forwardclient/add', data);
}

View File

@@ -1,9 +1,9 @@
<template>
<div class="status-server-wrap" :class="{connected:connected}">
<a href="javascript:;" @click="handleConfig">服务器 {{server}}</a>
<span class="num">{{serverLength}}</span>
<div class="status-server-wrap" :class="{connected:state.connected}">
<a href="javascript:;" @click="handleConfig">服务器 {{state.server}}</a>
<span class="num">{{state.serverLength}}</span>
</div>
<el-dialog v-model="state.show" title="登入设置" width="500">
<el-dialog v-model="state.show" title="登入设置" width="600">
<div>
<el-form :model="state.form" :rules="state.rules" label-width="6rem">
<el-form-item label="" label-width="0">
@@ -18,32 +18,18 @@
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="" label-width="0">
<el-col :span="12">
<el-form-item label="服务器" prop="server">
<el-select v-model="state.form.server">
<template v-for="(item,index) in servers" :key="index">
<el-option :label="item.Name" :value="item.Host" >
<div class="flex">
<span>{{item.Name}}</span>
<span>{{item.Host}}</span>
<span class="pdl-6" @click.stop>
<el-popconfirm title="删除不可逆,确认吗?" @confirm.stop="handleDel(item)">
<template #reference>
<el-button size="small">删除</el-button>
</template>
</el-popconfirm>
</span>
</div>
</el-option>
</template>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<div class="pdl-6"><el-button @click="handleAdd">添加服务器</el-button></div>
</el-col>
<el-form-item label="服务器" prop="servers">
<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>
</el-tab-pane>
<el-tab-pane label="打洞服务器" name="hole">
<Servers :data="state.holeServers"></Servers>
</el-tab-pane>
</el-tabs>
</el-form-item>
</el-form>
</div>
@@ -55,68 +41,42 @@
</template>
</el-dialog>
<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 { updateConfigSet, updateConfigSetServers } from '@/apis/signin';
import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus';
import { computed, reactive } from 'vue';
import Servers from './Servers.vue'
export default {
components:{Servers},
setup(props) {
const globalData = injectGlobalData();
const connected = computed(()=>globalData.value.signin.Connected);
const connecting = computed(()=>globalData.value.signin.Connecting);
const server = computed(()=>globalData.value.config.Client.Server);
const servers = computed(()=>globalData.value.config.Client.Servers || []);
const serverLength = computed(()=>(globalData.value.config.Client.Servers||[]).length);
const state = reactive({
show:false,
loading:false,
connected:computed(()=>globalData.value.signin.Connected),
connecting:computed(()=>globalData.value.signin.Connecting),
server:computed(()=>globalData.value.config.Client.Server),
serverLength:computed(()=>(globalData.value.config.Client.Servers||[]).length),
form:{
name:globalData.value.config.Client.Name,
server:globalData.value.config.Client.Server,
groupid:globalData.value.config.Client.GroupId,
},
rules:{},
showAdd:false,
formAdd:{
name:'',
host:''
},
rulesAdd:{
name:[
{ required: true, message: '听填写', trigger: 'blur' },
],
host:[
{ required: true, message: '听填写', trigger: 'blur' },
]
},
tab:'login',
servers:computed(()=>globalData.value.config.Client.Servers || []),
relayServers:computed(()=>(globalData.value.config.Client.Relay || {Servers:[]}).Servers || []),
holeServers:computed(()=>(globalData.value.config.Client.Tunnel || {Servers:[]}).Servers || []),
});
const handleConfig = ()=>{
state.form.name = globalData.value.config.Client.Name;
state.form.server = globalData.value.config.Client.Server;
state.form.groupid = globalData.value.config.Client.GroupId;
state.show = true;
}
@@ -132,50 +92,10 @@ export default {
ElMessage.success('操作失败!');
});
}
const handleDel = (item)=>{
const servers = (globalData.value.config.Client.Servers || []).filter(c=>c.Host != item.Host || c.Name != item.Name);
state.loading = true;
updateConfigSetServers(servers).then(()=>{
state.loading = false;
ElMessage.success('已操作');
globalData.value.updateFlag = Date.now();
}).catch((err)=>{
state.loading = false;
ElMessage.success('操作失败!');
});
}
const handleAdd = ()=>{
state.showAdd = true;
state.formAdd.name = '';
state.formAdd.host = '';
}
const handleSaveAdd = ()=>{
const servers = globalData.value.config.Client.Servers || [];
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});
state.loading = true;
updateConfigSetServers(servers).then(()=>{
state.loading = false;
state.showAdd = false;
ElMessage.success('已操作');
globalData.value.updateFlag = Date.now();
}).catch((err)=>{
state.loading = false;
ElMessage.success('操作失败!');
});
}
return {
connected,connecting,server,servers,serverLength,state,handleConfig,handleSave,
handleDel,handleAdd,handleSaveAdd
state,handleConfig,handleSave,
}
}
}

View File

@@ -0,0 +1,78 @@
<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>
</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'
export default {
props:{
data:{
type:Array,
default:[]
}
},
setup(props) {
const state = reactive({
list:props.data,
showAdd:false,
formAdd:{
name:'',
host:''
},
rulesAdd:{
name:[
{ required: true, message: '听填写', trigger: 'blur' },
],
host:[
{ required: true, message: '听填写', trigger: 'blur' },
]
},
});
const handleDel = (item)=>{
const servers = state.list.filter(c=>c.Host != item.Host || c.Name != item.Name);
}
const handleAdd = ()=>{
state.showAdd = true;
state.formAdd.name = '';
state.formAdd.host = '';
}
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});
}
return {state,handleDel,handleAdd,handleSaveAdd}
}
}
</script>
<style lang="stylus" scoped>
</style>

View File

@@ -0,0 +1,167 @@
<template>
<div>
<ul class="list">
<template v-for="(item,index) in state.data" :key="index">
<li>
<a href="javascript:;" @click="state.show = true" :class="{green:item.Started}">
<template v-if="item.Started"><strong>{{item.Port}}->{{item.TargetEP}}</strong></template>
<template v-else>{{item.Port}}->{{item.TargetEP}}</template>
</a>
</li>
</template>
</ul>
<el-dialog v-model="state.show" @open="handleOnShowList" append-to=".app-wrap" :title="`端口转发到【${state.machineName}】`" top="1vh" width="600">
<div>
<div class="t-c head">
<el-button type="success" size="small" @click="handleAdd">添加</el-button>
<el-button size="small" @click="handleRefresh">刷新</el-button>
</div>
<el-table :data="state.data" size="small" border height="500" @cell-dblclick="handleCellClick">
<el-table-column property="ID" label="ID" width="60" />
<el-table-column property="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 property="Port" label="本地端口" width="80" >
<template #default="scope">
<template v-if="scope.row.PortEditing">
<el-input type="number" autofocus size="small" v-model="scope.row.Port" @blur="handleEditBlur(scope.row,'Port')"></el-input>
</template>
<template v-else>
{{scope.row.Port}}
</template>
</template>
</el-table-column>
<el-table-column property="TargetEP" label="目标服务" width="140">
<template #default="scope">
<template v-if="scope.row.TargetEPEditing">
<el-input autofocus size="small" v-model="scope.row.TargetEP" @blur="handleEditBlur(scope.row,'TargetEP')"></el-input>
</template>
<template v-else>
{{scope.row.TargetEP}}
</template>
</template>
</el-table-column>
<el-table-column property="Started" label="启动状态" width="80" >
<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">
<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>
</template>
</el-popconfirm>
</template>
</el-table-column>
</el-table>
</div>
</el-dialog>
</div>
</template>
<script>
import { reactive, watch } from 'vue';
import { getForwardInfo,removeForwardInfo,addForwardInfo } from '@/apis/forward'
import { ElMessage } from 'element-plus';
export default {
props:{
data:{
type:Object,
default:[]
},
name:{
type:String,
default:''
}
},
emits:['change'],
setup(props,{emit}) {
const state = reactive({
show:false,
length:props.length,
machineName:props.name,
data:props.data,
});
watch(() => state.show, (val) => {
if (!val) {
setTimeout(() => {
emit('change');
}, 300);
}
});
const _getForwardInfo = ()=>{
getForwardInfo().then((res)=>{
state.data = res[state.machineName] || [];
}).catch(()=>{
});
}
const handleOnShowList = ()=>{
_getForwardInfo();
}
const handleCellClick = (row,column)=>{
handleEdit(row,column.property);
}
const handleRefresh = ()=>{
_getForwardInfo();
ElMessage.success('已刷新')
}
const handleAdd = ()=>{
saveRow({ID:0,Name:'',Port:0,TargetEP:'127.0.0.1:80',MachineName:state.machineName});
}
const handleEdit = (row,p)=>{
state.data.forEach(c=>{
c[`NameEditing`] = false;
c[`PortEditing`] = false;
c[`TargetEPEditing`] = false;
})
row[`${p}Editing`] =true;
}
const handleEditBlur = (row,p)=>{
row[`${p}Editing`] = false;
saveRow(row);
}
const handleDel = (id)=>{
removeForwardInfo(id).then(()=>{
_getForwardInfo();
})
}
const handleStartChange = (row)=>{
saveRow(row);
}
const saveRow = (row)=>{
row.Port = parseInt(row.Port)
addForwardInfo(row).then(()=>{
_getForwardInfo();
}).catch((err)=>{
ElMessage.error(err);
});
}
return {
state,handleOnShowList,handleCellClick,handleRefresh,handleAdd,handleEdit,handleEditBlur,handleDel,handleStartChange
}
}
}
</script>
<style lang="stylus" scoped>
.list{
a{
text-decoration: underline;
}
a.green{color:green}
}
.head{padding-bottom:1rem}
</style>

View File

@@ -16,20 +16,27 @@
</div>
</template>
</el-table-column>
<el-table-column prop="tunel" label="通道" width="90" >
<el-table-column prop="tunel" label="隧道测试" width="90" >
<template #default="scope">
<div v-if="machineName != scope.row.MachineName">
<Tunnel :data="scope.row"></Tunnel>
</div>
</template>
</el-table-column>
<el-table-column prop="tuntap" label="网卡" width="180">
<el-table-column prop="tuntap" label="虚拟网卡" width="180">
<template #default="scope">
<template v-if="state.tuntapInfos[scope.row.MachineName]">
<Tuntap @change="handleTuntapChange" :data="state.tuntapInfos[scope.row.MachineName]"></Tuntap>
</template>
</template>
</el-table-column>
<el-table-column prop="forward" label="端口转发" width="160">
<template #default="scope">
<template v-if="state.forwardInfos">
<Forward @change="handleForwardChange" :data="state.forwardInfos[scope.row.MachineName]" :name="scope.row.MachineName"></Forward>
</template>
</template>
</el-table-column>
<el-table-column prop="LastSignIn" label="其它" width="140" >
<template #default="scope">
<div>
@@ -38,7 +45,7 @@
</div>
</template>
</el-table-column>
<el-table-column label="操作">
<el-table-column label="操作" width="66">
<template #default="scope">
<el-popconfirm v-if="machineName != scope.row.MachineName" confirm-button-text="确认" cancel-button-text="取消" title="删除不可逆,是否确认?" @confirm="handleDel(scope.row.MachineName)">
<template #reference>
@@ -63,11 +70,12 @@ import {subWebsocketState} from '@/apis/request.js'
import {getTuntapInfo} from '@/apis/tuntap'
import {injectGlobalData} from '@/provide.js'
import {reactive,onMounted, ref, nextTick, onUnmounted, computed} from 'vue'
import { ElMessage } from 'element-plus'
import Tuntap from './Tuntap.vue'
import Tunnel from './Tunnel.vue'
import Forward from './Forward.vue'
import { getForwardInfo } from '@/apis/forward'
export default {
components:{Tuntap,Tunnel},
components:{Tuntap,Tunnel,Forward},
setup(props) {
const globalData = injectGlobalData();
@@ -81,6 +89,7 @@ export default {
},
tuntapInfos:{},
tuntapHashCode:0,
forwardInfos:null,
height:0,
});
@@ -108,6 +117,16 @@ export default {
tuntapTimer = setTimeout(_getTuntapInfo,1000);
}
}
const _getForwardInfo = ()=>{
getForwardInfo().then((res)=>{
state.forwardInfos = null;
nextTick(()=>{
state.forwardInfos = res;
});
}).catch(()=>{
});
}
const _getSignList = ()=>{
state.page.Request.GroupId = globalData.value.groupid;
@@ -115,6 +134,7 @@ export default {
state.page.Request = res.Request;
state.page.Count = res.Count;
state.page.List = res.List;
_getForwardInfo();
}).catch((err)=>{});
}
const handlePageChange = ()=>{
@@ -128,6 +148,9 @@ export default {
const handleTuntapChange = ()=>{
_getSignList();
}
const handleForwardChange = ()=>{
_getSignList();
}
const resizeTable = ()=>{
nextTick(()=>{
@@ -147,7 +170,7 @@ export default {
});
return {
machineName,state,wrap,handlePageChange,handleDel,handleTuntapChange
machineName,state,wrap,handlePageChange,handleDel,handleTuntapChange,handleForwardChange
}
}
}

View File

@@ -8,7 +8,9 @@ namespace cmonitor.client
[JsonIgnore]
public IConnection Connection { get; set; }
public bool Connecting { get; set; }
[JsonIgnore]
public bool connecting = false;
public bool Connecting => connecting;
public bool Connected => Connection != null && Connection.Connected;
private int networdkEnabledTimes = 0;

View File

@@ -38,7 +38,6 @@ namespace cmonitor.client
{
Task.Run(async () =>
{
await Task.Delay(10000);
while (true)
{
@@ -61,42 +60,41 @@ namespace cmonitor.client
public async Task SignIn()
{
if (clientSignInState.Connecting)
if (BooleanHelper.CompareExchange(ref clientSignInState.connecting, true, false))
{
return;
}
clientSignInState.Connecting = true;
IPEndPoint[] ips = new IPEndPoint[] { config.Data.Client.ServerEP };
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
Logger.Instance.Info($"get ip:{ips.ToJsonFormat()}");
foreach (IPEndPoint ip in ips)
if (string.IsNullOrWhiteSpace(config.Data.Client.Server))
{
try
{
if (await ConnectServer(ip) == false)
{
continue;
}
if (await SignIn2Server() == false)
{
continue;
}
GCHelper.FlushMemory();
clientSignInState.PushNetworkEnabled();
break;
}
catch (Exception ex)
{
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
Logger.Instance.Error(ex);
}
return;
}
clientSignInState.Connecting = false;
try
{
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
Logger.Instance.Info($"connect to signin server :{config.Data.Client.Server}");
IPEndPoint ip = NetworkHelper.GetEndPoint(config.Data.Client.Server, 1802);
if (await ConnectServer(ip) == false)
{
return;
}
if (await SignIn2Server() == false)
{
return;
}
GCHelper.FlushMemory();
clientSignInState.PushNetworkEnabled();
}
catch (Exception ex)
{
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
Logger.Instance.Error(ex);
}
BooleanHelper.CompareExchange(ref clientSignInState.connecting, false, true);
}
public void SignOut()
{

View File

@@ -11,7 +11,7 @@ namespace cmonitor.client
{
public sealed class ClientStartup : IStartup
{
public StartupLevel Level => StartupLevel.Normal;
public StartupLevel Level => StartupLevel.Bottom;
public string Name => "client";
public bool Required => true;
public string[] Dependent => new string[] { "firewall", "signin", "serialize" };
@@ -34,7 +34,6 @@ namespace cmonitor.client
public void UseClient(ServiceProvider serviceProvider, Config config, Assembly[] assemblies)
{
Logger.Instance.Info($"start client");
Logger.Instance.Info($"server ip {config.Data.Client.ServerEP}");
Logger.Instance.Info($"start client share memory");
ShareMemory shareMemory = serviceProvider.GetService<ShareMemory>();
@@ -43,6 +42,8 @@ namespace cmonitor.client
shareMemory.StartLoop();
Logger.Instance.Info($"start client signin transfer");
if (string.IsNullOrWhiteSpace(config.Data.Client.Server) && config.Data.Client.Servers.Length > 0)
config.Data.Client.Server = config.Data.Client.Servers.FirstOrDefault().Host;
ClientSignInTransfer clientTransfer = serviceProvider.GetService<ClientSignInTransfer>();
}

View File

@@ -1,7 +1,5 @@
using common.libs;
using common.libs.extends;
using common.libs.extends;
using System.Net;
using System.Text.Json.Serialization;
namespace cmonitor.config
{
@@ -12,35 +10,10 @@ namespace cmonitor.config
public sealed partial class ConfigClientInfo
{
private string server = new IPEndPoint(IPAddress.Loopback, 1802).ToString();
public string Server
{
get => server; set
{
server = value;
try
{
if (string.IsNullOrWhiteSpace(server) == false)
{
string[] arr = server.Split(':');
int port = arr.Length == 2 ? int.Parse(arr[1]) : 1802;
IPAddress ip = NetworkHelper.GetDomainIp(arr[0]);
ServerEP = new IPEndPoint(ip, port);
}
}
catch (Exception ex)
{
Logger.Instance.Error($"{server}->{ex}");
}
}
}
public ClientServerInfo[] Servers { get; set; } = new ClientServerInfo[] {
new ClientServerInfo{ Name="默认", Host=new IPEndPoint(IPAddress.Loopback, 1802).ToString() }
};
[JsonIgnore]
public IPEndPoint ServerEP { get; set; } = new IPEndPoint(IPAddress.Loopback, 1802);
private string name = Dns.GetHostName().SubStr(0, 12);
public string Name
@@ -57,6 +30,8 @@ namespace cmonitor.config
public int ShareMemoryCount { get; set; } = 100;
public int ShareMemorySize { get; set; } = 1024;
public string Server { get; set; } = new IPEndPoint(IPAddress.Loopback, 1802).ToString();
}
public sealed class ClientServerInfo

View File

@@ -23,10 +23,6 @@ namespace cmonitor.plugins.capi
public void UseClient(ServiceProvider serviceProvider, Config config, Assembly[] assemblies)
{
Logger.Instance.Info($"start client");
Logger.Instance.Info($"server ip {config.Data.Client.ServerEP}");
if (config.Data.Client.CApi.ApiPort > 0)
{
Logger.Instance.Info($"start client api server");

View File

@@ -14,17 +14,22 @@ namespace cmonitor.plugins.forward
this.forwardTransfer = forwardTransfer;
}
public Dictionary<string, List<ForwardInfo>> Get(ApiControllerParamsInfo param)
{
return forwardTransfer.Get();
}
public bool Add(ApiControllerParamsInfo param)
{
ForwardInfo info = param.Content.DeJson<ForwardInfo>();
return forwardTransfer.AddForward(info);
return forwardTransfer.Add(info);
}
public bool Remove(ApiControllerParamsInfo param)
{
if (uint.TryParse(param.Content, out uint id))
{
return forwardTransfer.RemoveForward(id);
return forwardTransfer.Remove(id);
}
return false;
}

View File

@@ -12,52 +12,68 @@ namespace cmonitor.plugins.forward
private readonly NumberSpaceUInt32 ns = new NumberSpaceUInt32();
public ForwardTransfer( Config config, ForwardProxy forwardProxy, ClientSignInState clientSignInState)
public ForwardTransfer(Config config, ForwardProxy forwardProxy, ClientSignInState clientSignInState)
{
this.config = config;
this.forwardProxy = forwardProxy;
clientSignInState.NetworkFirstEnabledHandle += () =>
{
StartForward();
Start();
};
}
private void StartForward()
private void Start()
{
uint maxid = config.Data.Client.Forward.Forwards.Count > 0 ? config.Data.Client.Forward.Forwards.Max(c=>c.ID) : 1;
uint maxid = config.Data.Client.Forward.Forwards.Count > 0 ? config.Data.Client.Forward.Forwards.Max(c => c.ID) : 1;
ns.Reset(maxid);
foreach (var item in config.Data.Client.Forward.Forwards)
{
try
if (item.Started)
{
if (item.Started)
if (item.Proxy == null)
{
if (item.Proxy == null)
try
{
Logger.Instance.Debug($"start forward {item.Port}->{item.MachineName}->{item.TargetEP}");
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
Logger.Instance.Debug($"start forward {item.Port}->{item.MachineName}->{item.TargetEP}");
item.Proxy = forwardProxy;
item.Proxy.Start(item.Port);
item.Proxy.Start(item.Port, item.TargetEP, item.MachineName);
item.Port = item.Proxy.LocalEndpoint.Port;
}
catch (Exception ex)
{
item.Started = false;
Logger.Instance.Error(ex);
}
}
else
}
else
{
try
{
if (item.Proxy != null)
{
Logger.Instance.Debug($"stop forward {item.Port}->{item.MachineName}->{item.TargetEP}");
if (Logger.Instance.LoggerLevel <= LoggerTypes.DEBUG)
Logger.Instance.Debug($"stop forward {item.Port}->{item.MachineName}->{item.TargetEP}");
item.Proxy.Stop(item.Port);
item.Proxy = null;
}
}
}
catch (Exception ex)
{
Logger.Instance.Error(ex);
catch (Exception ex)
{
Logger.Instance.Error(ex);
}
}
}
}
public bool AddForward(ForwardInfo forwardInfo)
public Dictionary<string, List<ForwardInfo>> Get()
{
return config.Data.Client.Forward.Forwards.GroupBy(c => c.MachineName).ToDictionary((a) => a.Key, (b) => b.ToList());
}
public bool Add(ForwardInfo forwardInfo)
{
//同名或者同端口但是ID不一样
ForwardInfo old = config.Data.Client.Forward.Forwards.FirstOrDefault(c => (c.Port == forwardInfo.Port || c.Name == forwardInfo.Name));
@@ -80,18 +96,18 @@ namespace cmonitor.plugins.forward
config.Data.Client.Forward.Forwards.Add(forwardInfo);
}
config.Save();
StartForward();
Start();
return true;
}
public bool RemoveForward(uint id)
public bool Remove(uint id)
{
//同名或者同端口但是ID不一样
ForwardInfo old = config.Data.Client.Forward.Forwards.FirstOrDefault(c => c.ID == id);
if (old == null) return false;
old.Started = false;
StartForward();
Start();
config.Data.Client.Forward.Forwards.Remove(old);
config.Save();

View File

@@ -2,6 +2,7 @@
using cmonitor.plugins.relay;
using cmonitor.plugins.tunnel;
using common.libs;
using common.libs.extends;
using System.Collections.Concurrent;
using System.Net;

View File

@@ -3,6 +3,7 @@ using cmonitor.client.tunnel;
using cmonitor.config;
using common.libs;
using common.libs.api;
using common.libs.extends;
using System.Text;
namespace cmonitor.plugins.relay
@@ -20,6 +21,13 @@ namespace cmonitor.plugins.relay
RelayTest();
}
public bool SetServers(ApiControllerParamsInfo param)
{
config.Data.Client.Relay.Servers = param.Content.DeJson<RelayCompactInfo[]>();
config.Save();
return true;
}
public void Connect(ApiControllerParamsInfo param)
{
Task.Run(async () =>

View File

@@ -30,7 +30,12 @@ namespace cmonitor.plugins.relay
{
config.Data.Client.Relay.Servers = new RelayCompactInfo[]
{
new RelayCompactInfo{ Name="self", Disabled = false, Host = config.Data.Client.Server }
new RelayCompactInfo{
Name="默认",
Type= RelayCompactType.Self,
Disabled = false,
Host = config.Data.Client.Servers.FirstOrDefault().Host
}
};
}
}

View File

@@ -51,10 +51,10 @@ namespace cmonitor.plugins.relay
public async Task<ITunnelConnection> ConnectAsync(string remoteMachineName, string transactionId)
{
IEnumerable<ITransport> _transports = transports.OrderBy(c => c.Name);
IEnumerable<ITransport> _transports = transports.OrderBy(c => c.Type);
foreach (RelayCompactInfo item in config.Data.Client.Relay.Servers.Where(c => c.Disabled == false))
{
ITransport transport = _transports.FirstOrDefault(c => c.Name == item.Name);
ITransport transport = _transports.FirstOrDefault(c => c.Type == item.Type);
if (transport == null)
{
continue;

View File

@@ -22,8 +22,14 @@
public sealed class RelayCompactInfo
{
public string Name { get; set; }
public RelayCompactType Type { get; set; } = RelayCompactType.Self;
public string SecretKey { get; set; } = "snltty";
public string Host { get; set; }
public bool Disabled { get; set; }
}
public enum RelayCompactType : byte
{
Self = 0,
}
}

View File

@@ -1,4 +1,5 @@
using cmonitor.client.tunnel;
using cmonitor.config;
using MemoryPack;
using System.Net;
@@ -7,6 +8,7 @@ namespace cmonitor.plugins.relay.transport
public interface ITransport
{
public string Name { get; }
public RelayCompactType Type { get; }
public TunnelProtocolType ProtocolType { get; }
public Task<ITunnelConnection> RelayAsync(RelayInfo relayInfo);

View File

@@ -1,4 +1,5 @@
using cmonitor.client.tunnel;
using cmonitor.config;
using cmonitor.plugins.relay.messenger;
using cmonitor.server;
using common.libs;
@@ -11,7 +12,8 @@ namespace cmonitor.plugins.relay.transport
{
public sealed class TransportSelfHost : ITransport
{
public string Name => "self";
public string Name => "默认";
public RelayCompactType Type => RelayCompactType.Self;
public TunnelProtocolType ProtocolType => TunnelProtocolType.Tcp;
private readonly TcpServer tcpServer;

View File

@@ -61,15 +61,15 @@ namespace cmonitor.plugins.signin
ConfigSetInfo info = param.Content.DeJson<ConfigSetInfo>();
config.Data.Client.Name = info.Name;
config.Data.Client.GroupId = info.GroupId;
config.Data.Client.Server = info.Server;
config.Save();
clientSignInTransfer.SignOut();
_ = clientSignInTransfer.SignIn();
}
public void SetServers(ApiControllerParamsInfo param)
public bool SetServers(ApiControllerParamsInfo param)
{
config.Data.Client.Servers = param.Content.DeJson<ClientServerInfo[]>();
config.Save();
return true;
}
public ClientSignInState Info(ApiControllerParamsInfo param)
@@ -107,6 +107,5 @@ namespace cmonitor.plugins.signin
{
public string Name { get; set; }
public string GroupId { get; set; }
public string Server { get; set; }
}
}

View File

@@ -1,7 +1,9 @@
using cmonitor.client.capi;
using cmonitor.client.tunnel;
using cmonitor.config;
using common.libs;
using common.libs.api;
using common.libs.extends;
using System.Text;
namespace cmonitor.plugins.tunnel
@@ -9,14 +11,21 @@ namespace cmonitor.plugins.tunnel
public sealed class TunnelApiController : IApiClientController
{
private readonly TunnelTransfer tunnelTransfer;
public TunnelApiController(TunnelTransfer tunnelTransfer)
private readonly Config config;
public TunnelApiController(TunnelTransfer tunnelTransfer, Config config)
{
this.tunnelTransfer = tunnelTransfer;
this.config = config;
TunnelTest();
}
public bool SetServers(ApiControllerParamsInfo param)
{
config.Data.Client.Tunnel.Servers = param.Content.DeJson<TunnelCompactInfo[]>();
config.Save();
return true;
}
public void Connect(ApiControllerParamsInfo param)
{
Task.Run(async () =>

View File

@@ -46,7 +46,12 @@ namespace cmonitor.plugins.tunnel
{
config.Data.Client.Tunnel.Servers = new TunnelCompactInfo[]
{
new TunnelCompactInfo{ Name="self", Disabled = false, Host = config.Data.Client.Server }
new TunnelCompactInfo{
Name="默认",
Type= TunnelCompactType.Self,
Disabled = false,
Host = config.Data.Client.Servers.FirstOrDefault().Host
}
};
}
}

View File

@@ -1,4 +1,5 @@
using cmonitor.plugins.tunnel.messenger;
using cmonitor.config;
using cmonitor.plugins.tunnel.messenger;
using cmonitor.server;
using common.libs;
using common.libs.extends;
@@ -12,6 +13,8 @@ namespace cmonitor.plugins.tunnel.compact
{
public string Name => "self";
public TunnelCompactType Type => TunnelCompactType.Self;
private readonly TcpServer tcpServer;
private readonly MessengerSender messengerSender;

View File

@@ -4,7 +4,6 @@ using common.libs;
using Microsoft.Extensions.DependencyInjection;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
namespace cmonitor.plugins.tunnel.compact
@@ -37,7 +36,7 @@ namespace cmonitor.plugins.tunnel.compact
{
TunnelCompactInfo item = config.Data.Client.Tunnel.Servers[i];
if (item.Disabled) continue;
ICompact compact = compacts.FirstOrDefault(c => c.Name == item.Name);
ICompact compact = compacts.FirstOrDefault(c => c.Type == item.Type);
if (compact == null) continue;
try

View File

@@ -1,10 +1,12 @@
using System.Net;
using cmonitor.config;
using System.Net;
namespace cmonitor.plugins.tunnel.compact
{
public interface ICompact
{
public string Name { get; }
public TunnelCompactType Type { get; }
public Task<TunnelCompactIPEndPoint> GetTcpExternalIPAsync(IPEndPoint server);
public Task<TunnelCompactIPEndPoint> GetUdpExternalIPAsync(IPEndPoint server);
}

View File

@@ -21,7 +21,13 @@ namespace cmonitor.config
public sealed class TunnelCompactInfo
{
public string Name { get; set; }
public TunnelCompactType Type { get; set; }
public string Host { get; set; }
public bool Disabled { get; set; }
}
public enum TunnelCompactType : byte
{
Self = 0
}
}

View File

@@ -1,4 +1,5 @@
using cmonitor.client.tunnel;
using cmonitor.config;
using MemoryPack;
using System.Net;

View File

@@ -1,4 +1,5 @@
using cmonitor.client.tunnel;
using cmonitor.config;
using cmonitor.plugins.tunnel.server;
using common.libs;
using common.libs.extends;
@@ -18,6 +19,8 @@ namespace cmonitor.plugins.tunnel.transport
public Func<TunnelTransportInfo, Task> OnSendConnectSuccess { get; set; } = async (info) => { await Task.CompletedTask; };
public Action<ITunnelConnection> OnConnected { get; set; } = (state) => { };
private readonly TunnelBindServer tunnelBindServer;
public TransportTcpNutssb(TunnelBindServer tunnelBindServer)
{
@@ -278,6 +281,6 @@ namespace cmonitor.plugins.tunnel.transport
return ip.AddressFamily == AddressFamily.InterNetworkV6 && (NetworkHelper.IPv6Support == false);
}
}
}

View File

@@ -9,7 +9,7 @@ namespace cmonitor.startup
/// <summary>
/// 插件名
/// </summary>
public string Name { get; }
public string Name { get; }
/// <summary>
/// 必须的
/// </summary>
@@ -48,6 +48,8 @@ namespace cmonitor.startup
public enum StartupLevel
{
Bottom = int.MinValue,
Low9 = -9,
Low8 = -8,
Low7 = -7,

View File

@@ -0,0 +1,18 @@
using System.Threading;
namespace common.libs
{
public static class BooleanHelper
{
public static bool CompareExchange(ref bool location, bool value, bool comparand)
{
bool result = location;
if(location == comparand)
{
location = value;
}
return result;
}
}
}