commit 6cb8527c355882148fabfe111ef4427ae42a171e Author: Khanh Ngo Date: Sat Apr 18 16:17:49 2020 +0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..603c61e --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +vendor/ +assets/ +db/ + + + diff --git a/client/khanh.json b/client/khanh.json new file mode 100644 index 0000000..ef5f7c7 --- /dev/null +++ b/client/khanh.json @@ -0,0 +1,4 @@ +{ + "privateKey": "xyz", + "pulbicKey": "123" +} \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c6855d4 --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module github.com/ngoduykhanh/wireguard-ui + +go 1.14 + +require ( + github.com/go-playground/universal-translator v0.17.0 // indirect + github.com/jcelliott/lumber v0.0.0-20160324203708-dd349441af25 // indirect + github.com/labstack/echo/v4 v4.1.16 + github.com/labstack/gommon v0.3.0 + github.com/leodido/go-urn v1.2.0 // indirect + github.com/rs/xid v1.2.1 + github.com/sdomino/scribble v0.0.0-20191024200645-4116320640ba + golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf + gopkg.in/go-playground/validator.v9 v9.31.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..8483930 --- /dev/null +++ b/go.sum @@ -0,0 +1,85 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/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/labstack/echo v3.3.10+incompatible h1:pGRcYk231ExFAyoAjAfD85kQzRJCRI8bbnE7CX5OEgg= +github.com/labstack/echo/v4 v4.1.16 h1:8swiwjE5Jkai3RPfZoahp8kjVCRNq+y7Q0hPji2Kz0o= +github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI= +github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= +github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= +github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mdlayher/genetlink v1.0.0/go.mod h1:0rJ0h4itni50A86M2kHcgS85ttZazNt7a8H2a2cw0Gc= +github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= +github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M= +github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY= +github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/sdomino/scribble v0.0.0-20191024200645-4116320640ba h1:8QAc9wFAf2b/9cAXskm0wBylObZ0bTpRcaP7ThjLPVQ= +github.com/sdomino/scribble v0.0.0-20191024200645-4116320640ba/go.mod h1:W6zxGUBCXRR5QugSd/nFcFVmwoGnvpjiNY/JwT03Wew= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4= +github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +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/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-20190827160401-ba9fcec4b297/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-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/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/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-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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.20200320 h1:1vE6zVeO7fix9cJX1Z9ZQ+ikPIIx7vIyU0o0tLDD88g= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf h1:rWUZHukj3poXegPQMZOXgxjTGIBe3mLNHNVvL5DsHus= +golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf/go.mod h1:UdS9frhv65KTfwxME1xE8+rHYoFpbm36gOud1GhBe9c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/go-playground/validator.v9 v9.31.0 h1:bmXmP2RSNtFES+bn4uYuHT7iJFJv7Vj+an+ZQdDaD1M= +gopkg.in/go-playground/validator.v9 v9.31.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/handler/routes.go b/handler/routes.go new file mode 100644 index 0000000..f36fc7c --- /dev/null +++ b/handler/routes.go @@ -0,0 +1,101 @@ +package handler + +import ( + "encoding/json" + "net/http" + "time" + + "github.com/labstack/echo/v4" + "github.com/ngoduykhanh/wireguard-ui/model" + "github.com/sdomino/scribble" + "github.com/labstack/gommon/log" + "github.com/rs/xid" + "golang.zx2c4.com/wireguard/wgctrl/wgtypes" +) + +// Home handler +func Home() echo.HandlerFunc { + return func(c echo.Context) error { + // initialize database directory + dir := "./db" + db, err := scribble.New(dir, nil) + if err != nil { + log.Error("Cannot initialize the database: ", err) + } + + records, err := db.ReadAll("clients") + if err != nil { + log.Error("Cannot fetch clients from database: ", err) + } + + clients := []model.Client{} + for _, f := range records { + client := model.Client{} + if err := json.Unmarshal([]byte(f), &client); err != nil { + log.Error("Cannot decode client json structure: ", err) + } + clients = append(clients, client) + } + + return c.Render(http.StatusOK, "home.html", map[string]interface{}{ + "name": "Khanh", + "clients": clients, + }) + } +} + +// NewClient handler +func NewClient() echo.HandlerFunc { + return func (c echo.Context) error { + client := new(model.Client) + c.Bind(client) + + // gen ID + guid := xid.New() + client.ID = guid.String() + + // gen Wireguard key pairs + key, err := wgtypes.GeneratePrivateKey() + if err != nil { + return err + } + client.PrivateKey = key.String() + client.PublicKey = key.PublicKey().String() + client.CreatedAt = time.Now().UTC() + client.UpdatedAt = client.CreatedAt + + // write to the database + dir := "./db" + db, err := scribble.New(dir, nil) + if err != nil { + log.Error("Cannot initialize the database: ", err) + } + db.Write("clients", client.ID, client) + log.Infof("Created wireguard client: %v", client) + + return c.JSON(http.StatusOK, client) + } +} + +// RemoveClient handler +func RemoveClient() echo.HandlerFunc { + return func (c echo.Context) error { + client := new(model.Client) + c.Bind(client) + + // delete from database + dir := "./db" + db, err := scribble.New(dir, nil) + if err != nil { + log.Error("Cannot initialize the database: ", err) + } + + if err := db.Delete("clients", client.ID); err != nil { + log.Error("Cannot delete wireguard client: ", err) + } + + log.Infof("Removed wireguard client: %v", client) + + return c.JSON(http.StatusOK, "Client removed!") + } +} \ No newline at end of file diff --git a/main.go b/main.go new file mode 100644 index 0000000..95d1d20 --- /dev/null +++ b/main.go @@ -0,0 +1,16 @@ +package main + +import ( + "github.com/ngoduykhanh/wireguard-ui/handler" + "github.com/ngoduykhanh/wireguard-ui/router" +) + +func main() { + app := router.New() + + app.GET("/", handler.Home()) + app.POST("/new-client", handler.NewClient()) + app.POST("/remove-client", handler.RemoveClient()) + + app.Logger.Fatal(app.Start("127.0.0.1:5000")) +} diff --git a/model/client.go b/model/client.go new file mode 100644 index 0000000..09fc5e3 --- /dev/null +++ b/model/client.go @@ -0,0 +1,19 @@ +package model + +import ( + "time" +) + +// Client model +type Client struct { + ID string `json:"id"` + PrivateKey string `json:"private_key"` + PublicKey string `json:"pulbic_key"` + Name string `json:"name"` + Email string `json:"email"` + AllocatedIPs []string `json:"allocated_ips"` + AllowedIPs []string `json:"allowed_ips"` + Enabled bool `json:"enabled"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/router/router.go b/router/router.go new file mode 100644 index 0000000..d30d710 --- /dev/null +++ b/router/router.go @@ -0,0 +1,49 @@ +package router + +import ( + "errors" + "io" + "text/template" + + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" + "github.com/labstack/gommon/log" +) + +// TemplateRegistry is a custom html/template renderer for Echo framework +type TemplateRegistry struct { + templates map[string]*template.Template +} + +// Render e.Renderer interface +func (t *TemplateRegistry) Render(w io.Writer, name string, data interface{}, c echo.Context) error { + tmpl, ok := t.templates[name] + if !ok { + err := errors.New("Template not found -> " + name) + return err + } + return tmpl.ExecuteTemplate(w, "base.html", data) +} + +// New function +func New() *echo.Echo { + e := echo.New() + templates := make(map[string]*template.Template) + templates["home.html"] = template.Must(template.ParseFiles("templates/home.html", "templates/base.html")) + + e.Logger.SetLevel(log.DEBUG) + e.Pre(middleware.RemoveTrailingSlash()) + e.Use(middleware.Logger()) + e.Use(middleware.CORSWithConfig(middleware.CORSConfig{ + AllowOrigins: []string{"*"}, + AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept, echo.HeaderAuthorization}, + AllowMethods: []string{echo.GET, echo.HEAD, echo.PUT, echo.PATCH, echo.POST, echo.DELETE}, + })) + e.Validator = NewValidator() + e.Static("/static", "assets") + e.Renderer = &TemplateRegistry{ + templates: templates, + } + + return e +} diff --git a/router/validator.go b/router/validator.go new file mode 100644 index 0000000..a35c66f --- /dev/null +++ b/router/validator.go @@ -0,0 +1,20 @@ +package router + +import "gopkg.in/go-playground/validator.v9" + +// NewValidator func +func NewValidator() *Validator { + return &Validator{ + validator: validator.New(), + } +} + +// Validator struct +type Validator struct { + validator *validator.Validate +} + +// Validate func +func (v *Validator) Validate(i interface{}) error { + return v.validator.Struct(i) +} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..08b0107 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,299 @@ +{{define "base.html"}} + + + + + + + {{template "title" .}} + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + +
+ +
+
+
+
+

{{template "page_title" .}}

+
+
+
+
+ + + {{template "page_content" .}} + +
+ + +
+
+ Version 0.1 +
+ Copyright © 2020 Wireguard UI. All rights + reserved. +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + {{template "bottom_js" .}} + + + + +{{end}} \ No newline at end of file diff --git a/templates/home.html b/templates/home.html new file mode 100644 index 0000000..6ab82c1 --- /dev/null +++ b/templates/home.html @@ -0,0 +1,113 @@ +{{define "title"}} +Home +{{end}} + +{{define "username"}} +{{index . "name"}} +{{end}} + +{{define "page_title"}} +Dashboard +{{end}} + +{{define "page_content"}} +
+
+
Wireguard Clients
+
+ {{range .clients}} +
+
+ +
+
+ + + + +
+
+ {{ .Name }} + {{ .Email }} + + {{ .CreatedAt.Format "2 Jan 2006 15:04" }} + + {{ .UpdatedAt.Format "2 Jan 2006 15:04" }} + IP Allocation + {{range .AllocatedIPs}} + {{.}} + {{end}} + Allowed IPs + {{range .AllowedIPs}} + {{.}} + {{end}} +
+ +
+ +
+ + {{end}} +
+ +
+
+ + + +{{end}} + +{{define "bottom_js"}} + +{{end}} \ No newline at end of file diff --git a/wireguard-ui b/wireguard-ui new file mode 100755 index 0000000..6783098 Binary files /dev/null and b/wireguard-ui differ