From 8c6303c1e57408cac22c6e7f192f35b68af565d7 Mon Sep 17 00:00:00 2001 From: fatedier Date: Sun, 26 Feb 2023 02:54:53 +0800 Subject: [PATCH] web/frpc: support more info (#3334) --- README.md | 10 +- .../{index-a2ed7ed4.js => index-7dd223da.js} | 2 +- assets/frpc/static/index.html | 2 +- client/admin_api.go | 113 ++++-------------- cmd/frpc/sub/status.go | 68 +++-------- go.mod | 14 +-- go.sum | 32 ++--- pkg/util/util/slice.go | 25 ++++ pkg/util/util/slice_test.go | 49 ++++++++ test/e2e/framework/process.go | 2 +- test/e2e/pkg/port/port.go | 8 +- test/e2e/pkg/sdk/client/client.go | 40 +------ web/frpc/.eslintrc.cjs | 28 ++--- web/frpc/.prettierrc.json | 6 +- web/frpc/src/App.vue | 16 +-- web/frpc/src/components/ClientConfigure.vue | 84 ++++++------- web/frpc/src/components/Overview.vue | 62 +++++----- web/frpc/src/main.ts | 18 +-- web/frpc/src/router/index.ts | 18 +-- web/frpc/vite.config.ts | 20 ++-- 20 files changed, 277 insertions(+), 340 deletions(-) rename assets/frpc/static/{index-a2ed7ed4.js => index-7dd223da.js} (99%) create mode 100644 pkg/util/util/slice.go create mode 100644 pkg/util/util/slice_test.go diff --git a/README.md b/README.md index 92c0518e..1c3fe788 100644 --- a/README.md +++ b/README.md @@ -22,9 +22,9 @@ ## What is frp? -frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the Internet. As of now, it supports **TCP** and **UDP**, as well as **HTTP** and **HTTPS** protocols, where requests can be forwarded to internal services by domain name. +frp is a fast reverse proxy that allows you to expose a local server located behind a NAT or firewall to the Internet. It currently supports **TCP** and **UDP**, as well as **HTTP** and **HTTPS** protocols, enabling requests to be forwarded to internal services via domain name. -frp also has a P2P connect mode. +frp also offers a P2P connect mode. ## Table of Contents @@ -89,11 +89,11 @@ frp also has a P2P connect mode. ## Development Status -frp is under development. Try the latest release version in the `master` branch, or use the `dev` branch for the version in development. +frp is currently under development. You can try the latest release version in the `master` branch, or use the `dev` branch to access the version currently in development. -We are working on v2 version and trying to do some code refactor and improvements. It won't be compatible with v1. +We are currently working on version 2 and attempting to perform some code refactoring and improvements. However, please note that it will not be compatible with version 1. -We will switch v0 to v1 at the right time and only accept bug fixes and improvements instead of big feature requirements. +We will transition from version 0 to version 1 at the appropriate time and will only accept bug fixes and improvements, rather than big feature requests. ## Architecture diff --git a/assets/frpc/static/index-a2ed7ed4.js b/assets/frpc/static/index-7dd223da.js similarity index 99% rename from assets/frpc/static/index-a2ed7ed4.js rename to assets/frpc/static/index-7dd223da.js index 3c129d95..75888cd6 100644 --- a/assets/frpc/static/index-a2ed7ed4.js +++ b/assets/frpc/static/index-7dd223da.js @@ -29,4 +29,4 @@ var Dh=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var N$=Dh((At,Mt) * vue-router v4.1.6 * (c) 2022 Eduardo San Martin Morote * @license MIT - */const $r=typeof window<"u";function A4(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const je=Object.assign;function Hl(e,t){const n={};for(const r in t){const o=t[r];n[r]=Gt(o)?o.map(e):e(o)}return n}const Co=()=>{},Gt=Array.isArray,M4=/\/$/,P4=e=>e.replace(M4,"");function Dl(e,t,n="/"){let r,o={},s="",l="";const a=t.indexOf("#");let i=t.indexOf("?");return a=0&&(i=-1),i>-1&&(r=t.slice(0,i),s=t.slice(i+1,a>-1?a:t.length),o=e(s)),a>-1&&(r=r||t.slice(0,a),l=t.slice(a,t.length)),r=L4(r??t,n),{fullPath:r+(s&&"?")+s+l,path:r,query:o,hash:l}}function R4(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function Qc(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function k4(e,t,n){const r=t.matched.length-1,o=n.matched.length-1;return r>-1&&r===o&&Yr(t.matched[r],n.matched[o])&&Sh(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function Yr(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Sh(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!I4(e[n],t[n]))return!1;return!0}function I4(e,t){return Gt(e)?ef(e,t):Gt(t)?ef(t,e):e===t}function ef(e,t){return Gt(t)?e.length===t.length&&e.every((n,r)=>n===t[r]):e.length===1&&e[0]===t}function L4(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),r=e.split("/");let o=n.length-1,s,l;for(s=0;s1&&o--;else break;return n.slice(0,o).join("/")+"/"+r.slice(s-(s===r.length?1:0)).join("/")}var Ho;(function(e){e.pop="pop",e.push="push"})(Ho||(Ho={}));var Eo;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Eo||(Eo={}));function N4(e){if(!e)if($r){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),P4(e)}const F4=/^[^#]+#/;function B4(e,t){return e.replace(F4,"#")+t}function H4(e,t){const n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}const yl=()=>({left:window.pageXOffset,top:window.pageYOffset});function D4(e){let t;if("el"in e){const n=e.el,r=typeof n=="string"&&n.startsWith("#"),o=typeof n=="string"?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!o)return;t=H4(o,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.pageXOffset,t.top!=null?t.top:window.pageYOffset)}function tf(e,t){return(history.state?history.state.position-t:-1)+e}const Aa=new Map;function z4(e,t){Aa.set(e,t)}function j4(e){const t=Aa.get(e);return Aa.delete(e),t}let W4=()=>location.protocol+"//"+location.host;function xh(e,t){const{pathname:n,search:r,hash:o}=t,s=e.indexOf("#");if(s>-1){let a=o.includes(e.slice(s))?e.slice(s).length:1,i=o.slice(a);return i[0]!=="/"&&(i="/"+i),Qc(i,"")}return Qc(n,e)+r+o}function K4(e,t,n,r){let o=[],s=[],l=null;const a=({state:d})=>{const h=xh(e,location),p=n.value,v=t.value;let b=0;if(d){if(n.value=h,t.value=d,l&&l===p){l=null;return}b=v?d.position-v.position:0}else r(h);o.forEach(m=>{m(n.value,p,{delta:b,type:Ho.pop,direction:b?b>0?Eo.forward:Eo.back:Eo.unknown})})};function i(){l=n.value}function u(d){o.push(d);const h=()=>{const p=o.indexOf(d);p>-1&&o.splice(p,1)};return s.push(h),h}function c(){const{history:d}=window;d.state&&d.replaceState(je({},d.state,{scroll:yl()}),"")}function f(){for(const d of s)d();s=[],window.removeEventListener("popstate",a),window.removeEventListener("beforeunload",c)}return window.addEventListener("popstate",a),window.addEventListener("beforeunload",c),{pauseListeners:i,listen:u,destroy:f}}function nf(e,t,n,r=!1,o=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:o?yl():null}}function V4(e){const{history:t,location:n}=window,r={value:xh(e,n)},o={value:t.state};o.value||s(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function s(i,u,c){const f=e.indexOf("#"),d=f>-1?(n.host&&document.querySelector("base")?e:e.slice(f))+i:W4()+e+i;try{t[c?"replaceState":"pushState"](u,"",d),o.value=u}catch(h){console.error(h),n[c?"replace":"assign"](d)}}function l(i,u){const c=je({},t.state,nf(o.value.back,i,o.value.forward,!0),u,{position:o.value.position});s(i,c,!0),r.value=i}function a(i,u){const c=je({},o.value,t.state,{forward:i,scroll:yl()});s(c.current,c,!0);const f=je({},nf(r.value,i,null),{position:c.position+1},u);s(i,f,!1),r.value=i}return{location:r,state:o,push:a,replace:l}}function U4(e){e=N4(e);const t=V4(e),n=K4(e,t.state,t.location,t.replace);function r(s,l=!0){l||n.pauseListeners(),history.go(s)}const o=je({location:"",base:e,go:r,createHref:B4.bind(null,e)},t,n);return Object.defineProperty(o,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(o,"state",{enumerable:!0,get:()=>t.state.value}),o}function q4(e){return e=location.host?e||location.pathname+location.search:"",e.includes("#")||(e+="#"),U4(e)}function Y4(e){return typeof e=="string"||e&&typeof e=="object"}function Th(e){return typeof e=="string"||typeof e=="symbol"}const Mn={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},Oh=Symbol("");var rf;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(rf||(rf={}));function Gr(e,t){return je(new Error,{type:e,[Oh]:!0},t)}function vn(e,t){return e instanceof Error&&Oh in e&&(t==null||!!(e.type&t))}const of="[^/]+?",G4={sensitive:!1,strict:!1,start:!0,end:!0},X4=/[.+*?^${}()[\]/\\]/g;function J4(e,t){const n=je({},G4,t),r=[];let o=n.start?"^":"";const s=[];for(const u of e){const c=u.length?[]:[90];n.strict&&!u.length&&(o+="/");for(let f=0;ft.length?t.length===1&&t[0]===40+40?1:-1:0}function Q4(e,t){let n=0;const r=e.score,o=t.score;for(;n0&&t[t.length-1]<0}const e$={type:0,value:""},t$=/[a-zA-Z0-9_]/;function n$(e){if(!e)return[[]];if(e==="/")return[[e$]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(h){throw new Error(`ERR (${n})/"${u}": ${h}`)}let n=0,r=n;const o=[];let s;function l(){s&&o.push(s),s=[]}let a=0,i,u="",c="";function f(){u&&(n===0?s.push({type:0,value:u}):n===1||n===2||n===3?(s.length>1&&(i==="*"||i==="+")&&t(`A repeatable param (${u}) must be alone in its segment. eg: '/:ids+.`),s.push({type:1,value:u,regexp:c,repeatable:i==="*"||i==="+",optional:i==="*"||i==="?"})):t("Invalid state to consume buffer"),u="")}function d(){u+=i}for(;a{l(w)}:Co}function l(c){if(Th(c)){const f=r.get(c);f&&(r.delete(c),n.splice(n.indexOf(f),1),f.children.forEach(l),f.alias.forEach(l))}else{const f=n.indexOf(c);f>-1&&(n.splice(f,1),c.record.name&&r.delete(c.record.name),c.children.forEach(l),c.alias.forEach(l))}}function a(){return n}function i(c){let f=0;for(;f=0&&(c.record.path!==n[f].record.path||!$h(c,n[f]));)f++;n.splice(f,0,c),c.record.name&&!af(c)&&r.set(c.record.name,c)}function u(c,f){let d,h={},p,v;if("name"in c&&c.name){if(d=r.get(c.name),!d)throw Gr(1,{location:c});v=d.record.name,h=je(lf(f.params,d.keys.filter(w=>!w.optional).map(w=>w.name)),c.params&&lf(c.params,d.keys.map(w=>w.name))),p=d.stringify(h)}else if("path"in c)p=c.path,d=n.find(w=>w.re.test(p)),d&&(h=d.parse(p),v=d.record.name);else{if(d=f.name?r.get(f.name):n.find(w=>w.re.test(f.path)),!d)throw Gr(1,{location:c,currentLocation:f});v=d.record.name,h=je({},f.params,c.params),p=d.stringify(h)}const b=[];let m=d;for(;m;)b.unshift(m.record),m=m.parent;return{name:v,path:p,params:h,matched:b,meta:a$(b)}}return e.forEach(c=>s(c)),{addRoute:s,resolve:u,removeRoute:l,getRoutes:a,getRecordMatcher:o}}function lf(e,t){const n={};for(const r of t)r in e&&(n[r]=e[r]);return n}function s$(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:l$(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function l$(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const r in e.components)t[r]=typeof n=="boolean"?n:n[r];return t}function af(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function a$(e){return e.reduce((t,n)=>je(t,n.meta),{})}function uf(e,t){const n={};for(const r in e)n[r]=r in t?t[r]:e[r];return n}function $h(e,t){return t.children.some(n=>n===e||$h(e,n))}const Ah=/#/g,i$=/&/g,u$=/\//g,c$=/=/g,f$=/\?/g,Mh=/\+/g,d$=/%5B/g,p$=/%5D/g,Ph=/%5E/g,h$=/%60/g,Rh=/%7B/g,v$=/%7C/g,kh=/%7D/g,m$=/%20/g;function Ni(e){return encodeURI(""+e).replace(v$,"|").replace(d$,"[").replace(p$,"]")}function g$(e){return Ni(e).replace(Rh,"{").replace(kh,"}").replace(Ph,"^")}function Ma(e){return Ni(e).replace(Mh,"%2B").replace(m$,"+").replace(Ah,"%23").replace(i$,"%26").replace(h$,"`").replace(Rh,"{").replace(kh,"}").replace(Ph,"^")}function y$(e){return Ma(e).replace(c$,"%3D")}function b$(e){return Ni(e).replace(Ah,"%23").replace(f$,"%3F")}function w$(e){return e==null?"":b$(e).replace(u$,"%2F")}function Ks(e){try{return decodeURIComponent(""+e)}catch{}return""+e}function _$(e){const t={};if(e===""||e==="?")return t;const r=(e[0]==="?"?e.slice(1):e).split("&");for(let o=0;os&&Ma(s)):[r&&Ma(r)]).forEach(s=>{s!==void 0&&(t+=(t.length?"&":"")+n,s!=null&&(t+="="+s))})}return t}function C$(e){const t={};for(const n in e){const r=e[n];r!==void 0&&(t[n]=Gt(r)?r.map(o=>o==null?null:""+o):r==null?r:""+r)}return t}const E$=Symbol(""),ff=Symbol(""),Fi=Symbol(""),Ih=Symbol(""),Pa=Symbol("");function uo(){let e=[];function t(r){return e.push(r),()=>{const o=e.indexOf(r);o>-1&&e.splice(o,1)}}function n(){e=[]}return{add:t,list:()=>e,reset:n}}function Ln(e,t,n,r,o){const s=r&&(r.enterCallbacks[o]=r.enterCallbacks[o]||[]);return()=>new Promise((l,a)=>{const i=f=>{f===!1?a(Gr(4,{from:n,to:t})):f instanceof Error?a(f):Y4(f)?a(Gr(2,{from:t,to:f})):(s&&r.enterCallbacks[o]===s&&typeof f=="function"&&s.push(f),l())},u=e.call(r&&r.instances[o],t,n,i);let c=Promise.resolve(u);e.length<3&&(c=c.then(i)),c.catch(f=>a(f))})}function zl(e,t,n,r){const o=[];for(const s of e)for(const l in s.components){let a=s.components[l];if(!(t!=="beforeRouteEnter"&&!s.instances[l]))if(S$(a)){const u=(a.__vccOpts||a)[t];u&&o.push(Ln(u,n,r,s,l))}else{let i=a();o.push(()=>i.then(u=>{if(!u)return Promise.reject(new Error(`Couldn't resolve component "${l}" at "${s.path}"`));const c=A4(u)?u.default:u;s.components[l]=c;const d=(c.__vccOpts||c)[t];return d&&Ln(d,n,r,s,l)()}))}}return o}function S$(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function df(e){const t=Te(Fi),n=Te(Ih),r=$(()=>t.resolve(y(e.to))),o=$(()=>{const{matched:i}=r.value,{length:u}=i,c=i[u-1],f=n.matched;if(!c||!f.length)return-1;const d=f.findIndex(Yr.bind(null,c));if(d>-1)return d;const h=pf(i[u-2]);return u>1&&pf(c)===h&&f[f.length-1].path!==h?f.findIndex(Yr.bind(null,i[u-2])):d}),s=$(()=>o.value>-1&&$$(n.params,r.value.params)),l=$(()=>o.value>-1&&o.value===n.matched.length-1&&Sh(n.params,r.value.params));function a(i={}){return O$(i)?t[y(e.replace)?"replace":"push"](y(e.to)).catch(Co):Promise.resolve()}return{route:r,href:$(()=>r.value.href),isActive:s,isExactActive:l,navigate:a}}const x$=ue({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:df,setup(e,{slots:t}){const n=Xt(df(e)),{options:r}=Te(Fi),o=$(()=>({[hf(e.activeClass,r.linkActiveClass,"router-link-active")]:n.isActive,[hf(e.exactActiveClass,r.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const s=t.default&&t.default(n);return e.custom?s:pe("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:o.value},s)}}}),T$=x$;function O$(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function $$(e,t){for(const n in t){const r=t[n],o=e[n];if(typeof r=="string"){if(r!==o)return!1}else if(!Gt(o)||o.length!==r.length||r.some((s,l)=>s!==o[l]))return!1}return!0}function pf(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const hf=(e,t,n)=>e??t??n,A$=ue({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const r=Te(Pa),o=$(()=>e.route||r.value),s=Te(ff,0),l=$(()=>{let u=y(s);const{matched:c}=o.value;let f;for(;(f=c[u])&&!f.components;)u++;return u}),a=$(()=>o.value.matched[l.value]);it(ff,$(()=>l.value+1)),it(E$,a),it(Pa,o);const i=P();return de(()=>[i.value,a.value,e.name],([u,c,f],[d,h,p])=>{c&&(c.instances[f]=u,h&&h!==c&&u&&u===d&&(c.leaveGuards.size||(c.leaveGuards=h.leaveGuards),c.updateGuards.size||(c.updateGuards=h.updateGuards))),u&&c&&(!h||!Yr(c,h)||!d)&&(c.enterCallbacks[f]||[]).forEach(v=>v(u))},{flush:"post"}),()=>{const u=o.value,c=e.name,f=a.value,d=f&&f.components[c];if(!d)return vf(n.default,{Component:d,route:u});const h=f.props[c],p=h?h===!0?u.params:typeof h=="function"?h(u):h:null,b=pe(d,je({},p,t,{onVnodeUnmounted:m=>{m.component.isUnmounted&&(f.instances[c]=null)},ref:i}));return vf(n.default,{Component:b,route:u})||b}}});function vf(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const M$=A$;function P$(e){const t=o$(e.routes,e),n=e.parseQuery||_$,r=e.stringifyQuery||cf,o=e.history,s=uo(),l=uo(),a=uo(),i=kr(Mn);let u=Mn;$r&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const c=Hl.bind(null,L=>""+L),f=Hl.bind(null,w$),d=Hl.bind(null,Ks);function h(L,q){let U,ee;return Th(L)?(U=t.getRecordMatcher(L),ee=q):ee=L,t.addRoute(ee,U)}function p(L){const q=t.getRecordMatcher(L);q&&t.removeRoute(q)}function v(){return t.getRoutes().map(L=>L.record)}function b(L){return!!t.getRecordMatcher(L)}function m(L,q){if(q=je({},q||i.value),typeof L=="string"){const _=Dl(n,L,q.path),T=t.resolve({path:_.path},q),N=o.createHref(_.fullPath);return je(_,T,{params:d(T.params),hash:Ks(_.hash),redirectedFrom:void 0,href:N})}let U;if("path"in L)U=je({},L,{path:Dl(n,L.path,q.path).path});else{const _=je({},L.params);for(const T in _)_[T]==null&&delete _[T];U=je({},L,{params:f(L.params)}),q.params=f(q.params)}const ee=t.resolve(U,q),we=L.hash||"";ee.params=c(d(ee.params));const Ne=R4(r,je({},L,{hash:g$(we),path:ee.path})),_e=o.createHref(Ne);return je({fullPath:Ne,hash:we,query:r===cf?C$(L.query):L.query||{}},ee,{redirectedFrom:void 0,href:_e})}function w(L){return typeof L=="string"?Dl(n,L,i.value.path):je({},L)}function S(L,q){if(u!==L)return Gr(8,{from:q,to:L})}function g(L){return C(L)}function E(L){return g(je(w(L),{replace:!0}))}function O(L){const q=L.matched[L.matched.length-1];if(q&&q.redirect){const{redirect:U}=q;let ee=typeof U=="function"?U(L):U;return typeof ee=="string"&&(ee=ee.includes("?")||ee.includes("#")?ee=w(ee):{path:ee},ee.params={}),je({query:L.query,hash:L.hash,params:"path"in ee?{}:L.params},ee)}}function C(L,q){const U=u=m(L),ee=i.value,we=L.state,Ne=L.force,_e=L.replace===!0,_=O(U);if(_)return C(je(w(_),{state:typeof _=="object"?je({},we,_.state):we,force:Ne,replace:_e}),q||U);const T=U;T.redirectedFrom=q;let N;return!Ne&&k4(r,ee,U)&&(N=Gr(16,{to:T,from:ee}),te(ee,ee,!0,!1)),(N?Promise.resolve(N):k(T,ee)).catch(H=>vn(H)?vn(H,2)?H:R(H):se(H,T,ee)).then(H=>{if(H){if(vn(H,2))return C(je({replace:_e},w(H.to),{state:typeof H.to=="object"?je({},we,H.to.state):we,force:Ne}),q||T)}else H=F(T,ee,!0,_e,we);return M(T,ee,H),H})}function x(L,q){const U=S(L,q);return U?Promise.reject(U):Promise.resolve()}function k(L,q){let U;const[ee,we,Ne]=R$(L,q);U=zl(ee.reverse(),"beforeRouteLeave",L,q);for(const _ of ee)_.leaveGuards.forEach(T=>{U.push(Ln(T,L,q))});const _e=x.bind(null,L,q);return U.push(_e),Tr(U).then(()=>{U=[];for(const _ of s.list())U.push(Ln(_,L,q));return U.push(_e),Tr(U)}).then(()=>{U=zl(we,"beforeRouteUpdate",L,q);for(const _ of we)_.updateGuards.forEach(T=>{U.push(Ln(T,L,q))});return U.push(_e),Tr(U)}).then(()=>{U=[];for(const _ of L.matched)if(_.beforeEnter&&!q.matched.includes(_))if(Gt(_.beforeEnter))for(const T of _.beforeEnter)U.push(Ln(T,L,q));else U.push(Ln(_.beforeEnter,L,q));return U.push(_e),Tr(U)}).then(()=>(L.matched.forEach(_=>_.enterCallbacks={}),U=zl(Ne,"beforeRouteEnter",L,q),U.push(_e),Tr(U))).then(()=>{U=[];for(const _ of l.list())U.push(Ln(_,L,q));return U.push(_e),Tr(U)}).catch(_=>vn(_,8)?_:Promise.reject(_))}function M(L,q,U){for(const ee of a.list())ee(L,q,U)}function F(L,q,U,ee,we){const Ne=S(L,q);if(Ne)return Ne;const _e=q===Mn,_=$r?history.state:{};U&&(ee||_e?o.replace(L.fullPath,je({scroll:_e&&_&&_.scroll},we)):o.push(L.fullPath,we)),i.value=L,te(L,q,U,_e),R()}let A;function D(){A||(A=o.listen((L,q,U)=>{if(!Le.listening)return;const ee=m(L),we=O(ee);if(we){C(je(we,{replace:!0}),ee).catch(Co);return}u=ee;const Ne=i.value;$r&&z4(tf(Ne.fullPath,U.delta),yl()),k(ee,Ne).catch(_e=>vn(_e,12)?_e:vn(_e,2)?(C(_e.to,ee).then(_=>{vn(_,20)&&!U.delta&&U.type===Ho.pop&&o.go(-1,!1)}).catch(Co),Promise.reject()):(U.delta&&o.go(-U.delta,!1),se(_e,ee,Ne))).then(_e=>{_e=_e||F(ee,Ne,!1),_e&&(U.delta&&!vn(_e,8)?o.go(-U.delta,!1):U.type===Ho.pop&&vn(_e,20)&&o.go(-1,!1)),M(ee,Ne,_e)}).catch(Co)}))}let Q=uo(),V=uo(),Z;function se(L,q,U){R(L);const ee=V.list();return ee.length?ee.forEach(we=>we(L,q,U)):console.error(L),Promise.reject(L)}function j(){return Z&&i.value!==Mn?Promise.resolve():new Promise((L,q)=>{Q.add([L,q])})}function R(L){return Z||(Z=!L,D(),Q.list().forEach(([q,U])=>L?U(L):q()),Q.reset()),L}function te(L,q,U,ee){const{scrollBehavior:we}=e;if(!$r||!we)return Promise.resolve();const Ne=!U&&j4(tf(L.fullPath,0))||(ee||!U)&&history.state&&history.state.scroll||null;return Ke().then(()=>we(L,q,Ne)).then(_e=>_e&&D4(_e)).catch(_e=>se(_e,L,q))}const he=L=>o.go(L);let Ee;const Re=new Set,Le={currentRoute:i,listening:!0,addRoute:h,removeRoute:p,hasRoute:b,getRoutes:v,resolve:m,options:e,push:g,replace:E,go:he,back:()=>he(-1),forward:()=>he(1),beforeEach:s.add,beforeResolve:l.add,afterEach:a.add,onError:V.add,isReady:j,install(L){const q=this;L.component("RouterLink",T$),L.component("RouterView",M$),L.config.globalProperties.$router=q,Object.defineProperty(L.config.globalProperties,"$route",{enumerable:!0,get:()=>y(i)}),$r&&!Ee&&i.value===Mn&&(Ee=!0,g(o.location).catch(we=>{}));const U={};for(const we in Mn)U[we]=$(()=>i.value[we]);L.provide(Fi,q),L.provide(Ih,Xt(U)),L.provide(Pa,i);const ee=L.unmount;Re.add(L),L.unmount=function(){Re.delete(L),Re.size<1&&(u=Mn,A&&A(),A=null,i.value=Mn,Ee=!1,Z=!1),ee()}}};return Le}function Tr(e){return e.reduce((t,n)=>t.then(()=>n()),Promise.resolve())}function R$(e,t){const n=[],r=[],o=[],s=Math.max(t.matched.length,e.matched.length);for(let l=0;lYr(u,a))?r.push(a):n.push(a));const i=e.matched[l];i&&(t.matched.find(u=>Yr(u,i))||o.push(i))}return[n,r,o]}const k$=ue({__name:"Overview",setup(e){let t=P([]);return(()=>{fetch("/api/status",{credentials:"include"}).then(r=>r.json()).then(r=>{t.value=new Array;for(let o of r.tcp)t.value.push(o);for(let o of r.udp)t.value.push(o);for(let o of r.http)t.value.push(o);for(let o of r.https)t.value.push(o);for(let o of r.stcp)t.value.push(o);for(let o of r.sudp)t.value.push(o);for(let o of r.xtcp)t.value.push(o)}).catch(()=>{nr({showClose:!0,message:"Get status info from frpc failed!",type:"warning"})})})(),(r,o)=>{const s=UO,l=VO,a=ih,i=Pi;return B(),re("div",null,[ie(i,null,{default:ce(()=>[ie(a,{md:24},{default:ce(()=>[oe("div",null,[ie(l,{data:y(t),stripe:"",style:{width:"100%"},"default-sort":{prop:"type",order:"ascending"}},{default:ce(()=>[ie(s,{prop:"name",label:"name"}),ie(s,{prop:"type",label:"type",width:"150"}),ie(s,{prop:"local_addr",label:"local address",width:"200"}),ie(s,{prop:"plugin",label:"plugin",width:"200"}),ie(s,{prop:"remote_addr",label:"remote address"}),ie(s,{prop:"status",label:"status",width:"150"}),ie(s,{prop:"err",label:"info"})]),_:1},8,["data"])])]),_:1})]),_:1})])}}}),I$=ue({__name:"ClientConfigure",setup(e){let t=P("");const n=()=>{fetch("/api/config",{credentials:"include"}).then(o=>o.text()).then(o=>{t.value=o}).catch(()=>{nr({showClose:!0,message:"Get configure content from frpc failed!",type:"warning"})})},r=()=>{w4.confirm("This operation will upload your frpc configure file content and hot reload it, do you want to continue?","Notice",{confirmButtonText:"Yes",cancelButtonText:"No",type:"warning"}).then(()=>{if(t.value==""){nr({message:"Configure content can not be empty!",type:"warning"});return}fetch("/api/config",{credentials:"include",method:"PUT",body:t.value}).then(()=>{fetch("/api/reload",{credentials:"include"}).then(()=>{nr({type:"success",message:"Success"})}).catch(o=>{nr({showClose:!0,message:"Reload frpc configure file error, "+o,type:"warning"})})}).catch(()=>{nr({showClose:!0,message:"Put config to frpc and hot reload failed!",type:"warning"})})}).catch(()=>{nr({message:"Canceled",type:"info"})})};return n(),(o,s)=>{const l=Gp,a=Pi,i=Lp;return B(),re("div",null,[ie(a,{id:"head"},{default:ce(()=>[ie(l,{type:"primary",onClick:n},{default:ce(()=>[Ct("Refresh")]),_:1}),ie(l,{type:"primary",onClick:r},{default:ce(()=>[Ct("Upload")]),_:1})]),_:1}),ie(i,{type:"textarea",autosize:"",modelValue:y(t),"onUpdate:modelValue":s[0]||(s[0]=u=>Ve(t)?t.value=u:t=u),placeholder:"frpc configrue file, can not be empty..."},null,8,["modelValue"])])}}}),L$=P$({history:q4(),routes:[{path:"/",name:"Overview",component:k$},{path:"/configure",name:"ClientConfigure",component:I$}]}),Lh=hg($4);Lh.use(L$);Lh.mount("#app")});export default N$(); + */const $r=typeof window<"u";function A4(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const je=Object.assign;function Hl(e,t){const n={};for(const r in t){const o=t[r];n[r]=Gt(o)?o.map(e):e(o)}return n}const Co=()=>{},Gt=Array.isArray,M4=/\/$/,P4=e=>e.replace(M4,"");function Dl(e,t,n="/"){let r,o={},s="",l="";const a=t.indexOf("#");let i=t.indexOf("?");return a=0&&(i=-1),i>-1&&(r=t.slice(0,i),s=t.slice(i+1,a>-1?a:t.length),o=e(s)),a>-1&&(r=r||t.slice(0,a),l=t.slice(a,t.length)),r=L4(r??t,n),{fullPath:r+(s&&"?")+s+l,path:r,query:o,hash:l}}function R4(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function Qc(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function k4(e,t,n){const r=t.matched.length-1,o=n.matched.length-1;return r>-1&&r===o&&Yr(t.matched[r],n.matched[o])&&Sh(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function Yr(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Sh(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!I4(e[n],t[n]))return!1;return!0}function I4(e,t){return Gt(e)?ef(e,t):Gt(t)?ef(t,e):e===t}function ef(e,t){return Gt(t)?e.length===t.length&&e.every((n,r)=>n===t[r]):e.length===1&&e[0]===t}function L4(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),r=e.split("/");let o=n.length-1,s,l;for(s=0;s1&&o--;else break;return n.slice(0,o).join("/")+"/"+r.slice(s-(s===r.length?1:0)).join("/")}var Ho;(function(e){e.pop="pop",e.push="push"})(Ho||(Ho={}));var Eo;(function(e){e.back="back",e.forward="forward",e.unknown=""})(Eo||(Eo={}));function N4(e){if(!e)if($r){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),P4(e)}const F4=/^[^#]+#/;function B4(e,t){return e.replace(F4,"#")+t}function H4(e,t){const n=document.documentElement.getBoundingClientRect(),r=e.getBoundingClientRect();return{behavior:t.behavior,left:r.left-n.left-(t.left||0),top:r.top-n.top-(t.top||0)}}const yl=()=>({left:window.pageXOffset,top:window.pageYOffset});function D4(e){let t;if("el"in e){const n=e.el,r=typeof n=="string"&&n.startsWith("#"),o=typeof n=="string"?r?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!o)return;t=H4(o,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.pageXOffset,t.top!=null?t.top:window.pageYOffset)}function tf(e,t){return(history.state?history.state.position-t:-1)+e}const Aa=new Map;function z4(e,t){Aa.set(e,t)}function j4(e){const t=Aa.get(e);return Aa.delete(e),t}let W4=()=>location.protocol+"//"+location.host;function xh(e,t){const{pathname:n,search:r,hash:o}=t,s=e.indexOf("#");if(s>-1){let a=o.includes(e.slice(s))?e.slice(s).length:1,i=o.slice(a);return i[0]!=="/"&&(i="/"+i),Qc(i,"")}return Qc(n,e)+r+o}function K4(e,t,n,r){let o=[],s=[],l=null;const a=({state:d})=>{const h=xh(e,location),p=n.value,v=t.value;let b=0;if(d){if(n.value=h,t.value=d,l&&l===p){l=null;return}b=v?d.position-v.position:0}else r(h);o.forEach(m=>{m(n.value,p,{delta:b,type:Ho.pop,direction:b?b>0?Eo.forward:Eo.back:Eo.unknown})})};function i(){l=n.value}function u(d){o.push(d);const h=()=>{const p=o.indexOf(d);p>-1&&o.splice(p,1)};return s.push(h),h}function c(){const{history:d}=window;d.state&&d.replaceState(je({},d.state,{scroll:yl()}),"")}function f(){for(const d of s)d();s=[],window.removeEventListener("popstate",a),window.removeEventListener("beforeunload",c)}return window.addEventListener("popstate",a),window.addEventListener("beforeunload",c),{pauseListeners:i,listen:u,destroy:f}}function nf(e,t,n,r=!1,o=!1){return{back:e,current:t,forward:n,replaced:r,position:window.history.length,scroll:o?yl():null}}function V4(e){const{history:t,location:n}=window,r={value:xh(e,n)},o={value:t.state};o.value||s(r.value,{back:null,current:r.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function s(i,u,c){const f=e.indexOf("#"),d=f>-1?(n.host&&document.querySelector("base")?e:e.slice(f))+i:W4()+e+i;try{t[c?"replaceState":"pushState"](u,"",d),o.value=u}catch(h){console.error(h),n[c?"replace":"assign"](d)}}function l(i,u){const c=je({},t.state,nf(o.value.back,i,o.value.forward,!0),u,{position:o.value.position});s(i,c,!0),r.value=i}function a(i,u){const c=je({},o.value,t.state,{forward:i,scroll:yl()});s(c.current,c,!0);const f=je({},nf(r.value,i,null),{position:c.position+1},u);s(i,f,!1),r.value=i}return{location:r,state:o,push:a,replace:l}}function U4(e){e=N4(e);const t=V4(e),n=K4(e,t.state,t.location,t.replace);function r(s,l=!0){l||n.pauseListeners(),history.go(s)}const o=je({location:"",base:e,go:r,createHref:B4.bind(null,e)},t,n);return Object.defineProperty(o,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(o,"state",{enumerable:!0,get:()=>t.state.value}),o}function q4(e){return e=location.host?e||location.pathname+location.search:"",e.includes("#")||(e+="#"),U4(e)}function Y4(e){return typeof e=="string"||e&&typeof e=="object"}function Th(e){return typeof e=="string"||typeof e=="symbol"}const Mn={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},Oh=Symbol("");var rf;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(rf||(rf={}));function Gr(e,t){return je(new Error,{type:e,[Oh]:!0},t)}function vn(e,t){return e instanceof Error&&Oh in e&&(t==null||!!(e.type&t))}const of="[^/]+?",G4={sensitive:!1,strict:!1,start:!0,end:!0},X4=/[.+*?^${}()[\]/\\]/g;function J4(e,t){const n=je({},G4,t),r=[];let o=n.start?"^":"";const s=[];for(const u of e){const c=u.length?[]:[90];n.strict&&!u.length&&(o+="/");for(let f=0;ft.length?t.length===1&&t[0]===40+40?1:-1:0}function Q4(e,t){let n=0;const r=e.score,o=t.score;for(;n0&&t[t.length-1]<0}const e$={type:0,value:""},t$=/[a-zA-Z0-9_]/;function n$(e){if(!e)return[[]];if(e==="/")return[[e$]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(h){throw new Error(`ERR (${n})/"${u}": ${h}`)}let n=0,r=n;const o=[];let s;function l(){s&&o.push(s),s=[]}let a=0,i,u="",c="";function f(){u&&(n===0?s.push({type:0,value:u}):n===1||n===2||n===3?(s.length>1&&(i==="*"||i==="+")&&t(`A repeatable param (${u}) must be alone in its segment. eg: '/:ids+.`),s.push({type:1,value:u,regexp:c,repeatable:i==="*"||i==="+",optional:i==="*"||i==="?"})):t("Invalid state to consume buffer"),u="")}function d(){u+=i}for(;a{l(w)}:Co}function l(c){if(Th(c)){const f=r.get(c);f&&(r.delete(c),n.splice(n.indexOf(f),1),f.children.forEach(l),f.alias.forEach(l))}else{const f=n.indexOf(c);f>-1&&(n.splice(f,1),c.record.name&&r.delete(c.record.name),c.children.forEach(l),c.alias.forEach(l))}}function a(){return n}function i(c){let f=0;for(;f=0&&(c.record.path!==n[f].record.path||!$h(c,n[f]));)f++;n.splice(f,0,c),c.record.name&&!af(c)&&r.set(c.record.name,c)}function u(c,f){let d,h={},p,v;if("name"in c&&c.name){if(d=r.get(c.name),!d)throw Gr(1,{location:c});v=d.record.name,h=je(lf(f.params,d.keys.filter(w=>!w.optional).map(w=>w.name)),c.params&&lf(c.params,d.keys.map(w=>w.name))),p=d.stringify(h)}else if("path"in c)p=c.path,d=n.find(w=>w.re.test(p)),d&&(h=d.parse(p),v=d.record.name);else{if(d=f.name?r.get(f.name):n.find(w=>w.re.test(f.path)),!d)throw Gr(1,{location:c,currentLocation:f});v=d.record.name,h=je({},f.params,c.params),p=d.stringify(h)}const b=[];let m=d;for(;m;)b.unshift(m.record),m=m.parent;return{name:v,path:p,params:h,matched:b,meta:a$(b)}}return e.forEach(c=>s(c)),{addRoute:s,resolve:u,removeRoute:l,getRoutes:a,getRecordMatcher:o}}function lf(e,t){const n={};for(const r of t)r in e&&(n[r]=e[r]);return n}function s$(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:l$(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function l$(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const r in e.components)t[r]=typeof n=="boolean"?n:n[r];return t}function af(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function a$(e){return e.reduce((t,n)=>je(t,n.meta),{})}function uf(e,t){const n={};for(const r in e)n[r]=r in t?t[r]:e[r];return n}function $h(e,t){return t.children.some(n=>n===e||$h(e,n))}const Ah=/#/g,i$=/&/g,u$=/\//g,c$=/=/g,f$=/\?/g,Mh=/\+/g,d$=/%5B/g,p$=/%5D/g,Ph=/%5E/g,h$=/%60/g,Rh=/%7B/g,v$=/%7C/g,kh=/%7D/g,m$=/%20/g;function Ni(e){return encodeURI(""+e).replace(v$,"|").replace(d$,"[").replace(p$,"]")}function g$(e){return Ni(e).replace(Rh,"{").replace(kh,"}").replace(Ph,"^")}function Ma(e){return Ni(e).replace(Mh,"%2B").replace(m$,"+").replace(Ah,"%23").replace(i$,"%26").replace(h$,"`").replace(Rh,"{").replace(kh,"}").replace(Ph,"^")}function y$(e){return Ma(e).replace(c$,"%3D")}function b$(e){return Ni(e).replace(Ah,"%23").replace(f$,"%3F")}function w$(e){return e==null?"":b$(e).replace(u$,"%2F")}function Ks(e){try{return decodeURIComponent(""+e)}catch{}return""+e}function _$(e){const t={};if(e===""||e==="?")return t;const r=(e[0]==="?"?e.slice(1):e).split("&");for(let o=0;os&&Ma(s)):[r&&Ma(r)]).forEach(s=>{s!==void 0&&(t+=(t.length?"&":"")+n,s!=null&&(t+="="+s))})}return t}function C$(e){const t={};for(const n in e){const r=e[n];r!==void 0&&(t[n]=Gt(r)?r.map(o=>o==null?null:""+o):r==null?r:""+r)}return t}const E$=Symbol(""),ff=Symbol(""),Fi=Symbol(""),Ih=Symbol(""),Pa=Symbol("");function uo(){let e=[];function t(r){return e.push(r),()=>{const o=e.indexOf(r);o>-1&&e.splice(o,1)}}function n(){e=[]}return{add:t,list:()=>e,reset:n}}function Ln(e,t,n,r,o){const s=r&&(r.enterCallbacks[o]=r.enterCallbacks[o]||[]);return()=>new Promise((l,a)=>{const i=f=>{f===!1?a(Gr(4,{from:n,to:t})):f instanceof Error?a(f):Y4(f)?a(Gr(2,{from:t,to:f})):(s&&r.enterCallbacks[o]===s&&typeof f=="function"&&s.push(f),l())},u=e.call(r&&r.instances[o],t,n,i);let c=Promise.resolve(u);e.length<3&&(c=c.then(i)),c.catch(f=>a(f))})}function zl(e,t,n,r){const o=[];for(const s of e)for(const l in s.components){let a=s.components[l];if(!(t!=="beforeRouteEnter"&&!s.instances[l]))if(S$(a)){const u=(a.__vccOpts||a)[t];u&&o.push(Ln(u,n,r,s,l))}else{let i=a();o.push(()=>i.then(u=>{if(!u)return Promise.reject(new Error(`Couldn't resolve component "${l}" at "${s.path}"`));const c=A4(u)?u.default:u;s.components[l]=c;const d=(c.__vccOpts||c)[t];return d&&Ln(d,n,r,s,l)()}))}}return o}function S$(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function df(e){const t=Te(Fi),n=Te(Ih),r=$(()=>t.resolve(y(e.to))),o=$(()=>{const{matched:i}=r.value,{length:u}=i,c=i[u-1],f=n.matched;if(!c||!f.length)return-1;const d=f.findIndex(Yr.bind(null,c));if(d>-1)return d;const h=pf(i[u-2]);return u>1&&pf(c)===h&&f[f.length-1].path!==h?f.findIndex(Yr.bind(null,i[u-2])):d}),s=$(()=>o.value>-1&&$$(n.params,r.value.params)),l=$(()=>o.value>-1&&o.value===n.matched.length-1&&Sh(n.params,r.value.params));function a(i={}){return O$(i)?t[y(e.replace)?"replace":"push"](y(e.to)).catch(Co):Promise.resolve()}return{route:r,href:$(()=>r.value.href),isActive:s,isExactActive:l,navigate:a}}const x$=ue({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:df,setup(e,{slots:t}){const n=Xt(df(e)),{options:r}=Te(Fi),o=$(()=>({[hf(e.activeClass,r.linkActiveClass,"router-link-active")]:n.isActive,[hf(e.exactActiveClass,r.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const s=t.default&&t.default(n);return e.custom?s:pe("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:o.value},s)}}}),T$=x$;function O$(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function $$(e,t){for(const n in t){const r=t[n],o=e[n];if(typeof r=="string"){if(r!==o)return!1}else if(!Gt(o)||o.length!==r.length||r.some((s,l)=>s!==o[l]))return!1}return!0}function pf(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const hf=(e,t,n)=>e??t??n,A$=ue({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const r=Te(Pa),o=$(()=>e.route||r.value),s=Te(ff,0),l=$(()=>{let u=y(s);const{matched:c}=o.value;let f;for(;(f=c[u])&&!f.components;)u++;return u}),a=$(()=>o.value.matched[l.value]);it(ff,$(()=>l.value+1)),it(E$,a),it(Pa,o);const i=P();return de(()=>[i.value,a.value,e.name],([u,c,f],[d,h,p])=>{c&&(c.instances[f]=u,h&&h!==c&&u&&u===d&&(c.leaveGuards.size||(c.leaveGuards=h.leaveGuards),c.updateGuards.size||(c.updateGuards=h.updateGuards))),u&&c&&(!h||!Yr(c,h)||!d)&&(c.enterCallbacks[f]||[]).forEach(v=>v(u))},{flush:"post"}),()=>{const u=o.value,c=e.name,f=a.value,d=f&&f.components[c];if(!d)return vf(n.default,{Component:d,route:u});const h=f.props[c],p=h?h===!0?u.params:typeof h=="function"?h(u):h:null,b=pe(d,je({},p,t,{onVnodeUnmounted:m=>{m.component.isUnmounted&&(f.instances[c]=null)},ref:i}));return vf(n.default,{Component:b,route:u})||b}}});function vf(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const M$=A$;function P$(e){const t=o$(e.routes,e),n=e.parseQuery||_$,r=e.stringifyQuery||cf,o=e.history,s=uo(),l=uo(),a=uo(),i=kr(Mn);let u=Mn;$r&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const c=Hl.bind(null,L=>""+L),f=Hl.bind(null,w$),d=Hl.bind(null,Ks);function h(L,q){let U,ee;return Th(L)?(U=t.getRecordMatcher(L),ee=q):ee=L,t.addRoute(ee,U)}function p(L){const q=t.getRecordMatcher(L);q&&t.removeRoute(q)}function v(){return t.getRoutes().map(L=>L.record)}function b(L){return!!t.getRecordMatcher(L)}function m(L,q){if(q=je({},q||i.value),typeof L=="string"){const _=Dl(n,L,q.path),T=t.resolve({path:_.path},q),N=o.createHref(_.fullPath);return je(_,T,{params:d(T.params),hash:Ks(_.hash),redirectedFrom:void 0,href:N})}let U;if("path"in L)U=je({},L,{path:Dl(n,L.path,q.path).path});else{const _=je({},L.params);for(const T in _)_[T]==null&&delete _[T];U=je({},L,{params:f(L.params)}),q.params=f(q.params)}const ee=t.resolve(U,q),we=L.hash||"";ee.params=c(d(ee.params));const Ne=R4(r,je({},L,{hash:g$(we),path:ee.path})),_e=o.createHref(Ne);return je({fullPath:Ne,hash:we,query:r===cf?C$(L.query):L.query||{}},ee,{redirectedFrom:void 0,href:_e})}function w(L){return typeof L=="string"?Dl(n,L,i.value.path):je({},L)}function S(L,q){if(u!==L)return Gr(8,{from:q,to:L})}function g(L){return C(L)}function E(L){return g(je(w(L),{replace:!0}))}function O(L){const q=L.matched[L.matched.length-1];if(q&&q.redirect){const{redirect:U}=q;let ee=typeof U=="function"?U(L):U;return typeof ee=="string"&&(ee=ee.includes("?")||ee.includes("#")?ee=w(ee):{path:ee},ee.params={}),je({query:L.query,hash:L.hash,params:"path"in ee?{}:L.params},ee)}}function C(L,q){const U=u=m(L),ee=i.value,we=L.state,Ne=L.force,_e=L.replace===!0,_=O(U);if(_)return C(je(w(_),{state:typeof _=="object"?je({},we,_.state):we,force:Ne,replace:_e}),q||U);const T=U;T.redirectedFrom=q;let N;return!Ne&&k4(r,ee,U)&&(N=Gr(16,{to:T,from:ee}),te(ee,ee,!0,!1)),(N?Promise.resolve(N):k(T,ee)).catch(H=>vn(H)?vn(H,2)?H:R(H):se(H,T,ee)).then(H=>{if(H){if(vn(H,2))return C(je({replace:_e},w(H.to),{state:typeof H.to=="object"?je({},we,H.to.state):we,force:Ne}),q||T)}else H=F(T,ee,!0,_e,we);return M(T,ee,H),H})}function x(L,q){const U=S(L,q);return U?Promise.reject(U):Promise.resolve()}function k(L,q){let U;const[ee,we,Ne]=R$(L,q);U=zl(ee.reverse(),"beforeRouteLeave",L,q);for(const _ of ee)_.leaveGuards.forEach(T=>{U.push(Ln(T,L,q))});const _e=x.bind(null,L,q);return U.push(_e),Tr(U).then(()=>{U=[];for(const _ of s.list())U.push(Ln(_,L,q));return U.push(_e),Tr(U)}).then(()=>{U=zl(we,"beforeRouteUpdate",L,q);for(const _ of we)_.updateGuards.forEach(T=>{U.push(Ln(T,L,q))});return U.push(_e),Tr(U)}).then(()=>{U=[];for(const _ of L.matched)if(_.beforeEnter&&!q.matched.includes(_))if(Gt(_.beforeEnter))for(const T of _.beforeEnter)U.push(Ln(T,L,q));else U.push(Ln(_.beforeEnter,L,q));return U.push(_e),Tr(U)}).then(()=>(L.matched.forEach(_=>_.enterCallbacks={}),U=zl(Ne,"beforeRouteEnter",L,q),U.push(_e),Tr(U))).then(()=>{U=[];for(const _ of l.list())U.push(Ln(_,L,q));return U.push(_e),Tr(U)}).catch(_=>vn(_,8)?_:Promise.reject(_))}function M(L,q,U){for(const ee of a.list())ee(L,q,U)}function F(L,q,U,ee,we){const Ne=S(L,q);if(Ne)return Ne;const _e=q===Mn,_=$r?history.state:{};U&&(ee||_e?o.replace(L.fullPath,je({scroll:_e&&_&&_.scroll},we)):o.push(L.fullPath,we)),i.value=L,te(L,q,U,_e),R()}let A;function D(){A||(A=o.listen((L,q,U)=>{if(!Le.listening)return;const ee=m(L),we=O(ee);if(we){C(je(we,{replace:!0}),ee).catch(Co);return}u=ee;const Ne=i.value;$r&&z4(tf(Ne.fullPath,U.delta),yl()),k(ee,Ne).catch(_e=>vn(_e,12)?_e:vn(_e,2)?(C(_e.to,ee).then(_=>{vn(_,20)&&!U.delta&&U.type===Ho.pop&&o.go(-1,!1)}).catch(Co),Promise.reject()):(U.delta&&o.go(-U.delta,!1),se(_e,ee,Ne))).then(_e=>{_e=_e||F(ee,Ne,!1),_e&&(U.delta&&!vn(_e,8)?o.go(-U.delta,!1):U.type===Ho.pop&&vn(_e,20)&&o.go(-1,!1)),M(ee,Ne,_e)}).catch(Co)}))}let Q=uo(),V=uo(),Z;function se(L,q,U){R(L);const ee=V.list();return ee.length?ee.forEach(we=>we(L,q,U)):console.error(L),Promise.reject(L)}function j(){return Z&&i.value!==Mn?Promise.resolve():new Promise((L,q)=>{Q.add([L,q])})}function R(L){return Z||(Z=!L,D(),Q.list().forEach(([q,U])=>L?U(L):q()),Q.reset()),L}function te(L,q,U,ee){const{scrollBehavior:we}=e;if(!$r||!we)return Promise.resolve();const Ne=!U&&j4(tf(L.fullPath,0))||(ee||!U)&&history.state&&history.state.scroll||null;return Ke().then(()=>we(L,q,Ne)).then(_e=>_e&&D4(_e)).catch(_e=>se(_e,L,q))}const he=L=>o.go(L);let Ee;const Re=new Set,Le={currentRoute:i,listening:!0,addRoute:h,removeRoute:p,hasRoute:b,getRoutes:v,resolve:m,options:e,push:g,replace:E,go:he,back:()=>he(-1),forward:()=>he(1),beforeEach:s.add,beforeResolve:l.add,afterEach:a.add,onError:V.add,isReady:j,install(L){const q=this;L.component("RouterLink",T$),L.component("RouterView",M$),L.config.globalProperties.$router=q,Object.defineProperty(L.config.globalProperties,"$route",{enumerable:!0,get:()=>y(i)}),$r&&!Ee&&i.value===Mn&&(Ee=!0,g(o.location).catch(we=>{}));const U={};for(const we in Mn)U[we]=$(()=>i.value[we]);L.provide(Fi,q),L.provide(Ih,Xt(U)),L.provide(Pa,i);const ee=L.unmount;Re.add(L),L.unmount=function(){Re.delete(L),Re.size<1&&(u=Mn,A&&A(),A=null,i.value=Mn,Ee=!1,Z=!1),ee()}}};return Le}function Tr(e){return e.reduce((t,n)=>t.then(()=>n()),Promise.resolve())}function R$(e,t){const n=[],r=[],o=[],s=Math.max(t.matched.length,e.matched.length);for(let l=0;lYr(u,a))?r.push(a):n.push(a));const i=e.matched[l];i&&(t.matched.find(u=>Yr(u,i))||o.push(i))}return[n,r,o]}const k$=ue({__name:"Overview",setup(e){let t=P([]);return(()=>{fetch("/api/status",{credentials:"include"}).then(r=>r.json()).then(r=>{t.value=new Array;for(let o in r)for(let s of r[o])console.log(s),t.value.push(s)}).catch(r=>{nr({showClose:!0,message:"Get status info from frpc failed!"+r,type:"warning"})})})(),(r,o)=>{const s=UO,l=VO,a=ih,i=Pi;return B(),re("div",null,[ie(i,null,{default:ce(()=>[ie(a,{md:24},{default:ce(()=>[oe("div",null,[ie(l,{data:y(t),stripe:"",style:{width:"100%"},"default-sort":{prop:"type",order:"ascending"}},{default:ce(()=>[ie(s,{prop:"name",label:"name",sortable:""}),ie(s,{prop:"type",label:"type",width:"150",sortable:""}),ie(s,{prop:"local_addr",label:"local address",width:"200",sortable:""}),ie(s,{prop:"plugin",label:"plugin",width:"200",sortable:""}),ie(s,{prop:"remote_addr",label:"remote address",sortable:""}),ie(s,{prop:"status",label:"status",width:"150",sortable:""}),ie(s,{prop:"err",label:"info"})]),_:1},8,["data"])])]),_:1})]),_:1})])}}}),I$=ue({__name:"ClientConfigure",setup(e){let t=P("");const n=()=>{fetch("/api/config",{credentials:"include"}).then(o=>o.text()).then(o=>{t.value=o}).catch(()=>{nr({showClose:!0,message:"Get configure content from frpc failed!",type:"warning"})})},r=()=>{w4.confirm("This operation will upload your frpc configure file content and hot reload it, do you want to continue?","Notice",{confirmButtonText:"Yes",cancelButtonText:"No",type:"warning"}).then(()=>{if(t.value==""){nr({message:"Configure content can not be empty!",type:"warning"});return}fetch("/api/config",{credentials:"include",method:"PUT",body:t.value}).then(()=>{fetch("/api/reload",{credentials:"include"}).then(()=>{nr({type:"success",message:"Success"})}).catch(o=>{nr({showClose:!0,message:"Reload frpc configure file error, "+o,type:"warning"})})}).catch(()=>{nr({showClose:!0,message:"Put config to frpc and hot reload failed!",type:"warning"})})}).catch(()=>{nr({message:"Canceled",type:"info"})})};return n(),(o,s)=>{const l=Gp,a=Pi,i=Lp;return B(),re("div",null,[ie(a,{id:"head"},{default:ce(()=>[ie(l,{type:"primary",onClick:n},{default:ce(()=>[Ct("Refresh")]),_:1}),ie(l,{type:"primary",onClick:r},{default:ce(()=>[Ct("Upload")]),_:1})]),_:1}),ie(i,{type:"textarea",autosize:"",modelValue:y(t),"onUpdate:modelValue":s[0]||(s[0]=u=>Ve(t)?t.value=u:t=u),placeholder:"frpc configrue file, can not be empty..."},null,8,["modelValue"])])}}}),L$=P$({history:q4(),routes:[{path:"/",name:"Overview",component:k$},{path:"/configure",name:"ClientConfigure",component:I$}]}),Lh=hg($4);Lh.use(L$);Lh.mount("#app")});export default N$(); diff --git a/assets/frpc/static/index.html b/assets/frpc/static/index.html index 512019e1..eacf292b 100644 --- a/assets/frpc/static/index.html +++ b/assets/frpc/static/index.html @@ -4,7 +4,7 @@ frp client admin UI - + diff --git a/client/admin_api.go b/client/admin_api.go index 545ba7fc..d0177076 100644 --- a/client/admin_api.go +++ b/client/admin_api.go @@ -28,6 +28,7 @@ import ( "github.com/fatedier/frp/client/proxy" "github.com/fatedier/frp/pkg/config" "github.com/fatedier/frp/pkg/util/log" + "github.com/fatedier/frp/pkg/util/util" ) type GeneralResponse struct { @@ -70,15 +71,7 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) { log.Info("success reload conf") } -type StatusResp struct { - TCP []ProxyStatusResp `json:"tcp"` - UDP []ProxyStatusResp `json:"udp"` - HTTP []ProxyStatusResp `json:"http"` - HTTPS []ProxyStatusResp `json:"https"` - STCP []ProxyStatusResp `json:"stcp"` - XTCP []ProxyStatusResp `json:"xtcp"` - SUDP []ProxyStatusResp `json:"sudp"` -} +type StatusResp map[string][]ProxyStatusResp type ProxyStatusResp struct { Name string `json:"name"` @@ -90,12 +83,6 @@ type ProxyStatusResp struct { RemoteAddr string `json:"remote_addr"` } -type ByProxyStatusResp []ProxyStatusResp - -func (a ByProxyStatusResp) Len() int { return len(a) } -func (a ByProxyStatusResp) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a ByProxyStatusResp) Less(i, j int) bool { return strings.Compare(a[i].Name, a[j].Name) < 0 } - func NewProxyStatusResp(status *proxy.WorkingStatus, serverAddr string) ProxyStatusResp { psr := ProxyStatusResp{ Name: status.Name, @@ -103,53 +90,17 @@ func NewProxyStatusResp(status *proxy.WorkingStatus, serverAddr string) ProxySta Status: status.Phase, Err: status.Err, } - switch cfg := status.Cfg.(type) { - case *config.TCPProxyConf: - if cfg.LocalPort != 0 { - psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort)) - } - psr.Plugin = cfg.Plugin - if status.Err != "" { - psr.RemoteAddr = net.JoinHostPort(serverAddr, strconv.Itoa(cfg.RemotePort)) - } else { - psr.RemoteAddr = serverAddr + status.RemoteAddr - } - case *config.UDPProxyConf: - if cfg.LocalPort != 0 { - psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort)) - } - if status.Err != "" { - psr.RemoteAddr = net.JoinHostPort(serverAddr, strconv.Itoa(cfg.RemotePort)) - } else { - psr.RemoteAddr = serverAddr + status.RemoteAddr - } - case *config.HTTPProxyConf: - if cfg.LocalPort != 0 { - psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort)) - } - psr.Plugin = cfg.Plugin + baseCfg := status.Cfg.GetBaseInfo() + if baseCfg.LocalPort != 0 { + psr.LocalAddr = net.JoinHostPort(baseCfg.LocalIP, strconv.Itoa(baseCfg.LocalPort)) + } + psr.Plugin = baseCfg.Plugin + + if status.Err == "" { psr.RemoteAddr = status.RemoteAddr - case *config.HTTPSProxyConf: - if cfg.LocalPort != 0 { - psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort)) + if util.InSlice(status.Type, []string{"tcp", "udp"}) { + psr.RemoteAddr = serverAddr + psr.RemoteAddr } - psr.Plugin = cfg.Plugin - psr.RemoteAddr = status.RemoteAddr - case *config.STCPProxyConf: - if cfg.LocalPort != 0 { - psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort)) - } - psr.Plugin = cfg.Plugin - case *config.XTCPProxyConf: - if cfg.LocalPort != 0 { - psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort)) - } - psr.Plugin = cfg.Plugin - case *config.SUDPProxyConf: - if cfg.LocalPort != 0 { - psr.LocalAddr = net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort)) - } - psr.Plugin = cfg.Plugin } return psr } @@ -158,15 +109,8 @@ func NewProxyStatusResp(status *proxy.WorkingStatus, serverAddr string) ProxySta func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) { var ( buf []byte - res StatusResp + res StatusResp = make(map[string][]ProxyStatusResp) ) - res.TCP = make([]ProxyStatusResp, 0) - res.UDP = make([]ProxyStatusResp, 0) - res.HTTP = make([]ProxyStatusResp, 0) - res.HTTPS = make([]ProxyStatusResp, 0) - res.STCP = make([]ProxyStatusResp, 0) - res.XTCP = make([]ProxyStatusResp, 0) - res.SUDP = make([]ProxyStatusResp, 0) log.Info("Http request [/api/status]") defer func() { @@ -177,30 +121,17 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) { ps := svr.ctl.pm.GetAllProxyStatus() for _, status := range ps { - switch status.Type { - case "tcp": - res.TCP = append(res.TCP, NewProxyStatusResp(status, svr.cfg.ServerAddr)) - case "udp": - res.UDP = append(res.UDP, NewProxyStatusResp(status, svr.cfg.ServerAddr)) - case "http": - res.HTTP = append(res.HTTP, NewProxyStatusResp(status, svr.cfg.ServerAddr)) - case "https": - res.HTTPS = append(res.HTTPS, NewProxyStatusResp(status, svr.cfg.ServerAddr)) - case "stcp": - res.STCP = append(res.STCP, NewProxyStatusResp(status, svr.cfg.ServerAddr)) - case "xtcp": - res.XTCP = append(res.XTCP, NewProxyStatusResp(status, svr.cfg.ServerAddr)) - case "sudp": - res.SUDP = append(res.SUDP, NewProxyStatusResp(status, svr.cfg.ServerAddr)) - } + res[status.Type] = append(res[status.Type], NewProxyStatusResp(status, svr.cfg.ServerAddr)) + } + + for _, arrs := range res { + if len(arrs) <= 1 { + continue + } + sort.Slice(arrs, func(i, j int) bool { + return strings.Compare(arrs[i].Name, arrs[j].Name) < 0 + }) } - sort.Sort(ByProxyStatusResp(res.TCP)) - sort.Sort(ByProxyStatusResp(res.UDP)) - sort.Sort(ByProxyStatusResp(res.HTTP)) - sort.Sort(ByProxyStatusResp(res.HTTPS)) - sort.Sort(ByProxyStatusResp(res.STCP)) - sort.Sort(ByProxyStatusResp(res.XTCP)) - sort.Sort(ByProxyStatusResp(res.SUDP)) } // GET api/config diff --git a/cmd/frpc/sub/status.go b/cmd/frpc/sub/status.go index c9a6a98d..db7471e3 100644 --- a/cmd/frpc/sub/status.go +++ b/cmd/frpc/sub/status.go @@ -81,67 +81,27 @@ func status(clientCfg config.ClientCommonConf) error { if err != nil { return err } - res := &client.StatusResp{} + res := make(client.StatusResp) err = json.Unmarshal(body, &res) if err != nil { return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body))) } fmt.Println("Proxy Status...") - if len(res.TCP) > 0 { - fmt.Println("TCP") - tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.TCP { - tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) + types := []string{"tcp", "udp", "tcpmux", "http", "https", "stcp", "sudp", "xtcp"} + for _, pxyType := range types { + arrs := res[pxyType] + if len(arrs) == 0 { + continue } - tbl.Print() - fmt.Println("") - } - if len(res.UDP) > 0 { - fmt.Println("UDP") - tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.UDP { - tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) - } - tbl.Print() - fmt.Println("") - } - if len(res.HTTP) > 0 { - fmt.Println("HTTP") - tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.HTTP { - tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) - } - tbl.Print() - fmt.Println("") - } - if len(res.HTTPS) > 0 { - fmt.Println("HTTPS") - tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.HTTPS { - tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) - } - tbl.Print() - fmt.Println("") - } - if len(res.STCP) > 0 { - fmt.Println("STCP") - tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.STCP { - tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) - } - tbl.Print() - fmt.Println("") - } - if len(res.XTCP) > 0 { - fmt.Println("XTCP") - tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") - for _, ps := range res.XTCP { - tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) - } - tbl.Print() - fmt.Println("") - } + fmt.Println(strings.ToUpper(pxyType)) + tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error") + for _, ps := range arrs { + tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err) + } + tbl.Print() + fmt.Println("") + } return nil } diff --git a/go.mod b/go.mod index dd631841..6c261c2e 100644 --- a/go.mod +++ b/go.mod @@ -14,19 +14,19 @@ require ( github.com/gorilla/websocket v1.5.0 github.com/hashicorp/yamux v0.1.1 github.com/onsi/ginkgo v1.16.4 - github.com/onsi/gomega v1.20.2 + github.com/onsi/gomega v1.23.0 github.com/pires/go-proxyproto v0.6.2 github.com/prometheus/client_golang v1.13.0 github.com/quic-go/quic-go v0.32.0 github.com/rodaine/table v1.0.1 github.com/spf13/cobra v1.1.3 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.8.0 golang.org/x/net v0.4.0 golang.org/x/oauth2 v0.3.0 golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 gopkg.in/ini.v1 v1.67.0 - k8s.io/apimachinery v0.25.0 - k8s.io/client-go v0.25.0 + k8s.io/apimachinery v0.26.1 + k8s.io/client-go v0.26.1 ) require ( @@ -41,7 +41,7 @@ require ( github.com/golang/mock v1.6.0 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.3 // indirect - github.com/google/go-cmp v0.5.8 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/klauspost/cpuid/v2 v2.0.6 // indirect @@ -49,7 +49,7 @@ require ( github.com/leodido/go-urn v1.2.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/onsi/ginkgo/v2 v2.2.0 // indirect + github.com/onsi/ginkgo/v2 v2.4.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect @@ -73,5 +73,5 @@ require ( gopkg.in/square/go-jose.v2 v2.6.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed // indirect + k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect ) diff --git a/go.sum b/go.sum index 521f06b2..500e06b3 100644 --- a/go.sum +++ b/go.sum @@ -141,7 +141,7 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= @@ -205,8 +205,9 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -338,12 +339,12 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI= -github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/ginkgo/v2 v2.4.0 h1:+Ig9nvqgS5OBSACXNk15PLdp0U9XPYROt9CFzVdFGIs= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY= -github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= +github.com/onsi/gomega v1.23.0 h1:/oxKu9c2HVap+F3PfKort2Hw5DEU+HGlW8n+tguWsys= +github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8= @@ -414,7 +415,6 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= @@ -425,13 +425,16 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= @@ -967,13 +970,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU= -k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0= -k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= -k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed h1:jAne/RjBTyawwAy0utX5eqigAwz/lQhTmy+Hr/Cpue4= -k8s.io/utils v0.0.0-20220728103510-ee6ede2d64ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= +k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= +k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= +k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= +k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= +k8s.io/utils v0.0.0-20221107191617-1a15be271d1d h1:0Smp/HP1OH4Rvhe+4B8nWGERtlqAGSftbSbbmm45oFs= +k8s.io/utils v0.0.0-20221107191617-1a15be271d1d/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/util/util/slice.go b/pkg/util/util/slice.go new file mode 100644 index 00000000..6d4f14ec --- /dev/null +++ b/pkg/util/util/slice.go @@ -0,0 +1,25 @@ +package util + +func InSlice[T comparable](v T, s []T) bool { + for _, vv := range s { + if v == vv { + return true + } + } + return false +} + +func InSliceAny[T any](v T, s []T, equalFn func(a, b T) bool) bool { + for _, vv := range s { + if equalFn(v, vv) { + return true + } + } + return false +} + +func InSliceAnyFunc[T any](equalFn func(a, b T) bool) func(v T, s []T) bool { + return func(v T, s []T) bool { + return InSliceAny(v, s, equalFn) + } +} diff --git a/pkg/util/util/slice_test.go b/pkg/util/util/slice_test.go new file mode 100644 index 00000000..3e9d1428 --- /dev/null +++ b/pkg/util/util/slice_test.go @@ -0,0 +1,49 @@ +package util + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestInSlice(t *testing.T) { + require := require.New(t) + require.True(InSlice(1, []int{1, 2, 3})) + require.False(InSlice(0, []int{1, 2, 3})) + require.True(InSlice("foo", []string{"foo", "bar"})) + require.False(InSlice("not exist", []string{"foo", "bar"})) +} + +type testStructA struct { + Name string + Age int +} + +func TestInSliceAny(t *testing.T) { + require := require.New(t) + + a := testStructA{Name: "foo", Age: 20} + b := testStructA{Name: "foo", Age: 30} + c := testStructA{Name: "bar", Age: 20} + + equalFn := func(o, p testStructA) bool { + return o.Name == p.Name + } + require.True(InSliceAny(a, []testStructA{b, c}, equalFn)) + require.False(InSliceAny(c, []testStructA{a, b}, equalFn)) +} + +func TestInSliceAnyFunc(t *testing.T) { + require := require.New(t) + + a := testStructA{Name: "foo", Age: 20} + b := testStructA{Name: "foo", Age: 30} + c := testStructA{Name: "bar", Age: 20} + + equalFn := func(o, p testStructA) bool { + return o.Name == p.Name + } + testStructAInSlice := InSliceAnyFunc(equalFn) + require.True(testStructAInSlice(a, []testStructA{b, c})) + require.False(testStructAInSlice(c, []testStructA{a, b})) +} diff --git a/test/e2e/framework/process.go b/test/e2e/framework/process.go index 6761d8f4..814ce04a 100644 --- a/test/e2e/framework/process.go +++ b/test/e2e/framework/process.go @@ -38,7 +38,7 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str err = p.Start() ExpectNoError(err) } - time.Sleep(time.Second) + time.Sleep(2 * time.Second) currentClientProcesses := make([]*process.Process, 0, len(clientTemplates)) for i := range clientTemplates { diff --git a/test/e2e/pkg/port/port.go b/test/e2e/pkg/port/port.go index 298892e7..f1136942 100644 --- a/test/e2e/pkg/port/port.go +++ b/test/e2e/pkg/port/port.go @@ -10,8 +10,8 @@ import ( ) type Allocator struct { - reserved sets.Int - used sets.Int + reserved sets.Set[int] + used sets.Set[int] mu sync.Mutex } @@ -20,8 +20,8 @@ type Allocator struct { // Reserved ports: 13, 17 func NewAllocator(from int, to int, mod int, index int) *Allocator { pa := &Allocator{ - reserved: sets.NewInt(), - used: sets.NewInt(), + reserved: sets.New[int](), + used: sets.New[int](), } for i := from; i <= to; i++ { diff --git a/test/e2e/pkg/sdk/client/client.go b/test/e2e/pkg/sdk/client/client.go index 2bca92e7..4fb580d1 100644 --- a/test/e2e/pkg/sdk/client/client.go +++ b/test/e2e/pkg/sdk/client/client.go @@ -39,43 +39,15 @@ func (c *Client) GetProxyStatus(name string) (*client.ProxyStatusResp, error) { if err != nil { return nil, err } - allStatus := &client.StatusResp{} + allStatus := make(client.StatusResp) if err = json.Unmarshal([]byte(content), &allStatus); err != nil { return nil, fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(content)) } - for _, s := range allStatus.TCP { - if s.Name == name { - return &s, nil - } - } - for _, s := range allStatus.UDP { - if s.Name == name { - return &s, nil - } - } - for _, s := range allStatus.HTTP { - if s.Name == name { - return &s, nil - } - } - for _, s := range allStatus.HTTPS { - if s.Name == name { - return &s, nil - } - } - for _, s := range allStatus.STCP { - if s.Name == name { - return &s, nil - } - } - for _, s := range allStatus.XTCP { - if s.Name == name { - return &s, nil - } - } - for _, s := range allStatus.SUDP { - if s.Name == name { - return &s, nil + for _, pss := range allStatus { + for _, ps := range pss { + if ps.Name == name { + return &ps, nil + } } } return nil, fmt.Errorf("no proxy status found") diff --git a/web/frpc/.eslintrc.cjs b/web/frpc/.eslintrc.cjs index 0667b52c..d1ed4702 100644 --- a/web/frpc/.eslintrc.cjs +++ b/web/frpc/.eslintrc.cjs @@ -1,30 +1,30 @@ /* eslint-env node */ -require("@rushstack/eslint-patch/modern-module-resolution"); +require('@rushstack/eslint-patch/modern-module-resolution') module.exports = { root: true, extends: [ - "plugin:vue/vue3-essential", - "eslint:recommended", - "@vue/eslint-config-typescript", - "@vue/eslint-config-prettier", + 'plugin:vue/vue3-essential', + 'eslint:recommended', + '@vue/eslint-config-typescript', + '@vue/eslint-config-prettier', ], parserOptions: { - ecmaVersion: "latest", + ecmaVersion: 'latest', }, rules: { - "@typescript-eslint/no-unused-vars": [ - "warn", + '@typescript-eslint/no-unused-vars': [ + 'warn', { - argsIgnorePattern: "^_", - varsIgnorePattern: "^_", + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', }, ], - "vue/multi-word-component-names": [ - "error", + 'vue/multi-word-component-names': [ + 'error', { - ignores: ["Overview"], + ignores: ['Overview'], }, ], }, -}; +} diff --git a/web/frpc/.prettierrc.json b/web/frpc/.prettierrc.json index 9e26dfee..f8dec5b1 100644 --- a/web/frpc/.prettierrc.json +++ b/web/frpc/.prettierrc.json @@ -1 +1,5 @@ -{} \ No newline at end of file +{ + "tabWidth": 2, + "semi": false, + "singleQuote": true +} diff --git a/web/frpc/src/App.vue b/web/frpc/src/App.vue index b72ed715..7addad61 100644 --- a/web/frpc/src/App.vue +++ b/web/frpc/src/App.vue @@ -48,18 +48,18 @@ diff --git a/web/frpc/src/main.ts b/web/frpc/src/main.ts index 566b7365..636b05a6 100644 --- a/web/frpc/src/main.ts +++ b/web/frpc/src/main.ts @@ -1,13 +1,13 @@ -import { createApp } from "vue"; -import "element-plus/dist/index.css"; -import "element-plus/theme-chalk/dark/css-vars.css"; -import App from "./App.vue"; -import router from "./router"; +import { createApp } from 'vue' +import 'element-plus/dist/index.css' +import 'element-plus/theme-chalk/dark/css-vars.css' +import App from './App.vue' +import router from './router' -import "./assets/dark.css"; +import './assets/dark.css' -const app = createApp(App); +const app = createApp(App) -app.use(router); +app.use(router) -app.mount("#app"); +app.mount('#app') diff --git a/web/frpc/src/router/index.ts b/web/frpc/src/router/index.ts index ea63b712..7c42c258 100644 --- a/web/frpc/src/router/index.ts +++ b/web/frpc/src/router/index.ts @@ -1,21 +1,21 @@ -import { createRouter, createWebHashHistory } from "vue-router"; -import Overview from "../components/Overview.vue"; -import ClientConfigure from "../components/ClientConfigure.vue"; +import { createRouter, createWebHashHistory } from 'vue-router' +import Overview from '../components/Overview.vue' +import ClientConfigure from '../components/ClientConfigure.vue' const router = createRouter({ history: createWebHashHistory(), routes: [ { - path: "/", - name: "Overview", + path: '/', + name: 'Overview', component: Overview, }, { - path: "/configure", - name: "ClientConfigure", + path: '/configure', + name: 'ClientConfigure', component: ClientConfigure, }, ], -}); +}) -export default router; +export default router diff --git a/web/frpc/vite.config.ts b/web/frpc/vite.config.ts index 6c1e5bdd..656ab9eb 100644 --- a/web/frpc/vite.config.ts +++ b/web/frpc/vite.config.ts @@ -1,14 +1,14 @@ -import { fileURLToPath, URL } from "node:url"; +import { fileURLToPath, URL } from 'node:url' -import { defineConfig } from "vite"; -import vue from "@vitejs/plugin-vue"; -import AutoImport from "unplugin-auto-import/vite"; -import Components from "unplugin-vue-components/vite"; -import { ElementPlusResolver } from "unplugin-vue-components/resolvers"; +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' +import AutoImport from 'unplugin-auto-import/vite' +import Components from 'unplugin-vue-components/vite' +import { ElementPlusResolver } from 'unplugin-vue-components/resolvers' // https://vitejs.dev/config/ export default defineConfig({ - base: "", + base: '', plugins: [ vue(), AutoImport({ @@ -20,10 +20,10 @@ export default defineConfig({ ], resolve: { alias: { - "@": fileURLToPath(new URL("./src", import.meta.url)), + '@': fileURLToPath(new URL('./src', import.meta.url)), }, }, build: { - assetsDir: "", + assetsDir: '', }, -}); +})