diff --git a/.env b/.env
index 9a8241785f6c65304c0c1153eb06bc08ad589324..d87d58d73e83b76b04ea267dea45faccc5bc0326 100644
--- a/.env
+++ b/.env
@@ -1,12 +1,41 @@
+# IP address to listen to
 SERVER=0.0.0.0
+# port to bind
 PORT=8080
+# Gin framework release mode
 GIN_MODE=release
-
+# where to write all generated config files
 WG_CONF_DIR=./wireguard
+# WireGuard main config file name, generally <interface name>.conf
 WG_INTERFACE_NAME=wg0.conf
 
+# SMTP settings to send email to clients
 SMTP_HOST=smtp.gmail.com
 SMTP_PORT=587
 SMTP_USERNAME=account@gmail.com
-SMTP_PASSWORD="*************"
-SMTP_FROM="Wg Gen Web <account@gmail.com>"
+SMTP_PASSWORD=*************
+SMTP_FROM=Wg Gen Web <account@gmail.com>
+
+# example with gitlab, which is RFC implementation and no need any custom stuff
+OAUTH2_PROVIDER_NAME=oauth2oidc
+OAUTH2_PROVIDER=https://gitlab.com
+OAUTH2_CLIENT_ID=
+OAUTH2_CLIENT_SECRET=
+OAUTH2_REDIRECT_URL=https://wg-gen-web-demo.127-0-0-1.fr
+
+# example with google
+OAUTH2_PROVIDER_NAME=google
+OAUTH2_PROVIDER=
+OAUTH2_CLIENT_ID=
+OAUTH2_CLIENT_SECRET=
+OAUTH2_REDIRECT_URL=
+
+# example with github
+OAUTH2_PROVIDER_NAME=github
+OAUTH2_PROVIDER=
+OAUTH2_CLIENT_ID=
+OAUTH2_CLIENT_SECRET=
+OAUTH2_REDIRECT_URL=
+
+# set provider name to fake to disable auth, also the default
+OAUTH2_PROVIDER_NAME=fake
diff --git a/api/api.go b/api/api.go
index c1bba54bfa3ec169a15dcee1995af968ea758768..8063cecafb7cdf39235a16dbc5bc1d251be7e221 100644
--- a/api/api.go
+++ b/api/api.go
@@ -2,244 +2,12 @@ package api
 
 import (
 	"github.com/gin-gonic/gin"
-	log "github.com/sirupsen/logrus"
-	"github.com/skip2/go-qrcode"
-	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/core"
-	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/model"
-	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/template"
-	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/version"
-	"net/http"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api/v1"
 )
 
-// ApplyRoutes applies router to gin Router
-func ApplyRoutes(r *gin.Engine) {
-	client := r.Group("/api/v1.0/client")
+func ApplyRoutes(r *gin.Engine, private bool) {
+	api := r.Group("/api")
 	{
-
-		client.POST("", createClient)
-		client.GET("/:id", readClient)
-		client.PATCH("/:id", updateClient)
-		client.DELETE("/:id", deleteClient)
-		client.GET("", readClients)
-		client.GET("/:id/config", configClient)
-		client.GET("/:id/email", emailClient)
-	}
-
-	server := r.Group("/api/v1.0/server")
-	{
-		server.GET("", readServer)
-		server.PATCH("", updateServer)
-		server.GET("/config", configServer)
-		server.GET("/version", versionStr)
-	}
-}
-
-func createClient(c *gin.Context) {
-	var data model.Client
-
-	if err := c.ShouldBindJSON(&data); err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to bind")
-		c.AbortWithStatus(http.StatusUnprocessableEntity)
-		return
-	}
-
-	client, err := core.CreateClient(&data)
-	if err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to create client")
-		c.AbortWithStatus(http.StatusInternalServerError)
-		return
-	}
-
-	c.JSON(http.StatusOK, client)
-}
-
-func readClient(c *gin.Context) {
-	id := c.Param("id")
-
-	client, err := core.ReadClient(id)
-	if err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to read client")
-		c.AbortWithStatus(http.StatusInternalServerError)
-		return
-	}
-
-	c.JSON(http.StatusOK, client)
-}
-
-func updateClient(c *gin.Context) {
-	var data model.Client
-	id := c.Param("id")
-
-	if err := c.ShouldBindJSON(&data); err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to bind")
-		c.AbortWithStatus(http.StatusUnprocessableEntity)
-		return
-	}
-
-	client, err := core.UpdateClient(id, &data)
-	if err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to update client")
-		c.AbortWithStatus(http.StatusInternalServerError)
-		return
-	}
-
-	c.JSON(http.StatusOK, client)
-}
-
-func deleteClient(c *gin.Context) {
-	id := c.Param("id")
-
-	err := core.DeleteClient(id)
-	if err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to remove client")
-		c.AbortWithStatus(http.StatusInternalServerError)
-		return
-	}
-
-	c.JSON(http.StatusOK, gin.H{})
-}
-
-func readClients(c *gin.Context) {
-	clients, err := core.ReadClients()
-	if err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to list clients")
-		c.AbortWithStatus(http.StatusInternalServerError)
-		return
-	}
-
-	c.JSON(http.StatusOK, clients)
-}
-
-func configClient(c *gin.Context) {
-	configData, err := core.ReadClientConfig(c.Param("id"))
-	if err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to read client config")
-		c.AbortWithStatus(http.StatusInternalServerError)
-		return
-	}
-
-	formatQr := c.DefaultQuery("qrcode", "false")
-	if formatQr == "false" {
-		// return config as txt file
-		c.Header("Content-Disposition", "attachment; filename=wg0.conf")
-		c.Data(http.StatusOK, "application/config", configData)
-		return
-	}
-	// return config as png qrcode
-	png, err := qrcode.Encode(string(configData), qrcode.Medium, 250)
-	if err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to create qrcode")
-		c.AbortWithStatus(http.StatusInternalServerError)
-		return
-	}
-	c.Data(http.StatusOK, "image/png", png)
-	return
-}
-
-func emailClient(c *gin.Context) {
-	id := c.Param("id")
-
-	err := core.EmailClient(id)
-	if err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to send email to client")
-		c.AbortWithStatus(http.StatusInternalServerError)
-		return
-	}
-
-	c.JSON(http.StatusOK, gin.H{})
-}
-
-func readServer(c *gin.Context) {
-	client, err := core.ReadServer()
-	if err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to read client")
-		c.AbortWithStatus(http.StatusInternalServerError)
-		return
-	}
-
-	c.JSON(http.StatusOK, client)
-}
-
-func updateServer(c *gin.Context) {
-	var data model.Server
-
-	if err := c.ShouldBindJSON(&data); err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to bind")
-		c.AbortWithStatus(http.StatusUnprocessableEntity)
-		return
-	}
-
-	client, err := core.UpdateServer(&data)
-	if err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to update client")
-		c.AbortWithStatus(http.StatusInternalServerError)
-		return
-	}
-
-	c.JSON(http.StatusOK, client)
-}
-
-func configServer(c *gin.Context) {
-	clients, err := core.ReadClients()
-	if err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to read clients")
-		c.AbortWithStatus(http.StatusUnprocessableEntity)
-		return
-	}
-
-	server, err := core.ReadServer()
-	if err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to read server")
-		c.AbortWithStatus(http.StatusUnprocessableEntity)
-		return
-	}
-
-	configData, err := template.DumpServerWg(clients, server)
-	if err != nil {
-		log.WithFields(log.Fields{
-			"err": err,
-		}).Error("failed to dump wg config")
-		c.AbortWithStatus(http.StatusUnprocessableEntity)
-		return
+		apiv1.ApplyRoutes(api, private)
 	}
-
-	// return config as txt file
-	c.Header("Content-Disposition", "attachment; filename=wg0.conf")
-	c.Data(http.StatusOK, "application/config", configData)
-}
-
-func versionStr(c *gin.Context) {
-	c.JSON(http.StatusOK, gin.H{
-		"version": version.Version,
-	})
 }
diff --git a/api/v1/auth/auth.go b/api/v1/auth/auth.go
new file mode 100644
index 0000000000000000000000000000000000000000..66fcd0a954de39aacb9c0a1c08c1ecc3c840c47f
--- /dev/null
+++ b/api/v1/auth/auth.go
@@ -0,0 +1,134 @@
+package auth
+
+import (
+	"github.com/gin-gonic/gin"
+	"github.com/patrickmn/go-cache"
+	log "github.com/sirupsen/logrus"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/auth"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/model"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/util"
+	"golang.org/x/oauth2"
+	"net/http"
+	"time"
+)
+
+// ApplyRoutes applies router to gin Router
+func ApplyRoutes(r *gin.RouterGroup) {
+	g := r.Group("/auth")
+	{
+		g.GET("/oauth2_url", oauth2_url)
+		g.POST("/oauth2_exchange", oauth2_exchange)
+		g.GET("/user", user)
+		g.GET("/logout", logout)
+	}
+}
+
+/*
+ * generate redirect url to get OAuth2 code or let client know that OAuth2 is disabled
+ */
+func oauth2_url(c *gin.Context) {
+	cacheDb := c.MustGet("cache").(*cache.Cache)
+
+	state, err := util.GenerateRandomString(32)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to generate state random string")
+		c.AbortWithStatus(http.StatusInternalServerError)
+	}
+
+	clientId, err := util.GenerateRandomString(32)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to generate state random string")
+		c.AbortWithStatus(http.StatusInternalServerError)
+	}
+	// save clientId and state so we can retrieve for verification
+	cacheDb.Set(clientId, state, 5*time.Minute)
+
+	oauth2Client := c.MustGet("oauth2Client").(auth.Auth)
+
+	data := &model.Auth{
+		Oauth2:   true,
+		ClientId: clientId,
+		State:    state,
+		CodeUrl:  oauth2Client.CodeUrl(state),
+	}
+
+	c.JSON(http.StatusOK, data)
+}
+
+/*
+ * exchange code and get user infos, if OAuth2 is disable just send fake data
+ */
+func oauth2_exchange(c *gin.Context) {
+	var loginVals model.Auth
+	if err := c.ShouldBind(&loginVals); err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("code and state fields are missing")
+		c.AbortWithStatus(http.StatusUnprocessableEntity)
+		return
+	}
+
+	cacheDb := c.MustGet("cache").(*cache.Cache)
+	savedState, exists := cacheDb.Get(loginVals.ClientId)
+
+	if !exists || savedState != loginVals.State {
+		log.WithFields(log.Fields{
+			"state":      loginVals.State,
+			"savedState": savedState,
+		}).Error("saved state and client provided state mismatch")
+		c.AbortWithStatus(http.StatusBadRequest)
+		return
+	}
+	oauth2Client := c.MustGet("oauth2Client").(auth.Auth)
+
+	oauth2Token, err := oauth2Client.Exchange(loginVals.Code)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to exchange code for token")
+		c.AbortWithStatus(http.StatusBadRequest)
+		return
+	}
+
+	cacheDb.Delete(loginVals.ClientId)
+	cacheDb.Set(oauth2Token.AccessToken, oauth2Token, cache.DefaultExpiration)
+
+	c.JSON(http.StatusOK, oauth2Token.AccessToken)
+}
+
+func logout(c *gin.Context) {
+	cacheDb := c.MustGet("cache").(*cache.Cache)
+	cacheDb.Delete(c.Request.Header.Get(util.AuthTokenHeaderName))
+	c.JSON(http.StatusOK, gin.H{})
+}
+
+func user(c *gin.Context) {
+	cacheDb := c.MustGet("cache").(*cache.Cache)
+	oauth2Token, exists := cacheDb.Get(c.Request.Header.Get(util.AuthTokenHeaderName))
+
+	if exists && oauth2Token.(*oauth2.Token).AccessToken == c.Request.Header.Get(util.AuthTokenHeaderName) {
+		oauth2Client := c.MustGet("oauth2Client").(auth.Auth)
+		user, err := oauth2Client.UserInfo(oauth2Token.(*oauth2.Token))
+		if err != nil {
+			log.WithFields(log.Fields{
+				"err": err,
+			}).Error("failed to get user from oauth2 AccessToken")
+			c.AbortWithStatus(http.StatusBadRequest)
+			return
+		}
+
+		c.JSON(http.StatusOK, user)
+		return
+	}
+
+	log.WithFields(log.Fields{
+		"exists":                 exists,
+		util.AuthTokenHeaderName: c.Request.Header.Get(util.AuthTokenHeaderName),
+	}).Error("oauth2 AccessToken is not recognized")
+
+	c.AbortWithStatus(http.StatusUnauthorized)
+}
diff --git a/api/v1/client/client.go b/api/v1/client/client.go
new file mode 100644
index 0000000000000000000000000000000000000000..13be7bad4e3634b658ce14ef0d2ba620b828dac1
--- /dev/null
+++ b/api/v1/client/client.go
@@ -0,0 +1,190 @@
+package client
+
+import (
+	"github.com/gin-gonic/gin"
+	log "github.com/sirupsen/logrus"
+	"github.com/skip2/go-qrcode"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/auth"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/core"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/model"
+	"golang.org/x/oauth2"
+	"net/http"
+)
+
+// ApplyRoutes applies router to gin Router
+func ApplyRoutes(r *gin.RouterGroup) {
+	g := r.Group("/client")
+	{
+
+		g.POST("", createClient)
+		g.GET("/:id", readClient)
+		g.PATCH("/:id", updateClient)
+		g.DELETE("/:id", deleteClient)
+		g.GET("", readClients)
+		g.GET("/:id/config", configClient)
+		g.GET("/:id/email", emailClient)
+	}
+}
+
+func createClient(c *gin.Context) {
+	var data model.Client
+
+	if err := c.ShouldBindJSON(&data); err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to bind")
+		c.AbortWithStatus(http.StatusUnprocessableEntity)
+		return
+	}
+
+	// get creation user from token and add to client infos
+	oauth2Token := c.MustGet("oauth2Token").(*oauth2.Token)
+	oauth2Client := c.MustGet("oauth2Client").(auth.Auth)
+	user, err := oauth2Client.UserInfo(oauth2Token)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"oauth2Token": oauth2Token,
+			"err":         err,
+		}).Error("failed to get user with oauth token")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+	data.CreatedBy = user.Name
+
+	client, err := core.CreateClient(&data)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to create client")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+
+	c.JSON(http.StatusOK, client)
+}
+
+func readClient(c *gin.Context) {
+	id := c.Param("id")
+
+	client, err := core.ReadClient(id)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to read client")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+
+	c.JSON(http.StatusOK, client)
+}
+
+func updateClient(c *gin.Context) {
+	var data model.Client
+	id := c.Param("id")
+
+	if err := c.ShouldBindJSON(&data); err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to bind")
+		c.AbortWithStatus(http.StatusUnprocessableEntity)
+		return
+	}
+
+	// get update user from token and add to client infos
+	oauth2Token := c.MustGet("oauth2Token").(*oauth2.Token)
+	oauth2Client := c.MustGet("oauth2Client").(auth.Auth)
+	user, err := oauth2Client.UserInfo(oauth2Token)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"oauth2Token": oauth2Token,
+			"err":         err,
+		}).Error("failed to get user with oauth token")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+	data.UpdatedBy = user.Name
+
+	client, err := core.UpdateClient(id, &data)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to update client")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+
+	c.JSON(http.StatusOK, client)
+}
+
+func deleteClient(c *gin.Context) {
+	id := c.Param("id")
+
+	err := core.DeleteClient(id)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to remove client")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+
+	c.JSON(http.StatusOK, gin.H{})
+}
+
+func readClients(c *gin.Context) {
+	clients, err := core.ReadClients()
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to list clients")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+
+	c.JSON(http.StatusOK, clients)
+}
+
+func configClient(c *gin.Context) {
+	configData, err := core.ReadClientConfig(c.Param("id"))
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to read client config")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+
+	formatQr := c.DefaultQuery("qrcode", "false")
+	if formatQr == "false" {
+		// return config as txt file
+		c.Header("Content-Disposition", "attachment; filename=wg0.conf")
+		c.Data(http.StatusOK, "application/config", configData)
+		return
+	}
+	// return config as png qrcode
+	png, err := qrcode.Encode(string(configData), qrcode.Medium, 250)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to create qrcode")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+	c.Data(http.StatusOK, "image/png", png)
+	return
+}
+
+func emailClient(c *gin.Context) {
+	id := c.Param("id")
+
+	err := core.EmailClient(id)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to send email to client")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+
+	c.JSON(http.StatusOK, gin.H{})
+}
diff --git a/api/v1/server/server.go b/api/v1/server/server.go
new file mode 100644
index 0000000000000000000000000000000000000000..158deb91e11d625fe90c24f69b96790eb9c289a4
--- /dev/null
+++ b/api/v1/server/server.go
@@ -0,0 +1,113 @@
+package server
+
+import (
+	"github.com/gin-gonic/gin"
+	log "github.com/sirupsen/logrus"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/auth"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/core"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/model"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/template"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/version"
+	"golang.org/x/oauth2"
+	"net/http"
+)
+
+// ApplyRoutes applies router to gin Router
+func ApplyRoutes(r *gin.RouterGroup) {
+	g := r.Group("/server")
+	{
+		g.GET("", readServer)
+		g.PATCH("", updateServer)
+		g.GET("/config", configServer)
+		g.GET("/version", versionStr)
+	}
+}
+
+func readServer(c *gin.Context) {
+	client, err := core.ReadServer()
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to read client")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+
+	c.JSON(http.StatusOK, client)
+}
+
+func updateServer(c *gin.Context) {
+	var data model.Server
+
+	if err := c.ShouldBindJSON(&data); err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to bind")
+		c.AbortWithStatus(http.StatusUnprocessableEntity)
+		return
+	}
+
+	// get update user from token and add to server infos
+	oauth2Token := c.MustGet("oauth2Token").(*oauth2.Token)
+	oauth2Client := c.MustGet("oauth2Client").(auth.Auth)
+	user, err := oauth2Client.UserInfo(oauth2Token)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"oauth2Token": oauth2Token,
+			"err":         err,
+		}).Error("failed to get user with oauth token")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+	data.UpdatedBy = user.Name
+
+	server, err := core.UpdateServer(&data)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to update client")
+		c.AbortWithStatus(http.StatusInternalServerError)
+		return
+	}
+
+	c.JSON(http.StatusOK, server)
+}
+
+func configServer(c *gin.Context) {
+	clients, err := core.ReadClients()
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to read clients")
+		c.AbortWithStatus(http.StatusUnprocessableEntity)
+		return
+	}
+
+	server, err := core.ReadServer()
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to read server")
+		c.AbortWithStatus(http.StatusUnprocessableEntity)
+		return
+	}
+
+	configData, err := template.DumpServerWg(clients, server)
+	if err != nil {
+		log.WithFields(log.Fields{
+			"err": err,
+		}).Error("failed to dump wg config")
+		c.AbortWithStatus(http.StatusUnprocessableEntity)
+		return
+	}
+
+	// return config as txt file
+	c.Header("Content-Disposition", "attachment; filename=wg0.conf")
+	c.Data(http.StatusOK, "application/config", configData)
+}
+
+func versionStr(c *gin.Context) {
+	c.JSON(http.StatusOK, gin.H{
+		"version": version.Version,
+	})
+}
diff --git a/api/v1/v1.go b/api/v1/v1.go
new file mode 100644
index 0000000000000000000000000000000000000000..ce1339f6e9a01ac2d1698a1767fc897038b61144
--- /dev/null
+++ b/api/v1/v1.go
@@ -0,0 +1,21 @@
+package apiv1
+
+import (
+	"github.com/gin-gonic/gin"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api/v1/auth"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api/v1/client"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api/v1/server"
+)
+
+func ApplyRoutes(r *gin.RouterGroup, private bool) {
+	v1 := r.Group("/v1.0")
+	{
+		if private {
+			client.ApplyRoutes(v1)
+			server.ApplyRoutes(v1)
+		} else {
+			auth.ApplyRoutes(v1)
+
+		}
+	}
+}
diff --git a/auth/auth.go b/auth/auth.go
new file mode 100644
index 0000000000000000000000000000000000000000..d4dadaed4a3e590dda33f0afcd4b50d10d7e0c06
--- /dev/null
+++ b/auth/auth.go
@@ -0,0 +1,13 @@
+package auth
+
+import (
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/model"
+	"golang.org/x/oauth2"
+)
+
+type Auth interface {
+	Setup() error
+	CodeUrl(state string) string
+	Exchange(code string) (*oauth2.Token, error)
+	UserInfo(oauth2Token *oauth2.Token) (*model.User, error)
+}
diff --git a/auth/fake/fake.go b/auth/fake/fake.go
new file mode 100644
index 0000000000000000000000000000000000000000..0ebe8d8218ee53ecc39dd46926518294c5431570
--- /dev/null
+++ b/auth/fake/fake.go
@@ -0,0 +1,47 @@
+package fake
+
+import (
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/model"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/util"
+	"golang.org/x/oauth2"
+	"time"
+)
+
+type Fake struct{}
+
+// Setup validate provider
+func (o *Fake) Setup() error {
+	return nil
+}
+
+// CodeUrl get url to redirect client for auth
+func (o *Fake) CodeUrl(state string) string {
+	return "_magic_string_fake_auth_no_redirect_"
+}
+
+// Exchange exchange code for Oauth2 token
+func (o *Fake) Exchange(code string) (*oauth2.Token, error) {
+	rand, err := util.GenerateRandomString(32)
+	if err != nil {
+		return nil, err
+	}
+
+	return &oauth2.Token{
+		AccessToken:  rand,
+		TokenType:    "",
+		RefreshToken: "",
+		Expiry:       time.Time{},
+	}, nil
+}
+
+// UserInfo get token user
+func (o *Fake) UserInfo(oauth2Token *oauth2.Token) (*model.User, error) {
+	return &model.User{
+		Sub:      "unknown",
+		Name:     "Unknown",
+		Email:    "unknown",
+		Profile:  "unknown",
+		Issuer:   "unknown",
+		IssuedAt: time.Time{},
+	}, nil
+}
diff --git a/auth/github/github.go b/auth/github/github.go
new file mode 100644
index 0000000000000000000000000000000000000000..d2e73c266e17fc179d15f61f271d04f46cf9bb11
--- /dev/null
+++ b/auth/github/github.go
@@ -0,0 +1 @@
+package github
diff --git a/auth/google/goolge.go b/auth/google/goolge.go
new file mode 100644
index 0000000000000000000000000000000000000000..71664db3c875f7f0158d04ecdd883734371235ac
--- /dev/null
+++ b/auth/google/goolge.go
@@ -0,0 +1 @@
+package google
diff --git a/auth/oauth2oidc/oauth2oidc.go b/auth/oauth2oidc/oauth2oidc.go
new file mode 100644
index 0000000000000000000000000000000000000000..b50444aac60d93010c0ca717681d965c0184d9fb
--- /dev/null
+++ b/auth/oauth2oidc/oauth2oidc.go
@@ -0,0 +1,108 @@
+package oauth2oidc
+
+import (
+	"context"
+	"fmt"
+	"github.com/coreos/go-oidc"
+	log "github.com/sirupsen/logrus"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/model"
+	"golang.org/x/oauth2"
+	"os"
+)
+
+type Oauth2idc struct{}
+
+var (
+	oauth2Config        *oauth2.Config
+	oidcProvider        *oidc.Provider
+	oidcIDTokenVerifier *oidc.IDTokenVerifier
+)
+
+// Setup validate provider
+func (o *Oauth2idc) Setup() error {
+	var err error
+
+	oidcProvider, err = oidc.NewProvider(context.TODO(), os.Getenv("OAUTH2_PROVIDER"))
+	if err != nil {
+		return err
+	}
+
+	oidcIDTokenVerifier = oidcProvider.Verifier(&oidc.Config{
+		ClientID: os.Getenv("OAUTH2_CLIENT_ID"),
+	})
+
+	oauth2Config = &oauth2.Config{
+		ClientID:     os.Getenv("OAUTH2_CLIENT_ID"),
+		ClientSecret: os.Getenv("OAUTH2_CLIENT_SECRET"),
+		RedirectURL:  os.Getenv("OAUTH2_REDIRECT_URL"),
+		Scopes:       []string{oidc.ScopeOpenID, "profile", "email"},
+		Endpoint:     oidcProvider.Endpoint(),
+	}
+
+	return nil
+}
+
+// CodeUrl get url to redirect client for auth
+func (o *Oauth2idc) CodeUrl(state string) string {
+	return oauth2Config.AuthCodeURL(state)
+}
+
+// Exchange exchange code for Oauth2 token
+func (o *Oauth2idc) Exchange(code string) (*oauth2.Token, error) {
+	oauth2Token, err := oauth2Config.Exchange(context.TODO(), code)
+	if err != nil {
+		return nil, err
+	}
+
+	return oauth2Token, nil
+}
+
+// UserInfo get token user
+func (o *Oauth2idc) UserInfo(oauth2Token *oauth2.Token) (*model.User, error) {
+	rawIDToken, ok := oauth2Token.Extra("id_token").(string)
+	if !ok {
+		return nil, fmt.Errorf("no id_token field in oauth2 token")
+	}
+
+	iDToken, err := oidcIDTokenVerifier.Verify(context.TODO(), rawIDToken)
+	if err != nil {
+		return nil, err
+	}
+
+	userInfo, err := oidcProvider.UserInfo(context.TODO(), oauth2.StaticTokenSource(oauth2Token))
+	if err != nil {
+		return nil, err
+	}
+
+	type UserInfo struct {
+		Subject       string `json:"sub"`
+		Profile       string `json:"profile"`
+		Email         string `json:"email"`
+		EmailVerified bool   `json:"email_verified"`
+
+		claims []byte
+	}
+
+	// ID Token payload is just JSON
+	var claims map[string]interface{}
+	if err := userInfo.Claims(&claims); err != nil {
+		return nil, fmt.Errorf("failed to get id token claims: %s", err)
+	}
+
+	// get some infos about user
+	user := &model.User{}
+	user.Sub = userInfo.Subject
+	user.Email = userInfo.Email
+	user.Profile = userInfo.Profile
+
+	if v, found := claims["name"]; found && v != nil {
+		user.Name = v.(string)
+	} else {
+		log.Error("name not found in user info claims")
+	}
+
+	user.Issuer = iDToken.Issuer
+	user.IssuedAt = iDToken.IssuedAt
+
+	return user, nil
+}
diff --git a/cmd/wg-gen-web/main.go b/cmd/wg-gen-web/main.go
index cdb044ea13650959c01eb92a69841768bfbe8250..14222080a8e541a6342e7422459f9a237a05eb7a 100644
--- a/cmd/wg-gen-web/main.go
+++ b/cmd/wg-gen-web/main.go
@@ -7,13 +7,24 @@ import (
 	"github.com/gin-contrib/static"
 	"github.com/gin-gonic/gin"
 	"github.com/joho/godotenv"
+	"github.com/patrickmn/go-cache"
 	log "github.com/sirupsen/logrus"
 	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/api"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/auth"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/auth/fake"
+	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/auth/oauth2oidc"
 	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/core"
 	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/util"
 	"gitlab.127-0-0-1.fr/vx3r/wg-gen-web/version"
+	"golang.org/x/oauth2"
+	"net/http"
 	"os"
 	"path/filepath"
+	"time"
+)
+
+var (
+	cacheDb = cache.New(60*time.Minute, 10*time.Minute)
 )
 
 func init() {
@@ -80,21 +91,88 @@ func main() {
 	// cors middleware
 	config := cors.DefaultConfig()
 	config.AllowAllOrigins = true
+	config.AddAllowHeaders("Authorization")
 	app.Use(cors.New(config))
 
 	// protection middleware
 	app.Use(helmet.Default())
 
-	// no route redirect to frontend app
-	app.NoRoute(func(c *gin.Context) {
-		c.Redirect(301, "/index.html")
+	// add cache storage to gin app
+	app.Use(func(ctx *gin.Context) {
+		ctx.Set("cache", cacheDb)
+		ctx.Next()
 	})
 
 	// serve static files
 	app.Use(static.Serve("/", static.LocalFile("./ui/dist", false)))
 
-	// apply api router
-	api.ApplyRoutes(app)
+	// setup Oauth2 client if enabled
+	var oauth2Client auth.Auth
+
+	switch os.Getenv("OAUTH2_PROVIDER_NAME") {
+	case "fake":
+		log.Warn("Oauth is set to fake, no actual authentication will be performed")
+		oauth2Client = &fake.Fake{}
+		err = oauth2Client.Setup()
+		if err != nil {
+			log.WithFields(log.Fields{
+				"OAUTH2_PROVIDER_NAME": "oauth2oidc",
+				"err":                  err,
+			}).Fatal("failed to setup Oauth2")
+		}
+	case "oauth2oidc":
+		log.Warn("Oauth is set to oauth2oidc, must be RFC implementation on server side")
+		oauth2Client = &oauth2oidc.Oauth2idc{}
+		err = oauth2Client.Setup()
+		if err != nil {
+			log.WithFields(log.Fields{
+				"OAUTH2_PROVIDER_NAME": "oauth2oidc",
+				"err":                  err,
+			}).Fatal("failed to setup Oauth2")
+		}
+	default:
+		log.WithFields(log.Fields{
+			"OAUTH2_PROVIDER_NAME": os.Getenv("OAUTH2_PROVIDER_NAME"),
+		}).Fatal("auth provider name unknown")
+	}
+
+	if os.Getenv("OAUTH2_ENABLE") == "true" {
+		oauth2Client = &oauth2oidc.Oauth2idc{}
+		err = oauth2Client.Setup()
+		if err != nil {
+			log.WithFields(log.Fields{
+				"err": err,
+			}).Fatal("failed to setup Oauth2")
+		}
+	}
+	app.Use(func(ctx *gin.Context) {
+		ctx.Set("oauth2Client", oauth2Client)
+		ctx.Next()
+	})
+
+	// apply api routes public
+	api.ApplyRoutes(app, false)
+
+	// simple middleware to check auth
+	app.Use(func(c *gin.Context) {
+		cacheDb := c.MustGet("cache").(*cache.Cache)
+
+		token := c.Request.Header.Get(util.AuthTokenHeaderName)
+
+		oauth2Token, exists := cacheDb.Get(token)
+		if exists && oauth2Token.(*oauth2.Token).AccessToken == token {
+			// will be accessible in auth endpoints
+			c.Set("oauth2Token", oauth2Token)
+			c.Next()
+			return
+		}
+
+		c.AbortWithStatus(http.StatusUnauthorized)
+		return
+	})
+
+	// apply api router private
+	api.ApplyRoutes(app, true)
 
 	err = app.Run(fmt.Sprintf("%s:%s", os.Getenv("SERVER"), os.Getenv("PORT")))
 	if err != nil {
diff --git a/go.mod b/go.mod
index cf77582e136c0b55126d0eda03851dc4d56efa07..1d31fa6ca24c081062a2959165f40e3bf575f2cc 100644
--- a/go.mod
+++ b/go.mod
@@ -3,15 +3,26 @@ module gitlab.127-0-0-1.fr/vx3r/wg-gen-web
 go 1.14
 
 require (
+	github.com/appleboy/gin-jwt/v2 v2.6.3 // indirect
+	github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect
+	github.com/coreos/go-oidc v2.2.1+incompatible
 	github.com/danielkov/gin-helmet v0.0.0-20171108135313-1387e224435e
 	github.com/gin-contrib/cors v1.3.1
+	github.com/gin-contrib/sessions v0.0.3
 	github.com/gin-contrib/static v0.0.0-20191128031702-f81c604d8ac2
+	github.com/gin-gonic/contrib v0.0.0-20191209060500-d6e26eeaa607 // indirect
 	github.com/gin-gonic/gin v1.6.2
+	github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
+	github.com/gorilla/sessions v1.2.0 // indirect
 	github.com/joho/godotenv v1.3.0
+	github.com/patrickmn/go-cache v2.1.0+incompatible
+	github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
 	github.com/satori/go.uuid v1.2.0
 	github.com/sirupsen/logrus v1.5.0
 	github.com/skip2/go-qrcode v0.0.0-20191027152451-9434209cb086
+	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
 	golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200324154536-ceff61240acf
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
+	gopkg.in/square/go-jose.v2 v2.5.0 // indirect
 )
diff --git a/model/auth.go b/model/auth.go
new file mode 100644
index 0000000000000000000000000000000000000000..941dfeebc62ceaa354f14a4b19390ac2f0d02752
--- /dev/null
+++ b/model/auth.go
@@ -0,0 +1,9 @@
+package model
+
+type Auth struct {
+	Oauth2   bool   `json:"oauth2"`
+	ClientId string `json:"clientId"`
+	Code     string `json:"code"`
+	State    string `json:"state"`
+	CodeUrl  string `json:"codeUrl"`
+}
diff --git a/model/client.go b/model/client.go
index 78cda45deea72ef593001f858db73eadbfe6b887..4a40b279e8a20f9bd3ac35ad44add8bd30eb5893 100644
--- a/model/client.go
+++ b/model/client.go
@@ -18,6 +18,8 @@ type Client struct {
 	Address                   []string  `json:"address"`
 	PrivateKey                string    `json:"privateKey"`
 	PublicKey                 string    `json:"publicKey"`
+	CreatedBy                 string    `json:"createdBy"`
+	UpdatedBy                 string    `json:"updatedBy"`
 	Created                   time.Time `json:"created"`
 	Updated                   time.Time `json:"updated"`
 }
diff --git a/model/server.go b/model/server.go
index 592aa366e928c256d475daa9268cd0c87c0e0f03..eb45782162e7663a9a3149a722832192844925a2 100644
--- a/model/server.go
+++ b/model/server.go
@@ -21,6 +21,7 @@ type Server struct {
 	PostUp              string    `json:"postUp"`
 	PreDown             string    `json:"preDown"`
 	PostDown            string    `json:"postDown"`
+	UpdatedBy           string    `json:"updatedBy"`
 	Created             time.Time `json:"created"`
 	Updated             time.Time `json:"updated"`
 }
diff --git a/model/user.go b/model/user.go
new file mode 100644
index 0000000000000000000000000000000000000000..852123f69ae0b09627d2a3ff17abf509491ae83c
--- /dev/null
+++ b/model/user.go
@@ -0,0 +1,12 @@
+package model
+
+import "time"
+
+type User struct {
+	Sub      string    `json:"sub"`
+	Name     string    `json:"name"`
+	Email    string    `json:"email"`
+	Profile  string    `json:"profile"`
+	Issuer   string    `json:"issuer"`
+	IssuedAt time.Time `json:"issuedAt"`
+}
diff --git a/ui/package-lock.json b/ui/package-lock.json
index c3a8613d3b23241b72523e5b7bd7d55b352c766f..8aa703ec60d076bd6ac9ecfd313ea07720ef6e88 100644
--- a/ui/package-lock.json
+++ b/ui/package-lock.json
@@ -109,19 +109,19 @@
       "dependencies": {
         "ansi-regex": {
           "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/ansi-regex/download/ansi-regex-2.1.1.tgz",
           "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
           "dev": true
         },
         "ansi-styles": {
           "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/ansi-styles/download/ansi-styles-2.2.1.tgz",
           "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
           "dev": true
         },
         "chalk": {
           "version": "1.1.3",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+          "resolved": "https://registry.npm.taobao.org/chalk/download/chalk-1.1.3.tgz",
           "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
           "dev": true,
           "requires": {
@@ -134,7 +134,7 @@
         },
         "strip-ansi": {
           "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-3.0.1.tgz?cache=0&sync_timestamp=1573280577145&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-3.0.1.tgz",
           "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
           "dev": true,
           "requires": {
@@ -143,7 +143,7 @@
         },
         "supports-color": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/supports-color/download/supports-color-2.0.0.tgz?cache=0&sync_timestamp=1569557363805&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsupports-color%2Fdownload%2Fsupports-color-2.0.0.tgz",
           "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
           "dev": true
         }
@@ -339,7 +339,7 @@
         },
         "yallist": {
           "version": "2.1.2",
-          "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
+          "resolved": "https://registry.npm.taobao.org/yallist/download/yallist-2.1.2.tgz",
           "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
           "dev": true
         }
@@ -608,7 +608,7 @@
     },
     "alphanum-sort": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/alphanum-sort/download/alphanum-sort-1.0.2.tgz",
       "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
       "dev": true
     },
@@ -620,7 +620,7 @@
     },
     "ansi-html": {
       "version": "0.0.7",
-      "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
+      "resolved": "https://registry.npm.taobao.org/ansi-html/download/ansi-html-0.0.7.tgz",
       "integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=",
       "dev": true
     },
@@ -641,7 +641,7 @@
     },
     "any-promise": {
       "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/any-promise/download/any-promise-1.3.0.tgz",
       "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=",
       "dev": true
     },
@@ -678,7 +678,7 @@
     },
     "arr-diff": {
       "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/arr-diff/download/arr-diff-4.0.0.tgz",
       "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
       "dev": true
     },
@@ -690,19 +690,19 @@
     },
     "arr-union": {
       "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/arr-union/download/arr-union-3.1.0.tgz",
       "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
       "dev": true
     },
     "array-flatten": {
       "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/array-flatten/download/array-flatten-1.1.1.tgz?cache=0&sync_timestamp=1574313492546&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Farray-flatten%2Fdownload%2Farray-flatten-1.1.1.tgz",
       "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
       "dev": true
     },
     "array-union": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/array-union/download/array-union-1.0.2.tgz",
       "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=",
       "dev": true,
       "requires": {
@@ -711,13 +711,13 @@
     },
     "array-uniq": {
       "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz",
+      "resolved": "https://registry.npm.taobao.org/array-uniq/download/array-uniq-1.0.3.tgz",
       "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=",
       "dev": true
     },
     "array-unique": {
       "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/array-unique/download/array-unique-0.3.2.tgz",
       "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
       "dev": true
     },
@@ -753,13 +753,13 @@
       "dependencies": {
         "inherits": {
           "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.1.tgz",
           "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
           "dev": true
         },
         "util": {
           "version": "0.10.3",
-          "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz",
+          "resolved": "https://registry.npm.taobao.org/util/download/util-0.10.3.tgz",
           "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
           "dev": true,
           "requires": {
@@ -770,13 +770,13 @@
     },
     "assert-plus": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/assert-plus/download/assert-plus-1.0.0.tgz",
       "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
       "dev": true
     },
     "assign-symbols": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/assign-symbols/download/assign-symbols-1.0.0.tgz",
       "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
       "dev": true
     },
@@ -803,7 +803,7 @@
     },
     "asynckit": {
       "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz",
       "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
       "dev": true
     },
@@ -830,7 +830,7 @@
     },
     "aws-sign2": {
       "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/aws-sign2/download/aws-sign2-0.7.0.tgz",
       "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
       "dev": true
     },
@@ -894,7 +894,7 @@
       "dependencies": {
         "define-property": {
           "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-1.0.0.tgz",
           "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
           "dev": true,
           "requires": {
@@ -940,13 +940,13 @@
     },
     "batch": {
       "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/batch/download/batch-0.6.1.tgz",
       "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
       "dev": true
     },
     "bcrypt-pbkdf": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/bcrypt-pbkdf/download/bcrypt-pbkdf-1.0.2.tgz",
       "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
       "dev": true,
       "requires": {
@@ -1018,7 +1018,7 @@
         },
         "ms": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
           "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
           "dev": true
         },
@@ -1032,7 +1032,7 @@
     },
     "bonjour": {
       "version": "3.5.0",
-      "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/bonjour/download/bonjour-3.5.0.tgz",
       "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=",
       "dev": true,
       "requires": {
@@ -1054,7 +1054,7 @@
     },
     "boolbase": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/boolbase/download/boolbase-1.0.0.tgz",
       "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=",
       "dev": true
     },
@@ -1088,7 +1088,7 @@
       "dependencies": {
         "extend-shallow": {
           "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz",
           "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
           "requires": {
@@ -1099,7 +1099,7 @@
     },
     "brorand": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/brorand/download/brorand-1.1.0.tgz",
       "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
       "dev": true
     },
@@ -1142,7 +1142,7 @@
     },
     "browserify-rsa": {
       "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/browserify-rsa/download/browserify-rsa-4.0.1.tgz",
       "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
       "dev": true,
       "requires": {
@@ -1152,7 +1152,7 @@
     },
     "browserify-sign": {
       "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
+      "resolved": "https://registry.npm.taobao.org/browserify-sign/download/browserify-sign-4.0.4.tgz",
       "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
       "dev": true,
       "requires": {
@@ -1217,13 +1217,13 @@
     },
     "buffer-xor": {
       "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
+      "resolved": "https://registry.npm.taobao.org/buffer-xor/download/buffer-xor-1.0.3.tgz",
       "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
       "dev": true
     },
     "builtin-status-codes": {
       "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/builtin-status-codes/download/builtin-status-codes-3.0.0.tgz",
       "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
       "dev": true
     },
@@ -1300,13 +1300,13 @@
     },
     "call-me-maybe": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/call-me-maybe/download/call-me-maybe-1.0.1.tgz",
       "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=",
       "dev": true
     },
     "caller-callsite": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/caller-callsite/download/caller-callsite-2.0.0.tgz?cache=0&sync_timestamp=1562696843228&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcaller-callsite%2Fdownload%2Fcaller-callsite-2.0.0.tgz",
       "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=",
       "dev": true,
       "requires": {
@@ -1315,7 +1315,7 @@
     },
     "caller-path": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/caller-path/download/caller-path-2.0.0.tgz?cache=0&sync_timestamp=1574395542397&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcaller-path%2Fdownload%2Fcaller-path-2.0.0.tgz",
       "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=",
       "dev": true,
       "requires": {
@@ -1324,13 +1324,13 @@
     },
     "callsites": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/callsites/download/callsites-2.0.0.tgz",
       "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=",
       "dev": true
     },
     "camel-case": {
       "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/camel-case/download/camel-case-3.0.0.tgz",
       "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=",
       "dev": true,
       "requires": {
@@ -1370,7 +1370,7 @@
     },
     "caseless": {
       "version": "0.12.0",
-      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/caseless/download/caseless-0.12.0.tgz",
       "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
       "dev": true
     },
@@ -1498,7 +1498,7 @@
       "dependencies": {
         "define-property": {
           "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz",
           "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
           "dev": true,
           "requires": {
@@ -1524,7 +1524,7 @@
     },
     "cli-cursor": {
       "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/cli-cursor/download/cli-cursor-2.1.0.tgz",
       "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
       "dev": true,
       "requires": {
@@ -1654,7 +1654,7 @@
     },
     "clone": {
       "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+      "resolved": "https://registry.npm.taobao.org/clone/download/clone-1.0.4.tgz",
       "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
       "dev": true
     },
@@ -1682,13 +1682,13 @@
     },
     "code-point-at": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/code-point-at/download/code-point-at-1.1.0.tgz",
       "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
       "dev": true
     },
     "collection-visit": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/collection-visit/download/collection-visit-1.0.0.tgz",
       "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
       "dev": true,
       "requires": {
@@ -1717,7 +1717,7 @@
     },
     "color-name": {
       "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "resolved": "https://registry.npm.taobao.org/color-name/download/color-name-1.1.3.tgz",
       "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
       "dev": true
     },
@@ -1748,7 +1748,7 @@
     },
     "commondir": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/commondir/download/commondir-1.0.1.tgz",
       "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
       "dev": true
     },
@@ -1784,7 +1784,7 @@
       "dependencies": {
         "bytes": {
           "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/bytes/download/bytes-3.0.0.tgz",
           "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
           "dev": true
         },
@@ -1799,7 +1799,7 @@
         },
         "ms": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
           "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
           "dev": true
         },
@@ -1852,7 +1852,7 @@
     },
     "constants-browserify": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/constants-browserify/download/constants-browserify-1.0.0.tgz",
       "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
       "dev": true
     },
@@ -1887,7 +1887,7 @@
     },
     "cookie-signature": {
       "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
+      "resolved": "https://registry.npm.taobao.org/cookie-signature/download/cookie-signature-1.0.6.tgz",
       "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
       "dev": true
     },
@@ -1907,7 +1907,7 @@
     },
     "copy-descriptor": {
       "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/copy-descriptor/download/copy-descriptor-0.1.1.tgz",
       "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
       "dev": true
     },
@@ -1953,7 +1953,7 @@
         },
         "globby": {
           "version": "7.1.1",
-          "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/globby/download/globby-7.1.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fglobby%2Fdownload%2Fglobby-7.1.1.tgz",
           "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=",
           "dev": true,
           "requires": {
@@ -1967,7 +1967,7 @@
           "dependencies": {
             "pify": {
               "version": "3.0.0",
-              "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+              "resolved": "https://registry.npm.taobao.org/pify/download/pify-3.0.0.tgz",
               "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
               "dev": true
             }
@@ -2047,7 +2047,7 @@
     },
     "core-util-is": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/core-util-is/download/core-util-is-1.0.2.tgz",
       "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
       "dev": true
     },
@@ -2154,7 +2154,7 @@
     },
     "css-color-names": {
       "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
+      "resolved": "https://registry.npm.taobao.org/css-color-names/download/css-color-names-0.0.4.tgz",
       "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=",
       "dev": true
     },
@@ -2306,13 +2306,13 @@
     },
     "cssnano-util-get-arguments": {
       "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/cssnano-util-get-arguments/download/cssnano-util-get-arguments-4.0.0.tgz",
       "integrity": "sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8=",
       "dev": true
     },
     "cssnano-util-get-match": {
       "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/cssnano-util-get-match/download/cssnano-util-get-match-4.0.0.tgz",
       "integrity": "sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0=",
       "dev": true
     },
@@ -2360,13 +2360,13 @@
     },
     "cyclist": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/cyclist/download/cyclist-1.0.1.tgz",
       "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
       "dev": true
     },
     "dashdash": {
       "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/dashdash/download/dashdash-1.14.1.tgz",
       "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
       "dev": true,
       "requires": {
@@ -2390,13 +2390,13 @@
     },
     "decamelize": {
       "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/decamelize/download/decamelize-1.2.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdecamelize%2Fdownload%2Fdecamelize-1.2.0.tgz",
       "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
       "dev": true
     },
     "decode-uri-component": {
       "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/decode-uri-component/download/decode-uri-component-0.2.0.tgz",
       "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
       "dev": true
     },
@@ -2537,7 +2537,7 @@
     },
     "defaults": {
       "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz",
+      "resolved": "https://registry.npm.taobao.org/defaults/download/defaults-1.0.3.tgz",
       "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=",
       "dev": true,
       "requires": {
@@ -2611,7 +2611,7 @@
       "dependencies": {
         "globby": {
           "version": "6.1.0",
-          "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/globby/download/globby-6.1.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fglobby%2Fdownload%2Fglobby-6.1.0.tgz",
           "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=",
           "dev": true,
           "requires": {
@@ -2624,7 +2624,7 @@
           "dependencies": {
             "pify": {
               "version": "2.3.0",
-              "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+              "resolved": "https://registry.npm.taobao.org/pify/download/pify-2.3.0.tgz",
               "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
               "dev": true
             }
@@ -2640,13 +2640,13 @@
     },
     "delayed-stream": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/delayed-stream/download/delayed-stream-1.0.0.tgz",
       "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
       "dev": true
     },
     "depd": {
       "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/depd/download/depd-1.1.2.tgz",
       "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
       "dev": true
     },
@@ -2662,7 +2662,7 @@
     },
     "destroy": {
       "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
+      "resolved": "https://registry.npm.taobao.org/destroy/download/destroy-1.0.4.tgz",
       "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
       "dev": true
     },
@@ -2694,7 +2694,7 @@
     },
     "dns-equal": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/dns-equal/download/dns-equal-1.0.0.tgz",
       "integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=",
       "dev": true
     },
@@ -2710,7 +2710,7 @@
     },
     "dns-txt": {
       "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/dns-txt/download/dns-txt-2.0.2.tgz",
       "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=",
       "dev": true,
       "requires": {
@@ -2798,7 +2798,7 @@
     },
     "duplexer": {
       "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/duplexer/download/duplexer-0.1.1.tgz",
       "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
       "dev": true
     },
@@ -2816,13 +2816,13 @@
     },
     "easy-stack": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/easy-stack/download/easy-stack-1.0.0.tgz",
       "integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=",
       "dev": true
     },
     "ecc-jsbn": {
       "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/ecc-jsbn/download/ecc-jsbn-0.1.2.tgz",
       "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
       "dev": true,
       "requires": {
@@ -2832,7 +2832,7 @@
     },
     "ee-first": {
       "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/ee-first/download/ee-first-1.1.1.tgz",
       "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
       "dev": true
     },
@@ -2877,7 +2877,7 @@
     },
     "encodeurl": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/encodeurl/download/encodeurl-1.0.2.tgz",
       "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
       "dev": true
     },
@@ -2978,13 +2978,13 @@
     },
     "escape-html": {
       "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+      "resolved": "https://registry.npm.taobao.org/escape-html/download/escape-html-1.0.3.tgz",
       "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
       "dev": true
     },
     "escape-string-regexp": {
       "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "resolved": "https://registry.npm.taobao.org/escape-string-regexp/download/escape-string-regexp-1.0.5.tgz",
       "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
       "dev": true
     },
@@ -3021,7 +3021,7 @@
     },
     "etag": {
       "version": "1.8.1",
-      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/etag/download/etag-1.8.1.tgz",
       "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
       "dev": true
     },
@@ -3079,7 +3079,7 @@
     },
     "expand-brackets": {
       "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+      "resolved": "https://registry.npm.taobao.org/expand-brackets/download/expand-brackets-2.1.4.tgz",
       "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
       "dev": true,
       "requires": {
@@ -3103,7 +3103,7 @@
         },
         "define-property": {
           "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz",
           "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
           "dev": true,
           "requires": {
@@ -3112,7 +3112,7 @@
         },
         "extend-shallow": {
           "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz",
           "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
           "requires": {
@@ -3121,7 +3121,7 @@
         },
         "ms": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
           "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
           "dev": true
         }
@@ -3176,7 +3176,7 @@
         },
         "ms": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
           "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
           "dev": true
         },
@@ -3202,7 +3202,7 @@
     },
     "extend-shallow": {
       "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-3.0.2.tgz",
       "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
       "dev": true,
       "requires": {
@@ -3239,7 +3239,7 @@
       "dependencies": {
         "define-property": {
           "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-1.0.0.tgz",
           "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
           "dev": true,
           "requires": {
@@ -3248,7 +3248,7 @@
         },
         "extend-shallow": {
           "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz",
           "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
           "requires": {
@@ -3288,7 +3288,7 @@
     },
     "extsprintf": {
       "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/extsprintf/download/extsprintf-1.3.0.tgz",
       "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
       "dev": true
     },
@@ -3320,7 +3320,7 @@
     },
     "faye-websocket": {
       "version": "0.10.0",
-      "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/faye-websocket/download/faye-websocket-0.10.0.tgz",
       "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=",
       "dev": true,
       "requires": {
@@ -3351,7 +3351,7 @@
     },
     "fill-range": {
       "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/fill-range/download/fill-range-4.0.0.tgz",
       "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
       "dev": true,
       "requires": {
@@ -3363,7 +3363,7 @@
       "dependencies": {
         "extend-shallow": {
           "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz",
           "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
           "requires": {
@@ -3398,7 +3398,7 @@
         },
         "ms": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
           "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
           "dev": true
         }
@@ -3456,13 +3456,13 @@
     },
     "for-in": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/for-in/download/for-in-1.0.2.tgz",
       "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
       "dev": true
     },
     "forever-agent": {
       "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/forever-agent/download/forever-agent-0.6.1.tgz",
       "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
       "dev": true
     },
@@ -3479,13 +3479,13 @@
     },
     "forwarded": {
       "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/forwarded/download/forwarded-0.1.2.tgz",
       "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
       "dev": true
     },
     "fragment-cache": {
       "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/fragment-cache/download/fragment-cache-0.2.1.tgz",
       "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
       "dev": true,
       "requires": {
@@ -3494,13 +3494,13 @@
     },
     "fresh": {
       "version": "0.5.2",
-      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/fresh/download/fresh-0.5.2.tgz",
       "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
       "dev": true
     },
     "from2": {
       "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/from2/download/from2-2.3.0.tgz",
       "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
       "dev": true,
       "requires": {
@@ -3530,7 +3530,7 @@
     },
     "fs-write-stream-atomic": {
       "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
+      "resolved": "https://registry.npm.taobao.org/fs-write-stream-atomic/download/fs-write-stream-atomic-1.0.10.tgz",
       "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
       "dev": true,
       "requires": {
@@ -3576,13 +3576,13 @@
     },
     "get-value": {
       "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+      "resolved": "https://registry.npm.taobao.org/get-value/download/get-value-2.0.6.tgz",
       "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
       "dev": true
     },
     "getpass": {
       "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+      "resolved": "https://registry.npm.taobao.org/getpass/download/getpass-0.1.7.tgz",
       "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
       "dev": true,
       "requires": {
@@ -3605,7 +3605,7 @@
     },
     "glob-parent": {
       "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/glob-parent/download/glob-parent-3.1.0.tgz",
       "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
       "dev": true,
       "requires": {
@@ -3615,7 +3615,7 @@
       "dependencies": {
         "is-glob": {
           "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/is-glob/download/is-glob-3.1.0.tgz",
           "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
           "dev": true,
           "requires": {
@@ -3626,7 +3626,7 @@
     },
     "glob-to-regexp": {
       "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/glob-to-regexp/download/glob-to-regexp-0.3.0.tgz",
       "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=",
       "dev": true
     },
@@ -3684,7 +3684,7 @@
     },
     "har-schema": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/har-schema/download/har-schema-2.0.0.tgz",
       "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
       "dev": true
     },
@@ -3709,7 +3709,7 @@
     },
     "has-ansi": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/has-ansi/download/has-ansi-2.0.0.tgz",
       "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
       "dev": true,
       "requires": {
@@ -3718,7 +3718,7 @@
       "dependencies": {
         "ansi-regex": {
           "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/ansi-regex/download/ansi-regex-2.1.1.tgz",
           "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
           "dev": true
         }
@@ -3726,7 +3726,7 @@
     },
     "has-flag": {
       "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/has-flag/download/has-flag-3.0.0.tgz",
       "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
       "dev": true
     },
@@ -3738,7 +3738,7 @@
     },
     "has-value": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/has-value/download/has-value-1.0.0.tgz",
       "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
       "dev": true,
       "requires": {
@@ -3749,7 +3749,7 @@
     },
     "has-values": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/has-values/download/has-values-1.0.0.tgz",
       "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
       "dev": true,
       "requires": {
@@ -3759,7 +3759,7 @@
       "dependencies": {
         "kind-of": {
           "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-4.0.0.tgz?cache=0&sync_timestamp=1579193926808&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fkind-of%2Fdownload%2Fkind-of-4.0.0.tgz",
           "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
           "dev": true,
           "requires": {
@@ -3770,7 +3770,7 @@
     },
     "hash-base": {
       "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
+      "resolved": "https://registry.npm.taobao.org/hash-base/download/hash-base-3.0.4.tgz",
       "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
       "dev": true,
       "requires": {
@@ -3814,7 +3814,7 @@
     },
     "hmac-drbg": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/hmac-drbg/download/hmac-drbg-1.0.1.tgz",
       "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
       "dev": true,
       "requires": {
@@ -3837,7 +3837,7 @@
     },
     "hpack.js": {
       "version": "2.1.6",
-      "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
+      "resolved": "https://registry.npm.taobao.org/hpack.js/download/hpack.js-2.1.6.tgz",
       "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=",
       "dev": true,
       "requires": {
@@ -3849,13 +3849,13 @@
     },
     "hsl-regex": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/hsl-regex/-/hsl-regex-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/hsl-regex/download/hsl-regex-1.0.0.tgz",
       "integrity": "sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4=",
       "dev": true
     },
     "hsla-regex": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/hsla-regex/-/hsla-regex-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/hsla-regex/download/hsla-regex-1.0.0.tgz",
       "integrity": "sha1-wc56MWjIxmFAM6S194d/OyJfnDg=",
       "dev": true
     },
@@ -3888,7 +3888,7 @@
     },
     "html-webpack-plugin": {
       "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-3.2.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/html-webpack-plugin/download/html-webpack-plugin-3.2.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhtml-webpack-plugin%2Fdownload%2Fhtml-webpack-plugin-3.2.0.tgz",
       "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=",
       "dev": true,
       "requires": {
@@ -3909,13 +3909,13 @@
         },
         "json5": {
           "version": "0.5.1",
-          "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/json5/download/json5-0.5.1.tgz",
           "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
           "dev": true
         },
         "loader-utils": {
           "version": "0.2.17",
-          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz",
+          "resolved": "https://registry.npm.taobao.org/loader-utils/download/loader-utils-0.2.17.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Floader-utils%2Fdownload%2Floader-utils-0.2.17.tgz",
           "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=",
           "dev": true,
           "requires": {
@@ -3972,7 +3972,7 @@
     },
     "http-deceiver": {
       "version": "1.2.7",
-      "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
+      "resolved": "https://registry.npm.taobao.org/http-deceiver/download/http-deceiver-1.2.7.tgz",
       "integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=",
       "dev": true
     },
@@ -3991,7 +3991,7 @@
       "dependencies": {
         "inherits": {
           "version": "2.0.3",
-          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz",
           "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
           "dev": true
         }
@@ -3999,7 +3999,7 @@
     },
     "http-parser-js": {
       "version": "0.4.10",
-      "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz",
+      "resolved": "https://registry.npm.taobao.org/http-parser-js/download/http-parser-js-0.4.10.tgz",
       "integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=",
       "dev": true
     },
@@ -4028,7 +4028,7 @@
     },
     "http-signature": {
       "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/http-signature/download/http-signature-1.2.0.tgz?cache=0&sync_timestamp=1572997318670&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fhttp-signature%2Fdownload%2Fhttp-signature-1.2.0.tgz",
       "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
       "dev": true,
       "requires": {
@@ -4039,7 +4039,7 @@
     },
     "https-browserify": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/https-browserify/download/https-browserify-1.0.0.tgz",
       "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
       "dev": true
     },
@@ -4075,7 +4075,7 @@
     },
     "iferr": {
       "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
+      "resolved": "https://registry.npm.taobao.org/iferr/download/iferr-0.1.5.tgz",
       "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
       "dev": true
     },
@@ -4087,7 +4087,7 @@
     },
     "import-cwd": {
       "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/import-cwd/download/import-cwd-2.1.0.tgz",
       "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=",
       "dev": true,
       "requires": {
@@ -4096,7 +4096,7 @@
     },
     "import-fresh": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/import-fresh/download/import-fresh-2.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fimport-fresh%2Fdownload%2Fimport-fresh-2.0.0.tgz",
       "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=",
       "dev": true,
       "requires": {
@@ -4106,7 +4106,7 @@
     },
     "import-from": {
       "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/import-from/-/import-from-2.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/import-from/download/import-from-2.1.0.tgz",
       "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=",
       "dev": true,
       "requires": {
@@ -4179,7 +4179,7 @@
     },
     "imurmurhash": {
       "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "resolved": "https://registry.npm.taobao.org/imurmurhash/download/imurmurhash-0.1.4.tgz",
       "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
       "dev": true
     },
@@ -4191,7 +4191,7 @@
     },
     "indexes-of": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/indexes-of/download/indexes-of-1.0.1.tgz",
       "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
       "dev": true
     },
@@ -4253,7 +4253,7 @@
     },
     "ip": {
       "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz",
+      "resolved": "https://registry.npm.taobao.org/ip/download/ip-1.1.5.tgz",
       "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=",
       "dev": true
     },
@@ -4270,13 +4270,13 @@
     },
     "is-absolute-url": {
       "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/is-absolute-url/download/is-absolute-url-2.1.0.tgz?cache=0&sync_timestamp=1569735663940&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-absolute-url%2Fdownload%2Fis-absolute-url-2.1.0.tgz",
       "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=",
       "dev": true
     },
     "is-accessor-descriptor": {
       "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
+      "resolved": "https://registry.npm.taobao.org/is-accessor-descriptor/download/is-accessor-descriptor-0.1.6.tgz",
       "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
       "dev": true,
       "requires": {
@@ -4285,7 +4285,7 @@
       "dependencies": {
         "kind-of": {
           "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz?cache=0&sync_timestamp=1579193926808&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fkind-of%2Fdownload%2Fkind-of-3.2.2.tgz",
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
@@ -4302,7 +4302,7 @@
     },
     "is-arrayish": {
       "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/is-arrayish/download/is-arrayish-0.2.1.tgz",
       "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
       "dev": true
     },
@@ -4337,7 +4337,7 @@
     },
     "is-color-stop": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-color-stop/-/is-color-stop-1.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/is-color-stop/download/is-color-stop-1.1.0.tgz",
       "integrity": "sha1-z/9HGu5N1cnhWFmPvhKWe1za00U=",
       "dev": true,
       "requires": {
@@ -4351,7 +4351,7 @@
     },
     "is-data-descriptor": {
       "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
+      "resolved": "https://registry.npm.taobao.org/is-data-descriptor/download/is-data-descriptor-0.1.4.tgz",
       "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
       "dev": true,
       "requires": {
@@ -4360,7 +4360,7 @@
       "dependencies": {
         "kind-of": {
           "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz?cache=0&sync_timestamp=1579193926808&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fkind-of%2Fdownload%2Fkind-of-3.2.2.tgz",
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
@@ -4396,13 +4396,13 @@
     },
     "is-directory": {
       "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/is-directory/download/is-directory-0.3.1.tgz",
       "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=",
       "dev": true
     },
     "is-extendable": {
       "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/is-extendable/download/is-extendable-0.1.1.tgz",
       "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
       "dev": true
     },
@@ -4414,7 +4414,7 @@
     },
     "is-fullwidth-code-point": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-2.0.0.tgz",
       "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
       "dev": true
     },
@@ -4429,7 +4429,7 @@
     },
     "is-number": {
       "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/is-number/download/is-number-3.0.0.tgz",
       "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
       "dev": true,
       "requires": {
@@ -4438,7 +4438,7 @@
       "dependencies": {
         "kind-of": {
           "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz?cache=0&sync_timestamp=1579193926808&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fkind-of%2Fdownload%2Fkind-of-3.2.2.tgz",
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
@@ -4479,7 +4479,7 @@
     },
     "is-plain-obj": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/is-plain-obj/download/is-plain-obj-1.1.0.tgz?cache=0&sync_timestamp=1579602956062&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-plain-obj%2Fdownload%2Fis-plain-obj-1.1.0.tgz",
       "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
       "dev": true
     },
@@ -4509,7 +4509,7 @@
     },
     "is-stream": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/is-stream/download/is-stream-1.1.0.tgz",
       "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
       "dev": true
     },
@@ -4533,7 +4533,7 @@
     },
     "is-typedarray": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/is-typedarray/download/is-typedarray-1.0.0.tgz",
       "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
       "dev": true
     },
@@ -4545,19 +4545,19 @@
     },
     "is-wsl": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/is-wsl/download/is-wsl-1.1.0.tgz?cache=0&sync_timestamp=1569219566107&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fis-wsl%2Fdownload%2Fis-wsl-1.1.0.tgz",
       "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
       "dev": true
     },
     "isarray": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/isarray/download/isarray-1.0.0.tgz",
       "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
       "dev": true
     },
     "isexe": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/isexe/download/isexe-2.0.0.tgz",
       "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
       "dev": true
     },
@@ -4569,7 +4569,7 @@
     },
     "isstream": {
       "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/isstream/download/isstream-0.1.2.tgz",
       "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
       "dev": true
     },
@@ -4608,13 +4608,13 @@
     },
     "js-message": {
       "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz",
+      "resolved": "https://registry.npm.taobao.org/js-message/download/js-message-1.0.5.tgz?cache=0&sync_timestamp=1575284239628&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fjs-message%2Fdownload%2Fjs-message-1.0.5.tgz",
       "integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=",
       "dev": true
     },
     "js-queue": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/js-queue/download/js-queue-2.0.0.tgz",
       "integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=",
       "dev": true,
       "requires": {
@@ -4639,7 +4639,7 @@
     },
     "jsbn": {
       "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/jsbn/download/jsbn-0.1.1.tgz",
       "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
       "dev": true
     },
@@ -4651,7 +4651,7 @@
     },
     "json-schema": {
       "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+      "resolved": "https://registry.npm.taobao.org/json-schema/download/json-schema-0.2.3.tgz",
       "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
       "dev": true
     },
@@ -4663,7 +4663,7 @@
     },
     "json-stringify-safe": {
       "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/json-stringify-safe/download/json-stringify-safe-5.0.1.tgz",
       "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
       "dev": true
     },
@@ -4684,7 +4684,7 @@
     },
     "jsonfile": {
       "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/jsonfile/download/jsonfile-4.0.0.tgz",
       "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
       "dev": true,
       "requires": {
@@ -4693,7 +4693,7 @@
     },
     "jsprim": {
       "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/jsprim/download/jsprim-1.4.1.tgz",
       "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
       "dev": true,
       "requires": {
@@ -4745,7 +4745,7 @@
     },
     "lines-and-columns": {
       "version": "1.1.6",
-      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
+      "resolved": "https://registry.npm.taobao.org/lines-and-columns/download/lines-and-columns-1.1.6.tgz",
       "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=",
       "dev": true
     },
@@ -4790,25 +4790,25 @@
     },
     "lodash.mapvalues": {
       "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/lodash.mapvalues/-/lodash.mapvalues-4.6.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/lodash.mapvalues/download/lodash.mapvalues-4.6.0.tgz",
       "integrity": "sha1-G6+lAF3p3W9PJmaMMMo3IwzJaJw=",
       "dev": true
     },
     "lodash.memoize": {
       "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/lodash.memoize/download/lodash.memoize-4.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash.memoize%2Fdownload%2Flodash.memoize-4.1.2.tgz",
       "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
       "dev": true
     },
     "lodash.transform": {
       "version": "4.6.0",
-      "resolved": "https://registry.npmjs.org/lodash.transform/-/lodash.transform-4.6.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/lodash.transform/download/lodash.transform-4.6.0.tgz",
       "integrity": "sha1-EjBkIvYzJK7YSD0/ODMrX2cFR6A=",
       "dev": true
     },
     "lodash.uniq": {
       "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/lodash.uniq/download/lodash.uniq-4.5.0.tgz",
       "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
       "dev": true
     },
@@ -4829,7 +4829,7 @@
     },
     "lower-case": {
       "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz",
+      "resolved": "https://registry.npm.taobao.org/lower-case/download/lower-case-1.1.4.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flower-case%2Fdownload%2Flower-case-1.1.4.tgz",
       "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=",
       "dev": true
     },
@@ -4862,13 +4862,13 @@
     },
     "map-cache": {
       "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/map-cache/download/map-cache-0.2.2.tgz",
       "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
       "dev": true
     },
     "map-visit": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/map-visit/download/map-visit-1.0.0.tgz",
       "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
       "dev": true,
       "requires": {
@@ -4894,7 +4894,7 @@
     },
     "media-typer": {
       "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/media-typer/download/media-typer-0.3.0.tgz",
       "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
       "dev": true
     },
@@ -4919,7 +4919,7 @@
     },
     "memory-fs": {
       "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/memory-fs/download/memory-fs-0.4.1.tgz?cache=0&sync_timestamp=1570537511500&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fmemory-fs%2Fdownload%2Fmemory-fs-0.4.1.tgz",
       "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
       "dev": true,
       "requires": {
@@ -4929,7 +4929,7 @@
     },
     "merge-descriptors": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/merge-descriptors/download/merge-descriptors-1.0.1.tgz",
       "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
       "dev": true
     },
@@ -4956,7 +4956,7 @@
     },
     "methods": {
       "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/methods/download/methods-1.1.2.tgz",
       "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
       "dev": true
     },
@@ -5032,7 +5032,7 @@
       "dependencies": {
         "normalize-url": {
           "version": "1.9.1",
-          "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/normalize-url/download/normalize-url-1.9.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fnormalize-url%2Fdownload%2Fnormalize-url-1.9.1.tgz",
           "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=",
           "dev": true,
           "requires": {
@@ -5063,7 +5063,7 @@
     },
     "minimalistic-crypto-utils": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/minimalistic-crypto-utils/download/minimalistic-crypto-utils-1.0.1.tgz",
       "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
       "dev": true
     },
@@ -5181,7 +5181,7 @@
     },
     "move-concurrently": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/move-concurrently/download/move-concurrently-1.0.1.tgz",
       "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
       "dev": true,
       "requires": {
@@ -5211,7 +5211,7 @@
     },
     "multicast-dns-service-types": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/multicast-dns-service-types/download/multicast-dns-service-types-1.1.0.tgz",
       "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
       "dev": true
     },
@@ -5322,7 +5322,7 @@
       "dependencies": {
         "punycode": {
           "version": "1.4.1",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/punycode/download/punycode-1.4.1.tgz",
           "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
           "dev": true
         }
@@ -5362,7 +5362,7 @@
     },
     "normalize-range": {
       "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/normalize-range/download/normalize-range-0.1.2.tgz",
       "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
       "dev": true
     },
@@ -5374,7 +5374,7 @@
     },
     "npm-run-path": {
       "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/npm-run-path/download/npm-run-path-2.0.2.tgz",
       "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
       "dev": true,
       "requires": {
@@ -5392,13 +5392,13 @@
     },
     "num2fraction": {
       "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/num2fraction/download/num2fraction-1.2.2.tgz",
       "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=",
       "dev": true
     },
     "number-is-nan": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/number-is-nan/download/number-is-nan-1.0.1.tgz",
       "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
       "dev": true
     },
@@ -5410,13 +5410,13 @@
     },
     "object-assign": {
       "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/object-assign/download/object-assign-4.1.1.tgz",
       "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
       "dev": true
     },
     "object-copy": {
       "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/object-copy/download/object-copy-0.1.0.tgz",
       "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
       "dev": true,
       "requires": {
@@ -5427,7 +5427,7 @@
       "dependencies": {
         "define-property": {
           "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz",
           "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
           "dev": true,
           "requires": {
@@ -5436,7 +5436,7 @@
         },
         "kind-of": {
           "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz?cache=0&sync_timestamp=1579193926808&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fkind-of%2Fdownload%2Fkind-of-3.2.2.tgz",
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
@@ -5469,7 +5469,7 @@
     },
     "object-visit": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/object-visit/download/object-visit-1.0.1.tgz",
       "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
       "dev": true,
       "requires": {
@@ -5500,7 +5500,7 @@
     },
     "object.pick": {
       "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/object.pick/download/object.pick-1.3.0.tgz",
       "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
       "dev": true,
       "requires": {
@@ -5527,7 +5527,7 @@
     },
     "on-finished": {
       "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/on-finished/download/on-finished-2.3.0.tgz",
       "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
       "dev": true,
       "requires": {
@@ -5551,7 +5551,7 @@
     },
     "onetime": {
       "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/onetime/download/onetime-2.0.1.tgz",
       "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
       "dev": true,
       "requires": {
@@ -5624,7 +5624,7 @@
     },
     "os-browserify": {
       "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/os-browserify/download/os-browserify-0.3.0.tgz",
       "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
       "dev": true
     },
@@ -5641,13 +5641,13 @@
     },
     "p-defer": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/p-defer/download/p-defer-1.0.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fp-defer%2Fdownload%2Fp-defer-1.0.0.tgz",
       "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
       "dev": true
     },
     "p-finally": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/p-finally/download/p-finally-1.0.0.tgz",
       "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
       "dev": true
     },
@@ -5718,7 +5718,7 @@
     },
     "param-case": {
       "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/param-case/download/param-case-2.1.1.tgz?cache=0&sync_timestamp=1576721509342&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fparam-case%2Fdownload%2Fparam-case-2.1.1.tgz",
       "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=",
       "dev": true,
       "requires": {
@@ -5774,7 +5774,7 @@
     },
     "pascalcase": {
       "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/pascalcase/download/pascalcase-0.1.1.tgz",
       "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
       "dev": true
     },
@@ -5786,7 +5786,7 @@
     },
     "path-dirname": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/path-dirname/download/path-dirname-1.0.2.tgz",
       "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
       "dev": true
     },
@@ -5804,13 +5804,13 @@
     },
     "path-is-inside": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/path-is-inside/download/path-is-inside-1.0.2.tgz",
       "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
       "dev": true
     },
     "path-key": {
       "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/path-key/download/path-key-2.0.1.tgz?cache=0&sync_timestamp=1574441404712&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fpath-key%2Fdownload%2Fpath-key-2.0.1.tgz",
       "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
       "dev": true
     },
@@ -5822,7 +5822,7 @@
     },
     "path-to-regexp": {
       "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
+      "resolved": "https://registry.npm.taobao.org/path-to-regexp/download/path-to-regexp-0.1.7.tgz",
       "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
       "dev": true
     },
@@ -5837,7 +5837,7 @@
       "dependencies": {
         "pify": {
           "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/pify/download/pify-3.0.0.tgz",
           "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
           "dev": true
         }
@@ -5858,7 +5858,7 @@
     },
     "performance-now": {
       "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/performance-now/download/performance-now-2.1.0.tgz",
       "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
       "dev": true
     },
@@ -5876,13 +5876,13 @@
     },
     "pinkie": {
       "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+      "resolved": "https://registry.npm.taobao.org/pinkie/download/pinkie-2.0.4.tgz",
       "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
       "dev": true
     },
     "pinkie-promise": {
       "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/pinkie-promise/download/pinkie-promise-2.0.1.tgz",
       "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
       "dev": true,
       "requires": {
@@ -5991,7 +5991,7 @@
     },
     "posix-character-classes": {
       "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/posix-character-classes/download/posix-character-classes-0.1.1.tgz",
       "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
       "dev": true
     },
@@ -6575,7 +6575,7 @@
     },
     "prepend-http": {
       "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
+      "resolved": "https://registry.npm.taobao.org/prepend-http/download/prepend-http-1.0.4.tgz",
       "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
       "dev": true
     },
@@ -6588,7 +6588,7 @@
     },
     "pretty-error": {
       "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-2.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/pretty-error/download/pretty-error-2.1.1.tgz",
       "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=",
       "dev": true,
       "requires": {
@@ -6598,7 +6598,7 @@
     },
     "process": {
       "version": "0.11.10",
-      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "resolved": "https://registry.npm.taobao.org/process/download/process-0.11.10.tgz",
       "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
       "dev": true
     },
@@ -6610,7 +6610,7 @@
     },
     "promise-inflight": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/promise-inflight/download/promise-inflight-1.0.1.tgz",
       "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
       "dev": true
     },
@@ -6626,13 +6626,13 @@
     },
     "prr": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/prr/download/prr-1.0.1.tgz",
       "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
       "dev": true
     },
     "pseudomap": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/pseudomap/download/pseudomap-1.0.2.tgz",
       "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
       "dev": true
     },
@@ -6697,7 +6697,7 @@
     },
     "q": {
       "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/q/download/q-1.5.1.tgz",
       "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
       "dev": true
     },
@@ -6709,7 +6709,7 @@
     },
     "query-string": {
       "version": "4.3.4",
-      "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
+      "resolved": "https://registry.npm.taobao.org/query-string/download/query-string-4.3.4.tgz",
       "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
       "dev": true,
       "requires": {
@@ -6719,13 +6719,13 @@
     },
     "querystring": {
       "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/querystring/download/querystring-0.2.0.tgz",
       "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
       "dev": true
     },
     "querystring-es3": {
       "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/querystring-es3/download/querystring-es3-0.2.1.tgz",
       "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
       "dev": true
     },
@@ -6847,13 +6847,13 @@
     },
     "relateurl": {
       "version": "0.2.7",
-      "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+      "resolved": "https://registry.npm.taobao.org/relateurl/download/relateurl-0.2.7.tgz",
       "integrity": "sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=",
       "dev": true
     },
     "remove-trailing-separator": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/remove-trailing-separator/download/remove-trailing-separator-1.1.0.tgz",
       "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
       "dev": true
     },
@@ -6872,13 +6872,13 @@
       "dependencies": {
         "ansi-regex": {
           "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/ansi-regex/download/ansi-regex-2.1.1.tgz",
           "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
           "dev": true
         },
         "css-select": {
           "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/css-select/download/css-select-1.2.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcss-select%2Fdownload%2Fcss-select-1.2.0.tgz",
           "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
           "dev": true,
           "requires": {
@@ -6896,7 +6896,7 @@
         },
         "domutils": {
           "version": "1.5.1",
-          "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/domutils/download/domutils-1.5.1.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdomutils%2Fdownload%2Fdomutils-1.5.1.tgz",
           "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
           "dev": true,
           "requires": {
@@ -6906,7 +6906,7 @@
         },
         "strip-ansi": {
           "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-3.0.1.tgz?cache=0&sync_timestamp=1573280577145&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-3.0.1.tgz",
           "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
           "dev": true,
           "requires": {
@@ -6923,7 +6923,7 @@
     },
     "repeat-string": {
       "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/repeat-string/download/repeat-string-1.6.1.tgz",
       "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
       "dev": true
     },
@@ -6977,7 +6977,7 @@
     },
     "require-directory": {
       "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/require-directory/download/require-directory-2.1.1.tgz",
       "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
       "dev": true
     },
@@ -6989,7 +6989,7 @@
     },
     "requires-port": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/requires-port/download/requires-port-1.0.0.tgz",
       "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
       "dev": true
     },
@@ -7004,7 +7004,7 @@
     },
     "resolve-cwd": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/resolve-cwd/download/resolve-cwd-2.0.0.tgz",
       "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
       "dev": true,
       "requires": {
@@ -7013,19 +7013,19 @@
     },
     "resolve-from": {
       "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/resolve-from/download/resolve-from-3.0.0.tgz",
       "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
       "dev": true
     },
     "resolve-url": {
       "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/resolve-url/download/resolve-url-0.2.1.tgz",
       "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
       "dev": true
     },
     "restore-cursor": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/restore-cursor/download/restore-cursor-2.0.0.tgz",
       "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
       "dev": true,
       "requires": {
@@ -7041,19 +7041,19 @@
     },
     "retry": {
       "version": "0.12.0",
-      "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/retry/download/retry-0.12.0.tgz",
       "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=",
       "dev": true
     },
     "rgb-regex": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/rgb-regex/-/rgb-regex-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/rgb-regex/download/rgb-regex-1.0.1.tgz",
       "integrity": "sha1-wODWiC3w4jviVKR16O3UGRX+rrE=",
       "dev": true
     },
     "rgba-regex": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/rgba-regex/-/rgba-regex-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/rgba-regex/download/rgba-regex-1.0.0.tgz",
       "integrity": "sha1-QzdOLiyglosO8VI0YLfXMP8i7rM=",
       "dev": true
     },
@@ -7078,7 +7078,7 @@
     },
     "run-queue": {
       "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
+      "resolved": "https://registry.npm.taobao.org/run-queue/download/run-queue-1.0.3.tgz",
       "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
       "dev": true,
       "requires": {
@@ -7093,7 +7093,7 @@
     },
     "safe-regex": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/safe-regex/download/safe-regex-1.1.0.tgz?cache=0&sync_timestamp=1571687571136&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsafe-regex%2Fdownload%2Fsafe-regex-1.1.0.tgz",
       "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
       "dev": true,
       "requires": {
@@ -7146,7 +7146,7 @@
     },
     "select-hose": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/select-hose/download/select-hose-2.0.0.tgz",
       "integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=",
       "dev": true
     },
@@ -7197,7 +7197,7 @@
           "dependencies": {
             "ms": {
               "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+              "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
               "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
               "dev": true
             }
@@ -7225,7 +7225,7 @@
     },
     "serve-index": {
       "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/serve-index/download/serve-index-1.9.1.tgz",
       "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
       "dev": true,
       "requires": {
@@ -7249,7 +7249,7 @@
         },
         "http-errors": {
           "version": "1.6.3",
-          "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+          "resolved": "https://registry.npm.taobao.org/http-errors/download/http-errors-1.6.3.tgz",
           "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
           "dev": true,
           "requires": {
@@ -7261,13 +7261,13 @@
         },
         "inherits": {
           "version": "2.0.3",
-          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz",
           "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
           "dev": true
         },
         "ms": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
           "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
           "dev": true
         },
@@ -7293,7 +7293,7 @@
     },
     "set-blocking": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/set-blocking/download/set-blocking-2.0.0.tgz",
       "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
       "dev": true
     },
@@ -7311,7 +7311,7 @@
       "dependencies": {
         "extend-shallow": {
           "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz",
           "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
           "requires": {
@@ -7322,7 +7322,7 @@
     },
     "setimmediate": {
       "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+      "resolved": "https://registry.npm.taobao.org/setimmediate/download/setimmediate-1.0.5.tgz",
       "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
       "dev": true
     },
@@ -7353,7 +7353,7 @@
     },
     "shebang-command": {
       "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/shebang-command/download/shebang-command-1.2.0.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fshebang-command%2Fdownload%2Fshebang-command-1.2.0.tgz",
       "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
       "dev": true,
       "requires": {
@@ -7362,7 +7362,7 @@
     },
     "shebang-regex": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/shebang-regex/download/shebang-regex-1.0.0.tgz",
       "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
       "dev": true
     },
@@ -7391,7 +7391,7 @@
     },
     "simple-swizzle": {
       "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/simple-swizzle/download/simple-swizzle-0.2.2.tgz",
       "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=",
       "dev": true,
       "requires": {
@@ -7408,7 +7408,7 @@
     },
     "slash": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/slash/download/slash-1.0.0.tgz",
       "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
       "dev": true
     },
@@ -7439,7 +7439,7 @@
         },
         "define-property": {
           "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz",
           "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
           "dev": true,
           "requires": {
@@ -7448,7 +7448,7 @@
         },
         "extend-shallow": {
           "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/extend-shallow/download/extend-shallow-2.0.1.tgz",
           "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
           "requires": {
@@ -7457,13 +7457,13 @@
         },
         "ms": {
           "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
           "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
           "dev": true
         },
         "source-map": {
           "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
+          "resolved": "https://registry.npm.taobao.org/source-map/download/source-map-0.5.7.tgz",
           "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
           "dev": true
         }
@@ -7482,7 +7482,7 @@
       "dependencies": {
         "define-property": {
           "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-1.0.0.tgz",
           "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
           "dev": true,
           "requires": {
@@ -7531,7 +7531,7 @@
       "dependencies": {
         "kind-of": {
           "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz?cache=0&sync_timestamp=1579193926808&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fkind-of%2Fdownload%2Fkind-of-3.2.2.tgz",
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
@@ -7586,7 +7586,7 @@
     },
     "sort-keys": {
       "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/sort-keys/download/sort-keys-1.1.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fsort-keys%2Fdownload%2Fsort-keys-1.1.2.tgz",
       "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=",
       "dev": true,
       "requires": {
@@ -7630,7 +7630,7 @@
     },
     "source-map-url": {
       "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/source-map-url/download/source-map-url-0.4.0.tgz",
       "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
       "dev": true
     },
@@ -7717,7 +7717,7 @@
     },
     "sprintf-js": {
       "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "resolved": "https://registry.npm.taobao.org/sprintf-js/download/sprintf-js-1.0.3.tgz",
       "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
       "dev": true
     },
@@ -7762,7 +7762,7 @@
     },
     "static-extend": {
       "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/static-extend/download/static-extend-0.1.2.tgz",
       "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
       "dev": true,
       "requires": {
@@ -7772,7 +7772,7 @@
       "dependencies": {
         "define-property": {
           "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "resolved": "https://registry.npm.taobao.org/define-property/download/define-property-0.2.5.tgz",
           "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
           "dev": true,
           "requires": {
@@ -7783,13 +7783,13 @@
     },
     "statuses": {
       "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/statuses/download/statuses-1.5.0.tgz",
       "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
       "dev": true
     },
     "stealthy-require": {
       "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/stealthy-require/download/stealthy-require-1.1.1.tgz",
       "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=",
       "dev": true
     },
@@ -7834,7 +7834,7 @@
     },
     "strict-uri-encode": {
       "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/strict-uri-encode/download/strict-uri-encode-1.1.0.tgz",
       "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
       "dev": true
     },
@@ -7850,13 +7850,13 @@
       "dependencies": {
         "ansi-regex": {
           "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/ansi-regex/download/ansi-regex-3.0.0.tgz",
           "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
           "dev": true
         },
         "strip-ansi": {
           "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-4.0.0.tgz?cache=0&sync_timestamp=1573280577145&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-4.0.0.tgz",
           "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
           "dev": true,
           "requires": {
@@ -7943,7 +7943,7 @@
     },
     "strip-eof": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/strip-eof/download/strip-eof-1.0.0.tgz",
       "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
       "dev": true
     },
@@ -8094,7 +8094,7 @@
     },
     "thenify": {
       "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/thenify/download/thenify-3.3.0.tgz",
       "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=",
       "dev": true,
       "requires": {
@@ -8103,7 +8103,7 @@
     },
     "thenify-all": {
       "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/thenify-all/download/thenify-all-1.6.0.tgz",
       "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=",
       "dev": true,
       "requires": {
@@ -8148,19 +8148,19 @@
     },
     "timsort": {
       "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/timsort/download/timsort-0.3.0.tgz",
       "integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=",
       "dev": true
     },
     "to-arraybuffer": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/to-arraybuffer/download/to-arraybuffer-1.0.1.tgz",
       "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
       "dev": true
     },
     "to-object-path": {
       "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/to-object-path/download/to-object-path-0.3.0.tgz",
       "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
       "dev": true,
       "requires": {
@@ -8169,7 +8169,7 @@
       "dependencies": {
         "kind-of": {
           "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "resolved": "https://registry.npm.taobao.org/kind-of/download/kind-of-3.2.2.tgz?cache=0&sync_timestamp=1579193926808&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fkind-of%2Fdownload%2Fkind-of-3.2.2.tgz",
           "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
           "dev": true,
           "requires": {
@@ -8192,7 +8192,7 @@
     },
     "to-regex-range": {
       "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/to-regex-range/download/to-regex-range-2.1.1.tgz",
       "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
       "dev": true,
       "requires": {
@@ -8208,7 +8208,7 @@
     },
     "toposort": {
       "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/toposort/-/toposort-1.0.7.tgz",
+      "resolved": "https://registry.npm.taobao.org/toposort/download/toposort-1.0.7.tgz",
       "integrity": "sha1-LmhELZ9k7HILjMieZEOsbKqVACk=",
       "dev": true
     },
@@ -8242,13 +8242,13 @@
     },
     "tty-browserify": {
       "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/tty-browserify/download/tty-browserify-0.0.0.tgz",
       "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
       "dev": true
     },
     "tunnel-agent": {
       "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/tunnel-agent/download/tunnel-agent-0.6.0.tgz",
       "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
       "dev": true,
       "requires": {
@@ -8257,7 +8257,7 @@
     },
     "tweetnacl": {
       "version": "0.14.5",
-      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+      "resolved": "https://registry.npm.taobao.org/tweetnacl/download/tweetnacl-0.14.5.tgz",
       "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
       "dev": true
     },
@@ -8279,7 +8279,7 @@
     },
     "typedarray": {
       "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+      "resolved": "https://registry.npm.taobao.org/typedarray/download/typedarray-0.0.6.tgz",
       "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
       "dev": true
     },
@@ -8315,13 +8315,13 @@
     },
     "uniq": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/uniq/download/uniq-1.0.1.tgz",
       "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
       "dev": true
     },
     "uniqs": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/uniqs/download/uniqs-2.0.0.tgz",
       "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=",
       "dev": true
     },
@@ -8351,19 +8351,19 @@
     },
     "unpipe": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/unpipe/download/unpipe-1.0.0.tgz",
       "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
       "dev": true
     },
     "unquote": {
       "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/unquote/-/unquote-1.1.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/unquote/download/unquote-1.1.1.tgz",
       "integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=",
       "dev": true
     },
     "unset-value": {
       "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/unset-value/download/unset-value-1.0.0.tgz",
       "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
       "dev": true,
       "requires": {
@@ -8373,7 +8373,7 @@
       "dependencies": {
         "has-value": {
           "version": "0.3.1",
-          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/has-value/download/has-value-0.3.1.tgz",
           "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
           "dev": true,
           "requires": {
@@ -8384,7 +8384,7 @@
           "dependencies": {
             "isobject": {
               "version": "2.1.0",
-              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+              "resolved": "https://registry.npm.taobao.org/isobject/download/isobject-2.1.0.tgz",
               "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
               "dev": true,
               "requires": {
@@ -8395,7 +8395,7 @@
         },
         "has-values": {
           "version": "0.1.4",
-          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+          "resolved": "https://registry.npm.taobao.org/has-values/download/has-values-0.1.4.tgz",
           "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
           "dev": true
         }
@@ -8409,7 +8409,7 @@
     },
     "upper-case": {
       "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz",
+      "resolved": "https://registry.npm.taobao.org/upper-case/download/upper-case-1.1.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fupper-case%2Fdownload%2Fupper-case-1.1.3.tgz",
       "integrity": "sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=",
       "dev": true
     },
@@ -8424,13 +8424,13 @@
     },
     "urix": {
       "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/urix/download/urix-0.1.0.tgz",
       "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
       "dev": true
     },
     "url": {
       "version": "0.11.0",
-      "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/url/download/url-0.11.0.tgz",
       "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
       "dev": true,
       "requires": {
@@ -8440,7 +8440,7 @@
       "dependencies": {
         "punycode": {
           "version": "1.3.2",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+          "resolved": "https://registry.npm.taobao.org/punycode/download/punycode-1.3.2.tgz",
           "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
           "dev": true
         }
@@ -8484,7 +8484,7 @@
       "dependencies": {
         "inherits": {
           "version": "2.0.3",
-          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "resolved": "https://registry.npm.taobao.org/inherits/download/inherits-2.0.3.tgz",
           "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
           "dev": true
         }
@@ -8492,7 +8492,7 @@
     },
     "util-deprecate": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz",
       "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
       "dev": true
     },
@@ -8510,13 +8510,13 @@
     },
     "utila": {
       "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/utila/download/utila-0.4.0.tgz",
       "integrity": "sha1-ihagXURWV6Oupe7MWxKk+lN5dyw=",
       "dev": true
     },
     "utils-merge": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/utils-merge/download/utils-merge-1.0.1.tgz",
       "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
       "dev": true
     },
@@ -8538,7 +8538,7 @@
     },
     "vary": {
       "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "resolved": "https://registry.npm.taobao.org/vary/download/vary-1.1.2.tgz",
       "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
       "dev": true
     },
@@ -8550,7 +8550,7 @@
     },
     "verror": {
       "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/verror/download/verror-1.10.0.tgz",
       "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
       "dev": true,
       "requires": {
@@ -8570,6 +8570,11 @@
       "resolved": "https://registry.npm.taobao.org/vue/download/vue-2.6.11.tgz",
       "integrity": "sha1-dllNh31LEiNEBuhONSdcbVFBJcU="
     },
+    "vue-axios": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/vue-axios/-/vue-axios-2.1.5.tgz",
+      "integrity": "sha512-th5xVbInVoyIoe+qY+9GCflEVezxAvztD4xpFF39SRQYqpoKD2qkmX8yv08jJG9a2SgNOCjirjJGSwg/wTrbmA=="
+    },
     "vue-cli-plugin-vuetify": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/vue-cli-plugin-vuetify/-/vue-cli-plugin-vuetify-2.0.5.tgz",
@@ -8676,6 +8681,11 @@
         "loader-utils": "^1.2.0"
       }
     },
+    "vuex": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.2.0.tgz",
+      "integrity": "sha512-qBZGJJ1gUNWZbfZlH7ylIPwENg3R0Ckpq+qPexF065kOMOt1Ixt8WDJmtssVv7OhepWD0+Qie7pOS8f0oQy1JA=="
+    },
     "watchpack": {
       "version": "1.6.1",
       "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz",
@@ -9316,7 +9326,7 @@
     },
     "wcwidth": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+      "resolved": "https://registry.npm.taobao.org/wcwidth/download/wcwidth-1.0.1.tgz",
       "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=",
       "dev": true,
       "requires": {
@@ -9564,7 +9574,7 @@
       "dependencies": {
         "ansi-regex": {
           "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/ansi-regex/download/ansi-regex-2.1.1.tgz",
           "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
           "dev": true
         },
@@ -9628,13 +9638,13 @@
           "dependencies": {
             "ansi-regex": {
               "version": "3.0.0",
-              "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+              "resolved": "https://registry.npm.taobao.org/ansi-regex/download/ansi-regex-3.0.0.tgz",
               "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
               "dev": true
             },
             "strip-ansi": {
               "version": "4.0.0",
-              "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+              "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-4.0.0.tgz?cache=0&sync_timestamp=1573280577145&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-4.0.0.tgz",
               "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
               "dev": true,
               "requires": {
@@ -10224,7 +10234,7 @@
         },
         "is-fullwidth-code-point": {
           "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/is-fullwidth-code-point/download/is-fullwidth-code-point-1.0.0.tgz",
           "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
           "dev": true,
           "requires": {
@@ -10278,7 +10288,7 @@
         },
         "require-main-filename": {
           "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/require-main-filename/download/require-main-filename-1.0.1.tgz",
           "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
           "dev": true
         },
@@ -10295,7 +10305,7 @@
         },
         "strip-ansi": {
           "version": "3.0.1",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+          "resolved": "https://registry.npm.taobao.org/strip-ansi/download/strip-ansi-3.0.1.tgz?cache=0&sync_timestamp=1573280577145&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fstrip-ansi%2Fdownload%2Fstrip-ansi-3.0.1.tgz",
           "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
           "dev": true,
           "requires": {
@@ -10313,7 +10323,7 @@
         },
         "wrap-ansi": {
           "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+          "resolved": "https://registry.npm.taobao.org/wrap-ansi/download/wrap-ansi-2.1.0.tgz",
           "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
           "dev": true,
           "requires": {
@@ -10323,7 +10333,7 @@
           "dependencies": {
             "string-width": {
               "version": "1.0.2",
-              "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+              "resolved": "https://registry.npm.taobao.org/string-width/download/string-width-1.0.2.tgz",
               "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
               "dev": true,
               "requires": {
@@ -10423,7 +10433,7 @@
     },
     "which-module": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+      "resolved": "https://registry.npm.taobao.org/which-module/download/which-module-2.0.0.tgz",
       "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
       "dev": true
     },
diff --git a/ui/package.json b/ui/package.json
index 4f0b8dd199d14c7b8b96f3ea692dd15bd1abad23..19a032c638c9fe47c4c250ce964e656ac9969928 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -11,9 +11,11 @@
     "is-cidr": "^3.1.0",
     "moment": "^2.24.0",
     "vue": "^2.6.10",
+    "vue-axios": "^2.1.5",
     "vue-moment": "^4.1.0",
     "vue-router": "^3.1.6",
-    "vuetify": "^2.2.22"
+    "vuetify": "^2.2.22",
+    "vuex": "^3.2.0"
   },
   "devDependencies": {
     "@vue/cli-plugin-router": "^4.3.1",
diff --git a/ui/src/App.vue b/ui/src/App.vue
index 519cf15852646d77584f31f4069eab1e7e20c216..32c317fe5f0b7eb5d039a65014d10761f90a125b 100644
--- a/ui/src/App.vue
+++ b/ui/src/App.vue
@@ -1,69 +1,36 @@
 <template>
   <v-app id="inspire">
+    <Notification v-bind:notification="notification"/>
+    <div v-if="this.isAuthenticated">
+      <Header/>
 
-    <v-app-bar app>
-      <img class="mr-3" :src="require('./assets/logo.png')" height="50" alt="Wg Gen Web"/>
-      <v-toolbar-title to="/">Wg Gen Web</v-toolbar-title>
-
-      <v-spacer />
-
-      <v-toolbar-items>
-        <v-btn to="/clients">
-          Clients
-          <v-icon right dark>mdi-account-network-outline</v-icon>
-        </v-btn>
-        <v-btn to="/server">
-          Server
-          <v-icon right dark>mdi-vpn</v-icon>
-        </v-btn>
-      </v-toolbar-items>
-
-    </v-app-bar>
-
-    <v-content>
-      <v-container>
-        <router-view />
-      </v-container>
-      <Notification v-bind:notification="notification"/>
-    </v-content>
-
-    <v-footer app>
-      <v-row justify="start" no-gutters>
-        <v-col cols="12" lg="6" md="12" sm="12">
-          <div :align="$vuetify.breakpoint.smAndDown ? 'center' : 'left'">
-            <small>Copyright &copy; {{ new Date().getFullYear() }}, Wg Gen Web.</small>
-            <small>This work is licensed under a <a class="pr-1 pl-1" href="http://www.wtfpl.net/" target="_blank">WTFPL License.</a></small>
-          </div>
-        </v-col>
-      </v-row>
-      <v-row justify="end" no-gutters>
-        <v-col cols="12" lg="6" md="12" sm="12">
-          <div :align="$vuetify.breakpoint.smAndDown ? 'center' : 'right'">
-            <small>Created with</small>
-            <v-icon class="pr-1 pl-1">mdi-heart</v-icon><span>by</span><a class="pr-2 pl-1" href="mailto:vx3r@127-0-0-1.fr">vx3r</a>
-            <a :href="'https://github.com/vx3r/wg-gen-web/commit/' + version"><kbd>Version: {{ version.substring(0,7) }}</kbd></a>
-          </div>
-        </v-col>
-      </v-row>
-    </v-footer>
+      <v-content>
+        <v-container>
+          <router-view />
+        </v-container>
+      </v-content>
 
+      <Footer/>
+    </div>
   </v-app>
 </template>
 
 <script>
-  import {ApiService} from "./services/ApiService";
   import Notification from './components/Notification'
+  import Header from "./components/Header";
+  import Footer from "./components/Footer";
+  import {mapActions, mapGetters} from "vuex";
 
   export default {
     name: 'App',
 
     components: {
+      Footer,
+      Header,
       Notification
     },
 
     data: () => ({
-      api: null,
-      version:'N/A',
       notification: {
         show: false,
         color: '',
@@ -71,23 +38,69 @@
       },
     }),
 
-    mounted() {
-      this.api = new ApiService();
-      this.getVersion()
+    computed:{
+      ...mapGetters({
+        isAuthenticated: 'auth/isAuthenticated',
+        authStatus: 'auth/authStatus',
+        authRedirectUrl: 'auth/authRedirectUrl',
+        authError: 'auth/error',
+        clientError: 'client/error',
+        serverError: 'server/error',
+      })
     },
 
     created () {
       this.$vuetify.theme.dark = true
     },
 
-    methods: {
-      getVersion() {
-        this.api.get('/server/version').then((res) => {
-          this.version = res.version;
-        }).catch((e) => {
-          this.notify('error', e.response.status + ' ' + e.response.statusText);
-        });
+    mounted() {
+      if (this.$route.query.code && this.$route.query.state) {
+        this.oauth2_exchange({
+          code: this.$route.query.code,
+          state: this.$route.query.state
+        })
+      } else {
+        this.oauth2_url()
+      }
+    },
+
+    watch: {
+      authError(newValue, oldValue) {
+        console.log(newValue)
+        this.notify('error', newValue);
+      },
+
+      clientError(newValue, oldValue) {
+        console.log(newValue)
+        this.notify('error', newValue);
+      },
+
+      serverError(newValue, oldValue) {
+        console.log(newValue)
+        this.notify('error', newValue);
+      },
+
+      isAuthenticated(newValue, oldValue) {
+        console.log(`Updating isAuthenticated from ${oldValue} to ${newValue}`);
+        if (newValue === true) {
+          this.$router.push('/clients')
+        }
+      },
+
+      authStatus(newValue, oldValue) {
+        console.log(`Updating authStatus from ${oldValue} to ${newValue}`);
+        if (newValue === 'redirect') {
+          window.location.replace(this.authRedirectUrl)
+        }
       },
+    },
+
+    methods: {
+      ...mapActions('auth', {
+        oauth2_exchange: 'oauth2_exchange',
+        oauth2_url: 'oauth2_url',
+      }),
+
       notify(color, msg) {
         this.notification.show = true;
         this.notification.color = color;
diff --git a/ui/src/components/Clients.vue b/ui/src/components/Clients.vue
index e2c5786e41446302f36084ce977dd2d7742e27b1..57cd215e5e2ebce0f763fa3b564ff8fb2740027f 100644
--- a/ui/src/components/Clients.vue
+++ b/ui/src/components/Clients.vue
@@ -9,7 +9,7 @@
                         </v-list-item-content>
                         <v-btn
                                 color="success"
-                                @click="startAddClient"
+                                @click="startCreate"
                         >
                             Add new client
                             <v-icon right dark>mdi-account-multiple-plus-outline</v-icon>
@@ -31,15 +31,15 @@
                                     <v-list-item-content>
                                         <v-list-item-title class="headline">{{ client.name }}</v-list-item-title>
                                         <v-list-item-subtitle>{{ client.email }}</v-list-item-subtitle>
-                                        <v-list-item-subtitle>Created: {{ client.created | formatDate }}</v-list-item-subtitle>
-                                        <v-list-item-subtitle>Updated: {{ client.updated | formatDate }}</v-list-item-subtitle>
+                                        <v-list-item-subtitle>Created: {{ client.created | formatDate }} by {{ client.createdBy }}</v-list-item-subtitle>
+                                        <v-list-item-subtitle>Updated: {{ client.updated | formatDate }} by {{ client.updatedBy }}</v-list-item-subtitle>
                                     </v-list-item-content>
 
                                     <v-list-item-avatar
                                             tile
                                             size="150"
                                     >
-                                        <v-img :src="`${apiBaseUrl}/client/${client.id}/config?qrcode=true`"/>
+                                        <v-img :src="'data:image/png;base64, ' + getClientQrcode(client.id)"/>
                                     </v-list-item-avatar>
                                 </v-list-item>
 
@@ -55,61 +55,61 @@
                                     </v-chip>
                                 </v-card-text>
                                 <v-card-actions>
-                                  <v-tooltip bottom>
-                                    <template v-slot:activator="{ on }">
-                                      <v-btn
-                                              text
-                                              :href="`${apiBaseUrl}/client/${client.id}/config?qrcode=false`"
-                                              v-on="on"
-                                      >
-                                          <span class="d-none d-lg-flex">Download</span>
-                                          <v-icon right dark>mdi-cloud-download-outline</v-icon>
-                                      </v-btn>
-                                    </template>
-                                    <span>Download</span>
-                                  </v-tooltip>
+                                    <v-tooltip bottom>
+                                        <template v-slot:activator="{ on }">
+                                            <v-btn
+                                                    text
+                                                    v-on:click="forceFileDownload(client)"
+                                                    v-on="on"
+                                            >
+                                                <span class="d-none d-lg-flex">Download</span>
+                                                <v-icon right dark>mdi-cloud-download-outline</v-icon>
+                                            </v-btn>
+                                        </template>
+                                        <span>Download</span>
+                                    </v-tooltip>
 
-                                  <v-tooltip bottom>
-                                    <template v-slot:activator="{ on }">
-                                      <v-btn
-                                            text
-                                            @click.stop="startUpdateClient(client)"
-                                            v-on="on"
-                                      >
-                                        <span class="d-none d-lg-flex">Edit</span>
-                                        <v-icon right dark>mdi-square-edit-outline</v-icon>
-                                    </v-btn>
-                                    </template>
-                                    <span>Edit</span>
-                                  </v-tooltip>
+                                    <v-tooltip bottom>
+                                        <template v-slot:activator="{ on }">
+                                            <v-btn
+                                                    text
+                                                    @click.stop="startUpdate(client)"
+                                                    v-on="on"
+                                            >
+                                                <span class="d-none d-lg-flex">Edit</span>
+                                                <v-icon right dark>mdi-square-edit-outline</v-icon>
+                                            </v-btn>
+                                        </template>
+                                        <span>Edit</span>
+                                    </v-tooltip>
 
-                                  <v-tooltip bottom>
-                                    <template v-slot:activator="{ on }">
-                                      <v-btn
-                                              text
-                                              @click="deleteClient(client)"
-                                              v-on="on"
-                                      >
-                                          <span class="d-none d-lg-flex">Delete</span>
-                                          <v-icon right dark>mdi-trash-can-outline</v-icon>
-                                      </v-btn>
-                                    </template>
-                                    <span>Delete</span>
-                                  </v-tooltip>
+                                    <v-tooltip bottom>
+                                        <template v-slot:activator="{ on }">
+                                            <v-btn
+                                                    text
+                                                    @click="remove(client)"
+                                                    v-on="on"
+                                            >
+                                                <span class="d-none d-lg-flex">Delete</span>
+                                                <v-icon right dark>mdi-trash-can-outline</v-icon>
+                                            </v-btn>
+                                        </template>
+                                        <span>Delete</span>
+                                    </v-tooltip>
 
-                                  <v-tooltip bottom>
-                                    <template v-slot:activator="{ on }">
-                                      <v-btn
-                                              text
-                                              @click="sendEmailClient(client.id)"
-                                              v-on="on"
-                                      >
-                                          <span class="d-none d-lg-flex">Send Email</span>
-                                          <v-icon right dark>mdi-email-send-outline</v-icon>
-                                      </v-btn>
-                                    </template>
-                                    <span>Send Email</span>
-                                  </v-tooltip>
+                                    <v-tooltip bottom>
+                                        <template v-slot:activator="{ on }">
+                                            <v-btn
+                                                    text
+                                                    @click="email(client)"
+                                                    v-on="on"
+                                            >
+                                                <span class="d-none d-lg-flex">Send Email</span>
+                                                <v-icon right dark>mdi-email-send-outline</v-icon>
+                                            </v-btn>
+                                        </template>
+                                        <span>Send Email</span>
+                                    </v-tooltip>
                                     <v-spacer/>
                                     <v-tooltip right>
                                         <template v-slot:activator="{ on }">
@@ -118,7 +118,7 @@
                                                     v-on="on"
                                                     color="success"
                                                     v-model="client.enable"
-                                                    v-on:change="updateClient(client)"
+                                                    v-on:change="update(client)"
                                             />
                                         </template>
                                         <span> {{client.enable ? 'Disable' : 'Enable'}} this client</span>
@@ -133,7 +133,7 @@
         </v-row>
         <v-dialog
                 v-if="client"
-                v-model="dialogAddClient"
+                v-model="dialogCreate"
                 max-width="550"
         >
             <v-card>
@@ -210,14 +210,14 @@
                     <v-btn
                             :disabled="!valid"
                             color="success"
-                            @click="addClient(client)"
+                            @click="create(client)"
                     >
                         Submit
                         <v-icon right dark>mdi-check-outline</v-icon>
                     </v-btn>
                     <v-btn
                             color="primary"
-                            @click="dialogAddClient = false"
+                            @click="dialogCreate = false"
                     >
                         Cancel
                         <v-icon right dark>mdi-close-circle-outline</v-icon>
@@ -227,7 +227,7 @@
         </v-dialog>
         <v-dialog
                 v-if="client"
-                v-model="dialogEditClient"
+                v-model="dialogUpdate"
                 max-width="550"
         >
             <v-card>
@@ -308,14 +308,14 @@
                     <v-btn
                             :disabled="!valid"
                             color="success"
-                            @click="updateClient(client)"
+                            @click="update(client)"
                     >
                         Submit
                         <v-icon right dark>mdi-check-outline</v-icon>
                     </v-btn>
                     <v-btn
                             color="primary"
-                            @click="dialogEditClient = false"
+                            @click="dialogUpdate = false"
                     >
                         Cancel
                         <v-icon right dark>mdi-close-circle-outline</v-icon>
@@ -323,61 +323,50 @@
                 </v-card-actions>
             </v-card>
         </v-dialog>
-        <Notification v-bind:notification="notification"/>
     </v-container>
 </template>
 <script>
-  import {ApiService, API_BASE_URL} from '../services/ApiService'
-  import Notification from '../components/Notification'
+  import { mapActions, mapGetters } from 'vuex'
 
   export default {
     name: 'Clients',
 
-    components: {
-      Notification
-    },
-
     data: () => ({
-      api: null,
-      apiBaseUrl: API_BASE_URL,
-      clients: [],
-      notification: {
-        show: false,
-        color: '',
-        text: '',
-      },
-      dialogAddClient: false,
-      dialogEditClient: false,
+      dialogCreate: false,
+      dialogUpdate: false,
       client: null,
-      server: null,
       valid: false,
     }),
 
+    computed:{
+      ...mapGetters({
+        getClientQrcode: 'client/getClientQrcode',
+        getClientConfig: 'client/getClientConfig',
+        server: 'server/server',
+        clients: 'client/clients',
+        clientQrcodes: 'client/clientQrcodes',
+      }),
+    },
+
     mounted () {
-      this.api = new ApiService();
-      this.getClients();
-      this.getServer()
+      this.readAllClients()
+      this.readServer()
     },
 
     methods: {
-      getClients() {
-        this.api.get('/client').then((res) => {
-          this.clients = res
-        }).catch((e) => {
-          this.notify('error', e.response.status + ' ' + e.response.statusText);
-        });
-      },
-
-      getServer() {
-        this.api.get('/server').then((res) => {
-          this.server = res;
-        }).catch((e) => {
-          this.notify('error', e.response.status + ' ' + e.response.statusText);
-        });
-      },
+      ...mapActions('client', {
+        errorClient: 'error',
+        readAllClients: 'readAll',
+        creatClient: 'create',
+        updateClient: 'update',
+        deleteClient: 'delete',
+        emailClient: 'email',
+      }),
+      ...mapActions('server', {
+        readServer: 'read',
+      }),
 
-      startAddClient() {
-        this.dialogAddClient = true;
+      startCreate() {
         this.client = {
           name: "",
           email: "",
@@ -385,91 +374,87 @@
           allowedIPs: this.server.allowedips,
           address: this.server.address,
         }
+        this.dialogCreate = true;
       },
-      addClient(client) {
+
+      create(client) {
         if (client.allowedIPs.length < 1) {
-          this.notify('error', 'Please provide at least one valid CIDR address for client allowed IPs');
+          this.errorClient('Please provide at least one valid CIDR address for client allowed IPs')
           return;
         }
         for (let i = 0; i < client.allowedIPs.length; i++){
           if (this.$isCidr(client.allowedIPs[i]) === 0) {
-            this.notify('error', 'Invalid CIDR detected, please correct before submitting');
+            this.errorClient('Invalid CIDR detected, please correct before submitting')
             return
           }
         }
-        this.dialogAddClient = false;
-
-        this.api.post('/client', client).then((res) => {
-          this.notify('success', `Client ${res.name} successfully added`);
-          this.getClients()
-        }).catch((e) => {
-          this.notify('error', e.response.status + ' ' + e.response.statusText);
-        });
+        this.dialogCreate = false;
+        this.creatClient(client)
       },
 
-      deleteClient(client) {
+      remove(client) {
         if(confirm(`Do you really want to delete ${client.name} ?`)){
-          this.api.delete(`/client/${client.id}`).then((res) => {
-            this.notify('success', "Client successfully deleted");
-            this.getClients()
-          }).catch((e) => {
-            this.notify('error', e.response.status + ' ' + e.response.statusText);
-          });
+          this.deleteClient(client)
         }
       },
 
-      sendEmailClient(id) {
-        this.api.get(`/client/${id}/email`).then((res) => {
-          this.notify('success', "Email successfully sent");
-          this.getClients()
-        }).catch((e) => {
-          this.notify('error', e.response.status + ' ' + e.response.statusText);
-        });
+      email(client) {
+        if (!client.email){
+          this.errorClient('Client email is not defined')
+          return
+        }
+
+        if(confirm(`Do you really want to send email to ${client.email} with all configurations ?`)){
+          this.emailClient(client)
+        }
       },
 
-      startUpdateClient(client) {
+      startUpdate(client) {
         this.client = client;
-        this.dialogEditClient = true;
+        this.dialogUpdate = true;
       },
-      updateClient(client) {
+
+      update(client) {
         // check allowed IPs
         if (client.allowedIPs.length < 1) {
-          this.notify('error', 'Please provide at least one valid CIDR address for client allowed IPs');
+          this.errorClient('Please provide at least one valid CIDR address for client allowed IPs');
           return;
         }
         for (let i = 0; i < client.allowedIPs.length; i++){
           if (this.$isCidr(client.allowedIPs[i]) === 0) {
-            this.notify('error', 'Invalid CIDR detected, please correct before submitting');
+            this.errorClient('Invalid CIDR detected, please correct before submitting');
             return
           }
         }
         // check address
         if (client.address.length < 1) {
-          this.notify('error', 'Please provide at least one valid CIDR address for client');
+          this.errorClient('Please provide at least one valid CIDR address for client');
           return;
         }
         for (let i = 0; i < client.address.length; i++){
           if (this.$isCidr(client.address[i]) === 0) {
-            this.notify('error', 'Invalid CIDR detected, please correct before submitting');
+            this.errorClient('Invalid CIDR detected, please correct before submitting');
             return
           }
         }
         // all good, submit
-        this.dialogEditClient = false;
-
-        this.api.patch(`/client/${client.id}`, client).then((res) => {
-          this.notify('success', `Client ${res.name} successfully updated`);
-          this.getClients()
-        }).catch((e) => {
-          this.notify('error', e.response.status + ' ' + e.response.statusText);
-        });
+        this.dialogUpdate = false;
+        this.updateClient(client)
       },
 
-      notify(color, msg) {
-        this.notification.show = true;
-        this.notification.color = color;
-        this.notification.text = msg;
-      }
+      forceFileDownload(client){
+        let config = this.getClientConfig(client.id)
+        if (!config) {
+          this.errorClient('Failed to download client config');
+          return
+        }
+        const url = window.URL.createObjectURL(new Blob([config]))
+        const link = document.createElement('a')
+        link.href = url
+        link.setAttribute('download', 'wg0.conf') //or any other extension
+        document.body.appendChild(link)
+        link.click()
+      },
     }
   };
 </script>
diff --git a/ui/src/components/Footer.vue b/ui/src/components/Footer.vue
new file mode 100644
index 0000000000000000000000000000000000000000..336f8a8d2cba89129a2c6ca45a7affcf4453dbce
--- /dev/null
+++ b/ui/src/components/Footer.vue
@@ -0,0 +1,51 @@
+<template>
+    <v-container>
+        <v-footer app>
+            <v-row justify="start" no-gutters>
+                <v-col cols="12" lg="6" md="12" sm="12">
+                    <div :align="$vuetify.breakpoint.smAndDown ? 'center' : 'left'">
+                        <small>Copyright &copy; {{ new Date().getFullYear() }}, Wg Gen Web. </small>
+                        <small>This work is licensed under <a class="pr-1 pl-1" href="http://www.wtfpl.net/" target="_blank">WTFPL License.</a></small>
+                    </div>
+                </v-col>
+            </v-row>
+            <v-row justify="end" no-gutters>
+                <v-col cols="12" lg="6" md="12" sm="12">
+                    <div :align="$vuetify.breakpoint.smAndDown ? 'center' : 'right'">
+                        <small>Created with</small>
+                        <v-icon class="pr-1 pl-1">mdi-heart</v-icon><span>by</span><a class="pr-2 pl-1" href="mailto:vx3r@127-0-0-1.fr">vx3r</a>
+                        <a :href="'https://github.com/vx3r/wg-gen-web/commit/' + version"><kbd>Version: {{ version.substring(0,7) }}</kbd></a>
+                    </div>
+                </v-col>
+            </v-row>
+        </v-footer>
+    </v-container>
+</template>
+
+<script>
+  import {mapActions, mapGetters} from "vuex";
+
+  export default {
+    name: 'Footer',
+
+    data: () => ({
+
+    }),
+
+    computed:{
+      ...mapGetters({
+        version: 'server/version',
+      }),
+    },
+
+    mounted() {
+      this.versionServer()
+    },
+
+    methods: {
+      ...mapActions('server', {
+        versionServer: 'version',
+      }),
+    }
+  }
+</script>
diff --git a/ui/src/components/Header.vue b/ui/src/components/Header.vue
new file mode 100644
index 0000000000000000000000000000000000000000..58ca18fa712add7492071ab0d89928f1f6a2cf1a
--- /dev/null
+++ b/ui/src/components/Header.vue
@@ -0,0 +1,77 @@
+<template>
+    <v-container>
+        <v-app-bar app>
+            <img class="mr-3" :src="require('../assets/logo.png')" height="50" alt="Wg Gen Web"/>
+            <v-toolbar-title to="/">Wg Gen Web</v-toolbar-title>
+
+            <v-spacer />
+
+            <v-toolbar-items>
+                <v-btn to="/clients">
+                    Clients
+                    <v-icon right dark>mdi-account-network-outline</v-icon>
+                </v-btn>
+                <v-btn to="/server">
+                    Server
+                    <v-icon right dark>mdi-vpn</v-icon>
+                </v-btn>
+            </v-toolbar-items>
+
+            <v-menu
+                    left
+                    bottom
+            >
+                <template v-slot:activator="{ on }">
+                    <v-btn icon v-on="on">
+                        <v-icon>mdi-account-circle</v-icon>
+                    </v-btn>
+                </template>
+
+                <v-card
+                        class="mx-auto"
+                        max-width="344"
+                        outlined
+                >
+                    <v-list-item three-line>
+                        <v-list-item-content>
+                            <div class="overline mb-4">connected as</div>
+                            <v-list-item-title class="headline mb-1">{{user.name}}</v-list-item-title>
+                            <v-list-item-subtitle>Email: {{user.email}}</v-list-item-subtitle>
+                            <v-list-item-subtitle>Issuer: {{user.issuer}}</v-list-item-subtitle>
+                            <v-list-item-subtitle>Issued at: {{ user.issuedAt | formatDate }}</v-list-item-subtitle>
+                        </v-list-item-content>
+                    </v-list-item>
+                    <v-card-actions>
+                        <v-btn small
+                                v-on:click="logout()"
+                        >
+                            logout
+                            <v-icon small right dark>mdi-logout</v-icon>
+                        </v-btn>
+                    </v-card-actions>
+                </v-card>
+            </v-menu>
+
+        </v-app-bar>
+    </v-container>
+</template>
+
+<script>
+  import {mapActions, mapGetters} from "vuex";
+
+  export default {
+    name: 'Header',
+
+    computed:{
+      ...mapGetters({
+        user: 'auth/user',
+      }),
+    },
+
+    methods: {
+      ...mapActions('auth', {
+        logout: 'logout',
+      }),
+    }
+  }
+</script>
diff --git a/ui/src/components/Server.vue b/ui/src/components/Server.vue
index c8fdffcff90b00f4bc4ca008a348fa7cd10c02af..5a70e94a9105248b0bca8d90f925c3e14ff96237 100644
--- a/ui/src/components/Server.vue
+++ b/ui/src/components/Server.vue
@@ -158,7 +158,7 @@
             <v-btn
                     class="ma-2"
                     color="success"
-                    :href="`${apiBaseUrl}/server/config`"
+                    v-on:click="forceFileDownload()"
             >
                 Download server configuration
                 <v-icon right dark>mdi-cloud-download-outline</v-icon>
@@ -167,52 +167,44 @@
             <v-btn
                     class="ma-2"
                     color="warning"
-                    @click="updateServer"
+                    @click="update"
             >
                 Update server configuration
                 <v-icon right dark>mdi-update</v-icon>
             </v-btn>
             <v-divider dark/>
         </v-row>
-        <Notification v-bind:notification="notification"/>
     </v-container>
 </template>
 <script>
-  import {API_BASE_URL, ApiService} from "../services/ApiService";
-  import Notification from '../components/Notification'
+  import {mapActions, mapGetters} from "vuex";
 
   export default {
     name: 'Server',
 
-    components: {
-      Notification
-    },
-
     data: () => ({
-      api: null,
-      server: null,
-      apiBaseUrl: API_BASE_URL,
-      notification: {
-        show: false,
-        color: '',
-        text: '',
-      },
+
     }),
 
+    computed:{
+      ...mapGetters({
+        server: 'server/server',
+        config: 'server/config',
+      }),
+    },
+
     mounted () {
-      this.api = new ApiService();
-      this.getServer()
+      this.readServer()
     },
 
     methods: {
-      getServer() {
-        this.api.get('/server').then((res) => {
-          this.server = res;
-        }).catch((e) => {
-          this.notify('error', e.response.status + ' ' + e.response.statusText);
-        });
-      },
-      updateServer () {
+      ...mapActions('server', {
+        errorServer: 'error',
+        readServer: 'read',
+        updateServer: 'update',
+      }),
+
+      update() {
         // convert int values
         this.server.listenPort = parseInt(this.server.listenPort, 10);
         this.server.persistentKeepalive = parseInt(this.server.persistentKeepalive, 10);
@@ -220,12 +212,12 @@
 
         // check server addresses
         if (this.server.address.length < 1) {
-          this.notify('error', 'Please provide at least one valid CIDR address for server interface');
+          this.errorServer('Please provide at least one valid CIDR address for server interface');
           return;
         }
         for (let i = 0; i < this.server.address.length; i++){
           if (this.$isCidr(this.server.address[i]) === 0) {
-            this.notify('error', `Invalid CIDR detected, please correct ${this.server.address[i]} before submitting`);
+            this.errorServer(`Invalid CIDR detected, please correct ${this.server.address[i]} before submitting`);
             return
           }
         }
@@ -233,35 +225,34 @@
         // check DNS correct
         for (let i = 0; i < this.server.dns.length; i++){
           if (this.$isCidr(this.server.dns[i] + '/32') === 0) {
-            this.notify('error', `Invalid IP detected, please correct ${this.server.dns[i]} before submitting`);
+            this.errorServer(`Invalid IP detected, please correct ${this.server.dns[i]} before submitting`);
             return
           }
         }
 
         // check client AllowedIPs
         if (this.server.allowedips.length < 1) {
-          this.notify('error', 'Please provide at least one valid CIDR address for client allowed IPs');
+          this.errorServer('Please provide at least one valid CIDR address for client allowed IPs');
           return;
         }
         for (let i = 0; i < this.server.allowedips.length; i++){
           if (this.$isCidr(this.server.allowedips[i]) === 0) {
-            this.notify('error', 'Invalid CIDR detected, please correct before submitting');
+            this.errorServer('Invalid CIDR detected, please correct before submitting');
             return
           }
         }
 
-        this.api.patch('/server', this.server).then((res) => {
-          this.notify('success', "Server successfully updated");
-          this.server = res;
-        }).catch((e) => {
-          this.notify('error', e.response.status + ' ' + e.response.statusText);
-        });
+        this.updateServer(this.server)
+      },
+
+      forceFileDownload(){
+        const url = window.URL.createObjectURL(new Blob([this.config]))
+        const link = document.createElement('a')
+        link.href = url
+        link.setAttribute('download', 'wg0.conf') //or any other extension
+        document.body.appendChild(link)
+        link.click()
       },
-      notify(color, msg) {
-        this.notification.show = true;
-        this.notification.color = color;
-        this.notification.text = msg;
-      }
     }
   };
 </script>
diff --git a/ui/src/main.js b/ui/src/main.js
index bfb2c507e3d5233979b13bdaeb0b7341de2b7990..fea553f62d81535f07cd77c7b3aeccca88014676 100644
--- a/ui/src/main.js
+++ b/ui/src/main.js
@@ -1,14 +1,18 @@
 import Vue from 'vue'
 import App from './App.vue'
 import router from './router'
+import store from './store'
 import vuetify from './plugins/vuetify';
 import './plugins/moment';
 import './plugins/cidr'
+import './plugins/axios'
 
-Vue.config.productionTip = false
+// Don't warn about using the dev version of Vue in development.
+Vue.config.productionTip = process.env.NODE_ENV === 'production'
 
 new Vue({
   router,
+  store,
   vuetify,
   render: function (h) { return h(App) }
 }).$mount('#app')
diff --git a/ui/src/plugins/axios.js b/ui/src/plugins/axios.js
new file mode 100644
index 0000000000000000000000000000000000000000..444ef90787f9bc91a3126388a5c54d927449df4e
--- /dev/null
+++ b/ui/src/plugins/axios.js
@@ -0,0 +1,26 @@
+import Vue from 'vue'
+import axios from "axios";
+import VueAxios from "vue-axios";
+import TokenService from "../services/token.service";
+
+Vue.use(VueAxios, axios);
+
+let baseUrl = "/api/v1.0";
+if (process.env.NODE_ENV === "development"){
+  baseUrl = process.env.VUE_APP_API_BASE_URL;
+}
+
+Vue.axios.defaults.baseURL = baseUrl;
+
+Vue.axios.interceptors.response.use(function (response) {
+  return response;
+}, function (error) {
+  if (401 === error.response.status) {
+    TokenService.destroyToken();
+    TokenService.destroyClientId();
+    window.location = '/';
+  } else {
+    return Promise.reject(error);
+  }
+});
+
diff --git a/ui/src/router/index.js b/ui/src/router/index.js
index bbd46bb8b6567b11ed662cb764ad1547f51fd867..ee29359639b949ce6a018767c37c9931482b6c85 100644
--- a/ui/src/router/index.js
+++ b/ui/src/router/index.js
@@ -1,22 +1,19 @@
 import Vue from 'vue'
 import VueRouter from 'vue-router'
+import store from "../store";
 
 Vue.use(VueRouter);
 
 const routes = [
-  {
-    path: '/',
-    name: 'index',
-    component: function () {
-      return import(/* webpackChunkName: "Index" */ '../views/Index.vue')
-    },
-  },
   {
     path: '/clients',
     name: 'clients',
     component: function () {
       return import(/* webpackChunkName: "Clients" */ '../views/Clients.vue')
     },
+    meta: {
+      requiresAuth: true
+    }
   },
   {
     path: '/server',
@@ -24,6 +21,9 @@ const routes = [
     component: function () {
       return import(/* webpackChunkName: "Server" */ '../views/Server.vue')
     },
+    meta: {
+      requiresAuth: true
+    }
   }
 ];
 
@@ -33,4 +33,16 @@ const router = new VueRouter({
   routes
 });
 
+router.beforeEach((to, from, next) => {
+  if(to.matched.some(record => record.meta.requiresAuth)) {
+    if (store.getters["auth/isAuthenticated"]) {
+      next()
+      return
+    }
+    next('/')
+  } else {
+    next()
+  }
+})
+
 export default router
diff --git a/ui/src/services/ApiService.js b/ui/src/services/ApiService.js
deleted file mode 100644
index dc55572b974b1acb4e294a1ccfca6d20af70a6e9..0000000000000000000000000000000000000000
--- a/ui/src/services/ApiService.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import axios from 'axios'
-
-let baseUrl = "/api/v1.0";
-if (process.env.NODE_ENV === "development"){
-  baseUrl = process.env.VUE_APP_API_BASE_URL
-}
-
-export const API_BASE_URL = baseUrl;
-
-export class ApiService {
-  get(resource) {
-    return axios
-      .get(`${API_BASE_URL}${resource}`)
-      .then(response => response.data)
-  };
-
-  post(resource, data) {
-    return axios
-      .post(`${API_BASE_URL}${resource}`, data)
-      .then(response => response.data)
-  };
-
-  put(resource, data) {
-    return axios
-      .put(`${API_BASE_URL}${resource}`, data)
-      .then(response => response.data)
-  };
-
-  patch(resource, data) {
-    return axios
-      .patch(`${API_BASE_URL}${resource}`, data)
-      .then(response => response.data)
-  };
-
-  delete(resource) {
-    return axios
-      .delete(`${API_BASE_URL}${resource}`)
-      .then(response => response.data)
-  };
-}
diff --git a/ui/src/services/api.service.js b/ui/src/services/api.service.js
new file mode 100644
index 0000000000000000000000000000000000000000..97b0093c0a1e2b444f5aaee3effb9100a98dcb47
--- /dev/null
+++ b/ui/src/services/api.service.js
@@ -0,0 +1,59 @@
+import Vue from "vue";
+import TokenService from "./token.service";
+
+const ApiService = {
+
+  setHeader() {
+    Vue.axios.defaults.headers.common.Authorization = `${TokenService.getToken()}`;
+  },
+
+  get(resource) {
+    return Vue.axios.get(resource)
+      .then(response => response.data)
+      .catch(error => {
+        throw new Error(`ApiService: ${error}`)
+      });
+  },
+
+  post(resource, params) {
+    return Vue.axios.post(resource, params)
+      .then(response => response.data)
+      .catch(error => {
+        throw new Error(`ApiService: ${error}`)
+      });
+  },
+
+  put(resource, params) {
+    return Vue.axios.put(resource, params)
+      .then(response => response.data)
+      .catch(error => {
+        throw new Error(`ApiService: ${error}`)
+      });
+  },
+
+  patch(resource, params) {
+    return Vue.axios.patch(resource, params)
+      .then(response => response.data)
+      .catch(error => {
+        throw new Error(`ApiService: ${error}`)
+      });
+  },
+
+  delete(resource) {
+    return Vue.axios.delete(resource)
+      .then(response => response.data)
+      .catch(error => {
+        throw new Error(`ApiService: ${error}`)
+      });
+  },
+
+  getWithConfig(resource, config) {
+    return Vue.axios.get(resource, config)
+      .then(response => response.data)
+      .catch(error => {
+        throw new Error(`ApiService: ${error}`)
+      });
+  },
+};
+
+export default ApiService;
diff --git a/ui/src/services/token.service.js b/ui/src/services/token.service.js
new file mode 100644
index 0000000000000000000000000000000000000000..cbbb12de36ed2039c0dffef73a69b7fc67608d03
--- /dev/null
+++ b/ui/src/services/token.service.js
@@ -0,0 +1,35 @@
+const TOKEN_KEY = "token";
+const CLIENT_ID_KEY = "client_id";
+
+export const getToken = () => {
+  return window.localStorage.getItem(TOKEN_KEY);
+};
+
+export const saveToken = token => {
+  window.localStorage.setItem(TOKEN_KEY, token);
+};
+
+export const destroyToken = () => {
+  window.localStorage.removeItem(TOKEN_KEY);
+};
+
+export const getClientId = () => {
+  return window.localStorage.getItem(CLIENT_ID_KEY);
+};
+
+export const saveClientId = token => {
+  window.localStorage.setItem(CLIENT_ID_KEY, token);
+};
+
+export const destroyClientId = () => {
+  window.localStorage.removeItem(CLIENT_ID_KEY);
+};
+
+export default {
+  getToken,
+  saveToken,
+  destroyToken,
+  getClientId,
+  saveClientId,
+  destroyClientId
+};
diff --git a/ui/src/store/index.js b/ui/src/store/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..97b5b767ebbfaf23cac1b4503bc1589ca0c627dc
--- /dev/null
+++ b/ui/src/store/index.js
@@ -0,0 +1,19 @@
+import Vue from 'vue'
+import Vuex from 'vuex'
+import auth from "./modules/auth";
+import client from "./modules/client";
+import server from "./modules/server";
+
+Vue.use(Vuex)
+
+export default new Vuex.Store({
+  state: {},
+  getters : {},
+  mutations: {},
+  actions:{},
+  modules: {
+    auth,
+    client,
+    server
+  }
+})
diff --git a/ui/src/store/modules/auth.js b/ui/src/store/modules/auth.js
new file mode 100644
index 0000000000000000000000000000000000000000..90b7e0769b64044cca4f981c4e846c0bbd8e55ec
--- /dev/null
+++ b/ui/src/store/modules/auth.js
@@ -0,0 +1,126 @@
+import ApiService from "../../services/api.service";
+import TokenService from "../../services/token.service";
+
+const state = {
+  error: null,
+  user: null,
+  authStatus: '',
+  authRedirectUrl: '',
+};
+
+const getters = {
+  error(state) {
+    return state.error;
+  },
+  user(state) {
+    return state.user;
+  },
+  isAuthenticated(state) {
+    return state.user !== null;
+  },
+  authRedirectUrl(state) {
+    return state.authRedirectUrl
+  },
+  authStatus(state) {
+    return state.authStatus
+  },
+};
+
+const actions = {
+  user({ commit }){
+    ApiService.get("/auth/user")
+      .then( resp => {
+        commit('user', resp)
+      })
+      .catch(err => {
+        commit('error', err);
+        commit('logout')
+      });
+  },
+
+  oauth2_url({ commit, dispatch }){
+    if (TokenService.getToken()) {
+      ApiService.setHeader();
+      dispatch('user');
+      return
+    }
+    ApiService.get("/auth/oauth2_url")
+      .then(resp => {
+        if (resp.codeUrl === '_magic_string_fake_auth_no_redirect_'){
+          console.log("server report oauth2 is disabled, fake exchange")
+          commit('authStatus', 'disabled')
+          TokenService.saveClientId(resp.clientId)
+          dispatch('oauth2_exchange', {code: "", state: resp.state})
+        } else {
+          commit('authStatus', 'redirect')
+          commit('authRedirectUrl', resp)
+        }
+      })
+      .catch(err => {
+        commit('authStatus', 'error')
+        commit('error', err);
+        commit('logout')
+      })
+  },
+
+  oauth2_exchange({ commit, dispatch }, data){
+    data.clientId = TokenService.getClientId()
+    ApiService.post("/auth/oauth2_exchange", data)
+      .then(resp => {
+        commit('authStatus', 'success')
+        commit('token', resp)
+        dispatch('user');
+      })
+      .catch(err => {
+        commit('authStatus', 'error')
+        commit('error', err);
+        commit('logout')
+      })
+  },
+
+  logout({ commit }){
+    ApiService.get("/auth/logout")
+      .then(resp => {
+        commit('logout')
+      })
+      .catch(err => {
+        commit('authStatus', '')
+        commit('error', err);
+        commit('logout')
+      })
+  },
+}
+
+const mutations = {
+  error(state, error) {
+    state.error = error;
+  },
+  authStatus(state, authStatus) {
+    state.authStatus = authStatus;
+  },
+  authRedirectUrl(state, resp) {
+    state.authRedirectUrl = resp.codeUrl;
+    TokenService.saveClientId(resp.clientId);
+  },
+  token(state, token) {
+    TokenService.saveToken(token);
+    ApiService.setHeader();
+    TokenService.destroyClientId();
+  },
+  user(state, user) {
+    state.user = user;
+  },
+  logout(state) {
+    state.user = null;
+    TokenService.destroyToken();
+    TokenService.destroyClientId();
+  }
+};
+
+export default {
+  namespaced: true,
+  state,
+  getters,
+  actions,
+  mutations
+}
diff --git a/ui/src/store/modules/client.js b/ui/src/store/modules/client.js
new file mode 100644
index 0000000000000000000000000000000000000000..8453b351be918938a310506005e063bfc6a66980
--- /dev/null
+++ b/ui/src/store/modules/client.js
@@ -0,0 +1,177 @@
+import ApiService from "../../services/api.service";
+
+const state = {
+  error: null,
+  clients: [],
+  clientQrcodes: [],
+  clientConfigs: []
+}
+
+const getters = {
+  error(state) {
+    return state.error;
+  },
+  clients(state) {
+    return state.clients;
+  },
+  getClientQrcode: (state) => (id) => {
+    let item = state.clientQrcodes.find(item => item.id === id)
+    // initial load fails, must wait promise and stuff...
+    return item ? item.qrcode : "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg=="
+  },
+  getClientConfig: (state) => (id) => {
+    let item = state.clientConfigs.find(item => item.id === id)
+    return item ? item.config : null
+  }
+}
+
+const actions = {
+  error({ commit }, error){
+    commit('error', error)
+  },
+
+  readAll({ commit, dispatch }){
+    ApiService.get("/client")
+      .then(resp => {
+        commit('clients', resp)
+        dispatch('readQrcodes')
+        dispatch('readConfigs')
+      })
+      .catch(err => {
+        commit('error', err)
+      })
+  },
+
+  create({ commit, dispatch }, client){
+    ApiService.post("/client", client)
+      .then(resp => {
+        dispatch('readQrcode', resp)
+        dispatch('readConfig', resp)
+        commit('create', resp)
+      })
+      .catch(err => {
+        commit('error', err)
+      })
+  },
+
+  update({ commit, dispatch }, client){
+    ApiService.patch(`/client/${client.id}`, client)
+      .then(resp => {
+        dispatch('readQrcode', resp)
+        dispatch('readConfig', resp)
+        commit('update', resp)
+      })
+      .catch(err => {
+        commit('error', err)
+      })
+  },
+
+  delete({ commit }, client){
+    ApiService.delete(`/client/${client.id}`)
+      .then(() => {
+        commit('delete', client)
+      })
+      .catch(err => {
+        commit('error', err)
+      })
+  },
+
+  email({ commit }, client){
+    ApiService.get(`/client/${client.id}/email`)
+      .then(() => {
+      })
+      .catch(err => {
+        commit('error', err)
+      })
+  },
+
+  readQrcode({ state, commit }, client){
+    ApiService.getWithConfig(`/client/${client.id}/config?qrcode=true`, {responseType: 'arraybuffer'})
+      .then(resp => {
+        let image = Buffer.from(resp, 'binary').toString('base64')
+        commit('clientQrcodes', { client, image })
+      })
+      .catch(err => {
+        commit('error', err)
+      })
+  },
+
+  readConfig({ state, commit }, client){
+    ApiService.getWithConfig(`/client/${client.id}/config?qrcode=false`, {responseType: 'arraybuffer'})
+      .then(resp => {
+        commit('clientConfigs', { client: client, config: resp })
+      })
+      .catch(err => {
+        commit('error', err)
+      })
+  },
+
+  readQrcodes({ state, dispatch }){
+    state.clients.forEach(client => {
+      dispatch('readQrcode', client)
+    })
+  },
+
+  readConfigs({ state, dispatch }){
+    state.clients.forEach(client => {
+      dispatch('readConfig', client)
+    })
+  },
+}
+
+const mutations = {
+  error(state, error) {
+    state.error = error;
+  },
+  clients(state, clients){
+    state.clients = clients
+  },
+  create(state, client){
+    state.clients.push(client)
+  },
+  update(state, client){
+    let index = state.clients.findIndex(x => x.id === client.id);
+    if (index !== -1) {
+      state.clients.splice(index, 1);
+      state.clients.push(client);
+    } else {
+      state.error = "update client failed, not in list"
+    }
+  },
+  delete(state, client){
+    let index = state.clients.findIndex(x => x.id === client.id);
+    if (index !== -1) {
+      state.clients.splice(index, 1);
+    } else {
+      state.error = "delete client failed, not in list"
+    }
+  },
+  clientQrcodes(state, { client, image }){
+    let index = state.clientQrcodes.findIndex(x => x.id === client.id);
+    if (index !== -1) {
+      state.clientQrcodes.splice(index, 1);
+    }
+    state.clientQrcodes.push({
+      id: client.id,
+      qrcode: image
+    })
+  },
+  clientConfigs(state, { client, config }){
+    let index = state.clientConfigs.findIndex(x => x.id === client.id);
+    if (index !== -1) {
+      state.clientConfigs.splice(index, 1);
+    }
+    state.clientConfigs.push({
+      id: client.id,
+      config: config
+    })
+  },
+}
+
+export default {
+  namespaced: true,
+  state,
+  getters,
+  actions,
+  mutations
+}
diff --git a/ui/src/store/modules/server.js b/ui/src/store/modules/server.js
new file mode 100644
index 0000000000000000000000000000000000000000..63ba16968c332d47365b40f28cbcad9638a31348
--- /dev/null
+++ b/ui/src/store/modules/server.js
@@ -0,0 +1,100 @@
+import ApiService from "../../services/api.service";
+
+const state = {
+  error: null,
+  server: null,
+  config: '',
+  version: '_ci_build_not_run_properly_',
+}
+
+const getters = {
+  error(state) {
+    return state.error;
+  },
+
+  server(state) {
+    return state.server;
+  },
+
+  version(state) {
+    return state.version;
+  },
+
+  config(state) {
+    return state.config;
+  },
+}
+
+const actions = {
+  error({ commit }, error){
+    commit('error', error)
+  },
+
+  read({ commit, dispatch }){
+    ApiService.get("/server")
+      .then(resp => {
+        commit('server', resp)
+        dispatch('config')
+      })
+      .catch(err => {
+        commit('error', err)
+      })
+  },
+
+  update({ commit }, server){
+    ApiService.patch(`/server`, server)
+      .then(resp => {
+        commit('server', resp)
+      })
+      .catch(err => {
+        commit('error', err)
+      })
+  },
+
+  config({ commit }){
+    ApiService.getWithConfig("/server/config", {responseType: 'arraybuffer'})
+      .then(resp => {
+        commit('config', resp)
+      })
+      .catch(err => {
+        commit('error', err)
+      })
+  },
+
+  version({ commit }){
+    ApiService.get("/server/version")
+      .then(resp => {
+        commit('version', resp.version)
+      })
+      .catch(err => {
+        commit('error', err)
+      })
+  },
+
+}
+
+const mutations = {
+  error(state, error) {
+    state.error = error;
+  },
+
+  server(state, server){
+    state.server = server
+  },
+
+  config(state, config){
+    state.config = config
+  },
+
+  version(state, version){
+    state.version = version
+  },
+}
+
+export default {
+  namespaced: true,
+  state,
+  getters,
+  actions,
+  mutations
+}
diff --git a/ui/src/views/Index.vue b/ui/src/views/Index.vue
deleted file mode 100644
index 46ed1f1861dfdd31540f4eca4b23b25ba4e21f55..0000000000000000000000000000000000000000
--- a/ui/src/views/Index.vue
+++ /dev/null
@@ -1,10 +0,0 @@
-<template>
-</template>
-
-<script>
-  export default {
-    created () {
-      this.$router.replace({ name: 'clients' })
-    }
-  }
-</script>
diff --git a/util/util.go b/util/util.go
index 5d7e323c57e616b6c0404eb23351df0e22674ebe..3d3f88c73adcfe99564589000b82831df49ece19 100644
--- a/util/util.go
+++ b/util/util.go
@@ -1,6 +1,8 @@
 package util
 
 import (
+	"crypto/rand"
+	"encoding/base64"
 	"errors"
 	"io/ioutil"
 	"net"
@@ -9,6 +11,7 @@ import (
 )
 
 var (
+	AuthTokenHeaderName = "Authorization"
 	// RegexpEmail check valid email
 	RegexpEmail = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
 )
@@ -51,7 +54,7 @@ func DirectoryExists(name string) bool {
 	return info.IsDir()
 }
 
-// GetAvailableIp search for an available in cidr against a list of reserved ips
+// GetAvailableIp search for an available ip in cidr against a list of reserved ips
 func GetAvailableIp(cidr string, reserved []string) (string, error) {
 	ip, ipnet, err := net.ParseCIDR(cidr)
 	if err != nil {
@@ -132,3 +135,28 @@ func BroadcastAddr(n *net.IPNet) net.IP {
 	}
 	return broadcast
 }
+
+// GenerateRandomBytes returns securely generated random bytes.
+// It will return an error if the system's secure random
+// number generator fails to function correctly, in which
+// case the caller should not continue.
+func GenerateRandomBytes(n int) ([]byte, error) {
+	b := make([]byte, n)
+	_, err := rand.Read(b)
+	// Note that err == nil only if we read len(b) bytes.
+	if err != nil {
+		return nil, err
+	}
+
+	return b, nil
+}
+
+// GenerateRandomString returns a URL-safe, base64 encoded
+// securely generated random string.
+// It will return an error if the system's secure random
+// number generator fails to function correctly, in which
+// case the caller should not continue.
+func GenerateRandomString(s int) (string, error) {
+	b, err := GenerateRandomBytes(s)
+	return base64.URLEncoding.EncodeToString(b), err
+}