mirror of
https://gitee.com/dromara/mayfly-go
synced 2026-01-27 16:05:47 +08:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
400db0402a | ||
|
|
f0ae178183 | ||
|
|
4641e448d2 | ||
|
|
f0de65b7ce | ||
|
|
0472c5101f | ||
|
|
185cd6f82b | ||
|
|
aa6ad39b83 |
@@ -57,7 +57,7 @@ function build() {
|
||||
execFileName="${execFileName}.exe"
|
||||
fi
|
||||
go mod tidy
|
||||
CGO_ENABLE=0 GOOS=${os} GOARCH=${arch} go build -ldflags=-w -o ${execFileName} main.go
|
||||
CGO_ENABLE=0 GOOS=${os} GOARCH=${arch} go build -trimpath -ldflags=-w -o ${execFileName} main.go
|
||||
|
||||
if [ -d ${toFolder} ] ; then
|
||||
echo_green "The desired folder already exists. Clear the folder"
|
||||
|
||||
@@ -11,20 +11,20 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"@logicflow/core": "^2.1.4",
|
||||
"@logicflow/extension": "^2.1.6",
|
||||
"@logicflow/core": "^2.1.7",
|
||||
"@logicflow/extension": "^2.1.9",
|
||||
"@vueuse/core": "^14.1.0",
|
||||
"@xterm/addon-fit": "^0.11.0",
|
||||
"@xterm/addon-search": "^0.16.0",
|
||||
"@xterm/addon-web-links": "^0.12.0",
|
||||
"@xterm/xterm": "^6.0.0",
|
||||
"asciinema-player": "^3.13.5",
|
||||
"asciinema-player": "^3.14.0",
|
||||
"axios": "^1.6.2",
|
||||
"clipboard": "^2.0.11",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.19",
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "^2.13.0",
|
||||
"element-plus": "^2.13.1",
|
||||
"js-base64": "^3.7.8",
|
||||
"jsencrypt": "^3.5.4",
|
||||
"monaco-editor": "^0.55.1",
|
||||
@@ -37,8 +37,8 @@
|
||||
"sql-formatter": "^15.6.12",
|
||||
"trzsz": "^1.1.5",
|
||||
"uuid": "^13.0.0",
|
||||
"vue": "^v3.6.0-beta.1",
|
||||
"vue-i18n": "^11.2.7",
|
||||
"vue": "^v3.6.0-beta.2",
|
||||
"vue-i18n": "^11.2.8",
|
||||
"vue-router": "^4.6.4",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"xlsx": "^0.18.5"
|
||||
|
||||
@@ -13,9 +13,6 @@ export function getBaseApiUrl() {
|
||||
const config = {
|
||||
baseApiUrl: `${(window as any).globalConfig.BaseApiUrl || location.protocol + '//' + getBaseApiUrl()}/api`,
|
||||
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
|
||||
|
||||
// 系统版本
|
||||
version: 'v1.10.4',
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
@@ -169,6 +169,7 @@ export default {
|
||||
transfer2Db: 'Transfer to DB',
|
||||
transfer2File: 'Transfer to File',
|
||||
fileSaveDays: 'File retention days',
|
||||
fileType: 'File Type',
|
||||
transferStrategy: 'Transfer Strategy',
|
||||
day: 'Day',
|
||||
transferFull: 'Full',
|
||||
|
||||
@@ -47,10 +47,12 @@ export default {
|
||||
machineSecurityCmdSvae: 'Cmd Config-Save',
|
||||
machineSecurityCmdDelete: 'Cmd Config-Delete',
|
||||
|
||||
db: 'Database',
|
||||
dbms: 'DBMS',
|
||||
dbDataOp: 'Data Operation',
|
||||
dbDataOpBase: 'Base Permission',
|
||||
dbDataOpSqlScriptRun: 'SQL Script Run',
|
||||
dbDataOpBase: 'DB-Base Permission',
|
||||
dbDataOpSqlScriptRun: 'DB-SQL Script Run',
|
||||
dbDataExport: 'DB-Data Export',
|
||||
dbInstance: 'DB Instance',
|
||||
dbInstanceBase: 'Base Permission',
|
||||
dbInstanceSave: 'Save Instance',
|
||||
@@ -77,22 +79,28 @@ export default {
|
||||
dbTransferFileRun: 'Transfer File-Run',
|
||||
|
||||
redis: 'Redis',
|
||||
redisDataOp: 'Data Operation',
|
||||
redisDataOpBase: 'Base Permission',
|
||||
redisDataOpSave: 'Save Data',
|
||||
redisDataOpDelete: 'Delete Data',
|
||||
redisSave: 'Save Redis',
|
||||
redisDel: 'Delete Redis',
|
||||
redisDataOp: 'Redis - Data Operation',
|
||||
redisDataOpBase: 'Redis - Base Permission',
|
||||
redisDataOpSave: 'Redis - Save Data',
|
||||
redisDataOpDelete: 'Redis - Delete Data',
|
||||
redisManage: 'Redis Manage',
|
||||
redisManageBase: 'Base Permission',
|
||||
redisManageBase: 'Redis - Base Permission',
|
||||
|
||||
mongo: 'Mongo',
|
||||
mongoDataOp: 'Data Operation',
|
||||
mongoDataOpBase: 'Base Permission',
|
||||
mongoDataOpSave: 'Save Data',
|
||||
mongoDataOpDelete: 'Delete Data',
|
||||
mongoSave: 'Save Mongo',
|
||||
mongoDel: 'Delete Mongo',
|
||||
mongoDataOp: 'Mongo - Data Operation',
|
||||
mongoDataOpBase: 'Mongo - Base Permission',
|
||||
mongoDataOpSave: 'Mongo - Save Data',
|
||||
mongoDataOpDelete: 'Mongo - Delete Data',
|
||||
mongoManage: 'Mongo Manage',
|
||||
mongoManageBase: 'Base Permission',
|
||||
mongoManageBase: 'Mongo - Base Permission',
|
||||
|
||||
containerManageBase: 'Container Manage - Base Permission',
|
||||
container: 'Container',
|
||||
containerSave: 'Save Container',
|
||||
containerDel: 'Delete Container',
|
||||
|
||||
flow: 'Flow',
|
||||
myTask: 'My Task',
|
||||
|
||||
@@ -165,6 +165,7 @@ export default {
|
||||
transfer2Db: '迁移到数据库',
|
||||
transfer2File: '迁移到文件',
|
||||
fileSaveDays: '文件保留天数',
|
||||
fileType: '文件类型',
|
||||
transferStrategy: '迁移策略',
|
||||
day: '天',
|
||||
transferFull: '全量',
|
||||
|
||||
@@ -47,17 +47,19 @@ export default {
|
||||
machineSecurityCmdSvae: '机器-命令配置-保存',
|
||||
machineSecurityCmdDelete: '机器-命令配置-删除',
|
||||
|
||||
db: '数据库',
|
||||
dbms: 'DBMS',
|
||||
dbDataOp: '数据操作',
|
||||
dbDataOpBase: 'Db-数据操作-基本权限',
|
||||
dbDataOpSqlScriptRun: 'Db-SQL脚本执行',
|
||||
dbDataOp: 'DB-数据操作',
|
||||
dbDataOpBase: 'DB-数据操作-基本权限',
|
||||
dbDataOpSqlScriptRun: 'DB-SQL脚本执行',
|
||||
dbDataExport: 'DB-数据导出',
|
||||
dbInstance: '数据库实例',
|
||||
dbInstanceBase: 'Db-基本权限',
|
||||
dbInstanceSave: 'Db-保存实例',
|
||||
dbInstanceDelete: 'Db-删除实例',
|
||||
dbInstanceBase: 'DB-基本权限',
|
||||
dbInstanceSave: 'DB-保存实例',
|
||||
dbInstanceDelete: 'DB-删除实例',
|
||||
dbBase: '数据库基本权限',
|
||||
dbSave: 'Db-保存数据库',
|
||||
dbDelete: 'Db-删除数据库',
|
||||
dbSave: 'DB-保存数据库',
|
||||
dbDelete: 'DB-删除数据库',
|
||||
dbDataSync: '数据同步',
|
||||
dbDataSyncBase: '基本权限',
|
||||
dbDataSyncSave: '保存同步',
|
||||
@@ -77,6 +79,8 @@ export default {
|
||||
dbTransferFileRun: '迁移文件-执行',
|
||||
|
||||
redis: 'Redis',
|
||||
redisSave: 'Redis-保存',
|
||||
redisDel: 'Redis-删除',
|
||||
redisDataOp: 'Redis-数据操作',
|
||||
redisDataOpBase: 'Redis-数据操作-基本权限',
|
||||
redisDataOpSave: 'Redis-数据操作-数据保存',
|
||||
@@ -85,6 +89,8 @@ export default {
|
||||
redisManageBase: 'Redis-管理-基本权限',
|
||||
|
||||
mongo: 'Mongo',
|
||||
mongoSave: 'Mongo-保存',
|
||||
mongoDel: 'Mongo-删除',
|
||||
mongoDataOp: '数据操作',
|
||||
mongoDataOpBase: 'Mongo-数据操作-基本权限',
|
||||
mongoDataOpSave: 'Mongo-数据操作-数据保存',
|
||||
@@ -92,7 +98,9 @@ export default {
|
||||
mongoManage: 'Mongo管理',
|
||||
mongoManageBase: 'Mongo-管理-基本权限',
|
||||
|
||||
containerManageBase: '容器-管理-基本权限',
|
||||
container: '容器',
|
||||
containerSave: '容器-保存',
|
||||
containerDel: '容器-删除',
|
||||
|
||||
flow: '工单流程',
|
||||
myTask: '我的任务',
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<span class="logo-title">
|
||||
{{ `${themeConfig.globalTitle}` }}
|
||||
<sub
|
||||
><span style="font-size: 10px; color: goldenrod">{{ ` ${config.version}` }}</span></sub
|
||||
><span style="font-size: 10px; color: goldenrod">{{ ` ${themeConfig.version}` }}</span></sub
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
@@ -17,7 +17,6 @@
|
||||
import { computed } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
import config from '@/common/config';
|
||||
|
||||
const { themeConfig } = storeToRefs(useThemeConfig());
|
||||
|
||||
|
||||
@@ -308,17 +308,7 @@
|
||||
|
||||
<!-- 其它设置 -->
|
||||
<el-divider content-position="left">{{ $t('layout.config.otherSetting') }}</el-divider>
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('layout.config.tagsStyle') }}</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex-value">
|
||||
<el-select v-model="themeConfig.tagsStyle" placeholder="请选择" size="small" style="width: 90px">
|
||||
<el-option label="风格1" value="tags-style-one"></el-option>
|
||||
<el-option label="风格2" value="tags-style-two"></el-option>
|
||||
<el-option label="风格3" value="tags-style-three"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt-3.5!">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('layout.config.animation') }}</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex-value">
|
||||
<el-select v-model="themeConfig.animation" size="small" style="width: 90px">
|
||||
@@ -328,7 +318,7 @@
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="layout-breadcrumb-seting-bar-flex !mt-3.5 !mb-5.5">
|
||||
<div class="layout-breadcrumb-seting-bar-flex mt-3.5! mb-5.5!">
|
||||
<div class="layout-breadcrumb-seting-bar-flex-label">
|
||||
{{ $t('layout.config.columnsAsideStyle') }}
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="layout-navbars-tagsview" :class="{ 'layout-navbars-tagsview-shadow': themeConfig.layout === 'classic' }">
|
||||
<el-scrollbar ref="scrollbarRef" @wheel.prevent="onHandleScroll">
|
||||
<ul class="layout-navbars-tagsview-ul" :class="setTagsStyle" ref="tagsUlRef">
|
||||
<ul class="layout-navbars-tagsview-ul" ref="tagsUlRef">
|
||||
<li
|
||||
v-for="(v, k) in tagsViews"
|
||||
:key="k"
|
||||
@@ -18,26 +18,20 @@
|
||||
>
|
||||
<SvgIcon :name="v.icon" class="layout-navbars-tagsview-ul-li-iconfont" v-if="themeConfig.isTagsviewIcon" />
|
||||
<span>{{ $t(v.title) }}</span>
|
||||
|
||||
<template v-if="isActive(v)">
|
||||
<SvgIcon
|
||||
name="RefreshRight"
|
||||
class="!text-[14px] ml-1 layout-navbars-tagsview-ul-li-refresh"
|
||||
class="text-[14px]! ml-1 layout-navbars-tagsview-ul-li-icon layout-navbars-tagsview-ul-li-refresh"
|
||||
@click.stop="refreshCurrentTagsView($route.fullPath)"
|
||||
/>
|
||||
<SvgIcon
|
||||
name="Close"
|
||||
class="!text-[14px] layout-navbars-tagsview-ul-li-icon layout-icon-active"
|
||||
class="text-[14px]! layout-navbars-tagsview-ul-li-icon layout-navbars-tagsview-ul-li-close layout-icon-active"
|
||||
v-if="!v.isAffix"
|
||||
@click.stop="closeCurrentTagsView(themeConfig.isShareTagsView ? v.path : v.path)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<SvgIcon
|
||||
name="Close"
|
||||
class="!text-[14px] layout-navbars-tagsview-ul-li-icon layout-icon-three"
|
||||
v-if="!v.isAffix"
|
||||
@click.stop="closeCurrentTagsView(themeConfig.isShareTagsView ? v.path : v.path)"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</el-scrollbar>
|
||||
@@ -46,7 +40,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="layoutTagsView">
|
||||
import { reactive, onMounted, computed, ref, nextTick, onBeforeUpdate, getCurrentInstance, watch } from 'vue';
|
||||
import { reactive, onMounted, ref, nextTick, onBeforeUpdate, getCurrentInstance, watch } from 'vue';
|
||||
import { useRoute, useRouter, onBeforeRouteUpdate } from 'vue-router';
|
||||
import screenfull from 'screenfull';
|
||||
import { storeToRefs } from 'pinia';
|
||||
@@ -105,11 +99,6 @@ const state = reactive({
|
||||
},
|
||||
});
|
||||
|
||||
// 动态设置 tagsView 风格样式
|
||||
const setTagsStyle = computed(() => {
|
||||
return themeConfig.value.tagsStyle;
|
||||
});
|
||||
|
||||
// 存储 tagsViewList 到浏览器临时缓存中,页面刷新时,保留记录
|
||||
const addBrowserSetSession = (tagsViewList: Array<object>) => {
|
||||
setTagViews(tagsViewList);
|
||||
@@ -403,163 +392,120 @@ onBeforeRouteUpdate((to) => {
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
<style scoped lang="css">
|
||||
.layout-navbars-tagsview {
|
||||
background-color: var(--bg-main-color);
|
||||
border-bottom: 1px solid var(--el-border-color-light, #ebeef5);
|
||||
position: relative;
|
||||
z-index: 4;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
:deep(.el-scrollbar__wrap) {
|
||||
overflow-x: auto !important;
|
||||
}
|
||||
.layout-navbars-tagsview :deep(.el-scrollbar__wrap) {
|
||||
overflow-x: auto !important;
|
||||
}
|
||||
|
||||
&-ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 34px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--el-text-color-regular);
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
padding: 0 15px;
|
||||
.layout-navbars-tagsview-ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 38px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--el-text-color-regular);
|
||||
font-size: 13px;
|
||||
white-space: nowrap;
|
||||
padding: 0 15px;
|
||||
}
|
||||
|
||||
&-li {
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
padding: 0 15px;
|
||||
margin-right: 5px;
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
cursor: pointer;
|
||||
justify-content: space-between;
|
||||
.layout-navbars-tagsview-ul-li {
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
border-radius: 6px;
|
||||
padding: 0 12px;
|
||||
margin-right: 8px;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
cursor: pointer;
|
||||
justify-content: space-between;
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid var(--el-border-color, #dcdfe6);
|
||||
box-sizing: border-box;
|
||||
background-color: var(--el-bg-color, #fafafa);
|
||||
color: var(--el-text-color-regular, #606266);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
color: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary-light-5);
|
||||
}
|
||||
.layout-navbars-tagsview-ul-li:not(.is-active):hover {
|
||||
background-color: var(--el-fill-color-blank, #f5f7fa);
|
||||
color: var(--el-text-color-primary, #303133);
|
||||
border-color: var(--el-color-primary-light-7, #c6e2ff);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
&-iconfont {
|
||||
position: relative;
|
||||
left: -5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.layout-navbars-tagsview-ul-li-iconfont {
|
||||
position: relative;
|
||||
left: -3px;
|
||||
font-size: 12px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
border-radius: 100%;
|
||||
position: relative;
|
||||
height: 14px;
|
||||
width: 14px;
|
||||
text-align: center;
|
||||
line-height: 14px;
|
||||
right: -5px;
|
||||
.layout-navbars-tagsview-ul-li-icon {
|
||||
border-radius: 4px;
|
||||
position: relative;
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
text-align: center;
|
||||
line-height: 18px;
|
||||
right: -3px;
|
||||
margin-left: 4px;
|
||||
transition: all 0.25s ease;
|
||||
color: var(--el-text-color-secondary, #909399);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--el-color-white);
|
||||
background-color: var(--el-color-primary-light-3);
|
||||
}
|
||||
}
|
||||
.layout-navbars-tagsview-ul-li-icon:hover {
|
||||
background-color: var(--el-color-info-light-7);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.layout-icon-active {
|
||||
display: block;
|
||||
}
|
||||
.layout-icon-active {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.layout-icon-three {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.layout-navbars-tagsview-ul .is-active {
|
||||
color: var(--el-color-primary, #409eff);
|
||||
background: var(--el-color-primary-light-9, #ecf5ff);
|
||||
border-color: var(--el-color-primary-light-5, #409eff);
|
||||
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.2);
|
||||
}
|
||||
|
||||
.is-active {
|
||||
color: var(--el-color-white);
|
||||
background: var(--el-color-primary);
|
||||
border-color: var(--el-color-primary);
|
||||
transition: border-color 3s ease;
|
||||
}
|
||||
}
|
||||
.layout-navbars-tagsview-ul .is-active .layout-navbars-tagsview-ul-li-icon {
|
||||
color: var(--el-color-primary, #409eff);
|
||||
}
|
||||
|
||||
// 风格2
|
||||
.tags-style-two {
|
||||
.layout-navbars-tagsview-ul-li {
|
||||
margin-right: 0 !important;
|
||||
border: none !important;
|
||||
position: relative;
|
||||
border-radius: 3px !important;
|
||||
.layout-navbars-tagsview-ul .is-active .layout-navbars-tagsview-ul-li-icon:hover {
|
||||
background-color: var(--el-color-primary);
|
||||
color: var(--el-color-white);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
.layout-icon-active {
|
||||
display: none;
|
||||
}
|
||||
.layout-navbars-tagsview-ul .is-active .layout-navbars-tagsview-ul-li-close:hover {
|
||||
background-color: var(--el-color-danger);
|
||||
color: var(--el-color-white);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.layout-icon-three {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.is-active {
|
||||
background: none !important;
|
||||
color: var(--el-color-primary) !important;
|
||||
}
|
||||
}
|
||||
|
||||
// 风格3
|
||||
.tags-style-three {
|
||||
align-items: flex-end;
|
||||
|
||||
.tgs-style-three-svg {
|
||||
-webkit-mask-image:
|
||||
url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbD0ibm9uZSI+CgogPGc+CiAgPHRpdGxlPkxheWVyIDE8L3RpdGxlPgogIDxwYXRoIHRyYW5zZm9ybT0icm90YXRlKC0wLjEzMzUwNiA1MC4xMTkyIDUwKSIgaWQ9InN2Z18xIiBkPSJtMTAwLjExOTE5LDEwMGMtNTUuMjI4LDAgLTEwMCwtNDQuNzcyIC0xMDAsLTEwMGwwLDEwMGwxMDAsMHoiIG9wYWNpdHk9InVuZGVmaW5lZCIgc3Ryb2tlPSJudWxsIiBmaWxsPSIjRjhFQUU3Ii8+CiAgPHBhdGggZD0ibS0wLjYzNzY2LDcuMzEyMjhjMC4xMTkxOSwwIDAuMjE3MzcsMC4wNTc5NiAwLjQ3Njc2LDAuMTE5MTljMC4yMzIsMC4wNTQ3NyAwLjI3MzI5LDAuMDM0OTEgMC4zNTc1NywwLjExOTE5YzAuMDg0MjgsMC4wODQyOCAwLjM1NzU3LDAgMC40NzY3NiwwbDAuMTE5MTksMGwwLjIzODM4LDAiIGlkPSJzdmdfMiIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHBhdGggZD0ibTI4LjkyMTM0LDY5LjA1MjQ0YzAsMC4xMTkxOSAwLDAuMjM4MzggMCwwLjM1NzU3bDAsMC4xMTkxOWwwLDAuMTE5MTkiIGlkPSJzdmdfMyIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHJlY3QgaWQ9InN2Z180IiBoZWlnaHQ9IjAiIHdpZHRoPSIxLjMxMTA4IiB5PSI2LjgzNTUyIiB4PSItMC4wNDE3MSIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHJlY3QgaWQ9InN2Z181IiBoZWlnaHQ9IjEuNzg3ODQiIHdpZHRoPSIwLjExOTE5IiB5PSI2OC40NTY1IiB4PSIyOC45MjEzNCIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiAgPHJlY3QgaWQ9InN2Z182IiBoZWlnaHQ9IjQuODg2NzciIHdpZHRoPSIxOS4wNzAzMiIgeT0iNTEuMjkzMjEiIHg9IjM2LjY2ODY2IiBzdHJva2U9Im51bGwiIGZpbGw9Im5vbmUiLz4KIDwvZz4KPC9zdmc+'),
|
||||
url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNzAiIGhlaWdodD0iNzAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbD0ibm9uZSI+CiA8Zz4KICA8dGl0bGU+TGF5ZXIgMTwvdGl0bGU+CiAgPHBhdGggdHJhbnNmb3JtPSJyb3RhdGUoLTg5Ljc2MjQgNy4zMzAxNCA1NS4xMjUyKSIgc3Ryb2tlPSJudWxsIiBpZD0ic3ZnXzEiIGZpbGw9IiNGOEVBRTciIGQ9Im02Mi41NzQ0OSwxMTcuNTIwODZjLTU1LjIyOCwwIC0xMDAsLTQ0Ljc3MiAtMTAwLC0xMDBsMCwxMDBsMTAwLDB6IiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPgogIDxwYXRoIGQ9Im0tMC42Mzc2Niw3LjMxMjI4YzAuMTE5MTksMCAwLjIxNzM3LDAuMDU3OTYgMC40NzY3NiwwLjExOTE5YzAuMjMyLDAuMDU0NzcgMC4yNzMyOSwwLjAzNDkxIDAuMzU3NTcsMC4xMTkxOWMwLjA4NDI4LDAuMDg0MjggMC4zNTc1NywwIDAuNDc2NzYsMGwwLjExOTE5LDBsMC4yMzgzOCwwIiBpZD0ic3ZnXzIiIHN0cm9rZT0ibnVsbCIgZmlsbD0ibm9uZSIvPgogIDxwYXRoIGQ9Im0yOC45MjEzNCw2OS4wNTI0NGMwLDAuMTE5MTkgMCwwLjIzODM4IDAsMC4zNTc1N2wwLDAuMTE5MTlsMCwwLjExOTE5IiBpZD0ic3ZnXzMiIHN0cm9rZT0ibnVsbCIgZmlsbD0ibm9uZSIvPgogIDxyZWN0IGlkPSJzdmdfNCIgaGVpZ2h0PSIwIiB3aWR0aD0iMS4zMTEwOCIgeT0iNi44MzU1MiIgeD0iLTAuMDQxNzEiIHN0cm9rZT0ibnVsbCIgZmlsbD0ibm9uZSIvPgogIDxyZWN0IGlkPSJzdmdfNSIgaGVpZ2h0PSIxLjc4Nzg0IiB3aWR0aD0iMC4xMTkxOSIgeT0iNjguNDU2NSIgeD0iMjguOTIxMzQiIHN0cm9rZT0ibnVsbCIgZmlsbD0ibm9uZSIvPgogIDxyZWN0IGlkPSJzdmdfNiIgaGVpZ2h0PSI0Ljg4Njc3IiB3aWR0aD0iMTkuMDcwMzIiIHk9IjUxLjI5MzIxIiB4PSIzNi42Njg2NiIgc3Ryb2tlPSJudWxsIiBmaWxsPSJub25lIi8+CiA8L2c+Cjwvc3ZnPg=='),
|
||||
url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><rect rx='8' width='100%' height='100%' fill='%23F8EAE7'/></svg>");
|
||||
-webkit-mask-size:
|
||||
18px 30px,
|
||||
20px 30px,
|
||||
calc(100% - 30px) calc(100% + 17px);
|
||||
-webkit-mask-position:
|
||||
right bottom,
|
||||
left bottom,
|
||||
center top;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.layout-navbars-tagsview-ul-li {
|
||||
padding: 0 5px;
|
||||
border-width: 15px 27px 15px;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
margin: 0 -15px;
|
||||
|
||||
.layout-icon-active {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.layout-icon-three {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@extend .tgs-style-three-svg;
|
||||
background: var(--tagsview3-active-background-color);
|
||||
color: unset;
|
||||
}
|
||||
}
|
||||
|
||||
.is-active {
|
||||
@extend .tgs-style-three-svg;
|
||||
background: var(--tagsview3-active-background-color) !important;
|
||||
color: var(--el-color-primary) !important;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
.layout-navbars-tagsview-ul .is-active .layout-navbars-tagsview-ul-li-refresh:hover {
|
||||
background-color: var(--el-color-primary);
|
||||
color: var(--el-color-white);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.layout-navbars-tagsview-shadow {
|
||||
|
||||
@@ -98,8 +98,6 @@ export const useThemeConfig = defineStore('themeConfig', {
|
||||
|
||||
/* 其它设置
|
||||
------------------------------- */
|
||||
// 默认 Tagsview 风格,可选 1、 tags-style-one 2、 tags-style-two 3、 tags-style-three
|
||||
tagsStyle: 'tags-style-three',
|
||||
// 默认主页面切换动画,可选 1、 slide-right 2、 slide-left 3、 opacitys
|
||||
animation: 'slide-right',
|
||||
// 默认分栏高亮风格,可选 1、 圆角 columns-round 2、 卡片 columns-card
|
||||
@@ -137,6 +135,7 @@ export const useThemeConfig = defineStore('themeConfig', {
|
||||
appSlogan: 'common.appSlogan',
|
||||
// 网站logo icon, base64编码内容
|
||||
logoIcon: logoIcon,
|
||||
version: 'latest',
|
||||
// 默认初始语言,可选值"<zh-cn|en|zh-tw>",默认 zh-cn
|
||||
globalI18n: 'zh-cn',
|
||||
// 默认全局组件大小,可选值"<|large|default|small>",默认 ''
|
||||
@@ -155,12 +154,13 @@ export const useThemeConfig = defineStore('themeConfig', {
|
||||
if (tc) {
|
||||
this.themeConfig = tc;
|
||||
document.documentElement.style.cssText = getLocal('themeConfigStyle');
|
||||
} else {
|
||||
getServerConf().then((res) => {
|
||||
this.themeConfig.globalI18n = res.i18n;
|
||||
});
|
||||
}
|
||||
|
||||
getServerConf().then((res) => {
|
||||
this.themeConfig.globalI18n = res.i18n;
|
||||
this.themeConfig.version = res.version;
|
||||
});
|
||||
|
||||
// 根据后台系统配置初始化
|
||||
getSysStyleConfig().then((res) => {
|
||||
if (res?.title) {
|
||||
|
||||
2
frontend/src/types/pinia.d.ts
vendored
2
frontend/src/types/pinia.d.ts
vendored
@@ -40,7 +40,6 @@ declare interface ThemeConfigState {
|
||||
isInvert: boolean;
|
||||
isWatermark: boolean;
|
||||
watermarkText: Array<string>;
|
||||
tagsStyle: string;
|
||||
animation: string;
|
||||
columnsAsideStyle: string;
|
||||
layout: string;
|
||||
@@ -49,6 +48,7 @@ declare interface ThemeConfigState {
|
||||
globalViceTitle: string;
|
||||
appSlogan: string;
|
||||
logoIcon: string;
|
||||
version: string;
|
||||
globalI18n: string;
|
||||
globalComponentSize: string;
|
||||
terminalTheme: string;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<div class="card !p-1 flex items-center justify-between">
|
||||
<div class="card p-1! flex items-center justify-between">
|
||||
<div>
|
||||
<el-link @click="onRunSql()" underline="never" class="ml-3.5" icon="VideoPlay"> </el-link>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
@@ -39,32 +39,32 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-splitter style="height: calc(100vh - 200px)" layout="vertical" @resize-end="onResizeTableHeight">
|
||||
<el-splitter style="height: calc(100vh - 220px)" layout="vertical" @resize-end="onResizeTableHeight">
|
||||
<el-splitter-panel :size="state.editorSize" max="80%">
|
||||
<MonacoEditor ref="monacoEditorRef" class="mt-1" v-model="state.sql" language="sql" height="100%" :id="'MonacoTextarea-' + getKey()" />
|
||||
</el-splitter-panel>
|
||||
|
||||
<el-splitter-panel>
|
||||
<div class="sql-exec-res !h-full">
|
||||
<div class="sql-exec-res h-full!">
|
||||
<el-tabs
|
||||
class="!h-full !w-full"
|
||||
class="h-full! w-full!"
|
||||
v-if="state.execResTabs.length > 0"
|
||||
@tab-remove="onRemoveTab"
|
||||
@tab-change="active"
|
||||
v-model="state.activeTab"
|
||||
>
|
||||
<el-tab-pane class="!h-full" closable v-for="dt in state.execResTabs" :label="dt.id" :name="dt.id" :key="dt.id">
|
||||
<el-tab-pane class="h-full!" closable v-for="dt in state.execResTabs" :label="dt.id" :name="dt.id" :key="dt.id">
|
||||
<template #label>
|
||||
<el-popover :show-after="1000" placement="top-start" :title="$t('db.execInfo')" trigger="hover" :width="300">
|
||||
<template #reference>
|
||||
<div>
|
||||
<span>
|
||||
<span v-if="dt.loading">
|
||||
<SvgIcon class="!mb-0.5 is-loading" name="Loading" color="var(--el-color-primary)" />
|
||||
<SvgIcon class="mb-0.5! is-loading" name="Loading" color="var(--el-color-primary)" />
|
||||
</span>
|
||||
<span v-else>
|
||||
<SvgIcon class="!mb-0.5" v-if="!dt.errorMsg" name="CircleCheck" color="var(--el-color-success)" />
|
||||
<SvgIcon class="!mb-0.5" v-if="dt.errorMsg" name="CircleClose" color="var(--el-color-error)" />
|
||||
<SvgIcon class="mb-0.5!" v-if="!dt.errorMsg" name="CircleCheck" color="var(--el-color-success)" />
|
||||
<SvgIcon class="mb-0.5!" v-if="dt.errorMsg" name="CircleClose" color="var(--el-color-error)" />
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -289,7 +289,7 @@ const onResizeTableHeight = (index: number, sizes: number[]) => {
|
||||
editorHeight = plitpaneHeight / 2;
|
||||
}
|
||||
|
||||
let tableDataHeight = plitpaneHeight - editorHeight - 43;
|
||||
let tableDataHeight = plitpaneHeight - editorHeight - 47;
|
||||
|
||||
state.editorSize = editorHeight;
|
||||
state.tableDataHeight = tableDataHeight + 'px';
|
||||
@@ -486,6 +486,7 @@ const runSql = async (sql: string, remark = '', newTab = false) => {
|
||||
state.execResTabs[i].tableColumn = colAndData.columns.map((x: any) => {
|
||||
return {
|
||||
columnName: x.name,
|
||||
key: x.key,
|
||||
columnType: x.type,
|
||||
show: true,
|
||||
};
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<span v-if="column.dataTypeSubscript === 'icon-clock'">
|
||||
<SvgIcon :size="9" name="Clock" style="cursor: unset" />
|
||||
</span>
|
||||
<span class="!text-[8px]" v-else>{{ column.dataTypeSubscript }}</span>
|
||||
<span class="text-[8px]!" v-else>{{ column.dataTypeSubscript }}</span>
|
||||
</div>
|
||||
|
||||
<div v-if="showColumnTip">
|
||||
@@ -53,7 +53,7 @@
|
||||
<div
|
||||
v-if="dbConfig.showColumnComment"
|
||||
style="color: var(--el-color-info-light-3)"
|
||||
class="!text-[10px] el-text el-text--small is-truncated"
|
||||
class="text-[10px]! el-text el-text--small is-truncated"
|
||||
>
|
||||
{{ column.columnComment }}
|
||||
</div>
|
||||
@@ -77,9 +77,7 @@
|
||||
<!-- 排序箭头图标 -->
|
||||
<SvgIcon
|
||||
v-if="
|
||||
column.title == nowSortColumn?.columnName &&
|
||||
!showColumnActions[column.key] &&
|
||||
!columnActionVisible[column.key]
|
||||
column.key == nowSortColumn?.key && !showColumnActions[column.key] && !columnActionVisible[column.key]
|
||||
"
|
||||
:color="'var(--el-color-primary)'"
|
||||
:name="nowSortColumn?.order == 'asc' ? 'top' : 'bottom'"
|
||||
@@ -135,7 +133,7 @@
|
||||
<div v-else @dblclick="onEnterEditMode(rowData, column, rowIndex, columnIndex)">
|
||||
<div v-if="canEdit(rowIndex, columnIndex)">
|
||||
<ColumnFormItem
|
||||
v-model="rowData[column.dataKey!]"
|
||||
v-model="rowData[column.key!]"
|
||||
:data-type="column.dataType"
|
||||
@blur="onExitEditMode(rowData, column, rowIndex)"
|
||||
:column-name="column.columnName"
|
||||
@@ -143,11 +141,11 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-else :class="isUpdated(rowIndex, column.dataKey) ? 'update_field_active ml-0.5 mr-0.5' : 'ml-0.5 mr-0.5'">
|
||||
<span v-if="rowData[column.dataKey!] === null" style="color: var(--el-color-info-light-5)"> NULL </span>
|
||||
<div v-else :class="isUpdated(rowIndex, column.key) ? 'update_field_active ml-0.5 mr-0.5' : 'ml-0.5 mr-0.5'">
|
||||
<span v-if="rowData[column.key!] === null" style="color: var(--el-color-info-light-5)"> NULL </span>
|
||||
|
||||
<span v-else :title="rowData[column.dataKey!]" class="el-text el-text--small is-truncated">
|
||||
{{ rowData[column.dataKey!] }}
|
||||
<span v-else :title="rowData[column.key!]" class="el-text el-text--small is-truncated">
|
||||
{{ rowData[column.key!] }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -160,7 +158,7 @@
|
||||
<SvgIcon class="is-loading" name="loading" color="var(--el-color-primary)" :size="28" />
|
||||
<el-text class="ml-1" tag="b">{{ $t('db.execTime') }} - {{ state.execTime.toFixed(1) }}s</el-text>
|
||||
</div>
|
||||
<div v-if="loading && abortFn" class="!mt-2">
|
||||
<div v-if="loading && abortFn" class="mt-2!">
|
||||
<el-button @click="cancelLoading" type="info" size="small" plain>{{ $t('common.cancel') }}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -275,7 +273,7 @@ const columnActionVisible = ref({} as any);
|
||||
const cmDataCopyCell = new ContextmenuItem('copyValue', 'common.copy')
|
||||
.withIcon('CopyDocument')
|
||||
.withOnClick(async (data: any) => {
|
||||
await copyToClipboard(data.rowData[data.column.dataKey]);
|
||||
await copyToClipboard(data.rowData[data.column.key]);
|
||||
})
|
||||
.withHideFunc(() => {
|
||||
// 选中多条则隐藏该复制按钮
|
||||
@@ -303,16 +301,23 @@ const cmDataGenInsertSql = new ContextmenuItem('genInsertSql', 'Insert SQL')
|
||||
|
||||
const cmDataGenJson = new ContextmenuItem('genJson', 'db.genJson').withIcon('tickets').withOnClick(() => onGenerateJson());
|
||||
|
||||
const cmDataExportCsv = new ContextmenuItem('exportCsv', 'db.exportCsv').withIcon('document').withOnClick(() => onExportCsv());
|
||||
const cmDataExportCsv = new ContextmenuItem('exportCsv', 'db.exportCsv')
|
||||
.withIcon('document')
|
||||
.withOnClick(() => onExportCsv())
|
||||
.withPermission('db:data:export');
|
||||
|
||||
const cmDataExportExcel = new ContextmenuItem('exportExcel', 'db.exportExcel').withIcon('document').withOnClick(() => onExportExcel());
|
||||
const cmDataExportExcel = new ContextmenuItem('exportExcel', 'db.exportExcel')
|
||||
.withIcon('document')
|
||||
.withOnClick(() => onExportExcel())
|
||||
.withPermission('db:data:export');
|
||||
|
||||
const cmDataExportSql = new ContextmenuItem('exportSql', 'db.exportSql')
|
||||
.withIcon('document')
|
||||
.withOnClick(() => onExportSql())
|
||||
.withHideFunc(() => {
|
||||
return state.table == '';
|
||||
});
|
||||
})
|
||||
.withPermission('db:data:export');
|
||||
|
||||
class NowUpdateCell {
|
||||
rowIndex: number;
|
||||
@@ -402,7 +407,6 @@ const dbConfig = useStorage('dbConfig', DbThemeConfig);
|
||||
const rowNoColumn = {
|
||||
title: 'No.',
|
||||
key: 'tableDataRowNo',
|
||||
dataKey: 'tableDataRowNo',
|
||||
width: 45,
|
||||
fixed: true,
|
||||
align: 'center',
|
||||
@@ -508,8 +512,6 @@ const setTableColumns = (columns: any) => {
|
||||
x.remark = `${x.columnType} ${x.columnComment ? ' | ' + x.columnComment : ''}`;
|
||||
return {
|
||||
...x,
|
||||
key: columnName,
|
||||
dataKey: columnName,
|
||||
width: DbInst.flexColumnWidth(columnName, state.datas),
|
||||
title: columnName,
|
||||
align: x.dataType == DataType.Number ? 'right' : 'left',
|
||||
@@ -558,21 +560,21 @@ const hideColumnAction = () => {
|
||||
const handleColumnCommand = (column: any, command: string) => {
|
||||
switch (command) {
|
||||
case 'sort-asc':
|
||||
onTableSortChange({ columnName: column.dataKey, order: 'asc' });
|
||||
onTableSortChange({ key: column.key, order: 'asc' });
|
||||
break;
|
||||
case 'sort-desc':
|
||||
onTableSortChange({ columnName: column.dataKey, order: 'desc' });
|
||||
onTableSortChange({ key: column.key, order: 'desc' });
|
||||
break;
|
||||
case 'fix':
|
||||
state.columns.forEach((col: any) => {
|
||||
if (col.dataKey == column.dataKey) {
|
||||
if (col.key == column.key) {
|
||||
col.fixed = true;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'unfix':
|
||||
state.columns.forEach((col: any) => {
|
||||
if (col.dataKey == column.dataKey) {
|
||||
if (col.key == column.key) {
|
||||
col.fixed = false;
|
||||
}
|
||||
});
|
||||
@@ -711,7 +713,7 @@ const onGenerateJson = async () => {
|
||||
let obj: any = {};
|
||||
for (let column of state.columns) {
|
||||
if (column.show) {
|
||||
obj[column.title] = selectionData[column.dataKey];
|
||||
obj[column.title] = selectionData[column.key];
|
||||
}
|
||||
}
|
||||
jsonObj.push(obj);
|
||||
@@ -768,7 +770,7 @@ const onEnterEditMode = (rowData: any, column: any, rowIndex = 0, columnIndex =
|
||||
nowUpdateCell.value = {
|
||||
rowIndex: rowIndex,
|
||||
colIndex: columnIndex,
|
||||
oldValue: rowData[column.dataKey],
|
||||
oldValue: rowData[column.key],
|
||||
dataType: column.dataType,
|
||||
};
|
||||
};
|
||||
@@ -778,7 +780,7 @@ const onExitEditMode = (rowData: any, column: any, rowIndex = 0) => {
|
||||
return;
|
||||
}
|
||||
const oldValue = nowUpdateCell.value.oldValue;
|
||||
const newValue = rowData[column.dataKey];
|
||||
const newValue = rowData[column.key];
|
||||
|
||||
// 未改变单元格值
|
||||
if (oldValue == newValue) {
|
||||
@@ -793,7 +795,7 @@ const onExitEditMode = (rowData: any, column: any, rowIndex = 0) => {
|
||||
cellUpdateMap.value.set(rowIndex, updatedRow);
|
||||
}
|
||||
|
||||
const columnName = column.dataKey;
|
||||
const columnName = column.key;
|
||||
let cellData = updatedRow.columnsMap.get(columnName);
|
||||
if (cellData) {
|
||||
// 多次修改情况,可能又修改回原值,则移除该修改单元格
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
<el-text
|
||||
id="copyValue"
|
||||
style="color: var(--el-color-info-light-3)"
|
||||
class="is-truncated !text-[12px] mt-1"
|
||||
class="is-truncated text-[12px]! mt-1"
|
||||
@click="copyToClipboard(sql)"
|
||||
:title="sql"
|
||||
>{{ sql }}</el-text
|
||||
@@ -392,6 +392,7 @@ const selectData = async () => {
|
||||
const columns = await getNowDbInst().loadColumns(props.dbName, props.tableName);
|
||||
columns.forEach((x: any) => {
|
||||
x.show = true;
|
||||
x.key = x.columnName;
|
||||
});
|
||||
state.columns = columns;
|
||||
}
|
||||
@@ -592,7 +593,7 @@ const onSelectByCondition = async () => {
|
||||
*/
|
||||
const onTableSortChange = async (sort: any) => {
|
||||
const sortType = sort.order == 'desc' ? 'DESC' : 'ASC';
|
||||
state.orderBy = `ORDER BY ${state.dbDialect.quoteIdentifier(sort.columnName)} ${sortType}`;
|
||||
state.orderBy = `ORDER BY ${state.dbDialect.quoteIdentifier(sort.key)} ${sortType}`;
|
||||
await onRefresh();
|
||||
};
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
<el-row class="mb-1">
|
||||
<el-popover v-model:visible="state.dumpInfo.visible" trigger="click" :width="470" placement="right">
|
||||
<template #reference>
|
||||
<el-button :disabled="state.dumpInfo.tables?.length == 0" class="ml-1" type="success" size="small">{{ $t('db.dump') }}</el-button>
|
||||
<el-button v-auth="'db:data:export'" :disabled="state.dumpInfo.tables?.length == 0" class="ml-1" type="success" size="small">
|
||||
{{ $t('db.dump') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<el-form-item :label="$t('db.exportContent')">
|
||||
<el-radio-group v-model="dumpInfo.type">
|
||||
|
||||
@@ -86,13 +86,13 @@
|
||||
@tab-remove="onRemoveTab"
|
||||
@tab-change="onTabChange"
|
||||
v-model="state.activeName"
|
||||
class="!h-full w-full"
|
||||
class="h-full! w-full"
|
||||
>
|
||||
<el-tab-pane class="!h-full" closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
|
||||
<el-tab-pane class="h-full!" closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
|
||||
<template #label>
|
||||
<el-popover :show-after="1000" placement="bottom-start" trigger="hover" :width="250">
|
||||
<template #reference>
|
||||
<span @contextmenu.prevent="onTabContextmenu(dt, $event)" class="!text-[12px]">{{ dt.label }}</span>
|
||||
<span @contextmenu.prevent="onTabContextmenu(dt, $event)" class="text-[12px]!">{{ dt.label }}</span>
|
||||
</template>
|
||||
<template #default>
|
||||
<el-descriptions :column="1" size="small">
|
||||
|
||||
@@ -59,8 +59,8 @@
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="form.mode === 2">
|
||||
<el-row class="w-full!">
|
||||
<el-col :span="12">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="10">
|
||||
<el-form-item prop="targetFileDbType" :label="$t('db.dbFileType')" :required="form.mode === 2">
|
||||
<el-select v-model="form.targetFileDbType" clearable filterable>
|
||||
<el-option
|
||||
@@ -79,7 +79,13 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-col :span="6">
|
||||
<el-form-item :label="$t('db.fileType')">
|
||||
<el-select v-model="form.extra.fileType" :options="fileTypeOptions"> </el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8">
|
||||
<el-form-item :label="$t('db.fileSaveDays')">
|
||||
<el-input-number v-model="form.fileSaveDays" :min="-1" :max="1000">
|
||||
<template #suffix>
|
||||
@@ -138,10 +144,8 @@
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<div>
|
||||
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">{{ $t('common.confirm') }}</el-button>
|
||||
</div>
|
||||
<el-button @click="cancel()">{{ $t('common.cancel') }}</el-button>
|
||||
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">{{ $t('common.confirm') }}</el-button>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</div>
|
||||
@@ -187,6 +191,11 @@ const rules = {
|
||||
cron: [Rules.requiredSelect('cron')],
|
||||
};
|
||||
|
||||
const fileTypeOptions = [
|
||||
{ label: '.sql', value: 'sql' },
|
||||
{ label: '.zip', value: 'zip' },
|
||||
];
|
||||
|
||||
const dbForm: any = ref(null);
|
||||
|
||||
type FormData = {
|
||||
@@ -215,6 +224,7 @@ type FormData = {
|
||||
deleteTable?: 1 | 2;
|
||||
checkedKeys: string;
|
||||
runningState: 1 | 2;
|
||||
extra: { fileType: string };
|
||||
};
|
||||
|
||||
const basicFormData = {
|
||||
@@ -226,6 +236,7 @@ const basicFormData = {
|
||||
deleteTable: 1,
|
||||
checkedKeys: '',
|
||||
runningState: 1,
|
||||
extra: { fileType: fileTypeOptions[0].value },
|
||||
} as FormData;
|
||||
|
||||
const srcTableList = ref<{ tableName: string; tableComment: string }[]>([]);
|
||||
@@ -264,6 +275,7 @@ watch(dialogVisible, async (newValue: boolean) => {
|
||||
if (!newValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
const propsData = props.data as any;
|
||||
if (!propsData?.id) {
|
||||
let d = {} as FormData;
|
||||
@@ -275,8 +287,8 @@ watch(dialogVisible, async (newValue: boolean) => {
|
||||
return;
|
||||
}
|
||||
|
||||
state.form = deepClone(props.data) as FormData;
|
||||
let { srcDbId, targetDbId } = state.form;
|
||||
const form = deepClone(props.data) as FormData;
|
||||
let { srcDbId, targetDbId } = form;
|
||||
|
||||
// 初始化src数据源
|
||||
if (srcDbId) {
|
||||
@@ -301,11 +313,14 @@ watch(dialogVisible, async (newValue: boolean) => {
|
||||
}
|
||||
|
||||
// 初始化勾选迁移表
|
||||
srcTreeRef.value.setCheckedKeys(state.form.checkedKeys.split(','));
|
||||
srcTreeRef.value.setCheckedKeys(form.checkedKeys.split(','));
|
||||
|
||||
// 初始化默认值
|
||||
state.form.cronAble = state.form.cronAble || 0;
|
||||
state.form.mode = state.form.mode || 1;
|
||||
form.cronAble = form.cronAble || -1;
|
||||
form.mode = form.mode || 1;
|
||||
form.extra = form.extra || { fileType: fileTypeOptions[0].value };
|
||||
|
||||
state.form = form;
|
||||
});
|
||||
|
||||
watch(
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
lazy
|
||||
>
|
||||
<template #tableHeader>
|
||||
<el-button type="primary" icon="plus" @click="editContainerConf(false)" plain>{{ $t('common.create') }}</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectionData.length < 1" @click="deleteConf" plain>{{ $t('common.delete') }}</el-button>
|
||||
<el-button v-auth="'container:save'" type="primary" icon="plus" @click="editContainerConf(false)" plain>{{ $t('common.create') }}</el-button>
|
||||
<el-button v-auth="'container:del'" type="danger" icon="delete" :disabled="selectionData.length < 1" @click="deleteConf" plain>
|
||||
{{ $t('common.delete') }}
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<template #tagPath="{ data }">
|
||||
@@ -22,7 +24,7 @@
|
||||
|
||||
<template #action="{ data }">
|
||||
<el-button @click="showDetail(data)" link>{{ $t('common.detail') }}</el-button>
|
||||
<el-button type="primary" link @click="editContainerConf(data)">{{ $t('common.edit') }}</el-button>
|
||||
<el-button v-auth="'container:save'" type="primary" link @click="editContainerConf(data)">{{ $t('common.edit') }}</el-button>
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<div class="file-manage">
|
||||
<el-dialog :title="$t('machine.process')" v-model="dialogVisible" :destroy-on-close="true" :show-close="true" :before-close="handleClose" width="65%">
|
||||
<div class="card !p-1">
|
||||
<div class="card p-1!">
|
||||
<el-row>
|
||||
<el-col :span="4">
|
||||
<el-input size="small" :placeholder="$t('machine.processName')" v-model="params.name" plain clearable></el-input>
|
||||
</el-col>
|
||||
<el-col :span="4" class="ml-1">
|
||||
<el-select class="!w-full" @change="getProcess" size="small" v-model="params.sortType" :placeholder="$t('machine.selectSortType')">
|
||||
<el-select @change="getProcess" size="small" v-model="params.sortType" :placeholder="$t('machine.selectSortType')">
|
||||
<el-option key="cpu" :label="$t('machine.cpuDesc')" value="1"> </el-option>
|
||||
<el-option key="cpu" :label="$t('machine.memDesc')" value="2"> </el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="4" class="ml-1">
|
||||
<el-select class="!w-full" @change="getProcess" size="small" v-model="params.count" :placeholder="$t('machine.selectProcessNum')">
|
||||
<el-select @change="getProcess" size="small" v-model="params.count" :placeholder="$t('machine.selectProcessNum')">
|
||||
<el-option key="10" label="10" value="10"> </el-option>
|
||||
<el-option key="15" label="15" value="15"> </el-option>
|
||||
<el-option key="20" label="20" value="20"> </el-option>
|
||||
@@ -26,7 +26,7 @@
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<el-table :data="processList" size="small" style="width: 100%">
|
||||
<el-table :data="processList" size="small" :height="500">
|
||||
<el-table-column prop="user" label="USER" :min-width="50"> </el-table-column>
|
||||
<el-table-column prop="pid" label="PID" :min-width="50" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="cpu" label="%CPU" :min-width="40"> </el-table-column>
|
||||
|
||||
@@ -741,7 +741,11 @@ function getParentPath(filePath: string) {
|
||||
|
||||
const deleteFile = async (files: any) => {
|
||||
try {
|
||||
await useI18nDeleteConfirm(files.map((x: any) => `[${x.path}]`).join('\n'));
|
||||
let confirmMsg = files.map((x: any) => `[${x.path}]`).join('\n');
|
||||
if (confirmMsg.length > 400) {
|
||||
confirmMsg = confirmMsg.substring(0, 400) + '...';
|
||||
}
|
||||
await useI18nDeleteConfirm(confirmMsg);
|
||||
state.loading = true;
|
||||
await machineApi.rmFile.request({
|
||||
fileId: props.fileId,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="h-full machine-terminal-tabs">
|
||||
<el-tabs v-if="state.tabs.size > 0" type="card" @tab-remove="onRemoveTab" v-model="state.activeTermName" class="!h-full w-full">
|
||||
<el-tab-pane class="!h-full flex flex-col" closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
|
||||
<el-tab-pane class="h-full! flex flex-col" closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
|
||||
<template #label>
|
||||
<el-popconfirm @confirm="handleReconnect(dt, true)" :title="$t('machine.reConnTips')">
|
||||
<template #reference>
|
||||
|
||||
@@ -85,12 +85,18 @@ const NodeTypeAuthCert = new NodeType(12)
|
||||
(await node.ctx?.addResourceComponent(MachineOpComp)).openTerminal(node.params);
|
||||
})
|
||||
.withContextMenuItems([
|
||||
new ContextmenuItem('term', 'machine.openTerminal').withIcon('Monitor').withOnClick(async (node: TagTreeNode) => {
|
||||
(await node.ctx?.addResourceComponent(MachineOpComp))?.openTerminal(node.params);
|
||||
}),
|
||||
new ContextmenuItem('term-ex', 'machine.newTabOpenTerminal').withIcon('Monitor').withOnClick(async (node: TagTreeNode) => {
|
||||
(await node.ctx?.addResourceComponent(MachineOpComp))?.openTerminal(node.params, true);
|
||||
}),
|
||||
new ContextmenuItem('term', 'machine.openTerminal')
|
||||
.withIcon('Monitor')
|
||||
.withPermission('machine:terminal')
|
||||
.withOnClick(async (node: TagTreeNode) => {
|
||||
(await node.ctx?.addResourceComponent(MachineOpComp))?.openTerminal(node.params);
|
||||
}),
|
||||
new ContextmenuItem('term-ex', 'machine.newTabOpenTerminal')
|
||||
.withIcon('Monitor')
|
||||
.withPermission('machine:terminal')
|
||||
.withOnClick(async (node: TagTreeNode) => {
|
||||
(await node.ctx?.addResourceComponent(MachineOpComp))?.openTerminal(node.params, true);
|
||||
}),
|
||||
new ContextmenuItem('files', 'machine.fileManage').withIcon('FolderOpened').withOnClick(async (node: any) => {
|
||||
(await node.ctx?.addResourceComponent(MachineOpComp)).showFileManage(node.params);
|
||||
}),
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
lazy
|
||||
>
|
||||
<template #tableHeader>
|
||||
<el-button type="primary" icon="plus" @click="editMongo(false)" plain>{{ $t('common.create') }}</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectionData.length < 1" @click="deleteMongo" plain>{{ $t('common.delete') }}</el-button>
|
||||
<el-button v-auth="'mongo:save'" type="primary" icon="plus" @click="editMongo(false)" plain>{{ $t('common.create') }}</el-button>
|
||||
<el-button v-auth="'mongo:del'" type="danger" icon="delete" :disabled="selectionData.length < 1" @click="deleteMongo" plain>
|
||||
{{ $t('common.delete') }}
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<template #tagPath="{ data }">
|
||||
@@ -25,7 +27,7 @@
|
||||
|
||||
<el-button @click="showUsers(data.id)" link type="success">cmd</el-button>
|
||||
|
||||
<el-button @click="editMongo(data)" link type="primary">{{ $t('common.edit') }}</el-button>
|
||||
<el-button v-auth="'mongo:save'" @click="editMongo(data)" link type="primary">{{ $t('common.edit') }}</el-button>
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
lazy
|
||||
>
|
||||
<template #tableHeader>
|
||||
<el-button type="primary" icon="plus" @click="editRedis(false)" plain>{{ $t('common.create') }}</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="selectionData.length < 1" @click="deleteRedis" plain>{{ $t('common.delete') }}</el-button>
|
||||
<el-button v-auth="'redis:save'" type="primary" icon="plus" @click="editRedis(false)" plain>{{ $t('common.create') }}</el-button>
|
||||
<el-button v-auth="'redis:del'" type="danger" icon="delete" :disabled="selectionData.length < 1" @click="deleteRedis" plain>
|
||||
{{ $t('common.delete') }}
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<template #tagPath="{ data }">
|
||||
@@ -27,7 +29,7 @@
|
||||
<el-button @click="onShowClusterInfo(data)" v-if="data.mode === 'cluster'" type="primary" link>{{ $t('redis.clusterInfo') }}</el-button>
|
||||
|
||||
<el-button @click="showDetail(data)" link>{{ $t('common.detail') }}</el-button>
|
||||
<el-button type="primary" link @click="editRedis(data)">{{ $t('common.edit') }}</el-button>
|
||||
<el-button v-auth="'redis:save'" type="primary" link @click="editRedis(data)">{{ $t('common.edit') }}</el-button>
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<template v-for="item in contextMenuItems" :key="item.clickId">
|
||||
<el-dropdown-item v-if="!item.isHide(props.data)" :command="item">
|
||||
<el-dropdown-item v-if="!item.isHide(props.data) && hasPerm(item.permission)" :command="item">
|
||||
<SvgIcon v-if="item.icon" :name="item.icon" class="mr-1" />{{ $t(item.txt) }}
|
||||
</el-dropdown-item>
|
||||
</template>
|
||||
@@ -54,6 +54,7 @@ import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
import { ContextmenuItem } from '@/components/contextmenu';
|
||||
import { ResourceOpCtx, TagTreeNode } from '@/views/ops/component/tag';
|
||||
import { ResourceOpCtxKey } from '@/views/ops/resource/resource';
|
||||
import { hasPerm } from '@/components/auth/auth';
|
||||
|
||||
const resourceOpCtx: ResourceOpCtx | undefined = inject(ResourceOpCtxKey, undefined);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="h-full">
|
||||
<el-splitter @resize="onResizeOpPanel">
|
||||
<el-splitter-panel size="24%" max="40%">
|
||||
<el-card class="h-full flex" body-class="bg-(--el-bg-color) !p-0 flex flex-col w-full">
|
||||
<el-card class="h-full flex" body-class="!p-0 flex flex-col w-full">
|
||||
<div class="tag-tree-header flex justify-between items-center">
|
||||
<el-input v-model="filterText" :placeholder="$t('tag.tagFilterPlaceholder')" clearable size="small" class="tag-tree-search w-full">
|
||||
<template #prefix>
|
||||
|
||||
@@ -375,6 +375,7 @@ const allowDrop = (draggingNode: any, dropNode: any, type: any) => {
|
||||
// 只有权限节点可移动至菜单节点下 或者移动菜单
|
||||
return (
|
||||
(draggingNode.data.type == permissionTypeValue && dropNode.data.type == menuTypeValue) ||
|
||||
(draggingNode.data.type == permissionTypeValue && dropNode.data.type == permissionTypeValue) ||
|
||||
(draggingNode.data.type == menuTypeValue && dropNode.data.type == menuTypeValue)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -18,20 +18,19 @@ jwt:
|
||||
expire-time: 720
|
||||
# refreshToken过期时间单位分钟
|
||||
refresh-token-expire-time: 4320
|
||||
# 资源密码aes加密key
|
||||
aes:
|
||||
key: 1111111111111111
|
||||
# 若存在mysql配置,优先使用mysql
|
||||
mysql:
|
||||
host: mysql:3306
|
||||
# 数据库配置,dialect支持mysql、sqlite
|
||||
db:
|
||||
dialect: mysql
|
||||
address: mysql:3306
|
||||
name: mayfly-go
|
||||
username: root
|
||||
password: 111049
|
||||
db-name: mayfly-go
|
||||
config: charset=utf8&loc=Local&parseTime=true
|
||||
max-idle-conns: 5
|
||||
sqlite:
|
||||
path: ./mayfly-go.sqlite
|
||||
max-idle-conns: 5
|
||||
# db:
|
||||
# dialect: sqlite
|
||||
# address: ./mayfly-go.db
|
||||
# max-idle-conns: 5
|
||||
# 若同时部署多台机器,则需要配置redis信息用于缓存权限码、验证码、公私钥等
|
||||
# redis:
|
||||
# host: localhost
|
||||
@@ -55,3 +54,6 @@ log:
|
||||
# max-age: 60
|
||||
# # 是否使用 gzip 压缩方式压缩轮转后的日志文件
|
||||
# compress: true
|
||||
# 资源密码aes加密key
|
||||
aes:
|
||||
key: 1111111111111111
|
||||
@@ -36,7 +36,7 @@ require (
|
||||
github.com/tidwall/gjson v1.18.0
|
||||
github.com/veops/go-ansiterm v0.0.5
|
||||
go.mongodb.org/mongo-driver/v2 v2.3.0 // mongo
|
||||
golang.org/x/crypto v0.46.0 // ssh
|
||||
golang.org/x/crypto v0.47.0 // ssh
|
||||
golang.org/x/oauth2 v0.34.0
|
||||
golang.org/x/sync v0.19.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
@@ -132,11 +132,11 @@ require (
|
||||
golang.org/x/arch v0.21.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9 // indirect
|
||||
golang.org/x/image v0.31.0 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/sys v0.39.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/tools v0.39.0 // indirect
|
||||
golang.org/x/mod v0.31.0 // indirect
|
||||
golang.org/x/net v0.48.0 // indirect
|
||||
golang.org/x/sys v0.40.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
golang.org/x/tools v0.40.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
modernc.org/libc v1.66.10 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"mayfly-go/internal/ai/config"
|
||||
aimodel "mayfly-go/internal/ai/model"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/logx"
|
||||
|
||||
"github.com/cloudwego/eino/components/tool"
|
||||
@@ -77,6 +78,10 @@ func (aiAgent *AiAgent) Chat(ctx context.Context, sysPrompt string, question str
|
||||
go func() {
|
||||
defer close(ch)
|
||||
defer close(errCh)
|
||||
defer gox.Recover(func(err error) {
|
||||
errCh <- err
|
||||
})
|
||||
|
||||
sr, err := aiAgent.Stream(ctx, []*schema.Message{
|
||||
{
|
||||
Role: schema.System,
|
||||
|
||||
@@ -2,12 +2,12 @@ package init
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/ai/api"
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/pkg/starter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// 注册AI模块的IoC组件
|
||||
initialize.AddInitIocFunc(func() {
|
||||
starter.AddInitIocFunc(func() {
|
||||
api.InitIoc()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/cache"
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"mayfly-go/pkg/utils/netx"
|
||||
@@ -57,7 +58,9 @@ func LastLoginCheck(ctx context.Context, account *sysentity.Account, accountLogi
|
||||
res["refresh_token"] = refreshToken
|
||||
// 不进行otp二次校验则直接返回accessToken
|
||||
// 保存登录消息
|
||||
go saveLogin(ctx, account, loginIp)
|
||||
gox.Go(func() {
|
||||
saveLogin(ctx, account, loginIp)
|
||||
})
|
||||
}
|
||||
|
||||
// 赋值otp状态
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package init
|
||||
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/auth/api"
|
||||
"mayfly-go/internal/auth/application"
|
||||
"mayfly-go/internal/auth/infra/persistence"
|
||||
"mayfly-go/pkg/starter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(func() {
|
||||
starter.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
api.InitIoc()
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package init
|
||||
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/common/api"
|
||||
"mayfly-go/pkg/starter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(func() {
|
||||
starter.AddInitIocFunc(func() {
|
||||
api.InitIoc()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
fileapp "mayfly-go/internal/file/application"
|
||||
tagapp "mayfly-go/internal/tag/application"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/req"
|
||||
"strings"
|
||||
@@ -150,12 +151,13 @@ func (d *DbTransferTask) FileRun(rc *req.Ctx) {
|
||||
|
||||
filename, reader, err := d.fileApp.GetReader(context.TODO(), tFile.FileKey)
|
||||
biz.ErrIsNil(err)
|
||||
go func() {
|
||||
biz.ErrIsNil(d.dbSqlExecApp.ExecReader(rc.MetaCtx, &dto.SqlReaderExec{
|
||||
|
||||
gox.GoCtx(rc.MetaCtx, func(ctx context.Context) {
|
||||
biz.ErrIsNil(d.dbSqlExecApp.ExecReader(ctx, &dto.SqlReaderExec{
|
||||
Reader: reader,
|
||||
Filename: filename,
|
||||
DbConn: targetDbConn,
|
||||
ClientId: fm.ClientId,
|
||||
}))
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ type DataSyncTaskForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
TaskName string `binding:"required" json:"taskName"`
|
||||
TaskCron string `binding:"required" json:"taskCron"`
|
||||
TaskKey string `json:"taskKey"`
|
||||
Status int `binding:"required" json:"status"`
|
||||
|
||||
SrcDbId int64 `binding:"required" json:"srcDbId"`
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
package form
|
||||
|
||||
import "mayfly-go/pkg/model"
|
||||
|
||||
type DbTransferTaskForm struct {
|
||||
model.ExtraData
|
||||
|
||||
Id uint64 `json:"id"`
|
||||
|
||||
TaskName string `binding:"required" json:"taskName"` // 任务名称
|
||||
@@ -28,14 +32,17 @@ type DbTransferTaskForm struct {
|
||||
TargetInstName string `json:"targetInstName"` // 目标库实例名
|
||||
TargetTagPath string `json:"targetTagPath"` // 目标库tagPath
|
||||
}
|
||||
|
||||
type DbTransferTaskStatusForm struct {
|
||||
Id uint64 `binding:"required" json:"taskId" form:"taskId"`
|
||||
Status int8 `json:"status" form:"status"`
|
||||
}
|
||||
|
||||
type DbTransferFileForm struct {
|
||||
Id uint64 `json:"id"`
|
||||
FileName string `json:"fileName" form:"fileName"`
|
||||
}
|
||||
|
||||
type DbTransferFileRunForm struct {
|
||||
Id uint64 `json:"id"` // 文件ID
|
||||
TargetDbId uint64 `json:"targetDbId" form:"targetDbId"` // 需要执行sql的数据库id
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
package vo
|
||||
|
||||
import (
|
||||
"mayfly-go/pkg/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
type DbTransferTaskListVO struct {
|
||||
model.ExtraData
|
||||
|
||||
Id uint64 `json:"id"`
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
Creator string `json:"creator"`
|
||||
|
||||
@@ -36,10 +36,9 @@ type Db interface {
|
||||
// 删除数据库信息
|
||||
Delete(ctx context.Context, id uint64) error
|
||||
|
||||
// 获取数据库连接实例
|
||||
// @param id 数据库id
|
||||
//
|
||||
// @param dbName 数据库名
|
||||
// GetDbConn 获取数据库连接实例
|
||||
// - dbId: 数据库id
|
||||
// - dbName: 数据库名
|
||||
GetDbConn(ctx context.Context, dbId uint64, dbName string) (*dbi.DbConn, error)
|
||||
|
||||
// 根据数据库实例id获取连接,随机返回该instanceId下已连接的conn,若不存在则是使用该instanceId关联的db进行连接并返回。
|
||||
@@ -228,7 +227,7 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *dto.DumpDb) error {
|
||||
}
|
||||
|
||||
writer := writerx.NewStringWriter(reqParam.Writer)
|
||||
defer writer.Close()
|
||||
|
||||
dbId := reqParam.DbId
|
||||
dbName := reqParam.DbName
|
||||
tables := reqParam.Tables
|
||||
@@ -291,14 +290,16 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *dto.DumpDb) error {
|
||||
// 按表名排序
|
||||
sort.Strings(tables)
|
||||
quoteSchema := srcDialect.Quoter().Quote(dbConn.Info.CurrentSchema())
|
||||
dumpHelper := targetDialect.GetDumpHelper()
|
||||
|
||||
targetDumpHelper := targetDialect.GetDumpHelper()
|
||||
targetSqlGenerator := targetDialect.GetSQLGenerator()
|
||||
targetDialectQuote := targetDialect.Quoter().Quote
|
||||
// targetDialectQuote := targetDialect.Quoter().Quote
|
||||
|
||||
srcDialectQuote := srcDialect.Quoter().Quote
|
||||
// 遍历获取每个表的信息
|
||||
for _, tableName := range tables {
|
||||
log(fmt.Sprintf("get table [%s] information...", tableName))
|
||||
quoteTableName := targetDialectQuote(tableName)
|
||||
// targetQuoteTableName := targetDialectQuote(tableName)
|
||||
srcQuoteTableName := srcDialectQuote(tableName)
|
||||
|
||||
// 查询表信息,主要是为了查询表注释
|
||||
tbs, err := srcMeta.GetTables(tableName)
|
||||
@@ -332,22 +333,22 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *dto.DumpDb) error {
|
||||
log(fmt.Sprintf("generate table [%s] DML...", tableName))
|
||||
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- Data: %s \n-- ----------------------------\n", tableName))
|
||||
|
||||
dumpHelper.BeforeInsert(writer, quoteTableName)
|
||||
targetDumpHelper.BeforeInsert(writer, tableName)
|
||||
|
||||
dataCount := 0
|
||||
rows := make([][]any, 0)
|
||||
_, err = dbConn.WalkTableRows(ctx, quoteTableName, func(row map[string]any, _ []*dbi.QueryColumn) error {
|
||||
_, err = dbConn.WalkTableRows(ctx, srcQuoteTableName, func(row map[string]any, _ []*dbi.QueryColumn) error {
|
||||
rowValues := make([]any, len(columns))
|
||||
for i, col := range columns {
|
||||
rowValues[i] = row[col.ColumnName]
|
||||
}
|
||||
rows = append(rows, rowValues)
|
||||
dataCount++
|
||||
if dataCount%500 != 0 {
|
||||
if dataCount%100 != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
beforeInsert := dumpHelper.BeforeInsertSql(quoteSchema, quoteTableName)
|
||||
beforeInsert := targetDumpHelper.BeforeInsertSql(quoteSchema, tableName)
|
||||
if beforeInsert != "" {
|
||||
writer.WriteString(beforeInsert)
|
||||
}
|
||||
@@ -365,7 +366,7 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *dto.DumpDb) error {
|
||||
}
|
||||
|
||||
if len(rows) > 0 {
|
||||
beforeInsert := dumpHelper.BeforeInsertSql(quoteSchema, quoteTableName)
|
||||
beforeInsert := targetDumpHelper.BeforeInsertSql(quoteSchema, tableName)
|
||||
if beforeInsert != "" {
|
||||
writer.WriteString(beforeInsert)
|
||||
}
|
||||
@@ -375,7 +376,7 @@ func (d *dbAppImpl) DumpDb(ctx context.Context, reqParam *dto.DumpDb) error {
|
||||
}
|
||||
}
|
||||
|
||||
dumpHelper.AfterInsert(writer, tableName, columns)
|
||||
targetDumpHelper.AfterInsert(writer, tableName, columns)
|
||||
progress(tableName, dbi.StmtTypeInsert, dataCount, true)
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"mayfly-go/pkg/cache"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/i18n"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
@@ -70,7 +71,13 @@ func (app *dataSyncAppImpl) Save(ctx context.Context, taskEntity *entity.DataSyn
|
||||
taskEntity.TaskKey = uuid.New().String()
|
||||
err = app.Insert(ctx, taskEntity)
|
||||
} else {
|
||||
taskEntity.TaskKey = ""
|
||||
if taskEntity.TaskKey == "" {
|
||||
task, err := app.GetById(taskEntity.Id)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("db sync task not found")
|
||||
}
|
||||
taskEntity.TaskKey = task.TaskKey
|
||||
}
|
||||
err = app.UpdateById(ctx, taskEntity)
|
||||
}
|
||||
if err != nil {
|
||||
@@ -126,7 +133,13 @@ func (app *dataSyncAppImpl) Run(ctx context.Context, id uint64) error {
|
||||
CreateTime: &now,
|
||||
Status: entity.DataSyncTaskStateFail, // 默认失败
|
||||
}
|
||||
|
||||
defer app.endRunning(task, syncLog)
|
||||
defer gox.Recover(func(err error) {
|
||||
syncLog.ErrText = i18n.T(imsg.DataSyncFailMsg, "msg", err.Error())
|
||||
logx.ErrorContext(ctx, syncLog.ErrText)
|
||||
syncLog.Status = entity.DataSyncTaskStateFail
|
||||
})
|
||||
|
||||
// 通过占位符格式化sql
|
||||
updSql := ""
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package application
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"mayfly-go/pkg/cache"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/scheduler"
|
||||
@@ -75,11 +77,19 @@ func (app *dbTransferAppImpl) Save(ctx context.Context, taskEntity *entity.DbTra
|
||||
taskEntity.TaskKey = uuid.New().String()
|
||||
err = app.Insert(ctx, taskEntity)
|
||||
} else {
|
||||
if taskEntity.TaskKey == "" {
|
||||
task, err := app.GetById(taskEntity.Id)
|
||||
if err != nil {
|
||||
return errorx.NewBiz("db transfer task not found")
|
||||
}
|
||||
taskEntity.TaskKey = task.TaskKey
|
||||
}
|
||||
err = app.UpdateById(ctx, taskEntity)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
app.addCronJob(ctx, taskEntity)
|
||||
return nil
|
||||
}
|
||||
@@ -131,7 +141,6 @@ func (app *dbTransferAppImpl) Run(ctx context.Context, taskId uint64) (uint64, e
|
||||
}
|
||||
|
||||
logId, _ := app.CreateLog(ctx, taskId)
|
||||
start := time.Now()
|
||||
// 修改状态与关联日志id
|
||||
task.LogId = logId
|
||||
task.RunningState = entity.DbTransferTaskRunStateRunning
|
||||
@@ -142,7 +151,7 @@ func (app *dbTransferAppImpl) Run(ctx context.Context, taskId uint64) (uint64, e
|
||||
// 标记该任务开始执行
|
||||
app.MarkRunning(taskId)
|
||||
|
||||
go func() {
|
||||
gox.Go(func() {
|
||||
// 获取源库连接、目标库连接,判断连接可用性,否则记录日志:xx连接不可用
|
||||
// 获取源库表信息
|
||||
srcConn, err := app.dbApp.GetDbConn(ctx, uint64(task.SrcDbId), task.SrcDbName)
|
||||
@@ -171,19 +180,21 @@ func (app *dbTransferAppImpl) Run(ctx context.Context, taskId uint64) (uint64, e
|
||||
// 迁移到文件或数据库
|
||||
switch task.Mode {
|
||||
case entity.DbTransferTaskModeFile:
|
||||
app.transfer2File(ctx, taskId, logId, task, srcConn, start, tables)
|
||||
app.transfer2File(ctx, logId, task, tables)
|
||||
case entity.DbTransferTaskModeDb:
|
||||
app.transfer2Db(ctx, taskId, logId, task, srcConn, start, tables)
|
||||
app.transfer2Db(ctx, logId, task, tables)
|
||||
default:
|
||||
app.EndTransfer(ctx, logId, taskId, "error in transfer mode, only migrating to files or databases is currently supported", err, nil)
|
||||
return
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
return logId, nil
|
||||
}
|
||||
|
||||
func (app *dbTransferAppImpl) transfer2Db(ctx context.Context, taskId uint64, logId uint64, task *entity.DbTransferTask, srcConn *dbi.DbConn, start time.Time, tables []dbi.Table) {
|
||||
func (app *dbTransferAppImpl) transfer2Db(ctx context.Context, logId uint64, task *entity.DbTransferTask, tables []dbi.Table) {
|
||||
startTime := time.Now()
|
||||
taskId := task.Id
|
||||
defer app.MarkStop(taskId)
|
||||
defer app.logApp.Flush(logId, true)
|
||||
|
||||
@@ -203,13 +214,18 @@ func (app *dbTransferAppImpl) transfer2Db(ctx context.Context, taskId uint64, lo
|
||||
|
||||
for _, tables := range tableGroups {
|
||||
errGroup.Go(func() error {
|
||||
defer gox.Recover()
|
||||
|
||||
if !app.IsRunning(taskId) {
|
||||
return errorx.NewBiz("transfer stopped")
|
||||
}
|
||||
|
||||
currentDumpTable := tables[0]
|
||||
pr, pw := io.Pipe()
|
||||
go func() {
|
||||
defer pr.Close()
|
||||
|
||||
gox.Go(func () {
|
||||
defer pw.Close()
|
||||
err := app.dbApp.DumpDb(ctx, &dto.DumpDb{
|
||||
LogId: logId,
|
||||
DbId: uint64(task.SrcDbId),
|
||||
@@ -240,20 +256,16 @@ func (app *dbTransferAppImpl) transfer2Db(ctx context.Context, taskId uint64, lo
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
app.Log(ctx, logId, fmt.Sprintf("db dump failed: %s", err.Error()))
|
||||
pr.CloseWithError(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
pw.CloseWithError(err)
|
||||
app.EndTransfer(ctx, logId, taskId, "transfer table failed", err, nil)
|
||||
return err
|
||||
}
|
||||
tx, _ := targetConn.Begin()
|
||||
err = sqlparser.SQLSplit(pr, ';', func(stmt string) error {
|
||||
if _, err := targetConn.TxExec(tx, stmt); err != nil {
|
||||
app.EndTransfer(ctx, logId, taskId, fmt.Sprintf("执行sql出错: %s", stmt), err, nil)
|
||||
app.Log(ctx, logId, fmt.Sprintf("sql exec failed: %s", stmt), err, nil)
|
||||
pw.CloseWithError(err)
|
||||
return err
|
||||
}
|
||||
@@ -275,10 +287,11 @@ func (app *dbTransferAppImpl) transfer2Db(ctx context.Context, taskId uint64, lo
|
||||
app.EndTransfer(ctx, logId, taskId, "transfer table failed", err, nil)
|
||||
return
|
||||
}
|
||||
app.EndTransfer(ctx, logId, taskId, fmt.Sprintf("execute transfer task [taskId = %d] complete, time: %v", taskId, time.Since(start)), nil, nil)
|
||||
app.EndTransfer(ctx, logId, taskId, fmt.Sprintf("execute transfer task [taskId = %d] complete, time: %v", taskId, time.Since(startTime)), nil, nil)
|
||||
}
|
||||
|
||||
func (app *dbTransferAppImpl) transfer2File(ctx context.Context, taskId uint64, logId uint64, task *entity.DbTransferTask, srcConn *dbi.DbConn, start time.Time, tables []dbi.Table) {
|
||||
func (app *dbTransferAppImpl) transfer2File(ctx context.Context, logId uint64, task *entity.DbTransferTask, tables []dbi.Table) {
|
||||
taskId := task.Id
|
||||
// 1、新增迁移文件数据
|
||||
nowTime := time.Now()
|
||||
tFile := &entity.DbTransferFile{
|
||||
@@ -290,8 +303,9 @@ func (app *dbTransferAppImpl) transfer2File(ctx context.Context, taskId uint64,
|
||||
}
|
||||
_ = app.transferFileApp.Save(ctx, tFile)
|
||||
|
||||
filename := fmt.Sprintf("dtf_%s.sql", timex.TimeNo())
|
||||
fileKey, writer, saveFileFunc, err := app.fileApp.NewWriter(ctx, "", filename)
|
||||
fileType := cmp.Or(task.GetExtraString("fileType"), "sql")
|
||||
filename := fmt.Sprintf("dtf_%s.%s", timex.TimeNo(), fileType)
|
||||
fileKey, writer, closeFunc, err := app.fileApp.NewWriter(ctx, "", filename)
|
||||
if err != nil {
|
||||
app.EndTransfer(ctx, logId, taskId, "create file error", err, nil)
|
||||
return
|
||||
@@ -305,11 +319,30 @@ func (app *dbTransferAppImpl) transfer2File(ctx context.Context, taskId uint64,
|
||||
|
||||
go func() {
|
||||
var err error
|
||||
defer saveFileFunc(&err)
|
||||
|
||||
defer closeFunc(&err)
|
||||
defer app.MarkStop(taskId)
|
||||
defer app.logApp.Flush(logId, true)
|
||||
defer gox.Recover(func(e error) {
|
||||
err = e
|
||||
app.EndTransfer(ctx, logId, taskId, "transfer to file panic", e, nil)
|
||||
tFile.Status = entity.DbTransferFileStatusFail
|
||||
app.transferFileApp.UpdateById(ctx, tFile)
|
||||
})
|
||||
|
||||
ctx = context.Background()
|
||||
|
||||
if fileType == "zip" {
|
||||
zipWriter := zip.NewWriter(writer)
|
||||
defer zipWriter.Close()
|
||||
// 创建SQL文件在ZIP内的条目
|
||||
writer, err = zipWriter.Create(strings.Replace(filename, "zip", "sql", 1))
|
||||
if err != nil {
|
||||
app.EndTransfer(ctx, logId, taskId, "create zip file error", err, nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
err = app.dbApp.DumpDb(ctx, &dto.DumpDb{
|
||||
LogId: logId,
|
||||
DbId: uint64(task.SrcDbId),
|
||||
@@ -333,7 +366,7 @@ func (app *dbTransferAppImpl) transfer2File(ctx context.Context, taskId uint64,
|
||||
|
||||
tFile.Status = entity.DbTransferFileStatusSuccess
|
||||
tFile.FileKey = fileKey
|
||||
_ = app.transferFileApp.UpdateById(ctx, tFile)
|
||||
app.transferFileApp.UpdateById(ctx, tFile)
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -358,6 +391,7 @@ func (app *dbTransferAppImpl) Stop(ctx context.Context, taskId uint64) error {
|
||||
func (d *dbTransferAppImpl) TimerDeleteTransferFile() {
|
||||
logx.Debug("start deleting transfer files periodically...")
|
||||
scheduler.AddFun("@every 100m", func() {
|
||||
defer gox.Recover()
|
||||
dts, err := d.ListByCond(model.NewCond().Eq("mode", entity.DbTransferTaskModeFile).Ge("file_save_days", 1))
|
||||
if err != nil {
|
||||
logx.Errorf("the task to periodically get database transfer to file failed: %s", err.Error())
|
||||
@@ -385,12 +419,6 @@ func (app *dbTransferAppImpl) addCronJob(ctx context.Context, taskEntity *entity
|
||||
|
||||
// 根据状态添加新的任务
|
||||
if taskEntity.Status == entity.DbTransferTaskStatusEnable && taskEntity.CronAble == entity.DbTransferTaskCronAbleEnable {
|
||||
if key == "" {
|
||||
taskEntity.TaskKey = uuid.New().String()
|
||||
key = taskEntity.TaskKey
|
||||
_ = app.UpdateById(ctx, taskEntity)
|
||||
}
|
||||
|
||||
taskId := taskEntity.Id
|
||||
if err := scheduler.AddFunByKey(key, taskEntity.Cron, func() {
|
||||
logx.Infof("start the transfer task: %d", taskId)
|
||||
|
||||
@@ -22,7 +22,7 @@ type DumpDb struct {
|
||||
|
||||
LogId uint64
|
||||
|
||||
Writer io.WriteCloser
|
||||
Writer io.Writer
|
||||
TargetDbType dbi.DbType
|
||||
|
||||
Log func(msg string)
|
||||
|
||||
@@ -43,6 +43,7 @@ var DefaultDbDataType = NewDbDataType("string", DTString).WithCT(CTVarchar)
|
||||
type Column struct {
|
||||
TableName string `json:"tableName"` // 表名
|
||||
ColumnName string `json:"columnName"` // 列名
|
||||
ColumnType string `json:"columnType"` // 完整列类型,带有数据类型以及长度、精度等。如varchar(2000),decimal(20,2)
|
||||
DataType string `json:"dataType"` // 数据类型
|
||||
ColumnComment string `json:"columnComment"` // 列备注
|
||||
IsPrimaryKey bool `json:"isPrimaryKey"` // 是否为主键
|
||||
@@ -57,6 +58,10 @@ type Column struct {
|
||||
|
||||
// GetColumnType 获取完整的列类型,拼接数据类型与长度等。如varchar(2000),decimal(20,2)
|
||||
func (c *Column) GetColumnType() string {
|
||||
if c.ColumnType != "" {
|
||||
return c.ColumnType
|
||||
}
|
||||
|
||||
if c.CharMaxLength > 0 {
|
||||
return fmt.Sprintf("%s(%d)", c.DataType, c.CharMaxLength)
|
||||
}
|
||||
@@ -178,6 +183,8 @@ func SQLValueBool(val any) string {
|
||||
return fmt.Sprintf("%v", cast.ToBool(val))
|
||||
}
|
||||
|
||||
// SQLValueString 转换为SQL字符串值,处理特殊字符与单引号
|
||||
// 适用于普通字符串类型,会将双引号等特殊字符进行转义
|
||||
func SQLValueString(val any) string {
|
||||
if val == nil {
|
||||
return NULL
|
||||
@@ -197,6 +204,26 @@ func SQLValueString(val any) string {
|
||||
return fmt.Sprintf("'%s'", quoted)
|
||||
}
|
||||
|
||||
// SQLValuePreserveSpecialChars 转换为SQL字符串值,保留特殊字符如双引号、换行符等
|
||||
// 仅转义单引号为两个单引号(SQL标准转义方式)
|
||||
func SQLValuePreserveSpecialChars(val any) string {
|
||||
if val == nil {
|
||||
return NULL
|
||||
}
|
||||
|
||||
strVal, ok := val.(string)
|
||||
if !ok {
|
||||
return fmt.Sprintf("%v", val)
|
||||
}
|
||||
|
||||
// 直接处理 SQL 特殊字符,保留双引号和其他字符
|
||||
// 只转义单引号为两个单引号(SQL 标准转义方式)
|
||||
escapedStr := strings.ReplaceAll(strVal, "'", "''")
|
||||
|
||||
// 返回 SQL 字符串,保持原始的双引号、换行符等
|
||||
return fmt.Sprintf("'%s'", escapedStr)
|
||||
}
|
||||
|
||||
var (
|
||||
DTBit = &DataType{
|
||||
Name: "bit",
|
||||
@@ -266,6 +293,13 @@ var (
|
||||
SQLValue: SQLValueString,
|
||||
}
|
||||
|
||||
// DTStringPreserveSpecial 用于需要保留双引号换行符等特殊字符的字符串类型
|
||||
DTStringPreserveSpecial = &DataType{
|
||||
Name: "string",
|
||||
Valuer: ValuerString,
|
||||
SQLValue: SQLValuePreserveSpecialChars,
|
||||
}
|
||||
|
||||
DTDate = &DataType{
|
||||
Name: "date",
|
||||
Valuer: ValuerDate,
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/logx"
|
||||
@@ -49,7 +50,7 @@ func (d *DbConn) Ping() error {
|
||||
stats := d.db.Stats()
|
||||
logx.Debugf("[%s] db stats -> open: %d, idle: %d, inUse: %d, maxOpen: %d", d.Info.Name, stats.OpenConnections, stats.Idle, stats.InUse, stats.MaxOpenConnections)
|
||||
if stats.OpenConnections == 0 {
|
||||
return errors.New("no open connections")
|
||||
logx.Infof("[%s]-[%s] db stats: no open connections", d.Info.Name, d.Info.Database)
|
||||
}
|
||||
|
||||
return d.db.Ping()
|
||||
@@ -58,6 +59,7 @@ func (d *DbConn) Ping() error {
|
||||
// 执行数据库查询返回的列信息
|
||||
type QueryColumn struct {
|
||||
Name string `json:"name"` // 列名
|
||||
Key string `json:"key"` // 列唯一标识
|
||||
Type string `json:"type"` // 数据类型
|
||||
|
||||
DbDataType *DbDataType `json:"-"`
|
||||
@@ -67,6 +69,7 @@ type QueryColumn struct {
|
||||
func NewQueryColumn(colName string, columnType *DbDataType) *QueryColumn {
|
||||
return &QueryColumn{
|
||||
Name: colName,
|
||||
Key: colName,
|
||||
Type: columnType.DataType.Name,
|
||||
DbDataType: columnType,
|
||||
valuer: columnType.DataType.Valuer(),
|
||||
@@ -77,6 +80,7 @@ func (qc *QueryColumn) getValuePtr() any {
|
||||
return qc.valuer.NewValuePtr()
|
||||
}
|
||||
|
||||
// value 获取列值
|
||||
func (qc *QueryColumn) value() any {
|
||||
return qc.valuer.Value()
|
||||
}
|
||||
@@ -244,7 +248,12 @@ func (d *DbConn) walkQueryRows(ctx context.Context, selectSql string, walkFn Wal
|
||||
rowData := make(map[string]any, lenCols)
|
||||
// 把values中的数据复制到row中
|
||||
for i := range scans {
|
||||
rowData[cols[i].Name] = cols[i].value()
|
||||
colname := cols[i].Name
|
||||
if _, e := rowData[colname]; e {
|
||||
colname = colname + strconv.Itoa(i)
|
||||
cols[i].Key = colname
|
||||
}
|
||||
rowData[colname] = cols[i].value()
|
||||
}
|
||||
if err = walkFn(rowData, cols); err != nil {
|
||||
logx.ErrorfContext(ctx, "[%s] cursor traversal query result set error, exit traversal: %s", selectSql, err.Error())
|
||||
|
||||
@@ -131,6 +131,9 @@ func ConvToTargetDbColumn(srcDbType DbType, targetDbType DbType, targetDialect D
|
||||
return nil
|
||||
}
|
||||
|
||||
// 需要转换至异构数据库时,需要将该字段清空,否则如mysql可以查出该值,其他数据库可能不行,会导致Column.GetColumnType错误。
|
||||
column.ColumnType = ""
|
||||
|
||||
srcMap := commonTypeConverters[srcDbType]
|
||||
if srcMap == nil {
|
||||
return fmt.Errorf("src database type [%s] not suport transfer", srcDbType)
|
||||
|
||||
@@ -3,6 +3,7 @@ package dm
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -38,7 +39,7 @@ func (dd *DMDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||
|
||||
// 复制数据
|
||||
if copy.CopyData {
|
||||
go func() {
|
||||
gox.Go(func() {
|
||||
// 设置允许填充自增列之后,显示指定列名可以插入自增列\
|
||||
identityInsert := fmt.Sprintf("set identity_insert \"%s\" on", newTableName)
|
||||
// 获取列名
|
||||
@@ -50,8 +51,7 @@ func (dd *DMDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||
columnStr := strings.Join(columnArr, ",")
|
||||
// 插入新数据并显示指定列
|
||||
_, _ = dd.dc.Exec(fmt.Sprintf("%s insert into \"%s\" (%s) select %s from \"%s\"", identityInsert, newTableName, columnStr, columnStr, tableName))
|
||||
|
||||
}()
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package mssql
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/logx"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -42,7 +43,7 @@ func (md *MssqlDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||
}
|
||||
// 复制数据
|
||||
if copy.CopyData {
|
||||
go func() {
|
||||
gox.Go(func() {
|
||||
// 查询所有的列
|
||||
columns, err := msMetadata.GetColumns(copy.TableName)
|
||||
if err != nil {
|
||||
@@ -71,7 +72,7 @@ func (md *MssqlDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||
if err != nil {
|
||||
logx.Warnf("复制表[%s]数据失败: %s", copy.TableName, err.Error())
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
return err
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/internal/db/dbm/sqlparser"
|
||||
"mayfly-go/internal/db/dbm/sqlparser/mysql"
|
||||
"mayfly-go/pkg/gox"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -42,9 +43,9 @@ func (md *MysqlDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||
|
||||
// 复制数据
|
||||
if copy.CopyData {
|
||||
go func() {
|
||||
gox.Go(func() {
|
||||
_, _ = md.dc.Exec(fmt.Sprintf("insert into %s select * from %s", newTableName, tableName))
|
||||
}()
|
||||
})
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -97,10 +97,10 @@ func (md *MysqlMetadata) GetColumns(tableNames ...string) ([]dbi.Column, error)
|
||||
|
||||
columns := make([]dbi.Column, 0)
|
||||
for _, re := range res {
|
||||
|
||||
column := dbi.Column{
|
||||
TableName: cast.ToString(re["tableName"]),
|
||||
ColumnName: cast.ToString(re["columnName"]),
|
||||
ColumnType: cast.ToString(re["columnType"]),
|
||||
DataType: cast.ToString(re["dataType"]),
|
||||
ColumnComment: cast.ToString(re["columnComment"]),
|
||||
Nullable: cast.ToString(re["nullable"]) == "YES",
|
||||
|
||||
@@ -18,12 +18,13 @@ var (
|
||||
|
||||
Money = dbi.NewDbDataType("money", dbi.DTString).WithCT(dbi.CTVarchar)
|
||||
|
||||
Char = dbi.NewDbDataType("char", dbi.DTString).WithCT(dbi.CTChar)
|
||||
Nchar = dbi.NewDbDataType("nchar", dbi.DTString).WithCT(dbi.CTVarchar)
|
||||
Varchar = dbi.NewDbDataType("varchar", dbi.DTString).WithCT(dbi.CTVarchar)
|
||||
Text = dbi.NewDbDataType("text", dbi.DTString).WithCT(dbi.CTText).WithFixColumn(dbi.ClearCharMaxLength)
|
||||
Json = dbi.NewDbDataType("json", dbi.DTString).WithCT(dbi.CTJSON).WithFixColumn(dbi.ClearCharMaxLength)
|
||||
Bytea = dbi.NewDbDataType("bytea", dbi.DTString).WithCT(dbi.CTBinary)
|
||||
Char = dbi.NewDbDataType("char", dbi.DTStringPreserveSpecial).WithCT(dbi.CTChar)
|
||||
Nchar = dbi.NewDbDataType("nchar", dbi.DTStringPreserveSpecial).WithCT(dbi.CTVarchar)
|
||||
Varchar = dbi.NewDbDataType("varchar", dbi.DTStringPreserveSpecial).WithCT(dbi.CTVarchar)
|
||||
Text = dbi.NewDbDataType("text", dbi.DTStringPreserveSpecial).WithCT(dbi.CTText).WithFixColumn(dbi.ClearCharMaxLength)
|
||||
Json = dbi.NewDbDataType("json", dbi.DTStringPreserveSpecial).WithCT(dbi.CTJSON).WithFixColumn(dbi.ClearCharMaxLength)
|
||||
Jsonb = dbi.NewDbDataType("jsonb", dbi.DTStringPreserveSpecial).WithCT(dbi.CTJSON).WithFixColumn(dbi.ClearCharMaxLength)
|
||||
Bytea = dbi.NewDbDataType("bytea", dbi.DTStringPreserveSpecial).WithCT(dbi.CTBinary)
|
||||
|
||||
Date = dbi.NewDbDataType("date", dbi.DTDate).WithCT(dbi.CTDate).WithFixColumn(dbi.ClearCharMaxLength)
|
||||
Time = dbi.NewDbDataType("time", dbi.DTTime).WithCT(dbi.CTTime).WithFixColumn(dbi.ClearCharMaxLength)
|
||||
|
||||
@@ -3,6 +3,7 @@ package postgres
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/gox"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
@@ -26,9 +27,9 @@ func (pd *PgsqlDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||
|
||||
// 复制数据
|
||||
if copy.CopyData {
|
||||
go func() {
|
||||
gox.Go(func() {
|
||||
_, _ = pd.dc.Exec(fmt.Sprintf("insert into %s select * from %s", newTableName, tableName))
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
// 查询旧表的自增字段名 重新设置新表的序列序列器
|
||||
|
||||
@@ -31,7 +31,7 @@ func (dh *DumpHelper) AfterInsert(writer io.Writer, tableName string, columns []
|
||||
// 设置自增序列当前值
|
||||
for _, column := range columns {
|
||||
if column.AutoIncrement {
|
||||
seq := fmt.Sprintf("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));\n", tableName, column.ColumnName, column.ColumnName, tableName)
|
||||
seq := fmt.Sprintf("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM \"%s\"));\n", tableName, column.ColumnName, column.ColumnName, tableName)
|
||||
writer.Write([]byte(seq))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,7 +101,7 @@ func (pm *Meta) GetDbDataTypes() []*dbi.DbDataType {
|
||||
return collx.AsArray(
|
||||
Bool, Int2, Int4, Int8, Numeric, Decimal, Smallserial, Serial, Bigserial, Largeserial,
|
||||
Money,
|
||||
Char, Nchar, Varchar, Text, Json,
|
||||
Char, Nchar, Varchar, Text, Json, Jsonb,
|
||||
Date, Time, Timestamp,
|
||||
Bytea,
|
||||
)
|
||||
|
||||
@@ -3,6 +3,7 @@ package sqlite
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/gox"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -36,10 +37,10 @@ func (sd *SqliteDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||
|
||||
// 使用异步线程插入数据
|
||||
if copy.CopyData {
|
||||
go func() {
|
||||
gox.Go(func() {
|
||||
// 执行插入语句
|
||||
_, _ = sd.dc.Exec(fmt.Sprintf("INSERT INTO \"%s\" SELECT * FROM \"%s\"", newTableName, tableName))
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
return err
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
type DbTransferTask struct {
|
||||
model.Model
|
||||
model.ExtraData
|
||||
|
||||
TaskName string `json:"taskName" gorm:"size:255;not null;"` // 任务名称
|
||||
TaskKey string `json:"taskKey" gorm:"size:100;not null;"` // 定时任务唯一uuid key
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
package init
|
||||
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/db/ai/tools"
|
||||
"mayfly-go/internal/db/api"
|
||||
"mayfly-go/internal/db/application"
|
||||
"mayfly-go/internal/db/infra/persistence"
|
||||
"mayfly-go/pkg/starter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(func() {
|
||||
starter.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
api.InitIoc()
|
||||
})
|
||||
|
||||
initialize.AddInitFunc(application.Init)
|
||||
initialize.AddTerminateFunc(Terminate)
|
||||
starter.AddInitFunc(application.Init)
|
||||
starter.AddTerminateFunc(Terminate)
|
||||
// 注册AI数据库工具
|
||||
tools.Init()
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"mayfly-go/internal/docker/imsg"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
@@ -97,9 +98,9 @@ func (d *Container) GetContainersStats(rc *req.Ctx) {
|
||||
var mu sync.Mutex
|
||||
allStats := make([]vo.ContainerStats, 0)
|
||||
for _, c := range cs {
|
||||
go func(item container.Summary) {
|
||||
gox.Go(func() {
|
||||
defer wg.Done()
|
||||
if item.State != "running" {
|
||||
if c.State != "running" {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -125,7 +126,7 @@ func (d *Container) GetContainersStats(rc *req.Ctx) {
|
||||
mu.Lock()
|
||||
allStats = append(allStats, cs)
|
||||
mu.Unlock()
|
||||
}(c)
|
||||
})
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
@@ -222,7 +223,7 @@ func (d *Container) ContainerLogs(rc *req.Ctx) {
|
||||
biz.ErrIsNil(err)
|
||||
defer logs.Close()
|
||||
|
||||
go func() {
|
||||
gox.Go(func() {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -236,7 +237,7 @@ func (d *Container) ContainerLogs(rc *req.Ctx) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"mayfly-go/internal/machine/mcm"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/pool"
|
||||
|
||||
@@ -149,7 +150,7 @@ func (c Client) ContainerAttach(containerID string, wsConn *websocket.Conn, rows
|
||||
wsConn.WriteMessage(websocket.TextMessage, []byte("\033[2J\033[3J\033[1;1H")) // 清屏
|
||||
|
||||
// 转发容器输出到前端
|
||||
go func() {
|
||||
gox.Go(func() {
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
select {
|
||||
@@ -169,7 +170,7 @@ func (c Client) ContainerAttach(containerID string, wsConn *websocket.Conn, rows
|
||||
wsConn.WriteMessage(websocket.TextMessage, buf[:n])
|
||||
}
|
||||
}
|
||||
}()
|
||||
})
|
||||
|
||||
for {
|
||||
select {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package init
|
||||
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/docker/api"
|
||||
"mayfly-go/internal/docker/application"
|
||||
"mayfly-go/internal/docker/infra/persistence"
|
||||
"mayfly-go/pkg/starter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(func() {
|
||||
starter.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
api.InitIoc()
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package init
|
||||
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/es/api"
|
||||
"mayfly-go/internal/es/application"
|
||||
"mayfly-go/internal/es/infra/persistence"
|
||||
"mayfly-go/pkg/starter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(func() {
|
||||
starter.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
api.InitIoc()
|
||||
})
|
||||
|
||||
initialize.AddInitFunc(application.Init)
|
||||
starter.AddInitFunc(application.Init)
|
||||
}
|
||||
|
||||
@@ -24,33 +24,42 @@ type File interface {
|
||||
|
||||
// Upload 上传文件
|
||||
//
|
||||
// @param fileKey 文件key,若存在则使用存在的文件key,否则生成新的文件key。
|
||||
// 参数:
|
||||
// - fileKey: 文件key,若不为空则使用该文件key,否则生成新的文件key
|
||||
// - filename: 文件名,带文件后缀
|
||||
// - r: 文件内容读取流
|
||||
//
|
||||
// @param filename 文件名,带文件后缀
|
||||
// 返回值:
|
||||
// - fileKey: 文件key
|
||||
// - error: 错误信息
|
||||
//
|
||||
// @return fileKey 文件key
|
||||
// 注意:此方法会在defer中自动调用saveFunc,无论成功或失败都会正确处理文件保存或清理工作
|
||||
Upload(ctx context.Context, fileKey string, filename string, r io.Reader) (string, error)
|
||||
|
||||
// NewWriter 创建文件writer
|
||||
//
|
||||
// @param canEmptyFileKey 文件key,若不为空则使用该文件key,否则生成新的文件key。
|
||||
// 参数:
|
||||
// - canEmptyFileKey: 文件key,若不为空则使用该文件key,否则生成新的文件key
|
||||
// - filename: 文件名,带文件后缀
|
||||
//
|
||||
// @param filename 文件名,带文件后缀
|
||||
//
|
||||
// @return fileKey 文件key
|
||||
//
|
||||
// @return writer 文件writer
|
||||
//
|
||||
// @return saveFunc(*error) 保存文件信息的回调函数 (必须要defer中调用才会入库保存该文件信息),若*error不为nil则表示业务逻辑处理失败,不需要保存文件信息并将创建的文件删除
|
||||
NewWriter(ctx context.Context, canEmptyFileKey string, filename string) (fileKey string, writer *writerx.CountingWriteCloser, saveFunc func(*error) error, err error)
|
||||
// 返回值:
|
||||
// - fileKey: 文件key
|
||||
// - writer: 文件writer,实现了计数功能的io.Write
|
||||
// - closeFunc: 关闭回调。用于保存文件信息,关闭writer等操作
|
||||
// 必须在defer中调用才会入库保存该文件信息
|
||||
// 若传入的错误参数不为nil,则不会保存文件信息,并会删除已创建的文件
|
||||
// - err: 错误信息
|
||||
NewWriter(ctx context.Context, canEmptyFileKey string, filename string) (fileKey string, writer io.Writer, closeFunc func(*error) error, err error)
|
||||
|
||||
// GetReader 获取文件reader
|
||||
// GetReader 获取文件读取器
|
||||
//
|
||||
// @return filename 文件名
|
||||
// 参数:
|
||||
// - fileKey: 文件唯一标识key
|
||||
//
|
||||
// @return reader 文件reader
|
||||
//
|
||||
// @return err 错误
|
||||
// 返回值:
|
||||
// - filename: 文件名(带后缀)
|
||||
// - reader: 文件读取流,调用方需负责关闭
|
||||
// - err: 错误信息
|
||||
GetReader(ctx context.Context, fileKey string) (string, io.ReadCloser, error)
|
||||
|
||||
// Remove 删除文件
|
||||
@@ -63,11 +72,11 @@ type fileAppImpl struct {
|
||||
|
||||
func (f *fileAppImpl) Upload(ctx context.Context, fileKey string, filename string, r io.Reader) (string, error) {
|
||||
var err error
|
||||
fileKey, writer, saveFileFunc, err := f.NewWriter(ctx, fileKey, filename)
|
||||
fileKey, writer, closeFunc, err := f.NewWriter(ctx, fileKey, filename)
|
||||
if err != nil {
|
||||
return fileKey, err
|
||||
}
|
||||
defer saveFileFunc(&err)
|
||||
defer closeFunc(&err)
|
||||
|
||||
if _, err = io.Copy(writer, r); err != nil {
|
||||
return fileKey, err
|
||||
@@ -75,7 +84,7 @@ func (f *fileAppImpl) Upload(ctx context.Context, fileKey string, filename strin
|
||||
return fileKey, nil
|
||||
}
|
||||
|
||||
func (f *fileAppImpl) NewWriter(ctx context.Context, canEmptyFileKey string, filename string) (fileKey string, writer *writerx.CountingWriteCloser, saveFunc func(*error) error, err error) {
|
||||
func (f *fileAppImpl) NewWriter(ctx context.Context, canEmptyFileKey string, filename string) (fileKey string, writer io.Writer, closeFunc func(*error) error, err error) {
|
||||
isNewFile := true
|
||||
file := &entity.File{}
|
||||
|
||||
@@ -95,18 +104,20 @@ func (f *fileAppImpl) NewWriter(ctx context.Context, canEmptyFileKey string, fil
|
||||
f.remove(ctx, file)
|
||||
}
|
||||
|
||||
// 生产新的文件名
|
||||
// 生成新的文件名
|
||||
newFilename := canEmptyFileKey + filepath.Ext(filename)
|
||||
filepath, w, err := f.newWriter(newFilename)
|
||||
fp, w, err := f.newWriter(newFilename)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
file.Path = filepath
|
||||
file.Path = fp
|
||||
|
||||
fileKey = canEmptyFileKey
|
||||
writer = writerx.NewCountingWriteCloser(w)
|
||||
countWriter := writerx.NewCountingWriteCloser(w)
|
||||
// 创建回调函数
|
||||
saveFunc = func(e *error) error {
|
||||
closeFunc = func(e *error) error {
|
||||
countWriter.Close()
|
||||
|
||||
if e != nil {
|
||||
err := *e
|
||||
if err != nil {
|
||||
@@ -116,14 +127,14 @@ func (f *fileAppImpl) NewWriter(ctx context.Context, canEmptyFileKey string, fil
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// 获取已写入的字节数
|
||||
file.Size = writer.BytesWritten()
|
||||
writer.Close()
|
||||
file.Size = countWriter.BytesWritten()
|
||||
// 保存文件信息
|
||||
return f.Save(ctx, file)
|
||||
}
|
||||
|
||||
return fileKey, writer, saveFunc, nil
|
||||
return fileKey, countWriter, closeFunc, nil
|
||||
}
|
||||
|
||||
func (f *fileAppImpl) GetReader(ctx context.Context, fileKey string) (string, io.ReadCloser, error) {
|
||||
@@ -163,6 +174,7 @@ func (f *fileAppImpl) newWriter(filename string) (string, io.WriteCloser, error)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
return filePath, out, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package init
|
||||
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/file/api"
|
||||
"mayfly-go/internal/file/application"
|
||||
"mayfly-go/internal/file/infra/persistence"
|
||||
"mayfly-go/pkg/starter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(func() {
|
||||
starter.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
api.InitIoc()
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/eventbus"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
@@ -183,11 +184,11 @@ func (e *executionAppImpl) executeNode(ctx *ExecutionCtx) error {
|
||||
|
||||
// 执行节点逻辑
|
||||
if node.IsAsync() {
|
||||
go func() {
|
||||
gox.Go(func() {
|
||||
if err := node.Execute(ctx); err != nil {
|
||||
logx.Errorf("async execute node error: %v, procinst_id: %d, node_key: %s", err, ctx.Procinst.Id, flowNode.Key)
|
||||
}
|
||||
}()
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package init
|
||||
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/flow/api"
|
||||
"mayfly-go/internal/flow/application"
|
||||
"mayfly-go/internal/flow/infra/persistence"
|
||||
"mayfly-go/pkg/starter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(func() {
|
||||
starter.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
api.InitIoc()
|
||||
})
|
||||
|
||||
initialize.AddInitFunc(application.Init)
|
||||
starter.AddInitFunc(application.Init)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/req"
|
||||
@@ -378,7 +379,9 @@ func (m *Machine) WsGuacamole(rc *req.Ctx) {
|
||||
defer tunnel.ReleaseWriter()
|
||||
defer tunnel.ReleaseReader()
|
||||
|
||||
go guac.WsToGuacd(wsConn, tunnel, writer)
|
||||
gox.Go(func() {
|
||||
guac.WsToGuacd(wsConn, tunnel, writer)
|
||||
})
|
||||
guac.GuacdToWs(wsConn, tunnel, reader)
|
||||
|
||||
//OnConnect
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/scheduler"
|
||||
@@ -255,14 +256,11 @@ func (m *machineAppImpl) GetSshTunnelMachine(ctx context.Context, machineId int)
|
||||
func (m *machineAppImpl) TimerUpdateStats() {
|
||||
logx.Debug("start collecting and caching machine state information periodically...")
|
||||
scheduler.AddFun("@every 2m", func() {
|
||||
defer gox.Recover()
|
||||
machineIds, _ := m.ListByCond(model.NewModelCond(&entity.Machine{Status: entity.MachineStatusEnable, Protocol: entity.MachineProtocolSsh}).Columns("id"))
|
||||
for _, ma := range machineIds {
|
||||
go func(mid uint64) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logx.ErrorTrace(fmt.Sprintf("failed to get machine [id=%d] status information on time", mid), err.(error))
|
||||
}
|
||||
}()
|
||||
gox.Go(func() {
|
||||
mid := ma.Id
|
||||
logx.Debugf("time to get machine [id=%d] status information start", mid)
|
||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||
defer cancelFunc()
|
||||
@@ -273,7 +271,9 @@ func (m *machineAppImpl) TimerUpdateStats() {
|
||||
}
|
||||
cache.SaveMachineStats(mid, cli.GetAllStats())
|
||||
logx.Debugf("time to get the machine [id=%d] status information end", mid)
|
||||
}(ma.Id)
|
||||
}, func(err error) {
|
||||
logx.ErrorTrace(fmt.Sprintf("failed to get machine [id=%d] status information on time", ma.Id), err)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/rediscli"
|
||||
@@ -135,7 +136,9 @@ func (m *machineCronJobAppImpl) RunCronJob(key string) {
|
||||
})), "id")
|
||||
|
||||
for _, machine := range machines {
|
||||
go m.runCronJob0(machine.Id, cronJob)
|
||||
gox.Go(func() {
|
||||
m.runCronJob0(machine.Id, cronJob)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +152,7 @@ func (m *machineCronJobAppImpl) addCronJob(mcj *entity.MachineCronJob) {
|
||||
}
|
||||
|
||||
if err := scheduler.AddFunByKey(key, mcj.Cron, func() {
|
||||
defer gox.Recover()
|
||||
m.RunCronJob(key)
|
||||
}); err != nil {
|
||||
logx.ErrorTrace("add machine cron job failed", err)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/scheduler"
|
||||
@@ -61,11 +62,11 @@ func (m *machineTermOpAppImpl) TermConn(ctx context.Context, cli *mcm.Cli, wsCon
|
||||
termOpRecord.MachineId = cli.Info.Id
|
||||
termOpRecord.Username = cli.Info.Username
|
||||
|
||||
fileKey, wc, saveFileFunc, err := m.fileApp.NewWriter(ctx, "", fmt.Sprintf("mto_%d_%s.cast", termOpRecord.MachineId, timex.TimeNo()))
|
||||
fileKey, wc, closeFunc, err := m.fileApp.NewWriter(ctx, "", fmt.Sprintf("mto_%d_%s.cast", termOpRecord.MachineId, timex.TimeNo()))
|
||||
if err != nil {
|
||||
return errorx.NewBizf("failed to create a terminal playback log file: %v", err)
|
||||
}
|
||||
defer saveFileFunc(&err)
|
||||
defer closeFunc(&err)
|
||||
|
||||
termOpRecord.FileKey = fileKey
|
||||
recorder = mcm.NewRecorder(wc)
|
||||
@@ -117,6 +118,7 @@ func (m *machineTermOpAppImpl) GetPageList(condition *entity.MachineTermOp, page
|
||||
func (m *machineTermOpAppImpl) TimerDeleteTermOp() {
|
||||
logx.Debug("start deleting machine terminal playback records every hour...")
|
||||
scheduler.AddFun("@every 60m", func() {
|
||||
defer gox.Recover()
|
||||
startDate := time.Now().AddDate(0, 0, -config.GetMachine().TermOpSaveDays)
|
||||
cond := &entity.MachineTermOpQuery{
|
||||
StartCreateTime: &startDate,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package guac
|
||||
|
||||
import (
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/logx"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -72,7 +73,9 @@ func NewTunnelMap() *TunnelMap {
|
||||
tunnelMap: make(map[string]*LastAccessedTunnel),
|
||||
tunnelTimeout: TunnelTimeout,
|
||||
}
|
||||
go tunnelMap.tunnelTimeoutTask()
|
||||
gox.Go(func() {
|
||||
tunnelMap.tunnelTimeoutTask()
|
||||
})
|
||||
return tunnelMap
|
||||
}
|
||||
|
||||
@@ -116,7 +119,6 @@ func (m *TunnelMap) tunnelTimeoutTaskRun() {
|
||||
}
|
||||
}
|
||||
m.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the Tunnel having the given UUID, wrapped within a LastAccessedTunnel.
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
package init
|
||||
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/machine/api"
|
||||
"mayfly-go/internal/machine/application"
|
||||
"mayfly-go/internal/machine/infra/persistence"
|
||||
"mayfly-go/pkg/starter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(func() {
|
||||
starter.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
api.InitIoc()
|
||||
})
|
||||
|
||||
initialize.AddInitFunc(application.Init)
|
||||
starter.AddInitFunc(application.Init)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/pool"
|
||||
"mayfly-go/pkg/utils/netx"
|
||||
@@ -107,7 +108,9 @@ func (stm *SshTunnelMachine) OpenSshTunnel(id string, ip string, port int) (expo
|
||||
remotePort: port,
|
||||
listener: listener,
|
||||
}
|
||||
go tunnel.Open(stm.SshClient)
|
||||
gox.Go(func() {
|
||||
tunnel.Open(stm.SshClient)
|
||||
})
|
||||
stm.tunnels[tunnel.id] = tunnel
|
||||
|
||||
return localHost, localPort, nil
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/logx"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
@@ -95,8 +96,12 @@ func NewTerminalSession(param *CreateTerminalSessionParam) (*TerminalSession, er
|
||||
}
|
||||
|
||||
func (r TerminalSession) Start() {
|
||||
go r.readFromTerminal()
|
||||
go r.writeToWebsocket()
|
||||
gox.Go(func() {
|
||||
r.readFromTerminal()
|
||||
})
|
||||
gox.Go(func() {
|
||||
r.writeToWebsocket()
|
||||
})
|
||||
r.receiveWsMsg()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package init
|
||||
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/mongo/api"
|
||||
"mayfly-go/internal/mongo/application"
|
||||
"mayfly-go/internal/mongo/infra/persistence"
|
||||
"mayfly-go/pkg/starter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(func() {
|
||||
starter.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
api.InitIoc()
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
sysentity "mayfly-go/internal/sys/domain/entity"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/gox"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
@@ -194,8 +195,9 @@ func (m *msgTmplAppImpl) SendMsg(ctx context.Context, mts *dto.MsgTmplSend) erro
|
||||
logx.Warnf("channel is disabled => %s", channel.Code)
|
||||
continue
|
||||
}
|
||||
|
||||
go func(ch entity.MsgChannel) {
|
||||
|
||||
gox.Go(func() {
|
||||
ch := *channel
|
||||
if err := msgx.Send(ctx, &msgx.Channel{
|
||||
Type: ch.Type,
|
||||
Name: ch.Name,
|
||||
@@ -204,7 +206,7 @@ func (m *msgTmplAppImpl) SendMsg(ctx context.Context, mts *dto.MsgTmplSend) erro
|
||||
}, msg); err != nil {
|
||||
logx.Errorf("send msg error => channel=%s, msg=%s, err -> %v", ch.Code, msg.Content, err)
|
||||
}
|
||||
}(*channel)
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -2,7 +2,6 @@ package init
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/msg/api"
|
||||
"mayfly-go/internal/msg/application"
|
||||
"mayfly-go/internal/msg/application/dto"
|
||||
@@ -13,16 +12,17 @@ import (
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/ioc"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/starter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(func() {
|
||||
starter.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
api.InitIoc()
|
||||
})
|
||||
|
||||
initialize.AddInitFunc(Init)
|
||||
starter.AddInitFunc(Init)
|
||||
}
|
||||
|
||||
func Init() {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type MsgType int8
|
||||
@@ -71,7 +70,7 @@ type MsgSender interface {
|
||||
Send(ctx context.Context, channel *Channel, msg *Msg) error
|
||||
}
|
||||
|
||||
var messageSenders sync.Map
|
||||
var messageSenders *collx.SM[ChannelType, MsgSender] = collx.NewSM[ChannelType, MsgSender]()
|
||||
|
||||
// RegisterMsgSender 注册消息发送器
|
||||
func RegisterMsgSender(channel ChannelType, sender MsgSender) {
|
||||
@@ -84,5 +83,5 @@ func GetMsgSender(channel ChannelType) (MsgSender, error) {
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported message channel %s", channel)
|
||||
}
|
||||
return sender.(MsgSender), nil
|
||||
return sender, nil
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"mayfly-go/internal/msg/msgx"
|
||||
"mayfly-go/pkg/i18n"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
"mayfly-go/pkg/ws"
|
||||
|
||||
@@ -27,7 +28,7 @@ func (e WsSender) Send(ctx context.Context, channel *msgx.Channel, msg *msgx.Msg
|
||||
}
|
||||
}
|
||||
|
||||
jsonMsg := msg.TmplExtra
|
||||
jsonMsg := collx.CopyM(msg.TmplExtra)
|
||||
jsonMsg["msg"] = content
|
||||
jsonMsg["title"] = msg.Title
|
||||
jsonMsg["params"] = msg.Params
|
||||
|
||||
@@ -2,7 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/pkg/utils/assert"
|
||||
"mayfly-go/pkg/starter"
|
||||
"mayfly-go/pkg/utils/cryptox"
|
||||
)
|
||||
|
||||
@@ -20,11 +20,15 @@ func (a *Aes) DecryptBase64(data string) ([]byte, error) {
|
||||
return cryptox.AesDecryptBase64(data, []byte(a.Key))
|
||||
}
|
||||
|
||||
func (a *Aes) Valid() {
|
||||
func (a *Aes) ApplyDefaults() error {
|
||||
if a.Key == "" {
|
||||
return
|
||||
return nil
|
||||
}
|
||||
aesKeyLen := len(a.Key)
|
||||
assert.IsTrue(aesKeyLen == 16 || aesKeyLen == 24 || aesKeyLen == 32,
|
||||
fmt.Sprintf("config.yml之 [aes.key] 长度需为16、24、32位长度, 当前为%d位", aesKeyLen))
|
||||
|
||||
if aesKeyLen != 16 && aesKeyLen != 24 && aesKeyLen != 32 {
|
||||
return starter.NewConfigError("aes.key", fmt.Sprintf("长度需为16、24、32位长度, 当前为%d位", aesKeyLen))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
package config
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
AppName = "mayfly-go"
|
||||
Version = "v1.10.4"
|
||||
Version = "v1.10.9"
|
||||
)
|
||||
|
||||
func GetAppInfo() string {
|
||||
return fmt.Sprintf("[%s:%s]", AppName, Version)
|
||||
}
|
||||
|
||||
@@ -4,24 +4,17 @@ import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/starter"
|
||||
"mayfly-go/pkg/utils/ymlx"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type ConfigItem interface {
|
||||
// 验证配置
|
||||
Valid()
|
||||
|
||||
// 如果不存在配置值,则设置默认值
|
||||
Default()
|
||||
}
|
||||
|
||||
// 配置文件映射对象
|
||||
var Conf *Config
|
||||
|
||||
func Init() {
|
||||
func Init() (*Config, error) {
|
||||
configFilePath := flag.String("e", "./config.yml", "配置文件路径,默认为可执行文件目录")
|
||||
flag.Parse()
|
||||
// 获取启动参数中,配置文件的绝对路径
|
||||
@@ -35,10 +28,12 @@ func Init() {
|
||||
// 尝试使用系统环境变量替换配置信息
|
||||
yc.ReplaceOsEnv()
|
||||
|
||||
yc.IfBlankDefaultValue()
|
||||
// 校验配置文件内容信息
|
||||
yc.Valid()
|
||||
if err := yc.ApplyDefaults(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Conf = yc
|
||||
return yc, nil
|
||||
}
|
||||
|
||||
// 启动配置参数
|
||||
@@ -48,39 +43,23 @@ type CmdConfigParam struct {
|
||||
|
||||
// yaml配置文件映射对象
|
||||
type Config struct {
|
||||
Server Server `yaml:"server"`
|
||||
Jwt Jwt `yaml:"jwt"`
|
||||
Aes Aes `yaml:"aes"`
|
||||
Mysql Mysql `yaml:"mysql"`
|
||||
Sqlite Sqlite `yaml:"sqlite"`
|
||||
Redis Redis `yaml:"redis"`
|
||||
Log Log `yaml:"log"`
|
||||
starter.Conf `yaml:",inline"`
|
||||
|
||||
Aes Aes `yaml:"aes"`
|
||||
}
|
||||
|
||||
func (c *Config) IfBlankDefaultValue() {
|
||||
c.Log.Default()
|
||||
// 优先初始化log,因为后续的一些default方法中会需要用到。统一日志输出
|
||||
logx.Init(logx.Config{
|
||||
Level: c.Log.Level,
|
||||
Type: c.Log.Type,
|
||||
AddSource: c.Log.AddSource,
|
||||
Filename: c.Log.File.Name,
|
||||
Filepath: c.Log.File.Path,
|
||||
MaxSize: c.Log.File.MaxSize,
|
||||
MaxAge: c.Log.File.MaxAge,
|
||||
Compress: c.Log.File.Compress,
|
||||
})
|
||||
var _ starter.ConfigItem = (*Config)(nil)
|
||||
|
||||
c.Server.Default()
|
||||
c.Jwt.Default()
|
||||
c.Mysql.Default()
|
||||
c.Sqlite.Default()
|
||||
}
|
||||
func (c *Config) ApplyDefaults() error {
|
||||
if err := c.Conf.ApplyDefaults(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 配置文件内容校验
|
||||
func (c *Config) Valid() {
|
||||
c.Jwt.Valid()
|
||||
c.Aes.Valid()
|
||||
if err := c.Aes.ApplyDefaults(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 替换系统环境变量,如果环境变量中存在该值,则优先使用环境变量设定的值
|
||||
@@ -96,27 +75,29 @@ func (c *Config) ReplaceOsEnv() {
|
||||
|
||||
dbHost := os.Getenv("MAYFLY_DB_HOST")
|
||||
if dbHost != "" {
|
||||
c.Mysql.Host = dbHost
|
||||
c.DB.Address = dbHost
|
||||
c.DB.Dialect = starter.DialectMySQL
|
||||
}
|
||||
|
||||
dbName := os.Getenv("MAYFLY_DB_NAME")
|
||||
if dbName != "" {
|
||||
c.Mysql.Dbname = dbName
|
||||
c.DB.Name = dbName
|
||||
}
|
||||
|
||||
dbUser := os.Getenv("MAYFLY_DB_USER")
|
||||
if dbUser != "" {
|
||||
c.Mysql.Username = dbUser
|
||||
c.DB.Username = dbUser
|
||||
}
|
||||
|
||||
dbPwd := os.Getenv("MAYFLY_DB_PASS")
|
||||
if dbPwd != "" {
|
||||
c.Mysql.Password = dbPwd
|
||||
c.DB.Password = dbPwd
|
||||
}
|
||||
|
||||
sqlitePath := os.Getenv("MAYFLY_SQLITE_PATH")
|
||||
if sqlitePath != "" {
|
||||
c.Sqlite.Path = sqlitePath
|
||||
c.DB.Address = sqlitePath
|
||||
c.DB.Dialect = starter.DialectSQLite
|
||||
}
|
||||
|
||||
aesKey := os.Getenv("MAYFLY_AES_KEY")
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/utils/assert"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
)
|
||||
|
||||
type Jwt struct {
|
||||
Key string `yaml:"key"`
|
||||
ExpireTime uint64 `yaml:"expire-time"` // 过期时间,单位分钟
|
||||
RefreshTokenExpireTime uint64 `yaml:"refresh-token-expire-time"` // 刷新token的过期时间,单位分钟
|
||||
}
|
||||
|
||||
func (j *Jwt) Default() {
|
||||
if j.Key == "" {
|
||||
// 如果配置文件中的jwt key为空,则随机生成字符串
|
||||
j.Key = stringx.Rand(32)
|
||||
logx.Warnf("未配置jwt.key, 随机生成key为: %s", j.Key)
|
||||
}
|
||||
|
||||
if j.ExpireTime == 0 {
|
||||
j.ExpireTime = 1440
|
||||
logx.Warnf("未配置jwt.expire-time, 默认值: %d", j.ExpireTime)
|
||||
}
|
||||
if j.RefreshTokenExpireTime == 0 {
|
||||
j.RefreshTokenExpireTime = j.ExpireTime * 5
|
||||
logx.Warnf("未配置jwt.refresh-token-expire-time, 默认值: %d", j.RefreshTokenExpireTime)
|
||||
}
|
||||
}
|
||||
|
||||
func (j *Jwt) Valid() {
|
||||
assert.IsTrue(j.ExpireTime != 0, "config.yml之[jwt.expire-time] 不能为空")
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"mayfly-go/pkg/logx"
|
||||
)
|
||||
|
||||
type Log struct {
|
||||
Level string `yaml:"level"`
|
||||
Type string `yaml:"type"`
|
||||
AddSource bool `yaml:"add-source"`
|
||||
File LogFile `yaml:"file"`
|
||||
}
|
||||
|
||||
func (l *Log) Default() {
|
||||
if l.Level == "" {
|
||||
l.Level = "info"
|
||||
logx.Warnf("未配置log.level, 默认值: %s", l.Level)
|
||||
}
|
||||
if l.Type == "" {
|
||||
l.Type = "text"
|
||||
}
|
||||
if l.File.Name == "" {
|
||||
l.File.Name = "mayfly-go.log"
|
||||
}
|
||||
}
|
||||
|
||||
type LogFile struct {
|
||||
Name string `yaml:"name"`
|
||||
Path string `yaml:"path"`
|
||||
MaxSize int `yaml:"max-size"`
|
||||
MaxAge int `yaml:"max-age"`
|
||||
Compress bool `yaml:"compress"`
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package config
|
||||
|
||||
import "mayfly-go/pkg/logx"
|
||||
|
||||
type Mysql struct {
|
||||
Host string `mapstructure:"path" json:"host" yaml:"host"`
|
||||
Config string `mapstructure:"config" json:"config" yaml:"config"`
|
||||
Dbname string `mapstructure:"db-name" json:"dbname" yaml:"db-name"`
|
||||
Username string `mapstructure:"username" json:"username" yaml:"username"`
|
||||
Password string `mapstructure:"password" json:"password" yaml:"password"`
|
||||
MaxIdleConns int `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"`
|
||||
MaxOpenConns int `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"`
|
||||
LogMode bool `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"`
|
||||
LogZap string `mapstructure:"log-zap" json:"logZap" yaml:"log-zap"`
|
||||
}
|
||||
|
||||
func (m *Mysql) Default() {
|
||||
if m.Host == "" {
|
||||
m.Host = "localhost:3306"
|
||||
logx.Warnf("[使用sqlite可忽略]未配置mysql.host, 默认值: %s", m.Host)
|
||||
}
|
||||
if m.Config == "" {
|
||||
m.Config = "charset=utf8&loc=Local&parseTime=true"
|
||||
}
|
||||
if m.MaxIdleConns == 0 {
|
||||
m.MaxIdleConns = 5
|
||||
}
|
||||
if m.MaxOpenConns == 0 {
|
||||
m.MaxOpenConns = m.MaxIdleConns
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Mysql) Dsn() string {
|
||||
return m.Username + ":" + m.Password + "@tcp(" + m.Host + ")/" + m.Dbname + "?" + m.Config
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package config
|
||||
|
||||
type Redis struct {
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
Password string `yaml:"password"`
|
||||
Db int `yaml:"db"`
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/pkg/i18n"
|
||||
)
|
||||
|
||||
type Server struct {
|
||||
Lang string `yaml:"lang"`
|
||||
Port int `yaml:"port"`
|
||||
Model string `yaml:"model"`
|
||||
ContextPath string `yaml:"context-path"` // 请求路径上下文
|
||||
Cors bool `yaml:"cors"`
|
||||
Tls *Tls `yaml:"tls"`
|
||||
Static *[]*Static `yaml:"static"`
|
||||
StaticFile *[]*StaticFile `yaml:"static-file"`
|
||||
}
|
||||
|
||||
func (s *Server) Default() {
|
||||
if s.Lang == "" {
|
||||
s.Lang = i18n.Zh_CN
|
||||
}
|
||||
if s.Model == "" {
|
||||
s.Model = "release"
|
||||
}
|
||||
if s.Port == 0 {
|
||||
s.Port = 18888
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) GetPort() string {
|
||||
return fmt.Sprintf(":%d", s.Port)
|
||||
}
|
||||
|
||||
type Static struct {
|
||||
RelativePath string `yaml:"relative-path"`
|
||||
Root string `yaml:"root"`
|
||||
}
|
||||
|
||||
type StaticFile struct {
|
||||
RelativePath string `yaml:"relative-path"`
|
||||
Filepath string `yaml:"filepath"`
|
||||
}
|
||||
|
||||
type Tls struct {
|
||||
Enable bool `yaml:"enable"` // 是否启用tls
|
||||
KeyFile string `yaml:"key-file"` // 私钥文件路径
|
||||
CertFile string `yaml:"cert-file"` // 证书文件路径
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package config
|
||||
|
||||
import "mayfly-go/pkg/logx"
|
||||
|
||||
type Sqlite struct {
|
||||
Path string `mapstructure:"path" json:"path" yaml:"path"`
|
||||
MaxIdleConns int `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"`
|
||||
MaxOpenConns int `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"`
|
||||
}
|
||||
|
||||
func (m *Sqlite) Default() {
|
||||
if m.Path == "" {
|
||||
m.Path = "./mayfly-go.sqlite"
|
||||
logx.Warnf("[使用mysql可忽略]未配置sqlite.path, 默认值: %s", m.Path)
|
||||
}
|
||||
if m.MaxIdleConns == 0 {
|
||||
m.MaxIdleConns = 5
|
||||
}
|
||||
if m.MaxOpenConns == 0 {
|
||||
m.MaxOpenConns = m.MaxIdleConns
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package starter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/pkg/config"
|
||||
"mayfly-go/pkg/logx"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
)
|
||||
|
||||
func printBanner() {
|
||||
buildInfo, _ := debug.ReadBuildInfo()
|
||||
logx.Print(fmt.Sprintf(`
|
||||
__ _
|
||||
_ __ ___ __ _ _ _ / _| |_ _ __ _ ___
|
||||
| '_ ' _ \ / _' | | | | |_| | | | |_____ / _' |/ _ \
|
||||
| | | | | | (_| | |_| | _| | |_| |_____| (_| | (_) | version: %s | go_version: %s | pid: %d
|
||||
|_| |_| |_|\__,_|\__, |_| |_|\__, | \__, |\___/
|
||||
|___/ |___/ |___/ `, config.Version, buildInfo.GoVersion, os.Getpid()))
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
package starter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"mayfly-go/internal/pkg/config"
|
||||
"mayfly-go/pkg/cache"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/rediscli"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// 有配置redis信息,则初始化redis。多台机器部署需要使用redis存储验证码、权限、公私钥等信息
|
||||
func initCache() {
|
||||
redisCli := connRedis()
|
||||
|
||||
if redisCli == nil {
|
||||
logx.Info("no redis configuration, using local cache")
|
||||
return
|
||||
}
|
||||
|
||||
logx.Info("redis connected successfully, using Redis for caching")
|
||||
rediscli.SetCli(connRedis())
|
||||
cache.SetCache(cache.NewRedisCache(redisCli))
|
||||
}
|
||||
|
||||
func connRedis() *redis.Client {
|
||||
// 设置redis客户端
|
||||
redisConf := config.Conf.Redis
|
||||
if redisConf.Host == "" {
|
||||
// logx.Panic("未找到redis配置信息")
|
||||
return nil
|
||||
}
|
||||
logx.Infof("redis connecting [%s:%d]", redisConf.Host, redisConf.Port)
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: fmt.Sprintf("%s:%d", redisConf.Host, redisConf.Port),
|
||||
Password: redisConf.Password, // no password set
|
||||
DB: redisConf.Db, // use default DB
|
||||
})
|
||||
// 测试连接
|
||||
_, e := rdb.Ping(context.TODO()).Result()
|
||||
if e != nil {
|
||||
logx.Panicf("redis connection faild! [%s:%d][%s]", redisConf.Host, redisConf.Port, e.Error())
|
||||
}
|
||||
return rdb
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
package starter
|
||||
|
||||
import (
|
||||
"log"
|
||||
"mayfly-go/internal/pkg/config"
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/logx"
|
||||
"time"
|
||||
|
||||
"github.com/glebarez/sqlite"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
"gorm.io/gorm/schema"
|
||||
)
|
||||
|
||||
func initDb() {
|
||||
global.Db = initGormDb()
|
||||
}
|
||||
|
||||
func initGormDb() *gorm.DB {
|
||||
m := config.Conf.Mysql
|
||||
// 存在msyql数据库名,则优先使用mysql
|
||||
if m.Dbname != "" {
|
||||
return initMysql(m)
|
||||
}
|
||||
|
||||
return initSqlite(config.Conf.Sqlite)
|
||||
}
|
||||
|
||||
func initMysql(m config.Mysql) *gorm.DB {
|
||||
logx.Infof("connecting to mysql [%s]", m.Host)
|
||||
mysqlConfig := mysql.Config{
|
||||
DSN: m.Dsn(), // DSN data source name
|
||||
DefaultStringSize: 191, // string 类型字段的默认长度
|
||||
DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
|
||||
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
|
||||
DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
|
||||
SkipInitializeWithVersion: false, // 根据版本自动配置
|
||||
}
|
||||
|
||||
if db, err := gorm.Open(mysql.New(mysqlConfig), getGormConfig()); err != nil {
|
||||
logx.Panicf("failed to connect to mysql! [%s]", err.Error())
|
||||
return nil
|
||||
} else {
|
||||
sqlDB, _ := db.DB()
|
||||
sqlDB.SetMaxIdleConns(m.MaxIdleConns)
|
||||
sqlDB.SetMaxOpenConns(m.MaxOpenConns)
|
||||
|
||||
// 如果是开发环境时,打印sql语句
|
||||
if logx.GetConfig().IsDebug() {
|
||||
db = db.Debug()
|
||||
}
|
||||
return db
|
||||
}
|
||||
}
|
||||
|
||||
func initSqlite(sc config.Sqlite) *gorm.DB {
|
||||
logx.Infof("connecting to sqlite [%s]", sc.Path)
|
||||
if db, err := gorm.Open(sqlite.Open(sc.Path), getGormConfig()); err != nil {
|
||||
logx.Panicf("failed to connect to sqlite! [%s]", err.Error())
|
||||
return nil
|
||||
} else {
|
||||
sqlDB, _ := db.DB()
|
||||
sqlDB.SetMaxIdleConns(sc.MaxIdleConns)
|
||||
sqlDB.SetMaxOpenConns(sc.MaxOpenConns)
|
||||
return db
|
||||
}
|
||||
}
|
||||
|
||||
func getGormConfig() *gorm.Config {
|
||||
sqlLogLevel := logger.Error
|
||||
logConf := logx.GetConfig()
|
||||
// 如果为配置文件中配置的系统日志级别为debug,则打印gorm执行的sql信息
|
||||
if logConf.IsDebug() {
|
||||
sqlLogLevel = logger.Info
|
||||
}
|
||||
|
||||
gormLogger := logger.New(
|
||||
log.New(logConf.GetLogOut(), "\r\n", log.LstdFlags), // io writer(日志输出的目标,前缀和日志包含的内容——译者注)
|
||||
logger.Config{
|
||||
SlowThreshold: time.Second, // 慢 SQL 阈值
|
||||
LogLevel: sqlLogLevel, // 日志级别, 改为logger.Info即可显示sql语句
|
||||
IgnoreRecordNotFoundError: true, // 忽略ErrRecordNotFound(记录未找到)错误
|
||||
Colorful: false, // 禁用彩色打印
|
||||
},
|
||||
)
|
||||
|
||||
return &gorm.Config{NamingStrategy: schema.NamingStrategy{
|
||||
TablePrefix: "t_",
|
||||
SingularTable: true,
|
||||
}, Logger: gormLogger}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package starter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/pkg/config"
|
||||
"mayfly-go/migration"
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/validatorx"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func RunWebServer() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-quit
|
||||
cancel()
|
||||
}()
|
||||
|
||||
// 初始化config.yml配置文件映射信息或使用环境变量。并初始化系统日志相关配置
|
||||
config.Init()
|
||||
|
||||
// 打印banner
|
||||
printBanner()
|
||||
|
||||
// 初始化并赋值数据库全局变量
|
||||
initDb()
|
||||
|
||||
// 初始化缓存
|
||||
initCache()
|
||||
|
||||
// 数据库升级操作
|
||||
if err := migration.RunMigrations(global.Db); err != nil {
|
||||
logx.Panicf("db migration failed: %v", err)
|
||||
}
|
||||
|
||||
// 参数校验器初始化、如错误提示中文转译等
|
||||
validatorx.Init()
|
||||
// 注册自定义正则表达式校验规则
|
||||
RegisterCustomPatterns()
|
||||
|
||||
// 初始化其他需要启动时运行的方法
|
||||
initialize.InitOther()
|
||||
|
||||
// 运行web服务
|
||||
runWebServer(ctx)
|
||||
}
|
||||
|
||||
// 注册自定义正则表达式校验规则
|
||||
func RegisterCustomPatterns() {
|
||||
// 账号用户名校验
|
||||
validatorx.RegisterPattern("account_username", "^[a-zA-Z0-9_]{5,20}$", "只允许输入5-20位大小写字母、数字、下划线")
|
||||
validatorx.RegisterPattern("resource_code", "^[a-zA-Z0-9_\\-.:]{1,32}$", "只允许输入1-32位大小写字母、数字、_-.:")
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package starter
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/pkg/config"
|
||||
"mayfly-go/pkg/i18n"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/middleware"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/static"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
sysapp "mayfly-go/internal/sys/application"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func runWebServer(ctx context.Context) {
|
||||
// 设置gin日志输出器
|
||||
logOut := logx.GetConfig().GetLogOut()
|
||||
gin.DefaultErrorWriter = logOut
|
||||
gin.DefaultWriter = logOut
|
||||
|
||||
// 权限处理器
|
||||
req.UseBeforeHandlerInterceptor(req.PermissionHandler)
|
||||
// 日志处理器
|
||||
req.UseAfterHandlerInterceptor(req.LogHandler)
|
||||
// 设置日志保存函数
|
||||
req.SetSaveLogFunc(sysapp.GetSyslogApp().SaveFromReq)
|
||||
|
||||
// jwt配置
|
||||
jwtConf := config.Conf.Jwt
|
||||
req.SetJwtConf(req.JwtConf{
|
||||
Key: jwtConf.Key,
|
||||
ExpireTime: jwtConf.ExpireTime,
|
||||
RefreshTokenExpireTime: jwtConf.RefreshTokenExpireTime,
|
||||
})
|
||||
|
||||
// i18n配置
|
||||
i18n.SetLang(config.Conf.Server.Lang)
|
||||
|
||||
// server配置
|
||||
serverConfig := config.Conf.Server
|
||||
gin.SetMode(serverConfig.Model)
|
||||
|
||||
var router = gin.New()
|
||||
router.MaxMultipartMemory = 8 << 20
|
||||
// 初始化接口路由
|
||||
initialize.InitRouter(router, initialize.RouterConfig{ContextPath: serverConfig.ContextPath})
|
||||
// 设置静态资源
|
||||
setStatic(serverConfig.ContextPath, router)
|
||||
// 是否允许跨域
|
||||
if serverConfig.Cors {
|
||||
router.Use(middleware.Cors())
|
||||
}
|
||||
|
||||
router.Use(gin.Recovery())
|
||||
|
||||
srv := http.Server{
|
||||
Addr: config.Conf.Server.GetPort(),
|
||||
// 注册路由
|
||||
Handler: router,
|
||||
}
|
||||
|
||||
go func() {
|
||||
<-ctx.Done()
|
||||
logx.Info("Shutdown HTTP Server ...")
|
||||
timeout, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
err := srv.Shutdown(timeout)
|
||||
if err != nil {
|
||||
logx.Errorf("failed to Shutdown HTTP Server: %v", err)
|
||||
}
|
||||
|
||||
initialize.Terminate()
|
||||
}()
|
||||
|
||||
confSrv := config.Conf.Server
|
||||
logx.Infof("Listening and serving HTTP on %s", srv.Addr+confSrv.ContextPath)
|
||||
var err error
|
||||
if confSrv.Tls != nil && confSrv.Tls.Enable {
|
||||
err = srv.ListenAndServeTLS(confSrv.Tls.CertFile, confSrv.Tls.KeyFile)
|
||||
} else {
|
||||
err = srv.ListenAndServe()
|
||||
}
|
||||
if errors.Is(err, http.ErrServerClosed) {
|
||||
logx.Info("HTTP Server Shutdown")
|
||||
} else if err != nil {
|
||||
logx.Errorf("Failed to Start HTTP Server: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func setStatic(contextPath string, router *gin.Engine) {
|
||||
// 使用embed打包静态资源至二进制文件中
|
||||
fsys, _ := fs.Sub(static.Static, "static")
|
||||
fileServer := http.FileServer(http.FS(fsys))
|
||||
handler := WrapStaticHandler(http.StripPrefix(contextPath, fileServer))
|
||||
|
||||
router.GET(contextPath+"/", handler)
|
||||
router.GET(contextPath+"/favicon.ico", handler)
|
||||
router.GET(contextPath+"/config.js", handler)
|
||||
// 所有/assets/**开头的都是静态资源文件
|
||||
router.GET(contextPath+"/assets/*file", handler)
|
||||
|
||||
// 设置静态资源
|
||||
if staticConfs := config.Conf.Server.Static; staticConfs != nil {
|
||||
for _, scs := range *staticConfs {
|
||||
router.StaticFS(scs.RelativePath, http.Dir(scs.Root))
|
||||
}
|
||||
}
|
||||
// 设置静态文件
|
||||
if staticFileConfs := config.Conf.Server.StaticFile; staticFileConfs != nil {
|
||||
for _, sfs := range *staticFileConfs {
|
||||
router.StaticFile(sfs.RelativePath, sfs.Filepath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func WrapStaticHandler(h http.Handler) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Cache-Control", `public, max-age=31536000`)
|
||||
h.ServeHTTP(c.Writer, c.Request)
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,18 @@
|
||||
package init
|
||||
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/redis/api"
|
||||
"mayfly-go/internal/redis/application"
|
||||
"mayfly-go/internal/redis/infra/persistence"
|
||||
"mayfly-go/pkg/starter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(func() {
|
||||
starter.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
api.InitIoc()
|
||||
})
|
||||
|
||||
initialize.AddInitFunc(application.Init)
|
||||
starter.AddInitFunc(application.Init)
|
||||
}
|
||||
|
||||
@@ -47,10 +47,10 @@ func (r *RedisConn) Ping() error {
|
||||
}
|
||||
|
||||
stats := r.Cli.PoolStats()
|
||||
if stats.TotalConns == 0 {
|
||||
return fmt.Errorf("no open connections")
|
||||
}
|
||||
logx.Debugf("[%s] redis stats -> open: %d, idle: %d", r.Info.Name, stats.TotalConns, stats.IdleConns)
|
||||
if stats.TotalConns == 0 {
|
||||
logx.Infof("[%s] redis stats: no open connections", r.Info.Name)
|
||||
}
|
||||
|
||||
cmd := r.Cli.Ping(context.Background())
|
||||
if cmd == nil {
|
||||
|
||||
@@ -63,5 +63,5 @@ func (c *Config) SaveConfig(rc *req.Ctx) {
|
||||
// GetServerConfig 获取当前系统启动配置
|
||||
func (c *Config) GetServerConfig(rc *req.Ctx) {
|
||||
conf := config.Conf
|
||||
rc.ResData = collx.Kvs("i18n", conf.Server.Lang)
|
||||
rc.ResData = collx.Kvs("i18n", conf.Server.Lang, "version", config.Version)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
package init
|
||||
|
||||
import (
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/sys/api"
|
||||
"mayfly-go/internal/sys/application"
|
||||
"mayfly-go/internal/sys/infra/persistence"
|
||||
"mayfly-go/pkg/starter"
|
||||
"mayfly-go/pkg/validatorx"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(func() {
|
||||
starter.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
api.InitIoc()
|
||||
})
|
||||
|
||||
// 账号用户名校验
|
||||
validatorx.RegisterPattern("account_username", "^[a-zA-Z0-9_]{5,20}$", "只允许输入5-20位大小写字母、数字、下划线")
|
||||
}
|
||||
|
||||
@@ -92,9 +92,6 @@ func (p *tagTreeAppImpl) SaveTag(ctx context.Context, pid uint64, tag *entity.Ta
|
||||
|
||||
tag.CodePath = parentTag.CodePath + tag.Code + entity.CodePathSeparator
|
||||
} else {
|
||||
if accountId != consts.AdminId {
|
||||
return errorx.NewBizI(ctx, imsg.ErrNoAdminCreateTag)
|
||||
}
|
||||
tag.CodePath = tag.Code + entity.CodePathSeparator
|
||||
}
|
||||
if p.CanAccess(accountId, tag.CodePath) != nil {
|
||||
|
||||
@@ -2,23 +2,23 @@ package init
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/initialize"
|
||||
"mayfly-go/internal/pkg/event"
|
||||
"mayfly-go/internal/tag/api"
|
||||
"mayfly-go/internal/tag/application"
|
||||
"mayfly-go/internal/tag/infra/persistence"
|
||||
"mayfly-go/pkg/eventbus"
|
||||
"mayfly-go/pkg/global"
|
||||
"mayfly-go/pkg/starter"
|
||||
)
|
||||
|
||||
func init() {
|
||||
initialize.AddInitIocFunc(func() {
|
||||
starter.AddInitIocFunc(func() {
|
||||
persistence.InitIoc()
|
||||
application.InitIoc()
|
||||
api.InitIoc()
|
||||
})
|
||||
|
||||
initialize.AddInitFunc(Init)
|
||||
starter.AddInitFunc(Init)
|
||||
}
|
||||
|
||||
func Init() {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
_ "mayfly-go/internal/ai/init"
|
||||
_ "mayfly-go/internal/auth/init"
|
||||
_ "mayfly-go/internal/common/init"
|
||||
@@ -12,12 +13,53 @@ import (
|
||||
_ "mayfly-go/internal/machine/init"
|
||||
_ "mayfly-go/internal/mongo/init"
|
||||
_ "mayfly-go/internal/msg/init"
|
||||
"mayfly-go/internal/pkg/starter"
|
||||
"mayfly-go/internal/pkg/config"
|
||||
_ "mayfly-go/internal/redis/init"
|
||||
_ "mayfly-go/internal/sys/init"
|
||||
_ "mayfly-go/internal/tag/init"
|
||||
"mayfly-go/migration"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/starter"
|
||||
"mayfly-go/static"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
|
||||
sysapp "mayfly-go/internal/sys/application"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func main() {
|
||||
starter.RunWebServer()
|
||||
// 初始化config.yml配置文件映射信息或使用环境变量
|
||||
config, err := config.Init()
|
||||
if err != nil {
|
||||
logx.Panicf("config init failed: %v", err)
|
||||
}
|
||||
printBanner()
|
||||
|
||||
if err = starter.Run(config.Conf,
|
||||
starter.WithOnDbReady(func(db *gorm.DB) error {
|
||||
// 数据库升级操作
|
||||
return migration.RunMigrations(db)
|
||||
}),
|
||||
starter.WithLogSaver(func() req.SaveLogFunc {
|
||||
// 日志保存
|
||||
return sysapp.GetSyslogApp().SaveFromReq
|
||||
}),
|
||||
starter.WithStaticRouter(static.Router()),
|
||||
); err != nil {
|
||||
logx.Panicf("starter server failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func printBanner() {
|
||||
buildInfo, _ := debug.ReadBuildInfo()
|
||||
logx.Print(fmt.Sprintf(`
|
||||
__ _
|
||||
_ __ ___ __ _ _ _ / _| |_ _ __ _ ___
|
||||
| '_ ' _ \ / _' | | | | |_| | | | |_____ / _' |/ _ \
|
||||
| | | | | | (_| | |_| | _| | |_| |_____| (_| | (_) | version: %s | go_version: %s | pid: %d
|
||||
|_| |_| |_|\__,_|\__, |_| |_|\__, | \__, |\___/
|
||||
|___/ |___/ |___/ `, config.Version, buildInfo.GoVersion, os.Getpid()))
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package migrations
|
||||
|
||||
import (
|
||||
"errors"
|
||||
dbentity "mayfly-go/internal/db/domain/entity"
|
||||
dockerentity "mayfly-go/internal/docker/domain/entity"
|
||||
esentity "mayfly-go/internal/es/domain/entity"
|
||||
flowentity "mayfly-go/internal/flow/domain/entity"
|
||||
@@ -23,6 +24,7 @@ func V1_10() []*gormigrate.Migration {
|
||||
migrations = append(migrations, V1_10_3()...)
|
||||
migrations = append(migrations, V1_10_4()...)
|
||||
migrations = append(migrations, V1_10_5()...)
|
||||
migrations = append(migrations, V1_10_6()...)
|
||||
return migrations
|
||||
}
|
||||
|
||||
@@ -352,23 +354,18 @@ func V1_10_4() []*gormigrate.Migration {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func V1_10_5() []*gormigrate.Migration {
|
||||
return []*gormigrate.Migration{
|
||||
{
|
||||
ID: "20251207-v1.10.4.1",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
config := &sysentity.Config{}
|
||||
// 查询是否存在该配置
|
||||
result := tx.Model(config).Where("key = ?", "AiModelConfig").First(config)
|
||||
result := tx.Model(config).Where("`key` = ?", "AiModelConfig").First(config)
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
// 如果不存在,则创建默认配置
|
||||
now := time.Now()
|
||||
aiConfig := &sysentity.Config{
|
||||
Key: "AiModelConfig",
|
||||
Name: "system.sysconf.aiModelConf'",
|
||||
Name: "system.sysconf.aiModelConf",
|
||||
Value: "{}", // 默认空JSON值
|
||||
Params: `[{"model":"modelType","name":"system.sysconf.aiModelType","placeholder":"system.sysconf.aiModelTypePlaceholder","options":"openai"},{"model":"model","name":"system.sysconf.aiModel","placeholder":"system.sysconf.aiModelPlaceholder"},{"model":"baseUrl","name":"system.sysconf.aiBaseUrl","placeholder":"system.sysconf.aiBaseUrlPlaceholder"},{"model":"apiKey","name":"ApiKey","placeholder":"api key"}]`,
|
||||
Permission: "all",
|
||||
@@ -391,3 +388,108 @@ func V1_10_5() []*gormigrate.Migration {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func V1_10_5() []*gormigrate.Migration {
|
||||
return []*gormigrate.Migration{
|
||||
{
|
||||
ID: "20260107-v1.10.5",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
resource := &sysentity.Resource{}
|
||||
// 查询是否存在该配置
|
||||
result := tx.Model(resource).Where("code = ?", "db:data:export").First(resource)
|
||||
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
|
||||
// 如果不存在,则创建默认配置
|
||||
resource = &sysentity.Resource{
|
||||
Code: "db:data:export",
|
||||
Name: "menu.dbDataExport",
|
||||
Type: sysentity.ResourceTypePermission,
|
||||
UiPath: "ocdrUNaa/fo59olyi/",
|
||||
Pid: 1756122788,
|
||||
Status: sysentity.ResourceStatusEnable,
|
||||
}
|
||||
resource.FillBaseInfo(model.IdGenTypeTimestamp, model.SysAccount)
|
||||
resource.Id = 1767788697
|
||||
return tx.Create(resource).Error
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func V1_10_6() []*gormigrate.Migration {
|
||||
return []*gormigrate.Migration{
|
||||
{
|
||||
ID: "20260107-v1.10.6-addDbTransferTaskExtraColumn",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
tx.AutoMigrate(&dbentity.DbTransferTask{})
|
||||
return nil
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: "20260107-v1.10.6-menuResourceChange",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
tx.Exec("INSERT INTO t_sys_resource (id, pid, ui_path, type, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES (1768728951, 1756122788, 'ocdrUNaa/IV13ydwK/', 2, 1, 'menu.machine', 'machine:base', 29999998, 'null', 1, 'admin', 1, 'admin', '2026-01-18 17:35:52', '2026-01-18 18:02:40', 0, NULL)")
|
||||
tx.Exec("INSERT INTO t_sys_resource (id, pid, ui_path, type, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES (1768729405, 1768728951, 'ocdrUNaa/IV13ydwK/V83yBBCM/', 2, 1, 'menu.machineScript', 'machine:script', 1768729405, 'null', 1, 'admin', 1, 'admin', '2026-01-18 17:43:25', '2026-01-18 17:49:39', 0, NULL)")
|
||||
tx.Exec("INSERT INTO t_sys_resource (id, pid, ui_path, type, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES (1768729856, 1756122788, 'ocdrUNaa/sd9PqK7U/', 2, 1, 'Redis', 'redis:base', 1768729856, 'null', 1, 'admin', 1, 'admin', '2026-01-18 17:50:57', '2026-01-18 17:50:57', 0, NULL)")
|
||||
tx.Exec("INSERT INTO t_sys_resource (id, pid, ui_path, type, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES (1768729911, 1756122788, 'ocdrUNaa/GCElqxQr/', 2, 1, 'Mongo', 'mongo', 1768729911, 'null', 1, 'admin', 1, 'admin', '2026-01-18 17:51:52', '2026-01-18 17:51:52', 0, NULL)")
|
||||
tx.Exec("INSERT INTO t_sys_resource (id, pid, ui_path, type, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES (1768729997, 1756122788, 'ocdrUNaa/mcZUtOzR/', 2, 1, 'menu.db', 'db:base', 1768729855, 'null', 1, 'admin', 1, 'admin', '2026-01-18 17:53:18', '2026-01-18 18:02:53', 0, NULL)")
|
||||
tx.Exec("INSERT INTO t_sys_resource (id, pid, ui_path, type, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES (1768730024, 1756122788, 'ocdrUNaa/BHfgTyd4/', 2, 1, 'Es', 'es:base', 1768730024, 'null', 1, 'admin', 1, 'admin', '2026-01-18 17:53:44', '2026-01-18 17:53:44', 0, NULL)")
|
||||
tx.Exec("INSERT INTO t_sys_resource (id, pid, ui_path, type, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES (1768731024, 94, 'Tag3fhad/glxajg23/FyQOFAkk/', 2, 1, 'Es', 'res:es', 1757145305, 'null', 1, 'admin', 1, 'admin', '2026-01-18 18:10:24', '2026-01-18 18:10:48', 0, NULL)")
|
||||
tx.Exec("INSERT INTO t_sys_resource (id, pid, ui_path, type, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES (1768731749, 64, 'Tag3fhad/glxajg23/IoxqAd31/fX8iVBwb/', 2, 1, 'menu.redisSave', 'redis:save', 1768731749, 'null', 1, 'admin', 1, 'admin', '2026-01-18 18:22:30', '2026-01-18 19:00:50', 0, NULL)")
|
||||
tx.Exec("INSERT INTO t_sys_resource (id, pid, ui_path, type, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES (1768731764, 64, 'Tag3fhad/glxajg23/IoxqAd31/ng7B41YS/', 2, 1, 'menu.redisDel', 'redis:del', 1768731764, 'null', 1, 'admin', 1, 'admin', '2026-01-18 18:22:45', '2026-01-18 19:00:55', 0, NULL)")
|
||||
tx.Exec("INSERT INTO t_sys_resource (id, pid, ui_path, type, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES (1768733843, 83, 'Tag3fhad/glxajg23/egljbla3/tiBNOllg/', 2, 1, 'menu.mongoSave', 'mongo:save', 1768733843, 'null', 1, 'admin', 1, 'admin', '2026-01-18 18:57:24', '2026-01-18 18:57:24', 0, NULL)")
|
||||
tx.Exec("INSERT INTO t_sys_resource (id, pid, ui_path, type, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES (1768733856, 83, 'Tag3fhad/glxajg23/egljbla3/S4lXWpyV/', 2, 1, 'menu.mongoDel', 'mongo:del', 1768733856, 'null', 1, 'admin', 1, 'admin', '2026-01-18 18:57:36', '2026-01-18 18:57:36', 0, NULL)")
|
||||
tx.Exec("INSERT INTO t_sys_resource (id, pid, ui_path, type, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES (1768734233, 1757145306, 'Tag3fhad/glxajg23/Bbrte5UH/emC3wrlg/', 2, 1, 'menu.containerSave', 'container:save', 1768734233, 'null', 1, 'admin', 1, 'admin', '2026-01-18 19:03:54', '2026-01-18 19:03:54', 0, NULL)")
|
||||
tx.Exec("INSERT INTO t_sys_resource (id, pid, ui_path, type, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES (1768734253, 1757145306, 'Tag3fhad/glxajg23/Bbrte5UH/ZcXsgEYp/', 2, 1, 'menu.containerDel', 'container:del', 1768734253, 'null', 1, 'admin', 1, 'admin', '2026-01-18 19:04:14', '2026-01-18 19:04:14', 0, NULL)")
|
||||
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=94,ui_path='Tag3fhad/glxajg23/Bbrte5UH/',type=2,status=1,name='menu.container',code='container',weight=1757145306,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2025-09-06 15:55:06',update_time='2026-01-18 19:02:57',is_deleted=0,delete_time=NULL WHERE id=1757145306")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=94,ui_path='Tag3fhad/glxajg23/egljbla3/',type=2,status=1,name='Mongo',code='mongo:manage:base',weight=1757145304,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2022-05-16 18:13:25',update_time='2026-01-18 18:57:12',is_deleted=0,delete_time=NULL WHERE id=83")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768731024,ui_path='Tag3fhad/glxajg23/FyQOFAkk/rcKBdxB5/',type=2,status=1,name='es.instanceSave',code='es:instance:save',weight=-1,meta='',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2025-05-21 12:44:18',update_time='2026-01-18 18:10:43',is_deleted=0,delete_time=NULL WHERE id=1745319410")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768731024,ui_path='Tag3fhad/glxajg23/FyQOFAkk/IMGhLSJK/',type=2,status=1,name='es.instanceDel',code='es:instance:del',weight=0,meta='',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2025-05-21 12:44:18',update_time='2026-01-18 18:10:41',is_deleted=0,delete_time=NULL WHERE id=1745319424")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=94,ui_path='Tag3fhad/glxajg23/mJlBeTCs/',type=2,status=1,name='menu.db',code='db:instance',weight=9999999,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2023-08-26 09:10:55',update_time='2026-01-18 18:09:25',is_deleted=0,delete_time=NULL WHERE id=137")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=94,ui_path='Tag3fhad/glxajg23/IoxqAd31/',type=2,status=1,name='menu.redis',code='redis:manage',weight=10000000,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-07-20 10:48:26',update_time='2026-01-18 18:09:07',is_deleted=0,delete_time=NULL WHERE id=64")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=94,ui_path='Tag3fhad/glxajg23/OJewex43/',type=2,status=1,name='menu.machine',code='machine',weight=9999999,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-07-09 10:48:02',update_time='2026-01-18 18:08:50',is_deleted=0,delete_time=NULL WHERE id=57")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=137,ui_path='Tag3fhad/glxajg23/mJlBeTCs/AceXe321/',type=2,status=1,name='menu.dbBase',code='db',weight=0,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-07-09 10:48:22',update_time='2026-01-18 18:08:37',is_deleted=0,delete_time=NULL WHERE id=58")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=137,ui_path='Tag3fhad/glxajg23/mJlBeTCs/ygjL3sxA/',type=2,status=1,name='menu.dbDelete',code='db:del',weight=2,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-07-08 17:30:48',update_time='2026-01-18 18:08:18',is_deleted=0,delete_time=NULL WHERE id=55")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=137,ui_path='Tag3fhad/glxajg23/mJlBeTCs/Sgg8uPwz/',type=2,status=1,name='menu.dbInstanceDelete',code='db:instance:del',weight=0,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2023-08-26 09:11:24',update_time='2026-01-18 18:08:13',is_deleted=0,delete_time=NULL WHERE id=138")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=137,ui_path='Tag3fhad/glxajg23/mJlBeTCs/D23fUiBr/',type=2,status=1,name='menu.dbInstanceSave',code='db:instance:save',weight=0,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2023-08-26 09:10:02',update_time='2026-01-18 18:08:08',is_deleted=0,delete_time=NULL WHERE id=136")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=137,ui_path='Tag3fhad/glxajg23/mJlBeTCs/leix3Axl/',type=2,status=1,name='menu.dbSave',code='db:save',weight=1,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-07-08 17:30:36',update_time='2026-01-18 18:07:57',is_deleted=0,delete_time=NULL WHERE id=54")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=57,ui_path='Tag3fhad/glxajg23/OJewex43/exIsqL31/',type=2,status=1,name='menu.machineCreate',code='machine:add',weight=-2,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-05-31 17:46:11',update_time='2026-01-18 18:06:44',is_deleted=0,delete_time=NULL WHERE id=16")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=57,ui_path='Tag3fhad/glxajg23/OJewex43/Liwakg2x/',type=2,status=1,name='menu.machineEdit',code='machine:update',weight=-1,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-05-31 17:46:23',update_time='2026-01-18 18:06:33',is_deleted=0,delete_time=NULL WHERE id=17")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=57,ui_path='Tag3fhad/glxajg23/OJewex43/Lieakenx/',type=2,status=1,name='menu.machineDelete',code='machine:del',weight=0,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-05-31 17:46:36',update_time='2026-01-18 18:06:29',is_deleted=0,delete_time=NULL WHERE id=18")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=15,ui_path='ocdrUNaa/IV13ydwK/Lsew24Kx/Ihfs2xaw/',type=2,status=1,name='menu.machineFileConfDelete',code='machine:file:del',weight=-1,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-06-08 11:06:49',update_time='2026-01-18 17:58:42',is_deleted=0,delete_time=NULL WHERE id=41")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=15,ui_path='ocdrUNaa/IV13ydwK/Lsew24Kx/L12wix43/',type=2,status=1,name='menu.machineFileDelete',code='machine:file:rm',weight=0,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-06-08 11:08:12',update_time='2026-01-18 17:58:35',is_deleted=0,delete_time=NULL WHERE id=44")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=15,ui_path='ocdrUNaa/IV13ydwK/Lsew24Kx/Keiqkx4L/',type=2,status=1,name='menu.machineFileConfCreate',code='machine:addFile',weight=-1,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-06-01 19:54:23',update_time='2026-01-18 17:56:02',is_deleted=0,delete_time=NULL WHERE id=37")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768729911,ui_path='ocdrUNaa/BHfgTyd4/TGFPA3Ez/TGFPA3Ez/TGFPA3Ez/',type=2,status=1,name='menu.mongoDataOpDelete',code='mongo:data:del',weight=1,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2023-08-22 11:29:24',update_time='2026-01-18 17:54:04',is_deleted=0,delete_time=NULL WHERE id=134")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768730024,ui_path='ocdrUNaa/BHfgTyd4/TGFPA3Ez/',type=2,status=1,name='es.dataDel',code='es:data:del',weight=2,meta='',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2025-05-21 12:44:18',update_time='2026-01-18 17:54:04',is_deleted=0,delete_time=NULL WHERE id=1745659315")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768730024,ui_path='ocdrUNaa/BHfgTyd4/TGFPA3Ez/',type=2,status=1,name='es.dataSave',code='es:data:save',weight=1,meta='',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2025-05-21 12:44:18',update_time='2026-01-18 17:53:49',is_deleted=0,delete_time=NULL WHERE id=1745659240")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768729997,ui_path='ocdrUNaa/mcZUtOzR/fo59olyi/',type=2,status=1,name='menu.dbDataExport',code='db:data:export',weight=0,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2026-01-07 20:24:57',update_time='2026-01-18 17:53:31',is_deleted=0,delete_time=NULL WHERE id=1767788697")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768729997,ui_path='ocdrUNaa/mcZUtOzR/TGFPA3Ez/',type=2,status=1,name='menu.dbDataOpSqlScriptRun',code='db:sqlscript:run',weight=1,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2024-10-23 15:22:12',update_time='2026-01-18 17:53:27',is_deleted=0,delete_time=NULL WHERE id=1729668131")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768729911,ui_path='ocdrUNaa/GCElqxQr/TGFPA3Ez/',type=2,status=1,name='menu.mongoDataOpSave',code='mongo:data:save',weight=0,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2023-08-22 11:29:04',update_time='2026-01-18 17:52:23',is_deleted=0,delete_time=NULL WHERE id=133")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768729856,ui_path='ocdrUNaa/sd9PqK7U/IUlxia23/',type=2,status=1,name='menu.redisDataOpSave',code='redis:data:save',weight=-1,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-08-17 11:20:37',update_time='2026-01-18 17:51:21',is_deleted=0,delete_time=NULL WHERE id=71")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768729856,ui_path='ocdrUNaa/sd9PqK7U/Gxlagheg/',type=2,status=1,name='menu.redisDataOpDelete',code='redis:data:del',weight=0,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2023-03-14 17:20:00',update_time='2026-01-18 17:51:18',is_deleted=0,delete_time=NULL WHERE id=108")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768729405,ui_path='ocdrUNaa/IV13ydwK/V83yBBCM/Ljeew43/',type=2,status=1,name='menu.machineScriptDelete',code='machine:script:del',weight=1,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-06-08 11:09:27',update_time='2026-01-18 17:50:13',is_deleted=0,delete_time=NULL WHERE id=46")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768729405,ui_path='ocdrUNaa/IV13ydwK/V83yBBCM/ODewix43/',type=2,status=1,name='menu.machineScriptRun',code='machine:script:run',weight=2,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-06-08 11:09:50',update_time='2026-01-18 17:50:10',is_deleted=0,delete_time=NULL WHERE id=47")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768729405,ui_path='ocdrUNaa/IV13ydwK/V83yBBCM/Ljewisd3/',type=2,status=1,name='menu.machineScriptSave',code='machine:script:save',weight=1,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-06-08 11:09:01',update_time='2026-01-18 17:49:50',is_deleted=0,delete_time=NULL WHERE id=45")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=15,ui_path='ocdrUNaa/IV13ydwK/Lsew24Kx/Ljewix43/',type=2,status=1,name='menu.machineFileUpload',code='machine:file:upload',weight=5,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-06-08 11:07:42',update_time='2026-01-18 17:45:03',is_deleted=0,delete_time=NULL WHERE id=43")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=15,ui_path='ocdrUNaa/IV13ydwK/Lsew24Kx/3ldkxJDx/',type=2,status=1,name='menu.machineFileWrite',code='machine:file:write',weight=2,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-06-08 11:07:27',update_time='2026-01-18 17:44:46',is_deleted=0,delete_time=NULL WHERE id=42")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=15,ui_path='ocdrUNaa/IV13ydwK/Lsew24Kx/Keal2Xke/',type=2,status=1,name='menu.machineFileCreate',code='machine:file:add',weight=1,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-06-08 11:06:26',update_time='2026-01-18 17:44:38',is_deleted=0,delete_time=NULL WHERE id=40")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768728951,ui_path='ocdrUNaa/IV13ydwK/Lsew24Kx/',type=2,status=1,name='menu.machineFileConf',code='machine:file',weight=1768729331,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-05-31 17:44:37',update_time='2026-01-18 17:44:34',is_deleted=0,delete_time=NULL WHERE id=15")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768728951,ui_path='ocdrUNaa/IV13ydwK/LIEwix43/',type=2,status=1,name='menu.machineKillprocess',code='machine:killprocess',weight=0,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-08-17 11:20:37',update_time='2026-01-18 17:41:40',is_deleted=0,delete_time=NULL WHERE id=72")
|
||||
tx.Exec("UPDATE t_sys_resource SET pid=1768728951,ui_path='ocdrUNaa/IV13ydwK/Alw1Xkq3/',type=2,status=1,name='menu.machineTerminal',code='machine:terminal',weight=1,meta='null',creator_id=1,creator='admin',modifier_id=1,modifier='admin',create_time='2021-05-28 14:06:02',update_time='2026-01-18 17:41:26',is_deleted=0,delete_time=NULL WHERE id=12")
|
||||
return nil
|
||||
},
|
||||
Rollback: func(tx *gorm.DB) error {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user