1
1
mirror of https://github.com/vx3r/wg-gen-web.git synced 2024-11-26 04:19:41 +01:00

oauth2 oidc, vuex store

This commit is contained in:
vx3r 2020-04-28 20:11:49 +09:00
parent f90124afbf
commit 125ddaef0f
36 changed files with 2050 additions and 847 deletions

35
.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

@ -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)
apiv1.ApplyRoutes(api, private)
}
}
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
}
// 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,
})
}

134
api/v1/auth/auth.go Normal file

@ -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)
}

190
api/v1/client/client.go Normal file

@ -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{})
}

113
api/v1/server/server.go Normal file

@ -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,
})
}

21
api/v1/v1.go Normal file

@ -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)
}
}
}

13
auth/auth.go Normal file

@ -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)
}

47
auth/fake/fake.go Normal file

@ -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
}

1
auth/github/github.go Normal file

@ -0,0 +1 @@
package github

1
auth/google/goolge.go Normal file

@ -0,0 +1 @@
package google

@ -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
}

@ -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 {

11
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
)

9
model/auth.go Normal file

@ -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"`
}

@ -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"`
}

@ -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"`
}

12
model/user.go Normal file

@ -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"`
}

602
ui/package-lock.json generated

@ -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
},

@ -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",

@ -1,69 +1,36 @@
<template>
<v-app id="inspire">
<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>
<Notification v-bind:notification="notification"/>
<div v-if="this.isAuthenticated">
<Header/>
<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>
<Footer/>
</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-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;

@ -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>
@ -59,7 +59,7 @@
<template v-slot:activator="{ on }">
<v-btn
text
:href="`${apiBaseUrl}/client/${client.id}/config?qrcode=false`"
v-on:click="forceFileDownload(client)"
v-on="on"
>
<span class="d-none d-lg-flex">Download</span>
@ -73,7 +73,7 @@
<template v-slot:activator="{ on }">
<v-btn
text
@click.stop="startUpdateClient(client)"
@click.stop="startUpdate(client)"
v-on="on"
>
<span class="d-none d-lg-flex">Edit</span>
@ -87,7 +87,7 @@
<template v-slot:activator="{ on }">
<v-btn
text
@click="deleteClient(client)"
@click="remove(client)"
v-on="on"
>
<span class="d-none d-lg-flex">Delete</span>
@ -101,7 +101,7 @@
<template v-slot:activator="{ on }">
<v-btn
text
@click="sendEmailClient(client.id)"
@click="email(client)"
v-on="on"
>
<span class="d-none d-lg-flex">Send Email</span>
@ -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);
});
},
...mapActions('client', {
errorClient: 'error',
readAllClients: 'readAll',
creatClient: 'create',
updateClient: 'update',
deleteClient: 'delete',
emailClient: 'email',
}),
...mapActions('server', {
readServer: 'read',
}),
getServer() {
this.api.get('/server').then((res) => {
this.server = res;
}).catch((e) => {
this.notify('error', e.response.status + ' ' + e.response.statusText);
});
},
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>

@ -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>

@ -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>

@ -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>

@ -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')

26
ui/src/plugins/axios.js Normal file

@ -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);
}
});

@ -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

@ -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)
};
}

@ -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;

@ -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
};

19
ui/src/store/index.js Normal file

@ -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
}
})

@ -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
}

@ -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
}

@ -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
}

@ -1,10 +0,0 @@
<template>
</template>
<script>
export default {
created () {
this.$router.replace({ name: 'clients' })
}
}
</script>

@ -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
}