Abstracts database storage & Status page UI (#88)
This commit is contained in:
		
							parent
							
								
									7bb80c0c2e
								
							
						
					
					
						commit
						b7c0990dcb
					
				|  | @ -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 | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,6 +5,9 @@ services: | ||||||
|     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 | ||||||
|  | @ -12,9 +15,6 @@ services: | ||||||
|       - SESSION_SECRET |       - SESSION_SECRET | ||||||
|       - WGUI_USERNAME=alpha |       - WGUI_USERNAME=alpha | ||||||
|       - WGUI_PASSWORD=this-unusual-password |       - WGUI_PASSWORD=this-unusual-password | ||||||
| 
 |  | ||||||
|     ports: |  | ||||||
|       - 5000:5000 |  | ||||||
|     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
								
								
								
								
							|  | @ -13,6 +13,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" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -57,14 +58,16 @@ func init() { | ||||||
| 	fmt.Println("Authentication\t:", !util.DisableLogin) | 	fmt.Println("Authentication\t:", !util.DisableLogin) | ||||||
| 	fmt.Println("Bind address\t:", util.BindAddress) | 	fmt.Println("Bind address\t:", util.BindAddress) | ||||||
| 
 | 
 | ||||||
| 	// 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 | ||||||
|  | @ -78,32 +81,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,49 +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 username_env_var = "WGUI_USERNAME" | 	conn   *scribble.Driver | ||||||
| const password_env_var = "WGUI_PASSWORD" | 	dbPath string | ||||||
| const defaultUsername = "admin" | } | ||||||
| const defaultPassword = "admin" |  | ||||||
| const defaultServerAddress = "10.252.1.0/24" |  | ||||||
| const defaultServerPort = 51820 |  | ||||||
| const defaultDNS = "1.1.1.1" |  | ||||||
| const defaultMTU = 1450 |  | ||||||
| const defaultPersistentKeepalive = 15 |  | ||||||
| const defaultConfigFilePath = "/etc/wireguard/wg0.conf" |  | ||||||
| 
 | 
 | ||||||
| // 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 | ||||||
|  | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func getCredVar(key, fallback string) string { | func (o *JsonDB) Init() error { | ||||||
|     if value, ok := os.LookupEnv(key); ok { | 	var clientPath string = path.Join(o.dbPath, "clients") | ||||||
| 		return value | 	var serverPath string = path.Join(o.dbPath, "server") | ||||||
|     } |  | ||||||
|     return fallback |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // InitDB to create the default database
 |  | ||||||
| func InitDB() error { |  | ||||||
| 	var clientPath string = path.Join(dbPath, "clients") |  | ||||||
| 	var serverPath string = path.Join(dbPath, "server") |  | ||||||
| 	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) | ||||||
|  | @ -61,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 { | ||||||
|  | @ -88,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 = getCredVar(username_env_var, defaultUsername) | 		user.Username = util.GetCredVar(util.UsernameEnvVar, util.DefaultUsername) | ||||||
| 		user.Password = getCredVar(password_env_var, 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 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -188,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 | ||||||
| 	} | 	} | ||||||
|  | @ -215,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 { | ||||||
|  | @ -234,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 { | ||||||
|  | @ -266,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 --> | ||||||
|  |  | ||||||
|  | @ -0,0 +1,57 @@ | ||||||
|  | {{define "title"}} | ||||||
|  | Connected Peers | ||||||
|  | {{end}} | ||||||
|  | 
 | ||||||
|  | {{define "top_css"}} | ||||||
|  | {{end}} | ||||||
|  | 
 | ||||||
|  | {{define "username"}} | ||||||
|  | {{ .username }} | ||||||
|  | {{end}} | ||||||
|  | 
 | ||||||
|  | {{define "page_title"}} | ||||||
|  | Connected Peers | ||||||
|  | {{end}} | ||||||
|  | 
 | ||||||
|  | {{define "page_content"}} | ||||||
|  | <section class="content"> | ||||||
|  |     <div class="container-fluid"> | ||||||
|  |         {{ if .error }} | ||||||
|  |         <div class="alert alert-warning" role="alert">{{.error}}</div> | ||||||
|  |         {{ end}} | ||||||
|  |         {{ range $dev := .devices }} | ||||||
|  |             <table class="table table-sm"> | ||||||
|  |                 <caption>List of connected peers for device with name {{ $dev.Name }} </caption> | ||||||
|  |               <thead> | ||||||
|  |                 <tr> | ||||||
|  |                   <th scope="col">#</th> | ||||||
|  |                   <th scope="col">Name</th> | ||||||
|  |                   <th scope="col">Email</th> | ||||||
|  |                   <th scope="col">Public Key</th> | ||||||
|  |                   <th scope="col">ReceiveBytes</th> | ||||||
|  |                   <th scope="col">TransmitBytes</th> | ||||||
|  |                   <th scope="col">Connected (Approximation)</th> | ||||||
|  |                   <th scope="col">LastHandshakeTime</th> | ||||||
|  |                 </tr> | ||||||
|  |               </thead> | ||||||
|  |               <tbody> | ||||||
|  |               {{ range $idx, $peer := $dev.Peers }} | ||||||
|  |               <tr {{ if $peer.Connected }} class="table-success" {{ end }}> | ||||||
|  |                 <th scope="row">{{ $idx }}</th> | ||||||
|  |                 <td>{{ $peer.Name }}</td> | ||||||
|  |                 <td>{{ $peer.Email }}</td> | ||||||
|  |                 <td>{{ $peer.PublicKey }}</td> | ||||||
|  |                 <td>{{ $peer.ReceivedBytes }}</td> | ||||||
|  |                 <td>{{ $peer.TransmitBytes }}</td> | ||||||
|  |                 <td>{{ $peer.Connected }}</td> | ||||||
|  |                 <td>{{ $peer.LastHandshakeTime }}</td> | ||||||
|  |                </tr> | ||||||
|  |               {{ end }} | ||||||
|  |               </tbody> | ||||||
|  |             </table> | ||||||
|  |         {{ end }} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     </div> | ||||||
|  | </section> | ||||||
|  | {{end}} | ||||||
|  | @ -11,3 +11,16 @@ var ( | ||||||
| 	EmailContent   string | 	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" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | @ -368,3 +368,11 @@ func WriteWireGuardServerConfig(tmplBox *rice.Box, serverConfig model.Server, cl | ||||||
| 
 | 
 | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // GetCredVar reads value from environment variable or returns fallback
 | ||||||
|  | func GetCredVar(key, fallback string) string { | ||||||
|  | 	if value, ok := os.LookupEnv(key); ok { | ||||||
|  | 		return value | ||||||
|  | 	} | ||||||
|  | 	return fallback | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue