mirror of
https://github.com/ngoduykhanh/wireguard-ui.git
synced 2025-04-18 19:49:30 +03:00
Abstracts database storage & Status page UI (#88)
This commit is contained in:
parent
7bb80c0c2e
commit
b7c0990dcb
14 changed files with 456 additions and 256 deletions
|
@ -1,5 +1,5 @@
|
|||
# Build stage
|
||||
FROM golang:1.14.2-alpine3.11 as builder
|
||||
FROM golang:1.16.7-alpine3.14 as builder
|
||||
LABEL maintainer="Khanh Ngo <k@ndk.name"
|
||||
ARG BUILD_DEPENDENCIES="npm \
|
||||
yarn"
|
||||
|
|
|
@ -21,6 +21,13 @@ You can take a look at this example of [docker-compose.yml](https://github.com/n
|
|||
```
|
||||
docker-compose up
|
||||
```
|
||||
|
||||
Note:
|
||||
|
||||
There is a Status option that needs docker to be able to access the network of the host in order to read the
|
||||
wireguard interface stats. See the `cap_add` and `network_mode` options on the docker-compose.yaml
|
||||
|
||||
|
||||
### Environment Variables
|
||||
|
||||
|
||||
|
|
|
@ -5,6 +5,9 @@ services:
|
|||
build: .
|
||||
#image: ngoduykhanh/wireguard-ui:latest
|
||||
container_name: wgui
|
||||
cap_add:
|
||||
- NET_ADMIN
|
||||
network_mode: host
|
||||
environment:
|
||||
- SENDGRID_API_KEY
|
||||
- EMAIL_FROM
|
||||
|
@ -12,9 +15,6 @@ services:
|
|||
- SESSION_SECRET
|
||||
- WGUI_USERNAME=alpha
|
||||
- WGUI_PASSWORD=this-unusual-password
|
||||
|
||||
ports:
|
||||
- 5000:5000
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
|
|
3
go.mod
3
go.mod
|
@ -17,7 +17,8 @@ require (
|
|||
github.com/sendgrid/rest v2.6.4+incompatible // indirect
|
||||
github.com/sendgrid/sendgrid-go v3.10.0+incompatible
|
||||
github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf
|
||||
golang.zx2c4.com/wireguard v0.0.20200121 // indirect
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
||||
gopkg.in/go-playground/validator.v9 v9.31.0
|
||||
)
|
||||
|
|
68
go.sum
68
go.sum
|
@ -35,8 +35,11 @@ github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
|
|||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
|
@ -48,8 +51,16 @@ github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/z
|
|||
github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25 h1:EFT6MH3igZK/dIVqgGbTqWVvkZ7wJ5iGN03SVtvvdd8=
|
||||
github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25/go.mod h1:sWkGw/wsaHtRsT9zGQ/WyJCotGWG/Anow/9hsAcBWRw=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850 h1:uhL5Gw7BINiiPAo24A2sxkcDI0Jt/sqp1v5xQCniEFA=
|
||||
github.com/josharian/native v0.0.0-20200817173448-b6b71def0850/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201216134343-bde56ed16391/go.mod h1:cR77jAZG3Y3bsb8hF6fHJbFoyFukLFOkQ98S0pQz3xw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20201220180245-69540ac93943/go.mod h1:z4c53zj6Eex712ROyh8WI0ihysb5j2ROyV42iNogmAs=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20210122163228-8d122574c736/go.mod h1:ZXpIyOK59ZnN7J0BV99cZUPmsqDRZ3eq5X+st7u/oSA=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b h1:c3NTyLNozICy8B4mlMXemD3z/gXgQzVXZS/HqT+i3do=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20210212075122-66c871082f2b/go.mod h1:8w9Rh8m+aHZIG69YPGGem1i5VzoyRC8nw2kA8B+ik5U=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
|
@ -73,10 +84,21 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y
|
|||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43 h1:WgyLFv10Ov49JAQI/ZLUkCZ7VJS3r74hwFIGXJsgZlY=
|
||||
github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo=
|
||||
github.com/mdlayher/genetlink v1.0.0 h1:OoHN1OdyEIkScEmRgxLEe2M9U8ClMytqA5niynLtfj0=
|
||||
github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc=
|
||||
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||
github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
|
||||
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
||||
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
|
||||
github.com/mdlayher/netlink v1.2.0/go.mod h1:kwVW1io0AZy9A1E2YYgaD4Cj+C+GPkU6klXCMzIJ9p8=
|
||||
github.com/mdlayher/netlink v1.2.1/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
|
||||
github.com/mdlayher/netlink v1.2.2-0.20210123213345-5cc92139ae3e/go.mod h1:bacnNlfhqHqqLo4WsYeXSqfyXkInQ9JneWI68v1KwSU=
|
||||
github.com/mdlayher/netlink v1.3.0/go.mod h1:xK/BssKuwcRXHrtN04UBkwQ6dY9VviGGuriDdoPSWys=
|
||||
github.com/mdlayher/netlink v1.4.0 h1:n3ARR+Fm0dDv37dj5wSWZXDKcy+U0zwcXS3zKMnSiT0=
|
||||
github.com/mdlayher/netlink v1.4.0/go.mod h1:dRJi5IABcZpBD2A3D0Mv/AiX8I9uDEu5oGkAVrekmf8=
|
||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721 h1:RlZweED6sbSArvlE924+mUcZuXKLBHA35U7LN621Bws=
|
||||
github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
@ -130,9 +152,11 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e h1:8foAy0aoO5GkqCvAEJ4VC4P3zksTg4X4aJCDpZzmgQI=
|
||||
golang.org/x/crypto v0.0.0-20210503195802-e9a32991a82e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
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=
|
||||
|
@ -142,8 +166,15 @@ golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
golang.org/x/net v0.0.0-20191003171128-d98b1b443823/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201216054612-986b41b23924/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d h1:nTDGCTeAu2LhcsHTRzjyIUbZHCJ4QePArsm27Hka0UM=
|
||||
golang.org/x/net v0.0.0-20210504132125-bbd867fde50d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -161,21 +192,40 @@ golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191003212358-c178f38b412c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201118182958-a01c418693c7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201218084310-7d0127a74742/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210110051926-789bb1bd4061/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210123111255-9b0068b26619/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210309040221-94ec62e08169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6 h1:cdsMqa2nXzqlgs183pHxtvoVwU7CyzaCTAUOg94af4c=
|
||||
golang.org/x/sys v0.0.0-20210503173754-0981d6026fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190608022120-eacb66d2a7c3/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.zx2c4.com/wireguard v0.0.0-20210427022245-097af6e1351b/go.mod h1:a057zjmoc00UN7gVkaJt2sXVK523kMJcogDTEvPIasg=
|
||||
golang.zx2c4.com/wireguard v0.0.20200121 h1:vcswa5Q6f+sylDfjqyrVNNrjsFUUbPsgAQTBCAg/Qf8=
|
||||
golang.zx2c4.com/wireguard v0.0.20200121/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf h1:rWUZHukj3poXegPQMZOXgxjTGIBe3mLNHNVvL5DsHus=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf/go.mod h1:UdS9frhv65KTfwxME1xE8+rHYoFpbm36gOud1GhBe9c=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c h1:ADNrRDI5NR23/TUCnEmlLZLt4u9DnZ2nwRkPrAcFvto=
|
||||
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20210803171230-4253848d036c/go.mod h1:+1XihzyZUBJcSc5WO9SwNA7v26puQwOEDwanaxfNXPQ=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
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=
|
||||
|
|
|
@ -5,20 +5,23 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
rice "github.com/GeertJohan/go.rice"
|
||||
|
||||
"github.com/gorilla/sessions"
|
||||
"github.com/labstack/echo-contrib/session"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/gommon/log"
|
||||
"github.com/rs/xid"
|
||||
"golang.zx2c4.com/wireguard/wgctrl"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"github.com/ngoduykhanh/wireguard-ui/emailer"
|
||||
"github.com/ngoduykhanh/wireguard-ui/model"
|
||||
"github.com/ngoduykhanh/wireguard-ui/store"
|
||||
"github.com/ngoduykhanh/wireguard-ui/util"
|
||||
"github.com/rs/xid"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
)
|
||||
|
||||
// LoginPage handler
|
||||
|
@ -29,12 +32,12 @@ func LoginPage() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// Login for signing in handler
|
||||
func Login() echo.HandlerFunc {
|
||||
func Login(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
user := new(model.User)
|
||||
c.Bind(user)
|
||||
|
||||
dbuser, err := util.GetUser()
|
||||
dbuser, err := db.GetUser()
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot query user from DB"})
|
||||
}
|
||||
|
@ -77,10 +80,10 @@ func Logout() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// WireGuardClients handler
|
||||
func WireGuardClients() echo.HandlerFunc {
|
||||
func WireGuardClients(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
clientDataList, err := util.GetClients(true)
|
||||
clientDataList, err := db.GetClients(true)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{
|
||||
false, fmt.Sprintf("Cannot get client list: %v", err),
|
||||
|
@ -95,10 +98,10 @@ func WireGuardClients() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// GetClients handler return a list of Wireguard client data
|
||||
func GetClients() echo.HandlerFunc {
|
||||
func GetClients(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
clientDataList, err := util.GetClients(true)
|
||||
clientDataList, err := db.GetClients(true)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{
|
||||
false, fmt.Sprintf("Cannot get client list: %v", err),
|
||||
|
@ -110,11 +113,11 @@ func GetClients() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// GetClient handler return a of Wireguard client data
|
||||
func GetClient() echo.HandlerFunc {
|
||||
func GetClient(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
clientID := c.Param("id")
|
||||
clientData, err := util.GetClientByID(clientID, true)
|
||||
clientData, err := db.GetClientByID(clientID, true)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
|
||||
}
|
||||
|
@ -124,27 +127,22 @@ func GetClient() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// NewClient handler
|
||||
func NewClient() echo.HandlerFunc {
|
||||
func NewClient(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
client := new(model.Client)
|
||||
c.Bind(client)
|
||||
|
||||
db, err := util.DBConn()
|
||||
if err != nil {
|
||||
log.Error("Cannot initialize database: ", err)
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot access database"})
|
||||
}
|
||||
var client model.Client
|
||||
c.Bind(&client)
|
||||
|
||||
// read server information
|
||||
serverInterface := model.ServerInterface{}
|
||||
if err := db.Read("server", "interfaces", &serverInterface); err != nil {
|
||||
log.Error("Cannot fetch server interface config from database: ", err)
|
||||
server, err := db.GetServer()
|
||||
if err != nil {
|
||||
log.Error("Cannot fetch server from database: ", err)
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()})
|
||||
}
|
||||
|
||||
// validate the input Allocation IPs
|
||||
allocatedIPs, err := util.GetAllocatedIPs("")
|
||||
check, err := util.ValidateIPAllocation(serverInterface.Addresses, allocatedIPs, client.AllocatedIPs)
|
||||
check, err := util.ValidateIPAllocation(server.Interface.Addresses, allocatedIPs, client.AllocatedIPs)
|
||||
if !check {
|
||||
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, fmt.Sprintf("%s", err)})
|
||||
}
|
||||
|
@ -181,7 +179,11 @@ func NewClient() echo.HandlerFunc {
|
|||
client.UpdatedAt = client.CreatedAt
|
||||
|
||||
// write client to the database
|
||||
db.Write("clients", client.ID, client)
|
||||
if err := db.SaveClient(client); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{
|
||||
false, err.Error(),
|
||||
})
|
||||
}
|
||||
log.Infof("Created wireguard client: %v", client)
|
||||
|
||||
return c.JSON(http.StatusOK, client)
|
||||
|
@ -189,7 +191,7 @@ func NewClient() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// EmailClient handler to sent the configuration via email
|
||||
func EmailClient(mailer emailer.Emailer, emailSubject, emailContent string) echo.HandlerFunc {
|
||||
func EmailClient(db store.IStore, mailer emailer.Emailer, emailSubject, emailContent string) echo.HandlerFunc {
|
||||
type clientIdEmailPayload struct {
|
||||
ID string `json:"id"`
|
||||
Email string `json:"email"`
|
||||
|
@ -200,15 +202,15 @@ func EmailClient(mailer emailer.Emailer, emailSubject, emailContent string) echo
|
|||
c.Bind(&payload)
|
||||
// TODO validate email
|
||||
|
||||
clientData, err := util.GetClientByID(payload.ID, true)
|
||||
clientData, err := db.GetClientByID(payload.ID, true)
|
||||
if err != nil {
|
||||
log.Errorf("Cannot generate client id %s config file for downloading: %v", payload.ID, err)
|
||||
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
|
||||
}
|
||||
|
||||
// build config
|
||||
server, _ := util.GetServer()
|
||||
globalSettings, _ := util.GetGlobalSettings()
|
||||
server, _ := db.GetServer()
|
||||
globalSettings, _ := db.GetGlobalSettings()
|
||||
config := util.BuildClientConfig(*clientData.Client, server, globalSettings)
|
||||
|
||||
cfg_att := emailer.Attachment{"wg0.conf", []byte(config)}
|
||||
|
@ -233,36 +235,28 @@ func EmailClient(mailer emailer.Emailer, emailSubject, emailContent string) echo
|
|||
}
|
||||
|
||||
// UpdateClient handler to update client information
|
||||
func UpdateClient() echo.HandlerFunc {
|
||||
func UpdateClient(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
_client := new(model.Client)
|
||||
c.Bind(_client)
|
||||
|
||||
db, err := util.DBConn()
|
||||
if err != nil {
|
||||
log.Error("Cannot initialize database: ", err)
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot access database"})
|
||||
}
|
||||
var _client model.Client
|
||||
c.Bind(&_client)
|
||||
|
||||
// validate client existence
|
||||
client := model.Client{}
|
||||
if err := db.Read("clients", _client.ID, &client); err != nil {
|
||||
clientData, err := db.GetClientByID(_client.ID, false)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
|
||||
}
|
||||
|
||||
// read server information
|
||||
serverInterface := model.ServerInterface{}
|
||||
if err := db.Read("server", "interfaces", &serverInterface); err != nil {
|
||||
log.Error("Cannot fetch server interface config from database: ", err)
|
||||
server, err := db.GetServer()
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{
|
||||
false, fmt.Sprintf("Cannot fetch server config: %s", err),
|
||||
})
|
||||
}
|
||||
|
||||
client := *clientData.Client
|
||||
// validate the input Allocation IPs
|
||||
allocatedIPs, err := util.GetAllocatedIPs(client.ID)
|
||||
check, err := util.ValidateIPAllocation(serverInterface.Addresses, allocatedIPs, _client.AllocatedIPs)
|
||||
check, err := util.ValidateIPAllocation(server.Interface.Addresses, allocatedIPs, _client.AllocatedIPs)
|
||||
if !check {
|
||||
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, fmt.Sprintf("%s", err)})
|
||||
}
|
||||
|
@ -283,7 +277,9 @@ func UpdateClient() echo.HandlerFunc {
|
|||
client.UpdatedAt = time.Now().UTC()
|
||||
|
||||
// write to the database
|
||||
db.Write("clients", client.ID, &client)
|
||||
if err := db.SaveClient(client); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()})
|
||||
}
|
||||
log.Infof("Updated client information successfully => %v", client)
|
||||
|
||||
return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Updated client successfully"})
|
||||
|
@ -291,7 +287,7 @@ func UpdateClient() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// SetClientStatus handler to enable / disable a client
|
||||
func SetClientStatus() echo.HandlerFunc {
|
||||
func SetClientStatus(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
data := make(map[string]interface{})
|
||||
|
@ -304,19 +300,17 @@ func SetClientStatus() echo.HandlerFunc {
|
|||
clientID := data["id"].(string)
|
||||
status := data["status"].(bool)
|
||||
|
||||
db, err := util.DBConn()
|
||||
clientdata, err := db.GetClientByID(clientID, false)
|
||||
if err != nil {
|
||||
log.Error("Cannot initialize database: ", err)
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot access database"})
|
||||
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, err.Error()})
|
||||
}
|
||||
|
||||
client := model.Client{}
|
||||
if err := db.Read("clients", clientID, &client); err != nil {
|
||||
log.Error("Cannot get client from database: ", err)
|
||||
}
|
||||
client := *clientdata.Client
|
||||
|
||||
client.Enabled = status
|
||||
db.Write("clients", clientID, &client)
|
||||
if err := db.SaveClient(client); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()})
|
||||
}
|
||||
log.Infof("Changed client %s enabled status to %v", client.ID, status)
|
||||
|
||||
return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Changed client status successfully"})
|
||||
|
@ -324,22 +318,28 @@ func SetClientStatus() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// DownloadClient handler
|
||||
func DownloadClient() echo.HandlerFunc {
|
||||
func DownloadClient(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
clientID := c.QueryParam("clientid")
|
||||
if clientID == "" {
|
||||
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Missing clientid parameter"})
|
||||
}
|
||||
|
||||
clientData, err := util.GetClientByID(clientID, false)
|
||||
clientData, err := db.GetClientByID(clientID, false)
|
||||
if err != nil {
|
||||
log.Errorf("Cannot generate client id %s config file for downloading: %v", clientID, err)
|
||||
return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
|
||||
}
|
||||
|
||||
// build config
|
||||
server, _ := util.GetServer()
|
||||
globalSettings, _ := util.GetGlobalSettings()
|
||||
server, err := db.GetServer()
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()})
|
||||
}
|
||||
globalSettings, err := db.GetGlobalSettings()
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()})
|
||||
}
|
||||
config := util.BuildClientConfig(*clientData.Client, server, globalSettings)
|
||||
|
||||
// create io reader from string
|
||||
|
@ -352,20 +352,15 @@ func DownloadClient() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// RemoveClient handler
|
||||
func RemoveClient() echo.HandlerFunc {
|
||||
func RemoveClient(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
client := new(model.Client)
|
||||
c.Bind(client)
|
||||
|
||||
// delete client from database
|
||||
db, err := util.DBConn()
|
||||
if err != nil {
|
||||
log.Error("Cannot initialize database: ", err)
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot access database"})
|
||||
}
|
||||
|
||||
if err := db.Delete("clients", client.ID); err != nil {
|
||||
if err := db.DeleteClient(client.ID); err != nil {
|
||||
log.Error("Cannot delete wireguard client: ", err)
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot delete client from database"})
|
||||
}
|
||||
|
@ -376,10 +371,10 @@ func RemoveClient() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// WireGuardServer handler
|
||||
func WireGuardServer() echo.HandlerFunc {
|
||||
func WireGuardServer(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
server, err := util.GetServer()
|
||||
server, err := db.GetServer()
|
||||
if err != nil {
|
||||
log.Error("Cannot get server config: ", err)
|
||||
}
|
||||
|
@ -393,11 +388,11 @@ func WireGuardServer() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// WireGuardServerInterfaces handler
|
||||
func WireGuardServerInterfaces() echo.HandlerFunc {
|
||||
func WireGuardServerInterfaces(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
serverInterface := new(model.ServerInterface)
|
||||
c.Bind(serverInterface)
|
||||
var serverInterface model.ServerInterface
|
||||
c.Bind(&serverInterface)
|
||||
|
||||
// validate the input addresses
|
||||
if util.ValidateServerAddresses(serverInterface.Addresses) == false {
|
||||
|
@ -408,13 +403,10 @@ func WireGuardServerInterfaces() echo.HandlerFunc {
|
|||
serverInterface.UpdatedAt = time.Now().UTC()
|
||||
|
||||
// write config to the database
|
||||
db, err := util.DBConn()
|
||||
if err != nil {
|
||||
log.Error("Cannot initialize database: ", err)
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot access database"})
|
||||
}
|
||||
|
||||
db.Write("server", "interfaces", serverInterface)
|
||||
if err := db.SaveServerInterface(serverInterface); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Interface IP address must be in CIDR format"})
|
||||
}
|
||||
log.Infof("Updated wireguard server interfaces settings: %v", serverInterface)
|
||||
|
||||
return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Updated interface addresses successfully"})
|
||||
|
@ -422,7 +414,7 @@ func WireGuardServerInterfaces() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// WireGuardServerKeyPair handler to generate private and public keys
|
||||
func WireGuardServerKeyPair() echo.HandlerFunc {
|
||||
func WireGuardServerKeyPair(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
// gen Wireguard key pair
|
||||
|
@ -432,19 +424,14 @@ func WireGuardServerKeyPair() echo.HandlerFunc {
|
|||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate Wireguard key pair"})
|
||||
}
|
||||
|
||||
serverKeyPair := new(model.ServerKeypair)
|
||||
var serverKeyPair model.ServerKeypair
|
||||
serverKeyPair.PrivateKey = key.String()
|
||||
serverKeyPair.PublicKey = key.PublicKey().String()
|
||||
serverKeyPair.UpdatedAt = time.Now().UTC()
|
||||
|
||||
// write config to the database
|
||||
db, err := util.DBConn()
|
||||
if err != nil {
|
||||
log.Error("Cannot initialize database: ", err)
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot access database"})
|
||||
if err := db.SaveServerKeyPair(serverKeyPair); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate Wireguard key pair"})
|
||||
}
|
||||
|
||||
db.Write("server", "keypair", serverKeyPair)
|
||||
log.Infof("Updated wireguard server interfaces settings: %v", serverKeyPair)
|
||||
|
||||
return c.JSON(http.StatusOK, serverKeyPair)
|
||||
|
@ -452,10 +439,10 @@ func WireGuardServerKeyPair() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// GlobalSettings handler
|
||||
func GlobalSettings() echo.HandlerFunc {
|
||||
func GlobalSettings(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
globalSettings, err := util.GetGlobalSettings()
|
||||
globalSettings, err := db.GetGlobalSettings()
|
||||
if err != nil {
|
||||
log.Error("Cannot get global settings: ", err)
|
||||
}
|
||||
|
@ -467,12 +454,99 @@ func GlobalSettings() echo.HandlerFunc {
|
|||
}
|
||||
}
|
||||
|
||||
// GlobalSettingSubmit handler to update the global settings
|
||||
func GlobalSettingSubmit() echo.HandlerFunc {
|
||||
// Status handler
|
||||
func Status(db store.IStore) echo.HandlerFunc {
|
||||
type PeerVM struct {
|
||||
Name string
|
||||
Email string
|
||||
PublicKey string
|
||||
ReceivedBytes int64
|
||||
TransmitBytes int64
|
||||
LastHandshakeTime time.Time
|
||||
LastHandshakeRel time.Duration
|
||||
Connected bool
|
||||
}
|
||||
|
||||
type DeviceVM struct {
|
||||
Name string
|
||||
Peers []PeerVM
|
||||
}
|
||||
return func(c echo.Context) error {
|
||||
|
||||
globalSettings := new(model.GlobalSetting)
|
||||
c.Bind(globalSettings)
|
||||
wgclient, err := wgctrl.New()
|
||||
if err != nil {
|
||||
return c.Render(http.StatusInternalServerError, "status.html", map[string]interface{}{
|
||||
"baseData": model.BaseData{Active: "status", CurrentUser: currentUser(c)},
|
||||
"error": err.Error(),
|
||||
"devices": nil,
|
||||
})
|
||||
}
|
||||
|
||||
devices, err := wgclient.Devices()
|
||||
if err != nil {
|
||||
return c.Render(http.StatusInternalServerError, "status.html", map[string]interface{}{
|
||||
"baseData": model.BaseData{Active: "status", CurrentUser: currentUser(c)},
|
||||
"error": err.Error(),
|
||||
"devices": nil,
|
||||
})
|
||||
}
|
||||
|
||||
devicesVm := make([]DeviceVM, 0, len(devices))
|
||||
if len(devices) > 0 {
|
||||
m := make(map[string]*model.Client)
|
||||
clients, err := db.GetClients(false)
|
||||
if err != nil {
|
||||
return c.Render(http.StatusInternalServerError, "status.html", map[string]interface{}{
|
||||
"baseData": model.BaseData{Active: "status", CurrentUser: currentUser(c)},
|
||||
"error": err.Error(),
|
||||
"devices": nil,
|
||||
})
|
||||
}
|
||||
for i := range clients {
|
||||
if clients[i].Client != nil {
|
||||
m[clients[i].Client.PublicKey] = clients[i].Client
|
||||
}
|
||||
}
|
||||
|
||||
conv := map[bool]int{true: 1, false: 0}
|
||||
for i := range devices {
|
||||
devVm := DeviceVM{Name: devices[i].Name}
|
||||
for j := range devices[i].Peers {
|
||||
pVm := PeerVM{
|
||||
PublicKey: devices[i].Peers[j].PublicKey.String(),
|
||||
ReceivedBytes: devices[i].Peers[j].ReceiveBytes,
|
||||
TransmitBytes: devices[i].Peers[j].TransmitBytes,
|
||||
LastHandshakeTime: devices[i].Peers[j].LastHandshakeTime,
|
||||
LastHandshakeRel: time.Since(devices[i].Peers[j].LastHandshakeTime),
|
||||
}
|
||||
pVm.Connected = pVm.LastHandshakeRel.Minutes() < 3.
|
||||
|
||||
if _client, ok := m[pVm.PublicKey]; ok {
|
||||
pVm.Name = _client.Name
|
||||
pVm.Email = _client.Email
|
||||
}
|
||||
devVm.Peers = append(devVm.Peers, pVm)
|
||||
}
|
||||
sort.SliceStable(devVm.Peers, func(i, j int) bool { return devVm.Peers[i].Name < devVm.Peers[j].Name })
|
||||
sort.SliceStable(devVm.Peers, func(i, j int) bool { return conv[devVm.Peers[i].Connected] > conv[devVm.Peers[j].Connected] })
|
||||
devicesVm = append(devicesVm, devVm)
|
||||
}
|
||||
}
|
||||
|
||||
return c.Render(http.StatusOK, "status.html", map[string]interface{}{
|
||||
"baseData": model.BaseData{Active: "status", CurrentUser: currentUser(c)},
|
||||
"devices": devicesVm,
|
||||
"error": "",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// GlobalSettingSubmit handler to update the global settings
|
||||
func GlobalSettingSubmit(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
var globalSettings model.GlobalSetting
|
||||
c.Bind(&globalSettings)
|
||||
|
||||
// validate the input dns server list
|
||||
if util.ValidateIPAddressList(globalSettings.DNSServers) == false {
|
||||
|
@ -483,13 +557,10 @@ func GlobalSettingSubmit() echo.HandlerFunc {
|
|||
globalSettings.UpdatedAt = time.Now().UTC()
|
||||
|
||||
// write config to the database
|
||||
db, err := util.DBConn()
|
||||
if err != nil {
|
||||
log.Error("Cannot initialize database: ", err)
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot access database"})
|
||||
if err := db.SaveGlobalSettings(globalSettings); err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate Wireguard key pair"})
|
||||
}
|
||||
|
||||
db.Write("server", "global_settings", globalSettings)
|
||||
log.Infof("Updated global settings: %v", globalSettings)
|
||||
|
||||
return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Updated global settings successfully"})
|
||||
|
@ -521,12 +592,13 @@ func MachineIPAddresses() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// SuggestIPAllocation handler to get the list of ip address for client
|
||||
func SuggestIPAllocation() echo.HandlerFunc {
|
||||
func SuggestIPAllocation(db store.IStore) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
server, err := util.GetServer()
|
||||
server, err := db.GetServer()
|
||||
if err != nil {
|
||||
log.Error("Cannot fetch server config from database: ", err)
|
||||
return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, err.Error()})
|
||||
}
|
||||
|
||||
// return the list of suggestedIPs
|
||||
|
@ -557,22 +629,22 @@ func SuggestIPAllocation() echo.HandlerFunc {
|
|||
}
|
||||
|
||||
// ApplyServerConfig handler to write config file and restart Wireguard server
|
||||
func ApplyServerConfig(tmplBox *rice.Box) echo.HandlerFunc {
|
||||
func ApplyServerConfig(db store.IStore, tmplBox *rice.Box) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
|
||||
server, err := util.GetServer()
|
||||
server, err := db.GetServer()
|
||||
if err != nil {
|
||||
log.Error("Cannot get server config: ", err)
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot get server config"})
|
||||
}
|
||||
|
||||
clients, err := util.GetClients(false)
|
||||
clients, err := db.GetClients(false)
|
||||
if err != nil {
|
||||
log.Error("Cannot get client config: ", err)
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot get client config"})
|
||||
}
|
||||
|
||||
settings, err := util.GetGlobalSettings()
|
||||
settings, err := db.GetGlobalSettings()
|
||||
if err != nil {
|
||||
log.Error("Cannot get global settings: ", err)
|
||||
return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot get global settings"})
|
||||
|
|
48
main.go
48
main.go
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/ngoduykhanh/wireguard-ui/emailer"
|
||||
"github.com/ngoduykhanh/wireguard-ui/handler"
|
||||
"github.com/ngoduykhanh/wireguard-ui/router"
|
||||
"github.com/ngoduykhanh/wireguard-ui/store/jsondb"
|
||||
"github.com/ngoduykhanh/wireguard-ui/util"
|
||||
)
|
||||
|
||||
|
@ -57,14 +58,16 @@ func init() {
|
|||
fmt.Println("Authentication\t:", !util.DisableLogin)
|
||||
fmt.Println("Bind address\t:", util.BindAddress)
|
||||
|
||||
// initialize DB
|
||||
err := util.InitDB()
|
||||
if err != nil {
|
||||
fmt.Print("Cannot init database: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
db, err := jsondb.New("./db")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := db.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// set app extra data
|
||||
extraData := make(map[string]string)
|
||||
extraData["appVersion"] = appVersion
|
||||
|
@ -78,32 +81,33 @@ func main() {
|
|||
// register routes
|
||||
app := router.New(tmplBox, extraData, util.SessionSecret)
|
||||
|
||||
app.GET("/", handler.WireGuardClients(), handler.ValidSession)
|
||||
app.GET("/", handler.WireGuardClients(db), handler.ValidSession)
|
||||
|
||||
if !util.DisableLogin {
|
||||
app.GET("/login", handler.LoginPage())
|
||||
app.POST("/login", handler.Login())
|
||||
app.POST("/login", handler.Login(db))
|
||||
}
|
||||
|
||||
sendmail := emailer.NewSendgridApiMail(util.SendgridApiKey, util.EmailFromName, util.EmailFrom)
|
||||
|
||||
app.GET("/logout", handler.Logout(), handler.ValidSession)
|
||||
app.POST("/new-client", handler.NewClient(), handler.ValidSession)
|
||||
app.POST("/update-client", handler.UpdateClient(), handler.ValidSession)
|
||||
app.POST("/email-client", handler.EmailClient(sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession)
|
||||
app.POST("/client/set-status", handler.SetClientStatus(), handler.ValidSession)
|
||||
app.POST("/remove-client", handler.RemoveClient(), handler.ValidSession)
|
||||
app.GET("/download", handler.DownloadClient(), handler.ValidSession)
|
||||
app.GET("/wg-server", handler.WireGuardServer(), handler.ValidSession)
|
||||
app.POST("wg-server/interfaces", handler.WireGuardServerInterfaces(), handler.ValidSession)
|
||||
app.POST("wg-server/keypair", handler.WireGuardServerKeyPair(), handler.ValidSession)
|
||||
app.GET("/global-settings", handler.GlobalSettings(), handler.ValidSession)
|
||||
app.POST("/global-settings", handler.GlobalSettingSubmit(), handler.ValidSession)
|
||||
app.GET("/api/clients", handler.GetClients(), handler.ValidSession)
|
||||
app.GET("/api/client/:id", handler.GetClient(), handler.ValidSession)
|
||||
app.POST("/new-client", handler.NewClient(db), handler.ValidSession)
|
||||
app.POST("/update-client", handler.UpdateClient(db), handler.ValidSession)
|
||||
app.POST("/email-client", handler.EmailClient(db, sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession)
|
||||
app.POST("/client/set-status", handler.SetClientStatus(db), handler.ValidSession)
|
||||
app.POST("/remove-client", handler.RemoveClient(db), handler.ValidSession)
|
||||
app.GET("/download", handler.DownloadClient(db), handler.ValidSession)
|
||||
app.GET("/wg-server", handler.WireGuardServer(db), handler.ValidSession)
|
||||
app.POST("wg-server/interfaces", handler.WireGuardServerInterfaces(db), handler.ValidSession)
|
||||
app.POST("wg-server/keypair", handler.WireGuardServerKeyPair(db), handler.ValidSession)
|
||||
app.GET("/global-settings", handler.GlobalSettings(db), handler.ValidSession)
|
||||
app.POST("/global-settings", handler.GlobalSettingSubmit(db), handler.ValidSession)
|
||||
app.GET("/status", handler.Status(db), handler.ValidSession)
|
||||
app.GET("/api/clients", handler.GetClients(db), handler.ValidSession)
|
||||
app.GET("/api/client/:id", handler.GetClient(db), handler.ValidSession)
|
||||
app.GET("/api/machine-ips", handler.MachineIPAddresses(), handler.ValidSession)
|
||||
app.GET("/api/suggest-client-ips", handler.SuggestIPAllocation(), handler.ValidSession)
|
||||
app.GET("/api/apply-wg-config", handler.ApplyServerConfig(tmplBox), handler.ValidSession)
|
||||
app.GET("/api/suggest-client-ips", handler.SuggestIPAllocation(db), handler.ValidSession)
|
||||
app.GET("/api/apply-wg-config", handler.ApplyServerConfig(db, tmplBox), handler.ValidSession)
|
||||
|
||||
// servers other static files
|
||||
app.GET("/static/*", echo.WrapHandler(http.StripPrefix("/static/", assetHandler)))
|
||||
|
|
|
@ -74,12 +74,18 @@ func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Ec
|
|||
log.Fatal(err)
|
||||
}
|
||||
|
||||
tmplStatusString, err := tmplBox.String("status.html")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// create template list
|
||||
templates := make(map[string]*template.Template)
|
||||
templates["login.html"] = template.Must(template.New("login").Parse(tmplLoginString))
|
||||
templates["clients.html"] = template.Must(template.New("clients").Parse(tmplBaseString + tmplClientsString))
|
||||
templates["server.html"] = template.Must(template.New("server").Parse(tmplBaseString + tmplServerString))
|
||||
templates["global_settings.html"] = template.Must(template.New("global_settings").Parse(tmplBaseString + tmplGlobalSettingsString))
|
||||
templates["status.html"] = template.Must(template.New("status").Parse(tmplBaseString + tmplStatusString))
|
||||
|
||||
e.Logger.SetLevel(log.DEBUG)
|
||||
e.Pre(middleware.RemoveTrailingSlash())
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package util
|
||||
package jsondb
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
@ -8,49 +8,40 @@ import (
|
|||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/ngoduykhanh/wireguard-ui/model"
|
||||
"github.com/sdomino/scribble"
|
||||
"github.com/skip2/go-qrcode"
|
||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||
|
||||
"github.com/ngoduykhanh/wireguard-ui/model"
|
||||
"github.com/ngoduykhanh/wireguard-ui/util"
|
||||
)
|
||||
|
||||
const dbPath = "./db"
|
||||
const username_env_var = "WGUI_USERNAME"
|
||||
const password_env_var = "WGUI_PASSWORD"
|
||||
const defaultUsername = "admin"
|
||||
const defaultPassword = "admin"
|
||||
const defaultServerAddress = "10.252.1.0/24"
|
||||
const defaultServerPort = 51820
|
||||
const defaultDNS = "1.1.1.1"
|
||||
const defaultMTU = 1450
|
||||
const defaultPersistentKeepalive = 15
|
||||
const defaultConfigFilePath = "/etc/wireguard/wg0.conf"
|
||||
type JsonDB struct {
|
||||
conn *scribble.Driver
|
||||
dbPath string
|
||||
}
|
||||
|
||||
// DBConn to initialize the database connection
|
||||
func DBConn() (*scribble.Driver, error) {
|
||||
db, err := scribble.New(dbPath, nil)
|
||||
// New returns a new pointer JsonDB
|
||||
func New(dbPath string) (*JsonDB, error) {
|
||||
conn, err := scribble.New(dbPath, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return db, nil
|
||||
ans := JsonDB{
|
||||
conn: conn,
|
||||
dbPath: dbPath,
|
||||
}
|
||||
return &ans, nil
|
||||
|
||||
}
|
||||
|
||||
func getCredVar(key, fallback string) string {
|
||||
if value, ok := os.LookupEnv(key); ok {
|
||||
return value
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
// InitDB to create the default database
|
||||
func InitDB() error {
|
||||
var clientPath string = path.Join(dbPath, "clients")
|
||||
var serverPath string = path.Join(dbPath, "server")
|
||||
func (o *JsonDB) Init() error {
|
||||
var clientPath string = path.Join(o.dbPath, "clients")
|
||||
var serverPath string = path.Join(o.dbPath, "server")
|
||||
var serverInterfacePath string = path.Join(serverPath, "interfaces.json")
|
||||
var serverKeyPairPath string = path.Join(serverPath, "keypair.json")
|
||||
var globalSettingPath string = path.Join(serverPath, "global_settings.json")
|
||||
var userPath string = path.Join(serverPath, "users.json")
|
||||
|
||||
// create directories if they do not exist
|
||||
if _, err := os.Stat(clientPath); os.IsNotExist(err) {
|
||||
os.MkdirAll(clientPath, os.ModePerm)
|
||||
|
@ -61,24 +52,15 @@ func InitDB() error {
|
|||
|
||||
// server's interface
|
||||
if _, err := os.Stat(serverInterfacePath); os.IsNotExist(err) {
|
||||
db, err := DBConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
serverInterface := new(model.ServerInterface)
|
||||
serverInterface.Addresses = []string{defaultServerAddress}
|
||||
serverInterface.ListenPort = defaultServerPort
|
||||
serverInterface.Addresses = []string{util.DefaultServerAddress}
|
||||
serverInterface.ListenPort = util.DefaultServerPort
|
||||
serverInterface.UpdatedAt = time.Now().UTC()
|
||||
db.Write("server", "interfaces", serverInterface)
|
||||
o.conn.Write("server", "interfaces", serverInterface)
|
||||
}
|
||||
|
||||
// server's key pair
|
||||
if _, err := os.Stat(serverKeyPairPath); os.IsNotExist(err) {
|
||||
db, err := DBConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key, err := wgtypes.GeneratePrivateKey()
|
||||
if err != nil {
|
||||
|
@ -88,97 +70,62 @@ func InitDB() error {
|
|||
serverKeyPair.PrivateKey = key.String()
|
||||
serverKeyPair.PublicKey = key.PublicKey().String()
|
||||
serverKeyPair.UpdatedAt = time.Now().UTC()
|
||||
db.Write("server", "keypair", serverKeyPair)
|
||||
o.conn.Write("server", "keypair", serverKeyPair)
|
||||
}
|
||||
|
||||
// global settings
|
||||
if _, err := os.Stat(globalSettingPath); os.IsNotExist(err) {
|
||||
db, err := DBConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
publicInterface, err := GetPublicIP()
|
||||
publicInterface, err := util.GetPublicIP()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
globalSetting := new(model.GlobalSetting)
|
||||
globalSetting.EndpointAddress = publicInterface.IPAddress
|
||||
globalSetting.DNSServers = []string{defaultDNS}
|
||||
globalSetting.MTU = defaultMTU
|
||||
globalSetting.PersistentKeepalive = defaultPersistentKeepalive
|
||||
globalSetting.ConfigFilePath = defaultConfigFilePath
|
||||
globalSetting.DNSServers = []string{util.DefaultDNS}
|
||||
globalSetting.MTU = util.DefaultMTU
|
||||
globalSetting.PersistentKeepalive = util.DefaultPersistentKeepalive
|
||||
globalSetting.ConfigFilePath = util.DefaultConfigFilePath
|
||||
globalSetting.UpdatedAt = time.Now().UTC()
|
||||
db.Write("server", "global_settings", globalSetting)
|
||||
o.conn.Write("server", "global_settings", globalSetting)
|
||||
}
|
||||
|
||||
// user info
|
||||
if _, err := os.Stat(userPath); os.IsNotExist(err) {
|
||||
db, err := DBConn()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
user := new(model.User)
|
||||
user.Username = getCredVar(username_env_var, defaultUsername)
|
||||
user.Password = getCredVar(password_env_var, defaultPassword)
|
||||
db.Write("server", "users", user)
|
||||
user.Username = util.GetCredVar(util.UsernameEnvVar, util.DefaultUsername)
|
||||
user.Password = util.GetCredVar(util.PasswordEnvVar, util.DefaultPassword)
|
||||
o.conn.Write("server", "users", user)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUser func to query user info from the database
|
||||
func GetUser() (model.User, error) {
|
||||
func (o *JsonDB) GetUser() (model.User, error) {
|
||||
user := model.User{}
|
||||
|
||||
db, err := DBConn()
|
||||
if err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
if err := db.Read("server", "users", &user); err != nil {
|
||||
return user, err
|
||||
}
|
||||
|
||||
return user, nil
|
||||
return user, o.conn.Read("server", "users", &user)
|
||||
}
|
||||
|
||||
// GetGlobalSettings func to query global settings from the database
|
||||
func GetGlobalSettings() (model.GlobalSetting, error) {
|
||||
func (o *JsonDB) GetGlobalSettings() (model.GlobalSetting, error) {
|
||||
settings := model.GlobalSetting{}
|
||||
|
||||
db, err := DBConn()
|
||||
if err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
if err := db.Read("server", "global_settings", &settings); err != nil {
|
||||
return settings, err
|
||||
}
|
||||
|
||||
return settings, nil
|
||||
return settings, o.conn.Read("server", "global_settings", &settings)
|
||||
}
|
||||
|
||||
// GetServer func to query Server setting from the database
|
||||
func GetServer() (model.Server, error) {
|
||||
func (o *JsonDB) GetServer() (model.Server, error) {
|
||||
server := model.Server{}
|
||||
|
||||
db, err := DBConn()
|
||||
if err != nil {
|
||||
return server, err
|
||||
}
|
||||
|
||||
// read server interface information
|
||||
serverInterface := model.ServerInterface{}
|
||||
if err := db.Read("server", "interfaces", &serverInterface); err != nil {
|
||||
if err := o.conn.Read("server", "interfaces", &serverInterface); err != nil {
|
||||
return server, err
|
||||
}
|
||||
|
||||
// read server key pair information
|
||||
serverKeyPair := model.ServerKeypair{}
|
||||
if err := db.Read("server", "keypair", &serverKeyPair); err != nil {
|
||||
if err := o.conn.Read("server", "keypair", &serverKeyPair); err != nil {
|
||||
return server, err
|
||||
}
|
||||
|
||||
|
@ -188,17 +135,11 @@ func GetServer() (model.Server, error) {
|
|||
return server, nil
|
||||
}
|
||||
|
||||
// GetClients to get all clients from the database
|
||||
func GetClients(hasQRCode bool) ([]model.ClientData, error) {
|
||||
func (o *JsonDB) GetClients(hasQRCode bool) ([]model.ClientData, error) {
|
||||
var clients []model.ClientData
|
||||
|
||||
db, err := DBConn()
|
||||
if err != nil {
|
||||
return clients, err
|
||||
}
|
||||
|
||||
// read all client json file in "clients" directory
|
||||
records, err := db.ReadAll("clients")
|
||||
records, err := o.conn.ReadAll("clients")
|
||||
if err != nil {
|
||||
return clients, err
|
||||
}
|
||||
|
@ -215,10 +156,10 @@ func GetClients(hasQRCode bool) ([]model.ClientData, error) {
|
|||
|
||||
// generate client qrcode image in base64
|
||||
if hasQRCode {
|
||||
server, _ := GetServer()
|
||||
globalSettings, _ := GetGlobalSettings()
|
||||
server, _ := o.GetServer()
|
||||
globalSettings, _ := o.GetGlobalSettings()
|
||||
|
||||
png, err := qrcode.Encode(BuildClientConfig(client, server, globalSettings), qrcode.Medium, 256)
|
||||
png, err := qrcode.Encode(util.BuildClientConfig(client, server, globalSettings), qrcode.Medium, 256)
|
||||
if err == nil {
|
||||
clientData.QRCode = "data:image/png;base64," + base64.StdEncoding.EncodeToString([]byte(png))
|
||||
} else {
|
||||
|
@ -234,27 +175,21 @@ func GetClients(hasQRCode bool) ([]model.ClientData, error) {
|
|||
return clients, nil
|
||||
}
|
||||
|
||||
// GetClientByID func to query a client from the database
|
||||
func GetClientByID(clientID string, hasQRCode bool) (model.ClientData, error) {
|
||||
func (o *JsonDB) GetClientByID(clientID string, hasQRCode bool) (model.ClientData, error) {
|
||||
client := model.Client{}
|
||||
clientData := model.ClientData{}
|
||||
|
||||
db, err := DBConn()
|
||||
if err != nil {
|
||||
return clientData, err
|
||||
}
|
||||
|
||||
// read client information
|
||||
if err := db.Read("clients", clientID, &client); err != nil {
|
||||
if err := o.conn.Read("clients", clientID, &client); err != nil {
|
||||
return clientData, err
|
||||
}
|
||||
|
||||
// generate client qrcode image in base64
|
||||
if hasQRCode {
|
||||
server, _ := GetServer()
|
||||
globalSettings, _ := GetGlobalSettings()
|
||||
server, _ := o.GetServer()
|
||||
globalSettings, _ := o.GetGlobalSettings()
|
||||
|
||||
png, err := qrcode.Encode(BuildClientConfig(client, server, globalSettings), qrcode.Medium, 256)
|
||||
png, err := qrcode.Encode(util.BuildClientConfig(client, server, globalSettings), qrcode.Medium, 256)
|
||||
if err == nil {
|
||||
clientData.QRCode = "data:image/png;base64," + base64.StdEncoding.EncodeToString([]byte(png))
|
||||
} else {
|
||||
|
@ -266,3 +201,23 @@ func GetClientByID(clientID string, hasQRCode bool) (model.ClientData, error) {
|
|||
|
||||
return clientData, nil
|
||||
}
|
||||
|
||||
func (o *JsonDB) SaveClient(client model.Client) error {
|
||||
return o.conn.Write("clients", client.ID, client)
|
||||
}
|
||||
|
||||
func (o *JsonDB) DeleteClient(clientID string) error {
|
||||
return o.conn.Delete("clients", clientID)
|
||||
}
|
||||
|
||||
func (o *JsonDB) SaveServerInterface(serverInterface model.ServerInterface) error {
|
||||
return o.conn.Write("server", "interfaces", serverInterface)
|
||||
}
|
||||
|
||||
func (o *JsonDB) SaveServerKeyPair(serverKeyPair model.ServerKeypair) error {
|
||||
return o.conn.Write("server", "keypair", serverKeyPair)
|
||||
}
|
||||
|
||||
func (o *JsonDB) SaveGlobalSettings(globalSettings model.GlobalSetting) error {
|
||||
return o.conn.Write("server", "global_settings", globalSettings)
|
||||
}
|
19
store/store.go
Normal file
19
store/store.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package store
|
||||
|
||||
import (
|
||||
"github.com/ngoduykhanh/wireguard-ui/model"
|
||||
)
|
||||
|
||||
type IStore interface {
|
||||
Init() error
|
||||
GetUser() (model.User, error)
|
||||
GetGlobalSettings() (model.GlobalSetting, error)
|
||||
GetServer() (model.Server, error)
|
||||
GetClients(hasQRCode bool) ([]model.ClientData, error)
|
||||
GetClientByID(clientID string, hasQRCode bool) (model.ClientData, error)
|
||||
SaveClient(client model.Client) error
|
||||
DeleteClient(clientID string) error
|
||||
SaveServerInterface(serverInterface model.ServerInterface) error
|
||||
SaveServerKeyPair(serverKeyPair model.ServerKeypair) error
|
||||
SaveGlobalSettings(globalSettings model.GlobalSetting) error
|
||||
}
|
|
@ -120,6 +120,14 @@
|
|||
</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a href="/status" class="nav-link {{if eq .baseData.Active "status" }}active{{end}}">
|
||||
<i class="nav-icon fas fa-user"></i>
|
||||
<p>
|
||||
Status
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<!-- /.sidebar-menu -->
|
||||
|
@ -446,4 +454,4 @@
|
|||
</body>
|
||||
|
||||
</html>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
57
templates/status.html
Normal file
57
templates/status.html
Normal file
|
@ -0,0 +1,57 @@
|
|||
{{define "title"}}
|
||||
Connected Peers
|
||||
{{end}}
|
||||
|
||||
{{define "top_css"}}
|
||||
{{end}}
|
||||
|
||||
{{define "username"}}
|
||||
{{ .username }}
|
||||
{{end}}
|
||||
|
||||
{{define "page_title"}}
|
||||
Connected Peers
|
||||
{{end}}
|
||||
|
||||
{{define "page_content"}}
|
||||
<section class="content">
|
||||
<div class="container-fluid">
|
||||
{{ if .error }}
|
||||
<div class="alert alert-warning" role="alert">{{.error}}</div>
|
||||
{{ end}}
|
||||
{{ range $dev := .devices }}
|
||||
<table class="table table-sm">
|
||||
<caption>List of connected peers for device with name {{ $dev.Name }} </caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Email</th>
|
||||
<th scope="col">Public Key</th>
|
||||
<th scope="col">ReceiveBytes</th>
|
||||
<th scope="col">TransmitBytes</th>
|
||||
<th scope="col">Connected (Approximation)</th>
|
||||
<th scope="col">LastHandshakeTime</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{ range $idx, $peer := $dev.Peers }}
|
||||
<tr {{ if $peer.Connected }} class="table-success" {{ end }}>
|
||||
<th scope="row">{{ $idx }}</th>
|
||||
<td>{{ $peer.Name }}</td>
|
||||
<td>{{ $peer.Email }}</td>
|
||||
<td>{{ $peer.PublicKey }}</td>
|
||||
<td>{{ $peer.ReceivedBytes }}</td>
|
||||
<td>{{ $peer.TransmitBytes }}</td>
|
||||
<td>{{ $peer.Connected }}</td>
|
||||
<td>{{ $peer.LastHandshakeTime }}</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</tbody>
|
||||
</table>
|
||||
{{ end }}
|
||||
|
||||
|
||||
</div>
|
||||
</section>
|
||||
{{end}}
|
|
@ -11,3 +11,16 @@ var (
|
|||
EmailContent string
|
||||
SessionSecret []byte
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultUsername = "admin"
|
||||
DefaultPassword = "admin"
|
||||
DefaultServerAddress = "10.252.1.0/24"
|
||||
DefaultServerPort = 51820
|
||||
DefaultDNS = "1.1.1.1"
|
||||
DefaultMTU = 1450
|
||||
DefaultPersistentKeepalive = 15
|
||||
DefaultConfigFilePath = "/etc/wireguard/wg0.conf"
|
||||
UsernameEnvVar = "WGUI_USERNAME"
|
||||
PasswordEnvVar = "WGUI_PASSWORD"
|
||||
)
|
||||
|
|
|
@ -368,3 +368,11 @@ func WriteWireGuardServerConfig(tmplBox *rice.Box, serverConfig model.Server, cl
|
|||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetCredVar reads value from environment variable or returns fallback
|
||||
func GetCredVar(key, fallback string) string {
|
||||
if value, ok := os.LookupEnv(key); ok {
|
||||
return value
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue