mirror of
https://github.com/Threnklyn/wg-ui.git
synced 2026-06-03 12:19:54 +02:00
Github actions & clean up to be able to do linting (#27)
* Github actions & clean up to be able to do linting * renamed user * pushing images to github packages * Fixed tags
This commit is contained in:
committed by
GitHub
parent
a127e67720
commit
b471f3dff5
@@ -1,24 +0,0 @@
|
|||||||
steps:
|
|
||||||
- name: Build container image
|
|
||||||
branches: "!master"
|
|
||||||
agents:
|
|
||||||
queue: default
|
|
||||||
os: linux
|
|
||||||
commands:
|
|
||||||
- buildah bud --format=docker --layers -f Dockerfile .
|
|
||||||
- name: Build & push container image
|
|
||||||
branches: "master"
|
|
||||||
agents:
|
|
||||||
queue: default
|
|
||||||
os: linux
|
|
||||||
commands:
|
|
||||||
- buildah bud --format=docker --layers -t embarkstudios/wireguard-ui:latest -f Dockerfile .
|
|
||||||
- buildah push --format=v2s2 --authfile /root/.dockerhub/config.json embarkstudios/wireguard-ui:latest
|
|
||||||
- name: Build and push debug container image
|
|
||||||
branches: "master"
|
|
||||||
agents:
|
|
||||||
queue: default
|
|
||||||
os: linux
|
|
||||||
commands:
|
|
||||||
- buildah bud --format=docker --layers -t embarkstudios/wireguard-ui:debug -f Dockerfile.debug .
|
|
||||||
- buildah push --format=v2s2 --authfile /root/.dockerhub/config.json embarkstudios/wireguard-ui:debug
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
name: Go
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches-ignore: master
|
||||||
|
jobs:
|
||||||
|
review:
|
||||||
|
name: Review code
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- name: bindfs
|
||||||
|
uses: "cedrickring/golang-action@1.4.1"
|
||||||
|
with:
|
||||||
|
args: go get -u github.com/go-bindata/go-bindata/... && go get -u github.com/elazarl/go-bindata-assetfs/... && go-bindata-assetfs -prefix ui/dist ui/dist
|
||||||
|
- name: Check styling error
|
||||||
|
uses: "cedrickring/golang-action@1.4.1"
|
||||||
|
with:
|
||||||
|
args: go get -u golang.org/x/lint/golint; golint -set_exit_status main.go server.go config.go # ./... update to use package when https://github.com/go-bindata/go-bindata/pull/37 is merged
|
||||||
|
- name: Check missing error check
|
||||||
|
uses: "cedrickring/golang-action@1.4.1"
|
||||||
|
with:
|
||||||
|
args: go get -u github.com/kisielk/errcheck; errcheck ./...
|
||||||
|
- name: Check suspicious constructs (1)
|
||||||
|
uses: "cedrickring/golang-action@1.4.1"
|
||||||
|
with:
|
||||||
|
args: go get honnef.co/go/tools/cmd/staticcheck; staticcheck -checks all,-ST1003,-U1000,-ST1005 ./... # have to disable ST1003,U1000,ST1005 due to the generated code
|
||||||
|
- name: Check suspicious constructs (2)
|
||||||
|
uses: "cedrickring/golang-action@1.4.1"
|
||||||
|
with:
|
||||||
|
args: go vet ./...
|
||||||
|
- name: Check missing error check
|
||||||
|
uses: "cedrickring/golang-action@1.4.1"
|
||||||
|
with:
|
||||||
|
args: go get github.com/securego/gosec/cmd/gosec; gosec ./... # https://github.com/securego/gosec
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- name: Install buildah
|
||||||
|
run: |
|
||||||
|
sudo apt-get install -qq -y software-properties-common
|
||||||
|
sudo add-apt-repository -y ppa:projectatomic/ppa
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get -qq -y install buildah
|
||||||
|
- name: Build the Docker image
|
||||||
|
run: buildah bud --format=docker --layers -f Dockerfile .
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
name: Docker Image CI
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Install buildah
|
||||||
|
run: |
|
||||||
|
sudo apt-get install -qq -y software-properties-common
|
||||||
|
sudo add-apt-repository -y ppa:projectatomic/ppa
|
||||||
|
sudo apt-get update -qq
|
||||||
|
sudo apt-get -qq -y install buildah
|
||||||
|
- name: Build & push the Docker image
|
||||||
|
run: |
|
||||||
|
buildah bud --format=docker --layers -t docker.pkg.github.com/EmbarkStudios/wireguard-ui/wireguard-ui:latest -t docker.pkg.github.com/EmbarkStudios/wireguard-ui/wireguard-ui:${GITHUB_SHA} -f Dockerfile .
|
||||||
|
buildah push --creds embarkbot:${{ secrets.GITHUB_TOKEN }} --format=v2s2 docker.pkg.github.com/EmbarkStudios/wireguard-ui/wireguard-ui
|
||||||
+2
-9
@@ -1,11 +1,4 @@
|
|||||||
FROM node:12-alpine AS ui
|
FROM docker.io/golang:1.13 AS build
|
||||||
WORKDIR /ui
|
|
||||||
COPY ui/package.json ui/package-lock.json /ui/
|
|
||||||
RUN npm install
|
|
||||||
COPY ui .
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
FROM golang:1.13 AS build
|
|
||||||
WORKDIR /wg
|
WORKDIR /wg
|
||||||
RUN go get github.com/go-bindata/go-bindata/... &&\
|
RUN go get github.com/go-bindata/go-bindata/... &&\
|
||||||
go get github.com/elazarl/go-bindata-assetfs/...
|
go get github.com/elazarl/go-bindata-assetfs/...
|
||||||
@@ -13,7 +6,7 @@ COPY go.mod .
|
|||||||
COPY go.sum .
|
COPY go.sum .
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
COPY . .
|
COPY . .
|
||||||
COPY --from=ui /ui/dist ui/dist
|
COPY /ui/dist ui/dist
|
||||||
RUN go-bindata-assetfs -prefix ui/dist ui/dist &&\
|
RUN go-bindata-assetfs -prefix ui/dist ui/dist &&\
|
||||||
go install .
|
go install .
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ A basic, self-contained management service for [WireGuard](https://wireguard.com
|
|||||||
|
|
||||||
The easiest way to run wireguard-ui is using the container image. To test it, run:
|
The easiest way to run wireguard-ui is using the container image. To test it, run:
|
||||||
|
|
||||||
```docker run --rm -it --privileged --entrypoint "/wireguard-ui" -v /tmp/wireguard-ui:/data -p 8080:8080 -p 5555:5555 embarkstudios/wireguard-ui --data-dir=/data --log-level=debug```
|
```docker run --rm -it --privileged --entrypoint "/wireguard-ui" -v /tmp/wireguard-ui:/data -p 8080:8080 -p 5555:5555 docker.pkg.github.com/embarkstudios/wireguard-ui:latest --data-dir=/data --log-level=debug```
|
||||||
|
|
||||||
|
When running in production, we recommend using the latest release as opposed to `latest`.
|
||||||
|
|
||||||
## Developing
|
## Developing
|
||||||
|
|
||||||
|
|||||||
@@ -11,18 +11,21 @@ import (
|
|||||||
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ServerConfig contains the reference to users, keys and where on disk the config is stored
|
||||||
type ServerConfig struct {
|
type ServerConfig struct {
|
||||||
configPath string `json:"-"`
|
configPath string
|
||||||
PrivateKey string
|
PrivateKey string
|
||||||
PublicKey string
|
PublicKey string
|
||||||
Users map[string]*UserConfig
|
Users map[string]*UserConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UserConfig represents a user and it's clients
|
||||||
type UserConfig struct {
|
type UserConfig struct {
|
||||||
Name string
|
Name string
|
||||||
Clients map[string]*ClientConfig
|
Clients map[string]*ClientConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ClientConfig represents a single client for a user
|
||||||
type ClientConfig struct {
|
type ClientConfig struct {
|
||||||
Name string
|
Name string
|
||||||
PrivateKey string
|
PrivateKey string
|
||||||
@@ -31,6 +34,7 @@ type ClientConfig struct {
|
|||||||
Notes string
|
Notes string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewServerConfig creates and returns a reference to a new ServerConfig
|
||||||
func NewServerConfig(cfgPath string) *ServerConfig {
|
func NewServerConfig(cfgPath string) *ServerConfig {
|
||||||
key, err := wgtypes.GeneratePrivateKey()
|
key, err := wgtypes.GeneratePrivateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -62,6 +66,7 @@ func NewServerConfig(cfgPath string) *ServerConfig {
|
|||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write writes the ServerConfig to the path specified in the config
|
||||||
func (cfg *ServerConfig) Write() error {
|
func (cfg *ServerConfig) Write() error {
|
||||||
data, err := json.MarshalIndent(cfg, "", " ")
|
data, err := json.MarshalIndent(cfg, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -70,6 +75,7 @@ func (cfg *ServerConfig) Write() error {
|
|||||||
return ioutil.WriteFile(cfg.configPath, data, 0600)
|
return ioutil.WriteFile(cfg.configPath, data, 0600)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetUserConfig returns a UserConfig for a specific user
|
||||||
func (cfg *ServerConfig) GetUserConfig(user string) *UserConfig {
|
func (cfg *ServerConfig) GetUserConfig(user string) *UserConfig {
|
||||||
c, ok := cfg.Users[user]
|
c, ok := cfg.Users[user]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -83,6 +89,7 @@ func (cfg *ServerConfig) GetUserConfig(user string) *UserConfig {
|
|||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewClientConfig initiates a new client, returning a reference to the new config
|
||||||
func NewClientConfig(ip net.IP) *ClientConfig {
|
func NewClientConfig(ip net.IP) *ClientConfig {
|
||||||
key, err := wgtypes.GeneratePrivateKey()
|
key, err := wgtypes.GeneratePrivateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/elazarl/go-bindata-assetfs"
|
assetfs "github.com/elazarl/go-bindata-assetfs"
|
||||||
"github.com/google/nftables"
|
"github.com/google/nftables"
|
||||||
"github.com/google/nftables/expr"
|
"github.com/google/nftables/expr"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
@@ -48,6 +48,11 @@ var (
|
|||||||
filenameRe = regexp.MustCompile("[^a-zA-Z0-9]+")
|
filenameRe = regexp.MustCompile("[^a-zA-Z0-9]+")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type contextKey string
|
||||||
|
|
||||||
|
const key = contextKey("user")
|
||||||
|
|
||||||
|
// Server is the running server
|
||||||
type Server struct {
|
type Server struct {
|
||||||
serverConfigPath string
|
serverConfigPath string
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
@@ -57,18 +62,15 @@ type Server struct {
|
|||||||
assets http.Handler
|
assets http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
type WgLink struct {
|
type wgLink struct {
|
||||||
attrs *netlink.LinkAttrs
|
attrs *netlink.LinkAttrs
|
||||||
}
|
}
|
||||||
|
|
||||||
type jwtClaims struct {
|
func (w *wgLink) Attrs() *netlink.LinkAttrs {
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WgLink) Attrs() *netlink.LinkAttrs {
|
|
||||||
return w.attrs
|
return w.attrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WgLink) Type() string {
|
func (w *wgLink) Type() string {
|
||||||
return "wireguard"
|
return "wireguard"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,6 +80,7 @@ func ifname(n string) []byte {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewServer returns an instance of Server which contains both the webserver and the reference to Wireguard
|
||||||
func NewServer() *Server {
|
func NewServer() *Server {
|
||||||
ipAddr, ipNet, err := net.ParseCIDR(*clientIPRange)
|
ipAddr, ipNet, err := net.ParseCIDR(*clientIPRange)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -108,7 +111,7 @@ func NewServer() *Server {
|
|||||||
return &s
|
return &s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) enableIpForward() error {
|
func (s *Server) enableIPForward() error {
|
||||||
log.Info("Enabling sys.net.ipv4.ip_forward")
|
log.Info("Enabling sys.net.ipv4.ip_forward")
|
||||||
p := "/proc/sys/net/ipv4/ip_forward"
|
p := "/proc/sys/net/ipv4/ip_forward"
|
||||||
return ioutil.WriteFile(p, []byte("1"), 0640)
|
return ioutil.WriteFile(p, []byte("1"), 0640)
|
||||||
@@ -118,7 +121,7 @@ func (s *Server) initInterface() error {
|
|||||||
attrs := netlink.NewLinkAttrs()
|
attrs := netlink.NewLinkAttrs()
|
||||||
attrs.Name = *wgLinkName
|
attrs.Name = *wgLinkName
|
||||||
|
|
||||||
link := WgLink{
|
link := wgLink{
|
||||||
attrs: &attrs,
|
attrs: &attrs,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,7 +217,7 @@ func (s *Server) allocateIP() net.IP {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if allocated[ip.String()] == false {
|
if !allocated[ip.String()] {
|
||||||
log.Debug("Allocated IP: ", ip)
|
log.Debug("Allocated IP: ", ip)
|
||||||
return ip
|
return ip
|
||||||
}
|
}
|
||||||
@@ -287,8 +290,9 @@ func (s *Server) configureWireGuard() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start configures wiregard and initiates the interfaces as well as starts the webserver to accept clients
|
||||||
func (s *Server) Start() error {
|
func (s *Server) Start() error {
|
||||||
err := s.enableIpForward()
|
err := s.enableIPForward()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -357,7 +361,7 @@ func (s *Server) userFromHeader(handler http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
http.SetCookie(w, &cookie)
|
http.SetCookie(w, &cookie)
|
||||||
|
|
||||||
ctx := context.WithValue(r.Context(), "user", user)
|
ctx := context.WithValue(r.Context(), key, user)
|
||||||
handler.ServeHTTP(w, r.WithContext(ctx))
|
handler.ServeHTTP(w, r.WithContext(ctx))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -366,7 +370,7 @@ func (s *Server) withAuth(handler httprouter.Handle) httprouter.Handle {
|
|||||||
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
log.Debug("Auth required")
|
log.Debug("Auth required")
|
||||||
|
|
||||||
user := r.Context().Value("user")
|
user := r.Context().Value(key)
|
||||||
if user == nil {
|
if user == nil {
|
||||||
log.Error("Error getting username from request context")
|
log.Error("Error getting username from request context")
|
||||||
w.WriteHeader(http.StatusInternalServerError)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
@@ -383,8 +387,9 @@ func (s *Server) withAuth(handler httprouter.Handle) httprouter.Handle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WhoAmI returns the identity of the current user
|
||||||
func (s *Server) WhoAmI(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
func (s *Server) WhoAmI(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
user := r.Context().Value("user").(string)
|
user := r.Context().Value(key).(string)
|
||||||
log.Debug(user)
|
log.Debug(user)
|
||||||
err := json.NewEncoder(w).Encode(struct{ User string }{user})
|
err := json.NewEncoder(w).Encode(struct{ User string }{user})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -393,8 +398,9 @@ func (s *Server) WhoAmI(w http.ResponseWriter, r *http.Request, ps httprouter.Pa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetClients returns a list of all clients for the current user
|
||||||
func (s *Server) GetClients(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
func (s *Server) GetClients(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
user := r.Context().Value("user").(string)
|
user := r.Context().Value(key).(string)
|
||||||
log.Debug(user)
|
log.Debug(user)
|
||||||
clients := map[string]*ClientConfig{}
|
clients := map[string]*ClientConfig{}
|
||||||
userConfig := s.Config.Users[user]
|
userConfig := s.Config.Users[user]
|
||||||
@@ -409,14 +415,16 @@ func (s *Server) GetClients(w http.ResponseWriter, r *http.Request, ps httproute
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Index returns the single-page app
|
||||||
func (s *Server) Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
func (s *Server) Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||||
log.Debug("Serving single-page app from URL: ", r.URL)
|
log.Debug("Serving single-page app from URL: ", r.URL)
|
||||||
r.URL.Path = "/"
|
r.URL.Path = "/"
|
||||||
s.assets.ServeHTTP(w, r)
|
s.assets.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetClient returns a specific client for the current user
|
||||||
func (s *Server) GetClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
func (s *Server) GetClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
user := r.Context().Value("user").(string)
|
user := r.Context().Value(key).(string)
|
||||||
usercfg := s.Config.Users[user]
|
usercfg := s.Config.Users[user]
|
||||||
if usercfg == nil {
|
if usercfg == nil {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
@@ -472,7 +480,10 @@ Endpoint = %s
|
|||||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
|
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
|
||||||
w.Header().Set("Content-Type", "application/config")
|
w.Header().Set("Content-Type", "application/config")
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
fmt.Fprintf(w, configData)
|
_, err := fmt.Fprint(w, configData)
|
||||||
|
if err != nil {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -484,8 +495,9 @@ Endpoint = %s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EditClient edits the specific client passed by the current user
|
||||||
func (s *Server) EditClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
func (s *Server) EditClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
user := r.Context().Value("user").(string)
|
user := r.Context().Value(key).(string)
|
||||||
usercfg := s.Config.Users[user]
|
usercfg := s.Config.Users[user]
|
||||||
if usercfg == nil {
|
if usercfg == nil {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
@@ -526,8 +538,9 @@ func (s *Server) EditClient(w http.ResponseWriter, r *http.Request, ps httproute
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteClient deletes the specified client for the current user
|
||||||
func (s *Server) DeleteClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
func (s *Server) DeleteClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
user := r.Context().Value("user").(string)
|
user := r.Context().Value(key).(string)
|
||||||
usercfg := s.Config.Users[user]
|
usercfg := s.Config.Users[user]
|
||||||
if usercfg == nil {
|
if usercfg == nil {
|
||||||
w.WriteHeader(http.StatusNotFound)
|
w.WriteHeader(http.StatusNotFound)
|
||||||
@@ -548,11 +561,12 @@ func (s *Server) DeleteClient(w http.ResponseWriter, r *http.Request, ps httprou
|
|||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateClient creates a new client for the current user
|
||||||
func (s *Server) CreateClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
func (s *Server) CreateClient(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
defer s.mutex.Unlock()
|
defer s.mutex.Unlock()
|
||||||
|
|
||||||
user := r.Context().Value("user").(string)
|
user := r.Context().Value(key).(string)
|
||||||
log.WithField("user", user).Debug("CreateClient")
|
log.WithField("user", user).Debug("CreateClient")
|
||||||
|
|
||||||
c := s.Config.GetUserConfig(user)
|
c := s.Config.GetUserConfig(user)
|
||||||
|
|||||||
@@ -1,3 +1,2 @@
|
|||||||
node_modules
|
node_modules
|
||||||
.idea
|
.idea
|
||||||
dist
|
|
||||||
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
Vendored
+15
@@ -0,0 +1,15 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport"
|
||||||
|
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||||
|
<title>wireguard-ui</title>
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,600,700">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto+Mono">
|
||||||
|
<link rel="shortcut icon" href="/favicon.png"><link href="/vendors~main.css?cea63361befb692ab573" rel="stylesheet"><link href="/main.css?cea63361befb692ab573" rel="stylesheet"></head>
|
||||||
|
<body>
|
||||||
|
<script type="text/javascript" src="/vendors~main.1.js?cea63361befb692ab573"></script><script type="text/javascript" src="/main.js?cea63361befb692ab573"></script></body>
|
||||||
|
</html>
|
||||||
Vendored
+236
@@ -0,0 +1,236 @@
|
|||||||
|
.mdc-typography {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
.mdc-typography--headline1 {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: 6rem;
|
||||||
|
line-height: 6rem;
|
||||||
|
font-weight: 300;
|
||||||
|
letter-spacing: -0.01562em;
|
||||||
|
text-decoration: inherit;
|
||||||
|
text-transform: inherit;
|
||||||
|
}
|
||||||
|
.mdc-typography--headline2 {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: 3.75rem;
|
||||||
|
line-height: 3.75rem;
|
||||||
|
font-weight: 300;
|
||||||
|
letter-spacing: -0.00833em;
|
||||||
|
text-decoration: inherit;
|
||||||
|
text-transform: inherit;
|
||||||
|
}
|
||||||
|
.mdc-typography--headline3 {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: 3rem;
|
||||||
|
line-height: 3.125rem;
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-decoration: inherit;
|
||||||
|
text-transform: inherit;
|
||||||
|
}
|
||||||
|
.mdc-typography--headline4 {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: 2.125rem;
|
||||||
|
line-height: 2.5rem;
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: 0.00735em;
|
||||||
|
text-decoration: inherit;
|
||||||
|
text-transform: inherit;
|
||||||
|
}
|
||||||
|
.mdc-typography--headline5 {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
line-height: 2rem;
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-decoration: inherit;
|
||||||
|
text-transform: inherit;
|
||||||
|
}
|
||||||
|
.mdc-typography--headline6 {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
line-height: 2rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.0125em;
|
||||||
|
text-decoration: inherit;
|
||||||
|
text-transform: inherit;
|
||||||
|
}
|
||||||
|
.mdc-typography--subtitle1 {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.75rem;
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: 0.00937em;
|
||||||
|
text-decoration: inherit;
|
||||||
|
text-transform: inherit;
|
||||||
|
}
|
||||||
|
.mdc-typography--subtitle2 {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.375rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.00714em;
|
||||||
|
text-decoration: inherit;
|
||||||
|
text-transform: inherit;
|
||||||
|
}
|
||||||
|
.mdc-typography--body1 {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: 1rem;
|
||||||
|
line-height: 1.5rem;
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: 0.03125em;
|
||||||
|
text-decoration: inherit;
|
||||||
|
text-transform: inherit;
|
||||||
|
}
|
||||||
|
.mdc-typography--body2 {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.25rem;
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: 0.01786em;
|
||||||
|
text-decoration: inherit;
|
||||||
|
text-transform: inherit;
|
||||||
|
}
|
||||||
|
.mdc-typography--caption {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: 1.25rem;
|
||||||
|
font-weight: 400;
|
||||||
|
letter-spacing: 0.03333em;
|
||||||
|
text-decoration: inherit;
|
||||||
|
text-transform: inherit;
|
||||||
|
}
|
||||||
|
.mdc-typography--button {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 2.25rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.08929em;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
.mdc-typography--overline {
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: 2rem;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.16667em;
|
||||||
|
text-decoration: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.float-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
dl {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
}
|
||||||
|
dt {
|
||||||
|
flex-basis: 20%;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
dd {
|
||||||
|
flex-basis: 70%;
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-left: 0.5em;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 800px) {
|
||||||
|
img.svelte-9q81nd {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
img.svelte-9q81nd {
|
||||||
|
margin-right: 40px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
.download.svelte-9q81nd {
|
||||||
|
margin-top: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.newClient.svelte-o5v00 {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
h2.svelte-o5v00 small.svelte-o5v00 {
|
||||||
|
display: block;
|
||||||
|
clear: left;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
.row.svelte-o5v00 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.col.svelte-o5v00 {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-basis: 100%;
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 2em;
|
||||||
|
}
|
||||||
|
.help.svelte-o5v00 {
|
||||||
|
flex-basis: 10%;
|
||||||
|
}
|
||||||
|
h2.svelte-o5v00 {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.back.svelte-101labk {
|
||||||
|
position: fixed;
|
||||||
|
left: 10px;
|
||||||
|
top: 70px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 800px) {
|
||||||
|
.user.svelte-1bvmsl0 {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
footer.svelte-vt2udd {
|
||||||
|
margin-top: 3em;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
text-align: center;
|
||||||
|
background: #f7f7f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=main.css.map*/
|
||||||
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+1561
File diff suppressed because it is too large
Load Diff
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+9303
File diff suppressed because it is too large
Load Diff
Vendored
+1
File diff suppressed because one or more lines are too long
Vendored
+3918
File diff suppressed because it is too large
Load Diff
Vendored
+1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user