Compare commits

...

21 Commits

Author SHA1 Message Date
wht
dd939b5c7e feat: improve tooltips and i18n for frontend (#1645)
* feat: Add english tooltips

* fix: add untranslated tooltips
2023-03-16 14:53:58 +08:00
Gucheng Wang
eeba21bf0d Support acme-challenge 2023-03-16 00:25:03 +08:00
Gucheng Wang
5e47406e09 Enable CORS for "OPTIONS" request 2023-03-16 00:24:18 +08:00
Yaodong Yu
fd883a3211 feat: improve verification error translation (#1660) 2023-03-15 23:44:38 +08:00
mos
312412ffe4 fix: disable cookie for static files (#1656)
Co-authored-by: chenjpu <bing.chen@hgcitech.com>
2023-03-15 22:09:10 +08:00
Yaodong Yu
295a69c5f7 feat: support LDAP with SSL/TLS enabled (#1655) 2023-03-15 11:12:31 +08:00
Yaodong Yu
a8a8f39963 feat: use GetUserCount to optimize login performance (#1653) 2023-03-14 14:38:39 +08:00
Yaodong Yu
90f8eba02d feat: can send Aliyun test SMS now (#1651) 2023-03-13 17:48:58 +08:00
Yaodong Yu
2cca1c9136 feat: refactor LDAP backend code and improve frontend operation (#1640)
* refactor: simplify ldap backend code and improve frontend operation

* chore: add skipCi tag in sync_test.go

* fix: ui
2023-03-12 11:12:51 +08:00
Gucheng Wang
c2eebd61a1 Add TestStartSyncJob() 2023-03-12 05:38:39 +08:00
Gucheng Wang
59566f61d7 Refactor sync code 2023-03-12 05:10:23 +08:00
Gucheng Wang
7e4c9c91cd improve sending text 2023-03-10 22:35:47 +08:00
Gucheng Wang
430ee616db fix user list shows all users bug 2023-03-10 21:59:57 +08:00
aiden
2e3a323528 feat: Dingtalk provider supports fetching organization email (#1636)
* feat(dingtalk): try to get email from corp app

* chore: format codes

* chore: format codes (#1)

* Delete .fleet directory

* fix: fix syntax errors

* Update dingtalk.go

* style: fmt codes with gofumpt

---------

Co-authored-by: aidenlu <aiden_lu@wochacha.com>
2023-03-10 21:47:54 +08:00
Gucheng Wang
09e8408a3d Fix Popconfirm text 2023-03-10 19:17:53 +08:00
陈温鹏
2998bbf4b9 fix: Put Popconfirm into a React component. (#1638)
* add "Sure to delete" to i18n(#1569)

* fix: add sure to delete to i18n

* fix: Put Popconfirm into a React component.
2023-03-10 19:16:08 +08:00
imp2002
404382f2e0 feat: fix incompatibility css inset when Safari version <=14.1 (#1635) 2023-03-09 22:01:39 +08:00
Gucheng Wang
71db1f62a9 Fix DingTalk oauth link 2023-03-09 21:11:16 +08:00
Gucheng Wang
07dc6bf7cd Refactor sysinfo page 2023-03-09 17:17:12 +08:00
longxu0509
2de3f6772d fix: add aheadCnt in sysinfo (#1632)
* feat: add sync module to bi-sync mysql

* feat: fix the delay problem

* feat: fix go mod

* feat: fix the varchar(100) parse error

* fix: fix go.mod space inconsistency

* fix: fix go.mod space inconsistency

* fix: use sql builder instead of concatenation

* fix: remove serverId

* fix: fix file is not `gofumpt`-ed (gofumpt) error

* feat: add mysql bi-sync

* feat: fix some data inconsistency problems

* feat: add function atuo get server uuid

* fix: encapsulate the struct to optimize the code

* fix: fix incorrect Casdoor version in system info page

* fix: fix incorrect root path

* Update sysytem_test.go

* feat: add aheadCnt means that the commit is ahead of version several times

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-03-09 16:07:13 +08:00
longxu0509
3f623570fd feat: fix incorrect Casdoor version in system info page (#1631)
* feat: add sync module to bi-sync mysql

* feat: fix the delay problem

* feat: fix go mod

* feat: fix the varchar(100) parse error

* fix: fix go.mod space inconsistency

* fix: fix go.mod space inconsistency

* fix: use sql builder instead of concatenation

* fix: remove serverId

* fix: fix file is not `gofumpt`-ed (gofumpt) error

* feat: add mysql bi-sync

* feat: fix some data inconsistency problems

* feat: add function atuo get server uuid

* fix: encapsulate the struct to optimize the code

* fix: fix incorrect Casdoor version in system info page

* fix: fix incorrect root path

* Update sysytem_test.go

---------

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2023-03-09 13:28:23 +08:00
75 changed files with 1103 additions and 825 deletions

View File

@@ -129,8 +129,8 @@ func (c *ApiController) Signup() {
if application.IsSignupItemVisible("Email") && application.GetSignupItemRule("Email") != "No verification" && form.Email != "" {
checkResult := object.CheckVerificationCode(form.Email, form.EmailCode, c.GetAcceptLanguage())
if len(checkResult) != 0 {
c.ResponseError(c.T("account:Email: %s"), checkResult)
if checkResult.Code != object.VerificationSuccess {
c.ResponseError(checkResult.Msg)
return
}
}
@@ -139,8 +139,8 @@ func (c *ApiController) Signup() {
if application.IsSignupItemVisible("Phone") && form.Phone != "" {
checkPhone, _ = util.GetE164Number(form.Phone, form.CountryCode)
checkResult := object.CheckVerificationCode(checkPhone, form.PhoneCode, c.GetAcceptLanguage())
if len(checkResult) != 0 {
c.ResponseError(c.T("account:Phone: %s"), checkResult)
if checkResult.Code != object.VerificationSuccess {
c.ResponseError(checkResult.Msg)
return
}
}

View File

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

View File

@@ -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,21 +36,17 @@ 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)
conn, err := ldapServer.GetLdapConn()
if err != nil {
c.ResponseError(err.Error())
return
@@ -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)
}

View File

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

View File

@@ -224,8 +224,8 @@ func (c *ApiController) ResetEmailOrPhone() {
return
}
}
if msg := object.CheckVerificationCode(checkDest, code, c.GetAcceptLanguage()); len(msg) != 0 {
c.ResponseError(msg)
if result := object.CheckVerificationCode(checkDest, code, c.GetAcceptLanguage()); result.Code != object.VerificationSuccess {
c.ResponseError(result.Msg)
return
}

2
go.mod
View File

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

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

View File

@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build !skipCi
// +build !skipCi
package i18n
import (

View File

@@ -1,9 +1,7 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
@@ -137,6 +135,7 @@
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"Unknown type": "Unknown type",
"Wrong parameter": "Wrong parameter",
"Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},

View File

@@ -1,9 +1,7 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
@@ -137,6 +135,7 @@
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"Unknown type": "Unknown type",
"Wrong parameter": "Wrong parameter",
"Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},

View File

@@ -1,9 +1,7 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
@@ -137,6 +135,7 @@
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"Unknown type": "Unknown type",
"Wrong parameter": "Wrong parameter",
"Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},

View File

@@ -1,9 +1,7 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
@@ -137,6 +135,7 @@
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"Unknown type": "Unknown type",
"Wrong parameter": "Wrong parameter",
"Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},

View File

@@ -1,9 +1,7 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
@@ -137,6 +135,7 @@
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"Unknown type": "Unknown type",
"Wrong parameter": "Wrong parameter",
"Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},

View File

@@ -1,9 +1,7 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
@@ -137,6 +135,7 @@
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"Unknown type": "Unknown type",
"Wrong parameter": "Wrong parameter",
"Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},

View File

@@ -1,9 +1,7 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
@@ -137,6 +135,7 @@
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"Unknown type": "Unknown type",
"Wrong parameter": "Wrong parameter",
"Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},

View File

@@ -1,9 +1,7 @@
{
"account": {
"Email: %s": "Email: %s",
"Get init score failed, error: %w": "Get init score failed, error: %w",
"Invalid information": "Invalid information",
"Phone: %s": "Phone: %s",
"Please sign out first before signing in": "Please sign out first before signing in",
"Please sign out first before signing up": "Please sign out first before signing up",
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
@@ -137,6 +135,7 @@
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"Unknown type": "Unknown type",
"Wrong parameter": "Wrong parameter",
"Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "You should verify your code in %d min!",
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
},

View File

@@ -1,9 +1,7 @@
{
"account": {
"Email: %s": "邮件: %s",
"Get init score failed, error: %w": "初始化分数失败: %w",
"Invalid information": "无效信息",
"Phone: %s": "手机号: %s",
"Please sign out first before signing in": "请在登录前先退出登录",
"Please sign out first before signing up": "请在注册前先退出登录",
"The application does not allow to sign up new account": "该应用不允许注册新用户"
@@ -137,6 +135,7 @@
"Unable to get the phone modify rule.": "无法获取手机号修改规则",
"Unknown type": "未知类型",
"Wrong parameter": "参数错误",
"Wrong verification code!": "验证码错误!",
"You should verify your code in %d min!": "请在 %d 分钟内输入正确验证码",
"the user does not exist, please sign up first": "用户不存在,请先注册"
},

View File

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

View File

@@ -55,7 +55,7 @@ func main() {
beego.SetStaticPath("/swagger", "swagger")
beego.SetStaticPath("/files", "files")
// https://studygolang.com/articles/2303
beego.InsertFilter("*", beego.BeforeRouter, routers.StaticFilter)
beego.InsertFilter("*", beego.BeforeStatic, routers.StaticFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.AutoSigninFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.CorsFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.AuthzFilter)

View File

@@ -84,15 +84,13 @@ func CheckUserSignup(application *Application, organization *Organization, usern
if email == "" {
if application.IsSignupItemRequired("Email") {
return i18n.Translate(lang, "check:Email cannot be empty")
} else {
return ""
}
}
if HasUserByField(organization.Name, "email", email) {
return i18n.Translate(lang, "check:Email already exists")
} else if !util.IsEmailValid(email) {
return i18n.Translate(lang, "check:Email is invalid")
} else {
if HasUserByField(organization.Name, "email", email) {
return i18n.Translate(lang, "check:Email already exists")
} else if !util.IsEmailValid(email) {
return i18n.Translate(lang, "check:Email is invalid")
}
}
}
@@ -100,17 +98,15 @@ func CheckUserSignup(application *Application, organization *Organization, usern
if phone == "" {
if application.IsSignupItemRequired("Phone") {
return i18n.Translate(lang, "check:Phone cannot be empty")
} else {
return ""
}
}
if HasUserByField(organization.Name, "phone", phone) {
return i18n.Translate(lang, "check:Phone already exists")
} else if !util.IsPhoneAllowInRegin(countryCode, organization.CountryCodes) {
return i18n.Translate(lang, "check:Your region is not allow to signup by phone")
} else if !util.IsPhoneValid(phone, countryCode) {
return i18n.Translate(lang, "check:Phone number is invalid")
} else {
if HasUserByField(organization.Name, "phone", phone) {
return i18n.Translate(lang, "check:Phone already exists")
} else if !util.IsPhoneAllowInRegin(countryCode, organization.CountryCodes) {
return i18n.Translate(lang, "check:Your region is not allow to signup by phone")
} else if !util.IsPhoneValid(phone, countryCode) {
return i18n.Translate(lang, "check:Phone number is invalid")
}
}
}
@@ -196,7 +192,7 @@ func checkLdapUserPassword(user *User, password string, lang string) (*User, str
ldaps := GetLdaps(user.Owner)
ldapLoginSuccess := false
for _, ldapServer := range ldaps {
conn, err := GetLdapConn(ldapServer.Host, ldapServer.Port, ldapServer.Admin, ldapServer.Passwd)
conn, err := ldapServer.GetLdapConn()
if err != nil {
continue
}

View File

@@ -33,6 +33,7 @@ type Ldap struct {
ServerName string `xorm:"varchar(100)" json:"serverName"`
Host string `xorm:"varchar(100)" json:"host"`
Port int `json:"port"`
EnableSsl bool `xorm:"bool" json:"enableSsl"`
Admin string `xorm:"varchar(100)" json:"admin"`
Passwd string `xorm:"varchar(100)" json:"passwd"`
BaseDn string `xorm:"varchar(100)" json:"baseDn"`
@@ -152,20 +153,26 @@ func isMicrosoftAD(Conn *goldap.Conn) (bool, error) {
return isMicrosoft, err
}
func GetLdapConn(host string, port int, adminUser string, adminPasswd string) (*ldapConn, error) {
conn, err := goldap.Dial("tcp", fmt.Sprintf("%s:%d", host, port))
func (ldap *Ldap) GetLdapConn() (c *ldapConn, err error) {
var conn *goldap.Conn
if ldap.EnableSsl {
conn, err = goldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ldap.Host, ldap.Port), nil)
} else {
conn, err = goldap.Dial("tcp", fmt.Sprintf("%s:%d", ldap.Host, ldap.Port))
}
if err != nil {
return nil, err
}
err = conn.Bind(adminUser, adminPasswd)
err = conn.Bind(ldap.Admin, ldap.Passwd)
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
}
@@ -352,7 +359,7 @@ func UpdateLdap(ldap *Ldap) bool {
}
affected, err := adapter.Engine.ID(ldap.Id).Cols("owner", "server_name", "host",
"port", "admin", "passwd", "base_dn", "auto_sync").Update(ldap)
"port", "enable_ssl", "admin", "passwd", "base_dn", "auto_sync").Update(ldap)
if err != nil {
panic(err)
}

View File

@@ -76,7 +76,7 @@ func (l *LdapAutoSynchronizer) syncRoutine(ldap *Ldap, stopChan chan struct{}) {
UpdateLdapSyncTime(ldap.Id)
// fetch all users
conn, err := GetLdapConn(ldap.Host, ldap.Port, ldap.Admin, ldap.Passwd)
conn, err := ldap.GetLdapConn()
if err != nil {
logs.Warning(fmt.Sprintf("autoSync failed for %s, error %s", ldap.Id, err))
continue

View File

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

View File

@@ -26,8 +26,16 @@ import (
"github.com/xorm-io/core"
)
type VerifyResult struct {
Code int
Msg string
}
const (
wrongCode = "wrongCode"
VerificationSuccess int = 0
wrongCodeError = 1
noRecordError = 2
timeoutError = 3
)
type VerificationRecord struct {
@@ -150,11 +158,11 @@ func getVerificationRecord(dest string) *VerificationRecord {
return &record
}
func CheckVerificationCode(dest, code, lang string) string {
func CheckVerificationCode(dest, code, lang string) *VerifyResult {
record := getVerificationRecord(dest)
if record == nil {
return i18n.Translate(lang, "verification:Code has not been sent yet!")
return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:Code has not been sent yet!")}
}
timeout, err := conf.GetConfigInt64("verificationCodeTimeout")
@@ -164,14 +172,14 @@ func CheckVerificationCode(dest, code, lang string) string {
now := time.Now().Unix()
if now-record.Time > timeout*60 {
return fmt.Sprintf(i18n.Translate(lang, "verification:You should verify your code in %d min!"), timeout)
return &VerifyResult{timeoutError, fmt.Sprintf(i18n.Translate(lang, "verification:You should verify your code in %d min!"), timeout)}
}
if record.Code != code {
return wrongCode
return &VerifyResult{wrongCodeError, i18n.Translate(lang, "verification:Wrong verification code!")}
}
return ""
return &VerifyResult{VerificationSuccess, ""}
}
func DisableVerificationCode(dest string) {
@@ -194,14 +202,14 @@ func CheckSigninCode(user *User, dest, code, lang string) string {
}
result := CheckVerificationCode(dest, code, lang)
switch result {
case "":
switch result.Code {
case VerificationSuccess:
resetUserSigninErrorTimes(user)
return ""
case wrongCode:
case wrongCodeError:
return recordSigninErrorInfo(user, lang)
default:
return result
return result.Msg
}
}

View File

@@ -36,7 +36,7 @@ func CorsFilter(ctx *context.Context) {
if origin != "" && originConf != "" && origin != originConf {
if object.IsOriginAllowed(origin) {
ctx.Output.Header(headerAllowOrigin, origin)
ctx.Output.Header(headerAllowMethods, "POST, GET, OPTIONS")
ctx.Output.Header(headerAllowMethods, "POST, GET, OPTIONS, DELETE")
ctx.Output.Header(headerAllowHeaders, "Content-Type, Authorization")
} else {
ctx.ResponseWriter.WriteHeader(http.StatusForbidden)
@@ -48,4 +48,11 @@ func CorsFilter(ctx *context.Context) {
return
}
}
if ctx.Input.Method() == "OPTIONS" {
ctx.Output.Header(headerAllowOrigin, "*")
ctx.Output.Header(headerAllowMethods, "POST, GET, OPTIONS, DELETE")
ctx.ResponseWriter.WriteHeader(http.StatusOK)
return
}
}

View File

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

View File

@@ -18,6 +18,7 @@ import (
"net/http"
"os"
"strings"
"time"
"github.com/beego/beego/context"
"github.com/casdoor/casdoor/conf"
@@ -31,6 +32,11 @@ var (
func StaticFilter(ctx *context.Context) {
urlPath := ctx.Request.URL.Path
if urlPath == "/.well-known/acme-challenge/filename" {
http.ServeContent(ctx.ResponseWriter, ctx.Request, "acme-challenge", time.Now(), strings.NewReader("content"))
}
if strings.HasPrefix(urlPath, "/api/") || strings.HasPrefix(urlPath, "/.well-known/") {
return
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -13,7 +13,7 @@
// limitations under the License.
import React from "react";
import {Button, Card, Col, Input, InputNumber, Row, Select} from "antd";
import {Button, Card, Col, Input, InputNumber, Row, Select, Switch} from "antd";
import {EyeInvisibleOutlined, EyeTwoTone} from "@ant-design/icons";
import * as LddpBackend from "./backend/LdapBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend";
@@ -83,7 +83,12 @@ class LdapEditPage extends React.Component {
<Card size="small" title={
<div>
{i18next.t("ldap:Edit LDAP")}&nbsp;&nbsp;&nbsp;&nbsp;
<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"}}>
@@ -141,6 +146,16 @@ class LdapEditPage extends React.Component {
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}>
{Setting.getLabel(i18next.t("ldap:Enable SSL"), i18next.t("ldap:Enable SSL - Tooltip"))} :
</Col>
<Col span={21} >
<Switch checked={this.state.ldap.enableSsl} onChange={checked => {
this.updateLdapField("enableSsl", checked);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}}>
<Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}>
{Setting.getLabel(i18next.t("ldap:Base DN"), i18next.t("ldap:Base DN - Tooltip"))} :
@@ -190,14 +205,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 +229,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>
);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -370,7 +370,7 @@ class SignupPage extends React.Component {
} else if (signupItem.name === "Phone") {
return (
<React.Fragment>
<Form.Item label={i18next.t("general:Phone")} required>
<Form.Item label={i18next.t("general:Phone")} required={required}>
<Input.Group compact>
<Form.Item
name="countryCode"
@@ -398,6 +398,10 @@ class SignupPage extends React.Component {
},
({getFieldValue}) => ({
validator: (_, value) => {
if (!required && value === "") {
return Promise.resolve();
}
if (value !== "" && !Setting.isValidPhone(value, getFieldValue("countryCode"))) {
this.setState({validPhone: false});
return Promise.reject(i18next.t("signup:The input is not valid Phone!"));

View File

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

View File

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

View File

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

View File

@@ -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": {
@@ -288,6 +288,8 @@
"CN": "KN",
"Edit LDAP": "LDAP bearbeiten",
"Email": "E-Mail",
"Enable SSL": "Enable SSL",
"Enable SSL - Tooltip": "Enable SSL - Tooltip",
"Group Id": "Gruppen Id",
"ID": "ID",
"Last Sync": "Letzter Sync",

View File

@@ -9,7 +9,7 @@
"Failed to sync policies": "Failed to sync policies",
"New Adapter": "New Adapter",
"Policies": "Policies",
"Policies - Tooltip": "Policies - Tooltip",
"Policies - Tooltip": "CURD to the policy rules",
"Repeated policy rules": "Repeated policy rules",
"Sync": "Sync",
"Sync policies successfully": "Sync policies successfully"
@@ -17,9 +17,9 @@
"application": {
"Always": "Always",
"Auto signin": "Auto signin",
"Auto signin - Tooltip": "Auto signin - Tooltip",
"Auto signin - Tooltip": "When a logged-in session exists in Casdoor, it is automatically used for application-side login",
"Background URL": "Background URL",
"Background URL - Tooltip": "Background URL - Tooltip",
"Background URL - Tooltip": "Link to the background image of the login page",
"Center": "Center",
"Copy SAML metadata URL": "Copy SAML metadata URL",
"Copy prompt page URL": "Copy prompt page URL",
@@ -27,90 +27,90 @@
"Copy signup page URL": "Copy signup page URL",
"Edit Application": "Edit Application",
"Enable SAML compress": "Enable SAML compress",
"Enable SAML compress - Tooltip": "Enable SAML compress - Tooltip",
"Enable SAML compress - Tooltip": "Whether to compress SAML response messages when Casdoor is used as SAML idp",
"Enable WebAuthn signin": "Enable WebAuthn signin",
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable WebAuthn signin - Tooltip": "Whether to allow login with cell phone or email verification code",
"Enable code signin": "Enable code signin",
"Enable code signin - Tooltip": "Enable code signin - Tooltip",
"Enable code signin - Tooltip": "Whether to allow login with cell phone or email verification code",
"Enable link accounts that with the same email": "Enable link accounts that with the same email",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email - Tooltip",
"Enable link accounts that with the same email - Tooltip": "Enable link accounts that with the same email",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signin session - Tooltip": "Whether Casdoor maintains a session after logging into Casdoor from the app",
"Enable signup": "Enable signup",
"Enable signup - Tooltip": "Enable signup - Tooltip",
"Enable signup - Tooltip": "Whether to allow users to register",
"Failed to log in": "Failed to log in",
"Failed to sign in": "Failed to sign in",
"File uploaded successfully": "File uploaded successfully",
"Follow organization theme": "Follow organization theme",
"Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "Form CSS - Tooltip",
"Form CSS - Tooltip": "CSS styling of forms (e.g. adding borders and shadows)",
"Form position": "Form position",
"Form position - Tooltip": "Form position - Tooltip",
"Form position - Tooltip": "Location of forms for registration, login, forgotten password, etc.",
"Grant types": "Grant types",
"Grant types - Tooltip": "Grant types - Tooltip",
"Grant types - Tooltip": "Select which Grant types are allowed in the OAuth protocol",
"Left": "Left",
"Logged in successfully": "Logged in successfully",
"Logged out successfully": "Logged out successfully",
"New Application": "New Application",
"None": "None",
"Password ON": "Password ON",
"Password ON - Tooltip": "Password ON - Tooltip",
"Password ON - Tooltip": "Whether to allow password login",
"Please input your application!": "Please input your application!",
"Please input your organization!": "Please input your organization!",
"Please select a HTML file": "Please select a HTML file",
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"Redirect URL": "Redirect URL",
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip",
"Redirect URL (Assertion Consumer Service POST Binding URL) - Tooltip": "Redirect URL (Assertion Consumer Service POST Binding URL)",
"Redirect URLs": "Redirect URLs",
"Redirect URLs - Tooltip": "Redirect URLs - Tooltip",
"Redirect URLs - Tooltip": "List of redirected addresses after successful login",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expire - Tooltip",
"Refresh token expire - Tooltip": "Refresh token expiration time",
"Right": "Right",
"Rule": "Rule",
"SAML Reply URL": "SAML Reply URL",
"SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata - Tooltip": "Metadata information of SAML protocol",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
"Side panel HTML": "Side panel HTML",
"Side panel HTML - Edit": "Side panel HTML - Edit",
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
"Side panel HTML - Tooltip": "Side panel HTML",
"Sign Up Error": "Sign Up Error",
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"Signin session": "Signin session",
"Signup items": "Signup items",
"Signup items - Tooltip": "Signup items - Tooltip",
"Signup items - Tooltip": "Items to be filled in when registering users",
"Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
"Token expire": "Token expire",
"Token expire - Tooltip": "Token expire - Tooltip",
"Token expire - Tooltip": "Access token expiration time",
"Token format": "Token format",
"Token format - Tooltip": "Token format - Tooltip",
"Token format - Tooltip": "Select access token format",
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
},
"cert": {
"Bit size": "Bit size",
"Bit size - Tooltip": "Bit size - Tooltip",
"Bit size - Tooltip": "Secret key length",
"Certificate": "Certificate",
"Certificate - Tooltip": "Certificate - Tooltip",
"Certificate - Tooltip": "The public key, configured on your server, is used to decrypt the signature",
"Certificate copied to clipboard successfully": "Certificate copied to clipboard successfully",
"Copy certificate": "Copy certificate",
"Copy private key": "Copy private key",
"Crypto algorithm": "Crypto algorithm",
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
"Crypto algorithm - Tooltip": "Encryption algorithm of the certificate",
"Download certificate": "Download certificate",
"Download private key": "Download private key",
"Edit Cert": "Edit Cert",
"Expire in years": "Expire in years",
"Expire in years - Tooltip": "Expire in years - Tooltip",
"Expire in years - Tooltip": "Expire in years",
"New Cert": "New Cert",
"Private key": "Private key",
"Private key - Tooltip": "Private key - Tooltip",
"Private key - Tooltip": "Private key for signature generation",
"Private key copied to clipboard successfully": "Private key copied to clipboard successfully",
"Scope": "Scope",
"Scope - Tooltip": "Scope - Tooltip",
"Scope - Tooltip": "Scope of the certificate",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip"
"Type - Tooltip": "Type of certificate"
},
"code": {
"Code You Received": "Code You Received",
@@ -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": {
@@ -140,120 +140,120 @@
"general": {
"Action": "Action",
"Adapter": "Adapter",
"Adapter - Tooltip": "Adapter - Tooltip",
"Adapter - Tooltip": "Table name of the policy store",
"Adapters": "Adapters",
"Add": "Add",
"Affiliation URL": "Affiliation URL",
"Affiliation URL - Tooltip": "Affiliation URL - Tooltip",
"Affiliation URL - Tooltip": "Affiliation URL",
"Application": "Application",
"Applications": "Applications",
"Applications that require authentication": "Applications that require authentication",
"Avatar": "Avatar",
"Avatar - Tooltip": "Avatar - Tooltip",
"Avatar - Tooltip": "Avatar to show to others",
"Back Home": "Back Home",
"Cancel": "Cancel",
"Captcha": "Captcha",
"Cert": "Cert",
"Cert - Tooltip": "Cert - Tooltip",
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
"Certs": "Certs",
"Click to Upload": "Click to Upload",
"Client IP": "Client IP",
"Close": "Close",
"Created time": "Created time",
"Default application": "Default application",
"Default application - Tooltip": "Default application - Tooltip",
"Default application - Tooltip": "Default application",
"Default avatar": "Default avatar",
"Default avatar - Tooltip": "Default avatar - Tooltip",
"Default avatar - Tooltip": "Default avatar",
"Delete": "Delete",
"Description": "Description",
"Description - Tooltip": "Description - Tooltip",
"Description - Tooltip": "Descriptive information related to this",
"Display name": "Display name",
"Display name - Tooltip": "Display name - Tooltip",
"Display name - Tooltip": "The name displayed, it can be repeated",
"Down": "Down",
"Edit": "Edit",
"Email": "Email",
"Email - Tooltip": "Email - Tooltip",
"Email - Tooltip": "Email address",
"Failed to add": "Failed to add",
"Failed to connect to server": "Failed to connect to server",
"Failed to delete": "Failed to delete",
"Failed to save": "Failed to save",
"Favicon": "Favicon",
"Favicon - Tooltip": "Favicon - Tooltip",
"Favicon - Tooltip": "Favicon icons for the website",
"First name": "First name",
"Forget URL": "Forget URL",
"Forget URL - Tooltip": "Forget URL - Tooltip",
"Forget URL - Tooltip": "Forgot password URL",
"Found some texts still not translated? Please help us translate at": "Found some texts still not translated? Please help us translate at",
"Go to writable demo site?": "Go to writable demo site?",
"Home": "Home",
"Home - Tooltip": "Home - Tooltip",
"Home - Tooltip": "Home page of the application",
"ID": "ID",
"ID - Tooltip": "ID - Tooltip",
"ID - Tooltip": "Unique random string",
"Is enabled": "Is enabled",
"Is enabled - Tooltip": "Is enabled - Tooltip",
"Is enabled - Tooltip": "Set whether it can use",
"LDAPs": "LDAPs",
"LDAPs - Tooltip": "LDAPs - Tooltip",
"LDAPs - Tooltip": "LDAP servers",
"Languages": "Languages",
"Languages - Tooltip": "Languages - Tooltip",
"Languages - Tooltip": "Available languages",
"Last name": "Last name",
"Logo": "Logo",
"Logo - Tooltip": "Logo - Tooltip",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"Master password": "Master password",
"Master password - Tooltip": "Master password - Tooltip",
"Master password - Tooltip": "Can be used to log in to all users under the organization and facilitate administrators to log in as that user to solve technical problems",
"Menu": "Menu",
"Method": "Method",
"Model": "Model",
"Model - Tooltip": "Model - Tooltip",
"Model - Tooltip": "Select one model belonging to the organization",
"Models": "Models",
"Name": "Name",
"Name - Tooltip": "Name - Tooltip",
"Name - Tooltip": "Unique, string-based ID",
"OAuth providers": "OAuth providers",
"Organization": "Organization",
"Organization - Tooltip": "Organization - Tooltip",
"Organization - Tooltip": "Select one from the organizations",
"Organizations": "Organizations",
"Password": "Password",
"Password - Tooltip": "Password - Tooltip",
"Password - Tooltip": "Make sure the password is correct",
"Password salt": "Password salt",
"Password salt - Tooltip": "Password salt - Tooltip",
"Password salt - Tooltip": "Random parameters for password encryption",
"Password type": "Password type",
"Password type - Tooltip": "Password type - Tooltip",
"Password type - Tooltip": "Form of password storage in the database",
"Payments": "Payments",
"Permissions": "Permissions",
"Permissions - Tooltip": "Permissions - Tooltip",
"Permissions - Tooltip": "Permissions owned",
"Phone": "Phone",
"Phone - Tooltip": "Phone - Tooltip",
"Phone - Tooltip": "Phone number",
"Preview": "Preview",
"Preview - Tooltip": "Preview - Tooltip",
"Preview - Tooltip": "Preview screen",
"Products": "Products",
"Provider": "Provider",
"Provider - Tooltip": "Provider - Tooltip",
"Provider - Tooltip": "Payment providers, such as paypel",
"Providers": "Providers",
"Providers - Tooltip": "Providers - Tooltip",
"Providers - Tooltip": "Configuration providers such as OAuth, SMS, storage",
"Real name": "Real name",
"Records": "Records",
"Request URI": "Request URI",
"Resources": "Resources",
"Roles": "Roles",
"Roles - Tooltip": "Roles - Tooltip",
"Roles - Tooltip": "Roles owned",
"Save": "Save",
"Save & Exit": "Save & Exit",
"Session ID": "Session ID",
"Sessions": "Sessions",
"Signin URL": "Signin URL",
"Signin URL - Tooltip": "Signin URL - Tooltip",
"Signin URL - Tooltip": "User's login address",
"Signup URL": "Signup URL",
"Signup URL - Tooltip": "Signup URL - Tooltip",
"Signup URL - Tooltip": "Registration address displayed to users",
"Signup application": "Signup application",
"Signup application - Tooltip": "Signup application - Tooltip",
"Signup application - Tooltip": "Which application the user registered through when registering",
"Sorry, the page you visited does not exist.": "Sorry, the page you visited does not exist.",
"Sorry, the user you visited does not exist or you are not authorized to access this user.": "Sorry, the user you visited does not exist or you are not authorized to access this user.",
"Sorry, you do not have permission to access this page or logged in status invalid.": "Sorry, you do not have permission to access this page or logged in status invalid.",
"State": "State",
"State - Tooltip": "State - Tooltip",
"State - Tooltip": "State",
"Successfully added": "Successfully added",
"Successfully deleted": "Successfully deleted",
"Successfully saved": "Successfully saved",
"Supported country codes": "Supported country codes",
"Supported country codes - Tooltip": "Supported country codes - Tooltip",
"Supported country codes - Tooltip": "Cell phone number prefix, used to distinguish between countries or regions",
"Sure to delete": "Sure to delete",
"Swagger": "Swagger",
"Sync": "Sync",
@@ -263,13 +263,13 @@
"Timestamp": "Timestamp",
"Tokens": "Tokens",
"URL": "URL",
"URL - Tooltip": "URL - Tooltip",
"URL - Tooltip": "URL Link",
"Up": "Up",
"User": "User",
"User - Tooltip": "User - Tooltip",
"User - Tooltip": "Make sure the username is spelled correctly",
"User containers": "User containers",
"User type": "User type",
"User type - Tooltip": "User type - Tooltip",
"User type - Tooltip": "User roles with different privileges",
"Users": "Users",
"Users under all organizations": "Users under all organizations",
"Webhooks": "Webhooks",
@@ -278,27 +278,29 @@
"ldap": {
"Address": "Address",
"Admin": "Admin",
"Admin - Tooltip": "Admin - Tooltip",
"Admin - Tooltip": "CN or ID of the LDAP server administrator",
"Admin Password": "Admin Password",
"Admin Password - Tooltip": "Admin Password - Tooltip",
"Admin Password - Tooltip": "LDAP server administrator password",
"Auto Sync": "Auto Sync",
"Auto Sync - Tooltip": "Auto Sync - Tooltip",
"Auto Sync - Tooltip": "Auto-sync configuration, disabled at 0",
"Base DN": "Base DN",
"Base DN - Tooltip": "Base DN - Tooltip",
"Base DN - Tooltip": "Base DN during LDAP search",
"CN": "CN",
"Edit LDAP": "Edit LDAP",
"Email": "Email",
"Enable SSL": "Enable SSL",
"Enable SSL - Tooltip": "Enable SSL - Tooltip",
"Group Id": "Group Id",
"ID": "ID",
"Last Sync": "Last Sync",
"Phone": "Phone",
"Server": "Server",
"Server Host": "Server Host",
"Server Host - Tooltip": "Server Host - Tooltip",
"Server Host - Tooltip": "LDAP server address",
"Server Name": "Server Name",
"Server Name - Tooltip": "Server Name - Tooltip",
"Server Name - Tooltip": "LDAP server configuration display name",
"Server Port": "Server Port",
"Server Port - Tooltip": "Server Port - Tooltip",
"Server Port - Tooltip": "LDAP server port",
"Sync": "Sync",
"The Auto Sync option will sync all users to specify organization": "The Auto Sync option will sync all users to specify organization",
"UidNumber / Uid": "UidNumber / Uid"
@@ -313,7 +315,7 @@
"No account?": "No account?",
"Or sign in with another account": "Or sign in with another account",
"Password": "Password",
"Password - Tooltip": "Password - Tooltip",
"Password - Tooltip": "Make sure the password is correct",
"Please input your Email or Phone!": "Please input your Email or Phone!",
"Please input your code!": "Please input your code!",
"Please input your password!": "Please input your password!",
@@ -334,108 +336,108 @@
"model": {
"Edit Model": "Edit Model",
"Model text": "Model text",
"Model text - Tooltip": "Model text - Tooltip",
"Model text - Tooltip": "Casbin Access Control Model",
"New Model": "New Model"
},
"organization": {
"Account items": "Account items",
"Account items - Tooltip": "Account items - Tooltip",
"Account items - Tooltip": "Items in the Personal settings page",
"Default avatar": "Default avatar",
"Edit Organization": "Edit Organization",
"Favicon": "Favicon",
"Follow global theme": "Follow global theme",
"InitScore": "InitScore",
"Is profile public": "Is profile public",
"Is profile public - Tooltip": "Is profile public - Tooltip",
"Is profile public - Tooltip": "When closed, only global administrators or users from the same organization can access the user home page",
"Modify rule": "Modify rule",
"New Organization": "New Organization",
"Soft deletion": "Soft deletion",
"Soft deletion - Tooltip": "Soft deletion - Tooltip",
"Soft deletion - Tooltip": "When enabled, deletion of user information will not be completely cleared in the database, but will be marked as deleted",
"Tags": "Tags",
"Tags - Tooltip": "Tags - Tooltip",
"The user's initScore - Tooltip": "The user's initScore - Tooltip",
"Tags - Tooltip": "Collection of user-selectable tags",
"The user's initScore - Tooltip": "User's initial points",
"View rule": "View rule",
"Visible": "Visible",
"Website URL": "Website URL",
"Website URL - Tooltip": "Website URL - Tooltip"
"Website URL - Tooltip": "Website URL"
},
"payment": {
"Confirm your invoice information": "Confirm your invoice information",
"Currency": "Currency",
"Currency - Tooltip": "Currency - Tooltip",
"Currency - Tooltip": "Payment Currency",
"Download Invoice": "Download Invoice",
"Edit Payment": "Edit Payment",
"Individual": "Individual",
"Invoice URL": "Invoice URL",
"Invoice URL - Tooltip": "Invoice URL - Tooltip",
"Invoice URL - Tooltip": "URL for downloading the invoice",
"Invoice actions": "Invoice actions",
"Invoice actions - Tooltip": "Invoice actions - Tooltip",
"Invoice actions - Tooltip": "Operation includes both invoicing and downloading invoices",
"Invoice remark": "Invoice remark",
"Invoice remark - Tooltip": "Invoice remark - Tooltip",
"Invoice remark - Tooltip": "Remarks no more than 50 words",
"Invoice tax ID": "Invoice tax ID",
"Invoice tax ID - Tooltip": "Invoice tax ID - Tooltip",
"Invoice tax ID - Tooltip": "When the invoicing type is unit, the unit taxpayer identification number must be entered; when the invoicing type is individual, it is not necessary to fill in",
"Invoice title": "Invoice title",
"Invoice title - Tooltip": "Invoice title - Tooltip",
"Invoice title - Tooltip": "When the invoicing type is unit, the name of the unit can be entered in the invoice payable; when the invoicing type is individual, the system automatically fills in the name of the contributor",
"Invoice type": "Invoice type",
"Invoice type - Tooltip": "Invoice type - Tooltip",
"Invoice type - Tooltip": "Invoicing type can be personal or unit",
"Issue Invoice": "Issue Invoice",
"Message": "Message",
"Message - Tooltip": "Message - Tooltip",
"Message - Tooltip": "Payment processing result message",
"New Payment": "New Payment",
"Organization": "Organization",
"Person Email": "Person Email",
"Person Email - Tooltip": "Person Email - Tooltip",
"Person Email - Tooltip": "Person Email",
"Person ID card": "Person ID card",
"Person ID card - Tooltip": "Person ID card - Tooltip",
"Person ID card - Tooltip": "ID number of the payer",
"Person name": "Person name",
"Person name - Tooltip": "Person name - Tooltip",
"Person name - Tooltip": "Name of the payer",
"Person phone": "Person phone",
"Person phone - Tooltip": "Person phone - Tooltip",
"Person phone - Tooltip": "The cell phone number of the payer",
"Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.": "Please carefully check your invoice information. Once the invoice is issued, it cannot be withdrawn or modified.",
"Please click the below button to return to the original website": "Please click the below button to return to the original website",
"Please pay the order first!": "Please pay the order first!",
"Price": "Price",
"Price - Tooltip": "Price - Tooltip",
"Price - Tooltip": "Price of payment",
"Processing...": "Processing...",
"Product": "Product",
"Product - Tooltip": "Product - Tooltip",
"Product - Tooltip": "Product Name",
"Result": "Result",
"Return to Website": "Return to Website",
"State": "State",
"State - Tooltip": "State - Tooltip",
"State - Tooltip": "Payment Status",
"The payment has failed": "The payment has failed",
"The payment is still under processing": "The payment is still under processing",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"Type - Tooltip": "Payment method when purchasing product",
"You have successfully completed the payment": "You have successfully completed the payment",
"please wait for a few seconds...": "please wait for a few seconds...",
"the current state is": "the current state is"
},
"permission": {
"Actions": "Actions",
"Actions - Tooltip": "Actions - Tooltip",
"Actions - Tooltip": "Authorized actions",
"Admin": "Admin",
"Allow": "Allow",
"Approve time": "Approve time",
"Approve time - Tooltip": "Approve time - Tooltip",
"Approve time - Tooltip": "The time when the authorization was approved",
"Approved": "Approved",
"Approver": "Approver",
"Approver - Tooltip": "Approver - Tooltip",
"Approver - Tooltip": "The person who approved the authorization",
"Deny": "Deny",
"Edit Permission": "Edit Permission",
"Effect": "Effect",
"Effect - Tooltip": "Effect - Tooltip",
"Effect - Tooltip": "Allow or reject",
"New Permission": "New Permission",
"Pending": "Pending",
"Read": "Read",
"Resource type": "Resource type",
"Resource type - Tooltip": "Resource type - Tooltip",
"Resource type - Tooltip": "Type of authorized resources",
"Resources": "Resources",
"Resources - Tooltip": "Resources - Tooltip",
"Resources - Tooltip": "Authorized resources",
"State": "State",
"State - Tooltip": "State - Tooltip",
"State - Tooltip": "The current statue of the authorization",
"Submitter": "Submitter",
"Submitter - Tooltip": "Submitter - Tooltip",
"Submitter - Tooltip": "The person applying for this authorization",
"TreeNode": "TreeNode",
"Write": "Write"
},
@@ -445,34 +447,34 @@
"Buy Product": "Buy Product",
"CNY": "CNY",
"Currency": "Currency",
"Currency - Tooltip": "Currency - Tooltip",
"Currency - Tooltip": "Currency accepted for the product",
"Description": "Description",
"Description - Tooltip": "Description - Tooltip",
"Description - Tooltip": "Product Description",
"Detail": "Detail",
"Detail - Tooltip": "Detail - Tooltip",
"Detail - Tooltip": "Product Details",
"Edit Product": "Edit Product",
"I have completed the payment": "I have completed the payment",
"Image": "Image",
"Image - Tooltip": "Image - Tooltip",
"Image - Tooltip": "Product Image",
"New Product": "New Product",
"Pay": "Pay",
"Payment providers": "Payment providers",
"Payment providers - Tooltip": "Payment providers - Tooltip",
"Payment providers - Tooltip": "Providers of payment services",
"Paypal": "Paypal",
"Placing order...": "Placing order...",
"Please provide your username in the remark": "Please provide your username in the remark",
"Please scan the QR code to pay": "Please scan the QR code to pay",
"Price": "Price",
"Price - Tooltip": "Price - Tooltip",
"Price - Tooltip": "Price of the product",
"Quantity": "Quantity",
"Quantity - Tooltip": "Quantity - Tooltip",
"Quantity - Tooltip": "Quantity of the product",
"Return URL": "Return URL",
"Return URL - Tooltip": "Return URL - Tooltip",
"Return URL - Tooltip": "The link that jumps after payment is complete",
"SKU": "SKU",
"Sold": "Sold",
"Sold - Tooltip": "Sold - Tooltip",
"Sold - Tooltip": "Number of this product sold",
"Tag": "Tag",
"Tag - Tooltip": "Tag - Tooltip",
"Tag - Tooltip": "Tag of the product",
"Test buy page..": "Test buy page..",
"There is no payment channel for this product.": "There is no payment channel for this product.",
"This product is currently not in sale.": "This product is currently not in sale.",
@@ -481,61 +483,61 @@
},
"provider": {
"Access key": "Access key",
"Access key - Tooltip": "Access key - Tooltip",
"Access key - Tooltip": "Access key",
"Agent ID": "Agent ID",
"Agent ID - Tooltip": "Agent ID - Tooltip",
"Agent ID - Tooltip": "Agent ID",
"App ID": "App ID",
"App ID - Tooltip": "App ID - Tooltip",
"App ID - Tooltip": "App ID",
"App key": "App key",
"App key - Tooltip": "App key - Tooltip",
"App key - Tooltip": "App key",
"App secret": "App secret",
"AppSecret - Tooltip": "AppSecret - Tooltip",
"AppSecret - Tooltip": "AppSecret",
"Auth URL": "Auth URL",
"Auth URL - Tooltip": "Auth URL - Tooltip",
"Auth URL - Tooltip": "Auth URL",
"Bucket": "Bucket",
"Bucket - Tooltip": "Bucket - Tooltip",
"Bucket - Tooltip": "Name of Bucket",
"Can not parse Metadata": "Can not parse Metadata",
"Can signin": "Can signin",
"Can signup": "Can signup",
"Can unlink": "Can unlink",
"Category": "Category",
"Category - Tooltip": "Category - Tooltip",
"Category - Tooltip": "Select a category",
"Channel No.": "Channel No.",
"Channel No. - Tooltip": "Channel No. - Tooltip",
"Channel No. - Tooltip": "Channel No.",
"Client ID": "Client ID",
"Client ID - Tooltip": "Client ID - Tooltip",
"Client ID - Tooltip": "Client ID",
"Client ID 2": "Client ID 2",
"Client ID 2 - Tooltip": "Client ID 2 - Tooltip",
"Client ID 2 - Tooltip": "The second Client ID",
"Client secret": "Client secret",
"Client secret - Tooltip": "Client secret - Tooltip",
"Client secret - Tooltip": "Client secret",
"Client secret 2": "Client secret 2",
"Client secret 2 - Tooltip": "Client secret 2 - Tooltip",
"Client secret 2 - Tooltip": "The second client secret key",
"Copy": "Copy",
"Disable SSL": "Disable SSL",
"Disable SSL - Tooltip": "Disable SSL - Tooltip",
"Disable SSL - Tooltip": "Whether to disable SSL security protocol when communicating with STMP server",
"Domain": "Domain",
"Domain - Tooltip": "Domain - Tooltip",
"Domain - Tooltip": "Storage node custom domain name",
"Edit Provider": "Edit Provider",
"Email content": "Email content",
"Email content - Tooltip": "Email content - Tooltip",
"Email content - Tooltip": "Content of the email",
"Email sent successfully": "Email sent successfully",
"Email title": "Email title",
"Email title - Tooltip": "Email title - Tooltip",
"Email title - Tooltip": "Title of the email",
"Enable QR code": "Enable QR code",
"Enable QR code - Tooltip": "Enable QR code - Tooltip",
"Enable QR code - Tooltip": "Enable scan code to login",
"Endpoint": "Endpoint",
"Endpoint (Intranet)": "Endpoint (Intranet)",
"Host": "Host",
"Host - Tooltip": "Host - Tooltip",
"Host - Tooltip": "Fill in the host address and make sure you can connect",
"IdP": "IdP",
"IdP certificate": "IdP certificate",
"Issuer URL": "Issuer URL",
"Issuer URL - Tooltip": "Issuer URL - Tooltip",
"Issuer URL - Tooltip": "Issuer URL",
"Link copied to clipboard successfully": "Link copied to clipboard successfully",
"Metadata": "Metadata",
"Metadata - Tooltip": "Metadata - Tooltip",
"Metadata - Tooltip": "Metadata",
"Method": "Method",
"Method - Tooltip": "Method - Tooltip",
"Method - Tooltip": "Login behavior, QR code or silent authorization login",
"Name": "Name",
"New Provider": "New Provider",
"Parse": "Parse",
@@ -543,10 +545,10 @@
"Path prefix": "Path prefix",
"Please use WeChat and scan the QR code to sign in": "Please use WeChat and scan the QR code to sign in",
"Port": "Port",
"Port - Tooltip": "Port - Tooltip",
"Port - Tooltip": "Make sure the port is open",
"Prompted": "Prompted",
"Provider URL": "Provider URL",
"Provider URL - Tooltip": "Provider URL - Tooltip",
"Provider URL - Tooltip": "Provider URL",
"Region ID": "Region ID",
"Region ID - Tooltip": "Region ID - Tooltip",
"Region endpoint for Internet": "Region endpoint for Internet",
@@ -554,51 +556,51 @@
"Required": "Required",
"SAML 2.0 Endpoint (HTTP)": "SAML 2.0 Endpoint (HTTP)",
"SMS Test": "SMS Test",
"SMS Test - Tooltip": "SMS Test - Tooltip",
"SMS Test - Tooltip": "Phone number for sending test SMS",
"SMS account": "SMS account",
"SMS account - Tooltip": "SMS account - Tooltip",
"SMS account - Tooltip": "SMS account",
"SMS sent successfully": "SMS sent successfully",
"SP ACS URL": "SP ACS URL",
"SP ACS URL - Tooltip": "SP ACS URL - Tooltip",
"SP ACS URL - Tooltip": "SP ACS URL",
"SP Entity ID": "SP Entity ID",
"Scene": "Scene",
"Scene - Tooltip": "Scene - Tooltip",
"Scene - Tooltip": "Scene",
"Scope": "Scope",
"Scope - Tooltip": "Scope - Tooltip",
"Scope - Tooltip": "Scope",
"Secret access key": "Secret access key",
"Secret key": "Secret key",
"Secret key - Tooltip": "Secret key - Tooltip",
"SecretAccessKey - Tooltip": "SecretAccessKey - Tooltip",
"Secret key - Tooltip": "Used by the server to call the verification code provider API for verification",
"SecretAccessKey - Tooltip": "SecretAccessKey",
"Send Testing Email": "Send Testing Email",
"Send Testing SMS": "Send Testing SMS",
"Sign Name": "Sign Name",
"Sign Name - Tooltip": "Sign Name - Tooltip",
"Sign Name - Tooltip": "Name of the signature to be used",
"Sign request": "Sign request",
"Sign request - Tooltip": "Sign request - Tooltip",
"Sign request - Tooltip": "Whether the request requires a signature",
"Signin HTML": "Signin HTML",
"Signin HTML - Edit": "Signin HTML - Edit",
"Signin HTML - Tooltip": "Signin HTML - Tooltip",
"Signin HTML - Tooltip": "Custom HTML for replacing the default login page style",
"Signup HTML": "Signup HTML",
"Signup HTML - Edit": "Signup HTML - Edit",
"Signup HTML - Tooltip": "Signup HTML - Tooltip",
"Signup HTML - Tooltip": "Custom HTML for replacing the default registration page style",
"Site key": "Site key",
"Site key - Tooltip": "Site key - Tooltip",
"Site key - Tooltip": "For front-end embedded pages",
"Sub type": "Sub type",
"Sub type - Tooltip": "Sub type - Tooltip",
"Sub type - Tooltip": "Sub type",
"Template Code": "Template Code",
"Template Code - Tooltip": "Template Code - Tooltip",
"Template Code - Tooltip": "The code of the template used",
"Terms of Use": "Terms of Use",
"Terms of Use - Tooltip": "Terms of Use - Tooltip",
"Terms of Use - Tooltip": "Terms of use to be followed by users",
"Test Connection": "Test Connection",
"Test Email": "Test Email",
"Test Email - Tooltip": "Test Email - Tooltip",
"The prefix path of the file - Tooltip": "The prefix path of the file - Tooltip",
"Test Email - Tooltip": "Address to accept test emails",
"The prefix path of the file - Tooltip": "The prefix path of the file",
"Token URL": "Token URL",
"Token URL - Tooltip": "Token URL - Tooltip",
"Token URL - Tooltip": "Token URL",
"Type": "Type",
"Type - Tooltip": "Type - Tooltip",
"Type - Tooltip": "Select a type",
"UserInfo URL": "UserInfo URL",
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
"UserInfo URL - Tooltip": "UserInfo URL",
"Visible": "Visible",
"admin (share)": "admin (share)",
"alertType": "alertType"
@@ -623,11 +625,11 @@
"Edit Role": "Edit Role",
"New Role": "New Role",
"Sub domains": "Sub domains",
"Sub domains - Tooltip": "Sub domains - Tooltip",
"Sub domains - Tooltip": "Sub domains included in the current role",
"Sub roles": "Sub roles",
"Sub roles - Tooltip": "Sub roles - Tooltip",
"Sub roles - Tooltip": "Sub-roles contained in the current role",
"Sub users": "Sub users",
"Sub users - Tooltip": "Sub users - Tooltip"
"Sub users - Tooltip": "Select the sub-users under the role"
},
"signup": {
"Accept": "Accept",
@@ -656,36 +658,36 @@
"The input is not valid Email!": "The input is not valid Email!",
"The input is not valid Phone!": "The input is not valid Phone!",
"Username": "Username",
"Username - Tooltip": "Username - Tooltip",
"Username - Tooltip": "Allowed characters include letters, numbers, underscores, and may not begin with a number",
"Your account has been created!": "Your account has been created!",
"Your confirmed password is inconsistent with the password!": "Your confirmed password is inconsistent with the password!",
"sign in now": "sign in now"
},
"syncer": {
"Affiliation table": "Affiliation table",
"Affiliation table - Tooltip": "Affiliation table - Tooltip",
"Affiliation table - Tooltip": "Database table name of the work unit",
"Avatar base URL": "Avatar base URL",
"Avatar base URL - Tooltip": "Avatar base URL - Tooltip",
"Avatar base URL - Tooltip": "Prefix of the avatar URL",
"Casdoor column": "Casdoor column",
"Column name": "Column name",
"Column type": "Column type",
"Database": "Database",
"Database - Tooltip": "Database - Tooltip",
"Database - Tooltip": "The original database name",
"Database type": "Database type",
"Database type - Tooltip": "Database type - Tooltip",
"Database type - Tooltip": "Database type, now support MySQL, PostgreSQL, SQL server, Oracle, SQLite 3",
"Edit Syncer": "Edit Syncer",
"Error text": "Error text",
"Error text - Tooltip": "Error text - Tooltip",
"Error text - Tooltip": "Error when synchronizer connects to database",
"Is hashed": "Is hashed",
"New Syncer": "New Syncer",
"Sync interval": "Sync interval",
"Sync interval - Tooltip": "Sync interval - Tooltip",
"Sync interval - Tooltip": "Unit in seconds",
"Table": "Table",
"Table - Tooltip": "Table - Tooltip",
"Table - Tooltip": "Name of table",
"Table columns": "Table columns",
"Table columns - Tooltip": "Table columns - Tooltip",
"Table columns - Tooltip": "Columns of the table that participate in data synchronization, columns that do not participate in synchronization do not need to be added",
"Table primary key": "Table primary key",
"Table primary key - Tooltip": "Table primary key - Tooltip"
"Table primary key - Tooltip": "Table primary key, such as id"
},
"system": {
"About Casdoor": "About Casdoor",
@@ -710,7 +712,7 @@
"Is compact": "Is compact",
"Primary color": "Primary color",
"Theme": "Theme",
"Theme - Tooltip": "Theme - Tooltip"
"Theme - Tooltip": "Select a theme for your application"
},
"token": {
"Access token": "Access token",
@@ -724,40 +726,40 @@
"user": {
"\" + destType + \" reset": "\" + destType + \" reset",
"3rd-party logins": "3rd-party logins",
"3rd-party logins - Tooltip": "3rd-party logins - Tooltip",
"3rd-party logins - Tooltip": "Login using a third-party application",
"Address": "Address",
"Address - Tooltip": "Address - Tooltip",
"Address - Tooltip": "Usual residence address",
"Affiliation": "Affiliation",
"Affiliation - Tooltip": "Affiliation - Tooltip",
"Affiliation - Tooltip": "Workplace, such as company name",
"Bio": "Bio",
"Bio - Tooltip": "Bio - Tooltip",
"Bio - Tooltip": "Self Introduction",
"Cancel": "Cancel",
"Captcha Verify Failed": "Captcha Verify Failed",
"Captcha Verify Success": "Captcha Verify Success",
"Code Sent": "Code Sent",
"Country code": "Country code",
"Country/Region": "Country/Region",
"Country/Region - Tooltip": "Country/Region - Tooltip",
"Country/Region - Tooltip": "Select Country/Region",
"Edit User": "Edit User",
"Email cannot be empty": "Email cannot be empty",
"Empty input!": "Empty input!",
"Homepage": "Homepage",
"Homepage - Tooltip": "Homepage - Tooltip",
"Homepage - Tooltip": "Link to personal homepage",
"ID card": "ID card",
"Input your email": "Input your email",
"Input your phone number": "Input your phone number",
"Is admin": "Is admin",
"Is admin - Tooltip": "Is admin - Tooltip",
"Is admin - Tooltip": "Is the application admin",
"Is deleted": "Is deleted",
"Is deleted - Tooltip": "Is deleted - Tooltip",
"Is deleted - Tooltip": "Is the account deleted",
"Is forbidden": "Is forbidden",
"Is forbidden - Tooltip": "Is forbidden - Tooltip",
"Is forbidden - Tooltip": "Is the account disabled",
"Is global admin": "Is global admin",
"Is global admin - Tooltip": "Is global admin - Tooltip",
"Is global admin - Tooltip": "Is the global application",
"Keys": "Keys",
"Link": "Link",
"Location": "Location",
"Location - Tooltip": "Location - Tooltip",
"Location - Tooltip": "City of residence",
"Managed accounts": "Managed accounts",
"Modify password...": "Modify password...",
"New Email": "New Email",
@@ -780,9 +782,9 @@
"Set new profile picture": "Set new profile picture",
"Set password...": "Set password...",
"Tag": "Tag",
"Tag - Tooltip": "Tag - Tooltip",
"Tag - Tooltip": "User's Tags",
"Title": "Title",
"Title - Tooltip": "Title - Tooltip",
"Title - Tooltip": "Position in the unit/company",
"Two passwords you typed do not match.": "Two passwords you typed do not match.",
"Unlink": "Unlink",
"Upload (.xlsx)": "Upload (.xlsx)",
@@ -793,20 +795,20 @@
},
"webhook": {
"Content type": "Content type",
"Content type - Tooltip": "Content type - Tooltip",
"Content type - Tooltip": "Content type",
"Edit Webhook": "Edit Webhook",
"Events": "Events",
"Events - Tooltip": "Events - Tooltip",
"Events - Tooltip": "Events",
"Headers": "Headers",
"Headers - Tooltip": "Headers - Tooltip",
"Headers - Tooltip": "HTTP protocol headers (key-value pairs)",
"Is user extended": "Is user extended",
"Is user extended - Tooltip": "Is user extended - Tooltip",
"Is user extended - Tooltip": "Add extendedUser to JSON to extend the user field",
"Method": "Method",
"Method - Tooltip": "Method - Tooltip",
"Method - Tooltip": "Http method",
"Name": "Name",
"New Webhook": "New Webhook",
"URL": "URL",
"URL - Tooltip": "URL - Tooltip",
"URL - Tooltip": "URL",
"Value": "Value"
}
}

View File

@@ -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": {
@@ -288,6 +288,8 @@
"CN": "CN",
"Edit LDAP": "Editar LDAP",
"Email": "Email",
"Enable SSL": "Enable SSL",
"Enable SSL - Tooltip": "Enable SSL - Tooltip",
"Group Id": "Group Id",
"ID": "ID",
"Last Sync": "Última Sincronización",

View File

@@ -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": {
@@ -288,6 +288,8 @@
"CN": "CN",
"Edit LDAP": "Modifier LDAP",
"Email": "Courriel",
"Enable SSL": "Enable SSL",
"Enable SSL - Tooltip": "Enable SSL - Tooltip",
"Group Id": "Identifiant du groupe",
"ID": "ID",
"Last Sync": "Dernière synchronisation",

View File

@@ -121,7 +121,7 @@
"Please input your phone verification code!": "電話番号を入力してください!",
"Please input your verification code!": "認証コードを入力してください!",
"Send Code": "コードを送信",
"Sending Code": "コードを送信中",
"Sending": "コードを送信中",
"Submit and complete": "提出して完了"
},
"forget": {
@@ -288,6 +288,8 @@
"CN": "CN",
"Edit LDAP": "LDAP を編集",
"Email": "Eメールアドレス",
"Enable SSL": "Enable SSL",
"Enable SSL - Tooltip": "Enable SSL - Tooltip",
"Group Id": "グループ ID",
"ID": "ID",
"Last Sync": "前回の同期",

View File

@@ -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": {
@@ -288,6 +288,8 @@
"CN": "CN",
"Edit LDAP": "Edit LDAP",
"Email": "Email",
"Enable SSL": "Enable SSL",
"Enable SSL - Tooltip": "Enable SSL - Tooltip",
"Group Id": "Group Id",
"ID": "ID",
"Last Sync": "Last Sync",

View File

@@ -121,7 +121,7 @@
"Please input your phone verification code!": "Пожалуйста, введите код подтверждения!",
"Please input your verification code!": "Пожалуйста, введите код подтверждения!",
"Send Code": "Отправить код",
"Sending Code": "Отправка кода",
"Sending": "Отправка кода",
"Submit and complete": "Отправить и завершить"
},
"forget": {
@@ -288,6 +288,8 @@
"CN": "КНР",
"Edit LDAP": "Редактировать LDAP",
"Email": "Почта",
"Enable SSL": "Enable SSL",
"Enable SSL - Tooltip": "Enable SSL - Tooltip",
"Group Id": "ID группы",
"ID": "ID",
"Last Sync": "Последняя синхронизация",

View File

@@ -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": {
@@ -288,6 +288,8 @@
"CN": "CN",
"Edit LDAP": "Edit LDAP",
"Email": "Email",
"Enable SSL": "Enable SSL",
"Enable SSL - Tooltip": "Enable SSL - Tooltip",
"Group Id": "Group Id",
"ID": "ID",
"Last Sync": "Last Sync",

View File

@@ -90,9 +90,9 @@
},
"cert": {
"Bit size": "位大小",
"Bit size - Tooltip": "位大小 - 工具提示",
"Bit size - Tooltip": "秘钥长度",
"Certificate": "证书",
"Certificate - Tooltip": "证书 - 工具提示",
"Certificate - Tooltip": "在你的服务器上配置的公钥,用于解密签名",
"Certificate copied to clipboard successfully": "证书已成功复制到剪贴板",
"Copy certificate": "复制证书",
"Copy private key": "复制私钥",
@@ -102,15 +102,15 @@
"Download private key": "下载私钥",
"Edit Cert": "编辑证书",
"Expire in years": "有效期(年)",
"Expire in years - Tooltip": "到期年份-工具提示",
"Expire in years - Tooltip": "到期年份",
"New Cert": "添加证书",
"Private key": "私钥",
"Private key - Tooltip": "私钥 - 工具提示",
"Private key - Tooltip": "私钥用于生成秘钥",
"Private key copied to clipboard successfully": "私钥已成功复制到剪贴板",
"Scope": "用途",
"Scope - Tooltip": "范围 - 工具提示",
"Scope - Tooltip": "证书作用范围",
"Type": "类型",
"Type - Tooltip": "类型 - 工具提示"
"Type - Tooltip": "证书的类型"
},
"code": {
"Code You Received": "验证码",
@@ -121,7 +121,7 @@
"Please input your phone verification code!": "请输入您的手机验证码!",
"Please input your verification code!": "请输入您的验证码!",
"Send Code": "发送验证码",
"Sending Code": "发送中",
"Sending": "发送中",
"Submit and complete": "完成提交"
},
"forget": {
@@ -253,7 +253,7 @@
"Successfully deleted": "删除成功",
"Successfully saved": "保存成功",
"Supported country codes": "支持的国家代码",
"Supported country codes - Tooltip": "支持发送短信的国家 - Tooltip",
"Supported country codes - Tooltip": "支持发送短信的国家",
"Sure to delete": "确定删除",
"Swagger": "API文档",
"Sync": "同步",
@@ -288,6 +288,8 @@
"CN": "CN",
"Edit LDAP": "编辑LDAP",
"Email": "电子邮件",
"Enable SSL": "启用 SSL",
"Enable SSL - Tooltip": "启用 SSL",
"Group Id": "组ID",
"ID": "ID",
"Last Sync": "最近同步",