From 2b91bbe185cb668bea69830284003225a640b4af Mon Sep 17 00:00:00 2001 From: "meilin.huang" <954537473@qq.com> Date: Thu, 19 Oct 2023 19:00:23 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20websocket=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=8D=95=E7=94=A8=E6=88=B7=E5=A4=9A=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 14 +- mayfly_go_web/package.json | 2 +- mayfly_go_web/src/common/request.ts | 9 +- mayfly_go_web/src/common/sockets.ts | 76 ---------- mayfly_go_web/src/common/syssocket.ts | 99 +++++++++++++ mayfly_go_web/src/common/utils/storage.ts | 19 +-- mayfly_go_web/src/common/utils/string.ts | 10 ++ mayfly_go_web/src/router/index.ts | 13 +- mayfly_go_web/src/views/ops/db/DbEdit.vue | 2 + mayfly_go_web/src/views/ops/db/DbList.vue | 6 +- mayfly_go_web/src/views/ops/db/SqlExec.vue | 3 + .../src/views/ops/db/component/tab/Query.vue | 49 ++++++- mayfly_go_web/src/views/ops/db/db.ts | 2 - .../src/views/ops/db/table/DbTableList.vue | 11 +- mayfly_go_web/src/views/ops/machine/api.ts | 2 +- .../views/ops/machine/file/MachineFile.vue | 3 +- mayfly_go_web/src/views/personal/index.vue | 2 +- mayfly_go_web/yarn.lock | 8 +- server/internal/auth/api/account_login.go | 2 +- server/internal/db/api/db.go | 61 +++----- .../internal/msg/application/dto/sys_msg.go | 7 + server/internal/msg/application/msg.go | 2 +- server/internal/sys/api/system.go | 11 +- server/pkg/model/login_account.go | 3 - server/pkg/req/permission_handler.go | 10 +- server/pkg/utils/collx/array.go | 11 ++ server/pkg/utils/uniqueid/uniqueid.go | 9 -- server/pkg/ws/client.go | 27 ++-- server/pkg/ws/client_manager.go | 133 ++++++++++++------ server/pkg/ws/msg.go | 8 +- server/pkg/ws/ws.go | 14 +- 31 files changed, 365 insertions(+), 263 deletions(-) delete mode 100644 mayfly_go_web/src/common/sockets.ts create mode 100644 mayfly_go_web/src/common/syssocket.ts delete mode 100644 server/pkg/utils/uniqueid/uniqueid.go diff --git a/README.md b/README.md index 957733f7..753ec6c1 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # 🌈mayfly-go

- - star - fork + + star + fork - - github star - github fork + + github star + github fork docker pulls @@ -100,4 +100,4 @@ http://go.mayfly.run #### 💌 支持作者 -如果觉得项目不错,或者已经在使用了,希望你可以去 Github 或者 Gitee 帮我点个 ⭐ Star,这将是对我极大的鼓励与支持。 +如果觉得项目不错,或者已经在使用了,希望你可以去 Github 或者 Gitee 帮我点个 ⭐ Star,这将是对我极大的鼓励与支持。 diff --git a/mayfly_go_web/package.json b/mayfly_go_web/package.json index d6a5df74..d1e74208 100644 --- a/mayfly_go_web/package.json +++ b/mayfly_go_web/package.json @@ -15,7 +15,7 @@ "countup.js": "^2.7.0", "cropperjs": "^1.5.11", "echarts": "^5.4.0", - "element-plus": "^2.4.0", + "element-plus": "^2.4.1", "jsencrypt": "^3.3.1", "lodash": "^4.17.21", "mitt": "^3.0.1", diff --git a/mayfly_go_web/src/common/request.ts b/mayfly_go_web/src/common/request.ts index f026f932..370ab98c 100755 --- a/mayfly_go_web/src/common/request.ts +++ b/mayfly_go_web/src/common/request.ts @@ -1,7 +1,7 @@ import router from '../router'; import Axios from 'axios'; import config from './config'; -import { getClientUuid, getToken, joinClientParams } from './utils/storage'; +import { getClientId, getToken } from './utils/storage'; import { templateResolve } from './utils/string'; import { ElMessage } from 'element-plus'; @@ -54,7 +54,7 @@ service.interceptors.request.use( if (token) { // 设置token config.headers['Authorization'] = token; - config.headers['Client-Uuid'] = getClientUuid(); + config.headers['ClientId'] = getClientId(); } return config; }, @@ -180,6 +180,11 @@ function getApiUrl(url: string) { return baseUrl + url + '?' + joinClientParams(); } +// 组装客户端参数,包括 token 和 clientId +export function joinClientParams(): string { + return `token=${getToken()}&clientId=${getClientId()}`; +} + export default { request, get, diff --git a/mayfly_go_web/src/common/sockets.ts b/mayfly_go_web/src/common/sockets.ts deleted file mode 100644 index 4ccda632..00000000 --- a/mayfly_go_web/src/common/sockets.ts +++ /dev/null @@ -1,76 +0,0 @@ -import Config from './config'; -import { ElNotification, NotificationHandle } from 'element-plus'; -import SocketBuilder from './SocketBuilder'; -import { getToken, joinClientParams } from '@/common/utils/storage'; -import { createVNode, reactive } from 'vue'; -import { buildProgressProps } from '@/components/progress-notify/progress-notify'; -import ProgressNotify from '/src/components/progress-notify/progress-notify.vue'; - -export default { - /** - * 全局系统消息websocket - */ - sysMsgSocket() { - const token = getToken(); - if (!token) { - return null; - } - const messageTypes = { - 0: 'error', - 1: 'success', - 2: 'info', - }; - const notifyMap: Map = new Map(); - const sysMsgUrl = `${Config.baseWsUrl}/sysmsg?${joinClientParams()}`; - - return SocketBuilder.builder(sysMsgUrl) - .message((event: { data: string }) => { - const message = JSON.parse(event.data); - const type = messageTypes[message.type]; - switch (message.category) { - case 'execSqlFileProgress': - const content = JSON.parse(message.msg); - const id = content.id; - let progress = notifyMap.get(id); - if (content.terminated) { - if (progress != undefined) { - progress.notification?.close(); - notifyMap.delete(id); - progress = undefined; - } - return; - } - if (progress == undefined) { - progress = { - props: reactive(buildProgressProps()), - notification: undefined, - }; - } - progress.props.progress.title = content.title; - progress.props.progress.executedStatements = content.executedStatements; - if (!notifyMap.has(id)) { - const vNodeMessage = createVNode(ProgressNotify, progress.props, null); - progress.notification = ElNotification({ - duration: 0, - title: message.title, - message: vNodeMessage, - type: type, - showClose: false, - }); - notifyMap.set(id, progress); - } - break; - default: - ElNotification({ - duration: 0, - title: message.title, - message: message.msg, - type: type, - }); - break; - } - }) - .open((event: any) => console.log(event)) - .build(); - }, -}; diff --git a/mayfly_go_web/src/common/syssocket.ts b/mayfly_go_web/src/common/syssocket.ts new file mode 100644 index 00000000..504b2762 --- /dev/null +++ b/mayfly_go_web/src/common/syssocket.ts @@ -0,0 +1,99 @@ +import Config from './config'; +import { ElNotification } from 'element-plus'; +import SocketBuilder from './SocketBuilder'; +import { getToken } from '@/common/utils/storage'; + +import { joinClientParams } from './request'; + +class SysSocket { + /** + * socket连接 + */ + socket: any; + + /** + * key -> 消息类别,value -> 消息对应的处理器函数 + */ + categoryHandlers: Map = new Map(); + + /** + * 消息类型 + */ + messageTypes = { + 0: 'error', + 1: 'success', + 2: 'info', + }; + + /** + * 初始化全局系统消息websocket + */ + init() { + // 存在则不需要重新建立连接 + if (this.socket) { + return; + } + const token = getToken(); + if (!token) { + return null; + } + + const sysMsgUrl = `${Config.baseWsUrl}/sysmsg?${joinClientParams()}`; + this.socket = SocketBuilder.builder(sysMsgUrl) + .message((event: { data: string }) => { + const message = JSON.parse(event.data); + // 存在消息类别对应的处理器,则进行处理,否则进行默认通知处理 + const handler = this.categoryHandlers.get(message.category); + if (handler) { + handler(message); + return; + } + + const type = this.getMsgType(message.type); + ElNotification({ + duration: 0, + title: message.title, + message: message.msg, + type: type, + }); + }) + .open((event: any) => console.log(event)) + .close(() => { + console.log('close sys socket'); + this.socket = null; + }) + .build(); + } + + destory() { + this.socket.close(); + this.socket = null; + this.categoryHandlers.clear(); + } + + /** + * 注册消息处理函数 + * + * @param category 消息类别 + * @param handlerFunc 消息处理函数 + */ + registerMsgHandler(category: any, handlerFunc: any) { + if (this.categoryHandlers.has(category)) { + console.log(`${category}该类别消息处理器已存在...`); + return; + } + if (typeof handlerFunc != 'function') { + throw new Error('message handler需为函数'); + } + this.categoryHandlers.set(category, handlerFunc); + } + + getMsgType(msgType: any) { + return this.messageTypes[msgType]; + } +} + +// 全局系统消息websocket; +const sysSocket = new SysSocket(); + +export default sysSocket; diff --git a/mayfly_go_web/src/common/utils/storage.ts b/mayfly_go_web/src/common/utils/storage.ts index 6e8739fc..ff9275dd 100644 --- a/mayfly_go_web/src/common/utils/storage.ts +++ b/mayfly_go_web/src/common/utils/storage.ts @@ -1,9 +1,9 @@ -import { v1 as uuidv1 } from 'uuid'; +import { randomUuid } from './string'; const TokenKey = 'token'; const UserKey = 'user'; const TagViewsKey = 'tagViews'; -const ClientUuid = 'clientUuid' +const ClientIdKey = 'clientId'; // 获取请求token export function getToken(): string { @@ -52,18 +52,13 @@ export function removeTagViews() { } // 获取客户端UUID -export function getClientUuid(): string { - let uuid = getSession(ClientUuid) +export function getClientId(): string { + let uuid = getSession(ClientIdKey); if (uuid == null) { - uuid = uuidv1() - setSession(ClientUuid, uuid) + uuid = randomUuid(); + setSession(ClientIdKey, uuid); } - return uuid -} - -// 组装客户端参数,包括 token 和 clientUuid -export function joinClientParams(): string { - return `token=${getToken()}&clientUuid=${getClientUuid()}` + return uuid; } // 1. localStorage diff --git a/mayfly_go_web/src/common/utils/string.ts b/mayfly_go_web/src/common/utils/string.ts index b0405b25..49e97af4 100644 --- a/mayfly_go_web/src/common/utils/string.ts +++ b/mayfly_go_web/src/common/utils/string.ts @@ -1,3 +1,5 @@ +import { v1 as uuidv1 } from 'uuid'; + /** * 模板字符串解析,如:template = 'hahaha{name}_{id}' ,param = {name: 'hh', id: 1} * 解析后为 hahahahh_1 @@ -129,3 +131,11 @@ export function getContentWidth(content: any): number { // } return flexWidth; } + +/** + * + * @returns uuid + */ +export function randomUuid() { + return uuidv1(); +} diff --git a/mayfly_go_web/src/router/index.ts b/mayfly_go_web/src/router/index.ts index 74f70f50..f216f6b7 100644 --- a/mayfly_go_web/src/router/index.ts +++ b/mayfly_go_web/src/router/index.ts @@ -6,7 +6,7 @@ import { templateResolve } from '@/common/utils/string'; import { NextLoading } from '@/common/utils/loading'; import { dynamicRoutes, staticRoutes, pathMatch } from './route'; import openApi from '@/common/openApi'; -import sockets from '@/common/sockets'; +import syssocket from '@/common/syssocket'; import pinia from '@/store/index'; import { useThemeConfig } from '@/store/themeConfig'; import { useUserInfo } from '@/store/userInfo'; @@ -179,7 +179,6 @@ export async function initRouter() { } } -let SysWs: any; let loadRouter = false; // 路由加载前 @@ -204,10 +203,7 @@ router.beforeEach(async (to, from, next) => { resetRoute(); NProgress.done(); - if (SysWs) { - SysWs.close(); - SysWs = undefined; - } + syssocket.destory(); return; } if (token && to.path === '/login') { @@ -217,9 +213,10 @@ router.beforeEach(async (to, from, next) => { } // 终端不需要连接系统websocket消息 - if (!SysWs && to.path != '/machine/terminal') { - SysWs = sockets.sysMsgSocket(); + if (to.path != '/machine/terminal') { + syssocket.init(); } + // 不存在路由(避免刷新页面找不到路由)并且未加载过(避免token过期,导致获取权限接口报权限不足,无限获取),则重新初始化路由 if (useRoutesList().routesList.length == 0 && !loadRouter) { await initRouter(); diff --git a/mayfly_go_web/src/views/ops/db/DbEdit.vue b/mayfly_go_web/src/views/ops/db/DbEdit.vue index cbb3a725..dbc6404e 100644 --- a/mayfly_go_web/src/views/ops/db/DbEdit.vue +++ b/mayfly_go_web/src/views/ops/db/DbEdit.vue @@ -171,6 +171,8 @@ const changeDatabase = () => { }; const getAllDatabase = async () => { + // 清空数据库列表,可能已经有选择库了 + state.databaseList = []; if (state.form.instanceId > 0) { state.allDatabases = await dbApi.getAllDatabase.request({ instanceId: state.form.instanceId }); } diff --git a/mayfly_go_web/src/views/ops/db/DbList.vue b/mayfly_go_web/src/views/ops/db/DbList.vue index dfbc1389..47c0b95c 100644 --- a/mayfly_go_web/src/views/ops/db/DbList.vue +++ b/mayfly_go_web/src/views/ops/db/DbList.vue @@ -172,7 +172,7 @@ import { ref, toRefs, reactive, onMounted, defineAsyncComponent } from 'vue'; import { ElMessage, ElMessageBox } from 'element-plus'; import { dbApi } from './api'; import config from '@/common/config'; -import { joinClientParams } from '@/common/utils/storage'; +import { joinClientParams } from '@/common/request'; import { isTrue } from '@/common/assert'; import { Search as SearchIcon } from '@element-plus/icons-vue'; import { dateFormat } from '@/common/utils/date'; @@ -355,7 +355,9 @@ const deleteDb = async () => { await dbApi.deleteDb.request({ id: state.selectionData.map((x: any) => x.id).join(',') }); ElMessage.success('删除成功'); search(); - } catch (err) {} + } catch (err) { + // + } }; const onShowSqlExec = async (row: any) => { diff --git a/mayfly_go_web/src/views/ops/db/SqlExec.vue b/mayfly_go_web/src/views/ops/db/SqlExec.vue index 4f773f8f..1f010deb 100644 --- a/mayfly_go_web/src/views/ops/db/SqlExec.vue +++ b/mayfly_go_web/src/views/ops/db/SqlExec.vue @@ -387,6 +387,9 @@ const addQueryTab = async (inst: any, db: string, sqlName: string = '') => { dbs: inst.dbs, }; state.tabs.set(label, tab); + + // 注册当前sql编辑框提示词 + registerDbCompletionItemProvider('sql', tab.dbId, tab.db, tab.params.dbs); }; const onRemoveTab = (targetName: string) => { diff --git a/mayfly_go_web/src/views/ops/db/component/tab/Query.vue b/mayfly_go_web/src/views/ops/db/component/tab/Query.vue index 998d71cb..9be9e50b 100644 --- a/mayfly_go_web/src/views/ops/db/component/tab/Query.vue +++ b/mayfly_go_web/src/views/ops/db/component/tab/Query.vue @@ -88,7 +88,7 @@