├── Makefile ├── mayfly_go_web ├── src │ ├── theme │ │ ├── base.scss │ │ ├── index.scss │ │ ├── media │ │ │ ├── home.scss │ │ │ ├── cityLinkage.scss │ │ │ ├── tagsView.scss │ │ │ ├── dialog.scss │ │ │ ├── form.scss │ │ │ ├── pagination.scss │ │ │ ├── personal.scss │ │ │ ├── media.scss │ │ │ ├── login.scss │ │ │ ├── error.scss │ │ │ ├── index.scss │ │ │ └── layout.scss │ │ ├── other.scss │ │ ├── iconSelector.scss │ │ └── dark.scss │ ├── views │ │ ├── ops │ │ │ ├── db │ │ │ │ ├── index.ts │ │ │ │ └── enums.ts │ │ │ ├── machine │ │ │ │ ├── index.ts │ │ │ │ ├── SshTerminalPage.vue │ │ │ │ └── enums.ts │ │ │ ├── redis │ │ │ │ ├── index.ts │ │ │ │ └── ViewerText.vue │ │ │ ├── mongo │ │ │ │ └── api.ts │ │ │ └── tag │ │ │ │ └── api.ts │ │ ├── system │ │ │ ├── role │ │ │ │ └── index.ts │ │ │ ├── account │ │ │ │ └── index.ts │ │ │ ├── resource │ │ │ │ └── index.ts │ │ │ └── enums.ts │ │ ├── home │ │ │ └── api.ts │ │ ├── personal │ │ │ ├── api.ts │ │ │ └── mock.ts │ │ └── oauth │ │ │ └── Oauth2Callback.vue │ ├── assets │ │ └── font │ │ │ ├── font.css │ │ │ └── JetBrainsMono-Regular.woff │ ├── common │ │ ├── pattern.ts │ │ ├── utils │ │ │ ├── componentSize.ts │ │ │ ├── mitt.ts │ │ │ ├── url.ts │ │ │ ├── arrayOperation.ts │ │ │ ├── viteBuild.ts │ │ │ ├── date.ts │ │ │ └── setIconfont.ts │ │ ├── commonEnum.ts │ │ ├── config.ts │ │ ├── openApi.ts │ │ ├── SocketBuilder.ts │ │ ├── rsa.ts │ │ └── echarts │ │ │ └── useEcharts.ts │ ├── components │ │ ├── terminal │ │ │ └── common.ts │ │ ├── Grid │ │ │ └── interface │ │ │ │ └── index.ts │ │ ├── dynamic-form │ │ │ └── index.js │ │ ├── progress-notify │ │ │ ├── progress-notify.ts │ │ │ └── progress-notify.vue │ │ ├── auth │ │ │ ├── auth.ts │ │ │ ├── auth.vue │ │ │ ├── authAll.vue │ │ │ └── auths.vue │ │ ├── monaco │ │ │ └── completionItemProvider.ts │ │ └── contextmenu │ │ │ └── index.ts │ ├── store │ │ ├── index.ts │ │ ├── routesList.ts │ │ ├── userInfo.ts │ │ └── tagsViews.ts │ ├── types │ │ ├── env.d.ts │ │ ├── source.d.ts │ │ └── shim.d.ts │ ├── directive │ │ └── index.ts │ ├── layout │ │ ├── main │ │ │ ├── transverse.vue │ │ │ ├── classic.vue │ │ │ ├── columns.vue │ │ │ └── defaults.vue │ │ ├── component │ │ │ └── header.vue │ │ ├── routerView │ │ │ └── link.vue │ │ ├── navBars │ │ │ └── index.vue │ │ └── footer │ │ │ └── index.vue │ └── main.ts ├── public │ ├── favicon.ico │ └── config.js ├── .env ├── .env.development ├── .env.production ├── .eslintignore ├── .gitignore ├── index.html ├── LICENSE └── .prettierrc.js ├── server ├── .gitignore ├── pkg │ ├── consts │ │ ├── consts.go │ │ └── gormx.go │ ├── model │ │ ├── login_account.go │ │ └── page.go │ ├── utils │ │ ├── timex │ │ │ └── timex.go │ │ ├── bytex │ │ │ ├── bytex_test.go │ │ │ └── bytex.go │ │ ├── stringx │ │ │ ├── template_test.go │ │ │ ├── template.go │ │ │ └── rand.go │ │ ├── assert │ │ │ └── assert.go │ │ ├── collx │ │ │ ├── map.go │ │ │ ├── byte.go │ │ │ ├── stack.go │ │ │ └── array_test.go │ │ ├── ymlx │ │ │ └── ymlx.go │ │ ├── runtimex │ │ │ └── runtimex.go │ │ └── netx │ │ │ └── ssh_conn_wrap.go │ ├── config │ │ ├── redis.go │ │ ├── app.go │ │ ├── sqlite.go │ │ ├── jwt.go │ │ ├── aes.go │ │ ├── log.go │ │ └── server.go │ ├── global │ │ └── global.go │ ├── biz │ │ └── assert_test.go │ ├── ws │ │ ├── msg.go │ │ └── ws.go │ ├── logx │ │ ├── color.go │ │ └── json_handler.go │ ├── cache │ │ └── cache.go │ ├── otp │ │ └── otp.go │ ├── starter │ │ ├── banner.go │ │ ├── run.go │ │ ├── redis.go │ │ └── web-server.go │ ├── middleware │ │ └── cors.go │ ├── errorx │ │ └── bizerror.go │ ├── httpclient │ │ └── httpclient_test.go │ ├── validatorx │ │ └── pattern.go │ ├── scheduler │ │ └── scheduler.go │ └── req │ │ └── token.go ├── static │ ├── static │ │ └── favicon.ico │ └── static.go ├── resources │ ├── data │ │ └── mayfly-go.sqlite │ └── script │ │ ├── sql │ │ ├── v1.5.3.sql │ │ └── v1.5.4.sql │ │ ├── shutdown.sh │ │ └── startup.sh ├── main.go ├── internal │ ├── auth │ │ ├── api │ │ │ ├── vo │ │ │ │ └── vo.go │ │ │ └── form │ │ │ │ └── form.go │ │ ├── domain │ │ │ ├── repository │ │ │ │ └── oauth2.go │ │ │ └── entity │ │ │ │ └── oauth2.go │ │ ├── application │ │ │ ├── application.go │ │ │ └── oauth2.go │ │ └── infrastructure │ │ │ └── persistence │ │ │ ├── persistence.go │ │ │ └── oauth2.go │ ├── tag │ │ ├── api │ │ │ ├── form │ │ │ │ ├── tag_tree.go │ │ │ │ └── team.go │ │ │ └── vo │ │ │ │ ├── team.go │ │ │ │ └── tag_tree.go │ │ ├── router │ │ │ ├── router.go │ │ │ └── tag_tree.go │ │ ├── domain │ │ │ ├── entity │ │ │ │ ├── team.go │ │ │ │ ├── tag_tree_team.go │ │ │ │ ├── team_member.go │ │ │ │ ├── tag_resource.go │ │ │ │ ├── tag_tree.go │ │ │ │ └── query.go │ │ │ └── repository │ │ │ │ ├── tag_resource.go │ │ │ │ ├── tag_tree.go │ │ │ │ ├── team.go │ │ │ │ ├── tag_tree_team.go │ │ │ │ └── team_member.go │ │ ├── infrastructure │ │ │ └── persistence │ │ │ │ ├── team.go │ │ │ │ ├── tag_tree_team.go │ │ │ │ └── persistence.go │ │ └── application │ │ │ ├── application.go │ │ │ └── tag_resource.go │ ├── msg │ │ ├── router │ │ │ ├── router.go │ │ │ └── msg.go │ │ ├── infrastructure │ │ │ └── persistence │ │ │ │ ├── persistence.go │ │ │ │ └── msg.go │ │ ├── application │ │ │ └── application.go │ │ ├── domain │ │ │ ├── repository │ │ │ │ └── msg.go │ │ │ └── entity │ │ │ │ └── msg.go │ │ └── api │ │ │ └── msg.go │ ├── mongo │ │ ├── router │ │ │ └── router.go │ │ ├── infrastructure │ │ │ └── persistence │ │ │ │ ├── persistence.go │ │ │ │ └── mongo.go │ │ ├── domain │ │ │ ├── entity │ │ │ │ ├── query.go │ │ │ │ └── mongo.go │ │ │ └── repository │ │ │ │ └── mongo.go │ │ ├── application │ │ │ └── application.go │ │ ├── mgm │ │ │ └── conn.go │ │ └── api │ │ │ └── form │ │ │ └── mongo.go │ ├── redis │ │ ├── router │ │ │ └── router.go │ │ ├── infrastructure │ │ │ └── persistence │ │ │ │ ├── persistence.go │ │ │ │ └── redis_repo.go │ │ ├── application │ │ │ └── application.go │ │ ├── domain │ │ │ ├── repository │ │ │ │ └── redis.go │ │ │ └── entity │ │ │ │ ├── query.go │ │ │ │ └── redis.go │ │ ├── api │ │ │ ├── string.go │ │ │ └── vo │ │ │ │ └── redis.go │ │ └── rdm │ │ │ └── conn.go │ ├── common │ │ ├── router │ │ │ ├── router.go │ │ │ ├── common.go │ │ │ └── index.go │ │ ├── api │ │ │ ├── common.go │ │ │ └── index.go │ │ └── consts │ │ │ └── consts.go │ ├── sys │ │ ├── consts │ │ │ └── consts.go │ │ ├── router │ │ │ ├── system.go │ │ │ ├── captcha.go │ │ │ ├── router.go │ │ │ ├── syslog.go │ │ │ ├── config.go │ │ │ ├── resource.go │ │ │ └── role.go │ │ ├── api │ │ │ ├── form │ │ │ │ ├── config.go │ │ │ │ ├── role.go │ │ │ │ ├── account.go │ │ │ │ └── resource.go │ │ │ ├── captcha.go │ │ │ ├── syslog.go │ │ │ ├── system.go │ │ │ ├── vo │ │ │ │ └── account.go │ │ │ └── config.go │ │ ├── domain │ │ │ ├── repository │ │ │ │ ├── config.go │ │ │ │ ├── account.go │ │ │ │ ├── syslog.go │ │ │ │ ├── resource.go │ │ │ │ └── role.go │ │ │ └── entity │ │ │ │ ├── query.go │ │ │ │ ├── syslog.go │ │ │ │ ├── resource.go │ │ │ │ ├── account.go │ │ │ │ └── role.go │ │ ├── application │ │ │ └── application.go │ │ └── infrastructure │ │ │ └── persistence │ │ │ ├── account.go │ │ │ ├── config.go │ │ │ ├── syslog.go │ │ │ ├── persistence.go │ │ │ └── account_role.go │ ├── db │ │ ├── domain │ │ │ ├── repository │ │ │ │ ├── db_sql.go │ │ │ │ ├── db_sql_exec.go │ │ │ │ ├── instance.go │ │ │ │ └── db.go │ │ │ └── entity │ │ │ │ ├── db_sql.go │ │ │ │ ├── db.go │ │ │ │ ├── db_sql_exec.go │ │ │ │ └── query.go │ │ ├── router │ │ │ ├── router.go │ │ │ ├── db_sql_exec.go │ │ │ ├── db_sql.go │ │ │ └── instance.go │ │ ├── infrastructure │ │ │ └── persistence │ │ │ │ ├── db_sql.go │ │ │ │ ├── persistence.go │ │ │ │ ├── instance.go │ │ │ │ └── db_sql_exec.go │ │ ├── application │ │ │ ├── db_sql.go │ │ │ └── application.go │ │ ├── config │ │ │ └── config.go │ │ ├── api │ │ │ ├── db_sql_exec.go │ │ │ ├── form │ │ │ │ ├── instance.go │ │ │ │ └── db.go │ │ │ ├── vo │ │ │ │ ├── instance.go │ │ │ │ └── db.go │ │ │ └── gzip_writer.go │ │ └── dbm │ │ │ └── db_type_test.go │ └── machine │ │ ├── router │ │ ├── router.go │ │ ├── auth_cert.go │ │ ├── machine_cronjob.go │ │ └── machine_script.go │ │ ├── domain │ │ ├── entity │ │ │ ├── machine_file.go │ │ │ ├── machine_monitor.go │ │ │ ├── machine_script.go │ │ │ ├── machine_term_op.go │ │ │ ├── query.go │ │ │ └── auth_cert.go │ │ └── repository │ │ │ ├── machine_file.go │ │ │ ├── machine_script.go │ │ │ ├── machine_term_op.go │ │ │ ├── auth_cert.go │ │ │ ├── machine.go │ │ │ └── machine_cronjob.go │ │ ├── infrastructure │ │ ├── cache │ │ │ └── machine_stats.go │ │ └── persistence │ │ │ ├── machine_file.go │ │ │ ├── machine_term_op.go │ │ │ ├── machine_script.go │ │ │ ├── machine_cronjob.go │ │ │ ├── machine_cronjob_exec.go │ │ │ ├── auth_cert.go │ │ │ ├── machine_cronjob_relate.go │ │ │ └── machine.go │ │ ├── config │ │ └── config.go │ │ └── init │ │ └── init.go ├── initialize │ ├── initialize.go │ └── savelog.go ├── readme.txt ├── migrations │ └── 20230720.go └── config.yml.example ├── .gitignore ├── docker-compose.yaml └── Dockerfile /Makefile: -------------------------------------------------------------------------------- 1 | 2 | docker: 3 | docker build . -t mayfly-go 4 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/base.scss: -------------------------------------------------------------------------------- 1 | @import 'common/transition.scss'; 2 | -------------------------------------------------------------------------------- /server/.gitignore: -------------------------------------------------------------------------------- 1 | static/static 2 | config.yml 3 | mayfly_rsa 4 | mayfly_rsa.pub -------------------------------------------------------------------------------- /mayfly_go_web/src/views/ops/db/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './SqlExec.vue'; 2 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/ops/machine/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './MachineList.vue'; 2 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/ops/redis/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './RedisList.vue'; 2 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/system/role/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './RoleList.vue'; 2 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/system/account/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './AccountList.vue'; 2 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/system/resource/index.ts: -------------------------------------------------------------------------------- 1 | export { default } from './ResourceList.vue'; 2 | -------------------------------------------------------------------------------- /server/pkg/consts/consts.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | const ( 4 | BoolTrue = 1 5 | BoolFalse = -1 6 | ) 7 | -------------------------------------------------------------------------------- /mayfly_go_web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodFrm/mayfly-go/master/mayfly_go_web/public/favicon.ico -------------------------------------------------------------------------------- /server/static/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodFrm/mayfly-go/master/server/static/static/favicon.ico -------------------------------------------------------------------------------- /server/resources/data/mayfly-go.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodFrm/mayfly-go/master/server/resources/data/mayfly-go.sqlite -------------------------------------------------------------------------------- /server/pkg/model/login_account.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type LoginAccount struct { 4 | Id uint64 5 | Username string 6 | } 7 | -------------------------------------------------------------------------------- /mayfly_go_web/src/assets/font/font.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'JetBrainsMono'; 3 | src: url('JetBrainsMono-Regular.woff'); 4 | } -------------------------------------------------------------------------------- /server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "mayfly-go/pkg/starter" 5 | ) 6 | 7 | func main() { 8 | starter.RunWebServer() 9 | } 10 | -------------------------------------------------------------------------------- /server/internal/auth/api/vo/vo.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | type Oauth2Status struct { 4 | Enable bool `json:"enable"` 5 | Bind bool `json:"bind"` 6 | } 7 | -------------------------------------------------------------------------------- /mayfly_go_web/src/assets/font/JetBrainsMono-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodFrm/mayfly-go/master/mayfly_go_web/src/assets/font/JetBrainsMono-Regular.woff -------------------------------------------------------------------------------- /mayfly_go_web/.env: -------------------------------------------------------------------------------- 1 | # port 端口号 2 | VITE_PORT = 8889 3 | 4 | # open 运行 npm run dev 时自动打开浏览器 5 | VITE_OPEN = false 6 | 7 | # public path 配置线上环境路径(打包) 8 | VITE_PUBLIC_PATH = '' -------------------------------------------------------------------------------- /mayfly_go_web/src/common/pattern.ts: -------------------------------------------------------------------------------- 1 | export const AccountUsernamePattern = { 2 | pattern: /^[a-zA-Z0-9_]{5,20}$/g, 3 | message: '只允许输入5-20位大小写字母、数字、下划线', 4 | }; 5 | -------------------------------------------------------------------------------- /server/internal/tag/api/form/tag_tree.go: -------------------------------------------------------------------------------- 1 | package form 2 | 3 | type TagTreeTeam struct { 4 | TeamId uint64 `json:"teamId"` 5 | TagIds []uint64 `json:"tagIds"` 6 | } 7 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/home/api.ts: -------------------------------------------------------------------------------- 1 | import Api from '@/common/Api'; 2 | 3 | export const indexApi = { 4 | getIndexCount: Api.newGet("/common/index/count"), 5 | } 6 | 7 | -------------------------------------------------------------------------------- /server/initialize/initialize.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import machineInit "mayfly-go/internal/machine/init" 4 | 5 | func InitOther() { 6 | machineInit.Init() 7 | } 8 | -------------------------------------------------------------------------------- /server/internal/msg/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func Init(router *gin.RouterGroup) { 6 | InitMsgRouter(router) 7 | } 8 | -------------------------------------------------------------------------------- /server/pkg/utils/timex/timex.go: -------------------------------------------------------------------------------- 1 | package timex 2 | 3 | import "time" 4 | 5 | func DefaultFormat(time time.Time) string { 6 | return time.Format("2006-01-02 15:04:05") 7 | } 8 | -------------------------------------------------------------------------------- /mayfly_go_web/.env.development: -------------------------------------------------------------------------------- 1 | # 本地环境 2 | ENV = 'development' 3 | 4 | # 本地环境接口地址 5 | VITE_API_URL = '/api' 6 | 7 | # 路由模式 8 | # Optional: hash | history 9 | VITE_ROUTER_MODE = hash -------------------------------------------------------------------------------- /mayfly_go_web/.env.production: -------------------------------------------------------------------------------- 1 | # 线上环境 2 | ENV = 'production' 3 | 4 | # 线上环境接口地址 5 | VITE_API_URL = '/api' 6 | 7 | # 路由模式 8 | # Optional: hash | history 9 | VITE_ROUTER_MODE = hash -------------------------------------------------------------------------------- /mayfly_go_web/src/components/terminal/common.ts: -------------------------------------------------------------------------------- 1 | export enum TerminalStatus { 2 | Error = -1, 3 | NoConnected = 0, 4 | Connected = 1, 5 | Disconnected = 2, 6 | } 7 | -------------------------------------------------------------------------------- /server/internal/mongo/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func Init(router *gin.RouterGroup) { 6 | InitMongoRouter(router) 7 | } 8 | -------------------------------------------------------------------------------- /server/internal/redis/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func Init(router *gin.RouterGroup) { 6 | InitRedisRouter(router) 7 | } 8 | -------------------------------------------------------------------------------- /server/internal/tag/api/form/team.go: -------------------------------------------------------------------------------- 1 | package form 2 | 3 | type TeamMember struct { 4 | TeamId uint64 `json:"teamId"` 5 | AccountIds []uint64 `json:"accountIds"` 6 | } 7 | -------------------------------------------------------------------------------- /server/static/static.go: -------------------------------------------------------------------------------- 1 | package static 2 | 3 | import "embed" 4 | 5 | // 使用1.16特性编译阶段将静态资源文件打包进编译好的程序 6 | var ( 7 | //go:embed static/** 8 | Static embed.FS 9 | ) 10 | -------------------------------------------------------------------------------- /mayfly_go_web/src/store/index.ts: -------------------------------------------------------------------------------- 1 | // https://pinia.vuejs.org/ 2 | import { createPinia } from 'pinia'; 3 | 4 | // 创建 5 | const pinia = createPinia(); 6 | 7 | // 导出 8 | export default pinia; 9 | -------------------------------------------------------------------------------- /mayfly_go_web/src/types/env.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import type { DefineComponent } from 'vue'; 3 | const component: DefineComponent<{}, {}, any>; 4 | export default component; 5 | } -------------------------------------------------------------------------------- /mayfly_go_web/src/components/Grid/interface/index.ts: -------------------------------------------------------------------------------- 1 | export type BreakPoint = "xs" | "sm" | "md" | "lg" | "xl"; 2 | 3 | export type Responsive = { 4 | span?: number; 5 | offset?: number; 6 | }; 7 | -------------------------------------------------------------------------------- /mayfly_go_web/src/common/utils/componentSize.ts: -------------------------------------------------------------------------------- 1 | import { getLocal } from '@/common/utils/storage'; 2 | 3 | // 全局组件大小 4 | export const globalComponentSize = getLocal('themeConfig')?.globalComponentSize; 5 | -------------------------------------------------------------------------------- /server/internal/common/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func Init(router *gin.RouterGroup) { 6 | InitCommonRouter(router) 7 | InitIndexRouter(router) 8 | } 9 | -------------------------------------------------------------------------------- /server/internal/tag/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func Init(router *gin.RouterGroup) { 6 | InitTagTreeRouter(router) 7 | InitTeamRouter(router) 8 | } 9 | -------------------------------------------------------------------------------- /server/internal/sys/consts/consts.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | type AccountRoleRelateType int 4 | 5 | const ( 6 | AccountRoleBind AccountRoleRelateType = 1 7 | AccountRoleUnbind AccountRoleRelateType = -1 8 | ) 9 | -------------------------------------------------------------------------------- /mayfly_go_web/src/types/source.d.ts: -------------------------------------------------------------------------------- 1 | // 声明一个模块,防止引入文件时报错 2 | declare module '*.json'; 3 | declare module '*.png'; 4 | declare module '*.jpg'; 5 | declare module '*.scss'; 6 | declare module '*.ts'; 7 | declare module '*.js'; -------------------------------------------------------------------------------- /server/pkg/config/redis.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | type Redis struct { 4 | Host string `yaml:"host"` 5 | Port int `yaml:"port"` 6 | Password string `yaml:"password"` 7 | Db int `yaml:"db"` 8 | } 9 | -------------------------------------------------------------------------------- /server/pkg/utils/bytex/bytex_test.go: -------------------------------------------------------------------------------- 1 | package bytex 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestParseSize(t *testing.T) { 9 | res, _ := ParseSize("1MB") 10 | fmt.Println(res) 11 | } 12 | -------------------------------------------------------------------------------- /mayfly_go_web/src/common/utils/mitt.ts: -------------------------------------------------------------------------------- 1 | // https://www.npmjs.com/package/mitt 2 | import mitt, { Emitter } from 'mitt'; 3 | 4 | // 类型 5 | const emitter: Emitter = mitt(); 6 | 7 | // 导出 8 | export default emitter; 9 | -------------------------------------------------------------------------------- /mayfly_go_web/.eslintignore: -------------------------------------------------------------------------------- 1 | 2 | *.sh 3 | node_modules 4 | lib 5 | *.md 6 | *.scss 7 | *.woff 8 | *.ttf 9 | .vscode 10 | .idea 11 | dist 12 | mock 13 | public 14 | bin 15 | build 16 | config 17 | index.html 18 | src/assets -------------------------------------------------------------------------------- /mayfly_go_web/src/types/shim.d.ts: -------------------------------------------------------------------------------- 1 | // 申明外部 npm 插件模块 2 | declare module 'sql-formatter'; 3 | declare module 'jsoneditor'; 4 | declare module 'asciinema-player'; 5 | declare module 'vue-grid-layout'; 6 | declare module 'splitpanes'; 7 | -------------------------------------------------------------------------------- /server/pkg/global/global.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "mayfly-go/pkg/eventbus" 5 | 6 | "gorm.io/gorm" 7 | ) 8 | 9 | var ( 10 | Db *gorm.DB // gorm 11 | 12 | EventBus eventbus.Bus = eventbus.New() 13 | ) 14 | -------------------------------------------------------------------------------- /server/internal/db/domain/repository/db_sql.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/db/domain/entity" 5 | "mayfly-go/pkg/base" 6 | ) 7 | 8 | type DbSql interface { 9 | base.Repo[*entity.DbSql] 10 | } 11 | -------------------------------------------------------------------------------- /server/pkg/config/app.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "fmt" 4 | 5 | const ( 6 | AppName = "mayfly-go" 7 | Version = "v1.6.1" 8 | ) 9 | 10 | func GetAppInfo() string { 11 | return fmt.Sprintf("[%s:%s]", AppName, Version) 12 | } 13 | -------------------------------------------------------------------------------- /mayfly_go_web/src/components/dynamic-form/index.js: -------------------------------------------------------------------------------- 1 | export { default as DynamicForm } from './DynamicForm.vue'; 2 | export { default as DynamicFormDialog } from './DynamicFormDialog.vue'; 3 | export { default as DynamicFormEdit } from './DynamicFormEdit.vue'; 4 | -------------------------------------------------------------------------------- /server/initialize/savelog.go: -------------------------------------------------------------------------------- 1 | package initialize 2 | 3 | import ( 4 | sysapp "mayfly-go/internal/sys/application" 5 | "mayfly-go/pkg/req" 6 | ) 7 | 8 | func InitSaveLogFunc() req.SaveLogFunc { 9 | return sysapp.GetSyslogApp().SaveFromReq 10 | } 11 | -------------------------------------------------------------------------------- /server/internal/auth/domain/repository/oauth2.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/auth/domain/entity" 5 | "mayfly-go/pkg/base" 6 | ) 7 | 8 | type Oauth2Account interface { 9 | base.Repo[*entity.Oauth2Account] 10 | } 11 | -------------------------------------------------------------------------------- /server/internal/tag/domain/entity/team.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "mayfly-go/pkg/model" 4 | 5 | // 团队信息 6 | type Team struct { 7 | model.Model 8 | 9 | Name string `json:"name"` // 名称 10 | Remark string `json:"remark"` // 备注说明 11 | } 12 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/index.scss: -------------------------------------------------------------------------------- 1 | @import './app.scss'; 2 | @import './base.scss'; 3 | @import './other.scss'; 4 | @import './element.scss'; 5 | @import './media/media.scss'; 6 | @import './waves.scss'; 7 | @import './dark.scss'; 8 | @import './iconSelector.scss'; -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/media/home.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于768px 4 | ------------------------------- */ 5 | @media screen and (max-width: $sm) { 6 | .home-warning-media, 7 | .home-dynamic-media { 8 | margin-top: 15px; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /server/internal/msg/infrastructure/persistence/persistence.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import "mayfly-go/internal/msg/domain/repository" 4 | 5 | var ( 6 | msgRepo = newMsgRepo() 7 | ) 8 | 9 | func GetMsgRepo() repository.Msg { 10 | return msgRepo 11 | } 12 | -------------------------------------------------------------------------------- /server/internal/db/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func Init(router *gin.RouterGroup) { 6 | InitInstanceRouter(router) 7 | InitDbRouter(router) 8 | InitDbSqlRouter(router) 9 | InitDbSqlExecRouter(router) 10 | } 11 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/media/cityLinkage.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于576px 4 | ------------------------------- */ 5 | @media screen and (max-width: $xs) { 6 | .el-cascader__dropdown.el-popper { 7 | overflow: auto; 8 | max-width: 100%; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/media/tagsView.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于768px 4 | ------------------------------- */ 5 | @media screen and (max-width: $sm) { 6 | .tags-view-form { 7 | .tags-view-form-col { 8 | margin-bottom: 20px; 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /server/internal/msg/application/application.go: -------------------------------------------------------------------------------- 1 | package application 2 | 3 | import ( 4 | "mayfly-go/internal/msg/infrastructure/persistence" 5 | ) 6 | 7 | var ( 8 | msgApp = newMsgApp(persistence.GetMsgRepo()) 9 | ) 10 | 11 | func GetMsgApp() Msg { 12 | return msgApp 13 | } 14 | -------------------------------------------------------------------------------- /server/readme.txt: -------------------------------------------------------------------------------- 1 | 相关配置文件: 2 | 后端: 3 | config.yml: 服务端口,mysql,aeskey(16 24 32位),jwtkey等信息在此配置即可。 4 | 建议务必将aes.key(资源密码加密如机器、数据库、redis等密码)与jwt.key(jwt秘钥)两信息使用随机字符串替换。 5 | 6 | 服务启动&重启:./startup.sh 7 | 服务关闭:./shutdown.sh 8 | 9 | 直接通过 host:ip即可访问项目 10 | 初始账号 admin/admin123. -------------------------------------------------------------------------------- /server/internal/auth/application/application.go: -------------------------------------------------------------------------------- 1 | package application 2 | 3 | import "mayfly-go/internal/auth/infrastructure/persistence" 4 | 5 | var ( 6 | authApp = newAuthApp(persistence.GetOauthAccountRepo()) 7 | ) 8 | 9 | func GetAuthApp() Oauth2 { 10 | return authApp 11 | } 12 | -------------------------------------------------------------------------------- /server/internal/redis/infrastructure/persistence/persistence.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import "mayfly-go/internal/redis/domain/repository" 4 | 5 | var ( 6 | redisRepo repository.Redis = newRedisRepo() 7 | ) 8 | 9 | func GetRedisRepo() repository.Redis { 10 | return redisRepo 11 | } 12 | -------------------------------------------------------------------------------- /mayfly_go_web/src/common/commonEnum.ts: -------------------------------------------------------------------------------- 1 | import EnumValue from './Enum'; 2 | 3 | // 标签关联的资源类型 4 | export const TagResourceTypeEnum = { 5 | Machine: EnumValue.of(1, '机器'), 6 | Db: EnumValue.of(2, '数据库'), 7 | Redis: EnumValue.of(3, 'redis'), 8 | Mongo: EnumValue.of(4, 'mongo'), 9 | }; 10 | -------------------------------------------------------------------------------- /server/internal/tag/domain/entity/tag_tree_team.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "mayfly-go/pkg/model" 4 | 5 | // 标签树与团队关联信息 6 | type TagTreeTeam struct { 7 | model.Model 8 | 9 | TagId uint64 `json:"tagId"` 10 | TagPath string `json:"tagPath"` 11 | TeamId uint64 `json:"teamId"` 12 | } 13 | -------------------------------------------------------------------------------- /server/pkg/biz/assert_test.go: -------------------------------------------------------------------------------- 1 | package biz 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestErrIsNil(t *testing.T) { 8 | // ErrIsNil(NewBizErr("xxx is error")) 9 | // ErrIsNil(NewBizErr("xxx is error"), "格式错误") 10 | // ErrIsNil(NewBizErr("xxx is error"), "格式错误: %s, %d", "xxx", 12) 11 | } 12 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/media/dialog.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于800px 4 | ------------------------------- */ 5 | @media screen and (max-width: 800px) { 6 | .el-dialog { 7 | width: 90% !important; 8 | } 9 | .el-dialog.is-fullscreen { 10 | width: 100% !important; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /server/internal/tag/domain/entity/team_member.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "mayfly-go/pkg/model" 4 | 5 | // 团队成员关联信息 6 | type TeamMember struct { 7 | model.Model 8 | 9 | TeamId uint64 `json:"teamId"` 10 | AccountId uint64 `json:"accountId"` 11 | Username string `json:"username"` 12 | } 13 | -------------------------------------------------------------------------------- /server/internal/auth/infrastructure/persistence/persistence.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import "mayfly-go/internal/auth/domain/repository" 4 | 5 | var ( 6 | authAccountRepo = newAuthAccountRepo() 7 | ) 8 | 9 | func GetOauthAccountRepo() repository.Oauth2Account { 10 | return authAccountRepo 11 | } 12 | -------------------------------------------------------------------------------- /server/internal/mongo/infrastructure/persistence/persistence.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/mongo/domain/repository" 5 | ) 6 | 7 | var ( 8 | mongoRepo repository.Mongo = newMongoRepo() 9 | ) 10 | 11 | func GetMongoRepo() repository.Mongo { 12 | return mongoRepo 13 | } 14 | -------------------------------------------------------------------------------- /mayfly_go_web/src/directive/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue'; 2 | import { authDirective } from './auth'; 3 | import { wavesDirective } from './waves'; 4 | 5 | // 导出指令方法 6 | export function directive(app: App) { 7 | // 用户权限指令 8 | authDirective(app); 9 | // 按钮波浪指令 10 | wavesDirective(app); 11 | } 12 | -------------------------------------------------------------------------------- /server/internal/machine/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func Init(router *gin.RouterGroup) { 6 | InitMachineRouter(router) 7 | InitMachineFileRouter(router) 8 | InitMachineScriptRouter(router) 9 | InitAuthCertRouter(router) 10 | InitMachineCronJobRouter(router) 11 | } 12 | -------------------------------------------------------------------------------- /server/internal/sys/router/system.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/sys/api" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | func InitSystemRouter(router *gin.RouterGroup) { 10 | s := &api.System{} 11 | sys := router.Group("sysmsg") 12 | 13 | { 14 | sys.GET("", s.ConnectWs) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/internal/db/domain/entity/db_sql.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "mayfly-go/pkg/model" 5 | ) 6 | 7 | type DbSql struct { 8 | model.Model `orm:"-"` 9 | 10 | DbId uint64 `json:"dbId"` 11 | Db string `json:"db"` 12 | Type int `json:"type"` // 类型 13 | Sql string `json:"sql"` 14 | Name string `json:"name"` 15 | } 16 | -------------------------------------------------------------------------------- /server/internal/machine/domain/entity/machine_file.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "mayfly-go/pkg/model" 4 | 5 | type MachineFile struct { 6 | model.Model 7 | 8 | Name string `json:"name"` 9 | // 机器id 10 | MachineId uint64 `json:"machineId"` 11 | Type int `json:"type"` 12 | // 路径 13 | Path string `json:"path"` 14 | } 15 | -------------------------------------------------------------------------------- /server/pkg/ws/msg.go: -------------------------------------------------------------------------------- 1 | package ws 2 | 3 | // 消息类型 4 | type MsgType uint8 5 | 6 | const ( 7 | JsonMsg MsgType = 1 8 | TextMsg MsgType = 2 9 | BinaryMsg MsgType = 3 10 | ) 11 | 12 | // 消息信息 13 | type Msg struct { 14 | ToUserId UserId // 用户id 15 | ToClientId string // 客户端id 16 | 17 | Type MsgType // 消息类型 18 | Data any 19 | } 20 | -------------------------------------------------------------------------------- /server/resources/script/sql/v1.5.3.sql: -------------------------------------------------------------------------------- 1 | UPDATE `t_sys_config` 2 | SET 3 | `params` = '[{"name":"是否启用","model":"isUse","placeholder":"是否启用水印","options":"true,false"},{"name":"自定义信息","model":"content","placeholder":"额外添加的水印内容,可添加公司名称等"}]', 4 | `value` = '', 5 | `remark` = '水印信息配置', 6 | `key` = 'UseWatermark' 7 | WHERE 8 | `key` = 'UseWartermark'; -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/media/form.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于576px 4 | ------------------------------- */ 5 | @media screen and (max-width: $xs) { 6 | .el-form-item__label { 7 | width: 100% !important; 8 | text-align: left !important; 9 | } 10 | .el-form-item__content { 11 | margin-left: 0 !important; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/internal/tag/domain/repository/tag_resource.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/tag/domain/entity" 5 | "mayfly-go/pkg/base" 6 | ) 7 | 8 | type TagResource interface { 9 | base.Repo[*entity.TagResource] 10 | 11 | SelectByCondition(condition *entity.TagResourceQuery, toEntity any, orderBy ...string) 12 | } 13 | -------------------------------------------------------------------------------- /server/internal/tag/domain/repository/tag_tree.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/tag/domain/entity" 5 | "mayfly-go/pkg/base" 6 | ) 7 | 8 | type TagTree interface { 9 | base.Repo[*entity.TagTree] 10 | 11 | // 根据条件查询 12 | SelectByCondition(condition *entity.TagTreeQuery, toEntity any, orderBy ...string) 13 | } 14 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/media/pagination.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于576px 4 | ------------------------------- */ 5 | @media screen and (max-width: $xs) { 6 | .el-pager, 7 | .el-pagination__jump { 8 | display: none !important; 9 | } 10 | } 11 | 12 | // 默认居中对齐 13 | .el-pagination { 14 | text-align: center !important; 15 | } 16 | -------------------------------------------------------------------------------- /server/resources/script/shutdown.sh: -------------------------------------------------------------------------------- 1 | #bin/bash 2 | 3 | pid=`ps ax | grep -i 'mayfly-go' | grep -v grep | awk '{print $1}'` 4 | if [ -z "${pid}" ] ; then 5 | echo "No mayfly-go running." 6 | exit -1; 7 | fi 8 | 9 | echo "The mayfly-go(${pid}) is running..." 10 | 11 | kill ${pid} 12 | 13 | echo "Send shutdown request to mayfly-go(${pid}) OK" -------------------------------------------------------------------------------- /mayfly_go_web/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /mayfly_go_web/src/components/progress-notify/progress-notify.ts: -------------------------------------------------------------------------------- 1 | export const buildProgressProps = (): any => { 2 | return { 3 | progress: { 4 | title: { 5 | type: String, 6 | }, 7 | executedStatements: { 8 | type: Number, 9 | }, 10 | }, 11 | }; 12 | }; 13 | -------------------------------------------------------------------------------- /server/internal/mongo/domain/entity/query.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "mayfly-go/pkg/model" 4 | 5 | type MongoQuery struct { 6 | model.Model 7 | 8 | Name string 9 | Uri string 10 | SshTunnelMachineId uint64 // ssh隧道机器id 11 | TagPath string `json:"tagPath" form:"tagPath"` 12 | 13 | Codes []string 14 | } 15 | -------------------------------------------------------------------------------- /server/internal/sys/api/form/config.go: -------------------------------------------------------------------------------- 1 | package form 2 | 3 | type ConfigForm struct { 4 | Id int `json:"id"` 5 | Name string `json:"name"` 6 | Key string `json:"key"` 7 | Params string `json:"params"` 8 | Value string `json:"value"` 9 | Remark string `json:"remark"` 10 | Permission string `json:"permission"` 11 | } 12 | -------------------------------------------------------------------------------- /mayfly_go_web/src/common/utils/url.ts: -------------------------------------------------------------------------------- 1 | const mode = import.meta.env.VITE_ROUTER_MODE; 2 | 3 | /** 4 | * @description 获取不同路由模式所对应的 url 5 | * @returns {String} 6 | */ 7 | export function getNowUrl() { 8 | const url = { 9 | hash: location.hash.substring(1), 10 | history: location.pathname + location.search, 11 | }; 12 | return url[mode]; 13 | } 14 | -------------------------------------------------------------------------------- /server/internal/sys/router/captcha.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/sys/api" 5 | "mayfly-go/pkg/req" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | func InitCaptchaRouter(router *gin.RouterGroup) { 11 | captcha := router.Group("sys/captcha") 12 | { 13 | req.NewGet("", api.GenerateCaptcha).DontNeedToken().Group(captcha) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /server/internal/mongo/application/application.go: -------------------------------------------------------------------------------- 1 | package application 2 | 3 | import ( 4 | "mayfly-go/internal/mongo/infrastructure/persistence" 5 | tagapp "mayfly-go/internal/tag/application" 6 | ) 7 | 8 | var ( 9 | mongoApp Mongo = newMongoAppImpl(persistence.GetMongoRepo(), tagapp.GetTagTreeApp()) 10 | ) 11 | 12 | func GetMongoApp() Mongo { 13 | return mongoApp 14 | } 15 | -------------------------------------------------------------------------------- /server/internal/redis/application/application.go: -------------------------------------------------------------------------------- 1 | package application 2 | 3 | import ( 4 | "mayfly-go/internal/redis/infrastructure/persistence" 5 | tagapp "mayfly-go/internal/tag/application" 6 | ) 7 | 8 | var ( 9 | redisApp Redis = newRedisApp(persistence.GetRedisRepo(), tagapp.GetTagTreeApp()) 10 | ) 11 | 12 | func GetRedisApp() Redis { 13 | return redisApp 14 | } 15 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/personal/api.ts: -------------------------------------------------------------------------------- 1 | import Api from '@/common/Api'; 2 | 3 | export const personApi = { 4 | accountInfo: Api.newGet('/sys/accounts/self'), 5 | updateAccount: Api.newPut('/sys/accounts/self'), 6 | authStatus: Api.newGet('/auth/oauth2/status'), 7 | getMsgs: Api.newGet('/msgs/self'), 8 | unbindOauth2: Api.newGet('/auth/oauth2/unbind'), 9 | }; 10 | -------------------------------------------------------------------------------- /server/internal/sys/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | func Init(router *gin.RouterGroup) { 6 | InitCaptchaRouter(router) 7 | InitAccountRouter(router) // 注册account路由 8 | InitResourceRouter(router) 9 | InitRoleRouter(router) 10 | InitSystemRouter(router) 11 | InitSyslogRouter(router) 12 | InitSysConfigRouter(router) 13 | } 14 | -------------------------------------------------------------------------------- /mayfly_go_web/src/common/utils/arrayOperation.ts: -------------------------------------------------------------------------------- 1 | // 判断两数组是否相同 2 | export function judementSameArr(news: Array, old: Array) { 3 | let count = 0; 4 | const leng = old.length; 5 | for (let i in old) { 6 | for (let j in news) { 7 | if (old[i] === news[j]) count++; 8 | } 9 | } 10 | return count === leng ? true : false; 11 | } 12 | -------------------------------------------------------------------------------- /server/internal/common/api/common.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "mayfly-go/pkg/biz" 5 | "mayfly-go/pkg/req" 6 | "mayfly-go/pkg/utils/cryptox" 7 | ) 8 | 9 | type Common struct { 10 | } 11 | 12 | func (i *Common) RasPublicKey(rc *req.Ctx) { 13 | publicKeyStr, err := cryptox.GetRsaPublicKey() 14 | biz.ErrIsNilAppendErr(err, "rsa生成公私钥失败") 15 | rc.ResData = publicKeyStr 16 | } 17 | -------------------------------------------------------------------------------- /server/internal/sys/api/captcha.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "mayfly-go/pkg/biz" 5 | "mayfly-go/pkg/captcha" 6 | "mayfly-go/pkg/req" 7 | "mayfly-go/pkg/utils/collx" 8 | ) 9 | 10 | func GenerateCaptcha(rc *req.Ctx) { 11 | id, image, err := captcha.Generate() 12 | biz.ErrIsNilAppendErr(err, "获取验证码错误: %s") 13 | rc.ResData = collx.M{"base64Captcha": image, "cid": id} 14 | } 15 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/media/personal.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于768px 4 | ------------------------------- */ 5 | @media screen and (max-width: $sm) { 6 | .personal-info { 7 | padding-left: 0 !important; 8 | margin-top: 15px; 9 | } 10 | .personal-recommend-col { 11 | margin-bottom: 15px; 12 | &:last-of-type { 13 | margin-bottom: 0; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/internal/msg/domain/repository/msg.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/msg/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type Msg interface { 10 | base.Repo[*entity.Msg] 11 | 12 | GetPageList(condition *entity.Msg, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 13 | } 14 | -------------------------------------------------------------------------------- /server/internal/tag/domain/repository/team.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/tag/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type Team interface { 10 | base.Repo[*entity.Team] 11 | 12 | GetPageList(condition *entity.Team, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 13 | } 14 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/media/media.scss: -------------------------------------------------------------------------------- 1 | @import './login.scss'; 2 | @import './error.scss'; 3 | @import './layout.scss'; 4 | @import './personal.scss'; 5 | @import './tagsView.scss'; 6 | @import './home.scss'; 7 | @import './chart.scss'; 8 | @import './form.scss'; 9 | @import './scrollbar.scss'; 10 | @import './pagination.scss'; 11 | @import './dialog.scss'; 12 | @import './cityLinkage.scss'; 13 | -------------------------------------------------------------------------------- /server/internal/db/domain/entity/db.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "mayfly-go/pkg/model" 5 | ) 6 | 7 | type Db struct { 8 | model.Model 9 | 10 | Code string `orm:"column(code)" json:"code"` 11 | Name string `orm:"column(name)" json:"name"` 12 | Database string `orm:"column(database)" json:"database"` 13 | Remark string `json:"remark"` 14 | InstanceId uint64 15 | } 16 | -------------------------------------------------------------------------------- /server/internal/sys/domain/repository/config.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/sys/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type Config interface { 10 | base.Repo[*entity.Config] 11 | 12 | GetPageList(condition *entity.Config, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 13 | } 14 | -------------------------------------------------------------------------------- /server/internal/tag/domain/entity/tag_resource.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "mayfly-go/pkg/model" 5 | ) 6 | 7 | // 标签资源关联 8 | type TagResource struct { 9 | model.Model 10 | 11 | TagId uint64 `json:"tagId"` 12 | TagPath string `json:"tagPath"` // 标签路径 13 | ResourceCode string `json:"resourceCode"` // 资源标识 14 | ResourceType int8 `json:"resourceType"` // 资源类型 15 | } 16 | -------------------------------------------------------------------------------- /server/pkg/model/page.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | // 分页参数 4 | type PageParam struct { 5 | PageNum int `json:"pageNum"` 6 | PageSize int `json:"pageSize"` 7 | } 8 | 9 | // 分页结果 10 | type PageResult[T any] struct { 11 | Total int64 `json:"total"` 12 | List T `json:"list"` 13 | } 14 | 15 | // 空分页结果 16 | func EmptyPageResult[T any]() *PageResult[T] { 17 | return &PageResult[T]{Total: 0} 18 | } 19 | -------------------------------------------------------------------------------- /server/internal/sys/domain/repository/account.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/sys/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type Account interface { 10 | base.Repo[*entity.Account] 11 | 12 | GetPageList(condition *entity.Account, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 13 | } 14 | -------------------------------------------------------------------------------- /server/internal/sys/domain/repository/syslog.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/sys/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type Syslog interface { 10 | base.Repo[*entity.SysLog] 11 | 12 | GetPageList(condition *entity.SysLogQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 13 | } 14 | -------------------------------------------------------------------------------- /server/internal/machine/domain/entity/machine_monitor.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type MachineMonitor struct { 8 | Id uint64 `json:"id"` 9 | MachineId uint64 `json:"machineId"` 10 | CpuRate float32 `json:"cpuRate"` 11 | MemRate float32 `json:"memRate"` 12 | SysLoad string `json:"sysLoad"` 13 | CreateTime time.Time `json:"createTime"` 14 | } 15 | -------------------------------------------------------------------------------- /server/internal/auth/api/form/form.go: -------------------------------------------------------------------------------- 1 | package form 2 | 3 | type LoginForm struct { 4 | Username string `json:"username" binding:"required"` 5 | Password string `binding:"required"` 6 | Captcha string `json:"captcha"` 7 | Cid string `json:"cid"` 8 | } 9 | 10 | type OtpVerfiy struct { 11 | OtpToken string `json:"otpToken" binding:"required"` 12 | Code string `json:"code" binding:"required"` 13 | } 14 | -------------------------------------------------------------------------------- /server/internal/common/router/common.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/common/api" 5 | "mayfly-go/pkg/req" 6 | 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | func InitCommonRouter(router *gin.RouterGroup) { 11 | common := router.Group("common") 12 | c := &api.Common{} 13 | { 14 | // 获取公钥 15 | req.NewGet("public-key", c.RasPublicKey).DontNeedToken().Group(common) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/internal/mongo/domain/repository/mongo.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/mongo/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type Mongo interface { 10 | base.Repo[*entity.Mongo] 11 | 12 | // 分页获取列表 13 | GetList(condition *entity.MongoQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 14 | } 15 | -------------------------------------------------------------------------------- /server/internal/tag/domain/repository/tag_tree_team.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/tag/domain/entity" 5 | "mayfly-go/pkg/base" 6 | ) 7 | 8 | type TagTreeTeam interface { 9 | base.Repo[*entity.TagTreeTeam] 10 | 11 | // 获取团队标签信息列表 12 | // ListTag(condition *entity.TagTreeTeam, toEntity any, orderBy ...string) 13 | 14 | SelectTagPathsByAccountId(accountId uint64) []string 15 | } 16 | -------------------------------------------------------------------------------- /server/internal/redis/domain/repository/redis.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/redis/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type Redis interface { 10 | base.Repo[*entity.Redis] 11 | 12 | // 分页获取机器信息列表 13 | GetRedisList(condition *entity.RedisQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 14 | } 15 | -------------------------------------------------------------------------------- /server/internal/db/domain/repository/db_sql_exec.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/db/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type DbSqlExec interface { 10 | base.Repo[*entity.DbSqlExec] 11 | 12 | // 分页获取 13 | GetPageList(condition *entity.DbSqlExecQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 14 | } 15 | -------------------------------------------------------------------------------- /server/internal/db/infrastructure/persistence/db_sql.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/db/domain/entity" 5 | "mayfly-go/internal/db/domain/repository" 6 | "mayfly-go/pkg/base" 7 | ) 8 | 9 | type dbSqlRepoImpl struct { 10 | base.RepoImpl[*entity.DbSql] 11 | } 12 | 13 | func newDbSqlRepo() repository.DbSql { 14 | return &dbSqlRepoImpl{base.RepoImpl[*entity.DbSql]{M: new(entity.DbSql)}} 15 | } 16 | -------------------------------------------------------------------------------- /server/internal/msg/router/msg.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/msg/api" 5 | "mayfly-go/internal/msg/application" 6 | "mayfly-go/pkg/req" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func InitMsgRouter(router *gin.RouterGroup) { 12 | msg := router.Group("msgs") 13 | a := &api.Msg{ 14 | MsgApp: application.GetMsgApp(), 15 | } 16 | 17 | req.NewGet("/self", a.GetMsgs).Group(msg) 18 | } 19 | -------------------------------------------------------------------------------- /server/internal/tag/api/vo/team.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | import "time" 4 | 5 | // 团队成员信息 6 | type TeamMember struct { 7 | Id uint64 `json:"id"` 8 | TeamId uint64 `json:"teamId"` 9 | AccountId uint64 `json:"accountId"` 10 | Username string `json:"username"` 11 | Name string `json:"name"` 12 | Creator string `json:"creator"` 13 | CreateTime *time.Time `json:"createTime"` 14 | } 15 | -------------------------------------------------------------------------------- /server/pkg/logx/color.go: -------------------------------------------------------------------------------- 1 | package logx 2 | 3 | const ( 4 | Reset = "\033[0m" 5 | Red = "\033[31m" 6 | Green = "\033[32m" 7 | Yellow = "\033[33m" 8 | Blue = "\033[34m" 9 | Magenta = "\033[35m" 10 | Cyan = "\033[36m" 11 | White = "\033[37m" 12 | BlueBold = "\033[34;1m" 13 | MagentaBold = "\033[35;1m" 14 | RedBold = "\033[31;1m" 15 | YellowBold = "\033[33;1m" 16 | ) 17 | -------------------------------------------------------------------------------- /server/internal/db/domain/repository/instance.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/db/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type Instance interface { 10 | base.Repo[*entity.DbInstance] 11 | 12 | // 分页获取数据库实例信息列表 13 | GetInstanceList(condition *entity.InstanceQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 14 | } 15 | -------------------------------------------------------------------------------- /server/resources/script/startup.sh: -------------------------------------------------------------------------------- 1 | #bin/bash 2 | 3 | execfile=./mayfly-go 4 | 5 | pid=`ps ax | grep -i 'mayfly-go' | grep -v grep | awk '{print $1}'` 6 | if [ ! -z "${pid}" ] ; then 7 | echo "The mayfly-go already running, shutdown and restart..." 8 | kill ${pid} 9 | fi 10 | 11 | if [ ! -x "${execfile}" ]; then 12 | sudo chmod +x "${execfile}" 13 | fi 14 | 15 | nohup "${execfile}" & 16 | 17 | echo "The mayfly-go running..." -------------------------------------------------------------------------------- /server/internal/sys/router/syslog.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/sys/api" 5 | "mayfly-go/internal/sys/application" 6 | "mayfly-go/pkg/req" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func InitSyslogRouter(router *gin.RouterGroup) { 12 | s := &api.Syslog{ 13 | SyslogApp: application.GetSyslogApp(), 14 | } 15 | sysG := router.Group("syslogs") 16 | 17 | req.NewGet("", s.Syslogs).Group(sysG) 18 | } 19 | -------------------------------------------------------------------------------- /server/internal/machine/domain/repository/machine_file.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/machine/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type MachineFile interface { 10 | base.Repo[*entity.MachineFile] 11 | 12 | // 分页获取机器脚本信息列表 13 | GetPageList(condition *entity.MachineFile, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 14 | } 15 | -------------------------------------------------------------------------------- /server/internal/db/domain/repository/db.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/db/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type Db interface { 10 | base.Repo[*entity.Db] 11 | 12 | // 分页获取数据信息列表 13 | GetDbList(condition *entity.DbQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 14 | 15 | Count(condition *entity.DbQuery) int64 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | *.lock 15 | *.sum 16 | 17 | */node_modules/ 18 | **/vendor/ 19 | .idea 20 | .vscode 21 | out 22 | 23 | server/docs/docker-compose 24 | server/config.yml 25 | server/ip2region.xdb 26 | mayfly-go.log 27 | -------------------------------------------------------------------------------- /server/internal/machine/domain/repository/machine_script.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/machine/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type MachineScript interface { 10 | base.Repo[*entity.MachineScript] 11 | 12 | // 分页获取机器脚本信息列表 13 | GetPageList(condition *entity.MachineScript, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 14 | } 15 | -------------------------------------------------------------------------------- /server/internal/machine/domain/repository/machine_term_op.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/machine/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type MachineTermOp interface { 10 | base.Repo[*entity.MachineTermOp] 11 | 12 | // 分页获取机器终端执行记录列表 13 | GetPageList(condition *entity.MachineTermOp, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 14 | } 15 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/ops/db/enums.ts: -------------------------------------------------------------------------------- 1 | import { EnumValue } from '@/common/Enum'; 2 | 3 | // 数据库sql执行类型 4 | export const DbSqlExecTypeEnum = { 5 | Update: EnumValue.of(1, 'UPDATE').setTagColor('#E4F5EB'), 6 | Delete: EnumValue.of(2, 'DELETE').setTagColor('#F9E2AE'), 7 | Insert: EnumValue.of(3, 'INSERT').setTagColor('#A8DEE0'), 8 | Query: EnumValue.of(4, 'QUERY').setTagColor('#A8DEE0'), 9 | Other: EnumValue.of(-1, 'OTHER').setTagColor('#F9E2AE'), 10 | }; 11 | -------------------------------------------------------------------------------- /server/internal/machine/domain/repository/auth_cert.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/machine/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type AuthCert interface { 10 | base.Repo[*entity.AuthCert] 11 | 12 | GetPageList(condition *entity.AuthCertQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 13 | 14 | // GetByIds(ids ...uint64) []*entity.AuthCert 15 | } 16 | -------------------------------------------------------------------------------- /mayfly_go_web/src/layout/main/transverse.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /server/internal/auth/infrastructure/persistence/oauth2.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/auth/domain/entity" 5 | "mayfly-go/internal/auth/domain/repository" 6 | "mayfly-go/pkg/base" 7 | ) 8 | 9 | type oauth2AccountRepoImpl struct { 10 | base.RepoImpl[*entity.Oauth2Account] 11 | } 12 | 13 | func newAuthAccountRepo() repository.Oauth2Account { 14 | return &oauth2AccountRepoImpl{base.RepoImpl[*entity.Oauth2Account]{M: new(entity.Oauth2Account)}} 15 | } 16 | -------------------------------------------------------------------------------- /server/internal/sys/domain/repository/resource.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/sys/domain/entity" 5 | "mayfly-go/pkg/base" 6 | ) 7 | 8 | type Resource interface { 9 | base.Repo[*entity.Resource] 10 | 11 | // 获取账号资源列表 12 | GetAccountResources(accountId uint64, toEntity any) error 13 | 14 | // 获取所有子节点id 15 | GetChildren(uiPath string) []entity.Resource 16 | 17 | // 根据uiPath右匹配更新所有相关类资源 18 | UpdateByUiPathLike(resource *entity.Resource) error 19 | } 20 | -------------------------------------------------------------------------------- /server/internal/mongo/mgm/conn.go: -------------------------------------------------------------------------------- 1 | package mgm 2 | 3 | import ( 4 | "context" 5 | "mayfly-go/pkg/logx" 6 | 7 | "go.mongodb.org/mongo-driver/mongo" 8 | ) 9 | 10 | type MongoConn struct { 11 | Id string 12 | Info *MongoInfo 13 | 14 | Cli *mongo.Client 15 | } 16 | 17 | func (mc *MongoConn) Close() { 18 | if mc.Cli != nil { 19 | if err := mc.Cli.Disconnect(context.Background()); err != nil { 20 | logx.Errorf("关闭mongo实例[%s]连接失败: %s", mc.Id, err) 21 | } 22 | mc.Cli = nil 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/internal/db/application/db_sql.go: -------------------------------------------------------------------------------- 1 | package application 2 | 3 | import ( 4 | "mayfly-go/internal/db/domain/entity" 5 | "mayfly-go/internal/db/domain/repository" 6 | "mayfly-go/pkg/base" 7 | ) 8 | 9 | type DbSql interface { 10 | base.App[*entity.DbSql] 11 | } 12 | 13 | type dbSqlAppImpl struct { 14 | base.AppImpl[*entity.DbSql, repository.DbSql] 15 | } 16 | 17 | func newDbSqlApp(dbSqlRepo repository.DbSql) DbSql { 18 | app := new(dbSqlAppImpl) 19 | app.Repo = dbSqlRepo 20 | return app 21 | } 22 | -------------------------------------------------------------------------------- /server/internal/db/router/db_sql_exec.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/db/api" 5 | "mayfly-go/internal/db/application" 6 | "mayfly-go/pkg/req" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func InitDbSqlExecRouter(router *gin.RouterGroup) { 12 | db := router.Group("/dbs/:dbId/sql-execs") 13 | 14 | d := &api.DbSqlExec{ 15 | DbSqlExecApp: application.GetDbSqlExecApp(), 16 | } 17 | 18 | // 获取所有数据库sql执行记录列表 19 | req.NewGet("", d.DbSqlExecs).Group(db) 20 | 21 | } 22 | -------------------------------------------------------------------------------- /server/internal/machine/domain/repository/machine.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/machine/api/vo" 5 | "mayfly-go/internal/machine/domain/entity" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/model" 8 | ) 9 | 10 | type Machine interface { 11 | base.Repo[*entity.Machine] 12 | 13 | // 分页获取机器信息列表 14 | GetMachineList(condition *entity.MachineQuery, pageParam *model.PageParam, toEntity *[]*vo.MachineVO, orderBy ...string) (*model.PageResult[*[]*vo.MachineVO], error) 15 | } 16 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/media/login.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于576px 4 | ------------------------------- */ 5 | @media screen and (max-width: $xs) { 6 | .login-container { 7 | .login-content { 8 | width: 90% !important; 9 | padding: 20px 0 !important; 10 | } 11 | .login-content-form-btn { 12 | width: 100% !important; 13 | padding: 12px 0 !important; 14 | } 15 | .login-copyright { 16 | .login-copyright-msg { 17 | white-space: unset !important; 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/migrations/20230720.go: -------------------------------------------------------------------------------- 1 | package migrations 2 | 3 | import ( 4 | authentity "mayfly-go/internal/auth/domain/entity" 5 | 6 | "github.com/go-gormigrate/gormigrate/v2" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | // T20230720 三方登录表 11 | func T20230720() *gormigrate.Migration { 12 | return &gormigrate.Migration{ 13 | ID: "20230720", 14 | Migrate: func(tx *gorm.DB) error { 15 | return tx.AutoMigrate(&authentity.Oauth2Account{}) 16 | }, 17 | Rollback: func(tx *gorm.DB) error { 18 | return nil 19 | }, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/internal/msg/domain/entity/msg.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "mayfly-go/pkg/model" 5 | "time" 6 | ) 7 | 8 | type Msg struct { 9 | model.DeletedModel 10 | 11 | CreateTime *time.Time `json:"createTime"` 12 | CreatorId uint64 `json:"creatorId"` 13 | Creator string `json:"creator"` 14 | 15 | Type int `json:"type"` 16 | Msg string `json:"msg"` 17 | RecipientId int64 `json:"recipientId"` // 接受者id 18 | } 19 | 20 | func (a *Msg) TableName() string { 21 | return "t_sys_msg" 22 | } 23 | -------------------------------------------------------------------------------- /server/internal/machine/domain/entity/machine_script.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "mayfly-go/pkg/model" 4 | 5 | type MachineScript struct { 6 | model.Model 7 | Name string `json:"name"` 8 | MachineId uint64 `json:"machineId"` // 机器id 9 | Type int `json:"type"` 10 | Description string `json:"description"` // 脚本描述 11 | Params string `json:"params"` // 参数列表json 12 | Script string `json:"script" gorm:"column:script;type:text"` // 脚本内容 13 | } 14 | -------------------------------------------------------------------------------- /server/pkg/cache/cache.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | type Cache interface { 4 | // 添加缓存,如果缓存则返回错误 5 | Add(k string, v any) error 6 | 7 | // 如果不存在则添加缓存值,否则直接返回 8 | AddIfAbsent(k string, v any) 9 | 10 | // 如果存在则直接返回,否则调用getValue回调函数获取值并添加该缓存值 11 | // @return 缓存值 12 | ComputeIfAbsent(k string, getValueFunc func(string) (any, error)) (any, error) 13 | 14 | // 获取缓存值,参数1为值,参数2->是否存在该缓存 15 | Get(k string) (any, bool) 16 | 17 | // 缓存数量 18 | Count() int 19 | 20 | // 删除缓存 21 | Delete(k string) 22 | 23 | // 清空所有缓存 24 | Clear() 25 | } 26 | -------------------------------------------------------------------------------- /server/pkg/utils/stringx/template_test.go: -------------------------------------------------------------------------------- 1 | package stringx 2 | 3 | import ( 4 | "fmt" 5 | "mayfly-go/pkg/utils/collx" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | func TestTemplateParse(t *testing.T) { 11 | tmpl := ` 12 | {{if gt .cpu 10*5}} 13 | 当前服务器[{{.asset.host}}]cpu使用率为{{.cpu}} 14 | {{end}} 15 | ` 16 | vars := collx.M{ 17 | "cpu": 60, 18 | "asset": collx.M{ 19 | "host": "localhost:121", 20 | }, 21 | } 22 | 23 | res, _ := TemplateParse(tmpl, vars) 24 | res2 := strings.TrimSpace(res) 25 | fmt.Println(res2) 26 | } 27 | -------------------------------------------------------------------------------- /server/internal/machine/domain/entity/machine_term_op.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "mayfly-go/pkg/model" 5 | "time" 6 | ) 7 | 8 | type MachineTermOp struct { 9 | model.DeletedModel 10 | 11 | MachineId uint64 `json:"machineId"` 12 | Username string `json:"username"` 13 | RecordFilePath string `json:"recordFilePath"` // 回放文件路径 14 | 15 | CreateTime *time.Time `json:"createTime"` 16 | CreatorId uint64 `json:"creatorId"` 17 | Creator string `json:"creator"` 18 | EndTime *time.Time `json:"endTime"` 19 | } 20 | -------------------------------------------------------------------------------- /mayfly_go_web/src/store/routesList.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | 3 | /** 4 | * 路由列表 5 | * @methods setRoutesList 设置路由数据 6 | * @methods setColumnsMenuHover 设置分栏布局菜单鼠标移入 boolean 7 | * @methods setColumnsNavHover 设置分栏布局最左侧导航鼠标移入 boolean 8 | */ 9 | export const useRoutesList = defineStore('routesList', { 10 | state: (): RoutesListState => ({ 11 | routesList: [], 12 | }), 13 | actions: { 14 | async setRoutesList(data: Array) { 15 | this.routesList = data; 16 | }, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /server/pkg/utils/assert/assert.go: -------------------------------------------------------------------------------- 1 | package assert 2 | 3 | import "fmt" 4 | 5 | // 断言条件为真,不满足的panic 6 | func IsTrue(condition bool, panicMsg string, params ...any) { 7 | if !condition { 8 | if len(params) != 0 { 9 | panic(fmt.Sprintf(panicMsg, params...)) 10 | } 11 | panic(panicMsg) 12 | } 13 | } 14 | 15 | func State(condition bool, panicMsg string, params ...any) { 16 | IsTrue(condition, panicMsg, params...) 17 | } 18 | 19 | func NotEmpty(str string, panicMsg string, params ...any) { 20 | IsTrue(str != "", panicMsg, params...) 21 | } 22 | -------------------------------------------------------------------------------- /server/internal/auth/domain/entity/oauth2.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "mayfly-go/pkg/model" 5 | "time" 6 | ) 7 | 8 | type Oauth2Account struct { 9 | model.DeletedModel 10 | 11 | AccountId uint64 `json:"accountId" gorm:"column:account_id;index:account_id,unique"` 12 | Identity string `json:"identity" gorm:"column:identity;index:identity,unique"` 13 | 14 | CreateTime *time.Time `json:"createTime"` 15 | UpdateTime *time.Time `json:"updateTime"` 16 | } 17 | 18 | func (Oauth2Account) TableName() string { 19 | return "t_oauth2_account" 20 | } 21 | -------------------------------------------------------------------------------- /server/pkg/utils/collx/map.go: -------------------------------------------------------------------------------- 1 | package collx 2 | 3 | import "mayfly-go/pkg/utils/anyx" 4 | 5 | // M is a shortcut for map[string]any 6 | type M map[string]any 7 | 8 | // 将偶数个元素转为对应的M (map[string]any) 9 | // 10 | // 偶数索引为key,奇数为value 11 | func Kvs(elements ...any) M { 12 | myMap := make(map[string]any) 13 | 14 | for i := 0; i < len(elements); i += 2 { 15 | key := anyx.ToString(elements[i]) 16 | if i+1 < len(elements) { 17 | value := elements[i+1] 18 | myMap[key] = value 19 | } else { 20 | myMap[key] = nil 21 | } 22 | } 23 | return myMap 24 | } 25 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/ops/machine/SshTerminalPage.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 14 | 19 | -------------------------------------------------------------------------------- /server/internal/machine/domain/entity/query.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type MachineQuery struct { 4 | Ids string `json:"ids" form:"ids"` 5 | Name string `json:"name" form:"name"` 6 | Status int8 `json:"status" form:"status"` 7 | Ip string `json:"ip" form:"ip"` // IP地址 8 | TagPath string `json:"tagPath" form:"tagPath"` 9 | 10 | Codes []string 11 | } 12 | 13 | type AuthCertQuery struct { 14 | Id uint64 `json:"id" form:"id"` 15 | Name string `json:"name" form:"name"` 16 | AuthMethod string `json:"authMethod" form:"authMethod"` // IP地址 17 | } 18 | -------------------------------------------------------------------------------- /server/internal/tag/domain/repository/team_member.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/tag/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type TeamMember interface { 10 | base.Repo[*entity.TeamMember] 11 | 12 | // 获取项目成员列表 13 | ListMemeber(condition *entity.TeamMember, toEntity any, orderBy ...string) 14 | 15 | GetPageList(condition *entity.TeamMember, pageParam *model.PageParam, toEntity any) (*model.PageResult[any], error) 16 | 17 | // 是否存在指定的团队成员关联信息 18 | IsExist(teamId, accountId uint64) bool 19 | } 20 | -------------------------------------------------------------------------------- /mayfly_go_web/src/store/userInfo.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia'; 2 | import { getUser } from '@/common/utils/storage'; 3 | 4 | export const useUserInfo = defineStore('userInfo', { 5 | state: (): UserInfoState => ({ 6 | userInfo: {}, 7 | }), 8 | actions: { 9 | // 设置用户信息 10 | async setUserInfo(data: object) { 11 | const ui = getUser(); 12 | if (ui) { 13 | this.userInfo = ui; 14 | } else { 15 | this.userInfo = data; 16 | } 17 | }, 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /server/resources/script/sql/v1.5.4.sql: -------------------------------------------------------------------------------- 1 | -- 新增机器相关系统配置 2 | INSERT INTO `t_sys_config` (name, `key`, params, value, remark, permission, create_time, creator_id, creator, update_time, modifier_id, modifier, is_deleted, delete_time) VALUES('机器相关配置', 'MachineConfig', '[{"name":"终端回放存储路径","model":"terminalRecPath","placeholder":"终端回放存储路径"},{"name":"uploadMaxFileSize","model":"uploadMaxFileSize","placeholder":"允许上传的最大文件大小(1MB\\\\2GB等)"}]', '{"terminalRecPath":"./rec","uploadMaxFileSize":"1GB"}', '机器相关配置,如终端回放路径等', 'admin,', '2023-07-13 16:26:44', 1, 'admin', '2023-11-09 22:01:31', 1, 'admin', 0, NULL); -------------------------------------------------------------------------------- /server/internal/db/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import sysapp "mayfly-go/internal/sys/application" 4 | 5 | const ( 6 | ConfigKeyDbSaveQuerySQL string = "DbSaveQuerySQL" // 数据库是否记录查询相关sql 7 | ConfigKeyDbQueryMaxCount string = "DbQueryMaxCount" // 数据库查询的最大数量 8 | ) 9 | 10 | // 获取数据库最大查询数量配置 11 | func GetDbQueryMaxCount() int { 12 | return sysapp.GetConfigApp().GetConfig(ConfigKeyDbQueryMaxCount).IntValue(200) 13 | } 14 | 15 | // 获取数据库是否记录查询相关sql配置 16 | func GetDbSaveQuerySql() bool { 17 | return sysapp.GetConfigApp().GetConfig(ConfigKeyDbSaveQuerySQL).BoolValue(false) 18 | } 19 | -------------------------------------------------------------------------------- /server/internal/db/api/db_sql_exec.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "mayfly-go/internal/db/application" 5 | "mayfly-go/internal/db/domain/entity" 6 | "mayfly-go/pkg/biz" 7 | "mayfly-go/pkg/ginx" 8 | "mayfly-go/pkg/req" 9 | ) 10 | 11 | type DbSqlExec struct { 12 | DbSqlExecApp application.DbSqlExec 13 | } 14 | 15 | func (d *DbSqlExec) DbSqlExecs(rc *req.Ctx) { 16 | queryCond, page := ginx.BindQueryAndPage(rc.GinCtx, new(entity.DbSqlExecQuery)) 17 | res, err := d.DbSqlExecApp.GetPageList(queryCond, page, new([]entity.DbSqlExec)) 18 | biz.ErrIsNil(err) 19 | rc.ResData = res 20 | } 21 | -------------------------------------------------------------------------------- /server/internal/msg/api/msg.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "mayfly-go/internal/msg/application" 5 | "mayfly-go/internal/msg/domain/entity" 6 | "mayfly-go/pkg/biz" 7 | "mayfly-go/pkg/ginx" 8 | "mayfly-go/pkg/req" 9 | ) 10 | 11 | type Msg struct { 12 | MsgApp application.Msg 13 | } 14 | 15 | // 获取账号接收的消息列表 16 | func (m *Msg) GetMsgs(rc *req.Ctx) { 17 | condition := &entity.Msg{ 18 | RecipientId: int64(rc.GetLoginAccount().Id), 19 | } 20 | res, err := m.MsgApp.GetPageList(condition, ginx.GetPageParam(rc.GinCtx), new([]entity.Msg)) 21 | biz.ErrIsNil(err) 22 | rc.ResData = res 23 | } 24 | -------------------------------------------------------------------------------- /server/internal/sys/api/syslog.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "mayfly-go/internal/sys/application" 5 | "mayfly-go/internal/sys/domain/entity" 6 | "mayfly-go/pkg/biz" 7 | "mayfly-go/pkg/ginx" 8 | "mayfly-go/pkg/req" 9 | ) 10 | 11 | type Syslog struct { 12 | SyslogApp application.Syslog 13 | } 14 | 15 | func (r *Syslog) Syslogs(rc *req.Ctx) { 16 | queryCond, page := ginx.BindQueryAndPage[*entity.SysLogQuery](rc.GinCtx, new(entity.SysLogQuery)) 17 | res, err := r.SyslogApp.GetPageList(queryCond, page, new([]entity.SysLog), "create_time DESC") 18 | biz.ErrIsNil(err) 19 | rc.ResData = res 20 | } 21 | -------------------------------------------------------------------------------- /server/pkg/otp/otp.go: -------------------------------------------------------------------------------- 1 | package otp 2 | 3 | import ( 4 | "time" 5 | 6 | otp_t "github.com/pquerna/otp" 7 | totp_t "github.com/pquerna/otp/totp" 8 | ) 9 | 10 | type GenerateOpts totp_t.GenerateOpts 11 | 12 | func NewTOTP(opt GenerateOpts) (*otp_t.Key, error) { 13 | return totp_t.Generate(totp_t.GenerateOpts(opt)) 14 | } 15 | 16 | func Validate(code string, secret string) bool { 17 | if secret == "" { 18 | return true 19 | } 20 | return totp_t.Validate(code, secret) 21 | } 22 | 23 | func GenTotpCode(code string, secret string) (string, error) { 24 | return totp_t.GenerateCode(secret, time.Now()) 25 | } 26 | -------------------------------------------------------------------------------- /server/pkg/utils/ymlx/ymlx.go: -------------------------------------------------------------------------------- 1 | package ymlx 2 | 3 | import ( 4 | "errors" 5 | "os" 6 | 7 | "gopkg.in/yaml.v3" 8 | ) 9 | 10 | // 从指定路径加载yaml文件 11 | func LoadYml(path string, out any) error { 12 | yamlFileBytes, readErr := os.ReadFile(path) 13 | if readErr != nil { 14 | return readErr 15 | } 16 | // yaml解析 17 | err := yaml.Unmarshal(yamlFileBytes, out) 18 | if err != nil { 19 | return errors.New("无法解析 [" + path + "] -- " + err.Error()) 20 | } 21 | return nil 22 | } 23 | 24 | func LoadYmlByString(yamlStr string, out any) error { 25 | // yaml解析 26 | return yaml.Unmarshal([]byte(yamlStr), out) 27 | } 28 | -------------------------------------------------------------------------------- /server/internal/tag/domain/entity/tag_tree.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "mayfly-go/pkg/model" 5 | "strings" 6 | ) 7 | 8 | // 标签树 9 | type TagTree struct { 10 | model.Model 11 | 12 | Pid uint64 `json:"pid"` 13 | Code string `json:"code"` // 标识 14 | CodePath string `json:"codePath"` // 标识路径 15 | Name string `json:"name"` // 名称 16 | Remark string `json:"remark"` // 备注说明 17 | } 18 | 19 | const ( 20 | // 标识路径分隔符 21 | CodePathSeparator = "/" 22 | ) 23 | 24 | // 获取根路径信息 25 | func (pt *TagTree) GetRootCode() string { 26 | return strings.Split(pt.CodePath, CodePathSeparator)[0] 27 | } 28 | -------------------------------------------------------------------------------- /server/pkg/utils/stringx/template.go: -------------------------------------------------------------------------------- 1 | package stringx 2 | 3 | import ( 4 | "bytes" 5 | "text/template" 6 | ) 7 | 8 | func parse(t *template.Template, vars any) (string, error) { 9 | var tmplBytes bytes.Buffer 10 | 11 | err := t.Execute(&tmplBytes, vars) 12 | if err != nil { 13 | return "", err 14 | } 15 | return tmplBytes.String(), nil 16 | } 17 | 18 | // 模板字符串解析 19 | // @param str 模板字符串 20 | // @param vars 参数变量 21 | func TemplateParse(str string, vars any) (string, error) { 22 | tmpl, err := template.New("tmpl").Parse(str) 23 | 24 | if err != nil { 25 | return "", err 26 | } 27 | return parse(tmpl, vars) 28 | } 29 | -------------------------------------------------------------------------------- /server/pkg/logx/json_handler.go: -------------------------------------------------------------------------------- 1 | package logx 2 | 3 | import ( 4 | "log/slog" 5 | "time" 6 | ) 7 | 8 | func NewJsonHandler(config *Config) *slog.JSONHandler { 9 | replace := func(groups []string, a slog.Attr) slog.Attr { 10 | // 格式化时间. 11 | if a.Key == slog.TimeKey && len(groups) == 0 { 12 | return slog.Attr{Key: "time", Value: slog.StringValue(time.Now().Local().Format("2006-01-02 15:04:05.000"))} 13 | } 14 | return a 15 | } 16 | 17 | return slog.NewJSONHandler(config.GetLogOut(), &slog.HandlerOptions{ 18 | Level: config.GetLevel(), 19 | AddSource: false, // 统一由添加公共commonAttrs时判断添加 20 | ReplaceAttr: replace, 21 | }) 22 | } 23 | -------------------------------------------------------------------------------- /server/pkg/utils/collx/byte.go: -------------------------------------------------------------------------------- 1 | package collx 2 | 3 | import "encoding/binary" 4 | 5 | func Bytes2Int8(bytes []byte) int8 { 6 | return int8(Byte2Uint16(bytes)) 7 | } 8 | 9 | func Bytes2Int(bytes []byte) int { 10 | return int(Byte2Uint64(bytes)) 11 | } 12 | 13 | func Bytes2Int64(bytes []byte) int64 { 14 | return int64(Byte2Uint64(bytes)) 15 | } 16 | 17 | func Byte2Uint64(bytes []byte) uint64 { 18 | return binary.LittleEndian.Uint64(bytes) 19 | } 20 | 21 | func Byte2Uint32(bytes []byte) uint32 { 22 | return binary.LittleEndian.Uint32(bytes) 23 | } 24 | 25 | func Byte2Uint16(bytes []byte) uint16 { 26 | return binary.LittleEndian.Uint16(bytes) 27 | } 28 | -------------------------------------------------------------------------------- /server/internal/sys/domain/entity/query.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | type SysLogQuery struct { 4 | CreatorId uint64 `json:"creatorId" form:"creatorId"` 5 | Type int8 `json:"type" form:"type"` 6 | Description string `json:"description" form:"description"` 7 | } 8 | 9 | type RoleQuery struct { 10 | Ids []uint64 `json:"ids"` 11 | Name string `json:"name" form:"name"` 12 | Code string `json:"code" form:"code"` 13 | NotIds []uint64 `json:"notIds"` 14 | } 15 | 16 | type RoleAccountQuery struct { 17 | RoleId uint64 `json:"roleId" ` 18 | Name string `json:"name" form:"name"` 19 | Username string `json:"username" form:"username"` 20 | } 21 | -------------------------------------------------------------------------------- /server/internal/redis/domain/entity/query.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "mayfly-go/pkg/model" 4 | 5 | type RedisQuery struct { 6 | model.Model 7 | 8 | Name string `orm:"column(name)" json:"name"` 9 | Host string `orm:"column(host)" json:"host"` 10 | Mode string `json:"mode"` 11 | Password string `orm:"column(password)" json:"-"` 12 | Db string `orm:"column(database)" json:"db"` 13 | SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id 14 | Remark string 15 | 16 | Codes []string 17 | TagPath string `form:"tagPath"` 18 | } 19 | -------------------------------------------------------------------------------- /server/internal/db/api/form/instance.go: -------------------------------------------------------------------------------- 1 | package form 2 | 3 | type InstanceForm struct { 4 | Id uint64 `json:"id"` 5 | Name string `binding:"required" json:"name"` 6 | Type string `binding:"required" json:"type"` // 类型,mysql oracle等 7 | Host string `binding:"required" json:"host"` 8 | Port int `binding:"required" json:"port"` 9 | Username string `binding:"required" json:"username"` 10 | Password string `json:"password"` 11 | Params string `json:"params"` 12 | Remark string `json:"remark"` 13 | SshTunnelMachineId int `json:"sshTunnelMachineId"` 14 | } 15 | -------------------------------------------------------------------------------- /server/pkg/config/sqlite.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import "mayfly-go/pkg/logx" 4 | 5 | type Sqlite struct { 6 | Path string `mapstructure:"path" json:"path" yaml:"path"` 7 | MaxIdleConns int `mapstructure:"max-idle-conns" json:"maxIdleConns" yaml:"max-idle-conns"` 8 | MaxOpenConns int `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"` 9 | } 10 | 11 | func (m *Sqlite) Default() { 12 | if m.Path == "" { 13 | m.Path = "./mayfly-go.sqlite" 14 | logx.Warnf("未配置sqlite.path, 默认值: %s", m.Path) 15 | } 16 | if m.MaxIdleConns == 0 { 17 | m.MaxIdleConns = 5 18 | } 19 | if m.MaxOpenConns == 0 { 20 | m.MaxOpenConns = m.MaxIdleConns 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/personal/mock.ts: -------------------------------------------------------------------------------- 1 | 2 | // 营销推荐 3 | export const recommendList: Array = [ 4 | { 5 | title: '优惠券', 6 | msg: '现金券、折扣券、营销必备', 7 | icon: 'el-icon-food', 8 | bg: '#48D18D', 9 | iconColor: '#64d89d' 10 | }, 11 | { 12 | title: '多人拼团', 13 | msg: '社交电商、开辟流量', 14 | icon: 'el-icon-shopping-bag-1', 15 | bg: '#F95959', 16 | iconColor: '#F86C6B' 17 | }, 18 | { 19 | title: '分销中心', 20 | msg: '轻松招募分销员,成功推广奖励', 21 | icon: 'el-icon-school', 22 | bg: '#8595F4', 23 | iconColor: '#92A1F4' 24 | }, 25 | { 26 | title: '秒杀', 27 | msg: '超低价抢购引导更多销量', 28 | icon: 'el-icon-alarm-clock', 29 | bg: '#FEBB50', 30 | iconColor: '#FDC566' 31 | } 32 | ]; 33 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/system/enums.ts: -------------------------------------------------------------------------------- 1 | import { EnumValue } from '@/common/Enum'; 2 | 3 | export const ResourceTypeEnum = { 4 | Menu: EnumValue.of(1, '菜单'), 5 | Permission: EnumValue.of(2, '权限'), 6 | }; 7 | 8 | export const AccountStatusEnum = { 9 | Enable: EnumValue.of(1, '正常').tagTypeSuccess(), 10 | Disable: EnumValue.of(-1, '禁用').tagTypeDanger(), 11 | }; 12 | 13 | export const RoleStatusEnum = { 14 | Enable: EnumValue.of(1, '正常').tagTypeSuccess(), 15 | Disable: EnumValue.of(-1, '禁用').tagTypeDanger(), 16 | }; 17 | 18 | export const LogTypeEnum = { 19 | Success: EnumValue.of(1, '成功').tagTypeSuccess(), 20 | Error: EnumValue.of(2, '失败').tagTypeDanger(), 21 | }; 22 | -------------------------------------------------------------------------------- /mayfly_go_web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 15 | 16 | 17 | mayfly 18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /server/internal/sys/api/form/role.go: -------------------------------------------------------------------------------- 1 | package form 2 | 3 | // 分配角色资源表单信息 4 | type RoleResourceForm struct { 5 | Id uint64 `json:"id"` 6 | ResourceIds string `json:"resourceIds"` 7 | } 8 | 9 | // 保存角色信息表单 10 | type RoleForm struct { 11 | Id int `json:"id"` 12 | Status int `json:"status"` // 1:可用;-1:不可用 13 | Name string `json:"name" binding:"required"` 14 | Code string `json:"code" binding:"required"` 15 | Remark string `json:"remark"` 16 | } 17 | 18 | // 账号分配角色表单 19 | type AccountRoleForm struct { 20 | Id uint64 `json:"id" binding:"required"` 21 | RoleId uint64 `json:"roleId" binding:"required"` 22 | RelateType int `json:"relateType" binding:"required"` 23 | } 24 | -------------------------------------------------------------------------------- /server/internal/db/domain/entity/db_sql_exec.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "mayfly-go/pkg/model" 4 | 5 | // 数据库sql执行记录 6 | type DbSqlExec struct { 7 | model.Model `orm:"-"` 8 | 9 | DbId uint64 `json:"dbId"` 10 | Db string `json:"db"` 11 | Table string `json:"table"` 12 | Type int8 `json:"type"` // 类型 13 | Sql string `json:"sql"` // 执行的sql 14 | OldValue string `json:"oldValue"` 15 | Remark string `json:"remark"` 16 | } 17 | 18 | const ( 19 | DbSqlExecTypeOther int8 = -1 // 其他类型 20 | DbSqlExecTypeUpdate int8 = 1 // 更新类型 21 | DbSqlExecTypeDelete int8 = 2 // 删除类型 22 | DbSqlExecTypeInsert int8 = 3 // 插入类型 23 | DbSqlExecTypeQuery int8 = 4 // 查询类型,如select、show等 24 | ) 25 | -------------------------------------------------------------------------------- /server/internal/db/infrastructure/persistence/persistence.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import "mayfly-go/internal/db/domain/repository" 4 | 5 | var ( 6 | instanceRepo repository.Instance = newInstanceRepo() 7 | dbRepo repository.Db = newDbRepo() 8 | dbSqlRepo repository.DbSql = newDbSqlRepo() 9 | dbSqlExecRepo repository.DbSqlExec = newDbSqlExecRepo() 10 | ) 11 | 12 | func GetInstanceRepo() repository.Instance { 13 | return instanceRepo 14 | } 15 | 16 | func GetDbRepo() repository.Db { 17 | return dbRepo 18 | } 19 | 20 | func GetDbSqlRepo() repository.DbSql { 21 | return dbSqlRepo 22 | } 23 | 24 | func GetDbSqlExecRepo() repository.DbSqlExec { 25 | return dbSqlExecRepo 26 | } 27 | -------------------------------------------------------------------------------- /server/pkg/starter/banner.go: -------------------------------------------------------------------------------- 1 | package starter 2 | 3 | import ( 4 | "fmt" 5 | "mayfly-go/pkg/config" 6 | "mayfly-go/pkg/logx" 7 | "os" 8 | "runtime/debug" 9 | ) 10 | 11 | func printBanner() { 12 | buildInfo, _ := debug.ReadBuildInfo() 13 | logx.Print(fmt.Sprintf(` 14 | __ _ 15 | _ __ ___ __ _ _ _ / _| |_ _ __ _ ___ 16 | | '_ ' _ \ / _' | | | | |_| | | | |_____ / _' |/ _ \ 17 | | | | | | | (_| | |_| | _| | |_| |_____| (_| | (_) | version: %s | go_version: %s | pid: %d 18 | |_| |_| |_|\__,_|\__, |_| |_|\__, | \__, |\___/ 19 | |___/ |___/ |___/ `, config.Version, buildInfo.GoVersion, os.Getpid())) 20 | } 21 | -------------------------------------------------------------------------------- /server/internal/db/router/db_sql.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/db/api" 5 | "mayfly-go/internal/db/application" 6 | "mayfly-go/pkg/req" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func InitDbSqlRouter(router *gin.RouterGroup) { 12 | db := router.Group("dbs") 13 | 14 | dbSql := &api.DbSql{ 15 | DbSqlApp: application.GetDbSqlApp(), 16 | } 17 | 18 | reqs := [...]*req.Conf{ 19 | 20 | // 用户sql相关 21 | req.NewPost(":dbId/sql", dbSql.SaveSql), 22 | 23 | req.NewGet(":dbId/sql", dbSql.GetSql), 24 | 25 | req.NewDelete(":dbId/sql", dbSql.DeleteSql), 26 | 27 | req.NewGet(":dbId/sql-names", dbSql.GetSqlNames), 28 | } 29 | 30 | req.BatchSetGroup(db, reqs[:]) 31 | 32 | } 33 | -------------------------------------------------------------------------------- /server/pkg/config/jwt.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "mayfly-go/pkg/logx" 5 | "mayfly-go/pkg/utils/assert" 6 | "mayfly-go/pkg/utils/stringx" 7 | ) 8 | 9 | type Jwt struct { 10 | Key string `yaml:"key"` 11 | ExpireTime uint64 `yaml:"expire-time"` // 过期时间,单位分钟 12 | } 13 | 14 | func (j *Jwt) Default() { 15 | if j.Key == "" { 16 | // 如果配置文件中的jwt key为空,则随机生成字符串 17 | j.Key = stringx.Rand(32) 18 | logx.Warnf("未配置jwt.key, 随机生成key为: %s", j.Key) 19 | } 20 | 21 | if j.ExpireTime == 0 { 22 | j.ExpireTime = 1440 23 | logx.Warnf("未配置jwt.expire-time, 默认值: %d", j.ExpireTime) 24 | } 25 | } 26 | 27 | func (j *Jwt) Valid() { 28 | assert.IsTrue(j.ExpireTime != 0, "config.yml之[jwt.expire-time] 不能为空") 29 | } 30 | -------------------------------------------------------------------------------- /mayfly_go_web/src/common/config.ts: -------------------------------------------------------------------------------- 1 | function getBaseApiUrl() { 2 | let path = window.location.pathname; 3 | if (path == '/') { 4 | return window.location.host; 5 | } 6 | if (path.endsWith('/')) { 7 | // 去除最后一个/ 8 | return window.location.host + path.replace(/\/$/, ''); 9 | } 10 | return window.location.host + path; 11 | } 12 | 13 | const config = { 14 | baseApiUrl: `${(window as any).globalConfig.BaseApiUrl || location.protocol + '//' + getBaseApiUrl()}/api`, 15 | baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`, 16 | 17 | // 系统版本 18 | version: 'v1.6.1', 19 | }; 20 | 21 | export default config; 22 | -------------------------------------------------------------------------------- /server/internal/common/consts/consts.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | import "time" 4 | 5 | const ( 6 | AdminId = 1 7 | 8 | MachineConnExpireTime = 60 * time.Minute 9 | DbConnExpireTime = 120 * time.Minute 10 | RedisConnExpireTime = 30 * time.Minute 11 | MongoConnExpireTime = 30 * time.Minute 12 | 13 | /**** 开发测试使用 ****/ 14 | // MachineConnExpireTime = 4 * time.Minute 15 | // DbConnExpireTime = 2 * time.Minute 16 | // RedisConnExpireTime = 2 * time.Minute 17 | // MongoConnExpireTime = 2 * time.Minute 18 | 19 | TagResourceTypeMachine = 1 20 | TagResourceTypeDb = 2 21 | TagResourceTypeRedis = 3 22 | TagResourceTypeMongo = 4 23 | 24 | // 删除机器的事件主题名 25 | DeleteMachineEventTopic = "machine:delete" 26 | ) 27 | -------------------------------------------------------------------------------- /mayfly_go_web/src/components/auth/auth.ts: -------------------------------------------------------------------------------- 1 | import { useUserInfo } from '@/store/userInfo'; 2 | 3 | /** 4 | * 判断当前用户是否拥有指定权限 5 | * @param code 权限code 6 | * @returns 7 | */ 8 | export function hasPerm(code: string) { 9 | if (!code) { 10 | return true; 11 | } 12 | return useUserInfo().userInfo.permissions.some((v: any) => v === code); 13 | } 14 | 15 | /** 16 | * 判断用户是否拥有权限对象里对应的code 17 | * @param perms { save: "xxx:save"} 18 | * @returns {"xxx:save": true} key->permission code 19 | */ 20 | export function hasPerms(permCodes: any[]) { 21 | const res = {}; 22 | for (let permCode of permCodes) { 23 | if (hasPerm(permCode)) { 24 | res[permCode] = true; 25 | } 26 | } 27 | return res; 28 | } 29 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/other.scss: -------------------------------------------------------------------------------- 1 | /* wangeditor富文本编辑器 2 | ------------------------------- */ 3 | .w-e-toolbar { 4 | border: 1px solid #ebeef5 !important; 5 | border-bottom: 1px solid #ebeef5 !important; 6 | border-top-left-radius: 3px; 7 | border-top-right-radius: 3px; 8 | z-index: 2 !important; 9 | } 10 | .w-e-text-container { 11 | border: 1px solid #ebeef5 !important; 12 | border-top: none !important; 13 | border-bottom-left-radius: 3px; 14 | border-bottom-right-radius: 3px; 15 | z-index: 1 !important; 16 | } 17 | 18 | /* web端自定义截屏 19 | ------------------------------- */ 20 | #screenShotContainer { 21 | z-index: 9998 !important; 22 | } 23 | #toolPanel { 24 | height: 42px !important; 25 | } 26 | #optionPanel { 27 | height: 37px !important; 28 | } 29 | -------------------------------------------------------------------------------- /server/pkg/consts/gormx.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | const ( 4 | Comma = "," 5 | LeftBracket = "(" 6 | RightBracket = ")" 7 | DefaultPrimaryName = "id" 8 | ) 9 | 10 | const ( 11 | And = "AND" 12 | Or = "OR" 13 | In = "IN" 14 | NotIn = "NOT IN" 15 | Not = "NOT" 16 | Like = "LIKE" 17 | Eq = "=" 18 | Ne = "<>" 19 | Gt = ">" 20 | Ge = ">=" 21 | Lt = "<" 22 | Le = "<=" 23 | IsNull = "IS NULL" 24 | IsNotNull = "IS NOT NULL" 25 | Between = "BETWEEN" 26 | Desc = "DESC" 27 | Asc = "ASC" 28 | As = "AS" 29 | SUM = "SUM" 30 | AVG = "AVG" 31 | MAX = "MAX" 32 | MIN = "MIN" 33 | COUNT = "COUNT" 34 | ) 35 | -------------------------------------------------------------------------------- /server/pkg/utils/stringx/rand.go: -------------------------------------------------------------------------------- 1 | package stringx 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | const Nums = "0123456789" 9 | const LowerChars = "abcdefghigklmnopqrstuvwxyz" 10 | const UpperChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 11 | 12 | // 生成随机字符串 13 | func Rand(l int) string { 14 | return RandByChars(l, Nums+LowerChars+UpperChars) 15 | } 16 | 17 | // 根据传入的chars,随机生成指定位数的字符串 18 | func RandByChars(l int, chars string) string { 19 | strList := []byte(chars) 20 | 21 | result := []byte{} 22 | i := 0 23 | 24 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 25 | charLen := len(strList) 26 | for i < l { 27 | new := strList[r.Intn(charLen)] 28 | result = append(result, new) 29 | i = i + 1 30 | } 31 | return string(result) 32 | } 33 | -------------------------------------------------------------------------------- /mayfly_go_web/src/components/auth/auth.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 29 | -------------------------------------------------------------------------------- /server/internal/sys/api/form/account.go: -------------------------------------------------------------------------------- 1 | package form 2 | 3 | type AccountCreateForm struct { 4 | Id uint64 `json:"id"` 5 | Name string `json:"name" binding:"required,max=16" msg:"required=姓名不能为空,max=姓名最大长度不能超过16位"` 6 | Username string `json:"username" binding:"pattern=account_username"` 7 | Password string `json:"password"` 8 | } 9 | 10 | type AccountUpdateForm struct { 11 | Name string `json:"name" binding:"max=16"` // 姓名 12 | Username string `json:"username" binding:"omitempty,pattern=account_username"` 13 | Password *string `json:"password"` 14 | } 15 | 16 | type AccountChangePasswordForm struct { 17 | Username string `json:"username"` 18 | OldPassword string `json:"oldPassword"` 19 | NewPassword string `json:"newPassword"` 20 | } 21 | -------------------------------------------------------------------------------- /server/internal/mongo/domain/entity/mongo.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "mayfly-go/internal/mongo/mgm" 5 | "mayfly-go/pkg/model" 6 | "mayfly-go/pkg/utils/structx" 7 | ) 8 | 9 | type Mongo struct { 10 | model.Model 11 | 12 | Code string `orm:"column(code)" json:"code"` 13 | Name string `orm:"column(name)" json:"name"` 14 | Uri string `orm:"column(uri)" json:"uri"` 15 | SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id 16 | } 17 | 18 | // 转换为mongoInfo进行连接 19 | func (me *Mongo) ToMongoInfo(tagPath ...string) *mgm.MongoInfo { 20 | mongoInfo := new(mgm.MongoInfo) 21 | structx.Copy(mongoInfo, me) 22 | mongoInfo.TagPath = tagPath 23 | return mongoInfo 24 | } 25 | -------------------------------------------------------------------------------- /server/internal/msg/infrastructure/persistence/msg.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/msg/domain/entity" 5 | "mayfly-go/internal/msg/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type msgRepoImpl struct { 12 | base.RepoImpl[*entity.Msg] 13 | } 14 | 15 | func newMsgRepo() repository.Msg { 16 | return &msgRepoImpl{base.RepoImpl[*entity.Msg]{M: new(entity.Msg)}} 17 | } 18 | 19 | func (m *msgRepoImpl) GetPageList(condition *entity.Msg, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 20 | qd := gormx.NewQuery(condition).WithCondModel(condition).WithOrderBy(orderBy...) 21 | return gormx.PageQuery(qd, pageParam, toEntity) 22 | } 23 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/iconSelector.scss: -------------------------------------------------------------------------------- 1 | /* Popover 弹出框(图标选择器) 2 | ------------------------------- */ 3 | .icon-selector-popper { 4 | padding: 0 !important; 5 | .icon-selector-warp { 6 | height: 260px; 7 | overflow: hidden; 8 | position: relative; 9 | .icon-selector-warp-title { 10 | position: absolute; 11 | height: 40px; 12 | line-height: 40px; 13 | left: 15px; 14 | } 15 | .el-tabs__header { 16 | display: flex; 17 | justify-content: flex-end; 18 | padding: 0 15px; 19 | border-bottom: 1px solid var(--el-border-color-light); 20 | margin: 0 !important; 21 | .el-tabs__nav-wrap { 22 | &::after { 23 | height: 0 !important; 24 | } 25 | .el-tabs__item { 26 | padding: 0 5px !important; 27 | } 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /server/internal/machine/infrastructure/cache/machine_stats.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "mayfly-go/internal/machine/mcm" 7 | global_cache "mayfly-go/pkg/cache" 8 | "mayfly-go/pkg/utils/jsonx" 9 | "time" 10 | ) 11 | 12 | const MachineStatCackeKey = "mayfly:machine:%d:stat" 13 | 14 | func SaveMachineStats(machineId uint64, stat *mcm.Stats) error { 15 | return global_cache.SetStr(fmt.Sprintf(MachineStatCackeKey, machineId), jsonx.ToStr(stat), 10*time.Minute) 16 | } 17 | 18 | func GetMachineStats(machineId uint64) (*mcm.Stats, error) { 19 | cacheStr := global_cache.GetStr(fmt.Sprintf(MachineStatCackeKey, machineId)) 20 | if cacheStr == "" { 21 | return nil, errors.New("不存在该值") 22 | } 23 | return jsonx.To(cacheStr, new(mcm.Stats)) 24 | } 25 | -------------------------------------------------------------------------------- /server/internal/tag/infrastructure/persistence/team.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/tag/domain/entity" 5 | "mayfly-go/internal/tag/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type teamRepoImpl struct { 12 | base.RepoImpl[*entity.Team] 13 | } 14 | 15 | func newTeamRepo() repository.Team { 16 | return &teamRepoImpl{base.RepoImpl[*entity.Team]{M: new(entity.Team)}} 17 | } 18 | 19 | func (p *teamRepoImpl) GetPageList(condition *entity.Team, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 20 | qd := gormx.NewQuery(condition).WithCondModel(condition).WithOrderBy(orderBy...) 21 | return gormx.PageQuery(qd, pageParam, toEntity) 22 | } 23 | -------------------------------------------------------------------------------- /server/pkg/config/aes.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "mayfly-go/pkg/utils/assert" 6 | "mayfly-go/pkg/utils/cryptox" 7 | ) 8 | 9 | type Aes struct { 10 | Key string `yaml:"key"` 11 | } 12 | 13 | // 编码并base64 14 | func (a *Aes) EncryptBase64(data []byte) (string, error) { 15 | return cryptox.AesEncryptBase64(data, []byte(a.Key)) 16 | } 17 | 18 | // base64解码后再aes解码 19 | func (a *Aes) DecryptBase64(data string) ([]byte, error) { 20 | return cryptox.AesDecryptBase64(data, []byte(a.Key)) 21 | } 22 | 23 | func (j *Aes) Valid() { 24 | if j.Key == "" { 25 | return 26 | } 27 | aesKeyLen := len(j.Key) 28 | assert.IsTrue(aesKeyLen == 16 || aesKeyLen == 24 || aesKeyLen == 32, 29 | fmt.Sprintf("config.yml之 [aes.key] 长度需为16、24、32位长度, 当前为%d位", aesKeyLen)) 30 | } 31 | -------------------------------------------------------------------------------- /mayfly_go_web/src/store/tagsViews.ts: -------------------------------------------------------------------------------- 1 | import { getNowUrl } from '@/common/utils/url'; 2 | import { defineStore } from 'pinia'; 3 | 4 | /** 5 | * tags view 6 | */ 7 | export const useTagsViews = defineStore('tagsViews', { 8 | state: (): TagsViewsState => ({ 9 | tagsViews: [], 10 | }), 11 | actions: { 12 | setTagsViews(data: Array) { 13 | this.tagsViews = data; 14 | }, 15 | // 设置当前页面的tags view title 16 | setNowTitle(title: string) { 17 | this.tagsViews.forEach((item) => { 18 | // console.log(getNowUrl(), item.path); 19 | if (item.path == getNowUrl()) { 20 | item.title = title; 21 | } 22 | }); 23 | }, 24 | }, 25 | }); 26 | -------------------------------------------------------------------------------- /server/internal/tag/application/application.go: -------------------------------------------------------------------------------- 1 | package application 2 | 3 | import ( 4 | "mayfly-go/internal/tag/infrastructure/persistence" 5 | ) 6 | 7 | var ( 8 | tagTreeApp TagTree = newTagTreeApp( 9 | persistence.GetTagTreeRepo(), 10 | GetTagResourceApp(), 11 | persistence.GetTagTreeTeamRepo(), 12 | ) 13 | 14 | teamApp Team = newTeamApp( 15 | persistence.GetTeamRepo(), 16 | persistence.GetTeamMemberRepo(), 17 | persistence.GetTagTreeTeamRepo(), 18 | ) 19 | 20 | tagResourceApp TagResource = newTagResourceApp(persistence.GetTagResourceRepo()) 21 | ) 22 | 23 | func GetTagTreeApp() TagTree { 24 | return tagTreeApp 25 | } 26 | 27 | func GetTeamApp() Team { 28 | return teamApp 29 | } 30 | 31 | func GetTagResourceApp() TagResource { 32 | return tagResourceApp 33 | } 34 | -------------------------------------------------------------------------------- /server/internal/db/api/vo/instance.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | import "time" 4 | 5 | type InstanceListVO struct { 6 | Id *int64 `json:"id"` 7 | Name *string `json:"name"` 8 | Host *string `json:"host"` 9 | Port *int `json:"port"` 10 | Type *string `json:"type"` 11 | Params *string `json:"params"` 12 | Username *string `json:"username"` 13 | Remark *string `json:"remark"` 14 | CreateTime *time.Time `json:"createTime"` 15 | Creator *string `json:"creator"` 16 | CreatorId *int64 `json:"creatorId"` 17 | 18 | UpdateTime *time.Time `json:"updateTime"` 19 | Modifier *string `json:"modifier"` 20 | ModifierId *int64 `json:"modifierId"` 21 | 22 | SshTunnelMachineId int `json:"sshTunnelMachineId"` 23 | } 24 | -------------------------------------------------------------------------------- /server/internal/sys/domain/entity/syslog.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "mayfly-go/pkg/model" 5 | "time" 6 | ) 7 | 8 | // 系统操作日志 9 | type SysLog struct { 10 | model.DeletedModel 11 | 12 | CreateTime time.Time `json:"createTime"` 13 | CreatorId uint64 `json:"creatorId"` 14 | Creator string `json:"creator"` 15 | 16 | Type int8 `json:"type"` 17 | Description string `json:"description"` 18 | ReqParam string `json:"reqParam" gorm:"column:req_param;type:varchar(1000)"` // 请求参数 19 | Resp string `json:"resp" gorm:"column:resp;type:varchar(1000)"` // 响应结构 20 | } 21 | 22 | func (a *SysLog) TableName() string { 23 | return "t_sys_log" 24 | } 25 | 26 | const ( 27 | SyslogTypeNorman int8 = 1 // 正常状态 28 | SyslogTypeError int8 = 2 // 错误状态 29 | ) 30 | -------------------------------------------------------------------------------- /mayfly_go_web/public/config.js: -------------------------------------------------------------------------------- 1 | window.globalConfig = { 2 | // 默认为空,以访问根目录为api请求地址。若前后端分离部署可单独配置该后端api请求地址 3 | BaseApiUrl: '', 4 | BaseWsUrl: '', 5 | }; 6 | 7 | // index.html添加百秒级时间戳,防止被浏览器缓存 8 | // !(function () { 9 | // let t = 't=' + new Date().getTime().toString().substring(0, 8); 10 | // let search = location.search; 11 | // let m = search && search.match(/t=\d*/g); 12 | 13 | // console.log(location); 14 | // if (m[0]) { 15 | // if (m[0] !== t) { 16 | // location.search = search.replace(m[0], t); 17 | // } 18 | // } else { 19 | // if (search.indexOf('?') > -1) { 20 | // location.search = search + '&' + t; 21 | // } else { 22 | // location.search = t; 23 | // } 24 | // } 25 | // })(); 26 | -------------------------------------------------------------------------------- /server/internal/sys/router/config.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/sys/api" 5 | "mayfly-go/internal/sys/application" 6 | "mayfly-go/pkg/req" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func InitSysConfigRouter(router *gin.RouterGroup) { 12 | r := &api.Config{ConfigApp: application.GetConfigApp()} 13 | configG := router.Group("sys/configs") 14 | 15 | baseP := req.NewPermission("config:base") 16 | 17 | reqs := [...]*req.Conf{ 18 | req.NewGet("", r.Configs).RequiredPermission(baseP), 19 | 20 | // 获取指定配置key对应的值 21 | req.NewGet("/value", r.GetConfigValueByKey).DontNeedToken(), 22 | 23 | req.NewPost("", r.SaveConfig).Log(req.NewLogSave("保存系统配置信息")). 24 | RequiredPermissionCode("config:save"), 25 | } 26 | 27 | req.BatchSetGroup(configG, reqs[:]) 28 | } 29 | -------------------------------------------------------------------------------- /server/pkg/starter/run.go: -------------------------------------------------------------------------------- 1 | package starter 2 | 3 | import ( 4 | "mayfly-go/initialize" 5 | "mayfly-go/migrations" 6 | "mayfly-go/pkg/config" 7 | "mayfly-go/pkg/global" 8 | "mayfly-go/pkg/logx" 9 | "mayfly-go/pkg/validatorx" 10 | ) 11 | 12 | func RunWebServer() { 13 | // 初始化config.yml配置文件映射信息或使用环境变量。并初始化系统日志相关配置 14 | config.Init() 15 | 16 | // 打印banner 17 | printBanner() 18 | 19 | // 初始化并赋值数据库全局变量 20 | initDb() 21 | 22 | // 有配置redis信息,则初始化redis。多台机器部署需要使用redis存储验证码、权限、公私钥等 23 | initRedis() 24 | 25 | // 数据库升级操作 26 | if err := migrations.RunMigrations(global.Db); err != nil { 27 | logx.Panicf("数据库升级失败: %v", err) 28 | } 29 | 30 | // 参数校验器初始化、如错误提示中文转译等 31 | validatorx.Init() 32 | 33 | // 初始化其他需要启动时运行的方法 34 | initialize.InitOther() 35 | 36 | // 运行web服务 37 | runWebServer() 38 | } 39 | -------------------------------------------------------------------------------- /server/pkg/utils/collx/stack.go: -------------------------------------------------------------------------------- 1 | package collx 2 | 3 | type Stack[T any] struct { 4 | items []T 5 | } 6 | 7 | // 入栈 8 | func (s *Stack[T]) Push(item T) { 9 | s.items = append(s.items, item) 10 | } 11 | 12 | // 出栈 13 | func (s *Stack[T]) Pop() T { 14 | var item T 15 | if len(s.items) == 0 { 16 | return item 17 | } 18 | lastIndex := len(s.items) - 1 19 | item = s.items[lastIndex] 20 | s.items = s.items[:lastIndex] 21 | return item 22 | } 23 | 24 | // 获取栈顶元素 25 | func (s *Stack[T]) Top() T { 26 | var item T 27 | if len(s.items) == 0 { 28 | return item 29 | } 30 | return s.items[len(s.items)-1] 31 | } 32 | 33 | // 检查栈是否为空 34 | func (s *Stack[T]) IsEmpty() bool { 35 | return len(s.items) == 0 36 | } 37 | 38 | // 返回栈的大小 39 | func (s *Stack[T]) Size() int { 40 | return len(s.items) 41 | } 42 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/ops/mongo/api.ts: -------------------------------------------------------------------------------- 1 | import Api from '@/common/Api'; 2 | 3 | export const mongoApi = { 4 | mongoList: Api.newGet('/mongos'), 5 | mongoTags: Api.newGet('/mongos/tags'), 6 | testConn: Api.newPost('/mongos/test-conn'), 7 | saveMongo: Api.newPost('/mongos'), 8 | deleteMongo: Api.newDelete('/mongos/{id}'), 9 | databases: Api.newGet('/mongos/{id}/databases'), 10 | collections: Api.newGet('/mongos/{id}/collections'), 11 | runCommand: Api.newPost('/mongos/{id}/run-command'), 12 | findCommand: Api.newPost('/mongos/{id}/command/find'), 13 | updateByIdCommand: Api.newPost('/mongos/{id}/command/update-by-id'), 14 | deleteByIdCommand: Api.newPost('/mongos/{id}/command/delete-by-id'), 15 | insertCommand: Api.newPost('/mongos/{id}/command/insert'), 16 | }; 17 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/media/error.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于768px 4 | ------------------------------- */ 5 | @media screen and (max-width: $sm) { 6 | .error { 7 | .error-flex { 8 | flex-direction: column-reverse !important; 9 | height: auto !important; 10 | width: 100% !important; 11 | } 12 | .right, 13 | .left { 14 | flex: unset !important; 15 | display: flex !important; 16 | } 17 | .left-item { 18 | margin: auto !important; 19 | } 20 | .right img { 21 | max-width: 450px !important; 22 | @extend .left-item; 23 | } 24 | } 25 | } 26 | 27 | /* 页面宽度大于768px小于992px 28 | ------------------------------- */ 29 | @media screen and (min-width: $sm) and (max-width: $md) { 30 | .error { 31 | .error-flex { 32 | padding-left: 30px !important; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /mayfly_go_web/src/components/auth/authAll.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 30 | -------------------------------------------------------------------------------- /server/internal/sys/domain/entity/resource.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "mayfly-go/pkg/model" 4 | 5 | type Resource struct { 6 | model.Model 7 | Pid int `json:"pid"` 8 | UiPath string `json:"ui_path"` // 唯一标识路径 9 | Type int8 `json:"type"` // 1:菜单路由;2:资源(按钮等) 10 | Status int8 `json:"status"` // 1:可用;-1:不可用 11 | Code string `json:"code"` 12 | Name string `json:"name"` 13 | Weight int `json:"weight"` 14 | Meta string `json:"meta"` 15 | } 16 | 17 | func (a *Resource) TableName() string { 18 | return "t_sys_resource" 19 | } 20 | 21 | const ( 22 | ResourceStatusEnable int8 = 1 // 启用状态 23 | ResourceStatusDisable int8 = -1 // 禁用状态 24 | 25 | // 资源状态 26 | ResourceTypeMenu int8 = 1 27 | ResourceTypePermission int8 = 2 28 | 29 | // 唯一标识路径分隔符 30 | ResourceUiPathSp string = "/" 31 | ) 32 | -------------------------------------------------------------------------------- /server/internal/db/api/gzip_writer.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "compress/gzip" 5 | "io" 6 | "mayfly-go/pkg/biz" 7 | ) 8 | 9 | type gzipWriter struct { 10 | tryFlushCount int 11 | writer *gzip.Writer 12 | aborted bool 13 | } 14 | 15 | func newGzipWriter(writer io.Writer) *gzipWriter { 16 | return &gzipWriter{writer: gzip.NewWriter(writer)} 17 | } 18 | 19 | func (g *gzipWriter) WriteString(data string) { 20 | if g.aborted { 21 | return 22 | } 23 | if _, err := g.writer.Write([]byte(data)); err != nil { 24 | g.aborted = true 25 | biz.IsTrue(false, "数据库导出失败:%s", err) 26 | } 27 | } 28 | 29 | func (g *gzipWriter) Close() { 30 | g.writer.Close() 31 | } 32 | 33 | func (g *gzipWriter) TryFlush() { 34 | if g.tryFlushCount%1000 == 0 { 35 | g.writer.Flush() 36 | } 37 | g.tryFlushCount += 1 38 | } 39 | -------------------------------------------------------------------------------- /server/internal/db/api/vo/db.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | import "time" 4 | 5 | type DbListVO struct { 6 | Id *int64 `json:"id"` 7 | Code string `json:"code"` 8 | Name *string `json:"name"` 9 | Database *string `json:"database"` 10 | Remark *string `json:"remark"` 11 | 12 | InstanceId *int64 `json:"instanceId"` 13 | InstanceName *string `json:"instanceName"` 14 | InstanceType *string `json:"type"` 15 | Host string `json:"host"` 16 | Port int `json:"port"` 17 | Username string `json:"username"` 18 | 19 | CreateTime *time.Time `json:"createTime"` 20 | Creator *string `json:"creator"` 21 | CreatorId *int64 `json:"creatorId"` 22 | UpdateTime *time.Time `json:"updateTime"` 23 | Modifier *string `json:"modifier"` 24 | ModifierId *int64 `json:"modifierId"` 25 | } 26 | -------------------------------------------------------------------------------- /server/internal/db/application/application.go: -------------------------------------------------------------------------------- 1 | package application 2 | 3 | import ( 4 | "mayfly-go/internal/db/infrastructure/persistence" 5 | tagapp "mayfly-go/internal/tag/application" 6 | ) 7 | 8 | var ( 9 | instanceApp Instance = newInstanceApp(persistence.GetInstanceRepo()) 10 | dbApp Db = newDbApp(persistence.GetDbRepo(), persistence.GetDbSqlRepo(), instanceApp, tagapp.GetTagTreeApp()) 11 | dbSqlExecApp DbSqlExec = newDbSqlExecApp(persistence.GetDbSqlExecRepo()) 12 | dbSqlApp DbSql = newDbSqlApp(persistence.GetDbSqlRepo()) 13 | ) 14 | 15 | func GetInstanceApp() Instance { 16 | return instanceApp 17 | } 18 | 19 | func GetDbApp() Db { 20 | return dbApp 21 | } 22 | 23 | func GetDbSqlApp() DbSql { 24 | return dbSqlApp 25 | } 26 | 27 | func GetDbSqlExecApp() DbSqlExec { 28 | return dbSqlExecApp 29 | } 30 | -------------------------------------------------------------------------------- /server/pkg/utils/runtimex/runtimex.go: -------------------------------------------------------------------------------- 1 | package runtimex 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | "runtime" 7 | "strings" 8 | ) 9 | 10 | // 获取指定堆栈描述信息 11 | // 12 | // @param skip: 跳过堆栈个数 13 | // @param nFrames: 需要描述的堆栈个数 14 | func StatckStr(skip, nFrames int) string { 15 | pcs := make([]uintptr, nFrames+1) 16 | n := runtime.Callers(skip+1, pcs) 17 | if n == 0 { 18 | return "(no stack)" 19 | } 20 | frames := runtime.CallersFrames(pcs[:n]) 21 | var b strings.Builder 22 | i := 0 23 | for { 24 | frame, more := frames.Next() 25 | fmt.Fprintf(&b, "called from %s (%s:%d)\n\t", frame.Function, filepath.Base(frame.File), frame.Line) 26 | if !more { 27 | break 28 | } 29 | i++ 30 | if i >= nFrames { 31 | fmt.Fprintf(&b, "(rest of stack elided...)\n") 32 | break 33 | } 34 | } 35 | return b.String() 36 | } 37 | -------------------------------------------------------------------------------- /server/internal/tag/application/tag_resource.go: -------------------------------------------------------------------------------- 1 | package application 2 | 3 | import ( 4 | "mayfly-go/internal/tag/domain/entity" 5 | "mayfly-go/internal/tag/domain/repository" 6 | "mayfly-go/pkg/base" 7 | ) 8 | 9 | type TagResource interface { 10 | base.App[*entity.TagResource] 11 | 12 | ListByQuery(condition *entity.TagResourceQuery, toEntity any) 13 | } 14 | 15 | func newTagResourceApp(tagResourceRepo repository.TagResource) TagResource { 16 | tagResourceApp := &tagResourceAppImpl{} 17 | tagResourceApp.Repo = tagResourceRepo 18 | return tagResourceApp 19 | } 20 | 21 | type tagResourceAppImpl struct { 22 | base.AppImpl[*entity.TagResource, repository.TagResource] 23 | } 24 | 25 | func (tr *tagResourceAppImpl) ListByQuery(condition *entity.TagResourceQuery, toEntity any) { 26 | tr.Repo.SelectByCondition(condition, toEntity) 27 | } 28 | -------------------------------------------------------------------------------- /server/internal/mongo/infrastructure/persistence/mongo.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/mongo/domain/entity" 5 | "mayfly-go/internal/mongo/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type mongoRepoImpl struct { 12 | base.RepoImpl[*entity.Mongo] 13 | } 14 | 15 | func newMongoRepo() repository.Mongo { 16 | return &mongoRepoImpl{base.RepoImpl[*entity.Mongo]{M: new(entity.Mongo)}} 17 | } 18 | 19 | // 分页获取数据库信息列表 20 | func (d *mongoRepoImpl) GetList(condition *entity.MongoQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 21 | qd := gormx.NewQuery(new(entity.Mongo)). 22 | Like("name", condition.Name). 23 | In("code", condition.Codes) 24 | return gormx.PageQuery(qd, pageParam, toEntity) 25 | } 26 | -------------------------------------------------------------------------------- /server/pkg/middleware/cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gin-gonic/gin" 7 | ) 8 | 9 | // 处理跨域请求,支持options访问 10 | func Cors() gin.HandlerFunc { 11 | return func(c *gin.Context) { 12 | method := c.Request.Method 13 | 14 | c.Header("Access-Control-Allow-Origin", "*") 15 | c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE") 16 | c.Header("Access-Control-Allow-Headers", "*") 17 | c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type") 18 | c.Header("Access-Control-Allow-Credentials", "true") 19 | 20 | //放行所有OPTIONS方法 21 | if method == "OPTIONS" { 22 | c.AbortWithStatus(http.StatusNoContent) 23 | } 24 | // 处理请求 25 | c.Next() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/ops/redis/ViewerText.vue: -------------------------------------------------------------------------------- 1 | 6 | 38 | 39 | -------------------------------------------------------------------------------- /server/internal/sys/application/application.go: -------------------------------------------------------------------------------- 1 | package application 2 | 3 | import ( 4 | "mayfly-go/internal/sys/infrastructure/persistence" 5 | ) 6 | 7 | var ( 8 | accountApp = newAccountApp(persistence.GetAccountRepo()) 9 | configApp = newConfigApp(persistence.GetConfigRepo()) 10 | resourceApp = newResourceApp(persistence.GetResourceRepo()) 11 | roleApp = newRoleApp(persistence.GetRoleRepo(), persistence.GetAccountRoleRepo()) 12 | syslogApp = newSyslogApp(persistence.GetSyslogRepo()) 13 | ) 14 | 15 | func GetAccountApp() Account { 16 | return accountApp 17 | } 18 | 19 | func GetConfigApp() Config { 20 | return configApp 21 | } 22 | 23 | func GetResourceApp() Resource { 24 | return resourceApp 25 | } 26 | 27 | func GetRoleApp() Role { 28 | return roleApp 29 | } 30 | 31 | func GetSyslogApp() Syslog { 32 | return syslogApp 33 | } 34 | -------------------------------------------------------------------------------- /server/internal/sys/infrastructure/persistence/account.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/sys/domain/entity" 5 | "mayfly-go/internal/sys/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type accountRepoImpl struct { 12 | base.RepoImpl[*entity.Account] 13 | } 14 | 15 | func newAccountRepo() repository.Account { 16 | return &accountRepoImpl{base.RepoImpl[*entity.Account]{M: new(entity.Account)}} 17 | } 18 | 19 | func (m *accountRepoImpl) GetPageList(condition *entity.Account, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 20 | qd := gormx.NewQuery(new(entity.Account)). 21 | Like("name", condition.Name). 22 | Like("username", condition.Username) 23 | return gormx.PageQuery(qd, pageParam, toEntity) 24 | } 25 | -------------------------------------------------------------------------------- /server/internal/redis/infrastructure/persistence/redis_repo.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/redis/domain/entity" 5 | "mayfly-go/internal/redis/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type redisRepoImpl struct { 12 | base.RepoImpl[*entity.Redis] 13 | } 14 | 15 | func newRedisRepo() repository.Redis { 16 | return &redisRepoImpl{base.RepoImpl[*entity.Redis]{M: new(entity.Redis)}} 17 | } 18 | 19 | // 分页获取机器信息列表 20 | func (r *redisRepoImpl) GetRedisList(condition *entity.RedisQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 21 | qd := gormx.NewQuery(new(entity.Redis)). 22 | Like("host", condition.Host). 23 | In("code", condition.Codes) 24 | return gormx.PageQuery(qd, pageParam, toEntity) 25 | } 26 | -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: "3.9" 2 | 3 | services: 4 | mysql: 5 | image: "mysql:8" 6 | container_name: mayfly-go-mysql 7 | environment: 8 | MYSQL_ROOT_PASSWORD: 111049 9 | MYSQL_DATABASE: mayfly-go 10 | TZ: Asia/Shanghai 11 | volumes: 12 | - ./server/docs/docker-compose/mysql/data/mydir:/mydir 13 | - ./server/docs/docker-compose/mysql/data/datadir:/var/lib/mysql 14 | restart: always 15 | 16 | server: 17 | image: mayfly-go:v1.3.1 18 | build: 19 | context: . 20 | dockerfile: Dockerfile 21 | container_name: mayfly-go-server 22 | ports: 23 | - "8888:8888" 24 | environment: 25 | TZ: Asia/Shanghai 26 | WAIT_HOSTS: mysql:3306 27 | volumes: 28 | - ./server/config.yml.example:/mayfly/config.yml 29 | depends_on: 30 | - mysql 31 | restart: always 32 | -------------------------------------------------------------------------------- /mayfly_go_web/src/layout/component/header.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 27 | -------------------------------------------------------------------------------- /server/internal/common/router/index.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/common/api" 5 | dbapp "mayfly-go/internal/db/application" 6 | machineapp "mayfly-go/internal/machine/application" 7 | mongoapp "mayfly-go/internal/mongo/application" 8 | redisapp "mayfly-go/internal/redis/application" 9 | tagapp "mayfly-go/internal/tag/application" 10 | "mayfly-go/pkg/req" 11 | 12 | "github.com/gin-gonic/gin" 13 | ) 14 | 15 | func InitIndexRouter(router *gin.RouterGroup) { 16 | index := router.Group("common/index") 17 | i := &api.Index{ 18 | TagApp: tagapp.GetTagTreeApp(), 19 | MachineApp: machineapp.GetMachineApp(), 20 | DbApp: dbapp.GetDbApp(), 21 | RedisApp: redisapp.GetRedisApp(), 22 | MongoApp: mongoapp.GetMongoApp(), 23 | } 24 | { 25 | // 首页基本信息统计 26 | req.NewGet("count", i.Count).Group(index) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /server/internal/machine/router/auth_cert.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/machine/api" 5 | "mayfly-go/internal/machine/application" 6 | "mayfly-go/pkg/req" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func InitAuthCertRouter(router *gin.RouterGroup) { 12 | r := &api.AuthCert{AuthCertApp: application.GetAuthCertApp()} 13 | 14 | ag := router.Group("sys/authcerts") 15 | 16 | reqs := [...]*req.Conf{ 17 | req.NewGet("", r.AuthCerts).RequiredPermissionCode("authcert"), 18 | 19 | // 基础授权凭证信息,不包含密码等 20 | req.NewGet("base", r.BaseAuthCerts), 21 | 22 | req.NewPost("", r.SaveAuthCert).Log(req.NewLogSave("保存授权凭证")).RequiredPermissionCode("authcert:save"), 23 | 24 | req.NewDelete(":id", r.Delete).Log(req.NewLogSave("删除授权凭证")).RequiredPermissionCode("authcert:del"), 25 | } 26 | 27 | req.BatchSetGroup(ag, reqs[:]) 28 | } 29 | -------------------------------------------------------------------------------- /server/pkg/utils/bytex/bytex.go: -------------------------------------------------------------------------------- 1 | package bytex 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | "strings" 7 | ) 8 | 9 | const ( 10 | KB = 1024 11 | MB = KB * 1024 12 | GB = MB * 1024 13 | ) 14 | 15 | // 解析字符串byte size 16 | // 17 | // 1kb -> 1024 18 | // 1mb -> 1024 * 1024 19 | func ParseSize(sizeStr string) (int64, error) { 20 | sizeStr = strings.TrimSpace(sizeStr) 21 | unit := sizeStr[len(sizeStr)-2:] 22 | 23 | valueStr := sizeStr[:len(sizeStr)-2] 24 | value, err := strconv.ParseInt(valueStr, 10, 64) 25 | if err != nil { 26 | return 0, err 27 | } 28 | 29 | var bytes int64 30 | 31 | switch strings.ToUpper(unit) { 32 | case "KB": 33 | bytes = value * KB 34 | case "MB": 35 | bytes = value * MB 36 | case "GB": 37 | bytes = value * GB 38 | default: 39 | return 0, fmt.Errorf("invalid size unit") 40 | } 41 | 42 | return bytes, nil 43 | } 44 | -------------------------------------------------------------------------------- /mayfly_go_web/src/layout/routerView/link.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 30 | -------------------------------------------------------------------------------- /server/internal/tag/domain/entity/query.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "mayfly-go/pkg/model" 4 | 5 | type TagTreeQuery struct { 6 | model.Model 7 | 8 | Pid uint64 9 | Code string `json:"code"` // 标识 10 | CodePath string `json:"codePath"` // 标识路径 11 | CodePaths []string 12 | Name string `json:"name"` // 名称 13 | CodePathLike string // 标识符路径模糊查询 14 | CodePathLikes []string 15 | } 16 | 17 | type TagResourceQuery struct { 18 | model.Model 19 | 20 | TagPath string `json:"string"` // 标签路径 21 | TagId uint64 `json:"tagId" form:"tagId"` 22 | ResourceType int8 `json:"resourceType" form:"resourceType"` // 资源编码 23 | ResourceCode string `json:"resourceCode" form:"resourceCode"` // 资源编码 24 | ResourceCodes []string // 资源编码列表 25 | 26 | TagPathLike string // 标签路径模糊查询 27 | TagPathLikes []string 28 | } 29 | -------------------------------------------------------------------------------- /server/pkg/utils/netx/ssh_conn_wrap.go: -------------------------------------------------------------------------------- 1 | package netx 2 | 3 | import ( 4 | "net" 5 | "time" 6 | ) 7 | 8 | type WrapSshConn struct { 9 | Conn net.Conn 10 | } 11 | 12 | func (c *WrapSshConn) Read(b []byte) (n int, err error) { 13 | return c.Conn.Read(b) 14 | } 15 | 16 | func (c *WrapSshConn) Write(b []byte) (n int, err error) { 17 | return c.Conn.Write(b) 18 | } 19 | func (c *WrapSshConn) Close() error { 20 | return c.Conn.Close() 21 | } 22 | func (c *WrapSshConn) LocalAddr() net.Addr { 23 | return c.Conn.LocalAddr() 24 | } 25 | func (c *WrapSshConn) RemoteAddr() net.Addr { 26 | return c.Conn.RemoteAddr() 27 | } 28 | func (c *WrapSshConn) SetDeadline(t time.Time) error { 29 | return nil 30 | } 31 | func (c *WrapSshConn) SetReadDeadline(t time.Time) error { 32 | return nil 33 | } 34 | func (c *WrapSshConn) SetWriteDeadline(t time.Time) error { 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /server/internal/machine/infrastructure/persistence/machine_file.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/machine/domain/entity" 5 | "mayfly-go/internal/machine/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type machineFileRepoImpl struct { 12 | base.RepoImpl[*entity.MachineFile] 13 | } 14 | 15 | func newMachineFileRepo() repository.MachineFile { 16 | return &machineFileRepoImpl{base.RepoImpl[*entity.MachineFile]{M: new(entity.MachineFile)}} 17 | } 18 | 19 | // 分页获取机器文件信息列表 20 | func (m *machineFileRepoImpl) GetPageList(condition *entity.MachineFile, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 21 | qd := gormx.NewQuery(condition).WithCondModel(condition).WithOrderBy(orderBy...) 22 | return gormx.PageQuery(qd, pageParam, toEntity) 23 | } 24 | -------------------------------------------------------------------------------- /server/internal/tag/infrastructure/persistence/tag_tree_team.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/tag/domain/entity" 5 | "mayfly-go/internal/tag/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | ) 9 | 10 | type tagTreeTeamRepoImpl struct { 11 | base.RepoImpl[*entity.TagTreeTeam] 12 | } 13 | 14 | func newTagTreeTeamRepo() repository.TagTreeTeam { 15 | return &tagTreeTeamRepoImpl{base.RepoImpl[*entity.TagTreeTeam]{M: new(entity.TagTreeTeam)}} 16 | } 17 | 18 | func (p *tagTreeTeamRepoImpl) SelectTagPathsByAccountId(accountId uint64) []string { 19 | var res []string 20 | gormx.GetListBySql2Model("SELECT DISTINCT(t1.tag_path) FROM t_tag_tree_team t1 JOIN t_team_member t2 ON t1.team_id = t2.team_id WHERE t2.account_id = ? AND t1.is_deleted = 0 AND t2.is_deleted = 0 ORDER BY t1.tag_path", &res, accountId) 21 | return res 22 | } 23 | -------------------------------------------------------------------------------- /server/internal/machine/infrastructure/persistence/machine_term_op.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/machine/domain/entity" 5 | "mayfly-go/internal/machine/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type machineTermOpRepoImpl struct { 12 | base.RepoImpl[*entity.MachineTermOp] 13 | } 14 | 15 | func newMachineTermOpRepoImpl() repository.MachineTermOp { 16 | return &machineTermOpRepoImpl{base.RepoImpl[*entity.MachineTermOp]{M: new(entity.MachineTermOp)}} 17 | } 18 | 19 | func (m *machineTermOpRepoImpl) GetPageList(condition *entity.MachineTermOp, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 20 | qd := gormx.NewQuery(condition).WithCondModel(condition).WithOrderBy(orderBy...) 21 | return gormx.PageQuery(qd, pageParam, toEntity) 22 | } 23 | -------------------------------------------------------------------------------- /server/internal/tag/api/vo/tag_tree.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | import ( 4 | "mayfly-go/internal/tag/domain/entity" 5 | ) 6 | 7 | type TagTreeVOS []*entity.TagTree 8 | 9 | type TagTreeItem struct { 10 | *entity.TagTree 11 | Children []TagTreeItem `json:"children"` 12 | } 13 | 14 | func (m *TagTreeVOS) ToTrees(pid uint64) []TagTreeItem { 15 | var resourceTree []TagTreeItem 16 | 17 | list := m.findChildren(pid) 18 | if len(list) == 0 { 19 | return resourceTree 20 | } 21 | 22 | for _, v := range list { 23 | Children := m.ToTrees(v.Id) 24 | resourceTree = append(resourceTree, TagTreeItem{v, Children}) 25 | } 26 | 27 | return resourceTree 28 | } 29 | 30 | func (m *TagTreeVOS) findChildren(pid uint64) []*entity.TagTree { 31 | child := []*entity.TagTree{} 32 | 33 | for _, v := range *m { 34 | if v.Pid == pid { 35 | child = append(child, v) 36 | } 37 | } 38 | return child 39 | } 40 | -------------------------------------------------------------------------------- /server/internal/machine/infrastructure/persistence/machine_script.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/machine/domain/entity" 5 | "mayfly-go/internal/machine/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type machineScriptRepoImpl struct { 12 | base.RepoImpl[*entity.MachineScript] 13 | } 14 | 15 | func newMachineScriptRepo() repository.MachineScript { 16 | return &machineScriptRepoImpl{base.RepoImpl[*entity.MachineScript]{M: new(entity.MachineScript)}} 17 | } 18 | 19 | // 分页获取机器信息列表 20 | func (m *machineScriptRepoImpl) GetPageList(condition *entity.MachineScript, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 21 | qd := gormx.NewQuery(condition).WithCondModel(condition).WithOrderBy(orderBy...) 22 | return gormx.PageQuery(qd, pageParam, toEntity) 23 | } 24 | -------------------------------------------------------------------------------- /server/internal/sys/infrastructure/persistence/config.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/sys/domain/entity" 5 | "mayfly-go/internal/sys/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type configRepoImpl struct { 12 | base.RepoImpl[*entity.Config] 13 | } 14 | 15 | func newConfigRepo() repository.Config { 16 | return &configRepoImpl{base.RepoImpl[*entity.Config]{M: new(entity.Config)}} 17 | } 18 | 19 | func (m *configRepoImpl) GetPageList(condition *entity.Config, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 20 | qd := gormx.NewQuery(condition). 21 | Eq("key", condition.Key). 22 | And("permission = 'all' OR permission LIKE ?", "%"+condition.Permission+",%"). 23 | WithOrderBy(orderBy...) 24 | return gormx.PageQuery(qd, pageParam, toEntity) 25 | } 26 | -------------------------------------------------------------------------------- /server/internal/sys/infrastructure/persistence/syslog.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/sys/domain/entity" 5 | "mayfly-go/internal/sys/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type syslogRepoImpl struct { 12 | base.RepoImpl[*entity.SysLog] 13 | } 14 | 15 | func newSyslogRepo() repository.Syslog { 16 | return &syslogRepoImpl{base.RepoImpl[*entity.SysLog]{M: new(entity.SysLog)}} 17 | } 18 | 19 | func (m *syslogRepoImpl) GetPageList(condition *entity.SysLogQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 20 | qd := gormx.NewQuery(new(entity.SysLog)).Like("description", condition.Description). 21 | Eq("creator_id", condition.CreatorId).Eq("type", condition.Type).WithOrderBy(orderBy...) 22 | return gormx.PageQuery(qd, pageParam, toEntity) 23 | } 24 | -------------------------------------------------------------------------------- /server/internal/sys/infrastructure/persistence/persistence.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import "mayfly-go/internal/sys/domain/repository" 4 | 5 | var ( 6 | accountRepo = newAccountRepo() 7 | configRepo = newConfigRepo() 8 | resourceRepo = newResourceRepo() 9 | roleRepo = newRoleRepo() 10 | accountRoleRepo = newAccountRoleRepo() 11 | syslogRepo = newSyslogRepo() 12 | ) 13 | 14 | func GetAccountRepo() repository.Account { 15 | return accountRepo 16 | } 17 | 18 | func GetConfigRepo() repository.Config { 19 | return configRepo 20 | } 21 | 22 | func GetResourceRepo() repository.Resource { 23 | return resourceRepo 24 | } 25 | 26 | func GetRoleRepo() repository.Role { 27 | return roleRepo 28 | } 29 | 30 | func GetAccountRoleRepo() repository.AccountRole { 31 | return accountRoleRepo 32 | } 33 | 34 | func GetSyslogRepo() repository.Syslog { 35 | return syslogRepo 36 | } 37 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # 构建前端资源 2 | FROM node:18-alpine3.16 as fe-builder 3 | 4 | WORKDIR /mayfly 5 | 6 | COPY mayfly_go_web . 7 | 8 | RUN yarn 9 | 10 | RUN yarn build 11 | 12 | # 构建后端资源 13 | FROM golang:1.21.0 as be-builder 14 | 15 | ENV GOPROXY https://goproxy.cn 16 | WORKDIR /mayfly 17 | 18 | # Copy the go source for building server 19 | COPY server . 20 | 21 | RUN go mod tidy && go mod download 22 | 23 | COPY --from=fe-builder /mayfly/dist /mayfly/static/static 24 | 25 | # Build 26 | RUN GO111MODULE=on CGO_ENABLED=0 GOOS=linux \ 27 | go build -a \ 28 | -o mayfly-go main.go 29 | 30 | FROM alpine:3.16 31 | 32 | RUN apk add --no-cache ca-certificates bash expat 33 | 34 | ENV TZ=Asia/Shanghai 35 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 36 | 37 | WORKDIR /mayfly 38 | 39 | COPY --from=be-builder /mayfly/mayfly-go /usr/local/bin/mayfly-go 40 | 41 | CMD ["mayfly-go"] 42 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/media/index.scss: -------------------------------------------------------------------------------- 1 | /* 栅格布局(媒体查询变量) 2 | * $xs <768px 响应式栅格 3 | * $sm ≥768px 响应式栅格 4 | * $md ≥992px 响应式栅格 5 | * $lg ≥1200px 响应式栅格 6 | * $xl ≥1920px 响应式栅格 7 | ------------------------------- */ 8 | $xs: 576px; 9 | $sm: 768px; 10 | $md: 992px; 11 | $lg: 1200px; 12 | $xl: 1920px; 13 | 14 | /* 页面宽度小于576px 15 | ------------------------------- */ 16 | @media screen and (max-width: $xs) { 17 | } 18 | 19 | /* 页面宽度小于768px 20 | ------------------------------- */ 21 | @media screen and (max-width: $sm) { 22 | } 23 | 24 | /* 页面宽度大于768px小于992px 25 | ------------------------------- */ 26 | @media screen and (min-width: $sm) and (max-width: $md) { 27 | } 28 | 29 | /* 页面宽度大于992px小于1200px 30 | ------------------------------- */ 31 | @media screen and (min-width: $md) and (max-width: $lg) { 32 | } 33 | 34 | /* 页面宽度大于1920px 35 | ------------------------------- */ 36 | @media screen and (min-width: $xl) { 37 | } 38 | -------------------------------------------------------------------------------- /server/internal/db/infrastructure/persistence/instance.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/db/domain/entity" 5 | "mayfly-go/internal/db/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type instanceRepoImpl struct { 12 | base.RepoImpl[*entity.DbInstance] 13 | } 14 | 15 | func newInstanceRepo() repository.Instance { 16 | return &instanceRepoImpl{base.RepoImpl[*entity.DbInstance]{M: new(entity.DbInstance)}} 17 | } 18 | 19 | // 分页获取数据库信息列表 20 | func (d *instanceRepoImpl) GetInstanceList(condition *entity.InstanceQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 21 | qd := gormx.NewQuery(new(entity.DbInstance)). 22 | Eq("id", condition.Id). 23 | Eq("host", condition.Host). 24 | Like("name", condition.Name) 25 | return gormx.PageQuery(qd, pageParam, toEntity) 26 | } 27 | -------------------------------------------------------------------------------- /server/pkg/ws/ws.go: -------------------------------------------------------------------------------- 1 | package ws 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/gorilla/websocket" 7 | ) 8 | 9 | var Upgrader = websocket.Upgrader{ 10 | ReadBufferSize: 1024, 11 | WriteBufferSize: 1024 * 1024 * 10, 12 | CheckOrigin: func(r *http.Request) bool { 13 | return true 14 | }, 15 | } 16 | 17 | var Manager = NewClientManager() // 管理者 18 | 19 | func init() { 20 | go Manager.Start() 21 | } 22 | 23 | // 添加ws客户端 24 | func AddClient(userId UserId, clientId string, conn *websocket.Conn) *Client { 25 | if len(clientId) == 0 { 26 | return nil 27 | } 28 | cli := NewClient(UserId(userId), clientId, conn) 29 | cli.Read() 30 | Manager.AddClient(cli) 31 | return cli 32 | } 33 | 34 | func CloseClient(uid UserId) { 35 | Manager.CloseByUid(uid) 36 | } 37 | 38 | // 对指定用户发送json类型消息 39 | func SendJsonMsg(userId UserId, clientId string, msg any) { 40 | Manager.SendJsonMsg(userId, clientId, msg) 41 | } 42 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/ops/tag/api.ts: -------------------------------------------------------------------------------- 1 | import Api from '@/common/Api'; 2 | 3 | export const tagApi = { 4 | listByQuery: Api.newGet('/tag-trees/query'), 5 | getTagTrees: Api.newGet('/tag-trees'), 6 | saveTagTree: Api.newPost('/tag-trees'), 7 | delTagTree: Api.newDelete('/tag-trees/{id}'), 8 | 9 | getResourceTagPaths: Api.newGet('/tag-trees/resources/{resourceType}/tag-paths'), 10 | getTagResources: Api.newGet('/tag-trees/resources'), 11 | 12 | getTeams: Api.newGet('/teams'), 13 | saveTeam: Api.newPost('/teams'), 14 | delTeam: Api.newDelete('/teams/{id}'), 15 | 16 | getTeamMem: Api.newGet('/teams/{teamId}/members'), 17 | saveTeamMem: Api.newPost('/teams/{teamId}/members'), 18 | delTeamMem: Api.newDelete('/teams/{teamId}/members/{accountId}'), 19 | 20 | getTeamTagIds: Api.newGet('/teams/{teamId}/tags'), 21 | saveTeamTags: Api.newPost('/teams/{teamId}/tags'), 22 | }; 23 | -------------------------------------------------------------------------------- /server/internal/tag/infrastructure/persistence/persistence.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import "mayfly-go/internal/tag/domain/repository" 4 | 5 | var ( 6 | tagTreeRepo repository.TagTree = newTagTreeRepo() 7 | tagTreeTeamRepo repository.TagTreeTeam = newTagTreeTeamRepo() 8 | tagResourceRepo repository.TagResource = newTagResourceRepo() 9 | teamRepo repository.Team = newTeamRepo() 10 | teamMemberRepo repository.TeamMember = newTeamMemberRepo() 11 | ) 12 | 13 | func GetTagTreeRepo() repository.TagTree { 14 | return tagTreeRepo 15 | } 16 | 17 | func GetTagTreeTeamRepo() repository.TagTreeTeam { 18 | return tagTreeTeamRepo 19 | } 20 | 21 | func GetTagResourceRepo() repository.TagResource { 22 | return tagResourceRepo 23 | } 24 | 25 | func GetTeamRepo() repository.Team { 26 | return teamRepo 27 | } 28 | 29 | func GetTeamMemberRepo() repository.TeamMember { 30 | return teamMemberRepo 31 | } 32 | -------------------------------------------------------------------------------- /server/internal/db/api/form/db.go: -------------------------------------------------------------------------------- 1 | package form 2 | 3 | type DbForm struct { 4 | Id uint64 `json:"id"` 5 | Name string `binding:"required" json:"name"` 6 | Database string `json:"database"` 7 | Remark string `json:"remark"` 8 | TagId []uint64 `binding:"required" json:"tagId"` 9 | InstanceId uint64 `binding:"required" json:"instanceId"` 10 | } 11 | 12 | type DbSqlSaveForm struct { 13 | Name string `json:"name" binding:"required"` 14 | Sql string `json:"sql" binding:"required"` 15 | Type int `json:"type" binding:"required"` 16 | Db string `json:"db" binding:"required"` 17 | } 18 | 19 | // 数据库SQL执行表单 20 | type DbSqlExecForm struct { 21 | ExecId string `json:"execId"` // 执行id(用于取消执行使用) 22 | Db string `binding:"required" json:"db"` //数据库名 23 | Sql string `binding:"required" json:"sql"` // 执行sql 24 | Remark string `json:"remark"` // 执行备注 25 | } 26 | -------------------------------------------------------------------------------- /mayfly_go_web/src/components/auth/auths.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 35 | -------------------------------------------------------------------------------- /server/internal/machine/infrastructure/persistence/machine_cronjob.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/machine/domain/entity" 5 | "mayfly-go/internal/machine/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type machineCropJobRepoImpl struct { 12 | base.RepoImpl[*entity.MachineCronJob] 13 | } 14 | 15 | func newMachineCronJobRepo() repository.MachineCronJob { 16 | return &machineCropJobRepoImpl{base.RepoImpl[*entity.MachineCronJob]{M: new(entity.MachineCronJob)}} 17 | } 18 | 19 | // 分页获取机器信息列表 20 | func (m *machineCropJobRepoImpl) GetPageList(condition *entity.MachineCronJob, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 21 | qd := gormx.NewQuery(condition).Like("name", condition.Name).Eq("status", condition.Status).WithOrderBy(orderBy...) 22 | return gormx.PageQuery(qd, pageParam, toEntity) 23 | } 24 | -------------------------------------------------------------------------------- /server/internal/machine/infrastructure/persistence/machine_cronjob_exec.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/machine/domain/entity" 5 | "mayfly-go/internal/machine/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type machineCropJobExecRepoImpl struct { 12 | base.RepoImpl[*entity.MachineCronJobExec] 13 | } 14 | 15 | func newMachineCronJobExecRepo() repository.MachineCronJobExec { 16 | return &machineCropJobExecRepoImpl{base.RepoImpl[*entity.MachineCronJobExec]{M: new(entity.MachineCronJobExec)}} 17 | } 18 | 19 | // 分页获取机器信息列表 20 | func (m *machineCropJobExecRepoImpl) GetPageList(condition *entity.MachineCronJobExec, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 21 | qd := gormx.NewQuery(condition).WithCondModel(condition).WithOrderBy(orderBy...) 22 | return gormx.PageQuery(qd, pageParam, toEntity) 23 | } 24 | -------------------------------------------------------------------------------- /mayfly_go_web/src/common/openApi.ts: -------------------------------------------------------------------------------- 1 | import request from './request'; 2 | 3 | export default { 4 | login: (param: any) => request.post('/auth/accounts/login', param), 5 | otpVerify: (param: any) => request.post('/auth/accounts/otp-verify', param), 6 | getPublicKey: () => request.get('/common/public-key'), 7 | getConfigValue: (params: any) => request.get('/sys/configs/value', params), 8 | oauth2LoginConfig: () => request.get('/auth/oauth2-config'), 9 | changePwd: (param: any) => request.post('/sys/accounts/change-pwd', param), 10 | captcha: () => request.get('/sys/captcha'), 11 | logout: () => request.post('/auth/accounts/logout'), 12 | getPermissions: () => request.get('/sys/accounts/permissions'), 13 | oauth2Callback: (params: any) => request.get('/auth/oauth2/callback', params), 14 | getLdapEnabled: () => request.get('/auth/ldap/enabled'), 15 | ldapLogin: (param: any) => request.post('/auth/ldap/login', param), 16 | }; 17 | -------------------------------------------------------------------------------- /server/internal/sys/api/form/resource.go: -------------------------------------------------------------------------------- 1 | package form 2 | 3 | type ResourceForm struct { 4 | Pid int `json:"pid"` 5 | Id int `json:"id"` 6 | Code string `json:"code" binding:"required"` 7 | Name string `json:"name" binding:"required"` 8 | Type int `json:"type" binding:"required,oneof=1 2"` 9 | Weight int `json:"weight"` 10 | Meta map[string]any `json:"meta"` 11 | } 12 | 13 | type MenuResourceMeta struct { 14 | RouteName string `json:"routeName" binding:"required"` 15 | Component string `json:"component" binding:"required"` 16 | Redirect string `json:"redirect"` 17 | Path string `json:"path" binding:"required"` 18 | IsKeepAlive bool `json:"isKeepAlive"` // 19 | IsHide bool `json:"isHide"` // 是否在菜单栏显示,默认显示 20 | IsAffix bool `json:"isAffix"` // tag标签是否不可删除 21 | IsIframe bool `json:"isIframe"` 22 | Link string `json:"link"` 23 | } 24 | -------------------------------------------------------------------------------- /server/internal/sys/domain/repository/role.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/sys/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type Role interface { 10 | base.Repo[*entity.Role] 11 | 12 | GetPageList(condition *entity.RoleQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 13 | 14 | ListByQuery(condition *entity.RoleQuery) ([]*entity.Role, error) 15 | 16 | // 获取角色拥有的资源id数组,从role_resource表获取 17 | GetRoleResourceIds(roleId uint64) []uint64 18 | 19 | GetRoleResources(roleId uint64, toEntity any) 20 | 21 | SaveRoleResource(rr []*entity.RoleResource) 22 | 23 | DeleteRoleResource(roleId uint64, resourceId uint64) 24 | } 25 | 26 | type AccountRole interface { 27 | base.Repo[*entity.AccountRole] 28 | 29 | GetPageList(condition *entity.RoleAccountQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 30 | } 31 | -------------------------------------------------------------------------------- /mayfly_go_web/src/components/monaco/completionItemProvider.ts: -------------------------------------------------------------------------------- 1 | import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; 2 | 3 | /** 4 | * key: language, value: CompletionItemProvider 5 | */ 6 | const completionItemProviders: Map = new Map(); 7 | 8 | export function registerCompletionItemProvider(language: string, completionItemProvider: any, replace: boolean = true) { 9 | const exist = completionItemProviders.get(language); 10 | if (exist) { 11 | if (!replace) { 12 | return; 13 | } 14 | exist.dispose(); 15 | } 16 | completionItemProviders.set(language, monaco.languages.registerCompletionItemProvider(language, completionItemProvider)); 17 | } 18 | 19 | export function dispposeCompletionItemProvider(language: string) { 20 | const exist = completionItemProviders.get(language); 21 | if (exist) { 22 | exist.dispose(); 23 | completionItemProviders.delete(language); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/internal/db/domain/entity/query.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import "mayfly-go/pkg/model" 4 | 5 | // 数据库实例查询 6 | type InstanceQuery struct { 7 | Id uint64 `json:"id" form:"id"` 8 | Name string `json:"name" form:"name"` 9 | Host string `json:"host" form:"host"` 10 | } 11 | 12 | // 数据库查询实体,不与数据库表字段一一对应 13 | type DbQuery struct { 14 | model.Model 15 | 16 | Name string `orm:"column(name)" json:"name"` 17 | Database string `orm:"column(database)" json:"database"` 18 | Remark string `json:"remark"` 19 | 20 | Codes []string 21 | TagIds []uint64 `orm:"column(tag_id)"` 22 | TagPath string `form:"tagPath"` 23 | 24 | InstanceId uint64 `form:"instanceId"` 25 | } 26 | 27 | type DbSqlExecQuery struct { 28 | Id uint64 `json:"id" form:"id"` 29 | DbId uint64 `json:"dbId" form:"dbId"` 30 | Db string `json:"db" form:"db"` 31 | Table string `json:"table" form:"table"` 32 | Type int8 `json:"type" form:"type"` // 类型 33 | 34 | CreatorId uint64 35 | } 36 | -------------------------------------------------------------------------------- /server/internal/machine/domain/repository/machine_cronjob.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "mayfly-go/internal/machine/domain/entity" 5 | "mayfly-go/pkg/base" 6 | "mayfly-go/pkg/model" 7 | ) 8 | 9 | type MachineCronJob interface { 10 | base.Repo[*entity.MachineCronJob] 11 | 12 | GetPageList(condition *entity.MachineCronJob, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 13 | } 14 | 15 | type MachineCronJobRelate interface { 16 | base.Repo[*entity.MachineCronJobRelate] 17 | 18 | GetList(condition *entity.MachineCronJobRelate) []entity.MachineCronJobRelate 19 | 20 | GetMachineIds(cronJobId uint64) []uint64 21 | 22 | GetCronJobIds(machineId uint64) []uint64 23 | } 24 | 25 | type MachineCronJobExec interface { 26 | base.Repo[*entity.MachineCronJobExec] 27 | 28 | GetPageList(condition *entity.MachineCronJobExec, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) 29 | } 30 | -------------------------------------------------------------------------------- /server/pkg/starter/redis.go: -------------------------------------------------------------------------------- 1 | package starter 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "mayfly-go/pkg/config" 7 | "mayfly-go/pkg/logx" 8 | "mayfly-go/pkg/rediscli" 9 | 10 | "github.com/redis/go-redis/v9" 11 | ) 12 | 13 | func initRedis() { 14 | rediscli.SetCli(connRedis()) 15 | } 16 | 17 | func connRedis() *redis.Client { 18 | // 设置redis客户端 19 | redisConf := config.Conf.Redis 20 | if redisConf.Host == "" { 21 | // logx.Panic("未找到redis配置信息") 22 | return nil 23 | } 24 | logx.Infof("连接redis [%s:%d]", redisConf.Host, redisConf.Port) 25 | rdb := redis.NewClient(&redis.Options{ 26 | Addr: fmt.Sprintf("%s:%d", redisConf.Host, redisConf.Port), 27 | Password: redisConf.Password, // no password set 28 | DB: redisConf.Db, // use default DB 29 | }) 30 | // 测试连接 31 | _, e := rdb.Ping(context.TODO()).Result() 32 | if e != nil { 33 | logx.Panicf("连接redis失败! [%s:%d][%s]", redisConf.Host, redisConf.Port, e.Error()) 34 | } 35 | return rdb 36 | } 37 | -------------------------------------------------------------------------------- /server/internal/redis/api/string.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "context" 5 | "mayfly-go/internal/redis/api/form" 6 | "mayfly-go/pkg/biz" 7 | "mayfly-go/pkg/ginx" 8 | "mayfly-go/pkg/req" 9 | "mayfly-go/pkg/utils/collx" 10 | "time" 11 | ) 12 | 13 | func (r *Redis) GetStringValue(rc *req.Ctx) { 14 | ri, key := r.checkKeyAndGetRedisConn(rc) 15 | str, err := ri.GetCmdable().Get(context.TODO(), key).Result() 16 | biz.ErrIsNilAppendErr(err, "获取字符串值失败: %s") 17 | rc.ResData = str 18 | } 19 | 20 | func (r *Redis) SetStringValue(rc *req.Ctx) { 21 | g := rc.GinCtx 22 | keyValue := new(form.StringValue) 23 | ginx.BindJsonAndValid(g, keyValue) 24 | 25 | ri := r.getRedisConn(rc) 26 | cmd := ri.GetCmdable() 27 | rc.ReqParam = collx.Kvs("redis", ri.Info, "string", keyValue) 28 | 29 | str, err := cmd.Set(context.TODO(), keyValue.Key, keyValue.Value, time.Second*time.Duration(keyValue.Timed)).Result() 30 | biz.ErrIsNilAppendErr(err, "保存字符串值失败: %s") 31 | rc.ResData = str 32 | } 33 | -------------------------------------------------------------------------------- /server/internal/sys/router/resource.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/sys/api" 5 | "mayfly-go/internal/sys/application" 6 | "mayfly-go/pkg/req" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func InitResourceRouter(router *gin.RouterGroup) { 12 | r := &api.Resource{ResourceApp: application.GetResourceApp()} 13 | rg := router.Group("sys/resources") 14 | 15 | reqs := [...]*req.Conf{ 16 | req.NewGet("", r.GetAllResourceTree), 17 | 18 | req.NewGet(":id", r.GetById), 19 | 20 | req.NewPost("", r.SaveResource).Log(req.NewLogSave("保存资源")).RequiredPermissionCode("resource:add"), 21 | 22 | req.NewPut(":id/:status", r.ChangeStatus).Log(req.NewLogSave("修改资源状态")).RequiredPermissionCode("resource:changeStatus"), 23 | 24 | req.NewPost("sort", r.Sort).Log(req.NewLogSave("资源排序")), 25 | 26 | req.NewDelete(":id", r.DelResource).Log(req.NewLogSave("删除资源")).RequiredPermissionCode("resource:delete"), 27 | } 28 | 29 | req.BatchSetGroup(rg, reqs[:]) 30 | } 31 | -------------------------------------------------------------------------------- /mayfly_go_web/src/layout/main/classic.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 25 | -------------------------------------------------------------------------------- /server/internal/db/infrastructure/persistence/db_sql_exec.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/db/domain/entity" 5 | "mayfly-go/internal/db/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type dbSqlExecRepoImpl struct { 12 | base.RepoImpl[*entity.DbSqlExec] 13 | } 14 | 15 | func newDbSqlExecRepo() repository.DbSqlExec { 16 | return &dbSqlExecRepoImpl{base.RepoImpl[*entity.DbSqlExec]{M: new(entity.DbSqlExec)}} 17 | } 18 | 19 | // 分页获取 20 | func (d *dbSqlExecRepoImpl) GetPageList(condition *entity.DbSqlExecQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 21 | qd := gormx.NewQuery(new(entity.DbSqlExec)). 22 | Eq("db_id", condition.DbId). 23 | Eq("`table`", condition.Table). 24 | Eq("type", condition.Type). 25 | Eq("creator_id", condition.CreatorId). 26 | RLike("db", condition.Db).WithOrderBy(orderBy...) 27 | return gormx.PageQuery(qd, pageParam, toEntity) 28 | } 29 | -------------------------------------------------------------------------------- /server/internal/db/router/instance.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/db/api" 5 | "mayfly-go/internal/db/application" 6 | "mayfly-go/pkg/req" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func InitInstanceRouter(router *gin.RouterGroup) { 12 | instances := router.Group("/instances") 13 | 14 | d := &api.Instance{ 15 | InstanceApp: application.GetInstanceApp(), 16 | DbApp: application.GetDbApp(), 17 | } 18 | 19 | reqs := [...]*req.Conf{ 20 | // 获取数据库列表 21 | req.NewGet("", d.Instances), 22 | 23 | req.NewPost("/test-conn", d.TestConn), 24 | 25 | req.NewPost("", d.SaveInstance).Log(req.NewLogSave("db-保存数据库实例信息")), 26 | 27 | req.NewGet(":instanceId", d.GetInstance), 28 | req.NewGet(":instanceId/pwd", d.GetInstancePwd), 29 | // 获取数据库实例的所有数据库名 30 | req.NewGet(":instanceId/databases", d.GetDatabaseNames), 31 | 32 | req.NewDelete(":instanceId", d.DeleteInstance).Log(req.NewLogSave("db-删除数据库实例")), 33 | } 34 | 35 | req.BatchSetGroup(instances, reqs[:]) 36 | } 37 | -------------------------------------------------------------------------------- /server/internal/machine/router/machine_cronjob.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/machine/api" 5 | "mayfly-go/internal/machine/application" 6 | "mayfly-go/pkg/req" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func InitMachineCronJobRouter(router *gin.RouterGroup) { 12 | cronjobs := router.Group("machine-cronjobs") 13 | cj := &api.MachineCronJob{ 14 | MachineCronJobApp: application.GetMachineCronJobApp(), 15 | } 16 | 17 | reqs := [...]*req.Conf{ 18 | // 获取机器任务列表 19 | req.NewGet("", cj.MachineCronJobs), 20 | 21 | req.NewGet("/machine-ids", cj.GetRelateMachineIds), 22 | 23 | req.NewGet("/cronjob-ids", cj.GetRelateCronJobIds), 24 | 25 | req.NewPost("", cj.Save).Log(req.NewLogSave("保存机器计划任务")), 26 | 27 | req.NewDelete(":ids", cj.Delete).Log(req.NewLogSave("删除机器计划任务")), 28 | 29 | req.NewPost("/run/:key", cj.RunCronJob).Log(req.NewLogSave("手动执行计划任务")), 30 | 31 | req.NewGet("/execs", cj.CronJobExecs), 32 | } 33 | 34 | req.BatchSetGroup(cronjobs, reqs[:]) 35 | } 36 | -------------------------------------------------------------------------------- /mayfly_go_web/src/common/utils/viteBuild.ts: -------------------------------------------------------------------------------- 1 | // vite 打包相关 2 | import dotenv from 'dotenv'; 3 | export interface ViteEnv { 4 | VITE_PORT: number; 5 | VITE_OPEN: boolean; 6 | VITE_PUBLIC_PATH: string; 7 | } 8 | 9 | export function loadEnv(): ViteEnv { 10 | const env = process.env.NODE_ENV; 11 | const ret: any = {}; 12 | const envList = [`.env.${env}.local`, `.env.${env}`, '.env.local', '.env', ,]; 13 | envList.forEach((e) => { 14 | dotenv.config({ path: e }); 15 | }); 16 | for (const envName of Object.keys(process.env)) { 17 | console.log(envName); 18 | let realName = (process.env as any)[envName].replace(/\\n/g, '\n'); 19 | realName = realName === 'true' ? true : realName === 'false' ? false : realName; 20 | if (envName === 'VITE_PORT') realName = Number(realName); 21 | if (envName === 'VITE_OPEN') realName = Boolean(realName); 22 | ret[envName] = realName; 23 | process.env[envName] = realName; 24 | } 25 | return ret; 26 | } 27 | -------------------------------------------------------------------------------- /server/pkg/config/log.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "mayfly-go/pkg/logx" 5 | "path" 6 | ) 7 | 8 | type Log struct { 9 | Level string `yaml:"level"` 10 | Type string `yaml:"type"` 11 | AddSource bool `yaml:"add-source"` 12 | File LogFile `yaml:"file"` 13 | } 14 | 15 | func (l *Log) Default() { 16 | if l.Level == "" { 17 | l.Level = "info" 18 | logx.Warnf("未配置log.level, 默认值: %s", l.Level) 19 | } 20 | if l.Type == "" { 21 | l.Type = "text" 22 | } 23 | if l.File.Name == "" { 24 | l.File.Name = "mayfly-go.log" 25 | } 26 | } 27 | 28 | type LogFile struct { 29 | Name string `yaml:"name"` 30 | Path string `yaml:"path"` 31 | } 32 | 33 | // 获取完整路径文件名 34 | func (l *LogFile) GetFilename() string { 35 | var filepath, filename string 36 | if fp := l.Path; fp == "" { 37 | filepath = "./" 38 | } else { 39 | filepath = fp 40 | } 41 | if fn := l.Name; fn == "" { 42 | filename = "default.log" 43 | } else { 44 | filename = fn 45 | } 46 | 47 | return path.Join(filepath, filename) 48 | } 49 | -------------------------------------------------------------------------------- /server/internal/tag/router/tag_tree.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/tag/api" 5 | "mayfly-go/internal/tag/application" 6 | "mayfly-go/pkg/req" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func InitTagTreeRouter(router *gin.RouterGroup) { 12 | m := &api.TagTree{ 13 | TagTreeApp: application.GetTagTreeApp(), 14 | TagResourceApp: application.GetTagResourceApp(), 15 | } 16 | 17 | tagTree := router.Group("/tag-trees") 18 | { 19 | reqs := [...]*req.Conf{ 20 | // 获取标签树列表 21 | req.NewGet("", m.GetTagTree), 22 | 23 | // 根据条件获取标签 24 | req.NewGet("query", m.ListByQuery), 25 | 26 | req.NewPost("", m.SaveTagTree).Log(req.NewLogSave("标签树-保存信息")).RequiredPermissionCode("tag:save"), 27 | 28 | req.NewDelete(":id", m.DelTagTree).Log(req.NewLogSave("标签树-删除信息")).RequiredPermissionCode("tag:del"), 29 | 30 | req.NewGet("/resources/:rtype/tag-paths", m.TagResources), 31 | 32 | req.NewGet("/resources", m.QueryTagResources), 33 | } 34 | 35 | req.BatchSetGroup(tagTree, reqs[:]) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /server/pkg/utils/collx/array_test.go: -------------------------------------------------------------------------------- 1 | package collx 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestArrayCompare(t *testing.T) { 9 | newArr := []any{1, 2, 3, 5} 10 | oldArr := []any{3, 6} 11 | add, del, unmodifier := ArrayCompare(newArr, oldArr, func(i1, i2 any) bool { 12 | return i1.(int) == i2.(int) 13 | }) 14 | fmt.Println(add...) 15 | fmt.Println(del...) 16 | fmt.Println(unmodifier...) 17 | } 18 | 19 | func TestArrayChunk(t *testing.T) { 20 | arr := []int{1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} 21 | res := ArrayChunk[int](arr, 3) 22 | fmt.Println(res) 23 | } 24 | 25 | func TestArraySplit(t *testing.T) { 26 | // arr := []int{1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15} 27 | arr := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} 28 | // arr := []int{1, 2, 3} 29 | res := ArraySplit(arr, 10) 30 | fmt.Println(res) 31 | } 32 | 33 | func TestArrayReduce(t *testing.T) { 34 | arr := []int{1, 2, 3, 5} 35 | res := ArrayReduce[int, int](arr, 0, func(i1, i2 int) int { return i1 + i2 }) 36 | fmt.Println(res) 37 | } 38 | -------------------------------------------------------------------------------- /server/internal/machine/infrastructure/persistence/auth_cert.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/machine/domain/entity" 5 | "mayfly-go/internal/machine/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type authCertRepoImpl struct { 12 | base.RepoImpl[*entity.AuthCert] 13 | } 14 | 15 | func newAuthCertRepo() repository.AuthCert { 16 | return &authCertRepoImpl{base.RepoImpl[*entity.AuthCert]{M: new(entity.AuthCert)}} 17 | } 18 | 19 | func (m *authCertRepoImpl) GetPageList(condition *entity.AuthCertQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 20 | qd := gormx.NewQuery(new(entity.AuthCert)).WithCondModel(condition).WithOrderBy(orderBy...) 21 | return gormx.PageQuery(qd, pageParam, toEntity) 22 | } 23 | 24 | // func (m *authCertRepoImpl) GetByIds(ids ...uint64) []*entity.AuthCert { 25 | // acs := new([]*entity.AuthCert) 26 | // gormx.GetByIdIn(new(entity.AuthCert), acs, ids) 27 | // return *acs 28 | // } 29 | -------------------------------------------------------------------------------- /server/internal/sys/router/role.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/sys/api" 5 | "mayfly-go/internal/sys/application" 6 | "mayfly-go/pkg/req" 7 | 8 | "github.com/gin-gonic/gin" 9 | ) 10 | 11 | func InitRoleRouter(router *gin.RouterGroup) { 12 | r := &api.Role{ 13 | RoleApp: application.GetRoleApp(), 14 | ResourceApp: application.GetResourceApp(), 15 | } 16 | rg := router.Group("sys/roles") 17 | 18 | reqs := [...]*req.Conf{ 19 | req.NewGet("", r.Roles), 20 | 21 | req.NewPost("", r.SaveRole).Log(req.NewLogSave("保存角色")).RequiredPermissionCode("role:add"), 22 | 23 | req.NewDelete(":id", r.DelRole).Log(req.NewLogSave("删除角色")).RequiredPermissionCode("role:del"), 24 | 25 | req.NewGet(":id/resourceIds", r.RoleResourceIds), 26 | 27 | req.NewGet(":id/resources", r.RoleResource), 28 | 29 | req.NewPost(":id/resources", r.SaveResource).Log(req.NewLogSave("保存角色资源")).RequiredPermissionCode("role:saveResources"), 30 | 31 | req.NewGet(":id/accounts", r.RoleAccount), 32 | } 33 | 34 | req.BatchSetGroup(rg, reqs[:]) 35 | } 36 | -------------------------------------------------------------------------------- /server/pkg/errorx/bizerror.go: -------------------------------------------------------------------------------- 1 | package errorx 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // 业务错误 8 | type BizError struct { 9 | code int16 10 | err string 11 | } 12 | 13 | var ( 14 | Success BizError = NewBizCode(200, "success") 15 | BizErr BizError = NewBizCode(400, "biz error") 16 | ServerError BizError = NewBizCode(500, "server error") 17 | PermissionErr BizError = NewBizCode(501, "token error") 18 | ) 19 | 20 | // 错误消息 21 | func (e BizError) Error() string { 22 | return e.err 23 | } 24 | 25 | // 错误码 26 | func (e BizError) Code() int16 { 27 | return e.code 28 | } 29 | 30 | func (e BizError) String() string { 31 | return fmt.Sprintf("errCode: %d, errMsg: %s", e.Code(), e.Error()) 32 | } 33 | 34 | // 创建业务逻辑错误结构体,默认为业务逻辑错误 35 | func NewBiz(msg string, formats ...any) BizError { 36 | return BizError{code: BizErr.code, err: fmt.Sprintf(msg, formats...)} 37 | } 38 | 39 | // 创建业务逻辑错误结构体,可设置指定错误code 40 | func NewBizCode(code int16, msg string, formats ...any) BizError { 41 | return BizError{code: code, err: fmt.Sprintf(msg, formats...)} 42 | } 43 | -------------------------------------------------------------------------------- /server/pkg/starter/web-server.go: -------------------------------------------------------------------------------- 1 | package starter 2 | 3 | import ( 4 | "mayfly-go/initialize" 5 | "mayfly-go/pkg/biz" 6 | "mayfly-go/pkg/config" 7 | "mayfly-go/pkg/logx" 8 | "mayfly-go/pkg/req" 9 | 10 | "github.com/gin-gonic/gin" 11 | ) 12 | 13 | func runWebServer() { 14 | // 设置gin日志输出器 15 | logOut := logx.GetConfig().GetLogOut() 16 | gin.DefaultErrorWriter = logOut 17 | gin.DefaultWriter = logOut 18 | 19 | // 权限处理器 20 | req.UseBeforeHandlerInterceptor(req.PermissionHandler) 21 | // 日志处理器 22 | req.UseAfterHandlerInterceptor(req.LogHandler) 23 | // 设置日志保存函数 24 | req.SetSaveLogFunc(initialize.InitSaveLogFunc()) 25 | 26 | // 注册路由 27 | web := initialize.InitRouter() 28 | 29 | server := config.Conf.Server 30 | port := server.GetPort() 31 | logx.Infof("Listening and serving HTTP on %s", port+server.ContextPath) 32 | 33 | var err error 34 | if server.Tls != nil && server.Tls.Enable { 35 | err = web.RunTLS(port, server.Tls.CertFile, server.Tls.KeyFile) 36 | } else { 37 | err = web.Run(port) 38 | } 39 | biz.ErrIsNilAppendErr(err, "服务启动失败: %s") 40 | } 41 | -------------------------------------------------------------------------------- /server/pkg/httpclient/httpclient_test.go: -------------------------------------------------------------------------------- 1 | package httpclient 2 | 3 | import ( 4 | "fmt" 5 | "mayfly-go/pkg/utils/collx" 6 | "testing" 7 | ) 8 | 9 | type TestStruct struct { 10 | Id uint64 11 | Username string 12 | } 13 | 14 | func TestGet(t *testing.T) { 15 | res, err := NewRequest("www.baidu.com").Get().BodyToString() 16 | fmt.Println(err) 17 | fmt.Println(res) 18 | } 19 | 20 | func TestGetBodyToMap(t *testing.T) { 21 | res, err := NewRequest("http://go.mayfly.run/api/syslogs?pageNum=1&pageSize=10").Get().BodyToMap() 22 | fmt.Println(err) 23 | fmt.Println(res["msg"]) 24 | fmt.Println(res["code"]) 25 | } 26 | 27 | func TestGetQueryBodyToMap(t *testing.T) { 28 | res, err := NewRequest("http://go.mayfly.run/api/syslogs"). 29 | Header("Authorization", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2OTUzOTQ5NTIsImlkIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.pGrczVZqk5nlId-FZPkjW_O5Sw3-2yjgzACp_j4JEXY"). 30 | GetByQuery(collx.M{"pageNum": 1, "pageSize": 10}). 31 | BodyToMap() 32 | fmt.Println(err) 33 | fmt.Println(res["msg"]) 34 | fmt.Println(res["code"]) 35 | } 36 | -------------------------------------------------------------------------------- /mayfly_go_web/src/common/SocketBuilder.ts: -------------------------------------------------------------------------------- 1 | class SocketBuilder { 2 | websocket: WebSocket; 3 | 4 | constructor(url: string) { 5 | if (typeof WebSocket === 'undefined') { 6 | throw new Error('不支持websocket'); 7 | } 8 | if (!url) { 9 | throw new Error('websocket url不能为空'); 10 | } 11 | this.websocket = new WebSocket(url); 12 | } 13 | 14 | static builder(url: string) { 15 | return new SocketBuilder(url); 16 | } 17 | 18 | open(onopen: any) { 19 | this.websocket.onopen = onopen; 20 | return this; 21 | } 22 | 23 | error(onerror: any) { 24 | this.websocket.onerror = onerror; 25 | return this; 26 | } 27 | 28 | message(onmessage: any) { 29 | this.websocket.onmessage = onmessage; 30 | return this; 31 | } 32 | 33 | close(onclose: any) { 34 | this.websocket.onclose = onclose; 35 | return this; 36 | } 37 | 38 | build() { 39 | return this.websocket; 40 | } 41 | } 42 | 43 | export default SocketBuilder; 44 | -------------------------------------------------------------------------------- /server/pkg/validatorx/pattern.go: -------------------------------------------------------------------------------- 1 | package validatorx 2 | 3 | import ( 4 | "mayfly-go/pkg/logx" 5 | "regexp" 6 | 7 | "github.com/go-playground/validator/v10" 8 | ) 9 | 10 | const CustomPatternTagName = "pattern" 11 | 12 | var ( 13 | regexpMap map[string]*regexp.Regexp 14 | patternErrMsg map[string]string 15 | ) 16 | 17 | // 注册自定义正则表达式校验规则 18 | func RegisterCustomPatterns() { 19 | // 账号用户名校验 20 | RegisterPattern("account_username", "^[a-zA-Z0-9_]{5,20}$", "只允许输入5-20位大小写字母、数字、下划线") 21 | } 22 | 23 | // 注册自定义正则表达式 24 | func RegisterPattern(patternName string, regexpStr string, errMsg string) { 25 | if regexpMap == nil { 26 | regexpMap = make(map[string]*regexp.Regexp, 0) 27 | patternErrMsg = make(map[string]string) 28 | } 29 | regexpMap[patternName] = regexp.MustCompile(regexpStr) 30 | patternErrMsg[patternName] = errMsg 31 | } 32 | 33 | func patternValidFunc(f validator.FieldLevel) bool { 34 | reg := regexpMap[f.Param()] 35 | if reg == nil { 36 | logx.Warnf("%s的正则校验规则不存在!", f.Param()) 37 | return false 38 | } 39 | 40 | return reg.MatchString(f.Field().String()) 41 | } 42 | -------------------------------------------------------------------------------- /server/internal/db/dbm/db_type_test.go: -------------------------------------------------------------------------------- 1 | package dbm 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func Test_QuoteLiteral(t *testing.T) { 10 | tests := []struct { 11 | dbType DbType 12 | sql string 13 | want string 14 | }{ 15 | { 16 | dbType: DbTypeMysql, 17 | sql: "\\a\\b", 18 | want: "'\\\\a\\\\b'", 19 | }, 20 | { 21 | dbType: DbTypeMysql, 22 | sql: "'a'", 23 | want: "'''a'''", 24 | }, 25 | { 26 | dbType: DbTypeMysql, 27 | sql: "a\u00A0b", 28 | want: "'a\u00A0b'", 29 | }, 30 | { 31 | dbType: DbTypePostgres, 32 | sql: "\\a\\b", 33 | want: " E'\\\\a\\\\b'", 34 | }, 35 | { 36 | dbType: DbTypePostgres, 37 | sql: "'a'", 38 | want: "'''a'''", 39 | }, 40 | { 41 | dbType: DbTypePostgres, 42 | sql: "a\u00A0b", 43 | want: "'a\u00A0b'", 44 | }, 45 | } 46 | for _, tt := range tests { 47 | t.Run(string(tt.dbType)+"_"+tt.sql, func(t *testing.T) { 48 | got := tt.dbType.QuoteLiteral(tt.sql) 49 | require.Equal(t, tt.want, got) 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /mayfly_go_web/src/common/rsa.ts: -------------------------------------------------------------------------------- 1 | import openApi from './openApi'; 2 | import JSEncrypt from 'jsencrypt'; 3 | import { notBlank } from './assert'; 4 | 5 | var encryptor: any = null; 6 | 7 | export async function getRsaPublicKey() { 8 | let publicKey = sessionStorage.getItem('RsaPublicKey'); 9 | if (publicKey) { 10 | return publicKey; 11 | } 12 | publicKey = (await openApi.getPublicKey()) as string; 13 | sessionStorage.setItem('RsaPublicKey', publicKey); 14 | return publicKey; 15 | } 16 | 17 | /** 18 | * 公钥加密指定值 19 | * 20 | * @param value value 21 | * @returns 加密后的值 22 | */ 23 | export async function RsaEncrypt(value: any) { 24 | // 不存在则返回空值 25 | if (!value) { 26 | return ''; 27 | } 28 | if (encryptor != null && sessionStorage.getItem('RsaPublicKey') != null) { 29 | return encryptor.encrypt(value); 30 | } 31 | encryptor = new JSEncrypt(); 32 | const publicKey = (await getRsaPublicKey()) as string; 33 | notBlank(publicKey, '获取公钥失败'); 34 | encryptor.setPublicKey(publicKey); //设置公钥 35 | return encryptor.encrypt(value); 36 | } 37 | -------------------------------------------------------------------------------- /mayfly_go_web/src/common/utils/date.ts: -------------------------------------------------------------------------------- 1 | export function dateFormat2(fmt: string, date: Date) { 2 | let ret; 3 | const opt = { 4 | 'y+': date.getFullYear().toString(), // 年 5 | 'M+': (date.getMonth() + 1).toString(), // 月 6 | 'd+': date.getDate().toString(), // 日 7 | 'H+': date.getHours().toString(), // 时 8 | 'm+': date.getMinutes().toString(), // 分 9 | 's+': date.getSeconds().toString(), // 秒 10 | 'S+': date.getMilliseconds() ? date.getMilliseconds().toString() : '', // 毫秒 11 | // 有其他格式化字符需求可以继续添加,必须转化成字符串 12 | }; 13 | for (const k in opt) { 14 | ret = new RegExp('(' + k + ')').exec(fmt); 15 | if (ret) { 16 | fmt = fmt.replace(ret[1], ret[1].length == 1 ? opt[k] : opt[k].padStart(ret[1].length, '0')); 17 | } 18 | } 19 | return fmt; 20 | } 21 | 22 | export function dateStrFormat(fmt: string, dateStr: string) { 23 | return dateFormat2(fmt, new Date(dateStr)); 24 | } 25 | 26 | export function dateFormat(dateStr: string) { 27 | return dateFormat2('yyyy-MM-dd HH:mm:ss', new Date(dateStr)); 28 | } 29 | -------------------------------------------------------------------------------- /mayfly_go_web/src/layout/navBars/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 28 | 29 | 37 | -------------------------------------------------------------------------------- /server/internal/sys/api/system.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "mayfly-go/pkg/biz" 5 | "mayfly-go/pkg/logx" 6 | "mayfly-go/pkg/req" 7 | "mayfly-go/pkg/utils/anyx" 8 | "mayfly-go/pkg/ws" 9 | 10 | "github.com/gin-gonic/gin" 11 | "github.com/gorilla/websocket" 12 | ) 13 | 14 | type System struct { 15 | } 16 | 17 | // 连接websocket 18 | func (s *System) ConnectWs(g *gin.Context) { 19 | wsConn, err := ws.Upgrader.Upgrade(g.Writer, g.Request, nil) 20 | defer func() { 21 | if err := recover(); err != nil { 22 | errInfo := anyx.ToString(err) 23 | logx.Errorf("websocket连接失败: %s", errInfo) 24 | if wsConn != nil { 25 | wsConn.WriteMessage(websocket.TextMessage, []byte(errInfo)) 26 | wsConn.Close() 27 | } 28 | } 29 | }() 30 | 31 | biz.ErrIsNilAppendErr(err, "%s") 32 | clientId := g.Query("clientId") 33 | biz.NotEmpty(clientId, "clientId不能为空") 34 | 35 | // 权限校验 36 | rc := req.NewCtxWithGin(g) 37 | err = req.PermissionHandler(rc) 38 | biz.ErrIsNil(err, "sys websocket没有权限连接") 39 | 40 | // 登录账号信息 41 | la := rc.GetLoginAccount() 42 | ws.AddClient(ws.UserId(la.Id), clientId, wsConn) 43 | } 44 | -------------------------------------------------------------------------------- /server/internal/sys/domain/entity/account.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "mayfly-go/internal/common/utils" 5 | "mayfly-go/pkg/model" 6 | "time" 7 | ) 8 | 9 | type Account struct { 10 | model.Model 11 | 12 | Name string `json:"name"` 13 | Username string `json:"username"` 14 | Password string `json:"-"` 15 | Status int8 `json:"status"` 16 | LastLoginTime *time.Time `json:"lastLoginTime"` 17 | LastLoginIp string `json:"lastLoginIp"` 18 | OtpSecret string `json:"-"` 19 | } 20 | 21 | func (a *Account) TableName() string { 22 | return "t_sys_account" 23 | } 24 | 25 | // 是否可用 26 | func (a *Account) IsEnable() bool { 27 | return a.Status == AccountEnableStatus 28 | } 29 | 30 | func (a *Account) OtpSecretEncrypt() { 31 | a.OtpSecret = utils.PwdAesEncrypt(a.OtpSecret) 32 | } 33 | 34 | func (a *Account) OtpSecretDecrypt() { 35 | if a.OtpSecret == "-" { 36 | return 37 | } 38 | a.OtpSecret = utils.PwdAesDecrypt(a.OtpSecret) 39 | } 40 | 41 | const ( 42 | AccountEnableStatus int8 = 1 // 启用状态 43 | AccountDisableStatus int8 = -1 // 禁用状态 44 | ) 45 | -------------------------------------------------------------------------------- /server/config.yml.example: -------------------------------------------------------------------------------- 1 | server: 2 | # debug release test 3 | model: release 4 | port: 18888 5 | # 上下文路径, 若设置了该值, 则请求地址为ip:port/context-path 6 | # context-path: /mayfly 7 | cors: true 8 | tls: 9 | enable: false 10 | key-file: ./default.key 11 | cert-file: ./default.pem 12 | jwt: 13 | # jwt key,不设置默认使用随机字符串 14 | key: 15 | # 过期时间单位分钟 16 | expire-time: 1440 17 | # 资源密码aes加密key 18 | aes: 19 | key: 1111111111111111 20 | # 若存在mysql配置,优先使用mysql 21 | mysql: 22 | # 自动升级数据库 23 | auto-migration: false 24 | host: mysql:3306 25 | username: root 26 | password: 111049 27 | db-name: mayfly-go 28 | config: charset=utf8&loc=Local&parseTime=true 29 | max-idle-conns: 5 30 | sqlite: 31 | path: ./mayfly-go.sqlite 32 | max-idle-conns: 5 33 | # 若同时部署多台机器,则需要配置redis信息用于缓存权限码、验证码、公私钥等 34 | # redis: 35 | # host: localhost 36 | # port: 6379 37 | # password: 111049 38 | # db: 0 39 | log: 40 | # 日志等级, debug, info, warn, error 41 | level: info 42 | # 日志格式类型, text/json 43 | type: text 44 | # 是否记录方法调用栈信息 45 | add-source: false 46 | # file: 47 | # path: ./ 48 | # name: mayfly-go.log -------------------------------------------------------------------------------- /server/internal/redis/api/vo/redis.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | import "time" 4 | 5 | type Redis struct { 6 | Id *int64 `json:"id"` 7 | Code *string `json:"code"` 8 | Name *string `json:"name"` 9 | Host *string `json:"host"` 10 | Db string `json:"db"` 11 | Mode *string `json:"mode"` 12 | SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id 13 | Remark *string `json:"remark"` 14 | CreateTime *time.Time `json:"createTime"` 15 | Creator *string `json:"creator"` 16 | CreatorId *int64 `json:"creatorId"` 17 | UpdateTime *time.Time `json:"updateTime"` 18 | Modifier *string `json:"modifier"` 19 | ModifierId *int64 `json:"modifierId"` 20 | } 21 | 22 | type Keys struct { 23 | Cursor map[string]uint64 `json:"cursor"` 24 | Keys []string `json:"keys"` 25 | DbSize int64 `json:"dbSize"` 26 | } 27 | 28 | type KeyInfo struct { 29 | Key string `json:"key"` 30 | Ttl int `json:"ttl"` 31 | Type string `json:"type"` 32 | } 33 | -------------------------------------------------------------------------------- /server/internal/sys/api/vo/account.go: -------------------------------------------------------------------------------- 1 | package vo 2 | 3 | import ( 4 | "mayfly-go/pkg/model" 5 | "time" 6 | ) 7 | 8 | type AccountManageVO struct { 9 | model.Model 10 | Name string `json:"name"` 11 | Username string `json:"username"` 12 | Status int `json:"status"` 13 | LastLoginTime *time.Time `json:"lastLoginTime"` 14 | OtpSecret string `json:"otpSecret"` 15 | } 16 | 17 | // 账号角色信息 18 | type AccountRoleVO struct { 19 | RoleId uint64 `json:"roleId"` 20 | RoleName string `json:"roleName"` 21 | Code string `json:"code"` 22 | Status int `json:"status"` 23 | AccountId uint64 `json:"accountId" gorm:"column:accountId"` 24 | AccountName string `json:"accountName" gorm:"column:accountName"` 25 | Username string `json:"username"` 26 | AccountStatus int `json:"accountStatus" gorm:"column:accountStatus"` 27 | CreateTime *time.Time `json:"createTime"` 28 | Creator string `json:"creator"` 29 | } 30 | 31 | // 账号个人信息 32 | type AccountPersonVO struct { 33 | Roles []*AccountRoleVO `json:"roles"` // 角色信息 34 | } 35 | -------------------------------------------------------------------------------- /mayfly_go_web/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 lyt-Top 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /server/pkg/config/server.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Server struct { 8 | Port int `yaml:"port"` 9 | Model string `yaml:"model"` 10 | ContextPath string `yaml:"context-path"` // 请求路径上下文 11 | Cors bool `yaml:"cors"` 12 | Tls *Tls `yaml:"tls"` 13 | Static *[]*Static `yaml:"static"` 14 | StaticFile *[]*StaticFile `yaml:"static-file"` 15 | } 16 | 17 | func (s *Server) Default() { 18 | if s.Model == "" { 19 | s.Model = "release" 20 | } 21 | if s.Port == 0 { 22 | s.Port = 8888 23 | } 24 | } 25 | 26 | func (s *Server) GetPort() string { 27 | return fmt.Sprintf(":%d", s.Port) 28 | } 29 | 30 | type Static struct { 31 | RelativePath string `yaml:"relative-path"` 32 | Root string `yaml:"root"` 33 | } 34 | 35 | type StaticFile struct { 36 | RelativePath string `yaml:"relative-path"` 37 | Filepath string `yaml:"filepath"` 38 | } 39 | 40 | type Tls struct { 41 | Enable bool `yaml:"enable"` // 是否启用tls 42 | KeyFile string `yaml:"key-file"` // 私钥文件路径 43 | CertFile string `yaml:"cert-file"` // 证书文件路径 44 | } 45 | -------------------------------------------------------------------------------- /mayfly_go_web/src/layout/main/columns.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 30 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/dark.scss: -------------------------------------------------------------------------------- 1 | html.dark { 2 | // 变量(自定义时,只需修改这里的值) 3 | --next-bg-main: #1f1f1f; 4 | --next-color-white: #ffffff; 5 | --next-color-disabled: #191919; 6 | --next-color-bar: #dadada; 7 | --next-color-primary: #303030; 8 | --next-border-color: #424242; 9 | --next-border-black: #333333; 10 | --next-border-columns: #2a2a2a; 11 | --next-color-seting: #505050; 12 | --next-text-color-regular: #9b9da1; 13 | --next-text-color-placeholder: #7a7a7a; 14 | --next-color-hover: #3c3c3c; 15 | --next-color-hover-rgba: rgba(0, 0, 0, 0.3); 16 | 17 | /* 自定义深色背景颜色 */ 18 | // root 19 | --bg-main-color: var(--next-bg-main) !important; 20 | --bg-topBar: var(--next-color-disabled) !important; 21 | --bg-topBarColor: var(--next-color-bar) !important; 22 | --bg-menuBar: var(--next-color-disabled) !important; 23 | --bg-menuBarColor: var(--next-color-bar) !important; 24 | --bg-menuBarActiveColor: var(--next-color-hover-rgba) !important; 25 | --bg-columnsMenuBar: var(--next-color-disabled) !important; 26 | --bg-columnsMenuBarColor: var(--next-color-bar) !important; 27 | 28 | --tagsview3-active-background-color: var(--next-color-hover); 29 | } -------------------------------------------------------------------------------- /server/internal/machine/config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | sysapp "mayfly-go/internal/sys/application" 5 | "mayfly-go/pkg/logx" 6 | "mayfly-go/pkg/utils/bytex" 7 | ) 8 | 9 | const ( 10 | ConfigKeyMachine string = "MachineConfig" // 机器相关配置 11 | ) 12 | 13 | type Machine struct { 14 | TerminalRecPath string // 终端操作记录存储位置 15 | UploadMaxFileSize int64 // 允许上传的最大文件size 16 | } 17 | 18 | // 获取机器相关配置 19 | func GetMachine() *Machine { 20 | c := sysapp.GetConfigApp().GetConfig(ConfigKeyMachine) 21 | jm := c.GetJsonMap() 22 | 23 | mc := new(Machine) 24 | 25 | terminalRecPath := jm["terminalRecPath"] 26 | if terminalRecPath == "" { 27 | terminalRecPath = "./rec" 28 | } 29 | mc.TerminalRecPath = terminalRecPath 30 | 31 | // 将1GB等字符串转为int64的byte 32 | uploadMaxFileSizeStr := jm["uploadMaxFileSize"] 33 | var uploadMaxFileSize int64 = 1 * bytex.GB 34 | if uploadMaxFileSizeStr != "" { 35 | var err error 36 | uploadMaxFileSize, err = bytex.ParseSize(uploadMaxFileSizeStr) 37 | if err != nil { 38 | logx.Errorf("解析机器配置的最大上传文件大小失败: uploadMaxFileSize=%s, 使用系统默认值1GB", uploadMaxFileSizeStr) 39 | } 40 | } 41 | mc.UploadMaxFileSize = uploadMaxFileSize 42 | return mc 43 | } 44 | -------------------------------------------------------------------------------- /server/internal/machine/domain/entity/auth_cert.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "mayfly-go/internal/common/utils" 5 | "mayfly-go/pkg/model" 6 | ) 7 | 8 | // 授权凭证 9 | type AuthCert struct { 10 | model.Model 11 | 12 | Name string `json:"name"` 13 | AuthMethod int8 `json:"authMethod"` // 1.密码 2.秘钥 14 | Password string `json:"password" gorm:"column:password;type:varchar(4200)"` // 密码or私钥 15 | Passphrase string `json:"passphrase"` // 私钥口令 16 | Remark string `json:"remark"` 17 | } 18 | 19 | func (a *AuthCert) TableName() string { 20 | return "t_auth_cert" 21 | } 22 | 23 | const ( 24 | AuthCertAuthMethodPassword int8 = 1 // 密码 25 | MachineAuthMethodPublicKey int8 = 2 // 密钥 26 | 27 | AuthCertTypePrivate int8 = 1 28 | AuthCertTypePublic int8 = 2 29 | ) 30 | 31 | // 密码加密 32 | func (ac *AuthCert) PwdEncrypt() { 33 | ac.Password = utils.PwdAesEncrypt(ac.Password) 34 | ac.Passphrase = utils.PwdAesEncrypt(ac.Passphrase) 35 | } 36 | 37 | // 密码解密 38 | func (ac *AuthCert) PwdDecrypt() { 39 | ac.Password = utils.PwdAesDecrypt(ac.Password) 40 | ac.Passphrase = utils.PwdAesDecrypt(ac.Passphrase) 41 | } 42 | -------------------------------------------------------------------------------- /mayfly_go_web/src/theme/media/layout.scss: -------------------------------------------------------------------------------- 1 | @import './index.scss'; 2 | 3 | /* 页面宽度小于576px 4 | ------------------------------- */ 5 | @media screen and (max-width: $xs) { 6 | // MessageBox 弹框 7 | .el-message-box { 8 | width: 80% !important; 9 | } 10 | } 11 | 12 | /* 页面宽度小于768px 13 | ------------------------------- */ 14 | @media screen and (max-width: $sm) { 15 | // Breadcrumb 面包屑 16 | .layout-navbars-breadcrumb-hide { 17 | display: none; 18 | } 19 | // 外链视图 20 | .layout-view-link { 21 | a { 22 | max-width: 80%; 23 | text-align: center; 24 | } 25 | } 26 | // 菜单搜索 27 | .layout-search-dialog { 28 | .el-autocomplete { 29 | width: 80% !important; 30 | } 31 | } 32 | } 33 | 34 | /* 页面宽度小于1000px 35 | ------------------------------- */ 36 | @media screen and (max-width: 1000px) { 37 | // 布局配置 38 | .layout-drawer-content-flex { 39 | position: relative; 40 | &::after { 41 | content: '手机版不支持切换布局'; 42 | position: absolute; 43 | top: 0; 44 | right: 0; 45 | bottom: 0; 46 | left: 0; 47 | z-index: 1; 48 | text-align: center; 49 | height: 140px; 50 | line-height: 140px; 51 | background: rgba(255, 255, 255, 0.9); 52 | color: #666666; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /mayfly_go_web/src/common/utils/setIconfont.ts: -------------------------------------------------------------------------------- 1 | // 字体图标 url 2 | const cssCdnUrlList: Array = []; 3 | // 第三方 js url 4 | const jsCdnUrlList: Array = []; 5 | 6 | // 动态批量设置字体图标 7 | export function setCssCdn() { 8 | if (cssCdnUrlList.length <= 0) return false; 9 | cssCdnUrlList.map((v) => { 10 | let link = document.createElement('link'); 11 | link.rel = 'stylesheet'; 12 | link.href = v; 13 | link.crossOrigin = 'anonymous'; 14 | document.getElementsByTagName('head')[0].appendChild(link); 15 | }); 16 | } 17 | 18 | // 动态批量设置第三方js 19 | export function setJsCdn() { 20 | if (jsCdnUrlList.length <= 0) return false; 21 | jsCdnUrlList.map((v) => { 22 | let link = document.createElement('script'); 23 | link.src = v; 24 | document.body.appendChild(link); 25 | }); 26 | } 27 | 28 | /** 29 | * 批量设置字体图标、动态js 30 | * @method cssCdn 动态批量设置字体图标 31 | * @method jsCdn 动态批量设置第三方js 32 | */ 33 | const setIntroduction = { 34 | // 设置css 35 | cssCdn: () => { 36 | setCssCdn(); 37 | }, 38 | // 设置js 39 | jsCdn: () => { 40 | setJsCdn(); 41 | }, 42 | }; 43 | 44 | // 导出函数方法 45 | export default setIntroduction; 46 | -------------------------------------------------------------------------------- /mayfly_go_web/src/common/echarts/useEcharts.ts: -------------------------------------------------------------------------------- 1 | // import * as echarts from 'echarts' 2 | 3 | // 引入 echarts 核心模块,核心模块提供了 echarts 使用必须要的接口。 4 | import * as echarts from 'echarts/core'; 5 | 6 | /** 图表后缀都为 Chart */ 7 | import { PieChart } from 'echarts/charts'; 8 | 9 | // 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component 10 | import { TitleComponent, TooltipComponent, GridComponent, DatasetComponent, TransformComponent, LegendComponent } from 'echarts/components'; 11 | 12 | // 标签自动布局,全局过渡动画等特性 13 | import { LabelLayout, UniversalTransition } from 'echarts/features'; 14 | 15 | // 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步 16 | import { CanvasRenderer } from 'echarts/renderers'; 17 | 18 | // 注册必须的组件 19 | echarts.use([ 20 | TitleComponent, 21 | TooltipComponent, 22 | GridComponent, 23 | DatasetComponent, 24 | TransformComponent, 25 | LegendComponent, 26 | // BarChart, 27 | LabelLayout, 28 | UniversalTransition, 29 | CanvasRenderer, 30 | // LineChart, 31 | PieChart, 32 | ]); 33 | 34 | export default function (dom: any, theme: any = null, option: any) { 35 | let chart = echarts.init(dom, theme); 36 | chart.setOption(option); 37 | return chart; 38 | } 39 | -------------------------------------------------------------------------------- /mayfly_go_web/src/components/progress-notify/progress-notify.vue: -------------------------------------------------------------------------------- 1 | 7 | 35 | -------------------------------------------------------------------------------- /server/internal/machine/router/machine_script.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "mayfly-go/internal/machine/api" 5 | "mayfly-go/internal/machine/application" 6 | tagapp "mayfly-go/internal/tag/application" 7 | "mayfly-go/pkg/req" 8 | 9 | "github.com/gin-gonic/gin" 10 | ) 11 | 12 | func InitMachineScriptRouter(router *gin.RouterGroup) { 13 | machines := router.Group("machines") 14 | ms := &api.MachineScript{ 15 | MachineScriptApp: application.GetMachineScriptApp(), 16 | MachineApp: application.GetMachineApp(), 17 | TagApp: tagapp.GetTagTreeApp(), 18 | } 19 | 20 | reqs := [...]*req.Conf{ 21 | // 获取指定机器脚本列表 22 | req.NewGet(":machineId/scripts", ms.MachineScripts), 23 | 24 | req.NewPost(":machineId/scripts", ms.SaveMachineScript).Log(req.NewLogSave("机器-保存脚本")).RequiredPermissionCode("machine:script:save"), 25 | 26 | req.NewDelete(":machineId/scripts/:scriptId", ms.DeleteMachineScript).Log(req.NewLogSave("机器-删除脚本")).RequiredPermissionCode("machine:script:del"), 27 | 28 | req.NewGet(":machineId/scripts/:scriptId/run", ms.RunMachineScript).Log(req.NewLogSave("机器-执行脚本")).RequiredPermissionCode("machine:script:run"), 29 | } 30 | 31 | req.BatchSetGroup(machines, reqs[:]) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /server/internal/sys/api/config.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "mayfly-go/internal/sys/api/form" 5 | "mayfly-go/internal/sys/application" 6 | "mayfly-go/internal/sys/domain/entity" 7 | "mayfly-go/pkg/biz" 8 | "mayfly-go/pkg/ginx" 9 | "mayfly-go/pkg/req" 10 | ) 11 | 12 | type Config struct { 13 | ConfigApp application.Config 14 | } 15 | 16 | func (c *Config) Configs(rc *req.Ctx) { 17 | g := rc.GinCtx 18 | condition := &entity.Config{Key: g.Query("key")} 19 | condition.Permission = rc.GetLoginAccount().Username 20 | res, err := c.ConfigApp.GetPageList(condition, ginx.GetPageParam(g), new([]entity.Config)) 21 | biz.ErrIsNil(err) 22 | rc.ResData = res 23 | } 24 | 25 | func (c *Config) GetConfigValueByKey(rc *req.Ctx) { 26 | key := rc.GinCtx.Query("key") 27 | biz.NotEmpty(key, "key不能为空") 28 | 29 | config := c.ConfigApp.GetConfig(key) 30 | // 判断是否为公开配置 31 | if config.Permission != "all" { 32 | rc.ResData = "" 33 | return 34 | } 35 | 36 | rc.ResData = config.Value 37 | } 38 | 39 | func (c *Config) SaveConfig(rc *req.Ctx) { 40 | form := &form.ConfigForm{} 41 | config := ginx.BindJsonAndCopyTo(rc.GinCtx, form, new(entity.Config)) 42 | rc.ReqParam = form 43 | biz.ErrIsNil(c.ConfigApp.Save(rc.MetaCtx, config)) 44 | } 45 | -------------------------------------------------------------------------------- /server/internal/auth/application/oauth2.go: -------------------------------------------------------------------------------- 1 | package application 2 | 3 | import ( 4 | "context" 5 | "mayfly-go/internal/auth/domain/entity" 6 | "mayfly-go/internal/auth/domain/repository" 7 | ) 8 | 9 | type Oauth2 interface { 10 | GetOAuthAccount(condition *entity.Oauth2Account, cols ...string) error 11 | 12 | BindOAuthAccount(e *entity.Oauth2Account) error 13 | 14 | Unbind(accountId uint64) 15 | } 16 | 17 | func newAuthApp(oauthAccountRepo repository.Oauth2Account) Oauth2 { 18 | return &oauth2AppImpl{ 19 | oauthAccountRepo: oauthAccountRepo, 20 | } 21 | } 22 | 23 | type oauth2AppImpl struct { 24 | oauthAccountRepo repository.Oauth2Account 25 | } 26 | 27 | func (a *oauth2AppImpl) GetOAuthAccount(condition *entity.Oauth2Account, cols ...string) error { 28 | return a.oauthAccountRepo.GetBy(condition, cols...) 29 | } 30 | 31 | func (a *oauth2AppImpl) BindOAuthAccount(e *entity.Oauth2Account) error { 32 | if e.Id == 0 { 33 | return a.oauthAccountRepo.Insert(context.Background(), e) 34 | } 35 | return a.oauthAccountRepo.UpdateById(context.Background(), e) 36 | } 37 | 38 | func (a *oauth2AppImpl) Unbind(accountId uint64) { 39 | a.oauthAccountRepo.DeleteByCond(context.Background(), &entity.Oauth2Account{AccountId: accountId}) 40 | } 41 | -------------------------------------------------------------------------------- /server/internal/mongo/api/form/mongo.go: -------------------------------------------------------------------------------- 1 | package form 2 | 3 | type Mongo struct { 4 | Id uint64 `json:"id"` 5 | Uri string `binding:"required" json:"uri"` 6 | SshTunnelMachineId int `json:"sshTunnelMachineId"` // ssh隧道机器id 7 | Name string `binding:"required" json:"name"` 8 | TagId []uint64 `binding:"required" json:"tagId"` 9 | } 10 | 11 | type MongoCommand struct { 12 | Database string `binding:"required" json:"database"` 13 | Collection string `binding:"required" json:"collection"` 14 | Filter map[string]any `json:"filter"` 15 | } 16 | 17 | type MongoRunCommand struct { 18 | Database string `binding:"required" json:"database"` 19 | Command []map[string]any `json:"command"` 20 | } 21 | 22 | type MongoFindCommand struct { 23 | MongoCommand 24 | Sort map[string]any `json:"sort"` 25 | Skip int64 `json:"skip"` 26 | Limit int64 `json:"limit"` 27 | } 28 | 29 | type MongoUpdateByIdCommand struct { 30 | MongoCommand 31 | DocId any `binding:"required" json:"docId"` 32 | Update map[string]any `json:"update"` 33 | } 34 | 35 | type MongoInsertCommand struct { 36 | MongoCommand 37 | Doc map[string]any `json:"doc"` 38 | } 39 | -------------------------------------------------------------------------------- /server/internal/common/api/index.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "mayfly-go/internal/common/consts" 5 | dbapp "mayfly-go/internal/db/application" 6 | machineapp "mayfly-go/internal/machine/application" 7 | mongoapp "mayfly-go/internal/mongo/application" 8 | redisapp "mayfly-go/internal/redis/application" 9 | tagapp "mayfly-go/internal/tag/application" 10 | "mayfly-go/pkg/req" 11 | "mayfly-go/pkg/utils/collx" 12 | ) 13 | 14 | type Index struct { 15 | TagApp tagapp.TagTree 16 | MachineApp machineapp.Machine 17 | DbApp dbapp.Db 18 | RedisApp redisapp.Redis 19 | MongoApp mongoapp.Mongo 20 | } 21 | 22 | func (i *Index) Count(rc *req.Ctx) { 23 | accountId := rc.GetLoginAccount().Id 24 | 25 | mongoNum := len(i.TagApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeMongo, "")) 26 | machienNum := len(i.TagApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeMachine, "")) 27 | dbNum := len(i.TagApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeDb, "")) 28 | redisNum := len(i.TagApp.GetAccountResourceCodes(accountId, consts.TagResourceTypeRedis, "")) 29 | 30 | rc.ResData = collx.M{ 31 | "mongoNum": mongoNum, 32 | "machineNum": machienNum, 33 | "dbNum": dbNum, 34 | "redisNum": redisNum, 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /server/internal/sys/infrastructure/persistence/account_role.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/sys/domain/entity" 5 | "mayfly-go/internal/sys/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | "mayfly-go/pkg/model" 9 | ) 10 | 11 | type accountRoleRepoImpl struct { 12 | base.RepoImpl[*entity.AccountRole] 13 | } 14 | 15 | func newAccountRoleRepo() repository.AccountRole { 16 | return &accountRoleRepoImpl{base.RepoImpl[*entity.AccountRole]{M: new(entity.AccountRole)}} 17 | } 18 | 19 | func (m *accountRoleRepoImpl) GetPageList(condition *entity.RoleAccountQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) { 20 | qd := gormx.NewQueryWithTableName("t_sys_account_role t"). 21 | Select("t.creator, t.create_time, a.username, a.name accountName, a.status accountStatus, a.id accountId"). 22 | Joins("JOIN t_sys_account a ON t.account_id = a.id AND a.status = 1"). 23 | Eq0("a.is_deleted", model.ModelUndeleted). 24 | Eq0("t.is_deleted", model.ModelUndeleted). 25 | RLike("a.username", condition.Username). 26 | RLike("a.name", condition.Name). 27 | Eq("t.role_id", condition.RoleId). 28 | OrderByDesc("t.id") 29 | return gormx.PageQuery(qd, pageParam, toEntity) 30 | } 31 | -------------------------------------------------------------------------------- /mayfly_go_web/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // 一行最多多少个字符 3 | printWidth: 160, 4 | // 指定每个缩进级别的空格数 5 | tabWidth: 4, 6 | // 使用制表符而不是空格缩进行 7 | useTabs: false, 8 | // 在语句末尾打印分号 9 | semi: true, 10 | // 使用单引号而不是双引号 11 | singleQuote: true, 12 | // 更改引用对象属性的时间 可选值"" 13 | quoteProps: 'as-needed', 14 | // 在JSX中使用单引号而不是双引号 15 | jsxSingleQuote: false, 16 | // 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"",默认none 17 | trailingComma: 'es5', 18 | // 在对象文字中的括号之间打印空格 19 | bracketSpacing: true, 20 | // jsx 标签的反尖括号需要换行 21 | jsxBracketSameLine: false, 22 | // 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x 23 | arrowParens: 'always', 24 | // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码 25 | rangeStart: 0, 26 | rangeEnd: Infinity, 27 | // 指定要使用的解析器,不需要写文件开头的 @prettier 28 | requirePragma: false, 29 | // 不需要自动在文件开头插入 @prettier 30 | insertPragma: false, 31 | // 使用默认的折行标准 always\never\preserve 32 | proseWrap: 'preserve', 33 | // 指定HTML文件的全局空格敏感度 css\strict\ignore 34 | htmlWhitespaceSensitivity: 'css', 35 | // Vue文件脚本和样式标签缩进 36 | vueIndentScriptAndStyle: false, 37 | // 换行符使用 lf 结尾是 可选值"" 38 | endOfLine: 'lf', 39 | }; 40 | -------------------------------------------------------------------------------- /server/pkg/scheduler/scheduler.go: -------------------------------------------------------------------------------- 1 | package scheduler 2 | 3 | import ( 4 | "mayfly-go/pkg/biz" 5 | "mayfly-go/pkg/logx" 6 | "sync" 7 | 8 | "github.com/robfig/cron/v3" 9 | ) 10 | 11 | func init() { 12 | Start() 13 | } 14 | 15 | var ( 16 | cronService = cron.New() 17 | key2IdMap sync.Map 18 | ) 19 | 20 | func Start() { 21 | cronService.Start() 22 | } 23 | 24 | func Stop() { 25 | cronService.Stop() 26 | } 27 | 28 | // 根据任务id移除 29 | func Remove(id cron.EntryID) { 30 | cronService.Remove(id) 31 | } 32 | 33 | // 根据任务key移除 34 | func RemoveByKey(key string) { 35 | logx.Debugf("移除cron任务 => [key = %s]", key) 36 | id, ok := key2IdMap.Load(key) 37 | if ok { 38 | Remove(id.(cron.EntryID)) 39 | key2IdMap.Delete(key) 40 | } 41 | } 42 | 43 | func GetCron() *cron.Cron { 44 | return cronService 45 | } 46 | 47 | // 添加任务 48 | func AddFun(spec string, cmd func()) cron.EntryID { 49 | id, err := cronService.AddFunc(spec, cmd) 50 | biz.ErrIsNilAppendErr(err, "添加任务失败: %s") 51 | return id 52 | } 53 | 54 | // 根据key添加定时任务 55 | func AddFunByKey(key, spec string, cmd func()) { 56 | logx.Debugf("添加cron任务 => [key = %s]", key) 57 | RemoveByKey(key) 58 | key2IdMap.Store(key, AddFun(spec, cmd)) 59 | } 60 | 61 | func ExistKey(key string) bool { 62 | _, ok := key2IdMap.Load(key) 63 | return ok 64 | } 65 | -------------------------------------------------------------------------------- /mayfly_go_web/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue'; 2 | import App from '@/App.vue'; 3 | 4 | import router from './router'; 5 | import pinia from '@/store/index'; 6 | import { directive } from '@/directive/index'; 7 | import { globalComponentSize } from '@/common/utils/componentSize'; 8 | import { registElSvgIcon } from '@/common/utils/svgIcons'; 9 | 10 | import ElementPlus from 'element-plus'; 11 | import 'element-plus/dist/index.css'; 12 | import 'element-plus/theme-chalk/dark/css-vars.css'; 13 | import zhCn from 'element-plus/es/locale/lang/zh-cn'; 14 | import { ElMessage } from 'element-plus'; 15 | 16 | import 'splitpanes/dist/splitpanes.css'; 17 | 18 | import '@/theme/index.scss'; 19 | import '@/assets/font/font.css'; 20 | import '@/assets/iconfont/iconfont.js'; 21 | 22 | const app = createApp(App); 23 | 24 | registElSvgIcon(app); 25 | directive(app); 26 | 27 | app.use(pinia).use(router).use(ElementPlus, { size: globalComponentSize, locale: zhCn }).mount('#app'); 28 | 29 | // 屏蔽警告信息 30 | app.config.warnHandler = () => null; 31 | // 全局error处理 32 | app.config.errorHandler = function (err: any, vm, info) { 33 | // 如果是断言错误,则进行提示即可 34 | if (err.name == 'AssertError') { 35 | ElMessage.error(err.message); 36 | } else { 37 | console.error(err, info); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /mayfly_go_web/src/layout/main/defaults.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 36 | -------------------------------------------------------------------------------- /mayfly_go_web/src/layout/footer/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 31 | 32 | 45 | -------------------------------------------------------------------------------- /server/internal/machine/infrastructure/persistence/machine_cronjob_relate.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/machine/domain/entity" 5 | "mayfly-go/internal/machine/domain/repository" 6 | "mayfly-go/pkg/base" 7 | "mayfly-go/pkg/gormx" 8 | ) 9 | 10 | type machineCropJobRelateRepoImpl struct { 11 | base.RepoImpl[*entity.MachineCronJobRelate] 12 | } 13 | 14 | func newMachineCropJobRelateRepo() repository.MachineCronJobRelate { 15 | return &machineCropJobRelateRepoImpl{base.RepoImpl[*entity.MachineCronJobRelate]{M: new(entity.MachineCronJobRelate)}} 16 | } 17 | 18 | func (m *machineCropJobRelateRepoImpl) GetList(condition *entity.MachineCronJobRelate) []entity.MachineCronJobRelate { 19 | list := new([]entity.MachineCronJobRelate) 20 | m.ListByCond(condition, list) 21 | return *list 22 | } 23 | 24 | func (m *machineCropJobRelateRepoImpl) GetMachineIds(cronJobId uint64) []uint64 { 25 | var machineIds []uint64 26 | m.ListByCond(&entity.MachineCronJobRelate{CronJobId: cronJobId}, &machineIds, "machine_id") 27 | return machineIds 28 | } 29 | 30 | func (m *machineCropJobRelateRepoImpl) GetCronJobIds(machineId uint64) []uint64 { 31 | var cronJobIds []uint64 32 | gormx.ListBy(&entity.MachineCronJobRelate{MachineId: machineId}, &cronJobIds, "cron_job_id") 33 | return cronJobIds 34 | } 35 | -------------------------------------------------------------------------------- /server/internal/machine/init/init.go: -------------------------------------------------------------------------------- 1 | package init 2 | 3 | import ( 4 | "context" 5 | "mayfly-go/internal/common/consts" 6 | "mayfly-go/internal/machine/application" 7 | "mayfly-go/internal/machine/domain/entity" 8 | "mayfly-go/pkg/eventbus" 9 | "mayfly-go/pkg/global" 10 | ) 11 | 12 | func Init() { 13 | application.GetMachineCronJobApp().InitCronJob() 14 | application.GetMachineApp().TimerUpdateStats() 15 | 16 | global.EventBus.Subscribe(consts.DeleteMachineEventTopic, "machineFile", func(ctx context.Context, event *eventbus.Event) error { 17 | me := event.Val.(*entity.Machine) 18 | return application.GetMachineFileApp().DeleteByCond(ctx, &entity.MachineFile{MachineId: me.Id}) 19 | }) 20 | 21 | global.EventBus.Subscribe(consts.DeleteMachineEventTopic, "machineScript", func(ctx context.Context, event *eventbus.Event) error { 22 | me := event.Val.(*entity.Machine) 23 | return application.GetMachineScriptApp().DeleteByCond(ctx, &entity.MachineScript{MachineId: me.Id}) 24 | }) 25 | 26 | global.EventBus.Subscribe(consts.DeleteMachineEventTopic, "machineCronJob", func(ctx context.Context, event *eventbus.Event) error { 27 | me := event.Val.(*entity.Machine) 28 | var jobIds []uint64 29 | application.GetMachineCronJobApp().MachineRelateCronJobs(ctx, me.Id, jobIds) 30 | return nil 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /server/internal/redis/rdm/conn.go: -------------------------------------------------------------------------------- 1 | package rdm 2 | 3 | import ( 4 | "context" 5 | "mayfly-go/pkg/logx" 6 | 7 | "github.com/redis/go-redis/v9" 8 | ) 9 | 10 | // redis连接信息 11 | type RedisConn struct { 12 | Id string 13 | Info *RedisInfo 14 | 15 | Cli *redis.Client 16 | ClusterCli *redis.ClusterClient 17 | } 18 | 19 | // 获取命令执行接口的具体实现 20 | func (r *RedisConn) GetCmdable() redis.Cmdable { 21 | redisMode := r.Info.Mode 22 | if redisMode == "" || redisMode == StandaloneMode || r.Info.Mode == SentinelMode { 23 | return r.Cli 24 | } 25 | if redisMode == ClusterMode { 26 | return r.ClusterCli 27 | } 28 | return nil 29 | } 30 | 31 | func (r *RedisConn) Scan(cursor uint64, match string, count int64) ([]string, uint64, error) { 32 | return r.GetCmdable().Scan(context.Background(), cursor, match, count).Result() 33 | } 34 | 35 | func (r *RedisConn) Close() { 36 | mode := r.Info.Mode 37 | if mode == StandaloneMode || mode == SentinelMode { 38 | if err := r.Cli.Close(); err != nil { 39 | logx.Errorf("关闭redis单机实例[%s]连接失败: %s", r.Id, err.Error()) 40 | } 41 | r.Cli = nil 42 | return 43 | } 44 | 45 | if mode == ClusterMode { 46 | if err := r.ClusterCli.Close(); err != nil { 47 | logx.Errorf("关闭redis集群实例[%s]连接失败: %s", r.Id, err.Error()) 48 | } 49 | r.ClusterCli = nil 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/oauth/Oauth2Callback.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 39 | 40 | -------------------------------------------------------------------------------- /server/internal/machine/infrastructure/persistence/machine.go: -------------------------------------------------------------------------------- 1 | package persistence 2 | 3 | import ( 4 | "mayfly-go/internal/machine/api/vo" 5 | "mayfly-go/internal/machine/domain/entity" 6 | "mayfly-go/internal/machine/domain/repository" 7 | "mayfly-go/pkg/base" 8 | "mayfly-go/pkg/gormx" 9 | "mayfly-go/pkg/model" 10 | "mayfly-go/pkg/utils/collx" 11 | "strconv" 12 | "strings" 13 | ) 14 | 15 | type machineRepoImpl struct { 16 | base.RepoImpl[*entity.Machine] 17 | } 18 | 19 | func newMachineRepo() repository.Machine { 20 | return &machineRepoImpl{base.RepoImpl[*entity.Machine]{M: new(entity.Machine)}} 21 | } 22 | 23 | // 分页获取机器信息列表 24 | func (m *machineRepoImpl) GetMachineList(condition *entity.MachineQuery, pageParam *model.PageParam, toEntity *[]*vo.MachineVO, orderBy ...string) (*model.PageResult[*[]*vo.MachineVO], error) { 25 | qd := gormx.NewQuery(new(entity.Machine)). 26 | Eq("status", condition.Status). 27 | Like("ip", condition.Ip). 28 | Like("name", condition.Name). 29 | In("code", condition.Codes) 30 | 31 | if condition.Ids != "" { 32 | // ,分割id转为id数组 33 | qd.In("id", collx.ArrayMap[string, uint64](strings.Split(condition.Ids, ","), func(val string) uint64 { 34 | id, _ := strconv.Atoi(val) 35 | return uint64(id) 36 | })) 37 | } 38 | 39 | return gormx.PageQuery(qd, pageParam, toEntity) 40 | } 41 | -------------------------------------------------------------------------------- /server/internal/redis/domain/entity/redis.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "mayfly-go/internal/common/utils" 5 | "mayfly-go/internal/redis/rdm" 6 | "mayfly-go/pkg/model" 7 | "mayfly-go/pkg/utils/structx" 8 | ) 9 | 10 | type Redis struct { 11 | model.Model 12 | 13 | Code string `orm:"column(code)" json:"code"` 14 | Name string `orm:"column(name)" json:"name"` 15 | Host string `orm:"column(host)" json:"host"` 16 | Mode string `json:"mode"` 17 | Username string `json:"username"` 18 | Password string `orm:"column(password)" json:"-"` 19 | Db string `orm:"column(database)" json:"db"` 20 | SshTunnelMachineId int `orm:"column(ssh_tunnel_machine_id)" json:"sshTunnelMachineId"` // ssh隧道机器id 21 | Remark string 22 | } 23 | 24 | func (r *Redis) PwdEncrypt() { 25 | // 密码替换为加密后的密码 26 | r.Password = utils.PwdAesEncrypt(r.Password) 27 | } 28 | 29 | func (r *Redis) PwdDecrypt() { 30 | // 密码替换为解密后的密码 31 | r.Password = utils.PwdAesDecrypt(r.Password) 32 | } 33 | 34 | // 转换为redisInfo进行连接 35 | func (re *Redis) ToRedisInfo(db int, tagPath ...string) *rdm.RedisInfo { 36 | redisInfo := new(rdm.RedisInfo) 37 | structx.Copy(redisInfo, re) 38 | redisInfo.Db = db 39 | redisInfo.TagPath = tagPath 40 | return redisInfo 41 | } 42 | -------------------------------------------------------------------------------- /mayfly_go_web/src/components/contextmenu/index.ts: -------------------------------------------------------------------------------- 1 | import Contextmenu from './index.vue'; 2 | 3 | class ContextmenuItem { 4 | clickId: any; 5 | 6 | txt: string; 7 | 8 | icon: string; 9 | 10 | affix: boolean; 11 | 12 | permission: string; 13 | 14 | /** 15 | * 是否隐藏回调函数 16 | */ 17 | hideFunc: (data: any) => boolean; 18 | 19 | onClickFunc: (data: any) => void; 20 | 21 | constructor(clickId: any, txt: string) { 22 | this.clickId = clickId; 23 | this.txt = txt; 24 | } 25 | 26 | withIcon(icon: string) { 27 | this.icon = icon; 28 | return this; 29 | } 30 | 31 | withPermission(permission: string) { 32 | this.permission = permission; 33 | return this; 34 | } 35 | 36 | withHideFunc(func: (data: any) => boolean) { 37 | this.hideFunc = func; 38 | return this; 39 | } 40 | 41 | withOnClick(func: (data: any) => void) { 42 | this.onClickFunc = func; 43 | return this; 44 | } 45 | 46 | /** 47 | * 是否隐藏 48 | * @param data 点击数据项 49 | * @returns 50 | */ 51 | isHide(data: any) { 52 | if (this.hideFunc) { 53 | return this.hideFunc(data); 54 | } 55 | return false; 56 | } 57 | } 58 | 59 | export { Contextmenu, ContextmenuItem }; 60 | -------------------------------------------------------------------------------- /mayfly_go_web/src/views/ops/machine/enums.ts: -------------------------------------------------------------------------------- 1 | import { EnumValue } from '@/common/Enum'; 2 | 3 | // 脚本执行结果类型 4 | export const ScriptResultEnum = { 5 | Result: EnumValue.of(1, '有结果'), 6 | NoResult: EnumValue.of(2, '无结果'), 7 | RealTime: EnumValue.of(3, '实时交互'), 8 | }; 9 | 10 | // 脚本类型 11 | export const ScriptTypeEnum = { 12 | Private: EnumValue.of(1, '私有'), 13 | Public: EnumValue.of(2, '公共'), 14 | }; 15 | 16 | // 文件类型枚举 17 | export const FileTypeEnum = { 18 | Directory: EnumValue.of(1, '目录'), 19 | File: EnumValue.of(2, '文件'), 20 | }; 21 | 22 | // 授权凭证认证方式枚举 23 | export const AuthMethodEnum = { 24 | Password: EnumValue.of(1, '密码').tagTypeSuccess(), 25 | PrivateKey: EnumValue.of(2, '秘钥'), 26 | }; 27 | 28 | // 计划任务状态 29 | export const CronJobStatusEnum = { 30 | Enable: EnumValue.of(1, '启用').tagTypeSuccess(), 31 | Disable: EnumValue.of(-1, '禁用').tagTypeDanger(), 32 | }; 33 | 34 | // 计划任务保存执行结果类型 35 | export const CronJobSaveExecResTypeEnum = { 36 | No: EnumValue.of(-1, '不记录').tagTypeDanger(), 37 | OnError: EnumValue.of(1, '错误时记录').tagTypeWarning(), 38 | Yes: EnumValue.of(2, '记录').tagTypeSuccess(), 39 | }; 40 | 41 | // 计划任务执行记录状态 42 | export const CronJobExecStatusEnum = { 43 | Error: EnumValue.of(-1, '错误').tagTypeDanger(), 44 | Success: EnumValue.of(1, '成功').tagTypeSuccess(), 45 | }; 46 | -------------------------------------------------------------------------------- /server/internal/sys/domain/entity/role.go: -------------------------------------------------------------------------------- 1 | package entity 2 | 3 | import ( 4 | "mayfly-go/pkg/model" 5 | "time" 6 | ) 7 | 8 | const ( 9 | RoleTypeCommon int = 1 // 公共角色类型 10 | RoleTypeSpecial int = 2 // 特殊角色类型 11 | ) 12 | 13 | type Role struct { 14 | model.Model 15 | Status int `json:"status"` // 1:可用;-1:不可用 16 | Name string `json:"name"` 17 | Remark string `json:"remark"` 18 | Code string `json:"code"` 19 | Type int `json:"type"` 20 | } 21 | 22 | func (a *Role) TableName() string { 23 | return "t_sys_role" 24 | } 25 | 26 | // 角色资源 27 | type RoleResource struct { 28 | model.DeletedModel 29 | 30 | RoleId uint64 `json:"roleId"` 31 | ResourceId uint64 `json:"resourceId"` 32 | CreateTime *time.Time `json:"createTime"` 33 | CreatorId uint64 `json:"creatorId"` 34 | Creator string `json:"creator"` 35 | } 36 | 37 | func (a *RoleResource) TableName() string { 38 | return "t_sys_role_resource" 39 | } 40 | 41 | // 账号角色 42 | type AccountRole struct { 43 | model.DeletedModel 44 | 45 | AccountId uint64 `json:"accountId"` 46 | RoleId uint64 `json:"roleId"` 47 | CreateTime *time.Time `json:"createTime"` 48 | CreatorId uint64 `json:"creatorId"` 49 | Creator string `json:"creator"` 50 | } 51 | 52 | func (a *AccountRole) TableName() string { 53 | return "t_sys_account_role" 54 | } 55 | -------------------------------------------------------------------------------- /server/pkg/req/token.go: -------------------------------------------------------------------------------- 1 | package req 2 | 3 | import ( 4 | "errors" 5 | "mayfly-go/pkg/config" 6 | "time" 7 | 8 | "github.com/golang-jwt/jwt/v5" 9 | ) 10 | 11 | // 创建用户token 12 | func CreateToken(userId uint64, username string) (string, error) { 13 | // 带权限创建令牌 14 | // 设置有效期,过期需要重新登录获取token 15 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ 16 | "id": userId, 17 | "username": username, 18 | "exp": time.Now().Add(time.Minute * time.Duration(config.Conf.Jwt.ExpireTime)).Unix(), 19 | }) 20 | 21 | // 使用自定义字符串加密 and get the complete encoded token as a string 22 | tokenString, err := token.SignedString([]byte(config.Conf.Jwt.Key)) 23 | if err != nil { 24 | return "", err 25 | } 26 | return tokenString, nil 27 | } 28 | 29 | // 解析token,并返回登录者账号信息 30 | func ParseToken(tokenStr string) (uint64, string, error) { 31 | if tokenStr == "" { 32 | return 0, "", errors.New("token error") 33 | } 34 | 35 | // Parse token 36 | token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (any, error) { 37 | return []byte(config.Conf.Jwt.Key), nil 38 | }) 39 | if err != nil || token == nil { 40 | return 0, "", err 41 | } 42 | if !token.Valid { 43 | return 0, "", errors.New("token invalid") 44 | } 45 | i := token.Claims.(jwt.MapClaims) 46 | return uint64(i["id"].(float64)), i["username"].(string), nil 47 | } 48 | --------------------------------------------------------------------------------