This commit is contained in:
snltty
2025-05-13 16:34:18 +08:00
parent f077ef760a
commit 36bb46e968
52 changed files with 199 additions and 159 deletions

View File

@@ -37,7 +37,7 @@ jobs:
release_name: v1.7.9.${{ steps.date.outputs.today }}
draft: false
prerelease: false
body: "1. 一些累计更新\r\n2. 优化安卓APP\r\n3. 新增防火墙用于网卡、端口转发、和socks5\r\n4. 优化重启清理数据,优化了网卡自启动问题\r\n5. 检测密钥是否正确\r\n6. 如果你设备很多,请尝试升级其中一个成功重启后再升级其它\r\n7. 先不要升级,先不要升级,先不要升级,等我先试试"
body: "1. 一些累计更新\r\n2. 优化安卓APP启动网卡时申请VPN权限有新路由时重建VPN\r\n3. 新增防火墙用于网卡、端口转发、和socks5\r\n4. 优化重启清理数据,优化了网卡自启动问题\r\n5. 检测密钥是否正确\r\n6. 如果你设备很多,请尝试升级其中一个成功重启后再升级其它"
- name: publish projects
run: ./publish.bat "C:\\Android\\android-sdk"
- name: upload-win-x86-oss

View File

@@ -75,6 +75,8 @@
<div align="center">
<p><img src="./readme/full.jpg"></p>
<p><img src="./readme/plan.jpg"></p>
<p><img src="./readme/firewall.jpg"></p>
</div>
## 🤝支持作者

BIN
readme/firewall.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -36,8 +36,6 @@ namespace linker.app
base.OnCreate(savedInstanceState);
ConfigureVpn();
intent = new Intent(this, typeof(ForegroundService));
StartForegroundService(intent);
}
@@ -56,22 +54,6 @@ namespace linker.app
}
}
private void ConfigureVpn()
{
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.BindVpnService) != Permission.Granted)
{
ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.BindVpnService }, 0);
}
var intent = VpnService.Prepare(this);
if (intent != null)
{
StartActivityForResult(intent, 0x0F);
}
else
{
OnActivityResult(0x0F, Result.Ok, null);
}
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
@@ -141,7 +123,6 @@ namespace linker.app
}
/// <summary>
/// 前台服务运行Linker
/// </summary>
@@ -255,6 +236,25 @@ namespace linker.app
TuntapTransfer tuntapTransfer = LinkerMessengerEntry.GetService<TuntapTransfer>();
tuntapTransfer.OnSetupBefore += () =>
{
try
{
MainThread.InvokeOnMainThreadAsync(() =>
{
if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.BindVpnService) != Permission.Granted)
{
ActivityCompat.RequestPermissions(Platform.CurrentActivity, new string[] { Manifest.Permission.BindVpnService }, 0);
}
Intent intent = VpnService.Prepare(this);
if (intent != null)
{
//Android.Widget.Toast.MakeText(this, "请允许Linker使用VPN服务", Android.Widget.ToastLength.Short)?.Show();
Platform.CurrentActivity.StartActivityForResult(intent, 0x0F);
}
}).Wait();
}
catch (Exception)
{
}
try
{
vpnIntent = new Intent(Android.App.Application.Context, typeof(VpnServiceLinker));
@@ -325,6 +325,14 @@ namespace linker.app
this.address = address;
this.prefixLength = prefixLength;
Build();
return true;
}
private void Build()
{
try
{
builder = new VpnService.Builder(vpnService);
builder.SetMtu(1420)
.AddAddress(address.ToString(), prefixLength)
@@ -342,9 +350,11 @@ namespace linker.app
vpnInput = new FileInputStream(vpnInterface.FileDescriptor);
vpnOutput = new FileOutputStream(vpnInterface.FileDescriptor);
fd = vpnInterface.Fd;
return true;
}
catch (Exception)
{ }
}
byte[] buffer = new byte[65 * 1024];
byte[] bufferWrite = new byte[65 * 1024];
@@ -353,7 +363,7 @@ namespace linker.app
length = 0;
try
{
while (fd > 0)
while (fd > 0 && vpnInput != null)
{
length = vpnInput.Read(buffer, 4, buffer.Length - 4);
if (length > 0)
@@ -377,7 +387,7 @@ namespace linker.app
private readonly object writeLockObj = new object();
public bool Write(ReadOnlyMemory<byte> buffer)
{
if (fd == 0) return false;
if (fd == 0 || vpnOutput == null) return false;
try
{
lock (writeLockObj)
@@ -403,6 +413,12 @@ namespace linker.app
vpnInterface?.Close();
vpnInterface = null;
vpnInput?.Dispose();
vpnInput = null;
vpnOutput?.Dispose();
vpnOutput = null;
fd = 0;
}
public void Refresh()
@@ -439,7 +455,15 @@ namespace linker.app
/// <param name="ips"></param>
public void AddRoute(LinkerTunDeviceRouteItem[] ips)
{
routes = ips.Select(c => new LinkerTunDeviceRouteItem { Address = c.Address, PrefixLength = c.PrefixLength }).ToArray();
LinkerTunDeviceRouteItem[] _routes = ips.Select(c => new LinkerTunDeviceRouteItem { Address = c.Address, PrefixLength = c.PrefixLength }).ToArray();
bool diff = _routes.Select(c => $"{c.Address}/{c.PrefixLength}").Except(routes.Select(c => $"{c.Address}/{c.PrefixLength}")).Any();
routes = _routes;
if (diff)
{
Shutdown();
Build();
}
}
public void RemoveRoute(LinkerTunDeviceRouteItem[] ips)
{
@@ -494,7 +518,6 @@ namespace linker.app
}
}
/// <summary>
/// 获取系统信息
/// </summary>
@@ -507,7 +530,6 @@ namespace linker.app
}
}
/// <summary>
/// 更新
/// </summary>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
.el-transfer.src-tranfer .el-transfer__buttons .el-button{display:block}.el-transfer.src-tranfer .el-transfer__buttons .el-button:nth-child(2){margin:1rem 0 0 0}.el-form-item[data-v-6c157ed4]{margin-bottom:1rem}.head[data-v-0679a99a]{margin-bottom:1rem;color:#555;border:1px solid #eee;padding:0 1rem 1rem 1rem}.firewall.el-table .action-1{color:green}.firewall.el-table .action-2{color:#c83f08}.firewall-setting-wrap[data-v-0411afe8]{padding:1rem;box-sizing:border-box;font-size:1.3rem}.firewall-setting-wrap .inner[data-v-0411afe8]{padding:1rem;width:100%;box-sizing:border-box}

View File

@@ -1 +1 @@
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>linker.web</title><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script><script defer="defer" src="js/chunk-vendors.59893432.js"></script><script defer="defer" src="js/app.085a999a.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.5b1ae1f9.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but linker.web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>linker.web</title><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script><script defer="defer" src="js/chunk-vendors.59893432.js"></script><script defer="defer" src="js/app.a10400b4.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.5b1ae1f9.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but linker.web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
"use strict";(self["webpackChunklinker_web"]=self["webpackChunklinker_web"]||[]).push([[291],{7332:function(e,n,a){a.r(n),a.d(n,{default:function(){return O}});var t=a(6768);const s={class:"net-wrap app-wrap"},l={class:"inner absolute flex flex-column flex-nowrap"},i={class:"head"},o={class:"body flex-1 relative"},c={class:"status"};function r(e,n,a,r,u,d){const g=(0,t.g2)("Head"),p=(0,t.g2)("List"),v=(0,t.g2)("Status");return(0,t.uX)(),(0,t.CE)("div",s,[(0,t.Lk)("div",l,[(0,t.Lk)("div",i,[(0,t.bF)(g)]),(0,t.Lk)("div",o,[(0,t.bF)(p)]),(0,t.Lk)("div",c,[(0,t.bF)(v,{config:!1})])])])}a(4114);var u=a(4232);const d=e=>((0,t.Qi)("data-v-6bfe19a3"),e=e(),(0,t.jt)(),e),g={class:"head-wrap"},p={class:"tools flex"},v={class:"label"},h=d((()=>(0,t.Lk)("span",{class:"flex-1"},null,-1))),f={style:{"margin-left":"1rem"}};function k(e,n,a,s,l,i){const o=(0,t.g2)("Refresh"),c=(0,t.g2)("el-icon"),r=(0,t.g2)("el-button"),d=(0,t.g2)("Background");return(0,t.uX)(),(0,t.CE)("div",g,[(0,t.Lk)("div",p,[(0,t.Lk)("span",v,"分组 : "+(0,u.v_)(s.state.group),1),h,(0,t.bF)(r,{size:"small",onClick:s.handleRefresh},{default:(0,t.k6)((()=>[(0,t.eW)(" 刷新(F5)"),(0,t.bF)(c,null,{default:(0,t.k6)((()=>[(0,t.bF)(o)])),_:1})])),_:1},8,["onClick"]),(0,t.Lk)("div",f,[(0,t.bF)(d,{name:"net"})])])])}var C=a(3830),m=a(144),b=a(7477),L=a(5096),_={components:{Edit:b.ffu,Refresh:b.C42,Background:L.A},setup(){const e=(0,C.B)(),n=(0,m.Kh)({server:(0,t.EW)((()=>e.value.config.Client.Server.Host)),group:(0,t.EW)((()=>e.value.config.Client.Group.Name))}),a=()=>{window.location.reload()};return{state:n,handleRefresh:a}}},w=a(1241);const S=(0,w.A)(_,[["render",k],["__scopeId","data-v-6bfe19a3"]]);var F=S;const x=e=>((0,t.Qi)("data-v-68d1c30a"),e=e(),(0,t.jt)(),e),T={class:"net-list-wrap flex flex-column absolute"},z={class:"flex-1 scrollbar"},E={class:"flex"},A=x((()=>(0,t.Lk)("div",{class:"flex-1"},null,-1))),I={class:"tuntap"},P={class:"page t-c"},B={class:"page-wrap t-c"};function R(e,n,a,s,l,i){const o=(0,t.g2)("DeviceName"),c=(0,t.g2)("UpdaterBtn"),r=(0,t.g2)("TuntapShow"),u=(0,t.g2)("el-pagination");return(0,t.uX)(),(0,t.CE)("div",T,[(0,t.Lk)("div",z,[(0,t.Lk)("ul",null,[((0,t.uX)(!0),(0,t.CE)(t.FK,null,(0,t.pI)(s.devices.page.List,((e,n)=>((0,t.uX)(),(0,t.CE)("li",{key:n},[(0,t.Lk)("dl",null,[(0,t.Lk)("dt",E,[(0,t.Lk)("div",null,[(0,t.bF)(o,{item:e},null,8,["item"])]),A,(0,t.Lk)("div",null,[(0,t.bF)(c,{config:!1,item:e},null,8,["item"])])]),(0,t.Lk)("dd",I,[s.tuntap.list[e.MachineId]?((0,t.uX)(),(0,t.Wv)(r,{key:0,item:e},null,8,["item"])):(0,t.Q3)("",!0)])])])))),128))])]),(0,t.Lk)("div",P,[(0,t.Lk)("div",B,[(0,t.bF)(u,{size:"small",background:"",layout:"prev,pager, next","pager-count":5,total:s.devices.page.Count,"page-size":s.devices.page.Request.Size,"current-page":s.devices.page.Request.Page,onCurrentChange:s.handlePageChange,onSizeChange:s.handlePageSizeChange,"page-sizes":[10,20,50,100,255]},null,8,["total","page-size","current-page","onCurrentChange","onSizeChange"])])])])}var D=a(8104),X=a(7985),y=a(9383),N=a(7115),U=a(263),Q=a(2573),W=a(9983),H={components:{StarFilled:b.BQ2,UpdaterBtn:N.A,DeviceName:U.A,TuntapShow:Q.A},setup(e){(0,C.B)();const n=(0,m.Kh)({}),{devices:a,machineId:s,_getSignList:l,_getSignList1:i,handleDeviceEdit:o,handlePageChange:c,handlePageSizeChange:r,handleDel:u,clearDevicesTimeout:d}=(0,X.r)(),{tuntap:g,_getTuntapInfo:p,handleTuntapRefresh:v,clearTuntapTimeout:h,handleTuntapEdit:f,sortTuntapIP:k}=(0,D.O)(),{_getUpdater:b,_subscribeUpdater:L,clearUpdaterTimeout:_}=(0,y.d)(),{connections:w,forwardConnections:S,_getForwardConnections:F,tuntapConnections:x,_getTuntapConnections:T,socks5Connections:z,_getSocks5Connections:E,handleTunnelConnections:A,clearConnectionsTimeout:I}=(0,W.L2)();return(0,t.sV)((()=>{c(),v(),l(),i(),p(),b(),L()})),(0,t.hi)((()=>{d(),h(),_()})),{state:n,devices:a,machineId:s,handlePageChange:c,handlePageSizeChange:r,tuntap:g}}};const K=(0,w.A)(H,[["render",R],["__scopeId","data-v-68d1c30a"]]);var j=K,q=a(5317),M=a(1387),V={components:{Head:F,List:j,Status:q.A},setup(){document.addEventListener("contextmenu",(function(e){e.preventDefault()}));const e=(0,C.B)(),n=(0,M.rd)();return(0,t.sV)((()=>{0==e.value.hasAccess("NetManager")&&n.push({name:"NoPermission"})})),{}}};const G=(0,w.A)(V,[["render",r],["__scopeId","data-v-6a3f3b43"]]);var O=G}}]);
"use strict";(self["webpackChunklinker_web"]=self["webpackChunklinker_web"]||[]).push([[373],{7332:function(e,n,a){a.r(n),a.d(n,{default:function(){return O}});var t=a(6768);const s={class:"net-wrap app-wrap"},l={class:"inner absolute flex flex-column flex-nowrap"},i={class:"head"},o={class:"body flex-1 relative"},c={class:"status"};function r(e,n,a,r,u,d){const g=(0,t.g2)("Head"),p=(0,t.g2)("List"),v=(0,t.g2)("Status");return(0,t.uX)(),(0,t.CE)("div",s,[(0,t.Lk)("div",l,[(0,t.Lk)("div",i,[(0,t.bF)(g)]),(0,t.Lk)("div",o,[(0,t.bF)(p)]),(0,t.Lk)("div",c,[(0,t.bF)(v,{config:!1})])])])}a(4114);var u=a(4232);const d=e=>((0,t.Qi)("data-v-6bfe19a3"),e=e(),(0,t.jt)(),e),g={class:"head-wrap"},p={class:"tools flex"},v={class:"label"},h=d((()=>(0,t.Lk)("span",{class:"flex-1"},null,-1))),f={style:{"margin-left":"1rem"}};function k(e,n,a,s,l,i){const o=(0,t.g2)("Refresh"),c=(0,t.g2)("el-icon"),r=(0,t.g2)("el-button"),d=(0,t.g2)("Background");return(0,t.uX)(),(0,t.CE)("div",g,[(0,t.Lk)("div",p,[(0,t.Lk)("span",v,"分组 : "+(0,u.v_)(s.state.group),1),h,(0,t.bF)(r,{size:"small",onClick:s.handleRefresh},{default:(0,t.k6)((()=>[(0,t.eW)(" 刷新(F5)"),(0,t.bF)(c,null,{default:(0,t.k6)((()=>[(0,t.bF)(o)])),_:1})])),_:1},8,["onClick"]),(0,t.Lk)("div",f,[(0,t.bF)(d,{name:"net"})])])])}var C=a(3830),m=a(144),b=a(7477),L=a(5096),_={components:{Edit:b.ffu,Refresh:b.C42,Background:L.A},setup(){const e=(0,C.B)(),n=(0,m.Kh)({server:(0,t.EW)((()=>e.value.config.Client.Server.Host)),group:(0,t.EW)((()=>e.value.config.Client.Group.Name))}),a=()=>{window.location.reload()};return{state:n,handleRefresh:a}}},w=a(1241);const S=(0,w.A)(_,[["render",k],["__scopeId","data-v-6bfe19a3"]]);var F=S;const x=e=>((0,t.Qi)("data-v-68d1c30a"),e=e(),(0,t.jt)(),e),T={class:"net-list-wrap flex flex-column absolute"},z={class:"flex-1 scrollbar"},E={class:"flex"},A=x((()=>(0,t.Lk)("div",{class:"flex-1"},null,-1))),I={class:"tuntap"},P={class:"page t-c"},B={class:"page-wrap t-c"};function R(e,n,a,s,l,i){const o=(0,t.g2)("DeviceName"),c=(0,t.g2)("UpdaterBtn"),r=(0,t.g2)("TuntapShow"),u=(0,t.g2)("el-pagination");return(0,t.uX)(),(0,t.CE)("div",T,[(0,t.Lk)("div",z,[(0,t.Lk)("ul",null,[((0,t.uX)(!0),(0,t.CE)(t.FK,null,(0,t.pI)(s.devices.page.List,((e,n)=>((0,t.uX)(),(0,t.CE)("li",{key:n},[(0,t.Lk)("dl",null,[(0,t.Lk)("dt",E,[(0,t.Lk)("div",null,[(0,t.bF)(o,{item:e},null,8,["item"])]),A,(0,t.Lk)("div",null,[(0,t.bF)(c,{config:!1,item:e},null,8,["item"])])]),(0,t.Lk)("dd",I,[s.tuntap.list[e.MachineId]?((0,t.uX)(),(0,t.Wv)(r,{key:0,item:e},null,8,["item"])):(0,t.Q3)("",!0)])])])))),128))])]),(0,t.Lk)("div",P,[(0,t.Lk)("div",B,[(0,t.bF)(u,{size:"small",background:"",layout:"prev,pager, next","pager-count":5,total:s.devices.page.Count,"page-size":s.devices.page.Request.Size,"current-page":s.devices.page.Request.Page,onCurrentChange:s.handlePageChange,onSizeChange:s.handlePageSizeChange,"page-sizes":[10,20,50,100,255]},null,8,["total","page-size","current-page","onCurrentChange","onSizeChange"])])])])}var D=a(8104),X=a(7985),y=a(9383),N=a(7115),U=a(263),Q=a(4877),W=a(9983),H={components:{StarFilled:b.BQ2,UpdaterBtn:N.A,DeviceName:U.A,TuntapShow:Q.A},setup(e){(0,C.B)();const n=(0,m.Kh)({}),{devices:a,machineId:s,_getSignList:l,_getSignList1:i,handleDeviceEdit:o,handlePageChange:c,handlePageSizeChange:r,handleDel:u,clearDevicesTimeout:d}=(0,X.r)(),{tuntap:g,_getTuntapInfo:p,handleTuntapRefresh:v,clearTuntapTimeout:h,handleTuntapEdit:f,sortTuntapIP:k}=(0,D.O)(),{_getUpdater:b,_subscribeUpdater:L,clearUpdaterTimeout:_}=(0,y.d)(),{connections:w,forwardConnections:S,_getForwardConnections:F,tuntapConnections:x,_getTuntapConnections:T,socks5Connections:z,_getSocks5Connections:E,handleTunnelConnections:A,clearConnectionsTimeout:I}=(0,W.L2)();return(0,t.sV)((()=>{c(),v(),l(),i(),p(),b(),L()})),(0,t.hi)((()=>{d(),h(),_()})),{state:n,devices:a,machineId:s,handlePageChange:c,handlePageSizeChange:r,tuntap:g}}};const K=(0,w.A)(H,[["render",R],["__scopeId","data-v-68d1c30a"]]);var j=K,q=a(5317),M=a(1387),V={components:{Head:F,List:j,Status:q.A},setup(){document.addEventListener("contextmenu",(function(e){e.preventDefault()}));const e=(0,C.B)(),n=(0,M.rd)();return(0,t.sV)((()=>{0==e.value.hasAccess("NetManager")&&n.push({name:"NoPermission"})})),{}}};const G=(0,w.A)(V,[["render",r],["__scopeId","data-v-6a3f3b43"]]);var O=G}}]);

View File

@@ -0,0 +1 @@
"use strict";(self["webpackChunklinker_web"]=self["webpackChunklinker_web"]||[]).push([[607],{91:function(e,n,t){t.r(n),t.d(n,{default:function(){return w}});var r=t(6768);const s={class:"firewall-setting-wrap flex flex-column h-100"},a={class:"inner"};function l(e,n,t,l,c,i){const u=(0,r.g2)("Firewall");return(0,r.uX)(),(0,r.CE)("div",s,[(0,r.Lk)("div",a,[(0,r.bF)(u)])])}var c=t(144),i=t(8827),u={components:{Firewall:i.A},setup(e,{emit:n}){const t=(0,c.Kh)({});return{state:t}}},o=t(1241);const f=(0,o.A)(u,[["render",l],["__scopeId","data-v-0411afe8"]]);var w=f}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -78,10 +78,12 @@ namespace linker.snat
}
IEnumerable<string> ids = c.SrcId.Split(',').Distinct();
return new LinkerFirewallRuleBuildInfo
{
SrcId = c.SrcId,
SrcIds = ids.Count() > 1 ? ids.ToHashSet() : null,
SrcId = ids.Count() <= 1 ? c.SrcId : string.Empty,
DstNetwork = NetworkHelper.ToNetworkValue(ip, prefixLength),
DstPrefixLength = NetworkHelper.ToPrefixValue(prefixLength),
DstPortStart = portStart,
@@ -93,6 +95,7 @@ namespace linker.snat
}
catch (Exception)
{
}
return null;
@@ -160,7 +163,7 @@ namespace linker.snat
//按顺序匹配规则
foreach (LinkerFirewallRuleBuildInfo item in buildedRules)
{
bool match = (item.SrcId == "*" || item.SrcId == srcId)
bool match = (item.SrcIds == null ? item.SrcId == "*" || item.SrcId == srcId : item.SrcIds.Contains("*") || item.SrcIds.Contains(srcId))
&& ((ip & item.DstPrefixLength) == item.DstNetwork)
&& ((port >= item.DstPortStart && port <= item.DstPortEnd) || item.DstPorts.Contains(port))
&& item.Protocol.HasFlag(_rotocol);
@@ -176,7 +179,8 @@ namespace linker.snat
sealed class LinkerFirewallRuleBuildInfo
{
public string SrcId { get; set; } = string.Empty;
public string SrcId { get; set; }
public HashSet<string> SrcIds { get; set; }
public uint DstNetwork { get; set; }
public uint DstPrefixLength { get; set; }

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<project ver="10" name="linker.tray.win" libEmbed="true" icon="..\linker\favicon.ico" ui="win" output="linker.tray.win.exe" CompanyName="snltty" FileDescription="linker.tray.win" LegalCopyright="Copyright (C) snltty 2024" ProductName="linker.tray.win" InternalName="linker.install.win" FileVersion="0.0.0.267" ProductVersion="0.0.0.267" publishDir="/dist/" dstrip="false" local="false" ignored="false">
<project ver="10" name="linker.tray.win" libEmbed="true" icon="..\linker\favicon.ico" ui="win" output="linker.tray.win.exe" CompanyName="snltty" FileDescription="linker.tray.win" LegalCopyright="Copyright (C) snltty 2024" ProductName="linker.tray.win" InternalName="linker.install.win" FileVersion="0.0.0.271" ProductVersion="0.0.0.271" publishDir="/dist/" dstrip="false" local="false" ignored="false">
<file name="main.aardio" path="main.aardio" comment="main.aardio"/>
<folder name="资源文件" path="res" embed="true" local="false" ignored="false">
<file name="favicon.ico" path="res\favicon.ico" comment="res\favicon.ico"/>

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
.el-form-item[data-v-49e775dd]{margin-bottom:1rem}.el-input-number--small[data-v-49e775dd]{width:10rem!important}.head[data-v-3294acdd]{margin-bottom:1rem;color:#555;border:1px solid #eee;padding:1rem}.firewall.el-table .action-1{color:green}.firewall.el-table .action-2{color:#c83f08}.firewall-setting-wrap[data-v-7a7faaa0]{padding:1rem;box-sizing:border-box;font-size:1.3rem}.firewall-setting-wrap .inner[data-v-7a7faaa0]{padding:1rem}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
.el-transfer.src-tranfer .el-transfer__buttons .el-button{display:block}.el-transfer.src-tranfer .el-transfer__buttons .el-button:nth-child(2){margin:1rem 0 0 0}.el-form-item[data-v-6c157ed4]{margin-bottom:1rem}.head[data-v-0679a99a]{margin-bottom:1rem;color:#555;border:1px solid #eee;padding:0 1rem 1rem 1rem}.firewall.el-table .action-1{color:green}.firewall.el-table .action-2{color:#c83f08}.firewall-setting-wrap[data-v-0411afe8]{padding:1rem;box-sizing:border-box;font-size:1.3rem}.firewall-setting-wrap .inner[data-v-0411afe8]{padding:1rem;width:100%;box-sizing:border-box}

View File

@@ -0,0 +1 @@
.el-form-item[data-v-23dfe420],.el-row[data-v-23dfe420]{width:100%}.el-form-item[data-v-23dfe420]:last-child{margin-bottom:0}@media screen and (max-width:768px){.el-col[data-v-23dfe420]{margin-top:.6rem}}.pages[data-v-63810b88]{padding:1rem 0 0 1rem}.page-wrap[data-v-63810b88]{display:inline-block}.logger-setting-wrap[data-v-63810b88]{padding:1rem;box-sizing:border-box}.logger-setting-wrap .inner[data-v-63810b88]{padding:1rem}.logger-setting-wrap .head[data-v-63810b88]{margin-bottom:1rem}.logger-setting-wrap .el-table .type-0{color:#00f}.logger-setting-wrap .el-table .type-1{color:#333}.logger-setting-wrap .el-table .type-2{color:#cd9906}.logger-setting-wrap .el-table .type-3{color:red}.logger-setting-wrap .el-table .type-4{color:red;font-weight:700}

View File

@@ -1 +1 @@
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>linker.web</title><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script><script defer="defer" src="js/chunk-vendors.59893432.js"></script><script defer="defer" src="js/app.085a999a.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.5b1ae1f9.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but linker.web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>
<!doctype html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>linker.web</title><link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin=""/><script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script><script defer="defer" src="js/chunk-vendors.59893432.js"></script><script defer="defer" src="js/app.a10400b4.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.5b1ae1f9.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but linker.web doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div></body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
"use strict";(self["webpackChunklinker_web"]=self["webpackChunklinker_web"]||[]).push([[23],{5738:function(e,n,t){t.r(n),t.d(n,{default:function(){return w}});var r=t(6768);const a={class:"firewall-setting-wrap flex flex-column h-100"},s={class:"inner"};function l(e,n,t,l,c,i){const u=(0,r.g2)("Firewall");return(0,r.uX)(),(0,r.CE)("div",a,[(0,r.Lk)("div",s,[(0,r.bF)(u)])])}var c=t(144),i=t(9864),u={components:{Firewall:i.A},setup(e,{emit:n}){const t=(0,c.Kh)({});return{state:t}}},o=t(1241);const f=(0,o.A)(u,[["render",l],["__scopeId","data-v-7a7faaa0"]]);var w=f}}]);

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
"use strict";(self["webpackChunklinker_web"]=self["webpackChunklinker_web"]||[]).push([[373],{7332:function(e,n,a){a.r(n),a.d(n,{default:function(){return O}});var t=a(6768);const s={class:"net-wrap app-wrap"},l={class:"inner absolute flex flex-column flex-nowrap"},i={class:"head"},o={class:"body flex-1 relative"},c={class:"status"};function r(e,n,a,r,u,d){const g=(0,t.g2)("Head"),p=(0,t.g2)("List"),v=(0,t.g2)("Status");return(0,t.uX)(),(0,t.CE)("div",s,[(0,t.Lk)("div",l,[(0,t.Lk)("div",i,[(0,t.bF)(g)]),(0,t.Lk)("div",o,[(0,t.bF)(p)]),(0,t.Lk)("div",c,[(0,t.bF)(v,{config:!1})])])])}a(4114);var u=a(4232);const d=e=>((0,t.Qi)("data-v-6bfe19a3"),e=e(),(0,t.jt)(),e),g={class:"head-wrap"},p={class:"tools flex"},v={class:"label"},h=d((()=>(0,t.Lk)("span",{class:"flex-1"},null,-1))),f={style:{"margin-left":"1rem"}};function k(e,n,a,s,l,i){const o=(0,t.g2)("Refresh"),c=(0,t.g2)("el-icon"),r=(0,t.g2)("el-button"),d=(0,t.g2)("Background");return(0,t.uX)(),(0,t.CE)("div",g,[(0,t.Lk)("div",p,[(0,t.Lk)("span",v,"分组 : "+(0,u.v_)(s.state.group),1),h,(0,t.bF)(r,{size:"small",onClick:s.handleRefresh},{default:(0,t.k6)((()=>[(0,t.eW)(" 刷新(F5)"),(0,t.bF)(c,null,{default:(0,t.k6)((()=>[(0,t.bF)(o)])),_:1})])),_:1},8,["onClick"]),(0,t.Lk)("div",f,[(0,t.bF)(d,{name:"net"})])])])}var C=a(3830),m=a(144),b=a(7477),L=a(5096),_={components:{Edit:b.ffu,Refresh:b.C42,Background:L.A},setup(){const e=(0,C.B)(),n=(0,m.Kh)({server:(0,t.EW)((()=>e.value.config.Client.Server.Host)),group:(0,t.EW)((()=>e.value.config.Client.Group.Name))}),a=()=>{window.location.reload()};return{state:n,handleRefresh:a}}},w=a(1241);const S=(0,w.A)(_,[["render",k],["__scopeId","data-v-6bfe19a3"]]);var F=S;const x=e=>((0,t.Qi)("data-v-68d1c30a"),e=e(),(0,t.jt)(),e),T={class:"net-list-wrap flex flex-column absolute"},z={class:"flex-1 scrollbar"},E={class:"flex"},A=x((()=>(0,t.Lk)("div",{class:"flex-1"},null,-1))),I={class:"tuntap"},P={class:"page t-c"},B={class:"page-wrap t-c"};function R(e,n,a,s,l,i){const o=(0,t.g2)("DeviceName"),c=(0,t.g2)("UpdaterBtn"),r=(0,t.g2)("TuntapShow"),u=(0,t.g2)("el-pagination");return(0,t.uX)(),(0,t.CE)("div",T,[(0,t.Lk)("div",z,[(0,t.Lk)("ul",null,[((0,t.uX)(!0),(0,t.CE)(t.FK,null,(0,t.pI)(s.devices.page.List,((e,n)=>((0,t.uX)(),(0,t.CE)("li",{key:n},[(0,t.Lk)("dl",null,[(0,t.Lk)("dt",E,[(0,t.Lk)("div",null,[(0,t.bF)(o,{item:e},null,8,["item"])]),A,(0,t.Lk)("div",null,[(0,t.bF)(c,{config:!1,item:e},null,8,["item"])])]),(0,t.Lk)("dd",I,[s.tuntap.list[e.MachineId]?((0,t.uX)(),(0,t.Wv)(r,{key:0,item:e},null,8,["item"])):(0,t.Q3)("",!0)])])])))),128))])]),(0,t.Lk)("div",P,[(0,t.Lk)("div",B,[(0,t.bF)(u,{size:"small",background:"",layout:"prev,pager, next","pager-count":5,total:s.devices.page.Count,"page-size":s.devices.page.Request.Size,"current-page":s.devices.page.Request.Page,onCurrentChange:s.handlePageChange,onSizeChange:s.handlePageSizeChange,"page-sizes":[10,20,50,100,255]},null,8,["total","page-size","current-page","onCurrentChange","onSizeChange"])])])])}var D=a(8104),X=a(7985),y=a(9383),N=a(7115),U=a(263),Q=a(4877),W=a(9983),H={components:{StarFilled:b.BQ2,UpdaterBtn:N.A,DeviceName:U.A,TuntapShow:Q.A},setup(e){(0,C.B)();const n=(0,m.Kh)({}),{devices:a,machineId:s,_getSignList:l,_getSignList1:i,handleDeviceEdit:o,handlePageChange:c,handlePageSizeChange:r,handleDel:u,clearDevicesTimeout:d}=(0,X.r)(),{tuntap:g,_getTuntapInfo:p,handleTuntapRefresh:v,clearTuntapTimeout:h,handleTuntapEdit:f,sortTuntapIP:k}=(0,D.O)(),{_getUpdater:b,_subscribeUpdater:L,clearUpdaterTimeout:_}=(0,y.d)(),{connections:w,forwardConnections:S,_getForwardConnections:F,tuntapConnections:x,_getTuntapConnections:T,socks5Connections:z,_getSocks5Connections:E,handleTunnelConnections:A,clearConnectionsTimeout:I}=(0,W.L2)();return(0,t.sV)((()=>{c(),v(),l(),i(),p(),b(),L()})),(0,t.hi)((()=>{d(),h(),_()})),{state:n,devices:a,machineId:s,handlePageChange:c,handlePageSizeChange:r,tuntap:g}}};const K=(0,w.A)(H,[["render",R],["__scopeId","data-v-68d1c30a"]]);var j=K,q=a(5317),M=a(1387),V={components:{Head:F,List:j,Status:q.A},setup(){document.addEventListener("contextmenu",(function(e){e.preventDefault()}));const e=(0,C.B)(),n=(0,M.rd)();return(0,t.sV)((()=>{0==e.value.hasAccess("NetManager")&&n.push({name:"NoPermission"})})),{}}};const G=(0,w.A)(V,[["render",r],["__scopeId","data-v-6a3f3b43"]]);var O=G}}]);

View File

@@ -0,0 +1 @@
"use strict";(self["webpackChunklinker_web"]=self["webpackChunklinker_web"]||[]).push([[607],{91:function(e,n,t){t.r(n),t.d(n,{default:function(){return w}});var r=t(6768);const s={class:"firewall-setting-wrap flex flex-column h-100"},a={class:"inner"};function l(e,n,t,l,c,i){const u=(0,r.g2)("Firewall");return(0,r.uX)(),(0,r.CE)("div",s,[(0,r.Lk)("div",a,[(0,r.bF)(u)])])}var c=t(144),i=t(8827),u={components:{Firewall:i.A},setup(e,{emit:n}){const t=(0,c.Kh)({});return{state:t}}},o=t(1241);const f=(0,o.A)(u,[["render",l],["__scopeId","data-v-0411afe8"]]);var w=f}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -250,8 +250,8 @@ export default {
'firewall.rule': 'Firewall rule',
'firewall.srcName': 'Src Device',
'firewall.dstCidr': 'IP',
'firewall.dstPort': 'Port',
'firewall.dstCidr': 'Dst IP',
'firewall.dstPort': 'Dst Port',
'firewall.protocol': 'Protocol',
'firewall.protocolall': 'Protocol all',
'firewall.actionall': 'Action all',
@@ -269,5 +269,7 @@ export default {
'firewall.delConfirm': 'Are you sure to delete this rule?',
'firewall.switch': 'Firewall switch,open or close',
'firewall.remark': 'Remark',
'firewall.unselect': 'All',
'firewall.selected': 'Selected',
}

View File

@@ -347,8 +347,8 @@ export default {
'firewall.rule': '防火墙协议',
'firewall.srcName': '源设备',
'firewall.dstCidr': 'IP',
'firewall.dstPort': '端口',
'firewall.dstCidr': '目标IP',
'firewall.dstPort': '目标端口',
'firewall.protocolall': '全部协议',
'firewall.protocol': '协议',
'firewall.actionall': '全部操作',
@@ -366,5 +366,7 @@ export default {
'firewall.delConfirm': '确认删除规则?',
'firewall.switch': '防火墙开关,是否开启防火墙',
'firewall.remark': '备注',
'firewall.unselect': '未选择',
'firewall.selected': '已选择',
}

View File

@@ -69,7 +69,7 @@
<router-link :to="{name:'FullAction'}"><img src="@/assets/login.svg" height="20" style="vertical-align: text-top;"/> {{$t('head.action')}}</router-link>
</el-dropdown-item>
<el-dropdown-item v-if="hasFirewall">
<router-link :to="{name:'FullFirewall'}"><img src="@/assets/anquan.svg" height="20" style="vertical-align: text-top;"/> {{$t('head.action')}}</router-link>
<router-link :to="{name:'FullFirewall'}"><img src="@/assets/anquan.svg" height="20" style="vertical-align: text-top;"/> {{$t('head.firewall')}}</router-link>
</el-dropdown-item>
<el-dropdown-item v-if="hasLogger">
<router-link :to="{name:'FullLogger'}"><img src="@/assets/rizhi.svg" height="20" style="vertical-align: text-top;"/> {{$t('head.logger')}}</router-link>

View File

@@ -28,7 +28,7 @@
</template>
</template>
<template v-else>
<strong>{{ tuntap.list[item.MachineId].IP }}</strong>
<strong class="disable" title="IP不生效可能是设备不在线">{{ tuntap.list[item.MachineId].IP }}</strong>
</template>
</a>
</div>

View File

@@ -3,28 +3,7 @@
<div>
<el-form ref="ruleFormRef" :model="state.ruleForm.Data" :rules="state.rules" label-width="auto">
<el-form-item :label="$t('firewall.srcName')" prop="SrcId">
<el-row class="w-100">
<el-col :span="12">
<el-select v-model="state.ruleForm.Data.SrcId" @change="handleMachineChange()"
filterable remote :loading="state.loading" :remote-method="handleMachineSearch">
<template #header>
<div class="t-c">
<div class="page-wrap">
<el-pagination small background layout="prev, pager, next"
:page-size="state.machineIds.Request.Size"
:total="state.machineIds.Count"
:pager-count="5"
:current-page="state.machineIds.Request.Page" @current-change="handleMachinePageChange" />
</div>
</div>
</template>
<el-option v-for="(item, index) in state.machineIds.List" :key="index" :label="item.MachineName" :value="item.MachineId">
</el-option>
</el-select>
</el-col>
<el-col :span="12"></el-col>
</el-row>
<el-input type="textarea" v-model="state.ruleForm.Data.SrcName" @click="handleSrcId" readonly resize="none" rows="2"></el-input>
</el-form-item>
<el-form-item></el-form-item>
<el-form-item :label="$t('firewall.dstCidr')" prop="DstCIDR">
@@ -90,14 +69,33 @@
</el-form>
</div>
</el-dialog>
<el-dialog class="options-center" :title="$t('firewall.srcName')" destroy-on-close v-model="state.showSrcName" width="54rem" top="2vh">
<div>
<el-transfer class="src-tranfer"
v-model="state.srcIdValues"
filterable
:filter-method="srcFilterMethod"
:data="state.srcIds"
:titles="[$t('firewall.unselect'), $t('firewall.selected')]"
:props="{
key: 'MachineId',
label: 'MachineName',
}"
/>
<div class="t-c w-100 mgt-1">
<el-button @click="state.showSrcName = false">{{$t('common.cancel')}}</el-button>
<el-button type="primary" @click="handleSrcName">{{$t('common.confirm')}}</el-button>
</div>
</div>
</el-dialog>
</template>
<script>
import { ElMessage } from 'element-plus';
import { inject, reactive, ref, watch } from 'vue'
import { inject, onMounted, reactive, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n';
import { addFirewall } from '@/apis/firewall';
import { getSignInIds } from '@/apis/signin';
import { getSignInIds, getSignInNames } from '@/apis/signin';
export default {
props: ['modelValue'],
emits: ['update:modelValue','success'],
@@ -108,13 +106,6 @@ export default {
const state = reactive({
show:true,
loading:false,
machineIds:{
Request: {
Page: 1, Size:10, Name: ''
},
Count: 0,
List: []
},
protocolChecks:[
(add.value.Data.Protocol & 1) ,
@@ -160,6 +151,10 @@ export default {
{label:t('firewall.actionAllow'),value:1},
{label:t('firewall.actionDeny'),value:2},
],
srcIds: [],
srcIdValues:[],
showSrcName:false,
});
watch(() => state.show, (val) => {
if (!val) {
@@ -185,45 +180,42 @@ export default {
});
}
const handleMachineChange = () => {
const machine = state.machineIds.List.find(c=>c.MachineId == state.ruleForm.Data.SrcId);
if(machine){
state.ruleForm.Data.SrcName = machine.MachineName;
const handleSrcId = ()=>{
state.srcIdValues = state.ruleForm.Data.SrcId.split(',').filter(c=>c);
state.showSrcName = true;
}
const handleSrcName = ()=>{
state.ruleForm.Data.SrcId = state.srcIdValues.join(',');
state.ruleForm.Data.SrcName = state.srcIds.filter(c=>state.srcIdValues.includes(c.MachineId)).map(c=>c.MachineName).join(',');
state.showSrcName = false;
}
const handleMachinePageChange = (page)=>{
state.machineIds.Request.Page = page;
_getMachineIds();
}
const handleMachineSearch = (name)=>{
state.machineIds.Request.Name = name;
_getMachineIds();
}
const _getMachineIds = ()=>{
const _getSignInNames = ()=>{
state.loading = true;
getSignInIds(state.machineIds.Request).then((res)=>{
getSignInNames().then((res)=>{
state.loading = false;
state.machineIds.Request = res.Request;
state.machineIds.Count = res.Count;
res.List.splice(0,0,{MachineId:'*',MachineName:'*'});
res.splice(0,0,{MachineId:'*',MachineName:'*'});
if(state.ruleForm.Data.SrcId){
if(res.List.filter(c=>c.MachineId == state.ruleForm.Data.SrcId).length == 0){
res.List.splice(1,0,{MachineId:state.ruleForm.Data.SrcId,MachineName:state.ruleForm.Data.SrcName});
}
}
state.machineIds.List = res.List;
state.srcIds = res;
}).catch((e)=>{
state.loading = false;
});
}
const srcFilterMethod = (query, item) => {
return item.MachineName.toLowerCase().includes(query.toLowerCase())
}
onMounted(()=>{
_getSignInNames();
});
return {state,ruleFormRef,handleSave,handleMachineSearch,handleMachinePageChange,handleMachineChange}
return {state,ruleFormRef,handleSave,srcFilterMethod,handleSrcId,handleSrcName}
}
}
</script>
<style lang="stylus">
.el-transfer.src-tranfer .el-transfer__buttons .el-button{display:block;}
.el-transfer.src-tranfer .el-transfer__buttons .el-button:nth-child(2){margin:1rem 0 0 0;}
</style>
<style lang="stylus" scoped>
.el-form-item{margin-bottom:1rem}
.el-input-number--small{width:10rem !important}
</style>

View File

@@ -1,6 +1,7 @@
<template>
<div class="head">
<div class="flex">
<div class="flex mgt-1">
<div>
<el-select v-model="state.search.Data.Action" @change="loadData" size="small" class="mgr-1" style="width: 9rem;">
<el-option :value="item.value" :label="item.label" v-for="(item,index) in state.actions"></el-option>
@@ -16,7 +17,8 @@
<el-option :value="item.value" :label="item.label" v-for="(item,index) in state.states"></el-option>
</el-select>
</div>
</div>
<div class="flex mgt-1">
<div>
<span>{{$t('firewall.srcName')}}/{{$t('firewall.dstCidr')}}/{{$t('firewall.dstPort')}}/{{$t('firewall.remark')}}</span>
<el-input v-model="state.search.Data.Str" @change="loadData" size="small" style="width:7rem"></el-input>
@@ -27,11 +29,12 @@
<div class="mgl-1">
<el-button type="success" size="small" :loading="state.loading" @click="handleAdd()">+</el-button>
</div>
</div>
<div class="flex-1"></div>
</div>
</div>
<div class="body flex-1 relative">
<el-table class="firewall" stripe border :data="state.data" size="small" :height="`${state.height}px`" width="100%" :row-class-name="tableRowClassName">
<el-table class="firewall" stripe border :data="state.data" size="small" :height="`${state.height}px`" :row-class-name="tableRowClassName">
<el-table-column prop="SrcName" :label="$t('firewall.srcName')" >
<template v-slot="scope">
<div class="ellipsis" :title="scope.row.SrcName">{{ scope.row.SrcName }}</div>
@@ -230,7 +233,7 @@ export default {
margin-bottom: 1rem;
color:#555;
border:1px solid #eee;
padding: 1rem;
padding:0 1rem 1rem 1rem;
}
</style>
<style lang="stylus">

View File

@@ -31,6 +31,8 @@ export default {
.inner {
padding: 1rem;
width 100%;
box-sizing: border-box;
}
}
</style>

View File

@@ -21,12 +21,11 @@
<Authors>snltty</Authors>
<Company>snltty</Company>
<Description>1. 一些累计更新
2. 优化安卓APP
2. 优化安卓APP启动网卡时申请VPN权限有新路由时重建VPN
3. 新增防火墙用于网卡、端口转发、和socks5
4. 优化重启清理数据,优化了网卡自启动问题
5. 检测密钥是否正确
6. 如果你设备很多,请尝试升级其中一个成功重启后再升级其它
7. 先不要升级,先不要升级,先不要升级,等我先试试</Description>
6. 如果你设备很多,请尝试升级其中一个成功重启后再升级其它</Description>
<Copyright>snltty</Copyright>
<PackageProjectUrl>https://github.com/snltty/linker</PackageProjectUrl>
<RepositoryUrl>https://github.com/snltty/linker</RepositoryUrl>

View File

@@ -1,9 +1,8 @@
v1.7.9
2025-05-13 10:22:20
2025-05-13 16:34:17
1. 一些累计更新
2. 优化安卓APP
2. 优化安卓APP启动网卡时申请VPN权限有新路由时重建VPN
3. 新增防火墙用于网卡、端口转发、和socks5
4. 优化重启清理数据,优化了网卡自启动问题
5. 检测密钥是否正确
6. 如果你设备很多,请尝试升级其中一个成功重启后再升级其它
7. 先不要升级,先不要升级,先不要升级,等我先试试