网速统计

This commit is contained in:
snltty
2024-09-24 16:23:26 +08:00
parent 85fd3a69c1
commit 0a16a79e9b
34 changed files with 671 additions and 242 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
#file-input[data-v-4892cd3c]{opacity:0;position:absolute;z-index:-1}.el-icon[data-v-4892cd3c]{font-size:1.6rem;vertical-align:middle;color:#555}#file-input[data-v-1b5cc276]{opacity:0;position:absolute;z-index:-1}.head[data-v-1b5cc276]{background-color:#f6f8fa;border-bottom:1px solid #d0d7de;box-shadow:1px 1px 4px rgba(0,0,0,.05);height:5rem;line-height:5rem}.head .logo[data-v-1b5cc276]{padding:.5rem 0 0 1rem}.head .logo img[data-v-1b5cc276]{vertical-align:top;height:4rem}.head .menu[data-v-1b5cc276]{padding-left:1rem;font-size:1.4rem}.head .menu li[data-v-1b5cc276]{box-sizing:border-box;padding:.5rem 0;margin-right:.5rem}.head .menu a[data-v-1b5cc276]{display:block;color:#333;padding:0 1rem;line-height:4rem}.head .menu a.router-link-active[data-v-1b5cc276],.head .menu a[data-v-1b5cc276]:hover{background-color:rgba(0,0,0,.1);font-weight:700}.head .menu a .el-icon[data-v-1b5cc276]{vertical-align:sub}.head .image[data-v-1b5cc276]{padding-right:1rem}body.sunny{background-image:url(../img/bg.a2158f12.jpg);background-repeat:no-repeat;background-size:cover;background-position:bottom;position:absolute;left:0;top:0;right:0;bottom:0}body.sunny .app-wrap{background-color:hsla(0,0%,100%,.5)}body.sunny .status-wrap{background-color:hsla(0,0%,96%,.3)}body.sunny .status-wrap .copy a{color:#333}body.sunny .el-table{background-color:hsla(0,0%,100%,.5)}body.sunny .head{background-color:rgba(246,248,250,.5)}body.sunny .el-table tr,body.sunny .el-table--striped .el-table__body tr.el-table__row--striped td.el-table__cell{background-color:rgba(246,248,250,.2)}.el-pagination__total,body.sunny .el-pagination__sizes,body.sunny .status-wrap .copy a{color:#000}body.sunny a{color:#576acf}.status-api-wrap[data-v-4470fcde]{padding-right:2rem}.status-api-wrap a[data-v-4470fcde]{color:#333}.status-api-wrap span[data-v-4470fcde]{border-radius:1rem;background-color:rgba(0,0,0,.1);padding:0 .6rem;margin-left:.2rem}.status-api-wrap.connected a[data-v-4470fcde]{color:green;font-weight:700}.status-api-wrap.connected span[data-v-4470fcde]{background-color:green;color:#fff}.status-api-wrap .el-icon[data-v-4470fcde]{vertical-align:text-top}.status-server-wrap[data-v-360b8353]{padding-right:.5rem}.status-server-wrap a[data-v-360b8353]{color:#333}.status-server-wrap a+a[data-v-360b8353]{margin-left:.6rem}.status-server-wrap.connected a[data-v-360b8353]{color:green;font-weight:700}.status-server-wrap .el-icon[data-v-360b8353]{vertical-align:text-bottom}.status-server-wrap a.download .el-icon[data-v-360b8353]{font-weight:700;margin-left:.3rem}.status-server-wrap a.download .el-icon.loading[data-v-360b8353]{animation:loading-360b8353 1s linear infinite}@keyframes loading-360b8353{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.el-col[data-v-bdd023b0]{text-align:left}.status-export-wrap[data-v-117ef7b0]{padding-right:2rem}.status-export-wrap a[data-v-117ef7b0]{color:#333}.status-export-wrap .el-icon[data-v-117ef7b0]{vertical-align:text-top}.status-export-wrap .el-col[data-v-117ef7b0]{text-align:left}a[data-v-56d38c60]{color:#666;text-decoration:underline}a.green[data-v-56d38c60]{color:green;font-weight:700}a.download[data-v-56d38c60]{margin-left:.6rem}a.download .el-icon[data-v-56d38c60]{vertical-align:middle;font-weight:700;margin-left:.3rem}a.download .el-icon.loading[data-v-56d38c60]{animation:loading-56d38c60 1s linear infinite}@keyframes loading-56d38c60{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.status-wrap[data-v-da8e5ef0]{border-top:1px solid #ddd;background-color:#f5f5f5;height:3rem;line-height:3rem;font-size:1.2rem;color:#555}.status-wrap .pay[data-v-da8e5ef0]{font-size:xxx-large}.status-wrap .pay img[data-v-da8e5ef0]{width:100%;margin:0}.status-wrap .copy[data-v-da8e5ef0]{padding-left:.5rem}.status-wrap .copy a[data-v-da8e5ef0]{color:#555}.status-wrap a.memory[data-v-da8e5ef0]{margin-right:.6rem}.status-wrap a.memory img[data-v-da8e5ef0]{height:3rem;vertical-align:bottom;margin-right:.1rem}.body[data-v-47b95c93]{padding:1rem 0 0 0}.footer[data-v-47b95c93]{padding:1rem 0}.body[data-v-c9a17ce0]{padding:1rem 0 0 0}.footer[data-v-c9a17ce0]{padding:1rem 0}.body[data-v-41d4c7eb],.el-card+.el-card[data-v-c9a17ce0]{margin-top:1rem}.footer[data-v-41d4c7eb]{margin-top:2rem}@media screen and (max-width:1000px){body .app-wrap[data-v-5ea3415a]{width:calc(100% - 40px);height:calc(100% - 40px);position:absolute;left:20px;top:20px;right:0;bottom:0;transform:none;max-width:calc(100% - 40px)}}.app-wrap[data-v-5ea3415a]{box-sizing:border-box;background-color:#fff;border:1px solid #d0d7de;width:81rem;max-width:80%;height:90%;position:absolute;left:50%;top:50%;transform:translateX(-50%) translateY(-50%)}

File diff suppressed because one or more lines are too long

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><script defer="defer" src="js/chunk-vendors.ac5be1e1.js"></script><script defer="defer" src="js/app.f8c0e614.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.acc92c6f.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><script defer="defer" src="js/chunk-vendors.ac5be1e1.js"></script><script defer="defer" src="js/app.b5b57425.js"></script><link href="css/chunk-vendors.d8267b33.css" rel="stylesheet"><link href="css/app.acc92c6f.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([[670],{3991:function(e,a,t){t.r(a),t.d(a,{default:function(){return M}});var n=t(6768);const l={class:"net-wrap app-wrap"},s={class:"inner absolute flex flex-column flex-nowrap"},i={class:"head"},r={class:"body flex-1 relative"},u={class:"status"};function d(e,a,t,d,c,o){const g=(0,n.g2)("Head"),p=(0,n.g2)("List"),v=(0,n.g2)("Status");return(0,n.uX)(),(0,n.CE)("div",l,[(0,n.Lk)("div",s,[(0,n.Lk)("div",i,[(0,n.bF)(g)]),(0,n.Lk)("div",r,[(0,n.bF)(p)]),(0,n.Lk)("div",u,[(0,n.bF)(v,{config:!1})])])])}t(4114);const c=e=>((0,n.Qi)("data-v-213d85ba"),e=e(),(0,n.jt)(),e),o={class:"head-wrap"},g={class:"tools flex"},p=c((()=>(0,n.Lk)("span",{class:"label"},"服务器 ",-1))),v=c((()=>(0,n.Lk)("span",{class:"flex-1"},null,-1))),h={style:{"margin-left":"1rem"}};function f(e,a,t,l,s,i){const r=(0,n.g2)("el-option"),u=(0,n.g2)("el-select"),d=(0,n.g2)("Refresh"),c=(0,n.g2)("el-icon"),f=(0,n.g2)("el-button"),k=(0,n.g2)("Background");return(0,n.uX)(),(0,n.CE)("div",o,[(0,n.Lk)("div",g,[p,(0,n.bF)(u,{modelValue:l.state.server,"onUpdate:modelValue":a[0]||(a[0]=e=>l.state.server=e),placeholder:"服务器",style:{width:"16rem"},size:"small"},{default:(0,n.k6)((()=>[((0,n.uX)(!0),(0,n.CE)(n.FK,null,(0,n.pI)(l.state.servers,(e=>((0,n.uX)(),(0,n.Wv)(r,{key:e.Host,label:e.Name,value:e.Host},null,8,["label","value"])))),128))])),_:1},8,["modelValue"]),v,(0,n.bF)(f,{size:"small",onClick:l.handleRefresh},{default:(0,n.k6)((()=>[(0,n.eW)(" 刷新(F5)"),(0,n.bF)(c,null,{default:(0,n.k6)((()=>[(0,n.bF)(d)])),_:1})])),_:1},8,["onClick"]),(0,n.Lk)("div",h,[(0,n.bF)(k,{name:"net"})])])])}var k=t(3830),m=t(144),b=t(7477),C=t(5096),L={components:{Edit:b.ffu,Refresh:b.C42,Background:C.A},setup(){const e=(0,k.B)(),a=(0,m.Kh)({server:"linker.snltty.com:1802",servers:[]});(0,n.wB)((()=>e.value.config.Client.Servers),(()=>{a.servers=(e.value.config.Client.Servers||[]).slice(0,1),a.server=e.value.config.Client.ServerInfo.Host}));const t=()=>{window.location.reload()};return{state:a,handleRefresh:t}}},w=t(1241);const S=(0,w.A)(L,[["render",f],["__scopeId","data-v-213d85ba"]]);var F=S;const _=e=>((0,n.Qi)("data-v-5052ddc7"),e=e(),(0,n.jt)(),e),x={class:"net-list-wrap flex flex-column absolute"},z={class:"flex-1 scrollbar"},I={class:"flex"},E=_((()=>(0,n.Lk)("div",{class:"flex-1"},null,-1))),T={class:"tuntap"},A={class:"page t-c"},B={class:"page-wrap t-c"};function P(e,a,t,l,s,i){const r=(0,n.g2)("DeviceName"),u=(0,n.g2)("UpdaterBtn"),d=(0,n.g2)("TuntapShow"),c=(0,n.g2)("el-pagination");return(0,n.uX)(),(0,n.CE)("div",x,[(0,n.Lk)("div",z,[(0,n.Lk)("ul",null,[((0,n.uX)(!0),(0,n.CE)(n.FK,null,(0,n.pI)(l.devices.page.List,((e,a)=>((0,n.uX)(),(0,n.CE)("li",{key:a},[(0,n.Lk)("dl",null,[(0,n.Lk)("dt",I,[(0,n.Lk)("div",null,[(0,n.bF)(r,{item:e},null,8,["item"])]),E,(0,n.Lk)("div",null,[(0,n.bF)(u,{config:!1,item:e},null,8,["item"])])]),(0,n.Lk)("dd",T,[l.tuntap.list[e.MachineId]?((0,n.uX)(),(0,n.Wv)(d,{key:0,item:e},null,8,["item"])):(0,n.Q3)("",!0)])])])))),128))])]),(0,n.Lk)("div",A,[(0,n.Lk)("div",B,[(0,n.bF)(c,{size:"small",background:"",layout:"prev,pager, next","pager-count":5,total:l.devices.page.Count,"page-size":l.devices.page.Request.Size,"current-page":l.devices.page.Request.Page,onCurrentChange:l.handlePageChange,onSizeChange:l.handlePageSizeChange,"page-sizes":[10,20,50,100,255]},null,8,["total","page-size","current-page","onCurrentChange","onSizeChange"])])])])}var y=t(8104),X=t(7985),R=t(9383),D=t(2126),H=t(886),N=t(3630),U={components:{StarFilled:b.BQ2,UpdaterBtn:D.A,DeviceName:H.A,TuntapShow:N.A},setup(e){(0,k.B)();const a=(0,m.Kh)({}),{devices:t,machineId:l,_getSignList:s,_getSignList1:i,handleDeviceEdit:r,handlePageChange:u,handlePageSizeChange:d,handleDel:c,clearDevicesTimeout:o}=(0,X.r)(),{tuntap:g,_getTuntapInfo:p,handleTuntapRefresh:v,clearTuntapTimeout:h,handleTuntapEdit:f,sortTuntapIP:b}=(0,y.O)(),{_getUpdater:C,clearUpdaterTimeout:L}=(0,R.d)();return(0,n.sV)((()=>{u(),v(),s(),i(),p(),C()})),(0,n.hi)((()=>{o(),h(),L()})),{state:a,devices:t,machineId:l,handlePageChange:u,handlePageSizeChange:d,tuntap:g}}};const V=(0,w.A)(U,[["render",P],["__scopeId","data-v-5052ddc7"]]);var K=V,Q=t(8406),W=t(1387),j={components:{Head:F,List:K,Status:Q.A},setup(){document.addEventListener("contextmenu",(function(e){e.preventDefault()}));const e=(0,k.B)(),a=(0,W.rd)();return(0,n.sV)((()=>{0==e.value.hasAccess("NetManager")&&a.push({name:"NoPermission"})})),{}}};const q=(0,w.A)(j,[["render",d],["__scopeId","data-v-6a3f3b43"]]);var M=q}}]);
"use strict";(self["webpackChunklinker_web"]=self["webpackChunklinker_web"]||[]).push([[530],{3991:function(e,a,t){t.r(a),t.d(a,{default:function(){return M}});var n=t(6768);const l={class:"net-wrap app-wrap"},s={class:"inner absolute flex flex-column flex-nowrap"},i={class:"head"},r={class:"body flex-1 relative"},u={class:"status"};function d(e,a,t,d,c,o){const g=(0,n.g2)("Head"),p=(0,n.g2)("List"),v=(0,n.g2)("Status");return(0,n.uX)(),(0,n.CE)("div",l,[(0,n.Lk)("div",s,[(0,n.Lk)("div",i,[(0,n.bF)(g)]),(0,n.Lk)("div",r,[(0,n.bF)(p)]),(0,n.Lk)("div",u,[(0,n.bF)(v,{config:!1})])])])}t(4114);const c=e=>((0,n.Qi)("data-v-213d85ba"),e=e(),(0,n.jt)(),e),o={class:"head-wrap"},g={class:"tools flex"},p=c((()=>(0,n.Lk)("span",{class:"label"},"服务器 ",-1))),v=c((()=>(0,n.Lk)("span",{class:"flex-1"},null,-1))),h={style:{"margin-left":"1rem"}};function f(e,a,t,l,s,i){const r=(0,n.g2)("el-option"),u=(0,n.g2)("el-select"),d=(0,n.g2)("Refresh"),c=(0,n.g2)("el-icon"),f=(0,n.g2)("el-button"),k=(0,n.g2)("Background");return(0,n.uX)(),(0,n.CE)("div",o,[(0,n.Lk)("div",g,[p,(0,n.bF)(u,{modelValue:l.state.server,"onUpdate:modelValue":a[0]||(a[0]=e=>l.state.server=e),placeholder:"服务器",style:{width:"16rem"},size:"small"},{default:(0,n.k6)((()=>[((0,n.uX)(!0),(0,n.CE)(n.FK,null,(0,n.pI)(l.state.servers,(e=>((0,n.uX)(),(0,n.Wv)(r,{key:e.Host,label:e.Name,value:e.Host},null,8,["label","value"])))),128))])),_:1},8,["modelValue"]),v,(0,n.bF)(f,{size:"small",onClick:l.handleRefresh},{default:(0,n.k6)((()=>[(0,n.eW)(" 刷新(F5)"),(0,n.bF)(c,null,{default:(0,n.k6)((()=>[(0,n.bF)(d)])),_:1})])),_:1},8,["onClick"]),(0,n.Lk)("div",h,[(0,n.bF)(k,{name:"net"})])])])}var k=t(3830),m=t(144),b=t(7477),C=t(5096),L={components:{Edit:b.ffu,Refresh:b.C42,Background:C.A},setup(){const e=(0,k.B)(),a=(0,m.Kh)({server:"linker.snltty.com:1802",servers:[]});(0,n.wB)((()=>e.value.config.Client.Servers),(()=>{a.servers=(e.value.config.Client.Servers||[]).slice(0,1),a.server=e.value.config.Client.ServerInfo.Host}));const t=()=>{window.location.reload()};return{state:a,handleRefresh:t}}},w=t(1241);const S=(0,w.A)(L,[["render",f],["__scopeId","data-v-213d85ba"]]);var F=S;const _=e=>((0,n.Qi)("data-v-5052ddc7"),e=e(),(0,n.jt)(),e),x={class:"net-list-wrap flex flex-column absolute"},z={class:"flex-1 scrollbar"},I={class:"flex"},E=_((()=>(0,n.Lk)("div",{class:"flex-1"},null,-1))),T={class:"tuntap"},A={class:"page t-c"},B={class:"page-wrap t-c"};function P(e,a,t,l,s,i){const r=(0,n.g2)("DeviceName"),u=(0,n.g2)("UpdaterBtn"),d=(0,n.g2)("TuntapShow"),c=(0,n.g2)("el-pagination");return(0,n.uX)(),(0,n.CE)("div",x,[(0,n.Lk)("div",z,[(0,n.Lk)("ul",null,[((0,n.uX)(!0),(0,n.CE)(n.FK,null,(0,n.pI)(l.devices.page.List,((e,a)=>((0,n.uX)(),(0,n.CE)("li",{key:a},[(0,n.Lk)("dl",null,[(0,n.Lk)("dt",I,[(0,n.Lk)("div",null,[(0,n.bF)(r,{item:e},null,8,["item"])]),E,(0,n.Lk)("div",null,[(0,n.bF)(u,{config:!1,item:e},null,8,["item"])])]),(0,n.Lk)("dd",T,[l.tuntap.list[e.MachineId]?((0,n.uX)(),(0,n.Wv)(d,{key:0,item:e},null,8,["item"])):(0,n.Q3)("",!0)])])])))),128))])]),(0,n.Lk)("div",A,[(0,n.Lk)("div",B,[(0,n.bF)(c,{size:"small",background:"",layout:"prev,pager, next","pager-count":5,total:l.devices.page.Count,"page-size":l.devices.page.Request.Size,"current-page":l.devices.page.Request.Page,onCurrentChange:l.handlePageChange,onSizeChange:l.handlePageSizeChange,"page-sizes":[10,20,50,100,255]},null,8,["total","page-size","current-page","onCurrentChange","onSizeChange"])])])])}var y=t(8104),X=t(7985),R=t(9383),D=t(2126),H=t(886),N=t(3630),U={components:{StarFilled:b.BQ2,UpdaterBtn:D.A,DeviceName:H.A,TuntapShow:N.A},setup(e){(0,k.B)();const a=(0,m.Kh)({}),{devices:t,machineId:l,_getSignList:s,_getSignList1:i,handleDeviceEdit:r,handlePageChange:u,handlePageSizeChange:d,handleDel:c,clearDevicesTimeout:o}=(0,X.r)(),{tuntap:g,_getTuntapInfo:p,handleTuntapRefresh:v,clearTuntapTimeout:h,handleTuntapEdit:f,sortTuntapIP:b}=(0,y.O)(),{_getUpdater:C,clearUpdaterTimeout:L}=(0,R.d)();return(0,n.sV)((()=>{u(),v(),s(),i(),p(),C()})),(0,n.hi)((()=>{o(),h(),L()})),{state:a,devices:t,machineId:l,handlePageChange:u,handlePageSizeChange:d,tuntap:g}}};const V=(0,w.A)(U,[["render",P],["__scopeId","data-v-5052ddc7"]]);var K=V,Q=t(6085),W=t(1387),j={components:{Head:F,List:K,Status:Q.A},setup(){document.addEventListener("contextmenu",(function(e){e.preventDefault()}));const e=(0,k.B)(),a=(0,W.rd)();return(0,n.sV)((()=>{0==e.value.hasAccess("NetManager")&&a.push({name:"NoPermission"})})),{}}};const q=(0,w.A)(j,[["render",d],["__scopeId","data-v-6a3f3b43"]]);var M=q}}]);

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,5 @@
import { sendWebsocketMsg } from './request'
export const getFlows = () => {
return sendWebsocketMsg('serverClient/GetFlows');
}

View File

@@ -1,237 +1,40 @@
<template>
<div class="status-server-wrap" :class="{ connected: state.connected }">
<a href="javascript:;" title="更改你的连接设置" @click="handleConfig"> <el-icon size="16"><Promotion /></el-icon> 信标服务器</a>
<a href="javascript:;" title="服务端的程序版本" @click="handleUpdate" class="download" :title="updateText()" :class="updateColor()">
<span>{{state.version}}</span>
<template v-if="updaterCurrent.Version">
<template v-if="updaterCurrent.Status == 1">
<el-icon size="14" class="loading"><Loading /></el-icon>
</template>
<template v-else-if="updaterServer.Status == 2">
<el-icon size="14"><Download /></el-icon>
</template>
<template v-else-if="updaterServer.Status == 3 || updaterServer.Status == 5">
<el-icon size="14" class="loading"><Loading /></el-icon>
<span class="progress" v-if="updaterServer.Length ==0">0%</span>
<span class="progress" v-else>{{parseInt(updaterServer.Current/updaterServer.Length*100)}}%</span>
</template>
<template v-else-if="updaterServer.Status == 6">
<el-icon size="14" class="yellow"><CircleCheck /></el-icon>
</template>
</template>
<template v-else>
<el-icon size="14"><Download /></el-icon>
</template>
</a>
<div class="status-server-wrap">
<ServerConfig :config="config"></ServerConfig>
<ServerVersion :config="config"></ServerVersion>
<ServerFlow :config="config"></ServerFlow>
</div>
<el-dialog v-model="state.show" title="连接设置" width="300">
<div>
<el-form :model="state.form" :rules="state.rules" label-width="6rem">
<el-form-item label="机器名" prop="name">
<el-input v-model="state.form.name" maxlength="12" show-word-limit />
</el-form-item>
<el-form-item label="分组名" prop="groupid">
<el-input v-model="state.form.groupid" type="password" show-password maxlength="36" show-word-limit />
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer t-c">
<el-button @click="state.show = false" :loading="state.loading">取消</el-button>
<el-button type="primary" @click="handleSave" :loading="state.loading">确定保存</el-button>
</div>
</template>
</el-dialog>
</template>
<script>
import { setSignIn } from '@/apis/signin';
import { injectGlobalData } from '@/provide';
import { ElMessage, ElMessageBox } from 'element-plus';
import { computed, onMounted, reactive, ref } from 'vue';
import {Promotion,Download,Loading,CircleCheck} from '@element-plus/icons-vue'
import { confirmServer, exitServer, getUpdaterCurrent, getUpdaterServer } from '@/apis/updater';
import { reactive } from 'vue';
import ServerConfig from './ServerConfig.vue';
import ServerFlow from './ServerFlow.vue';
import ServerVersion from './ServerVersion.vue';
export default {
components:{Promotion,Download,Loading,CircleCheck},
components:{ServerConfig,ServerFlow,ServerVersion},
props:['config'],
setup(props) {
const globalData = injectGlobalData();
const hasConfig = computed(()=>globalData.value.hasAccess('Config'));
const hasUpdateServer = computed(()=>globalData.value.hasAccess('UpdateServer'));
const updaterCurrent = ref({Version: '', Msg: [], DateTime: '', Status: 0, Length: 0, Current: 0});
const updaterServer = ref({Version: '', Status: 0, Length: 0, Current: 0});
const updaterMsg = computed(()=>{
return `${updaterCurrent.value.Version}->${updaterCurrent.value.DateTime}\n${updaterCurrent.value.Msg.map((value,index)=>`${index+1}${value}`).join('\n')}`;
});
const state = reactive({
show: false,
loading: false,
connected: computed(() => globalData.value.signin.Connected),
version: computed(() => globalData.value.signin.Version),
form: {
name: globalData.value.config.Client.Name,
groupid: globalData.value.config.Client.GroupId,
},
rules: {},
});
const _getUpdaterCurrent = ()=>{
getUpdaterCurrent().then((res)=>{
updaterCurrent.value.DateTime = res.DateTime;
updaterCurrent.value.Version = res.Version;
updaterCurrent.value.Status = res.Status;
updaterCurrent.value.Length = res.Length;
updaterCurrent.value.Current = res.Current;
updaterCurrent.value.Msg = res.Msg;
setTimeout(()=>{
_getUpdaterCurrent();
},1000);
}).catch(()=>{
setTimeout(()=>{
_getUpdaterCurrent();
},1000);
})
}
const _getUpdaterServer = ()=>{
getUpdaterServer().then((res)=>{
updaterServer.value.Version = res.Version;
updaterServer.value.Status = res.Status;
updaterServer.value.Length = res.Length;
updaterServer.value.Current = res.Current;
if(updaterServer.value.Status > 2 && updaterServer.value.Status < 6){
setTimeout(()=>{
_getUpdaterServer();
},1000);
}
}).catch(()=>{
setTimeout(()=>{
_getUpdaterServer();
},1000);
});
}
const updateText = ()=>{
if(!updaterCurrent.value.Version){
return '未检测到更新';
}
if(updaterServer.value.Status <= 2) {
return state.version != updaterCurrent.value.Version
? `不是最新版本(${updaterCurrent.value.Version}),建议更新\n${updaterMsg.value}`
: `是最新版本,但我无法阻止你喜欢更新\n${updaterMsg.value}`
}
return {
3:'正在下载',
4:'已下载',
5:'正在解压',
6:'已解压,请重启',
}[updaterServer.value.Status];
}
const updateColor = ()=>{
return state.version != updaterCurrent.value.Version ? 'yellow' :'green'
}
const handleUpdate = ()=>{
if(!props.config || !hasUpdateServer.value){
return;
}
if(!updaterCurrent.value.Version){
ElMessage.error('未检测到更新');
return;
}
//未检测,检测中,下载中,解压中
if([0,1,3,5].indexOf(updaterServer.value.Status)>=0){
ElMessage.error('操作中,请稍后!');
return;
}
//已解压
if(updaterServer.value.Status == 6){
ElMessageBox.confirm('确定关闭服务端吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
exitServer();
}).catch(() => {});
return;
}
//已检测
if(updaterCurrent.value.Status == 2){
ElMessageBox.confirm('确定更新服务端吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
confirmServer(updaterCurrent.value.Version).then(()=>{
setTimeout(()=>{
_getUpdaterServer();
},1000);
});
}).catch(() => {});
}
}
const handleConfig = () => {
if(!props.config || !hasConfig.value){
return;
}
state.form.name = globalData.value.config.Client.Name;
state.form.groupid = globalData.value.config.Client.GroupId;
state.show = true;
}
const handleSave = () => {
state.loading = true;
setSignIn(state.form).then(() => {
state.loading = false;
state.show = false;
ElMessage.success('已操作');
}).catch((err) => {
state.loading = false;
ElMessage.success('操作失败!');
});
}
onMounted(() => {
_getUpdaterCurrent();
_getUpdaterServer();
loading: false
});
return {
config:props.config, state, handleConfig, handleSave,updaterCurrent,updaterServer,handleUpdate,updateText,updateColor
config:props.config, state
}
}
}
</script>
<style lang="stylus" scoped>
@keyframes loading {
from{transform:rotate(0deg)}
to{transform:rotate(360deg)}
}
.status-server-wrap{
position:relative;
padding-right:.5rem;
a{color:#333;}
a+a{margin-left:.6rem;}
&.connected {
a{color:green;font-weight:bold;}
}
.el-icon{
vertical-align:text-bottom;
}
a.download{
.el-icon{
font-weight:bold;
&.loading{
animation:loading 1s linear infinite;
}
margin-left:.3rem
}
}
}
</style>

View File

@@ -0,0 +1,85 @@
<template>
<a href="javascript:;" :class="{connected:state.connected}" title="更改你的连接设置" @click="handleConfig">
<el-icon size="16"><Promotion /></el-icon> <span>信标服务器</span>
</a>
<el-dialog v-model="state.show" title="连接设置" width="300" append-to-body>
<div>
<el-form :model="state.form" :rules="state.rules" label-width="6rem">
<el-form-item label="机器名" prop="name">
<el-input v-model="state.form.name" maxlength="12" show-word-limit />
</el-form-item>
<el-form-item label="分组名" prop="groupid">
<el-input v-model="state.form.groupid" type="password" show-password maxlength="36" show-word-limit />
</el-form-item>
</el-form>
</div>
<template #footer>
<div class="dialog-footer t-c">
<el-button @click="state.show = false" :loading="state.loading">取消</el-button>
<el-button type="primary" @click="handleSave" :loading="state.loading">确定保存</el-button>
</div>
</template>
</el-dialog>
</template>
<script>
import { setSignIn } from '@/apis/signin';
import { injectGlobalData } from '@/provide';
import { ElMessage } from 'element-plus';
import { computed, reactive, ref } from 'vue';
import {Promotion} from '@element-plus/icons-vue'
export default {
components:{Promotion},
props:['config'],
setup(props) {
const globalData = injectGlobalData();
const hasConfig = computed(()=>globalData.value.hasAccess('Config'));
const state = reactive({
show: false,
loading: false,
connected: computed(() => globalData.value.signin.Connected),
form: {
name: globalData.value.config.Client.Name,
groupid: globalData.value.config.Client.GroupId,
},
rules: {},
});
const handleConfig = () => {
if(!props.config || !hasConfig.value){
return;
}
state.form.name = globalData.value.config.Client.Name;
state.form.groupid = globalData.value.config.Client.GroupId;
state.show = true;
}
const handleSave = () => {
state.loading = true;
setSignIn(state.form).then(() => {
state.loading = false;
state.show = false;
ElMessage.success('已操作');
}).catch((err) => {
state.loading = false;
ElMessage.success('操作失败!');
});
}
return {
config:props.config, state, handleConfig, handleSave
}
}
}
</script>
<style lang="stylus" scoped>
a{
a{color:#333;}
a{margin-left:.6rem;}
&.connected {
color:green;font-weight:bold;
}
.el-icon{
vertical-align:text-bottom;
}
}
</style>

View File

@@ -0,0 +1,191 @@
<template>
<a v-if="config" href="javascript:;" title="linker服务端网速点击查看详细信息" @click="handleShow">
<p>上传 {{state.overallSendtSpeed}}</p>
<p>下载 {{state.overallReceiveSpeed}}</p>
</a>
<el-dialog class="options-center" :title="state.time" destroy-on-close v-model="state.show" center width="580" top="1vh">
<div>
<el-table :data="state.list" border size="small" width="100%" height="60vh">
<el-table-column prop="id" label="信标id"></el-table-column>
<el-table-column prop="sendtBytes" label="总上传字节" sortable></el-table-column>
<el-table-column prop="sendtText" label="上传速度" sortable></el-table-column>
<el-table-column prop="receiveBytes" label="总下载字节" sortable></el-table-column>
<el-table-column prop="receiveText" label="下载速度" sortable></el-table-column>
</el-table>
</div>
</el-dialog>
</template>
<script>
import { getFlows } from '@/apis/server';
import { onMounted, onUnmounted, reactive } from 'vue';
export default {
props:['config'],
setup (props) {
const state = reactive({
show:true,
timer:0,
overallSendtSpeed: '0000.00KB/s',
overallReceiveSpeed: '0000.00KB/s',
time:'',
list:[],
old:null
});
const handleShow = ()=>{
state.show = true;
}
const id2text = {
'External':'外网端口',
'Relay':'中继',
'Messenger':'信标总计',
'0':'登入信标',
'1':'客户端列表',
'2':'客户端删除',
'3':'客户端改名',
'4':'客户端改名转发',
'7':'服务器版本',
'8':'客户端搜索ids',
'9':'客户端id列表',
'10':'客户端排序',
'11':'客户端在线',
'12':'生成客户端id',
'13':'登入信标V_1_3_1',
'2001':'外网端口',
'2002':'外网端口转发',
'2003':'开始打洞',
'2004':'开始打洞转发',
'2005':'打洞失败',
'2006':'打洞失败转发',
'2007':'打洞成功',
'2008':'打洞成功转发',
'2009':'隧道配置',
'2010':'隧道配置转发',
'2011':'隧道同步',
'2012':'隧道同步转发',
'2101':'收到中继',
'2102':'收到中继转发',
'2103':'中继请求',
'2104':'中继请求转发',
'2105':'中继测试',
'2200':'运行网卡',
'2201':'运行网卡转发',
'2202':'停止网卡',
'2203':'停止网卡转发',
'2204':'更新网卡',
'2205':'更新网卡转发',
'2206':'同步网卡',
'2207':'同步网卡转发',
'2301':'添加穿透',
'2302':'移除穿透',
'2303':'收到TCP穿透',
'2304':'收到UDP穿透',
'2305':'获取穿透列表',
'2306':'被获取穿透列表',
'2401':'测试端口转发',
'2402':'被测试端口转发',
'2403':'获取端口转发列表',
'2404':'被获取端口转发列表',
'2503':'被获取权限',
'2504':'获取权限转发',
'2505':'更新权限',
'2506':'更新权限转发',
'2507':'同步密钥',
'2508':'同步密钥转发',
'2509':'同步服务器',
'2510':'同步服务器转发',
'2601':'更新信息转发',
'2602':'更新信息',
'2603':'确认更新转发',
'2604':'更新转发',
'2605':'重启',
'2606':'重启转发',
'2607':'服务器更新信息',
'2608':'确认服务器更新',
'2609':'服务器重启',
}
const _getFlows = ()=>{
getFlows().then(res => {
const old = state.old || res;
let _receiveBytes = 0,_sendtBytes = 0,receiveBytes = 0,sendtBytes = 0;
for(let j in old.Resolvers){
_receiveBytes+=old.Resolvers[j].ReceiveBytes;
_sendtBytes+=old.Resolvers[j].SendtBytes;
}
for(let j in res.Resolvers){
receiveBytes+=res.Resolvers[j].ReceiveBytes;
sendtBytes+=res.Resolvers[j].SendtBytes;
}
state.overallSendtSpeed = parseSpeed(sendtBytes-_sendtBytes);
state.overallReceiveSpeed = parseSpeed(receiveBytes-_receiveBytes);
state.time = `linker 启动于 ${res.Start} 至今`;
const list = [];
for(let j in res.Resolvers){
list.push({
id:id2text[`${j}`],
sendtBytes:res.Resolvers[j].ReceiveBytes,
sendtSpeed:res.Resolvers[j].ReceiveBytes-old.Resolvers[j].ReceiveBytes,
sendtText:parseSpeed(res.Resolvers[j].SendtBytes-old.Resolvers[j].SendtBytes),
receiveBytes:res.Resolvers[j].ReceiveBytes,
receiveSpeed:res.Resolvers[j].ReceiveBytes-old.Resolvers[j].ReceiveBytes,
receiveText:parseSpeed(res.Resolvers[j].ReceiveBytes-old.Resolvers[j].ReceiveBytes),
});
}
for(let j in res.Messangers){
list.push({
id:id2text[`${j}`],
sendtBytes:res.Messangers[j].SendtBytes,
sendtSpeed:res.Messangers[j].SendtBytes-old.Messangers[j].SendtBytes,
sendtText:parseSpeed(res.Messangers[j].SendtBytes-old.Messangers[j].SendtBytes),
receiveBytes:res.Messangers[j].ReceiveBytes,
receiveSpeed:res.Messangers[j].ReceiveBytes-old.Messangers[j].ReceiveBytes,
receiveText:parseSpeed(res.Messangers[j].ReceiveBytes-old.Messangers[j].ReceiveBytes),
});
}
state.list = list;
state.old = res;
state.timer = setTimeout(_getFlows,1000);
}).catch((e)=>{
state.timer = setTimeout(_getFlows,1000);
});
}
const parseSpeed = (num) => {
let index = 0;
while (num >= 1024) {
num /= 1024;
index++;
}
return `${num.toFixed(2)}${['B/s', 'KB/s', 'MB/s', 'GB/s', 'TB/s'][index]}`;
}
onMounted(()=>{
_getFlows();
});
onUnmounted(()=>{
clearTimeout(state.timer);
});
return {
config:props.config,state,handleShow
}
}
}
</script>
<style lang="stylus" scoped>
a{
font-weight:bold;position:absolute;right:1rem;bottom:90%;
border:1px solid #ddd;
background-color:#fff;
p{
line-height:normal;
white-space: nowrap;
}
}
</style>

View File

@@ -0,0 +1,186 @@
<template>
<a href="javascript:;" title="服务端的程序版本" @click="handleUpdate" class="download" :title="updateText()" :class="updateColor()">
<span>{{state.version}}</span>
<template v-if="updaterCurrent.Version">
<template v-if="updaterCurrent.Status == 1">
<el-icon size="14" class="loading"><Loading /></el-icon>
</template>
<template v-else-if="updaterServer.Status == 2">
<el-icon size="14"><Download /></el-icon>
</template>
<template v-else-if="updaterServer.Status == 3 || updaterServer.Status == 5">
<el-icon size="14" class="loading"><Loading /></el-icon>
<span class="progress" v-if="updaterServer.Length ==0">0%</span>
<span class="progress" v-else>{{parseInt(updaterServer.Current/updaterServer.Length*100)}}%</span>
</template>
<template v-else-if="updaterServer.Status == 6">
<el-icon size="14" class="yellow"><CircleCheck /></el-icon>
</template>
</template>
<template v-else>
<el-icon size="14"><Download /></el-icon>
</template>
</a>
</template>
<script>
import { injectGlobalData } from '@/provide';
import { ElMessage, ElMessageBox } from 'element-plus';
import { computed, onMounted, reactive, ref } from 'vue';
import {Promotion,Download,Loading,CircleCheck} from '@element-plus/icons-vue'
import { confirmServer, exitServer, getUpdaterCurrent, getUpdaterServer } from '@/apis/updater';
import ServerFlow from './ServerFlow.vue';
export default {
components:{Promotion,Download,Loading,CircleCheck,ServerFlow},
props:['config'],
setup(props) {
const globalData = injectGlobalData();
const hasUpdateServer = computed(()=>globalData.value.hasAccess('UpdateServer'));
const updaterCurrent = ref({Version: '', Msg: [], DateTime: '', Status: 0, Length: 0, Current: 0});
const updaterServer = ref({Version: '', Status: 0, Length: 0, Current: 0});
const updaterMsg = computed(()=>{
return `${updaterCurrent.value.Version}->${updaterCurrent.value.DateTime}\n${updaterCurrent.value.Msg.map((value,index)=>`${index+1}${value}`).join('\n')}`;
});
const state = reactive({
show: false,
loading: false,
connected: computed(() => globalData.value.signin.Connected),
version: computed(() => globalData.value.signin.Version),
});
const _getUpdaterCurrent = ()=>{
getUpdaterCurrent().then((res)=>{
updaterCurrent.value.DateTime = res.DateTime;
updaterCurrent.value.Version = res.Version;
updaterCurrent.value.Status = res.Status;
updaterCurrent.value.Length = res.Length;
updaterCurrent.value.Current = res.Current;
updaterCurrent.value.Msg = res.Msg;
setTimeout(()=>{
_getUpdaterCurrent();
},1000);
}).catch(()=>{
setTimeout(()=>{
_getUpdaterCurrent();
},1000);
})
}
const _getUpdaterServer = ()=>{
getUpdaterServer().then((res)=>{
updaterServer.value.Version = res.Version;
updaterServer.value.Status = res.Status;
updaterServer.value.Length = res.Length;
updaterServer.value.Current = res.Current;
if(updaterServer.value.Status > 2 && updaterServer.value.Status < 6){
setTimeout(()=>{
_getUpdaterServer();
},1000);
}
}).catch(()=>{
setTimeout(()=>{
_getUpdaterServer();
},1000);
});
}
const updateText = ()=>{
if(!updaterCurrent.value.Version){
return '未检测到更新';
}
if(updaterServer.value.Status <= 2) {
return state.version != updaterCurrent.value.Version
? `不是最新版本(${updaterCurrent.value.Version}),建议更新\n${updaterMsg.value}`
: `是最新版本,但我无法阻止你喜欢更新\n${updaterMsg.value}`
}
return {
3:'正在下载',
4:'已下载',
5:'正在解压',
6:'已解压,请重启',
}[updaterServer.value.Status];
}
const updateColor = ()=>{
return state.version != updaterCurrent.value.Version ? 'yellow' :'green'
}
const handleUpdate = ()=>{
if(!props.config || !hasUpdateServer.value){
return;
}
if(!updaterCurrent.value.Version){
ElMessage.error('未检测到更新');
return;
}
//未检测,检测中,下载中,解压中
if([0,1,3,5].indexOf(updaterServer.value.Status)>=0){
ElMessage.error('操作中,请稍后!');
return;
}
//已解压
if(updaterServer.value.Status == 6){
ElMessageBox.confirm('确定关闭服务端吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
exitServer();
}).catch(() => {});
return;
}
//已检测
if(updaterCurrent.value.Status == 2){
ElMessageBox.confirm('确定更新服务端吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
confirmServer(updaterCurrent.value.Version).then(()=>{
setTimeout(()=>{
_getUpdaterServer();
},1000);
});
}).catch(() => {});
}
}
onMounted(() => {
_getUpdaterCurrent();
_getUpdaterServer();
});
return {
config:props.config, state, updaterCurrent,updaterServer,handleUpdate,updateText,updateColor
}
}
}
</script>
<style lang="stylus" scoped>
@keyframes loading {
from{transform:rotate(0deg)}
to{transform:rotate(360deg)}
}
a{
a{color:#333;}
a{margin-left:.6rem;}
.el-icon{
vertical-align:text-bottom;
}
&.download{
.el-icon{
font-weight:bold;
&.loading{
animation:loading 1s linear infinite;
}
margin-left:.3rem
}
}
}
</style>

View File

@@ -23,8 +23,6 @@ namespace linker.plugins.client
private readonly MessengerResolver messengerResolver;
private readonly SignInArgsTransfer signInArgsTransfer;
private string configKey = "signServers";
public ClientSignInTransfer(ClientSignInState clientSignInState, RunningConfig runningConfig, FileConfig config, MessengerSender messengerSender, MessengerResolver messengerResolver, SignInArgsTransfer signInArgsTransfer)
{
this.clientSignInState = clientSignInState;

View File

@@ -8,6 +8,7 @@ using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using linker.libs.extends;
using linker.plugins.resolver;
using MemoryPack;
namespace linker.plugins.messenger
{
@@ -26,6 +27,12 @@ namespace linker.plugins.messenger
private readonly MessengerSender messengerSender;
private readonly ServiceProvider serviceProvider;
public ulong ReceiveBytes { get; private set; }
public ulong SendtBytes { get; private set; }
private Dictionary<ushort, MessengerFlowItemInfo> messangerFlows { get; } = new Dictionary<ushort, MessengerFlowItemInfo>();
private X509Certificate serverCertificate;
public MessengerResolver(MessengerSender messengerSender, ServiceProvider serviceProvider)
{
@@ -152,8 +159,10 @@ namespace linker.plugins.messenger
{
cache.TaskMethod = (TaskDelegate)Delegate.CreateDelegate(typeof(TaskDelegate), obj, method);
}
messengers.TryAdd(mid.Id, cache);
if (mid.Id != 2701)
messangerFlows.TryAdd(mid.Id, new MessengerFlowItemInfo { });
}
else
{
@@ -203,6 +212,14 @@ namespace linker.plugins.messenger
}
return;
}
//流量统计
if (messangerFlows.TryGetValue(requestWrap.MessengerId, out MessengerFlowItemInfo messengerFlowItemInfo))
{
ReceiveBytes += (ulong)data.Length;
messengerFlowItemInfo.ReceiveBytes += (ulong)data.Length;
}
if (plugin.VoidMethod != null)
{
plugin.VoidMethod(connection);
@@ -214,6 +231,12 @@ namespace linker.plugins.messenger
//有需要回复的
if (requestWrap.Reply == true && connection.ResponseData.Length > 0)
{
//流量统计
if (messengerFlowItemInfo != null)
{
SendtBytes += (ulong)connection.ResponseData.Length;
messengerFlowItemInfo.SendtBytes += (ulong)connection.ResponseData.Length;
}
bool res = await messengerSender.ReplyOnly(new MessageResponseWrap
{
Connection = connection,
@@ -233,6 +256,11 @@ namespace linker.plugins.messenger
}
}
public Dictionary<ushort, MessengerFlowItemInfo> GetFlows()
{
return messangerFlows;
}
/// <summary>
/// 消息插件缓存
/// </summary>
@@ -251,5 +279,14 @@ namespace linker.plugins.messenger
/// </summary>
public TaskDelegate TaskMethod { get; set; }
}
}
[MemoryPackable]
public sealed partial class MessengerFlowItemInfo
{
public ulong ReceiveBytes { get; set; }
public ulong SendtBytes { get; set; }
}
}

View File

@@ -13,6 +13,8 @@ namespace linker.plugins.relay
public sealed class RelayResolver : IResolver
{
public ResolverType Type => ResolverType.Relay;
public ulong ReceiveBytes { get; private set; }
public ulong SendtBytes { get; private set; }
public RelayResolver()
{
@@ -106,6 +108,8 @@ namespace linker.plugins.relay
int bytesRead;
while ((bytesRead = await source.ReceiveAsync(buffer.AsMemory()).ConfigureAwait(false)) != 0)
{
ReceiveBytes += (ulong)bytesRead;
SendtBytes += (ulong)bytesRead;
await destination.SendAsync(buffer.AsMemory(0, bytesRead)).ConfigureAwait(false);
}
}

View File

@@ -22,7 +22,6 @@ namespace linker.plugins.relay
private readonly FileConfig fileConfig;
private readonly RunningConfig running;
private readonly ServiceProvider serviceProvider;
private string configKey = "relayServers";
private ConcurrentDictionary<string, bool> connectingDic = new ConcurrentDictionary<string, bool>();
private Dictionary<string, List<Action<ITunnelConnection>>> OnConnected { get; } = new Dictionary<string, List<Action<ITunnelConnection>>>();

View File

@@ -1,4 +1,5 @@
using System.Net;
using MemoryPack;
using System.Net;
using System.Net.Sockets;
namespace linker.plugins.resolver
@@ -11,9 +12,18 @@ namespace linker.plugins.resolver
}
public interface IResolver
{
public ulong ReceiveBytes { get; }
public ulong SendtBytes { get;}
public ResolverType Type { get; }
public Task Resolve(Socket socket);
public Task Resolve(Socket socket,IPEndPoint ep,Memory<byte> memory);
}
[MemoryPackable]
public sealed partial class ResolverFlowItemInfo
{
public ulong ReceiveBytes { get; set; }
public ulong SendtBytes { get; set; }
}
}

View File

@@ -69,5 +69,10 @@ namespace linker.plugins.resolver
await resolver.Resolve(socket, ep, memory);
}
}
public Dictionary<string, ResolverFlowItemInfo> GetFlows()
{
return resolvers.Values.ToDictionary(c => c.Type.ToString(), d => new ResolverFlowItemInfo { ReceiveBytes=d.ReceiveBytes, SendtBytes=d.SendtBytes });
}
}
}

View File

@@ -0,0 +1,43 @@
using linker.libs.api;
using linker.config;
using MemoryPack;
using linker.client.config;
using linker.plugins.client;
using linker.plugins.capi;
using linker.plugins.messenger;
using linker.plugins.server.messenger;
namespace linker.plugins.server
{
public sealed class ServerClientApiController : IApiClientController
{
private readonly MessengerSender messengerSender;
private readonly ClientSignInState clientSignInState;
private readonly FileConfig config;
private readonly RunningConfig runningConfig;
public ServerClientApiController(MessengerSender messengerSender, ClientSignInState clientSignInState, FileConfig config, RunningConfig runningConfig)
{
this.messengerSender = messengerSender;
this.clientSignInState = clientSignInState;
this.config = config;
this.runningConfig = runningConfig;
}
public async Task<ServerFlowInfo> GetFlows(ApiControllerParamsInfo param)
{
MessageResponeInfo resp = await messengerSender.SendReply(new MessageRequestWrap
{
Connection = clientSignInState.Connection,
MessengerId = (ushort)ServerMessengerIds.Flow,
});
if (resp.Code == MessageResponeCodes.OK && resp.Data.Length > 0)
{
return MemoryPackSerializer.Deserialize<ServerFlowInfo>(resp.Data.Span);
}
return new ServerFlowInfo();
}
}
}

View File

@@ -3,6 +3,7 @@ using linker.startup;
using linker.libs;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
using linker.plugins.server.messenger;
namespace linker.plugins.server
{
@@ -14,16 +15,18 @@ namespace linker.plugins.server
public StartupLevel Level => StartupLevel.Normal;
public string Name => "server";
public bool Required => true;
public string[] Dependent => new string[] {"messenger", "serialize", "firewall", "signin", "config" };
public string[] Dependent => new string[] {"messenger", "serialize", "firewall", "signin", "config", "resolver" };
public StartupLoadType LoadType => StartupLoadType.Normal;
public void AddClient(ServiceCollection serviceCollection, FileConfig config, Assembly[] assemblies)
{
serviceCollection.AddSingleton<ServerClientApiController>();
}
public void AddServer(ServiceCollection serviceCollection, FileConfig config, Assembly[] assemblies)
{
serviceCollection.AddSingleton<TcpServer>();
serviceCollection.AddSingleton<ServerMessenger>();
}
public void UseClient(ServiceProvider serviceProvider, FileConfig config, Assembly[] assemblies)

View File

@@ -1,4 +1,9 @@
using linker.libs.extends;
using linker.plugins.messenger;
using linker.plugins.resolver;
using MemoryPack;
using System.Collections.Concurrent;
using System.Net;
namespace linker.config
{
@@ -22,4 +27,17 @@ namespace linker.config
return obj.ToJsonFormat();
}
}
[MemoryPackable]
public sealed partial class ServerFlowInfo
{
public Dictionary<string, ResolverFlowItemInfo> Resolvers { get; set; }
public Dictionary<ushort, MessengerFlowItemInfo> Messangers { get; set; }
public DateTime Start { get; set; }
public DateTime Now { get; set; }
}
}

View File

@@ -0,0 +1,36 @@
using linker.config;
using linker.plugins.messenger;
using linker.plugins.resolver;
using MemoryPack;
namespace linker.plugins.server.messenger
{
public sealed class ServerMessenger : IMessenger
{
private readonly MessengerResolver messengerResolver;
private readonly ResolverTransfer resolverTransfer;
private DateTime start = DateTime.Now;
public ServerMessenger(MessengerResolver messengerResolver, ResolverTransfer resolverTransfer)
{
this.messengerResolver = messengerResolver;
this.resolverTransfer = resolverTransfer;
}
[MessengerId((ushort)ServerMessengerIds.Flow)]
public void Flow(IConnection connection)
{
ServerFlowInfo serverFlowInfo = new ServerFlowInfo
{
Messangers = messengerResolver.GetFlows(),
Resolvers = resolverTransfer.GetFlows(),
Start = start,
Now = DateTime.Now,
};
connection.Write(MemoryPackSerializer.Serialize(serverFlowInfo));
}
}
}

View File

@@ -0,0 +1,11 @@
namespace linker.plugins.server.messenger
{
public enum ServerMessengerIds : ushort
{
Min = 2700,
Flow = 2701,
Max = 2799
}
}

View File

@@ -19,8 +19,6 @@ namespace linker.plugins.sforward
private readonly ClientSignInState clientSignInState;
private readonly MessengerSender messengerSender;
private string configKey = "sforwardKey";
private readonly NumberSpaceUInt32 ns = new NumberSpaceUInt32();
public VersionManager Version { get; } = new VersionManager();

View File

@@ -11,7 +11,8 @@ namespace linker.plugins.tunnel
/// </summary>
public sealed class ExternalResolver : IResolver
{
public ulong ReceiveBytes { get; private set; }
public ulong SendtBytes { get; private set; }
public ResolverType Type => ResolverType.External;
public ExternalResolver()
@@ -27,10 +28,13 @@ namespace linker.plugins.tunnel
/// <returns></returns>
public async Task Resolve(Socket socket, IPEndPoint ep, Memory<byte> memory)
{
ReceiveBytes += (ulong)memory.Length;
byte[] sendData = ArrayPool<byte>.Shared.Rent(20);
try
{
await socket.SendToAsync(BuildSendData(sendData, ep), SocketFlags.None, ep).ConfigureAwait(false);
var send = BuildSendData(sendData, ep);
SendtBytes += (ulong)send.Length;
await socket.SendToAsync(send, SocketFlags.None, ep).ConfigureAwait(false);
}
catch (Exception)
{
@@ -51,6 +55,7 @@ namespace linker.plugins.tunnel
try
{
Memory<byte> memory = BuildSendData(sendData, socket.RemoteEndPoint as IPEndPoint);
SendtBytes += (ulong)memory.Length;
await socket.SendAsync(memory, SocketFlags.None).ConfigureAwait(false);
}
catch (Exception)

View File

@@ -29,9 +29,6 @@ namespace linker.plugins.tunnel
private readonly RunningConfig running;
private readonly TunnelExcludeIPTransfer excludeIPTransfer;
private string wanPortConfigKey = "tunnelWanPortProtocols";
private string transportConfigKey = "tunnelTransports";
public TunnelAdapter(ClientSignInState clientSignInState, MessengerSender messengerSender, FileConfig config, RunningConfig running, TunnelExcludeIPTransfer excludeIPTransfer)
{
this.clientSignInState = clientSignInState;

View File

@@ -98,7 +98,7 @@ namespace linker.plugins.tunnel
RefreshPortMap();
}
/// <summary>
/// 收到别人发给我的修改我的信息
/// 收到别人的信息
/// </summary>
/// <param name="tunnelTransportFileConfigInfo"></param>
/// <returns></returns>

View File

@@ -11,7 +11,6 @@ namespace linker.plugins.tunnel.excludeip
public sealed class TunnelExcludeIPTransfer
{
private List<ITunnelExcludeIP> excludeIPs;
private string exipConfigKey = "excludeIPConfig";
private readonly RunningConfig running;
private readonly ClientSignInState clientSignInState;

View File

@@ -18,29 +18,27 @@ namespace linker.plugins.updater
private readonly UpdaterClientTransfer updaterTransfer;
private readonly ClientSignInState clientSignInState;
private readonly FileConfig config;
private readonly UpdaterClientTransfer updaterClientTransfer;
private readonly RunningConfig runningConfig;
public UpdaterClientApiController(MessengerSender messengerSender, UpdaterClientTransfer updaterTransfer, ClientSignInState clientSignInState, FileConfig config, UpdaterClientTransfer updaterClientTransfer, RunningConfig runningConfig)
public UpdaterClientApiController(MessengerSender messengerSender, UpdaterClientTransfer updaterTransfer, ClientSignInState clientSignInState, FileConfig config, RunningConfig runningConfig)
{
this.messengerSender = messengerSender;
this.updaterTransfer = updaterTransfer;
this.clientSignInState = clientSignInState;
this.config = config;
this.updaterClientTransfer = updaterClientTransfer;
this.runningConfig = runningConfig;
}
[ClientApiAccessAttribute(ClientApiAccess.Config)]
public string GetSecretKey(ApiControllerParamsInfo param)
{
return updaterClientTransfer.GetSecretKey();
return updaterTransfer.GetSecretKey();
}
[ClientApiAccessAttribute(ClientApiAccess.Config)]
public void SetSecretKey(ApiControllerParamsInfo param)
{
updaterClientTransfer.SetSecretKey(param.Content);
updaterTransfer.SetSecretKey(param.Content);
}
public UpdateInfo GetCurrent(ApiControllerParamsInfo param)

View File

@@ -20,7 +20,6 @@ namespace linker.plugins.updater
private readonly UpdaterHelper updaterHelper;
private readonly RunningConfig running;
private string configKey = "updater";
public VersionManager Version { get; } = new VersionManager();

View File

@@ -1,6 +1,5 @@
using linker.config;
using linker.plugins.messenger;
using linker.plugins.server;
using linker.plugins.signin.messenger;
using linker.plugins.updater.config;
using MemoryPack;

View File

@@ -1,5 +1,5 @@
v1.4.3
2024-09-24 09:15:45
2024-09-24 16:23:26
1. 优化服务器代理穿透
2. 当提示端口被占用时,可以稍等一段时间再尝试启动穿透
3. 其它一些更改