diff --git a/controllers/chat.go b/controllers/chat.go new file mode 100644 index 0000000..ac8ac65 --- /dev/null +++ b/controllers/chat.go @@ -0,0 +1,108 @@ +// Copyright 2023 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controllers + +import ( + "encoding/json" + + "github.com/casbin/casibase/object" +) + +func (c *ApiController) GetGlobalChats() { + chats, err := object.GetGlobalChats() + if err != nil { + c.ResponseError(err.Error()) + return + } + + c.ResponseOk(chats) +} + +func (c *ApiController) GetChats() { + owner := c.Input().Get("owner") + + chats, err := object.GetChats(owner) + if err != nil { + c.ResponseError(err.Error()) + return + } + + c.ResponseOk(chats) +} + +func (c *ApiController) GetChat() { + id := c.Input().Get("id") + + chat, err := object.GetChat(id) + if err != nil { + c.ResponseError(err.Error()) + return + } + + c.ResponseOk(chat) +} + +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 + } + + success, err := object.UpdateChat(id, &chat) + if err != nil { + c.ResponseError(err.Error()) + return + } + + c.ResponseOk(success) +} + +func (c *ApiController) AddChat() { + var chat object.Chat + err := json.Unmarshal(c.Ctx.Input.RequestBody, &chat) + if err != nil { + c.ResponseError(err.Error()) + return + } + + success, err := object.AddChat(&chat) + if err != nil { + c.ResponseError(err.Error()) + return + } + + c.ResponseOk(success) +} + +func (c *ApiController) DeleteChat() { + var chat object.Chat + err := json.Unmarshal(c.Ctx.Input.RequestBody, &chat) + if err != nil { + c.ResponseError(err.Error()) + return + } + + success, err := object.DeleteChat(&chat) + if err != nil { + c.ResponseError(err.Error()) + return + } + + c.ResponseOk(success) +} diff --git a/object/adapter.go b/object/adapter.go index 0599300..c67ff86 100644 --- a/object/adapter.go +++ b/object/adapter.go @@ -127,4 +127,9 @@ func (a *Adapter) createTable() { if err != nil { panic(err) } + + err = a.engine.Sync2(new(Chat)) + if err != nil { + panic(err) + } } diff --git a/object/chat.go b/object/chat.go new file mode 100644 index 0000000..d266b65 --- /dev/null +++ b/object/chat.go @@ -0,0 +1,118 @@ +// Copyright 2023 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package object + +import ( + "fmt" + + "github.com/casbin/casibase/util" + "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"` + Category string `xorm:"varchar(100)" json:"category"` + Type string `xorm:"varchar(100)" json:"type"` + 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 GetGlobalChats() ([]*Chat, error) { + chats := []*Chat{} + err := adapter.engine.Asc("owner").Desc("created_time").Find(&chats) + if err != nil { + return chats, err + } + + return chats, nil +} + +func GetChats(owner string) ([]*Chat, error) { + chats := []*Chat{} + err := adapter.engine.Desc("created_time").Find(&chats, &Chat{Owner: owner}) + if err != nil { + return chats, err + } + + return chats, nil +} + +func getChat(owner, name string) (*Chat, error) { + chat := Chat{Owner: owner, Name: name} + existed, err := adapter.engine.Get(&chat) + if err != nil { + return nil, 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) + _, err := getChat(owner, name) + if err != nil { + return false, err + } + if chat == nil { + return false, nil + } + + _, err = adapter.engine.ID(core.PK{owner, name}).AllCols().Update(chat) + if err != nil { + return false, err + } + + //return affected != 0 + return true, nil +} + +func AddChat(chat *Chat) (bool, error) { + affected, err := adapter.engine.Insert(chat) + if err != nil { + return false, err + } + + return affected != 0, nil +} + +func DeleteChat(chat *Chat) (bool, error) { + affected, err := adapter.engine.ID(core.PK{chat.Owner, chat.Name}).Delete(&Chat{}) + if err != nil { + return false, err + } + + return affected != 0, nil +} + +func (chat *Chat) GetId() string { + return fmt.Sprintf("%s/%s", chat.Owner, chat.Name) +} diff --git a/routers/router.go b/routers/router.go index c5ae4cc..346d378 100644 --- a/routers/router.go +++ b/routers/router.go @@ -74,6 +74,13 @@ func initAPI() { beego.Router("/api/add-provider", &controllers.ApiController{}, "POST:AddProvider") beego.Router("/api/delete-provider", &controllers.ApiController{}, "POST:DeleteProvider") + beego.Router("/api/get-global-chats", &controllers.ApiController{}, "GET:GetGlobalChats") + 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/update-file", &controllers.ApiController{}, "POST:UpdateFile") beego.Router("/api/add-file", &controllers.ApiController{}, "POST:AddFile") beego.Router("/api/delete-file", &controllers.ApiController{}, "POST:DeleteFile") diff --git a/web/src/App.js b/web/src/App.js index 742621a..4687589 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -38,6 +38,8 @@ import ProviderEditPage from "./ProviderEditPage"; import SigninPage from "./SigninPage"; import i18next from "i18next"; import LanguageSelect from "./LanguageSelect"; +import ChatEditPage from "./ChatEditPage"; +import ChatListPage from "./ChatListPage"; const {Header, Footer} = Layout; @@ -88,6 +90,8 @@ class App extends Component { this.setState({selectedMenuKey: "/videos"}); } else if (uri.includes("/providers")) { this.setState({selectedMenuKey: "/providers"}); + } else if (uri.includes("/chats")) { + this.setState({selectedMenuKey: "/chats"}); } else { this.setState({selectedMenuKey: "null"}); } @@ -314,6 +318,13 @@ class App extends Component { ); + res.push( + + + {i18next.t("general:Chats")} + + + ); if (Setting.isLocalAdminUser(this.state.account)) { res.push( @@ -390,6 +401,8 @@ class App extends Component { this.renderSigninIfNotSignedIn()} /> this.renderSigninIfNotSignedIn()} /> this.renderSigninIfNotSignedIn()} /> + this.renderSigninIfNotSignedIn()} /> + this.renderSigninIfNotSignedIn()} /> ); diff --git a/web/src/ChatEditPage.js b/web/src/ChatEditPage.js new file mode 100644 index 0000000..f807b70 --- /dev/null +++ b/web/src/ChatEditPage.js @@ -0,0 +1,218 @@ +// Copyright 2023 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import React from "react"; +import {Button, Card, Col, Input, Row, Select} from "antd"; +import * as ChatBackend from "./backend/ChatBackend"; +import * as Setting from "./Setting"; +import i18next from "i18next"; + +const {Option} = Select; + +class ChatEditPage extends React.Component { + constructor(props) { + super(props); + this.state = { + classes: props, + chatName: props.match.params.chatName, + chat: null, + }; + } + + UNSAFE_componentWillMount() { + this.getChat(); + } + + getChat() { + ChatBackend.getChat(this.props.account.name, this.state.chatName) + .then((chat) => { + if (chat.status === "ok") { + this.setState({ + chat: chat.data, + }); + } else { + Setting.showMessage("error", `Failed to get chat: ${chat.msg}`); + } + }); + } + + 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 ( + + {i18next.t("chat:Edit Chat")}     + + + } style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner"> + {/* */} + {/* */} + {/* {Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :*/} + {/* */} + {/* */} + {/* { + this.updateChatField("name", e.target.value); + }} /> + + + + + {i18next.t("general:Display name")} : + + + { + this.updateChatField("displayName", e.target.value); + }} /> + + + + + {i18next.t("chat:Type")} : + + + + + + + + {i18next.t("chat:Category")} : + + + { + this.updateChatField("category", e.target.value); + }} /> + + + + + {i18next.t("chat:User1")} : + + + {this.updateChatField("user2", value);})} + options={[{label: "None", value: ""}, ...this.state.chat.users.map((user) => Setting.getOption(`${user}`, `${user}`))] + } /> + + + + + {i18next.t("chat:Users")} : + + +