From 084c36d4af9c4d933c84af410afd55545ffea328 Mon Sep 17 00:00:00 2001 From: Kelvin Chiu Date: Sun, 16 Jul 2023 19:01:15 +0800 Subject: [PATCH] feat: add message list (#18) --- controllers/message.go | 108 ++++++++++++ object/adapter.go | 5 + object/message.go | 113 ++++++++++++ routers/router.go | 7 + web/src/App.js | 13 ++ web/src/ChatEditPage.js | 2 + web/src/MessageEditPage.js | 277 ++++++++++++++++++++++++++++++ web/src/MessageListPage.js | 246 ++++++++++++++++++++++++++ web/src/backend/MessageBackend.js | 63 +++++++ 9 files changed, 834 insertions(+) create mode 100644 controllers/message.go create mode 100644 object/message.go create mode 100644 web/src/MessageEditPage.js create mode 100644 web/src/MessageListPage.js create mode 100644 web/src/backend/MessageBackend.js diff --git a/controllers/message.go b/controllers/message.go new file mode 100644 index 0000000..33e220d --- /dev/null +++ b/controllers/message.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) GetGlobalMessages() { + messages, err := object.GetGlobalMessages() + if err != nil { + c.ResponseError(err.Error()) + return + } + + c.ResponseOk(messages) +} + +func (c *ApiController) GetMessages() { + owner := c.Input().Get("owner") + + messages, err := object.GetMessages(owner) + if err != nil { + c.ResponseError(err.Error()) + return + } + + c.ResponseOk(messages) +} + +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) 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 + } + + success, err := object.UpdateMessage(id, &message) + if err != nil { + c.ResponseError(err.Error()) + return + } + + c.ResponseOk(success) +} + +func (c *ApiController) AddMessage() { + var message object.Message + err := json.Unmarshal(c.Ctx.Input.RequestBody, &message) + if err != nil { + c.ResponseError(err.Error()) + return + } + + success, err := object.AddMessage(&message) + if err != nil { + c.ResponseError(err.Error()) + return + } + + c.ResponseOk(success) +} + +func (c *ApiController) DeleteMessage() { + var message object.Message + err := json.Unmarshal(c.Ctx.Input.RequestBody, &message) + if err != nil { + c.ResponseError(err.Error()) + return + } + + success, err := object.DeleteMessage(&message) + if err != nil { + c.ResponseError(err.Error()) + return + } + + c.ResponseOk(success) +} diff --git a/object/adapter.go b/object/adapter.go index 95066fa..132e7ab 100644 --- a/object/adapter.go +++ b/object/adapter.go @@ -132,4 +132,9 @@ func (a *Adapter) createTable() { if err != nil { panic(err) } + + err = a.engine.Sync2(new(Message)) + if err != nil { + panic(err) + } } diff --git a/object/message.go b/object/message.go new file mode 100644 index 0000000..333c05b --- /dev/null +++ b/object/message.go @@ -0,0 +1,113 @@ +// 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 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 GetGlobalMessages() ([]*Message, error) { + messages := []*Message{} + err := adapter.engine.Asc("owner").Desc("created_time").Find(&messages) + if err != nil { + return messages, err + } + + return messages, nil +} + +func GetMessages(owner string) ([]*Message, error) { + messages := []*Message{} + err := adapter.engine.Desc("created_time").Find(&messages, &Message{Owner: owner}) + if err != nil { + return messages, err + } + + return messages, nil +} + +func getMessage(owner, name string) (*Message, error) { + message := Message{Owner: owner, Name: name} + existed, err := adapter.engine.Get(&message) + if err != nil { + return &message, 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) + _, err := getMessage(owner, name) + if err != nil { + return false, err + } + if message == nil { + return false, nil + } + + _, err = adapter.engine.ID(core.PK{owner, name}).AllCols().Update(message) + if err != nil { + return false, err + } + + return true, nil +} + +func AddMessage(message *Message) (bool, error) { + affected, err := adapter.engine.Insert(message) + if err != nil { + return false, err + } + + return affected != 0, nil +} + +func DeleteMessage(message *Message) (bool, error) { + affected, err := adapter.engine.ID(core.PK{message.Owner, message.Name}).Delete(&Message{}) + if err != nil { + return false, err + } + + return affected != 0, nil +} + +func (message *Message) GetId() string { + return fmt.Sprintf("%s/%s", message.Owner, message.Name) +} diff --git a/routers/router.go b/routers/router.go index f8a3f57..c6452fa 100644 --- a/routers/router.go +++ b/routers/router.go @@ -81,6 +81,13 @@ func initAPI() { beego.Router("/api/add-chat", &controllers.ApiController{}, "POST:AddChat") beego.Router("/api/delete-chat", &controllers.ApiController{}, "POST:DeleteChat") + beego.Router("/api/get-global-messages", &controllers.ApiController{}, "GET:GetGlobalMessages") + beego.Router("/api/get-messages", &controllers.ApiController{}, "GET:GetMessages") + beego.Router("/api/get-message", &controllers.ApiController{}, "GET:GetMessage") + 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/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 834576a..c3af26a 100644 --- a/web/src/App.js +++ b/web/src/App.js @@ -40,6 +40,8 @@ import i18next from "i18next"; import LanguageSelect from "./LanguageSelect"; import ChatEditPage from "./ChatEditPage"; import ChatListPage from "./ChatListPage"; +import MessageListPage from "./MessageListPage"; +import MessageEditPage from "./MessageEditPage"; const {Header, Footer} = Layout; @@ -92,6 +94,8 @@ class App extends Component { this.setState({selectedMenuKey: "/providers"}); } else if (uri.includes("/chats")) { this.setState({selectedMenuKey: "/chats"}); + } else if (uri.includes("/messages")) { + this.setState({selectedMenuKey: "/messages"}); } else { this.setState({selectedMenuKey: "null"}); } @@ -331,6 +335,13 @@ class App extends Component { ); + res.push( + + + {i18next.t("general:Messages")} + + + ); res.push( @@ -418,6 +429,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 index f807b70..b0cf313 100644 --- a/web/src/ChatEditPage.js +++ b/web/src/ChatEditPage.js @@ -27,11 +27,13 @@ class ChatEditPage extends React.Component { classes: props, chatName: props.match.params.chatName, chat: null, + // users: [], }; } UNSAFE_componentWillMount() { this.getChat(); + // this.getUser(); } getChat() { diff --git a/web/src/MessageEditPage.js b/web/src/MessageEditPage.js new file mode 100644 index 0000000..4409b67 --- /dev/null +++ b/web/src/MessageEditPage.js @@ -0,0 +1,277 @@ +// 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 Setting from "./Setting"; +import i18next from "i18next"; +import * as MessageBackend from "./backend/MessageBackend"; +import TextArea from "antd/es/input/TextArea"; +import * as ChatBackend from "./backend/ChatBackend"; + +class MessageEditPage extends React.Component { + constructor(props) { + super(props); + this.state = { + classes: props, + messageName: props.match.params.messageName, + messages: [], + message: null, + chats: [], + // users: [], + chat: null, + }; + } + + UNSAFE_componentWillMount() { + this.getMessage(); + this.getMessages(); + this.getChats(); + } + + getChats() { + ChatBackend.getChats(this.props.account.name) + .then((chats) => { + if (chats.status === "ok") { + this.setState({ + chats: chats.data, + }); + } else { + Setting.showMessage("error", `Failed to get chat: ${chats.msg}`); + } + }); + } + + getChat(chatName) { + ChatBackend.getChat(this.props.account.name, chatName) + .then((chat) => { + if (chat.status === "ok") { + this.setState({ + chat: chat.data, + }); + } else { + Setting.showMessage("error", `Failed to get chat: ${chat.msg}`); + } + }); + } + + getMessage() { + MessageBackend.getMessage(this.props.account.name, this.state.messageName) + .then((message) => { + if (message.status === "ok") { + this.setState({ + message: message.data, + }); + } else { + Setting.showMessage("error", `Failed to get message: ${message.msg}`); + } + }); + } + + getMessages() { + MessageBackend.getMessages(this.props.account.name) + .then((messages) => { + if (messages.status === "ok") { + this.setState({ + messages: messages.data, + }); + } else { + Setting.showMessage("error", `Failed to get messages: ${messages.msg}`); + } + }); + } + + 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 ( + + {i18next.t("message:Edit Chat")}     + + + } style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner"> + {/* */} + {/* */} + {/* {Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :*/} + {/* */} + {/* */} + {/* { + this.updateMessageField("name", e.target.value); + }} + /> + + + + + {i18next.t("message:Chat")}: + + + { + this.updateMessageField("author", value); + }} + options={ + this.state.chat !== null + ? this.state.chat.users.map((user) => + Setting.getOption(`${user}`, `${user}`) + ) + : [] + } + /> + + + + + {i18next.t("message:replyTo")}: + + +