mirror of
https://github.com/casdoor/casdoor.git
synced 2025-08-05 12:28:43 +08:00
Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
29f1ec08a2 | ||
![]() |
389744a27d | ||
![]() |
dc7b66822d | ||
![]() |
efacf8226c | ||
![]() |
6beb68dcce | ||
![]() |
c9b990a319 | ||
![]() |
eedcde3aa5 | ||
![]() |
950a274b23 | ||
![]() |
478bd05db4 |
17
.github/workflows/build.yml
vendored
17
.github/workflows/build.yml
vendored
@@ -97,21 +97,20 @@ jobs:
|
|||||||
- uses: actions/setup-node@v2
|
- uses: actions/setup-node@v2
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
|
- name: back start
|
||||||
|
run: nohup go run ./main.go &
|
||||||
|
working-directory: ./
|
||||||
- name: front install
|
- name: front install
|
||||||
run: yarn install
|
run: yarn install
|
||||||
working-directory: ./web
|
working-directory: ./web
|
||||||
- name: front start
|
- name: front start
|
||||||
run: nohup yarn start &
|
run: nohup yarn start &
|
||||||
working-directory: ./web
|
working-directory: ./web
|
||||||
- name: back start
|
- uses: cypress-io/github-action@v4
|
||||||
run: nohup go run ./main.go &
|
with:
|
||||||
working-directory: ./
|
working-directory: ./web
|
||||||
- name: Sleep for starting
|
wait-on: 'http://localhost:7001'
|
||||||
run: sleep 90s
|
wait-on-timeout: 180
|
||||||
shell: bash
|
|
||||||
- name: e2e
|
|
||||||
run: npx cypress run --spec "**/e2e/**.cy.js"
|
|
||||||
working-directory: ./web
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@v3
|
||||||
if: failure()
|
if: failure()
|
||||||
|
@@ -255,7 +255,9 @@ func (c *ApiController) Logout() {
|
|||||||
if accessToken == "" && redirectUri == "" {
|
if accessToken == "" && redirectUri == "" {
|
||||||
c.ClearUserSession()
|
c.ClearUserSession()
|
||||||
// TODO https://github.com/casdoor/casdoor/pull/1494#discussion_r1095675265
|
// TODO https://github.com/casdoor/casdoor/pull/1494#discussion_r1095675265
|
||||||
object.DeleteSessionId(util.GetSessionId(object.CasdoorOrganization, object.CasdoorApplication, user), c.Ctx.Input.CruSession.SessionID())
|
owner, username := util.GetOwnerAndNameFromId(user)
|
||||||
|
|
||||||
|
object.DeleteSessionId(util.GetSessionId(owner, username, object.CasdoorApplication), c.Ctx.Input.CruSession.SessionID())
|
||||||
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
|
util.LogInfo(c.Ctx, "API: [%s] logged out", user)
|
||||||
|
|
||||||
application := c.GetSessionApplication()
|
application := c.GetSessionApplication()
|
||||||
|
@@ -127,7 +127,7 @@ func (c *ApiController) DeleteSession() {
|
|||||||
// @Param id query string true "The id(organization/application/user) of session"
|
// @Param id query string true "The id(organization/application/user) of session"
|
||||||
// @Param sessionId query string true "sessionId to be checked"
|
// @Param sessionId query string true "sessionId to be checked"
|
||||||
// @Success 200 {array} string The Response object
|
// @Success 200 {array} string The Response object
|
||||||
// @router /is-user-session-duplicated [get]
|
// @router /is-session-duplicated [get]
|
||||||
func (c *ApiController) IsSessionDuplicated() {
|
func (c *ApiController) IsSessionDuplicated() {
|
||||||
id := c.Input().Get("sessionPkId")
|
id := c.Input().Get("sessionPkId")
|
||||||
sessionId := c.Input().Get("sessionId")
|
sessionId := c.Input().Get("sessionId")
|
||||||
|
@@ -49,19 +49,27 @@ func (*Migrator_1_240_0_PR_1539) DoMigration() *migrate.Migration {
|
|||||||
SessionId []string `json:"sessionId"`
|
SessionId []string `json:"sessionId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
|
||||||
tx := engine.NewSession()
|
tx := engine.NewSession()
|
||||||
|
|
||||||
defer tx.Close()
|
defer tx.Close()
|
||||||
|
|
||||||
tx.Begin()
|
err := tx.Begin()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
tx.Table("session_tmp").CreateTable(&Session{})
|
err = tx.Table("session_tmp").CreateTable(&Session{})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
oldSessions := []*oldSession{}
|
oldSessions := []*oldSession{}
|
||||||
newSessions := []*Session{}
|
newSessions := []*Session{}
|
||||||
|
|
||||||
tx.Table("session").Find(&oldSessions)
|
err = tx.Table("session").Find(&oldSessions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for _, oldSession := range oldSessions {
|
for _, oldSession := range oldSessions {
|
||||||
newApplication := "null"
|
newApplication := "null"
|
||||||
|
@@ -15,6 +15,8 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/beego/beego"
|
"github.com/beego/beego"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
@@ -72,20 +74,23 @@ func GetSessionCount(owner, field, value string) int {
|
|||||||
|
|
||||||
func GetSingleSession(id string) *Session {
|
func GetSingleSession(id string) *Session {
|
||||||
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
|
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
|
||||||
session := &Session{Owner: owner, Name: name, Application: application}
|
session := Session{Owner: owner, Name: name, Application: application}
|
||||||
_, err := adapter.Engine.ID(core.PK{owner, name, application}).Get(session)
|
get, err := adapter.Engine.Get(&session)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return session
|
if !get {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &session
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateSession(id string, session *Session) bool {
|
func UpdateSession(id string, session *Session) bool {
|
||||||
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
|
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
|
||||||
|
|
||||||
_, err := adapter.Engine.ID(core.PK{owner, name, application}).Get(session)
|
if GetSingleSession(id) == nil {
|
||||||
if err != nil {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,20 +102,23 @@ func UpdateSession(id string, session *Session) bool {
|
|||||||
return affected != 0
|
return affected != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddSession(session *Session) bool {
|
func removeExtraSessionIds(session *Session) {
|
||||||
owner, name, application := session.Owner, session.Name, session.Application
|
if len(session.SessionId) > 100 {
|
||||||
|
session.SessionId = session.SessionId[(len(session.SessionId) - 100):]
|
||||||
dbSession := &Session{Owner: owner, Name: name, Application: application}
|
|
||||||
get, err := adapter.Engine.ID(core.PK{owner, name, application}).Get(dbSession)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var affected int64
|
func AddSession(session *Session) bool {
|
||||||
var dbErr error
|
dbSession := GetSingleSession(session.GetId())
|
||||||
if !get {
|
if dbSession == nil {
|
||||||
session.CreatedTime = util.GetCurrentTime()
|
session.CreatedTime = util.GetCurrentTime()
|
||||||
affected, dbErr = adapter.Engine.Insert(session)
|
|
||||||
|
affected, err := adapter.Engine.Insert(session)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0
|
||||||
} else {
|
} else {
|
||||||
m := make(map[string]struct{})
|
m := make(map[string]struct{})
|
||||||
for _, v := range dbSession.SessionId {
|
for _, v := range dbSession.SessionId {
|
||||||
@@ -121,51 +129,46 @@ func AddSession(session *Session) bool {
|
|||||||
dbSession.SessionId = append(dbSession.SessionId, v)
|
dbSession.SessionId = append(dbSession.SessionId, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
affected, dbErr = adapter.Engine.ID(core.PK{owner, name, application}).Update(dbSession)
|
|
||||||
|
removeExtraSessionIds(dbSession)
|
||||||
|
|
||||||
|
return UpdateSession(dbSession.GetId(), dbSession)
|
||||||
}
|
}
|
||||||
if dbErr != nil {
|
|
||||||
panic(dbErr)
|
|
||||||
}
|
|
||||||
return affected != 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteSession(id string) bool {
|
func DeleteSession(id string) bool {
|
||||||
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
|
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
|
||||||
|
|
||||||
session := &Session{Owner: owner, Name: name, Application: application}
|
|
||||||
_, err := adapter.Engine.ID(core.PK{owner, name, application}).Get(session)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if owner == CasdoorOrganization && application == CasdoorApplication {
|
if owner == CasdoorOrganization && application == CasdoorApplication {
|
||||||
DeleteBeegoSession(session.SessionId)
|
session := GetSingleSession(id)
|
||||||
|
if session != nil {
|
||||||
|
DeleteBeegoSession(session.SessionId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := adapter.Engine.ID(core.PK{owner, name, application}).Delete(&Session{})
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, err := adapter.Engine.ID(core.PK{owner, name, application}).Delete(session)
|
|
||||||
return affected != 0
|
return affected != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteSessionId(id string, sessionId string) bool {
|
func DeleteSessionId(id string, sessionId string) bool {
|
||||||
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
|
session := GetSingleSession(id)
|
||||||
|
if session == nil {
|
||||||
session := &Session{Owner: owner, Name: name, Application: application}
|
|
||||||
_, err := adapter.Engine.ID(core.PK{owner, name, application}).Get(session)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
owner, _, application := util.GetOwnerAndNameAndOtherFromId(id)
|
||||||
if owner == CasdoorOrganization && application == CasdoorApplication {
|
if owner == CasdoorOrganization && application == CasdoorApplication {
|
||||||
DeleteBeegoSession([]string{sessionId})
|
DeleteBeegoSession([]string{sessionId})
|
||||||
}
|
}
|
||||||
session.SessionId = util.DeleteVal(session.SessionId, sessionId)
|
|
||||||
|
|
||||||
if len(session.SessionId) < 1 {
|
session.SessionId = util.DeleteVal(session.SessionId, sessionId)
|
||||||
affected, _ := adapter.Engine.ID(core.PK{owner, name, application}).Delete(session)
|
if len(session.SessionId) == 0 {
|
||||||
return affected != 0
|
return DeleteSession(id)
|
||||||
} else {
|
} else {
|
||||||
affected, _ := adapter.Engine.ID(core.PK{owner, name, application}).Update(session)
|
return UpdateSession(id, session)
|
||||||
return affected != 0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,11 +181,13 @@ func DeleteBeegoSession(sessionIds []string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (session *Session) GetId() string {
|
||||||
|
return fmt.Sprintf("%s/%s/%s", session.Owner, session.Name, session.Application)
|
||||||
|
}
|
||||||
|
|
||||||
func IsSessionDuplicated(id string, sessionId string) bool {
|
func IsSessionDuplicated(id string, sessionId string) bool {
|
||||||
owner, name, application := util.GetOwnerAndNameAndOtherFromId(id)
|
session := GetSingleSession(id)
|
||||||
session := &Session{Owner: owner, Name: name, Application: application}
|
if session == nil {
|
||||||
get, _ := adapter.Engine.ID(core.PK{owner, name, application}).Get(session)
|
|
||||||
if !get {
|
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
if len(session.SessionId) > 1 {
|
if len(session.SessionId) > 1 {
|
||||||
|
@@ -27,7 +27,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
hourMinutes = 60
|
hourSeconds = int(time.Hour / time.Second)
|
||||||
InvalidRequest = "invalid_request"
|
InvalidRequest = "invalid_request"
|
||||||
InvalidClient = "invalid_client"
|
InvalidClient = "invalid_client"
|
||||||
InvalidGrant = "invalid_grant"
|
InvalidGrant = "invalid_grant"
|
||||||
@@ -306,7 +306,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
|
|||||||
Code: util.GenerateClientId(),
|
Code: util.GenerateClientId(),
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
RefreshToken: refreshToken,
|
RefreshToken: refreshToken,
|
||||||
ExpiresIn: application.ExpireInHours * hourMinutes,
|
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||||
Scope: scope,
|
Scope: scope,
|
||||||
TokenType: "Bearer",
|
TokenType: "Bearer",
|
||||||
CodeChallenge: challenge,
|
CodeChallenge: challenge,
|
||||||
@@ -442,7 +442,7 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
|||||||
Code: util.GenerateClientId(),
|
Code: util.GenerateClientId(),
|
||||||
AccessToken: newAccessToken,
|
AccessToken: newAccessToken,
|
||||||
RefreshToken: newRefreshToken,
|
RefreshToken: newRefreshToken,
|
||||||
ExpiresIn: application.ExpireInHours * hourMinutes,
|
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||||
Scope: scope,
|
Scope: scope,
|
||||||
TokenType: "Bearer",
|
TokenType: "Bearer",
|
||||||
}
|
}
|
||||||
@@ -592,7 +592,7 @@ func GetPasswordToken(application *Application, username string, password string
|
|||||||
Code: util.GenerateClientId(),
|
Code: util.GenerateClientId(),
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
RefreshToken: refreshToken,
|
RefreshToken: refreshToken,
|
||||||
ExpiresIn: application.ExpireInHours * hourMinutes,
|
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||||
Scope: scope,
|
Scope: scope,
|
||||||
TokenType: "Bearer",
|
TokenType: "Bearer",
|
||||||
CodeIsUsed: true,
|
CodeIsUsed: true,
|
||||||
@@ -632,7 +632,7 @@ func GetClientCredentialsToken(application *Application, clientSecret string, sc
|
|||||||
User: nullUser.Name,
|
User: nullUser.Name,
|
||||||
Code: util.GenerateClientId(),
|
Code: util.GenerateClientId(),
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
ExpiresIn: application.ExpireInHours * hourMinutes,
|
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||||
Scope: scope,
|
Scope: scope,
|
||||||
TokenType: "Bearer",
|
TokenType: "Bearer",
|
||||||
CodeIsUsed: true,
|
CodeIsUsed: true,
|
||||||
@@ -659,7 +659,7 @@ func GetTokenByUser(application *Application, user *User, scope string, host str
|
|||||||
Code: util.GenerateClientId(),
|
Code: util.GenerateClientId(),
|
||||||
AccessToken: accessToken,
|
AccessToken: accessToken,
|
||||||
RefreshToken: refreshToken,
|
RefreshToken: refreshToken,
|
||||||
ExpiresIn: application.ExpireInHours * hourMinutes,
|
ExpiresIn: application.ExpireInHours * hourSeconds,
|
||||||
Scope: scope,
|
Scope: scope,
|
||||||
TokenType: "Bearer",
|
TokenType: "Bearer",
|
||||||
CodeIsUsed: true,
|
CodeIsUsed: true,
|
||||||
|
@@ -265,8 +265,8 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
|
|||||||
claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims)
|
claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims)
|
||||||
|
|
||||||
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
|
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
|
||||||
claims.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
|
claimsWithoutThirdIdp.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
|
||||||
claims.TokenType = "refresh-token"
|
claimsWithoutThirdIdp.TokenType = "refresh-token"
|
||||||
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
|
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -31,6 +31,6 @@ func GetCurrentUnixTime() string {
|
|||||||
|
|
||||||
func IsTokenExpired(createdTime string, expiresIn int) bool {
|
func IsTokenExpired(createdTime string, expiresIn int) bool {
|
||||||
createdTimeObj, _ := time.Parse(time.RFC3339, createdTime)
|
createdTimeObj, _ := time.Parse(time.RFC3339, createdTime)
|
||||||
expiresAtObj := createdTimeObj.Add(time.Duration(expiresIn) * time.Minute)
|
expiresAtObj := createdTimeObj.Add(time.Duration(expiresIn) * time.Second)
|
||||||
return time.Now().After(expiresAtObj)
|
return time.Now().After(expiresAtObj)
|
||||||
}
|
}
|
||||||
|
@@ -56,15 +56,15 @@ func Test_IsTokenExpired(t *testing.T) {
|
|||||||
description: "Token emitted now is valid for 60 minutes",
|
description: "Token emitted now is valid for 60 minutes",
|
||||||
input: input{
|
input: input{
|
||||||
createdTime: time.Now().Format(time.RFC3339),
|
createdTime: time.Now().Format(time.RFC3339),
|
||||||
expiresIn: 60,
|
expiresIn: 3600,
|
||||||
},
|
},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Token emitted 60 minutes before now is valid for 60 minutes",
|
description: "Token emitted 60 minutes before now is valid for 61 minutes",
|
||||||
input: input{
|
input: input{
|
||||||
createdTime: time.Now().Add(-time.Minute * 60).Format(time.RFC3339),
|
createdTime: time.Now().Add(-time.Minute * 60).Format(time.RFC3339),
|
||||||
expiresIn: 61,
|
expiresIn: 3660,
|
||||||
},
|
},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
@@ -72,7 +72,7 @@ func Test_IsTokenExpired(t *testing.T) {
|
|||||||
description: "Token emitted 2 hours before now is Expired after 60 minutes",
|
description: "Token emitted 2 hours before now is Expired after 60 minutes",
|
||||||
input: input{
|
input: input{
|
||||||
createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339),
|
createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339),
|
||||||
expiresIn: 60,
|
expiresIn: 3600,
|
||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
@@ -80,23 +80,23 @@ func Test_IsTokenExpired(t *testing.T) {
|
|||||||
description: "Token emitted 61 minutes before now is Expired after 60 minutes",
|
description: "Token emitted 61 minutes before now is Expired after 60 minutes",
|
||||||
input: input{
|
input: input{
|
||||||
createdTime: time.Now().Add(-time.Minute * 61).Format(time.RFC3339),
|
createdTime: time.Now().Add(-time.Minute * 61).Format(time.RFC3339),
|
||||||
expiresIn: 60,
|
expiresIn: 3600,
|
||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Token emitted 2 hours before now is valid for 120 minutes",
|
description: "Token emitted 2 hours before now is valid for 121 minutes",
|
||||||
input: input{
|
input: input{
|
||||||
createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339),
|
createdTime: time.Now().Add(-time.Hour * 2).Format(time.RFC3339),
|
||||||
expiresIn: 121,
|
expiresIn: 7260,
|
||||||
},
|
},
|
||||||
expected: false,
|
expected: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Token emitted 159 minutes before now is Expired after 60 minutes",
|
description: "Token emitted 159 minutes before now is Expired after 120 minutes",
|
||||||
input: input{
|
input: input{
|
||||||
createdTime: time.Now().Add(-time.Minute * 159).Format(time.RFC3339),
|
createdTime: time.Now().Add(-time.Minute * 159).Format(time.RFC3339),
|
||||||
expiresIn: 120,
|
expiresIn: 7200,
|
||||||
},
|
},
|
||||||
expected: true,
|
expected: true,
|
||||||
},
|
},
|
||||||
|
@@ -84,8 +84,8 @@ class App extends Component {
|
|||||||
uri: null,
|
uri: null,
|
||||||
menuVisible: false,
|
menuVisible: false,
|
||||||
themeAlgorithm: ["default"],
|
themeAlgorithm: ["default"],
|
||||||
themeData: Setting.ThemeDefault,
|
themeData: Conf.ThemeDefault,
|
||||||
logo: this.getLogo(Setting.getAlgorithmNames(Setting.ThemeDefault)),
|
logo: this.getLogo(Setting.getAlgorithmNames(Conf.ThemeDefault)),
|
||||||
};
|
};
|
||||||
|
|
||||||
Setting.initServerUrl();
|
Setting.initServerUrl();
|
||||||
|
@@ -18,6 +18,7 @@ import {CopyOutlined, LinkOutlined, UploadOutlined} from "@ant-design/icons";
|
|||||||
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
||||||
import * as CertBackend from "./backend/CertBackend";
|
import * as CertBackend from "./backend/CertBackend";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
|
import * as Conf from "./Conf";
|
||||||
import * as ProviderBackend from "./backend/ProviderBackend";
|
import * as ProviderBackend from "./backend/ProviderBackend";
|
||||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||||
import * as ResourceBackend from "./backend/ResourceBackend";
|
import * as ResourceBackend from "./backend/ResourceBackend";
|
||||||
@@ -717,7 +718,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
<Col span={22} style={{marginTop: "5px"}}>
|
<Col span={22} style={{marginTop: "5px"}}>
|
||||||
<Row>
|
<Row>
|
||||||
<Radio.Group value={this.state.application.themeData?.isEnabled ?? false} onChange={e => {
|
<Radio.Group value={this.state.application.themeData?.isEnabled ?? false} onChange={e => {
|
||||||
const {_, ...theme} = this.state.application.themeData ?? {...Setting.ThemeDefault, isEnabled: false};
|
const {_, ...theme} = this.state.application.themeData ?? {...Conf.ThemeDefault, isEnabled: false};
|
||||||
this.updateApplicationField("themeData", {...theme, isEnabled: e.target.value});
|
this.updateApplicationField("themeData", {...theme, isEnabled: e.target.value});
|
||||||
}} >
|
}} >
|
||||||
<Radio.Button value={false}>{i18next.t("application:Follow organization theme")}</Radio.Button>
|
<Radio.Button value={false}>{i18next.t("application:Follow organization theme")}</Radio.Button>
|
||||||
@@ -728,7 +729,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
this.state.application.themeData?.isEnabled ?
|
this.state.application.themeData?.isEnabled ?
|
||||||
<Row style={{marginTop: "20px"}}>
|
<Row style={{marginTop: "20px"}}>
|
||||||
<ThemeEditor themeData={this.state.application.themeData} onThemeChange={(_, nextThemeData) => {
|
<ThemeEditor themeData={this.state.application.themeData} onThemeChange={(_, nextThemeData) => {
|
||||||
const {isEnabled} = this.state.application.themeData ?? {...Setting.ThemeDefault, isEnabled: false};
|
const {isEnabled} = this.state.application.themeData ?? {...Conf.ThemeDefault, isEnabled: false};
|
||||||
this.updateApplicationField("themeData", {...nextThemeData, isEnabled});
|
this.updateApplicationField("themeData", {...nextThemeData, isEnabled});
|
||||||
}} />
|
}} />
|
||||||
</Row> : null
|
</Row> : null
|
||||||
@@ -764,7 +765,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderSignupSigninPreview() {
|
renderSignupSigninPreview() {
|
||||||
const themeData = this.state.application.themeData ?? Setting.ThemeDefault;
|
const themeData = this.state.application.themeData ?? Conf.ThemeDefault;
|
||||||
let signUpUrl = `/signup/${this.state.application.name}`;
|
let signUpUrl = `/signup/${this.state.application.name}`;
|
||||||
const signInUrl = `/login/oauth/authorize?client_id=${this.state.application.clientId}&response_type=code&redirect_uri=${this.state.application.redirectUris[0]}&scope=read&state=casdoor`;
|
const signInUrl = `/login/oauth/authorize?client_id=${this.state.application.clientId}&response_type=code&redirect_uri=${this.state.application.redirectUris[0]}&scope=read&state=casdoor`;
|
||||||
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "97%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "97%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
||||||
@@ -835,7 +836,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderPromptPreview() {
|
renderPromptPreview() {
|
||||||
const themeData = this.state.application.themeData ?? Setting.ThemeDefault;
|
const themeData = this.state.application.themeData ?? Conf.ThemeDefault;
|
||||||
const promptUrl = `/prompt/${this.state.application.name}`;
|
const promptUrl = `/prompt/${this.state.application.name}`;
|
||||||
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "100%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "100%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
||||||
return (
|
return (
|
||||||
|
@@ -17,6 +17,13 @@ export const GithubRepo = "https://github.com/casdoor/casdoor";
|
|||||||
|
|
||||||
export const ForceLanguage = "";
|
export const ForceLanguage = "";
|
||||||
export const DefaultLanguage = "en";
|
export const DefaultLanguage = "en";
|
||||||
export const InitThemeAlgorithm = true;
|
|
||||||
|
|
||||||
export const EnableExtraPages = true;
|
export const EnableExtraPages = true;
|
||||||
|
|
||||||
|
export const InitThemeAlgorithm = true;
|
||||||
|
export const ThemeDefault = {
|
||||||
|
themeType: "default",
|
||||||
|
colorPrimary: "#5734d3",
|
||||||
|
borderRadius: 6,
|
||||||
|
isCompact: false,
|
||||||
|
};
|
||||||
|
@@ -17,6 +17,7 @@ import {Redirect, Route, Switch} from "react-router-dom";
|
|||||||
import {Spin} from "antd";
|
import {Spin} from "antd";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
|
import * as Conf from "./Conf";
|
||||||
import SignupPage from "./auth/SignupPage";
|
import SignupPage from "./auth/SignupPage";
|
||||||
import SelfLoginPage from "./auth/SelfLoginPage";
|
import SelfLoginPage from "./auth/SelfLoginPage";
|
||||||
import LoginPage from "./auth/LoginPage";
|
import LoginPage from "./auth/LoginPage";
|
||||||
@@ -62,7 +63,7 @@ class EntryPage extends React.Component {
|
|||||||
application: application,
|
application: application,
|
||||||
});
|
});
|
||||||
|
|
||||||
const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Setting.ThemeDefault;
|
const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Conf.ThemeDefault;
|
||||||
this.props.updataThemeData(themeData);
|
this.props.updataThemeData(themeData);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -18,6 +18,7 @@ import * as OrganizationBackend from "./backend/OrganizationBackend";
|
|||||||
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
||||||
import * as LdapBackend from "./backend/LdapBackend";
|
import * as LdapBackend from "./backend/LdapBackend";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
|
import * as Conf from "./Conf";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import {LinkOutlined} from "@ant-design/icons";
|
import {LinkOutlined} from "@ant-design/icons";
|
||||||
import LdapTable from "./LdapTable";
|
import LdapTable from "./LdapTable";
|
||||||
@@ -324,7 +325,7 @@ class OrganizationEditPage extends React.Component {
|
|||||||
<Col span={22} style={{marginTop: "5px"}}>
|
<Col span={22} style={{marginTop: "5px"}}>
|
||||||
<Row>
|
<Row>
|
||||||
<Radio.Group value={this.state.organization.themeData?.isEnabled ?? false} onChange={e => {
|
<Radio.Group value={this.state.organization.themeData?.isEnabled ?? false} onChange={e => {
|
||||||
const {_, ...theme} = this.state.organization.themeData ?? {...Setting.ThemeDefault, isEnabled: false};
|
const {_, ...theme} = this.state.organization.themeData ?? {...Conf.ThemeDefault, isEnabled: false};
|
||||||
this.updateOrganizationField("themeData", {...theme, isEnabled: e.target.value});
|
this.updateOrganizationField("themeData", {...theme, isEnabled: e.target.value});
|
||||||
}} >
|
}} >
|
||||||
<Radio.Button value={false}>{i18next.t("organization:Follow global theme")}</Radio.Button>
|
<Radio.Button value={false}>{i18next.t("organization:Follow global theme")}</Radio.Button>
|
||||||
@@ -335,7 +336,7 @@ class OrganizationEditPage extends React.Component {
|
|||||||
this.state.organization.themeData?.isEnabled ?
|
this.state.organization.themeData?.isEnabled ?
|
||||||
<Row style={{marginTop: "20px"}}>
|
<Row style={{marginTop: "20px"}}>
|
||||||
<ThemeEditor themeData={this.state.organization.themeData} onThemeChange={(_, nextThemeData) => {
|
<ThemeEditor themeData={this.state.organization.themeData} onThemeChange={(_, nextThemeData) => {
|
||||||
const {isEnabled} = this.state.organization.themeData ?? {...Setting.ThemeDefault, isEnabled: false};
|
const {isEnabled} = this.state.organization.themeData ?? {...Conf.ThemeDefault, isEnabled: false};
|
||||||
this.updateOrganizationField("themeData", {...nextThemeData, isEnabled});
|
this.updateOrganizationField("themeData", {...nextThemeData, isEnabled});
|
||||||
}} />
|
}} />
|
||||||
</Row> : null
|
</Row> : null
|
||||||
|
@@ -17,7 +17,7 @@ import i18next from "i18next";
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
import * as UserBackend from "./backend/UserBackend";
|
import * as UserBackend from "./backend/UserBackend";
|
||||||
import {CountDownInput} from "./common/CountDownInput";
|
import {SendCodeInput} from "./common/SendCodeInput";
|
||||||
import {MailOutlined, PhoneOutlined} from "@ant-design/icons";
|
import {MailOutlined, PhoneOutlined} from "@ant-design/icons";
|
||||||
|
|
||||||
export const ResetModal = (props) => {
|
export const ResetModal = (props) => {
|
||||||
@@ -93,7 +93,7 @@ export const ResetModal = (props) => {
|
|||||||
/>
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
<Row style={{width: "100%", marginBottom: "20px"}}>
|
<Row style={{width: "100%", marginBottom: "20px"}}>
|
||||||
<CountDownInput
|
<SendCodeInput
|
||||||
textBefore={i18next.t("code:Code You Received")}
|
textBefore={i18next.t("code:Code You Received")}
|
||||||
onChange={setCode}
|
onChange={setCode}
|
||||||
method={"reset"}
|
method={"reset"}
|
||||||
|
@@ -18,6 +18,16 @@ import {Dropdown} from "antd";
|
|||||||
import "./App.less";
|
import "./App.less";
|
||||||
import {GlobalOutlined} from "@ant-design/icons";
|
import {GlobalOutlined} from "@ant-design/icons";
|
||||||
|
|
||||||
|
export const Countries = [{label: "English", key: "en", country: "US", alt: "English"},
|
||||||
|
{label: "简体中文", key: "zh", country: "CN", alt: "简体中文"},
|
||||||
|
{label: "Español", key: "es", country: "ES", alt: "Español"},
|
||||||
|
{label: "Français", key: "fr", country: "FR", alt: "Français"},
|
||||||
|
{label: "Deutsch", key: "de", country: "DE", alt: "Deutsch"},
|
||||||
|
{label: "日本語", key: "ja", country: "JP", alt: "日本語"},
|
||||||
|
{label: "한국어", key: "ko", country: "KR", alt: "한국어"},
|
||||||
|
{label: "Русский", key: "ru", country: "RU", alt: "Русский"},
|
||||||
|
];
|
||||||
|
|
||||||
function flagIcon(country, alt) {
|
function flagIcon(country, alt) {
|
||||||
return (
|
return (
|
||||||
<img width={24} alt={alt} src={`${Setting.StaticBaseUrl}/flag-icons/${country}.svg`} />
|
<img width={24} alt={alt} src={`${Setting.StaticBaseUrl}/flag-icons/${country}.svg`} />
|
||||||
@@ -32,12 +42,12 @@ class SelectLanguageBox extends React.Component {
|
|||||||
languages: props.languages ?? ["en", "zh", "es", "fr", "de", "ja", "ko", "ru"],
|
languages: props.languages ?? ["en", "zh", "es", "fr", "de", "ja", "ko", "ru"],
|
||||||
};
|
};
|
||||||
|
|
||||||
Setting.Countries.forEach((country) => {
|
Countries.forEach((country) => {
|
||||||
new Image().src = `${Setting.StaticBaseUrl}/flag-icons/${country.country}.svg`;
|
new Image().src = `${Setting.StaticBaseUrl}/flag-icons/${country.country}.svg`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
items = Setting.Countries.map((country) => Setting.getItem(country.label, country.key, flagIcon(country.country, country.alt)));
|
items = Countries.map((country) => Setting.getItem(country.label, country.key, flagIcon(country.country, country.alt)));
|
||||||
|
|
||||||
getOrganizationLanguages(languages) {
|
getOrganizationLanguages(languages) {
|
||||||
const select = [];
|
const select = [];
|
||||||
|
@@ -49,8 +49,8 @@ class SelectRegionBox extends React.Component {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
Setting.CountryRegionData.map((item, index) => (
|
Setting.getCountryNames().map((item) => (
|
||||||
<Option key={index} value={item.code} label={`${item.name} (${item.code})`} >
|
<Option key={item.code} value={item.code} label={`${item.name} (${item.code})`} >
|
||||||
<img src={`${Setting.StaticBaseUrl}/flag-icons/${item.code}.svg`} alt={item.name} height={20} style={{marginRight: 10}} />
|
<img src={`${Setting.StaticBaseUrl}/flag-icons/${item.code}.svg`} alt={item.name} height={20} style={{marginRight: 10}} />
|
||||||
{`${item.name} (${item.code})`}
|
{`${item.name} (${item.code})`}
|
||||||
</Option>
|
</Option>
|
||||||
|
@@ -30,33 +30,13 @@ export const ServerUrl = "";
|
|||||||
// export const StaticBaseUrl = "https://cdn.jsdelivr.net/gh/casbin/static";
|
// export const StaticBaseUrl = "https://cdn.jsdelivr.net/gh/casbin/static";
|
||||||
export const StaticBaseUrl = "https://cdn.casbin.org";
|
export const StaticBaseUrl = "https://cdn.casbin.org";
|
||||||
|
|
||||||
// https://catamphetamine.gitlab.io/country-flag-icons/3x2/index.html
|
|
||||||
export const CountryRegionData = getCountryRegionData();
|
|
||||||
|
|
||||||
export const Countries = [{label: "English", key: "en", country: "US", alt: "English"},
|
|
||||||
{label: "简体中文", key: "zh", country: "CN", alt: "简体中文"},
|
|
||||||
{label: "Español", key: "es", country: "ES", alt: "Español"},
|
|
||||||
{label: "Français", key: "fr", country: "FR", alt: "Français"},
|
|
||||||
{label: "Deutsch", key: "de", country: "DE", alt: "Deutsch"},
|
|
||||||
{label: "日本語", key: "ja", country: "JP", alt: "日本語"},
|
|
||||||
{label: "한국어", key: "ko", country: "KR", alt: "한국어"},
|
|
||||||
{label: "Русский", key: "ru", country: "RU", alt: "Русский"},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const ThemeDefault = {
|
|
||||||
themeType: "default",
|
|
||||||
colorPrimary: "#5734d3",
|
|
||||||
borderRadius: 6,
|
|
||||||
isCompact: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
export function getThemeData(organization, application) {
|
export function getThemeData(organization, application) {
|
||||||
if (application?.themeData?.isEnabled) {
|
if (application?.themeData?.isEnabled) {
|
||||||
return application.themeData;
|
return application.themeData;
|
||||||
} else if (organization?.themeData?.isEnabled) {
|
} else if (organization?.themeData?.isEnabled) {
|
||||||
return organization.themeData;
|
return organization.themeData;
|
||||||
} else {
|
} else {
|
||||||
return ThemeDefault;
|
return Conf.ThemeDefault;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,18 +188,18 @@ export const OtherProviderInfo = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function getCountryRegionData() {
|
export function getCountriesData() {
|
||||||
let language = i18next.language;
|
|
||||||
if (language === null || language === "null") {
|
|
||||||
language = Conf.DefaultLanguage;
|
|
||||||
}
|
|
||||||
|
|
||||||
const countries = require("i18n-iso-countries");
|
const countries = require("i18n-iso-countries");
|
||||||
countries.registerLocale(require("i18n-iso-countries/langs/" + language + ".json"));
|
countries.registerLocale(require("i18n-iso-countries/langs/" + getLanguage() + ".json"));
|
||||||
const data = countries.getNames(language, {select: "official"});
|
return countries;
|
||||||
const result = [];
|
}
|
||||||
for (const i in data) {result.push({code: i, name: data[i]});}
|
|
||||||
return result;
|
export function getCountryNames() {
|
||||||
|
const data = getCountriesData().getNames(getLanguage(), {select: "official"});
|
||||||
|
|
||||||
|
return Object.entries(data).map(items => {
|
||||||
|
return {code: items[0], name: items[1]};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function initServerUrl() {
|
export function initServerUrl() {
|
||||||
@@ -702,7 +682,7 @@ export function getLanguageText(text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getLanguage() {
|
export function getLanguage() {
|
||||||
return i18next.language;
|
return i18next.language ?? Conf.DefaultLanguage;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setLanguage(language) {
|
export function setLanguage(language) {
|
||||||
|
@@ -135,13 +135,6 @@ class UserListPage extends BaseListPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderTable(users) {
|
renderTable(users) {
|
||||||
// transfer country code to name based on selected language
|
|
||||||
const countries = require("i18n-iso-countries");
|
|
||||||
countries.registerLocale(require("i18n-iso-countries/langs/" + i18next.language + ".json"));
|
|
||||||
for (const index in users) {
|
|
||||||
users[index].region = countries.getName(users[index].region, i18next.language, {select: "official"});
|
|
||||||
}
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
title: i18next.t("general:Organization"),
|
title: i18next.t("general:Organization"),
|
||||||
@@ -267,6 +260,9 @@ class UserListPage extends BaseListPage {
|
|||||||
width: "140px",
|
width: "140px",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("region"),
|
...this.getColumnSearchProps("region"),
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return Setting.getCountriesData().getName(record.region, Setting.getLanguage(), {select: "official"});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: i18next.t("user:Tag"),
|
title: i18next.t("user:Tag"),
|
||||||
|
@@ -19,7 +19,7 @@ import * as ApplicationBackend from "../backend/ApplicationBackend";
|
|||||||
import * as Util from "./Util";
|
import * as Util from "./Util";
|
||||||
import * as Setting from "../Setting";
|
import * as Setting from "../Setting";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import {CountDownInput} from "../common/CountDownInput";
|
import {SendCodeInput} from "../common/SendCodeInput";
|
||||||
import * as UserBackend from "../backend/UserBackend";
|
import * as UserBackend from "../backend/UserBackend";
|
||||||
import {CheckCircleOutlined, KeyOutlined, LockOutlined, SolutionOutlined, UserOutlined} from "@ant-design/icons";
|
import {CheckCircleOutlined, KeyOutlined, LockOutlined, SolutionOutlined, UserOutlined} from "@ant-design/icons";
|
||||||
import CustomGithubCorner from "../CustomGithubCorner";
|
import CustomGithubCorner from "../CustomGithubCorner";
|
||||||
@@ -350,14 +350,14 @@ class ForgetPage extends React.Component {
|
|||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{this.state.verifyType === "email" ? (
|
{this.state.verifyType === "email" ? (
|
||||||
<CountDownInput
|
<SendCodeInput
|
||||||
disabled={this.state.username === "" || this.state.verifyType === ""}
|
disabled={this.state.username === "" || this.state.verifyType === ""}
|
||||||
method={"forget"}
|
method={"forget"}
|
||||||
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(this.getApplicationObj()), this.state.name]}
|
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(this.getApplicationObj()), this.state.name]}
|
||||||
application={application}
|
application={application}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<CountDownInput
|
<SendCodeInput
|
||||||
disabled={this.state.username === "" || this.state.verifyType === ""}
|
disabled={this.state.username === "" || this.state.verifyType === ""}
|
||||||
method={"forget"}
|
method={"forget"}
|
||||||
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(this.getApplicationObj()), this.state.name]}
|
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(this.getApplicationObj()), this.state.name]}
|
||||||
|
@@ -26,7 +26,7 @@ import * as Setting from "../Setting";
|
|||||||
import SelfLoginButton from "./SelfLoginButton";
|
import SelfLoginButton from "./SelfLoginButton";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import CustomGithubCorner from "../CustomGithubCorner";
|
import CustomGithubCorner from "../CustomGithubCorner";
|
||||||
import {CountDownInput} from "../common/CountDownInput";
|
import {SendCodeInput} from "../common/SendCodeInput";
|
||||||
import SelectLanguageBox from "../SelectLanguageBox";
|
import SelectLanguageBox from "../SelectLanguageBox";
|
||||||
import {CaptchaModal} from "../common/CaptchaModal";
|
import {CaptchaModal} from "../common/CaptchaModal";
|
||||||
import RedirectForm from "../common/RedirectForm";
|
import RedirectForm from "../common/RedirectForm";
|
||||||
@@ -419,7 +419,7 @@ class LoginPage extends React.Component {
|
|||||||
rules={[
|
rules={[
|
||||||
{
|
{
|
||||||
required: true,
|
required: true,
|
||||||
message: i18next.t("login:Please input your username, Email or phone!"),
|
message: i18next.t("login:Please input your Email or Phone!"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
validator: (_, value) => {
|
validator: (_, value) => {
|
||||||
@@ -755,7 +755,7 @@ class LoginPage extends React.Component {
|
|||||||
name="code"
|
name="code"
|
||||||
rules={[{required: true, message: i18next.t("login:Please input your code!")}]}
|
rules={[{required: true, message: i18next.t("login:Please input your code!")}]}
|
||||||
>
|
>
|
||||||
<CountDownInput
|
<SendCodeInput
|
||||||
disabled={this.state.username?.length === 0 || !this.state.validEmailOrPhone}
|
disabled={this.state.username?.length === 0 || !this.state.validEmailOrPhone}
|
||||||
method={"login"}
|
method={"login"}
|
||||||
onButtonClickArgs={[this.state.username, this.state.validEmail ? "email" : "phone", Setting.getApplicationName(application)]}
|
onButtonClickArgs={[this.state.username, this.state.validEmail ? "email" : "phone", Setting.getApplicationName(application)]}
|
||||||
|
@@ -21,7 +21,7 @@ import i18next from "i18next";
|
|||||||
import * as Util from "./Util";
|
import * as Util from "./Util";
|
||||||
import {authConfig} from "./Auth";
|
import {authConfig} from "./Auth";
|
||||||
import * as ApplicationBackend from "../backend/ApplicationBackend";
|
import * as ApplicationBackend from "../backend/ApplicationBackend";
|
||||||
import {CountDownInput} from "../common/CountDownInput";
|
import {SendCodeInput} from "../common/SendCodeInput";
|
||||||
import SelectRegionBox from "../SelectRegionBox";
|
import SelectRegionBox from "../SelectRegionBox";
|
||||||
import CustomGithubCorner from "../CustomGithubCorner";
|
import CustomGithubCorner from "../CustomGithubCorner";
|
||||||
import SelectLanguageBox from "../SelectLanguageBox";
|
import SelectLanguageBox from "../SelectLanguageBox";
|
||||||
@@ -365,7 +365,7 @@ class SignupPage extends React.Component {
|
|||||||
message: i18next.t("code:Please input your verification code!"),
|
message: i18next.t("code:Please input your verification code!"),
|
||||||
}]}
|
}]}
|
||||||
>
|
>
|
||||||
<CountDownInput
|
<SendCodeInput
|
||||||
disabled={!this.state.validEmail}
|
disabled={!this.state.validEmail}
|
||||||
method={"signup"}
|
method={"signup"}
|
||||||
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(application)]}
|
onButtonClickArgs={[this.state.email, "email", Setting.getApplicationName(application)]}
|
||||||
@@ -419,7 +419,7 @@ class SignupPage extends React.Component {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<CountDownInput
|
<SendCodeInput
|
||||||
disabled={!this.state.validPhone}
|
disabled={!this.state.validPhone}
|
||||||
method={"signup"}
|
method={"signup"}
|
||||||
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(application)]}
|
onButtonClickArgs={[this.state.phone, "phone", Setting.getApplicationName(application)]}
|
||||||
|
@@ -109,7 +109,7 @@ export function setPassword(userOwner, userName, oldPassword, newPassword) {
|
|||||||
}).then(res => res.json());
|
}).then(res => res.json());
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sendCode(checkType, checkId, checkKey, method, dest, type, applicationId, checkUser) {
|
export function sendCode(checkType, checkId, checkKey, method, dest, type, applicationId, checkUser = "") {
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append("checkType", checkType);
|
formData.append("checkType", checkType);
|
||||||
formData.append("checkId", checkId);
|
formData.append("checkId", checkId);
|
||||||
|
@@ -21,7 +21,7 @@ import {CaptchaWidget} from "./CaptchaWidget";
|
|||||||
|
|
||||||
const {Search} = Input;
|
const {Search} = Input;
|
||||||
|
|
||||||
export const CountDownInput = (props) => {
|
export const SendCodeInput = (props) => {
|
||||||
const {disabled, textBefore, onChange, onButtonClickArgs, application, method} = props;
|
const {disabled, textBefore, onChange, onButtonClickArgs, application, method} = props;
|
||||||
const [visible, setVisible] = React.useState(false);
|
const [visible, setVisible] = React.useState(false);
|
||||||
const [key, setKey] = React.useState("");
|
const [key, setKey] = React.useState("");
|
@@ -14,14 +14,13 @@
|
|||||||
|
|
||||||
import {Card, ConfigProvider, Form, Layout, Switch, theme} from "antd";
|
import {Card, ConfigProvider, Form, Layout, Switch, theme} from "antd";
|
||||||
import ThemePicker from "./ThemePicker";
|
import ThemePicker from "./ThemePicker";
|
||||||
import ColorPicker from "./ColorPicker";
|
import ColorPicker, {GREEN_COLOR, PINK_COLOR} from "./ColorPicker";
|
||||||
import RadiusPicker from "./RadiusPicker";
|
import RadiusPicker from "./RadiusPicker";
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {GREEN_COLOR, PINK_COLOR} from "./ColorPicker";
|
import {useEffect} from "react";
|
||||||
import {Content} from "antd/es/layout/layout";
|
import {Content} from "antd/es/layout/layout";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import {useEffect} from "react";
|
import * as Conf from "../../Conf";
|
||||||
import * as Setting from "../../Setting";
|
|
||||||
|
|
||||||
const ThemesInfo = {
|
const ThemesInfo = {
|
||||||
default: {},
|
default: {},
|
||||||
@@ -41,7 +40,7 @@ const ThemesInfo = {
|
|||||||
const onChange = () => {};
|
const onChange = () => {};
|
||||||
|
|
||||||
export default function ThemeEditor(props) {
|
export default function ThemeEditor(props) {
|
||||||
const themeData = props.themeData ?? Setting.ThemeDefault;
|
const themeData = props.themeData ?? Conf.ThemeDefault;
|
||||||
const onThemeChange = props.onThemeChange ?? onChange;
|
const onThemeChange = props.onThemeChange ?? onChange;
|
||||||
|
|
||||||
const {isCompact, themeType, ...themeToken} = themeData;
|
const {isCompact, themeType, ...themeToken} = themeData;
|
||||||
@@ -59,7 +58,7 @@ export default function ThemeEditor(props) {
|
|||||||
}, [isLight, isCompact]);
|
}, [isLight, isCompact]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const mergedData = Object.assign(Object.assign(Object.assign({}, Setting.ThemeDefault), {themeType}), ThemesInfo[themeType]);
|
const mergedData = Object.assign(Object.assign(Object.assign({}, Conf.ThemeDefault), {themeType}), ThemesInfo[themeType]);
|
||||||
onThemeChange(null, mergedData);
|
onThemeChange(null, mergedData);
|
||||||
form.setFieldsValue(mergedData);
|
form.setFieldsValue(mergedData);
|
||||||
}, [themeType]);
|
}, [themeType]);
|
||||||
|
Reference in New Issue
Block a user