mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-08 00:50:28 +08:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
8435f7b5a2 | |||
98734837a8 | |||
218b96ed74 | |||
89476a79ad | |||
b003838b00 | |||
5efc866a84 | |||
d77278fb93 | |||
17b974f895 | |||
046ed8fe3b | |||
2422943a59 | |||
723c68822c |
2
.env.template
Normal file
2
.env.template
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
HTTP_PORT=:7000
|
||||||
|
DB_DATABASE_SOURCE=root:123@tcp(localhost:3306)/casdoor
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -22,4 +22,6 @@ tmpFiles/
|
|||||||
*.tmp
|
*.tmp
|
||||||
logs/
|
logs/
|
||||||
lastupdate.tmp
|
lastupdate.tmp
|
||||||
commentsRouter*.go
|
commentsRouter*.go
|
||||||
|
|
||||||
|
.env
|
||||||
|
37
README.md
37
README.md
@ -1 +1,36 @@
|
|||||||
# casdoor
|
# Casdoor
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
Go (1.15 or later)
|
||||||
|
MySQL (5.8 or higher)
|
||||||
|
Node.js (version 8 or higher)
|
||||||
|
Yarn
|
||||||
|
|
||||||
|
### Initialize project
|
||||||
|
|
||||||
|
Create a database named casdoor.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE DATABASE IF NOT EXISTS casdoor default charset utf8 COLLATE utf8_general_ci
|
||||||
|
```
|
||||||
|
|
||||||
|
Configure database source in the `.env` file. If you have not copied the `.env.template` file to a new file named `.env`, which should now be performed.
|
||||||
|
|
||||||
|
### Run project
|
||||||
|
|
||||||
|
#### backend
|
||||||
|
|
||||||
|
```shell
|
||||||
|
go run cmd/main.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### frontend
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cd web
|
||||||
|
yarn
|
||||||
|
yarn start
|
||||||
|
```
|
||||||
|
68
cmd/main.go
Normal file
68
cmd/main.go
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/internal/config"
|
||||||
|
"github.com/casdoor/casdoor/internal/handler"
|
||||||
|
"github.com/casdoor/casdoor/internal/store"
|
||||||
|
"github.com/casdoor/casdoor/internal/store/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cfg, err := config.NewConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db, err := shared.NewDB(cfg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
userStore := store.NewUserStore(db)
|
||||||
|
applicationStore := store.NewApplicationStore(db)
|
||||||
|
|
||||||
|
srv := &http.Server{
|
||||||
|
Addr: cfg.HTTPPort,
|
||||||
|
Handler: handler.New(userStore, applicationStore),
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
log.Printf("listen and serve on %s", srv.Addr)
|
||||||
|
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
|
log.Fatalf("listen: %s\n", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
quit := make(chan os.Signal)
|
||||||
|
signal.Notify(quit, os.Interrupt)
|
||||||
|
<-quit
|
||||||
|
log.Println("Shutdown Server ...")
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
if err := srv.Shutdown(ctx); err != nil {
|
||||||
|
log.Fatal("Server Shutdown:", err)
|
||||||
|
}
|
||||||
|
log.Println("Server exiting")
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
appname = casdoor
|
|
||||||
httpport = 7000
|
|
||||||
runmode = dev
|
|
||||||
SessionOn = true
|
|
||||||
copyrequestbody = true
|
|
||||||
dataSourceName = root:123@tcp(localhost:3306)/
|
|
@ -1,70 +0,0 @@
|
|||||||
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package controllers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/object"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (c *ApiController) GetUsers() {
|
|
||||||
owner := c.Input().Get("owner")
|
|
||||||
|
|
||||||
c.Data["json"] = object.GetUsers(owner)
|
|
||||||
c.ServeJSON()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ApiController) GetUser() {
|
|
||||||
id := c.Input().Get("id")
|
|
||||||
|
|
||||||
c.Data["json"] = object.GetUser(id)
|
|
||||||
c.ServeJSON()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ApiController) UpdateUser() {
|
|
||||||
id := c.Input().Get("id")
|
|
||||||
|
|
||||||
var user object.User
|
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Data["json"] = object.UpdateUser(id, &user)
|
|
||||||
c.ServeJSON()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ApiController) AddUser() {
|
|
||||||
var user object.User
|
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Data["json"] = object.AddUser(&user)
|
|
||||||
c.ServeJSON()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ApiController) DeleteUser() {
|
|
||||||
var user object.User
|
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Data["json"] = object.DeleteUser(&user)
|
|
||||||
c.ServeJSON()
|
|
||||||
}
|
|
15
go.mod
15
go.mod
@ -3,8 +3,21 @@ module github.com/casdoor/casdoor
|
|||||||
go 1.15
|
go 1.15
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/astaxie/beego v1.12.2
|
github.com/gin-contrib/cors v1.3.1
|
||||||
|
github.com/gin-gonic/gin v1.6.3
|
||||||
|
github.com/go-playground/validator/v10 v10.4.0 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.5.0
|
github.com/go-sql-driver/mysql v1.5.0
|
||||||
|
github.com/golang/protobuf v1.4.3 // indirect
|
||||||
|
github.com/google/uuid v1.1.2
|
||||||
|
github.com/joho/godotenv v1.3.0
|
||||||
|
github.com/json-iterator/go v1.1.10 // indirect
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 // indirect
|
||||||
|
github.com/ugorji/go v1.1.12 // indirect
|
||||||
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 // indirect
|
||||||
|
google.golang.org/protobuf v1.25.0 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.3.0 // indirect
|
||||||
xorm.io/core v0.7.2
|
xorm.io/core v0.7.2
|
||||||
xorm.io/xorm v0.8.1
|
xorm.io/xorm v0.8.1
|
||||||
)
|
)
|
||||||
|
184
go.sum
184
go.sum
@ -1,55 +1,53 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
|
||||||
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
|
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
|
||||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
|
||||||
github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
|
|
||||||
github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
|
|
||||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||||
github.com/astaxie/beego v1.12.2 h1:CajUexhSX5ONWDiSCpeQBNVfTzOtPb9e9d+3vuU5FuU=
|
|
||||||
github.com/astaxie/beego v1.12.2/go.mod h1:TMcqhsbhN3UFpN+RCfysaxPAbrhox6QSS3NIAEp/uzE=
|
|
||||||
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
|
|
||||||
github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
|
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
|
||||||
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
|
|
||||||
github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
|
|
||||||
github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
|
|
||||||
github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
|
|
||||||
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
|
|
||||||
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4 h1:YcpmyvADGYw5LqMnHqSkyIELsHCGF6PkrmM31V8rF7o=
|
||||||
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
||||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
|
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||||
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
|
github.com/gin-contrib/cors v1.3.1 h1:doAsuITavI4IOcd0Y19U4B+O0dNWihRyX//nn4sEmgA=
|
||||||
|
github.com/gin-contrib/cors v1.3.1/go.mod h1:jjEJ4268OPZUcU7k9Pm653S7lXUGcqMADzFA61xsmDk=
|
||||||
|
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||||
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
|
github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do=
|
||||||
|
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
|
||||||
|
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||||
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
|
github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM=
|
||||||
|
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
|
||||||
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
|
github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY=
|
||||||
|
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
|
||||||
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
|
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
|
||||||
|
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||||
|
github.com/go-playground/validator/v10 v10.4.0 h1:72qIR/m8ybvL8L5TIyfgrigqkrw7kVYAvjEvpT85l70=
|
||||||
|
github.com/go-playground/validator/v10 v10.4.0/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
|
||||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
|
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
|
||||||
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
|
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
@ -57,117 +55,117 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
|
|||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
|
||||||
github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
|
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||||
|
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||||
|
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
|
||||||
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||||
github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
|
|
||||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
|
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
|
||||||
|
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
|
github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ=
|
github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw=
|
||||||
|
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
|
||||||
|
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
|
||||||
|
github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||||
|
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||||
|
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||||
|
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||||
|
github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
|
||||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
|
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
|
||||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||||
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
|
||||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
|
||||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||||
github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
|
||||||
github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
|
|
||||||
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||||
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
|
||||||
github.com/prometheus/client_golang v1.7.0 h1:wCi7urQOGBsYcQROHqpUUX4ct84xp40t9R9JX0FuA/U=
|
|
||||||
github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
|
||||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
|
||||||
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
|
||||||
github.com/prometheus/common v0.10.0 h1:RyRA7RzGXQZiW+tGMr7sxa85G1z0yOpM1qq5c8lNawc=
|
|
||||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
|
||||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo=
|
|
||||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
|
|
||||||
github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
|
|
||||||
github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s=
|
|
||||||
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
|
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
|
||||||
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
|
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
|
||||||
github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||||
github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
|
github.com/ugorji/go v1.1.12 h1:CzCiySZwEUgSwlgT8bTjFfV3rR6+Ti0atNsQkCRJnek=
|
||||||
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
|
github.com/ugorji/go v1.1.12/go.mod h1:Ne4Hz4JKQXNr/qi+hC0ovwseF9muoPjAZp2lylyih70=
|
||||||
github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
|
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
|
||||||
|
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
|
||||||
|
github.com/ugorji/go/codec v1.1.12 h1:pv4DBnMb5X9XXCNC0DyEmhU3I/61gWDdyH7iZps5DLs=
|
||||||
|
github.com/ugorji/go/codec v1.1.12/go.mod h1:U/SFD954ms+MwaHihwfeIz/sGz5OFgHt81tHc+Duy5k=
|
||||||
|
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E=
|
||||||
|
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@ -177,9 +175,6 @@ golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73r
|
|||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -187,7 +182,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -195,21 +189,22 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
|
|||||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418 h1:HlFl4V6pEMziuLXyRkm5BIYq1y1GAbb02pRlWvI54OM=
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80=
|
golang.org/x/sys v0.0.0-20201029080932-201ba4db2418/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
|
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20200117065230-39095c1d176c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
@ -218,32 +213,39 @@ google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
|||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||||
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
|
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
|
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||||
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||||
|
gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||||
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
|
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
|
||||||
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
|
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
|
||||||
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
|
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
|
||||||
|
48
internal/config/config.go
Normal file
48
internal/config/config.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/joho/godotenv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config holds shared configuration on server.
|
||||||
|
type Config struct {
|
||||||
|
HTTPPort string
|
||||||
|
DBDataSource string
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewConfig reads shared configuration from .env file.
|
||||||
|
func NewConfig() (*Config, error) {
|
||||||
|
err := godotenv.Load()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("godotenv.Load: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := &Config{
|
||||||
|
DBDataSource: os.Getenv("DB_DATABASE_SOURCE"),
|
||||||
|
HTTPPort: os.Getenv("HTTP_PORT"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.DBDataSource == "" {
|
||||||
|
return nil, errors.New("DB_DATABASE_SOURCE is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
return cfg, nil
|
||||||
|
}
|
46
internal/handler/api.go
Normal file
46
internal/handler/api.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/internal/handler/application"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/internal/handler/user"
|
||||||
|
"github.com/casdoor/casdoor/internal/store"
|
||||||
|
|
||||||
|
"github.com/gin-contrib/cors"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
var corsConfig = cors.Config{
|
||||||
|
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE"},
|
||||||
|
AllowHeaders: []string{"Origin", "Content-Length", "Content-Type"},
|
||||||
|
ExposeHeaders: []string{"Content-Length"},
|
||||||
|
AllowCredentials: true,
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSNotSupportingCredentials
|
||||||
|
AllowOrigins: []string{"http://localhost:3000"},
|
||||||
|
MaxAge: 300,
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(userStore *store.UserStore, applicationStore *store.ApplicationStore) http.Handler {
|
||||||
|
r := gin.New()
|
||||||
|
r.Use(gin.Logger())
|
||||||
|
r.Use(gin.Recovery())
|
||||||
|
r.Use(cors.New(corsConfig))
|
||||||
|
|
||||||
|
//r.StaticFS("/", http.Dir("web/build/index.html"))
|
||||||
|
|
||||||
|
apiGroup := r.Group("/api")
|
||||||
|
userHandler := user.New(userStore)
|
||||||
|
apiGroup.GET("/get-users", userHandler.GetUsers)
|
||||||
|
apiGroup.GET("/get-user", userHandler.GetUser)
|
||||||
|
apiGroup.POST("/update-user", userHandler.UpdateUser)
|
||||||
|
apiGroup.POST("/add-user", userHandler.AddUser)
|
||||||
|
apiGroup.POST("/delete-user", userHandler.DeleteUser)
|
||||||
|
|
||||||
|
applicationHandler := application.New(applicationStore)
|
||||||
|
apiGroup.GET("/applications", applicationHandler.List)
|
||||||
|
apiGroup.POST("/applications", applicationHandler.Create)
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
62
internal/handler/application/application.go
Normal file
62
internal/handler/application/application.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/internal/object"
|
||||||
|
"github.com/casdoor/casdoor/internal/store"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
applicationStore *store.ApplicationStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(applicationStore *store.ApplicationStore) *Handler {
|
||||||
|
return &Handler{applicationStore: applicationStore}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) Create(g *gin.Context) {
|
||||||
|
app := &object.Application{}
|
||||||
|
err := g.BindJSON(app)
|
||||||
|
if err != nil {
|
||||||
|
_ = g.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
app.Id = uuid.New().String()
|
||||||
|
app.CreatedTime = time.Now().Format(time.RFC3339)
|
||||||
|
app.CreatedBy = "TODO"
|
||||||
|
err = h.applicationStore.Create(app)
|
||||||
|
if err != nil {
|
||||||
|
_ = g.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) List(g *gin.Context) {
|
||||||
|
limit, _ := strconv.Atoi(g.DefaultQuery("limit", "20"))
|
||||||
|
offset, _ := strconv.Atoi(g.DefaultQuery("offset", "0"))
|
||||||
|
apps, err := h.applicationStore.List(limit, offset)
|
||||||
|
if err != nil {
|
||||||
|
_ = g.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.JSON(http.StatusOK, apps)
|
||||||
|
}
|
85
internal/handler/user/user.go
Normal file
85
internal/handler/user/user.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package user
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/internal/object"
|
||||||
|
"github.com/casdoor/casdoor/internal/store"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handler struct {
|
||||||
|
userStore *store.UserStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(userStore *store.UserStore) *Handler {
|
||||||
|
return &Handler{userStore: userStore}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) GetUsers(g *gin.Context) {
|
||||||
|
owner := g.GetString("owner")
|
||||||
|
user, err := h.userStore.GetUsers(owner)
|
||||||
|
if err != nil {
|
||||||
|
_ = g.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
g.JSON(http.StatusOK, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) GetUser(g *gin.Context) {
|
||||||
|
id := g.GetString("id")
|
||||||
|
u, err := h.userStore.GetUser(id)
|
||||||
|
if err != nil {
|
||||||
|
_ = g.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.JSON(http.StatusOK, u)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) UpdateUser(g *gin.Context) {
|
||||||
|
id := g.GetString("id")
|
||||||
|
|
||||||
|
var user object.User
|
||||||
|
err := g.BindJSON(&user)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := h.userStore.UpdateUser(id, &user)
|
||||||
|
if err != nil {
|
||||||
|
_ = g.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.JSON(http.StatusOK, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) AddUser(g *gin.Context) {
|
||||||
|
var user object.User
|
||||||
|
err := g.BindJSON(&user)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
ok, err := h.userStore.AddUser(&user)
|
||||||
|
if err != nil {
|
||||||
|
_ = g.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.JSON(http.StatusOK, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) DeleteUser(g *gin.Context) {
|
||||||
|
var user object.User
|
||||||
|
err := g.BindJSON(&user)
|
||||||
|
if err != nil {
|
||||||
|
_ = g.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ok, err := h.userStore.DeleteUser(&user)
|
||||||
|
if err != nil {
|
||||||
|
_ = g.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.JSON(http.StatusOK, ok)
|
||||||
|
}
|
9
internal/object/application.go
Normal file
9
internal/object/application.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package object
|
||||||
|
|
||||||
|
type Application struct {
|
||||||
|
Id string `xorm:"notnull pk" json:"id"`
|
||||||
|
Name string `xorm:"notnull pk" json:"name"`
|
||||||
|
|
||||||
|
CreatedTime string `json:"createdTime"`
|
||||||
|
CreatedBy string `xorm:"notnull" json:"createdBy"`
|
||||||
|
}
|
27
internal/object/user.go
Normal file
27
internal/object/user.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package object
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||||
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
|
|
||||||
|
Password string `xorm:"varchar(100)" json:"password"`
|
||||||
|
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
||||||
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
|
Email string `xorm:"varchar(100)" json:"email"`
|
||||||
|
Phone string `xorm:"varchar(100)" json:"phone"`
|
||||||
|
}
|
41
internal/store/application.go
Normal file
41
internal/store/application.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/casdoor/casdoor/internal/object"
|
||||||
|
"github.com/casdoor/casdoor/internal/store/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ApplicationStore struct {
|
||||||
|
db *shared.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewApplicationStore(db *shared.DB) *ApplicationStore {
|
||||||
|
return &ApplicationStore{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApplicationStore) Create(app *object.Application) error {
|
||||||
|
_, err := a.db.GetEngine().Insert(app)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *ApplicationStore) List(limit, offset int) ([]*object.Application, error) {
|
||||||
|
var apps []*object.Application
|
||||||
|
err := a.db.GetEngine().Desc("created_time").Limit(limit, offset).Find(&apps)
|
||||||
|
return apps, err
|
||||||
|
}
|
63
internal/store/shared/db.go
Normal file
63
internal/store/shared/db.go
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package shared
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/internal/config"
|
||||||
|
"github.com/casdoor/casdoor/internal/object"
|
||||||
|
_ "github.com/go-sql-driver/mysql"
|
||||||
|
"xorm.io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DB struct {
|
||||||
|
engine *xorm.Engine
|
||||||
|
driverName string
|
||||||
|
dataSourceName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDB(cfg *config.Config) (*DB, error) {
|
||||||
|
db := &DB{
|
||||||
|
dataSourceName: cfg.DBDataSource,
|
||||||
|
driverName: "mysql",
|
||||||
|
}
|
||||||
|
engine, err := xorm.NewEngine(db.driverName, db.dataSourceName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("xorm.NewEngine: %v", err)
|
||||||
|
}
|
||||||
|
err = engine.Ping()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("engine.Ping(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
db.engine = engine
|
||||||
|
|
||||||
|
err = db.createTable()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("db.createTable(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return db, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) GetEngine() *xorm.Engine {
|
||||||
|
return db.engine
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *DB) createTable() error {
|
||||||
|
err := db.engine.Sync2(new(object.User), new(object.Application))
|
||||||
|
return err
|
||||||
|
}
|
95
internal/store/user.go
Normal file
95
internal/store/user.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package store
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/casdoor/casdoor/internal/object"
|
||||||
|
"github.com/casdoor/casdoor/internal/store/shared"
|
||||||
|
"github.com/casdoor/casdoor/pkg/util"
|
||||||
|
"xorm.io/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserStore struct {
|
||||||
|
db *shared.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserStore(db *shared.DB) *UserStore {
|
||||||
|
return &UserStore{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserStore) GetUsers(owner string) ([]*object.User, error) {
|
||||||
|
var users []*object.User
|
||||||
|
err := u.db.GetEngine().Desc("created_time").Find(&users, &object.User{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserStore) getUser(owner string, name string) (*object.User, error) {
|
||||||
|
user := object.User{Owner: owner, Name: name}
|
||||||
|
existed, err := u.db.GetEngine().Get(&user)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if existed {
|
||||||
|
return &user, nil
|
||||||
|
} else {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserStore) GetUser(id string) (*object.User, error) {
|
||||||
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
|
return u.getUser(owner, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserStore) UpdateUser(id string, user *object.User) (bool, error) {
|
||||||
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
|
|
||||||
|
data, err := u.getUser(owner, name)
|
||||||
|
if err != nil || data == nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = u.db.GetEngine().Id(core.PK{owner, name}).AllCols().Update(user)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserStore) AddUser(user *object.User) (bool, error) {
|
||||||
|
affected, err := u.db.GetEngine().Insert(user)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *UserStore) DeleteUser(user *object.User) (bool, error) {
|
||||||
|
affected, err := u.db.GetEngine().Id(core.PK{user.Owner, user.Name}).Delete(&object.User{})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0, nil
|
||||||
|
}
|
48
main.go
48
main.go
@ -1,48 +0,0 @@
|
|||||||
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/astaxie/beego"
|
|
||||||
"github.com/astaxie/beego/plugins/cors"
|
|
||||||
"github.com/casdoor/casdoor/object"
|
|
||||||
"github.com/casdoor/casdoor/routers"
|
|
||||||
|
|
||||||
_ "github.com/casdoor/casdoor/routers"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
object.InitAdapter()
|
|
||||||
|
|
||||||
beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{
|
|
||||||
AllowOrigins: []string{"*"},
|
|
||||||
AllowMethods: []string{"GET", "PUT", "PATCH"},
|
|
||||||
AllowHeaders: []string{"Origin"},
|
|
||||||
ExposeHeaders: []string{"Content-Length"},
|
|
||||||
AllowCredentials: true,
|
|
||||||
}))
|
|
||||||
|
|
||||||
//beego.DelStaticPath("/static")
|
|
||||||
beego.SetStaticPath("/static", "web/build/static")
|
|
||||||
// https://studygolang.com/articles/2303
|
|
||||||
beego.InsertFilter("/", beego.BeforeRouter, routers.TransparentStatic) // must has this for default page
|
|
||||||
beego.InsertFilter("/*", beego.BeforeRouter, routers.TransparentStatic)
|
|
||||||
|
|
||||||
beego.BConfig.WebConfig.Session.SessionProvider="file"
|
|
||||||
beego.BConfig.WebConfig.Session.SessionProviderConfig = "./tmp"
|
|
||||||
beego.BConfig.WebConfig.Session.SessionGCMaxLifetime = 3600 * 24 * 365
|
|
||||||
|
|
||||||
beego.Run()
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package object
|
|
||||||
|
|
||||||
import (
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
|
||||||
_ "github.com/go-sql-driver/mysql"
|
|
||||||
"xorm.io/xorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
var adapter *Adapter
|
|
||||||
|
|
||||||
func InitAdapter() {
|
|
||||||
adapter = NewAdapter("mysql", beego.AppConfig.String("dataSourceName"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adapter represents the MySQL adapter for policy storage.
|
|
||||||
type Adapter struct {
|
|
||||||
driverName string
|
|
||||||
dataSourceName string
|
|
||||||
engine *xorm.Engine
|
|
||||||
}
|
|
||||||
|
|
||||||
// finalizer is the destructor for Adapter.
|
|
||||||
func finalizer(a *Adapter) {
|
|
||||||
err := a.engine.Close()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewAdapter is the constructor for Adapter.
|
|
||||||
func NewAdapter(driverName string, dataSourceName string) *Adapter {
|
|
||||||
a := &Adapter{}
|
|
||||||
a.driverName = driverName
|
|
||||||
a.dataSourceName = dataSourceName
|
|
||||||
|
|
||||||
// Open the DB, create it if not existed.
|
|
||||||
a.open()
|
|
||||||
|
|
||||||
// Call the destructor when the object is released.
|
|
||||||
runtime.SetFinalizer(a, finalizer)
|
|
||||||
|
|
||||||
return a
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Adapter) createDatabase() error {
|
|
||||||
engine, err := xorm.NewEngine(a.driverName, a.dataSourceName)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer engine.Close()
|
|
||||||
|
|
||||||
_, err = engine.Exec("CREATE DATABASE IF NOT EXISTS casdoor default charset utf8 COLLATE utf8_general_ci")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Adapter) open() {
|
|
||||||
if err := a.createDatabase(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
engine, err := xorm.NewEngine(a.driverName, a.dataSourceName+"casdoor")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
a.engine = engine
|
|
||||||
a.createTable()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Adapter) close() {
|
|
||||||
a.engine.Close()
|
|
||||||
a.engine = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *Adapter) createTable() {
|
|
||||||
err := a.engine.Sync2(new(User))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,94 +0,0 @@
|
|||||||
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package object
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/casdoor/casdoor/util"
|
|
||||||
"xorm.io/core"
|
|
||||||
)
|
|
||||||
|
|
||||||
type User struct {
|
|
||||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
|
||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
|
||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
|
||||||
|
|
||||||
Password string `xorm:"varchar(100)" json:"password"`
|
|
||||||
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
|
||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
|
||||||
Email string `xorm:"varchar(100)" json:"email"`
|
|
||||||
Phone string `xorm:"varchar(100)" json:"phone"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetUsers(owner string) []*User {
|
|
||||||
users := []*User{}
|
|
||||||
err := adapter.engine.Desc("created_time").Find(&users, &User{Owner: owner})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return users
|
|
||||||
}
|
|
||||||
|
|
||||||
func getUser(owner string, name string) *User {
|
|
||||||
user := User{Owner: owner, Name: name}
|
|
||||||
existed, err := adapter.engine.Get(&user)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if existed {
|
|
||||||
return &user
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetUser(id string) *User {
|
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
|
||||||
return getUser(owner, name)
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateUser(id string, user *User) bool {
|
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
|
||||||
if getUser(owner, name) == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := adapter.engine.Id(core.PK{owner, name}).AllCols().Update(user)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//return affected != 0
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddUser(user *User) bool {
|
|
||||||
affected, err := adapter.engine.Insert(user)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return affected != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func DeleteUser(user *User) bool {
|
|
||||||
affected, err := adapter.engine.Id(core.PK{user.Owner, user.Name}).Delete(&User{})
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return affected != 0
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package routers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/astaxie/beego"
|
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/controllers"
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
initAPI()
|
|
||||||
}
|
|
||||||
|
|
||||||
func initAPI() {
|
|
||||||
ns :=
|
|
||||||
beego.NewNamespace("/api",
|
|
||||||
beego.NSInclude(
|
|
||||||
&controllers.ApiController{},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
beego.AddNamespace(ns)
|
|
||||||
|
|
||||||
beego.Router("/api/get-users", &controllers.ApiController{}, "GET:GetUsers")
|
|
||||||
beego.Router("/api/get-user", &controllers.ApiController{}, "GET:GetUser")
|
|
||||||
beego.Router("/api/update-user", &controllers.ApiController{}, "POST:UpdateUser")
|
|
||||||
beego.Router("/api/add-user", &controllers.ApiController{}, "POST:AddUser")
|
|
||||||
beego.Router("/api/delete-user", &controllers.ApiController{}, "POST:DeleteUser")
|
|
||||||
}
|
|
5
web/.prettierrc
Normal file
5
web/.prettierrc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"printWidth": 140
|
||||||
|
}
|
46
web/README.md
Normal file
46
web/README.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Getting Started with Create React App
|
||||||
|
|
||||||
|
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||||
|
|
||||||
|
## Available Scripts
|
||||||
|
|
||||||
|
In the project directory, you can run:
|
||||||
|
|
||||||
|
### `yarn start`
|
||||||
|
|
||||||
|
Runs the app in the development mode.\
|
||||||
|
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||||
|
|
||||||
|
The page will reload if you make edits.\
|
||||||
|
You will also see any lint errors in the console.
|
||||||
|
|
||||||
|
### `yarn test`
|
||||||
|
|
||||||
|
Launches the test runner in the interactive watch mode.\
|
||||||
|
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||||
|
|
||||||
|
### `yarn build`
|
||||||
|
|
||||||
|
Builds the app for production to the `build` folder.\
|
||||||
|
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||||
|
|
||||||
|
The build is minified and the filenames include the hashes.\
|
||||||
|
Your app is ready to be deployed!
|
||||||
|
|
||||||
|
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||||
|
|
||||||
|
### `yarn eject`
|
||||||
|
|
||||||
|
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||||
|
|
||||||
|
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||||
|
|
||||||
|
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||||
|
|
||||||
|
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||||
|
|
||||||
|
To learn React, check out the [React documentation](https://reactjs.org/).
|
36
web/craco.config.js
Normal file
36
web/craco.config.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
const CracoLessPlugin = require('craco-less');
|
||||||
|
|
||||||
|
const emotionPresetOptions = {};
|
||||||
|
const emotionBabelPreset = require('@emotion/babel-preset-css-prop').default(undefined, emotionPresetOptions);
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
babel: {
|
||||||
|
plugins: [...emotionBabelPreset.plugins],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
{
|
||||||
|
plugin: CracoLessPlugin,
|
||||||
|
options: {
|
||||||
|
lessLoaderOptions: {
|
||||||
|
lessOptions: {
|
||||||
|
javascriptEnabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
@ -4,25 +4,28 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ant-design/icons": "^4.2.2",
|
"@ant-design/icons": "^4.2.2",
|
||||||
"@testing-library/jest-dom": "^4.2.4",
|
"@emotion/core": "^10.1.1",
|
||||||
"@testing-library/react": "^9.3.2",
|
"@emotion/styled": "^10.0.27",
|
||||||
"@testing-library/user-event": "^7.1.2",
|
|
||||||
"antd": "^4.7.2",
|
"antd": "^4.7.2",
|
||||||
|
"axios": "^0.21.0",
|
||||||
|
"history": "^5.0.0",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"react": "^16.14.0",
|
"react": "^17.0.1",
|
||||||
"react-device-detect": "^1.14.0",
|
"react-device-detect": "^1.14.0",
|
||||||
"react-dom": "^16.14.0",
|
"react-dom": "^17.0.1",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^6.0.0-beta.0",
|
||||||
"react-scripts": "3.4.3"
|
"twin.macro": "^1.12.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "set PORT=7001 && react-scripts start",
|
"start": "craco start",
|
||||||
"build": "react-scripts build",
|
"build": "craco build",
|
||||||
"test": "react-scripts test",
|
"test": "craco test"
|
||||||
"eject": "react-scripts eject"
|
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
"extends": "react-app"
|
"extends": [
|
||||||
|
"react-app",
|
||||||
|
"react-app/jest"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"browserslist": {
|
"browserslist": {
|
||||||
"production": [
|
"production": [
|
||||||
@ -35,5 +38,20 @@
|
|||||||
"last 1 firefox version",
|
"last 1 firefox version",
|
||||||
"last 1 safari version"
|
"last 1 safari version"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@craco/craco": "^5.7.0",
|
||||||
|
"@emotion/babel-preset-css-prop": "^10.1.0",
|
||||||
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
|
"@testing-library/react": "^11.1.0",
|
||||||
|
"@testing-library/user-event": "^12.1.10",
|
||||||
|
"@types/jest": "^26.0.15",
|
||||||
|
"@types/node": "^12.0.0",
|
||||||
|
"@types/react": "^16.9.53",
|
||||||
|
"@types/react-dom": "^16.9.8",
|
||||||
|
"craco-less": "^1.17.0",
|
||||||
|
"prettier": "2.1.2",
|
||||||
|
"react-scripts": "^4.0.0",
|
||||||
|
"typescript": "^4.0.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,7 @@
|
|||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
<meta content="Web site created using create-react-app" name="description" />
|
||||||
name="description"
|
|
||||||
content="Web site created using create-react-app"
|
|
||||||
/>
|
|
||||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||||
<!--
|
<!--
|
||||||
manifest.json provides metadata used when your web app is installed on a
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
@import '~antd/dist/antd.css';
|
|
||||||
|
|
||||||
.App {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-logo {
|
|
||||||
height: 40vmin;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-header {
|
|
||||||
background-color: #282c34;
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: calc(10px + 2vmin);
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.App-link {
|
|
||||||
color: #61dafb;
|
|
||||||
}
|
|
||||||
|
|
||||||
#parent-area {
|
|
||||||
position: relative;
|
|
||||||
min-height: 100vh;
|
|
||||||
}
|
|
||||||
|
|
||||||
#content-wrap {
|
|
||||||
padding-bottom: 70px; /* Footer height */
|
|
||||||
}
|
|
||||||
|
|
||||||
#footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 70px; /* Footer height */
|
|
||||||
}
|
|
289
web/src/App.js
289
web/src/App.js
@ -1,289 +0,0 @@
|
|||||||
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
import React, {Component} from 'react';
|
|
||||||
import './App.css';
|
|
||||||
import * as Setting from "./Setting";
|
|
||||||
import {DownOutlined, LogoutOutlined, SettingOutlined} from '@ant-design/icons';
|
|
||||||
import {Avatar, BackTop, Dropdown, Layout, Menu} from 'antd';
|
|
||||||
import {Switch, Route, withRouter, Redirect} from 'react-router-dom'
|
|
||||||
import * as AccountBackend from "./backend/AccountBackend";
|
|
||||||
import UserListPage from "./UserListPage";
|
|
||||||
import UserEditPage from "./UserEditPage";
|
|
||||||
|
|
||||||
const { Header, Footer } = Layout;
|
|
||||||
|
|
||||||
class App extends Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
classes: props,
|
|
||||||
selectedMenuKey: 0,
|
|
||||||
account: undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
Setting.initServerUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
this.updateMenuKey();
|
|
||||||
this.getAccount();
|
|
||||||
}
|
|
||||||
|
|
||||||
updateMenuKey() {
|
|
||||||
// eslint-disable-next-line no-restricted-globals
|
|
||||||
const uri = location.pathname;
|
|
||||||
if (uri === '/') {
|
|
||||||
this.setState({ selectedMenuKey: 0 });
|
|
||||||
} else if (uri.includes('users')) {
|
|
||||||
this.setState({ selectedMenuKey: 1 });
|
|
||||||
} else {
|
|
||||||
this.setState({ selectedMenuKey: -1 });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onLogined() {
|
|
||||||
this.getAccount();
|
|
||||||
}
|
|
||||||
|
|
||||||
onUpdateAccount(account) {
|
|
||||||
this.setState({
|
|
||||||
account: account
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getAccount() {
|
|
||||||
AccountBackend.getAccount()
|
|
||||||
.then((res) => {
|
|
||||||
const account = Setting.parseJson(res.data);
|
|
||||||
if (window.location.pathname === '/' && account === null) {
|
|
||||||
Setting.goToLink("/");
|
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
account: account,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (account !== undefined && account !== null) {
|
|
||||||
window.mouselogUserId = account.username;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
logout() {
|
|
||||||
this.setState({
|
|
||||||
expired: false,
|
|
||||||
submitted: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
AccountBackend.logout()
|
|
||||||
.then((res) => {
|
|
||||||
if (res.status === 'ok') {
|
|
||||||
this.setState({
|
|
||||||
account: null
|
|
||||||
});
|
|
||||||
|
|
||||||
Setting.showMessage("success", `Successfully logged out, redirected to homepage`);
|
|
||||||
|
|
||||||
Setting.goToLink("/");
|
|
||||||
} else {
|
|
||||||
Setting.showMessage("error", `Logout failed: ${res.msg}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleRightDropdownClick(e) {
|
|
||||||
if (e.key === '0') {
|
|
||||||
this.props.history.push(`/account`);
|
|
||||||
} else if (e.key === '1') {
|
|
||||||
this.logout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderRightDropdown() {
|
|
||||||
const menu = (
|
|
||||||
<Menu onClick={this.handleRightDropdownClick.bind(this)}>
|
|
||||||
<Menu.Item key='0'>
|
|
||||||
<SettingOutlined />
|
|
||||||
My Account
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item key='1'>
|
|
||||||
<LogoutOutlined />
|
|
||||||
Logout
|
|
||||||
</Menu.Item>
|
|
||||||
</Menu>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dropdown key="4" overlay={menu} >
|
|
||||||
<a className="ant-dropdown-link" href="#" style={{float: 'right'}}>
|
|
||||||
<Avatar style={{ backgroundColor: Setting.getAvatarColor(this.state.account.name), verticalAlign: 'middle' }} size="large">
|
|
||||||
{Setting.getShortName(this.state.account.name)}
|
|
||||||
</Avatar>
|
|
||||||
|
|
||||||
|
|
||||||
{Setting.isMobile() ? null : Setting.getShortName(this.state.account.name)} <DownOutlined />
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</a>
|
|
||||||
</Dropdown>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderAccount() {
|
|
||||||
let res = [];
|
|
||||||
|
|
||||||
if (this.state.account !== null && this.state.account !== undefined) {
|
|
||||||
res.push(this.renderRightDropdown());
|
|
||||||
} else {
|
|
||||||
res.push(
|
|
||||||
<Menu.Item key="1" style={{float: 'right', marginRight: '20px'}}>
|
|
||||||
<a href="/register">
|
|
||||||
Register
|
|
||||||
</a>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
res.push(
|
|
||||||
<Menu.Item key="2" style={{float: 'right'}}>
|
|
||||||
<a href="/login">
|
|
||||||
Login
|
|
||||||
</a>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
res.push(
|
|
||||||
<Menu.Item key="4" style={{float: 'right'}}>
|
|
||||||
<a href="/">
|
|
||||||
Home
|
|
||||||
</a>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderMenu() {
|
|
||||||
let res = [];
|
|
||||||
|
|
||||||
// if (this.state.account === null || this.state.account === undefined) {
|
|
||||||
// return [];
|
|
||||||
// }
|
|
||||||
|
|
||||||
res.push(
|
|
||||||
<Menu.Item key="0">
|
|
||||||
<a href="/">
|
|
||||||
Home
|
|
||||||
</a>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
res.push(
|
|
||||||
<Menu.Item key="1">
|
|
||||||
<a href="/users">
|
|
||||||
Users
|
|
||||||
</a>
|
|
||||||
</Menu.Item>
|
|
||||||
);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
renderHomeIfLogined(component) {
|
|
||||||
if (this.state.account !== null && this.state.account !== undefined) {
|
|
||||||
return <Redirect to='/' />
|
|
||||||
} else {
|
|
||||||
return component;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderLoginIfNotLogined(component) {
|
|
||||||
if (this.state.account === null) {
|
|
||||||
return <Redirect to='/login' />
|
|
||||||
} else if (this.state.account === undefined) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return component;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isStartPages() {
|
|
||||||
return window.location.pathname.startsWith('/login') ||
|
|
||||||
window.location.pathname.startsWith('/register') ||
|
|
||||||
window.location.pathname === '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
renderContent() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Header style={{ padding: '0', marginBottom: '3px'}}>
|
|
||||||
{
|
|
||||||
Setting.isMobile() ? null : <a className="logo" href={"/"} />
|
|
||||||
}
|
|
||||||
<Menu
|
|
||||||
// theme="dark"
|
|
||||||
mode={(Setting.isMobile() && this.isStartPages()) ? "inline" : "horizontal"}
|
|
||||||
defaultSelectedKeys={[`${this.state.selectedMenuKey}`]}
|
|
||||||
style={{ lineHeight: '64px' }}
|
|
||||||
>
|
|
||||||
{
|
|
||||||
this.renderMenu()
|
|
||||||
}
|
|
||||||
{
|
|
||||||
this.renderAccount()
|
|
||||||
}
|
|
||||||
</Menu>
|
|
||||||
</Header>
|
|
||||||
<Switch>
|
|
||||||
<Route exact path="/users" component={UserListPage}/>
|
|
||||||
<Route exact path="/users/:userName" component={UserEditPage}/>
|
|
||||||
</Switch>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
renderFooter() {
|
|
||||||
// How to keep your footer where it belongs ?
|
|
||||||
// https://www.freecodecamp.org/neyarnws/how-to-keep-your-footer-where-it-belongs-59c6aa05c59c/
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Footer id="footer" style={
|
|
||||||
{
|
|
||||||
borderTop: '1px solid #e8e8e8',
|
|
||||||
backgroundColor: 'white',
|
|
||||||
textAlign: 'center',
|
|
||||||
}
|
|
||||||
}>
|
|
||||||
Made with <span style={{color: 'rgb(255, 255, 255)'}}>❤️</span> by <a style={{fontWeight: "bold", color: "black"}} target="_blank" href="https://casbin.org">Casbin</a>
|
|
||||||
</Footer>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div id="parent-area">
|
|
||||||
<BackTop />
|
|
||||||
<div id="content-wrap">
|
|
||||||
{
|
|
||||||
this.renderContent()
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
{
|
|
||||||
this.renderFooter()
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default withRouter(App);
|
|
@ -1,9 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { render } from '@testing-library/react';
|
|
||||||
import App from './App';
|
|
||||||
|
|
||||||
test('renders learn react link', () => {
|
|
||||||
const { getByText } = render(<App />);
|
|
||||||
const linkElement = getByText(/learn react/i);
|
|
||||||
expect(linkElement).toBeInTheDocument();
|
|
||||||
});
|
|
@ -1,167 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import {Button, Card, Col, Input, Row} from 'antd';
|
|
||||||
import {LinkOutlined} from "@ant-design/icons";
|
|
||||||
import * as UserBackend from "./backend/UserBackend";
|
|
||||||
import * as Setting from "./Setting";
|
|
||||||
|
|
||||||
class UserEditPage extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
classes: props,
|
|
||||||
userName: props.match.params.userName,
|
|
||||||
user: null,
|
|
||||||
tasks: [],
|
|
||||||
resources: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
this.getUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
getUser() {
|
|
||||||
UserBackend.getUser("admin", this.state.userName)
|
|
||||||
.then((user) => {
|
|
||||||
this.setState({
|
|
||||||
user: user,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
parseUserField(key, value) {
|
|
||||||
// if ([].includes(key)) {
|
|
||||||
// value = Setting.myParseInt(value);
|
|
||||||
// }
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateUserField(key, value) {
|
|
||||||
value = this.parseUserField(key, value);
|
|
||||||
|
|
||||||
let user = this.state.user;
|
|
||||||
user[key] = value;
|
|
||||||
this.setState({
|
|
||||||
user: user,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
renderUser() {
|
|
||||||
return (
|
|
||||||
<Card size="small" title={
|
|
||||||
<div>
|
|
||||||
Edit User
|
|
||||||
<Button type="primary" onClick={this.submitUserEdit.bind(this)}>Save</Button>
|
|
||||||
</div>
|
|
||||||
} style={{marginLeft: '5px'}} type="inner">
|
|
||||||
<Row style={{marginTop: '10px'}} >
|
|
||||||
<Col style={{marginTop: '5px'}} span={2}>
|
|
||||||
Name:
|
|
||||||
</Col>
|
|
||||||
<Col span={22} >
|
|
||||||
<Input value={this.state.user.name} onChange={e => {
|
|
||||||
this.updateUserField('name', e.target.value);
|
|
||||||
}} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row style={{marginTop: '20px'}} >
|
|
||||||
<Col style={{marginTop: '5px'}} span={2}>
|
|
||||||
Password Type:
|
|
||||||
</Col>
|
|
||||||
<Col span={22} >
|
|
||||||
<Input value={this.state.user.passwordType} onChange={e => {
|
|
||||||
this.updateUserField('passwordType', e.target.value);
|
|
||||||
}} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row style={{marginTop: '20px'}} >
|
|
||||||
<Col style={{marginTop: '5px'}} span={2}>
|
|
||||||
Password:
|
|
||||||
</Col>
|
|
||||||
<Col span={22} >
|
|
||||||
<Input value={this.state.user.password} onChange={e => {
|
|
||||||
this.updateUserField('password', e.target.value);
|
|
||||||
}} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row style={{marginTop: '20px'}} >
|
|
||||||
<Col style={{marginTop: '5px'}} span={2}>
|
|
||||||
Display Name:
|
|
||||||
</Col>
|
|
||||||
<Col span={22} >
|
|
||||||
<Input value={this.state.user.displayName} onChange={e => {
|
|
||||||
this.updateUserField('displayName', e.target.value);
|
|
||||||
}} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row style={{marginTop: '20px'}} >
|
|
||||||
<Col style={{marginTop: '5px'}} span={2}>
|
|
||||||
Email:
|
|
||||||
</Col>
|
|
||||||
<Col span={22} >
|
|
||||||
<Input value={this.state.user.email} onChange={e => {
|
|
||||||
this.updateUserField('email', e.target.value);
|
|
||||||
}} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row style={{marginTop: '20px'}} >
|
|
||||||
<Col style={{marginTop: '5px'}} span={2}>
|
|
||||||
Phone:
|
|
||||||
</Col>
|
|
||||||
<Col span={22} >
|
|
||||||
<Input value={this.state.user.phone} onChange={e => {
|
|
||||||
this.updateUserField('phone', e.target.value);
|
|
||||||
}} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</Card>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
submitUserEdit() {
|
|
||||||
let user = Setting.deepCopy(this.state.user);
|
|
||||||
UserBackend.updateUser(this.state.user.owner, this.state.userName, user)
|
|
||||||
.then((res) => {
|
|
||||||
if (res) {
|
|
||||||
Setting.showMessage("success", `Successfully saved`);
|
|
||||||
this.setState({
|
|
||||||
userName: this.state.user.name,
|
|
||||||
});
|
|
||||||
this.props.history.push(`/users/${this.state.user.name}`);
|
|
||||||
} else {
|
|
||||||
Setting.showMessage("error", `failed to save: server side failure`);
|
|
||||||
this.updateUserField('name', this.state.userName);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
Setting.showMessage("error", `failed to save: ${error}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Row style={{width: "100%"}}>
|
|
||||||
<Col span={1}>
|
|
||||||
</Col>
|
|
||||||
<Col span={22}>
|
|
||||||
{
|
|
||||||
this.state.user !== null ? this.renderUser() : null
|
|
||||||
}
|
|
||||||
</Col>
|
|
||||||
<Col span={1}>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row style={{margin: 10}}>
|
|
||||||
<Col span={2}>
|
|
||||||
</Col>
|
|
||||||
<Col span={18}>
|
|
||||||
<Button type="primary" size="large" onClick={this.submitUserEdit.bind(this)}>Save</Button>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default UserEditPage;
|
|
@ -1,185 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import {Button, Col, Popconfirm, Row, Table} from 'antd';
|
|
||||||
import moment from "moment";
|
|
||||||
import * as Setting from "./Setting";
|
|
||||||
import * as UserBackend from "./backend/UserBackend";
|
|
||||||
|
|
||||||
class UserListPage extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
classes: props,
|
|
||||||
users: null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillMount() {
|
|
||||||
this.getUsers();
|
|
||||||
}
|
|
||||||
|
|
||||||
getUsers() {
|
|
||||||
UserBackend.getUsers("admin")
|
|
||||||
.then((res) => {
|
|
||||||
this.setState({
|
|
||||||
users: res,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
newUser() {
|
|
||||||
return {
|
|
||||||
owner: "admin", // this.props.account.username,
|
|
||||||
name: `user_${this.state.users.length}`,
|
|
||||||
createdTime: moment().format(),
|
|
||||||
password: "123456",
|
|
||||||
passwordType: "plain",
|
|
||||||
displayName: `New User - ${this.state.users.length}`,
|
|
||||||
email: "user@example.com",
|
|
||||||
phone: "1-12345678",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addUser() {
|
|
||||||
const newUser = this.newUser();
|
|
||||||
UserBackend.addUser(newUser)
|
|
||||||
.then((res) => {
|
|
||||||
Setting.showMessage("success", `User added successfully`);
|
|
||||||
this.setState({
|
|
||||||
users: Setting.prependRow(this.state.users, newUser),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.catch(error => {
|
|
||||||
Setting.showMessage("error", `User failed to add: ${error}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteUser(i) {
|
|
||||||
UserBackend.deleteUser(this.state.users[i])
|
|
||||||
.then((res) => {
|
|
||||||
Setting.showMessage("success", `User deleted successfully`);
|
|
||||||
this.setState({
|
|
||||||
users: Setting.deleteRow(this.state.users, i),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.catch(error => {
|
|
||||||
Setting.showMessage("error", `User failed to delete: ${error}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
renderTable(users) {
|
|
||||||
const columns = [
|
|
||||||
{
|
|
||||||
title: 'Name',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
width: '120px',
|
|
||||||
sorter: (a, b) => a.name.localeCompare(b.name),
|
|
||||||
render: (text, record, index) => {
|
|
||||||
return (
|
|
||||||
<a href={`/users/${text}`}>{text}</a>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Created Time',
|
|
||||||
dataIndex: 'createdTime',
|
|
||||||
key: 'createdTime',
|
|
||||||
width: '160px',
|
|
||||||
sorter: (a, b) => a.createdTime.localeCompare(b.createdTime),
|
|
||||||
render: (text, record, index) => {
|
|
||||||
return Setting.getFormattedDate(text);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'PasswordType',
|
|
||||||
dataIndex: 'passwordType',
|
|
||||||
key: 'passwordType',
|
|
||||||
width: '150px',
|
|
||||||
sorter: (a, b) => a.passwordType.localeCompare(b.passwordType),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Password',
|
|
||||||
dataIndex: 'password',
|
|
||||||
key: 'password',
|
|
||||||
width: '150px',
|
|
||||||
sorter: (a, b) => a.password.localeCompare(b.password),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Display Name',
|
|
||||||
dataIndex: 'displayName',
|
|
||||||
key: 'displayName',
|
|
||||||
// width: '100px',
|
|
||||||
sorter: (a, b) => a.displayName.localeCompare(b.displayName),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Email',
|
|
||||||
dataIndex: 'email',
|
|
||||||
key: 'email',
|
|
||||||
width: '150px',
|
|
||||||
sorter: (a, b) => a.email.localeCompare(b.email),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Phone',
|
|
||||||
dataIndex: 'phone',
|
|
||||||
key: 'phone',
|
|
||||||
width: '120px',
|
|
||||||
sorter: (a, b) => a.phone.localeCompare(b.phone),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'Action',
|
|
||||||
dataIndex: '',
|
|
||||||
key: 'op',
|
|
||||||
width: '170px',
|
|
||||||
render: (text, record, index) => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => Setting.goToLink(`/users/${record.name}`)}>Edit</Button>
|
|
||||||
<Popconfirm
|
|
||||||
title={`Sure to delete user: ${record.name} ?`}
|
|
||||||
onConfirm={() => this.deleteUser(index)}
|
|
||||||
>
|
|
||||||
<Button style={{marginBottom: '10px'}} type="danger">Delete</Button>
|
|
||||||
</Popconfirm>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Table columns={columns} dataSource={users} rowKey="name" size="middle" bordered pagination={{pageSize: 100}}
|
|
||||||
title={() => (
|
|
||||||
<div>
|
|
||||||
Users
|
|
||||||
<Button type="primary" size="small" onClick={this.addUser.bind(this)}>Add</Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
loading={users === null}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<Row style={{width: "100%"}}>
|
|
||||||
<Col span={1}>
|
|
||||||
</Col>
|
|
||||||
<Col span={22}>
|
|
||||||
{
|
|
||||||
this.renderTable(this.state.users)
|
|
||||||
}
|
|
||||||
</Col>
|
|
||||||
<Col span={1}>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default UserListPage;
|
|
222
web/src/app.tsx
Normal file
222
web/src/app.tsx
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import * as Setting from './setting';
|
||||||
|
import { DownOutlined, LogoutOutlined, SettingOutlined } from '@ant-design/icons';
|
||||||
|
import { Avatar, BackTop, Dropdown, Layout, Menu } from 'antd';
|
||||||
|
import { Route, Routes, useNavigate } from 'react-router-dom';
|
||||||
|
import * as AccountBackend from './backend/account-backend';
|
||||||
|
import { UserRoutes } from './user/user-routes';
|
||||||
|
import tw from 'twin.macro';
|
||||||
|
|
||||||
|
const { Header, Footer } = Layout;
|
||||||
|
const { SubMenu } = Menu;
|
||||||
|
|
||||||
|
interface Account {
|
||||||
|
username: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStartPages() {
|
||||||
|
return (
|
||||||
|
window.location.pathname.startsWith('/login') || window.location.pathname.startsWith('/register') || window.location.pathname === '/'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AppHeader() {
|
||||||
|
return (
|
||||||
|
<Header css={tw`fixed w-screen px-0`}>
|
||||||
|
{/*eslint-disable-next-line*/}
|
||||||
|
{Setting.isMobile() ? null : <a href="/" className="logo" />}
|
||||||
|
<AppMenu />
|
||||||
|
</Header>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AppMenu() {
|
||||||
|
const [selectedMenuKey, setSelectedMenuKey] = useState(0);
|
||||||
|
const [account, setAccount] = useState<Account | undefined>(undefined);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// TODO: Waiting for consolidation backend
|
||||||
|
// if (window.location.pathname !== '/' && window.location.pathname !== '/login' && window.location.pathname !== '/register' && !account) {
|
||||||
|
// history.replace('/login');
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
updateMenu();
|
||||||
|
// getAccount();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
function handleRightDropdownClick(e: any) {
|
||||||
|
if (e.key === 'account') {
|
||||||
|
navigate(`/account`);
|
||||||
|
} else if (e.key === 'logout') {
|
||||||
|
logout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateMenu() {
|
||||||
|
const uri = window.location.pathname;
|
||||||
|
if (uri === '/') {
|
||||||
|
setSelectedMenuKey(0);
|
||||||
|
} else if (uri.includes('users')) {
|
||||||
|
setSelectedMenuKey(1);
|
||||||
|
} else {
|
||||||
|
setSelectedMenuKey(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAccount() {
|
||||||
|
AccountBackend.getAccount().then((res) => {
|
||||||
|
const account = Setting.parseJson(res.data);
|
||||||
|
setAccount(account);
|
||||||
|
if (account) {
|
||||||
|
// @ts-ignore Mouselog plugins
|
||||||
|
window.mouselogUserId = account.username;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function logout() {
|
||||||
|
AccountBackend.logout().then((res) => {
|
||||||
|
// if (res.statusText === 'ok') {
|
||||||
|
// setAccount(undefined);
|
||||||
|
// Setting.showMessage('success', `Successfully logged out, redirected to homepage`);
|
||||||
|
// history.replace('/');
|
||||||
|
// } else {
|
||||||
|
// Setting.showMessage('error', `Logout failed: ${res.msg}`);
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div css={tw`h-16`}>
|
||||||
|
<Menu
|
||||||
|
// theme="dark"
|
||||||
|
mode={Setting.isMobile() && isStartPages() ? 'inline' : 'horizontal'}
|
||||||
|
defaultSelectedKeys={[`${selectedMenuKey}`]}
|
||||||
|
>
|
||||||
|
<Menu.Item key="home">
|
||||||
|
<a href="/">Home</a>
|
||||||
|
</Menu.Item>
|
||||||
|
{account ? (
|
||||||
|
<Dropdown
|
||||||
|
key="4"
|
||||||
|
overlay={
|
||||||
|
<Menu onClick={handleRightDropdownClick}>
|
||||||
|
<Menu.Item key="account">
|
||||||
|
<SettingOutlined />
|
||||||
|
My Account
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item key="logout">
|
||||||
|
<LogoutOutlined />
|
||||||
|
Logout
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{/*eslint-disable-next-line*/}
|
||||||
|
<a className="ant-dropdown-link" href="#" style={{ float: 'right' }}>
|
||||||
|
<Avatar
|
||||||
|
style={{
|
||||||
|
backgroundColor: Setting.getAvatarColor(account.name),
|
||||||
|
verticalAlign: 'middle',
|
||||||
|
}}
|
||||||
|
size="large"
|
||||||
|
>
|
||||||
|
{Setting.getShortName(account.name)}
|
||||||
|
</Avatar>
|
||||||
|
|
||||||
|
{Setting.isMobile() ? null : Setting.getShortName(account.name)}
|
||||||
|
<DownOutlined />
|
||||||
|
|
||||||
|
</a>
|
||||||
|
</Dropdown>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Menu.Item key="register" style={{ float: 'right', marginRight: '20px' }}>
|
||||||
|
<a href="/register">Register</a>
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item key="login" style={{ float: 'right' }}>
|
||||||
|
<a href="/login">Login</a>
|
||||||
|
</Menu.Item>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Menu>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function AppFooter() {
|
||||||
|
return (
|
||||||
|
<Footer css={tw`bg-white text-center px-0 py-0`}>
|
||||||
|
<div>
|
||||||
|
Made with <span css={tw`text-white`}>❤️</span> by{' '}
|
||||||
|
<a css={tw`font-bold text-black`} rel="noreferrer" target="_blank" href="https://casbin.org">
|
||||||
|
Casbin
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</Footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const navigator = useNavigate();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<AppHeader />
|
||||||
|
<BackTop />
|
||||||
|
<div css={tw`flex h-screen pt-16`}>
|
||||||
|
<div css={tw`flex flex-col justify-between w-64 overflow-y-auto static py-4 shadow`}>
|
||||||
|
<Menu
|
||||||
|
onClick={({ key }) => navigator(key as string)}
|
||||||
|
style={{ border: 'none' }}
|
||||||
|
defaultSelectedKeys={['applications-list']}
|
||||||
|
defaultOpenKeys={['applications']}
|
||||||
|
mode="inline"
|
||||||
|
>
|
||||||
|
<SubMenu key="applications" title="Applications">
|
||||||
|
<Menu.Item key="applications-list">List</Menu.Item>
|
||||||
|
</SubMenu>
|
||||||
|
<SubMenu key="identities" title="Identities">
|
||||||
|
<Menu.Item key="groups">Groups</Menu.Item>
|
||||||
|
<Menu.Item key="users">Users</Menu.Item>
|
||||||
|
</SubMenu>
|
||||||
|
<SubMenu key="permissions" title="Permissions">
|
||||||
|
<Menu.Item key="authorization">Authorization</Menu.Item>
|
||||||
|
<Menu.Item key="policies">Policies</Menu.Item>
|
||||||
|
</SubMenu>
|
||||||
|
<SubMenu key="audit-logs" title="Audit Logs">
|
||||||
|
<Menu.Item key="user-activity">User Activity</Menu.Item>
|
||||||
|
<Menu.Item key="admin-activity">Admin Activity</Menu.Item>
|
||||||
|
</SubMenu>
|
||||||
|
</Menu>
|
||||||
|
|
||||||
|
<AppFooter />
|
||||||
|
</div>
|
||||||
|
<div css={tw`flex flex-1 flex-col overflow-x-hidden overflow-y-auto px-6 py-8`}>
|
||||||
|
<div css={tw`container ml-auto mr-auto`}>
|
||||||
|
<Routes>
|
||||||
|
<Route path="users/*" element={<UserRoutes />} />
|
||||||
|
</Routes>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App;
|
@ -1,52 +0,0 @@
|
|||||||
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
import * as Setting from "../Setting";
|
|
||||||
|
|
||||||
export function getUser(username) {
|
|
||||||
return fetch(`${Setting.ServerUrl}/api/get-user?username=${username}`, {
|
|
||||||
method: 'GET',
|
|
||||||
credentials: 'include'
|
|
||||||
}).then(res => res.json());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getAccount() {
|
|
||||||
return fetch(`${Setting.ServerUrl}/api/get-account`, {
|
|
||||||
method: 'GET',
|
|
||||||
credentials: 'include'
|
|
||||||
}).then(res => res.json());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function register(values) {
|
|
||||||
return fetch(`${Setting.ServerUrl}/api/register`, {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: "include",
|
|
||||||
body: JSON.stringify(values),
|
|
||||||
}).then(res => res.json());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function login(values) {
|
|
||||||
return fetch(`${Setting.ServerUrl}/api/login`, {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: "include",
|
|
||||||
body: JSON.stringify(values),
|
|
||||||
}).then(res => res.json());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function logout() {
|
|
||||||
return fetch(`${Setting.ServerUrl}/api/logout`, {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: "include",
|
|
||||||
}).then(res => res.json());
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
import * as Setting from "../Setting";
|
|
||||||
|
|
||||||
export function getUsers(owner) {
|
|
||||||
return fetch(`${Setting.ServerUrl}/api/get-users?owner=${owner}`, {
|
|
||||||
method: "GET",
|
|
||||||
credentials: "include"
|
|
||||||
}).then(res => res.json());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUser(owner, name) {
|
|
||||||
return fetch(`${Setting.ServerUrl}/api/get-user?id=${owner}/${encodeURIComponent(name)}`, {
|
|
||||||
method: "GET",
|
|
||||||
credentials: "include"
|
|
||||||
}).then(res => res.json());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateUser(owner, name, user) {
|
|
||||||
let newUser = Setting.deepCopy(user);
|
|
||||||
return fetch(`${Setting.ServerUrl}/api/update-user?id=${owner}/${encodeURIComponent(name)}`, {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include',
|
|
||||||
body: JSON.stringify(newUser),
|
|
||||||
}).then(res => res.json());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addUser(user) {
|
|
||||||
let newUser = Setting.deepCopy(user);
|
|
||||||
return fetch(`${Setting.ServerUrl}/api/add-user`, {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include',
|
|
||||||
body: JSON.stringify(newUser),
|
|
||||||
}).then(res => res.json());
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deleteUser(user) {
|
|
||||||
let newUser = Setting.deepCopy(user);
|
|
||||||
return fetch(`${Setting.ServerUrl}/api/delete-user`, {
|
|
||||||
method: 'POST',
|
|
||||||
credentials: 'include',
|
|
||||||
body: JSON.stringify(newUser),
|
|
||||||
}).then(res => res.json());
|
|
||||||
}
|
|
@ -12,32 +12,24 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package routers
|
import { httpClient } from './backend';
|
||||||
|
|
||||||
import (
|
export function getUser(username: string) {
|
||||||
"net/http"
|
return httpClient.get(`/get-user?username=${username}`);
|
||||||
"strings"
|
}
|
||||||
|
|
||||||
"github.com/astaxie/beego/context"
|
export function getAccount() {
|
||||||
"github.com/casdoor/casdoor/util"
|
return httpClient.get(`/get-account`);
|
||||||
)
|
}
|
||||||
|
|
||||||
func TransparentStatic(ctx *context.Context) {
|
export function register(values: object) {
|
||||||
urlPath := ctx.Request.URL.Path
|
return httpClient.post(`/register`, values);
|
||||||
if strings.HasPrefix(urlPath, "/api/") {
|
}
|
||||||
return
|
|
||||||
}
|
export function login(values: object) {
|
||||||
|
return httpClient.post(`/login`, values);
|
||||||
path := "web/build"
|
}
|
||||||
if urlPath == "/" {
|
|
||||||
path += "/index.html"
|
export function logout() {
|
||||||
} else {
|
return httpClient.post(`/logout`);
|
||||||
path += urlPath
|
|
||||||
}
|
|
||||||
|
|
||||||
if util.FileExist(path) {
|
|
||||||
http.ServeFile(ctx.ResponseWriter, ctx.Request, path)
|
|
||||||
} else {
|
|
||||||
http.ServeFile(ctx.ResponseWriter, ctx.Request, "web/build/index.html")
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -12,23 +12,17 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package controllers
|
import axios from 'axios';
|
||||||
|
|
||||||
import "github.com/astaxie/beego"
|
function getBaseURL() {
|
||||||
|
const hostname = window.location.hostname;
|
||||||
type ApiController struct {
|
if (hostname === 'localhost') {
|
||||||
beego.Controller
|
return `http://${hostname}:7000/api`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApiController) GetSessionUser() string {
|
export const httpClient = axios.create({
|
||||||
user := c.GetSession("username")
|
baseURL: getBaseURL(),
|
||||||
if user == nil {
|
withCredentials: true,
|
||||||
return ""
|
});
|
||||||
}
|
|
||||||
|
|
||||||
return user.(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ApiController) SetSessionUser(user string) {
|
|
||||||
c.SetSession("username", user)
|
|
||||||
}
|
|
45
web/src/backend/user-backend.ts
Normal file
45
web/src/backend/user-backend.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import { httpClient } from './backend';
|
||||||
|
|
||||||
|
export interface User {
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
owner: string;
|
||||||
|
passwordType: string;
|
||||||
|
password: string;
|
||||||
|
displayName: string;
|
||||||
|
phone: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUsers(owner: string) {
|
||||||
|
return httpClient.get(`/get-users?owner=${owner}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUser(owner: string, name: string) {
|
||||||
|
return httpClient.get(`/get-user?id=${owner}/${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateUser(owner: string, name: string, user: any) {
|
||||||
|
return httpClient.post(`/update-user?id=${owner}/${name}`, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addUser(user: any) {
|
||||||
|
return httpClient.post(`/add-user`, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteUser(user: any) {
|
||||||
|
return httpClient.post(`/delete-user`, user);
|
||||||
|
}
|
@ -1,31 +0,0 @@
|
|||||||
body {
|
|
||||||
margin: 0;
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
|
||||||
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
|
||||||
sans-serif;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
|
||||||
monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo {
|
|
||||||
background: url("logo.png");
|
|
||||||
background-size: 108px, 33px;
|
|
||||||
width: 108px;
|
|
||||||
height: 27px;
|
|
||||||
/*background: rgba(0, 0, 0, 0.2);*/
|
|
||||||
margin: 17px 10px 16px 20px;
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-table.ant-table-middle .ant-table-title, .ant-table.ant-table-middle .ant-table-footer, .ant-table.ant-table-middle thead > tr > th, .ant-table.ant-table-middle tbody > tr > td {
|
|
||||||
padding: 1px 8px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-list-sm .ant-list-item {
|
|
||||||
padding: 2px !important;
|
|
||||||
}
|
|
38
web/src/index.less
Normal file
38
web/src/index.less
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
@import '~antd/dist/antd.less';
|
||||||
|
|
||||||
|
@primary-color: #0052cc; // primary color for all components
|
||||||
|
@link-color: #0052cc; // link color
|
||||||
|
@success-color: #36b37e; // success state color
|
||||||
|
@warning-color: #ffab00; // warning state color
|
||||||
|
@error-color: #ff5630; // error state color
|
||||||
|
|
||||||
|
@font-size-base: 14px; // major text font size
|
||||||
|
@heading-color: rgba(0, 0, 0, 0.85); // heading text color
|
||||||
|
@text-color: rgba(0, 0, 0, 0.65); // major text color
|
||||||
|
@text-color-secondary: rgba(0, 0, 0, 0.45); // secondary text color
|
||||||
|
@disabled-color: rgba(0, 0, 0, 0.25); // disable state color
|
||||||
|
@border-radius-base: 2px; // major border radius
|
||||||
|
@border-color-base: #d9d9d9; // major border color
|
||||||
|
@box-shadow-base: 0 3px 6px -4px rgba(0, 0, 0, 0.12),
|
||||||
|
0 6px 16px 0 rgba(0, 0, 0, 0.08), 0 9px 28px 8px rgba(0, 0, 0, 0.05); // major shadow for layers
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
background: url('logo.png');
|
||||||
|
background-size: 108px, 33px;
|
||||||
|
width: 108px;
|
||||||
|
height: 27px;
|
||||||
|
/*background: rgba(0, 0, 0, 0.2);*/
|
||||||
|
margin: 17px 10px 16px 20px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-table.ant-table-middle .ant-table-title,
|
||||||
|
.ant-table.ant-table-middle .ant-table-footer,
|
||||||
|
.ant-table.ant-table-middle thead > tr > th,
|
||||||
|
.ant-table.ant-table-middle tbody > tr > td {
|
||||||
|
padding: 1px 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-list-sm .ant-list-item {
|
||||||
|
padding: 2px !important;
|
||||||
|
}
|
@ -14,19 +14,17 @@
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import './index.css';
|
import App from './app';
|
||||||
import App from './App';
|
import { ConfigProvider } from 'antd';
|
||||||
import * as serviceWorker from './serviceWorker';
|
import enUS from 'antd/es/locale/en_US';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
import './index.less';
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<BrowserRouter>
|
<ConfigProvider locale={enUS}>
|
||||||
<App />
|
<BrowserRouter>
|
||||||
</BrowserRouter>,
|
<App />
|
||||||
|
</BrowserRouter>
|
||||||
|
</ConfigProvider>,
|
||||||
document.getElementById('root')
|
document.getElementById('root')
|
||||||
);
|
);
|
||||||
|
|
||||||
// If you want your app to work offline and load faster, you can change
|
|
||||||
// unregister() to register() below. Note this comes with some pitfalls.
|
|
||||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
|
||||||
serviceWorker.unregister();
|
|
15
web/src/react-app-env.d.ts
vendored
Normal file
15
web/src/react-app-env.d.ts
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
/// <reference types="react-scripts" />
|
@ -1,141 +0,0 @@
|
|||||||
// This optional code is used to register a service worker.
|
|
||||||
// register() is not called by default.
|
|
||||||
|
|
||||||
// This lets the app load faster on subsequent visits in production, and gives
|
|
||||||
// it offline capabilities. However, it also means that developers (and users)
|
|
||||||
// will only see deployed updates on subsequent visits to a page, after all the
|
|
||||||
// existing tabs open on the page have been closed, since previously cached
|
|
||||||
// resources are updated in the background.
|
|
||||||
|
|
||||||
// To learn more about the benefits of this model and instructions on how to
|
|
||||||
// opt-in, read https://bit.ly/CRA-PWA
|
|
||||||
|
|
||||||
const isLocalhost = Boolean(
|
|
||||||
window.location.hostname === 'localhost' ||
|
|
||||||
// [::1] is the IPv6 localhost address.
|
|
||||||
window.location.hostname === '[::1]' ||
|
|
||||||
// 127.0.0.0/8 are considered localhost for IPv4.
|
|
||||||
window.location.hostname.match(
|
|
||||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
export function register(config) {
|
|
||||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
|
||||||
// The URL constructor is available in all browsers that support SW.
|
|
||||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
|
|
||||||
if (publicUrl.origin !== window.location.origin) {
|
|
||||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
|
||||||
// from what our page is served on. This might happen if a CDN is used to
|
|
||||||
// serve assets; see https://github.com/facebook/create-react-app/issues/2374
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('load', () => {
|
|
||||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
|
||||||
|
|
||||||
if (isLocalhost) {
|
|
||||||
// This is running on localhost. Let's check if a service worker still exists or not.
|
|
||||||
checkValidServiceWorker(swUrl, config);
|
|
||||||
|
|
||||||
// Add some additional logging to localhost, pointing developers to the
|
|
||||||
// service worker/PWA documentation.
|
|
||||||
navigator.serviceWorker.ready.then(() => {
|
|
||||||
console.log(
|
|
||||||
'This web app is being served cache-first by a service ' +
|
|
||||||
'worker. To learn more, visit https://bit.ly/CRA-PWA'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Is not localhost. Just register service worker
|
|
||||||
registerValidSW(swUrl, config);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function registerValidSW(swUrl, config) {
|
|
||||||
navigator.serviceWorker
|
|
||||||
.register(swUrl)
|
|
||||||
.then(registration => {
|
|
||||||
registration.onupdatefound = () => {
|
|
||||||
const installingWorker = registration.installing;
|
|
||||||
if (installingWorker == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
installingWorker.onstatechange = () => {
|
|
||||||
if (installingWorker.state === 'installed') {
|
|
||||||
if (navigator.serviceWorker.controller) {
|
|
||||||
// At this point, the updated precached content has been fetched,
|
|
||||||
// but the previous service worker will still serve the older
|
|
||||||
// content until all client tabs are closed.
|
|
||||||
console.log(
|
|
||||||
'New content is available and will be used when all ' +
|
|
||||||
'tabs for this page are closed. See https://bit.ly/CRA-PWA.'
|
|
||||||
);
|
|
||||||
|
|
||||||
// Execute callback
|
|
||||||
if (config && config.onUpdate) {
|
|
||||||
config.onUpdate(registration);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// At this point, everything has been precached.
|
|
||||||
// It's the perfect time to display a
|
|
||||||
// "Content is cached for offline use." message.
|
|
||||||
console.log('Content is cached for offline use.');
|
|
||||||
|
|
||||||
// Execute callback
|
|
||||||
if (config && config.onSuccess) {
|
|
||||||
config.onSuccess(registration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Error during service worker registration:', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkValidServiceWorker(swUrl, config) {
|
|
||||||
// Check if the service worker can be found. If it can't reload the page.
|
|
||||||
fetch(swUrl, {
|
|
||||||
headers: { 'Service-Worker': 'script' },
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
// Ensure service worker exists, and that we really are getting a JS file.
|
|
||||||
const contentType = response.headers.get('content-type');
|
|
||||||
if (
|
|
||||||
response.status === 404 ||
|
|
||||||
(contentType != null && contentType.indexOf('javascript') === -1)
|
|
||||||
) {
|
|
||||||
// No service worker found. Probably a different app. Reload the page.
|
|
||||||
navigator.serviceWorker.ready.then(registration => {
|
|
||||||
registration.unregister().then(() => {
|
|
||||||
window.location.reload();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// Service worker found. Proceed as normal.
|
|
||||||
registerValidSW(swUrl, config);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
console.log(
|
|
||||||
'No internet connection found. App is running in offline mode.'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unregister() {
|
|
||||||
if ('serviceWorker' in navigator) {
|
|
||||||
navigator.serviceWorker.ready
|
|
||||||
.then(registration => {
|
|
||||||
registration.unregister();
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error(error.message);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,125 +1,115 @@
|
|||||||
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
// You may obtain a copy of the License at
|
// You may obtain a copy of the License at
|
||||||
//
|
//
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
//
|
//
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import {message} from "antd";
|
import { message } from 'antd';
|
||||||
import React from "react";
|
import { isMobile as isMobileDevice } from 'react-device-detect';
|
||||||
import {isMobile as isMobileDevice} from "react-device-detect";
|
|
||||||
|
export function parseJson(s: string) {
|
||||||
export let ServerUrl = '';
|
if (s === '') {
|
||||||
|
return null;
|
||||||
export function initServerUrl() {
|
} else {
|
||||||
const hostname = window.location.hostname;
|
return JSON.parse(s);
|
||||||
if (hostname === 'localhost') {
|
}
|
||||||
ServerUrl = `http://${hostname}:7000`;
|
}
|
||||||
}
|
|
||||||
}
|
export function myParseInt(i: string) {
|
||||||
|
const res = parseInt(i);
|
||||||
export function parseJson(s) {
|
return isNaN(res) ? 0 : res;
|
||||||
if (s === "") {
|
}
|
||||||
return null;
|
|
||||||
} else {
|
export function openLink(link: string) {
|
||||||
return JSON.parse(s);
|
const w = window.open('about:blank');
|
||||||
}
|
if (w) {
|
||||||
}
|
w.location.href = link;
|
||||||
|
}
|
||||||
export function myParseInt(i) {
|
}
|
||||||
const res = parseInt(i);
|
|
||||||
return isNaN(res) ? 0 : res;
|
export function goToLink(link: string) {
|
||||||
}
|
window.location.href = link;
|
||||||
|
}
|
||||||
export function openLink(link) {
|
|
||||||
// this.props.history.push(link);
|
export function showMessage(type: '' | 'success' | 'error', text: string) {
|
||||||
const w = window.open('about:blank');
|
if (type === '') {
|
||||||
w.location.href = link;
|
return;
|
||||||
}
|
} else if (type === 'success') {
|
||||||
|
message.success(text);
|
||||||
export function goToLink(link) {
|
} else if (type === 'error') {
|
||||||
window.location.href = link;
|
message.error(text);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
export function showMessage(type, text) {
|
|
||||||
if (type === "") {
|
export function deepCopy(obj: object) {
|
||||||
return;
|
return Object.assign({}, obj);
|
||||||
} else if (type === "success") {
|
}
|
||||||
message.success(text);
|
|
||||||
} else if (type === "error") {
|
export function addRow(array: Array<any>, row: any) {
|
||||||
message.error(text);
|
return [...array, row];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
export function prependRow(array: Array<any>, row: any) {
|
||||||
export function deepCopy(obj) {
|
return [row, ...array];
|
||||||
return Object.assign({}, obj);
|
}
|
||||||
}
|
|
||||||
|
export function deleteRow(array: Array<any>, i: number) {
|
||||||
export function addRow(array, row) {
|
// return array = array.slice(0, i).concat(array.slice(i + 1));
|
||||||
return [...array, row];
|
return [...array.slice(0, i), ...array.slice(i + 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function prependRow(array, row) {
|
export function swapRow(array: Array<any>, i: number, j: number) {
|
||||||
return [row, ...array];
|
return [...array.slice(0, i), array[j], ...array.slice(i + 1, j), array[i], ...array.slice(j + 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deleteRow(array, i) {
|
export function isMobile() {
|
||||||
// return array = array.slice(0, i).concat(array.slice(i + 1));
|
return isMobileDevice;
|
||||||
return [...array.slice(0, i), ...array.slice(i + 1)];
|
}
|
||||||
}
|
|
||||||
|
export function getFormattedDate(date: string | undefined) {
|
||||||
export function swapRow(array, i, j) {
|
if (date === undefined) {
|
||||||
return [...array.slice(0, i), array[j], ...array.slice(i + 1, j), array[i], ...array.slice(j + 1)];
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isMobile() {
|
date = date.replace('T', ' ');
|
||||||
// return getIsMobileView();
|
date = date.replace('+08:00', ' ');
|
||||||
return isMobileDevice;
|
return date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getFormattedDate(date) {
|
export function getFormattedDateShort(date: string) {
|
||||||
if (date === undefined) {
|
return date.slice(0, 10);
|
||||||
return null;
|
}
|
||||||
}
|
|
||||||
|
export function getShortName(s: string) {
|
||||||
date = date.replace('T', ' ');
|
return s.split('/').slice(-1)[0];
|
||||||
date = date.replace('+08:00', ' ');
|
}
|
||||||
return date;
|
|
||||||
}
|
function getRandomInt(s: string) {
|
||||||
|
let hash = 0;
|
||||||
export function getFormattedDateShort(date) {
|
if (s.length !== 0) {
|
||||||
return date.slice(0, 10);
|
for (let i = 0; i < s.length; i++) {
|
||||||
}
|
let char = s.charCodeAt(i);
|
||||||
|
hash = (hash << 5) - hash + char;
|
||||||
export function getShortName(s) {
|
hash = hash & hash;
|
||||||
return s.split('/').slice(-1)[0];
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRandomInt(s) {
|
return hash;
|
||||||
let hash = 0;
|
}
|
||||||
if (s.length !== 0) {
|
|
||||||
for (let i = 0; i < s.length; i ++) {
|
export function getAvatarColor(s: string) {
|
||||||
let char = s.charCodeAt(i);
|
const colorList = ['#f56a00', '#7265e6', '#ffbf00', '#00a2ae'];
|
||||||
hash = ((hash << 5) - hash) + char;
|
let random = getRandomInt(s);
|
||||||
hash = hash & hash;
|
if (random < 0) {
|
||||||
}
|
random = -random;
|
||||||
}
|
}
|
||||||
|
return colorList[random % 4];
|
||||||
return hash;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export function getAvatarColor(s) {
|
|
||||||
const colorList = ['#f56a00', '#7265e6', '#ffbf00', '#00a2ae'];
|
|
||||||
let random = getRandomInt(s);
|
|
||||||
if (random < 0) {
|
|
||||||
random = -random;
|
|
||||||
}
|
|
||||||
return colorList[random % 4];
|
|
||||||
}
|
|
19
web/src/setup-tests.ts
Normal file
19
web/src/setup-tests.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||||
|
// allows you to do things like:
|
||||||
|
// expect(element).toHaveTextContent(/react/i)
|
||||||
|
// learn more: https://github.com/testing-library/jest-dom
|
||||||
|
import '@testing-library/jest-dom';
|
@ -1,5 +0,0 @@
|
|||||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
|
||||||
// allows you to do things like:
|
|
||||||
// expect(element).toHaveTextContent(/react/i)
|
|
||||||
// learn more: https://github.com/testing-library/jest-dom
|
|
||||||
import '@testing-library/jest-dom/extend-expect';
|
|
184
web/src/user/user-edit-page.tsx
Normal file
184
web/src/user/user-edit-page.tsx
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Button, Card, Col, Input, Row } from 'antd';
|
||||||
|
import * as userBackend from '../backend/user-backend';
|
||||||
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
import { showMessage } from '../setting';
|
||||||
|
|
||||||
|
function UserEditPage() {
|
||||||
|
const [userName, setUserName] = useState(useParams().userName);
|
||||||
|
const [user, setUser] = useState<userBackend.User | undefined>(undefined);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
userBackend.getUser('admin', userName).then((res) => {
|
||||||
|
setUser(res.data);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
function submitUserEdit() {
|
||||||
|
if (user == undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
userBackend
|
||||||
|
.updateUser(user.owner, userName, user)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.data) {
|
||||||
|
showMessage('success', `Successfully saved`);
|
||||||
|
setUserName(user.name);
|
||||||
|
navigate(`/users/${user.name}`);
|
||||||
|
} else {
|
||||||
|
showMessage('error', `failed to save: server side failure`);
|
||||||
|
updateUserField('name', userName);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
showMessage('error', `failed to save: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateUserField(key: keyof userBackend.User, value: string) {
|
||||||
|
if (user) {
|
||||||
|
let newUser = {
|
||||||
|
...user,
|
||||||
|
};
|
||||||
|
newUser[key] = value;
|
||||||
|
setUser(newUser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderUser() {
|
||||||
|
if (!user) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
size="small"
|
||||||
|
title={
|
||||||
|
<div>
|
||||||
|
Edit User
|
||||||
|
<Button type="primary" onClick={submitUserEdit}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
style={{ marginLeft: '5px' }}
|
||||||
|
type="inner"
|
||||||
|
>
|
||||||
|
<Row style={{ marginTop: '10px' }}>
|
||||||
|
<Col style={{ marginTop: '5px' }} span={2}>
|
||||||
|
Name:
|
||||||
|
</Col>
|
||||||
|
<Col span={22}>
|
||||||
|
<Input
|
||||||
|
value={user.name}
|
||||||
|
onChange={(e) => {
|
||||||
|
console.warn(e.target.value);
|
||||||
|
updateUserField('name', e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{ marginTop: '20px' }}>
|
||||||
|
<Col style={{ marginTop: '5px' }} span={2}>
|
||||||
|
Password Type:
|
||||||
|
</Col>
|
||||||
|
<Col span={22}>
|
||||||
|
<Input
|
||||||
|
value={user.passwordType}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateUserField('passwordType', e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{ marginTop: '20px' }}>
|
||||||
|
<Col style={{ marginTop: '5px' }} span={2}>
|
||||||
|
Password:
|
||||||
|
</Col>
|
||||||
|
<Col span={22}>
|
||||||
|
<Input
|
||||||
|
value={user.password}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateUserField('password', e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{ marginTop: '20px' }}>
|
||||||
|
<Col style={{ marginTop: '5px' }} span={2}>
|
||||||
|
Display Name:
|
||||||
|
</Col>
|
||||||
|
<Col span={22}>
|
||||||
|
<Input
|
||||||
|
value={user.displayName}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateUserField('displayName', e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{ marginTop: '20px' }}>
|
||||||
|
<Col style={{ marginTop: '5px' }} span={2}>
|
||||||
|
Email:
|
||||||
|
</Col>
|
||||||
|
<Col span={22}>
|
||||||
|
<Input
|
||||||
|
value={user.email}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateUserField('email', e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{ marginTop: '20px' }}>
|
||||||
|
<Col style={{ marginTop: '5px' }} span={2}>
|
||||||
|
Phone:
|
||||||
|
</Col>
|
||||||
|
<Col span={22}>
|
||||||
|
<Input
|
||||||
|
value={user.phone}
|
||||||
|
onChange={(e) => {
|
||||||
|
updateUserField('phone', e.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Row style={{ width: '100%' }}>
|
||||||
|
<Col span={1} />
|
||||||
|
<Col span={22}>{renderUser()}</Col>
|
||||||
|
<Col span={1} />
|
||||||
|
</Row>
|
||||||
|
<Row style={{ margin: 10 }}>
|
||||||
|
<Col span={2} />
|
||||||
|
<Col span={18}>
|
||||||
|
<Button type="primary" size="large" onClick={submitUserEdit}>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserEditPage;
|
188
web/src/user/user-list-page.tsx
Normal file
188
web/src/user/user-list-page.tsx
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { Button, Popconfirm, Table } from 'antd';
|
||||||
|
import { getFormattedDate, showMessage } from '../setting';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import moment from 'moment';
|
||||||
|
import * as userBackend from '../backend/user-backend';
|
||||||
|
import tw from 'twin.macro';
|
||||||
|
|
||||||
|
function UserTable() {
|
||||||
|
const [users, setUsers] = useState<Array<any>>([]);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
userBackend.getUsers('admin').then((res) => {
|
||||||
|
setUsers(res.data);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
function newUser() {
|
||||||
|
return {
|
||||||
|
owner: 'admin', // this.props.account.username,
|
||||||
|
name: `user_${users.length}`,
|
||||||
|
createdTime: moment().format(),
|
||||||
|
password: '123456',
|
||||||
|
passwordType: 'plain',
|
||||||
|
displayName: `New User - ${users.length}`,
|
||||||
|
email: 'user@example.com',
|
||||||
|
phone: '1-12345678',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function addUser() {
|
||||||
|
const value = newUser();
|
||||||
|
userBackend
|
||||||
|
.addUser(value)
|
||||||
|
.then((res) => {
|
||||||
|
showMessage('success', `User added successfully`);
|
||||||
|
setUsers([...users, value]);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
showMessage('error', `User failed to add: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteUser(i: number) {
|
||||||
|
userBackend
|
||||||
|
.deleteUser(users[i])
|
||||||
|
.then((res) => {
|
||||||
|
showMessage('success', `User deleted successfully`);
|
||||||
|
setUsers(users.filter((n) => n != users[i]));
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
showMessage('error', `User failed to delete: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: 'Name',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
width: '120px',
|
||||||
|
sorter: (a: any, b: any) => a.name.localeCompare(b.name),
|
||||||
|
render: (text: string) => {
|
||||||
|
return <a href={`/users/${text}`}>{text}</a>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Created Time',
|
||||||
|
dataIndex: 'createdTime',
|
||||||
|
key: 'createdTime',
|
||||||
|
width: '160px',
|
||||||
|
sorter: (a: any, b: any) => a.createdTime.localeCompare(b.createdTime),
|
||||||
|
render: (text: string) => {
|
||||||
|
return getFormattedDate(text);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'PasswordType',
|
||||||
|
dataIndex: 'passwordType',
|
||||||
|
key: 'passwordType',
|
||||||
|
width: '150px',
|
||||||
|
sorter: (a: any, b: any) => a.passwordType.localeCompare(b.passwordType),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Password',
|
||||||
|
dataIndex: 'password',
|
||||||
|
key: 'password',
|
||||||
|
width: '150px',
|
||||||
|
sorter: (a: any, b: any) => a.password.localeCompare(b.password),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Display Name',
|
||||||
|
dataIndex: 'displayName',
|
||||||
|
key: 'displayName',
|
||||||
|
// width: '100px',
|
||||||
|
sorter: (a: any, b: any) => a.displayName.localeCompare(b.displayName),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Email',
|
||||||
|
dataIndex: 'email',
|
||||||
|
key: 'email',
|
||||||
|
width: '150px',
|
||||||
|
sorter: (a: any, b: any) => a.email.localeCompare(b.email),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Phone',
|
||||||
|
dataIndex: 'phone',
|
||||||
|
key: 'phone',
|
||||||
|
width: '120px',
|
||||||
|
sorter: (a: any, b: any) => a.phone.localeCompare(b.phone),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Action',
|
||||||
|
dataIndex: '',
|
||||||
|
key: 'op',
|
||||||
|
width: '170px',
|
||||||
|
render: (text: string, record: any, index: number) => {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
marginTop: '10px',
|
||||||
|
marginBottom: '10px',
|
||||||
|
marginRight: '10px',
|
||||||
|
}}
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
navigate(`/users/${record.name}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
<Popconfirm title={`Sure to delete user: ${record.name} ?`} onConfirm={() => deleteUser(index)}>
|
||||||
|
<Button style={{ marginBottom: '10px' }} type="dashed">
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div css={tw`flex items-center justify-between`}>
|
||||||
|
<h1 css={tw`m-0 text-2xl`}>Users</h1>
|
||||||
|
<Button type="primary" size="middle" onClick={addUser}>
|
||||||
|
Add
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div css={tw`py-4`}>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
dataSource={users}
|
||||||
|
rowKey="name"
|
||||||
|
size="middle"
|
||||||
|
pagination={{ pageSize: 100 }}
|
||||||
|
loading={users === null || users === undefined}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function UserListPage() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<UserTable />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UserListPage;
|
27
web/src/user/user-routes.tsx
Normal file
27
web/src/user/user-routes.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { Route, Routes } from 'react-router-dom';
|
||||||
|
import UserListPage from './user-list-page';
|
||||||
|
import UserEditPage from './user-edit-page';
|
||||||
|
|
||||||
|
export function UserRoutes() {
|
||||||
|
return (
|
||||||
|
<Routes>
|
||||||
|
<Route path="/" element={<UserListPage />} />
|
||||||
|
<Route path=":userName" element={<UserEditPage />} />
|
||||||
|
</Routes>
|
||||||
|
);
|
||||||
|
}
|
17
web/tailwind.config.js
Normal file
17
web/tailwind.config.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
plugins: [],
|
||||||
|
};
|
20
web/tsconfig.json
Normal file
20
web/tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"module": "esnext",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "react"
|
||||||
|
},
|
||||||
|
"include": ["src","types"]
|
||||||
|
}
|
59
web/types/static.d.ts
vendored
Normal file
59
web/types/static.d.ts
vendored
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
/* Use this file to declare any custom file extensions for importing */
|
||||||
|
/* Use this folder to also add/extend a package d.ts file, if needed. */
|
||||||
|
|
||||||
|
/* CSS MODULES */
|
||||||
|
declare module '*.module.css' {
|
||||||
|
const classes: { [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.module.scss' {
|
||||||
|
const classes: { [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.module.sass' {
|
||||||
|
const classes: { [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.module.less' {
|
||||||
|
const classes: { [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
declare module '*.module.styl' {
|
||||||
|
const classes: { [key: string]: string };
|
||||||
|
export default classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CSS */
|
||||||
|
declare module '*.css';
|
||||||
|
declare module '*.scss';
|
||||||
|
declare module '*.sass';
|
||||||
|
declare module '*.less';
|
||||||
|
declare module '*.styl';
|
||||||
|
|
||||||
|
/* IMAGES */
|
||||||
|
declare module '*.svg' {
|
||||||
|
const ref: string;
|
||||||
|
export default ref;
|
||||||
|
}
|
||||||
|
declare module '*.bmp' {
|
||||||
|
const ref: string;
|
||||||
|
export default ref;
|
||||||
|
}
|
||||||
|
declare module '*.gif' {
|
||||||
|
const ref: string;
|
||||||
|
export default ref;
|
||||||
|
}
|
||||||
|
declare module '*.jpg' {
|
||||||
|
const ref: string;
|
||||||
|
export default ref;
|
||||||
|
}
|
||||||
|
declare module '*.jpeg' {
|
||||||
|
const ref: string;
|
||||||
|
export default ref;
|
||||||
|
}
|
||||||
|
declare module '*.png' {
|
||||||
|
const ref: string;
|
||||||
|
export default ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CUSTOM: ADD YOUR OWN HERE */
|
36
web/types/twin.d.ts
vendored
Normal file
36
web/types/twin.d.ts
vendored
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// Copyright 2020 The casbin Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import 'twin.macro';
|
||||||
|
import styledImport from '@emotion/styled';
|
||||||
|
import { css as cssImport } from '@emotion/core';
|
||||||
|
|
||||||
|
import 'tailwindcss/dist/base.min.css';
|
||||||
|
|
||||||
|
declare module 'twin.macro' {
|
||||||
|
// The styled and css imports
|
||||||
|
const styled: typeof styledImport;
|
||||||
|
const css: typeof cssImport;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'react' {
|
||||||
|
// The css prop
|
||||||
|
interface HTMLAttributes<T> extends DOMAttributes<T> {
|
||||||
|
css?: CSSProp;
|
||||||
|
}
|
||||||
|
// The inline svg css prop
|
||||||
|
interface SVGProps<T> extends SVGProps<SVGSVGElement> {
|
||||||
|
css?: CSSProp;
|
||||||
|
}
|
||||||
|
}
|
6502
web/yarn.lock
6502
web/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user