diff --git a/build_release.sh b/build_release.sh index 462eea0b..b1e32b94 100755 --- a/build_release.sh +++ b/build_release.sh @@ -104,7 +104,6 @@ function buildMac() { function buildDocker() { echo_yellow "-------------------构建docker镜像开始-------------------" imageVersion=$1 - cd ${server_folder} imageName="mayflygo/mayfly-go:${imageVersion}" docker build --platform linux/amd64 -t "${imageName}" . echo_green "docker镜像构建完成->[${imageName}]" @@ -114,7 +113,6 @@ function buildDocker() { function buildxDocker() { echo_yellow "-------------------docker buildx构建镜像开始-------------------" imageVersion=$1 - cd ${server_folder} imageName="ccr.ccs.tencentyun.com/mayfly/mayfly-go:${imageVersion}" docker buildx build --push --platform linux/amd64,linux/arm64 -t "${imageName}" . echo_green "docker多版本镜像构建完成->[${imageName}]" @@ -147,6 +145,11 @@ function runBuild() { # 进入目标路径,并赋值全路径 cd ${toPath} toPath=`pwd` + + # read -p "是否构建前端[0|其他->否 1->是 2->构建并拷贝至server/static/static]: " runBuildWeb + runBuildWeb="2" + # 编译web前端 + buildWeb ${runBuildWeb} fi if [[ "${buildType}" == "5" ]] || [[ "${buildType}" == "6" ]] ; then @@ -157,12 +160,6 @@ function runBuild() { fi fi - - # read -p "是否构建前端[0|其他->否 1->是 2->构建并拷贝至server/static/static]: " runBuildWeb - runBuildWeb="2" - # 编译web前端 - buildWeb ${runBuildWeb} - case ${buildType} in "1") buildLinuxAmd64 ${toPath} ${copyDocScript} @@ -190,11 +187,13 @@ function runBuild() { ;; esac - echo_green "删除['${server_folder}/static/static']下静态资源文件." - # 删除静态资源文件,保留一个favicon.ico,否则后端启动会报错 - rm -rf ${server_folder}/static/static/assets - rm -rf ${server_folder}/static/static/config.js - rm -rf ${server_folder}/static/static/index.html + if [[ "${buildType}" != "5" ]] && [[ "${buildType}" != "6" ]] ; then + echo_green "删除['${server_folder}/static/static']下静态资源文件." + # 删除静态资源文件,保留一个favicon.ico,否则后端启动会报错 + rm -rf ${server_folder}/static/static/assets + rm -rf ${server_folder}/static/static/config.js + rm -rf ${server_folder}/static/static/index.html + fi } runBuild diff --git a/mayfly_go_web/src/assets/iconfont/iconfont.js b/mayfly_go_web/src/assets/iconfont/iconfont.js index 80adf06a..b9b4f4d5 100644 --- a/mayfly_go_web/src/assets/iconfont/iconfont.js +++ b/mayfly_go_web/src/assets/iconfont/iconfont.js @@ -1 +1,66 @@ -window._iconfont_svg_string_3953964='',function(c){var t=(t=document.getElementsByTagName("script"))[t.length-1],a=t.getAttribute("data-injectcss"),t=t.getAttribute("data-disable-injectsvg");if(!t){var l,e,i,o,n,h=function(t,a){a.parentNode.insertBefore(t,a)};if(a&&!c.__iconfont__svg__cssinject__){c.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(t){console&&console.log(t)}}l=function(){var t,a=document.createElement("div");a.innerHTML=c._iconfont_svg_string_3953964,(a=a.getElementsByTagName("svg")[0])&&(a.setAttribute("aria-hidden","true"),a.style.position="absolute",a.style.width=0,a.style.height=0,a.style.overflow="hidden",a=a,(t=document.body).firstChild?h(a,t.firstChild):t.appendChild(a))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(e=function(){document.removeEventListener("DOMContentLoaded",e,!1),l()},document.addEventListener("DOMContentLoaded",e,!1)):document.attachEvent&&(i=l,o=c.document,n=!1,s(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,d())})}function d(){n||(n=!0,i())}function s(){try{o.documentElement.doScroll("left")}catch(t){return void setTimeout(s,50)}d()}}(window); \ No newline at end of file +(window._iconfont_svg_string_3953964 = + ''), + (function (c) { + var t = (t = document.getElementsByTagName('script'))[t.length - 1], + a = t.getAttribute('data-injectcss'), + t = t.getAttribute('data-disable-injectsvg'); + if (!t) { + var l, + e, + i, + o, + n, + h = function (t, a) { + a.parentNode.insertBefore(t, a); + }; + if (a && !c.__iconfont__svg__cssinject__) { + c.__iconfont__svg__cssinject__ = !0; + try { + document.write( + '' + ); + } catch (t) { + console && console.log(t); + } + } + (l = function () { + var t, + a = document.createElement('div'); + (a.innerHTML = c._iconfont_svg_string_3953964), + (a = a.getElementsByTagName('svg')[0]) && + (a.setAttribute('aria-hidden', 'true'), + (a.style.position = 'absolute'), + (a.style.width = 0), + (a.style.height = 0), + (a.style.overflow = 'hidden'), + (a = a), + (t = document.body).firstChild ? h(a, t.firstChild) : t.appendChild(a)); + }), + document.addEventListener + ? ~['complete', 'loaded', 'interactive'].indexOf(document.readyState) + ? setTimeout(l, 0) + : ((e = function () { + document.removeEventListener('DOMContentLoaded', e, !1), l(); + }), + document.addEventListener('DOMContentLoaded', e, !1)) + : document.attachEvent && + ((i = l), + (o = c.document), + (n = !1), + s(), + (o.onreadystatechange = function () { + 'complete' == o.readyState && ((o.onreadystatechange = null), d()); + })); + } + function d() { + n || ((n = !0), i()); + } + function s() { + try { + o.documentElement.doScroll('left'); + } catch (t) { + return void setTimeout(s, 50); + } + d(); + } + })(window); diff --git a/mayfly_go_web/src/components/pagetable/PageTable.vue b/mayfly_go_web/src/components/pagetable/PageTable.vue index 18f92500..b84a2995 100644 --- a/mayfly_go_web/src/components/pagetable/PageTable.vue +++ b/mayfly_go_web/src/components/pagetable/PageTable.vue @@ -102,7 +102,7 @@ props.queryForm, (newValue: any) => { @@ -304,7 +306,7 @@ onMounted(() => { state.pageNum = props.pageNum; state.pageSize = pageSize; state.queryForm = props.queryForm; - state.pageSizes = [pageSize, pageSize * 2, pageSize * 3, pageSize * 4]; + state.pageSizes = [pageSize, pageSize * 2, pageSize * 3, pageSize * 4, pageSize * 5]; // 如果没传输入框宽度,则根据组件size设置默认宽度 if (!props.inputWidth) { diff --git a/mayfly_go_web/src/views/ops/machine/cronjob/CronJobExecList.vue b/mayfly_go_web/src/views/ops/machine/cronjob/CronJobExecList.vue index 2aab95f9..4054bc6d 100644 --- a/mayfly_go_web/src/views/ops/machine/cronjob/CronJobExecList.vue +++ b/mayfly_go_web/src/views/ops/machine/cronjob/CronJobExecList.vue @@ -64,7 +64,7 @@ const columns = ref([ TableColumn.new('machineIp', '机器IP').setMinWidth(120), TableColumn.new('machineName', '机器名称').setMinWidth(100), TableColumn.new('status', '状态').typeTag(CronJobExecStatusEnum).setMinWidth(70), - TableColumn.new('res', '执行结果').setMinWidth(250), + TableColumn.new('res', '执行结果').setMinWidth(250).canBeautify(), TableColumn.new('execTime', '执行时间').isTime().setMinWidth(150), ]); diff --git a/server/initialize/router.go b/server/initialize/router.go index 2105dc4e..c36c1f09 100644 --- a/server/initialize/router.go +++ b/server/initialize/router.go @@ -20,13 +20,6 @@ import ( "github.com/gin-gonic/gin" ) -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) - } -} - func InitRouter() *gin.Engine { // server配置 serverConfig := config.Conf.Server @@ -40,28 +33,8 @@ func InitRouter() *gin.Engine { g.JSON(http.StatusNotFound, gin.H{"code": 404, "msg": fmt.Sprintf("not found '%s:%s'", g.Request.Method, g.Request.URL.Path)}) }) - // 使用embed打包静态资源至二进制文件中 - fsys, _ := fs.Sub(static.Static, "static") - fileServer := http.FileServer(http.FS(fsys)) - handler := WrapStaticHandler(fileServer) - router.GET("/", handler) - router.GET("/favicon.ico", handler) - router.GET("/config.js", handler) - // 所有/assets/**开头的都是静态资源文件 - router.GET("/assets/*file", handler) - // 设置静态资源 - if staticConfs := serverConfig.Static; staticConfs != nil { - for _, scs := range *staticConfs { - router.StaticFS(scs.RelativePath, http.Dir(scs.Root)) - } - } - // 设置静态文件 - if staticFileConfs := serverConfig.StaticFile; staticFileConfs != nil { - for _, sfs := range *staticFileConfs { - router.StaticFile(sfs.RelativePath, sfs.Filepath) - } - } + setStatic(router) // 是否允许跨域 if serverConfig.Cors { @@ -71,8 +44,7 @@ func InitRouter() *gin.Engine { // 设置路由组 api := router.Group("/api") { - common_router.InitIndexRouter(api) - common_router.InitCommonRouter(api) + common_router.Init(api) auth_router.Init(api) @@ -88,3 +60,35 @@ func InitRouter() *gin.Engine { return router } + +func setStatic(router *gin.Engine) { + // 使用embed打包静态资源至二进制文件中 + fsys, _ := fs.Sub(static.Static, "static") + fileServer := http.FileServer(http.FS(fsys)) + handler := WrapStaticHandler(fileServer) + router.GET("/", handler) + router.GET("/favicon.ico", handler) + router.GET("/config.js", handler) + // 所有/assets/**开头的都是静态资源文件 + router.GET("/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) + } +} diff --git a/server/internal/common/router/router.go b/server/internal/common/router/router.go new file mode 100644 index 00000000..8c0f1c2b --- /dev/null +++ b/server/internal/common/router/router.go @@ -0,0 +1,8 @@ +package router + +import "github.com/gin-gonic/gin" + +func Init(router *gin.RouterGroup) { + InitCommonRouter(router) + InitIndexRouter(router) +} diff --git a/server/internal/machine/api/machine_file.go b/server/internal/machine/api/machine_file.go index c0d95922..de1270ce 100644 --- a/server/internal/machine/api/machine_file.go +++ b/server/internal/machine/api/machine_file.go @@ -104,6 +104,7 @@ func (m *MachineFile) GetDirEntry(rc *req.Ctx) { g := rc.GinCtx fid := GetMachineFileId(g) readPath := g.Query("path") + rc.ReqParam = fmt.Sprintf("path: %s", readPath) if !strings.HasSuffix(readPath, "/") { readPath = readPath + "/" @@ -120,7 +121,6 @@ func (m *MachineFile) GetDirEntry(rc *req.Ctx) { } sort.Sort(vo.MachineFileInfos(fisVO)) rc.ResData = fisVO - rc.ReqParam = fmt.Sprintf("path: %s", readPath) } func (m *MachineFile) GetDirSize(rc *req.Ctx) { diff --git a/server/internal/sys/api/form/account.go b/server/internal/sys/api/form/account.go index dc8ddf70..822b5e6f 100644 --- a/server/internal/sys/api/form/account.go +++ b/server/internal/sys/api/form/account.go @@ -2,7 +2,7 @@ package form type AccountCreateForm struct { Id uint64 `json:"id"` - Name string `json:"name" binding:"required,max=16"` + Name string `json:"name" binding:"required,max=16" msg:"required=姓名不能为空,max=姓名最大长度不能超过16位"` Username string `json:"username" binding:"pattern=account_username"` Password string `json:"password"` } diff --git a/server/pkg/validatorx/validatorx.go b/server/pkg/validatorx/validatorx.go index 0d16b47b..5788de7e 100644 --- a/server/pkg/validatorx/validatorx.go +++ b/server/pkg/validatorx/validatorx.go @@ -1,6 +1,7 @@ package validatorx import ( + "mayfly-go/pkg/utils/stringx" "mayfly-go/pkg/utils/structx" "reflect" "strings" @@ -12,6 +13,8 @@ import ( zh_trans "github.com/go-playground/validator/v10/translations/zh" ) +const CustomMsgTagName = "msg" + var ( trans ut.Translator ) @@ -26,7 +29,7 @@ func Init() { // 修改返回字段key的格式 validate.RegisterTagNameFunc(func(fld reflect.StructField) string { // 如果存在校验错误提示消息,则使用字段名,后续需要通过该字段名获取相应错误消息 - if _, ok := fld.Tag.Lookup("valid_msg"); ok { + if _, ok := fld.Tag.Lookup(CustomMsgTagName); ok { return fld.Name } name := strings.SplitN(fld.Tag.Get("json"), ",", 2)[0] @@ -63,16 +66,19 @@ func Translate(data any, err error) map[string][]string { // 判断该字段是否设置了自定义的错误描述信息,存在则使用自定义错误信息进行提示 if field, ok := structx.IndirectType(reflect.TypeOf(data)).FieldByName(fieldName); ok { - if errMsg, ok := field.Tag.Lookup("valid_msg"); ok { - result[fieldName] = append(result[fieldName], errMsg) - break + if errMsg, ok := field.Tag.Lookup(CustomMsgTagName); ok { + customMsg := getCustomErrMsg(err.Tag(), errMsg) + if customMsg != "" { + result[fieldName] = append(result[fieldName], customMsg) + continue + } } } // 如果是自定义正则校验规则,则使用自定义的错误描述信息 if err.Tag() == CustomPatternTagName { result[fieldName] = append(result[fieldName], fieldName+patternErrMsg[err.Param()]) - break + continue } result[fieldName] = append(result[fieldName], err.Translate(trans)) @@ -90,3 +96,21 @@ func Translate2Str(data any, err error) string { } return strings.Join(errMsgs, ", ") } + +// 获取自定义的错误提示消息 +// +// @param validTag 校验标签,如required等 +// @param customMsg 自定义错误消息 +func getCustomErrMsg(validTag, customMsg string) string { + // 解析 msg:"required=用户名不能为空,min=用户名长度不能小于8位" + msgs := strings.Split(customMsg, ",") + for _, msg := range msgs { + tagAndMsg := strings.Split(stringx.Trim(msg), "=") + if len(tagAndMsg) > 1 && validTag == stringx.Trim(tagAndMsg[0]) { + // 获取valid tag对应的错误消息 + return stringx.Trim(tagAndMsg[1]) + } + } + + return customMsg +}