From dc1469941a113345f3eac5866c28e2e7716583bd Mon Sep 17 00:00:00 2001 From: Zhang Minghan Date: Wed, 30 Aug 2023 16:48:22 +0800 Subject: [PATCH] update payment and order --- api/anonymous.go | 2 +- api/chat.go | 18 +- api/image.go | 5 +- api/stream.go | 19 +- app/router/index.ts | 7 + app/src/App.vue | 10 +- app/src/assets/script/conversation.ts | 12 + app/src/assets/script/notify.ts | 26 ++ app/src/components/Notification.vue | 85 +++++ app/src/components/icons/back.vue | 3 + app/src/components/icons/buy.vue | 3 + app/src/components/icons/chart.vue | 3 + app/src/components/icons/draw.vue | 3 + app/src/components/icons/gift.vue | 3 + app/src/components/icons/sale.vue | 3 + app/src/components/icons/wallet.vue | 11 + app/src/views/HomeView.vue | 25 ++ app/src/views/LoginView.vue | 2 +- app/src/views/SettingsView.vue | 494 ++++++++++++++++++++++++++ auth/cert.go | 31 ++ auth/controller.go | 101 ++++++ auth/package.go | 67 ++++ auth/payment.go | 64 ++++ auth/usage.go | 100 ++++++ connection/database.go | 57 ++- conversation/conversation.go | 28 +- main.go | 3 + middleware/throttle.go | 11 +- 28 files changed, 1137 insertions(+), 59 deletions(-) create mode 100644 app/src/assets/script/notify.ts create mode 100644 app/src/components/Notification.vue create mode 100644 app/src/components/icons/back.vue create mode 100644 app/src/components/icons/buy.vue create mode 100644 app/src/components/icons/chart.vue create mode 100644 app/src/components/icons/draw.vue create mode 100644 app/src/components/icons/gift.vue create mode 100644 app/src/components/icons/sale.vue create mode 100644 app/src/components/icons/wallet.vue create mode 100644 app/src/views/SettingsView.vue create mode 100644 auth/cert.go create mode 100644 auth/controller.go create mode 100644 auth/package.go create mode 100644 auth/payment.go create mode 100644 auth/usage.go diff --git a/api/anonymous.go b/api/anonymous.go index 339a2b7..133052a 100644 --- a/api/anonymous.go +++ b/api/anonymous.go @@ -35,7 +35,7 @@ func GetChatGPTResponse(message []types.ChatGPTMessage, token int) (string, erro } if res.(map[string]interface{})["choices"] == nil { - return "empty", nil + return res.(map[string]interface{})["error"].(map[string]interface{})["message"].(string), nil } data := res.(map[string]interface{})["choices"].([]interface{})[0].(map[string]interface{})["message"].(map[string]interface{})["content"] return data.(string), nil diff --git a/api/chat.go b/api/chat.go index db54d76..c57af11 100644 --- a/api/chat.go +++ b/api/chat.go @@ -24,12 +24,21 @@ func SendSegmentMessage(conn *websocket.Conn, message types.ChatGPTSegmentRespon _ = conn.WriteMessage(websocket.TextMessage, []byte(utils.ToJson(message))) } -func TextChat(conn *websocket.Conn, instance *conversation.Conversation) string { +func TextChat(db *sql.DB, user *auth.User, conn *websocket.Conn, instance *conversation.Conversation) string { keyword, segment := ChatWithWeb(conversation.CopyMessage(instance.GetMessageSegment(12)), true) SendSegmentMessage(conn, types.ChatGPTSegmentResponse{Keyword: keyword, End: false}) msg := "" - StreamRequest("gpt-3.5-turbo-16k-0613", segment, 2000, func(resp string) { + + if instance.IsEnableGPT4() && !auth.ReduceGPT4(db, user) { + SendSegmentMessage(conn, types.ChatGPTSegmentResponse{ + Message: "You have run out of GPT-4 usage. Please buy more.", + End: true, + }) + return "You have run out of GPT-4 usage. Please buy more." + } + + StreamRequest(instance.IsEnableGPT4(), segment, 2000, func(resp string) { msg += resp SendSegmentMessage(conn, types.ChatGPTSegmentResponse{ Message: resp, @@ -38,6 +47,9 @@ func TextChat(conn *websocket.Conn, instance *conversation.Conversation) string }) if msg == "" { msg = "There was something wrong... Please try again later." + if instance.IsEnableGPT4() { + auth.IncreaseGPT4(db, user, 1) + } SendSegmentMessage(conn, types.ChatGPTSegmentResponse{ Message: msg, End: false, @@ -150,7 +162,7 @@ func ChatAPI(c *gin.Context) { cache := c.MustGet("cache").(*redis.Client) msg = ImageChat(conn, instance, user, db, cache) } else { - msg = TextChat(conn, instance) + msg = TextChat(db, user, conn, instance) } instance.SaveResponse(db, msg) } diff --git a/api/image.go b/api/image.go index 9c5b5f9..6e501e0 100644 --- a/api/image.go +++ b/api/image.go @@ -61,7 +61,10 @@ func GetImageWithUserLimit(user *auth.User, prompt string, db *sql.DB, cache *re } if res == "5" { - return "", fmt.Errorf("you have reached your limit of 5 images per day") + if auth.ReduceDalle(db, user) { + return GetImageWithCache(context.Background(), prompt, cache) + } + return "", fmt.Errorf("you have reached your limit of 5 free images per day, please buy more dalle usage or wait until tomorrow") } else { cache.Set(context.Background(), GetLimitFormat(user.GetID(db)), fmt.Sprintf("%d", utils.ToInt(res)+1), time.Hour*24) return GetImageWithCache(context.Background(), prompt, cache) diff --git a/api/stream.go b/api/stream.go index f892a64..bab6ea0 100644 --- a/api/stream.go +++ b/api/stream.go @@ -5,6 +5,7 @@ import ( "chat/utils" "crypto/tls" "encoding/json" + "fmt" "github.com/spf13/viper" "io" "log" @@ -50,26 +51,28 @@ func processLine(buf []byte) []string { return resp } -func StreamRequest(model string, messages []types.ChatGPTMessage, token int, callback func(string)) { +func NativeStreamRequest(model string, endpoint string, apikeys string, messages []types.ChatGPTMessage, token int, callback func(string)) { http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true} client := &http.Client{} - req, err := http.NewRequest("POST", viper.GetString("openai.user_endpoint")+"/chat/completions", utils.ConvertBody(types.ChatGPTRequest{ + req, err := http.NewRequest("POST", endpoint+"/chat/completions", utils.ConvertBody(types.ChatGPTRequest{ Model: model, Messages: messages, MaxToken: token, Stream: true, })) if err != nil { + fmt.Println(err) return } req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Bearer "+GetRandomKey(viper.GetString("openai.user"))) + req.Header.Set("Authorization", "Bearer "+GetRandomKey(apikeys)) res, err := client.Do(req) if err != nil { - log.Fatal(err) + fmt.Println(err) + return } defer res.Body.Close() @@ -89,3 +92,11 @@ func StreamRequest(model string, messages []types.ChatGPTMessage, token int, cal } } } + +func StreamRequest(enableGPT4 bool, messages []types.ChatGPTMessage, token int, callback func(string)) { + if enableGPT4 { + NativeStreamRequest("gpt-4", viper.GetString("openai.gpt4_endpoint"), viper.GetString("openai.gpt4"), messages, token, callback) + } else { + NativeStreamRequest("gpt-3.5-turbo-16k-0613", viper.GetString("openai.user_endpoint"), viper.GetString("openai.user"), messages, token, callback) + } +} diff --git a/app/router/index.ts b/app/router/index.ts index 2b87f9a..8031f73 100644 --- a/app/router/index.ts +++ b/app/router/index.ts @@ -19,6 +19,13 @@ const router = createRouter({ //@ts-ignore meta: { title: "Login | Chat Nio", } + }, { + path: "/settings", + name: "settings", + component: () => import("../src/views/SettingsView.vue"), + meta: { + title: "Settings | Chat Nio", + } } ], }); diff --git a/app/src/App.vue b/app/src/App.vue index af874e9..79f6b4f 100644 --- a/app/src/App.vue +++ b/app/src/App.vue @@ -11,12 +11,13 @@ import Delete from "./components/icons/delete.vue"; import { deleteConversation } from "./assets/script/api"; import {ref} from "vue"; import Close from "./components/icons/close.vue"; +import Notification from "./components/Notification.vue"; const current = manager.getCurrent(); const sidebar = ref(false), padding = ref(false); function goto() { - window.location.href = "https://deeptrain.net/login?app=chatnio"; + window.location.href = "https://deeptrain.lightxi.com/login?app=chatnio"; } function toggle(n: boolean) { @@ -37,6 +38,7 @@ function toggleConversation(id: number) { diff --git a/app/src/assets/script/conversation.ts b/app/src/assets/script/conversation.ts index c82e607..d5297fb 100644 --- a/app/src/assets/script/conversation.ts +++ b/app/src/assets/script/conversation.ts @@ -4,6 +4,9 @@ import axios from "axios"; import { auth, token } from "./auth"; import { ws_api } from "./conf"; import { gpt4 } from "./shared"; +import {notify} from "./notify"; + +var state = true; export type Message = { content: string; @@ -37,6 +40,10 @@ export class Connection { this.state = false; this.connection.onopen = () => { this.state = true; + if (!state) { + notify("服务器连接已恢复", 1000); + state = true; + } this.send({ token: token.value, id: this.id, @@ -44,6 +51,10 @@ export class Connection { } this.connection.onclose = () => { this.state = false; + if (state) { + notify("服务器连接已断开,正在尝试重连中...", 3000); + state = false; + } setTimeout(() => { this.init(); }, 3000); @@ -130,6 +141,7 @@ export class Conversation { }) const status = this.connection?.send({ message: content, + gpt4: gpt4.value, }); if (status) { this.addDynamicMessageFromAI(message, keyword, end); diff --git a/app/src/assets/script/notify.ts b/app/src/assets/script/notify.ts new file mode 100644 index 0000000..17a96e1 --- /dev/null +++ b/app/src/assets/script/notify.ts @@ -0,0 +1,26 @@ +import {ref} from "vue"; + +type Notification = { + content: string; + expire: number; + leave?: boolean; +} + +export const notifications = ref([]); + +export function notify(content: string, expire: number = 5000): void { + if (!notifications.value) notifications.value = []; + notifications.value.push({content, expire: (new Date()).getTime() + expire}); +} + +setInterval(() => { + if (!notifications.value) return; + const now = (new Date()).getTime(); + // leave animation: 0.5s + notifications.value = notifications.value.filter((notification) => { + if (notification.expire < now) { + notification.leave = true; + } + return notification.expire > now - 500; + }); +}, 800); diff --git a/app/src/components/Notification.vue b/app/src/components/Notification.vue new file mode 100644 index 0000000..5be415c --- /dev/null +++ b/app/src/components/Notification.vue @@ -0,0 +1,85 @@ + + + + + diff --git a/app/src/components/icons/back.vue b/app/src/components/icons/back.vue new file mode 100644 index 0000000..a95a5b5 --- /dev/null +++ b/app/src/components/icons/back.vue @@ -0,0 +1,3 @@ + diff --git a/app/src/components/icons/buy.vue b/app/src/components/icons/buy.vue new file mode 100644 index 0000000..7350db9 --- /dev/null +++ b/app/src/components/icons/buy.vue @@ -0,0 +1,3 @@ + diff --git a/app/src/components/icons/chart.vue b/app/src/components/icons/chart.vue new file mode 100644 index 0000000..59d187e --- /dev/null +++ b/app/src/components/icons/chart.vue @@ -0,0 +1,3 @@ + diff --git a/app/src/components/icons/draw.vue b/app/src/components/icons/draw.vue new file mode 100644 index 0000000..a55072c --- /dev/null +++ b/app/src/components/icons/draw.vue @@ -0,0 +1,3 @@ + diff --git a/app/src/components/icons/gift.vue b/app/src/components/icons/gift.vue new file mode 100644 index 0000000..61d67f1 --- /dev/null +++ b/app/src/components/icons/gift.vue @@ -0,0 +1,3 @@ + diff --git a/app/src/components/icons/sale.vue b/app/src/components/icons/sale.vue new file mode 100644 index 0000000..a9ed42d --- /dev/null +++ b/app/src/components/icons/sale.vue @@ -0,0 +1,3 @@ + diff --git a/app/src/components/icons/wallet.vue b/app/src/components/icons/wallet.vue new file mode 100644 index 0000000..1d51ba0 --- /dev/null +++ b/app/src/components/icons/wallet.vue @@ -0,0 +1,11 @@ + diff --git a/app/src/views/HomeView.vue b/app/src/views/HomeView.vue index be34713..cf1f5e2 100644 --- a/app/src/views/HomeView.vue +++ b/app/src/views/HomeView.vue @@ -8,6 +8,7 @@ import { auth, username } from "../assets/script/auth"; import Loading from "../components/icons/loading.vue"; import Bing from "../components/icons/bing.vue"; import { manager } from "../assets/script/shared"; +import router from "../../router"; const state = manager.getState(), length = manager.getLength(), current = manager.getCurrent(); manager.setRefresh(function refreshScrollbar() { @@ -44,6 +45,14 @@ onMounted(() => { if (e.key === "Enter") await send(); }); }); + +function settings() { + router.push('/settings'); +} + +function login() { + location.href = "https://deeptrain.lightxi.com/login?app=chatnio"; +}