mirror of
https://github.com/casdoor/casdoor.git
synced 2025-08-18 05:34:45 +08:00
Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1a9d02be46 | ||
![]() |
eafaa135b4 | ||
![]() |
6746551447 | ||
![]() |
3cb46c3628 | ||
![]() |
558bcf95d6 | ||
![]() |
bb937c30c1 | ||
![]() |
8dfdf7f767 | ||
![]() |
62b2082e82 | ||
![]() |
a1806439f8 | ||
![]() |
01e58158b7 | ||
![]() |
15427ad9d6 | ||
![]() |
d058f78dc6 | ||
![]() |
fd9dbf8251 | ||
![]() |
3220a04fa9 | ||
![]() |
f06a4990bd | ||
![]() |
9df7de5f27 | ||
![]() |
56c808c091 | ||
![]() |
9fd2421564 | ||
![]() |
689d45c7fa | ||
![]() |
c24343bd53 | ||
![]() |
979f43638d | ||
![]() |
685a4514cd | ||
![]() |
a05ca3af24 | ||
![]() |
c6f301ff9e | ||
![]() |
d7b2bcf288 | ||
![]() |
67ac3d6d21 | ||
![]() |
912d5c6a7f | ||
![]() |
32fbb5b534 | ||
![]() |
21004f3009 | ||
![]() |
463bacd53b | ||
![]() |
78dc660041 | ||
![]() |
2fb9674171 | ||
![]() |
55c522d3b7 | ||
![]() |
f879170663 | ||
![]() |
12e5d9b583 | ||
![]() |
eefa1e6df4 |
141
ai/ai.go
141
ai/ai.go
@@ -1,141 +0,0 @@
|
||||
// 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 ai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sashabaranov/go-openai"
|
||||
)
|
||||
|
||||
func queryAnswer(authToken string, question string, timeout int) (string, error) {
|
||||
// fmt.Printf("Question: %s\n", question)
|
||||
|
||||
client := getProxyClientFromToken(authToken)
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(2+timeout*2)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
resp, err := client.CreateChatCompletion(
|
||||
ctx,
|
||||
openai.ChatCompletionRequest{
|
||||
Model: openai.GPT3Dot5Turbo,
|
||||
Messages: []openai.ChatCompletionMessage{
|
||||
{
|
||||
Role: openai.ChatMessageRoleUser,
|
||||
Content: question,
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
res := resp.Choices[0].Message.Content
|
||||
res = strings.Trim(res, "\n")
|
||||
// fmt.Printf("Answer: %s\n\n", res)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func QueryAnswerSafe(authToken string, question string) string {
|
||||
var res string
|
||||
var err error
|
||||
for i := 0; i < 10; i++ {
|
||||
res, err = queryAnswer(authToken, question, i)
|
||||
if err != nil {
|
||||
if i > 0 {
|
||||
fmt.Printf("\tFailed (%d): %s\n", i+1, err.Error())
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func QueryAnswerStream(authToken string, question string, writer io.Writer, builder *strings.Builder) error {
|
||||
client := getProxyClientFromToken(authToken)
|
||||
|
||||
ctx := context.Background()
|
||||
flusher, ok := writer.(http.Flusher)
|
||||
if !ok {
|
||||
return fmt.Errorf("writer does not implement http.Flusher")
|
||||
}
|
||||
// https://platform.openai.com/tokenizer
|
||||
// https://github.com/pkoukk/tiktoken-go#available-encodings
|
||||
promptTokens, err := getTokenSize(openai.GPT3TextDavinci003, question)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// https://platform.openai.com/docs/models/gpt-3-5
|
||||
maxTokens := 4097 - promptTokens
|
||||
|
||||
respStream, err := client.CreateCompletionStream(
|
||||
ctx,
|
||||
openai.CompletionRequest{
|
||||
Model: openai.GPT3TextDavinci003,
|
||||
Prompt: question,
|
||||
MaxTokens: maxTokens,
|
||||
Stream: true,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer respStream.Close()
|
||||
|
||||
isLeadingReturn := true
|
||||
for {
|
||||
completion, streamErr := respStream.Recv()
|
||||
if streamErr != nil {
|
||||
if streamErr == io.EOF {
|
||||
break
|
||||
}
|
||||
return streamErr
|
||||
}
|
||||
|
||||
data := completion.Choices[0].Text
|
||||
if isLeadingReturn && len(data) != 0 {
|
||||
if strings.Count(data, "\n") == len(data) {
|
||||
continue
|
||||
} else {
|
||||
isLeadingReturn = false
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("%s", data)
|
||||
|
||||
// Write the streamed data as Server-Sent Events
|
||||
if _, err = fmt.Fprintf(writer, "event: message\ndata: %s\n\n", data); err != nil {
|
||||
return err
|
||||
}
|
||||
flusher.Flush()
|
||||
// Append the response to the strings.Builder
|
||||
builder.WriteString(data)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
// 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.
|
||||
|
||||
//go:build !skipCi
|
||||
// +build !skipCi
|
||||
|
||||
package ai
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
"github.com/sashabaranov/go-openai"
|
||||
)
|
||||
|
||||
func TestRun(t *testing.T) {
|
||||
object.InitConfig()
|
||||
proxy.InitHttpClient()
|
||||
|
||||
text, err := queryAnswer("", "hi", 5)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
println(text)
|
||||
}
|
||||
|
||||
func TestToken(t *testing.T) {
|
||||
println(getTokenSize(openai.GPT3TextDavinci003, ""))
|
||||
}
|
@@ -33,11 +33,12 @@ func InitApi() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
Enforcer, err = e.InitEnforcer()
|
||||
err = e.InitEnforcer()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
Enforcer = e.Enforcer
|
||||
Enforcer.ClearPolicy()
|
||||
|
||||
// if len(Enforcer.GetPolicy()) == 0 {
|
||||
|
@@ -35,6 +35,7 @@ func (c *ApiController) Enforce() {
|
||||
permissionId := c.Input().Get("permissionId")
|
||||
modelId := c.Input().Get("modelId")
|
||||
resourceId := c.Input().Get("resourceId")
|
||||
enforcerId := c.Input().Get("enforcerId")
|
||||
|
||||
var request object.CasbinRequest
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &request)
|
||||
@@ -43,6 +44,29 @@ func (c *ApiController) Enforce() {
|
||||
return
|
||||
}
|
||||
|
||||
if enforcerId != "" {
|
||||
enforcer, err := object.GetEnforcer(enforcerId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = enforcer.InitEnforcer()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res, err := enforcer.Enforce(request...)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(res)
|
||||
return
|
||||
}
|
||||
|
||||
if permissionId != "" {
|
||||
permission, err := object.GetPermission(permissionId)
|
||||
if err != nil {
|
||||
@@ -121,6 +145,7 @@ func (c *ApiController) Enforce() {
|
||||
func (c *ApiController) BatchEnforce() {
|
||||
permissionId := c.Input().Get("permissionId")
|
||||
modelId := c.Input().Get("modelId")
|
||||
enforcerId := c.Input().Get("enforcerId")
|
||||
|
||||
var requests []object.CasbinRequest
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requests)
|
||||
@@ -129,6 +154,29 @@ func (c *ApiController) BatchEnforce() {
|
||||
return
|
||||
}
|
||||
|
||||
if enforcerId != "" {
|
||||
enforcer, err := object.GetEnforcer(enforcerId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = enforcer.InitEnforcer()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res, err := enforcer.BatchEnforce(requests)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(res)
|
||||
return
|
||||
}
|
||||
|
||||
if permissionId != "" {
|
||||
permission, err := object.GetPermission(permissionId)
|
||||
if err != nil {
|
||||
|
@@ -1,145 +0,0 @@
|
||||
// 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 controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/beego/beego/utils/pagination"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetChats
|
||||
// @Title GetChats
|
||||
// @Tag Chat API
|
||||
// @Description get chats
|
||||
// @Param owner query string true "The owner of chats"
|
||||
// @Success 200 {array} object.Chat The Response object
|
||||
// @router /get-chats [get]
|
||||
func (c *ApiController) GetChats() {
|
||||
owner := "admin"
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
|
||||
if limit == "" || page == "" {
|
||||
maskedChats, err := object.GetMaskedChats(object.GetChats(owner))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(maskedChats)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetChatCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
chats, err := object.GetMaskedChats(object.GetPaginationChats(owner, paginator.Offset(), limit, field, value, sortField, sortOrder))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(chats, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// GetChat
|
||||
// @Title GetChat
|
||||
// @Tag Chat API
|
||||
// @Description get chat
|
||||
// @Param id query string true "The id ( owner/name ) of the chat"
|
||||
// @Success 200 {object} object.Chat The Response object
|
||||
// @router /get-chat [get]
|
||||
func (c *ApiController) GetChat() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
maskedChat, err := object.GetMaskedChat(object.GetChat(id))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(maskedChat)
|
||||
}
|
||||
|
||||
// UpdateChat
|
||||
// @Title UpdateChat
|
||||
// @Tag Chat API
|
||||
// @Description update chat
|
||||
// @Param id query string true "The id ( owner/name ) of the chat"
|
||||
// @Param body body object.Chat true "The details of the chat"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-chat [post]
|
||||
func (c *ApiController) UpdateChat() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var chat object.Chat
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &chat)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateChat(id, &chat))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddChat
|
||||
// @Title AddChat
|
||||
// @Tag Chat API
|
||||
// @Description add chat
|
||||
// @Param body body object.Chat true "The details of the chat"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-chat [post]
|
||||
func (c *ApiController) AddChat() {
|
||||
var chat object.Chat
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &chat)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.AddChat(&chat))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteChat
|
||||
// @Title DeleteChat
|
||||
// @Tag Chat API
|
||||
// @Description delete chat
|
||||
// @Param body body object.Chat true "The details of the chat"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-chat [post]
|
||||
func (c *ApiController) DeleteChat() {
|
||||
var chat object.Chat
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &chat)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteChat(&chat))
|
||||
c.ServeJSON()
|
||||
}
|
@@ -1,313 +0,0 @@
|
||||
// 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 controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/beego/beego/utils/pagination"
|
||||
"github.com/casdoor/casdoor/ai"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
// GetMessages
|
||||
// @Title GetMessages
|
||||
// @Tag Message API
|
||||
// @Description get messages
|
||||
// @Param owner query string true "The owner of messages"
|
||||
// @Success 200 {array} object.Message The Response object
|
||||
// @router /get-messages [get]
|
||||
func (c *ApiController) GetMessages() {
|
||||
owner := c.Input().Get("owner")
|
||||
organization := c.Input().Get("organization")
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
chat := c.Input().Get("chat")
|
||||
|
||||
if limit == "" || page == "" {
|
||||
var messages []*object.Message
|
||||
var err error
|
||||
if chat == "" {
|
||||
messages, err = object.GetMessages(owner)
|
||||
} else {
|
||||
messages, err = object.GetChatMessages(chat)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetMaskedMessages(messages))
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetMessageCount(owner, organization, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
paginationMessages, err := object.GetPaginationMessages(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
messages := object.GetMaskedMessages(paginationMessages)
|
||||
c.ResponseOk(messages, paginator.Nums())
|
||||
}
|
||||
}
|
||||
|
||||
// GetMessage
|
||||
// @Title GetMessage
|
||||
// @Tag Message API
|
||||
// @Description get message
|
||||
// @Param id query string true "The id ( owner/name ) of the message"
|
||||
// @Success 200 {object} object.Message The Response object
|
||||
// @router /get-message [get]
|
||||
func (c *ApiController) GetMessage() {
|
||||
id := c.Input().Get("id")
|
||||
message, err := object.GetMessage(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(message)
|
||||
}
|
||||
|
||||
func (c *ApiController) ResponseErrorStream(errorText string) {
|
||||
event := fmt.Sprintf("event: myerror\ndata: %s\n\n", errorText)
|
||||
_, err := c.Ctx.ResponseWriter.Write([]byte(event))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// GetMessageAnswer
|
||||
// @Title GetMessageAnswer
|
||||
// @Tag Message API
|
||||
// @Description get message answer
|
||||
// @Param id query string true "The id ( owner/name ) of the message"
|
||||
// @Success 200 {object} object.Message The Response object
|
||||
// @router /get-message-answer [get]
|
||||
func (c *ApiController) GetMessageAnswer() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/event-stream")
|
||||
c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache")
|
||||
c.Ctx.ResponseWriter.Header().Set("Connection", "keep-alive")
|
||||
|
||||
message, err := object.GetMessage(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if message == nil {
|
||||
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The message: %s is not found"), id))
|
||||
return
|
||||
}
|
||||
|
||||
if message.Author != "AI" || message.ReplyTo == "" || message.Text != "" {
|
||||
c.ResponseErrorStream(c.T("chat:The message is invalid"))
|
||||
return
|
||||
}
|
||||
|
||||
chatId := util.GetId("admin", message.Chat)
|
||||
chat, err := object.GetChat(chatId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if chat == nil || chat.Organization != message.Organization {
|
||||
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The chat: %s is not found"), chatId))
|
||||
return
|
||||
}
|
||||
|
||||
if chat.Type != "AI" {
|
||||
c.ResponseErrorStream(c.T("chat:The chat type must be \"AI\""))
|
||||
return
|
||||
}
|
||||
|
||||
questionMessage, err := object.GetMessage(message.ReplyTo)
|
||||
if questionMessage == nil {
|
||||
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The message: %s is not found"), id))
|
||||
return
|
||||
}
|
||||
|
||||
providerId := util.GetId(chat.Owner, chat.User2)
|
||||
provider, err := object.GetProvider(providerId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if provider == nil {
|
||||
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The provider: %s is not found"), providerId))
|
||||
return
|
||||
}
|
||||
|
||||
if provider.Category != "AI" || provider.ClientSecret == "" {
|
||||
c.ResponseErrorStream(fmt.Sprintf(c.T("chat:The provider: %s is invalid"), providerId))
|
||||
return
|
||||
}
|
||||
|
||||
c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/event-stream")
|
||||
c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache")
|
||||
c.Ctx.ResponseWriter.Header().Set("Connection", "keep-alive")
|
||||
|
||||
authToken := provider.ClientSecret
|
||||
question := questionMessage.Text
|
||||
var stringBuilder strings.Builder
|
||||
|
||||
fmt.Printf("Question: [%s]\n", questionMessage.Text)
|
||||
fmt.Printf("Answer: [")
|
||||
|
||||
err = ai.QueryAnswerStream(authToken, question, c.Ctx.ResponseWriter, &stringBuilder)
|
||||
if err != nil {
|
||||
c.ResponseErrorStream(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("]\n")
|
||||
|
||||
event := fmt.Sprintf("event: end\ndata: %s\n\n", "end")
|
||||
_, err = c.Ctx.ResponseWriter.Write([]byte(event))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
answer := stringBuilder.String()
|
||||
|
||||
message.Text = answer
|
||||
_, err = object.UpdateMessage(message.GetId(), message)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateMessage
|
||||
// @Title UpdateMessage
|
||||
// @Tag Message API
|
||||
// @Description update message
|
||||
// @Param id query string true "The id ( owner/name ) of the message"
|
||||
// @Param body body object.Message true "The details of the message"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-message [post]
|
||||
func (c *ApiController) UpdateMessage() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
var message object.Message
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &message)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateMessage(id, &message))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// AddMessage
|
||||
// @Title AddMessage
|
||||
// @Tag Message API
|
||||
// @Description add message
|
||||
// @Param body body object.Message true "The details of the message"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /add-message [post]
|
||||
func (c *ApiController) AddMessage() {
|
||||
var message object.Message
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &message)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var chat *object.Chat
|
||||
if message.Chat != "" {
|
||||
chatId := util.GetId("admin", message.Chat)
|
||||
chat, err = object.GetChat(chatId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if chat == nil || chat.Organization != message.Organization {
|
||||
c.ResponseError(fmt.Sprintf(c.T("chat:The chat: %s is not found"), chatId))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
affected, err := object.AddMessage(&message)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if affected {
|
||||
if chat != nil && chat.Type == "AI" {
|
||||
answerMessage := &object.Message{
|
||||
Owner: message.Owner,
|
||||
Name: fmt.Sprintf("message_%s", util.GetRandomName()),
|
||||
CreatedTime: util.GetCurrentTimeEx(message.CreatedTime),
|
||||
Organization: message.Organization,
|
||||
Chat: message.Chat,
|
||||
ReplyTo: message.GetId(),
|
||||
Author: "AI",
|
||||
Text: "",
|
||||
}
|
||||
_, err = object.AddMessage(answerMessage)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(affected)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// DeleteMessage
|
||||
// @Title DeleteMessage
|
||||
// @Tag Message API
|
||||
// @Description delete message
|
||||
// @Param body body object.Message true "The details of the message"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /delete-message [post]
|
||||
func (c *ApiController) DeleteMessage() {
|
||||
var message object.Message
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &message)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteMessage(&message))
|
||||
c.ServeJSON()
|
||||
}
|
@@ -31,7 +31,6 @@ import (
|
||||
// @router /get-payments [get]
|
||||
func (c *ApiController) GetPayments() {
|
||||
owner := c.Input().Get("owner")
|
||||
organization := c.Input().Get("organization")
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
@@ -49,14 +48,14 @@ func (c *ApiController) GetPayments() {
|
||||
c.ResponseOk(payments)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetPaymentCount(owner, organization, field, value)
|
||||
count, err := object.GetPaymentCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
payments, err := object.GetPaginationPayments(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
payments, err := object.GetPaginationPayments(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -77,10 +76,9 @@ func (c *ApiController) GetPayments() {
|
||||
// @router /get-user-payments [get]
|
||||
func (c *ApiController) GetUserPayments() {
|
||||
owner := c.Input().Get("owner")
|
||||
organization := c.Input().Get("organization")
|
||||
user := c.Input().Get("user")
|
||||
|
||||
payments, err := object.GetUserPayments(owner, organization, user)
|
||||
payments, err := object.GetUserPayments(owner, user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -177,24 +175,18 @@ func (c *ApiController) DeletePayment() {
|
||||
// @router /notify-payment [post]
|
||||
func (c *ApiController) NotifyPayment() {
|
||||
owner := c.Ctx.Input.Param(":owner")
|
||||
providerName := c.Ctx.Input.Param(":provider")
|
||||
productName := c.Ctx.Input.Param(":product")
|
||||
paymentName := c.Ctx.Input.Param(":payment")
|
||||
orderId := c.Ctx.Input.Param("order")
|
||||
|
||||
body := c.Ctx.Input.RequestBody
|
||||
|
||||
err, errorResponse := object.NotifyPayment(c.Ctx.Request, body, owner, providerName, productName, paymentName, orderId)
|
||||
|
||||
_, err2 := c.Ctx.ResponseWriter.Write([]byte(errorResponse))
|
||||
if err2 != nil {
|
||||
panic(err2)
|
||||
}
|
||||
|
||||
payment, err := object.NotifyPayment(c.Ctx.Request, body, owner, paymentName, orderId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(payment)
|
||||
}
|
||||
|
||||
// InvoicePayment
|
||||
|
@@ -52,7 +52,22 @@ func (c *ApiController) GetResources() {
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
|
||||
if limit == "" || page == "" {
|
||||
if sortField == "Direct" {
|
||||
provider, err := c.GetProviderFromContext("Storage")
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
prefix := sortOrder
|
||||
resources, err := object.GetDirectResources(owner, user, provider, prefix, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(resources)
|
||||
} else if limit == "" || page == "" {
|
||||
resources, err := object.GetResources(owner, user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@@ -152,11 +167,16 @@ func (c *ApiController) DeleteResource() {
|
||||
return
|
||||
}
|
||||
|
||||
if resource.Provider != "" {
|
||||
c.Input().Set("provider", resource.Provider)
|
||||
}
|
||||
c.Input().Set("fullFilePath", resource.Name)
|
||||
provider, err := c.GetProviderFromContext("Storage")
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
_, resource.Name = refineFullFilePath(resource.Name)
|
||||
|
||||
err = object.DeleteFile(provider, resource.Name, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
@@ -216,6 +236,7 @@ func (c *ApiController) UploadResource() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
_, fullFilePath = refineFullFilePath(fullFilePath)
|
||||
|
||||
fileType := "unknown"
|
||||
contentType := header.Header.Get("Content-Type")
|
||||
|
@@ -90,7 +90,7 @@ func (c *ApiController) GetUsers() {
|
||||
|
||||
if limit == "" || page == "" {
|
||||
if groupName != "" {
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(groupName))
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(util.GetId(owner, groupName)))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -567,6 +567,22 @@ func (c *ApiController) RemoveUserFromGroup() {
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
groupName := c.Ctx.Request.Form.Get("groupName")
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.RemoveUserFromGroup(owner, name, groupName))
|
||||
c.ServeJSON()
|
||||
organization, err := object.GetOrganization(util.GetId("admin", owner))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
item := object.GetAccountItemByName("Groups", organization)
|
||||
res, msg := object.CheckAccountItemModifyRule(item, c.IsAdmin(), c.GetAcceptLanguage())
|
||||
if !res {
|
||||
c.ResponseError(msg)
|
||||
return
|
||||
}
|
||||
|
||||
affected, err := object.DeleteGroupForUser(util.GetId(owner, name), groupName)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(affected)
|
||||
}
|
||||
|
@@ -16,6 +16,7 @@ package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/i18n"
|
||||
@@ -143,8 +144,30 @@ func (c *ApiController) IsMaskedEnabled() (bool, bool) {
|
||||
return true, isMaskEnabled
|
||||
}
|
||||
|
||||
func refineFullFilePath(fullFilePath string) (string, string) {
|
||||
tokens := strings.Split(fullFilePath, "/")
|
||||
if len(tokens) >= 2 && tokens[0] == "Direct" && tokens[1] != "" {
|
||||
providerName := tokens[1]
|
||||
res := strings.Join(tokens[2:], "/")
|
||||
return providerName, "/" + res
|
||||
} else {
|
||||
return "", fullFilePath
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ApiController) GetProviderFromContext(category string) (*object.Provider, error) {
|
||||
providerName := c.Input().Get("provider")
|
||||
if providerName == "" {
|
||||
field := c.Input().Get("field")
|
||||
value := c.Input().Get("value")
|
||||
if field == "provider" && value != "" {
|
||||
providerName = value
|
||||
} else {
|
||||
fullFilePath := c.Input().Get("fullFilePath")
|
||||
providerName, _ = refineFullFilePath(fullFilePath)
|
||||
}
|
||||
}
|
||||
|
||||
if providerName != "" {
|
||||
provider, err := object.GetProvider(util.GetId("admin", providerName))
|
||||
if err != nil {
|
||||
|
23
go.mod
23
go.mod
@@ -12,13 +12,12 @@ require (
|
||||
github.com/beevik/etree v1.1.0
|
||||
github.com/casbin/casbin v1.9.1 // indirect
|
||||
github.com/casbin/casbin/v2 v2.30.1
|
||||
github.com/casdoor/go-sms-sender v0.6.1
|
||||
github.com/casdoor/go-sms-sender v0.12.0
|
||||
github.com/casdoor/gomail/v2 v2.0.1
|
||||
github.com/casdoor/oss v1.2.0
|
||||
github.com/casdoor/oss v1.3.0
|
||||
github.com/casdoor/xorm-adapter/v3 v3.0.4
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
github.com/denisenkom/go-mssqldb v0.9.0
|
||||
github.com/dlclark/regexp2 v1.9.0 // indirect
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect
|
||||
github.com/fogleman/gg v1.3.0
|
||||
github.com/forestmgy/ldapserver v1.1.0
|
||||
@@ -29,9 +28,6 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/go-webauthn/webauthn v0.6.0
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/mux v1.7.3 // indirect
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||
@@ -42,21 +38,19 @@ require (
|
||||
github.com/mitchellh/mapstructure v1.5.0
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||
github.com/nyaruka/phonenumbers v1.1.5
|
||||
github.com/pkoukk/tiktoken-go v0.1.1
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.11.1
|
||||
github.com/prometheus/client_model v0.2.0
|
||||
github.com/prometheus/client_model v0.3.0
|
||||
github.com/qiangmzsx/string-adapter/v2 v2.1.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/russellhaering/gosaml2 v0.9.0
|
||||
github.com/russellhaering/goxmldsig v1.2.0
|
||||
github.com/sashabaranov/go-openai v1.12.0
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/stretchr/testify v1.8.3
|
||||
github.com/tealeg/xlsx v1.0.5
|
||||
github.com/thanhpk/randstr v1.0.4
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
@@ -64,12 +58,11 @@ require (
|
||||
github.com/xorm-io/core v0.7.4
|
||||
github.com/xorm-io/xorm v1.1.6
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.6.0
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
|
||||
golang.org/x/net v0.7.0
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
|
||||
golang.org/x/crypto v0.11.0
|
||||
golang.org/x/net v0.13.0
|
||||
golang.org/x/oauth2 v0.10.0
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84
|
||||
modernc.org/sqlite v1.18.2
|
||||
)
|
||||
|
@@ -33,6 +33,9 @@ func TestGenerateI18nFrontend(t *testing.T) {
|
||||
applyToOtherLanguage("frontend", "ru", data)
|
||||
applyToOtherLanguage("frontend", "vi", data)
|
||||
applyToOtherLanguage("frontend", "pt", data)
|
||||
applyToOtherLanguage("frontend", "it", data)
|
||||
applyToOtherLanguage("frontend", "ms", data)
|
||||
applyToOtherLanguage("frontend", "tr", data)
|
||||
}
|
||||
|
||||
func TestGenerateI18nBackend(t *testing.T) {
|
||||
@@ -49,4 +52,7 @@ func TestGenerateI18nBackend(t *testing.T) {
|
||||
applyToOtherLanguage("backend", "ru", data)
|
||||
applyToOtherLanguage("backend", "vi", data)
|
||||
applyToOtherLanguage("backend", "pt", data)
|
||||
applyToOtherLanguage("backend", "it", data)
|
||||
applyToOtherLanguage("backend", "ms", data)
|
||||
applyToOtherLanguage("backend", "tr", data)
|
||||
}
|
||||
|
@@ -24,14 +24,6 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Service %s und %s stimmen nicht überein"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Zugehörigkeit darf nicht leer sein",
|
||||
"DisplayName cannot be blank": "Anzeigename kann nicht leer sein",
|
||||
|
@@ -24,14 +24,6 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"DisplayName cannot be blank": "DisplayName cannot be blank",
|
||||
|
@@ -24,14 +24,6 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Los servicios %s y %s no coinciden"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Afiliación no puede estar en blanco",
|
||||
"DisplayName cannot be blank": "El nombre de visualización no puede estar en blanco",
|
||||
|
@@ -24,14 +24,6 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Les services %s et %s ne correspondent pas"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation ne peut pas être vide",
|
||||
"DisplayName cannot be blank": "Le nom d'affichage ne peut pas être vide",
|
||||
|
@@ -24,14 +24,6 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Layanan %s dan %s tidak cocok"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Keterkaitan tidak boleh kosong",
|
||||
"DisplayName cannot be blank": "Nama Pengguna tidak boleh kosong",
|
||||
|
150
i18n/locales/it/data.json
Normal file
150
i18n/locales/it/data.json
Normal file
@@ -0,0 +1,150 @@
|
||||
{
|
||||
"account": {
|
||||
"Failed to add user": "Failed to add user",
|
||||
"Get init score failed, error: %w": "Get init score failed, error: %w",
|
||||
"Please sign out first": "Please sign out first",
|
||||
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
|
||||
},
|
||||
"auth": {
|
||||
"Challenge method should be S256": "Challenge method should be S256",
|
||||
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
|
||||
"Failed to login in: %s": "Failed to login in: %s",
|
||||
"Invalid token": "Invalid token",
|
||||
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
||||
},
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"DisplayName cannot be blank": "DisplayName cannot be blank",
|
||||
"DisplayName is not valid real name": "DisplayName is not valid real name",
|
||||
"Email already exists": "Email already exists",
|
||||
"Email cannot be empty": "Email cannot be empty",
|
||||
"Email is invalid": "Email is invalid",
|
||||
"Empty username.": "Empty username.",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||
"LastName cannot be blank": "LastName cannot be blank",
|
||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
"Session outdated, please login again": "Session outdated, please login again",
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||
"password or code is incorrect": "password or code is incorrect",
|
||||
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
|
||||
"unsupported password type: %s": "unsupported password type: %s"
|
||||
},
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Ldap server exist"
|
||||
},
|
||||
"link": {
|
||||
"Please link first": "Please link first",
|
||||
"This application has no providers": "This application has no providers",
|
||||
"This application has no providers of type": "This application has no providers of type",
|
||||
"This provider can't be unlinked": "This provider can't be unlinked",
|
||||
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
|
||||
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
|
||||
},
|
||||
"organization": {
|
||||
"Only admin can modify the %s.": "Only admin can modify the %s.",
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
},
|
||||
"resource": {
|
||||
"User is nil for tag: avatar": "User is nil for tag: avatar",
|
||||
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
|
||||
},
|
||||
"saml": {
|
||||
"Application %s not found": "Application %s not found"
|
||||
},
|
||||
"saml_sp": {
|
||||
"provider %s's category is not SAML": "provider %s's category is not SAML"
|
||||
},
|
||||
"service": {
|
||||
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
|
||||
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
|
||||
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
|
||||
"The provider type: %s is not supported": "The provider type: %s is not supported"
|
||||
},
|
||||
"token": {
|
||||
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
|
||||
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
|
||||
"Invalid client_id": "Invalid client_id",
|
||||
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
|
||||
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Display name cannot be empty",
|
||||
"New password cannot contain blank space.": "New password cannot contain blank space."
|
||||
},
|
||||
"user_upload": {
|
||||
"Failed to import users": "Failed to import users"
|
||||
},
|
||||
"util": {
|
||||
"No application is found for userId: %s": "No application is found for userId: %s",
|
||||
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"verification": {
|
||||
"Code has not been sent yet!": "Code has not been sent yet!",
|
||||
"Invalid captcha provider.": "Invalid captcha provider.",
|
||||
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
|
||||
"Turing test failed.": "Turing test failed.",
|
||||
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
|
||||
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
|
||||
"Unknown type": "Unknown type",
|
||||
"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"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
|
||||
}
|
||||
}
|
@@ -24,14 +24,6 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "サービス%sと%sは一致しません"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "所属は空白にできません",
|
||||
"DisplayName cannot be blank": "表示名は空白にできません",
|
||||
|
@@ -24,14 +24,6 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "서비스 %s와 %s는 일치하지 않습니다"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "소속은 비워 둘 수 없습니다",
|
||||
"DisplayName cannot be blank": "DisplayName는 비어 있을 수 없습니다",
|
||||
|
150
i18n/locales/ms/data.json
Normal file
150
i18n/locales/ms/data.json
Normal file
@@ -0,0 +1,150 @@
|
||||
{
|
||||
"account": {
|
||||
"Failed to add user": "Failed to add user",
|
||||
"Get init score failed, error: %w": "Get init score failed, error: %w",
|
||||
"Please sign out first": "Please sign out first",
|
||||
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
|
||||
},
|
||||
"auth": {
|
||||
"Challenge method should be S256": "Challenge method should be S256",
|
||||
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
|
||||
"Failed to login in: %s": "Failed to login in: %s",
|
||||
"Invalid token": "Invalid token",
|
||||
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
||||
},
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"DisplayName cannot be blank": "DisplayName cannot be blank",
|
||||
"DisplayName is not valid real name": "DisplayName is not valid real name",
|
||||
"Email already exists": "Email already exists",
|
||||
"Email cannot be empty": "Email cannot be empty",
|
||||
"Email is invalid": "Email is invalid",
|
||||
"Empty username.": "Empty username.",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||
"LastName cannot be blank": "LastName cannot be blank",
|
||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
"Session outdated, please login again": "Session outdated, please login again",
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||
"password or code is incorrect": "password or code is incorrect",
|
||||
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
|
||||
"unsupported password type: %s": "unsupported password type: %s"
|
||||
},
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Ldap server exist"
|
||||
},
|
||||
"link": {
|
||||
"Please link first": "Please link first",
|
||||
"This application has no providers": "This application has no providers",
|
||||
"This application has no providers of type": "This application has no providers of type",
|
||||
"This provider can't be unlinked": "This provider can't be unlinked",
|
||||
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
|
||||
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
|
||||
},
|
||||
"organization": {
|
||||
"Only admin can modify the %s.": "Only admin can modify the %s.",
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
},
|
||||
"resource": {
|
||||
"User is nil for tag: avatar": "User is nil for tag: avatar",
|
||||
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
|
||||
},
|
||||
"saml": {
|
||||
"Application %s not found": "Application %s not found"
|
||||
},
|
||||
"saml_sp": {
|
||||
"provider %s's category is not SAML": "provider %s's category is not SAML"
|
||||
},
|
||||
"service": {
|
||||
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
|
||||
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
|
||||
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
|
||||
"The provider type: %s is not supported": "The provider type: %s is not supported"
|
||||
},
|
||||
"token": {
|
||||
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
|
||||
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
|
||||
"Invalid client_id": "Invalid client_id",
|
||||
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
|
||||
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Display name cannot be empty",
|
||||
"New password cannot contain blank space.": "New password cannot contain blank space."
|
||||
},
|
||||
"user_upload": {
|
||||
"Failed to import users": "Failed to import users"
|
||||
},
|
||||
"util": {
|
||||
"No application is found for userId: %s": "No application is found for userId: %s",
|
||||
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"verification": {
|
||||
"Code has not been sent yet!": "Code has not been sent yet!",
|
||||
"Invalid captcha provider.": "Invalid captcha provider.",
|
||||
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
|
||||
"Turing test failed.": "Turing test failed.",
|
||||
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
|
||||
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
|
||||
"Unknown type": "Unknown type",
|
||||
"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"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
|
||||
}
|
||||
}
|
@@ -24,14 +24,6 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"DisplayName cannot be blank": "DisplayName cannot be blank",
|
||||
|
@@ -24,14 +24,6 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Сервисы %s и %s не совпадают"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Принадлежность не может быть пустым значением",
|
||||
"DisplayName cannot be blank": "Имя отображения не может быть пустым",
|
||||
|
150
i18n/locales/tr/data.json
Normal file
150
i18n/locales/tr/data.json
Normal file
@@ -0,0 +1,150 @@
|
||||
{
|
||||
"account": {
|
||||
"Failed to add user": "Failed to add user",
|
||||
"Get init score failed, error: %w": "Get init score failed, error: %w",
|
||||
"Please sign out first": "Please sign out first",
|
||||
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
|
||||
},
|
||||
"auth": {
|
||||
"Challenge method should be S256": "Challenge method should be S256",
|
||||
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
|
||||
"Failed to login in: %s": "Failed to login in: %s",
|
||||
"Invalid token": "Invalid token",
|
||||
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
|
||||
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||
"The application: %s does not exist": "The application: %s does not exist",
|
||||
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||
"Unauthorized operation": "Unauthorized operation",
|
||||
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
|
||||
"User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags"
|
||||
},
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||
"DisplayName cannot be blank": "DisplayName cannot be blank",
|
||||
"DisplayName is not valid real name": "DisplayName is not valid real name",
|
||||
"Email already exists": "Email already exists",
|
||||
"Email cannot be empty": "Email cannot be empty",
|
||||
"Email is invalid": "Email is invalid",
|
||||
"Empty username.": "Empty username.",
|
||||
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||
"LastName cannot be blank": "LastName cannot be blank",
|
||||
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||
"Organization does not exist": "Organization does not exist",
|
||||
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||
"Phone already exists": "Phone already exists",
|
||||
"Phone cannot be empty": "Phone cannot be empty",
|
||||
"Phone number is invalid": "Phone number is invalid",
|
||||
"Session outdated, please login again": "Session outdated, please login again",
|
||||
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
|
||||
"Username already exists": "Username already exists",
|
||||
"Username cannot be an email address": "Username cannot be an email address",
|
||||
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
||||
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||
"password or code is incorrect": "password or code is incorrect",
|
||||
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
|
||||
"unsupported password type: %s": "unsupported password type: %s"
|
||||
},
|
||||
"general": {
|
||||
"Missing parameter": "Missing parameter",
|
||||
"Please login first": "Please login first",
|
||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||
},
|
||||
"ldap": {
|
||||
"Ldap server exist": "Ldap server exist"
|
||||
},
|
||||
"link": {
|
||||
"Please link first": "Please link first",
|
||||
"This application has no providers": "This application has no providers",
|
||||
"This application has no providers of type": "This application has no providers of type",
|
||||
"This provider can't be unlinked": "This provider can't be unlinked",
|
||||
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
|
||||
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
|
||||
},
|
||||
"organization": {
|
||||
"Only admin can modify the %s.": "Only admin can modify the %s.",
|
||||
"The %s is immutable.": "The %s is immutable.",
|
||||
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||
},
|
||||
"provider": {
|
||||
"Invalid application id": "Invalid application id",
|
||||
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||
},
|
||||
"resource": {
|
||||
"User is nil for tag: avatar": "User is nil for tag: avatar",
|
||||
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
|
||||
},
|
||||
"saml": {
|
||||
"Application %s not found": "Application %s not found"
|
||||
},
|
||||
"saml_sp": {
|
||||
"provider %s's category is not SAML": "provider %s's category is not SAML"
|
||||
},
|
||||
"service": {
|
||||
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
|
||||
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
|
||||
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
|
||||
},
|
||||
"storage": {
|
||||
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
|
||||
"The provider type: %s is not supported": "The provider type: %s is not supported"
|
||||
},
|
||||
"token": {
|
||||
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
|
||||
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
|
||||
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
|
||||
"Invalid client_id": "Invalid client_id",
|
||||
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
|
||||
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Display name cannot be empty",
|
||||
"New password cannot contain blank space.": "New password cannot contain blank space."
|
||||
},
|
||||
"user_upload": {
|
||||
"Failed to import users": "Failed to import users"
|
||||
},
|
||||
"util": {
|
||||
"No application is found for userId: %s": "No application is found for userId: %s",
|
||||
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"verification": {
|
||||
"Code has not been sent yet!": "Code has not been sent yet!",
|
||||
"Invalid captcha provider.": "Invalid captcha provider.",
|
||||
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
|
||||
"Turing test failed.": "Turing test failed.",
|
||||
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
|
||||
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
|
||||
"Unknown type": "Unknown type",
|
||||
"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"
|
||||
},
|
||||
"webauthn": {
|
||||
"Found no credentials for this user": "Found no credentials for this user",
|
||||
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
|
||||
}
|
||||
}
|
@@ -24,14 +24,6 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "Dịch sang tiếng Việt: Dịch vụ %s và %s không khớp"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "Tình trạng liên kết không thể để trống",
|
||||
"DisplayName cannot be blank": "Tên hiển thị không thể để trống",
|
||||
|
@@ -24,14 +24,6 @@
|
||||
"cas": {
|
||||
"Service %s and %s do not match": "服务%s与%s不匹配"
|
||||
},
|
||||
"chat": {
|
||||
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||
"The chat: %s is not found": "The chat: %s is not found",
|
||||
"The message is invalid": "The message is invalid",
|
||||
"The message: %s is not found": "The message: %s is not found",
|
||||
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||
"The provider: %s is not found": "The provider: %s is not found"
|
||||
},
|
||||
"check": {
|
||||
"Affiliation cannot be blank": "工作单位不可为空",
|
||||
"DisplayName cannot be blank": "显示名称不可为空",
|
||||
|
@@ -132,7 +132,7 @@ func (idp *GiteeIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
"type": "User",
|
||||
"blog": null,
|
||||
"weibo": null,
|
||||
"bio": "个人博客:https://gitee.com/xxx/xxx/pages",
|
||||
"bio": "bio",
|
||||
"public_repos": 2,
|
||||
"public_gists": 0,
|
||||
"followers": 0,
|
||||
|
@@ -9,11 +9,11 @@
|
||||
"passwordType": "plain",
|
||||
"passwordSalt": "",
|
||||
"passwordOptions": ["AtLeast6"],
|
||||
"countryCodes": ["US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN"],
|
||||
"countryCodes": ["US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN", "IT", "MY", "TR"],
|
||||
"defaultAvatar": "",
|
||||
"defaultApplication": "",
|
||||
"tags": [],
|
||||
"languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi"],
|
||||
"languages": ["en", "zh", "es", "fr", "de", "id", "ja", "ko", "ru", "vi", "it", "ms", "tr"],
|
||||
"masterPassword": "",
|
||||
"initScore": 2000,
|
||||
"enableSoftDeletion": false,
|
||||
|
1
main.go
1
main.go
@@ -49,6 +49,7 @@ func main() {
|
||||
object.InitLdapAutoSynchronizer()
|
||||
proxy.InitHttpClient()
|
||||
authz.InitApi()
|
||||
object.InitUserManager()
|
||||
|
||||
util.SafeGoroutine(func() { object.RunSyncUsersJob() })
|
||||
|
||||
|
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/casdoor/casdoor/util"
|
||||
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
||||
"github.com/xorm-io/core"
|
||||
"github.com/xorm-io/xorm"
|
||||
)
|
||||
|
||||
type Adapter struct {
|
||||
@@ -33,13 +34,12 @@ type Adapter struct {
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
DatabaseType string `xorm:"varchar(100)" json:"databaseType"`
|
||||
Host string `xorm:"varchar(100)" json:"host"`
|
||||
Port string `xorm:"varchar(20)" json:"port"`
|
||||
Port int `json:"port"`
|
||||
User string `xorm:"varchar(100)" json:"user"`
|
||||
Password string `xorm:"varchar(100)" json:"password"`
|
||||
Database string `xorm:"varchar(100)" json:"database"`
|
||||
Table string `xorm:"varchar(100)" json:"table"`
|
||||
TableNamePrefix string `xorm:"varchar(100)" json:"tableNamePrefix"`
|
||||
File string `xorm:"varchar(100)" json:"file"`
|
||||
|
||||
IsEnabled bool `json:"isEnabled"`
|
||||
|
||||
@@ -154,24 +154,27 @@ func (adapter *Adapter) initAdapter() error {
|
||||
if adapter.Adapter == nil {
|
||||
var dataSourceName string
|
||||
|
||||
if adapter.buildInAdapter() {
|
||||
if adapter.builtInAdapter() {
|
||||
dataSourceName = conf.GetConfigString("dataSourceName")
|
||||
if adapter.DatabaseType == "mysql" {
|
||||
dataSourceName = dataSourceName + adapter.Database
|
||||
}
|
||||
} else {
|
||||
switch adapter.DatabaseType {
|
||||
case "mssql":
|
||||
dataSourceName = fmt.Sprintf("sqlserver://%s:%s@%s:%s?database=%s", adapter.User,
|
||||
dataSourceName = fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s", adapter.User,
|
||||
adapter.Password, adapter.Host, adapter.Port, adapter.Database)
|
||||
case "mysql":
|
||||
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%s)/", adapter.User,
|
||||
adapter.Password, adapter.Host, adapter.Port)
|
||||
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", adapter.User,
|
||||
adapter.Password, adapter.Host, adapter.Port, adapter.Database)
|
||||
case "postgres":
|
||||
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%s sslmode=disable dbname=%s", adapter.User,
|
||||
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%d sslmode=disable dbname=%s", adapter.User,
|
||||
adapter.Password, adapter.Host, adapter.Port, adapter.Database)
|
||||
case "CockroachDB":
|
||||
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%s sslmode=disable dbname=%s serial_normalization=virtual_sequence",
|
||||
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%d sslmode=disable dbname=%s serial_normalization=virtual_sequence",
|
||||
adapter.User, adapter.Password, adapter.Host, adapter.Port, adapter.Database)
|
||||
case "sqlite3":
|
||||
dataSourceName = fmt.Sprintf("file:%s", adapter.File)
|
||||
dataSourceName = fmt.Sprintf("file:%s", adapter.Host)
|
||||
default:
|
||||
return fmt.Errorf("unsupported database type: %s", adapter.DatabaseType)
|
||||
}
|
||||
@@ -182,7 +185,8 @@ func (adapter *Adapter) initAdapter() error {
|
||||
}
|
||||
|
||||
var err error
|
||||
adapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(NewAdapter(adapter.DatabaseType, dataSourceName, adapter.Database).Engine, adapter.getTable(), adapter.TableNamePrefix)
|
||||
engine, err := xorm.NewEngine(adapter.DatabaseType, dataSourceName)
|
||||
adapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(engine, adapter.getTable(), adapter.TableNamePrefix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -270,6 +274,11 @@ func UpdatePolicy(oldPolicy, newPolicy []string, adapter *Adapter) (bool, error)
|
||||
if err != nil {
|
||||
return affected, err
|
||||
}
|
||||
err = adapter.SavePolicy(casbinModel)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected, nil
|
||||
}
|
||||
|
||||
@@ -286,6 +295,10 @@ func AddPolicy(policy []string, adapter *Adapter) (bool, error) {
|
||||
}
|
||||
|
||||
casbinModel.AddPolicy("p", "p", policy)
|
||||
err = adapter.SavePolicy(casbinModel)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
@@ -306,15 +319,20 @@ func RemovePolicy(policy []string, adapter *Adapter) (bool, error) {
|
||||
if err != nil {
|
||||
return affected, err
|
||||
}
|
||||
err = adapter.SavePolicy(casbinModel)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected, nil
|
||||
}
|
||||
|
||||
func (adapter *Adapter) buildInAdapter() bool {
|
||||
func (adapter *Adapter) builtInAdapter() bool {
|
||||
if adapter.Owner != "built-in" {
|
||||
return false
|
||||
}
|
||||
|
||||
return adapter.Name == "permission-adapter-built-in" || adapter.Name == "api-adapter-built-in"
|
||||
return adapter.Name == "user-adapter-built-in" || adapter.Name == "api-adapter-built-in"
|
||||
}
|
||||
|
||||
func getModelDef() model.Model {
|
||||
|
@@ -162,7 +162,7 @@ func UpdateCert(id string, cert *Cert) (bool, error) {
|
||||
if name != cert.Name {
|
||||
err := certChangeTrigger(name, cert.Name)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(cert)
|
||||
|
166
object/chat.go
166
object/chat.go
@@ -1,166 +0,0 @@
|
||||
// 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 object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
)
|
||||
|
||||
type Chat struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
||||
|
||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
Category string `xorm:"varchar(100)" json:"category"`
|
||||
User1 string `xorm:"varchar(100)" json:"user1"`
|
||||
User2 string `xorm:"varchar(100)" json:"user2"`
|
||||
Users []string `xorm:"varchar(100)" json:"users"`
|
||||
MessageCount int `json:"messageCount"`
|
||||
}
|
||||
|
||||
func GetMaskedChat(chat *Chat, err ...error) (*Chat, error) {
|
||||
if len(err) > 0 && err[0] != nil {
|
||||
return nil, err[0]
|
||||
}
|
||||
|
||||
if chat == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return chat, nil
|
||||
}
|
||||
|
||||
func GetMaskedChats(chats []*Chat, errs ...error) ([]*Chat, error) {
|
||||
if len(errs) > 0 && errs[0] != nil {
|
||||
return nil, errs[0]
|
||||
}
|
||||
var err error
|
||||
for _, chat := range chats {
|
||||
chat, err = GetMaskedChat(chat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return chats, nil
|
||||
}
|
||||
|
||||
func GetChatCount(owner, field, value string) (int64, error) {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
return session.Count(&Chat{})
|
||||
}
|
||||
|
||||
func GetChats(owner string) ([]*Chat, error) {
|
||||
chats := []*Chat{}
|
||||
err := ormer.Engine.Desc("created_time").Find(&chats, &Chat{Owner: owner})
|
||||
if err != nil {
|
||||
return chats, err
|
||||
}
|
||||
|
||||
return chats, nil
|
||||
}
|
||||
|
||||
func GetPaginationChats(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Chat, error) {
|
||||
chats := []*Chat{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&chats)
|
||||
if err != nil {
|
||||
return chats, err
|
||||
}
|
||||
|
||||
return chats, nil
|
||||
}
|
||||
|
||||
func getChat(owner string, name string) (*Chat, error) {
|
||||
if owner == "" || name == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
chat := Chat{Owner: owner, Name: name}
|
||||
existed, err := ormer.Engine.Get(&chat)
|
||||
if err != nil {
|
||||
return &chat, err
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &chat, nil
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetChat(id string) (*Chat, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
return getChat(owner, name)
|
||||
}
|
||||
|
||||
func UpdateChat(id string, chat *Chat) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
if c, err := getChat(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if c == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(chat)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func AddChat(chat *Chat) (bool, error) {
|
||||
if chat.Type == "AI" && chat.User2 == "" {
|
||||
provider, err := getDefaultAiProvider()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if provider != nil {
|
||||
chat.User2 = provider.Name
|
||||
}
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.Insert(chat)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func DeleteChat(chat *Chat) (bool, error) {
|
||||
affected, err := ormer.Engine.ID(core.PK{chat.Owner, chat.Name}).Delete(&Chat{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if affected != 0 {
|
||||
return DeleteChatMessages(chat.Name)
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func (p *Chat) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", p.Owner, p.Name)
|
||||
}
|
@@ -15,7 +15,7 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
@@ -120,44 +120,50 @@ func DeleteEnforcer(enforcer *Enforcer) (bool, error) {
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func (enforcer *Enforcer) InitEnforcer() (*casbin.Enforcer, error) {
|
||||
if enforcer == nil {
|
||||
return nil, errors.New("enforcer is nil")
|
||||
}
|
||||
if enforcer.Model == "" || enforcer.Adapter == "" {
|
||||
return nil, errors.New("missing model or adapter")
|
||||
func (p *Enforcer) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", p.Owner, p.Name)
|
||||
}
|
||||
|
||||
func (enforcer *Enforcer) InitEnforcer() error {
|
||||
if enforcer.Enforcer != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
var m *Model
|
||||
var a *Adapter
|
||||
if enforcer.Model == "" {
|
||||
return fmt.Errorf("the model for enforcer: %s should not be empty", enforcer.GetId())
|
||||
}
|
||||
if enforcer.Adapter == "" {
|
||||
return fmt.Errorf("the adapter for enforcer: %s should not be empty", enforcer.GetId())
|
||||
}
|
||||
|
||||
if m, err = GetModel(enforcer.Model); err != nil {
|
||||
return nil, err
|
||||
m, err := GetModel(enforcer.Model)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if m == nil {
|
||||
return nil, errors.New("model not found")
|
||||
return fmt.Errorf("the model: %s for enforcer: %s is not found", enforcer.Model, enforcer.GetId())
|
||||
}
|
||||
|
||||
if a, err = GetAdapter(enforcer.Adapter); err != nil {
|
||||
return nil, err
|
||||
a, err := GetAdapter(enforcer.Adapter)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if a == nil {
|
||||
return nil, errors.New("adapter not found")
|
||||
return fmt.Errorf("the adapter: %s for enforcer: %s is not found", enforcer.Adapter, enforcer.GetId())
|
||||
}
|
||||
|
||||
err = m.initModel()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
err = a.initAdapter()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
e, err := casbin.NewEnforcer(m.Model, a.Adapter)
|
||||
casbinEnforcer, err := casbin.NewEnforcer(m.Model, a.Adapter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
|
||||
return e, nil
|
||||
enforcer.Enforcer = casbinEnforcer
|
||||
return nil
|
||||
}
|
||||
|
@@ -164,7 +164,7 @@ func DeleteGroup(group *Group) (bool, error) {
|
||||
return false, errors.New("group has children group")
|
||||
}
|
||||
|
||||
if count, err := GetGroupUserCount(group.Name, "", ""); err != nil {
|
||||
if count, err := GetGroupUserCount(group.GetId(), "", ""); err != nil {
|
||||
return false, err
|
||||
} else if count > 0 {
|
||||
return false, errors.New("group has users")
|
||||
@@ -214,39 +214,33 @@ func ConvertToTreeData(groups []*Group, parentId string) []*Group {
|
||||
return treeData
|
||||
}
|
||||
|
||||
func RemoveUserFromGroup(owner, name, groupName string) (bool, error) {
|
||||
user, err := getUser(owner, name)
|
||||
func GetGroupUserCount(groupId string, field, value string) (int64, error) {
|
||||
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
||||
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if user == nil {
|
||||
return false, errors.New("user not exist")
|
||||
return 0, err
|
||||
}
|
||||
|
||||
user.Groups = util.DeleteVal(user.Groups, groupName)
|
||||
affected, err := updateUser(user.GetId(), user, []string{"groups"})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return affected != 0, err
|
||||
}
|
||||
|
||||
func GetGroupUserCount(groupName string, field, value string) (int64, error) {
|
||||
if field == "" && value == "" {
|
||||
return ormer.Engine.Where(builder.Like{"`groups`", groupName}).
|
||||
Count(&User{})
|
||||
return int64(len(names)), nil
|
||||
} else {
|
||||
return ormer.Engine.Table("user").
|
||||
Where(builder.Like{"`groups`", groupName}).
|
||||
Where("owner = ?", owner).In("name", names).
|
||||
And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%").
|
||||
Count()
|
||||
}
|
||||
}
|
||||
|
||||
func GetPaginationGroupUsers(groupName string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
||||
func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
||||
users := []*User{}
|
||||
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
||||
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session := ormer.Engine.Table("user").
|
||||
Where(builder.Like{"`groups`", groupName + "\""})
|
||||
Where("owner = ?", owner).In("name", names)
|
||||
|
||||
if offset != -1 && limit != -1 {
|
||||
session.Limit(limit, offset)
|
||||
@@ -265,7 +259,7 @@ func GetPaginationGroupUsers(groupName string, offset, limit int, field, value,
|
||||
session = session.Desc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
|
||||
}
|
||||
|
||||
err := session.Find(&users)
|
||||
err = session.Find(&users)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -273,15 +267,15 @@ func GetPaginationGroupUsers(groupName string, offset, limit int, field, value,
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetGroupUsers(groupName string) ([]*User, error) {
|
||||
func GetGroupUsers(groupId string) ([]*User, error) {
|
||||
users := []*User{}
|
||||
err := ormer.Engine.Table("user").
|
||||
Where(builder.Like{"`groups`", groupName + "\""}).
|
||||
Find(&users)
|
||||
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
||||
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||
|
||||
err = ormer.Engine.Where("owner = ?", owner).In("name", names).Find(&users)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
|
@@ -37,11 +37,11 @@ func InitDb() {
|
||||
|
||||
existed = initBuiltInApiModel()
|
||||
if !existed {
|
||||
initBuildInApiAdapter()
|
||||
initBuiltInApiAdapter()
|
||||
initBuiltInApiEnforcer()
|
||||
initBuiltInPermissionModel()
|
||||
initBuildInPermissionAdapter()
|
||||
initBuiltInPermissionEnforcer()
|
||||
initBuiltInUserModel()
|
||||
initBuiltInUserAdapter()
|
||||
initBuiltInUserEnforcer()
|
||||
}
|
||||
|
||||
initWebAuthn()
|
||||
@@ -303,8 +303,8 @@ func initWebAuthn() {
|
||||
gob.Register(webauthn.SessionData{})
|
||||
}
|
||||
|
||||
func initBuiltInPermissionModel() {
|
||||
model, err := GetModel("built-in/permission-model-built-in")
|
||||
func initBuiltInUserModel() {
|
||||
model, err := GetModel("built-in/user-model-built-in")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -315,7 +315,7 @@ func initBuiltInPermissionModel() {
|
||||
|
||||
model = &Model{
|
||||
Owner: "built-in",
|
||||
Name: "permission-model-built-in",
|
||||
Name: "user-model-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: "Built-in Model",
|
||||
IsEnabled: true,
|
||||
@@ -325,11 +325,14 @@ r = sub, obj, act
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act`,
|
||||
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`,
|
||||
}
|
||||
_, err = AddModel(model)
|
||||
if err != nil {
|
||||
@@ -347,8 +350,7 @@ func initBuiltInApiModel() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
modelText := `
|
||||
[request_definition]
|
||||
modelText := `[request_definition]
|
||||
r = subOwner, subName, method, urlPath, objOwner, objName
|
||||
|
||||
[policy_definition]
|
||||
@@ -367,8 +369,7 @@ m = (r.subOwner == p.subOwner || p.subOwner == "*") && \
|
||||
(r.urlPath == p.urlPath || p.urlPath == "*") && \
|
||||
(r.objOwner == p.objOwner || p.objOwner == "*") && \
|
||||
(r.objName == p.objName || p.objName == "*") || \
|
||||
(r.subOwner == r.objOwner && r.subName == r.objName)
|
||||
`
|
||||
(r.subOwner == r.objOwner && r.subName == r.objName)`
|
||||
|
||||
model = &Model{
|
||||
Owner: "built-in",
|
||||
@@ -415,19 +416,19 @@ func initBuiltInPermission() {
|
||||
}
|
||||
}
|
||||
|
||||
func initBuildInPermissionAdapter() {
|
||||
permissionAdapter, err := GetAdapter("built-in/permission-adapter-built-in")
|
||||
func initBuiltInUserAdapter() {
|
||||
adapter, err := GetAdapter("built-in/user-adapter-built-in")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if permissionAdapter != nil {
|
||||
if adapter != nil {
|
||||
return
|
||||
}
|
||||
|
||||
permissionAdapter = &Adapter{
|
||||
adapter = &Adapter{
|
||||
Owner: "built-in",
|
||||
Name: "permission-adapter-built-in",
|
||||
Name: "user-adapter-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Type: "Database",
|
||||
DatabaseType: conf.GetConfigString("driverName"),
|
||||
@@ -436,23 +437,23 @@ func initBuildInPermissionAdapter() {
|
||||
Table: "casbin_user_rule",
|
||||
IsEnabled: true,
|
||||
}
|
||||
_, err = AddAdapter(permissionAdapter)
|
||||
_, err = AddAdapter(adapter)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initBuildInApiAdapter() {
|
||||
apiAdapter, err := GetAdapter("built-in/api-adapter-built-in")
|
||||
func initBuiltInApiAdapter() {
|
||||
adapter, err := GetAdapter("built-in/api-adapter-built-in")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if apiAdapter != nil {
|
||||
if adapter != nil {
|
||||
return
|
||||
}
|
||||
|
||||
apiAdapter = &Adapter{
|
||||
adapter = &Adapter{
|
||||
Owner: "built-in",
|
||||
Name: "api-adapter-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
@@ -463,49 +464,49 @@ func initBuildInApiAdapter() {
|
||||
Table: "casbin_api_rule",
|
||||
IsEnabled: true,
|
||||
}
|
||||
_, err = AddAdapter(apiAdapter)
|
||||
_, err = AddAdapter(adapter)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initBuiltInPermissionEnforcer() {
|
||||
permissionEnforcer, err := GetEnforcer("built-in/permission-enforcer-built-in")
|
||||
func initBuiltInUserEnforcer() {
|
||||
enforcer, err := GetEnforcer("built-in/user-enforcer-built-in")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if permissionEnforcer != nil {
|
||||
if enforcer != nil {
|
||||
return
|
||||
}
|
||||
|
||||
permissionEnforcer = &Enforcer{
|
||||
enforcer = &Enforcer{
|
||||
Owner: "built-in",
|
||||
Name: "permission-enforcer-built-in",
|
||||
Name: "user-enforcer-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: "Permission Enforcer",
|
||||
Model: "built-in/permission-model-built-in",
|
||||
Adapter: "built-in/permission-adapter-built-in",
|
||||
Model: "built-in/user-model-built-in",
|
||||
Adapter: "built-in/user-adapter-built-in",
|
||||
IsEnabled: true,
|
||||
}
|
||||
|
||||
_, err = AddEnforcer(permissionEnforcer)
|
||||
_, err = AddEnforcer(enforcer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initBuiltInApiEnforcer() {
|
||||
apiEnforcer, err := GetEnforcer("built-in/api-enforcer-built-in")
|
||||
enforcer, err := GetEnforcer("built-in/api-enforcer-built-in")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if apiEnforcer != nil {
|
||||
if enforcer != nil {
|
||||
return
|
||||
}
|
||||
|
||||
apiEnforcer = &Enforcer{
|
||||
enforcer = &Enforcer{
|
||||
Owner: "built-in",
|
||||
Name: "api-enforcer-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
@@ -515,7 +516,7 @@ func initBuiltInApiEnforcer() {
|
||||
IsEnabled: true,
|
||||
}
|
||||
|
||||
_, err = AddEnforcer(apiEnforcer)
|
||||
_, err = AddEnforcer(enforcer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@@ -41,6 +41,7 @@ type LdapUser struct {
|
||||
GidNumber string `json:"gidNumber"`
|
||||
// Gcn string
|
||||
Uuid string `json:"uuid"`
|
||||
UserPrincipalName string `json:"userPrincipalName"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Mail string
|
||||
Email string `json:"email"`
|
||||
@@ -51,9 +52,10 @@ type LdapUser struct {
|
||||
RegisteredAddress string
|
||||
PostalAddress string
|
||||
|
||||
GroupId string `json:"groupId"`
|
||||
Phone string `json:"phone"`
|
||||
Address string `json:"address"`
|
||||
GroupId string `json:"groupId"`
|
||||
Phone string `json:"phone"`
|
||||
Address string `json:"address"`
|
||||
MemberOf string `json:"memberOf"`
|
||||
}
|
||||
|
||||
func (ldap *Ldap) GetLdapConn() (c *LdapConn, err error) {
|
||||
@@ -168,6 +170,8 @@ func (l *LdapConn) GetLdapUsers(ldapServer *Ldap) ([]LdapUser, error) {
|
||||
user.Uuid = attribute.Values[0]
|
||||
case "objectGUID":
|
||||
user.Uuid = attribute.Values[0]
|
||||
case "userPrincipalName":
|
||||
user.UserPrincipalName = attribute.Values[0]
|
||||
case "displayName":
|
||||
user.DisplayName = attribute.Values[0]
|
||||
case "mail":
|
||||
@@ -186,6 +190,8 @@ func (l *LdapConn) GetLdapUsers(ldapServer *Ldap) ([]LdapUser, error) {
|
||||
user.RegisteredAddress = attribute.Values[0]
|
||||
case "postalAddress":
|
||||
user.PostalAddress = attribute.Values[0]
|
||||
case "memberOf":
|
||||
user.MemberOf = attribute.Values[0]
|
||||
}
|
||||
}
|
||||
ldapUsers = append(ldapUsers, user)
|
||||
|
@@ -1,143 +0,0 @@
|
||||
// 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 object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
|
||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||
Chat string `xorm:"varchar(100) index" json:"chat"`
|
||||
ReplyTo string `xorm:"varchar(100) index" json:"replyTo"`
|
||||
Author string `xorm:"varchar(100)" json:"author"`
|
||||
Text string `xorm:"mediumtext" json:"text"`
|
||||
}
|
||||
|
||||
func GetMaskedMessage(message *Message) *Message {
|
||||
if message == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
|
||||
func GetMaskedMessages(messages []*Message) []*Message {
|
||||
for _, message := range messages {
|
||||
message = GetMaskedMessage(message)
|
||||
}
|
||||
return messages
|
||||
}
|
||||
|
||||
func GetMessageCount(owner, organization, field, value string) (int64, error) {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
return session.Count(&Message{Organization: organization})
|
||||
}
|
||||
|
||||
func GetMessages(owner string) ([]*Message, error) {
|
||||
messages := []*Message{}
|
||||
err := ormer.Engine.Desc("created_time").Find(&messages, &Message{Owner: owner})
|
||||
return messages, err
|
||||
}
|
||||
|
||||
func GetChatMessages(chat string) ([]*Message, error) {
|
||||
messages := []*Message{}
|
||||
err := ormer.Engine.Asc("created_time").Find(&messages, &Message{Chat: chat})
|
||||
return messages, err
|
||||
}
|
||||
|
||||
func GetPaginationMessages(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) ([]*Message, error) {
|
||||
messages := []*Message{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&messages, &Message{Organization: organization})
|
||||
return messages, err
|
||||
}
|
||||
|
||||
func getMessage(owner string, name string) (*Message, error) {
|
||||
if owner == "" || name == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
message := Message{Owner: owner, Name: name}
|
||||
existed, err := ormer.Engine.Get(&message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existed {
|
||||
return &message, nil
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func GetMessage(id string) (*Message, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
return getMessage(owner, name)
|
||||
}
|
||||
|
||||
func UpdateMessage(id string, message *Message) (bool, error) {
|
||||
owner, name := util.GetOwnerAndNameFromId(id)
|
||||
if m, err := getMessage(owner, name); err != nil {
|
||||
return false, err
|
||||
} else if m == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(message)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func AddMessage(message *Message) (bool, error) {
|
||||
affected, err := ormer.Engine.Insert(message)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func DeleteMessage(message *Message) (bool, error) {
|
||||
affected, err := ormer.Engine.ID(core.PK{message.Owner, message.Name}).Delete(&Message{})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func DeleteChatMessages(chat string) (bool, error) {
|
||||
affected, err := ormer.Engine.Delete(&Message{Chat: chat})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func (p *Message) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", p.Owner, p.Name)
|
||||
}
|
@@ -189,7 +189,7 @@ func UpdateOrganization(id string, organization *Organization) (bool, error) {
|
||||
if name != organization.Name {
|
||||
err := organizationChangeTrigger(name, organization.Name)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,8 +431,8 @@ func organizationChangeTrigger(oldName string, newName string) error {
|
||||
}
|
||||
|
||||
payment := new(Payment)
|
||||
payment.Organization = newName
|
||||
_, err = session.Where("organization=?", oldName).Update(payment)
|
||||
payment.Owner = newName
|
||||
_, err = session.Where("owner=?", oldName).Update(payment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -249,16 +249,6 @@ func (a *Ormer) createTable() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Chat))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Message))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Product))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@@ -18,6 +18,8 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/casdoor/casdoor/pp"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
)
|
||||
@@ -27,38 +29,39 @@ type Payment struct {
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
|
||||
Provider string `xorm:"varchar(100)" json:"provider"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||
User string `xorm:"varchar(100)" json:"user"`
|
||||
ProductName string `xorm:"varchar(100)" json:"productName"`
|
||||
ProductDisplayName string `xorm:"varchar(100)" json:"productDisplayName"`
|
||||
|
||||
Detail string `xorm:"varchar(255)" json:"detail"`
|
||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||
Price float64 `json:"price"`
|
||||
|
||||
PayUrl string `xorm:"varchar(2000)" json:"payUrl"`
|
||||
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
|
||||
State string `xorm:"varchar(100)" json:"state"`
|
||||
Message string `xorm:"varchar(2000)" json:"message"`
|
||||
|
||||
PersonName string `xorm:"varchar(100)" json:"personName"`
|
||||
PersonIdCard string `xorm:"varchar(100)" json:"personIdCard"`
|
||||
PersonEmail string `xorm:"varchar(100)" json:"personEmail"`
|
||||
PersonPhone string `xorm:"varchar(100)" json:"personPhone"`
|
||||
// Payment Provider Info
|
||||
Provider string `xorm:"varchar(100)" json:"provider"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
// Product Info
|
||||
ProductName string `xorm:"varchar(100)" json:"productName"`
|
||||
ProductDisplayName string `xorm:"varchar(100)" json:"productDisplayName"`
|
||||
Detail string `xorm:"varchar(255)" json:"detail"`
|
||||
Tag string `xorm:"varchar(100)" json:"tag"`
|
||||
Currency string `xorm:"varchar(100)" json:"currency"`
|
||||
Price float64 `json:"price"`
|
||||
ReturnUrl string `xorm:"varchar(1000)" json:"returnUrl"`
|
||||
// Payer Info
|
||||
User string `xorm:"varchar(100)" json:"user"`
|
||||
PersonName string `xorm:"varchar(100)" json:"personName"`
|
||||
PersonIdCard string `xorm:"varchar(100)" json:"personIdCard"`
|
||||
PersonEmail string `xorm:"varchar(100)" json:"personEmail"`
|
||||
PersonPhone string `xorm:"varchar(100)" json:"personPhone"`
|
||||
// Invoice Info
|
||||
InvoiceType string `xorm:"varchar(100)" json:"invoiceType"`
|
||||
InvoiceTitle string `xorm:"varchar(100)" json:"invoiceTitle"`
|
||||
InvoiceTaxId string `xorm:"varchar(100)" json:"invoiceTaxId"`
|
||||
InvoiceRemark string `xorm:"varchar(100)" json:"invoiceRemark"`
|
||||
InvoiceUrl string `xorm:"varchar(255)" json:"invoiceUrl"`
|
||||
// Order Info
|
||||
OutOrderId string `xorm:"varchar(100)" json:"outOrderId"`
|
||||
PayUrl string `xorm:"varchar(2000)" json:"payUrl"`
|
||||
State pp.PaymentState `xorm:"varchar(100)" json:"state"`
|
||||
Message string `xorm:"varchar(2000)" json:"message"`
|
||||
}
|
||||
|
||||
func GetPaymentCount(owner, organization, field, value string) (int64, error) {
|
||||
func GetPaymentCount(owner, field, value string) (int64, error) {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
return session.Count(&Payment{Organization: organization})
|
||||
return session.Count(&Payment{Owner: owner})
|
||||
}
|
||||
|
||||
func GetPayments(owner string) ([]*Payment, error) {
|
||||
@@ -71,9 +74,9 @@ func GetPayments(owner string) ([]*Payment, error) {
|
||||
return payments, nil
|
||||
}
|
||||
|
||||
func GetUserPayments(owner string, organization string, user string) ([]*Payment, error) {
|
||||
func GetUserPayments(owner, user string) ([]*Payment, error) {
|
||||
payments := []*Payment{}
|
||||
err := ormer.Engine.Desc("created_time").Find(&payments, &Payment{Owner: owner, Organization: organization, User: user})
|
||||
err := ormer.Engine.Desc("created_time").Find(&payments, &Payment{Owner: owner, User: user})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -81,10 +84,10 @@ func GetUserPayments(owner string, organization string, user string) ([]*Payment
|
||||
return payments, nil
|
||||
}
|
||||
|
||||
func GetPaginationPayments(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) ([]*Payment, error) {
|
||||
func GetPaginationPayments(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Payment, error) {
|
||||
payments := []*Payment{}
|
||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
err := session.Find(&payments, &Payment{Organization: organization})
|
||||
err := session.Find(&payments, &Payment{Owner: owner})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -125,7 +128,7 @@ func UpdatePayment(id string, payment *Payment) (bool, error) {
|
||||
|
||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(payment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
@@ -149,73 +152,76 @@ func DeletePayment(payment *Payment) (bool, error) {
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func notifyPayment(request *http.Request, body []byte, owner string, providerName string, productName string, paymentName string, orderId string) (*Payment, error, string) {
|
||||
provider, err := getProvider(owner, providerName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pProvider, cert, err := provider.getPaymentProvider()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
func notifyPayment(request *http.Request, body []byte, owner string, paymentName string, orderId string) (*Payment, *pp.NotifyResult, error) {
|
||||
payment, err := getPayment(owner, paymentName)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if payment == nil {
|
||||
err = fmt.Errorf("the payment: %s does not exist", paymentName)
|
||||
return nil, err, pProvider.GetResponseError(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
product, err := getProduct(owner, productName)
|
||||
provider, err := getProvider(owner, payment.Provider)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, nil, err
|
||||
}
|
||||
pProvider, cert, err := provider.getPaymentProvider()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
product, err := getProduct(owner, payment.ProductName)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if product == nil {
|
||||
err = fmt.Errorf("the product: %s does not exist", productName)
|
||||
return payment, err, pProvider.GetResponseError(err)
|
||||
err = fmt.Errorf("the product: %s does not exist", payment.ProductName)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
productDisplayName, paymentName, price, productName, providerName, err := pProvider.Notify(request, body, cert.AuthorityPublicKey, orderId)
|
||||
if orderId == "" {
|
||||
orderId = payment.OutOrderId
|
||||
}
|
||||
|
||||
notifyResult, err := pProvider.Notify(request, body, cert.AuthorityPublicKey, orderId)
|
||||
if err != nil {
|
||||
return payment, err, pProvider.GetResponseError(err)
|
||||
return payment, nil, err
|
||||
}
|
||||
if notifyResult.PaymentStatus != pp.PaymentStatePaid {
|
||||
return payment, notifyResult, nil
|
||||
}
|
||||
// Only check paid payment
|
||||
if notifyResult.ProductDisplayName != "" && notifyResult.ProductDisplayName != product.DisplayName {
|
||||
err = fmt.Errorf("the payment's product name: %s doesn't equal to the expected product name: %s", notifyResult.ProductDisplayName, product.DisplayName)
|
||||
return payment, nil, err
|
||||
}
|
||||
|
||||
if productDisplayName != "" && productDisplayName != product.DisplayName {
|
||||
err = fmt.Errorf("the payment's product name: %s doesn't equal to the expected product name: %s", productDisplayName, product.DisplayName)
|
||||
return payment, err, pProvider.GetResponseError(err)
|
||||
if notifyResult.Price != product.Price {
|
||||
err = fmt.Errorf("the payment's price: %f doesn't equal to the expected price: %f", notifyResult.Price, product.Price)
|
||||
return payment, nil, err
|
||||
}
|
||||
|
||||
if price != product.Price {
|
||||
err = fmt.Errorf("the payment's price: %f doesn't equal to the expected price: %f", price, product.Price)
|
||||
return payment, err, pProvider.GetResponseError(err)
|
||||
}
|
||||
|
||||
err = nil
|
||||
return payment, err, pProvider.GetResponseError(err)
|
||||
return payment, notifyResult, nil
|
||||
}
|
||||
|
||||
func NotifyPayment(request *http.Request, body []byte, owner string, providerName string, productName string, paymentName string, orderId string) (error, string) {
|
||||
payment, err, errorResponse := notifyPayment(request, body, owner, providerName, productName, paymentName, orderId)
|
||||
func NotifyPayment(request *http.Request, body []byte, owner string, paymentName string, orderId string) (*Payment, error) {
|
||||
payment, notifyResult, err := notifyPayment(request, body, owner, paymentName, orderId)
|
||||
if payment != nil {
|
||||
if err != nil {
|
||||
payment.State = "Error"
|
||||
payment.State = pp.PaymentStateError
|
||||
payment.Message = err.Error()
|
||||
} else {
|
||||
payment.State = "Paid"
|
||||
payment.State = notifyResult.PaymentStatus
|
||||
payment.Message = notifyResult.NotifyMessage
|
||||
}
|
||||
|
||||
_, err = UpdatePayment(payment.GetId(), payment)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return err, errorResponse
|
||||
return payment, nil
|
||||
}
|
||||
|
||||
func invoicePayment(payment *Payment) (string, error) {
|
||||
@@ -242,7 +248,7 @@ func invoicePayment(payment *Payment) (string, error) {
|
||||
}
|
||||
|
||||
func InvoicePayment(payment *Payment) (string, error) {
|
||||
if payment.State != "Paid" {
|
||||
if payment.State != pp.PaymentStatePaid {
|
||||
return "", fmt.Errorf("the payment state is supposed to be: \"%s\", got: \"%s\"", "Paid", payment.State)
|
||||
}
|
||||
|
||||
|
@@ -58,10 +58,7 @@ type PermissionRule struct {
|
||||
Id string `xorm:"varchar(100) index not null default ''" json:"id"`
|
||||
}
|
||||
|
||||
const (
|
||||
builtInAvailableField = 5 // Casdoor built-in adapter, use V5 to filter permission, so has 5 available field
|
||||
builtInAdapter = "permission_rule"
|
||||
)
|
||||
const builtInAvailableField = 5 // Casdoor built-in adapter, use V5 to filter permission, so has 5 available field
|
||||
|
||||
func (p *Permission) GetId() string {
|
||||
return util.GetId(p.Owner, p.Name)
|
||||
@@ -290,7 +287,7 @@ func GetPermissionsAndRolesByUser(userId string) ([]*Permission, []*Role, error)
|
||||
|
||||
for _, role := range roles {
|
||||
perms := []*Permission{}
|
||||
err := ormer.Engine.Where("roles like ?", "%"+role.Name+"\"%").Find(&perms)
|
||||
err := ormer.Engine.Where("roles like ?", "%"+role.GetId()+"\"%").Find(&perms)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@@ -335,7 +335,7 @@ m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`
|
||||
policyDefinition := strings.Split(cfg.String("policy_definition::p"), ",")
|
||||
fieldsNum := len(policyDefinition)
|
||||
if fieldsNum > builtInAvailableField {
|
||||
panic(fmt.Errorf("the maximum policy_definition field number cannot exceed %d", builtInAvailableField))
|
||||
panic(fmt.Errorf("the maximum policy_definition field number cannot exceed %d, got %d", builtInAvailableField, fieldsNum))
|
||||
}
|
||||
// filled empty field with "" and V5 with "permissionId"
|
||||
for i := builtInAvailableField - fieldsNum; i > 0; i-- {
|
||||
|
@@ -17,6 +17,8 @@ package object
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/pp"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
)
|
||||
@@ -183,36 +185,39 @@ func BuyProduct(id string, providerName string, user *User, host string) (string
|
||||
productDisplayName := product.DisplayName
|
||||
|
||||
originFrontend, originBackend := getOriginFromHost(host)
|
||||
returnUrl := fmt.Sprintf("%s/payments/%s/result", originFrontend, paymentName)
|
||||
notifyUrl := fmt.Sprintf("%s/api/notify-payment/%s/%s/%s/%s", originBackend, owner, providerName, productName, paymentName)
|
||||
|
||||
returnUrl := fmt.Sprintf("%s/payments/%s/%s/result", originFrontend, owner, paymentName)
|
||||
notifyUrl := fmt.Sprintf("%s/api/notify-payment/%s/%s", originBackend, owner, paymentName)
|
||||
// Create an Order and get the payUrl
|
||||
payUrl, orderId, err := pProvider.Pay(providerName, productName, payerName, paymentName, productDisplayName, product.Price, product.Currency, returnUrl, notifyUrl)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Create a Payment linked with Product and Order
|
||||
payment := Payment{
|
||||
Owner: "admin",
|
||||
Name: paymentName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: paymentName,
|
||||
Provider: provider.Name,
|
||||
Type: provider.Type,
|
||||
Organization: user.Owner,
|
||||
User: user.Name,
|
||||
Owner: product.Owner,
|
||||
Name: paymentName,
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: paymentName,
|
||||
|
||||
Provider: provider.Name,
|
||||
Type: provider.Type,
|
||||
|
||||
ProductName: productName,
|
||||
ProductDisplayName: productDisplayName,
|
||||
Detail: product.Detail,
|
||||
Tag: product.Tag,
|
||||
Currency: product.Currency,
|
||||
Price: product.Price,
|
||||
PayUrl: payUrl,
|
||||
ReturnUrl: product.ReturnUrl,
|
||||
State: "Created",
|
||||
|
||||
User: user.Name,
|
||||
PayUrl: payUrl,
|
||||
State: pp.PaymentStateCreated,
|
||||
OutOrderId: orderId,
|
||||
}
|
||||
|
||||
if provider.Type == "Dummy" {
|
||||
payment.State = "Paid"
|
||||
payment.State = pp.PaymentStatePaid
|
||||
}
|
||||
|
||||
affected, err := AddPayment(&payment)
|
||||
|
@@ -182,20 +182,6 @@ func GetProvider(id string) (*Provider, error) {
|
||||
return getProvider(owner, name)
|
||||
}
|
||||
|
||||
func getDefaultAiProvider() (*Provider, error) {
|
||||
provider := Provider{Owner: "admin", Category: "AI"}
|
||||
existed, err := ormer.Engine.Get(&provider)
|
||||
if err != nil {
|
||||
return &provider, err
|
||||
}
|
||||
|
||||
if !existed {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return &provider, nil
|
||||
}
|
||||
|
||||
func GetWechatMiniProgramProvider(application *Application) *Provider {
|
||||
providers := application.Providers
|
||||
for _, provider := range providers {
|
||||
@@ -217,7 +203,7 @@ func UpdateProvider(id string, provider *Provider) (bool, error) {
|
||||
if name != provider.Name {
|
||||
err := providerChangeTrigger(name, provider.Name)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,7 +254,8 @@ func DeleteProvider(provider *Provider) (bool, error) {
|
||||
func (p *Provider) getPaymentProvider() (pp.PaymentProvider, *Cert, error) {
|
||||
cert := &Cert{}
|
||||
if p.Cert != "" {
|
||||
cert, err := getCert(p.Owner, p.Cert)
|
||||
var err error
|
||||
cert, err = getCert(p.Owner, p.Cert)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@@ -46,7 +46,7 @@ func GetResourceCount(owner, user, field, value string) (int64, error) {
|
||||
}
|
||||
|
||||
func GetResources(owner string, user string) ([]*Resource, error) {
|
||||
if owner == "built-in" {
|
||||
if owner == "built-in" || owner == "" {
|
||||
owner = ""
|
||||
user = ""
|
||||
}
|
||||
@@ -61,7 +61,7 @@ func GetResources(owner string, user string) ([]*Resource, error) {
|
||||
}
|
||||
|
||||
func GetPaginationResources(owner, user string, offset, limit int, field, value, sortField, sortOrder string) ([]*Resource, error) {
|
||||
if owner == "built-in" {
|
||||
if owner == "built-in" || owner == "" {
|
||||
owner = ""
|
||||
user = ""
|
||||
}
|
||||
|
46
object/resource_direct.go
Normal file
46
object/resource_direct.go
Normal file
@@ -0,0 +1,46 @@
|
||||
// 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 object
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
func GetDirectResources(owner string, user string, provider *Provider, prefix string, lang string) ([]*Resource, error) {
|
||||
storageProvider, err := getStorageProvider(provider, lang)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := []*Resource{}
|
||||
objects, err := storageProvider.List(prefix)
|
||||
for _, obj := range objects {
|
||||
resource := &Resource{
|
||||
Owner: owner,
|
||||
Name: strings.TrimPrefix(obj.Path, "/"),
|
||||
CreatedTime: obj.LastModified.Local().Format(time.RFC3339),
|
||||
User: user,
|
||||
Provider: "",
|
||||
Application: "",
|
||||
FileSize: int(obj.Size),
|
||||
Url: util.UrlJoin(provider.Domain, obj.Path),
|
||||
}
|
||||
res = append(res, resource)
|
||||
}
|
||||
return res, err
|
||||
}
|
@@ -133,7 +133,7 @@ func UpdateRole(id string, role *Role) (bool, error) {
|
||||
if name != role.Name {
|
||||
err := roleChangeTrigger(name, role.Name)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -24,7 +24,7 @@ func getSmsClient(provider *Provider) (sender.SmsClient, error) {
|
||||
var client sender.SmsClient
|
||||
var err error
|
||||
|
||||
if provider.Type == sender.HuaweiCloud {
|
||||
if provider.Type == sender.HuaweiCloud || provider.Type == sender.AzureACS {
|
||||
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.ProviderUrl, provider.AppId)
|
||||
} else {
|
||||
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.AppId)
|
||||
|
@@ -25,6 +25,7 @@ type TableColumn struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
CasdoorName string `json:"casdoorName"`
|
||||
IsKey bool `json:"isKey"`
|
||||
IsHashed bool `json:"isHashed"`
|
||||
Values []string `json:"values"`
|
||||
}
|
||||
|
@@ -29,6 +29,23 @@ const (
|
||||
UserPropertiesWechatOpenId = "wechatOpenId"
|
||||
)
|
||||
|
||||
const UserEnforcerId = "built-in/user-enforcer-built-in"
|
||||
|
||||
var userEnforcer *UserGroupEnforcer
|
||||
|
||||
func InitUserManager() {
|
||||
enforcer, err := GetEnforcer(UserEnforcerId)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = enforcer.InitEnforcer()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
userEnforcer = NewUserGroupEnforcer(enforcer.Enforcer)
|
||||
}
|
||||
|
||||
type User struct {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
@@ -230,7 +247,7 @@ func GetUserCount(owner, field, value string, groupName string) (int64, error) {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
|
||||
if groupName != "" {
|
||||
return GetGroupUserCount(groupName, field, value)
|
||||
return GetGroupUserCount(util.GetId(owner, groupName), field, value)
|
||||
}
|
||||
|
||||
return session.Count(&User{})
|
||||
@@ -274,7 +291,7 @@ func GetPaginationUsers(owner string, offset, limit int, field, value, sortField
|
||||
users := []*User{}
|
||||
|
||||
if groupName != "" {
|
||||
return GetPaginationGroupUsers(groupName, offset, limit, field, value, sortField, sortOrder)
|
||||
return GetPaginationGroupUsers(util.GetId(owner, groupName), offset, limit, field, value, sortField, sortOrder)
|
||||
}
|
||||
|
||||
session := GetSessionForUser(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
@@ -531,6 +548,13 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
||||
columns = append(columns, "name", "email", "phone", "country_code")
|
||||
}
|
||||
|
||||
if util.ContainsString(columns, "groups") {
|
||||
_, err := userEnforcer.UpdateGroupsForUser(user.GetId(), user.Groups)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
affected, err := updateUser(id, user, columns)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -568,7 +592,7 @@ func UpdateUserForAllFields(id string, user *User) (bool, error) {
|
||||
if name != user.Name {
|
||||
err := userChangeTrigger(name, user.Name)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -778,6 +802,10 @@ func ExtendUserWithRolesAndPermissions(user *User) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteGroupForUser(user string, group string) (bool, error) {
|
||||
return userEnforcer.DeleteGroupForUser(user, group)
|
||||
}
|
||||
|
||||
func userChangeTrigger(oldName string, newName string) error {
|
||||
session := ormer.Engine.NewSession()
|
||||
defer session.Close()
|
||||
|
@@ -69,6 +69,8 @@ func downloadImage(client *http.Client, url string) (*bytes.Buffer, string, erro
|
||||
fileExtension = ".ico"
|
||||
case "image/x-icon":
|
||||
fileExtension = ".ico"
|
||||
case "image/svg+xml":
|
||||
fileExtension = ".svg"
|
||||
default:
|
||||
return nil, "", fmt.Errorf("unsupported content type: %s", contentType)
|
||||
}
|
||||
|
95
object/user_enforcer.go
Normal file
95
object/user_enforcer.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/casbin/casbin/v2/errors"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
type UserGroupEnforcer struct {
|
||||
// use rbac model implement use group, the enforcer can also implement user role
|
||||
enforcer *casbin.Enforcer
|
||||
}
|
||||
|
||||
func NewUserGroupEnforcer(enforcer *casbin.Enforcer) *UserGroupEnforcer {
|
||||
return &UserGroupEnforcer{
|
||||
enforcer: enforcer,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *UserGroupEnforcer) AddGroupForUser(user string, group string) (bool, error) {
|
||||
return e.enforcer.AddRoleForUser(user, GetGroupWithPrefix(group))
|
||||
}
|
||||
|
||||
func (e *UserGroupEnforcer) AddGroupsForUser(user string, groups []string) (bool, error) {
|
||||
g := make([]string, len(groups))
|
||||
for i, group := range groups {
|
||||
g[i] = GetGroupWithPrefix(group)
|
||||
}
|
||||
return e.enforcer.AddRolesForUser(user, g)
|
||||
}
|
||||
|
||||
func (e *UserGroupEnforcer) DeleteGroupForUser(user string, group string) (bool, error) {
|
||||
return e.enforcer.DeleteRoleForUser(user, GetGroupWithPrefix(group))
|
||||
}
|
||||
|
||||
func (e *UserGroupEnforcer) DeleteGroupsForUser(user string) (bool, error) {
|
||||
return e.enforcer.DeleteRolesForUser(user)
|
||||
}
|
||||
|
||||
func (e *UserGroupEnforcer) GetGroupsForUser(user string) ([]string, error) {
|
||||
groups, err := e.enforcer.GetRolesForUser(user)
|
||||
for i, group := range groups {
|
||||
groups[i] = GetGroupWithoutPrefix(group)
|
||||
}
|
||||
return groups, err
|
||||
}
|
||||
|
||||
func (e *UserGroupEnforcer) GetAllUsersByGroup(group string) ([]string, error) {
|
||||
users, err := e.enforcer.GetUsersForRole(GetGroupWithPrefix(group))
|
||||
if err != nil {
|
||||
if err == errors.ERR_NAME_NOT_FOUND {
|
||||
return []string{}, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetGroupWithPrefix(group string) string {
|
||||
return "group:" + group
|
||||
}
|
||||
|
||||
func GetGroupWithoutPrefix(group string) string {
|
||||
return group[len("group:"):]
|
||||
}
|
||||
|
||||
func (e *UserGroupEnforcer) GetUserNamesByGroupName(groupName string) ([]string, error) {
|
||||
var names []string
|
||||
|
||||
userIds, err := e.GetAllUsersByGroup(groupName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, userId := range userIds {
|
||||
_, name := util.GetOwnerAndNameFromIdNoCheck(userId)
|
||||
names = append(names, name)
|
||||
}
|
||||
|
||||
return names, nil
|
||||
}
|
||||
|
||||
func (e *UserGroupEnforcer) UpdateGroupsForUser(user string, groups []string) (bool, error) {
|
||||
_, err := e.DeleteGroupsForUser(user)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
affected, err := e.AddGroupsForUser(user, groups)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected, nil
|
||||
}
|
21
pp/alipay.go
21
pp/alipay.go
@@ -16,7 +16,6 @@ package pp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
@@ -67,10 +66,10 @@ func (pp *AlipayPaymentProvider) Pay(providerName string, productName string, pa
|
||||
return payUrl, "", nil
|
||||
}
|
||||
|
||||
func (pp *AlipayPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (string, string, float64, string, string, error) {
|
||||
func (pp *AlipayPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {
|
||||
bm, err := alipay.ParseNotifyToBodyMap(request)
|
||||
if err != nil {
|
||||
return "", "", 0, "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
providerName := bm.Get("providerName")
|
||||
@@ -82,13 +81,21 @@ func (pp *AlipayPaymentProvider) Notify(request *http.Request, body []byte, auth
|
||||
|
||||
ok, err := alipay.VerifySignWithCert(authorityPublicKey, bm)
|
||||
if err != nil {
|
||||
return "", "", 0, "", "", err
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return "", "", 0, "", "", fmt.Errorf("VerifySignWithCert() failed: %v", ok)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return productDisplayName, paymentName, price, productName, providerName, nil
|
||||
notifyResult := &NotifyResult{
|
||||
ProductName: productName,
|
||||
ProductDisplayName: productDisplayName,
|
||||
ProviderName: providerName,
|
||||
OutOrderId: orderId,
|
||||
PaymentStatus: PaymentStatePaid,
|
||||
Price: price,
|
||||
PaymentName: paymentName,
|
||||
}
|
||||
return notifyResult, nil
|
||||
}
|
||||
|
||||
func (pp *AlipayPaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
|
||||
|
@@ -31,8 +31,10 @@ func (pp *DummyPaymentProvider) Pay(providerName string, productName string, pay
|
||||
return payUrl, "", nil
|
||||
}
|
||||
|
||||
func (pp *DummyPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (string, string, float64, string, string, error) {
|
||||
return "", "", 0, "", "", nil
|
||||
func (pp *DummyPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {
|
||||
return &NotifyResult{
|
||||
PaymentStatus: PaymentStatePaid,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (pp *DummyPaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
|
||||
|
22
pp/gc.go
22
pp/gc.go
@@ -216,11 +216,11 @@ func (pp *GcPaymentProvider) Pay(providerName string, productName string, payerN
|
||||
return payRespInfo.PayUrl, "", nil
|
||||
}
|
||||
|
||||
func (pp *GcPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (string, string, float64, string, string, error) {
|
||||
func (pp *GcPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {
|
||||
reqBody := GcRequestBody{}
|
||||
m, err := url.ParseQuery(string(body))
|
||||
if err != nil {
|
||||
return "", "", 0, "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reqBody.Op = m["op"][0]
|
||||
@@ -232,13 +232,13 @@ func (pp *GcPaymentProvider) Notify(request *http.Request, body []byte, authorit
|
||||
|
||||
notifyReqInfoBytes, err := base64.StdEncoding.DecodeString(reqBody.Data)
|
||||
if err != nil {
|
||||
return "", "", 0, "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var notifyRespInfo GcNotifyRespInfo
|
||||
err = json.Unmarshal(notifyReqInfoBytes, ¬ifyRespInfo)
|
||||
if err != nil {
|
||||
return "", "", 0, "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
providerName := ""
|
||||
@@ -249,10 +249,18 @@ func (pp *GcPaymentProvider) Notify(request *http.Request, body []byte, authorit
|
||||
price := notifyRespInfo.Amount
|
||||
|
||||
if notifyRespInfo.OrderState != "1" {
|
||||
return "", "", 0, "", "", fmt.Errorf("error order state: %s", notifyRespInfo.OrderDate)
|
||||
return nil, fmt.Errorf("error order state: %s", notifyRespInfo.OrderDate)
|
||||
}
|
||||
|
||||
return productDisplayName, paymentName, price, productName, providerName, nil
|
||||
notifyResult := &NotifyResult{
|
||||
ProductName: productName,
|
||||
ProductDisplayName: productDisplayName,
|
||||
ProviderName: providerName,
|
||||
OutOrderId: orderId,
|
||||
Price: price,
|
||||
PaymentStatus: PaymentStatePaid,
|
||||
PaymentName: paymentName,
|
||||
}
|
||||
return notifyResult, nil
|
||||
}
|
||||
|
||||
func (pp *GcPaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
|
||||
|
108
pp/paypal.go
108
pp/paypal.go
@@ -17,9 +17,12 @@ package pp
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
|
||||
"github.com/go-pay/gopay"
|
||||
"github.com/go-pay/gopay/paypal"
|
||||
"github.com/go-pay/gopay/pkg/util"
|
||||
@@ -31,8 +34,14 @@ type PaypalPaymentProvider struct {
|
||||
|
||||
func NewPaypalPaymentProvider(clientID string, secret string) (*PaypalPaymentProvider, error) {
|
||||
pp := &PaypalPaymentProvider{}
|
||||
|
||||
client, err := paypal.NewClient(clientID, secret, false)
|
||||
isProd := false
|
||||
if conf.GetConfigString("runmode") == "prod" {
|
||||
isProd = true
|
||||
}
|
||||
client, err := paypal.NewClient(clientID, secret, isProd)
|
||||
//if !isProd {
|
||||
// client.DebugSwitch = gopay.DebugOn
|
||||
//}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -42,27 +51,27 @@ func NewPaypalPaymentProvider(clientID string, secret string) (*PaypalPaymentPro
|
||||
}
|
||||
|
||||
func (pp *PaypalPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
||||
// pp.Client.DebugSwitch = gopay.DebugOn // Set log to terminal stdout
|
||||
|
||||
// https://github.com/go-pay/gopay/blob/main/doc/paypal.md
|
||||
priceStr := strconv.FormatFloat(price, 'f', 2, 64)
|
||||
var pus []*paypal.PurchaseUnit
|
||||
item := &paypal.PurchaseUnit{
|
||||
units := make([]*paypal.PurchaseUnit, 0, 1)
|
||||
unit := &paypal.PurchaseUnit{
|
||||
ReferenceId: util.GetRandomString(16),
|
||||
Amount: &paypal.Amount{
|
||||
CurrencyCode: currency,
|
||||
Value: priceStr,
|
||||
CurrencyCode: currency, // e.g."USD"
|
||||
Value: priceStr, // e.g."100.00"
|
||||
},
|
||||
Description: joinAttachString([]string{productDisplayName, productName, providerName}),
|
||||
}
|
||||
pus = append(pus, item)
|
||||
units = append(units, unit)
|
||||
|
||||
bm := make(gopay.BodyMap)
|
||||
bm.Set("intent", "CAPTURE")
|
||||
bm.Set("purchase_units", pus)
|
||||
bm.Set("purchase_units", units)
|
||||
bm.SetBodyMap("application_context", func(b gopay.BodyMap) {
|
||||
b.Set("brand_name", "Casdoor")
|
||||
b.Set("locale", "en-PT")
|
||||
b.Set("return_url", returnUrl)
|
||||
b.Set("cancel_url", returnUrl)
|
||||
})
|
||||
|
||||
ppRsp, err := pp.Client.CreateOrder(context.Background(), bm)
|
||||
@@ -72,31 +81,84 @@ func (pp *PaypalPaymentProvider) Pay(providerName string, productName string, pa
|
||||
if ppRsp.Code != paypal.Success {
|
||||
return "", "", errors.New(ppRsp.Error)
|
||||
}
|
||||
|
||||
// {"id":"9BR68863NE220374S","status":"CREATED",
|
||||
// "links":[{"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S","rel":"self","method":"GET"},
|
||||
// {"href":"https://www.sandbox.paypal.com/checkoutnow?token=9BR68863NE220374S","rel":"approve","method":"GET"},
|
||||
// {"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S","rel":"update","method":"PATCH"},
|
||||
// {"href":"https://api.sandbox.paypal.com/v2/checkout/orders/9BR68863NE220374S/capture","rel":"capture","method":"POST"}]}
|
||||
return ppRsp.Response.Links[1].Href, ppRsp.Response.Id, nil
|
||||
}
|
||||
|
||||
func (pp *PaypalPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (string, string, float64, string, string, error) {
|
||||
ppRsp, err := pp.Client.OrderCapture(context.Background(), orderId, nil)
|
||||
func (pp *PaypalPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {
|
||||
notifyResult := &NotifyResult{}
|
||||
captureRsp, err := pp.Client.OrderCapture(context.Background(), orderId, nil)
|
||||
if err != nil {
|
||||
return "", "", 0, "", "", err
|
||||
return nil, err
|
||||
}
|
||||
if ppRsp.Code != paypal.Success {
|
||||
return "", "", 0, "", "", errors.New(ppRsp.Error)
|
||||
if captureRsp.Code != paypal.Success {
|
||||
errDetail := captureRsp.ErrorResponse.Details[0]
|
||||
switch errDetail.Issue {
|
||||
// If order is already captured, just skip this type of error and check the order detail
|
||||
case "ORDER_ALREADY_CAPTURED":
|
||||
// skip
|
||||
case "ORDER_NOT_APPROVED":
|
||||
notifyResult.PaymentStatus = PaymentStateCanceled
|
||||
notifyResult.NotifyMessage = errDetail.Description
|
||||
return notifyResult, nil
|
||||
default:
|
||||
err = fmt.Errorf(errDetail.Description)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Check the order detail
|
||||
detailRsp, err := pp.Client.OrderDetail(context.Background(), orderId, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if captureRsp.Code != paypal.Success {
|
||||
errDetail := captureRsp.ErrorResponse.Details[0]
|
||||
switch errDetail.Issue {
|
||||
case "ORDER_NOT_APPROVED":
|
||||
notifyResult.PaymentStatus = PaymentStateCanceled
|
||||
notifyResult.NotifyMessage = errDetail.Description
|
||||
return notifyResult, nil
|
||||
default:
|
||||
err = fmt.Errorf(errDetail.Description)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
paymentName := ppRsp.Response.Id
|
||||
price, err := strconv.ParseFloat(ppRsp.Response.PurchaseUnits[0].Amount.Value, 64)
|
||||
paymentName := detailRsp.Response.Id
|
||||
price, err := strconv.ParseFloat(detailRsp.Response.PurchaseUnits[0].Amount.Value, 64)
|
||||
if err != nil {
|
||||
return "", "", 0, "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
productDisplayName, productName, providerName, err := parseAttachString(ppRsp.Response.PurchaseUnits[0].Description)
|
||||
currency := detailRsp.Response.PurchaseUnits[0].Amount.CurrencyCode
|
||||
productDisplayName, productName, providerName, err := parseAttachString(detailRsp.Response.PurchaseUnits[0].Description)
|
||||
if err != nil {
|
||||
return "", "", 0, "", "", err
|
||||
return nil, err
|
||||
}
|
||||
// TODO: status better handler, e.g.`hanging`
|
||||
var paymentStatus PaymentState
|
||||
switch detailRsp.Response.Status { // CREATED、SAVED、APPROVED、VOIDED、COMPLETED、PAYER_ACTION_REQUIRED
|
||||
case "COMPLETED":
|
||||
paymentStatus = PaymentStatePaid
|
||||
default:
|
||||
paymentStatus = PaymentStateError
|
||||
}
|
||||
notifyResult = &NotifyResult{
|
||||
PaymentStatus: paymentStatus,
|
||||
PaymentName: paymentName,
|
||||
|
||||
return productDisplayName, paymentName, price, productName, providerName, nil
|
||||
ProductName: productName,
|
||||
ProductDisplayName: productDisplayName,
|
||||
ProviderName: providerName,
|
||||
Price: price,
|
||||
Currency: currency,
|
||||
|
||||
OutOrderId: orderId,
|
||||
}
|
||||
return notifyResult, nil
|
||||
}
|
||||
|
||||
func (pp *PaypalPaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
|
||||
|
@@ -14,11 +14,36 @@
|
||||
|
||||
package pp
|
||||
|
||||
import "net/http"
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type PaymentState string
|
||||
|
||||
const (
|
||||
PaymentStatePaid PaymentState = "Paid"
|
||||
PaymentStateCreated PaymentState = "Created"
|
||||
PaymentStateCanceled PaymentState = "Canceled"
|
||||
PaymentStateError PaymentState = "Error"
|
||||
)
|
||||
|
||||
type NotifyResult struct {
|
||||
PaymentName string
|
||||
PaymentStatus PaymentState
|
||||
NotifyMessage string
|
||||
|
||||
ProviderName string
|
||||
ProductName string
|
||||
ProductDisplayName string
|
||||
Price float64
|
||||
Currency string
|
||||
|
||||
OutOrderId string
|
||||
}
|
||||
|
||||
type PaymentProvider interface {
|
||||
Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error)
|
||||
Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (string, string, float64, string, string, error)
|
||||
Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error)
|
||||
GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error)
|
||||
GetResponseError(err error) string
|
||||
}
|
||||
|
@@ -83,22 +83,22 @@ func (pp *WechatPaymentProvider) Pay(providerName string, productName string, pa
|
||||
return wxRsp.Response.CodeUrl, "", nil
|
||||
}
|
||||
|
||||
func (pp *WechatPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (string, string, float64, string, string, error) {
|
||||
func (pp *WechatPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {
|
||||
notifyReq, err := wechat.V3ParseNotify(request)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert := pp.Client.WxPublicKey()
|
||||
err = notifyReq.VerifySignByPK(cert)
|
||||
if err != nil {
|
||||
return "", "", 0, "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
apiKey := string(pp.Client.ApiV3Key)
|
||||
result, err := notifyReq.DecryptCipherText(apiKey)
|
||||
if err != nil {
|
||||
return "", "", 0, "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
paymentName := result.OutTradeNo
|
||||
@@ -106,10 +106,19 @@ func (pp *WechatPaymentProvider) Notify(request *http.Request, body []byte, auth
|
||||
|
||||
productDisplayName, productName, providerName, err := parseAttachString(result.Attach)
|
||||
if err != nil {
|
||||
return "", "", 0, "", "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return productDisplayName, paymentName, price, productName, providerName, nil
|
||||
notifyResult := &NotifyResult{
|
||||
ProductName: productName,
|
||||
ProductDisplayName: productDisplayName,
|
||||
ProviderName: providerName,
|
||||
OutOrderId: orderId,
|
||||
Price: price,
|
||||
PaymentStatus: PaymentStatePaid,
|
||||
PaymentName: paymentName,
|
||||
}
|
||||
return notifyResult, nil
|
||||
}
|
||||
|
||||
func (pp *WechatPaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
|
||||
|
@@ -211,19 +211,6 @@ func initAPI() {
|
||||
beego.Router("/api/add-cert", &controllers.ApiController{}, "POST:AddCert")
|
||||
beego.Router("/api/delete-cert", &controllers.ApiController{}, "POST:DeleteCert")
|
||||
|
||||
beego.Router("/api/get-chats", &controllers.ApiController{}, "GET:GetChats")
|
||||
beego.Router("/api/get-chat", &controllers.ApiController{}, "GET:GetChat")
|
||||
beego.Router("/api/update-chat", &controllers.ApiController{}, "POST:UpdateChat")
|
||||
beego.Router("/api/add-chat", &controllers.ApiController{}, "POST:AddChat")
|
||||
beego.Router("/api/delete-chat", &controllers.ApiController{}, "POST:DeleteChat")
|
||||
|
||||
beego.Router("/api/get-messages", &controllers.ApiController{}, "GET:GetMessages")
|
||||
beego.Router("/api/get-message", &controllers.ApiController{}, "GET:GetMessage")
|
||||
beego.Router("/api/get-message-answer", &controllers.ApiController{}, "GET:GetMessageAnswer")
|
||||
beego.Router("/api/update-message", &controllers.ApiController{}, "POST:UpdateMessage")
|
||||
beego.Router("/api/add-message", &controllers.ApiController{}, "POST:AddMessage")
|
||||
beego.Router("/api/delete-message", &controllers.ApiController{}, "POST:DeleteMessage")
|
||||
|
||||
beego.Router("/api/get-subscriptions", &controllers.ApiController{}, "GET:GetSubscriptions")
|
||||
beego.Router("/api/get-subscription", &controllers.ApiController{}, "GET:GetSubscription")
|
||||
beego.Router("/api/update-subscription", &controllers.ApiController{}, "POST:UpdateSubscription")
|
||||
@@ -255,7 +242,7 @@ func initAPI() {
|
||||
beego.Router("/api/update-payment", &controllers.ApiController{}, "POST:UpdatePayment")
|
||||
beego.Router("/api/add-payment", &controllers.ApiController{}, "POST:AddPayment")
|
||||
beego.Router("/api/delete-payment", &controllers.ApiController{}, "POST:DeletePayment")
|
||||
beego.Router("/api/notify-payment/?:owner/?:provider/?:product/?:payment", &controllers.ApiController{}, "POST:NotifyPayment")
|
||||
beego.Router("/api/notify-payment/?:owner/?:payment", &controllers.ApiController{}, "POST:NotifyPayment")
|
||||
beego.Router("/api/invoice-payment", &controllers.ApiController{}, "POST:InvoicePayment")
|
||||
|
||||
beego.Router("/api/send-email", &controllers.ApiController{}, "POST:SendEmail")
|
||||
|
@@ -55,15 +55,12 @@ func StaticFilter(ctx *context.Context) {
|
||||
path += urlPath
|
||||
}
|
||||
|
||||
path2 := strings.TrimPrefix(path, "web/build/images/")
|
||||
if util.FileExist(path2) {
|
||||
makeGzipResponse(ctx.ResponseWriter, ctx.Request, path2)
|
||||
return
|
||||
}
|
||||
|
||||
if !util.FileExist(path) {
|
||||
path = "web/build/index.html"
|
||||
}
|
||||
if !util.FileExist(path) {
|
||||
return
|
||||
}
|
||||
|
||||
if oldStaticBaseUrl == newStaticBaseUrl {
|
||||
makeGzipResponse(ctx.ResponseWriter, ctx.Request, path)
|
||||
|
@@ -12,17 +12,20 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ai
|
||||
package storage
|
||||
|
||||
import (
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
"github.com/sashabaranov/go-openai"
|
||||
"github.com/casdoor/oss"
|
||||
"github.com/casdoor/oss/googlecloud"
|
||||
)
|
||||
|
||||
func getProxyClientFromToken(authToken string) *openai.Client {
|
||||
config := openai.DefaultConfig(authToken)
|
||||
config.HTTPClient = proxy.ProxyHttpClient
|
||||
func NewGoogleCloudStorageProvider(clientId string, clientSecret string, bucket string, endpoint string) oss.StorageInterface {
|
||||
sp, _ := googlecloud.New(&googlecloud.Config{
|
||||
AccessID: clientId,
|
||||
AccessKey: clientSecret,
|
||||
Bucket: bucket,
|
||||
Endpoint: endpoint,
|
||||
})
|
||||
|
||||
c := openai.NewClientWithConfig(config)
|
||||
return c
|
||||
return sp
|
||||
}
|
@@ -12,17 +12,21 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ai
|
||||
package storage
|
||||
|
||||
import "github.com/pkoukk/tiktoken-go"
|
||||
import (
|
||||
"github.com/casdoor/oss"
|
||||
"github.com/casdoor/oss/qiniu"
|
||||
)
|
||||
|
||||
func getTokenSize(model string, prompt string) (int, error) {
|
||||
tkm, err := tiktoken.EncodingForModel(model)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
func NewQiniuCloudKodoStorageProvider(clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
|
||||
sp := qiniu.New(&qiniu.Config{
|
||||
AccessID: clientId,
|
||||
AccessKey: clientSecret,
|
||||
Region: region,
|
||||
Bucket: bucket,
|
||||
Endpoint: endpoint,
|
||||
})
|
||||
|
||||
token := tkm.Encode(prompt, nil, nil)
|
||||
res := len(token)
|
||||
return res, nil
|
||||
return sp
|
||||
}
|
@@ -30,6 +30,10 @@ func GetStorageProvider(providerType string, clientId string, clientSecret strin
|
||||
return NewTencentCloudCosStorageProvider(clientId, clientSecret, region, bucket, endpoint)
|
||||
case "Azure Blob":
|
||||
return NewAzureBlobStorageProvider(clientId, clientSecret, region, bucket, endpoint)
|
||||
case "Qiniu Cloud Kodo":
|
||||
return NewQiniuCloudKodoStorageProvider(clientId, clientSecret, region, bucket, endpoint)
|
||||
case "Google Cloud Storage":
|
||||
return NewGoogleCloudStorageProvider(clientId, clientSecret, bucket, endpoint)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@@ -25,5 +25,12 @@ func CasbinToSlice(casbinRule xormadapter.CasbinRule) []string {
|
||||
casbinRule.V4,
|
||||
casbinRule.V5,
|
||||
}
|
||||
// remove empty strings from end, for update model policy map
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
if s[i] != "" {
|
||||
s = s[:i+1]
|
||||
break
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
@@ -3,7 +3,7 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@ant-design/cssinjs": "^1.8.1",
|
||||
"@ant-design/cssinjs": "1.16.1",
|
||||
"@ant-design/icons": "^4.7.0",
|
||||
"@craco/craco": "^6.4.5",
|
||||
"@crowdin/cli": "^3.7.10",
|
||||
|
@@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {Button, Card, Col, Input, Row, Select, Switch} from "antd";
|
||||
import {Button, Card, Col, Input, InputNumber, Row, Select, Switch} from "antd";
|
||||
import * as AdapterBackend from "./backend/AdapterBackend";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import * as Setting from "./Setting";
|
||||
@@ -91,64 +91,47 @@ class AdapterEditPage extends React.Component {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<React.Fragment>{
|
||||
this.state.adapter.databaseType === "sqlite3" ?
|
||||
(
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("syncer:File"), i18next.t("provider:File - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.adapter.host} onChange={e => {
|
||||
this.updateAdapterField("file", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Host"), i18next.t("provider:Host - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.adapter.host} onChange={e => {
|
||||
this.updateAdapterField("host", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Port"), i18next.t("provider:Port - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.adapter.port} onChange={e => {
|
||||
this.updateAdapterField("port", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:User"), i18next.t("general:User - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.adapter.user} onChange={e => {
|
||||
this.updateAdapterField("user", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.adapter.password} onChange={e => {
|
||||
this.updateAdapterField("password", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
<React.Fragment>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Host"), i18next.t("provider:Host - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.adapter.host} onChange={e => {
|
||||
this.updateAdapterField("host", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Port"), i18next.t("provider:Port - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<InputNumber value={this.state.adapter.port} min={0} max={65535} onChange={value => {
|
||||
this.updateAdapterField("port", value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:User"), i18next.t("general:User - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.adapter.user} onChange={e => {
|
||||
this.updateAdapterField("user", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.adapter.password} onChange={e => {
|
||||
this.updateAdapterField("password", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
@@ -32,7 +32,7 @@ class AdapterListPage extends BaseListPage {
|
||||
createdTime: moment().format(),
|
||||
type: "Database",
|
||||
host: "localhost",
|
||||
port: "3306",
|
||||
port: 3306,
|
||||
user: "root",
|
||||
password: "123456",
|
||||
databaseType: "mysql",
|
||||
|
241
web/src/App.js
241
web/src/App.js
@@ -18,7 +18,7 @@ import {Helmet} from "react-helmet";
|
||||
import {MfaRuleRequired} from "./Setting";
|
||||
import * as Setting from "./Setting";
|
||||
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
|
||||
import {BarsOutlined, CommentOutlined, DownOutlined, InfoCircleFilled, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
|
||||
import {AppstoreTwoTone, BarsOutlined, DollarTwoTone, DownOutlined, HomeTwoTone, InfoCircleFilled, LockTwoTone, LogoutOutlined, SafetyCertificateTwoTone, SettingOutlined, SettingTwoTone, WalletTwoTone} 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";
|
||||
import OrganizationListPage from "./OrganizationListPage";
|
||||
@@ -56,11 +56,6 @@ import PricingListPage from "./PricingListPage";
|
||||
import PricingEditPage from "./PricingEditPage";
|
||||
import PlanListPage from "./PlanListPage";
|
||||
import PlanEditPage from "./PlanEditPage";
|
||||
import ChatListPage from "./ChatListPage";
|
||||
import ChatEditPage from "./ChatEditPage";
|
||||
import ChatPage from "./ChatPage";
|
||||
import MessageEditPage from "./MessageEditPage";
|
||||
import MessageListPage from "./MessageListPage";
|
||||
import ProductListPage from "./ProductListPage";
|
||||
import ProductEditPage from "./ProductEditPage";
|
||||
import ProductBuyPage from "./ProductBuyPage";
|
||||
@@ -96,6 +91,10 @@ import AccountAvatar from "./account/AccountAvatar";
|
||||
|
||||
const {Header, Footer, Content} = Layout;
|
||||
|
||||
import {setTwoToneColor} from "@ant-design/icons";
|
||||
|
||||
setTwoToneColor("rgb(87,52,211)");
|
||||
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -152,62 +151,24 @@ class App extends Component {
|
||||
});
|
||||
if (uri === "/") {
|
||||
this.setState({selectedMenuKey: "/"});
|
||||
} else if (uri.includes("/organizations") || uri.includes("/trees")) {
|
||||
this.setState({selectedMenuKey: "/organizations"});
|
||||
} else if (uri.includes("/users")) {
|
||||
this.setState({selectedMenuKey: "/users"});
|
||||
} else if (uri.includes("/groups")) {
|
||||
this.setState({selectedMenuKey: "/groups"});
|
||||
} else if (uri.includes("/roles")) {
|
||||
this.setState({selectedMenuKey: "/roles"});
|
||||
} else if (uri.includes("/permissions")) {
|
||||
this.setState({selectedMenuKey: "/permissions"});
|
||||
} else if (uri.includes("/models")) {
|
||||
this.setState({selectedMenuKey: "/models"});
|
||||
} else if (uri.includes("/adapters")) {
|
||||
this.setState({selectedMenuKey: "/adapters"});
|
||||
} else if (uri.includes("/enforcers")) {
|
||||
this.setState({selectedMenuKey: "/enforcers"});
|
||||
} else if (uri.includes("/providers")) {
|
||||
this.setState({selectedMenuKey: "/providers"});
|
||||
} else if (uri.includes("/applications")) {
|
||||
this.setState({selectedMenuKey: "/applications"});
|
||||
} else if (uri.includes("/resources")) {
|
||||
this.setState({selectedMenuKey: "/resources"});
|
||||
} else if (uri.includes("/records")) {
|
||||
this.setState({selectedMenuKey: "/records"});
|
||||
} else if (uri.includes("/tokens")) {
|
||||
this.setState({selectedMenuKey: "/tokens"});
|
||||
} else if (uri.includes("/sessions")) {
|
||||
this.setState({selectedMenuKey: "/sessions"});
|
||||
} else if (uri.includes("/webhooks")) {
|
||||
this.setState({selectedMenuKey: "/webhooks"});
|
||||
} else if (uri.includes("/syncers")) {
|
||||
this.setState({selectedMenuKey: "/syncers"});
|
||||
} else if (uri.includes("/certs")) {
|
||||
this.setState({selectedMenuKey: "/certs"});
|
||||
} else if (uri.includes("/chats")) {
|
||||
this.setState({selectedMenuKey: "/chats"});
|
||||
} else if (uri.includes("/messages")) {
|
||||
this.setState({selectedMenuKey: "/messages"});
|
||||
} else if (uri.includes("/products")) {
|
||||
this.setState({selectedMenuKey: "/products"});
|
||||
} else if (uri.includes("/payments")) {
|
||||
this.setState({selectedMenuKey: "/payments"});
|
||||
} else if (uri.includes("/organizations") || uri.includes("/trees") || uri.includes("/users") || uri.includes("/groups")) {
|
||||
this.setState({selectedMenuKey: "/orgs"});
|
||||
} else if (uri.includes("/roles") || uri.includes("/permissions") || uri.includes("/models") || uri.includes("/adapters") || uri.includes("/enforcers")) {
|
||||
this.setState({selectedMenuKey: "/auth"});
|
||||
} else if (uri.includes("/applications") || uri.includes("/providers") || uri.includes("/resources") || uri.includes("/certs")) {
|
||||
this.setState({selectedMenuKey: "/identity"});
|
||||
} else if (uri.includes("/records") || uri.includes("/tokens") || uri.includes("/sessions")) {
|
||||
this.setState({selectedMenuKey: "/logs"});
|
||||
} else if (uri.includes("/products") || uri.includes("/payments") || uri.includes("/plans") || uri.includes("/pricings") || uri.includes("/subscriptions")) {
|
||||
this.setState({selectedMenuKey: "/business"});
|
||||
} else if (uri.includes("/sysinfo") || uri.includes("/syncers") || uri.includes("/webhooks")) {
|
||||
this.setState({selectedMenuKey: "/admin"});
|
||||
} else if (uri.includes("/signup")) {
|
||||
this.setState({selectedMenuKey: "/signup"});
|
||||
} else if (uri.includes("/login")) {
|
||||
this.setState({selectedMenuKey: "/login"});
|
||||
} else if (uri.includes("/result")) {
|
||||
this.setState({selectedMenuKey: "/result"});
|
||||
} else if (uri.includes("/sysinfo")) {
|
||||
this.setState({selectedMenuKey: "/sysinfo"});
|
||||
} else if (uri.includes("/subscriptions")) {
|
||||
this.setState({selectedMenuKey: "/subscriptions"});
|
||||
} else if (uri.includes("/plans")) {
|
||||
this.setState({selectedMenuKey: "/plans"});
|
||||
} else if (uri.includes("/pricings")) {
|
||||
this.setState({selectedMenuKey: "/pricings"});
|
||||
} else {
|
||||
this.setState({selectedMenuKey: -1});
|
||||
}
|
||||
@@ -368,11 +329,6 @@ class App extends Component {
|
||||
items.push(Setting.getItem(<><SettingOutlined /> {i18next.t("account:My Account")}</>,
|
||||
"/account"
|
||||
));
|
||||
if (Conf.EnableChatPages) {
|
||||
items.push(Setting.getItem(<><CommentOutlined /> {i18next.t("account:Chats & Messages")}</>,
|
||||
"/chat"
|
||||
));
|
||||
}
|
||||
}
|
||||
items.push(Setting.getItem(<><LogoutOutlined /> {i18next.t("account:Logout")}</>,
|
||||
"/logout"));
|
||||
@@ -382,8 +338,6 @@ class App extends Component {
|
||||
this.props.history.push("/account");
|
||||
} else if (e.key === "/subscription") {
|
||||
this.props.history.push("/subscription");
|
||||
} else if (e.key === "/chat") {
|
||||
this.props.history.push("/chat");
|
||||
} else if (e.key === "/logout") {
|
||||
this.logout();
|
||||
}
|
||||
@@ -447,7 +401,7 @@ class App extends Component {
|
||||
return [];
|
||||
}
|
||||
|
||||
res.push(Setting.getItem(<Link to="/">{i18next.t("general:Home")}</Link>, "/"));
|
||||
res.push(Setting.getItem(<Link to="/">{i18next.t("general:Home")}</Link>, "/", <HomeTwoTone />));
|
||||
|
||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||
if (Conf.ShowGithubCorner) {
|
||||
@@ -458,119 +412,54 @@ class App extends Component {
|
||||
</a>, "#"));
|
||||
}
|
||||
|
||||
res.push(Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>,
|
||||
"/organizations"));
|
||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/organizations">{i18next.t("general:User Management")}</Link>, "/orgs", <AppstoreTwoTone />, [
|
||||
Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>, "/organizations"),
|
||||
Setting.getItem(<Link to="/groups">{i18next.t("general:Groups")}</Link>, "/groups"),
|
||||
Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>, "/users"),
|
||||
]));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/groups">{i18next.t("general:Groups")}</Link>,
|
||||
"/groups"));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>,
|
||||
"/users"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/roles">{i18next.t("general:Roles")}</Link>,
|
||||
"/roles"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/permissions">{i18next.t("general:Permissions")}</Link>,
|
||||
"/permissions"
|
||||
));
|
||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/roles">{i18next.t("general:Authorization")}</Link>, "/auth", <SafetyCertificateTwoTone />, [
|
||||
Setting.getItem(<Link to="/roles">{i18next.t("general:Roles")}</Link>, "/roles"),
|
||||
Setting.getItem(<Link to="/permissions">{i18next.t("general:Permissions")}</Link>, "/permissions"),
|
||||
Setting.getItem(<Link to="/models">{i18next.t("general:Models")}</Link>, "/models"),
|
||||
Setting.getItem(<Link to="/adapters">{i18next.t("general:Adapters")}</Link>, "/adapters"),
|
||||
Setting.getItem(<Link to="/enforcers">{i18next.t("general:Enforcers")}</Link>, "/enforcers"),
|
||||
].filter(item => {
|
||||
if (!Setting.isLocalAdminUser(this.state.account) && ["/models", "/adapters", "/enforcers"].includes(item.key)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
})));
|
||||
}
|
||||
|
||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||
res.push(Setting.getItem(<Link to="/models">{i18next.t("general:Models")}</Link>,
|
||||
"/models"
|
||||
));
|
||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/applications">{i18next.t("general:Identity")}</Link>, "/identity", <LockTwoTone />, [
|
||||
Setting.getItem(<Link to="/applications">{i18next.t("general:Applications")}</Link>, "/applications"),
|
||||
Setting.getItem(<Link to="/providers">{i18next.t("general:Providers")}</Link>, "/providers"),
|
||||
Setting.getItem(<Link to="/resources">{i18next.t("general:Resources")}</Link>, "/resources"),
|
||||
Setting.getItem(<Link to="/certs">{i18next.t("general:Certs")}</Link>, "/certs"),
|
||||
]));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/adapters">{i18next.t("general:Adapters")}</Link>,
|
||||
"/adapters"
|
||||
));
|
||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/records">{i18next.t("general:Logging & Auditing")}</Link>, "/logs", <WalletTwoTone />, [
|
||||
Setting.getItem(<Link to="/records">{i18next.t("general:Records")}</Link>, "/records"),
|
||||
Setting.getItem(<Link to="/tokens">{i18next.t("general:Tokens")}</Link>, "/tokens"),
|
||||
Setting.getItem(<Link to="/sessions">{i18next.t("general:Sessions")}</Link>, "/sessions"),
|
||||
]));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/enforcers">{i18next.t("general:Enforcers")}</Link>,
|
||||
"/enforcers"
|
||||
));
|
||||
}
|
||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/products">{i18next.t("general:Business & Payments")}</Link>, "/business", <DollarTwoTone />, [
|
||||
Setting.getItem(<Link to="/products">{i18next.t("general:Products")}</Link>, "/products"),
|
||||
Setting.getItem(<Link to="/payments">{i18next.t("general:Payments")}</Link>, "/payments"),
|
||||
Setting.getItem(<Link to="/plans">{i18next.t("general:Plans")}</Link>, "/plans"),
|
||||
Setting.getItem(<Link to="/pricings">{i18next.t("general:Pricings")}</Link>, "/pricings"),
|
||||
Setting.getItem(<Link to="/subscriptions">{i18next.t("general:Subscriptions")}</Link>, "/subscriptions"),
|
||||
]));
|
||||
|
||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||
res.push(Setting.getItem(<Link to="/applications">{i18next.t("general:Applications")}</Link>,
|
||||
"/applications"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/providers">{i18next.t("general:Providers")}</Link>,
|
||||
"/providers"
|
||||
));
|
||||
|
||||
if (Conf.EnableChatPages) {
|
||||
res.push(Setting.getItem(<Link to="/chats">{i18next.t("general:Chats")}</Link>,
|
||||
"/chats"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/messages">{i18next.t("general:Messages")}</Link>,
|
||||
"/messages"
|
||||
));
|
||||
}
|
||||
|
||||
res.push(Setting.getItem(<Link to="/resources">{i18next.t("general:Resources")}</Link>,
|
||||
"/resources"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/records">{i18next.t("general:Records")}</Link>,
|
||||
"/records"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/plans">{i18next.t("general:Plans")}</Link>,
|
||||
"/plans"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/pricings">{i18next.t("general:Pricings")}</Link>,
|
||||
"/pricings"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/subscriptions">{i18next.t("general:Subscriptions")}</Link>,
|
||||
"/subscriptions"
|
||||
));
|
||||
}
|
||||
|
||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||
res.push(Setting.getItem(<Link to="/tokens">{i18next.t("general:Tokens")}</Link>,
|
||||
"/tokens"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/sessions">{i18next.t("general:Sessions")}</Link>,
|
||||
"/sessions"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>,
|
||||
"/webhooks"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>,
|
||||
"/syncers"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/certs">{i18next.t("general:Certs")}</Link>,
|
||||
"/certs"
|
||||
));
|
||||
|
||||
if (Conf.EnableExtraPages) {
|
||||
res.push(Setting.getItem(<Link to="/products">{i18next.t("general:Products")}</Link>,
|
||||
"/products"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/payments">{i18next.t("general:Payments")}</Link>,
|
||||
"/payments"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (Setting.isAdminUser(this.state.account)) {
|
||||
res.push(Setting.getItem(<Link to="/sysinfo">{i18next.t("general:System Info")}</Link>,
|
||||
"/sysinfo"
|
||||
));
|
||||
res.push(Setting.getItem(<a target="_blank" rel="noreferrer"
|
||||
href={Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger"}>{i18next.t("general:Swagger")}</a>,
|
||||
"/swagger"
|
||||
));
|
||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/sysinfo">{i18next.t("general:Admin")}</Link>, "/admin", <SettingTwoTone />, [
|
||||
Setting.getItem(<Link to="/sysinfo">{i18next.t("general:System Info")}</Link>, "/sysinfo"),
|
||||
Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>, "/syncers"),
|
||||
Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>, "/webhooks"),
|
||||
Setting.getItem(<a target="_blank" rel="noreferrer" href={Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger"}>{i18next.t("general:Swagger")}</a>, "/swagger")]));
|
||||
}
|
||||
|
||||
return res;
|
||||
@@ -628,11 +517,6 @@ class App extends Component {
|
||||
<Route exact path="/syncers/:syncerName" render={(props) => this.renderLoginIfNotLoggedIn(<SyncerEditPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/certs" render={(props) => this.renderLoginIfNotLoggedIn(<CertListPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/certs/:organizationName/:certName" render={(props) => this.renderLoginIfNotLoggedIn(<CertEditPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/chats" render={(props) => this.renderLoginIfNotLoggedIn(<ChatListPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/chats/:chatName" render={(props) => this.renderLoginIfNotLoggedIn(<ChatEditPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/chat" render={(props) => this.renderLoginIfNotLoggedIn(<ChatPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/messages" render={(props) => this.renderLoginIfNotLoggedIn(<MessageListPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/messages/:messageName" render={(props) => this.renderLoginIfNotLoggedIn(<MessageEditPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/plans" render={(props) => this.renderLoginIfNotLoggedIn(<PlanListPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/plans/:organizationName/:planName" render={(props) => this.renderLoginIfNotLoggedIn(<PlanEditPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/pricings" render={(props) => this.renderLoginIfNotLoggedIn(<PricingListPage account={this.state.account} {...props} />)} />
|
||||
@@ -643,8 +527,8 @@ class App extends Component {
|
||||
<Route exact path="/products/:organizationName/:productName" render={(props) => this.renderLoginIfNotLoggedIn(<ProductEditPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/products/:organizationName/:productName/buy" render={(props) => this.renderLoginIfNotLoggedIn(<ProductBuyPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/payments" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentListPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/payments/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/payments/:paymentName/result" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentResultPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/payments/:organizationName/:paymentName" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentEditPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/payments/:organizationName/:paymentName/result" render={(props) => this.renderLoginIfNotLoggedIn(<PaymentResultPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/records" render={(props) => this.renderLoginIfNotLoggedIn(<RecordListPage account={this.state.account} {...props} />)} />
|
||||
<Route exact path="/mfa/setup" render={(props) => this.renderLoginIfNotLoggedIn(<MfaSetupPage account={this.state.account} onfinish={() => this.setState({requiredEnableMfa: false})} {...props} />)} />
|
||||
<Route exact path="/.well-known/openid-configuration" render={(props) => <OdicDiscoveryPage />} />
|
||||
@@ -668,8 +552,7 @@ class App extends Component {
|
||||
};
|
||||
|
||||
isWithoutCard() {
|
||||
return Setting.isMobile() || window.location.pathname === "/chat" ||
|
||||
window.location.pathname.startsWith("/trees");
|
||||
return Setting.isMobile() || window.location.pathname.startsWith("/trees");
|
||||
}
|
||||
|
||||
renderContent() {
|
||||
|
@@ -1,217 +0,0 @@
|
||||
// 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.
|
||||
|
||||
import React from "react";
|
||||
import {Alert, Avatar, Input, List, Spin} from "antd";
|
||||
import {CopyOutlined, DislikeOutlined, LikeOutlined, SendOutlined} from "@ant-design/icons";
|
||||
import i18next from "i18next";
|
||||
|
||||
const {TextArea} = Input;
|
||||
|
||||
class ChatBox extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
inputValue: "",
|
||||
};
|
||||
|
||||
this.listContainerRef = React.createRef();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.messages !== this.props.messages && this.props.messages !== undefined && this.props.messages !== null) {
|
||||
this.scrollToListItem(this.props.messages.length);
|
||||
}
|
||||
}
|
||||
|
||||
handleKeyDown = (e) => {
|
||||
if (e.key === "Enter" && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
|
||||
if (this.state.inputValue !== "") {
|
||||
this.send(this.state.inputValue);
|
||||
this.setState({inputValue: ""});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
scrollToListItem(index) {
|
||||
const listContainerElement = this.listContainerRef.current;
|
||||
|
||||
if (!listContainerElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targetItem = listContainerElement.querySelector(
|
||||
`#chatbox-list-item-${index}`
|
||||
);
|
||||
|
||||
if (!targetItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
const scrollDistance = targetItem.offsetTop - listContainerElement.offsetTop;
|
||||
|
||||
listContainerElement.scrollTo({
|
||||
top: scrollDistance,
|
||||
behavior: "smooth",
|
||||
});
|
||||
}
|
||||
|
||||
send = (text) => {
|
||||
this.props.sendMessage(text);
|
||||
this.setState({inputValue: ""});
|
||||
};
|
||||
|
||||
renderText(text) {
|
||||
const lines = text.split("\n").map((line, index) => (
|
||||
<React.Fragment key={index}>
|
||||
{line}
|
||||
<br />
|
||||
</React.Fragment>
|
||||
));
|
||||
return <div>{lines}</div>;
|
||||
}
|
||||
|
||||
renderList() {
|
||||
if (this.props.messages === undefined || this.props.messages === null) {
|
||||
return (
|
||||
<div style={{display: "flex", justifyContent: "center", alignItems: "center"}}>
|
||||
<Spin size="large" tip={i18next.t("login:Loading")} style={{paddingTop: "20%"}} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref={this.listContainerRef} style={{position: "relative", maxHeight: "calc(100vh - 140px)", overflowY: "auto"}}>
|
||||
<List
|
||||
itemLayout="horizontal"
|
||||
dataSource={[...this.props.messages, {}]}
|
||||
renderItem={(item, index) => {
|
||||
if (Object.keys(item).length === 0 && item.constructor === Object) {
|
||||
return <List.Item id={`chatbox-list-item-${index}`} style={{
|
||||
height: "160px",
|
||||
backgroundColor: index % 2 === 0 ? "white" : "rgb(247,247,248)",
|
||||
borderBottom: "1px solid rgb(229, 229, 229)",
|
||||
position: "relative",
|
||||
}} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<List.Item id={`chatbox-list-item-${index}`} style={{
|
||||
backgroundColor: index % 2 === 0 ? "white" : "rgb(247,247,248)",
|
||||
borderBottom: "1px solid rgb(229, 229, 229)",
|
||||
position: "relative",
|
||||
}}>
|
||||
<div style={{width: "800px", margin: "0 auto", position: "relative"}}>
|
||||
<List.Item.Meta
|
||||
avatar={<Avatar style={{width: "30px", height: "30px", borderRadius: "3px"}} src={item.author === `${this.props.account.owner}/${this.props.account.name}` ? this.props.account.avatar : "https://cdn.casbin.com/casdoor/resource/built-in/admin/gpt.png"} />}
|
||||
title={
|
||||
<div style={{fontSize: "16px", fontWeight: "normal", lineHeight: "24px", marginTop: "-15px", marginLeft: "5px", marginRight: "80px"}}>
|
||||
{
|
||||
!item.text.includes("#ERROR#") ? this.renderText(item.text) : (
|
||||
<Alert message={item.text.slice("#ERROR#: ".length)} type="error" showIcon />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
<div style={{position: "absolute", top: "0px", right: "0px"}}
|
||||
>
|
||||
<CopyOutlined style={{color: "rgb(172,172,190)", margin: "5px"}} />
|
||||
<LikeOutlined style={{color: "rgb(172,172,190)", margin: "5px"}} />
|
||||
<DislikeOutlined style={{color: "rgb(172,172,190)", margin: "5px"}} />
|
||||
</div>
|
||||
</div>
|
||||
</List.Item>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<div style={{
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
height: "120px",
|
||||
background: "linear-gradient(transparent 0%, rgba(255, 255, 255, 0.8) 50%, white 100%)",
|
||||
pointerEvents: "none",
|
||||
}} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderInput() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: "fixed",
|
||||
bottom: "90px",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<div style={{position: "relative", width: "760px", marginLeft: "-280px"}}>
|
||||
<TextArea
|
||||
placeholder={"Send a message..."}
|
||||
autoSize={{maxRows: 8}}
|
||||
value={this.state.inputValue}
|
||||
onChange={(e) => this.setState({inputValue: e.target.value})}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
style={{
|
||||
fontSize: "16px",
|
||||
fontWeight: "normal",
|
||||
lineHeight: "24px",
|
||||
width: "770px",
|
||||
height: "48px",
|
||||
borderRadius: "6px",
|
||||
borderColor: "rgb(229,229,229)",
|
||||
boxShadow: "0 0 15px rgba(0, 0, 0, 0.1)",
|
||||
paddingLeft: "17px",
|
||||
paddingRight: "17px",
|
||||
paddingTop: "12px",
|
||||
paddingBottom: "12px",
|
||||
}}
|
||||
suffix={<SendOutlined style={{color: "rgb(210,210,217"}} onClick={() => this.send(this.state.inputValue)} />}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<SendOutlined
|
||||
style={{
|
||||
color: this.state.inputValue === "" ? "rgb(210,210,217)" : "rgb(142,142,160)",
|
||||
position: "absolute",
|
||||
bottom: "17px",
|
||||
right: "17px",
|
||||
}}
|
||||
onClick={() => this.send(this.state.inputValue)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
this.renderList()
|
||||
}
|
||||
{
|
||||
this.renderInput()
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ChatBox;
|
@@ -1,257 +0,0 @@
|
||||
// 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.
|
||||
|
||||
import React from "react";
|
||||
import {Button, Card, Col, Input, Row, Select} from "antd";
|
||||
import * as ChatBackend from "./backend/ChatBackend";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import * as UserBackend from "./backend/UserBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
|
||||
class ChatEditPage extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
chatName: props.match.params.chatName,
|
||||
chat: null,
|
||||
organizations: [],
|
||||
users: [],
|
||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.getChat();
|
||||
this.getOrganizations();
|
||||
}
|
||||
|
||||
getChat() {
|
||||
ChatBackend.getChat("admin", this.state.chatName)
|
||||
.then((res) => {
|
||||
if (res.data === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
chat: res.data,
|
||||
});
|
||||
|
||||
this.getUsers(res.data.organization);
|
||||
});
|
||||
}
|
||||
|
||||
getOrganizations() {
|
||||
OrganizationBackend.getOrganizations("admin")
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
organizations: res.data || [],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getUsers(organizationName) {
|
||||
UserBackend.getUsers(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
users: res.data,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
parseChatField(key, value) {
|
||||
if ([].includes(key)) {
|
||||
value = Setting.myParseInt(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
updateChatField(key, value) {
|
||||
value = this.parseChatField(key, value);
|
||||
|
||||
const chat = this.state.chat;
|
||||
chat[key] = value;
|
||||
this.setState({
|
||||
chat: chat,
|
||||
});
|
||||
}
|
||||
|
||||
renderChat() {
|
||||
return (
|
||||
<Card size="small" title={
|
||||
<div>
|
||||
{this.state.mode === "add" ? i18next.t("chat:New Chat") : i18next.t("chat:Edit Chat")}
|
||||
<Button onClick={() => this.submitChatEdit(false)}>{i18next.t("general:Save")}</Button>
|
||||
<Button style={{marginLeft: "20px"}} type="primary" onClick={() => this.submitChatEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
|
||||
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} onClick={() => this.deleteChat()}>{i18next.t("general:Cancel")}</Button> : null}
|
||||
</div>
|
||||
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
|
||||
<Row style={{marginTop: "10px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} disabled={!Setting.isAdminUser(this.props.account)} style={{width: "100%"}} value={this.state.chat.organization} onChange={(value => {this.updateChatField("organization", value);})}
|
||||
options={this.state.organizations.map((organization) => Setting.getOption(organization.name, organization.name))
|
||||
} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.chat.name} onChange={e => {
|
||||
this.updateChatField("name", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Display name"), i18next.t("general:Display name - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.chat.displayName} onChange={e => {
|
||||
this.updateChatField("displayName", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Type"), i18next.t("provider:Type - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.chat.type} onChange={(value => {
|
||||
this.updateChatField("type", value);
|
||||
})}
|
||||
options={[
|
||||
{value: "Single", name: i18next.t("chat:Single")},
|
||||
{value: "Group", name: i18next.t("chat:Group")},
|
||||
{value: "AI", name: i18next.t("chat:AI")},
|
||||
].map((item) => Setting.getOption(item.name, item.value))}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Category"), i18next.t("provider:Category - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.chat.category} onChange={e => {
|
||||
this.updateChatField("category", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("chat:User1"), i18next.t("chat:User1 - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.chat.user1} onChange={(value => {this.updateChatField("user1", value);})}
|
||||
options={this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`))
|
||||
} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("chat:User2"), i18next.t("chat:User2 - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.chat.user2} onChange={(value => {this.updateChatField("user2", value);})}
|
||||
options={this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`))
|
||||
} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Users"), i18next.t("chat:Users - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} mode="multiple" style={{width: "100%"}} value={this.state.chat.users}
|
||||
onChange={(value => {this.updateChatField("users", value);})}
|
||||
options={this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`))}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
submitChatEdit(willExist) {
|
||||
const chat = Setting.deepCopy(this.state.chat);
|
||||
ChatBackend.updateChat(this.state.chat.owner, this.state.chatName, chat)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||
this.setState({
|
||||
chatName: this.state.chat.name,
|
||||
});
|
||||
|
||||
if (willExist) {
|
||||
this.props.history.push("/chats");
|
||||
} else {
|
||||
this.props.history.push(`/chats/${this.state.chat.name}`);
|
||||
}
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||
this.updateChatField("name", this.state.chatName);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
deleteChat() {
|
||||
ChatBackend.deleteChat(this.state.chat)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.props.history.push("/chats");
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
this.state.chat !== null ? this.renderChat() : null
|
||||
}
|
||||
<div style={{marginTop: "20px", marginLeft: "40px"}}>
|
||||
<Button size="large" onClick={() => this.submitChatEdit(false)}>{i18next.t("general:Save")}</Button>
|
||||
<Button style={{marginLeft: "20px"}} type="primary" size="large" onClick={() => this.submitChatEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
|
||||
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} size="large" onClick={() => this.deleteChat()}>{i18next.t("general:Cancel")}</Button> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ChatEditPage;
|
@@ -1,297 +0,0 @@
|
||||
// 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.
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as ChatBackend from "./backend/ChatBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./common/modal/PopconfirmModal";
|
||||
|
||||
class ChatListPage extends BaseListPage {
|
||||
newChat() {
|
||||
const randomName = Setting.getRandomName();
|
||||
const organizationName = Setting.getRequestOrganization(this.props.account);
|
||||
return {
|
||||
owner: "admin", // this.props.account.applicationName,
|
||||
name: `chat_${randomName}`,
|
||||
createdTime: moment().format(),
|
||||
updatedTime: moment().format(),
|
||||
organization: organizationName,
|
||||
displayName: `New Chat - ${randomName}`,
|
||||
type: "Single",
|
||||
category: "Chat Category - 1",
|
||||
user1: `${this.props.account.owner}/${this.props.account.name}`,
|
||||
user2: "",
|
||||
users: [`${this.props.account.owner}/${this.props.account.name}`],
|
||||
messageCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
addChat() {
|
||||
const newChat = this.newChat();
|
||||
ChatBackend.addChat(newChat)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.props.history.push({pathname: `/chats/${newChat.name}`, mode: "add"});
|
||||
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
deleteChat(i) {
|
||||
ChatBackend.deleteChat(this.state.data[i])
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||
this.setState({
|
||||
data: Setting.deleteRow(this.state.data, i),
|
||||
pagination: {total: this.state.pagination.total - 1},
|
||||
});
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
renderTable(chats) {
|
||||
const columns = [
|
||||
{
|
||||
title: i18next.t("general:Organization"),
|
||||
dataIndex: "organization",
|
||||
key: "organization",
|
||||
width: "150px",
|
||||
fixed: "left",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("organization"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/organizations/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
width: "120px",
|
||||
fixed: "left",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("name"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/chats/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Created time"),
|
||||
dataIndex: "createdTime",
|
||||
key: "createdTime",
|
||||
width: "150px",
|
||||
sorter: true,
|
||||
render: (text, record, index) => {
|
||||
return Setting.getFormattedDate(text);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Updated time"),
|
||||
dataIndex: "updatedTime",
|
||||
key: "updatedTime",
|
||||
width: "15 0px",
|
||||
sorter: true,
|
||||
render: (text, record, index) => {
|
||||
return Setting.getFormattedDate(text);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Display name"),
|
||||
dataIndex: "displayName",
|
||||
key: "displayName",
|
||||
// width: '100px',
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("displayName"),
|
||||
},
|
||||
{
|
||||
title: i18next.t("provider:Type"),
|
||||
dataIndex: "type",
|
||||
key: "type",
|
||||
width: "110px",
|
||||
sorter: true,
|
||||
filterMultiple: false,
|
||||
filters: [
|
||||
{text: "Single", value: "Single"},
|
||||
{text: "Group", value: "Group"},
|
||||
{text: "AI", value: "AI"},
|
||||
],
|
||||
render: (text, record, index) => {
|
||||
return i18next.t(`chat:${text}`);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("provider:Category"),
|
||||
dataIndex: "category",
|
||||
key: "category",
|
||||
// width: '100px',
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("category"),
|
||||
},
|
||||
{
|
||||
title: i18next.t("chat:User1"),
|
||||
dataIndex: "user1",
|
||||
key: "user1",
|
||||
width: "120px",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("user1"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/users/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("chat:User2"),
|
||||
dataIndex: "user2",
|
||||
key: "user2",
|
||||
width: "120px",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("user2"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/users/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Users"),
|
||||
dataIndex: "users",
|
||||
key: "users",
|
||||
// width: '100px',
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("users"),
|
||||
render: (text, record, index) => {
|
||||
return Setting.getTags(text, "users");
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("chat:Message count"),
|
||||
dataIndex: "messageCount",
|
||||
key: "messageCount",
|
||||
// width: '100px',
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("messageCount"),
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Action"),
|
||||
dataIndex: "",
|
||||
key: "op",
|
||||
width: "170px",
|
||||
fixed: (Setting.isMobile()) ? "false" : "right",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/chats/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteChat(index)}
|
||||
>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const paginationProps = {
|
||||
total: this.state.pagination.total,
|
||||
showQuickJumper: true,
|
||||
showSizeChanger: true,
|
||||
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table scroll={{x: "max-content"}} columns={columns} dataSource={chats} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
|
||||
title={() => (
|
||||
<div>
|
||||
{i18next.t("general:Chats")}
|
||||
<Button type="primary" size="small" onClick={this.addChat.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
</div>
|
||||
)}
|
||||
loading={this.state.loading}
|
||||
onChange={this.handleTableChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
fetch = (params = {}) => {
|
||||
let field = params.searchedColumn, value = params.searchText;
|
||||
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
if (params.category !== undefined && params.category !== null) {
|
||||
field = "category";
|
||||
value = params.category;
|
||||
} else if (params.type !== undefined && params.type !== null) {
|
||||
field = "type";
|
||||
value = params.type;
|
||||
}
|
||||
this.setState({loading: true});
|
||||
ChatBackend.getChats("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
data: res.data,
|
||||
pagination: {
|
||||
...params.pagination,
|
||||
total: res.data2,
|
||||
},
|
||||
searchText: params.searchText,
|
||||
searchedColumn: params.searchedColumn,
|
||||
});
|
||||
} else {
|
||||
if (Setting.isResponseDenied(res)) {
|
||||
this.setState({
|
||||
isAuthorized: false,
|
||||
});
|
||||
} else {
|
||||
Setting.showMessage("error", res.msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default ChatListPage;
|
@@ -1,178 +0,0 @@
|
||||
// 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.
|
||||
|
||||
import React from "react";
|
||||
import {Button, Menu} from "antd";
|
||||
import {DeleteOutlined, LayoutOutlined, PlusOutlined} from "@ant-design/icons";
|
||||
|
||||
class ChatMenu extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const items = this.chatsToItems(this.props.chats);
|
||||
const openKeys = items.map((item) => item.key);
|
||||
|
||||
this.state = {
|
||||
openKeys: openKeys,
|
||||
selectedKeys: ["0-0"],
|
||||
};
|
||||
}
|
||||
|
||||
chatsToItems(chats) {
|
||||
const categories = {};
|
||||
chats.forEach((chat) => {
|
||||
if (!categories[chat.category]) {
|
||||
categories[chat.category] = [];
|
||||
}
|
||||
categories[chat.category].push(chat);
|
||||
});
|
||||
|
||||
const selectedKeys = this.state === undefined ? [] : this.state.selectedKeys;
|
||||
return Object.keys(categories).map((category, index) => {
|
||||
return {
|
||||
key: `${index}`,
|
||||
icon: <LayoutOutlined />,
|
||||
label: category,
|
||||
children: categories[category].map((chat, chatIndex) => {
|
||||
const globalChatIndex = chats.indexOf(chat);
|
||||
const isSelected = selectedKeys.includes(`${index}-${chatIndex}`);
|
||||
return {
|
||||
key: `${index}-${chatIndex}`,
|
||||
index: globalChatIndex,
|
||||
label: (
|
||||
<div
|
||||
className="menu-item-container"
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
{chat.displayName}
|
||||
{isSelected && (
|
||||
<DeleteOutlined
|
||||
className="menu-item-delete-icon"
|
||||
style={{
|
||||
visibility: "visible",
|
||||
color: "inherit",
|
||||
transition: "color 0.3s",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.color = "rgba(89,54,213,0.6)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.color = "inherit";
|
||||
}}
|
||||
onMouseDown={(e) => {
|
||||
e.currentTarget.style.color = "rgba(89,54,213,0.4)";
|
||||
}}
|
||||
onMouseUp={(e) => {
|
||||
e.currentTarget.style.color = "rgba(89,54,213,0.6)";
|
||||
}}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (this.props.onDeleteChat) {
|
||||
this.props.onDeleteChat(globalChatIndex);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
),
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
onSelect = (info) => {
|
||||
const [categoryIndex, chatIndex] = info.selectedKeys[0].split("-").map(Number);
|
||||
const selectedItem = this.chatsToItems(this.props.chats)[categoryIndex].children[chatIndex];
|
||||
this.setState({selectedKeys: [`${categoryIndex}-${chatIndex}`]});
|
||||
|
||||
if (this.props.onSelectChat) {
|
||||
this.props.onSelectChat(selectedItem.index);
|
||||
}
|
||||
};
|
||||
|
||||
getRootSubmenuKeys(items) {
|
||||
return items.map((item, index) => `${index}`);
|
||||
}
|
||||
|
||||
setSelectedKeyToNewChat(chats) {
|
||||
const items = this.chatsToItems(chats);
|
||||
const openKeys = items.map((item) => item.key);
|
||||
|
||||
this.setState({
|
||||
openKeys: openKeys,
|
||||
selectedKeys: ["0-0"],
|
||||
});
|
||||
}
|
||||
|
||||
onOpenChange = (keys) => {
|
||||
const items = this.chatsToItems(this.props.chats);
|
||||
const rootSubmenuKeys = this.getRootSubmenuKeys(items);
|
||||
const latestOpenKey = keys.find((key) => this.state.openKeys.indexOf(key) === -1);
|
||||
|
||||
if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
|
||||
this.setState({openKeys: keys});
|
||||
} else {
|
||||
this.setState({openKeys: latestOpenKey ? [latestOpenKey] : []});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const items = this.chatsToItems(this.props.chats);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
icon={<PlusOutlined />}
|
||||
style={{
|
||||
width: "calc(100% - 8px)",
|
||||
height: "40px",
|
||||
margin: "4px",
|
||||
borderColor: "rgb(229,229,229)",
|
||||
}}
|
||||
onMouseEnter={(e) => {
|
||||
e.currentTarget.style.borderColor = "rgba(89,54,213,0.6)";
|
||||
}}
|
||||
onMouseLeave={(e) => {
|
||||
e.currentTarget.style.borderColor = "rgba(0, 0, 0, 0.1)";
|
||||
}}
|
||||
onMouseDown={(e) => {
|
||||
e.currentTarget.style.borderColor = "rgba(89,54,213,0.4)";
|
||||
}}
|
||||
onMouseUp={(e) => {
|
||||
e.currentTarget.style.borderColor = "rgba(89,54,213,0.6)";
|
||||
}}
|
||||
onClick={this.props.onAddChat}
|
||||
>
|
||||
New Chat
|
||||
</Button>
|
||||
<Menu
|
||||
style={{maxHeight: "calc(100vh - 140px - 40px - 8px)", overflowY: "auto"}}
|
||||
mode="inline"
|
||||
openKeys={this.state.openKeys}
|
||||
selectedKeys={this.state.selectedKeys}
|
||||
onOpenChange={this.onOpenChange}
|
||||
onSelect={this.onSelect}
|
||||
items={items}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ChatMenu;
|
@@ -1,292 +0,0 @@
|
||||
// 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.
|
||||
|
||||
import React from "react";
|
||||
import {Spin} from "antd";
|
||||
import moment from "moment";
|
||||
import ChatMenu from "./ChatMenu";
|
||||
import ChatBox from "./ChatBox";
|
||||
import * as Setting from "./Setting";
|
||||
import * as ChatBackend from "./backend/ChatBackend";
|
||||
import * as MessageBackend from "./backend/MessageBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
|
||||
class ChatPage extends BaseListPage {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.menu = React.createRef();
|
||||
}
|
||||
|
||||
newChat(chat) {
|
||||
const randomName = Setting.getRandomName();
|
||||
return {
|
||||
owner: "admin", // this.props.account.applicationName,
|
||||
name: `chat_${randomName}`,
|
||||
createdTime: moment().format(),
|
||||
updatedTime: moment().format(),
|
||||
organization: this.props.account.owner,
|
||||
displayName: `New Chat - ${randomName}`,
|
||||
type: "AI",
|
||||
category: chat !== undefined ? chat.category : "Chat Category - 1",
|
||||
user1: `${this.props.account.owner}/${this.props.account.name}`,
|
||||
user2: "",
|
||||
users: [`${this.props.account.owner}/${this.props.account.name}`],
|
||||
messageCount: 0,
|
||||
};
|
||||
}
|
||||
|
||||
newMessage(text) {
|
||||
const randomName = Setting.getRandomName();
|
||||
return {
|
||||
owner: this.props.account.owner, // this.props.account.messagename,
|
||||
name: `message_${randomName}`,
|
||||
createdTime: moment().format(),
|
||||
organization: this.props.account.owner,
|
||||
chat: this.state.chatName,
|
||||
replyTo: "",
|
||||
author: `${this.props.account.owner}/${this.props.account.name}`,
|
||||
text: text,
|
||||
};
|
||||
}
|
||||
|
||||
sendMessage(text) {
|
||||
const newMessage = this.newMessage(text);
|
||||
MessageBackend.addMessage(newMessage)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.getMessages(this.state.chatName);
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
getMessages(chatName) {
|
||||
MessageBackend.getChatMessages(chatName)
|
||||
.then((res) => {
|
||||
const messages = res.data;
|
||||
this.setState({
|
||||
messages: messages,
|
||||
});
|
||||
|
||||
if (messages.length > 0) {
|
||||
const lastMessage = messages[messages.length - 1];
|
||||
if (lastMessage.author === "AI" && lastMessage.replyTo !== "" && lastMessage.text === "") {
|
||||
let text = "";
|
||||
MessageBackend.getMessageAnswer(lastMessage.owner, lastMessage.name, (data) => {
|
||||
if (data === "") {
|
||||
data = "\n";
|
||||
}
|
||||
|
||||
const lastMessage2 = Setting.deepCopy(lastMessage);
|
||||
text += data;
|
||||
lastMessage2.text = text;
|
||||
messages[messages.length - 1] = lastMessage2;
|
||||
this.setState({
|
||||
messages: messages,
|
||||
});
|
||||
}, (error) => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to get answer")}: ${error}`);
|
||||
|
||||
const lastMessage2 = Setting.deepCopy(lastMessage);
|
||||
lastMessage2.text = `#ERROR#: ${error}`;
|
||||
messages[messages.length - 1] = lastMessage2;
|
||||
this.setState({
|
||||
messages: messages,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Setting.scrollToDiv(`chatbox-list-item-${messages.length}`);
|
||||
});
|
||||
}
|
||||
|
||||
addChat(chat) {
|
||||
const newChat = this.newChat(chat);
|
||||
ChatBackend.addChat(newChat)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||
this.setState({
|
||||
chatName: newChat.name,
|
||||
messages: null,
|
||||
});
|
||||
this.getMessages(newChat.name);
|
||||
|
||||
const {pagination} = this.state;
|
||||
this.fetch({pagination}, false);
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
deleteChat(chats, i, chat) {
|
||||
ChatBackend.deleteChat(chat)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||
const data = Setting.deleteRow(this.state.data, i);
|
||||
const j = Math.min(i, data.length - 1);
|
||||
if (j < 0) {
|
||||
this.setState({
|
||||
chatName: undefined,
|
||||
messages: [],
|
||||
data: data,
|
||||
});
|
||||
} else {
|
||||
const focusedChat = data[j];
|
||||
this.setState({
|
||||
chatName: focusedChat.name,
|
||||
messages: null,
|
||||
data: data,
|
||||
});
|
||||
this.getMessages(focusedChat.name);
|
||||
}
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
getCurrentChat() {
|
||||
return this.state.data.filter(chat => chat.name === this.state.chatName)[0];
|
||||
}
|
||||
|
||||
renderTable(chats) {
|
||||
const onSelectChat = (i) => {
|
||||
const chat = chats[i];
|
||||
this.setState({
|
||||
chatName: chat.name,
|
||||
messages: null,
|
||||
});
|
||||
this.getMessages(chat.name);
|
||||
};
|
||||
|
||||
const onAddChat = () => {
|
||||
const chat = this.getCurrentChat();
|
||||
this.addChat(chat);
|
||||
};
|
||||
|
||||
const onDeleteChat = (i) => {
|
||||
const chat = chats[i];
|
||||
this.deleteChat(chats, i, chat);
|
||||
};
|
||||
|
||||
if (this.state.loading) {
|
||||
return (
|
||||
<div style={{display: "flex", justifyContent: "center", alignItems: "center"}}>
|
||||
<Spin size="large" tip={i18next.t("login:Loading")} style={{paddingTop: "10%"}} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{display: "flex", height: "calc(100vh - 140px)"}}>
|
||||
<div style={{width: "250px", height: "100%", backgroundColor: "white", borderRight: "1px solid rgb(245,245,245)", borderBottom: "1px solid rgb(245,245,245)"}}>
|
||||
<ChatMenu ref={this.menu} chats={chats} onSelectChat={onSelectChat} onAddChat={onAddChat} onDeleteChat={onDeleteChat} />
|
||||
</div>
|
||||
<div style={{flex: 1, height: "100%", backgroundColor: "white", position: "relative"}}>
|
||||
{
|
||||
(this.state.messages === undefined || this.state.messages === null) ? null : (
|
||||
<div style={{
|
||||
position: "absolute",
|
||||
top: -50,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
backgroundImage: "url(https://cdn.casbin.org/img/casdoor-logo_1185x256.png)",
|
||||
backgroundPosition: "center",
|
||||
backgroundRepeat: "no-repeat",
|
||||
backgroundSize: "200px auto",
|
||||
backgroundBlendMode: "luminosity",
|
||||
filter: "grayscale(80%) brightness(140%) contrast(90%)",
|
||||
opacity: 0.5,
|
||||
pointerEvents: "none",
|
||||
}}>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<ChatBox messages={this.state.messages || []} sendMessage={(text) => {this.sendMessage(text);}} account={this.props.account} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
fetch = (params = {}, setLoading = true) => {
|
||||
let field = params.searchedColumn, value = params.searchText;
|
||||
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
if (params.category !== undefined && params.category !== null) {
|
||||
field = "category";
|
||||
value = params.category;
|
||||
} else if (params.type !== undefined && params.type !== null) {
|
||||
field = "type";
|
||||
value = params.type;
|
||||
}
|
||||
if (setLoading) {
|
||||
this.setState({loading: true});
|
||||
}
|
||||
ChatBackend.getChats("admin", params.pagination.current, -1, field, value, sortField, sortOrder)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
loading: false,
|
||||
data: res.data,
|
||||
messages: [],
|
||||
pagination: {
|
||||
...params.pagination,
|
||||
total: res.data2,
|
||||
},
|
||||
searchText: params.searchText,
|
||||
searchedColumn: params.searchedColumn,
|
||||
});
|
||||
|
||||
const chats = res.data;
|
||||
if (this.state.chatName === undefined && chats.length > 0) {
|
||||
const chat = chats[0];
|
||||
this.getMessages(chat.name);
|
||||
this.setState({
|
||||
chatName: chat.name,
|
||||
});
|
||||
}
|
||||
|
||||
if (!setLoading) {
|
||||
this.menu.current.setSelectedKeyToNewChat(chats);
|
||||
}
|
||||
} else {
|
||||
if (Setting.isResponseDenied(res)) {
|
||||
this.setState({
|
||||
isAuthorized: false,
|
||||
});
|
||||
} else {
|
||||
Setting.showMessage("error", res.msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default ChatPage;
|
@@ -20,9 +20,6 @@ export const IsDemoMode = false;
|
||||
export const ForceLanguage = "";
|
||||
export const DefaultLanguage = "en";
|
||||
|
||||
export const EnableExtraPages = true;
|
||||
export const EnableChatPages = true;
|
||||
|
||||
export const InitThemeAlgorithm = true;
|
||||
export const ThemeDefault = {
|
||||
themeType: "default",
|
||||
|
@@ -221,6 +221,7 @@ class GroupTreePage extends React.Component {
|
||||
onChange={(value) => {
|
||||
this.setState({
|
||||
organizationName: value,
|
||||
groupName: "",
|
||||
});
|
||||
this.props.history.push(`/trees/${value}`);
|
||||
}}
|
||||
|
@@ -1,233 +0,0 @@
|
||||
// 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.
|
||||
|
||||
import React from "react";
|
||||
import {Button, Card, Col, Input, Row, Select} from "antd";
|
||||
import * as ChatBackend from "./backend/ChatBackend";
|
||||
import * as MessageBackend from "./backend/MessageBackend";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import * as UserBackend from "./backend/UserBackend";
|
||||
import * as Setting from "./Setting";
|
||||
import i18next from "i18next";
|
||||
|
||||
const {TextArea} = Input;
|
||||
|
||||
class MessageEditPage extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
messageName: props.match.params.messageName,
|
||||
message: null,
|
||||
organizations: [],
|
||||
chats: [],
|
||||
users: [],
|
||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
this.getMessage();
|
||||
this.getOrganizations();
|
||||
this.getChats();
|
||||
}
|
||||
|
||||
getMessage() {
|
||||
MessageBackend.getMessage("admin", this.state.messageName)
|
||||
.then((res) => {
|
||||
if (res.data === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
message: res.data,
|
||||
});
|
||||
this.getUsers(res.data.organization);
|
||||
});
|
||||
}
|
||||
|
||||
getOrganizations() {
|
||||
OrganizationBackend.getOrganizations("admin")
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
organizations: res.data || [],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getChats() {
|
||||
ChatBackend.getChats("admin")
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
chats: res.data || [],
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getUsers(organizationName) {
|
||||
UserBackend.getUsers(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
users: res.data,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
parseMessageField(key, value) {
|
||||
if ([].includes(key)) {
|
||||
value = Setting.myParseInt(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
updateMessageField(key, value) {
|
||||
value = this.parseMessageField(key, value);
|
||||
|
||||
const message = this.state.message;
|
||||
message[key] = value;
|
||||
this.setState({
|
||||
message: message,
|
||||
});
|
||||
}
|
||||
|
||||
renderMessage() {
|
||||
return (
|
||||
<Card size="small" title={
|
||||
<div>
|
||||
{this.state.mode === "add" ? i18next.t("message:New Message") : i18next.t("message:Edit Message")}
|
||||
<Button onClick={() => this.submitMessageEdit(false)}>{i18next.t("general:Save")}</Button>
|
||||
<Button style={{marginLeft: "20px"}} type="primary" onClick={() => this.submitMessageEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
|
||||
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} onClick={() => this.deleteMessage()}>{i18next.t("general:Cancel")}</Button> : null}
|
||||
</div>
|
||||
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
|
||||
<Row style={{marginTop: "10px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} disabled={!Setting.isAdminUser(this.props.account)} style={{width: "100%"}} value={this.state.message.organization} onChange={(value => {this.updateMessageField("organization", value);})}
|
||||
options={this.state.organizations.map((organization) => Setting.getOption(organization.name, organization.name))
|
||||
} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.message.name} onChange={e => {
|
||||
this.updateMessageField("name", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("message:Chat"), i18next.t("message:Chat - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.message.chat} onChange={(value => {this.updateMessageField("chat", value);})}
|
||||
options={this.state.chats.map((chat) => Setting.getOption(chat.name, chat.name))
|
||||
} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("message:Author"), i18next.t("message:Author - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.message.author} onChange={(value => {this.updateMessageField("author", value);})}
|
||||
options={this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`))
|
||||
} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("message:Text"), i18next.t("message:Text - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22}>
|
||||
<TextArea rows={10} value={this.state.message.text} onChange={e => {
|
||||
this.updateMessageField("text", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
submitMessageEdit(willExist) {
|
||||
const message = Setting.deepCopy(this.state.message);
|
||||
MessageBackend.updateMessage(this.state.message.owner, this.state.messageName, message)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||
this.setState({
|
||||
messageName: this.state.message.name,
|
||||
});
|
||||
|
||||
if (willExist) {
|
||||
this.props.history.push("/messages");
|
||||
} else {
|
||||
this.props.history.push(`/messages/${this.state.message.name}`);
|
||||
}
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||
this.updateMessageField("name", this.state.messageName);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
deleteMessage() {
|
||||
MessageBackend.deleteMessage(this.state.message)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.props.history.push("/messages");
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
this.state.message !== null ? this.renderMessage() : null
|
||||
}
|
||||
<div style={{marginTop: "20px", marginLeft: "40px"}}>
|
||||
<Button size="large" onClick={() => this.submitMessageEdit(false)}>{i18next.t("general:Save")}</Button>
|
||||
<Button style={{marginLeft: "20px"}} type="primary" size="large" onClick={() => this.submitMessageEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
|
||||
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} size="large" onClick={() => this.deleteMessage()}>{i18next.t("general:Cancel")}</Button> : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default MessageEditPage;
|
@@ -1,240 +0,0 @@
|
||||
// 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.
|
||||
|
||||
import React from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import {Button, Table} from "antd";
|
||||
import moment from "moment";
|
||||
import * as Setting from "./Setting";
|
||||
import * as MessageBackend from "./backend/MessageBackend";
|
||||
import i18next from "i18next";
|
||||
import BaseListPage from "./BaseListPage";
|
||||
import PopconfirmModal from "./common/modal/PopconfirmModal";
|
||||
|
||||
class MessageListPage extends BaseListPage {
|
||||
newMessage() {
|
||||
const randomName = Setting.getRandomName();
|
||||
const organizationName = Setting.getRequestOrganization(this.props.account);
|
||||
return {
|
||||
owner: "admin", // this.props.account.messagename,
|
||||
name: `message_${randomName}`,
|
||||
createdTime: moment().format(),
|
||||
organization: organizationName,
|
||||
chat: "",
|
||||
replyTo: "",
|
||||
author: `${this.props.account.owner}/${this.props.account.name}`,
|
||||
text: "",
|
||||
};
|
||||
}
|
||||
|
||||
addMessage() {
|
||||
const newMessage = this.newMessage();
|
||||
MessageBackend.addMessage(newMessage)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.props.history.push({pathname: `/messages/${newMessage.name}`, mode: "add"});
|
||||
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
deleteMessage(i) {
|
||||
MessageBackend.deleteMessage(this.state.data[i])
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||
this.setState({
|
||||
data: Setting.deleteRow(this.state.data, i),
|
||||
pagination: {total: this.state.pagination.total - 1},
|
||||
});
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
renderTable(messages) {
|
||||
const columns = [
|
||||
{
|
||||
title: i18next.t("general:Organization"),
|
||||
dataIndex: "organization",
|
||||
key: "organization",
|
||||
width: "150px",
|
||||
fixed: "left",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("organization"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/organizations/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Name"),
|
||||
dataIndex: "name",
|
||||
key: "name",
|
||||
width: "120px",
|
||||
fixed: "left",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("name"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/messages/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Created time"),
|
||||
dataIndex: "createdTime",
|
||||
key: "createdTime",
|
||||
width: "150px",
|
||||
sorter: true,
|
||||
render: (text, record, index) => {
|
||||
return Setting.getFormattedDate(text);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("message:Chat"),
|
||||
dataIndex: "chat",
|
||||
key: "chat",
|
||||
width: "120px",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("chat"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/chats/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("message:Author"),
|
||||
dataIndex: "author",
|
||||
key: "author",
|
||||
width: "120px",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("author"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/users/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("message:Text"),
|
||||
dataIndex: "text",
|
||||
key: "text",
|
||||
// width: '100px',
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("text"),
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Action"),
|
||||
dataIndex: "",
|
||||
key: "op",
|
||||
width: "170px",
|
||||
fixed: (Setting.isMobile()) ? "false" : "right",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/messages/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deleteMessage(index)}
|
||||
>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const paginationProps = {
|
||||
total: this.state.pagination.total,
|
||||
showQuickJumper: true,
|
||||
showSizeChanger: true,
|
||||
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table scroll={{x: "max-content"}} columns={columns} dataSource={messages} rowKey={(record) => `${record.owner}/${record.name}`}size="middle" bordered pagination={paginationProps}
|
||||
title={() => (
|
||||
<div>
|
||||
{i18next.t("general:Messages")}
|
||||
<Button type="primary" size="small" onClick={this.addMessage.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||
</div>
|
||||
)}
|
||||
loading={this.state.loading}
|
||||
onChange={this.handleTableChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
fetch = (params = {}) => {
|
||||
let field = params.searchedColumn, value = params.searchText;
|
||||
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||
if (params.category !== undefined && params.category !== null) {
|
||||
field = "category";
|
||||
value = params.category;
|
||||
} else if (params.type !== undefined && params.type !== null) {
|
||||
field = "type";
|
||||
value = params.type;
|
||||
}
|
||||
this.setState({loading: true});
|
||||
MessageBackend.getMessages("admin", Setting.isDefaultOrganizationSelected(this.props.account) ? "" : Setting.getRequestOrganization(this.props.account), params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
});
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
data: res.data,
|
||||
pagination: {
|
||||
...params.pagination,
|
||||
total: res.data2,
|
||||
},
|
||||
searchText: params.searchText,
|
||||
searchedColumn: params.searchedColumn,
|
||||
});
|
||||
} else {
|
||||
if (Setting.isResponseDenied(res)) {
|
||||
this.setState({
|
||||
isAuthorized: false,
|
||||
});
|
||||
} else {
|
||||
Setting.showMessage("error", res.msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export default MessageListPage;
|
@@ -40,7 +40,7 @@ class PaymentEditPage extends React.Component {
|
||||
}
|
||||
|
||||
getPayment() {
|
||||
PaymentBackend.getPayment("admin", this.state.paymentName)
|
||||
PaymentBackend.getPayment(this.state.organizationName, this.state.paymentName)
|
||||
.then((res) => {
|
||||
if (res.data === null) {
|
||||
this.props.history.push("/404");
|
||||
@@ -163,7 +163,7 @@ class PaymentEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={true} value={this.state.payment.organization} onChange={e => {
|
||||
<Input disabled={true} value={this.state.payment.owner} onChange={e => {
|
||||
// this.updatePaymentField('organization', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
|
@@ -28,13 +28,12 @@ class PaymentListPage extends BaseListPage {
|
||||
const randomName = Setting.getRandomName();
|
||||
const organizationName = Setting.getRequestOrganization(this.props.account);
|
||||
return {
|
||||
owner: "admin",
|
||||
owner: organizationName,
|
||||
name: `payment_${randomName}`,
|
||||
createdTime: moment().format(),
|
||||
displayName: `New Payment - ${randomName}`,
|
||||
provider: "provider_pay_paypal",
|
||||
type: "PayPal",
|
||||
organization: organizationName,
|
||||
user: "admin",
|
||||
productName: "computer-1",
|
||||
productDisplayName: "A notebook computer",
|
||||
@@ -54,7 +53,7 @@ class PaymentListPage extends BaseListPage {
|
||||
PaymentBackend.addPayment(newPayment)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.props.history.push({pathname: `/payments/${newPayment.name}`, mode: "add"});
|
||||
this.props.history.push({pathname: `/payments/${newPayment.owner}/${newPayment.name}`, mode: "add"});
|
||||
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||
@@ -96,7 +95,7 @@ class PaymentListPage extends BaseListPage {
|
||||
...this.getColumnSearchProps("name"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/payments/${text}`}>
|
||||
<Link to={`/payments/${record.owner}/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
@@ -112,7 +111,7 @@ class PaymentListPage extends BaseListPage {
|
||||
...this.getColumnSearchProps("provider"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/providers/${text}`}>
|
||||
<Link to={`/providers/${record.owner}/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
@@ -120,11 +119,11 @@ class PaymentListPage extends BaseListPage {
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Organization"),
|
||||
dataIndex: "organization",
|
||||
key: "organization",
|
||||
dataIndex: "owner",
|
||||
key: "owner",
|
||||
width: "120px",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("organization"),
|
||||
...this.getColumnSearchProps("owner"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/organizations/${text}`}>
|
||||
@@ -142,7 +141,7 @@ class PaymentListPage extends BaseListPage {
|
||||
...this.getColumnSearchProps("user"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/users/${record.organization}/${text}`}>
|
||||
<Link to={`/users/${record.owner}/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
@@ -222,8 +221,8 @@ class PaymentListPage extends BaseListPage {
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<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>
|
||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => this.props.history.push(`/payments/${record.owner}/${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.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||
onConfirm={() => this.deletePayment(index)}
|
||||
@@ -266,7 +265,7 @@ class PaymentListPage extends BaseListPage {
|
||||
value = params.type;
|
||||
}
|
||||
this.setState({loading: true});
|
||||
PaymentBackend.getPayments("admin", Setting.isDefaultOrganizationSelected(this.props.account) ? "" : Setting.getRequestOrganization(this.props.account), params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||
PaymentBackend.getPayments(Setting.getRequestOrganization(this.props.account), Setting.isDefaultOrganizationSelected(this.props.account) ? "" : Setting.getRequestOrganization(this.props.account), params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
|
@@ -24,6 +24,7 @@ class PaymentResultPage extends React.Component {
|
||||
this.state = {
|
||||
classes: props,
|
||||
paymentName: props.match.params.paymentName,
|
||||
organizationName: props.match.params.organizationName,
|
||||
payment: null,
|
||||
timeout: null,
|
||||
};
|
||||
@@ -40,18 +41,37 @@ class PaymentResultPage extends React.Component {
|
||||
}
|
||||
|
||||
getPayment() {
|
||||
PaymentBackend.getPayment("admin", this.state.paymentName)
|
||||
PaymentBackend.getPayment(this.state.organizationName, this.state.paymentName)
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
payment: res.data,
|
||||
});
|
||||
|
||||
// window.console.log("payment=", res.data);
|
||||
if (res.data.state === "Created") {
|
||||
this.setState({timeout: setTimeout(() => this.getPayment(), 1000)});
|
||||
if (res.data.type === "PayPal") {
|
||||
this.setState({
|
||||
timeout: setTimeout(() => {
|
||||
PaymentBackend.notifyPayment(this.state.organizationName, this.state.paymentName)
|
||||
.then((res) => {
|
||||
this.getPayment();
|
||||
});
|
||||
}, 1000),
|
||||
});
|
||||
} else {
|
||||
this.setState({timeout: setTimeout(() => this.getPayment(), 1000)});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
goToPaymentUrl(payment) {
|
||||
if (payment.returnUrl === undefined || payment.returnUrl === null || payment.returnUrl === "") {
|
||||
Setting.goToLink(`${window.location.origin}/products/${payment.owner}/${payment.productName}/buy`);
|
||||
} else {
|
||||
Setting.goToLink(payment.returnUrl);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const payment = this.state.payment;
|
||||
|
||||
@@ -71,7 +91,7 @@ class PaymentResultPage extends React.Component {
|
||||
subTitle={i18next.t("payment:Please click the below button to return to the original website")}
|
||||
extra={[
|
||||
<Button type="primary" key="returnUrl" onClick={() => {
|
||||
Setting.goToLink(payment.returnUrl);
|
||||
this.goToPaymentUrl(payment);
|
||||
}}>
|
||||
{i18next.t("payment:Return to Website")}
|
||||
</Button>,
|
||||
@@ -95,6 +115,26 @@ class PaymentResultPage extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else if (payment.state === "Canceled") {
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
Setting.renderHelmet(payment)
|
||||
}
|
||||
<Result
|
||||
status="warning"
|
||||
title={`${i18next.t("payment:The payment has been canceled")}: ${payment.productDisplayName}, ${i18next.t("payment:the current state is")}: ${payment.state}`}
|
||||
subTitle={i18next.t("payment:Please click the below button to return to the original website")}
|
||||
extra={[
|
||||
<Button type="primary" key="returnUrl" onClick={() => {
|
||||
this.goToPaymentUrl(payment);
|
||||
}}>
|
||||
{i18next.t("payment:Return to Website")}
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
@@ -104,10 +144,10 @@ class PaymentResultPage extends React.Component {
|
||||
<Result
|
||||
status="error"
|
||||
title={`${i18next.t("payment:The payment has failed")}: ${payment.productDisplayName}, ${i18next.t("payment:the current state is")}: ${payment.state}`}
|
||||
subTitle={i18next.t("payment:Please click the below button to return to the original website")}
|
||||
subTitle={`${i18next.t("payment:Failed reason")}: ${payment.message}`}
|
||||
extra={[
|
||||
<Button type="primary" key="returnUrl" onClick={() => {
|
||||
Setting.goToLink(payment.returnUrl);
|
||||
this.goToPaymentUrl(payment);
|
||||
}}>
|
||||
{i18next.t("payment:Return to Website")}
|
||||
</Button>,
|
||||
|
@@ -24,7 +24,8 @@ class ProductBuyPage extends React.Component {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
productName: props.match?.params.productName,
|
||||
organizationName: props.organizationName !== undefined ? props.organizationName : props?.match?.params?.organizationName,
|
||||
productName: props.productName !== undefined ? props.productName : props?.match?.params?.productName,
|
||||
product: null,
|
||||
isPlacingOrder: false,
|
||||
qrCodeModalProvider: null,
|
||||
@@ -36,17 +37,15 @@ class ProductBuyPage extends React.Component {
|
||||
}
|
||||
|
||||
getProduct() {
|
||||
if (this.state.productName === undefined) {
|
||||
return;
|
||||
if (this.state.productName === undefined || this.state.organizationName === undefined) {
|
||||
return ;
|
||||
}
|
||||
|
||||
ProductBackend.getProduct(this.props.account.owner, this.state.productName)
|
||||
ProductBackend.getProduct(this.state.organizationName, this.state.productName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
product: res.data,
|
||||
});
|
||||
@@ -97,7 +96,7 @@ class ProductBuyPage extends React.Component {
|
||||
isPlacingOrder: true,
|
||||
});
|
||||
|
||||
ProductBackend.buyProduct(this.state.product.owner, this.state.productName, provider.name)
|
||||
ProductBackend.buyProduct(product.owner, product.name, provider.name)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
const payUrl = res.data;
|
||||
|
@@ -102,6 +102,13 @@ class ProductListPage extends BaseListPage {
|
||||
width: "150px",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("owner"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/organizations/${text}`}>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Created time"),
|
||||
@@ -189,6 +196,7 @@ class ProductListPage extends BaseListPage {
|
||||
width: "500px",
|
||||
...this.getColumnSearchProps("providers"),
|
||||
render: (text, record, index) => {
|
||||
const providerOwner = record.owner;
|
||||
const providers = text;
|
||||
if (providers.length === 0) {
|
||||
return `(${i18next.t("general:empty")})`;
|
||||
@@ -207,9 +215,9 @@ class ProductListPage extends BaseListPage {
|
||||
<List.Item>
|
||||
<div style={{display: "inline"}}>
|
||||
<Tooltip placement="topLeft" title="Edit">
|
||||
<Button style={{marginRight: "5px"}} icon={<EditOutlined />} size="small" onClick={() => Setting.goToLinkSoft(this, `/providers/${providerName}`)} />
|
||||
<Button style={{marginRight: "5px"}} icon={<EditOutlined />} size="small" onClick={() => Setting.goToLinkSoft(this, `/providers/${providerOwner}/${providerName}`)} />
|
||||
</Tooltip>
|
||||
<Link to={`/providers/${providerName}`}>
|
||||
<Link to={`/providers/${providerOwner}/${providerName}`}>
|
||||
{providerName}
|
||||
</Link>
|
||||
</div>
|
||||
|
@@ -134,10 +134,14 @@ class ProviderEditPage extends React.Component {
|
||||
case "Email":
|
||||
return Setting.getLabel(i18next.t("signup:Username"), i18next.t("signup:Username - Tooltip"));
|
||||
case "SMS":
|
||||
if (provider.type === "Volc Engine SMS") {
|
||||
if (provider.type === "Volc Engine SMS" || provider.type === "Amazon SNS" || provider.type === "Baidu Cloud SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:Access key"), i18next.t("provider:Access key - Tooltip"));
|
||||
} else if (provider.type === "Huawei Cloud SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:App key"), i18next.t("provider:App key - Tooltip"));
|
||||
} else if (provider.type === "UCloud SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:Public key"), i18next.t("provider:Public key - Tooltip"));
|
||||
} else if (provider.type === "Msg91 SMS" || provider.type === "Infobip SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:Sender Id"), i18next.t("provider:Sender Id - Tooltip"));
|
||||
} else {
|
||||
return Setting.getLabel(i18next.t("provider:Client ID"), i18next.t("provider:Client ID - Tooltip"));
|
||||
}
|
||||
@@ -157,10 +161,16 @@ class ProviderEditPage extends React.Component {
|
||||
case "Email":
|
||||
return Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"));
|
||||
case "SMS":
|
||||
if (provider.type === "Volc Engine SMS") {
|
||||
if (provider.type === "Volc Engine SMS" || provider.type === "Amazon SNS" || provider.type === "Baidu Cloud SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:Secret access key"), i18next.t("provider:Secret access key - Tooltip"));
|
||||
} else if (provider.type === "Huawei Cloud SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:App secret"), i18next.t("provider:AppSecret - Tooltip"));
|
||||
} else if (provider.type === "UCloud SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:Private Key"), i18next.t("provider:Private Key - Tooltip"));
|
||||
} else if (provider.type === "Msg91 SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:Auth Key"), i18next.t("provider:Auth Key - Tooltip"));
|
||||
} else if (provider.type === "Infobip SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:Api Key"), i18next.t("provider:Api Key - Tooltip"));
|
||||
} else {
|
||||
return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip"));
|
||||
}
|
||||
@@ -170,8 +180,6 @@ class ProviderEditPage extends React.Component {
|
||||
} else {
|
||||
return Setting.getLabel(i18next.t("provider:Secret key"), i18next.t("provider:Secret key - Tooltip"));
|
||||
}
|
||||
case "AI":
|
||||
return Setting.getLabel(i18next.t("provider:Secret key"), i18next.t("provider:Secret key - Tooltip"));
|
||||
default:
|
||||
return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip"));
|
||||
}
|
||||
@@ -236,7 +244,7 @@ class ProviderEditPage extends React.Component {
|
||||
tooltip = i18next.t("provider:Agent ID - Tooltip");
|
||||
}
|
||||
} else if (provider.category === "SMS") {
|
||||
if (provider.type === "Twilio SMS") {
|
||||
if (provider.type === "Twilio SMS" || provider.type === "Azure ACS") {
|
||||
text = i18next.t("provider:Sender number");
|
||||
tooltip = i18next.t("provider:Sender number - Tooltip");
|
||||
} else if (provider.type === "Tencent Cloud SMS") {
|
||||
@@ -248,6 +256,18 @@ class ProviderEditPage extends React.Component {
|
||||
} else if (provider.type === "Huawei Cloud SMS") {
|
||||
text = i18next.t("provider:Channel No.");
|
||||
tooltip = i18next.t("provider:Channel No. - Tooltip");
|
||||
} else if (provider.type === "Amazon SNS") {
|
||||
text = i18next.t("provider:Region");
|
||||
tooltip = i18next.t("provider:Region - Tooltip");
|
||||
} else if (provider.type === "Baidu Cloud SMS") {
|
||||
text = i18next.t("provider:Endpoint");
|
||||
tooltip = i18next.t("provider:Endpoint - Tooltip");
|
||||
} else if (provider.type === "Infobip SMS") {
|
||||
text = i18next.t("provider:Base URL");
|
||||
tooltip = i18next.t("provider:Base URL - Tooltip");
|
||||
} else if (provider.type === "UCloud SMS") {
|
||||
text = i18next.t("provider:Project Id");
|
||||
tooltip = i18next.t("provider:Project Id - Tooltip");
|
||||
}
|
||||
} else if (provider.category === "Email") {
|
||||
if (provider.type === "SUBMAIL") {
|
||||
@@ -356,15 +376,12 @@ class ProviderEditPage extends React.Component {
|
||||
this.updateProviderField("type", "PayPal");
|
||||
} else if (value === "Captcha") {
|
||||
this.updateProviderField("type", "Default");
|
||||
} else if (value === "AI") {
|
||||
this.updateProviderField("type", "OpenAI API - GPT");
|
||||
} else if (value === "Web3") {
|
||||
this.updateProviderField("type", "MetaMask");
|
||||
}
|
||||
})}>
|
||||
{
|
||||
[
|
||||
{id: "AI", name: "AI"},
|
||||
{id: "Captcha", name: "Captcha"},
|
||||
{id: "Email", name: "Email"},
|
||||
{id: "OAuth", name: "OAuth"},
|
||||
@@ -385,7 +402,7 @@ class ProviderEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("provider:Type"), i18next.t("provider:Type - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.provider.type} onChange={(value => {
|
||||
<Select virtual={false} style={{width: "100%"}} showSearch value={this.state.provider.type} onChange={(value => {
|
||||
this.updateProviderField("type", value);
|
||||
if (value === "Local File System") {
|
||||
this.updateProviderField("domain", Setting.getFullServerUrl());
|
||||
@@ -400,7 +417,10 @@ class ProviderEditPage extends React.Component {
|
||||
{
|
||||
Setting.getProviderTypeOptions(this.state.provider.category)
|
||||
.sort((a, b) => a.name.localeCompare(b.name))
|
||||
.map((providerType, index) => <Option key={index} value={providerType.id}>{providerType.name}</Option>)
|
||||
.map((providerType, index) => <Option key={index} value={providerType.id}>
|
||||
<img width={20} height={20} style={{marginBottom: "3px", marginRight: "10px"}} src={Setting.getProviderLogoURL({category: this.state.provider.category, type: providerType.id})} alt={providerType.id} />
|
||||
{providerType.name}
|
||||
</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
@@ -529,20 +549,16 @@ class ProviderEditPage extends React.Component {
|
||||
{
|
||||
(this.state.provider.category === "Captcha" && this.state.provider.type === "Default") || (this.state.provider.category === "Web3") || (this.state.provider.category === "Storage" && this.state.provider.type === "Local File System") ? null : (
|
||||
<React.Fragment>
|
||||
{
|
||||
this.state.provider.category === "AI" ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{this.getClientIdLabel(this.state.provider)} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.provider.clientId} onChange={e => {
|
||||
this.updateProviderField("clientId", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{this.getClientIdLabel(this.state.provider)} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.provider.clientId} onChange={e => {
|
||||
this.updateProviderField("clientId", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{this.getClientSecretLabel(this.state.provider)} :
|
||||
@@ -601,7 +617,7 @@ class ProviderEditPage extends React.Component {
|
||||
)
|
||||
}
|
||||
{
|
||||
this.state.provider.type !== "Adfs" && this.state.provider.type !== "AzureAD" && this.state.provider.type !== "Casdoor" && this.state.provider.type !== "Okta" ? null : (
|
||||
this.state.provider.type !== "ADFS" && this.state.provider.type !== "AzureAD" && this.state.provider.type !== "Casdoor" && this.state.provider.type !== "Okta" ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
|
||||
@@ -628,7 +644,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
{["Local File System", "MinIO", "Tencent Cloud COS"].includes(this.state.provider.type) ? null : (
|
||||
{["Local File System", "MinIO", "Tencent Cloud COS", "Google Cloud Storage", "Qiniu Cloud Kodo"].includes(this.state.provider.type) ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{Setting.getLabel(i18next.t("provider:Endpoint (Intranet)"), i18next.t("provider:Region endpoint for Intranet"))} :
|
||||
@@ -662,7 +678,7 @@ class ProviderEditPage extends React.Component {
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
{["MinIO"].includes(this.state.provider.type) ? null : (
|
||||
{["MinIO", "Google Cloud Storage", "Qiniu Cloud Kodo"].includes(this.state.provider.type) ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
|
||||
@@ -674,7 +690,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
{["AWS S3", "Tencent Cloud COS"].includes(this.state.provider.type) ? (
|
||||
{["AWS S3", "Tencent Cloud COS", "Qiniu Cloud Kodo"].includes(this.state.provider.type) ? (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{Setting.getLabel(i18next.t("provider:Region ID"), i18next.t("provider:Region ID - Tooltip"))} :
|
||||
@@ -763,7 +779,7 @@ class ProviderEditPage extends React.Component {
|
||||
</React.Fragment>
|
||||
) : this.state.provider.category === "SMS" ? (
|
||||
<React.Fragment>
|
||||
{this.state.provider.type === "Twilio SMS" ?
|
||||
{["Twilio SMS", "Amazon SNS", "Azure ACS", "Msg91 SMS", "Infobip SMS"].includes(this.state.provider.type) ?
|
||||
null :
|
||||
(<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
@@ -777,16 +793,20 @@ class ProviderEditPage extends React.Component {
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Template code"), i18next.t("provider:Template code - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.provider.templateCode} onChange={e => {
|
||||
this.updateProviderField("templateCode", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
{["Infobip SMS"].includes(this.state.provider.type) ?
|
||||
null :
|
||||
(<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Template code"), i18next.t("provider:Template code - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.provider.templateCode} onChange={e => {
|
||||
this.updateProviderField("templateCode", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:SMS Test"), i18next.t("provider:SMS Test - Tooltip"))} :
|
||||
|
@@ -37,7 +37,7 @@ class ProviderListPage extends BaseListPage {
|
||||
|
||||
newProvider() {
|
||||
const randomName = Setting.getRandomName();
|
||||
const owner = Setting.isDefaultOrganizationSelected(this.props.account) ? this.state.owner : Setting.getRequestOrganization();
|
||||
const owner = Setting.isDefaultOrganizationSelected(this.props.account) ? this.state.owner : Setting.getRequestOrganization(this.props.account);
|
||||
return {
|
||||
owner: owner,
|
||||
name: `provider_${randomName}`,
|
||||
|
@@ -30,7 +30,6 @@ const {Option} = Select;
|
||||
|
||||
export const ServerUrl = "";
|
||||
|
||||
// export const StaticBaseUrl = "https://cdn.jsdelivr.net/gh/casbin/static";
|
||||
export const StaticBaseUrl = "https://cdn.casbin.org";
|
||||
|
||||
export const Countries = [{label: "English", key: "en", country: "US", alt: "English"},
|
||||
@@ -44,6 +43,9 @@ export const Countries = [{label: "English", key: "en", country: "US", alt: "Eng
|
||||
{label: "Русский", key: "ru", country: "RU", alt: "Русский"},
|
||||
{label: "TiếngViệt", key: "vi", country: "VN", alt: "TiếngViệt"},
|
||||
{label: "Português", key: "pt", country: "BR", alt: "Português"},
|
||||
{label: "Itariano", key: "it", country: "IT", alt: "Itariano"},
|
||||
{label: "Marley", key: "ms", country: "MY", alt: "Marley"},
|
||||
{label: "Tkiš", key: "tr", country: "TR", alt: "Tkiš"},
|
||||
];
|
||||
|
||||
export function getThemeData(organization, application) {
|
||||
@@ -83,10 +85,26 @@ export const OtherProviderInfo = {
|
||||
logo: `${StaticBaseUrl}/img/social_aliyun.png`,
|
||||
url: "https://aliyun.com/product/sms",
|
||||
},
|
||||
"Amazon SNS": {
|
||||
logo: `${StaticBaseUrl}/img/social_aws.png`,
|
||||
url: "https://aws.amazon.com/cn/sns/",
|
||||
},
|
||||
"Azure ACS": {
|
||||
logo: `${StaticBaseUrl}/img/social_azure.png`,
|
||||
url: "https://azure.microsoft.com/en-us/products/communication-services",
|
||||
},
|
||||
"Infobip SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_infobip.png`,
|
||||
url: "https://portal.infobip.com/homepage/",
|
||||
},
|
||||
"Tencent Cloud SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_tencent_cloud.jpg`,
|
||||
url: "https://cloud.tencent.com/product/sms",
|
||||
},
|
||||
"Baidu Cloud SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_baidu_cloud.png`,
|
||||
url: "https://cloud.baidu.com/product/sms.html",
|
||||
},
|
||||
"Volc Engine SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_volc_engine.jpg`,
|
||||
url: "https://www.volcengine.com/products/cloud-sms",
|
||||
@@ -95,6 +113,10 @@ export const OtherProviderInfo = {
|
||||
logo: `${StaticBaseUrl}/img/social_huawei.png`,
|
||||
url: "https://www.huaweicloud.com/product/msgsms.html",
|
||||
},
|
||||
"UCloud SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_ucloud.png`,
|
||||
url: "https://www.ucloud.cn/site/product/usms.html",
|
||||
},
|
||||
"Twilio SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_twilio.svg`,
|
||||
url: "https://www.twilio.com/messaging",
|
||||
@@ -107,6 +129,10 @@ export const OtherProviderInfo = {
|
||||
logo: `${StaticBaseUrl}/img/social_submail.svg`,
|
||||
url: "https://www.mysubmail.com",
|
||||
},
|
||||
"Msg91 SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_msg91.ico`,
|
||||
url: "https://control.msg91.com/app/",
|
||||
},
|
||||
"Mock SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_default.png`,
|
||||
url: "",
|
||||
@@ -151,6 +177,14 @@ export const OtherProviderInfo = {
|
||||
logo: `${StaticBaseUrl}/img/social_azure.png`,
|
||||
url: "https://azure.microsoft.com/en-us/services/storage/blobs/",
|
||||
},
|
||||
"Qiniu Cloud Kodo": {
|
||||
logo: `${StaticBaseUrl}/img/social_qiniu_cloud.png`,
|
||||
url: "https://www.qiniu.com/solutions/storage",
|
||||
},
|
||||
"Google Cloud Storage": {
|
||||
logo: `${StaticBaseUrl}/img/social_google_cloud.png`,
|
||||
url: "https://cloud.google.com/storage",
|
||||
},
|
||||
},
|
||||
SAML: {
|
||||
"Aliyun IDaaS": {
|
||||
@@ -737,7 +771,7 @@ export function getClickable(text) {
|
||||
|
||||
export function getProviderLogoURL(provider) {
|
||||
if (provider.category === "OAuth") {
|
||||
if (provider.type === "Custom") {
|
||||
if (provider.type === "Custom" && provider.customLogo) {
|
||||
return provider.customLogo;
|
||||
}
|
||||
return `${StaticBaseUrl}/img/social_${provider.type.toLowerCase()}.png`;
|
||||
@@ -776,7 +810,7 @@ export function getProviderTypeOptions(category) {
|
||||
{id: "WeCom", name: "WeCom"},
|
||||
{id: "Lark", name: "Lark"},
|
||||
{id: "GitLab", name: "GitLab"},
|
||||
{id: "Adfs", name: "Adfs"},
|
||||
{id: "ADFS", name: "ADFS"},
|
||||
{id: "Baidu", name: "Baidu"},
|
||||
{id: "Alipay", name: "Alipay"},
|
||||
{id: "Casdoor", name: "Casdoor"},
|
||||
@@ -851,13 +885,19 @@ export function getProviderTypeOptions(category) {
|
||||
} else if (category === "SMS") {
|
||||
return (
|
||||
[
|
||||
{id: "Aliyun SMS", name: "Aliyun SMS"},
|
||||
{id: "Aliyun SMS", name: "Alibaba Cloud SMS"},
|
||||
{id: "Amazon SNS", name: "Amazon SNS"},
|
||||
{id: "Azure ACS", name: "Azure ACS"},
|
||||
{id: "Infobip SMS", name: "Infobip SMS"},
|
||||
{id: "Tencent Cloud SMS", name: "Tencent Cloud SMS"},
|
||||
{id: "Baidu Cloud SMS", name: "Baidu Cloud SMS"},
|
||||
{id: "Volc Engine SMS", name: "Volc Engine SMS"},
|
||||
{id: "Huawei Cloud SMS", name: "Huawei Cloud SMS"},
|
||||
{id: "UCloud SMS", name: "UCloud SMS"},
|
||||
{id: "Twilio SMS", name: "Twilio SMS"},
|
||||
{id: "SmsBao SMS", name: "SmsBao SMS"},
|
||||
{id: "SUBMAIL SMS", name: "SUBMAIL SMS"},
|
||||
{id: "Msg91 SMS", name: "Msg91 SMS"},
|
||||
]
|
||||
);
|
||||
} else if (category === "Storage") {
|
||||
@@ -869,6 +909,8 @@ export function getProviderTypeOptions(category) {
|
||||
{id: "Aliyun OSS", name: "Aliyun OSS"},
|
||||
{id: "Tencent Cloud COS", name: "Tencent Cloud COS"},
|
||||
{id: "Azure Blob", name: "Azure Blob"},
|
||||
{id: "Qiniu Cloud Kodo", name: "Qiniu Cloud Kodo"},
|
||||
{id: "Google Cloud Storage", name: "Google Cloud Storage"},
|
||||
]
|
||||
);
|
||||
} else if (category === "SAML") {
|
||||
@@ -893,10 +935,6 @@ export function getProviderTypeOptions(category) {
|
||||
{id: "GEETEST", name: "GEETEST"},
|
||||
{id: "Cloudflare Turnstile", name: "Cloudflare Turnstile"},
|
||||
]);
|
||||
} else if (category === "AI") {
|
||||
return ([
|
||||
{id: "OpenAI API - GPT", name: "OpenAI API - GPT"},
|
||||
]);
|
||||
} else if (category === "Web3") {
|
||||
return ([
|
||||
{id: "MetaMask", name: "MetaMask"},
|
||||
@@ -1045,13 +1083,7 @@ export function getLabel(text, tooltip) {
|
||||
}
|
||||
|
||||
export function getItem(label, key, icon, children, type) {
|
||||
return {
|
||||
key,
|
||||
icon,
|
||||
children,
|
||||
label,
|
||||
type,
|
||||
};
|
||||
return {label: label, key: key, icon: icon, children: children, type: type};
|
||||
}
|
||||
|
||||
export function getOption(label, value) {
|
||||
@@ -1061,42 +1093,6 @@ export function getOption(label, value) {
|
||||
};
|
||||
}
|
||||
|
||||
function repeat(str, len) {
|
||||
while (str.length < len) {
|
||||
str += str.substr(0, len - str.length);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
function maskString(s) {
|
||||
if (s.length <= 2) {
|
||||
return s;
|
||||
} else {
|
||||
return `${s[0]}${repeat("*", s.length - 2)}${s[s.length - 1]}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function getMaskedPhone(s) {
|
||||
return s.replace(/(\d{3})\d*(\d{4})/, "$1****$2");
|
||||
}
|
||||
|
||||
export function getMaskedEmail(email) {
|
||||
if (email === "") {return;}
|
||||
const tokens = email.split("@");
|
||||
let username = tokens[0];
|
||||
username = maskString(username);
|
||||
|
||||
const domain = tokens[1];
|
||||
const domainTokens = domain.split(".");
|
||||
domainTokens[domainTokens.length - 2] = maskString(domainTokens[domainTokens.length - 2]);
|
||||
|
||||
return `${username}@${domainTokens.join(".")}`;
|
||||
}
|
||||
|
||||
export function IsEmail(s) {
|
||||
return s.includes("@");
|
||||
}
|
||||
|
||||
export function getArrayItem(array, key, value) {
|
||||
const res = array.filter(item => item[key] === value)[0];
|
||||
return res;
|
||||
@@ -1154,10 +1150,6 @@ export function getTag(color, text) {
|
||||
);
|
||||
}
|
||||
|
||||
export function getApplicationOrgName(application) {
|
||||
return `${application?.organizationObj.owner}/${application?.organizationObj.name}`;
|
||||
}
|
||||
|
||||
export function getApplicationName(application) {
|
||||
return `${application?.owner}/${application?.name}`;
|
||||
}
|
||||
@@ -1221,11 +1213,11 @@ export function isDefaultOrganizationSelected(account) {
|
||||
|
||||
const BuiltInObjects = [
|
||||
"api-enforcer-built-in",
|
||||
"permission-enforcer-built-in",
|
||||
"user-enforcer-built-in",
|
||||
"api-model-built-in",
|
||||
"permission-model-built-in",
|
||||
"user-model-built-in",
|
||||
"api-adapter-built-in",
|
||||
"permission-adapter-built-in",
|
||||
"user-adapter-built-in",
|
||||
];
|
||||
|
||||
export function builtInObject(obj) {
|
||||
|
@@ -133,13 +133,25 @@ class UserEditPage extends React.Component {
|
||||
|
||||
this.setState({
|
||||
application: res.data,
|
||||
isGroupsVisible: res.data?.organizationObj.accountItems?.some((item) => item.name === "Groups" && item.visible),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getUserOrganization() {
|
||||
return this.state.organizations.filter(organization => organization.name === this.state.user.owner)[0];
|
||||
}
|
||||
|
||||
isGroupsVisible() {
|
||||
const organization = this.getUserOrganization();
|
||||
if (!organization) {
|
||||
return false;
|
||||
} else {
|
||||
return organization.accountItems?.some((item) => item.name === "Groups" && item.visible);
|
||||
}
|
||||
}
|
||||
|
||||
getGroups(organizationName) {
|
||||
if (this.state.isGroupsVisible) {
|
||||
if (this.isGroupsVisible()) {
|
||||
GroupBackend.getGroups(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
@@ -319,7 +331,7 @@ class UserEditPage extends React.Component {
|
||||
})}
|
||||
>
|
||||
{
|
||||
this.state.groups?.map((group) => <Option key={group.name} value={group.name}>
|
||||
this.state.groups?.map((group) => <Option key={group.name} value={`${group.owner}/${group.name}`}>
|
||||
<Space>
|
||||
{group.type === "Physical" ? <UsergroupAddOutlined /> : <HolderOutlined />}
|
||||
{group.displayName}
|
||||
@@ -401,7 +413,7 @@ class UserEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<PasswordModal user={this.state.user} organization={this.state.application?.organizationObj} account={this.props.account} disabled={disabled} />
|
||||
<PasswordModal user={this.state.user} organization={this.getUserOrganization()} account={this.props.account} disabled={disabled} />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
@@ -442,7 +454,7 @@ class UserEditPage extends React.Component {
|
||||
onChange={(value) => {
|
||||
this.updateUserField("countryCode", value);
|
||||
}}
|
||||
countryCodes={this.state.application?.organizationObj.countryCodes}
|
||||
countryCodes={this.getUserOrganization()?.countryCodes}
|
||||
/>
|
||||
<Input value={this.state.user.phone}
|
||||
style={{width: "70%"}}
|
||||
@@ -599,10 +611,10 @@ class UserEditPage extends React.Component {
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
{
|
||||
this.state.application?.organizationObj.tags?.length > 0 ? (
|
||||
this.getUserOrganization()?.tags?.length > 0 ? (
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.user.tag}
|
||||
onChange={(value => {this.updateUserField("tag", value);})}
|
||||
options={this.state.application.organizationObj.tags?.map((tag) => {
|
||||
options={this.getUserOrganization()?.tags?.map((tag) => {
|
||||
const tokens = tag.split("|");
|
||||
const value = tokens[0];
|
||||
const displayValue = Setting.getLanguage() !== "zh" ? tokens[0] : tokens[1];
|
||||
@@ -888,7 +900,7 @@ class UserEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("mfa:Multi-factor authentication"), i18next.t("mfa:Multi-factor authentication - Tooltip "))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Card title={i18next.t("mfa:Multi-factor methods")}
|
||||
<Card size="small" title={i18next.t("mfa:Multi-factor methods")}
|
||||
extra={this.state.multiFactorAuths?.some(mfaProps => mfaProps.enabled) ?
|
||||
<PopconfirmModal
|
||||
text={i18next.t("general:Disable")}
|
||||
@@ -1008,7 +1020,7 @@ class UserEditPage extends React.Component {
|
||||
</div>
|
||||
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
|
||||
{
|
||||
this.state.application?.organizationObj.accountItems?.map(accountItem => {
|
||||
this.getUserOrganization()?.accountItems?.map(accountItem => {
|
||||
return (
|
||||
<React.Fragment key={accountItem.name}>
|
||||
{
|
||||
|
@@ -76,7 +76,7 @@ class UserListPage extends BaseListPage {
|
||||
phone: Setting.getRandomNumber(),
|
||||
countryCode: this.state.organization.countryCodes?.length > 0 ? this.state.organization.countryCodes[0] : "",
|
||||
address: [],
|
||||
groups: this.props.groupName ? [this.props.groupName] : [],
|
||||
groups: this.props.groupName ? [`${owner}/${this.props.groupName}`] : [],
|
||||
affiliation: "Example Inc.",
|
||||
tag: "staff",
|
||||
region: "",
|
||||
|
@@ -73,7 +73,7 @@ const authInfo = {
|
||||
scope: "read_user+profile",
|
||||
endpoint: "https://gitlab.com/oauth/authorize",
|
||||
},
|
||||
Adfs: {
|
||||
ADFS: {
|
||||
scope: "openid",
|
||||
endpoint: "http://example.com",
|
||||
},
|
||||
@@ -433,7 +433,7 @@ export function getAuthUrl(application, provider, method) {
|
||||
}
|
||||
} else if (provider.type === "Lark") {
|
||||
return `${endpoint}?app_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}`;
|
||||
} else if (provider.type === "Adfs") {
|
||||
} else if (provider.type === "ADFS") {
|
||||
return `${provider.domain}/adfs/oauth2/authorize?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&nonce=casdoor&scope=openid`;
|
||||
} else if (provider.type === "Baidu") {
|
||||
return `${endpoint}?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&scope=${scope}&display=popup`;
|
||||
|
@@ -71,7 +71,7 @@ function getSigninButton(provider) {
|
||||
return <LarkLoginButton text={text} align={"center"} />;
|
||||
} else if (provider.type === "GitLab") {
|
||||
return <GitLabLoginButton text={text} align={"center"} />;
|
||||
} else if (provider.type === "Adfs") {
|
||||
} else if (provider.type === "ADFS") {
|
||||
return <AdfsLoginButton text={text} align={"center"} />;
|
||||
} else if (provider.type === "Casdoor") {
|
||||
return <CasdoorLoginButton text={text} align={"center"} />;
|
||||
|
@@ -1,71 +0,0 @@
|
||||
// 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.
|
||||
|
||||
import * as Setting from "../Setting";
|
||||
|
||||
export function getChats(owner, page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "") {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-chats?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getChat(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-chat?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function updateChat(owner, name, chat) {
|
||||
const newChat = Setting.deepCopy(chat);
|
||||
return fetch(`${Setting.ServerUrl}/api/update-chat?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newChat),
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function addChat(chat) {
|
||||
const newChat = Setting.deepCopy(chat);
|
||||
return fetch(`${Setting.ServerUrl}/api/add-chat`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newChat),
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function deleteChat(chat) {
|
||||
const newChat = Setting.deepCopy(chat);
|
||||
return fetch(`${Setting.ServerUrl}/api/delete-chat`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newChat),
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
@@ -1,98 +0,0 @@
|
||||
// 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.
|
||||
|
||||
import * as Setting from "../Setting";
|
||||
|
||||
export function getMessages(owner, organization, page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "") {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-messages?owner=${owner}&organization=${organization}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getChatMessages(chat) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-messages?chat=${chat}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getMessage(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-message?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getMessageAnswer(owner, name, onMessage, onError) {
|
||||
const eventSource = new EventSource(`${Setting.ServerUrl}/api/get-message-answer?id=${owner}/${encodeURIComponent(name)}`);
|
||||
|
||||
eventSource.addEventListener("message", (e) => {
|
||||
onMessage(e.data);
|
||||
});
|
||||
|
||||
eventSource.addEventListener("myerror", (e) => {
|
||||
onError(e.data);
|
||||
eventSource.close();
|
||||
});
|
||||
|
||||
eventSource.addEventListener("end", (e) => {
|
||||
eventSource.close();
|
||||
});
|
||||
}
|
||||
|
||||
export function updateMessage(owner, name, message) {
|
||||
const newMessage = Setting.deepCopy(message);
|
||||
return fetch(`${Setting.ServerUrl}/api/update-message?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newMessage),
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function addMessage(message) {
|
||||
const newMessage = Setting.deepCopy(message);
|
||||
return fetch(`${Setting.ServerUrl}/api/add-message`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newMessage),
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function deleteMessage(message) {
|
||||
const newMessage = Setting.deepCopy(message);
|
||||
return fetch(`${Setting.ServerUrl}/api/delete-message`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(newMessage),
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
@@ -79,3 +79,13 @@ export function invoicePayment(owner, name) {
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function notifyPayment(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/notify-payment/${owner}/${name}`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
@@ -24,6 +24,9 @@ import ko from "./locales/ko/data.json";
|
||||
import ru from "./locales/ru/data.json";
|
||||
import vi from "./locales/vi/data.json";
|
||||
import pt from "./locales/pt/data.json";
|
||||
import it from "./locales/it/data.json";
|
||||
import ms from "./locales/ms/data.json";
|
||||
import tr from "./locales/tr/data.json";
|
||||
import * as Conf from "./Conf";
|
||||
import {initReactI18next} from "react-i18next";
|
||||
|
||||
@@ -39,6 +42,9 @@ const resources = {
|
||||
ru: ru,
|
||||
vi: vi,
|
||||
pt: pt,
|
||||
it: it,
|
||||
ms: ms,
|
||||
tr: tr,
|
||||
};
|
||||
|
||||
function initLanguage() {
|
||||
@@ -88,6 +94,15 @@ function initLanguage() {
|
||||
case "pt":
|
||||
language = "pt";
|
||||
break;
|
||||
case "it":
|
||||
language = "it";
|
||||
break;
|
||||
case "ms":
|
||||
language = "ms";
|
||||
break;
|
||||
case "tr":
|
||||
language = "tr";
|
||||
break;
|
||||
default:
|
||||
language = Conf.DefaultLanguage;
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"account": {
|
||||
"Chats & Messages": "Chats & Messages",
|
||||
"Logout": "Abmelden",
|
||||
"My Account": "Mein Konto",
|
||||
"Sign Up": "Anmelden"
|
||||
@@ -127,19 +126,6 @@
|
||||
"Scope - Tooltip": "Nutzungsszenarien des Zertifikats",
|
||||
"Type - Tooltip": "Art des Zertifikats"
|
||||
},
|
||||
"chat": {
|
||||
"AI": "AI",
|
||||
"Edit Chat": "Edit Chat",
|
||||
"Group": "Group",
|
||||
"Message count": "Message count",
|
||||
"New Chat": "New Chat",
|
||||
"Single": "Single",
|
||||
"User1": "User1",
|
||||
"User1 - Tooltip": "User1 - Tooltip",
|
||||
"User2": "User2",
|
||||
"User2 - Tooltip": "User2 - Tooltip",
|
||||
"Users - Tooltip": "Users - Tooltip"
|
||||
},
|
||||
"code": {
|
||||
"Code you received": "Der Code, den Sie erhalten haben",
|
||||
"Email code": "E-Mail-Code",
|
||||
@@ -152,6 +138,10 @@
|
||||
"Sending": "Sendet",
|
||||
"Submit and complete": "Einreichen und abschließen"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
},
|
||||
"forget": {
|
||||
"Account": "Konto",
|
||||
"Change Password": "Passwort ändern",
|
||||
@@ -190,7 +180,6 @@
|
||||
"Cert": "Zertifikat",
|
||||
"Cert - Tooltip": "Das Public-Key-Zertifikat, das vom Client-SDK, das mit dieser Anwendung korrespondiert, verifiziert werden muss",
|
||||
"Certs": "Zertifikate",
|
||||
"Chats": "Chats",
|
||||
"Click to Upload": "Klicken Sie zum Hochladen",
|
||||
"Client IP": "Client-IP",
|
||||
"Close": "Schließen",
|
||||
@@ -215,11 +204,11 @@
|
||||
"Enable": "Enable",
|
||||
"Enabled": "Enabled",
|
||||
"Enabled successfully": "Enabled successfully",
|
||||
"Enforcers": "Enforcers",
|
||||
"Failed to add": "Fehler beim hinzufügen",
|
||||
"Failed to connect to server": "Die Verbindung zum Server konnte nicht hergestellt werden",
|
||||
"Failed to delete": "Konnte nicht gelöscht werden",
|
||||
"Failed to enable": "Failed to enable",
|
||||
"Failed to get answer": "Failed to get answer",
|
||||
"Failed to remove": "Failed to remove",
|
||||
"Failed to save": "Konnte nicht gespeichert werden",
|
||||
"Failed to verify": "Failed to verify",
|
||||
@@ -252,7 +241,6 @@
|
||||
"Master password": "Hauptpasswort",
|
||||
"Master password - Tooltip": "Kann zum Einloggen aller Benutzer unter dieser Organisation verwendet werden, was es Administratoren bequem macht, sich als dieser Benutzer einzuloggen, um technische Probleme zu lösen",
|
||||
"Menu": "Menü",
|
||||
"Messages": "Messages",
|
||||
"Method": "Methode",
|
||||
"Model": "Modell",
|
||||
"Model - Tooltip": "Casbin-Zugriffskontrollmodell",
|
||||
@@ -420,16 +408,6 @@
|
||||
"sign up now": "Melde dich jetzt an",
|
||||
"username, Email or phone": "Benutzername, E-Mail oder Telefon"
|
||||
},
|
||||
"message": {
|
||||
"Author": "Author",
|
||||
"Author - Tooltip": "Author - Tooltip",
|
||||
"Chat": "Chat",
|
||||
"Chat - Tooltip": "Chat - Tooltip",
|
||||
"Edit Message": "Edit Message",
|
||||
"New Message": "New Message",
|
||||
"Text": "Text",
|
||||
"Text - Tooltip": "Text - Tooltip"
|
||||
},
|
||||
"mfa": {
|
||||
"Each time you sign in to your Account, you'll need your password and a authentication code": "Each time you sign in to your Account, you'll need your password and a authentication code",
|
||||
"Enable multi-factor authentication": "Enable multi-factor authentication",
|
||||
|
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"account": {
|
||||
"Chats & Messages": "Chats & Messages",
|
||||
"Logout": "Logout",
|
||||
"My Account": "My Account",
|
||||
"Sign Up": "Sign Up"
|
||||
@@ -127,19 +126,6 @@
|
||||
"Scope - Tooltip": "Usage scenarios of the certificate",
|
||||
"Type - Tooltip": "Type of certificate"
|
||||
},
|
||||
"chat": {
|
||||
"AI": "AI",
|
||||
"Edit Chat": "Edit Chat",
|
||||
"Group": "Group",
|
||||
"Message count": "Message count",
|
||||
"New Chat": "New Chat",
|
||||
"Single": "Single",
|
||||
"User1": "User1",
|
||||
"User1 - Tooltip": "User1 - Tooltip",
|
||||
"User2": "User2",
|
||||
"User2 - Tooltip": "User2 - Tooltip",
|
||||
"Users - Tooltip": "Users - Tooltip"
|
||||
},
|
||||
"code": {
|
||||
"Code you received": "Code you received",
|
||||
"Email code": "Email code",
|
||||
@@ -152,6 +138,10 @@
|
||||
"Sending": "Sending",
|
||||
"Submit and complete": "Submit and complete"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
},
|
||||
"forget": {
|
||||
"Account": "Account",
|
||||
"Change Password": "Change Password",
|
||||
@@ -190,7 +180,6 @@
|
||||
"Cert": "Cert",
|
||||
"Cert - Tooltip": "The public key certificate that needs to be verified by the client SDK corresponding to this application",
|
||||
"Certs": "Certs",
|
||||
"Chats": "Chats",
|
||||
"Click to Upload": "Click to Upload",
|
||||
"Client IP": "Client IP",
|
||||
"Close": "Close",
|
||||
@@ -215,11 +204,11 @@
|
||||
"Enable": "Enable",
|
||||
"Enabled": "Enabled",
|
||||
"Enabled successfully": "Enabled successfully",
|
||||
"Enforcers": "Enforcers",
|
||||
"Failed to add": "Failed to add",
|
||||
"Failed to connect to server": "Failed to connect to server",
|
||||
"Failed to delete": "Failed to delete",
|
||||
"Failed to enable": "Failed to enable",
|
||||
"Failed to get answer": "Failed to get answer",
|
||||
"Failed to remove": "Failed to remove",
|
||||
"Failed to save": "Failed to save",
|
||||
"Failed to verify": "Failed to verify",
|
||||
@@ -252,7 +241,6 @@
|
||||
"Master password": "Master password",
|
||||
"Master password - Tooltip": "Can be used to log in to all users under this organization, making it convenient for administrators to log in as this user to solve technical issues",
|
||||
"Menu": "Menu",
|
||||
"Messages": "Messages",
|
||||
"Method": "Method",
|
||||
"Model": "Model",
|
||||
"Model - Tooltip": "Casbin access control model",
|
||||
@@ -420,16 +408,6 @@
|
||||
"sign up now": "sign up now",
|
||||
"username, Email or phone": "username, Email or phone"
|
||||
},
|
||||
"message": {
|
||||
"Author": "Author",
|
||||
"Author - Tooltip": "Author - Tooltip",
|
||||
"Chat": "Chat",
|
||||
"Chat - Tooltip": "Chat - Tooltip",
|
||||
"Edit Message": "Edit Message",
|
||||
"New Message": "New Message",
|
||||
"Text": "Text",
|
||||
"Text - Tooltip": "Text - Tooltip"
|
||||
},
|
||||
"mfa": {
|
||||
"Each time you sign in to your Account, you'll need your password and a authentication code": "Each time you sign in to your Account, you'll need your password and a authentication code",
|
||||
"Enable multi-factor authentication": "Enable multi-factor authentication",
|
||||
|
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"account": {
|
||||
"Chats & Messages": "Chats & Messages",
|
||||
"Logout": "Cierre de sesión",
|
||||
"My Account": "Mi cuenta",
|
||||
"Sign Up": "Registrarse"
|
||||
@@ -127,19 +126,6 @@
|
||||
"Scope - Tooltip": "Escenarios de uso del certificado",
|
||||
"Type - Tooltip": "Tipo de certificado"
|
||||
},
|
||||
"chat": {
|
||||
"AI": "AI",
|
||||
"Edit Chat": "Edit Chat",
|
||||
"Group": "Group",
|
||||
"Message count": "Message count",
|
||||
"New Chat": "New Chat",
|
||||
"Single": "Single",
|
||||
"User1": "User1",
|
||||
"User1 - Tooltip": "User1 - Tooltip",
|
||||
"User2": "User2",
|
||||
"User2 - Tooltip": "User2 - Tooltip",
|
||||
"Users - Tooltip": "Users - Tooltip"
|
||||
},
|
||||
"code": {
|
||||
"Code you received": "Código que recibió",
|
||||
"Email code": "Código de correo electrónico",
|
||||
@@ -152,6 +138,10 @@
|
||||
"Sending": "Envío",
|
||||
"Submit and complete": "Enviar y completar"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
},
|
||||
"forget": {
|
||||
"Account": "Cuenta",
|
||||
"Change Password": "Cambiar contraseña",
|
||||
@@ -190,7 +180,6 @@
|
||||
"Cert": "ificado",
|
||||
"Cert - Tooltip": "El certificado de clave pública que necesita ser verificado por el SDK del cliente correspondiente a esta aplicación",
|
||||
"Certs": "Certificaciones",
|
||||
"Chats": "Chats",
|
||||
"Click to Upload": "Haz clic para cargar",
|
||||
"Client IP": "Dirección IP del cliente",
|
||||
"Close": "Cerca",
|
||||
@@ -215,11 +204,11 @@
|
||||
"Enable": "Enable",
|
||||
"Enabled": "Enabled",
|
||||
"Enabled successfully": "Enabled successfully",
|
||||
"Enforcers": "Enforcers",
|
||||
"Failed to add": "No se pudo agregar",
|
||||
"Failed to connect to server": "No se pudo conectar al servidor",
|
||||
"Failed to delete": "No se pudo eliminar",
|
||||
"Failed to enable": "Failed to enable",
|
||||
"Failed to get answer": "Failed to get answer",
|
||||
"Failed to remove": "Failed to remove",
|
||||
"Failed to save": "No se pudo guardar",
|
||||
"Failed to verify": "Failed to verify",
|
||||
@@ -252,7 +241,6 @@
|
||||
"Master password": "Contraseña maestra",
|
||||
"Master password - Tooltip": "Se puede usar para iniciar sesión en todos los usuarios de esta organización, lo que hace conveniente que los administradores inicien sesión como este usuario para resolver problemas técnicos",
|
||||
"Menu": "Menú",
|
||||
"Messages": "Messages",
|
||||
"Method": "Método",
|
||||
"Model": "Modelo",
|
||||
"Model - Tooltip": "Modelo de control de acceso Casbin",
|
||||
@@ -420,16 +408,6 @@
|
||||
"sign up now": "Regístrate ahora",
|
||||
"username, Email or phone": "Nombre de usuario, correo electrónico o teléfono"
|
||||
},
|
||||
"message": {
|
||||
"Author": "Author",
|
||||
"Author - Tooltip": "Author - Tooltip",
|
||||
"Chat": "Chat",
|
||||
"Chat - Tooltip": "Chat - Tooltip",
|
||||
"Edit Message": "Edit Message",
|
||||
"New Message": "New Message",
|
||||
"Text": "Text",
|
||||
"Text - Tooltip": "Text - Tooltip"
|
||||
},
|
||||
"mfa": {
|
||||
"Each time you sign in to your Account, you'll need your password and a authentication code": "Each time you sign in to your Account, you'll need your password and a authentication code",
|
||||
"Enable multi-factor authentication": "Enable multi-factor authentication",
|
||||
|
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"account": {
|
||||
"Chats & Messages": "Chats & Messages",
|
||||
"Logout": "Déconnexion",
|
||||
"My Account": "Mon Compte",
|
||||
"Sign Up": "S'inscrire"
|
||||
@@ -127,19 +126,6 @@
|
||||
"Scope - Tooltip": "Scénarios d'utilisation du certificat",
|
||||
"Type - Tooltip": "Type de certificat"
|
||||
},
|
||||
"chat": {
|
||||
"AI": "AI",
|
||||
"Edit Chat": "Edit Chat",
|
||||
"Group": "Group",
|
||||
"Message count": "Message count",
|
||||
"New Chat": "New Chat",
|
||||
"Single": "Single",
|
||||
"User1": "User1",
|
||||
"User1 - Tooltip": "User1 - Tooltip",
|
||||
"User2": "User2",
|
||||
"User2 - Tooltip": "User2 - Tooltip",
|
||||
"Users - Tooltip": "Users - Tooltip"
|
||||
},
|
||||
"code": {
|
||||
"Code you received": "Le code que vous avez reçu",
|
||||
"Email code": "Code email",
|
||||
@@ -152,6 +138,10 @@
|
||||
"Sending": "Envoi",
|
||||
"Submit and complete": "Soumettre et compléter"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
},
|
||||
"forget": {
|
||||
"Account": "Compte",
|
||||
"Change Password": "Changer le mot de passe",
|
||||
@@ -190,7 +180,6 @@
|
||||
"Cert": "ainement",
|
||||
"Cert - Tooltip": "Le certificat de clé publique qui doit être vérifié par le kit de développement client correspondant à cette application",
|
||||
"Certs": "Certains",
|
||||
"Chats": "Chats",
|
||||
"Click to Upload": "Cliquez pour télécharger",
|
||||
"Client IP": "Adresse IP du client",
|
||||
"Close": "Fermer",
|
||||
@@ -215,11 +204,11 @@
|
||||
"Enable": "Enable",
|
||||
"Enabled": "Enabled",
|
||||
"Enabled successfully": "Enabled successfully",
|
||||
"Enforcers": "Enforcers",
|
||||
"Failed to add": "Échec d'ajout",
|
||||
"Failed to connect to server": "Échec de la connexion au serveur",
|
||||
"Failed to delete": "Échec de la suppression",
|
||||
"Failed to enable": "Failed to enable",
|
||||
"Failed to get answer": "Failed to get answer",
|
||||
"Failed to remove": "Failed to remove",
|
||||
"Failed to save": "Échec de sauvegarde",
|
||||
"Failed to verify": "Failed to verify",
|
||||
@@ -252,7 +241,6 @@
|
||||
"Master password": "Mot de passe principal",
|
||||
"Master password - Tooltip": "Peut être utilisé pour se connecter à tous les utilisateurs sous cette organisation, ce qui facilite la connexion des administrateurs en tant que cet utilisateur pour résoudre les problèmes techniques",
|
||||
"Menu": "Menu",
|
||||
"Messages": "Messages",
|
||||
"Method": "Méthode",
|
||||
"Model": "Modèle",
|
||||
"Model - Tooltip": "Modèle de contrôle d'accès Casbin",
|
||||
@@ -420,16 +408,6 @@
|
||||
"sign up now": "Inscrivez-vous maintenant",
|
||||
"username, Email or phone": "Nom d'utilisateur, e-mail ou téléphone"
|
||||
},
|
||||
"message": {
|
||||
"Author": "Author",
|
||||
"Author - Tooltip": "Author - Tooltip",
|
||||
"Chat": "Chat",
|
||||
"Chat - Tooltip": "Chat - Tooltip",
|
||||
"Edit Message": "Edit Message",
|
||||
"New Message": "New Message",
|
||||
"Text": "Text",
|
||||
"Text - Tooltip": "Text - Tooltip"
|
||||
},
|
||||
"mfa": {
|
||||
"Each time you sign in to your Account, you'll need your password and a authentication code": "Each time you sign in to your Account, you'll need your password and a authentication code",
|
||||
"Enable multi-factor authentication": "Enable multi-factor authentication",
|
||||
|
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"account": {
|
||||
"Chats & Messages": "Chats & Messages",
|
||||
"Logout": "Keluar",
|
||||
"My Account": "Akun Saya",
|
||||
"Sign Up": "Mendaftar"
|
||||
@@ -127,19 +126,6 @@
|
||||
"Scope - Tooltip": "Skema penggunaan sertifikat:",
|
||||
"Type - Tooltip": "Jenis sertifikat"
|
||||
},
|
||||
"chat": {
|
||||
"AI": "AI",
|
||||
"Edit Chat": "Edit Chat",
|
||||
"Group": "Group",
|
||||
"Message count": "Message count",
|
||||
"New Chat": "New Chat",
|
||||
"Single": "Single",
|
||||
"User1": "User1",
|
||||
"User1 - Tooltip": "User1 - Tooltip",
|
||||
"User2": "User2",
|
||||
"User2 - Tooltip": "User2 - Tooltip",
|
||||
"Users - Tooltip": "Users - Tooltip"
|
||||
},
|
||||
"code": {
|
||||
"Code you received": "Kode yang kamu terima",
|
||||
"Email code": "Kode email",
|
||||
@@ -152,6 +138,10 @@
|
||||
"Sending": "Mengirimkan",
|
||||
"Submit and complete": "Kirim dan selesaikan"
|
||||
},
|
||||
"enforcer": {
|
||||
"Edit Enforcer": "Edit Enforcer",
|
||||
"New Enforcer": "New Enforcer"
|
||||
},
|
||||
"forget": {
|
||||
"Account": "Akun",
|
||||
"Change Password": "Ubah Kata Sandi",
|
||||
@@ -190,7 +180,6 @@
|
||||
"Cert": "Sertifikat",
|
||||
"Cert - Tooltip": "Sertifikat kunci publik yang perlu diverifikasi oleh SDK klien yang sesuai dengan aplikasi ini",
|
||||
"Certs": "Sertifikat",
|
||||
"Chats": "Chats",
|
||||
"Click to Upload": "Klik untuk Mengunggah",
|
||||
"Client IP": "IP klien",
|
||||
"Close": "Tutup",
|
||||
@@ -215,11 +204,11 @@
|
||||
"Enable": "Enable",
|
||||
"Enabled": "Enabled",
|
||||
"Enabled successfully": "Enabled successfully",
|
||||
"Enforcers": "Enforcers",
|
||||
"Failed to add": "Gagal menambahkan",
|
||||
"Failed to connect to server": "Gagal terhubung ke server",
|
||||
"Failed to delete": "Gagal menghapus",
|
||||
"Failed to enable": "Failed to enable",
|
||||
"Failed to get answer": "Failed to get answer",
|
||||
"Failed to remove": "Failed to remove",
|
||||
"Failed to save": "Gagal menyimpan",
|
||||
"Failed to verify": "Failed to verify",
|
||||
@@ -252,7 +241,6 @@
|
||||
"Master password": "Kata sandi utama",
|
||||
"Master password - Tooltip": "Dapat digunakan untuk masuk ke semua pengguna di bawah organisasi ini, sehingga memudahkan administrator untuk masuk sebagai pengguna ini untuk menyelesaikan masalah teknis",
|
||||
"Menu": "Daftar makanan",
|
||||
"Messages": "Messages",
|
||||
"Method": "Metode",
|
||||
"Model": "Model",
|
||||
"Model - Tooltip": "Model kontrol akses Casbin",
|
||||
@@ -420,16 +408,6 @@
|
||||
"sign up now": "Daftar sekarang",
|
||||
"username, Email or phone": "nama pengguna, Email atau nomor telepon"
|
||||
},
|
||||
"message": {
|
||||
"Author": "Author",
|
||||
"Author - Tooltip": "Author - Tooltip",
|
||||
"Chat": "Chat",
|
||||
"Chat - Tooltip": "Chat - Tooltip",
|
||||
"Edit Message": "Edit Message",
|
||||
"New Message": "New Message",
|
||||
"Text": "Text",
|
||||
"Text - Tooltip": "Text - Tooltip"
|
||||
},
|
||||
"mfa": {
|
||||
"Each time you sign in to your Account, you'll need your password and a authentication code": "Each time you sign in to your Account, you'll need your password and a authentication code",
|
||||
"Enable multi-factor authentication": "Enable multi-factor authentication",
|
||||
|
1009
web/src/locales/it/data.json
Normal file
1009
web/src/locales/it/data.json
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user