mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 16:56:38 +08:00
添加会话授权支持
This commit is contained in:
parent
3bb6814493
commit
005d219a8c
@ -8,6 +8,7 @@
|
|||||||
* [ ] 使用 MySQL 保存用户的聊天的历史记录
|
* [ ] 使用 MySQL 保存用户的聊天的历史记录
|
||||||
* [ ] 用户聊天鉴权,设置口令模式
|
* [ ] 用户聊天鉴权,设置口令模式
|
||||||
* [ ] 每次连接自动加载历史记录
|
* [ ] 每次连接自动加载历史记录
|
||||||
|
* [ ] OpenAI API 负载均衡,限制每个 API Key 每分钟之内调用次数不超过 15次,防止被封
|
||||||
* [ ] 角色设定,预设一些角色,比如程序员,产品经理,医生,作家,老师...
|
* [ ] 角色设定,预设一些角色,比如程序员,产品经理,医生,作家,老师...
|
||||||
* [ ] markdown 语法解析
|
* [ ] markdown 语法解析
|
||||||
* [ ] 用户配置界面
|
* [ ] 用户配置界面
|
||||||
|
@ -10,6 +10,12 @@ import (
|
|||||||
|
|
||||||
// ConfigSetHandle set configs
|
// ConfigSetHandle set configs
|
||||||
func (s *Server) ConfigSetHandle(c *gin.Context) {
|
func (s *Server) ConfigSetHandle(c *gin.Context) {
|
||||||
|
token := c.Query("token")
|
||||||
|
if token != "RockYang" {
|
||||||
|
c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: types.ErrorMsg})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var data map[string]string
|
var data map[string]string
|
||||||
err := json.NewDecoder(c.Request.Body).Decode(&data)
|
err := json.NewDecoder(c.Request.Body).Decode(&data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -71,6 +77,23 @@ func (s *Server) ConfigSetHandle(c *gin.Context) {
|
|||||||
s.Config.Chat.EnableContext = v
|
s.Config.Chat.EnableContext = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enable auth
|
||||||
|
if enableAuth, ok := data["enable_auth"]; ok {
|
||||||
|
v, err := strconv.ParseBool(enableAuth)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusOK, types.BizVo{
|
||||||
|
Code: types.InvalidParams,
|
||||||
|
Message: "enable_auth must be a bool parameter",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Config.EnableAuth = v
|
||||||
|
}
|
||||||
|
|
||||||
|
if token, ok := data["token"]; ok {
|
||||||
|
s.Config.Tokens = append(s.Config.Tokens, token)
|
||||||
|
}
|
||||||
|
|
||||||
// 保存配置文件
|
// 保存配置文件
|
||||||
logger.Infof("Config: %+v", s.Config)
|
logger.Infof("Config: %+v", s.Config)
|
||||||
err = types.SaveConfig(s.Config, s.ConfigPath)
|
err = types.SaveConfig(s.Config, s.ConfigPath)
|
||||||
|
@ -2,6 +2,7 @@ package server
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-contrib/sessions/cookie"
|
"github.com/gin-contrib/sessions/cookie"
|
||||||
@ -12,6 +13,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
logger2 "openai/logger"
|
logger2 "openai/logger"
|
||||||
"openai/types"
|
"openai/types"
|
||||||
|
"openai/utils"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@ -35,6 +37,8 @@ type Server struct {
|
|||||||
ConfigPath string
|
ConfigPath string
|
||||||
Client *http.Client
|
Client *http.Client
|
||||||
History map[string][]types.Message
|
History map[string][]types.Message
|
||||||
|
|
||||||
|
WsSession map[string]string // 关闭 Websocket 会话
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(configPath string) (*Server, error) {
|
func NewServer(configPath string) (*Server, error) {
|
||||||
@ -55,7 +59,9 @@ func NewServer(configPath string) (*Server, error) {
|
|||||||
Config: config,
|
Config: config,
|
||||||
Client: client,
|
Client: client,
|
||||||
ConfigPath: configPath,
|
ConfigPath: configPath,
|
||||||
History: make(map[string][]types.Message, 16)}, nil
|
History: make(map[string][]types.Message, 16),
|
||||||
|
WsSession: make(map[string]string),
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) Run(webRoot embed.FS, path string) {
|
func (s *Server) Run(webRoot embed.FS, path string) {
|
||||||
@ -63,9 +69,11 @@ func (s *Server) Run(webRoot embed.FS, path string) {
|
|||||||
engine := gin.Default()
|
engine := gin.Default()
|
||||||
engine.Use(sessionMiddleware(s.Config))
|
engine.Use(sessionMiddleware(s.Config))
|
||||||
engine.Use(corsMiddleware())
|
engine.Use(corsMiddleware())
|
||||||
engine.Use(AuthorizeMiddleware())
|
engine.Use(AuthorizeMiddleware(s))
|
||||||
|
|
||||||
engine.GET("/hello", Hello)
|
engine.GET("/hello", Hello)
|
||||||
|
engine.POST("/api/session/get", s.GetSessionHandle)
|
||||||
|
engine.POST("/api/login", s.LoginHandle)
|
||||||
engine.Any("/api/chat", s.ChatHandle)
|
engine.Any("/api/chat", s.ChatHandle)
|
||||||
engine.POST("/api/config/set", s.ConfigSetHandle)
|
engine.POST("/api/config/set", s.ConfigSetHandle)
|
||||||
|
|
||||||
@ -109,7 +117,7 @@ func corsMiddleware() gin.HandlerFunc {
|
|||||||
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
|
c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
|
||||||
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
|
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
|
||||||
//允许跨域设置可以返回其他子段,可以自定义字段
|
//允许跨域设置可以返回其他子段,可以自定义字段
|
||||||
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, Content-Type, Session-Name, Session")
|
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, Content-Type, ChatGPT-Token, Session")
|
||||||
// 允许浏览器(客户端)可以解析的头部 (重要)
|
// 允许浏览器(客户端)可以解析的头部 (重要)
|
||||||
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
|
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
|
||||||
//设置缓存时间
|
//设置缓存时间
|
||||||
@ -133,27 +141,63 @@ func corsMiddleware() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AuthorizeMiddleware 用户授权验证
|
// AuthorizeMiddleware 用户授权验证
|
||||||
func AuthorizeMiddleware() gin.HandlerFunc {
|
func AuthorizeMiddleware(s *Server) gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
c.Next()
|
if !s.Config.EnableAuth || c.Request.URL.Path == "/api/login" || c.Request.URL.Path == "/api/config/set" {
|
||||||
//if c.Request.URL.Path == "/login" {
|
c.Next()
|
||||||
// c.Next()
|
return
|
||||||
// return
|
}
|
||||||
//}
|
|
||||||
|
|
||||||
//sessionName := c.GetHeader("Session-Name")
|
tokenName := c.GetHeader("Sec-WebSocket-Protocol")
|
||||||
//session, err := c.Cookie(sessionName)
|
logger.Info(s.WsSession)
|
||||||
//if err == nil {
|
logger.Info(tokenName)
|
||||||
// c.Request.Header.Set(utils.SessionKey, session)
|
if addr, ok := s.WsSession[tokenName]; ok && addr == c.ClientIP() {
|
||||||
// c.Next()
|
c.Next()
|
||||||
//} else {
|
return
|
||||||
// logger.Fatal(err)
|
}
|
||||||
// c.Abort()
|
|
||||||
// c.JSON(http.StatusUnauthorized, "No session data found")
|
tokenName = c.GetHeader(types.TokenName)
|
||||||
//}
|
session := sessions.Default(c)
|
||||||
|
user := session.Get(tokenName)
|
||||||
|
if user != nil {
|
||||||
|
c.Set(types.SessionKey, user)
|
||||||
|
c.Next()
|
||||||
|
} else {
|
||||||
|
c.Abort()
|
||||||
|
c.JSON(http.StatusOK, types.BizVo{
|
||||||
|
Code: types.NotAuthorized,
|
||||||
|
Message: "Not Authorized",
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) GetSessionHandle(c *gin.Context) {
|
||||||
|
session := sessions.Default(c)
|
||||||
|
c.JSON(http.StatusOK, types.BizVo{Code: types.Success, Data: session.Get(types.TokenName)})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) LoginHandle(c *gin.Context) {
|
||||||
|
var data map[string]string
|
||||||
|
err := json.NewDecoder(c.Request.Body).Decode(&data)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: types.ErrorMsg})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
token := data["token"]
|
||||||
|
if !utils.ContainsItem(s.Config.Tokens, token) {
|
||||||
|
c.JSON(http.StatusOK, types.BizVo{Code: types.Failed, Message: "Invalid token"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionId := utils.RandString(42)
|
||||||
|
session := sessions.Default(c)
|
||||||
|
session.Set(sessionId, token)
|
||||||
|
// 记录客户端 IP 地址
|
||||||
|
s.WsSession[sessionId] = c.ClientIP()
|
||||||
|
c.JSON(http.StatusOK, types.BizVo{Code: types.Success, Data: sessionId})
|
||||||
|
}
|
||||||
|
|
||||||
func Hello(c *gin.Context) {
|
func Hello(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": fmt.Sprintf("HELLO, XWEBSSH !!!")})
|
c.JSON(http.StatusOK, gin.H{"code": 0, "message": fmt.Sprintf("HELLO, XWEBSSH !!!")})
|
||||||
}
|
}
|
||||||
|
@ -10,10 +10,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Listen string
|
Listen string
|
||||||
Session Session
|
Session Session
|
||||||
ProxyURL string
|
ProxyURL string
|
||||||
Chat Chat
|
Chat Chat
|
||||||
|
EnableAuth bool // 是否开启鉴权
|
||||||
|
Tokens []string // 授权的白名单列表 TODO: 后期要存储到 LevelDB 或者 Mysql 数据库
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chat configs struct
|
// Chat configs struct
|
||||||
@ -60,6 +62,7 @@ func NewDefaultConfig() *Config {
|
|||||||
Temperature: 1.0,
|
Temperature: 1.0,
|
||||||
EnableContext: true,
|
EnableContext: true,
|
||||||
},
|
},
|
||||||
|
EnableAuth: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,5 +31,9 @@ const (
|
|||||||
InvalidParams = BizCode(101) // 非法参数
|
InvalidParams = BizCode(101) // 非法参数
|
||||||
NotAuthorized = BizCode(400) // 未授权
|
NotAuthorized = BizCode(400) // 未授权
|
||||||
|
|
||||||
OkMsg = "Success"
|
OkMsg = "Success"
|
||||||
|
ErrorMsg = "系统开小差了"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const TokenName = "ChatGPT-Token"
|
||||||
|
const SessionKey = "WEB_SSH_SESSION"
|
||||||
|
@ -30,3 +30,12 @@ func Long2IP(ipInt int64) string {
|
|||||||
func IsBlank(value string) bool {
|
func IsBlank(value string) bool {
|
||||||
return len(strings.TrimSpace(value)) == 0
|
return len(strings.TrimSpace(value)) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ContainsItem(slice []string, item string) bool {
|
||||||
|
for _, e := range slice {
|
||||||
|
if e == item {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -1 +1,2 @@
|
|||||||
VUE_APP_WS_HOST=ws://172.22.11.200:5678
|
VUE_APP_API_HOST=172.22.11.200:5678
|
||||||
|
VUE_APP_API_SECURE=false
|
@ -1 +1,2 @@
|
|||||||
VUE_APP_WS_HOST=ws://127.0.0.1:5678
|
VUE_APP_WS_HOST=ws://127.0.0.1:5678
|
||||||
|
VUE_APP_API_SECURE=false
|
||||||
|
112
web/package-lock.json
generated
112
web/package-lock.json
generated
@ -14,6 +14,7 @@
|
|||||||
"element-plus": "^2.1.11",
|
"element-plus": "^2.1.11",
|
||||||
"good-storage": "^1.1.1",
|
"good-storage": "^1.1.1",
|
||||||
"json-bigint": "^1.0.0",
|
"json-bigint": "^1.0.0",
|
||||||
|
"qs": "^6.11.1",
|
||||||
"vue": "^3.2.13",
|
"vue": "^3.2.13",
|
||||||
"vue-router": "^4.0.15"
|
"vue-router": "^4.0.15"
|
||||||
},
|
},
|
||||||
@ -3624,6 +3625,18 @@
|
|||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/body-parser/node_modules/qs": {
|
||||||
|
"version": "6.9.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
|
||||||
|
"integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bonjour-service": {
|
"node_modules/bonjour-service": {
|
||||||
"version": "1.0.12",
|
"version": "1.0.12",
|
||||||
"resolved": "https://registry.npmmirror.com/bonjour-service/-/bonjour-service-1.0.12.tgz",
|
"resolved": "https://registry.npmmirror.com/bonjour-service/-/bonjour-service-1.0.12.tgz",
|
||||||
@ -3712,7 +3725,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.2.tgz",
|
||||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.1",
|
"function-bind": "^1.1.1",
|
||||||
"get-intrinsic": "^1.0.2"
|
"get-intrinsic": "^1.0.2"
|
||||||
@ -5728,6 +5740,18 @@
|
|||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/express/node_modules/qs": {
|
||||||
|
"version": "6.9.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
|
||||||
|
"integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/express/node_modules/safe-buffer": {
|
"node_modules/express/node_modules/safe-buffer": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
@ -6013,8 +6037,7 @@
|
|||||||
"node_modules/function-bind": {
|
"node_modules/function-bind": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/functional-red-black-tree": {
|
"node_modules/functional-red-black-tree": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@ -6044,7 +6067,6 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
|
||||||
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
|
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.1",
|
"function-bind": "^1.1.1",
|
||||||
"has": "^1.0.3",
|
"has": "^1.0.3",
|
||||||
@ -6157,7 +6179,6 @@
|
|||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmmirror.com/has/-/has-1.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/has/-/has-1.0.3.tgz",
|
||||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"function-bind": "^1.1.1"
|
"function-bind": "^1.1.1"
|
||||||
},
|
},
|
||||||
@ -6187,7 +6208,6 @@
|
|||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
@ -7682,6 +7702,14 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/object-inspect": {
|
||||||
|
"version": "1.12.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||||
|
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/object-keys": {
|
"node_modules/object-keys": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz",
|
||||||
@ -8747,12 +8775,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
"node_modules/qs": {
|
||||||
"version": "6.9.7",
|
"version": "6.11.1",
|
||||||
"resolved": "https://registry.npmmirror.com/qs/-/qs-6.9.7.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz",
|
||||||
"integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==",
|
"integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==",
|
||||||
"dev": true,
|
"dependencies": {
|
||||||
|
"side-channel": "^1.0.4"
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.6"
|
"node": ">=0.6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/queue-microtask": {
|
"node_modules/queue-microtask": {
|
||||||
@ -9308,6 +9341,19 @@
|
|||||||
"integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
|
"integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"node_modules/side-channel": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||||
|
"dependencies": {
|
||||||
|
"call-bind": "^1.0.0",
|
||||||
|
"get-intrinsic": "^1.0.2",
|
||||||
|
"object-inspect": "^1.9.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/signal-exit": {
|
"node_modules/signal-exit": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz",
|
"resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
@ -13832,6 +13878,12 @@
|
|||||||
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
|
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.0.0.tgz",
|
||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
|
},
|
||||||
|
"qs": {
|
||||||
|
"version": "6.9.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
|
||||||
|
"integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==",
|
||||||
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -13911,7 +13963,6 @@
|
|||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.2.tgz",
|
"resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.2.tgz",
|
||||||
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"function-bind": "^1.1.1",
|
"function-bind": "^1.1.1",
|
||||||
"get-intrinsic": "^1.0.2"
|
"get-intrinsic": "^1.0.2"
|
||||||
@ -15514,6 +15565,12 @@
|
|||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"qs": {
|
||||||
|
"version": "6.9.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz",
|
||||||
|
"integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.2.1",
|
"version": "5.2.1",
|
||||||
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
"resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
@ -15745,8 +15802,7 @@
|
|||||||
"function-bind": {
|
"function-bind": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz",
|
||||||
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
|
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"functional-red-black-tree": {
|
"functional-red-black-tree": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@ -15770,7 +15826,6 @@
|
|||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
|
||||||
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
|
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"function-bind": "^1.1.1",
|
"function-bind": "^1.1.1",
|
||||||
"has": "^1.0.3",
|
"has": "^1.0.3",
|
||||||
@ -15865,7 +15920,6 @@
|
|||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmmirror.com/has/-/has-1.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/has/-/has-1.0.3.tgz",
|
||||||
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"function-bind": "^1.1.1"
|
"function-bind": "^1.1.1"
|
||||||
}
|
}
|
||||||
@ -15888,8 +15942,7 @@
|
|||||||
"has-symbols": {
|
"has-symbols": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz",
|
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"hash-sum": {
|
"hash-sum": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@ -17079,6 +17132,11 @@
|
|||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"object-inspect": {
|
||||||
|
"version": "1.12.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz",
|
||||||
|
"integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g=="
|
||||||
|
},
|
||||||
"object-keys": {
|
"object-keys": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz",
|
"resolved": "https://registry.npmmirror.com/object-keys/-/object-keys-1.1.1.tgz",
|
||||||
@ -17844,10 +17902,12 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"qs": {
|
"qs": {
|
||||||
"version": "6.9.7",
|
"version": "6.11.1",
|
||||||
"resolved": "https://registry.npmmirror.com/qs/-/qs-6.9.7.tgz",
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz",
|
||||||
"integrity": "sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==",
|
"integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==",
|
||||||
"dev": true
|
"requires": {
|
||||||
|
"side-channel": "^1.0.4"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"queue-microtask": {
|
"queue-microtask": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
@ -18317,6 +18377,16 @@
|
|||||||
"integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
|
"integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"side-channel": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==",
|
||||||
|
"requires": {
|
||||||
|
"call-bind": "^1.0.0",
|
||||||
|
"get-intrinsic": "^1.0.2",
|
||||||
|
"object-inspect": "^1.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"signal-exit": {
|
"signal-exit": {
|
||||||
"version": "3.0.7",
|
"version": "3.0.7",
|
||||||
"resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz",
|
"resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"element-plus": "^2.1.11",
|
"element-plus": "^2.1.11",
|
||||||
"good-storage": "^1.1.1",
|
"good-storage": "^1.1.1",
|
||||||
"json-bigint": "^1.0.0",
|
"json-bigint": "^1.0.0",
|
||||||
|
"qs": "^6.11.1",
|
||||||
"vue": "^3.2.13",
|
"vue": "^3.2.13",
|
||||||
"vue-router": "^4.0.15"
|
"vue-router": "^4.0.15"
|
||||||
},
|
},
|
||||||
|
@ -24,10 +24,6 @@ export default defineComponent({
|
|||||||
icon: {
|
icon: {
|
||||||
type: String,
|
type: String,
|
||||||
default: 'images/gpt-icon.png',
|
default: 'images/gpt-icon.png',
|
||||||
},
|
|
||||||
cursor: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
@ -3,15 +3,17 @@ import {createApp} from 'vue'
|
|||||||
import ElementPlus from "element-plus"
|
import ElementPlus from "element-plus"
|
||||||
import "element-plus/dist/index.css"
|
import "element-plus/dist/index.css"
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import Home from './views/Chat.vue'
|
import Chat from './views/Chat.vue'
|
||||||
import NotFound from './views/404.vue'
|
import NotFound from './views/404.vue'
|
||||||
import './utils/prototype'
|
import './utils/prototype'
|
||||||
import "./assets/css/bootstrap.min.css"
|
import "./assets/css/bootstrap.min.css"
|
||||||
|
import {Global} from "@/utils/storage";
|
||||||
|
|
||||||
|
Global['Chat'] = Chat
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
name: 'home', path: '/', component: Home, meta: {
|
name: 'home', path: '/', component: Chat, meta: {
|
||||||
title: 'ChatGPT-Console'
|
title: 'ChatGPT-Console'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
54
web/src/utils/http.js
Normal file
54
web/src/utils/http.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import axios from 'axios'
|
||||||
|
import {getSessionId} from "@/utils/storage";
|
||||||
|
|
||||||
|
axios.defaults.timeout = 5000
|
||||||
|
axios.defaults.baseURL = process.env.VUE_APP_API_SECURE === true ? 'https://' + process.env.VUE_APP_API_HOST : 'http://' + process.env.VUE_APP_API_HOST
|
||||||
|
axios.defaults.withCredentials = true
|
||||||
|
axios.defaults.headers.post['Content-Type'] = 'application/json'
|
||||||
|
|
||||||
|
// HTTP拦截器
|
||||||
|
axios.interceptors.request.use(
|
||||||
|
config => {
|
||||||
|
// set token
|
||||||
|
config.headers['ChatGPT-Token'] = getSessionId();
|
||||||
|
return config
|
||||||
|
}, error => {
|
||||||
|
return Promise.reject(error)
|
||||||
|
})
|
||||||
|
axios.interceptors.response.use(
|
||||||
|
response => {
|
||||||
|
let data = response.data;
|
||||||
|
if (data.code === 0) {
|
||||||
|
return response
|
||||||
|
} else {
|
||||||
|
return Promise.reject(response.data)
|
||||||
|
}
|
||||||
|
}, error => {
|
||||||
|
return Promise.reject(error)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// send a http get request
|
||||||
|
export function httpGet(url, params = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios.get(url, {
|
||||||
|
params: params
|
||||||
|
}).then(response => {
|
||||||
|
resolve(response.data)
|
||||||
|
}).catch(err => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// send a http post request
|
||||||
|
export function httpPost(url, data = {}, options = {}) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
axios.post(url, data, options).then(response => {
|
||||||
|
resolve(response.data)
|
||||||
|
}).catch(err => {
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
16
web/src/utils/storage.js
Normal file
16
web/src/utils/storage.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/* eslint-disable no-constant-condition */
|
||||||
|
/**
|
||||||
|
* storage handler
|
||||||
|
*/
|
||||||
|
import Storage from 'good-storage'
|
||||||
|
|
||||||
|
const SessionIdKey = 'ChatGPT_SESSION_ID';
|
||||||
|
export const Global = {}
|
||||||
|
|
||||||
|
export function getSessionId() {
|
||||||
|
return Storage.get(SessionIdKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setSessionId(value) {
|
||||||
|
Storage.set(SessionIdKey, value)
|
||||||
|
}
|
@ -1,6 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="body">
|
<div class="body" v-loading="loading">
|
||||||
<div id="container">
|
<div id="container">
|
||||||
|
<div class="tool-box">
|
||||||
|
<el-image style="width: 24px; height: 24px" :src="logo"/>
|
||||||
|
<el-button round>欢迎来到人工智能时代</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="chat-box" :style="{height: chatBoxHeight+'px'}">
|
<div class="chat-box" :style="{height: chatBoxHeight+'px'}">
|
||||||
<div v-for="chat in chatData" :key="chat.id">
|
<div v-for="chat in chatData" :key="chat.id">
|
||||||
<chat-prompt
|
<chat-prompt
|
||||||
@ -9,7 +14,6 @@
|
|||||||
:content="chat.content"/>
|
:content="chat.content"/>
|
||||||
<chat-reply v-else-if="chat.type==='reply'"
|
<chat-reply v-else-if="chat.type==='reply'"
|
||||||
:icon="chat.icon"
|
:icon="chat.icon"
|
||||||
:cursor="chat.cursor"
|
|
||||||
:content="chat.content"/>
|
:content="chat.content"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -25,14 +29,14 @@
|
|||||||
v-on:focus="focus"
|
v-on:focus="focus"
|
||||||
autofocus
|
autofocus
|
||||||
type="textarea"
|
type="textarea"
|
||||||
placeholder="Input any thing here..."
|
placeholder="开始你的提问"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="btn-container">
|
<div class="btn-container">
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-button type="success" class="send" :disabled="sending" v-on:click="sendMessage">发送</el-button>
|
<el-button type="success" class="send" :disabled="sending" v-on:click="sendMessage">发送</el-button>
|
||||||
<el-button type="info" class="config" circle @click="showDialog = true">
|
<el-button type="info" class="config" circle @click="showConnectDialog = true">
|
||||||
<el-icon>
|
<el-icon>
|
||||||
<Tools/>
|
<Tools/>
|
||||||
</el-icon>
|
</el-icon>
|
||||||
@ -44,7 +48,28 @@
|
|||||||
|
|
||||||
</div><!-- end container -->
|
</div><!-- end container -->
|
||||||
|
|
||||||
<config-dialog v-model:show="showDialog"></config-dialog>
|
<config-dialog v-model:show="showConnectDialog"></config-dialog>
|
||||||
|
|
||||||
|
<div class="token-dialog">
|
||||||
|
<el-dialog
|
||||||
|
v-model="showLoginDialog"
|
||||||
|
:show-close="false"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
title="请输入口令继续访问"
|
||||||
|
>
|
||||||
|
<el-row>
|
||||||
|
<el-input v-model="token" placeholder="在此输入口令">
|
||||||
|
<template #prefix>
|
||||||
|
<el-icon class="el-input__icon">
|
||||||
|
<Lock/>
|
||||||
|
</el-icon>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
<el-button type="primary" @click="submitToken">提交</el-button>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -54,34 +79,63 @@ import ChatPrompt from "@/components/ChatPrompt.vue";
|
|||||||
import ChatReply from "@/components/ChatReply.vue";
|
import ChatReply from "@/components/ChatReply.vue";
|
||||||
import {randString} from "@/utils/libs";
|
import {randString} from "@/utils/libs";
|
||||||
import {ElMessage, ElMessageBox} from 'element-plus'
|
import {ElMessage, ElMessageBox} from 'element-plus'
|
||||||
import {Tools} from '@element-plus/icons-vue'
|
import {Tools, Lock} from '@element-plus/icons-vue'
|
||||||
import ConfigDialog from '@/components/ConfigDialog.vue'
|
import ConfigDialog from '@/components/ConfigDialog.vue'
|
||||||
|
import {httpPost} from "@/utils/http";
|
||||||
|
import {getSessionId, setSessionId} from "@/utils/storage";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "XChat",
|
name: "XChat",
|
||||||
components: {ChatPrompt, ChatReply, Tools, ConfigDialog},
|
components: {ChatPrompt, ChatReply, Tools, Lock, ConfigDialog},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
title: "ChatGPT 控制台",
|
title: 'ChatGPT 控制台',
|
||||||
|
logo: 'images/logo.png',
|
||||||
chatData: [],
|
chatData: [],
|
||||||
inputValue: '',
|
inputValue: '',
|
||||||
chatBoxHeight: 0,
|
chatBoxHeight: 0,
|
||||||
showDialog: false,
|
showConnectDialog: false,
|
||||||
|
showLoginDialog: false,
|
||||||
|
token: '',
|
||||||
|
|
||||||
connectingMessageBox: null,
|
connectingMessageBox: null,
|
||||||
socket: null,
|
socket: null,
|
||||||
sending: false
|
toolBoxHeight: 61 + 42,
|
||||||
|
sending: false,
|
||||||
|
loading: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {},
|
|
||||||
|
|
||||||
mounted: function () {
|
mounted: function () {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
this.chatBoxHeight = window.innerHeight - 61;
|
this.chatBoxHeight = window.innerHeight - this.toolBoxHeight;
|
||||||
})
|
})
|
||||||
this.connect();
|
|
||||||
|
|
||||||
|
// 获取会话
|
||||||
|
httpPost("/api/session/get").then(() => {
|
||||||
|
this.connect();
|
||||||
|
}).catch(() => {
|
||||||
|
this.showLoginDialog = true;
|
||||||
|
})
|
||||||
|
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
this.chatData.push({
|
||||||
|
type: "prompt",
|
||||||
|
id: randString(32),
|
||||||
|
icon: 'images/user-icon.png',
|
||||||
|
content: "孙悟空为什么可以把金棍棒放进耳朵?",
|
||||||
|
});
|
||||||
|
this.chatData.push({
|
||||||
|
type: "reply",
|
||||||
|
id: randString(32),
|
||||||
|
icon: 'images/gpt-icon.png',
|
||||||
|
content: "孙悟空是中国神话中的人物,传说中他可以把金箍棒放进耳朵里,这是一种超自然能力,无法用现代科学解释。这种能力可能是象征孙悟空超人力量的古代文化传说。",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener("resize", () => {
|
||||||
|
this.chatBoxHeight = window.innerHeight - this.toolBoxHeight;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@ -91,7 +145,8 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 初始化 WebSocket 对象
|
// 初始化 WebSocket 对象
|
||||||
const socket = new WebSocket(process.env.VUE_APP_WS_HOST + '/api/chat');
|
const token = getSessionId();
|
||||||
|
const socket = new WebSocket('ws://' + process.env.VUE_APP_API_HOST + '/api/chat', [token]);
|
||||||
socket.addEventListener('open', () => {
|
socket.addEventListener('open', () => {
|
||||||
ElMessage.success('创建会话成功!');
|
ElMessage.success('创建会话成功!');
|
||||||
|
|
||||||
@ -122,6 +177,9 @@ export default defineComponent({
|
|||||||
let content = data.content;
|
let content = data.content;
|
||||||
// 替换换行符
|
// 替换换行符
|
||||||
if (content.indexOf("\n\n") >= 0) {
|
if (content.indexOf("\n\n") >= 0) {
|
||||||
|
if (this.chatData[this.chatData.length - 1]["content"].length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
content = content.replace("\n\n", "<br />");
|
content = content.replace("\n\n", "<br />");
|
||||||
}
|
}
|
||||||
this.chatData[this.chatData.length - 1]["content"] += content;
|
this.chatData[this.chatData.length - 1]["content"] += content;
|
||||||
@ -182,7 +240,6 @@ export default defineComponent({
|
|||||||
content: this.inputValue
|
content: this.inputValue
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: 使用 websocket 提交数据到后端
|
|
||||||
this.sending = true;
|
this.sending = true;
|
||||||
this.socket.send(this.inputValue);
|
this.socket.send(this.inputValue);
|
||||||
this.$refs["text-input"].blur();
|
this.$refs["text-input"].blur();
|
||||||
@ -199,6 +256,26 @@ export default defineComponent({
|
|||||||
}, 200)
|
}, 200)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 提交 Token
|
||||||
|
submitToken: function () {
|
||||||
|
this.showLoginDialog = false;
|
||||||
|
this.loading = true
|
||||||
|
|
||||||
|
// 获取会话
|
||||||
|
httpPost("/api/login", {
|
||||||
|
token: this.token
|
||||||
|
}).then((res) => {
|
||||||
|
setSessionId(res.data)
|
||||||
|
this.connect();
|
||||||
|
this.loading = false;
|
||||||
|
}).catch(() => {
|
||||||
|
ElMessage.error("口令错误");
|
||||||
|
this.token = '';
|
||||||
|
this.showLoginDialog = true;
|
||||||
|
this.loading = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
})
|
})
|
||||||
@ -211,7 +288,7 @@ export default defineComponent({
|
|||||||
.body {
|
.body {
|
||||||
background-color: rgba(247, 247, 248, 1);
|
background-color: rgba(247, 247, 248, 1);
|
||||||
display flex;
|
display flex;
|
||||||
justify-content center;
|
//justify-content center;
|
||||||
align-items flex-start;
|
align-items flex-start;
|
||||||
height 100%;
|
height 100%;
|
||||||
|
|
||||||
@ -219,13 +296,24 @@ export default defineComponent({
|
|||||||
overflow auto;
|
overflow auto;
|
||||||
width 100%;
|
width 100%;
|
||||||
|
|
||||||
|
.tool-box {
|
||||||
|
padding-top 10px;
|
||||||
|
display flex;
|
||||||
|
justify-content center;
|
||||||
|
align-items center;
|
||||||
|
|
||||||
|
.el-image {
|
||||||
|
margin-right 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.chat-box {
|
.chat-box {
|
||||||
// 变量定义
|
// 变量定义
|
||||||
--content-font-size: 16px;
|
--content-font-size: 16px;
|
||||||
--content-color: #374151;
|
--content-color: #374151;
|
||||||
|
|
||||||
font-family 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
font-family 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
||||||
padding: 20px 10px;
|
padding: 0 10px 10px 10px;
|
||||||
|
|
||||||
.chat-line {
|
.chat-line {
|
||||||
padding 10px;
|
padding 10px;
|
||||||
@ -304,10 +392,27 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
.el-message {
|
.el-message {
|
||||||
width 90%;
|
min-width: 100px;
|
||||||
min-width: 300px;
|
|
||||||
max-width 600px;
|
max-width 600px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.token-dialog {
|
||||||
|
.el-dialog {
|
||||||
|
--el-dialog-width 90%;
|
||||||
|
max-width 400px;
|
||||||
|
|
||||||
|
.el-dialog__body {
|
||||||
|
padding 10px 10px 20px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-row {
|
||||||
|
flex-wrap nowrap
|
||||||
|
|
||||||
|
button {
|
||||||
|
margin-left 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
Reference in New Issue
Block a user