Merge branch 'master' of https://github.com/ngoduykhanh/wireguard-ui
This commit is contained in:
		
						commit
						fc8fa428f1
					
				| 
						 | 
					@ -35,9 +35,9 @@ jobs:
 | 
				
			||||||
      uses: managedkaos/print-env@v1.0
 | 
					      uses: managedkaos/print-env@v1.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # setup node
 | 
					    # setup node
 | 
				
			||||||
    - uses: actions/setup-node@v1
 | 
					    - uses: actions/setup-node@v2
 | 
				
			||||||
      with:
 | 
					      with:
 | 
				
			||||||
        node-version: '10.x'
 | 
					        node-version: '14'
 | 
				
			||||||
        registry-url: 'https://registry.npmjs.org'
 | 
					        registry-url: 'https://registry.npmjs.org'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # prepare assets for go rice
 | 
					    # prepare assets for go rice
 | 
				
			||||||
| 
						 | 
					@ -61,7 +61,7 @@ jobs:
 | 
				
			||||||
        github_token: ${{ secrets.GITHUB_TOKEN }}
 | 
					        github_token: ${{ secrets.GITHUB_TOKEN }}
 | 
				
			||||||
        goos: ${{ matrix.goos }}
 | 
					        goos: ${{ matrix.goos }}
 | 
				
			||||||
        goarch: ${{ matrix.goarch }}
 | 
					        goarch: ${{ matrix.goarch }}
 | 
				
			||||||
        goversion: "https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz"
 | 
					        goversion: "https://dl.google.com/go/go1.16.1.linux-amd64.tar.gz"
 | 
				
			||||||
        binary_name: "wireguard-ui"
 | 
					        binary_name: "wireguard-ui"
 | 
				
			||||||
        build_flags: -v
 | 
					        build_flags: -v
 | 
				
			||||||
        ldflags: -X "main.appVersion=${{ env.APP_VERSION }}" -X "main.buildTime=${{ env.BUILD_TIME }}" -X main.gitCommit=${{ github.sha }} -X main.gitRef=${{ github.ref }}
 | 
					        ldflags: -X "main.appVersion=${{ env.APP_VERSION }}" -X "main.buildTime=${{ env.BUILD_TIME }}" -X main.gitCommit=${{ github.sha }} -X main.gitRef=${{ github.ref }}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
# Build stage
 | 
					# 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"
 | 
					LABEL maintainer="Khanh Ngo <k@ndk.name"
 | 
				
			||||||
ARG BUILD_DEPENDENCIES="npm \
 | 
					ARG BUILD_DEPENDENCIES="npm \
 | 
				
			||||||
    yarn"
 | 
					    yarn"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,13 @@ You can take a look at this example of [docker-compose.yml](https://github.com/n
 | 
				
			||||||
```
 | 
					```
 | 
				
			||||||
docker-compose up
 | 
					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
 | 
					### Environment Variables
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,20 @@
 | 
				
			||||||
version: '3'
 | 
					version: "3"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
services:
 | 
					services:
 | 
				
			||||||
  wg:
 | 
					  wg:
 | 
				
			||||||
    build: .
 | 
					    build: .
 | 
				
			||||||
    #image: ngoduykhanh/wireguard-ui:latest
 | 
					    #image: ngoduykhanh/wireguard-ui:latest
 | 
				
			||||||
    container_name: wgui
 | 
					    container_name: wgui
 | 
				
			||||||
 | 
					    cap_add:
 | 
				
			||||||
 | 
					      - NET_ADMIN
 | 
				
			||||||
 | 
					    network_mode: host
 | 
				
			||||||
    environment:
 | 
					    environment:
 | 
				
			||||||
      - SENDGRID_API_KEY
 | 
					      - SENDGRID_API_KEY
 | 
				
			||||||
      - EMAIL_FROM
 | 
					      - EMAIL_FROM
 | 
				
			||||||
      - EMAIL_FROM_NAME
 | 
					      - EMAIL_FROM_NAME
 | 
				
			||||||
      - SESSION_SECRET
 | 
					      - SESSION_SECRET
 | 
				
			||||||
    ports:
 | 
					      - WGUI_USERNAME=alpha
 | 
				
			||||||
      - 5000:5000
 | 
					      - WGUI_PASSWORD=this-unusual-password
 | 
				
			||||||
    logging:
 | 
					    logging:
 | 
				
			||||||
      driver: json-file
 | 
					      driver: json-file
 | 
				
			||||||
      options:
 | 
					      options:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										3
									
								
								go.mod
								
								
								
								
							| 
						 | 
					@ -17,7 +17,8 @@ require (
 | 
				
			||||||
	github.com/sendgrid/rest v2.6.4+incompatible // indirect
 | 
						github.com/sendgrid/rest v2.6.4+incompatible // indirect
 | 
				
			||||||
	github.com/sendgrid/sendgrid-go v3.10.0+incompatible
 | 
						github.com/sendgrid/sendgrid-go v3.10.0+incompatible
 | 
				
			||||||
	github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086
 | 
						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/assert.v1 v1.2.1 // indirect
 | 
				
			||||||
	gopkg.in/go-playground/validator.v9 v9.31.0
 | 
						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.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.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.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.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/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 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
 | 
				
			||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
 | 
					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 h1:EFT6MH3igZK/dIVqgGbTqWVvkZ7wJ5iGN03SVtvvdd8=
 | 
				
			||||||
github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25/go.mod h1:sWkGw/wsaHtRsT9zGQ/WyJCotGWG/Anow/9hsAcBWRw=
 | 
					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/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-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-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.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
				
			||||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
					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=
 | 
					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 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
 | 
				
			||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 | 
					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/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/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 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.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.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/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-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
				
			||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/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-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-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-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-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-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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/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-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-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-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-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-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-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/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-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-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-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-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-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-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.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.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-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/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-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 h1:vcswa5Q6f+sylDfjqyrVNNrjsFUUbPsgAQTBCAg/Qf8=
 | 
				
			||||||
golang.zx2c4.com/wireguard v0.0.20200121/go.mod h1:P2HsVp8SKwZEufsnezXZA4GRX/T49/HlU7DGuelXsU4=
 | 
					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-20210803171230-4253848d036c h1:ADNrRDI5NR23/TUCnEmlLZLt4u9DnZ2nwRkPrAcFvto=
 | 
				
			||||||
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/go.mod h1:+1XihzyZUBJcSc5WO9SwNA7v26puQwOEDwanaxfNXPQ=
 | 
				
			||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 | 
					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 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 | 
				
			||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
					gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,20 +5,23 @@ import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"sort"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rice "github.com/GeertJohan/go.rice"
 | 
						rice "github.com/GeertJohan/go.rice"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/gorilla/sessions"
 | 
						"github.com/gorilla/sessions"
 | 
				
			||||||
	"github.com/labstack/echo-contrib/session"
 | 
						"github.com/labstack/echo-contrib/session"
 | 
				
			||||||
	"github.com/labstack/echo/v4"
 | 
						"github.com/labstack/echo/v4"
 | 
				
			||||||
	"github.com/labstack/gommon/log"
 | 
						"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/emailer"
 | 
				
			||||||
	"github.com/ngoduykhanh/wireguard-ui/model"
 | 
						"github.com/ngoduykhanh/wireguard-ui/model"
 | 
				
			||||||
 | 
						"github.com/ngoduykhanh/wireguard-ui/store"
 | 
				
			||||||
	"github.com/ngoduykhanh/wireguard-ui/util"
 | 
						"github.com/ngoduykhanh/wireguard-ui/util"
 | 
				
			||||||
	"github.com/rs/xid"
 | 
					 | 
				
			||||||
	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoginPage handler
 | 
					// LoginPage handler
 | 
				
			||||||
| 
						 | 
					@ -29,12 +32,12 @@ func LoginPage() echo.HandlerFunc {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Login for signing in handler
 | 
					// Login for signing in handler
 | 
				
			||||||
func Login() echo.HandlerFunc {
 | 
					func Login(db store.IStore) echo.HandlerFunc {
 | 
				
			||||||
	return func(c echo.Context) error {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
		user := new(model.User)
 | 
							user := new(model.User)
 | 
				
			||||||
		c.Bind(user)
 | 
							c.Bind(user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		dbuser, err := util.GetUser()
 | 
							dbuser, err := db.GetUser()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot query user from DB"})
 | 
								return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot query user from DB"})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -77,10 +80,10 @@ func Logout() echo.HandlerFunc {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WireGuardClients handler
 | 
					// WireGuardClients handler
 | 
				
			||||||
func WireGuardClients() echo.HandlerFunc {
 | 
					func WireGuardClients(db store.IStore) echo.HandlerFunc {
 | 
				
			||||||
	return func(c echo.Context) error {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		clientDataList, err := util.GetClients(true)
 | 
							clientDataList, err := db.GetClients(true)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{
 | 
								return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{
 | 
				
			||||||
				false, fmt.Sprintf("Cannot get client list: %v", err),
 | 
									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
 | 
					// 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 {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		clientDataList, err := util.GetClients(true)
 | 
							clientDataList, err := db.GetClients(true)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{
 | 
								return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{
 | 
				
			||||||
				false, fmt.Sprintf("Cannot get client list: %v", err),
 | 
									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
 | 
					// 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 {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		clientID := c.Param("id")
 | 
							clientID := c.Param("id")
 | 
				
			||||||
		clientData, err := util.GetClientByID(clientID, true)
 | 
							clientData, err := db.GetClientByID(clientID, true)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
 | 
								return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -124,27 +127,22 @@ func GetClient() echo.HandlerFunc {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewClient handler
 | 
					// NewClient handler
 | 
				
			||||||
func NewClient() echo.HandlerFunc {
 | 
					func NewClient(db store.IStore) echo.HandlerFunc {
 | 
				
			||||||
	return func(c echo.Context) error {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		client := new(model.Client)
 | 
							var client model.Client
 | 
				
			||||||
		c.Bind(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"})
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// read server information
 | 
							// read server information
 | 
				
			||||||
		serverInterface := model.ServerInterface{}
 | 
							server, err := db.GetServer()
 | 
				
			||||||
		if err := db.Read("server", "interfaces", &serverInterface); err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Error("Cannot fetch server interface config from database: ", err)
 | 
								log.Error("Cannot fetch server from database: ", err)
 | 
				
			||||||
 | 
								return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, err.Error()})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// validate the input Allocation IPs
 | 
							// validate the input Allocation IPs
 | 
				
			||||||
		allocatedIPs, err := util.GetAllocatedIPs("")
 | 
							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 {
 | 
							if !check {
 | 
				
			||||||
			return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, fmt.Sprintf("%s", err)})
 | 
								return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, fmt.Sprintf("%s", err)})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -181,7 +179,11 @@ func NewClient() echo.HandlerFunc {
 | 
				
			||||||
		client.UpdatedAt = client.CreatedAt
 | 
							client.UpdatedAt = client.CreatedAt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// write client to the database
 | 
							// 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)
 | 
							log.Infof("Created wireguard client: %v", client)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return c.JSON(http.StatusOK, client)
 | 
							return c.JSON(http.StatusOK, client)
 | 
				
			||||||
| 
						 | 
					@ -189,7 +191,7 @@ func NewClient() echo.HandlerFunc {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// EmailClient handler to sent the configuration via email
 | 
					// 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 {
 | 
						type clientIdEmailPayload struct {
 | 
				
			||||||
		ID    string `json:"id"`
 | 
							ID    string `json:"id"`
 | 
				
			||||||
		Email string `json:"email"`
 | 
							Email string `json:"email"`
 | 
				
			||||||
| 
						 | 
					@ -200,15 +202,15 @@ func EmailClient(mailer emailer.Emailer, emailSubject, emailContent string) echo
 | 
				
			||||||
		c.Bind(&payload)
 | 
							c.Bind(&payload)
 | 
				
			||||||
		// TODO validate email
 | 
							// TODO validate email
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		clientData, err := util.GetClientByID(payload.ID, true)
 | 
							clientData, err := db.GetClientByID(payload.ID, true)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Errorf("Cannot generate client id %s config file for downloading: %v", payload.ID, err)
 | 
								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"})
 | 
								return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// build config
 | 
							// build config
 | 
				
			||||||
		server, _ := util.GetServer()
 | 
							server, _ := db.GetServer()
 | 
				
			||||||
		globalSettings, _ := util.GetGlobalSettings()
 | 
							globalSettings, _ := db.GetGlobalSettings()
 | 
				
			||||||
		config := util.BuildClientConfig(*clientData.Client, server, globalSettings)
 | 
							config := util.BuildClientConfig(*clientData.Client, server, globalSettings)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		cfg_att := emailer.Attachment{"wg0.conf", []byte(config)}
 | 
							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
 | 
					// UpdateClient handler to update client information
 | 
				
			||||||
func UpdateClient() echo.HandlerFunc {
 | 
					func UpdateClient(db store.IStore) echo.HandlerFunc {
 | 
				
			||||||
	return func(c echo.Context) error {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_client := new(model.Client)
 | 
							var _client model.Client
 | 
				
			||||||
		c.Bind(_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"})
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// validate client existence
 | 
							// validate client existence
 | 
				
			||||||
		client := model.Client{}
 | 
							clientData, err := db.GetClientByID(_client.ID, false)
 | 
				
			||||||
		if err := db.Read("clients", _client.ID, &client); err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
 | 
								return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// read server information
 | 
							server, err := db.GetServer()
 | 
				
			||||||
		serverInterface := model.ServerInterface{}
 | 
							if err != nil {
 | 
				
			||||||
		if err := db.Read("server", "interfaces", &serverInterface); err != nil {
 | 
					 | 
				
			||||||
			log.Error("Cannot fetch server interface config from database: ", err)
 | 
					 | 
				
			||||||
			return c.JSON(http.StatusBadRequest, jsonHTTPResponse{
 | 
								return c.JSON(http.StatusBadRequest, jsonHTTPResponse{
 | 
				
			||||||
				false, fmt.Sprintf("Cannot fetch server config: %s", err),
 | 
									false, fmt.Sprintf("Cannot fetch server config: %s", err),
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							client := *clientData.Client
 | 
				
			||||||
		// validate the input Allocation IPs
 | 
							// validate the input Allocation IPs
 | 
				
			||||||
		allocatedIPs, err := util.GetAllocatedIPs(client.ID)
 | 
							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 {
 | 
							if !check {
 | 
				
			||||||
			return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, fmt.Sprintf("%s", err)})
 | 
								return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, fmt.Sprintf("%s", err)})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -283,7 +277,9 @@ func UpdateClient() echo.HandlerFunc {
 | 
				
			||||||
		client.UpdatedAt = time.Now().UTC()
 | 
							client.UpdatedAt = time.Now().UTC()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// write to the database
 | 
							// 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)
 | 
							log.Infof("Updated client information successfully => %v", client)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Updated client successfully"})
 | 
							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
 | 
					// SetClientStatus handler to enable / disable a client
 | 
				
			||||||
func SetClientStatus() echo.HandlerFunc {
 | 
					func SetClientStatus(db store.IStore) echo.HandlerFunc {
 | 
				
			||||||
	return func(c echo.Context) error {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		data := make(map[string]interface{})
 | 
							data := make(map[string]interface{})
 | 
				
			||||||
| 
						 | 
					@ -304,19 +300,17 @@ func SetClientStatus() echo.HandlerFunc {
 | 
				
			||||||
		clientID := data["id"].(string)
 | 
							clientID := data["id"].(string)
 | 
				
			||||||
		status := data["status"].(bool)
 | 
							status := data["status"].(bool)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		db, err := util.DBConn()
 | 
							clientdata, err := db.GetClientByID(clientID, false)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Error("Cannot initialize database: ", err)
 | 
								return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, err.Error()})
 | 
				
			||||||
			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot access database"})
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		client := model.Client{}
 | 
							client := *clientdata.Client
 | 
				
			||||||
		if err := db.Read("clients", clientID, &client); err != nil {
 | 
					 | 
				
			||||||
			log.Error("Cannot get client from database: ", err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		client.Enabled = status
 | 
							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)
 | 
							log.Infof("Changed client %s enabled status to %v", client.ID, status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Changed client status successfully"})
 | 
							return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Changed client status successfully"})
 | 
				
			||||||
| 
						 | 
					@ -324,22 +318,28 @@ func SetClientStatus() echo.HandlerFunc {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DownloadClient handler
 | 
					// DownloadClient handler
 | 
				
			||||||
func DownloadClient() echo.HandlerFunc {
 | 
					func DownloadClient(db store.IStore) echo.HandlerFunc {
 | 
				
			||||||
	return func(c echo.Context) error {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
		clientID := c.QueryParam("clientid")
 | 
							clientID := c.QueryParam("clientid")
 | 
				
			||||||
		if clientID == "" {
 | 
							if clientID == "" {
 | 
				
			||||||
			return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Missing clientid parameter"})
 | 
								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 {
 | 
							if err != nil {
 | 
				
			||||||
			log.Errorf("Cannot generate client id %s config file for downloading: %v", clientID, err)
 | 
								log.Errorf("Cannot generate client id %s config file for downloading: %v", clientID, err)
 | 
				
			||||||
			return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
 | 
								return c.JSON(http.StatusNotFound, jsonHTTPResponse{false, "Client not found"})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// build config
 | 
							// build config
 | 
				
			||||||
		server, _ := util.GetServer()
 | 
							server, err := db.GetServer()
 | 
				
			||||||
		globalSettings, _ := util.GetGlobalSettings()
 | 
							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)
 | 
							config := util.BuildClientConfig(*clientData.Client, server, globalSettings)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// create io reader from string
 | 
							// create io reader from string
 | 
				
			||||||
| 
						 | 
					@ -352,20 +352,15 @@ func DownloadClient() echo.HandlerFunc {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RemoveClient handler
 | 
					// RemoveClient handler
 | 
				
			||||||
func RemoveClient() echo.HandlerFunc {
 | 
					func RemoveClient(db store.IStore) echo.HandlerFunc {
 | 
				
			||||||
	return func(c echo.Context) error {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		client := new(model.Client)
 | 
							client := new(model.Client)
 | 
				
			||||||
		c.Bind(client)
 | 
							c.Bind(client)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// delete client from database
 | 
							// 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)
 | 
								log.Error("Cannot delete wireguard client: ", err)
 | 
				
			||||||
			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot delete client from database"})
 | 
								return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot delete client from database"})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -376,10 +371,10 @@ func RemoveClient() echo.HandlerFunc {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WireGuardServer handler
 | 
					// WireGuardServer handler
 | 
				
			||||||
func WireGuardServer() echo.HandlerFunc {
 | 
					func WireGuardServer(db store.IStore) echo.HandlerFunc {
 | 
				
			||||||
	return func(c echo.Context) error {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		server, err := util.GetServer()
 | 
							server, err := db.GetServer()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Error("Cannot get server config: ", err)
 | 
								log.Error("Cannot get server config: ", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -393,11 +388,11 @@ func WireGuardServer() echo.HandlerFunc {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WireGuardServerInterfaces handler
 | 
					// WireGuardServerInterfaces handler
 | 
				
			||||||
func WireGuardServerInterfaces() echo.HandlerFunc {
 | 
					func WireGuardServerInterfaces(db store.IStore) echo.HandlerFunc {
 | 
				
			||||||
	return func(c echo.Context) error {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		serverInterface := new(model.ServerInterface)
 | 
							var serverInterface model.ServerInterface
 | 
				
			||||||
		c.Bind(serverInterface)
 | 
							c.Bind(&serverInterface)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// validate the input addresses
 | 
							// validate the input addresses
 | 
				
			||||||
		if util.ValidateServerAddresses(serverInterface.Addresses) == false {
 | 
							if util.ValidateServerAddresses(serverInterface.Addresses) == false {
 | 
				
			||||||
| 
						 | 
					@ -408,13 +403,10 @@ func WireGuardServerInterfaces() echo.HandlerFunc {
 | 
				
			||||||
		serverInterface.UpdatedAt = time.Now().UTC()
 | 
							serverInterface.UpdatedAt = time.Now().UTC()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// write config to the database
 | 
							// 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)
 | 
							log.Infof("Updated wireguard server interfaces settings: %v", serverInterface)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Updated interface addresses successfully"})
 | 
							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
 | 
					// 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 {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// gen Wireguard key pair
 | 
							// gen Wireguard key pair
 | 
				
			||||||
| 
						 | 
					@ -432,19 +424,14 @@ func WireGuardServerKeyPair() echo.HandlerFunc {
 | 
				
			||||||
			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate Wireguard key pair"})
 | 
								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.PrivateKey = key.String()
 | 
				
			||||||
		serverKeyPair.PublicKey = key.PublicKey().String()
 | 
							serverKeyPair.PublicKey = key.PublicKey().String()
 | 
				
			||||||
		serverKeyPair.UpdatedAt = time.Now().UTC()
 | 
							serverKeyPair.UpdatedAt = time.Now().UTC()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// write config to the database
 | 
							if err := db.SaveServerKeyPair(serverKeyPair); err != nil {
 | 
				
			||||||
		db, err := util.DBConn()
 | 
								return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate Wireguard key pair"})
 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			log.Error("Cannot initialize database: ", err)
 | 
					 | 
				
			||||||
			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot access database"})
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		db.Write("server", "keypair", serverKeyPair)
 | 
					 | 
				
			||||||
		log.Infof("Updated wireguard server interfaces settings: %v", serverKeyPair)
 | 
							log.Infof("Updated wireguard server interfaces settings: %v", serverKeyPair)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return c.JSON(http.StatusOK, serverKeyPair)
 | 
							return c.JSON(http.StatusOK, serverKeyPair)
 | 
				
			||||||
| 
						 | 
					@ -452,10 +439,10 @@ func WireGuardServerKeyPair() echo.HandlerFunc {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GlobalSettings handler
 | 
					// GlobalSettings handler
 | 
				
			||||||
func GlobalSettings() echo.HandlerFunc {
 | 
					func GlobalSettings(db store.IStore) echo.HandlerFunc {
 | 
				
			||||||
	return func(c echo.Context) error {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		globalSettings, err := util.GetGlobalSettings()
 | 
							globalSettings, err := db.GetGlobalSettings()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Error("Cannot get global settings: ", err)
 | 
								log.Error("Cannot get global settings: ", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -467,12 +454,99 @@ func GlobalSettings() echo.HandlerFunc {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GlobalSettingSubmit handler to update the global settings
 | 
					// Status handler
 | 
				
			||||||
func GlobalSettingSubmit() echo.HandlerFunc {
 | 
					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 {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		globalSettings := new(model.GlobalSetting)
 | 
							wgclient, err := wgctrl.New()
 | 
				
			||||||
		c.Bind(globalSettings)
 | 
							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
 | 
							// validate the input dns server list
 | 
				
			||||||
		if util.ValidateIPAddressList(globalSettings.DNSServers) == false {
 | 
							if util.ValidateIPAddressList(globalSettings.DNSServers) == false {
 | 
				
			||||||
| 
						 | 
					@ -483,13 +557,10 @@ func GlobalSettingSubmit() echo.HandlerFunc {
 | 
				
			||||||
		globalSettings.UpdatedAt = time.Now().UTC()
 | 
							globalSettings.UpdatedAt = time.Now().UTC()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// write config to the database
 | 
							// write config to the database
 | 
				
			||||||
		db, err := util.DBConn()
 | 
							if err := db.SaveGlobalSettings(globalSettings); err != nil {
 | 
				
			||||||
		if err != nil {
 | 
								return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot generate Wireguard key pair"})
 | 
				
			||||||
			log.Error("Cannot initialize database: ", err)
 | 
					 | 
				
			||||||
			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot access database"})
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		db.Write("server", "global_settings", globalSettings)
 | 
					 | 
				
			||||||
		log.Infof("Updated global settings: %v", globalSettings)
 | 
							log.Infof("Updated global settings: %v", globalSettings)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Updated global settings successfully"})
 | 
							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
 | 
					// 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 {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		server, err := util.GetServer()
 | 
							server, err := db.GetServer()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Error("Cannot fetch server config from database: ", err)
 | 
								log.Error("Cannot fetch server config from database: ", err)
 | 
				
			||||||
 | 
								return c.JSON(http.StatusBadRequest, jsonHTTPResponse{false, err.Error()})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// return the list of suggestedIPs
 | 
							// return the list of suggestedIPs
 | 
				
			||||||
| 
						 | 
					@ -557,22 +629,22 @@ func SuggestIPAllocation() echo.HandlerFunc {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ApplyServerConfig handler to write config file and restart Wireguard server
 | 
					// 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 {
 | 
						return func(c echo.Context) error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		server, err := util.GetServer()
 | 
							server, err := db.GetServer()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Error("Cannot get server config: ", err)
 | 
								log.Error("Cannot get server config: ", err)
 | 
				
			||||||
			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot get server config"})
 | 
								return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot get server config"})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		clients, err := util.GetClients(false)
 | 
							clients, err := db.GetClients(false)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Error("Cannot get client config: ", err)
 | 
								log.Error("Cannot get client config: ", err)
 | 
				
			||||||
			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot get client config"})
 | 
								return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot get client config"})
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		settings, err := util.GetGlobalSettings()
 | 
							settings, err := db.GetGlobalSettings()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Error("Cannot get global settings: ", err)
 | 
								log.Error("Cannot get global settings: ", err)
 | 
				
			||||||
			return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot get global settings"})
 | 
								return c.JSON(http.StatusInternalServerError, jsonHTTPResponse{false, "Cannot get global settings"})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										48
									
								
								main.go
								
								
								
								
							
							
						
						
									
										48
									
								
								main.go
								
								
								
								
							| 
						 | 
					@ -12,6 +12,7 @@ import (
 | 
				
			||||||
	"github.com/ngoduykhanh/wireguard-ui/emailer"
 | 
						"github.com/ngoduykhanh/wireguard-ui/emailer"
 | 
				
			||||||
	"github.com/ngoduykhanh/wireguard-ui/handler"
 | 
						"github.com/ngoduykhanh/wireguard-ui/handler"
 | 
				
			||||||
	"github.com/ngoduykhanh/wireguard-ui/router"
 | 
						"github.com/ngoduykhanh/wireguard-ui/router"
 | 
				
			||||||
 | 
						"github.com/ngoduykhanh/wireguard-ui/store/jsondb"
 | 
				
			||||||
	"github.com/ngoduykhanh/wireguard-ui/util"
 | 
						"github.com/ngoduykhanh/wireguard-ui/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,14 +73,16 @@ func init() {
 | 
				
			||||||
	fmt.Println("Email from name\t:", util.EmailFromName)
 | 
						fmt.Println("Email from name\t:", util.EmailFromName)
 | 
				
			||||||
	//fmt.Println("Session secret\t:", util.SessionSecret)
 | 
						//fmt.Println("Session secret\t:", util.SessionSecret)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// initialize DB
 | 
					 | 
				
			||||||
	err := util.InitDB()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		fmt.Print("Cannot init database: ", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
 | 
						db, err := jsondb.New("./db")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := db.Init(); err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	// set app extra data
 | 
						// set app extra data
 | 
				
			||||||
	extraData := make(map[string]string)
 | 
						extraData := make(map[string]string)
 | 
				
			||||||
	extraData["appVersion"] = appVersion
 | 
						extraData["appVersion"] = appVersion
 | 
				
			||||||
| 
						 | 
					@ -93,32 +96,33 @@ func main() {
 | 
				
			||||||
	// register routes
 | 
						// register routes
 | 
				
			||||||
	app := router.New(tmplBox, extraData, util.SessionSecret)
 | 
						app := router.New(tmplBox, extraData, util.SessionSecret)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	app.GET("/", handler.WireGuardClients(), handler.ValidSession)
 | 
						app.GET("/", handler.WireGuardClients(db), handler.ValidSession)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !util.DisableLogin {
 | 
						if !util.DisableLogin {
 | 
				
			||||||
		app.GET("/login", handler.LoginPage())
 | 
							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)
 | 
						sendmail := emailer.NewSendgridApiMail(util.SendgridApiKey, util.EmailFromName, util.EmailFrom)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	app.GET("/logout", handler.Logout(), handler.ValidSession)
 | 
						app.GET("/logout", handler.Logout(), handler.ValidSession)
 | 
				
			||||||
	app.POST("/new-client", handler.NewClient(), handler.ValidSession)
 | 
						app.POST("/new-client", handler.NewClient(db), handler.ValidSession)
 | 
				
			||||||
	app.POST("/update-client", handler.UpdateClient(), handler.ValidSession)
 | 
						app.POST("/update-client", handler.UpdateClient(db), handler.ValidSession)
 | 
				
			||||||
	app.POST("/email-client", handler.EmailClient(sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession)
 | 
						app.POST("/email-client", handler.EmailClient(db, sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession)
 | 
				
			||||||
	app.POST("/client/set-status", handler.SetClientStatus(), handler.ValidSession)
 | 
						app.POST("/client/set-status", handler.SetClientStatus(db), handler.ValidSession)
 | 
				
			||||||
	app.POST("/remove-client", handler.RemoveClient(), handler.ValidSession)
 | 
						app.POST("/remove-client", handler.RemoveClient(db), handler.ValidSession)
 | 
				
			||||||
	app.GET("/download", handler.DownloadClient(), handler.ValidSession)
 | 
						app.GET("/download", handler.DownloadClient(db), handler.ValidSession)
 | 
				
			||||||
	app.GET("/wg-server", handler.WireGuardServer(), handler.ValidSession)
 | 
						app.GET("/wg-server", handler.WireGuardServer(db), handler.ValidSession)
 | 
				
			||||||
	app.POST("wg-server/interfaces", handler.WireGuardServerInterfaces(), handler.ValidSession)
 | 
						app.POST("wg-server/interfaces", handler.WireGuardServerInterfaces(db), handler.ValidSession)
 | 
				
			||||||
	app.POST("wg-server/keypair", handler.WireGuardServerKeyPair(), handler.ValidSession)
 | 
						app.POST("wg-server/keypair", handler.WireGuardServerKeyPair(db), handler.ValidSession)
 | 
				
			||||||
	app.GET("/global-settings", handler.GlobalSettings(), handler.ValidSession)
 | 
						app.GET("/global-settings", handler.GlobalSettings(db), handler.ValidSession)
 | 
				
			||||||
	app.POST("/global-settings", handler.GlobalSettingSubmit(), handler.ValidSession)
 | 
						app.POST("/global-settings", handler.GlobalSettingSubmit(db), handler.ValidSession)
 | 
				
			||||||
	app.GET("/api/clients", handler.GetClients(), handler.ValidSession)
 | 
						app.GET("/status", handler.Status(db), handler.ValidSession)
 | 
				
			||||||
	app.GET("/api/client/:id", handler.GetClient(), 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/machine-ips", handler.MachineIPAddresses(), handler.ValidSession)
 | 
				
			||||||
	app.GET("/api/suggest-client-ips", handler.SuggestIPAllocation(), handler.ValidSession)
 | 
						app.GET("/api/suggest-client-ips", handler.SuggestIPAllocation(db), handler.ValidSession)
 | 
				
			||||||
	app.GET("/api/apply-wg-config", handler.ApplyServerConfig(tmplBox), handler.ValidSession)
 | 
						app.GET("/api/apply-wg-config", handler.ApplyServerConfig(db, tmplBox), handler.ValidSession)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// servers other static files
 | 
						// servers other static files
 | 
				
			||||||
	app.GET("/static/*", echo.WrapHandler(http.StripPrefix("/static/", assetHandler)))
 | 
						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)
 | 
							log.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tmplStatusString, err := tmplBox.String("status.html")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// create template list
 | 
						// create template list
 | 
				
			||||||
	templates := make(map[string]*template.Template)
 | 
						templates := make(map[string]*template.Template)
 | 
				
			||||||
	templates["login.html"] = template.Must(template.New("login").Parse(tmplLoginString))
 | 
						templates["login.html"] = template.Must(template.New("login").Parse(tmplLoginString))
 | 
				
			||||||
	templates["clients.html"] = template.Must(template.New("clients").Parse(tmplBaseString + tmplClientsString))
 | 
						templates["clients.html"] = template.Must(template.New("clients").Parse(tmplBaseString + tmplClientsString))
 | 
				
			||||||
	templates["server.html"] = template.Must(template.New("server").Parse(tmplBaseString + tmplServerString))
 | 
						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["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.Logger.SetLevel(log.DEBUG)
 | 
				
			||||||
	e.Pre(middleware.RemoveTrailingSlash())
 | 
						e.Pre(middleware.RemoveTrailingSlash())
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
package util
 | 
					package jsondb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/base64"
 | 
						"encoding/base64"
 | 
				
			||||||
| 
						 | 
					@ -8,40 +8,40 @@ import (
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/ngoduykhanh/wireguard-ui/model"
 | 
					 | 
				
			||||||
	"github.com/sdomino/scribble"
 | 
						"github.com/sdomino/scribble"
 | 
				
			||||||
	"github.com/skip2/go-qrcode"
 | 
						"github.com/skip2/go-qrcode"
 | 
				
			||||||
	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 | 
						"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/ngoduykhanh/wireguard-ui/model"
 | 
				
			||||||
 | 
						"github.com/ngoduykhanh/wireguard-ui/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const dbPath = "./db"
 | 
					type JsonDB struct {
 | 
				
			||||||
const defaultUsername = "admin"
 | 
						conn   *scribble.Driver
 | 
				
			||||||
const defaultPassword = "admin"
 | 
						dbPath string
 | 
				
			||||||
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"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// DBConn to initialize the database connection
 | 
					// New returns a new pointer JsonDB
 | 
				
			||||||
func DBConn() (*scribble.Driver, error) {
 | 
					func New(dbPath string) (*JsonDB, error) {
 | 
				
			||||||
	db, err := scribble.New(dbPath, nil)
 | 
						conn, err := scribble.New(dbPath, nil)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return db, nil
 | 
						ans := JsonDB{
 | 
				
			||||||
 | 
							conn:   conn,
 | 
				
			||||||
 | 
							dbPath: dbPath,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &ans, nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// InitDB to create the default database
 | 
					func (o *JsonDB) Init() error {
 | 
				
			||||||
func InitDB() error {
 | 
						var clientPath string = path.Join(o.dbPath, "clients")
 | 
				
			||||||
	var clientPath string = path.Join(dbPath, "clients")
 | 
						var serverPath string = path.Join(o.dbPath, "server")
 | 
				
			||||||
	var serverPath string = path.Join(dbPath, "server")
 | 
					 | 
				
			||||||
	var serverInterfacePath string = path.Join(serverPath, "interfaces.json")
 | 
						var serverInterfacePath string = path.Join(serverPath, "interfaces.json")
 | 
				
			||||||
	var serverKeyPairPath string = path.Join(serverPath, "keypair.json")
 | 
						var serverKeyPairPath string = path.Join(serverPath, "keypair.json")
 | 
				
			||||||
	var globalSettingPath string = path.Join(serverPath, "global_settings.json")
 | 
						var globalSettingPath string = path.Join(serverPath, "global_settings.json")
 | 
				
			||||||
	var userPath string = path.Join(serverPath, "users.json")
 | 
						var userPath string = path.Join(serverPath, "users.json")
 | 
				
			||||||
 | 
					 | 
				
			||||||
	// create directories if they do not exist
 | 
						// create directories if they do not exist
 | 
				
			||||||
	if _, err := os.Stat(clientPath); os.IsNotExist(err) {
 | 
						if _, err := os.Stat(clientPath); os.IsNotExist(err) {
 | 
				
			||||||
		os.MkdirAll(clientPath, os.ModePerm)
 | 
							os.MkdirAll(clientPath, os.ModePerm)
 | 
				
			||||||
| 
						 | 
					@ -52,24 +52,15 @@ func InitDB() error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// server's interface
 | 
						// server's interface
 | 
				
			||||||
	if _, err := os.Stat(serverInterfacePath); os.IsNotExist(err) {
 | 
						if _, err := os.Stat(serverInterfacePath); os.IsNotExist(err) {
 | 
				
			||||||
		db, err := DBConn()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		serverInterface := new(model.ServerInterface)
 | 
							serverInterface := new(model.ServerInterface)
 | 
				
			||||||
		serverInterface.Addresses = []string{defaultServerAddress}
 | 
							serverInterface.Addresses = []string{util.DefaultServerAddress}
 | 
				
			||||||
		serverInterface.ListenPort = defaultServerPort
 | 
							serverInterface.ListenPort = util.DefaultServerPort
 | 
				
			||||||
		serverInterface.UpdatedAt = time.Now().UTC()
 | 
							serverInterface.UpdatedAt = time.Now().UTC()
 | 
				
			||||||
		db.Write("server", "interfaces", serverInterface)
 | 
							o.conn.Write("server", "interfaces", serverInterface)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// server's key pair
 | 
						// server's key pair
 | 
				
			||||||
	if _, err := os.Stat(serverKeyPairPath); os.IsNotExist(err) {
 | 
						if _, err := os.Stat(serverKeyPairPath); os.IsNotExist(err) {
 | 
				
			||||||
		db, err := DBConn()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		key, err := wgtypes.GeneratePrivateKey()
 | 
							key, err := wgtypes.GeneratePrivateKey()
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -79,97 +70,62 @@ func InitDB() error {
 | 
				
			||||||
		serverKeyPair.PrivateKey = key.String()
 | 
							serverKeyPair.PrivateKey = key.String()
 | 
				
			||||||
		serverKeyPair.PublicKey = key.PublicKey().String()
 | 
							serverKeyPair.PublicKey = key.PublicKey().String()
 | 
				
			||||||
		serverKeyPair.UpdatedAt = time.Now().UTC()
 | 
							serverKeyPair.UpdatedAt = time.Now().UTC()
 | 
				
			||||||
		db.Write("server", "keypair", serverKeyPair)
 | 
							o.conn.Write("server", "keypair", serverKeyPair)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// global settings
 | 
						// global settings
 | 
				
			||||||
	if _, err := os.Stat(globalSettingPath); os.IsNotExist(err) {
 | 
						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 {
 | 
							if err != nil {
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		globalSetting := new(model.GlobalSetting)
 | 
							globalSetting := new(model.GlobalSetting)
 | 
				
			||||||
		globalSetting.EndpointAddress = publicInterface.IPAddress
 | 
							globalSetting.EndpointAddress = publicInterface.IPAddress
 | 
				
			||||||
		globalSetting.DNSServers = []string{defaultDNS}
 | 
							globalSetting.DNSServers = []string{util.DefaultDNS}
 | 
				
			||||||
		globalSetting.MTU = defaultMTU
 | 
							globalSetting.MTU = util.DefaultMTU
 | 
				
			||||||
		globalSetting.PersistentKeepalive = defaultPersistentKeepalive
 | 
							globalSetting.PersistentKeepalive = util.DefaultPersistentKeepalive
 | 
				
			||||||
		globalSetting.ConfigFilePath = defaultConfigFilePath
 | 
							globalSetting.ConfigFilePath = util.DefaultConfigFilePath
 | 
				
			||||||
		globalSetting.UpdatedAt = time.Now().UTC()
 | 
							globalSetting.UpdatedAt = time.Now().UTC()
 | 
				
			||||||
		db.Write("server", "global_settings", globalSetting)
 | 
							o.conn.Write("server", "global_settings", globalSetting)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// user info
 | 
						// user info
 | 
				
			||||||
	if _, err := os.Stat(userPath); os.IsNotExist(err) {
 | 
						if _, err := os.Stat(userPath); os.IsNotExist(err) {
 | 
				
			||||||
		db, err := DBConn()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		user := new(model.User)
 | 
							user := new(model.User)
 | 
				
			||||||
		user.Username = defaultUsername
 | 
							user.Username = util.GetCredVar(util.UsernameEnvVar, util.DefaultUsername)
 | 
				
			||||||
		user.Password = defaultPassword
 | 
							user.Password = util.GetCredVar(util.PasswordEnvVar, util.DefaultPassword)
 | 
				
			||||||
		db.Write("server", "users", user)
 | 
							o.conn.Write("server", "users", user)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetUser func to query user info from the database
 | 
					// GetUser func to query user info from the database
 | 
				
			||||||
func GetUser() (model.User, error) {
 | 
					func (o *JsonDB) GetUser() (model.User, error) {
 | 
				
			||||||
	user := model.User{}
 | 
						user := model.User{}
 | 
				
			||||||
 | 
						return user, o.conn.Read("server", "users", &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
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetGlobalSettings func to query global settings from the database
 | 
					// GetGlobalSettings func to query global settings from the database
 | 
				
			||||||
func GetGlobalSettings() (model.GlobalSetting, error) {
 | 
					func (o *JsonDB) GetGlobalSettings() (model.GlobalSetting, error) {
 | 
				
			||||||
	settings := model.GlobalSetting{}
 | 
						settings := model.GlobalSetting{}
 | 
				
			||||||
 | 
						return settings, o.conn.Read("server", "global_settings", &settings)
 | 
				
			||||||
	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
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetServer func to query Server setting from the database
 | 
					// GetServer func to query Server setting from the database
 | 
				
			||||||
func GetServer() (model.Server, error) {
 | 
					func (o *JsonDB) GetServer() (model.Server, error) {
 | 
				
			||||||
	server := model.Server{}
 | 
						server := model.Server{}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	db, err := DBConn()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return server, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// read server interface information
 | 
						// read server interface information
 | 
				
			||||||
	serverInterface := model.ServerInterface{}
 | 
						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
 | 
							return server, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// read server key pair information
 | 
						// read server key pair information
 | 
				
			||||||
	serverKeyPair := model.ServerKeypair{}
 | 
						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
 | 
							return server, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -179,17 +135,11 @@ func GetServer() (model.Server, error) {
 | 
				
			||||||
	return server, nil
 | 
						return server, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetClients to get all clients from the database
 | 
					func (o *JsonDB) GetClients(hasQRCode bool) ([]model.ClientData, error) {
 | 
				
			||||||
func GetClients(hasQRCode bool) ([]model.ClientData, error) {
 | 
					 | 
				
			||||||
	var clients []model.ClientData
 | 
						var clients []model.ClientData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db, err := DBConn()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return clients, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// read all client json file in "clients" directory
 | 
						// read all client json file in "clients" directory
 | 
				
			||||||
	records, err := db.ReadAll("clients")
 | 
						records, err := o.conn.ReadAll("clients")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return clients, err
 | 
							return clients, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -206,10 +156,10 @@ func GetClients(hasQRCode bool) ([]model.ClientData, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// generate client qrcode image in base64
 | 
							// generate client qrcode image in base64
 | 
				
			||||||
		if hasQRCode {
 | 
							if hasQRCode {
 | 
				
			||||||
			server, _ := GetServer()
 | 
								server, _ := o.GetServer()
 | 
				
			||||||
			globalSettings, _ := GetGlobalSettings()
 | 
								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 {
 | 
								if err == nil {
 | 
				
			||||||
				clientData.QRCode = "data:image/png;base64," + base64.StdEncoding.EncodeToString([]byte(png))
 | 
									clientData.QRCode = "data:image/png;base64," + base64.StdEncoding.EncodeToString([]byte(png))
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
| 
						 | 
					@ -225,27 +175,21 @@ func GetClients(hasQRCode bool) ([]model.ClientData, error) {
 | 
				
			||||||
	return clients, nil
 | 
						return clients, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetClientByID func to query a client from the database
 | 
					func (o *JsonDB) GetClientByID(clientID string, hasQRCode bool) (model.ClientData, error) {
 | 
				
			||||||
func GetClientByID(clientID string, hasQRCode bool) (model.ClientData, error) {
 | 
					 | 
				
			||||||
	client := model.Client{}
 | 
						client := model.Client{}
 | 
				
			||||||
	clientData := model.ClientData{}
 | 
						clientData := model.ClientData{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	db, err := DBConn()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return clientData, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// read client information
 | 
						// 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
 | 
							return clientData, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// generate client qrcode image in base64
 | 
						// generate client qrcode image in base64
 | 
				
			||||||
	if hasQRCode {
 | 
						if hasQRCode {
 | 
				
			||||||
		server, _ := GetServer()
 | 
							server, _ := o.GetServer()
 | 
				
			||||||
		globalSettings, _ := GetGlobalSettings()
 | 
							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 {
 | 
							if err == nil {
 | 
				
			||||||
			clientData.QRCode = "data:image/png;base64," + base64.StdEncoding.EncodeToString([]byte(png))
 | 
								clientData.QRCode = "data:image/png;base64," + base64.StdEncoding.EncodeToString([]byte(png))
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
| 
						 | 
					@ -257,3 +201,23 @@ func GetClientByID(clientID string, hasQRCode bool) (model.ClientData, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return clientData, nil
 | 
						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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -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>
 | 
					                                </p>
 | 
				
			||||||
                            </a>
 | 
					                            </a>
 | 
				
			||||||
                        </li>
 | 
					                        </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>
 | 
					                    </ul>
 | 
				
			||||||
                </nav>
 | 
					                </nav>
 | 
				
			||||||
                <!-- /.sidebar-menu -->
 | 
					                <!-- /.sidebar-menu -->
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,10 +41,10 @@ Wireguard Clients
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
            <form name="frm_email_client" id="frm_email_client">
 | 
					            <form name="frm_email_client" id="frm_email_client">
 | 
				
			||||||
                <div class="modal-body">
 | 
					                <div class="modal-body">
 | 
				
			||||||
                    <input type="hidden" id="_client_id" name="_client_id">
 | 
					                    <input type="hidden" id="e_client_id" name="e_client_id">
 | 
				
			||||||
                    <div class="form-group">
 | 
					                    <div class="form-group">
 | 
				
			||||||
                        <label for="_client_email" class="control-label">Email</label>
 | 
					                        <label for="e_client_email" class="control-label">Email</label>
 | 
				
			||||||
                        <input type="text" class="form-control" id="_client_email" name="client_email">
 | 
					                        <input type="text" class="form-control" id="e_client_email" name="client_email">
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
                <div class="modal-footer justify-content-between">
 | 
					                <div class="modal-footer justify-content-between">
 | 
				
			||||||
| 
						 | 
					@ -340,8 +340,8 @@ Wireguard Clients
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // submitEmailClient function for sending an email to the client with the configuration
 | 
					        // submitEmailClient function for sending an email to the client with the configuration
 | 
				
			||||||
        function submitEmailClient() {
 | 
					        function submitEmailClient() {
 | 
				
			||||||
            const client_id = $("#_client_id").val();
 | 
					            const client_id = $("#e_client_id").val();
 | 
				
			||||||
            const email = $("#_client_email").val();
 | 
					            const email = $("#e_client_email").val();
 | 
				
			||||||
            const data = {"id": client_id, "email": email};
 | 
					            const data = {"id": client_id, "email": email};
 | 
				
			||||||
            $.ajax({
 | 
					            $.ajax({
 | 
				
			||||||
                cache: false,
 | 
					                cache: false,
 | 
				
			||||||
| 
						 | 
					@ -429,8 +429,8 @@ Wireguard Clients
 | 
				
			||||||
                    const client = resp.Client;
 | 
					                    const client = resp.Client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    modal.find(".modal-title").text("Email Client " + client.name);
 | 
					                    modal.find(".modal-title").text("Email Client " + client.name);
 | 
				
			||||||
                    modal.find("#_client_id").val(client.id);
 | 
					                    modal.find("#e_client_id").val(client.id);
 | 
				
			||||||
                    modal.find("#_client_email").val(client.email);
 | 
					                    modal.find("#e_client_email").val(client.email);
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                error: function (jqXHR, exception) {
 | 
					                error: function (jqXHR, exception) {
 | 
				
			||||||
                    const responseJson = jQuery.parseJSON(jqXHR.responseText);
 | 
					                    const responseJson = jQuery.parseJSON(jqXHR.responseText);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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}}
 | 
				
			||||||
 | 
					{{define "bottom_js"}}
 | 
				
			||||||
 | 
					{{end}}
 | 
				
			||||||
| 
						 | 
					@ -11,3 +11,16 @@ var (
 | 
				
			||||||
	EmailContent   string
 | 
						EmailContent   string
 | 
				
			||||||
	SessionSecret  []byte
 | 
						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"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -396,4 +396,11 @@ func LookupEnvOrInt(key string, defaultVal int) int {
 | 
				
			||||||
		return v
 | 
							return v
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return defaultVal
 | 
						return defaultVal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 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…
	
		Reference in New Issue