mirror of
https://github.com/snltty/linker.git
synced 2025-10-09 19:10:24 +08:00
sync
This commit is contained in:
@@ -12,6 +12,8 @@ sidebar_position: 3
|
|||||||
4. 在`powershell`尝试运行`2`命令,如果没有报错,运行`3`命令删除,你可以使用NetNat
|
4. 在`powershell`尝试运行`2`命令,如果没有报错,运行`3`命令删除,你可以使用NetNat
|
||||||
5. 如果报错,那你需要打开`Hyper-V`功能, `控制面板\程序\启用或关闭 Windows 功能`
|
5. 如果报错,那你需要打开`Hyper-V`功能, `控制面板\程序\启用或关闭 Windows 功能`
|
||||||
6. 确认支持即可,剩下的交给linker
|
6. 确认支持即可,剩下的交给linker
|
||||||
:::
|
|
||||||
|
|
||||||

|

|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
@@ -6,9 +6,10 @@ sidebar_position: 2
|
|||||||
|
|
||||||
:::danger[重要]
|
:::danger[重要]
|
||||||
1. 你要确定你知道你在搞什么,如果只是简单的P2P通信,上面的内容已经够了,以下的东西不要看,不要看,不要看
|
1. 你要确定你知道你在搞什么,如果只是简单的P2P通信,上面的内容已经够了,以下的东西不要看,不要看,不要看
|
||||||
2. 确定要搞,那么请看示意图
|
2. 确定要搞,那么请看示意图(仅示例,以下绿色内容与此图无关)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
:::tip[1、情况1,你的设备支持NAT转发时]
|
:::tip[1、情况1,你的设备支持NAT转发时]
|
||||||
@@ -28,7 +29,7 @@ nat on en0 from 10.18.18.0/24 to any -> (en0)
|
|||||||
sudo pfctl -f /etc/pf.conf -e
|
sudo pfctl -f /etc/pf.conf -e
|
||||||
```
|
```
|
||||||
|
|
||||||
这里我`公司-linux`的局域网IP为`192.168.1.18`,配置完成后,即可访问其所在局域网`192.168.1.18/24`的所有设备
|
这里我`公司-linux`的局域网IP为`192.168.1.18`,如图配置,`其它客户端`即可通过`192.168.1.18/24`访问`公司-linux`的所有设备
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||
|
@@ -21,6 +21,7 @@ namespace linker.gen
|
|||||||
new GeneratorInfo{ ClassName="StartupTransfer", ClassNameSpace="linker.startup", InterfaceName="linker.startup.IStartup", Instance=true },
|
new GeneratorInfo{ ClassName="StartupTransfer", ClassNameSpace="linker.startup", InterfaceName="linker.startup.IStartup", Instance=true },
|
||||||
new GeneratorInfo{ ClassName="MessengerResolverTypes", ClassNameSpace="linker.plugins.messenger", InterfaceName="linker.plugins.messenger.IMessenger"},
|
new GeneratorInfo{ ClassName="MessengerResolverTypes", ClassNameSpace="linker.plugins.messenger", InterfaceName="linker.plugins.messenger.IMessenger"},
|
||||||
new GeneratorInfo{ ClassName="ApiClientServer", ClassNameSpace="linker.plugins.capi", InterfaceName="linker.plugins.capi.IApiClientController"},
|
new GeneratorInfo{ ClassName="ApiClientServer", ClassNameSpace="linker.plugins.capi", InterfaceName="linker.plugins.capi.IApiClientController"},
|
||||||
|
new GeneratorInfo{ ClassName="ConfigSyncTreansfer", ClassNameSpace="linker.plugins.config", InterfaceName="linker.plugins.config.IConfigSync"},
|
||||||
};
|
};
|
||||||
|
|
||||||
public void Initialize(IncrementalGeneratorInitializationContext context)
|
public void Initialize(IncrementalGeneratorInitializationContext context)
|
||||||
|
@@ -21,13 +21,6 @@ namespace linker.libs
|
|||||||
await Task.Delay(-1, cancellationTokenSource.Token).ConfigureAwait(false);
|
await Task.Delay(-1, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static DateTime startTime = new DateTime(1970, 1, 1);
|
|
||||||
public static long Timestamp()
|
|
||||||
{
|
|
||||||
return (long)(DateTime.UtcNow.Subtract(startTime)).TotalMilliseconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private delegate bool ConsoleCtrlDelegate(int ctrlType);
|
private delegate bool ConsoleCtrlDelegate(int ctrlType);
|
||||||
private static ConsoleCtrlDelegate handler;
|
private static ConsoleCtrlDelegate handler;
|
||||||
[DllImport("Kernel32")]
|
[DllImport("Kernel32")]
|
||||||
|
@@ -107,7 +107,7 @@ namespace linker.libs
|
|||||||
for (ushort i = 1; i <= 5; i++)
|
for (ushort i = 1; i <= 5; i++)
|
||||||
{
|
{
|
||||||
using Ping pinger = new();
|
using Ping pinger = new();
|
||||||
PingReply reply = pinger.Send(target, 100, Encoding.ASCII.GetBytes("snltty"), new PingOptions { Ttl = i, DontFragment = true });
|
PingReply reply = pinger.Send(target, 100, Encoding.ASCII.GetBytes(Helper.GlobalString), new PingOptions { Ttl = i, DontFragment = true });
|
||||||
|
|
||||||
if (starts.Any(c => reply.Address.ToString().StartsWith(c)))
|
if (starts.Any(c => reply.Address.ToString().StartsWith(c)))
|
||||||
{
|
{
|
||||||
|
@@ -4,6 +4,7 @@ import { sendWebsocketMsg } from './request'
|
|||||||
export const setSignIn = (data) => {
|
export const setSignIn = (data) => {
|
||||||
return sendWebsocketMsg('signInclient/set', data);
|
return sendWebsocketMsg('signInclient/set', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setSignInServers = (servers) => {
|
export const setSignInServers = (servers) => {
|
||||||
return sendWebsocketMsg('signInclient/setservers', servers);
|
return sendWebsocketMsg('signInclient/setservers', servers);
|
||||||
}
|
}
|
||||||
@@ -27,3 +28,6 @@ export const signInDel = (machineId) => {
|
|||||||
export const setSignInName = (data) => {
|
export const setSignInName = (data) => {
|
||||||
return sendWebsocketMsg('signInclient/setname', data);
|
return sendWebsocketMsg('signInclient/setname', data);
|
||||||
}
|
}
|
||||||
|
export const setSignInGroups = (data) => {
|
||||||
|
return sendWebsocketMsg('signInclient/SetGroups', data);
|
||||||
|
}
|
@@ -27,6 +27,7 @@
|
|||||||
<TunnelEdit v-if="tunnel.showEdit" v-model="tunnel.showEdit" @change="handleTunnelRefresh"></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="handleTuntapRefresh"></TuntapEdit>
|
<TuntapEdit v-if="tuntap.showEdit" v-model="tuntap.showEdit" @change="handleTuntapRefresh"></TuntapEdit>
|
||||||
|
<TuntapDHCP v-if="tuntap.showDHCP" v-model="tuntap.showDHCP" @change="handleTuntapRefresh"></TuntapDHCP>
|
||||||
<ForwardEdit v-if="forward.showEdit" v-model="forward.showEdit" ></ForwardEdit>
|
<ForwardEdit v-if="forward.showEdit" v-model="forward.showEdit" ></ForwardEdit>
|
||||||
<ForwardCopy v-if="forward.showCopy" v-model="forward.showCopy" ></ForwardCopy>
|
<ForwardCopy v-if="forward.showCopy" v-model="forward.showCopy" ></ForwardCopy>
|
||||||
<SForwardEdit v-if="sforward.showEdit" v-model="sforward.showEdit" ></SForwardEdit>
|
<SForwardEdit v-if="sforward.showEdit" v-model="sforward.showEdit" ></SForwardEdit>
|
||||||
@@ -42,6 +43,7 @@ import DeviceEdit from './DeviceEdit.vue'
|
|||||||
import AccessEdit from './AccessEdit.vue'
|
import AccessEdit from './AccessEdit.vue'
|
||||||
import Tuntap from './Tuntap.vue'
|
import Tuntap from './Tuntap.vue'
|
||||||
import TuntapEdit from './TuntapEdit.vue'
|
import TuntapEdit from './TuntapEdit.vue'
|
||||||
|
import TuntapDHCP from './TuntapDHCP.vue'
|
||||||
import Tunnel from './Tunnel.vue'
|
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'
|
||||||
@@ -60,7 +62,7 @@ import { provideDevices } from './devices'
|
|||||||
import { provideUpdater } from './updater'
|
import { provideUpdater } from './updater'
|
||||||
import { provideAccess } from './access'
|
import { provideAccess } from './access'
|
||||||
export default {
|
export default {
|
||||||
components: {Oper,Device,DeviceEdit,AccessEdit,Tunnel,TunnelEdit,ConnectionsEdit, Tuntap,TuntapEdit, Forward,ForwardEdit,ForwardCopy,SForwardEdit,SForwardCopy },
|
components: {Oper,Device,DeviceEdit,AccessEdit,Tunnel,TunnelEdit,ConnectionsEdit, Tuntap,TuntapEdit,TuntapDHCP, Forward,ForwardEdit,ForwardCopy,SForwardEdit,SForwardCopy },
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
|
||||||
const globalData = injectGlobalData();
|
const globalData = injectGlobalData();
|
||||||
|
@@ -1,5 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-table-column prop="tuntap" label="虚拟网卡" width="160">
|
<el-table-column prop="tuntap" label="虚拟网卡" width="160">
|
||||||
|
<template #header>
|
||||||
|
<a href="javascript:;" class="a-line">虚拟网卡</a>
|
||||||
|
</template>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<div v-if="tuntap.list[scope.row.MachineId]">
|
<div v-if="tuntap.list[scope.row.MachineId]">
|
||||||
<TuntapShow :config="true" :item="scope.row" @edit="handleTuntapIP" @refresh="handleTuntapRefresh"></TuntapShow>
|
<TuntapShow :config="true" :item="scope.row" @edit="handleTuntapIP" @refresh="handleTuntapRefresh"></TuntapShow>
|
||||||
|
71
linker.web/src/views/full/devices/TuntapDHCP.vue
Normal file
71
linker.web/src/views/full/devices/TuntapDHCP.vue
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog v-model="state.show" :close-on-click-modal="false" append-to=".app-wrap" title="配置本组的网络" top="1vh" width="700">
|
||||||
|
<div>
|
||||||
|
<el-form ref="ruleFormRef" :model="state.ruleForm" :rules="state.rules" label-width="140">
|
||||||
|
<el-form-item prop="gateway" style="margin-bottom:0">
|
||||||
|
赐予此设备IP,其它设备可通过此IP访问
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="此设备的虚拟网卡IP" prop="IP">
|
||||||
|
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="" prop="Btns">
|
||||||
|
<div>
|
||||||
|
<el-button @click="state.show = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSave">确认</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import {updateTuntap } from '@/apis/tuntap';
|
||||||
|
import { injectGlobalData } from '@/provide';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { reactive, ref, watch } from 'vue';
|
||||||
|
import { useTuntap } from './tuntap';
|
||||||
|
import { Delete, Plus } from '@element-plus/icons-vue'
|
||||||
|
export default {
|
||||||
|
props: ['modelValue'],
|
||||||
|
emits: ['change','update:modelValue'],
|
||||||
|
components: {Delete,Plus},
|
||||||
|
setup(props, { emit }) {
|
||||||
|
|
||||||
|
const globalData = injectGlobalData();
|
||||||
|
const tuntap = useTuntap();
|
||||||
|
const ruleFormRef = ref(null);
|
||||||
|
const state = reactive({
|
||||||
|
show: true,
|
||||||
|
ruleForm: {
|
||||||
|
},
|
||||||
|
rules: {}
|
||||||
|
});
|
||||||
|
watch(() => state.show, (val) => {
|
||||||
|
if (!val) {
|
||||||
|
setTimeout(() => {
|
||||||
|
emit('update:modelValue', val);
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
const json = JSON.parse(JSON.stringify(tuntap.value.current));
|
||||||
|
updateTuntap(json).then(() => {
|
||||||
|
state.show = false;
|
||||||
|
ElMessage.success('已操作!');
|
||||||
|
emit('change')
|
||||||
|
}).catch(() => {
|
||||||
|
ElMessage.error('操作失败!');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
state, ruleFormRef, handleSave
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.el-switch.is-disabled{opacity :1;}
|
||||||
|
</style>
|
@@ -11,7 +11,9 @@ export const provideTuntap = () => {
|
|||||||
showEdit: false,
|
showEdit: false,
|
||||||
current: null,
|
current: null,
|
||||||
list: {},
|
list: {},
|
||||||
hashcode: 0
|
hashcode: 0,
|
||||||
|
|
||||||
|
showDHCP: false,
|
||||||
});
|
});
|
||||||
provide(tuntapSymbol, tuntap);
|
provide(tuntapSymbol, tuntap);
|
||||||
|
|
||||||
|
@@ -14,8 +14,8 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="分组名" prop="groupid">
|
<el-form-item label="网页端口" prop="web">
|
||||||
<el-input v-model="state.form.groupid" maxlength="36" show-word-limit />
|
<el-input style="width:44.5rem;" v-model="state.form.web" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -28,8 +28,8 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="网页端口" prop="web">
|
<el-form-item label="接口密码" prop="password">
|
||||||
<el-input v-model="state.form.web" />
|
<el-input type="password" v-model="state.form.password" show-password maxlength="36" show-word-limit/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -37,13 +37,22 @@
|
|||||||
<el-form-item label="" label-width="0">
|
<el-form-item label="" label-width="0">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="接口密码" prop="password">
|
<el-form-item label="分组名" prop="groupid">
|
||||||
<el-input style="width:42rem" type="password" v-model="state.form.password" show-password maxlength="36" show-word-limit/>
|
<el-input v-model="state.form.groupid" maxlength="36" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
|
<el-form-item label="分组密码" prop="groupPassword">
|
||||||
|
<el-input v-model="state.form.groupPassword" type="password" show-password maxlength="36" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="" label-width="0">
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="24">
|
||||||
<el-form-item label-width="8rem" prop="hasServer">
|
<el-form-item label-width="8rem" prop="hasServer">
|
||||||
<el-checkbox v-model="state.form.hasServer" label="我有服务器" size="large" />
|
<el-checkbox v-model="state.form.hasServer" label="我有服务器(私有部署服务端,使用自己的服务器)" size="large" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -107,7 +116,8 @@ export default {
|
|||||||
const state = reactive({
|
const state = reactive({
|
||||||
form: {
|
form: {
|
||||||
name:step.value.form.client.name || globalData.value.config.Client.Name,
|
name:step.value.form.client.name || globalData.value.config.Client.Name,
|
||||||
groupid: step.value.form.client.groupid ||globalData.value.config.Client.GroupId,
|
groupid: step.value.form.client.groupid ||globalData.value.config.Client.Group.Id,
|
||||||
|
groupPassword: step.value.form.client.groupPassword ||globalData.value.config.Client.GroupPassword,
|
||||||
api: step.value.form.client.api ||globalData.value.config.Client.CApi.ApiPort,
|
api: step.value.form.client.api ||globalData.value.config.Client.CApi.ApiPort,
|
||||||
web: step.value.form.client.web ||globalData.value.config.Client.CApi.WebPort,
|
web: step.value.form.client.web ||globalData.value.config.Client.CApi.WebPort,
|
||||||
password:step.value.form.client.password || globalData.value.config.Client.CApi.ApiPassword,
|
password:step.value.form.client.password || globalData.value.config.Client.CApi.ApiPassword,
|
||||||
@@ -122,6 +132,7 @@ export default {
|
|||||||
rules: {
|
rules: {
|
||||||
name: [{ required: true, message: "必填", trigger: "blur" }],
|
name: [{ required: true, message: "必填", trigger: "blur" }],
|
||||||
groupid: [{ required: true, message: "必填", trigger: "blur" }],
|
groupid: [{ required: true, message: "必填", trigger: "blur" }],
|
||||||
|
groupPassword: [{ required: true, message: "必填", trigger: "blur" }],
|
||||||
password: [{ required: true, message: "必填", trigger: "blur" }],
|
password: [{ required: true, message: "必填", trigger: "blur" }],
|
||||||
api: [
|
api: [
|
||||||
{ required: true, message: "必填", trigger: "blur" },
|
{ required: true, message: "必填", trigger: "blur" },
|
||||||
@@ -161,6 +172,7 @@ export default {
|
|||||||
Client:{
|
Client:{
|
||||||
name: state.form.name,
|
name: state.form.name,
|
||||||
groupid: state.form.groupid,
|
groupid: state.form.groupid,
|
||||||
|
groupPassword: state.form.groupPassword,
|
||||||
api: +state.form.api,
|
api: +state.form.api,
|
||||||
web: +state.form.web,
|
web: +state.form.web,
|
||||||
password: state.form.password,
|
password: state.form.password,
|
||||||
|
@@ -32,15 +32,6 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="" label-width="0">
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="24">
|
|
||||||
<el-form-item label="信标密钥" prop="signinSecretKey">
|
|
||||||
<el-input v-model="state.form.signinSecretKey" maxlength="36" show-word-limit />
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="" label-width="0">
|
<el-form-item label="" label-width="0">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
@@ -57,7 +48,12 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="" label-width="0">
|
<el-form-item label="" label-width="0">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="24">
|
<el-col :span="12">
|
||||||
|
<el-form-item label="信标密钥" prop="signinSecretKey">
|
||||||
|
<el-input v-model="state.form.signinSecretKey" maxlength="36" show-word-limit />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
<el-form-item label="更新密钥" prop="updaterSecretKey">
|
<el-form-item label="更新密钥" prop="updaterSecretKey">
|
||||||
<el-input v-model="state.form.updaterSecretKey" maxlength="36" show-word-limit />
|
<el-input v-model="state.form.updaterSecretKey" maxlength="36" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
118
linker.web/src/views/full/server/Groups.vue
Normal file
118
linker.web/src/views/full/server/Groups.vue
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
<template>
|
||||||
|
<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">
|
||||||
|
<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="Id" label="Id" >
|
||||||
|
<template #default="scope">
|
||||||
|
<template v-if="scope.row.IdEditing">
|
||||||
|
<el-input autofocus size="small" v-model="scope.row.Id"
|
||||||
|
@blur="handleEditBlur(scope.row, 'Id')"></el-input>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
{{ scope.row.Id }}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="Password" label="密码" >
|
||||||
|
<template #default="scope">
|
||||||
|
<template v-if="scope.row.PasswordEditing">
|
||||||
|
<el-input type="password" show-password size="small" v-model="scope.row.Password" @blur="handleEditBlur(scope.row, 'Password')"></el-input>
|
||||||
|
</template>
|
||||||
|
<template v-else></template>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="Oper" label="操作" width="110">
|
||||||
|
<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 { setSignInGroups } from '@/apis/signin';
|
||||||
|
import { injectGlobalData } from '@/provide';
|
||||||
|
import { ElMessage } from 'element-plus';
|
||||||
|
import { computed, reactive, watch } from 'vue'
|
||||||
|
import { Delete,Plus,Select } from '@element-plus/icons-vue';
|
||||||
|
export default {
|
||||||
|
label:'分组',
|
||||||
|
name:'signInGroups',
|
||||||
|
order:2,
|
||||||
|
components:{Delete,Plus,Select },
|
||||||
|
setup(props) {
|
||||||
|
const globalData = injectGlobalData();
|
||||||
|
const state = reactive({
|
||||||
|
list:globalData.value.config.Client.Groups || [],
|
||||||
|
height: computed(()=>globalData.value.height-90),
|
||||||
|
});
|
||||||
|
watch(()=>globalData.value.config.Client.Groups,()=>{
|
||||||
|
if(state.list.filter(c=>c['__editing']).length == 0){
|
||||||
|
state.list = globalData.value.config.Client.Groups;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleCellClick = (row, column) => {
|
||||||
|
handleEdit(row, column.property);
|
||||||
|
}
|
||||||
|
const handleEdit = (row, p) => {
|
||||||
|
state.list.forEach(c => {
|
||||||
|
c[`NameEditing`] = false;
|
||||||
|
c[`IdEditing`] = false;
|
||||||
|
c[`PasswordEditing`] = false;
|
||||||
|
})
|
||||||
|
row[`${p}Editing`] = true;
|
||||||
|
row[`__editing`] = true;
|
||||||
|
}
|
||||||
|
const handleEditBlur = (row, p) => {
|
||||||
|
row[`${p}Editing`] = false;
|
||||||
|
row[`__editing`] = false;
|
||||||
|
handleSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDel = (index)=>{
|
||||||
|
state.list.splice(index,1);
|
||||||
|
handleSave();
|
||||||
|
}
|
||||||
|
const handleAdd = (index)=>{
|
||||||
|
if(state.list.filter(c=>c.Id == '' || c.Name == '').length > 0){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.list.splice(index+1,0,{Name:'',Id:'',Password:''});
|
||||||
|
handleSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSave = ()=>{
|
||||||
|
setSignInGroups(state.list).then(()=>{
|
||||||
|
ElMessage.success('已操作');
|
||||||
|
}).catch(()=>{
|
||||||
|
ElMessage.error('操作失败');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {state,handleCellClick,handleEditBlur,handleDel,handleAdd}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
|
||||||
|
</style>
|
@@ -16,12 +16,19 @@ export default {
|
|||||||
components:{},
|
components:{},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
|
||||||
const excludes = ['./Index.vue','./Version.vue','./TunnelServers.vue']
|
const globalData = injectGlobalData();
|
||||||
|
const hasConfig = computed(()=>globalData.value.hasAccess('Config'))
|
||||||
|
const hasSync = computed(()=>globalData.value.hasAccess('Sync'));
|
||||||
|
|
||||||
|
const excludes = ['./Index.vue','./Version.vue','./TunnelServers.vue'];
|
||||||
|
if(hasSync.value == false){
|
||||||
|
excludes.push('./Async.vue');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const files = require.context('./', true, /.+\.vue/);
|
const files = require.context('./', true, /.+\.vue/);
|
||||||
const settingComponents = files.keys().filter(c=>excludes.includes(c)==false).map(c => files(c).default).sort((a,b)=>a.order-b.order);
|
const settingComponents = files.keys().filter(c=>excludes.includes(c)==false).map(c => files(c).default).sort((a,b)=>a.order-b.order);
|
||||||
const globalData = injectGlobalData();
|
|
||||||
const hasConfig = computed(()=>globalData.value.hasAccess('Config'))
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
tab:settingComponents[0].name
|
tab:settingComponents[0].name
|
||||||
});
|
});
|
||||||
|
@@ -86,7 +86,7 @@ import { Delete,Plus,Top,Bottom } from '@element-plus/icons-vue';
|
|||||||
export default {
|
export default {
|
||||||
label:'中继服务器',
|
label:'中继服务器',
|
||||||
name:'relayServers',
|
name:'relayServers',
|
||||||
order:4,
|
order:2,
|
||||||
components:{Delete,Plus,Top,Bottom},
|
components:{Delete,Plus,Top,Bottom},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const globalData = injectGlobalData();
|
const globalData = injectGlobalData();
|
||||||
|
@@ -16,7 +16,7 @@ import {onMounted, reactive } from 'vue'
|
|||||||
export default {
|
export default {
|
||||||
label:'服务器穿透',
|
label:'服务器穿透',
|
||||||
name:'sforward',
|
name:'sforward',
|
||||||
order:5,
|
order:3,
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const globalData = injectGlobalData();
|
const globalData = injectGlobalData();
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
|
@@ -16,7 +16,7 @@ import { computed, inject, onMounted, reactive } from 'vue'
|
|||||||
export default {
|
export default {
|
||||||
label:'服务器更新',
|
label:'服务器更新',
|
||||||
name:'updater',
|
name:'updater',
|
||||||
order:6,
|
order:4,
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const globalData = injectGlobalData();
|
const globalData = injectGlobalData();
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
|
@@ -9,7 +9,9 @@
|
|||||||
<el-input v-model="state.form.name" maxlength="12" show-word-limit />
|
<el-input v-model="state.form.name" maxlength="12" show-word-limit />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="分组名" prop="groupid">
|
<el-form-item label="分组名" prop="groupid">
|
||||||
<el-input v-model="state.form.groupid" type="password" show-password maxlength="36" show-word-limit />
|
<el-select v-model="state.groupid" @change="handleGroupChange">
|
||||||
|
<el-option v-for="item in state.form.groups" :key="item.Id" :label="item.Name" :value="item.Id"/>
|
||||||
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</div>
|
</div>
|
||||||
@@ -26,9 +28,9 @@ import { setSignIn } from '@/apis/signin';
|
|||||||
import { injectGlobalData } from '@/provide';
|
import { injectGlobalData } from '@/provide';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { computed, reactive, ref } from 'vue';
|
import { computed, reactive, ref } from 'vue';
|
||||||
import {Promotion} from '@element-plus/icons-vue'
|
import {Promotion,CirclePlus} from '@element-plus/icons-vue'
|
||||||
export default {
|
export default {
|
||||||
components:{Promotion},
|
components:{Promotion,CirclePlus},
|
||||||
props:['config'],
|
props:['config'],
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
|
||||||
@@ -38,9 +40,10 @@ export default {
|
|||||||
show: false,
|
show: false,
|
||||||
loading: false,
|
loading: false,
|
||||||
connected: computed(() => globalData.value.signin.Connected),
|
connected: computed(() => globalData.value.signin.Connected),
|
||||||
|
groupid: globalData.value.config.Client.Group.Id,
|
||||||
form: {
|
form: {
|
||||||
name: globalData.value.config.Client.Name,
|
name: globalData.value.config.Client.Name,
|
||||||
groupid: globalData.value.config.Client.GroupId,
|
groups: globalData.value.config.Client.Groups,
|
||||||
},
|
},
|
||||||
rules: {},
|
rules: {},
|
||||||
});
|
});
|
||||||
@@ -50,9 +53,22 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
state.form.name = globalData.value.config.Client.Name;
|
state.form.name = globalData.value.config.Client.Name;
|
||||||
state.form.groupid = globalData.value.config.Client.GroupId;
|
state.form.groups = globalData.value.config.Client.Groups;
|
||||||
|
|
||||||
|
state.groupid = globalData.value.config.Client.Group.Id;
|
||||||
state.show = true;
|
state.show = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleGroupChange = (value)=>{
|
||||||
|
const index = state.form.groups.map((item,index)=>{
|
||||||
|
item.$index = index;
|
||||||
|
return item;
|
||||||
|
}).filter(c=>c.Id == value)[0].$index;
|
||||||
|
const temp = state.form.groups[index];
|
||||||
|
state.form.groups[index] = state.form.groups[0];
|
||||||
|
state.form.groups[0] = temp;
|
||||||
|
console.log(state.form.groups);
|
||||||
|
}
|
||||||
const handleSave = () => {
|
const handleSave = () => {
|
||||||
state.loading = true;
|
state.loading = true;
|
||||||
setSignIn(state.form).then(() => {
|
setSignIn(state.form).then(() => {
|
||||||
@@ -68,7 +84,7 @@ export default {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
config:props.config, state, handleConfig, handleSave
|
config:props.config, state, handleConfig, handleSave,handleGroupChange
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<a v-if="config" href="javascript:;" title="linker服务端网速,点击查看详细信息" @click="handleShow">
|
<div class="flow-wrap" v-if="config">
|
||||||
<p>在线 {{state.overallOnline}}、{{ state.serverOnline }}</p>
|
<p>在线 <a href="javascript:;" :title="`本服务器\r\n在线数/7天内上线数`">{{state.overallOnline}}</a><a href="javascript:;" :title="`所有服务器\r\n在线数/7天内上线数/服务端数`">{{ state.serverOnline }}</a></p>
|
||||||
<p>上传 {{state.overallSendtSpeed}}/s</p>
|
<p>上传 <a href="javascript:;" :title="`本服务器\r\n发送速率`" @click="handleShow">{{state.overallSendtSpeed}}/s</a></p>
|
||||||
<p>下载 {{state.overallReceiveSpeed}}/s</p>
|
<p>下载 <a href="javascript:;" :title="`本服务器\r\n接收速率`" @click="handleShow">{{state.overallReceiveSpeed}}/s</a></p>
|
||||||
</a>
|
</div>
|
||||||
<el-dialog :title="state.time" destroy-on-close v-model="state.show" width="540">
|
<el-dialog :title="state.time" destroy-on-close v-model="state.show" width="540">
|
||||||
<div>
|
<div>
|
||||||
<el-table :data="state.list" border size="small" width="100%">
|
<el-table :data="state.list" border size="small" width="100%">
|
||||||
@@ -63,7 +63,7 @@ export default {
|
|||||||
overallSendtSpeed: '0000.00KB',
|
overallSendtSpeed: '0000.00KB',
|
||||||
overallReceiveSpeed: '0000.00KB',
|
overallReceiveSpeed: '0000.00KB',
|
||||||
overallOnline: '0/0',
|
overallOnline: '0/0',
|
||||||
serverOnline: '0/0/0',
|
serverOnline: '',
|
||||||
time:'',
|
time:'',
|
||||||
list:[],
|
list:[],
|
||||||
old:null,
|
old:null,
|
||||||
@@ -93,11 +93,11 @@ export default {
|
|||||||
state.overallOnline = `${res.Items['_'].SendtBytes}/${res.Items['_'].ReceiveBytes}`;
|
state.overallOnline = `${res.Items['_'].SendtBytes}/${res.Items['_'].ReceiveBytes}`;
|
||||||
delete res.Items['_'];
|
delete res.Items['_'];
|
||||||
}
|
}
|
||||||
if(res.Items['flow']){
|
if(res.Items['flow'] && res.Items['flow'].ReceiveBytes>0){
|
||||||
const online = (BigInt(res.Items['flow'].ReceiveBytes) >> BigInt(32)).toString();
|
const online = (BigInt(res.Items['flow'].ReceiveBytes) >> BigInt(32)).toString();
|
||||||
const total = (BigInt(res.Items['flow'].ReceiveBytes) & BigInt(0xffffffff)).toString();
|
const total = (BigInt(res.Items['flow'].ReceiveBytes) & BigInt(0xffffffff)).toString();
|
||||||
const server = res.Items['flow'].SendtBytes;
|
const server = res.Items['flow'].SendtBytes;
|
||||||
state.serverOnline = `${online}/${total}/${server}`;
|
state.serverOnline = `、${online}/${total}/${server}`;
|
||||||
delete res.Items['flow'];
|
delete res.Items['flow'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,14 +171,16 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
a{
|
.flow-wrap{
|
||||||
|
padding:.4rem;
|
||||||
font-weight:bold;position:absolute;right:1rem;bottom:80%;
|
font-weight:bold;position:absolute;right:1rem;bottom:80%;
|
||||||
border:1px solid #ddd;
|
border:1px solid #ddd;
|
||||||
background-color:#fff;
|
background-color:#fff;
|
||||||
z-index :9
|
z-index :9
|
||||||
p{
|
&>a,&>p{
|
||||||
line-height:normal;
|
line-height:normal;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
display:block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@@ -112,7 +112,7 @@ namespace linker.config
|
|||||||
Data.Updated--;
|
Data.Updated--;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}, 1000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -23,9 +23,6 @@ namespace linker.plugins.signin
|
|||||||
public bool SetArgs(ApiControllerParamsInfo param)
|
public bool SetArgs(ApiControllerParamsInfo param)
|
||||||
{
|
{
|
||||||
actionTransfer.SetActionArg(param.Content);
|
actionTransfer.SetActionArg(param.Content);
|
||||||
clientSignInTransfer.SignOut();
|
|
||||||
_ = clientSignInTransfer.SignIn();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,8 +30,6 @@ namespace linker.plugins.signin
|
|||||||
public bool SetServerArgs(ApiControllerParamsInfo param)
|
public bool SetServerArgs(ApiControllerParamsInfo param)
|
||||||
{
|
{
|
||||||
actionTransfer.SetActionArgs(param.Content.DeJson<Dictionary<string, string>>());
|
actionTransfer.SetActionArgs(param.Content.DeJson<Dictionary<string, string>>());
|
||||||
clientSignInTransfer.SignOut();
|
|
||||||
_ = clientSignInTransfer.SignIn();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -8,6 +8,7 @@ using System.Net;
|
|||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using linker.plugins.messenger;
|
using linker.plugins.messenger;
|
||||||
using linker.plugins.signIn.args;
|
using linker.plugins.signIn.args;
|
||||||
|
using linker.plugins.signin;
|
||||||
|
|
||||||
namespace linker.plugins.client
|
namespace linker.plugins.client
|
||||||
{
|
{
|
||||||
@@ -62,7 +63,7 @@ namespace linker.plugins.client
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task SignIn()
|
public async Task SignIn()
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(config.Data.Client.GroupId))
|
if (string.IsNullOrWhiteSpace(config.Data.Client.Group.Id))
|
||||||
{
|
{
|
||||||
LoggerHelper.Instance.Error($"please configure group id");
|
LoggerHelper.Instance.Error($"please configure group id");
|
||||||
return;
|
return;
|
||||||
@@ -149,7 +150,7 @@ namespace linker.plugins.client
|
|||||||
MachineId = config.Data.Client.Id,
|
MachineId = config.Data.Client.Id,
|
||||||
Version = config.Data.Version,
|
Version = config.Data.Version,
|
||||||
Args = args,
|
Args = args,
|
||||||
GroupId = config.Data.Client.GroupId,
|
GroupId = config.Data.Client.Group.Id,
|
||||||
})
|
})
|
||||||
}).ConfigureAwait(false);
|
}).ConfigureAwait(false);
|
||||||
if (resp.Code != MessageResponeCodes.OK)
|
if (resp.Code != MessageResponeCodes.OK)
|
||||||
@@ -207,39 +208,30 @@ namespace linker.plugins.client
|
|||||||
/// 修改客户端名称
|
/// 修改客户端名称
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="newName"></param>
|
/// <param name="newName"></param>
|
||||||
public void SetName(string newName)
|
public void Set(string newName)
|
||||||
{
|
{
|
||||||
string name = config.Data.Client.Name;
|
config.Data.Client.Name = newName;
|
||||||
|
config.Data.Update();
|
||||||
if (name != newName)
|
|
||||||
{
|
|
||||||
config.Data.Client.Name = newName;
|
|
||||||
config.Data.Update();
|
|
||||||
|
|
||||||
SignOut();
|
|
||||||
_ = SignIn();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 修改客户端名称和分组编号
|
/// 修改客户端名称和分组编号
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="newName"></param>
|
/// <param name="newName"></param>
|
||||||
/// <param name="newGroupid"></param>
|
/// <param name="groups"></param>
|
||||||
public void Set(string newName, string newGroupid)
|
public void Set(string newName, ClientGroupInfo[] groups)
|
||||||
{
|
{
|
||||||
string name = config.Data.Client.Name;
|
config.Data.Client.Name = newName;
|
||||||
string gid = config.Data.Client.GroupId;
|
config.Data.Client.Groups = groups.DistinctBy(c => c.Name).ToArray();
|
||||||
|
config.Data.Update();
|
||||||
if (name != newName || gid != newGroupid)
|
}
|
||||||
{
|
/// <summary>
|
||||||
config.Data.Client.Name = newName;
|
/// 设置分组编号
|
||||||
config.Data.Client.GroupId = newGroupid;
|
/// </summary>
|
||||||
config.Data.Update();
|
/// <param name="groups"></param>
|
||||||
SignOut();
|
public void Set(ClientGroupInfo[] groups)
|
||||||
_ = SignIn();
|
{
|
||||||
}
|
config.Data.Client.Groups = groups.DistinctBy(c=>c.Name).ToArray();
|
||||||
|
config.Data.Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -265,7 +257,6 @@ namespace linker.plugins.client
|
|||||||
|
|
||||||
return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray);
|
return resp.Code == MessageResponeCodes.OK && resp.Data.Span.SequenceEqual(Helper.TrueArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取一个新的id
|
/// 获取一个新的id
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -290,22 +281,14 @@ namespace linker.plugins.client
|
|||||||
/// 修改信标服务器列表
|
/// 修改信标服务器列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="servers"></param>
|
/// <param name="servers"></param>
|
||||||
public async Task SetServers(ClientServerInfo[] servers)
|
public void SetServers(ClientServerInfo[] servers)
|
||||||
{
|
{
|
||||||
await SetServersReSignin(servers);
|
SetServersReSignin(servers);
|
||||||
}
|
}
|
||||||
private async Task SetServersReSignin(ClientServerInfo[] servers)
|
private void SetServersReSignin(ClientServerInfo[] servers)
|
||||||
{
|
{
|
||||||
string str = config.Data.Client.ServerInfo.ToStr();
|
|
||||||
|
|
||||||
config.Data.Client.Servers = servers;
|
config.Data.Client.Servers = servers;
|
||||||
config.Data.Update();
|
config.Data.Update();
|
||||||
|
|
||||||
if (str != config.Data.Client.ServerInfo.ToStr())
|
|
||||||
{
|
|
||||||
SignOut();
|
|
||||||
await SignIn();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,11 +24,16 @@ namespace linker.plugins.client
|
|||||||
{
|
{
|
||||||
config.Data.Client.Name = Dns.GetHostName().SubStr(0, 12);
|
config.Data.Client.Name = Dns.GetHostName().SubStr(0, 12);
|
||||||
}
|
}
|
||||||
|
if (config.Data.Client.Groups.Length == 0)
|
||||||
|
{
|
||||||
|
config.Data.Client.Groups = new ClientGroupInfo[] { new ClientGroupInfo { Id = config.Data.Client.GroupId, Name= config.Data.Client.GroupId, Password = string.Empty } };
|
||||||
|
}
|
||||||
|
|
||||||
serviceCollection.AddSingleton<ClientSignInState>();
|
serviceCollection.AddSingleton<ClientSignInState>();
|
||||||
serviceCollection.AddSingleton<ClientSignInTransfer>();
|
serviceCollection.AddSingleton<ClientSignInTransfer>();
|
||||||
|
|
||||||
serviceCollection.AddSingleton<SignInArgsSecretKeyClient>();
|
serviceCollection.AddSingleton<SignInArgsSecretKeyClient>();
|
||||||
|
serviceCollection.AddSingleton<SignInArgsGroupPasswordClient>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +50,7 @@ namespace linker.plugins.client
|
|||||||
public void AddServer(ServiceCollection serviceCollection, FileConfig config)
|
public void AddServer(ServiceCollection serviceCollection, FileConfig config)
|
||||||
{
|
{
|
||||||
serviceCollection.AddSingleton<SignInArgsSecretKeyServer>();
|
serviceCollection.AddSingleton<SignInArgsSecretKeyServer>();
|
||||||
|
serviceCollection.AddSingleton<SignInArgsGroupPasswordServer>();
|
||||||
}
|
}
|
||||||
public void UseServer(ServiceProvider serviceProvider, FileConfig config)
|
public void UseServer(ServiceProvider serviceProvider, FileConfig config)
|
||||||
{
|
{
|
||||||
|
68
linker/plugins/client/SignInArgsGroupPassword.cs
Normal file
68
linker/plugins/client/SignInArgsGroupPassword.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using linker.plugins.signIn.args;
|
||||||
|
using linker.plugins.signin.messenger;
|
||||||
|
using linker.config;
|
||||||
|
|
||||||
|
namespace linker.plugins.client
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 添加分组密码
|
||||||
|
/// </summary>
|
||||||
|
public sealed class SignInArgsGroupPasswordClient : ISignInArgs
|
||||||
|
{
|
||||||
|
private readonly FileConfig fileConfig;
|
||||||
|
public SignInArgsGroupPasswordClient(FileConfig fileConfig)
|
||||||
|
{
|
||||||
|
this.fileConfig = fileConfig;
|
||||||
|
}
|
||||||
|
public async Task<string> Invoke(string host, Dictionary<string, string> args)
|
||||||
|
{
|
||||||
|
args.TryAdd("signin-gpwd", fileConfig.Data.Client.Group.Password);
|
||||||
|
await Task.CompletedTask;
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<string> Validate(SignInfo signInfo, SignCacheInfo cache)
|
||||||
|
{
|
||||||
|
await Task.CompletedTask;
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证分组密码
|
||||||
|
/// </summary>
|
||||||
|
public sealed class SignInArgsGroupPasswordServer : ISignInArgs
|
||||||
|
{
|
||||||
|
private readonly FileConfig fileConfig;
|
||||||
|
public SignInArgsGroupPasswordServer(FileConfig fileConfig)
|
||||||
|
{
|
||||||
|
this.fileConfig = fileConfig;
|
||||||
|
}
|
||||||
|
public async Task<string> Invoke(string host, Dictionary<string, string> args)
|
||||||
|
{
|
||||||
|
await Task.CompletedTask;
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证参数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="signInfo">新登录参数</param>
|
||||||
|
/// <param name="cache">之前的登录信息</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<string> Validate(SignInfo signInfo, SignCacheInfo cache)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(fileConfig.Data.Server.SignIn.SecretKey) == false)
|
||||||
|
{
|
||||||
|
if (signInfo.Args.TryGetValue("signin-gpwd", out string gpwd) && string.IsNullOrWhiteSpace(gpwd) == false)
|
||||||
|
{
|
||||||
|
signInfo.GroupId = $"{signInfo.GroupId}->{gpwd}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await Task.CompletedTask;
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -6,6 +6,7 @@ using MemoryPack;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace linker.client.config
|
namespace linker.client.config
|
||||||
{
|
{
|
||||||
@@ -91,10 +92,7 @@ namespace linker.config
|
|||||||
};
|
};
|
||||||
public ClientServerInfo[] Servers
|
public ClientServerInfo[] Servers
|
||||||
{
|
{
|
||||||
get => servers; set
|
get => servers; set { servers = value; }
|
||||||
{
|
|
||||||
servers = value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
public ClientServerInfo ServerInfo => servers[0];
|
public ClientServerInfo ServerInfo => servers[0];
|
||||||
|
|
||||||
@@ -108,6 +106,7 @@ namespace linker.config
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private string name = Dns.GetHostName().SubStr(0, 12);
|
private string name = Dns.GetHostName().SubStr(0, 12);
|
||||||
public string Name
|
public string Name
|
||||||
{
|
{
|
||||||
@@ -118,7 +117,7 @@ namespace linker.config
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
private string groupid = "snltty";
|
private string groupid = Helper.GlobalString;
|
||||||
#else
|
#else
|
||||||
private string groupid = string.Empty;
|
private string groupid = string.Empty;
|
||||||
#endif
|
#endif
|
||||||
@@ -130,8 +129,14 @@ namespace linker.config
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Certificate { get; set; } = "./snltty.pfx";
|
|
||||||
public string Password { get; set; } = "oeq9tw1o";
|
public ClientGroupInfo Group => Groups.Length == 0 ? new ClientGroupInfo { } : Groups[0];
|
||||||
|
public ClientGroupInfo[] Groups { get; set; } = Array.Empty<ClientGroupInfo>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 加密证书
|
||||||
|
/// </summary>
|
||||||
|
public ClientCertificateInfo SSL { get; set; } = new ClientCertificateInfo();
|
||||||
|
|
||||||
public string Serialize(object obj)
|
public string Serialize(object obj)
|
||||||
{
|
{
|
||||||
@@ -141,7 +146,6 @@ namespace linker.config
|
|||||||
return Convert.ToBase64String(crypto.Encode(Encoding.UTF8.GetBytes(obj.ToJson())));
|
return Convert.ToBase64String(crypto.Encode(Encoding.UTF8.GetBytes(obj.ToJson())));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Deserialize(string text)
|
public object Deserialize(string text)
|
||||||
{
|
{
|
||||||
if (text.Contains("ApiPassword"))
|
if (text.Contains("ApiPassword"))
|
||||||
@@ -150,8 +154,53 @@ namespace linker.config
|
|||||||
}
|
}
|
||||||
return Encoding.UTF8.GetString(crypto.Decode(Convert.FromBase64String(text)).ToArray()).DeJson<ConfigClientInfo>();
|
return Encoding.UTF8.GetString(crypto.Decode(Convert.FromBase64String(text)).ToArray()).DeJson<ConfigClientInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string ToGroupString()
|
||||||
|
{
|
||||||
|
return $"{name}->{Group.Id}->{Group.Password}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MemoryPackable]
|
||||||
|
public sealed partial class ClientGroupInfo
|
||||||
|
{
|
||||||
|
public ClientGroupInfo() { }
|
||||||
|
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
private string id = Helper.GlobalString;
|
||||||
|
#else
|
||||||
|
private string id = string.Empty;
|
||||||
|
#endif
|
||||||
|
public string Id
|
||||||
|
{
|
||||||
|
get => id; set
|
||||||
|
{
|
||||||
|
id = value.SubStr(0, 36);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string passord = string.Empty;
|
||||||
|
public string Password
|
||||||
|
{
|
||||||
|
get => passord; set
|
||||||
|
{
|
||||||
|
passord = value.SubStr(0, 36);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[MemoryPackable]
|
||||||
|
public sealed partial class ClientCertificateInfo
|
||||||
|
{
|
||||||
|
public ClientCertificateInfo() { }
|
||||||
|
public string File { get; set; } = "./snltty.pfx";
|
||||||
|
public string Password { get; set; } = "oeq9tw1o";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[MemoryPackable]
|
[MemoryPackable]
|
||||||
public sealed partial class ClientServerInfo
|
public sealed partial class ClientServerInfo
|
||||||
{
|
{
|
||||||
@@ -166,6 +215,7 @@ namespace linker.config
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
|
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)]
|
||||||
public sealed class ClientApiAccessAttribute : Attribute
|
public sealed class ClientApiAccessAttribute : Attribute
|
||||||
{
|
{
|
||||||
@@ -279,6 +329,9 @@ namespace linker.config
|
|||||||
[ClientAccessDisplayAttribute("查看流量")]
|
[ClientAccessDisplayAttribute("查看流量")]
|
||||||
Flow = ((ulong)1 << 33),
|
Flow = ((ulong)1 << 33),
|
||||||
|
|
||||||
|
[ClientAccessDisplayAttribute("同步配置")]
|
||||||
|
Sync = ((ulong)1 << 34),
|
||||||
|
|
||||||
Full = ulong.MaxValue >> (64 - 52),
|
Full = ulong.MaxValue >> (64 - 52),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -20,8 +20,9 @@ namespace linker.plugins.config
|
|||||||
private readonly MessengerSender sender;
|
private readonly MessengerSender sender;
|
||||||
private readonly ClientSignInState clientSignInState;
|
private readonly ClientSignInState clientSignInState;
|
||||||
private readonly AccessTransfer accessTransfer;
|
private readonly AccessTransfer accessTransfer;
|
||||||
|
private readonly ConfigSyncTreansfer configSyncTreansfer;
|
||||||
|
|
||||||
public ConfigClientApiController(RunningConfig runningConfig, FileConfig config, ClientSignInTransfer clientSignInTransfer, MessengerSender sender, ClientSignInState clientSignInState, AccessTransfer accessTransfer)
|
public ConfigClientApiController(RunningConfig runningConfig, FileConfig config, ClientSignInTransfer clientSignInTransfer, MessengerSender sender, ClientSignInState clientSignInState, AccessTransfer accessTransfer, ConfigSyncTreansfer configSyncTreansfer)
|
||||||
{
|
{
|
||||||
this.runningConfig = runningConfig;
|
this.runningConfig = runningConfig;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
@@ -29,6 +30,7 @@ namespace linker.plugins.config
|
|||||||
this.sender = sender;
|
this.sender = sender;
|
||||||
this.clientSignInState = clientSignInState;
|
this.clientSignInState = clientSignInState;
|
||||||
this.accessTransfer = accessTransfer;
|
this.accessTransfer = accessTransfer;
|
||||||
|
this.configSyncTreansfer = configSyncTreansfer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Get(ApiControllerParamsInfo param)
|
public object Get(ApiControllerParamsInfo param)
|
||||||
@@ -42,7 +44,7 @@ namespace linker.plugins.config
|
|||||||
if (info.Common.Modes.Contains("client"))
|
if (info.Common.Modes.Contains("client"))
|
||||||
{
|
{
|
||||||
config.Data.Client.Name = info.Client.Name;
|
config.Data.Client.Name = info.Client.Name;
|
||||||
config.Data.Client.GroupId = info.Client.GroupId;
|
config.Data.Client.Groups = new ClientGroupInfo[] { new ClientGroupInfo { Id = info.Client.GroupId, Name = info.Client.GroupId, Password = info.Client.GroupPassword } };
|
||||||
config.Data.Client.CApi.WebPort = info.Client.Web;
|
config.Data.Client.CApi.WebPort = info.Client.Web;
|
||||||
config.Data.Client.CApi.ApiPort = info.Client.Api;
|
config.Data.Client.CApi.ApiPort = info.Client.Api;
|
||||||
config.Data.Client.CApi.ApiPassword = info.Client.Password;
|
config.Data.Client.CApi.ApiPassword = info.Client.Password;
|
||||||
@@ -163,6 +165,8 @@ namespace linker.plugins.config
|
|||||||
client.Access = accessTransfer.AssignAccess((ClientApiAccess)configExportInfo.Access);
|
client.Access = accessTransfer.AssignAccess((ClientApiAccess)configExportInfo.Access);
|
||||||
client.OnlyNode = true;
|
client.OnlyNode = true;
|
||||||
client.Action.Args.Clear();
|
client.Action.Args.Clear();
|
||||||
|
|
||||||
|
client.Groups = new ClientGroupInfo[] { client.Group };
|
||||||
File.WriteAllText(Path.Combine(configPath, $"client.json"), client.Serialize(client));
|
File.WriteAllText(Path.Combine(configPath, $"client.json"), client.Serialize(client));
|
||||||
|
|
||||||
ConfigCommonInfo common = config.Data.Common.ToJson().DeJson<ConfigCommonInfo>();
|
ConfigCommonInfo common = config.Data.Common.ToJson().DeJson<ConfigCommonInfo>();
|
||||||
@@ -221,31 +225,17 @@ namespace linker.plugins.config
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<bool> SecretKeyAsync(ApiControllerParamsInfo param)
|
public List<ConfigSyncNameInfo> SyncNames(ApiControllerParamsInfo param)
|
||||||
{
|
{
|
||||||
SecretKeyAsyncInfo info = param.Content.DeJson<SecretKeyAsyncInfo>();
|
return configSyncTreansfer.GetNames();
|
||||||
await sender.SendOnly(new MessageRequestWrap
|
}
|
||||||
{
|
[ClientApiAccessAttribute(ClientApiAccess.Sync)]
|
||||||
Connection = clientSignInState.Connection,
|
public bool Sync(ApiControllerParamsInfo param)
|
||||||
MessengerId = (ushort)ConfigMessengerIds.SecretKeyAsyncForward,
|
{
|
||||||
Payload = MemoryPackSerializer.Serialize(info)
|
string[] names = param.Content.DeJson<string[]>();
|
||||||
});
|
configSyncTreansfer.Sync(names);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
public async Task<bool> ServerAsync(ApiControllerParamsInfo param)
|
|
||||||
{
|
|
||||||
ServerAsyncInfo info = param.Content.DeJson<ServerAsyncInfo>();
|
|
||||||
await sender.SendOnly(new MessageRequestWrap
|
|
||||||
{
|
|
||||||
Connection = clientSignInState.Connection,
|
|
||||||
MessengerId = (ushort)ConfigMessengerIds.ServerAsyncForward,
|
|
||||||
Payload = MemoryPackSerializer.Serialize(info)
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class ConfigInstallInfo
|
public sealed class ConfigInstallInfo
|
||||||
@@ -258,6 +248,8 @@ namespace linker.plugins.config
|
|||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string GroupId { get; set; }
|
public string GroupId { get; set; }
|
||||||
|
public string GroupPassword { get; set; }
|
||||||
|
|
||||||
public int Api { get; set; }
|
public int Api { get; set; }
|
||||||
public int Web { get; set; }
|
public int Web { get; set; }
|
||||||
public string Password { get; set; }
|
public string Password { get; set; }
|
||||||
@@ -302,7 +294,6 @@ namespace linker.plugins.config
|
|||||||
public string[] Modes { get; set; }
|
public string[] Modes { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public sealed class ConfigExportInfo
|
public sealed class ConfigExportInfo
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
@@ -28,6 +28,10 @@ namespace linker.plugins.config
|
|||||||
|
|
||||||
|
|
||||||
serviceCollection.AddSingleton<AccessTransfer>();
|
serviceCollection.AddSingleton<AccessTransfer>();
|
||||||
|
|
||||||
|
serviceCollection.AddSingleton<ConfigSyncTreansfer>();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddServer(ServiceCollection serviceCollection, FileConfig config)
|
public void AddServer(ServiceCollection serviceCollection, FileConfig config)
|
||||||
@@ -38,6 +42,8 @@ namespace linker.plugins.config
|
|||||||
public void UseClient(ServiceProvider serviceProvider, FileConfig config)
|
public void UseClient(ServiceProvider serviceProvider, FileConfig config)
|
||||||
{
|
{
|
||||||
RunningConfig runningConfig = serviceProvider.GetService<RunningConfig>();
|
RunningConfig runningConfig = serviceProvider.GetService<RunningConfig>();
|
||||||
|
|
||||||
|
ConfigSyncTreansfer configSyncTreansfer = serviceProvider.GetService<ConfigSyncTreansfer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UseServer(ServiceProvider serviceProvider, FileConfig config)
|
public void UseServer(ServiceProvider serviceProvider, FileConfig config)
|
||||||
|
87
linker/plugins/config/ConfigSyncTreansfer.cs
Normal file
87
linker/plugins/config/ConfigSyncTreansfer.cs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
using linker.libs;
|
||||||
|
using linker.plugins.client;
|
||||||
|
using linker.plugins.config.messenger;
|
||||||
|
using linker.plugins.messenger;
|
||||||
|
using MemoryPack;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace linker.plugins.config
|
||||||
|
{
|
||||||
|
public interface IConfigSync
|
||||||
|
{
|
||||||
|
public string Name { get; }
|
||||||
|
public string Label { get; }
|
||||||
|
public Memory<byte> GetData();
|
||||||
|
public void SetData(Memory<byte> data);
|
||||||
|
}
|
||||||
|
|
||||||
|
[MemoryPackable]
|
||||||
|
public sealed partial class ConfigAsyncInfo
|
||||||
|
{
|
||||||
|
public ConfigAsyncInfo() { }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public Memory<byte> Data { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class ConfigSyncNameInfo
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
public string Label { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
public sealed partial class ConfigSyncTreansfer
|
||||||
|
{
|
||||||
|
private readonly SemaphoreSlim slim = new SemaphoreSlim(1);
|
||||||
|
private List<IConfigSync> syncs = new List<IConfigSync>();
|
||||||
|
|
||||||
|
private readonly MessengerSender messengerSender;
|
||||||
|
private readonly ClientSignInState clientSignInState;
|
||||||
|
public ConfigSyncTreansfer(ServiceProvider serviceProvider, MessengerSender messengerSender, ClientSignInState clientSignInState)
|
||||||
|
{
|
||||||
|
this.messengerSender = messengerSender;
|
||||||
|
this.clientSignInState = clientSignInState;
|
||||||
|
|
||||||
|
IEnumerable<Type> types = GetSourceGeneratorTypes();
|
||||||
|
syncs = types.Select(c => (IConfigSync)serviceProvider.GetService(c)).Where(c => c != null).Where(c => string.IsNullOrWhiteSpace(c.Name) == false).ToList();
|
||||||
|
LoggerHelper.Instance.Info($"load config sync transport:{string.Join(",", syncs.Select(c => c.Name))}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ConfigSyncNameInfo> GetNames()
|
||||||
|
{
|
||||||
|
return syncs.Select(c => new ConfigSyncNameInfo { Label = c.Label, Name = c.Name }).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Sync(string[] names)
|
||||||
|
{
|
||||||
|
TimerHelper.Async(async () =>
|
||||||
|
{
|
||||||
|
await slim.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tasks = syncs.Where(c => names.Contains(c.Name)).Select(c =>
|
||||||
|
{
|
||||||
|
return messengerSender.SendOnly(new MessageRequestWrap
|
||||||
|
{
|
||||||
|
Connection = clientSignInState.Connection,
|
||||||
|
MessengerId = (ushort)ConfigMessengerIds.SyncForward,
|
||||||
|
Payload = c.GetData(),
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
slim.Release();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public void Sync(ConfigAsyncInfo info)
|
||||||
|
{
|
||||||
|
var sync = syncs.FirstOrDefault(c => c.Name == info.Name);
|
||||||
|
if (sync != null)
|
||||||
|
{
|
||||||
|
sync.SetData(info.Data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -3,9 +3,6 @@ using linker.libs;
|
|||||||
using LiteDB;
|
using LiteDB;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using linker.config;
|
using linker.config;
|
||||||
using MemoryPack;
|
|
||||||
using linker.libs.extends;
|
|
||||||
using linker.tunnel.wanport;
|
|
||||||
|
|
||||||
namespace linker.client.config
|
namespace linker.client.config
|
||||||
{
|
{
|
||||||
@@ -61,7 +58,7 @@ namespace linker.client.config
|
|||||||
Data.Updated--;
|
Data.Updated--;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}, 1000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
private void Save()
|
private void Save()
|
||||||
{
|
{
|
||||||
@@ -163,21 +160,5 @@ namespace linker.client.config
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[MemoryPackable]
|
|
||||||
public sealed partial class SecretKeyAsyncInfo
|
|
||||||
{
|
|
||||||
public SecretKeyAsyncInfo() { }
|
|
||||||
public string SignSecretKey { get; set; }
|
|
||||||
public string RelaySecretKey { get; set; }
|
|
||||||
public string SForwardSecretKey { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
[MemoryPackable]
|
|
||||||
public sealed partial class ServerAsyncInfo
|
|
||||||
{
|
|
||||||
public ServerAsyncInfo() { }
|
|
||||||
public ClientServerInfo[] SignServers { get; set; }
|
|
||||||
public RelayServerInfo[] RelayServers { get; set; }
|
|
||||||
public TunnelWanPortInfo[] TunnelServers { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -83,10 +83,10 @@ namespace linker.plugins.config.messenger
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[MessengerId((ushort)ConfigMessengerIds.SecretKeyAsyncForward)]
|
[MessengerId((ushort)ConfigMessengerIds.Sync)]
|
||||||
public async Task SecretKeyAsyncForward(IConnection connection)
|
public async Task SecretKeyAsyncForward(IConnection connection)
|
||||||
{
|
{
|
||||||
SecretKeyAsyncInfo info = MemoryPackSerializer.Deserialize<SecretKeyAsyncInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
ConfigAsyncInfo info = MemoryPackSerializer.Deserialize<ConfigAsyncInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
||||||
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
|
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
|
||||||
{
|
{
|
||||||
List<SignCacheInfo> caches = signCaching.Get(cache.GroupId);
|
List<SignCacheInfo> caches = signCaching.Get(cache.GroupId);
|
||||||
@@ -96,29 +96,7 @@ namespace linker.plugins.config.messenger
|
|||||||
tasks.Add(sender.SendOnly(new MessageRequestWrap
|
tasks.Add(sender.SendOnly(new MessageRequestWrap
|
||||||
{
|
{
|
||||||
Connection = item.Connection,
|
Connection = item.Connection,
|
||||||
MessengerId = (ushort)ConfigMessengerIds.SecretKeyAsync,
|
MessengerId = (ushort)ConfigMessengerIds.Sync,
|
||||||
Payload = connection.ReceiveRequestWrap.Payload,
|
|
||||||
Timeout = 1000,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.WhenAll(tasks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[MessengerId((ushort)ConfigMessengerIds.ServerAsyncForward)]
|
|
||||||
public async Task ServerAsyncForward(IConnection connection)
|
|
||||||
{
|
|
||||||
ServerAsyncInfo info = MemoryPackSerializer.Deserialize<ServerAsyncInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
|
||||||
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache))
|
|
||||||
{
|
|
||||||
List<SignCacheInfo> caches = signCaching.Get(cache.GroupId);
|
|
||||||
List<Task> tasks = new List<Task>();
|
|
||||||
foreach (SignCacheInfo item in caches.Where(c => c.MachineId != connection.Id && c.Connected))
|
|
||||||
{
|
|
||||||
tasks.Add(sender.SendOnly(new MessageRequestWrap
|
|
||||||
{
|
|
||||||
Connection = item.Connection,
|
|
||||||
MessengerId = (ushort)ConfigMessengerIds.ServerAsync,
|
|
||||||
Payload = connection.ReceiveRequestWrap.Payload,
|
Payload = connection.ReceiveRequestWrap.Payload,
|
||||||
Timeout = 1000,
|
Timeout = 1000,
|
||||||
}));
|
}));
|
||||||
@@ -134,12 +112,14 @@ namespace linker.plugins.config.messenger
|
|||||||
private readonly AccessTransfer accessTransfer;
|
private readonly AccessTransfer accessTransfer;
|
||||||
private readonly FileConfig fileConfig;
|
private readonly FileConfig fileConfig;
|
||||||
private readonly ClientSignInTransfer clientSignInTransfer;
|
private readonly ClientSignInTransfer clientSignInTransfer;
|
||||||
|
private readonly ConfigSyncTreansfer syncTreansfer;
|
||||||
|
|
||||||
public ConfigClientMessenger(AccessTransfer accessTransfer, FileConfig fileConfig, ClientSignInTransfer clientSignInTransfer)
|
public ConfigClientMessenger(AccessTransfer accessTransfer, FileConfig fileConfig, ClientSignInTransfer clientSignInTransfer, ConfigSyncTreansfer syncTreansfer)
|
||||||
{
|
{
|
||||||
this.accessTransfer = accessTransfer;
|
this.accessTransfer = accessTransfer;
|
||||||
this.fileConfig = fileConfig;
|
this.fileConfig = fileConfig;
|
||||||
this.clientSignInTransfer = clientSignInTransfer;
|
this.clientSignInTransfer = clientSignInTransfer;
|
||||||
|
this.syncTreansfer = syncTreansfer;
|
||||||
}
|
}
|
||||||
|
|
||||||
[MessengerId((ushort)ConfigMessengerIds.Access)]
|
[MessengerId((ushort)ConfigMessengerIds.Access)]
|
||||||
@@ -158,38 +138,12 @@ namespace linker.plugins.config.messenger
|
|||||||
connection.Write(Helper.TrueArray);
|
connection.Write(Helper.TrueArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MessengerId((ushort)ConfigMessengerIds.SecretKeyAsync)]
|
[MessengerId((ushort)ConfigMessengerIds.Sync)]
|
||||||
public void SecretKeyAsync(IConnection connection)
|
public void Sync(IConnection connection)
|
||||||
{
|
{
|
||||||
SecretKeyAsyncInfo info = MemoryPackSerializer.Deserialize<SecretKeyAsyncInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
ConfigAsyncInfo info = MemoryPackSerializer.Deserialize<ConfigAsyncInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
||||||
|
syncTreansfer.Sync(info);
|
||||||
foreach (var item in fileConfig.Data.Client.Servers)
|
|
||||||
{
|
|
||||||
item.SecretKey = info.SignSecretKey;
|
|
||||||
}
|
|
||||||
foreach (var item in fileConfig.Data.Client.Relay.Servers)
|
|
||||||
{
|
|
||||||
item.SecretKey = info.RelaySecretKey;
|
|
||||||
}
|
|
||||||
fileConfig.Data.Client.SForward.SecretKey = info.SForwardSecretKey;
|
|
||||||
|
|
||||||
fileConfig.Data.Update();
|
|
||||||
clientSignInTransfer.SignOut();
|
|
||||||
_ = clientSignInTransfer.SignIn();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MessengerId((ushort)ConfigMessengerIds.ServerAsync)]
|
|
||||||
public void ServerAsync(IConnection connection)
|
|
||||||
{
|
|
||||||
ServerAsyncInfo info = MemoryPackSerializer.Deserialize<ServerAsyncInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
|
||||||
|
|
||||||
fileConfig.Data.Client.Servers = info.SignServers;
|
|
||||||
fileConfig.Data.Client.Relay.Servers = info.RelayServers;
|
|
||||||
fileConfig.Data.Client.Tunnel.Servers = info.TunnelServers.ToList();
|
|
||||||
|
|
||||||
fileConfig.Data.Update();
|
|
||||||
clientSignInTransfer.SignOut();
|
|
||||||
_ = clientSignInTransfer.SignIn();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -13,11 +13,8 @@
|
|||||||
AccessUpdate = 2505,
|
AccessUpdate = 2505,
|
||||||
AccessUpdateForward = 2506,
|
AccessUpdateForward = 2506,
|
||||||
|
|
||||||
SecretKeyAsync = 2507,
|
Sync = 2507,
|
||||||
SecretKeyAsyncForward = 2508,
|
SyncForward = 2508,
|
||||||
|
|
||||||
ServerAsync = 2509,
|
|
||||||
ServerAsyncForward = 2510,
|
|
||||||
|
|
||||||
Max = 2599
|
Max = 2599
|
||||||
}
|
}
|
||||||
|
@@ -272,20 +272,24 @@ namespace linker.plugins.forward.proxy
|
|||||||
{
|
{
|
||||||
TimerHelper.SetInterval(() =>
|
TimerHelper.SetInterval(() =>
|
||||||
{
|
{
|
||||||
var connections = udpConnections.Where(c => c.Value.Timeout).Select(c => c.Key).ToList();
|
if(udpConnections.Count > 0)
|
||||||
foreach (var item in connections)
|
|
||||||
{
|
{
|
||||||
if (udpConnections.TryRemove(item, out AsyncUserUdpTokenTarget token))
|
var connections = udpConnections.Where(c => c.Value.Timeout).Select(c => c.Key).ToList();
|
||||||
|
foreach (var item in connections)
|
||||||
{
|
{
|
||||||
try
|
if (udpConnections.TryRemove(item, out AsyncUserUdpTokenTarget token))
|
||||||
{
|
|
||||||
token.Clear();
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
token.Clear();
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
@@ -41,7 +41,7 @@ namespace linker.plugins.messenger
|
|||||||
|
|
||||||
MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>();
|
MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>();
|
||||||
messengerResolver.LoadMessenger();
|
messengerResolver.LoadMessenger();
|
||||||
messengerResolver.Init(config.Data.Client.Certificate, config.Data.Client.Password);
|
messengerResolver.Init(config.Data.Client.SSL.File, config.Data.Client.SSL.Password);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ namespace linker.plugins.messenger
|
|||||||
|
|
||||||
MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>();
|
MessengerResolver messengerResolver = serviceProvider.GetService<MessengerResolver>();
|
||||||
messengerResolver.LoadMessenger();
|
messengerResolver.LoadMessenger();
|
||||||
messengerResolver.Init(config.Data.Server.Certificate, config.Data.Server.Password);
|
messengerResolver.Init(config.Data.Server.SSL.File, config.Data.Server.SSL.Password);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -293,7 +293,7 @@ namespace linker.plugins.relay
|
|||||||
await TaskRelay();
|
await TaskRelay();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}, 1000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
sealed class TestInfo
|
sealed class TestInfo
|
||||||
{
|
{
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
using linker.client.config;
|
using linker.client.config;
|
||||||
using linker.config;
|
using linker.config;
|
||||||
|
using linker.libs;
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
@@ -55,10 +56,11 @@ namespace linker.config
|
|||||||
}
|
}
|
||||||
public sealed class RelayConfigServerInfo
|
public sealed class RelayConfigServerInfo
|
||||||
{
|
{
|
||||||
/// <summary>
|
#if DEBUG
|
||||||
/// 中继密钥
|
public string SecretKey { get; set; } = Helper.GlobalString;
|
||||||
/// </summary>
|
#else
|
||||||
public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper();
|
public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper();
|
||||||
|
#endif
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 缓冲区
|
/// 缓冲区
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -83,7 +85,7 @@ namespace linker.config
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 密钥
|
/// 密钥
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SecretKey { get; set; } = "snltty";
|
public string SecretKey { get; set; } = Helper.GlobalString;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 服务器地址
|
/// 服务器地址
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -34,10 +34,10 @@ namespace linker.plugins.relay.transport
|
|||||||
this.messengerSender = messengerSender;
|
this.messengerSender = messengerSender;
|
||||||
this.clientSignInState = clientSignInState;
|
this.clientSignInState = clientSignInState;
|
||||||
|
|
||||||
string path = Path.GetFullPath(config.Data.Client.Certificate);
|
string path = Path.GetFullPath(config.Data.Client.SSL.File);
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
{
|
{
|
||||||
certificate = new X509Certificate2(path, config.Data.Client.Password, X509KeyStorageFlags.Exportable);
|
certificate = new X509Certificate2(path, config.Data.Client.SSL.Password, X509KeyStorageFlags.Exportable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,8 +10,7 @@ namespace linker.config
|
|||||||
public ConfigServerInfo() { }
|
public ConfigServerInfo() { }
|
||||||
public int ServicePort { get; set; } = 1802;
|
public int ServicePort { get; set; } = 1802;
|
||||||
|
|
||||||
public string Certificate { get; set; } = "./snltty.pfx";
|
public ServerCertificateInfo SSL { get; set; } = new ServerCertificateInfo();
|
||||||
public string Password { get; set; } = "oeq9tw1o";
|
|
||||||
|
|
||||||
public object Deserialize(string text)
|
public object Deserialize(string text)
|
||||||
{
|
{
|
||||||
@@ -22,4 +21,11 @@ namespace linker.config
|
|||||||
return obj.ToJsonFormat();
|
return obj.ToJsonFormat();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public sealed partial class ServerCertificateInfo
|
||||||
|
{
|
||||||
|
public ServerCertificateInfo() { }
|
||||||
|
public string File { get; set; } = "./snltty.pfx";
|
||||||
|
public string Password { get; set; } = "oeq9tw1o";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using LiteDB;
|
using linker.libs;
|
||||||
|
using LiteDB;
|
||||||
using MemoryPack;
|
using MemoryPack;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
@@ -11,7 +12,7 @@ namespace linker.client.config
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 服务器穿透密钥
|
/// 服务器穿透密钥
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SForwardSecretKey { get; set; } = "snltty";
|
public string SForwardSecretKey { get; set; } = Helper.GlobalString;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 服务器穿透列表
|
/// 服务器穿透列表
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -81,7 +82,7 @@ namespace linker.config
|
|||||||
}
|
}
|
||||||
public sealed class SForwardConfigInfo
|
public sealed class SForwardConfigInfo
|
||||||
{
|
{
|
||||||
public string SecretKey { get; set; } = "snltty";
|
public string SecretKey { get; set; } = Helper.GlobalString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -97,7 +98,11 @@ namespace linker.config
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 密钥
|
/// 密钥
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
#if DEBUG
|
||||||
|
public string SecretKey { get; set; } = Helper.GlobalString;
|
||||||
|
#else
|
||||||
public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper();
|
public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper();
|
||||||
|
#endif
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 缓冲区
|
/// 缓冲区
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -8,7 +8,7 @@ namespace linker.plugins.sforward.proxy
|
|||||||
{
|
{
|
||||||
|
|
||||||
private readonly NumberSpace ns = new NumberSpace();
|
private readonly NumberSpace ns = new NumberSpace();
|
||||||
private byte[] flagBytes = Encoding.UTF8.GetBytes($"snltty.sforward");
|
private byte[] flagBytes = Encoding.UTF8.GetBytes($"{Helper.GlobalString}.sforward");
|
||||||
|
|
||||||
private readonly SForwardFlow sForwardFlow;
|
private readonly SForwardFlow sForwardFlow;
|
||||||
public SForwardProxy(SForwardFlow sForwardFlow)
|
public SForwardProxy(SForwardFlow sForwardFlow)
|
||||||
|
@@ -27,7 +27,7 @@ namespace linker.plugins.signin
|
|||||||
public void Set(ApiControllerParamsInfo param)
|
public void Set(ApiControllerParamsInfo param)
|
||||||
{
|
{
|
||||||
ConfigSetInfo info = param.Content.DeJson<ConfigSetInfo>();
|
ConfigSetInfo info = param.Content.DeJson<ConfigSetInfo>();
|
||||||
clientSignInTransfer.Set(info.Name, info.GroupId);
|
clientSignInTransfer.Set(info.Name, info.Groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> SetName(ApiControllerParamsInfo param)
|
public async Task<bool> SetName(ApiControllerParamsInfo param)
|
||||||
@@ -38,7 +38,7 @@ namespace linker.plugins.signin
|
|||||||
{
|
{
|
||||||
if (config.Data.Client.HasAccess(ClientApiAccess.RenameSelf) == false) return false;
|
if (config.Data.Client.HasAccess(ClientApiAccess.RenameSelf) == false) return false;
|
||||||
|
|
||||||
clientSignInTransfer.SetName(info.NewName);
|
clientSignInTransfer.Set(info.NewName);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -53,12 +53,17 @@ namespace linker.plugins.signin
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
public void SetGroups(ApiControllerParamsInfo param)
|
||||||
|
{
|
||||||
|
ClientGroupInfo[] info = param.Content.DeJson<ClientGroupInfo[]>();
|
||||||
|
clientSignInTransfer.Set(info);
|
||||||
|
}
|
||||||
|
|
||||||
[ClientApiAccessAttribute(ClientApiAccess.Config)]
|
[ClientApiAccessAttribute(ClientApiAccess.Config)]
|
||||||
public async Task<bool> SetServers(ApiControllerParamsInfo param)
|
public bool SetServers(ApiControllerParamsInfo param)
|
||||||
{
|
{
|
||||||
ClientServerInfo[] servers = param.Content.DeJson<ClientServerInfo[]>();
|
ClientServerInfo[] servers = param.Content.DeJson<ClientServerInfo[]>();
|
||||||
await clientSignInTransfer.SetServers(servers);
|
clientSignInTransfer.SetServers(servers);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +122,6 @@ namespace linker.plugins.signin
|
|||||||
return new SignInIdsResponseInfo { };
|
return new SignInIdsResponseInfo { };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[MemoryPackable]
|
[MemoryPackable]
|
||||||
@@ -130,7 +134,7 @@ namespace linker.plugins.signin
|
|||||||
public sealed class ConfigSetInfo
|
public sealed class ConfigSetInfo
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string GroupId { get; set; }
|
public ClientGroupInfo[] Groups { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -20,7 +20,7 @@ namespace linker.plugins.signin.messenger
|
|||||||
public void Name(IConnection connection)
|
public void Name(IConnection connection)
|
||||||
{
|
{
|
||||||
ConfigSetNameInfo info = MemoryPackSerializer.Deserialize<ConfigSetNameInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
ConfigSetNameInfo info = MemoryPackSerializer.Deserialize<ConfigSetNameInfo>(connection.ReceiveRequestWrap.Payload.Span);
|
||||||
clientSignInTransfer.SetName(info.NewName);
|
clientSignInTransfer.Set(info.NewName);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -43,10 +43,10 @@ namespace linker.plugins.tunnel
|
|||||||
this.excludeIPTransfer = excludeIPTransfer;
|
this.excludeIPTransfer = excludeIPTransfer;
|
||||||
this.upnpTransfer = upnpTransfer;
|
this.upnpTransfer = upnpTransfer;
|
||||||
|
|
||||||
string path = Path.GetFullPath(config.Data.Client.Certificate);
|
string path = Path.GetFullPath(config.Data.Client.SSL.File);
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
{
|
{
|
||||||
Certificate = new X509Certificate2(path, config.Data.Client.Password, X509KeyStorageFlags.Exportable);
|
Certificate = new X509Certificate2(path, config.Data.Client.SSL.Password, X509KeyStorageFlags.Exportable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
22
linker/plugins/tuntap/DHCPTreansfer.cs
Normal file
22
linker/plugins/tuntap/DHCPTreansfer.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace linker.plugins.tuntap
|
||||||
|
{
|
||||||
|
public sealed class DHCPTreansfer
|
||||||
|
{
|
||||||
|
public void AddNetwork()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public IPAddress GetIP()
|
||||||
|
{
|
||||||
|
return IPAddress.Any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -25,6 +25,7 @@ namespace linker.plugins.tuntap.client
|
|||||||
private readonly LinkerTunDeviceAdapter linkerTunDeviceAdapter;
|
private readonly LinkerTunDeviceAdapter linkerTunDeviceAdapter;
|
||||||
|
|
||||||
private string groupid = string.Empty;
|
private string groupid = string.Empty;
|
||||||
|
|
||||||
private readonly FileConfig config;
|
private readonly FileConfig config;
|
||||||
private readonly RunningConfig runningConfig;
|
private readonly RunningConfig runningConfig;
|
||||||
private readonly ClientSignInState clientSignInState;
|
private readonly ClientSignInState clientSignInState;
|
||||||
@@ -36,6 +37,7 @@ namespace linker.plugins.tuntap.client
|
|||||||
this.runningConfig = runningConfig;
|
this.runningConfig = runningConfig;
|
||||||
this.linkerTunDeviceAdapter = linkerTunDeviceAdapter;
|
this.linkerTunDeviceAdapter = linkerTunDeviceAdapter;
|
||||||
this.clientSignInState = clientSignInState;
|
this.clientSignInState = clientSignInState;
|
||||||
|
|
||||||
clientSignInState.NetworkEnabledHandle += (times) => ClearIPs();
|
clientSignInState.NetworkEnabledHandle += (times) => ClearIPs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,16 +155,6 @@ namespace linker.plugins.tuntap.client
|
|||||||
list.Add(machineId);
|
list.Add(machineId);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ClearIPs()
|
|
||||||
{
|
|
||||||
if (groupid != config.Data.Client.GroupId)
|
|
||||||
{
|
|
||||||
ip2MachineDic.Clear();
|
|
||||||
ipConnections.Clear();
|
|
||||||
}
|
|
||||||
groupid = config.Data.Client.GroupId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 打洞或者中继
|
/// 打洞或者中继
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -190,5 +182,16 @@ namespace linker.plugins.tuntap.client
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void ClearIPs()
|
||||||
|
{
|
||||||
|
if (groupid != config.Data.Client.Group.Id)
|
||||||
|
{
|
||||||
|
ip2MachineDic.Clear();
|
||||||
|
ipConnections.Clear();
|
||||||
|
}
|
||||||
|
groupid = config.Data.Client.Group.Id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -56,7 +56,7 @@ namespace linker.plugins.tuntap.client
|
|||||||
}
|
}
|
||||||
private void NetworkEanble(int times)
|
private void NetworkEanble(int times)
|
||||||
{
|
{
|
||||||
if (runningConfig.Data.Tuntap.Group2IP.TryGetValue(config.Data.Client.GroupId, out TuntapGroup2IPInfo tuntapGroup2IPInfo))
|
if (runningConfig.Data.Tuntap.Group2IP.TryGetValue(config.Data.Client.Group.Id, out TuntapGroup2IPInfo tuntapGroup2IPInfo))
|
||||||
{
|
{
|
||||||
if (tuntapGroup2IPInfo.IP.Equals(runningConfig.Data.Tuntap.IP) == false || tuntapGroup2IPInfo.PrefixLength != runningConfig.Data.Tuntap.PrefixLength)
|
if (tuntapGroup2IPInfo.IP.Equals(runningConfig.Data.Tuntap.IP) == false || tuntapGroup2IPInfo.PrefixLength != runningConfig.Data.Tuntap.PrefixLength)
|
||||||
{
|
{
|
||||||
@@ -222,7 +222,7 @@ namespace linker.plugins.tuntap.client
|
|||||||
runningConfig.Data.Tuntap.Forwards = info.Forwards;
|
runningConfig.Data.Tuntap.Forwards = info.Forwards;
|
||||||
|
|
||||||
TuntapGroup2IPInfo tuntapGroup2IPInfo = new TuntapGroup2IPInfo { IP = info.IP, PrefixLength = info.PrefixLength };
|
TuntapGroup2IPInfo tuntapGroup2IPInfo = new TuntapGroup2IPInfo { IP = info.IP, PrefixLength = info.PrefixLength };
|
||||||
runningConfig.Data.Tuntap.Group2IP.AddOrUpdate(config.Data.Client.GroupId, tuntapGroup2IPInfo, (a, b) => tuntapGroup2IPInfo);
|
runningConfig.Data.Tuntap.Group2IP.AddOrUpdate(config.Data.Client.Group.Id, tuntapGroup2IPInfo, (a, b) => tuntapGroup2IPInfo);
|
||||||
|
|
||||||
runningConfig.Data.Update();
|
runningConfig.Data.Update();
|
||||||
if (Status == TuntapStatus.Running && needReboot)
|
if (Status == TuntapStatus.Running && needReboot)
|
||||||
@@ -434,16 +434,14 @@ namespace linker.plugins.tuntap.client
|
|||||||
/// <param name="infos"></param>
|
/// <param name="infos"></param>
|
||||||
private void CheckLanIPs()
|
private void CheckLanIPs()
|
||||||
{
|
{
|
||||||
uint[] localIps = config.Data.Client.Tunnel.LocalIPs.Where(c => c.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork).Concat(config.Data.Client.Tunnel.RouteIPs)
|
uint[] excludeIps = ExcludeIps();
|
||||||
.Select(c => BinaryPrimitives.ReadUInt32BigEndian(c.GetAddressBytes()))
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var ips = tuntapInfos.Values.Where(c => c.MachineId != config.Data.Client.Id).Select(c =>
|
var ips = tuntapInfos.Values.Where(c => c.MachineId != config.Data.Client.Id).Select(c =>
|
||||||
{
|
{
|
||||||
return new TuntapVeaLanIPAddressList
|
return new TuntapVeaLanIPAddressList
|
||||||
{
|
{
|
||||||
MachineId = c.MachineId,
|
MachineId = c.MachineId,
|
||||||
IPS = ParseIPs(c.LanIPs, c.Masks, c.MachineId).Where(c => localIps.Select(d => d & c.MaskValue).Contains(c.NetWork)).ToList(),
|
IPS = ParseIPs(c.LanIPs, c.Masks, c.MachineId).Where(c => excludeIps.Select(d => d & c.MaskValue).Contains(c.NetWork)).ToList(),
|
||||||
};
|
};
|
||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
@@ -456,16 +454,23 @@ namespace linker.plugins.tuntap.client
|
|||||||
Version.Add();
|
Version.Add();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private uint[] ExcludeIps()
|
||||||
private List<TuntapVeaLanIPAddressList> ParseIPs(List<TuntapInfo> infos)
|
|
||||||
{
|
{
|
||||||
uint[] localIps = config.Data.Client.Tunnel.LocalIPs.Where(c => c.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
return //本机局域网IP
|
||||||
.Concat(new IPAddress[] { runningConfig.Data.Tuntap.IP })
|
config.Data.Client.Tunnel.LocalIPs.Where(c => c.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
|
||||||
|
//网卡IP 服务器IP
|
||||||
|
.Concat(new IPAddress[] { runningConfig.Data.Tuntap.IP, clientSignInState.Connection.Address.Address })
|
||||||
|
//网卡配置的局域网IP
|
||||||
.Concat(runningConfig.Data.Tuntap.LanIPs.Where(c => c != null))
|
.Concat(runningConfig.Data.Tuntap.LanIPs.Where(c => c != null))
|
||||||
|
//路由上的IP
|
||||||
.Concat(config.Data.Client.Tunnel.RouteIPs)
|
.Concat(config.Data.Client.Tunnel.RouteIPs)
|
||||||
.Select(c => BinaryPrimitives.ReadUInt32BigEndian(c.GetAddressBytes()))
|
.Select(c => BinaryPrimitives.ReadUInt32BigEndian(c.GetAddressBytes()))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
}
|
||||||
|
private List<TuntapVeaLanIPAddressList> ParseIPs(List<TuntapInfo> infos)
|
||||||
|
{
|
||||||
|
//排除的IP,
|
||||||
|
uint[] excludeIps = ExcludeIps();
|
||||||
return infos
|
return infos
|
||||||
//自己的ip不要
|
//自己的ip不要
|
||||||
.Where(c => c.IP.Equals(runningConfig.Data.Tuntap.IP) == false && c.LastTicks.HasValue())
|
.Where(c => c.IP.Equals(runningConfig.Data.Tuntap.IP) == false && c.LastTicks.HasValue())
|
||||||
@@ -476,8 +481,7 @@ namespace linker.plugins.tuntap.client
|
|||||||
{
|
{
|
||||||
MachineId = c.MachineId,
|
MachineId = c.MachineId,
|
||||||
IPS = ParseIPs(c.LanIPs, c.Masks, c.MachineId)
|
IPS = ParseIPs(c.LanIPs, c.Masks, c.MachineId)
|
||||||
//这边的局域网IP也不要,为了防止将本机局域网IP路由到别的地方
|
.Where(c => excludeIps.Select(d => d & c.MaskValue).Contains(c.NetWork) == false).ToList(),
|
||||||
.Where(c => localIps.Select(d => d & c.MaskValue).Contains(c.NetWork) == false).ToList(),
|
|
||||||
};
|
};
|
||||||
}).ToList();
|
}).ToList();
|
||||||
}
|
}
|
||||||
|
@@ -60,7 +60,7 @@ namespace linker.config
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 密钥
|
/// 密钥
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string SecretKey { get; set; } = "snltty";
|
public string SecretKey { get; set; } = Helper.GlobalString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public partial class ConfigServerInfo
|
public partial class ConfigServerInfo
|
||||||
@@ -76,7 +76,11 @@ namespace linker.config
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 密钥
|
/// 密钥
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
#if DEBUG
|
||||||
|
public string SecretKey { get; set; } = Helper.GlobalString;
|
||||||
|
#else
|
||||||
public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper();
|
public string SecretKey { get; set; } = Guid.NewGuid().ToString().ToUpper();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using linker.libs.extends;
|
using linker.libs;
|
||||||
|
using linker.libs.extends;
|
||||||
using LiteDB;
|
using LiteDB;
|
||||||
using LiteDB.Engine;
|
using LiteDB.Engine;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
@@ -22,12 +23,12 @@ namespace linker.store
|
|||||||
if (Encoded() == false)
|
if (Encoded() == false)
|
||||||
{
|
{
|
||||||
database = new LiteDatabase(@"./configs/db.db", bsonMapper);
|
database = new LiteDatabase(@"./configs/db.db", bsonMapper);
|
||||||
var rebuildOptions = new RebuildOptions { Password = "snltty" };
|
var rebuildOptions = new RebuildOptions { Password = Helper.GlobalString };
|
||||||
database.Rebuild(rebuildOptions);
|
database.Rebuild(rebuildOptions);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
database = new LiteDatabase(new ConnectionString("Filename=./configs/db.db; Password=snltty"), bsonMapper);
|
database = new LiteDatabase(new ConnectionString($"Filename=./configs/db.db; Password={Helper.GlobalString}"), bsonMapper);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
v1.4.9
|
v1.4.9
|
||||||
2024-10-14 17:04:07
|
2024-10-16 17:33:40
|
||||||
1. 使用原生成器替代反射
|
1. 使用原生成器替代反射
|
||||||
2. 统计所有服务端,客户端数量
|
2. 统计所有服务端,客户端数量
|
||||||
3. 创建docker镜像清单,自动识别平台
|
3. 创建docker镜像清单,自动识别平台
|
||||||
|
Reference in New Issue
Block a user