mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 16:56:38 +08:00
完成聊天 websocket API 对接
This commit is contained in:
parent
c25cc97450
commit
9477b96629
@ -1,11 +1,34 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) Chat(c *gin.Context) {
|
func (s *Server) Chat(c *gin.Context) {
|
||||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": fmt.Sprintf("HELLO, ChatGPT !!!")})
|
ws, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer, c.Request, nil)
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.Infof("New websocket connected, IP: %s", c.Request.RemoteAddr)
|
||||||
|
client := NewWsClient(ws)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
_, message, err := client.Receive()
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
client.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: 接受消息,调用 ChatGPT 返回消息
|
||||||
|
logger.Info(string(message))
|
||||||
|
err = client.Send(message)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
@ -14,22 +14,18 @@ type Client interface {
|
|||||||
|
|
||||||
// WsClient websocket client
|
// WsClient websocket client
|
||||||
type WsClient struct {
|
type WsClient struct {
|
||||||
NodeId string
|
Conn *websocket.Conn
|
||||||
SessionId string
|
lock sync.Mutex
|
||||||
Conn *websocket.Conn
|
mt int
|
||||||
lock sync.Mutex
|
closed bool
|
||||||
mt int
|
|
||||||
closed bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewWsClient(nodeId string, sessionId string, conn *websocket.Conn) *WsClient {
|
func NewWsClient(conn *websocket.Conn) *WsClient {
|
||||||
return &WsClient{
|
return &WsClient{
|
||||||
NodeId: nodeId,
|
Conn: conn,
|
||||||
SessionId: sessionId,
|
lock: sync.Mutex{},
|
||||||
Conn: conn,
|
mt: 2, // fixed bug for 'Invalid UTF-8 in text frame'
|
||||||
lock: sync.Mutex{},
|
closed: false,
|
||||||
mt: 2, // fixed bug for 'Invalid UTF-8 in text frame'
|
|
||||||
closed: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
7
web/src/assets/css/bootstrap.min.css
vendored
Normal file
7
web/src/assets/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -6,6 +6,7 @@ import App from './App.vue'
|
|||||||
import Home from './views/Chat.vue'
|
import Home 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"
|
||||||
|
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
import axios from 'axios'
|
|
||||||
import JSONBigInt from 'json-bigint'
|
|
||||||
import qs from 'qs'
|
|
||||||
|
|
||||||
import { ElMessageBox } from 'element-plus'
|
|
||||||
|
|
||||||
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/x-www-form-urlencoded'
|
|
||||||
axios.defaults.transformResponse = [(data, headers) => {
|
|
||||||
if (headers['content-type'].indexOf('application/json') !== -1) {
|
|
||||||
try {
|
|
||||||
data = JSONBigInt.parse(data)
|
|
||||||
} catch (e) { /* Ignore */ }
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}]
|
|
||||||
|
|
||||||
|
|
||||||
// HTTP拦截器
|
|
||||||
axios.interceptors.request.use(
|
|
||||||
config => {
|
|
||||||
// set session-name
|
|
||||||
config.headers['Session-Name'] = "xwebssh-sess-token"
|
|
||||||
return config
|
|
||||||
}, error => {
|
|
||||||
return Promise.reject(error)
|
|
||||||
})
|
|
||||||
axios.interceptors.response.use(
|
|
||||||
response => {
|
|
||||||
if (response.data.code == 0) {
|
|
||||||
return response
|
|
||||||
} else {
|
|
||||||
return Promise.reject(response.data)
|
|
||||||
}
|
|
||||||
}, error => {
|
|
||||||
if (error.response.status === 401) {
|
|
||||||
ElMessageBox.alert('您未登录或者登录已退出,请先登录再操作。', '登录提醒', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
callback: () => {
|
|
||||||
// TODO: goto login page
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (error.response.status === 400) {
|
|
||||||
return Promise.reject(error.response.data)
|
|
||||||
}
|
|
||||||
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, qs.stringify(data), options).then(response => {
|
|
||||||
resolve(response.data)
|
|
||||||
}).catch(err => {
|
|
||||||
reject(err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
/* eslint-disable no-constant-condition */
|
|
||||||
/**
|
|
||||||
* storage handler
|
|
||||||
*/
|
|
||||||
// import Storage from 'good-storage'
|
|
||||||
|
|
||||||
|
|
@ -16,13 +16,17 @@
|
|||||||
<div class="input-box" :style="{width: inputBoxWidth+'px'}" id="input-box">
|
<div class="input-box" :style="{width: inputBoxWidth+'px'}" id="input-box">
|
||||||
<div class="input-container">
|
<div class="input-container">
|
||||||
<textarea class="input-text" id="input-text" rows="1" :style="{minHeight:'24px', height: textHeight+'px'}"
|
<textarea class="input-text" id="input-text" rows="1" :style="{minHeight:'24px', height: textHeight+'px'}"
|
||||||
v-on:keyup="inputKeyUp"
|
v-on:keydown="inputKeyDown"
|
||||||
v-model="inputValue"
|
v-model="inputValue"
|
||||||
placeholder="Input any thing here..."
|
placeholder="Input any thing here..."
|
||||||
autofocus></textarea>
|
autofocus></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-container">
|
<div class="btn-container">
|
||||||
<button type="button">发送</button>
|
<button type="button"
|
||||||
|
class="btn btn-success"
|
||||||
|
:disabled="sending"
|
||||||
|
v-on:click="sendMessage">发送
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -60,7 +64,10 @@ export default defineComponent({
|
|||||||
textHeight: 24,
|
textHeight: 24,
|
||||||
textWidth: 0,
|
textWidth: 0,
|
||||||
chatBoxHeight: 0,
|
chatBoxHeight: 0,
|
||||||
isMobile: false
|
isMobile: false,
|
||||||
|
|
||||||
|
socket: null,
|
||||||
|
sending: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -81,6 +88,37 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
|
|
||||||
window.addEventListener('resize', this.windowResize);
|
window.addEventListener('resize', this.windowResize);
|
||||||
|
|
||||||
|
// 初始化 WebSocket 对象
|
||||||
|
const socket = new WebSocket('ws://172.22.11.200:5678/api/chat');
|
||||||
|
socket.addEventListener('open', () => {
|
||||||
|
console.log('WebSocket 连接已打开');
|
||||||
|
});
|
||||||
|
socket.addEventListener('message', event => {
|
||||||
|
if (event.data instanceof Blob) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.readAsText(event.data, "UTF-8");
|
||||||
|
reader.onload = () => {
|
||||||
|
this.chatData.push({
|
||||||
|
type: "reply",
|
||||||
|
id: randString(32),
|
||||||
|
icon: 'images/gpt-icon.png',
|
||||||
|
content: reader.result
|
||||||
|
});
|
||||||
|
this.sending = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
socket.addEventListener('close', event => {
|
||||||
|
console.log('WebSocket 连接已关闭', event.reason);
|
||||||
|
});
|
||||||
|
socket.addEventListener('error', event => {
|
||||||
|
console.error('WebSocket 连接发生错误', event);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket = socket;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeUnmount() {
|
beforeUnmount() {
|
||||||
@ -88,22 +126,38 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
inputKeyUp: function (e) {
|
inputKeyDown: function (e) {
|
||||||
// PC 端按回车键直接提交数据
|
// PC 端按回车键直接提交数据
|
||||||
if (e.keyCode === 13 && !this.isMobile) {
|
if (e.keyCode === 13 && !this.isMobile) {
|
||||||
this.chatData.push({
|
e.stopPropagation();
|
||||||
type: "prompt",
|
e.preventDefault();
|
||||||
id: randString(32),
|
return this.sendMessage();
|
||||||
icon: 'images/user-icon.png',
|
|
||||||
content: this.inputValue
|
|
||||||
});
|
|
||||||
this.inputValue = '';
|
|
||||||
console.log("提交数据")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.inputResize();
|
this.inputResize();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 发送消息
|
||||||
|
sendMessage: function () {
|
||||||
|
if (this.sending) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 追加消息
|
||||||
|
this.chatData.push({
|
||||||
|
type: "prompt",
|
||||||
|
id: randString(32),
|
||||||
|
icon: 'images/user-icon.png',
|
||||||
|
content: this.inputValue
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: 使用 websocket 提交数据到后端
|
||||||
|
this.sending = true;
|
||||||
|
this.socket.send(this.inputValue);
|
||||||
|
this.inputValue = '';
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
inputResize: function () {
|
inputResize: function () {
|
||||||
// 根据输入的字数自动调整输入框的大小
|
// 根据输入的字数自动调整输入框的大小
|
||||||
@ -205,21 +259,6 @@ export default defineComponent({
|
|||||||
|
|
||||||
button {
|
button {
|
||||||
width 70px;
|
width 70px;
|
||||||
outline none
|
|
||||||
border none
|
|
||||||
cursor pointer
|
|
||||||
background-color: #07c160
|
|
||||||
position: relative;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
font-size 16px
|
|
||||||
box-sizing: border-box;
|
|
||||||
font-weight: 700;
|
|
||||||
text-align: center;
|
|
||||||
text-decoration: none;
|
|
||||||
color: #fff;
|
|
||||||
line-height: 2.25;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user