From 3cc518ea61aecab5de08d68b3a40c23a0b3e3c19 Mon Sep 17 00:00:00 2001 From: Curiouser Date: Fri, 15 Jan 2021 16:06:06 +0800 Subject: [PATCH] first commit --- .gitignore | 5 ++ README.md | 49 +++++++++++ assets/openvpn-manager-1.png | Bin 0 -> 38247 bytes go.mod | 12 +++ go.sum | 122 +++++++++++++++++++++++++++ main.go | 156 +++++++++++++++++++++++++++++++++++ public/index.html | 86 +++++++++++++++++++ 7 files changed, 430 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 assets/openvpn-manager-1.png create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 public/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1771638 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +statik +openvpn-manager +.idea +.DS_Store +.vscode \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..330ae58 --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# OpenVPN Manager + +# 一、简介 + +OpenVPN Manager,一个简单的OpenVPN Web管理工具。通过OpenVPN的管理端口获取数据,然后在Web页面上展示或者操作。 + +OpenVPN的管理端口详细介绍文章,请参考:https://openvpn.net/community-resources/management-interface/ + +# 二、功能 + +- 显示当前登录的客户端信息 +- 可下线用户 +- 简单的用户名密码认证 + +![](assets/openvpn-manager-1.png) + + +# 三、安装部署 + +## 1、部署所需条件: +- **OpenVPN 服务端需要开起管理端口** + + 在OpenVPN服务端配置文件中追加`management 127.0.0.1 123456`,然后重启OpenVPN即可。 + +- 支持的OpenVPN 版本 + + 2.4.x(2.4.4已测试) + +## 2、源码编译部署 + +> 注意:编译安装时需要第三方命令行工具statik(statik可以将静态资源文件打包进二进制文件中) + +```bash +git clone +cd openvpn-manager +go get github.com/rakyll/statik +go install github.com/rakyll/statik +statik -src=$PWD/public +CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o openvpn-manager main.go +nohup ./openvpn-manager -host openvpn服务端主机IP地址 -port openvpn管理端口 -admin-passwd OpenVPN Manager管理员admin的密码 >> /var/log/openvpn-manager.log 2>&1 & +``` + +# 四、TODO + +新增功能 +- [ ] 增加显示最近五条登录日志 +- [ ] 增加新增用户的功能 +- [ ] 增加对低版本OpenVPN的支持 +- [ ] 增加Docker部署方式及二进制包下载集成 \ No newline at end of file diff --git a/assets/openvpn-manager-1.png b/assets/openvpn-manager-1.png new file mode 100644 index 0000000000000000000000000000000000000000..1047abcad8ba1d403971f05404553e6ebb6d0630 GIT binary patch literal 38247 zcmeFZWmH^Cw>F9ecPCgNNE0AHAZR1O-Gf^P5AKb-yF+j%JGciL2p(KQaCdhJeiwVc z=R5MAU-#b~_Zwq%kER!^dabIOv*vuBXI8kfq7)YTOLRCmI4l`yaaA}t#5FiLcobA* z;F~v?_iDf&LY87;$}(bNuaq6_%q*=<;ov~=#zsbKGAy8<5Qve{&tYaJbVoPUu&^jq zBj14^KYxt=7|tEejZf3lTOq|=K~(RAD^TfZv=aF(Vy{hNl5%!Q{ZJ$sQ@LnP56htR zFeNMYApDV>pp%JN_yp%5>i9tg4H3~8Gp=0EKo#y258N!RgoqUUteVJyGpgr{uq-6V zG5j|)G)LqbHn>@YezI^Tkt@Vkx{PQtV9Ic;dJ?1=gy^AR1vK6yb>##d6@>h(bBs5U z?|yO05>oi*CH}RNo?2E?U}R!s^798Ax>eZTD9;bbPf)Kv?jS-)U^)XLV&WT77S;fN zh#}rsOEW6RdwOK#BX#5u!A1=HNc>f7qx)XfOfzdFh?AYUkx|yw)6=#+GO}xKx^Lyv z)05}j)040NX_r@||LPAOxFvUeXHFEF6QDzRO|@jq6cpgz0H0Ce;6p9pkbqC{!0RRO zf`dcIMfi_z5Z7`M|Kl?}%Ja#iz`zbTI1xA*@ek@y_=Bt$Y3tlCL+TGx($gV>L(ZmV zW@f#tjk;!LA7nt#W`0>o_+irfe3-|^ z)4Q4dUxOh{u(M9S;H$H6q47ZSAH%alj6m&m;-6t%)8F~MkJ3%=de%J0<(6N$m~RS+ zKz^lR*~hc-w3oakFXT;Q&$9b=mhJb=6pU@&qo(zRPm!!!+{<20JUxVJO;(kyQ&^ls z?CIN2H`_UjgKX`Y-^-Vh36#Lq85a5Bj>oQ3+UDO|ub1W@PP%Yxf!}g2$#T^>@|O9t zm0{Oh+5X6XEu^OKKAGI}pjx1?{cM1FQHC*#a4N@b$=w#X_Ast^B7)L&rsLsm`3XD2 z5W4z;XERAnL-wyr+E)m`As8mIOy$hhO!Th@-r+#1XuI9pFqqPF8aDaPiGc$?@w?k8 zi0#}iEr@Xho>^wN%ewV)3Og2@qrkH`sqe8{WV@3WdQ`P+)3~@bW#D;;y)RI^7WO*E z{_e2R@JK(^(C5ZT%P91hlJFv>_rl3lA^(vLmpVft3m8NNZw?*t-prWRckHB zZB9H3_F|4-LH_kgmHvn*&6RDpZw@%SFgcrmF`L$R`Tg+-xOtfv0=A38`RQn(+a24p zenuSF?ZqyEohwKe8jkixj8EV9;ga%BJppKN-uwQv4|4u)kY$}A)4sDxj|migxt-(d z-a@;-a@3mM>(Y1A`9TjT1yFVXYB+}Gd?IpJZ zyRC&YN;2uxhJEsQdbrjE&w8B?Z)Zc-!mA#F2o&uxSgJ(KbilWfEH%;wZerbL{_P_uCW(tHxK7zI#8dM{q@Enxvw)bl0n8f82T<@=lbm zgV{h%L)Yeg<4OQV3Vm0tooRg4ALpIR{mS;Ew@oVnHQsB&zK=I0-->c|ZgpheKz59y zI6~;%0Gp6fVanux+;MInK^*PUdaV`@2!VDi z19fF{xWJ~$onC)qP#EP1civpME+G1=?A#(G5XNS)_(s^rJHBQC=i>vZ8|S(@X3$AsGMO>_g=(T8?D7308l|A5_O3SMvxiX;_j;WWJNq zUQ47uZSu^25%%LJzUWcyfUSm3Il3S&cV+PA};dBQOj|5VTN%G-$FFc z(hftG^MnbdEK{LCd=d*TUw3=F*&bE*OPh>xm?#zp{EK%*0mNR`G8%OX$+3`?5sP|wWRE)abcDkbb6Ly$*DR5Z# zD@6HFwikOowCb)7{B(cb|5t`x+idIY-nV{VVs?X#WXt08V(T!G><1|J(1|*eVB%~~ z0(PbOe0k&2%j_&({Uy@@ajHsR@B)pu^;tIA0sX&Re4roUX?A>UGX@^6zk#A(HO#@{ zkUmvUwK%8`s{At@HfTT)OdI0n4h$$}|H;a!>|dvS)WT3|GPk=w>#-=K>{K$H#%KS5I!MpaS>GEO-pQwKB_VTn%98$x6-aP$;mPv3E5wwME@9;Ba)mf__qf^k{Wi zb)=QWICUH73iwR14G8AQiUvsv3{bi)jK`!FS>gXZHFi3HaO!$x=y!RzAL<1aLagAG zmSZhw5-Rr%ZGed2BgY`u8L-kZMDQ9{48M*f8BLK1olYGMCNUrtvc#bxu*18uUR^i{ zG@@JSx=Tp!pkS+&@e+zcpjbvKA~gMigAVHB?r5=z;GI7qXdEt#_V>FheV^!g%d2Nq*8Pb^rhQ+aRjK|7mN=K>F~I@RV*#Eo%{X7cAUb3` z*v)_bt}-1$E#}v6==-oKbblfi+9xz3Fu(Mdp_y{!)tjoa{n9Ib_6g%E-n)a^t_pE& z<>H|e`-jVE0U_yD$S$Ph$rrD@bLdvP6NYFe-sPvk#FYh3_$N5Lpa3kzQ_)~g5r|4X zIQ(hELg?H^o7TYr`KA%P1JWtELF+QT%<`mM*=}k{>E9`E_$m>^pdSg)zXm(hQV*bH z>n7PC#WEXME|q7#*G{9%kf~GNGY<9vLKJj@{PcH2X|pqta!?Nnj}gq-YALTQmnpPq zuSw4q7{ET#J8Q67K@I~*}Key}QQ)|`Nl-TN!7rn_dammS1;pg(ux6qiHpz!m!9%9`g z1;NWnZJ9d7zn8&W5n!^AfNGt;{vBw9!wn?#num1vkz?^qJ|)(#Qr3HZ zA~)`@4U(x#GhJxl?4OrqF5gHo@YpTeFx_Vt%GFEn?5)2#UKYMr^7LJQf|mS}WjYSw zQ39*r!0aefK=YV5D-`UKs~ePidYs5OLJnlFycR`g)+6b0eh$hehH$Lv-(H%W(m^pp zrR`#)Sx>)Pe@~H`pK#eqogE+k`Qy@a7B8Xa7y5cah zWID-yYBkynfkUl3ef92JWt*dM^m~n2t1qO66iNrd_+l1y!}8uh*k%4^4U}6Ps*BG7@wqANZR!1;Z_py%Mi6G3AO*glGHuB& zdtlF9ndPC+M*0N|1A4?>IAVjtuGt;hbD@((TTmx4=Kdll2WUe~~{?HFG&h&jPbT-}B`J$G6`dwS3r( zNtRrSj^b%ZurGg(f7e>;ouN&}5P2TRUaG#UwpR26HAd}^zG+bqe_ju)g+JSG5Vm*P z=AH@>gSs5pRMxRkDPtl)wZHf2yUzAJ$K~n$GF>g-6+S33t}QM`;}Xh}2mH93A|r_T zfcRRL#h&h9p&}H5DjyeWp5|7`Ni9OrYjf92Wo6)g1!NoZ#V2(*`{px|Ko1P!JkF9-?i=w&D-qZ96jXtU3eBPL z=IhZs*aW-r*kX$J6hCs_nbvMwl|kjz2LCoHz31pX!>!rzxyz(c<$=czM;Yx+U?68^ z=|;yzw@HZt;s@<-T~YhRVFi<`c(E^vz#2pAHVs2QdTuP$B#Tq)c$ExMY`yD|GY7|~ z%!A6#pM%fNVov^0o!lGS8%?}3AWL>j-~rFdOPeQVS4Yv%$Tdl-dDJ#=Vt{3}SV&2% z&767CJ?>9QOD0nDx0_}yG$ZTl&y@ZJ-ocnw>X!C!$bisgK>sdVoNAEx`;Uk3F^nl z!n@99b3AEOJ4ZUI)93vhm6A|1!(l7sNaCuvy8)1J^XU63i^vhZ$g6+CatpTSIEGo+ z`TcWOjWHoaF>^!oI4N&*28{0w)@5n>f;{@r|H^uuZG;|7L!~SCWzluMJg{;8s($(6 zkD0FFF`I#bTDMART4D6*8UvKv3N3M9cEhxOVrnkNjmR|R#EVX_7Irx53LA+UHfy-3 zn@+y=DGA2)Q0pd3G(5aC&GEj{4%Hm$sjYFcHozkB3`zenO6UOIN}}g%isR1yIaI;Q zeQ(4?1_{EmR{QflI7L`{p6r{!@T=1$&*R|r6Qa~}6H^jm(zhRGA=ZL;@X#tU5C6V7 zI>C8ZdF6JhbWG&iW{|kXOsk_%$mf}{7x{15U8}Nw`kSWeb3=Qhshz0U8-$O?5>Vvs$d>m;kt$PJOkZ~2?w533!QOl)7I0rV zv4N(<4o9|<8tmZZ!6Qe~N=M&J{8gsTT*dvnDJpt_7_Sv*BwHI)`8HMCEFIgpZPx4) zI^PqA>Voqxg>-_Ho(SIS$N;jzw!5Q@pv33=pAt4$_WGhVp*sRv{xd|jue5H7p`rHB z`}V>Cb~TLTEV#!Ee`k z8^Q#8ry+hU;fyiH0!~~;Y2m@14DIX@(rB^$7TV8iNyPKepPnnb{f%1q!$H0k6V@V_ zVo3OLwDwW^!qx>B&U?K z`r=D?F=}$-iNn>V4*A!x`GXZ#{i_TjS)2JU4}+NdL9T8k?AjF5Rm0r#NJ?C_)9dv@ zJGp_K2j{Rh7UIrvoBNt_#%4dq?m{b2GRr)Y4Qy>lbd#gTMB$Hl-|i5b@qi6jAn$Y- zzt`|Q#E%ypu80)JMF-SE%J8S#j!L?uKz=UB?kclGP%bRONC-aLFe5F_nG^=C&lLYB z^w`Ezw(uAoR9lP(P&+nyku=u44bT$zYtD5~n<%@N7?7VNhF%W&EQO8d)DoRZF&D0# z;p-=Egi4*3r=7H*ca}KR`)Im-7|YU-OxB3l#YRfc9wN=U9@`rU_^mH{S%oZ=!A2x@ z=nkVh>CxA^+^d_7u zx&@usbLMwPd}|dGr2A`~8QVtbI0U{wNHg-iSO_K=K7(b~j!9?JiEP*VrYGmH`|D+2 zyB3Caz#$rvGP4_~NTNyctX?_<-E}R@tUW9l1!8qNS#vHrEl25xz92xA{RbiSK!vBi zqW{Me?tDOk)oAbo8Ijyl;7_9iZ0h4p<{PxHvIgOUFsn)tx;rTS*9}8R%6;Sd&B^fu zweNlW_x7CN<_*T*dsVTTdvZc$tzGWx=MO+OLm9Ix_ASxay%G|7*wESjmQ|+D<<{P1 zKIw)$XS$u1nNy)C*@wcx@vU%7lWI-X(rA)?#OP-7U)Z}38*crlUfrVop_7bH(e6uz z!sl+hk3fp1eM!1>r*noJKXgAlAS}@CtK`0Q+PdA`u43qQM5rl>6)2rBo{DivI)vuW065 z&p-M+BFSl7-2rgU?;*{n&kZKjl~BgMI^%Pe6ZwC2S%i|`sph|+04iBHQ?i~Pp7nB- zurpaS)7+G9*#c@ciiEps$5fbR<3=Ciw7TlSAD`kK{D@Q>CAQY5$2)rJN2`pB4B9bF zi3LwR0g&UPi#EORu4cbyznJz)JCpl;!nIFLOYX(3_T^y1tSd8tki6h*jHsc^L-P0OxSl)$y25q&m%r`7;|)9mmN7zk#d9J)G*MeJ{i#== zLg4OkQ>X7}cOLD0D_JKCulMt7d04}4aYhKt9NBY8JIhB&05UF5+$X;sByxVs;+aK! zY;G>kw?1UU?KLsaL5k23wG~uCZT|zFh%MP+ikr=U7*Yiw6e+`sBfb;em0O>}0$Xvi zZ;-#x3_1K5gWtk8a_qYBoMq1XMcOx?4tYjoV>Odm0rcjq-`DQM9_AYBHpEuljZb8F z*%$RxWq!|}NSUBC)cEROK%0=i&DHS>=I-c(2(CmnOvf-|M3oz+p@mCq>%d8VmkK> z3tas~(f=DRs>6b3TC+(cYR3ILdtCLNa_Yw+mWQd%_av32VylQY#+ZT z<_;eZAkPr7Ri+YC|?DCGHgl#@*-2sxSY; z9RAD9wZH{z1q?S0aL^0rk-@t0ca zK9iJSvj5^Vu$by!rsvpS&Ep8*a$JQU{}^Y|X+K`g+X;M>a)R3vACUjH-@CCkZ|x7J^~JkWF#N>7{F9vE zYFk?-@{CiV(CuC+mFVb;5R;8Zin7$xw(t{^CzbZ~l&;!78n5?~>Fvv`-_&Q>1>bgWT`%EUA-*r0BuMn@w#unI zcJgdzO;Zo!jv|;V?18892AKe)mrUs4X~f-e`!XxAB-w_Lnc0`*_;}P<2H{EC11zWE zS-UjGNee)!B*+z?2?Fu$!ZU59p#=#x5ya57{a&h?IQpCcOdvbMs^WWs^Jypdyfwk20gykO?qLs-+tkIATNcJI0Q3Sm;VM9hE|&vj zq%APEOmc&_;4>hA+m_Kt*#Z(w7a$<@vcQz0aOfWU(@826h=St^&{Ce-9wXI1X(NLM ztN_eNsX_2!OpIo9XCoQ_68dkm>y(xFUVbiJvh2(BzM5SmRFhvd`(jy4w;wBT#LKf9 zL=f`CRs2k-dLDh>rifSqxC(8JUJX5m9_98^*l*@HgHO26p!XsW_w51^8vHdtw*`9~ z1Me<@$XJfggrc-`_E?J1LJ<|E@BOJ*D>vZNlp7Vmr`<=(PmhiB_FY&{d{u_cC!H8# z*$RA{NqOfA`n;EuYN6g=fBD>OlJMN0bfq5F0(R02%!#o@kNxsItb+3_;ytwiF`d#- zGXO_b_8kRAYj>FPu|PyQRNt|m-WKT9qpH?0W<`NRuIsZww$O$E&j2>>Pyk>YKOM?+ z9AL7YfrMf;O6oMk$4>S%F1lvu0!$x*aJIsn?AezqY!x45%+@muu{i_`mKCTF&#Vc- zHIV8dNtgpfo>M4=W7s?pt>eGFNCp{C&euIm(kHc(7(hE9QN>Z0$IHvC^^>ZQoEIc| z>=z}^g!cw%p?NFXA-w5X2MRUj1Nu~f^{~J(EH5j!jrX9cOLVr;``{X2Ecca1XSg4x zq>O5ZxiM@Urwr<*r_8FdZbSInQWk)|j^R%C;Eh#&e8xJ*NV0(OTJVEMTmbw7m8-<^ zD(}VcZr~C?#N=TcuLDFpn{*?p849ttn*jcuegKe{fdqZ$8iag zVAB>uek66`P->s1*MV~~RXc(Y7r$)3l{Yx58vrJ0S<0(?wv%b(FbgE_gJqu1Z>l<9 zP0^$~Psl8S3ZfDgUdp~EA`D<$S&L+IEnazM3vEw(NR%dk&aZQE|D8xVZv?+j?6n2( zygXMk#w`LzO|R7_)&GEhqRK;H02&;GJykz#u-`EADRN^EB8?KGSb&D}o-%>UdrjKHphLTtZ%7>E20D#q4yaXDx+k%7GriigbC_xd$ zcR+HGc)#%k(4r$s%1d1VPx-%^fq-^yd2D4|``v5t93gK}`oNmT^%#L>fX(HT85Fc` zyVu^YRC(hbz>kXFtO%ozWIBzI@jAA$^ojOmcxnUgInj8=mH!A}F0(u@ ze{0%&{If}s%i!sw)w4TnU6>C(6-@~+N zHH4WnO(hiq%RiW_?eH=5I+b8|1lTy4RreM5O?NKii~+~(8mgWz($s{iXUJV!0BJ~s zElF-nxe4^|ASJ-&=IMI_l&~nJg?XWIRPK<mg~DEh-RsjuxBd0F6m{@z0RP~g z;U0;c5=zcKjp1oZ{5A#7rVuw&6-Ofi)7TKHz*I0{bN8G1ldG&Jz~zN21GZQtaJy3wcqX19$G>SVu<5#A zVOmz*i;j(bD+ZbYFEgM}dsVF)i)xF;_manGU0{Y#-WpA^nrFFal&Wd<`boBmwkJPQ zKB-KV`)rd=Vxb-)pi@f2rg(aJ9+1JB6~l1F99wQoWyv0XPKwR0z2vK zK&?A!-VYfjg@)(X@8=GUnH>P;1`+6`h)QF)+`(y3a(ir0$BQLb&}{>hBVyY*DAZP{ z=!~+W{D>_C30dU9C|1}+IK*IBn${ncg9*{J9>+me5*E2#8!#>*cM>&H4`D1TWi~L8 z=T~=gRmG~Djk9VHNHmBKFk(50{M<9X->XPbz6WKwG8g(sx|4lHD*eO!v76MIW#hh| zF$Xgnj%w*!Mf1S31ZK4g>aS2$R{Ua=3K=da0tG?|bDOhdYy=Y1P!}%n&>o9Nr(@0#U;<+Y_>~U5I5!@{eqriGd(0hHgo`83_e8&Q;Xs1AeVjq z9i_tnt^5%H$Bm)&=Dwe^D0s0?d#5_WkmGe`PkOa^`xVwCG(bk__~zpqGo*c5N{_NF zQo$&znHj`(ezh1-2`L`4yPL{nt*Cfp(D4sM986OLJG9O0ZTw)2Z|_}5U3jowkK&Ia znv&+}spQar$w}!zatC(ksmX@Pn|@5!)8YgQNrj7^$E{uGa`WF08|Firt!nxiFb=+2 zy+NbG#dHTx2g0^7);Fj;5AYJ4D5B`RGnsPUj0B`CG# z>L)#O>y2_LUsRNXt;&=)YueWlH73Duk(4+zINyTRO%H$R@0Ut{s#Y|Ey)`JF==%1B zxGV;=#-N4xwFwlqAaJ*8+BGNcUs51Juch9hb_M7(nvyjQ_)cLbRgm%gfD$Ju6d@}X zJz`;AqaPd};7brg--(;X&=e$~;Eyf>LN8p`Xd;)FNN4_vRN3_WRY1;Cs}^KPw9rDym>` zo-9-CTnpbuF9ykzvf>VWV>K2J5D@M8`7W8}T8vaMz%e$IVxM-L40?|zy00=b3b3AZ z`LC@(e>7V+Er1mP)lJ?#KYjrJtep`RixuyUa|3%?{tgFm`u3OJlrVy9%Otx2zEPU2 zubKw#^dNU}0TmxmD#+w_2zM#V^ji+napjpEt?`umeZwd8Y>PRJJ!lL z_CK8z%rhT2z-RXWymm^%G;Rg@0e*6BbAPnV;o!n2lhhBUje)%Au$P`BfRs)`psNa4(6w;!rAWilD>KBLhDktH7 zvnu_D$N!bIx|h^A>LkY%i*K*AfFk?y0t`Pe>Q|!aqe49Ns<&3G9cguTExlJ+-VoCq zZ4`Wo-hLsxg8|7cQOt|=qZx*6PF`LxmcGW_qZnY8?Bx9mTOwjB~rt%0#IF&uqYi1gh~6uBG@G&9INL(e+xbn{~i;q(;$r8r$zG$oQ0fjJ^`an z;ojFY<%=A6nmWatP=VZUzd9Z+###kbT-j`@Yt;kzw9UA*WTocHbHf?(eNo#vJCrbT z=Yf;SFQQ8NR#dpKu`qocN{>B{vovh*xD6>rsi!q63RV4#=ZBK48)Wt^<>IyKh}D&+ zC))eCNB9s0HDw!tEHO-UXw5A%9&^Ojm{?C`&eys>;gHA}=_gI_+uds)M>9 zMMAUVi$_)+stt=b>ZbXRapv}&ePy@Tn40W8Wb_mtPB+B}-=On8z-5~w72qP%WFk-U zfMhYf*=Z(c2>C*6Xi1aUcL=nl=9T5x&s5A`)l*>C#}pkVqp^Gahyv}ZPaZP>(P_JC z;}}o(Sj_l~yc}Bt$6I0Ew6z(?Pj$iJR*d3l0b1p?w(HG1_7j#xV!^5LA%q;UE$(i+*0_!u;t&LWaQSJI`w>Y; zOLTo?1Vs@ZPJw%0c{M7UnxV#NMrekk*yabu7suRL?_rJ5aAJMu@hYCalEkp>A7>#U z5sHihZ>a>L-as8z6oN(8gICZt2;>gZtE+jS@OYS*LB~e^Tg@!pD0Vzcbo@F{QTbvZ zs#bQAvn;7;3xpd4Y*XYO-~Az(3u;6}j*wf{mh=k-fAC%;Q{xrb6x z?l!H(G!nUgUvgC6@GRe+JB8&@uxnErYg$6l45NBp8t(~aT>E2_MI@skQ=Jc~t95@& ziPpMe8+du+w+*+MydCc`()%fYQfKieo^%c|zb2_#m`8_*=z|<(*qF+88fYFAMjHGT z2aU1hS2XR2G9~!)Bx@2*k_%?cMy14}R%ni~j(OH*oe^|fsI=fK$ycXe*~I*xNm)7K z%=+JaE5j#rsIXC^&Qe#e4g=l7?KZ?Vgu7z-^Bp4g`b6c6+l?=EP#5**e}~#8=SZ+( zMSr(V0^Pn7r>6mFUU1^wGJ*$(wvD{W_V6Mvy283#Z|yFKa8ne@>^gi0N(6IAH+Bri zkke>Q?+Sh0D;x-4O(G`r;=eMS7)dJQTnVFn+7YhBv5`&bdeIu}kpjupD6WZ-A{P^e zm&m}mkx%~-7dW2g!#%*Gm`JL8jrVXDu!hsHcxSIYm00<&mic*c9b zIWwmHStwpeBbsY4b@!i<^bm5k-tp2P;Fk$HOgmH1Iro`3T@ADcC;g-a)EKQ><&qsH z0mw>g<7N1SMLx*&Cdv5aE7}T9z}w4a~eoYkmJjVw$M@ZXjm3ncA+=R4wg5n231EqnmfGVd={1I;W9h zlOyf|DMTyVow!P4J-)CkZzm7+4Aa=2i;WKJn?h+o{mb=Orsb+v2*K|nKEe`8Su|NKBDE<5y?bJ zmU|mo?0?e6LoEpNjl){Dx$$VK!(hI^tu&Bg{n1V!LYG05d z7Nz9OC!!D=uxq%p^O6N1U{uL1FX-dvPps|~F<4iSEQ8cML0#6|+ULD7N!eeKDwWCq z`jB4`@sYher~vkBXM9z{p@eqh7B#&g?$Z7&itT9hWp~stgox60pI!H*=-GGkrqkvO zt(88(YvNs5zPyThsSetsvYf`dEQe#LA9zjv2gXd8EhwQ3s!>9(*8D*o>&qfyoF*P28*m7B6Zh_4s zFbwMg&LB~#EYUtJ`YirTb0qp~4u~4_-;Yvx z^K_0Ihk0T{;q_iiH6v-y?i0QwTK+}ww>pV|(KM}N{bFHA2pedHrE|tul4{QLwj6v% zx(D52N(Fn8nxHXm%@!*4RS`_g1;v0%z@DhtuD3!L=cP~I(0ciuEVR5;SX&!@OZo99 z;wXMNi)#aUE9`ARiyfl8iFc))u?}CLyL&(hZqZciiZiF1UnX77@D4aIabC7UUOUd?;ata!i_CdS z;6GF2xv%?X3TMb|ESVoBwDX*Pc3Gi=H_^pVLhvyX<#kU;*Tq4^0Jd|P!d3mdae~jP zTq?bN#!Zsrt#)4=E{$1Uv44$hPmCC%jD)y(Qk5fryB-b^Hspa6V~+@YN&H53lVo%+ zhB0`AjYdWbR2?_*wKzd+XL62H``A75OIz(`Y3a4@e)Fyt1huewgKSkhI@p7p9upB? zw2u{=@pWCi1=Rq}=&M1lANCcE)?Znk#HhO1jM-JXSBKg3c28F7e|U<_f9}QJGt7_M zIhnyF+e6%cg;(zfnVmC$(QXh)nx16>4dNKTc8^%;Xkh;sBljSuAKa1b5v53ptPvAA z))5hW8VnUV30dV6YjTZB0>O)`E3-LJ@IOvz$HwBZsWs*`%>|jAHntDZ=mtU3lN^&IyePcbF4A+-O1=mmswLU!#zQ}RsAQNF6c3jDA_Z4~cF1+v z)Ebd=E12CC?Bl2tX4X?mj?wEv?n7KT+7bnyYQewfeVgfuRYxgahyO)UP?8JT8+#66 zDi0^kfp{HvbKp^pTLJGl>M`zmE+e_3C<0oBG+U!U`vHEHrmr(z)8B?T=}!|>`Si^S z2fOK;rI=9vX(}3Lu3e}uFXVPvGtjeZr7(iny3(T`08qT?(e8eAId@*pxdYFG?}4s z`ryb+oV+M@H3zS9t`K!a=R(qL84yl1Szobejq6C->)hg82ZJD(>^CuopJ~zvqwpAIQop!=Fx= zeHVN?E2<=rpx&@Zo=9``o7t`4-0)PX$6tLP#ydb_hGC^-v@4}8U;9pD60i$M_Ej2i z2k>1GM)@C<$<4$)_5x;_B!xpAtwyQ6i4EK=y^G4Y6Z)+!F+O!@8PObR-*sEo>t(=Q zzHcUSdGF0l-Yb@&kQ4gI;4W{VlG7_mn*Xv?;8S?$c<=uCTVui>A{K{))8g$jQCAe9 zKT(ss2Uzh)GXb@J?{=PdUfBdsBaPDK9?t1$v&}5e+V}2QZPuHU5IxIz8kK^I=}^O@ z`=-W(#xusoO-|vm^Qp`PO=3Brb6Eowwi`v!eotK4whye;#t=XfcgnM}-g+sd!>4$!1&OD0v zoL!uG7-MC+MS?L;VEpk=s+SnqfP(&wH@cP zM3&bz3sLv%21`u@ymv9G7FI06rM3590{Ue#w9&VRsw_gCGzU@-lC2gl#m7OZr6oUk zmUq$G+X^(!DEAYtVJS@{b97g=9sy2qOXNlg{5{N@^jGIgM+uq~U9NjuFJe;SQkS6Qa|V0()WdNb3YkwyMBP%&!G}Js=VLew zN(W+wS>7}lW#}lrr z=$`iVa|=?rgOqQK={~?ro8)DTvKrA-tFM=`@Tm#xs+rwm8y*f%2Dv9Th>mxesy!o#|}=QK!`?`lSbT6NPwE z>9>$`H>iNCn5(z?q&6r+ zWP^;&f1~+$8F@7oj^uaQPIvLC6!PIv6JUByajP~KkJ|078ScaS_DlP2O!N&iE5-+h zhm^j}uWp6fn;P(s=58|TO^o7#IqKp@Jb!idO@M?2Xl=}VT*12671i4#6#?d<4UuPf zsU!oqmrR#%^LV%18f-I<_jBh|m655lIX>7qS~A-d7j8SDrf8-Di^2o4(}`2>TUd(s zZDv%EEjTSQzP59G*2$7x%8OLyo)Arkkh>_*ca&;<70#$vTHrAfm?mn((TFuY%rKDB zpCfvp@rI2&Rx&E-zWj1jP8XcyAvT0|d>Dm$Yqi>b`WUoZ)K$2CD51?j7^0+OE#Fwq zSoqtls=?**9-sTL+|slK*VdSZC#bi$M|X0-AYBY(#8ThOj<=>Ci;MpfLyY{E7xAQQUWmtX zzb-%BOjQ=l;F_2s*z4zgWXW5~8j2kkt2v9i-1n&knTG-A2lK-Ih*g~{Dqm6V@cb(@7t^-E2vz4SmOaFn|#Zi zJdhN>`DfP$dmdEq`&JBLVM^(hzMT`&032l3YeICX$dJa;(xQdgrk3lnO@(1G zq@?m|_M@ogB}z-E#v}Icfy7CjLK)7z-vqRl?d+o(L2t{SRTug)7jarzhIjL*&1mt4ji8Tq$c) z{r^pL^|va^LjY*8wH0aS@gMp0fBg>~SwK%K=df?`KcDOsLTeV#;*tS^^V7edW2Jv3 z0V`^9dG~L!82=?jphY`2_-5aKKbL~Pqjr|C9rbW`?FOhs9RR3&FcHQ&L3rq)i>2VD zqoB9Fj}D(aJ$KPv%3YvQtn5>l;Cen$8NU-Lb}o5y!0b@QCK64*mI5KYS6-zxJk-wln~xj)}Kp6edhdZioPkuY{~PaH!!-zX}d|5 zlv7?ZjDy7B1fZk_LfTb-IQmfJGRa-M@IY-_>aW*B{TL?SNYSgl2Y??(S8F~ zhZJ}P#=~~`|FIXq|J4owlH~vQ8Yk~_y8lH|<$pwflJj)mr|-U*d;}C@?k-_ij?b24 zI?6UKdF+2jV@S)DFt=W`EDURmsUM;HpVpYg;D;~tT(u3HpL#{$mNvsc_jR?px_4o^1KS=Gz9Ie_b0!Krt{96zTulPe{Bz znKtyTU!;Emv_O;$f(yy^(!xcS}w(mD!}fNagCJ+b(IlY?$FG`*25E zZ^vXbDtWF3lQb&{-4yQ0FaUK*UsihnmOYrXU3NPNBz`;5t|EO9h{v>#XVO>T~Ube!I`ZLL80Q+{@)> zfrJoHvHe!x_?A@uuc|RojPKd(4XXfk{H!4|C?)jo4s-_O9N&e!Z+YJ+1u1sn%dy~5 zRRGQO9Rhm?K)k0kv)3PhJs)g=vP%2f5q_&F8ojBjG8gR|dqA%t@Wz87Ah(8^*WT#J z33f3dXA1~s9g+?KA%P$y!5yyLnh*Y^`GD!|IdH#0R*)#7RYgGIq*35$^T@ZesaW0($x-}fhN^(0GhqiusjH(YqnohH_ zQcg;EQR(pkpe7z|0>nBrH=`c6YRO2!2|~!S9EW4T%u1#k-dm>wDz2X8bTR@dilQw| zld5trHeG)B!GAzw@VQzlvgENP*&+g}q@y^lW1udsu(e2E^25P=W92-c3J{}a z_Z|Tl`B`s3RFU@pNOg+D3~}k>uOjwM(s68b?Bba#nmU2nUC2japlHWs7%-)r+*yDV zr@X1(`eGH*;D;qn%mQesV;*b!0X5II>w<&SUB*42!^-X`?QXif*aj3SG7~)*{3ygTFCg2td6z;%qx&dU5iHAzvoA&My!z(Lb>7xu=9~4KxBlKdKT!4! zV3jE?v2ZtlOskd`MsjMNq$246aHrMtW*IgM?N1MO*MNW!g?>E%<1OmVPa?Gtrg^R% z=eTx2UT0+gR|D2De6;VqwtdG7vC#ZGkh-wbEg9YWUM7A&Qi_vXC9ZMt7-W9dOE4m! zVS<+O01ieeX$aW+BT2|)$FIjl9xvUs=_|j6$)*&{Q*(fe@(|d5Ly%N<-B{INeS~xr zU6NqWNoZrX{qeFgj;Hmj7&+(Ofn)i7-TGSk+;dTep_P=?Zb^eXkv0rASe4i`6mWlwfHfaCG0vSV(f(2tD9->kI!PP__KiqnnfJIA zLA_}LBrhp>9ceYaFRkdt@u0oeGsdx1*slrsDdqtmJzO>PghB;xa^h%2Vcq_lq zP)Yk|jz1L;OxC{G-vi>G!ME@Cp|Rkzu!4a6{14AEMrA$~Y`Vh_aB_T0NGzw|KxZ@S zKX6!r0jcmE=h4>)5-53kYA8`$6Vfy+S??SHv90E!89 zOU7C=#vF6ZG0)%m|DS?_P>*7GK>FNo)*BoLH`uaH4fEpuvaTqYH~cy2TCST$kkY?s zstPa!+#j#+8dWe#eMl4El0nNALGq(94NO+IV6q~|?i>YA2t5}a!7^wu3l{jzE5Vu7 ze0nk8mlqatlJ?YLR$#WS)|MbyQp*<%vmQ``RjF=oO&k`FzJ-g~HXX=@w1rQVr>z;c ziNiIKMrger2Mt5CLDE$e`dY=yRv4q?Vt*RLfjiDuSY1Mfg=GAvl@+}EI5+nBRzOmE8Bk1)D++i%W;t1nT%{+id4AN>tE%dwONJFehn zmi{Wk{RA(LqBBOCSfrI;uMigM@%GyzB{U{4LfYqB#A7{1R!pUw4SFESGV(SY%b9R2 zXVb2V3TMg)iYiN%W(6jG9r*$G!;g4ghy{WeTVbv!Y;Gg{eMTd&8}b*GUko#8`dz;9 zb9iyWCsk0EoHd?V0Nvqq#taV+7MPzNa6bs&vMEit50RC-A8(M|zAY;kFifAA1)dc1 zyI`ts|KcAQA7+Hm&u>liCeH^`rX(|r+!F6j;jp~sJCl@AkmsB9d?mLQxsUX*j0#T= zj33*1`(Bm3=ck!r^pACSDh>^RmJ65CZZ1Eb&b`%SWa^l4SHr7uA{v ztN*mJntZg(UQxLUis={ZM_3x^s9`JyG&O_VsE)V7l_1U9d*6x!E*G0A{`GoQ-mU;u z*nE__7TkwS7^T{BG3pm7zuxsrig>+*H*B?_QLZ)qX<$9(UdA>pt|WPFB~n_D^OZAO z4=kpF{bN&pFqJzUuq!@VW1;+`KS!*i?^_n)cyA4d#t)JoS`(Z7^0%B86v9>A1)t{U z;qYGh-a!|!oVel6>77fRBaTS2OeyS)Q+%cD5pnpj_v;E@o2*bid4Bj|5VOQNiDpb4o?pkt{txXYMJy_v!<+#K(?<` z<+a8xDf8U?3$|-B;n+M8%u$joRB?ZCBk=`8dy^)T2&APk*N2-(kbJ4ta5u){*P}58 zO+*Waz|DzwUZ>jHWkct8`Dkz4;&hG6T{m`INo~Dp#;y)zk z(;MCpBy_y(heX9r9+GdgzD6z+#AiMj2uTb6RH!Bd^05<7F!sflQ=~cEyJbHr7S`6R z0}a3fgC>{)Ov>%v-qRivqWLQIUf{Q|-ri?AB6cF=j1t5b5WKCXSQmUInJy@S z(VMKZ#fp@7FQ+CnYq+c64cA~A!X54c=1KgU%!qVoBjNf4&N>2tkY$ybZv4JzF7jxo zrh0*e5A@xj9Y|3|M`IwrcLyFn4+i*6x_bP$`EVNu1tEfj3yy?`K|9oUe@Gy~MFb-6;hXl;fAgp8ZvSy!7xIUvXl%S1=-$$|~xmQw)oi5B`Oax60 z{TNA^&9V#ASue7J()Ht{CiGb5m!@ASdt8(wy)uecExr57^r7QhFIpg}X zi!!YCg7ThCS!ry88B9vdpxmr1!j|ytZ+Wx4R2uVrZ*J0yupyp3kx9j=A6zOMWT!*c5*uWtnAW=F=mSsjv1Eg1 z(MtK9`FGz467-F4){3G)QAyC{g5%Aw>7ESmg`rVxe|qU0e^@EH{zm0zg6QKQIik1m z+UxDqwnvytKU@<@)}x=kkjpn3t!@7Tma;MzYi_(4D3P#uj%}*}>LALTq>zsWe{$bR z-tqk*%=TOn$lo0TxC#HTyq+M0oZ7)mlj>uXew)k~cXtej&-l2SoJ8;DhZp2@r3$mm z#ycK!?7L6yKSifVb$av@Ugpgx*`we)t}Lp%N_I0-rM}pfj;D$~a4+WJI;61F_Cn@H z#x2XnNWP-_hCD6=dmwPt2QBCwW5_YvmXmk?yl`ThsoskPBO{AWY;k`JDn?Gj4%GX! z_sO5rvWXL!Hd!{m!L-ad%yrManb025J8CIBt+qO2A;w&Ouq^alt^A!(0{y}n-P7yV8^IP|5ih*?-g*% z!0M61=^FavOB+cW{{nhH0P^&?^2Ak+E2k$>er@b8fohlXXQY!8!ON6J**?-9uS!SP z?NpDxSBed!3A{faX%cp|)0jLseO3s0EZz*#?tCI$f_6&RzHPWROKb)k6^nCN{mN%t zMxqlyr^P9QO~EmOMTS&#dMtwaJ{Gzgr3ucui*pyE6MbPp;6Zrf3Mi+9)@!60{TCT|?haV5Km-xXXB)JjHK`xno zKI*z3Uf9til0V{KNSL4MA!W{UK5DKw>yM;CGKAVz$235@7aFBUp-LtNErCysGPXoVVAWw&p5S9q=n$KMxvm5ll z>1w9P55oz9vOiHSHISeyrux2LK-+s)-4J$T?r0z1-H`_UaT-2xT3~|XQ?+qyrrxzx ziC^w}+ky5Ss;vwu{3-FNz$zN=I4=1uhFK7p+duajjYcq-JYq_ad85I|L8*94GpZ$% zW>3cR%lJ{su(XhFY5C=bLxWG~s8~r72e~(M9&EAMt^LX+ z<{WxJp)0S_RKoOR!oGJd@JF~Ek!0@B99-rxuG{b5Hxn_1QWGTQp8Bx_Xn52SAzsi@ zxs`r$WVm%d0Z_Z^SCO2Q6R_}zgg-wk&P5gPgs-Jl$ZsUhh+E+AuaAr8T20j9d#kSa z_xeTRDhgVBRo)L@erBEeli`YU5y@E(5-1y6?jDdq7#3>(AeYDw17(L-FY@Jx_%*x& zZ>96As{Rmd=|_pf?^rTsq&8J?Z#grE6D@?Qe|S}C;Lvc{27Rr4yWr5yMI~rj7+1NUYutkQ9>ddc7nQv(x*(~c%iAFJ?cDw zp?=?khGPo!D#%=kUEDFH7T{<;L3cAFjQRH60Q$gQE80w%Hw1!_8;K?RYSwLlRTbB< z$KL8yO;z(vZB#Ao4ZB_?I+mm>){8#(Xv^QEf~9b*kgu<*Pq(&DOJ3Vv&MT|8b!KGv zWjamP!&$!`-|n6Rs>Je>YJiPKVyk~-pZ}Fv?s!Y}BzM6Sn^pC=i;KsCqdGc_>|0C5 z@PeG$bSdlF!!?pY8(~YcAvCFcAg!%M5O zmA`9$`AQ$l@uk&jCEA}(`pU01-buxhzx(IpWYt2+Ay{%$k)dgfF@v|Vs4!pU=NX};CG(_#sK{YiyqT<^utn-@}YFxFzu zo%%j^^osHP1oy^4&=D@h8Rx0%xm<64zgFEa``V~Mp&hPnS-Y_{k>j~ir$|Pz!rDz2 zdR9AQluW|(4eFR9FcAr9q}<-AA{gfFSk43?)o2_%GfIZf+=~26`Z}`Y+98q=kl5Sz z`Iy|o4HoT=!|E`Fi+Q%)HafKni{KrrwWn3m5Zko}e`779Z_v~yzc4Skyh$KZo?8fc zi*3C(RDQT1XBL^h!1TOhCh;~?_3f5erv-GD2I5PLZ@vvZphX&e8um9#A`Zdi7<98G zB^+sX7!lD}h3tjafA;vdto^?L%%(kB@;)3Zm!xx%42_|wQ1y^erim|J|L4CAR{m>~ z;!9Zjva}1^^vt5jfd#~3c+MFA1SR)q0Vokq$ya*#7vb=~cLfs#Sf+bK>ZkADIZueV zFQz<%UEOr+**}TRe>$N2#~#(G0UyGS*KN@HCwf^1?v{Gj!rv+W#+1N+i9!JAK`}{u zqW$;CxOCPz!u{-j&Ma`v@3 z!|r;f(#4Et&sSmp!^(sz=aP3o;P+*Wd7XD~LO9HI(cPAFL}Ew8lzTiCqZKwahlBnj zl3Db}runNzh`QU?bUH(=R1H>5nAl5&#*2+--n^OitRq?1Rt(nZUr3P?y5Bj4)qgwW z8TnxeccoNR{I^lXU%4MO$^*njm!Nir{^K$rL;9ou9{a!gcUiU>(*Hl*GTMxGdz5)s z$6DGn-V78yKRsw33tq)*s%`S+W6o zEl|_ATta1L4?y7ceo!z76*kK!rSH6a_%Q=Jj^OXmRQ!yfjZkz%_{eEbrfUE9yrtU7 ztrEo6Kw|vUf9xVF9qSrI&P%jds@E89LHH(R>IBH>>VR<|t7Gc(F!4qL;KhUOxDFK0 zcEFh~kqoGZ4eriC?Di)n9sE}ih;ux8R&u9>FrZy|*$V=|m+vPj1L?&x0Pq<>_^Li$ zJ*i#J+4?@Z)Ze)Yw(SJGNKL;r>;gIVeTyRE?T00SHz4wS?Ee1o>&utU&-4}o8|~lN zJwbtyA)wN1@#`wmOc?teh=GnGjPSJC^?{_ighX#XWh4F41N`MZ!m4ZiBGYvl>ue=T zEXex%Pw*^}(```Uo#{e&_9BAfPhPzpp}N#Y_#dCI&g|vIsoppGfQVAQ2Ry0~m}8Hp z1MeRSm!BZ|I}eoPo4gCID;3rN9t8;@)Zrj{_M%45rvhuVV0==?LsE7y;HwY*G0n$_4wJ(M_w;)#pYEepc$;8&03N=>K1Q2j>AMMF2yh4z2L(6*za~ z9I&lr)pPh8-dVS=s4@F$u*Yw!mhcX7 zX*p97Zr(9snAu+6e{7A|H10l19)I53aiP=ulm=?D>?QCAL=IL1?>kkrj;~z+O&XgK zrrLm0qH#4RZ~;sq1DsGY<4_m}I4Jyn=k}1Qcar(ekmb#*k}k2GB4suiFSATw#oE&h zECcogFU_YtOdMPaEI(2`tO~AWwJrYj8W;0214b#SJ_-7X7ms)wqMlz(bCt z$=)TM#{tOJD?LEKR;oK<%^8qUMHXtI7Yi%8?l(0UzD^g*pKa=v&=H^^aF})S^5Jux z_OWy7tz+FaADOL-9qFm?+YB(#d99G;OW-dsk zb$V`*^t%$+I5dC`9duFf{zT0AIs<^>Y|ZWKEiSQQa-}q=*qL64U zVg-S7NK#6T1kTiZ0f zop?Rz(cF>)yBH5paa%LifXC$BevfL;3|07{B2zY;wSm@ia~b@o`3V$b#iPDF7E zR#~H;O>*jao&1PqY}o=P!*#2yajx2fNjqzu_X>_C_&{J~(YtXqA+GJ)tG7y`PCRT^ zxbGd*S_mno8CY*yqc>d1Cs z_^f4VI{5@2>C&th6O(SQ^ zbDUf1azVyFRHq)A3v(+a7RDuk}l6Iuq2>HWb@dvyEqU+i?$V@D=pmc>H)v zSijNsSn0(ZXYKy?$BHdS#2H500WL|yt8&$X%RC@ydqEZ8-80*(Q)F0@KVypJACK;xj8QH@5f7;u z5%onxS%rQ|yCX&2LovPmo~BXJQC_o9KKh{Dvw26gevkUjiWihh3w;koU!!`tbdfz( z1co%OTVOWBoU4eOirf*@eB>2xJ}NLO^x4ak-lkuOSST;svwcv<8r)S3V#1X?hkbNO zO41B4w$EMHE% zdG2!5dzAPlI&OiCd{A$p9`;NvgVz~;4m?THp}fLP#`P4$W%zGarHT*sr=f(U#^~4& z5-k=5oDm5&WoQ=|B03O~D|jH0(g<)VUXjlfMPJ%<4N{T60}f}6Y_`$)7E_B(-nhU@ zh30$wK0|@0M<0}o@=}@;fCp01fV8NWbX2ckb6+PIxGYAcvQCf z@>LPNrl`cmOz{R!dl})y5L@Xq+iCw=eC#K&s+rm?m6L(#*p~?{W;XtUEP7GI4-X=; z!!zZCX>~@7mSz<2cFQs8av&k%5=CkU1qgb1@W*7MdgBf;a73suYD@+M&n;qc?9_-mOO z*4MUAZ?}pFastM^u!JWT>K+DW`Qhn8a9@0JcGUOE1^I2)g(&{z5M$y} zc(BO;<%uZec+&{iSfp#q#ad^1Q^&*g$5|svMS_wewSk_DT1Wu_&FkrF&|XSO~Mom+ejE7ZAvaK0pA1y@At)0e%A( zf{snF9K9SZ;?zaEmmGQj0n0<^igr_iP#y(%0fX{Mn2!P500PE5c`8)&=QLv%g+@{m zh2m$@OYO2L5&5=@EpKqoqN1kyOa{qVoH{T$^QOBVMhHhm{?J3iEIpZ=-M@ZG2SN_h zUN1VQ8;Y?Q+-i)nHTx?mu( z9hWPJ^iVT43Q5^4)w*U>AU`*Z6H4bpnT?Z|sgfb!!JFXY26GWrd3<{i1B^;KjDK@E z(|Sq64LzYR+XS!j66_*1M~+n8iRdB+?PA$zx>Cz*aLgH@AJp^k*HM?;)}d|zEnGCq zP#joqK(LXOOfhb5O95_2NCTMrUHI8dNCF>pvfVt1+-kmTkM8qfbYBs344Vh}E(Sj9 zcQjp@-s^~ZB$M`P!lpUX-fdq)}iHg}ahV;TIR1{Q!1eP-V1lz>iEdK89QFxf&xEWHdwEnKMniqI_)t zd$@Azks5!N<39Ius_rdS4I!gnnPPXW=0TRSkwk~%hu-)O<4GAM;=5;U`|7{E21#v6 z3LhVGRd!5#P)Fw0!0>w-q1ZwZh)cinF}Vz-yZB{qHAU&$sqX?P@HfjVofT4<&Ux4D~U|HX|+ER82JFfvY@@~{`eoY1f+cmhISZ${H7A3Fel89^~L*mdb? z_rd1A?#%!>xl`As>jg){yT(TjuksO33c)Gm4yo0L6keL+Iz30$YiS_?h}s8I#>fjV z<1OQ5nzW@ux)A_9_(muAnZjIemjY}vb{368KRKFMSvrg_^~B!TFF(}hfaPLz)Kwe$ zmP{;lpp{Gb!A)GSPd=fcYaXSMq0YK0*S)%DmrWy$yCVUKRVE1o_+HxauX+x+9+9_W zMjVD`Ngotzwmu8~9$WXPx9)>PfAAxr9Cv)--*76^7HZ)W*bm(fZgH<+x6t)pIt6tG zYysNy7-H+mM)Tm6lIk*Lp4}RAH&WmO$22YfbNV|v@y2k;2yXn+r;;L)#!pW00MfF> zDSLE*K2fF5-yYf?+|*H4*@l8mPicR7DXm8>jjJhJl$vbWGwWSr!129_Hu^lXkIsgk z%li|)YanX+gDZT&&HxM|X(Ik&X&q14Py$=1lj}Yg2NrUe>ugTC{dPx*Va@{v^S%d~BH2*>5W z(=~OVivwm`x&AZ(O*uv~g;;^OrS}M7+601qPTLp{NVS+lCkrw4=d)CJmUPAUd4I&Z zCOVBT>*jP?(d-YjzC>%RX?}F4DA$YK&jDdHlhRls`Svl3eL-yRXjqg#SpBp5OI9pr z*hb`BtJBN)7x9dj)jt9n$huPw%_IeSu>40&M{9CmbJ1c-FLsG#7KE$vR$MKZa0jTQ z3}HL2c324o!>iZFOX^5VD$>z;X-GGAE0W)ldQ!pLj0m)!6Q5I;)H5YuB5DQKq&-r+ zzRhx2+M55-m|3!M)XW*=QXOZ68t;OGnMROc5Y%HzqhKWG<|St&b3gDSI?!5QF=$T> zKgVK|EP4?5faZ4)2|AWhuYVYd>RQjvqcZxL>ArKQPNu6h-lUBwrg0NaCciL@yeZhT z-T!LsDbYOL0uQB?sfu)&F|Xls=_am(9>O)5(8FD(Z&-R7!s3a1ut#hYSP6cKNv53R zkQ%w*K(n-HaB{2>1ymPsG9NQ}4)#Ulutb$=^>6ASMIZmDU5+p1BV~}z) zq9|+%akfLdLREdwj}cR)+Dz&j1dPGX6l~C# z(g*s}g}ZcOZmMo~j?eiE581=@rzU@NE)I8%lAqF*st0U}b z(QlM4lQvNDA7|_etMM=hy9F7xCtGD)xHbbXKXJ-ui_tHnzl>4-i$U!W^{Z_&1-Ovq zf1Ki&z%8v9V2P}?#`m_lL7okB#c44a5#P9`*Q*GmepbVRRKM!mya@ z6GO|-Fgk3ngA*jbhdWj~o+MhANxEg<($Ev%>t1Lj7kkTzr$d*=)RoeL<<`6U>Idzk zcpuL0685RVOnh4~cj=WIhR{mggb)Ucnj}VV{MepyrDB|tWR7HrY$!W{ogIu~rYQyV z^-6gb7i&98s%LT(WA6z%3&b@eObOdxg`oz$_t5O^x?$PgSCq`e?vAviBWOd*B@8O4 z8ZGRJ4jfk{N&2pagh6*?z)6fL)om9&Moha(-pRUqw|bYkn#={yiOf!iVb1E~{DHpY zKpBfNj6}gXWrTV$lWhuCO|8RMrfaM$JZ?&BKd;BS7Z{E>w=pukTRpkNXjyIMPkAx) zg=t!k$AUgn@oacUw+Z7*r>UOQckP1&uNQb4j(5+~a@S>G64axchXdR@UzNABh`tvw263ISN^S!(Dk08_xjR^WxeNjRo6hx{HwLp^Oj5TtF4gz#RJ=nBTt%#P{orB?78xg(7jD6d zt_&$>ML|xY9gJyIE@cObdciktGAS?6P;1XAn>v}AT8cYyty^-qO=4cSjb%(;y80`A z)VY_8m5f$coOzKu5iutP|r584_&-YH9wSbKM05`x=L9?G7RcinJbCWrBRVBQDc4 z2O^J#U7geLB<{!37SqMEuj5o!6IF@tdL3sU((q)Ax(MOMpssV8yb=5u?6~A>M(+4B zgg2Dwo|5fe#9igNGZRL5-R7{Rd95Re{??L@o%|Jp5G5krSAWVdZ&N(EkFS(;mUQH@ zmh4s{uvfjR5Nv3w$iLNSXcy5rdzALdLOP2A(;V(9wCYzP@poME4qrdyjVFxKVR1al zs1;NI*e84I|5jS`zX`&!)cTb&<~A2wTgkYLLw2T0-)5{@#}t@P&Al^Uo_Mz``1S>6 zQPX4rlWa9fHCNzaZbDV6&yzK?Ba+%foPm+&+S3VC%pa=)ka}z zPX%WuZT{t99P;15UX{Y%98XB;GJ<2r|2kOz$7gYWfCz2zp{XhG= z>)Pfcq_{j7syQ7swSsg`faa)FzpeuTzWN^|2D4~HfR>KrVLT4YQlCZzO6&v$P>XOc zuzt#}EiJ5lU-;g|bmL}`Pbk}?!9!2`=A>{tL0ksSsf!h5%YdF58B1Oqg&_I^+W+`y zg1H%zl)WnYmp}dcqi+~!d4V?G`JdlMG!Ed8LfEDHz5cnihmcW7DP~@9%zthFe{MIP zhC?AIIdbg2Z%9+%GS(O8I({pA_$M^_i`9r16sJ0X1KbZ#tv?VTzvUqp4v3-ETB>K&R1XcKH>^(ClC znX4gshKhxQhN8l(at9)=u^=Iy>0$}bhaW%rFf`Zhl) zjKVKpX# z#&iKRN*%mTXXK7UV(gS_OL*B;u0Tztybi|VD(C=J z@9Pz+_X6`EPc!*?`$a41+DB%k1)#Nw1qzg$!P^!KIMR`jqjg$T3I?J`C&!-8p2y-B zkA0AT$?gWSq$Te@H)@7H*enNG$f7AACsOgZh~4`w$Ef{MpBu}LHlu72f7|IYgU2dI zdp>s_haflasZ+#^(LB(h^aBA>3=X9*|Gc?AaM-onJiobELzus&fMT(J1Z#p&d4vly z7f}5$z+MGlg)I*L`e+=h5DRdoh-b!G;m`qa#f~!uso-h|pWh}adbhbF(UtW_*iY*8 zWA%)l)%dJaMD-g0ErMzRU}?rc9;AW`R8sRhN1eBH;#Z*VC%hkrdk8;c1Ps5~?s)4~ zM0rC`KJNoyZisb5Mwjfei16_xR}KiZ0%S|2L4z34D%H7r*1w{5}!eZ$mY}X-i&&+b1v~JaQ{0Us?vbA+6YLj%GVY>DnqAs^^$h zME#3bIAm?kSb6Y#HZt-M24b;nH9eryI>^l{y1{G{zCB;5_8QyS9C^Su&ygKf4A$Xq zX8qg@fdB8#=~59JUA0 z@(`vCYsRX#4lkAb6ZKM?5rr1?5=l<5Xa;07XL3gV^ObcH~i@j4>%N8cYD z03T+)dZ1)_w|9|Nv#AQghLsvtNq<_T-nxNw`|Uq90GR~4+z!6IjYT-7wqGm@ANcw| zqNr~2U!9S5yRxSkiaCBk-on`frQ5`}YA6N+lym9hQUgLaHHTXm^#R!tv(zoiC*BhU zsFqYKfQc!wsGT=t-4BT3a$Kmb(!qH!Ui{qiPQve#D3Wd~kf@cW87l)-=(=jFH-Jov zW|M64s~Hs{L~Gr891a97j`3|u?jB4{e>CON6uc1-3gKwV;d*=CD;89wWA*Df8eFm? z*+zQ2m)fBLTs;ObJS_Z>@A~MX=wYJp9v8xRW{H43@3Y2sWI2PCUkIYRRG4K4fxD)e zqVE8_9F36SVYP=A#|ta~Oi7kT4Yy+qDqgi7xq|oc#615DS$0m`&2;NEdUn2R>q8b# zfzEQp5xQSog^o;)cRT_rdJqtatk{5psrMU-tggo1s|jx9J@JaVRcYO$GKDt%A>jl!cS`F9kATf4?s1~Q<4T8%sBDR0iU_M~LOvCQHLA>g*{PZ=oSp0IL2ohp+lwi+$9Y$0@8Z{=YK z%`F_i`N$df@N~(S@CH!;4OlW8LLIBL7sz`L##OPefbM~}9}Hdf!4In7^s0IDGVS!> zM(Z~DtjK9Lek%I_k%E8Lzxaf(SI7TZ$4{{>PZW6MU0R|Da&f3u{=BdnP- zr+v0RN_XcInYToXlNn$F+A{_*1)RMUGEm^>V0H!Ac&i}0$+%q?$+)M{>lcs6iW&G2HrMGq&IeWh3YiexC9PPBtj=yOx{7sZx~HnvCDq-z9PCFQ zB`PX0D2grM;oEpJFHB%<^OW{ic1e-G1+T_D|wFey(Tbx1^F!8^{zu|07-^jG`y%n|8|WZf+cH?Wi2h1H?U$|E$od8=4Xh#zhNB$s zNAk2Q#;WK_>>02ZP$8g5B%1LSID={0gwcVJ>sLTCnO6_SODObc2y zA9uzIlRK}Y##(8OQG2Q`{_@?8QeAPb+4lFA*WU!|1T(16v|1D6V8(FM)D!&=t@N64!;Dx`VV+2ka{o+pyy%S2^%)95oxA|VlmC`e0a^`|03 z1NYWckW3 z9=?6ITY<6V&lq3AOQNWp(_A$pYI2YrVX%12zT`0`&p193AX$c`tM7ZyPqhsuizt-7 z4<`iG0yKlBz8*6Pq1W?XI$R#rMdCFlP(~RaRG^DAh%{C)p;NE}=Wl&|d)#AYS!g@F ziYP+im`UT@RfHq|IFVowjI@dQFQesU-O;^m4PZFOfpX*JL5<2+V~J?#&-L8aq7y!Q zU8E8l_ZVhU(?wP$ZD1O=PBHHdRxz>li4q>Kq+ldaP2Zn++Gnh3p!`f9=oeLW-bt0Q z*GR0AZq%J;9BNiX!FPY(Ep>+$Mp5&hz%_fEzh@pmMVV*_$q1EVFw&q({Nfe{3;nYV z`-tks{uF5oN##<0jI89ug+zr+WWR>~40=@bxhbuVRt|YhSD+c?jE>;{eshT)c@2_D z9`Q|i4OfO%2yt&L?=Oji9EYWuZ%ZO68tZDGKb@!Zhk6Fx$cy)Vh65egC5LI6eGL#k zFU`LHbWB$!7l;$dcO*2Zq z+YN?%Se`S9Z%=XL=QlbSEt?Z?e&O8qFoJAlY`Lck(5wX4%1DZQ>6w!epW7pR3&umK zezZs)!gp{+Bb*BM6t5gU;~P$=5Ok9$hz}jb9zr73!xWY}G%8Mn?!x8~+8VWK#?!N6 z_VsXH{tmv_J(FWacrPgD*~@Swuc*|f*Wjr~!%A6NQgqWtZHT#=@StZ8qF^c%^tgw^ zud`xA^eVu?1qZ$vPh=vzd{Q;Q9ppB|Q&<{Wp8g1~*yHljb%eB~sx8xt`7Pa*lsc|g zvR-NCg4p4=VG`pGP9)t0|CkTR;-ft2pZ3s1<_IamY19_;P-y0!gOXj%K{WR)FFl^y zJ=)q3E^?>k&~}Q;{C(zi=Yuj$0kXWj?KU~|`dwG>&F-~KvlI(4VGrt1A4#{CGF4&k z`mg@X4fe*Li+%^?dOZzW$amMuDI3_9?a_YDcc_9=YOlgQG97}Z?5e(&w2-(FK!{Ty z7*|ZE+x-WvH)Lh`d>uMw=UP{I}K1Ha18OfkojZ1XQB34-hxqkmL)2Za^K9_?)^@_ZeV9;ad>-OYsIauvo?uYb+m zjapn4Xe6L+^e=CBWnfYIF$hHaoH^6oT(5@grf!tqvKi2-7!!H7!2krUvb(RC67&U zPA7Lx;EGs>jXesqml9yP6nhg!T6>kIMlBLK`cR8txl5fU_7RJCpXg8iiU#E1XkHVW z{Bd6sWH&!b+T5_=4n3Mr;-4&thLc+QX@^AzcZ;ZVW~DC@k&fhCwz?6n_B~Uz9&4?% z`|@Rhr@!=s#*l{>g|N;t4c-oTTbQBbRXr%hYB9bSoGxzHEDp7fLLCxZ&?%RBD``f{ zuYty^tJqIIU4*Z`2H8`QU_kou)Oa1@jkf{!eKBx4L)@6Uvr38?6z3Zet8&;i;`M~F zttnheWgNDkljyD7FUI$cZZm?iaGd*j%i7I|jT7!}7izksz8S~{EH&vWKQX%EV$YpQ z_Rwj8YRUY1r0>9NZVbaCjgaA$}|+iGUZqE>!NAfby>X1 zcgFmc*UiOTa=+IDDhS?Nt54`<8PAb(7FB#*x9h^3;vu8@o_Ojhl+4IwW7As&h3 z>d6%{W3ZjfeBOrWM7 zJ^Ud``PQvwRqI?V2dxVFQwHNR<~2TZ+%Y=e}XE1CFbYCxM9F0rLpzXJqjpjyJj zZ_2+d(_4J@)Av?jrh!Lj7AKgY8I2|iSyw($fb)-?Ro?GFOPG26-fT+y_!E~D3a>I)o#YH}iS_CFR2=sFWXr<>8T z4a*`GDHF}`#aG+8NImKG3X>S^(yY|QGIBLDuf99BU5J9roX*ve2GJZh;qjnXUwk(h zmCDhg$jH?xWzD2qzfqtwKcLh6fEwvQXBb*eF(q3N<=Y-fAbXK54KrhU6(mU+f`W%5 zLc}~nTw#G@#l?-uXQGe9$D%B+tQ8@zahI1Fs~}mwilrgr&1CsmXOHU2MJR!Kp_9M;Kf$xp)4Stfl!7KIZ7pEEA zXeQ9X%{F1PaQkkk@$cm}umZ*JqW%!R+ny|`R$u?q&Roo`FZk4vYGrP5!Owx8}oey1{47eOlalrY(=@y9T|2GjJ&-#$r>JX ziq@S`B6(dmuRCxLmSiF{L5$yMbm9Q?JklSGIDA4#D%2Is*N#L z;$rIFl)K0k+gndJ4~C}k(Kd|&i6RAlSPj&oU4Dl!2?azO80^Y79P52Vye!Z|D6Ba~=f3uLu@vF@dXX9iO8Fe8%<>S$Lpjw zsw-&fr}O85Sg3L+MjxKqX;~fT-E6Y+i(v+_1hMxj)Ox^=jaGB0IEY;JU8_^+aQP~3 zwD8r#`fZtDfo&0+yt?LfU3XH7rjfsy+aq;8W>DsO2A?cxFpW|k+|d_0OO7RXg|>0f zId<6FFO3yY`QrNhpMiqd$x{)!J>xA{;f zGS`Wr?L6n1FA~;@ADLN94C7HiUN6QCl?Ph0pN29=vN$v|pW&xXdGm@(EM3*Z-j0Jp zWu6)uRoWk3(;{0Y)CY~}xA2T?E{r*RkI2-_wHwRHzR<&Wv-c;rOwjqcHsxaZr{hfm zc*BO3U3_dx9`4Bz)aG-a9dP^E(&Oc1NioOtUW5@`sW#I~&zBByBcpH?XoVAahIAZ0 z=33Z1EVkp;>Os9T`7~y%bUNK~mxzQV`zN?NR@X@oi_1?YK8+A=bW3&oO(k-xlYg5p z5~oTNEVpTkl(_tO3p$cKi1hEIY4{^WI~fadSQuCMT|6nyc8@p4WM$K3AC#`CXTOei z2z-7$Wh9V9W<#z13Y68bag zTjXj4_A&J(rwHTGDIIq3tx^(wM=q0~uRdnXuC@7ZS4~k5_MHGk^D9MF6)n zO7O3~(QK*8RF9L4qh~*)b3TZxGHA*mf1Iasb!}?Bi&Wfb+0|dNVP2?Y-5{h|sPgdyXH7QUj=%81?Vq|c z7Z2{?u{p~lk2S2(#gAJ7jwV|;QWvb&Uqv|64!Kg@R%q4m)_0Sfw^!KzgsQd`6&1eX z*JIk6aVK~0AT)K`?cGpx#EK3ah_muIY>B&cql~+~#sOWB+#yL5^;7e`ySjr~t*F-nD|!!@d4xq0z5f-P<{ikHPhm3!Gl zp+56}!jpgR&)1v2?ZHSLgojUl7p)zOs))~R0gExhm8*tP%8V`eiyM7mzpX1D$aB|;qBYzSx}dzzTU@}D$C9A#Hc zT(0S6ZuIRx=#sTGXo(;lR7&`do}j1pJ!RsUoubPh8kq)6A{Ig?OAo3EKB@Fj`aNMY zHD_ghXu)egGKGnJWqEdN{9Pp1^pAlDPn^qQ=HTgXkDhPB{<|~Evl(APK6sUfsCw9t z@mlF{d7kgvu(wpvi5mAs{|b5UA^jd4_TM0t6U#O!@>$8Cg_EjQ@gF_O!jMI$$E=FS`1g(^fH8uW zq290mJOD*3K;yzD{1yJOwJ^{^d}ROSp9k;^9Q6M?o&R@r{*Ml}YrOvXCK8{*DkQ`| N1sPT85=ni({|~44D+K@m literal 0 HcmV?d00001 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..f1bee1a --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module curiouser.com/openvpn-manager + +go 1.14 + +require ( + github.com/gin-gonic/gin v1.6.3 + github.com/gobuffalo/packr v1.30.1 // indirect + github.com/kr/pretty v0.2.1 // indirect + github.com/rakyll/statik v0.1.7 + github.com/sirupsen/logrus v1.7.0 + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e616a0f --- /dev/null +++ b/go.sum @@ -0,0 +1,122 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +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.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/logger v1.0.0 h1:xw9Ko9EcC5iAFprrjJ6oZco9UpzS5MQ4jAwghsLHdy4= +github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg= +github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk= +github.com/gobuffalo/packr/v2 v2.5.1 h1:TFOeY2VoGamPjQLiNDT3mn//ytzk236VMO2j7iHxJR4= +github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw= +github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/karrick/godirwalk v1.10.12 h1:BqUm+LuJcXjGv1d2mj3gBiQyrQ57a0rYoAmhvJQ7RDU= +github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= +github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +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/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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4 h1:ydJNl0ENAG67pFbB+9tfhiL2pYqLhfoaZFw/cjLhY4A= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go new file mode 100644 index 0000000..16d1732 --- /dev/null +++ b/main.go @@ -0,0 +1,156 @@ +//go:generate statik -src=./public +package main + +import ( + _ "curiouser.com/openvpn-manager/statik" + "encoding/json" + "flag" + "fmt" + "github.com/gin-gonic/gin" + "github.com/rakyll/statik/fs" + "log" + "net" + "net/http" + "os" + "regexp" + "strings" +) + +type ClientConInfo struct { + Username string `json:"username"` + IP string `json:"ip"` + Port string `json:"port"` + VIP string `json:"v_ip"` + ReceivedBytes string `json:"received_bytes"` + SentBytes string `json:"sent_bytes"` + ConnectedSinceTimestamp string `json:"connected_since_timestamp"` + ClientId string `json:"client_id"` + PeerId string `json:"peer_id"` +} + +type OnlineClients struct { + Onlineclients []ClientConInfo `json:"onlineclient"` +} + +var ( + ip_reg = regexp.MustCompile(`(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}`) + port_reg = regexp.MustCompile(`\((.*?)\)`) + omhost string + omport string + ompasswd string + omadminpassw string + + router *gin.Engine + authorizedRoute *gin.RouterGroup +) + +func init() { + + flag.StringVar(&omhost, "host", "", "OpenVPN服务端地址") + flag.StringVar(&omport, "port", "", "OpenVPN服务端管理端口,默认为空") + flag.StringVar(&ompasswd, "passwd", "", "OpenVPN服务端管理端口密码") + flag.StringVar(&omadminpassw,"admin-passwd","","OpenVPN Manager管理员admin的密码") + flag.Parse() + + gin.SetMode(gin.ReleaseMode) + gin.ForceConsoleColor() + router = gin.Default() + + authorizedRoute = router.Group("/", gin.BasicAuth(gin.Accounts{ + "admin": omadminpassw, + })) + +} + +func main() { + + if omhost == "" && omport == "" && omadminpassw == ""{ + fmt.Println("没有设置OpenVPN服务端的主机IP地址、管理端口及管理员密码,请在启动命令后添加'-host','-port','-admin-passwd'参数设置") + os.Exit(0) + } else if omhost == "" { + fmt.Println("OpenVPN服务端主机IP地址没有设置,无法启动。请在启动命令后添加'-host'参数设置IP地址") + os.Exit(0) + } else if omport == "" { + fmt.Println("OpenVPN管理端口没有设置,无法启动。请在启动命令后添加'-port'参数设置管理端口号") + os.Exit(0) + }else if omadminpassw == "" { + fmt.Println("OpenVPN Manager管理员admin用户的密码没有设置,无法启动。请在启动命令后添加'-admin-passwd'参数进行设置") + os.Exit(0) + } + + statikFS, err := fs.New() + if err != nil { + log.Fatal(err) + } + + //根路由设置首页跳转到'/public'加载'index.html' + router.GET("/", func(context *gin.Context) { + context.Request.URL.Path = "/public" + router.HandleContext(context) + }) + + authorizedRoute.StaticFS("/public", statikFS) + authorizedRoute.StaticFile("/favicon.ico", "./public/favicon.ico") + + authorizedRoute.GET("/getOnlineClients", func(context *gin.Context) { + + res := sendDataToSocket(omhost+":"+omport, "status") + origin_status := strings.Split(string(res[0:len(res)]), "\r\n") + var client *ClientConInfo + var oc OnlineClients + for _, s := range origin_status { + if find := strings.HasPrefix(s, "CLIENT_LIST"); find { + var b = strings.Split(s, ",") + client = &ClientConInfo{ + Username: b[1], + //IP: ip_reg.FindAllString(b[2], -1)[0], + //Port: port_reg.FindAllString(b[2], -1)[0], + IP: strings.Split(b[2], ":")[0], + Port: strings.Split(b[2], ":")[1], + VIP: b[3], + ReceivedBytes: b[5], + SentBytes: b[6], + ConnectedSinceTimestamp: b[8], + ClientId: b[10], + PeerId: b[11], + } + oc.Onlineclients = append(oc.Onlineclients, *client) + } + } + + jsons, _ := json.Marshal(oc) + context.JSON(http.StatusOK, string(jsons)) + }) + authorizedRoute.POST("/kickOutClientByCN", func(context *gin.Context) { + username := context.PostForm("username") + _ = sendDataToSocket(omhost+":"+omport, "kill "+username) + }) + fmt.Println("OpenVPN Manager监听端口9090,访问地址:http://127.0.0.1:9090") + router.Run(":9090") + +} + +func sendDataToSocket(conf string, msg string) (resData []byte) { + + conn, err := net.Dial("tcp", conf) + + if err != nil { + log.Fatalf("连接失败") + } + buf1 := make([]byte, 2048) + buf := make([]byte, 2048) + conn.Read(buf) + writeMsg := msg + "\n" + _, err = conn.Write([]byte(writeMsg)) + if err != nil { + fmt.Printf("发送数据失败, %s\n", err) + } + _, err = conn.Read(buf1) + if err != nil { + fmt.Printf("读取数据失败, %s\n", err) + } + conn.Close() + return buf1 + +} + diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..d9c93f7 --- /dev/null +++ b/public/index.html @@ -0,0 +1,86 @@ + + + + + OpenVPN Manager + + + + + +

OpenVPN在线用户列表

+ + + +
ID 用户名 客户端路由公网IP地址 端口 虚拟IP地址 接收数据大小 发送数据大小 连接时间 是否下线用户
+ + +