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"))} :*/}
+ {/* */}
+ {/* */}
+ {/*
*/}
+
+
+ {i18next.t("general:Name")} :
+
+
+ {
+ 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")} :
+
+
+
+
+
+ {i18next.t("chat:User2")} :
+
+
+
+
+
+ {i18next.t("chat:Users")} :
+
+
+
+
+ );
+ }
+
+ submitChatEdit() {
+ const chat = Setting.deepCopy(this.state.chat);
+ ChatBackend.updateChat(this.state.chat.owner, this.state.chatName, chat)
+ .then((res) => {
+ if (res.status === "ok") {
+ if (res.data) {
+ Setting.showMessage("success", "Successfully saved");
+ this.setState({
+ chatName: this.state.chat.name,
+ });
+ this.props.history.push(`/chats/${this.state.chat.name}`);
+ } else {
+ Setting.showMessage("error", "failed to save: server side failure");
+ this.updateChatField("name", this.state.chatName);
+ }
+ } else {
+ Setting.showMessage("error", `failed to save: ${res.msg}`);
+ }
+ })
+ .catch(error => {
+ Setting.showMessage("error", `failed to save: ${error}`);
+ });
+ }
+
+ render() {
+ return (
+
+
+
+
+
+ {
+ this.state.chat !== null ? this.renderChat() : null
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default ChatEditPage;
diff --git a/web/src/ChatListPage.js b/web/src/ChatListPage.js
new file mode 100644
index 0000000..08e2e12
--- /dev/null
+++ b/web/src/ChatListPage.js
@@ -0,0 +1,275 @@
+// 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 {Link} from "react-router-dom";
+import {Button, Col, Popconfirm, Row, Table} from "antd";
+import * as Setting from "./Setting";
+import * as ChatBackend from "./backend/ChatBackend";
+import moment from "moment";
+import i18next from "i18next";
+
+class ChatListPage extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ classes: props,
+ chats: null,
+ };
+ }
+
+ UNSAFE_componentWillMount() {
+ this.getChats();
+ }
+
+ getChats() {
+ ChatBackend.getChats(this.props.account.name)
+ .then((res) => {
+ if (res.status === "ok") {
+ this.setState({
+ chats: res.data,
+ });
+ } else {
+ Setting.showMessage("error", `Failed to get chats: ${res.msg}`);
+ }
+ });
+ }
+
+ newChat() {
+ const randomName = Setting.getRandomName();
+ return {
+ owner: this.props.account.name,
+ name: `chat_${randomName}`,
+ createdTime: moment().format(),
+ updatedTime: moment().format(),
+ displayName: `New Chat - ${randomName}`,
+ category: "Chat Category - 1",
+ type: "Single",
+ 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") {
+ Setting.showMessage("success", "Chat added successfully");
+ this.setState({
+ chats: Setting.prependRow(this.state.chats, newChat),
+ });
+ } else {
+ Setting.showMessage("error", `Failed to add Chat: ${res.msg}`);
+ }
+ })
+ .catch(error => {
+ Setting.showMessage("error", `Chat failed to add: ${error}`);
+ });
+ }
+
+ deleteChat(i) {
+ ChatBackend.deleteChat(this.state.chats[i])
+ .then((res) => {
+ if (res.status === "ok") {
+ Setting.showMessage("success", "Chat deleted successfully");
+ this.setState({
+ chats: Setting.deleteRow(this.state.chats, i),
+ });
+ } else {
+ Setting.showMessage("error", `Failed to delete Chat: ${res.msg}`);
+ }
+ })
+ .catch(error => {
+ Setting.showMessage("error", `Chat failed to delete: ${error}`);
+ });
+ }
+
+ renderTable(chats) {
+ const columns = [
+ {
+ title: i18next.t("general:Name"),
+ dataIndex: "name",
+ key: "name",
+ width: "140px",
+ sorter: (a, b) => a.name.localeCompare(b.name),
+ render: (text, record, index) => {
+ return (
+
+ {text}
+
+ );
+ },
+ },
+ {
+ 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("chat: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("chat: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 (
+
+ {text}
+
+ );
+ },
+ },
+ {
+ title: i18next.t("chat:User2"),
+ dataIndex: "user2",
+ key: "user2",
+ width: "120px",
+ sorter: true,
+ // ...this.getColumnSearchProps("user2"),
+ render: (text, record, index) => {
+ return (
+
+ {text}
+
+ );
+ },
+ },
+ {
+ 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: "action",
+ key: "action",
+ width: "180px",
+ render: (text, record, index) => {
+ return (
+
+
+
this.deleteChat(index)}
+ okText="OK"
+ cancelText="Cancel"
+ >
+
+
+
+ );
+ },
+ },
+ ];
+
+ return (
+
+
(
+
+ {i18next.t("chat:Chats")}
+
+
+ )}
+ loading={chats === null}
+ />
+
+ );
+ }
+
+ render() {
+ return (
+
+
+
+
+
+ {
+ this.renderTable(this.state.chats)
+ }
+
+
+
+
+
+ );
+ }
+}
+
+export default ChatListPage;
diff --git a/web/src/Setting.js b/web/src/Setting.js
index dc27c4b..547ff12 100644
--- a/web/src/Setting.js
+++ b/web/src/Setting.js
@@ -289,11 +289,19 @@ export function getTag(text, type, state) {
}
}
-export function getTags(vectors) {
+export function getTags(vectors, type) {
if (!vectors) {
return [];
}
+ if (type === "vectors") {
+ return getVectorTag(vectors);
+ } else if (type === "users") {
+ return getUserTag(vectors);
+ }
+}
+
+function getVectorTag(vectors) {
const res = [];
vectors.forEach((vector, i) => {
if (vector.data.length !== 0) {
@@ -315,6 +323,28 @@ export function getTags(vectors) {
return res;
}
+function getUserTag(users) {
+ const res = [];
+ users.forEach((user, i) => {
+ if (user.length !== 0) {
+ res.push(
+
+
+ {user}
+
+
+ );
+ } else {
+ res.push(
+
+ {user}
+
+ );
+ }
+ });
+ return res;
+}
+
export function getLabelTags(labels) {
if (!labels) {
return [];
@@ -593,3 +623,10 @@ export function getItem(label, key, icon, children, type) {
type,
};
}
+
+export function getOption(label, value) {
+ return {
+ label,
+ value,
+ };
+}
diff --git a/web/src/VectorsetListPage.js b/web/src/VectorsetListPage.js
index 59ea9e6..6f9f210 100644
--- a/web/src/VectorsetListPage.js
+++ b/web/src/VectorsetListPage.js
@@ -164,7 +164,7 @@ class VectorsetListPage extends React.Component {
// width: '120px',
sorter: (a, b) => a.vectors.localeCompare(b.vectors),
render: (text, record, index) => {
- return Setting.getTags(text);
+ return Setting.getTags(text, "vectors");
},
},
{
diff --git a/web/src/WordsetListPage.js b/web/src/WordsetListPage.js
index 9f4856e..4c2e17e 100644
--- a/web/src/WordsetListPage.js
+++ b/web/src/WordsetListPage.js
@@ -124,7 +124,7 @@ class WordsetListPage extends React.Component {
// width: '120px',
sorter: (a, b) => a.vectors.localeCompare(b.vectors),
render: (text, record, index) => {
- return Setting.getTags(text);
+ return Setting.getTags(text, "vectors");
},
},
// {
diff --git a/web/src/backend/ChatBackend.js b/web/src/backend/ChatBackend.js
new file mode 100644
index 0000000..a0e3327
--- /dev/null
+++ b/web/src/backend/ChatBackend.js
@@ -0,0 +1,63 @@
+// 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 * as Setting from "../Setting";
+
+export function getGlobalChats() {
+ return fetch(`${Setting.ServerUrl}/api/get-global-chats`, {
+ method: "GET",
+ credentials: "include",
+ }).then(res => res.json());
+}
+
+export function getChats(owner) {
+ return fetch(`${Setting.ServerUrl}/api/get-chats?owner=${owner}`, {
+ method: "GET",
+ credentials: "include",
+ }).then(res => res.json());
+}
+
+export function getChat(owner, name) {
+ return fetch(`${Setting.ServerUrl}/api/get-chat?id=${owner}/${encodeURIComponent(name)}`, {
+ method: "GET",
+ credentials: "include",
+ }).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),
+ }).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),
+ }).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),
+ }).then(res => res.json());
+}