mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-30 08:10:29 +08:00
Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a8a8f39963 | ||
![]() |
90f8eba02d | ||
![]() |
2cca1c9136 | ||
![]() |
c2eebd61a1 | ||
![]() |
59566f61d7 | ||
![]() |
7e4c9c91cd | ||
![]() |
430ee616db | ||
![]() |
2e3a323528 | ||
![]() |
09e8408a3d | ||
![]() |
2998bbf4b9 | ||
![]() |
404382f2e0 | ||
![]() |
71db1f62a9 | ||
![]() |
07dc6bf7cd | ||
![]() |
2de3f6772d | ||
![]() |
3f623570fd |
@@ -451,7 +451,7 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
properties := map[string]string{}
|
||||
properties["no"] = strconv.Itoa(len(object.GetUsers(application.Organization)) + 2)
|
||||
properties["no"] = strconv.Itoa(object.GetUserCount(application.Organization, "", "") + 2)
|
||||
initScore, err := getInitScore(organization)
|
||||
if err != nil {
|
||||
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
|
||||
|
@@ -21,14 +21,6 @@ import (
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
type LdapServer struct {
|
||||
Host string `json:"host"`
|
||||
Port int `json:"port"`
|
||||
Admin string `json:"admin"`
|
||||
Passwd string `json:"passwd"`
|
||||
BaseDn string `json:"baseDn"`
|
||||
}
|
||||
|
||||
type LdapResp struct {
|
||||
// Groups []LdapRespGroup `json:"groups"`
|
||||
Users []object.LdapRespUser `json:"users"`
|
||||
@@ -44,19 +36,15 @@ type LdapSyncResp struct {
|
||||
Failed []object.LdapRespUser `json:"failed"`
|
||||
}
|
||||
|
||||
// GetLdapUser
|
||||
// GetLdapUsers
|
||||
// @Tag Account API
|
||||
// @Title GetLdapser
|
||||
// @router /get-ldap-user [post]
|
||||
func (c *ApiController) GetLdapUser() {
|
||||
ldapServer := LdapServer{}
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldapServer)
|
||||
if err != nil || util.IsStringsEmpty(ldapServer.Host, ldapServer.Admin, ldapServer.Passwd, ldapServer.BaseDn) {
|
||||
c.ResponseError(c.T("general:Missing parameter"))
|
||||
return
|
||||
}
|
||||
// @router /get-ldap-users [get]
|
||||
func (c *ApiController) GetLdapUsers() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var resp LdapResp
|
||||
_, ldapId := util.GetOwnerAndNameFromId(id)
|
||||
ldapServer := object.GetLdap(ldapId)
|
||||
|
||||
conn, err := object.GetLdapConn(ldapServer.Host, ldapServer.Port, ldapServer.Admin, ldapServer.Passwd)
|
||||
if err != nil {
|
||||
@@ -83,6 +71,8 @@ func (c *ApiController) GetLdapUser() {
|
||||
return
|
||||
}
|
||||
|
||||
var resp LdapResp
|
||||
uuids := make([]string, len(users))
|
||||
for _, user := range users {
|
||||
resp.Users = append(resp.Users, object.LdapRespUser{
|
||||
UidNumber: user.UidNumber,
|
||||
@@ -95,9 +85,12 @@ func (c *ApiController) GetLdapUser() {
|
||||
Phone: util.GetMaxLenStr(user.TelephoneNumber, user.Mobile, user.MobileTelephoneNumber),
|
||||
Address: util.GetMaxLenStr(user.RegisteredAddress, user.PostalAddress),
|
||||
})
|
||||
uuids = append(uuids, user.Uuid)
|
||||
}
|
||||
|
||||
c.ResponseOk(resp)
|
||||
existUuids := object.CheckLdapUuidExist(ldapServer.Owner, uuids)
|
||||
|
||||
c.ResponseOk(resp, existUuids)
|
||||
}
|
||||
|
||||
// GetLdaps
|
||||
@@ -134,7 +127,7 @@ func (c *ApiController) AddLdap() {
|
||||
var ldap object.Ldap
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
|
||||
if err != nil {
|
||||
c.ResponseError(c.T("general:Missing parameter"))
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -150,14 +143,14 @@ func (c *ApiController) AddLdap() {
|
||||
|
||||
affected := object.AddLdap(&ldap)
|
||||
resp := wrapActionResponse(affected)
|
||||
if affected {
|
||||
resp.Data2 = ldap
|
||||
}
|
||||
resp.Data2 = ldap
|
||||
|
||||
if ldap.AutoSync != 0 {
|
||||
object.GetLdapAutoSynchronizer().StartAutoSync(ldap.Id)
|
||||
}
|
||||
|
||||
c.ResponseOk(resp)
|
||||
c.Data["json"] = resp
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateLdap
|
||||
@@ -174,17 +167,15 @@ func (c *ApiController) UpdateLdap() {
|
||||
|
||||
prevLdap := object.GetLdap(ldap.Id)
|
||||
affected := object.UpdateLdap(&ldap)
|
||||
resp := wrapActionResponse(affected)
|
||||
if affected {
|
||||
resp.Data2 = ldap
|
||||
}
|
||||
|
||||
if ldap.AutoSync != 0 {
|
||||
object.GetLdapAutoSynchronizer().StartAutoSync(ldap.Id)
|
||||
} else if ldap.AutoSync == 0 && prevLdap.AutoSync != 0 {
|
||||
object.GetLdapAutoSynchronizer().StopAutoSync(ldap.Id)
|
||||
}
|
||||
|
||||
c.ResponseOk(resp)
|
||||
c.Data["json"] = wrapActionResponse(affected)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteLdap
|
||||
@@ -199,8 +190,12 @@ func (c *ApiController) DeleteLdap() {
|
||||
return
|
||||
}
|
||||
|
||||
affected := object.DeleteLdap(&ldap)
|
||||
|
||||
object.GetLdapAutoSynchronizer().StopAutoSync(ldap.Id)
|
||||
c.ResponseOk(wrapActionResponse(object.DeleteLdap(&ldap)))
|
||||
|
||||
c.Data["json"] = wrapActionResponse(affected)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// SyncLdapUsers
|
||||
@@ -226,20 +221,3 @@ func (c *ApiController) SyncLdapUsers() {
|
||||
Failed: *failed,
|
||||
})
|
||||
}
|
||||
|
||||
// CheckLdapUsersExist
|
||||
// @Tag Account API
|
||||
// @Title CheckLdapUserExist
|
||||
// @router /check-ldap-users-exist [post]
|
||||
func (c *ApiController) CheckLdapUsersExist() {
|
||||
owner := c.Input().Get("owner")
|
||||
var uuids []string
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &uuids)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
exist := object.CheckLdapUuidExist(owner, uuids)
|
||||
c.ResponseOk(exist)
|
||||
}
|
||||
|
@@ -15,16 +15,9 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
type SystemInfo struct {
|
||||
MemoryUsed uint64 `json:"memory_used"`
|
||||
MemoryTotal uint64 `json:"memory_total"`
|
||||
CpuUsage []float64 `json:"cpu_usage"`
|
||||
}
|
||||
|
||||
// GetSystemInfo
|
||||
// @Title GetSystemInfo
|
||||
// @Tag System API
|
||||
@@ -33,50 +26,32 @@ type SystemInfo struct {
|
||||
// @Success 200 {object} object.SystemInfo The Response object
|
||||
// @router /get-system-info [get]
|
||||
func (c *ApiController) GetSystemInfo() {
|
||||
id := c.GetString("id")
|
||||
if id == "" {
|
||||
id = c.GetSessionUsername()
|
||||
}
|
||||
|
||||
user := object.GetUser(id)
|
||||
if user == nil || !user.IsGlobalAdmin {
|
||||
c.ResponseError(c.T("auth:Unauthorized operation"))
|
||||
_, ok := c.RequireAdmin()
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
cpuUsage, err := util.GetCpuUsage()
|
||||
systemInfo, err := util.GetSystemInfo()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
memoryUsed, memoryTotal, err := util.GetMemoryUsage()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = SystemInfo{
|
||||
CpuUsage: cpuUsage,
|
||||
MemoryUsed: memoryUsed,
|
||||
MemoryTotal: memoryTotal,
|
||||
}
|
||||
c.ServeJSON()
|
||||
c.ResponseOk(systemInfo)
|
||||
}
|
||||
|
||||
// GitRepoVersion
|
||||
// @Title GitRepoVersion
|
||||
// GetVersionInfo
|
||||
// @Title GetVersionInfo
|
||||
// @Tag System API
|
||||
// @Description get local github repo's latest release version info
|
||||
// @Success 200 {string} local latest version hash of casdoor
|
||||
// @router /get-release [get]
|
||||
func (c *ApiController) GitRepoVersion() {
|
||||
version, err := util.GetGitRepoVersion()
|
||||
// @Description get local git repo's latest release version info
|
||||
// @Success 200 {string} local latest version hash of Casdoor
|
||||
// @router /get-version-info [get]
|
||||
func (c *ApiController) GetVersionInfo() {
|
||||
versionInfo, err := util.GetVersionInfo()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = version
|
||||
c.ServeJSON()
|
||||
c.ResponseOk(versionInfo)
|
||||
}
|
||||
|
2
go.mod
2
go.mod
@@ -18,6 +18,7 @@ require (
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
github.com/denisenkom/go-mssqldb v0.9.0
|
||||
github.com/forestmgy/ldapserver v1.1.0
|
||||
github.com/go-git/go-git/v5 v5.6.0
|
||||
github.com/go-ldap/ldap/v3 v3.3.0
|
||||
github.com/go-mysql-org/go-mysql v1.7.0
|
||||
github.com/go-pay/gopay v1.5.72
|
||||
@@ -25,7 +26,6 @@ require (
|
||||
github.com/go-webauthn/webauthn v0.8.2
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-cmp v0.5.8 // indirect
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||
github.com/lestrrat-go/jwx v1.2.21
|
||||
|
81
go.sum
81
go.sum
@@ -60,9 +60,15 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/Masterminds/squirrel v1.5.3 h1:YPpoceAcxuzIljlr5iWpNKaql7hLeG1KLSrhvdHpkZc=
|
||||
github.com/Masterminds/squirrel v1.5.3/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
|
||||
github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I=
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
|
||||
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b h1:EgJ6N2S0h1WfFIjU5/VVHWbMSVYXAluop97Qxpr/lfQ=
|
||||
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b/go.mod h1:3SAoF0F5EbcOuBD5WT9nYkbIJieBS84cUQXADbXeBsU=
|
||||
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
|
||||
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
|
||||
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=
|
||||
@@ -76,7 +82,11 @@ github.com/aliyun/alibaba-cloud-sdk-go v1.62.188 h1:8lIVcOlHW+fKCGMEf6nuGTTEFOt/
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.188/go.mod h1:Api2AkmMgGaSUAhmk76oaFObkoeCPc/bKAqcyplPODs=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible h1:9gWa46nstkJ9miBReJcN8Gq34cBFbzSpQZVVT9N09TM=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
|
||||
github.com/aws/aws-sdk-go v1.44.4 h1:ePN0CVJMdiz2vYUcJH96eyxRrtKGSDMgyhP6rah2OgE=
|
||||
github.com/aws/aws-sdk-go v1.44.4/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
|
||||
@@ -95,6 +105,7 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
|
||||
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/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/casbin/casbin v1.7.0 h1:PuzlE8w0JBg/DhIqnkF1Dewf3z+qmUZMVN07PonvVUQ=
|
||||
github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
|
||||
github.com/casbin/casbin/v2 v2.1.0/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
@@ -118,6 +129,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.1.0 h1:bZgT/A+cikZnKIwn7xL2OBj012Bmvho/o6RpRvv3GKY=
|
||||
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
|
||||
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
@@ -154,6 +167,8 @@ github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt
|
||||
github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
@@ -167,8 +182,19 @@ github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD
|
||||
github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
|
||||
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
||||
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
|
||||
github.com/go-git/gcfg v1.5.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
|
||||
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
|
||||
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
|
||||
github.com/go-git/go-billy/v5 v5.4.0 h1:Vaw7LaSTRJOUric7pe4vnzBSgyuf2KrLsu2Y4ZpQBDE=
|
||||
github.com/go-git/go-billy/v5 v5.4.0/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
|
||||
github.com/go-git/go-git/v5 v5.6.0 h1:JvBdYfcttd+0kdpuWO7KTu0FYgCf5W0t5VwkWGobaa4=
|
||||
github.com/go-git/go-git/v5 v5.6.0/go.mod h1:6nmJ0tJ3N4noMV1Omv7rC5FG3/o8Cm51TB4CJp7mRmE=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -258,8 +284,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-tpm v0.1.2-0.20190725015402-ae6dd98980d4/go.mod h1:H9HbmUG2YgV/PHITkO7p6wxEEj/v5nlsVWIwumwH2NI=
|
||||
github.com/google/go-tpm v0.3.0/go.mod h1:iVLWvrPp/bHeEkxTFi9WG6K9w0iy2yIszHwZGHPbzAw=
|
||||
github.com/google/go-tpm v0.3.3 h1:P/ZFNBZYXRxc+z7i5uyd8VP7MaDteuLZInzrH2idRGo=
|
||||
@@ -305,9 +331,14 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
|
||||
github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da h1:FjHUJJ7oBW4G/9j1KzlHaXL09LyMVM9rupS39lncbXk=
|
||||
github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko=
|
||||
github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
@@ -333,6 +364,8 @@ github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uia
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
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=
|
||||
@@ -376,6 +409,8 @@ github.com/markbates/going v1.0.0 h1:DQw0ZP7NbNlFGcKbcE/IVSOAFzScxRtLpd0rLMzLhq0
|
||||
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
|
||||
github.com/markbates/goth v1.75.2 h1:C7KloBMMk50JyXaHhzfqWYLW6+bDcSVIvUGHXneLWro=
|
||||
github.com/markbates/goth v1.75.2/go.mod h1:X6xdNgpapSENS0O35iTBBcMHoJDQDfI9bJl+APCkYMc=
|
||||
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
|
||||
github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
|
||||
github.com/mattermost/xml-roundtrip-validator v0.0.0-20201208211235-fe770d50d911 h1:erppMjjp69Rertg1zlgRbLJH1u+eCmRPxKjMZ5I8/Ro=
|
||||
github.com/mattermost/xml-roundtrip-validator v0.0.0-20201208211235-fe770d50d911/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
|
||||
github.com/mattn/go-ieproxy v0.0.1 h1:qiyop7gCflfhwCzGyeT0gro3sF9AIg9HU98JORTkqfI=
|
||||
@@ -392,6 +427,7 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
|
||||
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=
|
||||
@@ -428,6 +464,8 @@ github.com/pingcap/log v0.0.0-20210625125904-98ed8e2eb1c7 h1:k2BbABz9+TNpYRwsCCF
|
||||
github.com/pingcap/log v0.0.0-20210625125904-98ed8e2eb1c7/go.mod h1:8AanEdAHATuRurdGxZXBz0At+9avep+ub7U1AGYLIMM=
|
||||
github.com/pingcap/tidb/parser v0.0.0-20221126021158-6b02a5d8ba7d h1:1DyyRrgYeNjqPkgjrdEsaIbX+kHpuTTk5ZOCtrcRFcQ=
|
||||
github.com/pingcap/tidb/parser v0.0.0-20221126021158-6b02a5d8ba7d/go.mod h1:ElJiub4lRy6UZDb+0JHDkGEdr6aOli+ykhyej7VCLoI=
|
||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -479,6 +517,8 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
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/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
@@ -496,6 +536,9 @@ github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKz
|
||||
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.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/skeema/knownhosts v1.1.0 h1:Wvr9V0MxhjRbl3f9nMnKnFfiWTJmtECJ9Njkea3ysW0=
|
||||
github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
@@ -553,6 +596,8 @@ github.com/volcengine/volc-sdk-golang v1.0.19/go.mod h1:+GGi447k4p1I5PNdbpG2GLaF
|
||||
github.com/wendal/errors v0.0.0-20181209125328-7f31f4b264ec/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/xorm-io/builder v0.3.13 h1:J4oZxt4Gjgm/Si9iKazfzYwHB/ijEOD9EHInyjOSX+M=
|
||||
@@ -592,6 +637,7 @@ go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.18.1 h1:CSUJ2mjFszzEWt4CdKISEuChVIXGBn3lAPwkRGyVrc4=
|
||||
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
|
||||
golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@@ -609,6 +655,11 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220208233918-bba287dce954/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -644,8 +695,9 @@ golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -686,6 +738,9 @@ golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -751,22 +806,34 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210629170331-7dc0b73dc9fb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211020174200-9d6173849985/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -775,6 +842,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -831,8 +899,9 @@ golang.org/x/tools v0.0.0-20200929161345-d7fc70abf50f/go.mod h1:z6u4i615ZeAfBE4X
|
||||
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201125231158-b5590deeca9b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -943,6 +1012,8 @@ gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI
|
||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
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=
|
||||
@@ -953,6 +1024,7 @@ gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
@@ -1002,5 +1074,6 @@ modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfp
|
||||
modernc.org/z v1.0.1 h1:WyIDpEpAIx4Hel6q/Pcgj/VhaQV5XPJ2I6ryIYbjnpc=
|
||||
modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||
|
@@ -15,11 +15,9 @@
|
||||
package idp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -170,10 +168,18 @@ func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
|
||||
Email: dtUserInfo.Email,
|
||||
AvatarUrl: dtUserInfo.AvatarUrl,
|
||||
}
|
||||
isUserInOrg, err := idp.isUserInOrg(userInfo.UnionId)
|
||||
if !isUserInOrg {
|
||||
|
||||
corpAccessToken := idp.getInnerAppAccessToken()
|
||||
userId, err := idp.getUserId(userInfo.UnionId, corpAccessToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
corpEmail, err := idp.getUserCorpEmail(userId, corpAccessToken)
|
||||
if err == nil && corpEmail != "" {
|
||||
userInfo.Email = corpEmail
|
||||
}
|
||||
|
||||
return &userInfo, nil
|
||||
}
|
||||
|
||||
@@ -202,23 +208,14 @@ func (idp *DingTalkIdProvider) postWithBody(body interface{}, url string) ([]byt
|
||||
}
|
||||
|
||||
func (idp *DingTalkIdProvider) getInnerAppAccessToken() string {
|
||||
appKey := idp.Config.ClientID
|
||||
appSecret := idp.Config.ClientSecret
|
||||
body := make(map[string]string)
|
||||
body["appKey"] = appKey
|
||||
body["appSecret"] = appSecret
|
||||
bodyData, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
reader := bytes.NewReader(bodyData)
|
||||
request, err := http.NewRequest("POST", "https://api.dingtalk.com/v1.0/oauth2/accessToken", reader)
|
||||
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
|
||||
resp, err := idp.Client.Do(request)
|
||||
respBytes, err := ioutil.ReadAll(resp.Body)
|
||||
body["appKey"] = idp.Config.ClientID
|
||||
body["appSecret"] = idp.Config.ClientSecret
|
||||
respBytes, err := idp.postWithBody(body, "https://api.dingtalk.com/v1.0/oauth2/accessToken")
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
|
||||
var data struct {
|
||||
ExpireIn int `json:"expireIn"`
|
||||
AccessToken string `json:"accessToken"`
|
||||
@@ -230,34 +227,53 @@ func (idp *DingTalkIdProvider) getInnerAppAccessToken() string {
|
||||
return data.AccessToken
|
||||
}
|
||||
|
||||
func (idp *DingTalkIdProvider) isUserInOrg(unionId string) (bool, error) {
|
||||
func (idp *DingTalkIdProvider) getUserId(unionId string, accessToken string) (string, error) {
|
||||
body := make(map[string]string)
|
||||
body["unionid"] = unionId
|
||||
bodyData, err := json.Marshal(body)
|
||||
respBytes, err := idp.postWithBody(body, "https://oapi.dingtalk.com/topapi/user/getbyunionid?access_token="+accessToken)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
reader := bytes.NewReader(bodyData)
|
||||
accessToken := idp.getInnerAppAccessToken()
|
||||
request, _ := http.NewRequest("POST", "https://oapi.dingtalk.com/topapi/user/getbyunionid?access_token="+accessToken, reader)
|
||||
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
|
||||
resp, err := idp.Client.Do(request)
|
||||
respBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
||||
var data struct {
|
||||
ErrCode int `json:"errcode"`
|
||||
ErrMessage string `json:"errmsg"`
|
||||
Result struct {
|
||||
UserId string `json:"userid"`
|
||||
} `json:"result"`
|
||||
}
|
||||
err = json.Unmarshal(respBytes, &data)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
return "", err
|
||||
}
|
||||
if data.ErrCode == 60121 {
|
||||
return false, fmt.Errorf("the user is not found in the organization where clientId and clientSecret belong")
|
||||
return "", fmt.Errorf("the user is not found in the organization where clientId and clientSecret belong")
|
||||
} else if data.ErrCode != 0 {
|
||||
return false, fmt.Errorf(data.ErrMessage)
|
||||
return "", fmt.Errorf(data.ErrMessage)
|
||||
}
|
||||
return true, nil
|
||||
return data.Result.UserId, nil
|
||||
}
|
||||
|
||||
func (idp *DingTalkIdProvider) getUserCorpEmail(userId string, accessToken string) (string, error) {
|
||||
body := make(map[string]string)
|
||||
body["userid"] = userId
|
||||
respBytes, err := idp.postWithBody(body, "https://oapi.dingtalk.com/topapi/v2/user/get?access_token="+accessToken)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var data struct {
|
||||
ErrMessage string `json:"errmsg"`
|
||||
Result struct {
|
||||
Email string `json:"email"`
|
||||
} `json:"result"`
|
||||
}
|
||||
err = json.Unmarshal(respBytes, &data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if data.ErrMessage != "ok" {
|
||||
return "", fmt.Errorf(data.ErrMessage)
|
||||
}
|
||||
return data.Result.Email, nil
|
||||
}
|
||||
|
@@ -160,12 +160,12 @@ func GetLdapConn(host string, port int, adminUser string, adminPasswd string) (*
|
||||
|
||||
err = conn.Bind(adminUser, adminPasswd)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to login Ldap server with [%s]", adminUser)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
isAD, err := isMicrosoftAD(conn)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to get Ldap server type [%s]", adminUser)
|
||||
return nil, err
|
||||
}
|
||||
return &ldapConn{Conn: conn, IsAD: isAD}, nil
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ func SendSms(provider *Provider, content string, phoneNumbers ...string) error {
|
||||
|
||||
if provider.Type == sender.Aliyun {
|
||||
for i, number := range phoneNumbers {
|
||||
phoneNumbers[i] = strings.TrimPrefix(number, "+")
|
||||
phoneNumbers[i] = strings.TrimPrefix(number, "+86")
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -118,13 +118,12 @@ func initAPI() {
|
||||
beego.Router("/api/reset-email-or-phone", &controllers.ApiController{}, "POST:ResetEmailOrPhone")
|
||||
beego.Router("/api/get-captcha", &controllers.ApiController{}, "GET:GetCaptcha")
|
||||
|
||||
beego.Router("/api/get-ldap-user", &controllers.ApiController{}, "POST:GetLdapUser")
|
||||
beego.Router("/api/get-ldap-users", &controllers.ApiController{}, "GET:GetLdapUsers")
|
||||
beego.Router("/api/get-ldaps", &controllers.ApiController{}, "GET:GetLdaps")
|
||||
beego.Router("/api/get-ldap", &controllers.ApiController{}, "GET:GetLdap")
|
||||
beego.Router("/api/add-ldap", &controllers.ApiController{}, "POST:AddLdap")
|
||||
beego.Router("/api/update-ldap", &controllers.ApiController{}, "POST:UpdateLdap")
|
||||
beego.Router("/api/delete-ldap", &controllers.ApiController{}, "POST:DeleteLdap")
|
||||
beego.Router("/api/check-ldap-users-exist", &controllers.ApiController{}, "POST:CheckLdapUsersExist")
|
||||
beego.Router("/api/sync-ldap-users", &controllers.ApiController{}, "POST:SyncLdapUsers")
|
||||
|
||||
beego.Router("/api/get-providers", &controllers.ApiController{}, "GET:GetProviders")
|
||||
@@ -225,5 +224,5 @@ func initAPI() {
|
||||
beego.Router("/api/webauthn/signin/finish", &controllers.ApiController{}, "Post:WebAuthnSigninFinish")
|
||||
|
||||
beego.Router("/api/get-system-info", &controllers.ApiController{}, "GET:GetSystemInfo")
|
||||
beego.Router("/api/get-release", &controllers.ApiController{}, "GET:GitRepoVersion")
|
||||
beego.Router("/api/get-version-info", &controllers.ApiController{}, "GET:GetVersionInfo")
|
||||
}
|
||||
|
@@ -636,14 +636,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/check-ldap-users-exist": {
|
||||
"post": {
|
||||
"tags": [
|
||||
"Account API"
|
||||
],
|
||||
"operationId": "ApiController.CheckLdapUserExist"
|
||||
}
|
||||
},
|
||||
"/api/check-user-password": {
|
||||
"post": {
|
||||
"tags": [
|
||||
@@ -1349,8 +1341,8 @@
|
||||
"operationId": "ApiController.GetLdap"
|
||||
}
|
||||
},
|
||||
"/api/get-ldap-user": {
|
||||
"post": {
|
||||
"/api/get-ldap-users": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Account API"
|
||||
],
|
||||
@@ -1835,20 +1827,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/get-release": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"System API"
|
||||
],
|
||||
"description": "get local github repo's latest release version info",
|
||||
"operationId": "ApiController.GitRepoVersion",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{string} local latest version hash of casdoor"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/get-resource": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -2340,6 +2318,20 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/get-version-info": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"System API"
|
||||
],
|
||||
"description": "get local git repo's latest release version info",
|
||||
"operationId": "ApiController.GetVersionInfo",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "{string} local latest version hash of Casdoor"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/api/get-webhook": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -3635,11 +3627,11 @@
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"2346.0xc000278ab0.false": {
|
||||
"2268.0xc000528cf0.false": {
|
||||
"title": "false",
|
||||
"type": "object"
|
||||
},
|
||||
"2381.0xc000278ae0.false": {
|
||||
"2302.0xc000528d20.false": {
|
||||
"title": "false",
|
||||
"type": "object"
|
||||
},
|
||||
@@ -3766,10 +3758,10 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"data": {
|
||||
"$ref": "#/definitions/2346.0xc000278ab0.false"
|
||||
"$ref": "#/definitions/2268.0xc000528cf0.false"
|
||||
},
|
||||
"data2": {
|
||||
"$ref": "#/definitions/2381.0xc000278ae0.false"
|
||||
"$ref": "#/definitions/2302.0xc000528d20.false"
|
||||
},
|
||||
"msg": {
|
||||
"type": "string"
|
||||
|
@@ -412,11 +412,6 @@ paths:
|
||||
description: The Response object
|
||||
schema:
|
||||
$ref: '#/definitions/controllers.Response'
|
||||
/api/check-ldap-users-exist:
|
||||
post:
|
||||
tags:
|
||||
- Account API
|
||||
operationId: ApiController.CheckLdapUserExist
|
||||
/api/check-user-password:
|
||||
post:
|
||||
tags:
|
||||
@@ -875,8 +870,8 @@ paths:
|
||||
tags:
|
||||
- Account API
|
||||
operationId: ApiController.GetLdap
|
||||
/api/get-ldap-user:
|
||||
post:
|
||||
/api/get-ldap-users:
|
||||
get:
|
||||
tags:
|
||||
- Account API
|
||||
operationId: ApiController.GetLdapser
|
||||
@@ -1193,15 +1188,6 @@ paths:
|
||||
description: The Response object
|
||||
schema:
|
||||
$ref: '#/definitions/object.Record'
|
||||
/api/get-release:
|
||||
get:
|
||||
tags:
|
||||
- System API
|
||||
description: get local github repo's latest release version info
|
||||
operationId: ApiController.GitRepoVersion
|
||||
responses:
|
||||
"200":
|
||||
description: '{string} local latest version hash of casdoor'
|
||||
/api/get-resource:
|
||||
get:
|
||||
tags:
|
||||
@@ -1525,6 +1511,15 @@ paths:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/definitions/object.User'
|
||||
/api/get-version-info:
|
||||
get:
|
||||
tags:
|
||||
- System API
|
||||
description: get local git repo's latest release version info
|
||||
operationId: ApiController.GetVersionInfo
|
||||
responses:
|
||||
"200":
|
||||
description: '{string} local latest version hash of Casdoor'
|
||||
/api/get-webhook:
|
||||
get:
|
||||
tags:
|
||||
@@ -2379,10 +2374,10 @@ paths:
|
||||
schema:
|
||||
$ref: '#/definitions/Response'
|
||||
definitions:
|
||||
2346.0xc000278ab0.false:
|
||||
2268.0xc000528cf0.false:
|
||||
title: "false"
|
||||
type: object
|
||||
2381.0xc000278ae0.false:
|
||||
2302.0xc000528d20.false:
|
||||
title: "false"
|
||||
type: object
|
||||
Response:
|
||||
@@ -2469,9 +2464,9 @@ definitions:
|
||||
type: object
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/definitions/2346.0xc000278ab0.false'
|
||||
$ref: '#/definitions/2268.0xc000528cf0.false'
|
||||
data2:
|
||||
$ref: '#/definitions/2381.0xc000278ae0.false'
|
||||
$ref: '#/definitions/2302.0xc000528d20.false'
|
||||
msg:
|
||||
type: string
|
||||
name:
|
||||
|
17
sync/conf.go
17
sync/conf.go
@@ -1,17 +0,0 @@
|
||||
package sync
|
||||
|
||||
var (
|
||||
host1 = "127.0.0.1"
|
||||
port1 = 3306
|
||||
username1 = "root"
|
||||
password1 = "123456"
|
||||
database1 = "db"
|
||||
)
|
||||
|
||||
var (
|
||||
host2 = "127.0.0.1"
|
||||
port2 = 3306
|
||||
username2 = "root"
|
||||
password2 = "123456"
|
||||
database2 = "db"
|
||||
)
|
100
sync/database.go
Normal file
100
sync/database.go
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright 2023 The Casdoor 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 sync
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-mysql-org/go-mysql/canal"
|
||||
"github.com/xorm-io/xorm"
|
||||
)
|
||||
|
||||
type Database struct {
|
||||
host string
|
||||
port int
|
||||
database string
|
||||
username string
|
||||
password string
|
||||
|
||||
engine *xorm.Engine
|
||||
serverId uint32
|
||||
serverUuid string
|
||||
Gtid string
|
||||
canal.DummyEventHandler
|
||||
}
|
||||
|
||||
func newDatabase(host string, port int, database string, username string, password string) *Database {
|
||||
db := &Database{
|
||||
host: host,
|
||||
port: port,
|
||||
database: database,
|
||||
username: username,
|
||||
password: password,
|
||||
}
|
||||
|
||||
dataSourceName := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", username, password, host, port, database)
|
||||
engine, err := createEngine(dataSourceName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
db.engine = engine
|
||||
|
||||
db.serverId, err = getServerId(engine)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
db.serverUuid, err = getServerUuid(engine)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *Database) getCanalConfig() *canal.Config {
|
||||
// config canal
|
||||
cfg := canal.NewDefaultConfig()
|
||||
cfg.Addr = fmt.Sprintf("%s:%d", db.host, db.port)
|
||||
cfg.Password = db.password
|
||||
cfg.User = db.username
|
||||
// We only care table in database1
|
||||
cfg.Dump.TableDB = db.database
|
||||
return cfg
|
||||
}
|
||||
|
||||
func (db *Database) startCanal(targetDb *Database) error {
|
||||
canalConfig := db.getCanalConfig()
|
||||
c, err := canal.NewCanal(canalConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gtidSet, err := c.GetMasterGTIDSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Register a handler to handle RowsEvent
|
||||
c.SetEventHandler(targetDb)
|
||||
|
||||
// Start replication
|
||||
err = c.StartFromGTID(gtidSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@@ -17,84 +17,32 @@ package sync
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-mysql-org/go-mysql/canal"
|
||||
"github.com/go-mysql-org/go-mysql/mysql"
|
||||
"github.com/go-mysql-org/go-mysql/replication"
|
||||
"github.com/siddontang/go-log/log"
|
||||
"github.com/xorm-io/xorm"
|
||||
)
|
||||
|
||||
type MyEventHandler struct {
|
||||
dataSourceName string
|
||||
engine *xorm.Engine
|
||||
serverId uint32
|
||||
serverUUID string
|
||||
GTID string
|
||||
canal.DummyEventHandler
|
||||
}
|
||||
|
||||
func StartCanal(cfg *canal.Config, username string, password string, host string, port int, database string) error {
|
||||
c, err := canal.NewCanal(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
GTIDSet, err := c.GetMasterGTIDSet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
eventHandler := GetMyEventHandler(username, password, host, port, database)
|
||||
// Register a handler to handle RowsEvent
|
||||
c.SetEventHandler(&eventHandler)
|
||||
|
||||
// Start replication
|
||||
err = c.StartFromGTID(GTIDSet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func StartBinlogSync() error {
|
||||
var wg sync.WaitGroup
|
||||
// init config
|
||||
cfg1 := GetCanalConfig(username1, password1, host1, port1, database1)
|
||||
cfg2 := GetCanalConfig(username2, password2, host2, port2, database2)
|
||||
|
||||
// start canal1 replication
|
||||
go StartCanal(cfg1, username2, password2, host2, port2, database2)
|
||||
wg.Add(1)
|
||||
|
||||
// start canal2 replication
|
||||
go StartCanal(cfg2, username1, password1, host1, port1, database1)
|
||||
wg.Add(1)
|
||||
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *MyEventHandler) OnGTID(header *replication.EventHeader, gtid mysql.GTIDSet) error {
|
||||
func (db *Database) OnGTID(header *replication.EventHeader, gtid mysql.GTIDSet) error {
|
||||
log.Info("OnGTID: ", gtid.String())
|
||||
h.GTID = gtid.String()
|
||||
db.Gtid = gtid.String()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *MyEventHandler) onDDL(header *replication.EventHeader, nextPos mysql.Position, queryEvent *replication.QueryEvent) error {
|
||||
func (db *Database) onDDL(header *replication.EventHeader, nextPos mysql.Position, queryEvent *replication.QueryEvent) error {
|
||||
log.Info("into DDL event")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *MyEventHandler) OnRow(e *canal.RowsEvent) error {
|
||||
func (db *Database) OnRow(e *canal.RowsEvent) error {
|
||||
log.Info("serverId: ", e.Header.ServerID)
|
||||
if strings.Contains(h.GTID, h.serverUUID) {
|
||||
if strings.Contains(db.Gtid, db.serverUuid) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Set the next gtid of the target library to the gtid of the current target library to avoid loopbacks
|
||||
h.engine.Exec(fmt.Sprintf("SET GTID_NEXT= '%s'", h.GTID))
|
||||
db.engine.Exec(fmt.Sprintf("SET GTID_NEXT= '%s'", db.Gtid))
|
||||
length := len(e.Table.Columns)
|
||||
columnNames := make([]string, length)
|
||||
oldColumnValue := make([]interface{}, length)
|
||||
@@ -110,11 +58,11 @@ func (h *MyEventHandler) OnRow(e *canal.RowsEvent) error {
|
||||
}
|
||||
}
|
||||
// get pk column name
|
||||
pkColumnNames := GetPKColumnNames(columnNames, e.Table.PKColumns)
|
||||
pkColumnNames := getPkColumnNames(columnNames, e.Table.PKColumns)
|
||||
|
||||
switch e.Action {
|
||||
case canal.UpdateAction:
|
||||
h.engine.Exec("BEGIN")
|
||||
db.engine.Exec("BEGIN")
|
||||
for i, row := range e.Rows {
|
||||
for j, item := range row {
|
||||
if i%2 == 0 {
|
||||
@@ -136,23 +84,23 @@ func (h *MyEventHandler) OnRow(e *canal.RowsEvent) error {
|
||||
}
|
||||
}
|
||||
if i%2 == 1 {
|
||||
pkColumnValue := GetPKColumnValues(oldColumnValue, e.Table.PKColumns)
|
||||
updateSql, args, err := GetUpdateSql(e.Table.Schema, e.Table.Name, columnNames, newColumnValue, pkColumnNames, pkColumnValue)
|
||||
pkColumnValue := getPkColumnValues(oldColumnValue, e.Table.PKColumns)
|
||||
updateSql, args, err := getUpdateSql(e.Table.Schema, e.Table.Name, columnNames, newColumnValue, pkColumnNames, pkColumnValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := h.engine.DB().Exec(updateSql, args...)
|
||||
res, err := db.engine.DB().Exec(updateSql, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info(updateSql, args, res)
|
||||
}
|
||||
}
|
||||
h.engine.Exec("COMMIT")
|
||||
h.engine.Exec("SET GTID_NEXT='automatic'")
|
||||
db.engine.Exec("COMMIT")
|
||||
db.engine.Exec("SET GTID_NEXT='automatic'")
|
||||
case canal.DeleteAction:
|
||||
h.engine.Exec("BEGIN")
|
||||
db.engine.Exec("BEGIN")
|
||||
for _, row := range e.Rows {
|
||||
for j, item := range row {
|
||||
if isChar[j] == true {
|
||||
@@ -162,22 +110,22 @@ func (h *MyEventHandler) OnRow(e *canal.RowsEvent) error {
|
||||
}
|
||||
}
|
||||
|
||||
pkColumnValue := GetPKColumnValues(oldColumnValue, e.Table.PKColumns)
|
||||
deleteSql, args, err := GetDeleteSql(e.Table.Schema, e.Table.Name, pkColumnNames, pkColumnValue)
|
||||
pkColumnValue := getPkColumnValues(oldColumnValue, e.Table.PKColumns)
|
||||
deleteSql, args, err := getDeleteSql(e.Table.Schema, e.Table.Name, pkColumnNames, pkColumnValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := h.engine.DB().Exec(deleteSql, args...)
|
||||
res, err := db.engine.DB().Exec(deleteSql, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info(deleteSql, args, res)
|
||||
}
|
||||
h.engine.Exec("COMMIT")
|
||||
h.engine.Exec("SET GTID_NEXT='automatic'")
|
||||
db.engine.Exec("COMMIT")
|
||||
db.engine.Exec("SET GTID_NEXT='automatic'")
|
||||
case canal.InsertAction:
|
||||
h.engine.Exec("BEGIN")
|
||||
db.engine.Exec("BEGIN")
|
||||
for _, row := range e.Rows {
|
||||
for j, item := range row {
|
||||
if isChar[j] == true {
|
||||
@@ -191,25 +139,25 @@ func (h *MyEventHandler) OnRow(e *canal.RowsEvent) error {
|
||||
}
|
||||
}
|
||||
|
||||
insertSql, args, err := GetInsertSql(e.Table.Schema, e.Table.Name, columnNames, newColumnValue)
|
||||
insertSql, args, err := getInsertSql(e.Table.Schema, e.Table.Name, columnNames, newColumnValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := h.engine.DB().Exec(insertSql, args...)
|
||||
res, err := db.engine.DB().Exec(insertSql, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info(insertSql, args, res)
|
||||
}
|
||||
h.engine.Exec("COMMIT")
|
||||
h.engine.Exec("SET GTID_NEXT='automatic'")
|
||||
db.engine.Exec("COMMIT")
|
||||
db.engine.Exec("SET GTID_NEXT='automatic'")
|
||||
default:
|
||||
log.Infof("%v", e.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *MyEventHandler) String() string {
|
||||
return "MyEventHandler"
|
||||
func (db *Database) String() string {
|
||||
return "Database"
|
||||
}
|
32
sync/sync.go
Normal file
32
sync/sync.go
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2023 The Casdoor 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 sync
|
||||
|
||||
import "sync"
|
||||
|
||||
func startSyncJob(db1 *Database, db2 *Database) error {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// start canal1 replication
|
||||
go db1.startCanal(db2)
|
||||
wg.Add(1)
|
||||
|
||||
// start canal2 replication
|
||||
go db2.startCanal(db1)
|
||||
wg.Add(1)
|
||||
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
|
||||
// Copyright 2023 The Casdoor 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.
|
||||
@@ -12,22 +12,19 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
//go:build !skipCi
|
||||
// +build !skipCi
|
||||
|
||||
package sync
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
func TestGetCpuUsage(t *testing.T) {
|
||||
usage, err := GetCpuUsage()
|
||||
assert.Nil(t, err)
|
||||
t.Log(usage)
|
||||
}
|
||||
|
||||
func TestGetMemoryUsage(t *testing.T) {
|
||||
used, total, err := GetMemoryUsage()
|
||||
assert.Nil(t, err)
|
||||
t.Log(used, total)
|
||||
func TestStartSyncJob(t *testing.T) {
|
||||
db1 := newDatabase("127.0.0.1", 3306, "casdoor", "root", "123456")
|
||||
db2 := newDatabase("127.0.0.1", 3306, "casdoor2", "root", "123456")
|
||||
startSyncJob(db1, db2)
|
||||
}
|
@@ -19,13 +19,11 @@ import (
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/go-mysql-org/go-mysql/canal"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/xorm-io/xorm"
|
||||
)
|
||||
|
||||
func GetUpdateSql(schemaName string, tableName string, columnNames []string, newColumnVal []interface{}, pkColumnNames []string, pkColumnValue []interface{}) (string, []interface{}, error) {
|
||||
func getUpdateSql(schemaName string, tableName string, columnNames []string, newColumnVal []interface{}, pkColumnNames []string, pkColumnValue []interface{}) (string, []interface{}, error) {
|
||||
updateSql := squirrel.Update(schemaName + "." + tableName)
|
||||
for i, columnName := range columnNames {
|
||||
updateSql = updateSql.Set(columnName, newColumnVal[i])
|
||||
@@ -43,13 +41,13 @@ func GetUpdateSql(schemaName string, tableName string, columnNames []string, new
|
||||
return sql, args, nil
|
||||
}
|
||||
|
||||
func GetInsertSql(schemaName string, tableName string, columnNames []string, columnValue []interface{}) (string, []interface{}, error) {
|
||||
func getInsertSql(schemaName string, tableName string, columnNames []string, columnValue []interface{}) (string, []interface{}, error) {
|
||||
insertSql := squirrel.Insert(schemaName + "." + tableName).Columns(columnNames...).Values(columnValue...)
|
||||
|
||||
return insertSql.ToSql()
|
||||
}
|
||||
|
||||
func GetDeleteSql(schemaName string, tableName string, pkColumnNames []string, pkColumnValue []interface{}) (string, []interface{}, error) {
|
||||
func getDeleteSql(schemaName string, tableName string, pkColumnNames []string, pkColumnValue []interface{}) (string, []interface{}, error) {
|
||||
deleteSql := squirrel.Delete(schemaName + "." + tableName)
|
||||
|
||||
for i, columnName := range pkColumnNames {
|
||||
@@ -59,7 +57,7 @@ func GetDeleteSql(schemaName string, tableName string, pkColumnNames []string, p
|
||||
return deleteSql.ToSql()
|
||||
}
|
||||
|
||||
func CreateEngine(dataSourceName string) (*xorm.Engine, error) {
|
||||
func createEngine(dataSourceName string) (*xorm.Engine, error) {
|
||||
engine, err := xorm.NewEngine("mysql", dataSourceName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -75,7 +73,7 @@ func CreateEngine(dataSourceName string) (*xorm.Engine, error) {
|
||||
return engine, nil
|
||||
}
|
||||
|
||||
func GetServerId(engin *xorm.Engine) (uint32, error) {
|
||||
func getServerId(engin *xorm.Engine) (uint32, error) {
|
||||
res, err := engin.QueryInterface("SELECT @@server_id")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -84,16 +82,16 @@ func GetServerId(engin *xorm.Engine) (uint32, error) {
|
||||
return uint32(serverId), nil
|
||||
}
|
||||
|
||||
func GetServerUUID(engin *xorm.Engine) (string, error) {
|
||||
func getServerUuid(engin *xorm.Engine) (string, error) {
|
||||
res, err := engin.QueryString("show variables like 'server_uuid'")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
serverUUID := fmt.Sprintf("%s", res[0]["Value"])
|
||||
return serverUUID, err
|
||||
serverUuid := fmt.Sprintf("%s", res[0]["Value"])
|
||||
return serverUuid, err
|
||||
}
|
||||
|
||||
func GetPKColumnNames(columnNames []string, PKColumns []int) []string {
|
||||
func getPkColumnNames(columnNames []string, PKColumns []int) []string {
|
||||
pkColumnNames := make([]string, len(PKColumns))
|
||||
for i, index := range PKColumns {
|
||||
pkColumnNames[i] = columnNames[index]
|
||||
@@ -101,30 +99,10 @@ func GetPKColumnNames(columnNames []string, PKColumns []int) []string {
|
||||
return pkColumnNames
|
||||
}
|
||||
|
||||
func GetPKColumnValues(columnValues []interface{}, PKColumns []int) []interface{} {
|
||||
func getPkColumnValues(columnValues []interface{}, PKColumns []int) []interface{} {
|
||||
pkColumnNames := make([]interface{}, len(PKColumns))
|
||||
for i, index := range PKColumns {
|
||||
pkColumnNames[i] = columnValues[index]
|
||||
}
|
||||
return pkColumnNames
|
||||
}
|
||||
|
||||
func GetCanalConfig(username string, password string, host string, port int, database string) *canal.Config {
|
||||
// config canal
|
||||
cfg := canal.NewDefaultConfig()
|
||||
cfg.Addr = fmt.Sprintf("%s:%d", host, port)
|
||||
cfg.Password = password
|
||||
cfg.User = username
|
||||
// We only care table in database1
|
||||
cfg.Dump.TableDB = database
|
||||
return cfg
|
||||
}
|
||||
|
||||
func GetMyEventHandler(username string, password string, host string, port int, database string) MyEventHandler {
|
||||
var eventHandler MyEventHandler
|
||||
eventHandler.dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", username, password, host, port, database)
|
||||
eventHandler.engine, _ = CreateEngine(eventHandler.dataSourceName)
|
||||
eventHandler.serverId, _ = GetServerId(eventHandler.engine)
|
||||
eventHandler.serverUUID, _ = GetServerUUID(eventHandler.engine)
|
||||
return eventHandler
|
||||
}
|
124
util/system.go
124
util/system.go
@@ -15,26 +15,37 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
)
|
||||
|
||||
// get cpu usage
|
||||
func GetCpuUsage() ([]float64, error) {
|
||||
type SystemInfo struct {
|
||||
CpuUsage []float64 `json:"cpuUsage"`
|
||||
MemoryUsed uint64 `json:"memoryUsed"`
|
||||
MemoryTotal uint64 `json:"memoryTotal"`
|
||||
}
|
||||
|
||||
type VersionInfo struct {
|
||||
Version string `json:"version"`
|
||||
CommitId string `json:"commitId"`
|
||||
CommitOffset int `json:"commitOffset"`
|
||||
}
|
||||
|
||||
// getCpuUsage get cpu usage
|
||||
func getCpuUsage() ([]float64, error) {
|
||||
usage, err := cpu.Percent(time.Second, true)
|
||||
return usage, err
|
||||
}
|
||||
|
||||
var fileDate, version string
|
||||
|
||||
// get memory usage
|
||||
func GetMemoryUsage() (uint64, uint64, error) {
|
||||
// getMemoryUsage get memory usage
|
||||
func getMemoryUsage() (uint64, uint64, error) {
|
||||
virtualMem, err := mem.VirtualMemory()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
@@ -46,33 +57,86 @@ func GetMemoryUsage() (uint64, uint64, error) {
|
||||
return m.TotalAlloc, virtualMem.Total, nil
|
||||
}
|
||||
|
||||
// get github repo release version
|
||||
func GetGitRepoVersion() (string, error) {
|
||||
pwd, err := os.Getwd()
|
||||
func GetSystemInfo() (*SystemInfo, error) {
|
||||
cpuUsage, err := getCpuUsage()
|
||||
if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fileInfos, err := ioutil.ReadDir(pwd + "/.git/refs/heads")
|
||||
for _, v := range fileInfos {
|
||||
if v.Name() == "master" {
|
||||
if v.ModTime().String() == fileDate {
|
||||
return version, nil
|
||||
} else {
|
||||
fileDate = v.ModTime().String()
|
||||
break
|
||||
memoryUsed, memoryTotal, err := getMemoryUsage()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := &SystemInfo{
|
||||
CpuUsage: cpuUsage,
|
||||
MemoryUsed: memoryUsed,
|
||||
MemoryTotal: memoryTotal,
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// GetVersionInfo get git current commit and repo release version
|
||||
func GetVersionInfo() (*VersionInfo, error) {
|
||||
res := &VersionInfo{
|
||||
Version: "",
|
||||
CommitId: "",
|
||||
CommitOffset: -1,
|
||||
}
|
||||
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
rootPath := path.Dir(path.Dir(filename))
|
||||
r, err := git.PlainOpen(rootPath)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
ref, err := r.Head()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
tags, err := r.Tags()
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
tagMap := make(map[plumbing.Hash]string)
|
||||
err = tags.ForEach(func(t *plumbing.Reference) error {
|
||||
// This technique should work for both lightweight and annotated tags.
|
||||
revHash, err := r.ResolveRevision(plumbing.Revision(t.Name()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tagMap[*revHash] = t.Name().Short()
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
cIter, err := r.Log(&git.LogOptions{From: ref.Hash()})
|
||||
|
||||
commitOffset := 0
|
||||
version := ""
|
||||
// iterates over the commits
|
||||
err = cIter.ForEach(func(c *object.Commit) error {
|
||||
tag, ok := tagMap[c.Hash]
|
||||
if ok {
|
||||
if version == "" {
|
||||
version = tag
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadFile(pwd + "/.git/refs/heads/master")
|
||||
if version == "" {
|
||||
commitOffset++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
return res, err
|
||||
}
|
||||
|
||||
// Convert to full length
|
||||
temp := string(content)
|
||||
version = strings.ReplaceAll(temp, "\n", "")
|
||||
|
||||
return version, nil
|
||||
res = &VersionInfo{
|
||||
Version: version,
|
||||
CommitId: ref.Hash().String(),
|
||||
CommitOffset: commitOffset,
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
92
util/system_test.go
Normal file
92
util/system_test.go
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright 2022 The Casdoor 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.
|
||||
|
||||
//go:build !skipCi
|
||||
// +build !skipCi
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"path"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/go-git/go-git/v5"
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/object"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetCpuUsage(t *testing.T) {
|
||||
usage, err := getCpuUsage()
|
||||
assert.Nil(t, err)
|
||||
t.Log(usage)
|
||||
}
|
||||
|
||||
func TestGetMemoryUsage(t *testing.T) {
|
||||
used, total, err := getMemoryUsage()
|
||||
assert.Nil(t, err)
|
||||
t.Log(used, total)
|
||||
}
|
||||
|
||||
func TestGetGitRepoVersion(t *testing.T) {
|
||||
versionInfo, err := GetVersionInfo()
|
||||
assert.Nil(t, err)
|
||||
t.Log(versionInfo)
|
||||
}
|
||||
|
||||
func TestGetVersion(t *testing.T) {
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
root := path.Dir(path.Dir(filename))
|
||||
r, err := git.PlainOpen(root)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
tags, err := r.Tags()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
}
|
||||
tagMap := make(map[plumbing.Hash]string)
|
||||
err = tags.ForEach(func(t *plumbing.Reference) error {
|
||||
// This technique should work for both lightweight and annotated tags.
|
||||
revHash, err := r.ResolveRevision(plumbing.Revision(t.Name()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tagMap[*revHash] = t.Name().Short()
|
||||
return nil
|
||||
})
|
||||
|
||||
testHash := plumbing.NewHash("f8bc87eb4e5ba3256424cf14aafe0549f812f1cf")
|
||||
cIter, err := r.Log(&git.LogOptions{From: testHash})
|
||||
|
||||
aheadCnt := 0
|
||||
releaseVersion := ""
|
||||
// iterates over the commits
|
||||
err = cIter.ForEach(func(c *object.Commit) error {
|
||||
tag, ok := tagMap[c.Hash]
|
||||
if ok {
|
||||
if releaseVersion == "" {
|
||||
releaseVersion = tag
|
||||
}
|
||||
}
|
||||
if releaseVersion == "" {
|
||||
aheadCnt++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
assert.Equal(t, 3, aheadCnt)
|
||||
assert.Equal(t, "v1.257.0", releaseVersion)
|
||||
}
|
@@ -14,12 +14,13 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Switch, Table} from "antd";
|
||||
import {Button, Switch, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as AdapterBackend from "./backend/AdapterBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class AdapterListPage extends BaseListPage {
|
||||
newAdapter() {
|
||||
@@ -204,12 +205,11 @@ class AdapterListPage extends BaseListPage {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/adapters/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteAdapter(index)}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -16,7 +16,7 @@ import React, {Component} from "react";
|
||||
import "./App.less";
|
||||
import {Helmet} from "react-helmet";
|
||||
import * as Setting from "./Setting";
|
||||
import {StyleProvider} from "@ant-design/cssinjs";
|
||||
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
|
||||
import {BarsOutlined, DownOutlined, InfoCircleFilled, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
|
||||
import {Alert, Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result} from "antd";
|
||||
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
|
||||
@@ -730,7 +730,7 @@ class App extends Component {
|
||||
},
|
||||
algorithm: Setting.getAlgorithm(this.state.themeAlgorithm),
|
||||
}}>
|
||||
<StyleProvider hashPriority="high">
|
||||
<StyleProvider hashPriority="high" transformers={[legacyLogicalPropertiesTransformer]}>
|
||||
{
|
||||
this.renderPage()
|
||||
}
|
||||
|
@@ -14,13 +14,14 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Col, List, Popconfirm, Row, Table, Tooltip} from "antd";
|
||||
import {Button, Col, List, Row, Table, Tooltip} from "antd";
|
||||
import {EditOutlined} from "@ant-design/icons";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class ApplicationListPage extends BaseListPage {
|
||||
constructor(props) {
|
||||
@@ -232,13 +233,12 @@ class ApplicationListPage extends BaseListPage {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/applications/${record.organization}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteApplication(index)}
|
||||
disabled={record.name === "app-built-in"}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} disabled={record.name === "app-built-in"} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -14,12 +14,13 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Table} from "antd";
|
||||
import {Button, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as CertBackend from "./backend/CertBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class CertListPage extends BaseListPage {
|
||||
newCert() {
|
||||
@@ -168,12 +169,11 @@ class CertListPage extends BaseListPage {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/certs/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteCert(index)}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -83,7 +83,12 @@ class LdapEditPage extends React.Component {
|
||||
<Card size="small" title={
|
||||
<div>
|
||||
{i18next.t("ldap:Edit LDAP")}
|
||||
<Button type="primary" onClick={() => this.submitLdapEdit()}>{i18next.t("general:Save")}</Button>
|
||||
<Button onClick={() => this.submitLdapEdit()}>{i18next.t("general:Save")}</Button>
|
||||
<Button style={{marginLeft: "20px"}} type="primary" onClick={() => this.submitLdapEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
|
||||
<Button style={{marginLeft: "20px"}}
|
||||
onClick={() => Setting.goToLink(`/ldap/sync/${this.state.organizationName}/${this.state.ldapId}`)}>
|
||||
{i18next.t("ldap:Sync")} LDAP
|
||||
</Button>
|
||||
</div>
|
||||
} style={{marginLeft: "5px"}} type="inner">
|
||||
<Row style={{marginTop: "10px"}}>
|
||||
@@ -190,14 +195,18 @@ class LdapEditPage extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
submitLdapEdit() {
|
||||
submitLdapEdit(willExist) {
|
||||
LddpBackend.updateLdap(this.state.ldap)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", "Update LDAP server success");
|
||||
this.setState((prevState) => {
|
||||
prevState.ldap = res.data2;
|
||||
this.setState({
|
||||
organizationName: this.state.ldap.owner,
|
||||
});
|
||||
|
||||
if (willExist) {
|
||||
this.props.history.push(`/organizations/${this.state.organizationName}`);
|
||||
}
|
||||
} else {
|
||||
Setting.showMessage("error", res.msg);
|
||||
}
|
||||
@@ -210,25 +219,13 @@ class LdapEditPage extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Row style={{width: "100%"}}>
|
||||
<Col span={1}>
|
||||
</Col>
|
||||
<Col span={22}>
|
||||
{
|
||||
this.state.ldap !== null ? this.renderLdap() : 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.submitLdapEdit()}>{i18next.t("general:Save")}</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
{
|
||||
this.state.ldap !== null ? this.renderLdap() : null
|
||||
}
|
||||
<div style={{marginTop: "20px", marginLeft: "40px"}}>
|
||||
<Button size="large" onClick={() => this.submitLdapEdit()}>{i18next.t("general:Save")}</Button>
|
||||
<Button style={{marginLeft: "20px"}} type="primary" size="large" onClick={() => this.submitLdapEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -14,10 +14,11 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Col, Popconfirm, Row, Table} from "antd";
|
||||
import {Button, Col, Row, Table} from "antd";
|
||||
import * as Setting from "./Setting";
|
||||
import * as LdapBackend from "./backend/LdapBackend";
|
||||
import i18next from "i18next";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class LdapListPage extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -139,13 +140,11 @@ class LdapListPage extends React.Component {
|
||||
onClick={() => Setting.goToLink(`/ldap/sync/${record.id}`)}>{i18next.t("ldap:Sync")}</Button>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}}
|
||||
onClick={() => Setting.goToLink(`/ldap/${record.id}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.serverName} ?`}
|
||||
onConfirm={() => this.deleteLdap(index)}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}}
|
||||
type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -81,44 +81,28 @@ class LdapSyncPage extends React.Component {
|
||||
prevState.ldap = res.data;
|
||||
return prevState;
|
||||
});
|
||||
this.getLdapUser(res.data);
|
||||
this.getLdapUser();
|
||||
} else {
|
||||
Setting.showMessage("error", res.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getLdapUser(ldap) {
|
||||
LdapBackend.getLdapUser(ldap)
|
||||
getLdapUser() {
|
||||
LdapBackend.getLdapUser(this.state.organizationName, this.state.ldapId)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState((prevState) => {
|
||||
prevState.users = res.data.users;
|
||||
prevState.existUuids = res.data2?.length > 0 ? res.data2 : [];
|
||||
return prevState;
|
||||
});
|
||||
this.getExistUsers(ldap.owner, res.data.users);
|
||||
} else {
|
||||
Setting.showMessage("error", res.msg);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getExistUsers(owner, users) {
|
||||
const uuidArray = [];
|
||||
users.forEach(elem => {
|
||||
uuidArray.push(elem.uuid);
|
||||
});
|
||||
LdapBackend.checkLdapUsersExist(owner, uuidArray)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState(prevState => {
|
||||
prevState.existUuids = res.data?.length > 0 ? res.data : [];
|
||||
return prevState;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
buildValArray(data, key) {
|
||||
const valTypesArray = [];
|
||||
|
||||
@@ -220,9 +204,14 @@ class LdapSyncPage extends React.Component {
|
||||
title={"Please confirm to sync selected users"}
|
||||
onConfirm={() => this.syncUsers()}
|
||||
>
|
||||
<Button type="primary" size="small"
|
||||
style={{marginLeft: "10px"}}>{i18next.t("ldap:Sync")}</Button>
|
||||
<Button type="primary" style={{marginLeft: "10px"}}>
|
||||
{i18next.t("ldap:Sync")}
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
<Button style={{marginLeft: "20px"}}
|
||||
onClick={() => Setting.goToLink(`/ldap/${this.state.organizationName}/${this.state.ldapId}`)}>
|
||||
{i18next.t("general:Edit")} LDAP
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
loading={users === null}
|
||||
@@ -234,17 +223,20 @@ class LdapSyncPage extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Row style={{width: "100%"}}>
|
||||
<Col span={1}>
|
||||
</Col>
|
||||
<Row style={{width: "100%", justifyContent: "center"}}>
|
||||
<Col span={22}>
|
||||
{
|
||||
this.renderTable(this.state.users)
|
||||
}
|
||||
</Col>
|
||||
<Col span={1}>
|
||||
</Col>
|
||||
</Row>
|
||||
<div style={{marginTop: "20px", marginLeft: "40px"}}>
|
||||
<Button style={{marginLeft: "20px"}} type="primary" size="large" onClick={() => {
|
||||
this.props.history.push(`/organizations/${this.state.organizationName}`);
|
||||
}}>
|
||||
{i18next.t("general:Save & Exit")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -13,11 +13,12 @@
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {Button, Col, Popconfirm, Row, Table} from "antd";
|
||||
import {Button, Col, Row, Table} from "antd";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
import * as LdapBackend from "./backend/LdapBackend";
|
||||
import {Link} from "react-router-dom";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class LdapTable extends React.Component {
|
||||
constructor(props) {
|
||||
@@ -57,14 +58,14 @@ class LdapTable extends React.Component {
|
||||
LdapBackend.addLdap(newLdap)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", "Add LDAP server success");
|
||||
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||
if (table === undefined) {
|
||||
table = [];
|
||||
}
|
||||
table = Setting.addRow(table, res.data2);
|
||||
this.updateTable(table);
|
||||
} else {
|
||||
Setting.showMessage("error", res.msg);
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -77,14 +78,13 @@ class LdapTable extends React.Component {
|
||||
LdapBackend.deleteLdap(table[i])
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", "Delete LDAP server success");
|
||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||
table = Setting.deleteRow(table, i);
|
||||
this.updateTable(table);
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `Delete LDAP server failed: ${error}`);
|
||||
});
|
||||
@@ -152,18 +152,19 @@ class LdapTable extends React.Component {
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary"
|
||||
onClick={() => Setting.goToLink(`/ldap/sync/${record.owner}/${record.id}`)}>
|
||||
{i18next.t("ldap:Sync")}
|
||||
</Button>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}}
|
||||
type="primary"
|
||||
onClick={() => Setting.goToLink(`/ldap/sync/${record.owner}/${record.id}`)}>{i18next.t("ldap:Sync")}</Button>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}}
|
||||
onClick={() => Setting.goToLink(`/ldap/${record.owner}/${record.id}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
onClick={() => Setting.goToLink(`/ldap/${record.owner}/${record.id}`)}>
|
||||
{i18next.t("general:Edit")}
|
||||
</Button>
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.serverName} ?`}
|
||||
onConfirm={() => this.deleteRow(table, index)}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}}
|
||||
type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -14,12 +14,13 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Switch, Table} from "antd";
|
||||
import {Button, Switch, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as ModelBackend from "./backend/ModelBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class ModelListPage extends BaseListPage {
|
||||
newModel() {
|
||||
@@ -142,12 +143,11 @@ class ModelListPage extends BaseListPage {
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary"
|
||||
onClick={() => this.props.history.push(`/models/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteModel(index)}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -14,12 +14,13 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Switch, Table} from "antd";
|
||||
import {Button, Switch, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class OrganizationListPage extends BaseListPage {
|
||||
newOrganization() {
|
||||
@@ -226,13 +227,12 @@ class OrganizationListPage extends BaseListPage {
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/organizations/${record.name}/users`)}>{i18next.t("general:Users")}</Button>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => this.props.history.push(`/organizations/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteOrganization(index)}
|
||||
disabled={record.name === "built-in"}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} disabled={record.name === "built-in"} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -14,13 +14,14 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Table} from "antd";
|
||||
import {Button, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as PaymentBackend from "./backend/PaymentBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import * as Provider from "./auth/Provider";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class PaymentListPage extends BaseListPage {
|
||||
newPayment() {
|
||||
@@ -222,12 +223,11 @@ class PaymentListPage extends BaseListPage {
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => this.props.history.push(`/payments/${record.name}/result`)}>{i18next.t("payment:Result")}</Button>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/payments/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deletePayment(index)}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -14,12 +14,13 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Switch, Table} from "antd";
|
||||
import {Button, Switch, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as PermissionBackend from "./backend/PermissionBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class PermissionListPage extends BaseListPage {
|
||||
newPermission() {
|
||||
@@ -300,12 +301,11 @@ class PermissionListPage extends BaseListPage {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/permissions/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deletePermission(index)}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
33
web/src/PopconfirmModal.js
Normal file
33
web/src/PopconfirmModal.js
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2021 The Casdoor 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 {Button, Popconfirm} from "antd";
|
||||
import i18next from "i18next";
|
||||
import React from "react";
|
||||
|
||||
export const PopconfirmModal = (props) => {
|
||||
return (
|
||||
<Popconfirm
|
||||
title={props.title}
|
||||
onConfirm={props.onConfirm}
|
||||
disabled={props.disabled}
|
||||
okText={i18next.t("user:OK")}
|
||||
cancelText={i18next.t("general:Cancel")}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} disabled={props.disabled} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
);
|
||||
};
|
||||
|
||||
export default PopconfirmModal;
|
@@ -14,13 +14,14 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Col, List, Popconfirm, Row, Table, Tooltip} from "antd";
|
||||
import {Button, Col, List, Row, Table, Tooltip} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as ProductBackend from "./backend/ProductBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import {EditOutlined} from "@ant-design/icons";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class ProductListPage extends BaseListPage {
|
||||
newProduct() {
|
||||
@@ -239,12 +240,11 @@ class ProductListPage extends BaseListPage {
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => this.props.history.push(`/products/${record.name}/buy`)}>{i18next.t("product:Buy")}</Button>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/products/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteProduct(index)}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -14,13 +14,14 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Table} from "antd";
|
||||
import {Button, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as ProviderBackend from "./backend/ProviderBackend";
|
||||
import * as Provider from "./auth/Provider";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class ProviderListPage extends BaseListPage {
|
||||
constructor(props) {
|
||||
@@ -206,12 +207,11 @@ class ProviderListPage extends BaseListPage {
|
||||
return (
|
||||
<div>
|
||||
<Button disabled={!Setting.isAdminUser(this.props.account) && (record.owner !== this.props.account.owner)} style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/providers/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteProvider(index)}
|
||||
>
|
||||
<Button disabled={!Setting.isAdminUser(this.props.account) && (record.owner !== this.props.account.owner)} style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {Button, Popconfirm, Table, Upload} from "antd";
|
||||
import {Button, Table, Upload} from "antd";
|
||||
import {UploadOutlined} from "@ant-design/icons";
|
||||
import copy from "copy-to-clipboard";
|
||||
import * as Setting from "./Setting";
|
||||
@@ -21,6 +21,7 @@ import * as ResourceBackend from "./backend/ResourceBackend";
|
||||
import i18next from "i18next";
|
||||
import {Link} from "react-router-dom";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class ResourceListPage extends BaseListPage {
|
||||
constructor(props) {
|
||||
@@ -244,15 +245,13 @@ class ResourceListPage extends BaseListPage {
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
{/* <Button style={{marginTop: '10px', marginBottom: '10px', marginRight: '10px'}} type="primary" onClick={() => this.props.history.push(`/resources/${record.name}`)}>{i18next.t("general:Edit")}</Button>*/}
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteResource(index)}
|
||||
okText={i18next.t("user:OK")}
|
||||
cancelText={i18next.t("user:Cancel")}
|
||||
>
|
||||
<Button type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -14,12 +14,13 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Switch, Table} from "antd";
|
||||
import {Button, Switch, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as RoleBackend from "./backend/RoleBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class RoleListPage extends BaseListPage {
|
||||
newRole() {
|
||||
@@ -175,12 +176,11 @@ class RoleListPage extends BaseListPage {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/roles/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteRole(index)}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -16,9 +16,10 @@ import BaseListPage from "./BaseListPage";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Table, Tag} from "antd";
|
||||
import {Table, Tag} from "antd";
|
||||
import React from "react";
|
||||
import * as SessionBackend from "./backend/SessionBackend";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class SessionListPage extends BaseListPage {
|
||||
|
||||
@@ -97,12 +98,11 @@ class SessionListPage extends BaseListPage {
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteSession(index)}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -14,12 +14,13 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Switch, Table} from "antd";
|
||||
import {Button, Switch, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as SyncerBackend from "./backend/SyncerBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class SyncerListPage extends BaseListPage {
|
||||
newSyncer() {
|
||||
@@ -232,12 +233,11 @@ class SyncerListPage extends BaseListPage {
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.runSyncer(index)}>{i18next.t("general:Sync")}</Button>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => this.props.history.push(`/syncers/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteSyncer(index)}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -23,31 +23,24 @@ class SystemInfo extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
cpuUsage: [],
|
||||
memUsed: 0,
|
||||
memTotal: 0,
|
||||
latestVersion: undefined,
|
||||
systemInfo: {cpuUsage: [], memoryUsed: 0, memoryTotal: 0},
|
||||
versionInfo: {},
|
||||
intervalId: null,
|
||||
href: "",
|
||||
loading: true,
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
SystemBackend.getSystemInfo(this.props.account?.owner, this.props.account?.name).then(res => {
|
||||
SystemBackend.getSystemInfo().then(res => {
|
||||
this.setState({
|
||||
cpuUsage: res.cpu_usage,
|
||||
memUsed: res.memory_used,
|
||||
memTotal: res.memory_total,
|
||||
systemInfo: res.data,
|
||||
loading: false,
|
||||
});
|
||||
|
||||
const id = setInterval(() => {
|
||||
SystemBackend.getSystemInfo(this.props.account?.owner, this.props.account?.name).then(res => {
|
||||
SystemBackend.getSystemInfo().then(res => {
|
||||
this.setState({
|
||||
cpuUsage: res.cpu_usage,
|
||||
memUsed: res.memory_used,
|
||||
memTotal: res.memory_total,
|
||||
systemInfo: res.data,
|
||||
});
|
||||
}).catch(error => {
|
||||
Setting.showMessage("error", `System info failed to get: ${error}`);
|
||||
@@ -58,12 +51,12 @@ class SystemInfo extends React.Component {
|
||||
Setting.showMessage("error", `System info failed to get: ${error}`);
|
||||
});
|
||||
|
||||
SystemBackend.getGitHubLatestReleaseVersion().then(res => {
|
||||
const href = res && res.length >= 8 ? `https://github.com/casdoor/casdoor/commit/${res}` : "";
|
||||
const versionText = res && res.length >= 8 ? res.substring(0, 8) : i18next.t("system:Unknown Version");
|
||||
this.setState({latestVersion: versionText, href: href});
|
||||
SystemBackend.getVersionInfo().then(res => {
|
||||
this.setState({
|
||||
versionInfo: res.data,
|
||||
});
|
||||
}).catch(err => {
|
||||
Setting.showMessage("error", `get latest commit version failed: ${err}`);
|
||||
Setting.showMessage("error", `Version info failed to get: ${err}`);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -74,21 +67,25 @@ class SystemInfo extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const CPUInfo = this.state.cpuUsage?.length > 0 ?
|
||||
this.state.cpuUsage.map((usage, i) => {
|
||||
const cpuUi = this.state.systemInfo.cpuUsage?.length <= 0 ? i18next.t("system:Get CPU Usage Failed") :
|
||||
this.state.systemInfo.cpuUsage.map((usage, i) => {
|
||||
return (
|
||||
<Progress key={i} percent={Number(usage.toFixed(1))} />
|
||||
);
|
||||
}) : i18next.t("system:Get CPU Usage Failed");
|
||||
});
|
||||
|
||||
const MemInfo = (
|
||||
this.state.memUsed && this.state.memTotal && this.state.memTotal !== 0 ?
|
||||
<div>
|
||||
{Setting.getFriendlyFileSize(this.state.memUsed)} / {Setting.getFriendlyFileSize(this.state.memTotal)}
|
||||
<br /> <br />
|
||||
<Progress type="circle" percent={Number((Number(this.state.memUsed) / Number(this.state.memTotal) * 100).toFixed(2))} />
|
||||
</div> : i18next.t("system:Get Memory Usage Failed")
|
||||
);
|
||||
const memUi = this.state.systemInfo.memoryUsed && this.state.systemInfo.memoryTotal && this.state.systemInfo.memoryTotal <= 0 ? i18next.t("system:Get Memory Usage Failed") :
|
||||
<div>
|
||||
{Setting.getFriendlyFileSize(this.state.systemInfo.memoryUsed)} / {Setting.getFriendlyFileSize(this.state.systemInfo.memoryTotal)}
|
||||
<br /> <br />
|
||||
<Progress type="circle" percent={Number((Number(this.state.systemInfo.memoryUsed) / Number(this.state.systemInfo.memoryTotal) * 100).toFixed(2))} />
|
||||
</div>;
|
||||
|
||||
const link = this.state.versionInfo?.version !== "" ? `https://github.com/casdoor/casdoor/releases/tag/${this.state.versionInfo?.version}` : "";
|
||||
let versionText = this.state.versionInfo?.version !== "" ? this.state.versionInfo?.version : i18next.t("system:Unknown Version");
|
||||
if (this.state.versionInfo?.commitOffset > 0) {
|
||||
versionText += ` (ahead+${this.state.versionInfo?.commitOffset})`;
|
||||
}
|
||||
|
||||
if (!Setting.isMobile()) {
|
||||
return (
|
||||
@@ -98,25 +95,25 @@ class SystemInfo extends React.Component {
|
||||
<Row gutter={[10, 10]}>
|
||||
<Col span={12}>
|
||||
<Card title={i18next.t("system:CPU Usage")} bordered={true} style={{textAlign: "center", height: "100%"}}>
|
||||
{this.state.loading ? <Spin size="large" /> : CPUInfo}
|
||||
{this.state.loading ? <Spin size="large" /> : cpuUi}
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Card title={i18next.t("system:Memory Usage")} bordered={true} style={{textAlign: "center", height: "100%"}}>
|
||||
{this.state.loading ? <Spin size="large" /> : MemInfo}
|
||||
{this.state.loading ? <Spin size="large" /> : memUi}
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider />
|
||||
<Card title={i18next.t("system:About Casdoor")} bordered={true} style={{textAlign: "center"}}>
|
||||
<div>{i18next.t("system:An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS")}</div>
|
||||
GitHub: <a href="https://github.com/casdoor/casdoor">casdoor</a>
|
||||
GitHub: <a target="_blank" rel="noreferrer" href="https://github.com/casdoor/casdoor">Casdoor</a>
|
||||
<br />
|
||||
{i18next.t("system:Version")}: <a href={this.state.href}>{this.state.latestVersion}</a>
|
||||
{i18next.t("system:Version")}: <a target="_blank" rel="noreferrer" href={link}>{versionText}</a>
|
||||
<br />
|
||||
{i18next.t("system:Official Website")}: <a href="https://casdoor.org/">casdoor.org</a>
|
||||
{i18next.t("system:Official Website")}: <a target="_blank" rel="noreferrer" href="https://casdoor.org">https://casdoor.org</a>
|
||||
<br />
|
||||
{i18next.t("system:Community")}: <a href="https://casdoor.org/#:~:text=Casdoor%20API-,Community,-GitHub">contact us</a>
|
||||
{i18next.t("system:Community")}: <a target="_blank" rel="noreferrer" href="https://casdoor.org/#:~:text=Casdoor%20API-,Community,-GitHub">Get in Touch!</a>
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={6}></Col>
|
||||
@@ -127,24 +124,24 @@ class SystemInfo extends React.Component {
|
||||
<Row gutter={[16, 0]}>
|
||||
<Col span={24}>
|
||||
<Card title={i18next.t("system:CPU Usage")} bordered={true} style={{textAlign: "center", width: "100%"}}>
|
||||
{this.state.loading ? <Spin size="large" /> : CPUInfo}
|
||||
{this.state.loading ? <Spin size="large" /> : cpuUi}
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Card title={i18next.t("system:Memory Usage")} bordered={true} style={{textAlign: "center", width: "100%"}}>
|
||||
{this.state.loading ? <Spin size="large" /> : MemInfo}
|
||||
{this.state.loading ? <Spin size="large" /> : memUi}
|
||||
</Card>
|
||||
</Col>
|
||||
<Col span={24}>
|
||||
<Card title={i18next.t("system:About Casdoor")} bordered={true} style={{textAlign: "center"}}>
|
||||
<div>{i18next.t("system:An Identity and Access Management (IAM) / Single-Sign-On (SSO) platform with web UI supporting OAuth 2.0, OIDC, SAML and CAS")}</div>
|
||||
GitHub: <a href="https://github.com/casdoor/casdoor">casdoor</a>
|
||||
GitHub: <a target="_blank" rel="noreferrer" href="https://github.com/casdoor/casdoor">Casdoor</a>
|
||||
<br />
|
||||
{i18next.t("system:Version")}: <a href={this.state.href}>{this.state.latestVersion}</a>
|
||||
{i18next.t("system:Version")}: <a target="_blank" rel="noreferrer" href={link}>{versionText}</a>
|
||||
<br />
|
||||
{i18next.t("system:Official Website")}: <a href="https://casdoor.org/">casdoor.org</a>
|
||||
{i18next.t("system:Official Website")}: <a target="_blank" rel="noreferrer" href="https://casdoor.org">https://casdoor.org</a>
|
||||
<br />
|
||||
{i18next.t("system:Community")}: <a href="https://casdoor.org/#:~:text=Casdoor%20API-,Community,-GitHub">contact us</a>
|
||||
{i18next.t("system:Community")}: <a target="_blank" rel="noreferrer" href="https://casdoor.org/#:~:text=Casdoor%20API-,Community,-GitHub">Get in Touch!</a>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
@@ -14,12 +14,13 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Table} from "antd";
|
||||
import {Button, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as TokenBackend from "./backend/TokenBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class TokenListPage extends BaseListPage {
|
||||
newToken() {
|
||||
@@ -201,12 +202,11 @@ class TokenListPage extends BaseListPage {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/tokens/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteToken(index)}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -14,7 +14,7 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Switch, Table, Upload} from "antd";
|
||||
import {Button, Switch, Table, Upload} from "antd";
|
||||
import {UploadOutlined} from "@ant-design/icons";
|
||||
import moment from "moment";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
@@ -22,6 +22,7 @@ import * as Setting from "./Setting";
|
||||
import * as UserBackend from "./backend/UserBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class UserListPage extends BaseListPage {
|
||||
constructor(props) {
|
||||
@@ -341,13 +342,12 @@ class UserListPage extends BaseListPage {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/users/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteUser(index)}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Button disabled={disabled} style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
@@ -384,7 +384,7 @@ class UserListPage extends BaseListPage {
|
||||
const field = params.searchedColumn, value = params.searchText;
|
||||
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
this.setState({loading: true});
|
||||
if (this.state.organizationName === undefined) {
|
||||
if (this.props.match.params.organizationName === undefined) {
|
||||
(Setting.isAdminUser(this.props.account) ? UserBackend.getGlobalUsers(params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder) : UserBackend.getUsers(this.props.account.owner, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder))
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
@@ -413,7 +413,7 @@ class UserListPage extends BaseListPage {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
UserBackend.getUsers(this.state.organizationName, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||
UserBackend.getUsers(this.props.match.params.organizationName, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
|
@@ -14,12 +14,13 @@
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Popconfirm, Switch, Table} from "antd";
|
||||
import {Button, Switch, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as WebhookBackend from "./backend/WebhookBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./PopconfirmModal";
|
||||
|
||||
class WebhookListPage extends BaseListPage {
|
||||
newWebhook() {
|
||||
@@ -197,12 +198,11 @@ class WebhookListPage extends BaseListPage {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/webhooks/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<Popconfirm
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteWebhook(index)}
|
||||
>
|
||||
<Button style={{marginBottom: "10px"}} type="primary" danger>{i18next.t("general:Delete")}</Button>
|
||||
</Popconfirm>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
|
@@ -46,7 +46,6 @@ class LoginPage extends React.Component {
|
||||
username: null,
|
||||
validEmailOrPhone: false,
|
||||
validEmail: false,
|
||||
validPhone: false,
|
||||
loginMethod: "password",
|
||||
enableCaptchaModal: false,
|
||||
openCaptchaModal: false,
|
||||
@@ -427,16 +426,15 @@ class LoginPage extends React.Component {
|
||||
{
|
||||
validator: (_, value) => {
|
||||
if (this.state.loginMethod === "verificationCode") {
|
||||
if (!Setting.isValidEmail(this.state.username) && !Setting.isValidPhone(this.state.username)) {
|
||||
if (!Setting.isValidEmail(value) && !Setting.isValidPhone(value)) {
|
||||
this.setState({validEmailOrPhone: false});
|
||||
return Promise.reject(i18next.t("login:The input is not valid Email or Phone!"));
|
||||
}
|
||||
|
||||
if (Setting.isValidPhone(this.state.username)) {
|
||||
this.setState({validPhone: true});
|
||||
}
|
||||
if (Setting.isValidEmail(this.state.username)) {
|
||||
if (Setting.isValidEmail(value)) {
|
||||
this.setState({validEmail: true});
|
||||
} else {
|
||||
this.setState({validEmail: false});
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -378,7 +378,7 @@ export function getAuthUrl(application, provider, method) {
|
||||
const state = Util.getStateFromQueryParams(application.name, provider.name, method, isShortState);
|
||||
const codeChallenge = "P3S-a7dr8bgM4bF6vOyiKkKETDl16rcAzao9F8UIL1Y"; // SHA256(Base64-URL-encode("casdoor-verifier"))
|
||||
|
||||
if (provider.type === "Google" || provider.type === "GitHub" || provider.type === "QQ" || provider.type === "Facebook" || provider.type === "DingTalk"
|
||||
if (provider.type === "Google" || provider.type === "GitHub" || provider.type === "QQ" || provider.type === "Facebook"
|
||||
|| provider.type === "Weibo" || provider.type === "Gitee" || provider.type === "LinkedIn" || provider.type === "GitLab" || provider.type === "AzureAD"
|
||||
|| provider.type === "Slack" || provider.type === "Line" || provider.type === "Amazon" || provider.type === "Auth0" || provider.type === "BattleNet"
|
||||
|| provider.type === "Bitbucket" || provider.type === "Box" || provider.type === "CloudFoundry" || provider.type === "Dailymotion"
|
||||
@@ -390,6 +390,8 @@ export function getAuthUrl(application, provider, method) {
|
||||
|| provider.type === "Twitch" || provider.type === "Typetalk" || provider.type === "Uber" || provider.type === "VK" || provider.type === "Wepay"
|
||||
|| provider.type === "Xero" || provider.type === "Yahoo" || provider.type === "Yammer" || provider.type === "Yandex" || provider.type === "Zoom") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&state=${state}`;
|
||||
} else if (provider.type === "DingTalk") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&scope=${scope}&response_type=code&prompt=consent&state=${state}`;
|
||||
} else if (provider.type === "WeChat") {
|
||||
if (navigator.userAgent.includes("MicroMessenger")) {
|
||||
return `${authInfo[provider.type].mpEndpoint}?appid=${provider.clientId2}&redirect_uri=${redirectUri}&state=${state}&scope=${authInfo[provider.type].mpScope}&response_type=code#wechat_redirect`;
|
||||
|
@@ -67,11 +67,10 @@ export function updateLdap(body) {
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getLdapUser(body) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-ldap-user`, {
|
||||
method: "POST",
|
||||
export function getLdapUser(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-ldap-users?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
@@ -88,14 +87,3 @@ export function syncUsers(owner, ldapId, body) {
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function checkLdapUsersExist(owner, body) {
|
||||
return fetch(`${Setting.ServerUrl}/api/check-ldap-users-exist?owner=${owner}`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(body),
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
@@ -14,8 +14,8 @@
|
||||
|
||||
import * as Setting from "../Setting";
|
||||
|
||||
export function getSystemInfo(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-system-info?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
export function getSystemInfo() {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-system-info`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
@@ -24,8 +24,8 @@ export function getSystemInfo(owner, name) {
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getGitHubLatestReleaseVersion() {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-release`, {
|
||||
export function getVersionInfo() {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-version-info`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
|
@@ -67,7 +67,7 @@ export const SendCodeInput = (props) => {
|
||||
onChange={e => onChange(e.target.value)}
|
||||
enterButton={
|
||||
<Button style={{fontSize: 14}} type={"primary"} disabled={disabled || buttonLeftTime > 0} loading={buttonLoading}>
|
||||
{buttonLeftTime > 0 ? `${buttonLeftTime} s` : buttonLoading ? i18next.t("code:Sending Code") : i18next.t("code:Send Code")}
|
||||
{buttonLeftTime > 0 ? `${buttonLeftTime} s` : buttonLoading ? i18next.t("code:Sending") : i18next.t("code:Send Code")}
|
||||
</Button>
|
||||
}
|
||||
onSearch={() => setVisible(true)}
|
||||
|
@@ -121,7 +121,7 @@
|
||||
"Please input your phone verification code!": "Bitte geben Sie Ihren Telefon-Verifizierungscode ein!",
|
||||
"Please input your verification code!": "Bitte geben Sie Ihren Bestätigungscode ein!",
|
||||
"Send Code": "Code senden",
|
||||
"Sending Code": "Code wird gesendet",
|
||||
"Sending": "Code wird gesendet",
|
||||
"Submit and complete": "Absenden und abschließen"
|
||||
},
|
||||
"forget": {
|
||||
|
@@ -121,7 +121,7 @@
|
||||
"Please input your phone verification code!": "Please input your phone verification code!",
|
||||
"Please input your verification code!": "Please input your verification code!",
|
||||
"Send Code": "Send Code",
|
||||
"Sending Code": "Sending Code",
|
||||
"Sending": "Sending",
|
||||
"Submit and complete": "Submit and complete"
|
||||
},
|
||||
"forget": {
|
||||
|
@@ -121,7 +121,7 @@
|
||||
"Please input your phone verification code!": "Ingrese su código de verificación teléfonico!",
|
||||
"Please input your verification code!": "Ingrese su código de verificación!",
|
||||
"Send Code": "Enviar código",
|
||||
"Sending Code": "Enviando código",
|
||||
"Sending": "Enviando código",
|
||||
"Submit and complete": "Enviar y completar"
|
||||
},
|
||||
"forget": {
|
||||
|
@@ -121,7 +121,7 @@
|
||||
"Please input your phone verification code!": "Veuillez entrer le code de vérification de votre téléphone !",
|
||||
"Please input your verification code!": "Veuillez entrer votre code de vérification !",
|
||||
"Send Code": "Envoyer le code",
|
||||
"Sending Code": "Code d'envoi",
|
||||
"Sending": "Code d'envoi",
|
||||
"Submit and complete": "Soumettre et compléter"
|
||||
},
|
||||
"forget": {
|
||||
|
@@ -121,7 +121,7 @@
|
||||
"Please input your phone verification code!": "電話番号を入力してください!",
|
||||
"Please input your verification code!": "認証コードを入力してください!",
|
||||
"Send Code": "コードを送信",
|
||||
"Sending Code": "コードを送信中",
|
||||
"Sending": "コードを送信中",
|
||||
"Submit and complete": "提出して完了"
|
||||
},
|
||||
"forget": {
|
||||
|
@@ -121,7 +121,7 @@
|
||||
"Please input your phone verification code!": "Please input your phone verification code!",
|
||||
"Please input your verification code!": "Please input your verification code!",
|
||||
"Send Code": "Send Code",
|
||||
"Sending Code": "Sending Code",
|
||||
"Sending": "Sending",
|
||||
"Submit and complete": "Submit and complete"
|
||||
},
|
||||
"forget": {
|
||||
|
@@ -121,7 +121,7 @@
|
||||
"Please input your phone verification code!": "Пожалуйста, введите код подтверждения!",
|
||||
"Please input your verification code!": "Пожалуйста, введите код подтверждения!",
|
||||
"Send Code": "Отправить код",
|
||||
"Sending Code": "Отправка кода",
|
||||
"Sending": "Отправка кода",
|
||||
"Submit and complete": "Отправить и завершить"
|
||||
},
|
||||
"forget": {
|
||||
|
@@ -121,7 +121,7 @@
|
||||
"Please input your phone verification code!": "Please input your phone verification code!",
|
||||
"Please input your verification code!": "Please input your verification code!",
|
||||
"Send Code": "Send Code",
|
||||
"Sending Code": "Sending Code",
|
||||
"Sending": "Sending",
|
||||
"Submit and complete": "Submit and complete"
|
||||
},
|
||||
"forget": {
|
||||
|
@@ -121,7 +121,7 @@
|
||||
"Please input your phone verification code!": "请输入您的手机验证码!",
|
||||
"Please input your verification code!": "请输入您的验证码!",
|
||||
"Send Code": "发送验证码",
|
||||
"Sending Code": "发送中",
|
||||
"Sending": "发送中",
|
||||
"Submit and complete": "完成提交"
|
||||
},
|
||||
"forget": {
|
||||
|
Reference in New Issue
Block a user