diff --git a/adapter/chatgpt/chat.go b/adapter/chatgpt/chat.go
index 7b31d70c..46ddcf9b 100644
--- a/adapter/chatgpt/chat.go
+++ b/adapter/chatgpt/chat.go
@@ -115,6 +115,9 @@ func (c *ChatInstance) Test() bool {
Message: []globals.Message{{Role: "user", Content: "hi"}},
Token: 1,
})
+ if err != nil {
+ fmt.Println(fmt.Sprintf("%s: test failed (%s)", c.GetApiKey(), err.Error()))
+ }
return err == nil && len(result) > 0
}
diff --git a/app/src/components/home/ConversationSegment.tsx b/app/src/components/home/ConversationSegment.tsx
index 9ee053d4..e93d65c8 100644
--- a/app/src/components/home/ConversationSegment.tsx
+++ b/app/src/components/home/ConversationSegment.tsx
@@ -1,7 +1,7 @@
import { toggleConversation } from "../../conversation/history.ts";
import { filterMessage, mobile } from "../../utils.ts";
import { setMenu } from "../../store/menu.ts";
-import {MessageSquare, MoreHorizontal, Share2, Trash2} from "lucide-react";
+import { MessageSquare, MoreHorizontal, Share2, Trash2 } from "lucide-react";
import {
DropdownMenu,
DropdownMenuContent,
@@ -16,7 +16,10 @@ import { useState } from "react";
type ConversationSegmentProps = {
conversation: ConversationInstance;
current: number;
- operate: (conversation: { target: ConversationInstance, type: string }) => void;
+ operate: (conversation: {
+ target: ConversationInstance;
+ type: string;
+ }) => void;
};
function ConversationSegment({
conversation,
@@ -45,10 +48,13 @@ function ConversationSegment({
{filterMessage(conversation.name)}
{conversation.id}
- {
- setOpen(state);
- if (state) setOffset(new Date().getTime());
- }}>
+ {
+ setOpen(state);
+ if (state) setOffset(new Date().getTime());
+ }}
+ >
diff --git a/app/src/conf.ts b/app/src/conf.ts
index cdf4fa3c..3c940cde 100644
--- a/app/src/conf.ts
+++ b/app/src/conf.ts
@@ -1,7 +1,7 @@
import axios from "axios";
-export const version = "3.4.2";
-export const deploy: boolean = false;
+export const version = "3.4.3";
+export const deploy: boolean = true;
export let rest_api: string = "http://localhost:8094";
export let ws_api: string = "ws://localhost:8094";
diff --git a/app/src/conversation/conversation.ts b/app/src/conversation/conversation.ts
index a089726a..696e0ef3 100644
--- a/app/src/conversation/conversation.ts
+++ b/app/src/conversation/conversation.ts
@@ -1,5 +1,6 @@
import { ChatProps, Connection, StreamMessage } from "./connection.ts";
import { Message } from "./types.ts";
+import { event } from "../events/sharing.ts";
type ConversationCallback = (idx: number, message: Message[]) => void;
@@ -18,6 +19,15 @@ export class Conversation {
this.id = id;
this.end = true;
this.connection = new Connection(this.id);
+
+ if (id === -1 && this.idx === -1) {
+ event.bind(({ refer, data }) => {
+ console.log(
+ `[conversation] load from sharing event (ref: ${refer}, length: ${data.length})`,
+ );
+ this.load(data);
+ });
+ }
}
public setId(id: number): void {
diff --git a/app/src/conversation/manager.ts b/app/src/conversation/manager.ts
index a7d9a51f..047271b8 100644
--- a/app/src/conversation/manager.ts
+++ b/app/src/conversation/manager.ts
@@ -11,6 +11,7 @@ import { useShared } from "../utils.ts";
import { ChatProps } from "./connection.ts";
import { supportModelConvertor } from "../conf.ts";
import { AppDispatch } from "../store";
+import { event } from "../events/sharing.ts";
export class Manager {
conversations: Record;
@@ -21,6 +22,17 @@ export class Manager {
this.conversations = {};
this.conversations[-1] = this.createConversation(-1);
this.current = -1;
+
+ event.addEventListener(async (data) => {
+ console.debug(`[manager] accept sharing event (refer: ${data.refer})`);
+
+ const interval = setInterval(() => {
+ if (this.dispatch) {
+ this.toggle(this.dispatch, -1);
+ clearInterval(interval);
+ }
+ }, 100);
+ });
}
public setDispatch(dispatch: AppDispatch): void {
diff --git a/app/src/conversation/sharing.ts b/app/src/conversation/sharing.ts
index 559a27ea..d8e1d7a1 100644
--- a/app/src/conversation/sharing.ts
+++ b/app/src/conversation/sharing.ts
@@ -1,11 +1,11 @@
import axios from "axios";
-import {Message} from "./types.ts";
+import { Message } from "./types.ts";
export type SharingForm = {
status: boolean;
message: string;
data: string;
-}
+};
export type ViewData = {
name: string;
@@ -18,10 +18,11 @@ export type ViewForm = {
status: boolean;
message: string;
data: ViewData | null;
-}
+};
export async function shareConversation(
- id: number, refs: number[] = [-1],
+ id: number,
+ refs: number[] = [-1],
): Promise {
try {
const resp = await axios.post("/conversation/share", { id, refs });
@@ -31,9 +32,7 @@ export async function shareConversation(
}
}
-export async function viewConversation(
- hash: string,
-): Promise {
+export async function viewConversation(hash: string): Promise {
try {
const resp = await axios.get(`/conversation/view?hash=${hash}`);
return resp.data as ViewForm;
@@ -42,6 +41,6 @@ export async function viewConversation(
status: false,
message: (e as Error).message,
data: null,
- }
+ };
}
}
diff --git a/app/src/events/sharing.ts b/app/src/events/sharing.ts
new file mode 100644
index 00000000..3a7aca53
--- /dev/null
+++ b/app/src/events/sharing.ts
@@ -0,0 +1,12 @@
+import { EventCommitter } from "./struct.ts";
+import { Message } from "../conversation/types.ts";
+
+export type SharingEvent = {
+ refer: string;
+ data: Message[];
+};
+
+export const event = new EventCommitter({
+ name: "sharing",
+ destroyedAfterTrigger: true,
+});
diff --git a/app/src/events/struct.ts b/app/src/events/struct.ts
new file mode 100644
index 00000000..81a0c836
--- /dev/null
+++ b/app/src/events/struct.ts
@@ -0,0 +1,51 @@
+export type EventCommitterProps = {
+ name: string;
+ destroyedAfterTrigger?: boolean;
+};
+
+export class EventCommitter {
+ name: string;
+ trigger: ((data: T) => void) | undefined;
+ listeners: ((data: T) => void)[] = [];
+ destroyedAfterTrigger: boolean;
+
+ constructor({ name, destroyedAfterTrigger = false }: EventCommitterProps) {
+ this.name = name;
+ this.destroyedAfterTrigger = destroyedAfterTrigger;
+ }
+
+ protected setTrigger(trigger: (data: T) => void) {
+ this.trigger = trigger;
+ }
+
+ protected clearTrigger() {
+ this.trigger = undefined;
+ }
+
+ protected triggerEvent(data: T) {
+ this.trigger && this.trigger(data);
+ if (this.destroyedAfterTrigger) this.clearTrigger();
+
+ this.listeners.forEach((listener) => listener(data));
+ }
+
+ public emit(data: T) {
+ this.triggerEvent(data);
+ }
+
+ public bind(trigger: (data: T) => void) {
+ this.setTrigger(trigger);
+ }
+
+ public addEventListener(listener: (data: T) => void) {
+ this.listeners.push(listener);
+ }
+
+ public removeEventListener(listener: (data: T) => void) {
+ this.listeners = this.listeners.filter((item) => item !== listener);
+ }
+
+ public clearEventListener() {
+ this.listeners = [];
+ }
+}
diff --git a/app/src/i18n.ts b/app/src/i18n.ts
index a795ab63..511cf518 100644
--- a/app/src/i18n.ts
+++ b/app/src/i18n.ts
@@ -178,14 +178,15 @@ const resources = {
"share-conversation": "Share Conversation",
description: "Share this conversation with others: ",
"copy-link": "Copy Link",
- "view": "View",
+ view: "View",
success: "Share success",
failed: "Share failed",
copied: "Copied",
"copied-description": "Link has been copied to clipboard",
"not-found": "Conversation not found",
- "not-found-description": "Conversation not found, please check if the link is correct or the conversation has been deleted",
- }
+ "not-found-description":
+ "Conversation not found, please check if the link is correct or the conversation has been deleted",
+ },
},
},
cn: {
@@ -350,14 +351,15 @@ const resources = {
"share-conversation": "分享对话",
description: "将此对话与他人分享:",
"copy-link": "复制链接",
- "view": "查看",
+ view: "查看",
success: "分享成功",
failed: "分享失败",
copied: "复制成功",
"copied-description": "链接已复制到剪贴板",
"not-found": "对话未找到",
- "not-found-description": "对话未找到,请检查链接是否正确或对话是否已被删除",
- }
+ "not-found-description":
+ "对话未找到,请检查链接是否正确或对话是否已被删除",
+ },
},
},
ru: {
@@ -533,14 +535,15 @@ const resources = {
"share-conversation": "Поделиться разговором",
description: "Поделитесь этим разговором с другими: ",
"copy-link": "Скопировать ссылку",
- "view": "Посмотреть",
+ view: "Посмотреть",
success: "Поделиться успешно",
failed: "Поделиться не удалось",
copied: "Скопировано",
"copied-description": "Ссылка скопирована в буфер обмена",
"not-found": "Разговор не найден",
- "not-found-description": "Разговор не найден, пожалуйста, проверьте, правильная ли ссылка или разговор был удален",
- }
+ "not-found-description":
+ "Разговор не найден, пожалуйста, проверьте, правильная ли ссылка или разговор был удален",
+ },
},
},
};
diff --git a/app/src/router.ts b/app/src/router.ts
index d440c547..dd3a58bb 100644
--- a/app/src/router.ts
+++ b/app/src/router.ts
@@ -27,7 +27,7 @@ const router = createBrowserRouter([
id: "share",
path: "/share/:hash",
Component: Sharing,
- }
+ },
]);
export default router;
diff --git a/app/src/routes/Home.tsx b/app/src/routes/Home.tsx
index 3d9df5bb..c2e23e17 100644
--- a/app/src/routes/Home.tsx
+++ b/app/src/routes/Home.tsx
@@ -4,7 +4,8 @@ import { Input } from "../components/ui/input.tsx";
import { Toggle } from "../components/ui/toggle.tsx";
import {
ChevronDown,
- ChevronRight, Copy,
+ ChevronRight,
+ Copy,
FolderKanban,
Globe,
LogIn,
@@ -35,7 +36,8 @@ import {
formatMessage,
mobile,
useAnimation,
- useEffectAsync, copyClipboard,
+ useEffectAsync,
+ copyClipboard,
} from "../utils.ts";
import { toast, useToast } from "../components/ui/use-toast.ts";
import { ConversationInstance, Message } from "../conversation/types.ts";
@@ -74,11 +76,10 @@ function SideBar() {
const open = useSelector((state: RootState) => state.menu.open);
const auth = useSelector(selectAuthenticated);
const current = useSelector(selectCurrent);
- const [operateConversation, setOperateConversation] =
- useState<{
- target: ConversationInstance | null;
- type: string;
- }>({ target: null, type: "" });
+ const [operateConversation, setOperateConversation] = useState<{
+ target: ConversationInstance | null;
+ type: string;
+ }>({ target: null, type: "" });
const { toast } = useToast();
const history: ConversationInstance[] = useSelector(selectHistory);
const refresh = useRef(null);
@@ -140,7 +141,10 @@ function SideBar() {
)}
{
if (!open) setOperateConversation({ target: null, type: "" });
}}
@@ -194,16 +198,17 @@ function SideBar() {
{
if (!open) setOperateConversation({ target: null, type: "" });
}}
>
-
- {t("share.title")}
-
+ {t("share.title")}
{t("share.description")}
@@ -223,9 +228,13 @@ function SideBar() {
e.preventDefault();
e.stopPropagation();
- const resp = await shareConversation(operateConversation?.target?.id || -1);
- if (resp.status) setShared(`${location.origin}/share/${resp.data}`);
- else toast({
+ const resp = await shareConversation(
+ operateConversation?.target?.id || -1,
+ );
+ if (resp.status)
+ setShared(`${location.origin}/share/${resp.data}`);
+ else
+ toast({
title: t("share.failed"),
description: resp.message,
});
@@ -250,19 +259,21 @@ function SideBar() {
>
-
- {t("share.success")}
-
+ {t("share.success")}
-
diff --git a/app/src/routes/Sharing.tsx b/app/src/routes/Sharing.tsx
index 1192babc..a666dd0a 100644
--- a/app/src/routes/Sharing.tsx
+++ b/app/src/routes/Sharing.tsx
@@ -1,25 +1,33 @@
import "../assets/sharing.less";
-import {useParams} from "react-router-dom";
-import {viewConversation, ViewData, ViewForm} from "../conversation/sharing.ts";
-import {copyClipboard, saveAsFile, useEffectAsync} from "../utils.ts";
-import {useState} from "react";
-import {Copy, File, HelpCircle, Loader2, MessagesSquare} from "lucide-react";
-import {useTranslation} from "react-i18next";
+import { useParams } from "react-router-dom";
+import {
+ viewConversation,
+ ViewData,
+ ViewForm,
+} from "../conversation/sharing.ts";
+import { copyClipboard, saveAsFile, useEffectAsync } from "../utils.ts";
+import { useState } from "react";
+import { Copy, File, HelpCircle, Loader2, MessagesSquare } from "lucide-react";
+import { useTranslation } from "react-i18next";
import MessageSegment from "../components/Message.tsx";
-import {Button} from "../components/ui/button.tsx";
+import { Button } from "../components/ui/button.tsx";
import router from "../router.ts";
-import {useToast} from "../components/ui/use-toast.ts";
+import { useToast } from "../components/ui/use-toast.ts";
+import { event } from "../events/sharing.ts";
+import { Message } from "../conversation/types.ts";
type SharingFormProps = {
refer?: string;
data: ViewData | null;
-}
+};
function SharingForm({ refer, data }: SharingFormProps) {
if (data === null) return null;
const { t } = useTranslation();
const date = new Date(data.time);
- const time = `${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}`;
+ const time = `${
+ date.getMonth() + 1
+ }-${date.getDate()} ${date.getHours()}:${date.getMinutes()}`;
const value = JSON.stringify(data, null, 2);
const { toast } = useToast();
@@ -27,43 +35,56 @@ function SharingForm({ refer, data }: SharingFormProps) {
-
+
{data.username}
{data.name}
{time}
- {
- data.messages.map((message, i) => (
-
- ))
- }
+ {data.messages.map((message, i) => (
+
+ ))}
- {
- await copyClipboard(value);
- toast({
- title: t('share.copied'),
- });
- }}>
+ {
+ await copyClipboard(value);
+ toast({
+ title: t("share.copied"),
+ });
+ }}
+ >
- {t('message.copy')}
+ {t("message.copy")}
- saveAsFile("conversation.json", value)}>
+ saveAsFile("conversation.json", value)}
+ >
- {t('message.save')}
+ {t("message.save")}
- {
- refer && sessionStorage.setItem('refer', refer);
- await router.navigate('/');
- }}>
+ {
+ event.emit({
+ refer: refer as string,
+ data: data?.messages as Message[],
+ });
+ await router.navigate("/");
+ }}
+ >
- {t('message.use')}
+ {t("message.use")}
- )
+ );
}
function Sharing() {
@@ -84,25 +105,21 @@ function Sharing() {
return (
- {
- data === null ? (
-
-
-
- ) : (
- data.status ? (
-
- ) : (
-
-
-
{t('share.not-found')}
-
{t('share.not-found-description')}
-
- )
- )
- }
+ {data === null ? (
+
+
+
+ ) : data.status ? (
+
+ ) : (
+
+
+
{t("share.not-found")}
+
{t("share.not-found-description")}
+
+ )}
- )
+ );
}
export default Sharing;
diff --git a/app/src/store/api.ts b/app/src/store/api.ts
index 2f1d2d45..5ad7fd82 100644
--- a/app/src/store/api.ts
+++ b/app/src/store/api.ts
@@ -1,6 +1,6 @@
import { createSlice } from "@reduxjs/toolkit";
import { getKey } from "../conversation/addition.ts";
-import { AppDispatch } from "./index.ts";
+import { AppDispatch, RootState } from "./index.ts";
export const apiSlice = createSlice({
name: "api",
@@ -31,8 +31,8 @@ export const { toggleDialog, setDialog, openDialog, closeDialog, setKey } =
apiSlice.actions;
export default apiSlice.reducer;
-export const dialogSelector = (state: any): boolean => state.api.dialog;
-export const keySelector = (state: any): string => state.api.key;
+export const dialogSelector = (state: RootState): boolean => state.api.dialog;
+export const keySelector = (state: RootState): string => state.api.key;
export const getApiKey = async (dispatch: AppDispatch) => {
const response = await getKey();
diff --git a/main.go b/main.go
index e6f315df..8aa75050 100644
--- a/main.go
+++ b/main.go
@@ -1,7 +1,6 @@
package main
import (
- "chat/adapter/chatgpt"
"chat/addition"
"chat/auth"
"chat/manager"
@@ -22,8 +21,6 @@ func main() {
app := gin.Default()
middleware.RegisterMiddleware(app)
- fmt.Println(chatgpt.FilterKeys("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|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|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|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|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|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|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|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|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|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|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"))
- return
{
auth.Register(app)
manager.Register(app)