This commit is contained in:
snltty
2025-07-08 14:41:37 +08:00
parent c2ad42ebee
commit b45127607f
31 changed files with 296 additions and 43 deletions

View File

@@ -96,6 +96,7 @@
- 仰望 * 2
- 李氏の天下
- 小猪
- 菜菜(木子)
</div>
</details>

View File

@@ -6,7 +6,7 @@ sidebar_position: 10
:::tip[说明]
1. 首次运行手动初始化的客户端拥有完全管理权限,可导出配置,用以作为组网其它设备运行
2. 可以指定导出的设备名,管理端口,管理密码,和本客户端的其它配置信息,比如设定好的中继密钥什么的
2. 可以指定导出的设备名,管理端口,管理密码,和本客户端的其它配置信息
3. 可以指定导出的配置拥有什么权限
4. `下载`则导出压缩包,可以复制到其它电脑上直接运行
5. `复制`导出的配置复制到剪贴板,对应初始化配置时的`粘贴配置`

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 126 KiB

View File

@@ -18,6 +18,7 @@ sidebar_position: 96
- 仰望 * 2
- 李氏の天下
- 小猪
- 菜菜(木子)
</div>
</details>

View File

@@ -1,5 +1,4 @@
using linker.libs.extends;
using linker.messenger.relay.client.transport;
using linker.messenger.relay.server;
using linker.messenger.relay.server.validator;
using linker.messenger.sforward;
@@ -53,6 +52,10 @@ namespace linker.messenger.action
/// </summary>
public string GroupId { get; set; } = string.Empty;
/// <summary>
/// 用户id
/// </summary>
public string UserId { get; set; } = string.Empty;
/// <summary>
/// 超级管理
/// </summary>
public bool Super { get; set; }
@@ -169,7 +172,8 @@ namespace linker.messenger.action
MachineName = signInfo.MachineName,
MachineKey = machineKey,
IPAddress = signInfo.Connection.Address.Address,
Super = signInfo.Super
Super = signInfo.Super,
UserId = signInfo.UserId
}
};
return await actionTransfer.ExcuteActions(Replace(replace, str), actionServerStore.SignInActionUrl).ConfigureAwait(false);
@@ -221,7 +225,8 @@ namespace linker.messenger.action
MachineName = fromMachine.MachineName,
MachineKey = machineKey,
IPAddress = fromMachine.Connection.Address.Address,
Super = fromMachine.Super
Super = fromMachine.Super,
UserId = fromMachine.UserId
}
};
return await actionTransfer.ExcuteActions(Replace(replace, str), actionServerStore.RelayActionUrl).ConfigureAwait(false);
@@ -256,7 +261,8 @@ namespace linker.messenger.action
MachineName = fromMachine.MachineName,
MachineKey = machineKey,
IPAddress = fromMachine.Connection.Address.Address,
Super = fromMachine.Super
Super = fromMachine.Super,
UserId = fromMachine.UserId
}
};
string ids = await actionTransfer.ExcuteActions(Replace(replace, str), actionServerStore.RelayNodeUrl).ConfigureAwait(false);
@@ -302,7 +308,8 @@ namespace linker.messenger.action
MachineName = cache.MachineName,
MachineKey = machineKey,
IPAddress = cache.Connection.Address.Address,
Super = cache.Super
Super = cache.Super,
UserId = cache.UserId
}
};
return await actionTransfer.ExcuteActions(Replace(replace, str), actionServerStore.SForwardActionUrl).ConfigureAwait(false);

View File

@@ -54,6 +54,7 @@ namespace linker.messenger.flow
{
Connection = signInClientState.Connection,
MessengerId = messengerId,
Payload = serializer.Serialize(param.Content)
}).ConfigureAwait(false);
if (resp.Code == MessageResponeCodes.OK && resp.Data.Length > 0)
{
@@ -87,6 +88,7 @@ namespace linker.messenger.flow
{
Connection = signInClientState.Connection,
MessengerId = messengerId,
Payload = serializer.Serialize(param.Content)
}).ConfigureAwait(false);
if (resp.Code == MessageResponeCodes.OK && resp.Data.Length > 0)
{

View File

@@ -24,6 +24,8 @@ namespace linker.messenger.serializer.memorypack
MemoryPackFormatterProvider.Register(new SignInResponseInfoFormatter());
MemoryPackFormatterProvider.Register(new SignInConfigSetNameInfoFormatter());
MemoryPackFormatterProvider.Register(new SignInNamesResponseItemInfoFormatter());
MemoryPackFormatterProvider.Register(new SignInUserIdsResponseItemInfoFormatter());
MemoryPackFormatterProvider.Register(new SignInPushArgInfoFormatter());

View File

@@ -493,7 +493,59 @@ namespace linker.messenger.serializer.memorypack
value = wrapped.info;
}
}
[MemoryPackable]
public readonly partial struct SerializableSignInUserIdsResponseItemInfo
{
[MemoryPackIgnore]
public readonly SignInUserIdsResponseItemInfo info;
[MemoryPackInclude]
string UserId => info.UserId;
[MemoryPackInclude]
string MachineName => info.MachineName;
[MemoryPackInclude]
bool Online => info.Online;
[MemoryPackConstructor]
SerializableSignInUserIdsResponseItemInfo(string userId, string machineName, bool online)
{
var info = new SignInUserIdsResponseItemInfo { UserId = userId, MachineName = machineName, Online = online };
this.info = info;
}
public SerializableSignInUserIdsResponseItemInfo(SignInUserIdsResponseItemInfo signInfo)
{
this.info = signInfo;
}
}
public class SignInUserIdsResponseItemInfoFormatter : MemoryPackFormatter<SignInUserIdsResponseItemInfo>
{
public override void Serialize<TBufferWriter>(ref MemoryPackWriter<TBufferWriter> writer, scoped ref SignInUserIdsResponseItemInfo value)
{
if (value == null)
{
writer.WriteNullObjectHeader();
return;
}
writer.WritePackable(new SerializableSignInUserIdsResponseItemInfo(value));
}
public override void Deserialize(ref MemoryPackReader reader, scoped ref SignInUserIdsResponseItemInfo value)
{
if (reader.PeekIsNull())
{
reader.Advance(1); // skip null block
value = null;
return;
}
var wrapped = reader.ReadPackable<SerializableSignInUserIdsResponseItemInfo>();
value = wrapped.info;
}
}
[MemoryPackable]

View File

@@ -42,6 +42,7 @@
<ProjectReference Include="..\linker.messenger.plan\linker.messenger.plan.csproj" />
<ProjectReference Include="..\linker.messenger.signin\linker.messenger.signin.csproj" />
<ProjectReference Include="..\linker.messenger.sync\linker.messenger.sync.csproj" />
<ProjectReference Include="..\linker.messenger.wlist\linker.messenger.wlist.csproj" />
<ProjectReference Include="..\linker.messenger\linker.messenger.csproj" />
</ItemGroup>
</Project>

View File

@@ -1,4 +1,5 @@
using linker.messenger.signin;
using linker.messenger.wlist;
namespace linker.messenger.sforward.server.validator
{
@@ -10,14 +11,19 @@ namespace linker.messenger.sforward.server.validator
public string Name => "default";
private readonly ISForwardServerStore sForwardServerStore;
public SForwardValidator(ISForwardServerStore sForwardServerStore)
private readonly IWhiteListServerStore whiteListServerStore;
public SForwardValidator(ISForwardServerStore sForwardServerStore, IWhiteListServerStore whiteListServerStore)
{
this.sForwardServerStore = sForwardServerStore;
this.whiteListServerStore = whiteListServerStore;
}
public async Task<string> Validate(SignCacheInfo signCacheInfo, SForwardAddInfo sForwardAddInfo)
{
if (signCacheInfo.Super == false)
List<string> sforward = await whiteListServerStore.Get("SForward", signCacheInfo.UserId);
string target = string.IsNullOrWhiteSpace(sForwardAddInfo.Domain) ? sForwardAddInfo.RemotePort.ToString() : sForwardAddInfo.Domain;
if (signCacheInfo.Super == false && sforward.Contains(target) == false)
{
return $"need super key and password";
}

View File

@@ -14,6 +14,7 @@ namespace linker.messenger.signin
serviceCollection.AddSingleton<SignInArgsSuperClient>();
serviceCollection.AddSingleton<SignInArgsMachineKeyClient>();
serviceCollection.AddSingleton<SignInArgsVersionClient>();
serviceCollection.AddSingleton<SignInArgsUserIdClient>();
serviceCollection.AddSingleton<SignInClientState>();
serviceCollection.AddSingleton<SignInClientTransfer>();
@@ -34,6 +35,7 @@ namespace linker.messenger.signin
serviceProvider.GetService<SignInArgsSuperClient>(),
serviceProvider.GetService<SignInArgsMachineKeyClient>(),
serviceProvider.GetService<SignInArgsVersionClient>(),
serviceProvider.GetService<SignInArgsUserIdClient>(),
});
linker.messenger.api.IWebServer apiServer = serviceProvider.GetService<linker.messenger.api.IWebServer>();
@@ -63,6 +65,7 @@ namespace linker.messenger.signin
serviceCollection.AddSingleton<SignInArgsSuperServer>();
serviceCollection.AddSingleton<SignInArgsMachineKeyServer>();
serviceCollection.AddSingleton<SignInArgsVersionServer>();
serviceCollection.AddSingleton<SignInArgsUserIdServer>();
serviceCollection.AddSingleton<SignInServerMessenger>();
serviceCollection.AddSingleton<SignInServerCaching>();
@@ -81,6 +84,7 @@ namespace linker.messenger.signin
serviceProvider.GetService<SignInArgsSuperServer>(),
serviceProvider.GetService<SignInArgsMachineKeyServer>(),
serviceProvider.GetService<SignInArgsVersionServer>(),
serviceProvider.GetService<SignInArgsUserIdServer>(),
});
return serviceProvider;

View File

@@ -137,6 +137,20 @@ namespace linker.messenger.signin
return new List<SignInNamesResponseItemInfo>();
}
public async Task<List<SignInUserIdsResponseItemInfo>> UserIds(ApiControllerParamsInfo param)
{
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = signInClientState.Connection,
MessengerId = (ushort)SignInMessengerIds.UserIds,
Payload = serializer.Serialize(param.Content)
}).ConfigureAwait(false);
if (resp.Code == MessageResponeCodes.OK)
{
return serializer.Deserialize<List<SignInUserIdsResponseItemInfo>>(resp.Data.Span);
}
return new List<SignInUserIdsResponseItemInfo>();
}
public async Task CheckSuper(ApiControllerParamsInfo param)
{

View File

@@ -212,6 +212,19 @@ namespace linker.messenger.signin
connection.Write(serializer.Serialize(list));
}
}
[MessengerId((ushort)SignInMessengerIds.UserIds)]
public void UserIds(IConnection connection)
{
string name = serializer.Deserialize<string>(connection.ReceiveRequestWrap.Payload.Span);
if (signCaching.TryGet(connection.Id, out SignCacheInfo cache) == false || cache.Super == false)
{
connection.Write(serializer.Serialize(new List<SignInUserIdsResponseItemInfo>()));
return;
}
List<SignInUserIdsResponseItemInfo> list = signCaching.Get(name, 10).Select(c => new SignInUserIdsResponseItemInfo { UserId = c.UserId, MachineName = c.MachineName, Online = c.Connected }).ToList();
connection.Write(serializer.Serialize(list));
}
[MessengerId((ushort)SignInMessengerIds.Exists)]
@@ -374,7 +387,6 @@ namespace linker.messenger.signin
public int Count { get; set; }
public List<SignInIdsResponseItemInfo> List { get; set; } = new List<SignInIdsResponseItemInfo>();
}
public sealed class SignInIdsResponseItemInfo
{
public string MachineId { get; set; }
@@ -387,6 +399,12 @@ namespace linker.messenger.signin
public string MachineName { get; set; }
public bool Online { get; set; }
}
public sealed class SignInUserIdsResponseItemInfo
{
public string UserId { get; set; }
public string MachineName { get; set; }
public bool Online { get; set; }
}
/// <summary>
/// 登录返回

View File

@@ -32,6 +32,8 @@
CheckSuper = 19,
UserIds = 20,
None = 99
}
}

View File

@@ -72,6 +72,8 @@ namespace linker.messenger.signin
cache.Args = signInfo.Args;
cache.GroupId = signInfo.GroupId;
cache.Super = signInfo.Super;
cache.UserId = signInfo.UserId;
signInStore.Update(cache);
signInStore.Confirm();
return string.Empty;
@@ -101,6 +103,10 @@ namespace linker.messenger.signin
{
return Clients.Values.ToList();
}
public List<SignCacheInfo> Get(string name,int count)
{
return Clients.Values.Where(c=>c.MachineName.Contains(name) || c.UserId.Contains(name)).Take(count).ToList();
}
public List<SignCacheInfo> Get(SignCacheInfo other)
{
return Clients.Values.Where(c => c.GroupId == other.GroupId).ToList();
@@ -176,7 +182,7 @@ namespace linker.messenger.signin
/// <summary>
/// 登录缓存对象
/// </summary>
public sealed class SignCacheInfo
public partial class SignCacheInfo
{
public string Id { get; set; }
/// <summary>
@@ -240,6 +246,7 @@ namespace linker.messenger.signin
public bool Super { get; set; }
public string UserId { get; set; } = string.Empty;
/// <summary>
@@ -258,15 +265,13 @@ namespace linker.messenger.signin
/// <summary>
/// 登录参数
/// </summary>
public sealed class SignInfo
public partial class SignInfo
{
public string MachineId { get; set; } = string.Empty;
public string MachineName { get; set; } = string.Empty;
public string GroupId { get; set; } = string.Empty;
public string Version { get; set; } = string.Empty;
public bool Super { get; set; } = false;
public Dictionary<string, string> Args { get; set; } = new Dictionary<string, string>();
public IConnection Connection { get; set; }

View File

@@ -56,3 +56,13 @@
}
}
namespace linker.messenger.signin
{
public partial class SignInfo
{
public bool Super { get; set; } = false;
}
}

View File

@@ -0,0 +1,54 @@
using linker.libs;
using linker.messenger;
namespace linker.messenger.signin.args
{
/// <summary>
/// 版本限制
/// </summary>
public sealed class SignInArgsUserIdClient : ISignInArgsClient
{
public string Name => "userid";
public SignInArgsLevel Level => SignInArgsLevel.Default;
private readonly ISignInClientStore signInClientStore;
public SignInArgsUserIdClient(ISignInClientStore signInClientStore)
{
this.signInClientStore = signInClientStore;
}
public async Task<string> Invoke(string host, Dictionary<string, string> args)
{
args.TryAdd("userid", signInClientStore.Server.UserId);
return await Task.FromResult(string.Empty);
}
}
public sealed class SignInArgsUserIdServer : ISignInArgsServer
{
public string Name => "userid";
public SignInArgsLevel Level => SignInArgsLevel.Default;
/// <summary>
/// 服务端调用
/// </summary>
/// <param name="signInfo">新登录参数</param>
/// <param name="cache">之前的登录信息</param>
/// <returns></returns>
public async Task<string> Validate(SignInfo signInfo, SignCacheInfo cache)
{
if (signInfo.Args.TryGetValue("userid", out string userid))
{
signInfo.UserId = userid;
}
return await Task.FromResult(string.Empty);
}
}
}
namespace linker.messenger.signin
{
public partial class SignInfo
{
public string UserId { get; set; } = string.Empty;
}
}

View File

@@ -9,7 +9,7 @@ namespace linker.messenger.store.file.cdkey
{
public sealed class CdkeyServerStore : ICdkeyServerStore
{
private string regex = @"([0-9]+|\?)-([0-9]+|\?)-([0-9]+|\?)\s+([0-9]+|\?):([0-9]+|\?):([0-9]+|\?)";
private readonly string regex = @"([0-9]+|\?)-([0-9]+|\?)-([0-9]+|\?)\s+([0-9]+|\?):([0-9]+|\?):([0-9]+|\?)";
private int index = 0;
private readonly Storefactory dBfactory;

View File

@@ -78,7 +78,7 @@ namespace linker.messenger.tuntap.cidr
{
try
{
Dictionary<IPAddress, string> ip2machine = tuntapDecenter.Infos.Values.ToDictionary(c => c.IP, c => c.MachineId);
Dictionary<IPAddress, string> ip2machine = tuntapDecenter.Infos.Values.Where(c=>c.Available).DistinctBy(c=>c.IP).ToDictionary(c => c.IP, c => c.MachineId);
CidrAddInfo<string>[] cidrs = maps.Select(c =>
{
ip2machine.TryGetValue(c.Dst, out string machineId);

View File

@@ -1,9 +1,9 @@
import { sendWebsocketMsg } from './request'
export const getFlows = (machineId) => {
export const getFlows = (machineId = '') => {
return sendWebsocketMsg('flow/GetFlows',machineId);
}
export const getMessengerFlows = (machineId) => {
export const getMessengerFlows = (machineId = '') => {
return sendWebsocketMsg('flow/GetMessengerFlows',machineId);
}
export const getSForwardFlows = (data) => {
@@ -15,7 +15,7 @@ export const getRelayFlows = (data) => {
export const getCitys = () => {
return sendWebsocketMsg('flow/GetCitys');
}
export const getStopwatch = (machineId) => {
export const getStopwatch = (machineId = '') => {
return sendWebsocketMsg('flow/GetStopwatch',machineId);
}
export const getForwardFlows = (data) => {

View File

@@ -38,3 +38,6 @@ export const getSignInNames = () => {
export const checkSignInKey = () => {
return sendWebsocketMsg('signIn/CheckSuper');
}
export const getSignInUserIds = (name) => {
return sendWebsocketMsg('signIn/UserIds',name);
}

View File

@@ -246,6 +246,7 @@ export default {
'server.wlistAddTime': 'Add time',
'server.wlistNodes': 'Value',
'server.wlistNodesRelay': 'Nodes',
'server.wlistNodesSForward': 'Port/Domain',
'server.wlistOper': 'Oper',
'server.wlistDelConfirm': 'Are you sure to delete',
'server.wlistAdd': 'Add',
@@ -253,7 +254,7 @@ export default {
'server.wlistSelected': 'Selected',
'server.sforwardSecretKey': 'Server forward secretKey',
'server.sforward': 'Server forward',
'server.sforwardText': 'The server forward can be used when the key is correct',
'server.updater':'Updater',

View File

@@ -337,13 +337,14 @@ export default {
'server.wlistAddTime': '添加时间',
'server.wlistNodes': '内容',
'server.wlistNodesRelay': '节点',
'server.wlistNodesSForward': '端口/域名',
'server.wlistOper': '操作',
'server.wlistDelConfirm': '确认删除吗?',
'server.wlistAdd': '添加白名单',
'server.wlistUnselect': '未选择',
'server.wlistSelected': '已选择',
'server.sforwardSecretKey': '服务器穿透密钥',
'server.sforward': '服务器穿透',
'server.sforwardText': '密钥正确时可使用服务器穿透',
'server.updater': '更新',

View File

@@ -1,21 +1,23 @@
<template>
<el-form-item :label="$t('server.relay')">
<div >
<div>
<div class="flex">
<div class="mgr-1">
<el-checkbox class="mgr-1" v-model="state.list.SSL" :label="$t('server.relaySSL')" @change="handleSave" />
<el-checkbox v-model="state.list.Disabled" :label="$t('server.relayDisable')" @change="handleSave" />
<el-checkbox class="mgr-1" v-model="state.list.Disabled" :label="$t('server.relayDisable')" @change="handleSave" />
<el-checkbox v-model="state.list.SSL" :label="$t('server.relaySSL')" @change="handleSave" />
</div>
<div class="mgr-1" :title="$t('server.relayUseCdkeyTitle')">
<el-checkbox v-model="state.list.UseCdkey" :label="$t('server.relayUseCdkey')" @change="handleSave" />
</div>
<a href="javascript:;" @click="state.showModes=true" class="mgr-1 delay a-line" :class="{red:state.nodes.length==0,green:state.nodes.length>0}">
{{$t('server.relayNodes')}} : {{state.nodes.length}}
</a>
<Sync class="mgl-1" name="RelaySecretKey"></Sync>
</div>
<div class="flex">
<WhiteList type="Relay"></WhiteList>
<div class="mgr-1" :title="$t('server.relayUseCdkeyTitle')">
<el-checkbox v-model="state.list.UseCdkey" :label="$t('server.relayUseCdkey')" @change="handleSave" />
</div>
<Cdkey type="Relay"></Cdkey>
<Nodes v-if="state.showModes" v-model="state.showModes" :data="state.nodes"></Nodes>
<Sync class="mgl-1" name="RelaySecretKey"></Sync>
</div>
</div>
</el-form-item>

View File

@@ -0,0 +1,39 @@
<template>
<el-form-item :label="$t('server.sforward')">
<div>
<div class="flex">
<!-- <Cdkey type="SForward"></Cdkey> -->
<WhiteList type="SForward"></WhiteList>
</div>
</div>
</el-form-item>
</template>
<script>
import { injectGlobalData } from '@/provide';
import { onUnmounted, provide, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n';
import Cdkey from './cdkey/Index.vue'
import WhiteList from './wlist/Index.vue';
export default {
components:{Cdkey,WhiteList},
setup(props) {
const {t} = useI18n();
const globalData = injectGlobalData();
const state = reactive({
});
const nodes = ref([]);
provide('nodes',nodes);
const handleSave = ()=>{
}
onUnmounted(()=>{
clearTimeout(state.timer);
});
return {globalData,state,handleSave}
}
}
</script>
<style lang="stylus" scoped>
</style>

View File

@@ -38,6 +38,7 @@
</el-form-item>
<el-form-item></el-form-item>
<RelayServers class="mgt-2"></RelayServers>
<SForwardServers class="mgt-2"></SForwardServers>
<Updater></Updater>
</el-form>
</div>
@@ -56,10 +57,11 @@ import { ElMessage } from 'element-plus';
import { computed, onMounted, reactive } from 'vue'
import Updater from './Updater.vue';
import RelayServers from './RelayServers.vue';
import SForwardServers from './SForwardServers.vue';
import { useI18n } from 'vue-i18n';
import Sync from '../sync/Index.vue'
export default {
components:{Updater,RelayServers,Sync},
components:{Updater,RelayServers,SForwardServers,Sync},
setup(props) {
const {t} = useI18n();
const globalData = injectGlobalData();

View File

@@ -3,14 +3,20 @@
<div>
<el-form ref="ruleFormRef" :model="state.ruleForm" :rules="state.rules" label-width="auto">
<el-form-item :label="$t('server.wlistUserId')" prop="UserId">
<el-input maxlength="36" show-word-limit v-model="state.ruleForm.UserId" />
<el-select v-model="state.ruleForm.UserId" filterable remote :loading="state.loading" :remote-method="handleUserIds" @change="handleUserIdChange">
<el-option v-for="(item, index) in state.userids" :key="index" :label="item.MachineName" :value="item.UserId">
</el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('server.wlistName')" prop="Name">
<el-input v-model="state.ruleForm.Name" />
</el-form-item>
<el-form-item :label="$t(`server.wlistNodes${state.ruleForm.Type}`)" prop="Nodes">
<el-form-item v-if="state.ruleForm.Type == 'Relay'" :label="$t(`server.wlistNodes${state.ruleForm.Type}`)" prop="Nodes">
<el-input type="textarea" :value="state.nodes" @click="handleShowNodes" readonly resize="none" rows="4"></el-input>
</el-form-item>
<el-form-item v-if="state.ruleForm.Type == 'SForward'" :label="$t(`server.wlistNodes${state.ruleForm.Type}`)" prop="Nodes">
<el-input type="textarea" v-model="state.ports" resize="none" rows="4" @change="handlePortChange"></el-input>
</el-form-item>
<el-form-item :label="$t('server.wlistRemark')" prop="Remark">
<el-input v-model="state.ruleForm.Remark" />
</el-form-item>
@@ -47,9 +53,10 @@
<script>
import { ElMessage } from 'element-plus';
import { computed, inject, reactive, ref, watch } from 'vue'
import { computed, inject, onMounted, reactive, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n';
import { wlistAdd } from '@/apis/wlist';
import { getSignInUserIds } from '@/apis/signin';
export default {
props: ['modelValue'],
emits: ['update:modelValue','success'],
@@ -78,7 +85,11 @@ export default {
Nodes: [{ required: true, message: "required", trigger: "blur" }],
},
showNodes:false,
nodeIds: []
nodeIds: [],
ports: editState.value.Type == 'SForward' ? editState.value.Nodes.join(',') : '',
userids:[]
});
watch(() => state.show, (val) => {
if (!val) {
@@ -99,6 +110,21 @@ export default {
state.ruleForm.Nodes = state.nodeIds;
state.showNodes = false;
}
const handlePortChange = ()=>{
state.ruleForm.Nodes = state.ports.split(',').map(c=>c.replace(/\s/g,'')).filter(c=>!!c);
}
const handleUserIdChange = ()=>{
try{
state.ruleForm.Name = state.userids.filter(c=>c.UserId == state.ruleForm.UserId)[0].MachineName
}catch(e){
}
}
const handleUserIds = (query)=>{
getSignInUserIds(query).then(data=>{
state.userids = data;
});
}
const ruleFormRef = ref(null);
const handleSave = ()=>{
@@ -115,7 +141,12 @@ export default {
});
});
}
return {state,nodes,handleShowNodes,srcFilterMethod,handleNodes,ruleFormRef,handleSave}
onMounted(()=>{
handleUserIds(state.ruleForm.UserId);
})
return {state,nodes,handleShowNodes,srcFilterMethod,handleNodes,ruleFormRef,handleSave,handlePortChange,handleUserIdChange,handleUserIds}
}
}
</script>

View File

@@ -23,7 +23,8 @@
<el-table-column prop="Name" :label="$t('server.wlistName')"></el-table-column>
<el-table-column prop="Nodes" :label="$t(`server.wlistNodes${state.page.Type}`)">
<template #default="scope">
<span>{{ scope.row.Nodes.map(c=>state.nodes[c]).join(',') }}</span>
<span v-if="scope.row.Type == 'Relay'">{{ scope.row.Nodes.map(c=>state.nodes[c]).join(',') }}</span>
<span v-else-if="scope.row.Type == 'SForward'">{{ scope.row.Nodes.join(',') }}</span>
</template>
</el-table-column>
<el-table-column prop="Remark" :label="$t('server.wlistRemark')"></el-table-column>

View File

@@ -111,14 +111,11 @@ export default {
webport: globalData.value.config.Client.CApi.WebPort,
relay:true,
sforward:true,
updater:true,
server:true,
super:true,
super:false,
group:true,
tunnel:true,
cdkey:true,
whitelist:true,
copyContent:'',
showCopy:false,
@@ -144,14 +141,11 @@ export default {
apipassword:state.apipassword,
webport:+state.webport,
relay:state.relay,
sforward:state.sforward,
updater:state.updater,
server:state.server,
super:state.super,
group:state.group,
tunnel:state.tunnel,
cdkey:state.cdkey,
whitelist:state.whitelist,
tunnel:state.tunnel
}
if(json.single){

View File

@@ -1,5 +1,5 @@
v1.8.6
2025-07-07 17:50:09
2025-07-08 14:41:36
1. 一些累计更新
2. 白名单
3. 优化防火墙