├── .github └── ISSUE_TEMPLATE │ └── feature_request.md ├── .gitignore ├── README.md ├── api ├── logins │ └── login.go └── system │ ├── apis.go │ ├── ldap.go │ ├── menu.go │ ├── rbac.go │ ├── role.go │ └── user.go ├── cmd └── run.go ├── config-examplate.yaml ├── go.mod ├── go.sum ├── internal ├── model │ ├── public.go │ ├── request │ │ ├── login │ │ │ └── login.go │ │ └── system │ │ │ ├── apis.go │ │ │ ├── menu.go │ │ │ ├── role.go │ │ │ └── user.go │ ├── response │ │ ├── rbac.go │ │ └── user.go │ └── system │ │ ├── apis.go │ │ ├── ldap.go │ │ ├── menu.go │ │ ├── role.go │ │ └── user.go └── router │ ├── routers.go │ └── v1 │ ├── logins │ └── login.go │ └── system │ ├── apis.go │ ├── ldap.go │ ├── menu.go │ ├── rbac.go │ ├── role.go │ └── user.go ├── main.go ├── pkg ├── controller │ ├── login │ │ ├── engine.go │ │ ├── general.go │ │ └── ldap.go │ └── system │ │ ├── apis.go │ │ ├── ldap.go │ │ ├── menu.go │ │ ├── rbac.go │ │ ├── role.go │ │ └── user.go ├── global │ ├── cache.go │ ├── config.go │ ├── error.go │ ├── logger.go │ ├── logo.go │ ├── mysql.go │ ├── rbac.go │ └── response.go ├── middles │ ├── auth.go │ ├── cors.go │ ├── limit.go │ ├── log.go │ └── rbac.go └── utils │ └── aes.go ├── rbac_model.conf └── scripts ├── sys_ldap.sql ├── sys_role.sql └── system_apis.sql /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .git/ 3 | .github 4 | tmp/ 5 | config/application.yaml -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Go Easy Admin

4 |

5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 |

基于Gin+Gorm实现非常简单的脚手架

13 |
14 | 15 | ## 项目介绍 16 | 17 | `go-easy-admin`是一个非常简单的`gin+gorm`脚手架,非常适合学习完`golang`基础的同学来进行练习使用。其中角色、权限都已经设计好,我们只需要关注业务接口即可。 18 | 19 | ## 目录结构 20 | 21 | ```bash 22 | go-easy-admin 23 | 待补充 24 | ``` 25 | 26 | ## 中间件casbin 27 | ```shell 28 | go get github.com/casbin/gorm-adapter/v3 29 | go get github.com/casbin/casbin/v2 30 | ``` 31 | 32 | ## 快速开始 33 | 34 | ### 拉取代码 35 | 36 | ```bash 37 | git clone https://github.com/kubesre/go-easy-admin.git` 38 | `` 39 | 40 | ### 修改配置文件 41 | mv config-example.yaml config.yaml 42 | ```bash 43 | 44 | cat config.yaml 45 | server: 46 | port: 8899 47 | address: 127.0.0.1 48 | name: go-easy-admin 49 | # # 生产环境建议使用 release,debug:可以使用debug模式 50 | model: release 51 | adminUser: admin 52 | adminPwd: 25285442ebc7d3a0c20047e01d341c31 # 密码为 123456 53 | 54 | # 数据库配置 55 | mysql: 56 | DbHost: 127.0.0.1 57 | DbPort: 3306 58 | # 数据库名称 需要提前创建好 59 | DbName: go-easy-admin 60 | DbUser: root 61 | DbPwd: pwd@123456 62 | MaxIdleConns: 10 63 | MaxOpenConns: 100 64 | # 是否开启debug,1 开启 0 关闭 65 | ActiveDebug: 0 66 | 67 | # 密码加密 68 | aes: 69 | key: go-easy-admin 70 | 71 | jwt: 72 | realm: go-easy-admin 73 | # jwt加密因子 74 | key: anruo 75 | # jwt token过期时间 单位为小时 76 | timeout: 100 77 | # jwt token刷新时间 单位为小时 78 | maxRefresh: 1 79 | ``` 80 | 81 | ### 执行MySQL初始化脚本 82 | script/*.sql 83 | 84 | 暂未补充完整 85 | ### 启动服务 86 | 87 | ```bash 88 | go run main.go 89 | ``` 90 | -------------------------------------------------------------------------------- /api/logins/login.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/7 6 | */ 7 | 8 | package logins 9 | 10 | import ( 11 | "github.com/gin-gonic/gin" 12 | 13 | "go-easy-admin/pkg/controller/login" 14 | "go-easy-admin/pkg/global" 15 | ) 16 | 17 | type LoginInterface interface { 18 | //LdapLogin(ctx *gin.Context) 19 | //GeneralLogin(ctx *gin.Context) 20 | GetLoginUserResource(ctx *gin.Context) 21 | } 22 | 23 | type sysLogin struct{} 24 | 25 | func NewSysLogin() LoginInterface { 26 | return &sysLogin{} 27 | } 28 | 29 | //func (sl *sysLogin) LdapLogin(ctx *gin.Context) { 30 | // body := new(reqLogin.ReqLogin) 31 | // if err := ctx.ShouldBindJSON(&body); err != nil { 32 | // global.ReturnContext(ctx).Failed("参数错误", nil) 33 | // return 34 | // } 35 | // if err, data := login.NewSysLogin(ctx).LdapLogin(body); err != nil { 36 | // global.ReturnContext(ctx).Failed("登录失败", nil) 37 | // return 38 | // } else { 39 | // global.ReturnContext(ctx).Successful("登录成功", data) 40 | // } 41 | //} 42 | // 43 | //func (sl *sysLogin) GeneralLogin(ctx *gin.Context) { 44 | // body := new(reqLogin.ReqLogin) 45 | // if err := ctx.ShouldBindJSON(&body); err != nil { 46 | // global.ReturnContext(ctx).Failed("参数错误", nil) 47 | // return 48 | // } 49 | // utils.TagAes(body) 50 | // if err, data := login.NewSysLogin(ctx).GeneralLogin(body); err != nil { 51 | // global.ReturnContext(ctx).Failed("登录失败", nil) 52 | // return 53 | // } else { 54 | // global.ReturnContext(ctx).Successful("登录成功", data) 55 | // } 56 | //} 57 | 58 | func (sl *sysLogin) GetLoginUserResource(ctx *gin.Context) { 59 | var id uint 60 | if v, exists := ctx.Get("id"); exists { 61 | if uv, ok := v.(uint); ok { 62 | id = uv 63 | } 64 | } else { 65 | global.ReturnContext(ctx).Failed("用户未登录", nil) 66 | return 67 | } 68 | 69 | if err, data := login.GetLoginUserResource(int(id), ctx); err != nil { 70 | global.ReturnContext(ctx).Failed("获取失败", nil) 71 | return 72 | } else { 73 | global.ReturnContext(ctx).Successful("获取成功", data) 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /api/system/apis.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/8 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "strconv" 12 | 13 | "github.com/gin-gonic/gin" 14 | 15 | reqSystem "go-easy-admin/internal/model/request/system" 16 | "go-easy-admin/pkg/controller/system" 17 | "go-easy-admin/pkg/global" 18 | ) 19 | 20 | type ApisInterface interface { 21 | Create(ctx *gin.Context) 22 | Delete(ctx *gin.Context) 23 | Update(ctx *gin.Context) 24 | List(ctx *gin.Context) 25 | Get(ctx *gin.Context) 26 | GetApiGroup(ctx *gin.Context) 27 | } 28 | 29 | type sysApis struct{} 30 | 31 | func NewSysApis() ApisInterface { 32 | return &sysApis{} 33 | } 34 | 35 | func (sa *sysApis) Create(ctx *gin.Context) { 36 | body := new(reqSystem.CreateAPIsReq) 37 | if err := ctx.ShouldBindJSON(&body); err != nil { 38 | global.ReturnContext(ctx).Failed("参数错误", err.Error()) 39 | return 40 | } 41 | if err := system.NewSysApis(ctx).Create(body); err != nil { 42 | global.ReturnContext(ctx).Failed("创建失败", err.Error()) 43 | return 44 | } 45 | global.ReturnContext(ctx).Successful("创建成功", nil) 46 | } 47 | 48 | func (sa *sysApis) Delete(ctx *gin.Context) { 49 | id, _ := strconv.Atoi(ctx.Param("id")) 50 | if err := system.NewSysApis(ctx).Delete(id); err != nil { 51 | global.ReturnContext(ctx).Failed("删除失败", err.Error()) 52 | return 53 | } 54 | global.ReturnContext(ctx).Successful("删除成功", nil) 55 | } 56 | 57 | func (sa *sysApis) Update(ctx *gin.Context) { 58 | id, _ := strconv.Atoi(ctx.Param("id")) 59 | body := new(reqSystem.UpdateAPIsReq) 60 | if err := ctx.ShouldBindJSON(&body); err != nil { 61 | global.ReturnContext(ctx).Failed("参数错误", err.Error()) 62 | return 63 | } 64 | if err := system.NewSysApis(ctx).Update(id, body); err != nil { 65 | global.ReturnContext(ctx).Failed("更新失败", err.Error()) 66 | return 67 | } 68 | global.ReturnContext(ctx).Successful("更新成功", nil) 69 | } 70 | 71 | func (sa *sysApis) List(ctx *gin.Context) { 72 | if err, data := system.NewSysApis(ctx).List(); err != nil { 73 | global.ReturnContext(ctx).Failed("查询失败", err.Error()) 74 | return 75 | } else { 76 | global.ReturnContext(ctx).Successful("查询成功", data) 77 | } 78 | } 79 | 80 | func (sa *sysApis) Get(ctx *gin.Context) { 81 | id, _ := strconv.Atoi(ctx.Param("id")) 82 | if err, data := system.NewSysApis(ctx).Get(id); err != nil { 83 | global.ReturnContext(ctx).Failed("查询失败", err.Error()) 84 | return 85 | } else { 86 | global.ReturnContext(ctx).Successful("查询成功", data) 87 | } 88 | 89 | } 90 | func (sa *sysApis) GetApiGroup(ctx *gin.Context) { 91 | if err, data := system.NewSysApis(ctx).GetApiGroup(); err != nil { 92 | global.ReturnContext(ctx).Failed("删除失败", err.Error()) 93 | return 94 | } else { 95 | global.ReturnContext(ctx).Successful("查询成功", data) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /api/system/ldap.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/6 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "github.com/gin-gonic/gin" 12 | 13 | modSys "go-easy-admin/internal/model/system" 14 | "go-easy-admin/pkg/controller/system" 15 | "go-easy-admin/pkg/global" 16 | ) 17 | 18 | type LdapInterface interface { 19 | Create(ctx *gin.Context) 20 | Info(ctx *gin.Context) 21 | Ping(ctx *gin.Context) 22 | } 23 | 24 | type sysLdap struct{} 25 | 26 | func NewSysLdap() LdapInterface { 27 | return &sysLdap{} 28 | } 29 | 30 | func (s *sysLdap) Create(ctx *gin.Context) { 31 | body := new(modSys.Ldap) 32 | if err := ctx.ShouldBindJSON(&body); err != nil { 33 | global.ReturnContext(ctx).Failed("参数错误", err.Error()) 34 | return 35 | } 36 | if err := system.NewSysLdap(ctx).Create(body); err != nil { 37 | global.ReturnContext(ctx).Failed("创建或更新失败", err.Error()) 38 | return 39 | } 40 | global.ReturnContext(ctx).Successful("创建或更新成功", nil) 41 | } 42 | 43 | func (s *sysLdap) Info(ctx *gin.Context) { 44 | err, ldap := system.NewSysLdap(ctx).Info() 45 | if err != nil { 46 | global.ReturnContext(ctx).Failed("查询失败", err.Error()) 47 | return 48 | } 49 | global.ReturnContext(ctx).Successful("查询成功", ldap) 50 | } 51 | 52 | func (s *sysLdap) Ping(ctx *gin.Context) { 53 | body := new(modSys.Ldap) 54 | if err := ctx.ShouldBindJSON(&body); err != nil { 55 | global.ReturnContext(ctx).Failed("参数错误", err.Error()) 56 | return 57 | } 58 | err := system.NewSysLdap(ctx).Ping(body) 59 | if err != nil { 60 | global.ReturnContext(ctx).Failed("连接失败", err.Error()) 61 | return 62 | } 63 | global.ReturnContext(ctx).Successful("连接成功", nil) 64 | } 65 | -------------------------------------------------------------------------------- /api/system/menu.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/5 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "strconv" 12 | 13 | "github.com/gin-gonic/gin" 14 | 15 | reqSystem "go-easy-admin/internal/model/request/system" 16 | "go-easy-admin/pkg/controller/system" 17 | "go-easy-admin/pkg/global" 18 | ) 19 | 20 | type MenuInterface interface { 21 | Create(ctx *gin.Context) 22 | Delete(ctx *gin.Context) 23 | Update(ctx *gin.Context) 24 | List(ctx *gin.Context) 25 | Get(ctx *gin.Context) 26 | } 27 | 28 | type sysMenu struct{} 29 | 30 | func NewSysMenu() MenuInterface { 31 | return &sysMenu{} 32 | } 33 | 34 | func (sm *sysMenu) Create(ctx *gin.Context) { 35 | body := new(reqSystem.CreateMenuReq) 36 | if err := ctx.ShouldBindJSON(&body); err != nil { 37 | global.ReturnContext(ctx).Failed("参数错误", err.Error()) 38 | return 39 | } 40 | if err := system.NewSysMenu(ctx).Create(body); err != nil { 41 | global.ReturnContext(ctx).Failed("创建失败", err.Error()) 42 | return 43 | } 44 | global.ReturnContext(ctx).Successful("创建成功", nil) 45 | } 46 | 47 | func (sm *sysMenu) Delete(ctx *gin.Context) { 48 | id, _ := strconv.Atoi(ctx.Param("id")) 49 | if err := system.NewSysMenu(ctx).Delete(id); err != nil { 50 | global.ReturnContext(ctx).Failed("删除失败", err.Error()) 51 | return 52 | } 53 | global.ReturnContext(ctx).Successful("删除成功", nil) 54 | } 55 | 56 | func (sm *sysMenu) Update(ctx *gin.Context) { 57 | id, _ := strconv.Atoi(ctx.Param("id")) 58 | body := new(reqSystem.CreateMenuReq) 59 | if err := ctx.ShouldBindJSON(&body); err != nil { 60 | global.ReturnContext(ctx).Failed("参数错误", err.Error()) 61 | return 62 | } 63 | if err := system.NewSysMenu(ctx).Update(id, body); err != nil { 64 | global.ReturnContext(ctx).Failed("更新失败", err.Error()) 65 | return 66 | } 67 | global.ReturnContext(ctx).Successful("更新成功", nil) 68 | 69 | } 70 | 71 | func (sm *sysMenu) List(ctx *gin.Context) { 72 | err, menus := system.NewSysMenu(ctx).List() 73 | if err != nil { 74 | global.ReturnContext(ctx).Failed("查询失败", err.Error()) 75 | return 76 | } 77 | global.ReturnContext(ctx).Successful("查询成功", menus) 78 | } 79 | 80 | func (sm *sysMenu) Get(ctx *gin.Context) { 81 | id, _ := strconv.Atoi(ctx.Param("id")) 82 | err, menu := system.NewSysMenu(ctx).Get(id) 83 | if err != nil { 84 | global.ReturnContext(ctx).Failed("查询失败", err.Error()) 85 | return 86 | } 87 | global.ReturnContext(ctx).Successful("查询成功", menu) 88 | 89 | } 90 | -------------------------------------------------------------------------------- /api/system/rbac.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/9 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "strconv" 12 | 13 | "github.com/gin-gonic/gin" 14 | 15 | "go-easy-admin/pkg/controller/system" 16 | "go-easy-admin/pkg/global" 17 | ) 18 | 19 | type SysRBAC interface { 20 | Create(ctx *gin.Context) 21 | GetRbacByRoleID(ctx *gin.Context) 22 | } 23 | type sysRbac struct { 24 | } 25 | 26 | func NewSysRBAC() SysRBAC { 27 | return &sysRbac{} 28 | } 29 | 30 | func (sr *sysRbac) Create(ctx *gin.Context) { 31 | body := new(struct { 32 | ApisID []int `json:"apis_id"` 33 | }) 34 | roleID, _ := strconv.Atoi(ctx.Param("id")) 35 | if err := ctx.ShouldBindJSON(&body); err != nil { 36 | global.ReturnContext(ctx).Failed("参数错误", err.Error()) 37 | return 38 | } 39 | if err := system.NewSysRBAC(ctx).Create(body.ApisID, roleID); err != nil { 40 | global.ReturnContext(ctx).Failed("创建失败", err.Error()) 41 | return 42 | } 43 | global.ReturnContext(ctx).Successful("创建成功", nil) 44 | } 45 | 46 | func (sr *sysRbac) GetRbacByRoleID(ctx *gin.Context) { 47 | roleID, _ := strconv.Atoi(ctx.Param("id")) 48 | roleIDs := system.NewSysRBAC(ctx).GetRbacByRoleID(roleID) 49 | global.ReturnContext(ctx).Successful("获取已经授权角色成功", roleIDs) 50 | } 51 | -------------------------------------------------------------------------------- /api/system/role.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/6 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "strconv" 12 | 13 | "github.com/gin-gonic/gin" 14 | 15 | reqSystem "go-easy-admin/internal/model/request/system" 16 | "go-easy-admin/pkg/controller/system" 17 | "go-easy-admin/pkg/global" 18 | ) 19 | 20 | type RoleInterface interface { 21 | Create(ctx *gin.Context) 22 | Delete(ctx *gin.Context) 23 | Update(ctx *gin.Context) 24 | List(ctx *gin.Context) 25 | Get(ctx *gin.Context) 26 | } 27 | 28 | type sysRole struct{} 29 | 30 | func NewSysRole() RoleInterface { 31 | return &sysRole{} 32 | } 33 | 34 | func (sr *sysRole) Create(ctx *gin.Context) { 35 | body := new(reqSystem.CreateRoleReq) 36 | if err := ctx.ShouldBindJSON(&body); err != nil { 37 | global.ReturnContext(ctx).Failed("参数错误", err.Error()) 38 | return 39 | } 40 | if err := system.NewSysRole(ctx).Create(body); err != nil { 41 | global.ReturnContext(ctx).Failed("创建失败", err.Error()) 42 | return 43 | } 44 | global.ReturnContext(ctx).Successful("创建成功", nil) 45 | 46 | } 47 | 48 | func (sr *sysRole) Delete(ctx *gin.Context) { 49 | id, _ := strconv.Atoi(ctx.Param("id")) 50 | if err := system.NewSysRole(ctx).Delete(id); err != nil { 51 | global.ReturnContext(ctx).Failed("删除失败", err.Error()) 52 | return 53 | } 54 | global.ReturnContext(ctx).Successful("删除成功", nil) 55 | } 56 | 57 | func (sr *sysRole) Update(ctx *gin.Context) { 58 | id, _ := strconv.Atoi(ctx.Param("id")) 59 | body := new(reqSystem.CreateRoleReq) 60 | if err := ctx.ShouldBindJSON(&body); err != nil { 61 | global.ReturnContext(ctx).Failed("参数错误", err.Error()) 62 | return 63 | } 64 | if err := system.NewSysRole(ctx).Update(id, body); err != nil { 65 | global.ReturnContext(ctx).Failed("更新失败", err.Error()) 66 | return 67 | } 68 | global.ReturnContext(ctx).Successful("更新成功", nil) 69 | } 70 | 71 | func (sr *sysRole) List(ctx *gin.Context) { 72 | if err, data := system.NewSysRole(ctx).List(); err != nil { 73 | global.ReturnContext(ctx).Failed("查询失败", err.Error()) 74 | return 75 | } else { 76 | global.ReturnContext(ctx).Successful("查询成功", data) 77 | } 78 | } 79 | 80 | func (sr *sysRole) Get(ctx *gin.Context) { 81 | id, _ := strconv.Atoi(ctx.Param("id")) 82 | if err, data := system.NewSysRole(ctx).Get(id); err != nil { 83 | global.ReturnContext(ctx).Failed("查询失败", err.Error()) 84 | return 85 | } else { 86 | global.ReturnContext(ctx).Successful("查询成功", data) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /api/system/user.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "strconv" 12 | 13 | "github.com/gin-gonic/gin" 14 | 15 | reqSystem "go-easy-admin/internal/model/request/system" 16 | "go-easy-admin/pkg/controller/system" 17 | "go-easy-admin/pkg/global" 18 | "go-easy-admin/pkg/utils" 19 | ) 20 | 21 | type UserInterface interface { 22 | Create(ctx *gin.Context) 23 | Delete(ctx *gin.Context) 24 | Update(ctx *gin.Context) 25 | List(ctx *gin.Context) 26 | Get(ctx *gin.Context) 27 | } 28 | 29 | type sysUser struct{} 30 | 31 | func NewSysUser() UserInterface { 32 | return &sysUser{} 33 | } 34 | 35 | func (su *sysUser) Create(ctx *gin.Context) { 36 | body := new(reqSystem.CreateUserReq) 37 | if err := ctx.ShouldBindJSON(&body); err != nil { 38 | global.ReturnContext(ctx).Failed("参数错误", err.Error()) 39 | return 40 | } 41 | utils.TagAes(body) 42 | if err := system.NewSysUser(ctx).Create(body); err != nil { 43 | global.ReturnContext(ctx).Failed("创建失败", err.Error()) 44 | return 45 | } 46 | global.ReturnContext(ctx).Successful("创建成功", nil) 47 | } 48 | func (su *sysUser) Delete(ctx *gin.Context) { 49 | id, _ := strconv.Atoi(ctx.Param("id")) 50 | if err := system.NewSysUser(ctx).Delete(id); err != nil { 51 | global.ReturnContext(ctx).Failed("删除失败", err.Error()) 52 | return 53 | } 54 | global.ReturnContext(ctx).Successful("删除成功", nil) 55 | } 56 | 57 | func (su *sysUser) Update(ctx *gin.Context) { 58 | id, _ := strconv.Atoi(ctx.Param("id")) 59 | body := new(reqSystem.UpdateUserReq) 60 | if err := ctx.ShouldBindJSON(&body); err != nil { 61 | global.ReturnContext(ctx).Failed("参数错误", err.Error()) 62 | return 63 | } 64 | utils.TagAes(body) 65 | if err := system.NewSysUser(ctx).Update(id, body); err != nil { 66 | global.ReturnContext(ctx).Failed("更新失败", err.Error()) 67 | return 68 | } 69 | global.ReturnContext(ctx).Successful("更新成功", nil) 70 | } 71 | 72 | func (su *sysUser) List(ctx *gin.Context) { 73 | params := new(struct { 74 | UserName string `form:"username"` 75 | Limit int `form:"limit"` 76 | Page int `form:"page"` 77 | }) 78 | if err := ctx.ShouldBindQuery(¶ms); err != nil { 79 | global.ReturnContext(ctx).Failed("参数错误", err.Error()) 80 | return 81 | } 82 | if err, data := system.NewSysUser(ctx).List(params.UserName, params.Limit, params.Page); err != nil { 83 | global.ReturnContext(ctx).Failed("查询失败", err.Error()) 84 | return 85 | } else { 86 | global.ReturnContext(ctx).Successful("查询成功", data) 87 | } 88 | } 89 | 90 | func (su *sysUser) Get(ctx *gin.Context) { 91 | id, _ := strconv.Atoi(ctx.Param("id")) 92 | if err, data := system.NewSysUser(ctx).Get(id); err != nil { 93 | global.ReturnContext(ctx).Failed("查询失败", err.Error()) 94 | return 95 | } else { 96 | global.ReturnContext(ctx).Successful("查询成功", data) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /cmd/run.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package cmd 9 | 10 | import ( 11 | "context" 12 | "fmt" 13 | "log" 14 | "net/http" 15 | "os" 16 | "os/signal" 17 | "syscall" 18 | "time" 19 | 20 | "github.com/spf13/viper" 21 | 22 | "go-easy-admin/internal/router" 23 | "go-easy-admin/pkg/global" 24 | ) 25 | 26 | func Run() { 27 | srv := &http.Server{ 28 | Addr: fmt.Sprintf("%s:%d", viper.GetString("server.address"), 29 | viper.GetInt("server.port")), 30 | Handler: router.RegisterRouters(), 31 | MaxHeaderBytes: 1 << 20, 32 | } 33 | // 打印服务启动参数 34 | // 关闭服务 35 | go func() { 36 | if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { 37 | log.Fatalf("listen: %s\n", err) 38 | } 39 | }() 40 | quit := make(chan os.Signal) 41 | // 获取停止服务信号,kill -9 获取不到 42 | signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) 43 | <-quit 44 | 45 | // 执行延迟停止 46 | ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 47 | defer cancel() 48 | if err := srv.Shutdown(ctx); err != nil { 49 | log.Fatal("server shutdown:", err) 50 | } 51 | 52 | } 53 | 54 | func init() { 55 | global.InitConfig() 56 | global.InitSysTips() 57 | global.InitLog() 58 | global.InitMysql() 59 | global.InitCasbinEnforcer() 60 | } 61 | -------------------------------------------------------------------------------- /config-examplate.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8899 3 | address: 127.0.0.1 4 | name: go-easy-admin 5 | # # 生产环境建议使用 release,debug:可以使用debug模式 6 | model: release 7 | adminUser: admin 8 | adminPwd: 25285442ebc7d3a0c20047e01d341c31 9 | 10 | # 数据库配置 11 | mysql: 12 | DbHost: 127.0.0.1 13 | DbPort: 3306 14 | # 数据库名称 需要提前创建好 15 | DbName: go-easy-admin 16 | DbUser: root 17 | DbPwd: pwd@123456 18 | MaxIdleConns: 10 19 | MaxOpenConns: 100 20 | # 是否开启debug,1 开启 0 关闭 21 | ActiveDebug: 0 22 | 23 | # 密码加密 24 | aes: 25 | key: go-easy-admin 26 | 27 | jwt: 28 | realm: go-easy-admin 29 | # jwt加密因子 30 | key: anruo 31 | # jwt token过期时间 单位为小时 32 | timeout: 100 33 | # jwt token刷新时间 单位为小时 34 | maxRefresh: 1 -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module go-easy-admin 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/appleboy/gin-jwt/v2 v2.9.2 7 | github.com/casbin/casbin/v2 v2.98.0 8 | github.com/casbin/gorm-adapter/v3 v3.26.0 9 | github.com/fsnotify/fsnotify v1.7.0 10 | github.com/gin-gonic/gin v1.10.0 11 | github.com/go-ldap/ldap/v3 v3.4.8 12 | github.com/jinzhu/copier v0.4.0 13 | github.com/juju/ratelimit v1.0.2 14 | github.com/sirupsen/logrus v1.9.3 15 | github.com/spf13/viper v1.19.0 16 | gorm.io/driver/mysql v1.5.7 17 | gorm.io/gorm v1.25.11 18 | ) 19 | 20 | require ( 21 | github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect 22 | github.com/bytedance/sonic v1.11.6 // indirect 23 | github.com/bytedance/sonic/loader v0.1.1 // indirect 24 | github.com/casbin/govaluate v1.2.0 // indirect 25 | github.com/cloudwego/base64x v0.1.4 // indirect 26 | github.com/cloudwego/iasm v0.2.0 // indirect 27 | github.com/dustin/go-humanize v1.0.1 // indirect 28 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 29 | github.com/gin-contrib/sse v0.1.0 // indirect 30 | github.com/glebarez/go-sqlite v1.20.3 // indirect 31 | github.com/glebarez/sqlite v1.7.0 // indirect 32 | github.com/go-asn1-ber/asn1-ber v1.5.5 // indirect 33 | github.com/go-playground/locales v0.14.1 // indirect 34 | github.com/go-playground/universal-translator v0.18.1 // indirect 35 | github.com/go-playground/validator/v10 v10.20.0 // indirect 36 | github.com/go-sql-driver/mysql v1.7.0 // indirect 37 | github.com/goccy/go-json v0.10.2 // indirect 38 | github.com/golang-jwt/jwt/v4 v4.5.0 // indirect 39 | github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect 40 | github.com/golang-sql/sqlexp v0.1.0 // indirect 41 | github.com/google/uuid v1.6.0 // indirect 42 | github.com/hashicorp/hcl v1.0.0 // indirect 43 | github.com/jackc/pgpassfile v1.0.0 // indirect 44 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect 45 | github.com/jackc/pgx/v5 v5.4.3 // indirect 46 | github.com/jinzhu/inflection v1.0.0 // indirect 47 | github.com/jinzhu/now v1.1.5 // indirect 48 | github.com/json-iterator/go v1.1.12 // indirect 49 | github.com/klauspost/cpuid/v2 v2.2.7 // indirect 50 | github.com/leodido/go-urn v1.4.0 // indirect 51 | github.com/magiconair/properties v1.8.7 // indirect 52 | github.com/mattn/go-isatty v0.0.20 // indirect 53 | github.com/microsoft/go-mssqldb v1.6.0 // indirect 54 | github.com/mitchellh/mapstructure v1.5.0 // indirect 55 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 56 | github.com/modern-go/reflect2 v1.0.2 // indirect 57 | github.com/pelletier/go-toml/v2 v2.2.2 // indirect 58 | github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578 // indirect 59 | github.com/sagikazarmark/locafero v0.4.0 // indirect 60 | github.com/sagikazarmark/slog-shim v0.1.0 // indirect 61 | github.com/sourcegraph/conc v0.3.0 // indirect 62 | github.com/spf13/afero v1.11.0 // indirect 63 | github.com/spf13/cast v1.6.0 // indirect 64 | github.com/spf13/pflag v1.0.5 // indirect 65 | github.com/subosito/gotenv v1.6.0 // indirect 66 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 67 | github.com/ugorji/go/codec v1.2.12 // indirect 68 | go.uber.org/atomic v1.9.0 // indirect 69 | go.uber.org/multierr v1.9.0 // indirect 70 | golang.org/x/arch v0.8.0 // indirect 71 | golang.org/x/crypto v0.23.0 // indirect 72 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect 73 | golang.org/x/net v0.25.0 // indirect 74 | golang.org/x/sys v0.20.0 // indirect 75 | golang.org/x/text v0.15.0 // indirect 76 | google.golang.org/protobuf v1.34.1 // indirect 77 | gopkg.in/ini.v1 v1.67.0 // indirect 78 | gopkg.in/yaml.v3 v3.0.1 // indirect 79 | gorm.io/driver/postgres v1.5.7 // indirect 80 | gorm.io/driver/sqlserver v1.5.3 // indirect 81 | gorm.io/plugin/dbresolver v1.3.0 // indirect 82 | modernc.org/libc v1.22.2 // indirect 83 | modernc.org/mathutil v1.5.0 // indirect 84 | modernc.org/memory v1.5.0 // indirect 85 | modernc.org/sqlite v1.20.3 // indirect 86 | ) 87 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.4.0/go.mod h1:ON4tFdPTwRcgWEaVDrN3584Ef+b7GgSJaXxe5fW9t4M= 2 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= 3 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.6.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= 4 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY= 5 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= 6 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= 7 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= 8 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= 9 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.2.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= 10 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= 11 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= 12 | github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0 h1:yfJe15aSwEQ6Oo6J+gdfdulPNoZ3TEhmbhLIoxZcA+U= 13 | github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0/go.mod h1:Q28U+75mpCaSCDowNEmhIo/rmgdkqmkmzI7N6TGR4UY= 14 | github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 h1:T028gtTPiYt/RMUfs8nVsAL7FDQrfLlrm/NnRG/zcC4= 15 | github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0/go.mod h1:cw4zVQgBby0Z5f2v0itn6se2dDP17nTjbZFXW5uPyHA= 16 | github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8= 17 | github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= 18 | github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= 19 | github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0 h1:HCc0+LpPfpCKs6LGGLAhwBARt9632unrVcI6i8s/8os= 20 | github.com/AzureAD/microsoft-authentication-library-for-go v1.1.0/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= 21 | github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= 22 | github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= 23 | github.com/appleboy/gin-jwt/v2 v2.9.2 h1:GeS3lm9mb9HMmj7+GNjYUtpp3V1DAQ1TkUFa5poiZ7Y= 24 | github.com/appleboy/gin-jwt/v2 v2.9.2/go.mod h1:mxGjKt9Lrx9Xusy1SrnmsCJMZG6UJwmdHN9bN27/QDw= 25 | github.com/appleboy/gofight/v2 v2.1.2 h1:VOy3jow4vIK8BRQJoC/I9muxyYlJ2yb9ht2hZoS3rf4= 26 | github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= 27 | github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= 28 | github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= 29 | github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= 30 | github.com/casbin/casbin/v2 v2.98.0 h1:xjsnyQh1hhw5kYTZJTGh4K+pxXhPgYhcr+X7zEbEB4o= 31 | github.com/casbin/casbin/v2 v2.98.0/go.mod h1:G2UyxPbyyrClPvzHQ4Yog6rtTz0x+Y2lc8qOwfqWLuc= 32 | github.com/casbin/gorm-adapter/v3 v3.26.0 h1:4FhoNh6VqTa4CKV/B/LnwVCU073qMAFBEeQ85tlU4cc= 33 | github.com/casbin/gorm-adapter/v3 v3.26.0/go.mod h1:aftWi0cla0CC1bHQVrSFzBcX/98IFK28AvuPppCQgTs= 34 | github.com/casbin/govaluate v1.2.0 h1:wXCXFmqyY+1RwiKfYo3jMKyrtZmOL3kHwaqDyCPOYak= 35 | github.com/casbin/govaluate v1.2.0/go.mod h1:G/UnbIjZk/0uMNaLwZZmFQrR72tYRZWQkO70si/iR7A= 36 | github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= 37 | github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= 38 | github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= 39 | github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= 40 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 41 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 42 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 43 | github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= 44 | github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= 45 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 46 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= 47 | github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= 48 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 49 | github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 50 | github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= 51 | github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= 52 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 53 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 54 | github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= 55 | github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= 56 | github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4= 57 | github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0= 58 | github.com/glebarez/sqlite v1.7.0 h1:A7Xj/KN2Lvie4Z4rrgQHY8MsbebX3NyWsL3n2i82MVI= 59 | github.com/glebarez/sqlite v1.7.0/go.mod h1:PkeevrRlF/1BhQBCnzcMWzgrIk7IOop+qS2jUYLfHhk= 60 | github.com/go-asn1-ber/asn1-ber v1.5.5 h1:MNHlNMBDgEKD4TcKr36vQN68BA00aDfjIt3/bD50WnA= 61 | github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= 62 | github.com/go-ldap/ldap/v3 v3.4.8 h1:loKJyspcRezt2Q3ZRMq2p/0v8iOurlmeXDPw6fikSvQ= 63 | github.com/go-ldap/ldap/v3 v3.4.8/go.mod h1:qS3Sjlu76eHfHGpUdWkAXQTw4beih+cHsco2jXlIXrk= 64 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 65 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 66 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 67 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 68 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 69 | github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= 70 | github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= 71 | github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 72 | github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= 73 | github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= 74 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 75 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 76 | github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 77 | github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= 78 | github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 79 | github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= 80 | github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= 81 | github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= 82 | github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= 83 | github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= 84 | github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= 85 | github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= 86 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 87 | github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 88 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 89 | github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= 90 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 91 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 92 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 93 | github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= 94 | github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= 95 | github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 96 | github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= 97 | github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 98 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 99 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 100 | github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= 101 | github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= 102 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= 103 | github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= 104 | github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY= 105 | github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= 106 | github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= 107 | github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= 108 | github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= 109 | github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= 110 | github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg= 111 | github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= 112 | github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o= 113 | github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= 114 | github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8= 115 | github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= 116 | github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= 117 | github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= 118 | github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= 119 | github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= 120 | github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 121 | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= 122 | github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 123 | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= 124 | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= 125 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 126 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 127 | github.com/juju/ratelimit v1.0.2 h1:sRxmtRiajbvrcLQT7S+JbqU0ntsb9W2yhSdNN8tWfaI= 128 | github.com/juju/ratelimit v1.0.2/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk= 129 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 130 | github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= 131 | github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 132 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 133 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 134 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 135 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 136 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 137 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 138 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 139 | github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= 140 | github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= 141 | github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 142 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 143 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 144 | github.com/microsoft/go-mssqldb v1.6.0 h1:mM3gYdVwEPFrlg/Dvr2DNVEgYFG7L42l+dGc67NNNpc= 145 | github.com/microsoft/go-mssqldb v1.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU= 146 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 147 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 148 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 149 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 150 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 151 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 152 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 153 | github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= 154 | github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= 155 | github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= 156 | github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= 157 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= 158 | github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= 159 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 160 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 161 | github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 162 | github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578 h1:VstopitMQi3hZP0fzvnsLmzXZdQGc4bEcgu24cp+d4M= 163 | github.com/remyoudompheng/bigfft v0.0.0-20230126093431-47fa9a501578/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= 164 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 165 | github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= 166 | github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= 167 | github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= 168 | github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= 169 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 170 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 171 | github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= 172 | github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= 173 | github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= 174 | github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= 175 | github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= 176 | github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 177 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 178 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 179 | github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= 180 | github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= 181 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 182 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 183 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 184 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 185 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 186 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 187 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 188 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 189 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 190 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 191 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 192 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 193 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 194 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 195 | github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= 196 | github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= 197 | github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM= 198 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 199 | github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= 200 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 201 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 202 | github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= 203 | github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 204 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 205 | go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= 206 | go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 207 | go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= 208 | go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= 209 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 210 | golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= 211 | golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 212 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 213 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 214 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 215 | golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= 216 | golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= 217 | golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 218 | golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= 219 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= 220 | golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= 221 | golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= 222 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= 223 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= 224 | golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= 225 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 226 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 227 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 228 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 229 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 230 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 231 | golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 232 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 233 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 234 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 235 | golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= 236 | golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= 237 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 238 | golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= 239 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= 240 | golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= 241 | golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= 242 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= 243 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 244 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 245 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 246 | golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= 247 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 248 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 249 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 250 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 251 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 252 | golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 253 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 254 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 255 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 256 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 257 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 258 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 259 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 260 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 261 | golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 262 | golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= 263 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 264 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 265 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 266 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= 267 | golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= 268 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= 269 | golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= 270 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= 271 | golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= 272 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 273 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 274 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 275 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 276 | golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 277 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 278 | golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= 279 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 280 | golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= 281 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 282 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 283 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 284 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 285 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 286 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= 287 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 288 | google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= 289 | google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 290 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 291 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 292 | gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= 293 | gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 294 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 295 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 296 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 297 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 298 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 299 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 300 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 301 | gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U= 302 | gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= 303 | gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= 304 | gorm.io/driver/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM= 305 | gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA= 306 | gorm.io/driver/sqlserver v1.5.3 h1:rjupPS4PVw+rjJkfvr8jn2lJ8BMhT4UW5FwuJY0P3Z0= 307 | gorm.io/driver/sqlserver v1.5.3/go.mod h1:B+CZ0/7oFJ6tAlefsKoyxdgDCXJKSgwS2bMOQZT0I00= 308 | gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= 309 | gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= 310 | gorm.io/gorm v1.25.7-0.20240204074919-46816ad31dde/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= 311 | gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= 312 | gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= 313 | gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= 314 | gorm.io/plugin/dbresolver v1.3.0 h1:uFDX3bIuH9Lhj5LY2oyqR/bU6pqWuDgas35NAPF4X3M= 315 | gorm.io/plugin/dbresolver v1.3.0/go.mod h1:Pr7p5+JFlgDaiM6sOrli5olekJD16YRunMyA2S7ZfKk= 316 | modernc.org/libc v1.22.2 h1:4U7v51GyhlWqQmwCHj28Rdq2Yzwk55ovjFrdPjs8Hb0= 317 | modernc.org/libc v1.22.2/go.mod h1:uvQavJ1pZ0hIoC/jfqNoMLURIMhKzINIWypNM17puug= 318 | modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= 319 | modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= 320 | modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds= 321 | modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= 322 | modernc.org/sqlite v1.20.3 h1:SqGJMMxjj1PHusLxdYxeQSodg7Jxn9WWkaAQjKrntZs= 323 | modernc.org/sqlite v1.20.3/go.mod h1:zKcGyrICaxNTMEHSr1HQ2GUraP0j+845GYw37+EyT6A= 324 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= 325 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 326 | -------------------------------------------------------------------------------- /internal/model/public.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package model 9 | 10 | import ( 11 | "time" 12 | 13 | "gorm.io/gorm" 14 | ) 15 | 16 | // gorm 基础字段 17 | 18 | type BaseModel struct { 19 | ID uint `gorm:"primarykey" json:"id"` // 主键ID 20 | CreatedAt time.Time // 创建时间 21 | UpdatedAt time.Time // 更新时间 22 | DeletedAt gorm.DeletedAt `gorm:"index"` // 删除时间 23 | CreateBy string `gorm:"column:create_by;comment:'创建来源'" json:"create_by"` 24 | } 25 | -------------------------------------------------------------------------------- /internal/model/request/login/login.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/7 6 | */ 7 | 8 | package login 9 | 10 | type ReqLogin struct { 11 | Username string `json:"username" binding:"required"` 12 | Password string `json:"password" aes:"true" binding:"required"` 13 | } 14 | -------------------------------------------------------------------------------- /internal/model/request/system/apis.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/8 6 | */ 7 | 8 | package system 9 | 10 | type CreateAPIsReq struct { 11 | Path string `json:"path" binding:"required"` 12 | Method string `json:"method" binding:"required"` 13 | Desc string `json:"desc" binding:"required"` 14 | ApiGroup string `json:"apiGroup" binding:"required"` 15 | //Menus []int `json:"menus"` 16 | } 17 | 18 | type UpdateAPIsReq struct { 19 | Path string `json:"path"` 20 | Method string `json:"method"` 21 | Desc string `json:"desc"` 22 | ApiGroup string `json:"apiGroup"` 23 | //Menus []int `json:"menus"` 24 | } 25 | -------------------------------------------------------------------------------- /internal/model/request/system/menu.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/5 6 | */ 7 | 8 | package system 9 | 10 | type CreateMenuReq struct { 11 | Name string `json:"name" binding:"required"` 12 | NameCode string `json:"name_code"` 13 | IsShow uint `json:"is_show"` 14 | Icon string `json:"icon"` 15 | Path string `json:"path" binding:"required"` 16 | Sort int `json:"sort"` 17 | ParentId uint `json:"parent_id"` 18 | Component string `json:"component"` 19 | //APIs []int `json:"apis"` 20 | } 21 | -------------------------------------------------------------------------------- /internal/model/request/system/role.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/6 6 | */ 7 | 8 | package system 9 | 10 | type CreateRoleReq struct { 11 | Name string `json:"name"` 12 | Desc string `json:"desc"` 13 | Status uint `json:"status"` 14 | Users []int `json:"users"` 15 | Menus []int `json:"menus"` 16 | } 17 | -------------------------------------------------------------------------------- /internal/model/request/system/user.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package system 9 | 10 | type CreateUserReq struct { 11 | Username string `json:"userName" binding:"required"` // 用户登录名 12 | Password string `json:"password" aes:"true" binding:"required"` // 用户登录密码 13 | NickName string `json:"nickName" binding:"required"` // 用户昵称 14 | Avatar string `json:"avatar"` 15 | Email string `json:"email" binding:"required,email"` 16 | Phone string `json:"phone" binding:"required"` 17 | Roles []int `json:"roles"` 18 | } 19 | 20 | type UpdateUserReq struct { 21 | Username string `json:"userName"` // 用户登录名 22 | Password string `json:"password" aes:"true"` // 用户登录密码 23 | Avatar string `json:"avatar"` 24 | Status uint `json:"status"` 25 | Email string `json:"email"` 26 | Phone string `json:"phone"` 27 | Roles []int `json:"roles"` 28 | } 29 | -------------------------------------------------------------------------------- /internal/model/response/rbac.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/9 6 | */ 7 | 8 | package response 9 | 10 | type CasbinPolicy struct { 11 | PType string `gorm:"column:ptype" json:"p_type"` 12 | RoleID string `gorm:"column:v0" json:"V0"` 13 | Path string `gorm:"column:v1" json:"V1"` 14 | Method string `gorm:"column:v2" json:"V2"` 15 | Desc string `gorm:"column:v3" json:"V3"` 16 | } 17 | -------------------------------------------------------------------------------- /internal/model/response/user.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/5 6 | */ 7 | 8 | package response 9 | 10 | import ( 11 | "go-easy-admin/internal/model" 12 | "go-easy-admin/internal/model/system" 13 | ) 14 | 15 | type User struct { 16 | model.BaseModel 17 | Username string `json:"userName" gorm:"index;comment:用户登录名"` // 用户登录名 18 | Password string `json:"-" gorm:"comment:用户登录密码"` // 用户登录密码 19 | NickName string `json:"nickName" gorm:"default:系统用户;comment:用户昵称"` // 用户昵称 20 | Avatar string `gorm:"column:avatar;default:https://img1.baidu.com/it/u=2206814125,3628191178&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500;comment:'用户头像';size:128" json:"avatar"` 21 | Email string `gorm:"column:email;comment:'邮箱';size:128" json:"email"` 22 | Phone string `gorm:"column:phone;comment:'手机号码';size:11" json:"phone"` 23 | Status uint `gorm:"type:tinyint(1);default:1;comment:'用户状态(正常/禁用, 默认正常)'" json:"status"` 24 | Roles []Role `gorm:"many2many:sys_user_role;" json:"roles" copier:"-"` 25 | } 26 | 27 | type Role struct { 28 | model.BaseModel 29 | Name string `gorm:"column:name;comment:'角色名称';size:128" json:"name"` 30 | Desc string `gorm:"column:desc;comment:'角色描述';size:128" json:"desc"` 31 | Status uint `gorm:"type:tinyint(1);default:1;comment:'用户状态(正常/禁用, 默认正常)'" json:"status"` 32 | Users []User `gorm:"many2many:sys_user_role;" json:"users"` 33 | Menus []Menu `gorm:"many2many:sys_role_menu;" json:"menus"` 34 | } 35 | type Menu struct { 36 | model.BaseModel 37 | Name string `gorm:"comment:'菜单名称';size:64" json:"name"` 38 | NameCode string `gorm:"column:name_code;comment:'前端路径name';size:64" json:"name_code"` 39 | IsShow uint `gorm:"column:is_show;type:tinyint(1);default:2;comment:'状态(1隐藏/2显示, 默认正常)'" json:"is_show"` 40 | Icon string `gorm:"comment:'菜单图标';size:64" json:"icon"` 41 | Path string `gorm:"comment:'菜单访问路径';size:64" json:"path"` 42 | Sort int `gorm:"default:0;type:int(3);comment:'菜单顺序(同级菜单, 从0开始, 越小显示越靠前)'" json:"sort"` 43 | ParentId uint `gorm:"default:0;comment:'父菜单编号(编号为0时表示根菜单)'" json:"parent_id"` 44 | Component string `gorm:"comment:'前端路径';size:64" json:"component"` 45 | Children []Menu `gorm:"-" json:"children" copier:"-"` 46 | Roles []Role `gorm:"many2many:sys_role_menu;" json:"roles" copier:"-"` 47 | APIs []APIs `gorm:"many2many:sys_menu_api;" json:"apis" copier:"-"` 48 | } 49 | 50 | type APIs struct { 51 | model.BaseModel 52 | Path string `json:"path" binding:"required"` 53 | Method string `json:"method" binding:"required"` 54 | Desc string `json:"desc" binding:"required"` 55 | MenuId uint64 `gorm:"default:1;comment:'菜单外键'" json:"menu_id"` 56 | Menu Menu `gorm:"foreignkey:MenuId" json:"menu"` 57 | Menus []Menu `gorm:"many2many:sys_menu_api;" json:"menus"` 58 | } 59 | 60 | type LoginUser struct { 61 | model.BaseModel 62 | Username string `json:"userName" gorm:"index;comment:用户登录名"` // 用户登录名 63 | Password string `json:"-" gorm:"comment:用户登录密码"` // 用户登录密码 64 | NickName string `json:"nickName" gorm:"default:系统用户;comment:用户昵称"` // 用户昵称 65 | Avatar string `gorm:"column:avatar;default:https://img1.baidu.com/it/u=2206814125,3628191178&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500;comment:'用户头像';size:128" json:"avatar"` 66 | Email string `gorm:"column:email;comment:'邮箱';size:128" json:"email"` 67 | Phone string `gorm:"column:phone;comment:'手机号码';size:11" json:"phone"` 68 | Status uint `gorm:"type:tinyint(1);default:1;comment:'用户状态(正常/禁用, 默认正常)'" json:"status"` 69 | Roles []uint `gorm:"many2many:sys_user_role;" json:"roles" copier:"-"` 70 | Menus []system.Menu `gorm:"-" json:"menus" copier:"-"` 71 | } 72 | -------------------------------------------------------------------------------- /internal/model/system/apis.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package system 9 | 10 | import "go-easy-admin/internal/model" 11 | 12 | type APIs struct { 13 | model.BaseModel 14 | Path string `gorm:"not null;unique" json:"path" binding:"required"` 15 | Method string `json:"method" binding:"required"` 16 | Desc string `json:"desc" binding:"required"` 17 | ApiGroup string `json:"apiGroup" binding:"required"` 18 | //Menus []Menu `gorm:"many2many:sys_menu_api;" json:"menus"` 19 | } 20 | 21 | func (*APIs) TableName() string { 22 | return "system_apis" 23 | } 24 | -------------------------------------------------------------------------------- /internal/model/system/ldap.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/6 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "encoding/json" 12 | "errors" 13 | 14 | "database/sql/driver" 15 | 16 | "go-easy-admin/internal/model" 17 | ) 18 | 19 | // Ldap 用户登录ldap配置 20 | 21 | type JSON []byte 22 | 23 | func (j *JSON) Scan(value interface{}) error { 24 | if value == nil { 25 | *j = nil 26 | return nil 27 | } 28 | s, ok := value.([]byte) 29 | if !ok { 30 | return errors.New("invalid Scan Source") 31 | } 32 | *j = append((*j)[0:0], s...) 33 | return nil 34 | } 35 | 36 | func (j JSON) Value() (driver.Value, error) { 37 | if len(j) == 0 { 38 | return nil, nil 39 | } 40 | return string(j), nil 41 | } 42 | 43 | func (j JSON) MarshalJSON() ([]byte, error) { 44 | if j == nil { 45 | return []byte("null"), nil 46 | } 47 | return j, nil 48 | } 49 | 50 | func (j *JSON) UnmarshalJSON(data []byte) error { 51 | if j == nil { 52 | return errors.New("null point exception") 53 | } 54 | *j = append((*j)[0:0], data...) 55 | return nil 56 | } 57 | 58 | func (j *JSON) UnmarshalToJSON(i interface{}) error { 59 | err := json.Unmarshal(*j, i) 60 | return err 61 | } 62 | 63 | type Ldap struct { 64 | model.BaseModel 65 | Address string `gorm:"column:address;type:varchar(256);not null" json:"address"` 66 | DN string `gorm:"column:dn" json:"dn"` 67 | AdminUser string `gorm:"column:admin_user;not null" json:"admin_user"` 68 | Password string `gorm:"column:password" json:"password"` 69 | OU string `gorm:"column:ou" json:"ou"` 70 | Filter string `gorm:"column:filter;not null" json:"filter"` 71 | Mapping JSON `gorm:"column:mapping;type:json;comment:'属性映射'" json:"mapping"` 72 | SSL uint `gorm:"type:tinyint(1);default:2;comment:'状态(正常/禁用, 默认禁用)'" json:"ssl"` 73 | Status uint `gorm:"type:tinyint(1);default:2;comment:'状态(正常/禁用, 默认禁用)'" json:"status"` 74 | } 75 | 76 | func (*Ldap) TableName() string { 77 | return "sys_ldap" 78 | } 79 | -------------------------------------------------------------------------------- /internal/model/system/menu.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package system 9 | 10 | import "go-easy-admin/internal/model" 11 | 12 | type Menu struct { 13 | model.BaseModel 14 | Name string `gorm:"comment:'菜单名称';size:64" json:"name"` 15 | NameCode string `gorm:"column:name_code;comment:'前端路径name';size:64" json:"name_code"` 16 | IsShow uint `gorm:"column:is_show;type:tinyint(1);default:2;comment:'状态(1隐藏/2显示, 默认正常)'" json:"is_show"` 17 | Icon string `gorm:"comment:'菜单图标';size:64" json:"icon"` 18 | Path string `gorm:"comment:'菜单访问路径';size:64" json:"path"` 19 | Sort int `gorm:"default:0;type:int(3);comment:'菜单顺序(同级菜单, 从0开始, 越小显示越靠前)'" json:"sort"` 20 | ParentId uint `gorm:"default:0;comment:'父菜单编号(编号为0时表示根菜单)'" json:"parent_id"` 21 | Component string `gorm:"comment:'前端路径';size:64" json:"component"` 22 | Children []Menu `gorm:"-" json:"children" copier:"-"` 23 | Roles []Role `gorm:"many2many:sys_role_menu;" json:"-" copier:"-"` 24 | //APIs []APIs `gorm:"many2many:sys_menu_api;" json:"apis" copier:"-"` 25 | } 26 | 27 | func (*Menu) TableName() string { 28 | return "sys_menu" 29 | } 30 | -------------------------------------------------------------------------------- /internal/model/system/role.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package system 9 | 10 | import "go-easy-admin/internal/model" 11 | 12 | type Role struct { 13 | model.BaseModel 14 | Name string `gorm:"column:name;comment:'角色名称';size:128" json:"name"` 15 | Desc string `gorm:"column:desc;comment:'角色描述';size:128" json:"desc"` 16 | Status uint `gorm:"type:tinyint(1);default:1;comment:'用户状态(正常/禁用, 默认正常)'" json:"status"` 17 | Users []User `gorm:"many2many:sys_user_role;" json:"users" copier:"-"` 18 | Menus []Menu `gorm:"many2many:sys_role_menu;" json:"menus" copier:"-"` 19 | } 20 | 21 | func (*Role) TableName() string { 22 | return "sys_role" 23 | } 24 | -------------------------------------------------------------------------------- /internal/model/system/user.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "go-easy-admin/internal/model" 12 | ) 13 | 14 | type User struct { 15 | model.BaseModel 16 | Username string `json:"userName" gorm:"index;not null;unique;comment:用户登录名"` // 用户登录名 17 | Password string `json:"-" gorm:"comment:用户登录密码"` // 用户登录密码 18 | NickName string `json:"nickName" gorm:"default:系统用户;comment:用户昵称"` // 用户昵称 19 | Avatar string `gorm:"column:avatar;comment:'用户头像';type:longtext" json:"avatar"` 20 | Email string `gorm:"column:email;comment:'邮箱';size:128" json:"email"` 21 | Phone string `gorm:"column:phone;comment:'手机号码';size:11" json:"phone"` 22 | Status uint `gorm:"type:tinyint(1);default:1;comment:'用户状态(正常/禁用, 默认正常)'" json:"status"` 23 | Roles []Role `gorm:"many2many:sys_user_role;" json:"roles" copier:"-"` 24 | } 25 | 26 | func (*User) TableName() string { 27 | return "sys_user" 28 | } 29 | -------------------------------------------------------------------------------- /internal/router/routers.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | // 路由 9 | 10 | package router 11 | 12 | import ( 13 | "github.com/gin-gonic/gin" 14 | 15 | routerLogin "go-easy-admin/internal/router/v1/logins" 16 | routerSys "go-easy-admin/internal/router/v1/system" 17 | "go-easy-admin/pkg/middles" 18 | ) 19 | 20 | func RegisterRouters() *gin.Engine { 21 | r := gin.New() 22 | r.Use(middles.LogHandlerFunc(), gin.Recovery(), middles.Cors()) 23 | authMiddleware, err := middles.InitAuth() 24 | if err != nil { 25 | panic(err) 26 | } 27 | // 健康检查 28 | r.GET("/health", func(ctx *gin.Context) { 29 | return 30 | }) 31 | PrivateGroup := r.Group("") 32 | PrivateGroup.Use(authMiddleware.MiddlewareFunc(), middles.RbacMiddle()) 33 | //PrivateGroup.Use(authMiddleware.MiddlewareFunc()) 34 | { 35 | UserGroup := PrivateGroup.Group("/sys/user") 36 | { 37 | routerSys.User(UserGroup, authMiddleware) 38 | } 39 | MenuGroup := PrivateGroup.Group("/sys/menu") 40 | { 41 | routerSys.Menu(MenuGroup) 42 | } 43 | RoleGroup := PrivateGroup.Group("/sys/role") 44 | { 45 | routerSys.Role(RoleGroup) 46 | } 47 | ApisGroup := PrivateGroup.Group("/sys/apis") 48 | { 49 | routerSys.Apis(ApisGroup) 50 | } 51 | LdapGroup := PrivateGroup.Group("/sys/ldap") 52 | { 53 | routerSys.Ldap(LdapGroup) 54 | } 55 | RbacGroup := PrivateGroup.Group("/sys/rbac") 56 | { 57 | routerSys.RBAC(RbacGroup) 58 | } 59 | LoginUserResource := PrivateGroup.Group("/sys/login") 60 | { 61 | routerLogin.Resource(LoginUserResource) 62 | } 63 | } 64 | 65 | PublicGroup := r.Group("") 66 | { 67 | LoginGroup := PublicGroup.Group("/sys/login") 68 | { 69 | routerLogin.Login(LoginGroup, authMiddleware) 70 | } 71 | } 72 | 73 | r.NoRoute(func(ctx *gin.Context) { 74 | return 75 | }) 76 | return r 77 | } 78 | -------------------------------------------------------------------------------- /internal/router/v1/logins/login.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/7 6 | */ 7 | 8 | package logins 9 | 10 | import ( 11 | jwt "github.com/appleboy/gin-jwt/v2" 12 | "github.com/gin-gonic/gin" 13 | 14 | apiLogin "go-easy-admin/api/logins" 15 | ) 16 | 17 | func Login(r *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) gin.IRoutes { 18 | { 19 | r.POST("/ldap", authMiddleware.LoginHandler) 20 | r.POST("/general", authMiddleware.LoginHandler) 21 | } 22 | return r 23 | } 24 | 25 | func Resource(r *gin.RouterGroup) gin.IRoutes { 26 | { 27 | r.GET("/info", apiLogin.NewSysLogin().GetLoginUserResource) 28 | } 29 | return r 30 | } 31 | -------------------------------------------------------------------------------- /internal/router/v1/system/apis.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/8 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "github.com/gin-gonic/gin" 12 | 13 | apiSystem "go-easy-admin/api/system" 14 | ) 15 | 16 | func Apis(r *gin.RouterGroup) gin.IRoutes { 17 | { 18 | r.POST("/create", apiSystem.NewSysApis().Create) 19 | r.POST("/delete/:id", apiSystem.NewSysApis().Delete) 20 | r.POST("/update/:id", apiSystem.NewSysApis().Update) 21 | r.GET("/list", apiSystem.NewSysApis().List) 22 | r.GET("/get/:id", apiSystem.NewSysApis().Get) 23 | r.GET("/get/group", apiSystem.NewSysApis().GetApiGroup) 24 | } 25 | return r 26 | } 27 | -------------------------------------------------------------------------------- /internal/router/v1/system/ldap.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/6 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "github.com/gin-gonic/gin" 12 | 13 | apiSystem "go-easy-admin/api/system" 14 | ) 15 | 16 | func Ldap(r *gin.RouterGroup) gin.IRoutes { 17 | { 18 | r.POST("/create", apiSystem.NewSysLdap().Create) 19 | r.GET("/info", apiSystem.NewSysLdap().Info) 20 | r.POST("/ping", apiSystem.NewSysLdap().Ping) 21 | } 22 | return r 23 | } 24 | -------------------------------------------------------------------------------- /internal/router/v1/system/menu.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/5 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "github.com/gin-gonic/gin" 12 | 13 | apiSystem "go-easy-admin/api/system" 14 | ) 15 | 16 | func Menu(r *gin.RouterGroup) gin.IRoutes { 17 | { 18 | r.POST("/create", apiSystem.NewSysMenu().Create) 19 | r.POST("/delete/:id", apiSystem.NewSysMenu().Delete) 20 | r.POST("/update/:id", apiSystem.NewSysMenu().Update) 21 | r.GET("/list", apiSystem.NewSysMenu().List) 22 | r.GET("/get/:id", apiSystem.NewSysMenu().Get) 23 | } 24 | return r 25 | } 26 | -------------------------------------------------------------------------------- /internal/router/v1/system/rbac.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/9 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "github.com/gin-gonic/gin" 12 | 13 | apiSystem "go-easy-admin/api/system" 14 | ) 15 | 16 | func RBAC(r *gin.RouterGroup) gin.IRoutes { 17 | { 18 | r.POST("/create/:id", apiSystem.NewSysRBAC().Create) 19 | r.GET("/role/get/:id", apiSystem.NewSysRBAC().GetRbacByRoleID) 20 | } 21 | return r 22 | } 23 | -------------------------------------------------------------------------------- /internal/router/v1/system/role.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/6 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "github.com/gin-gonic/gin" 12 | 13 | apiSystem "go-easy-admin/api/system" 14 | ) 15 | 16 | func Role(r *gin.RouterGroup) gin.IRoutes { 17 | { 18 | r.POST("/create", apiSystem.NewSysRole().Create) 19 | r.POST("/delete/:id", apiSystem.NewSysRole().Delete) 20 | r.POST("/update/:id", apiSystem.NewSysRole().Update) 21 | r.GET("/list", apiSystem.NewSysRole().List) 22 | r.GET("/get/:id", apiSystem.NewSysRole().Get) 23 | } 24 | return r 25 | } 26 | -------------------------------------------------------------------------------- /internal/router/v1/system/user.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | jwt "github.com/appleboy/gin-jwt/v2" 12 | "github.com/gin-gonic/gin" 13 | 14 | apiSystem "go-easy-admin/api/system" 15 | ) 16 | 17 | func User(r *gin.RouterGroup, authMiddleware *jwt.GinJWTMiddleware) gin.IRoutes { 18 | { 19 | r.POST("/create", apiSystem.NewSysUser().Create) 20 | r.POST("/delete/:id", apiSystem.NewSysUser().Delete) 21 | r.POST("/update/:id", apiSystem.NewSysUser().Update) 22 | r.GET("/list", apiSystem.NewSysUser().List) 23 | r.GET("/get/:id", apiSystem.NewSysUser().Get) 24 | r.POST("/logout", authMiddleware.LogoutHandler) 25 | r.POST("/refresh", authMiddleware.RefreshHandler) 26 | } 27 | return r 28 | } 29 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "go-easy-admin/cmd" 4 | 5 | func main() { 6 | cmd.Run() 7 | } 8 | -------------------------------------------------------------------------------- /pkg/controller/login/engine.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/7 6 | */ 7 | 8 | package login 9 | 10 | import ( 11 | "context" 12 | 13 | reqLogin "go-easy-admin/internal/model/request/login" 14 | ) 15 | 16 | type SysLogin interface { 17 | LdapLogin(request *reqLogin.ReqLogin) (error, interface{}) 18 | GeneralLogin(request *reqLogin.ReqLogin) (error, interface{}) 19 | } 20 | type sysLogin struct { 21 | ctx context.Context 22 | } 23 | 24 | func NewSysLogin(ctx context.Context) SysLogin { 25 | return &sysLogin{ctx: ctx} 26 | } 27 | -------------------------------------------------------------------------------- /pkg/controller/login/general.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/7 6 | */ 7 | 8 | /* UserGeneralLogin*/ 9 | 10 | package login 11 | 12 | import ( 13 | "errors" 14 | 15 | reqLogin "go-easy-admin/internal/model/request/login" 16 | "go-easy-admin/pkg/controller/system" 17 | "go-easy-admin/pkg/global" 18 | ) 19 | 20 | func (sl *sysLogin) GeneralLogin(request *reqLogin.ReqLogin) (error, interface{}) { 21 | ok, userInfo := system.NewSysUser(sl.ctx).GetByUsernameAndPwd(request.Username, request.Password) 22 | if ok && userInfo != nil { 23 | return nil, userInfo 24 | } 25 | return global.OtherErr(errors.New("登录失败")), nil 26 | } 27 | -------------------------------------------------------------------------------- /pkg/controller/login/ldap.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/7 6 | */ 7 | 8 | package login 9 | 10 | import ( 11 | "context" 12 | "crypto/tls" 13 | "encoding/json" 14 | "errors" 15 | "fmt" 16 | "github.com/go-ldap/ldap/v3" 17 | "github.com/jinzhu/copier" 18 | 19 | reqLogin "go-easy-admin/internal/model/request/login" 20 | reqSystem "go-easy-admin/internal/model/request/system" 21 | "go-easy-admin/internal/model/response" 22 | modeSystem "go-easy-admin/internal/model/system" 23 | "go-easy-admin/pkg/controller/system" 24 | "go-easy-admin/pkg/global" 25 | ) 26 | 27 | func (sl *sysLogin) LdapLogin(request *reqLogin.ReqLogin) (error, interface{}) { 28 | var ld *ldap.Conn 29 | err, req := system.NewSysLdap(sl.ctx).Get() 30 | if err != nil { 31 | return err, nil 32 | } 33 | if req.SSL == 1 { 34 | ld, err = ldap.DialURL("ldaps://"+req.Address, ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: true})) 35 | } else { 36 | ld, err = ldap.DialURL("ldap://" + req.Address) 37 | } 38 | if err != nil { 39 | return err, nil 40 | } 41 | defer ld.Close() 42 | if ld != nil { 43 | if err = ld.Bind(req.AdminUser, req.Password); err != nil { 44 | return global.OtherErr(errors.New("连接失败" + err.Error())), nil 45 | } 46 | } 47 | searchRequest := ldap.NewSearchRequest(req.DN, ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, 48 | fmt.Sprintf(req.Filter, request.Username), []string{}, nil) 49 | sr, err := ld.Search(searchRequest) 50 | if err != nil { 51 | return err, nil 52 | } 53 | if len(sr.Entries) != 1 { 54 | return global.OtherErr(errors.New("user does not exist or too many entries returned")), nil 55 | } 56 | // ldap普通用户登录 57 | userDN := sr.Entries[0].DN 58 | if err = ld.Bind(userDN, request.Password); err != nil { 59 | return global.OtherErr(errors.New("登录失败"), err.Error()), nil 60 | } 61 | var userMap map[string]interface{} 62 | var createUser reqSystem.CreateUserReq 63 | if err = json.Unmarshal(req.Mapping, &userMap); err != nil { 64 | return global.OtherErr(errors.New("ldap映射关系错误"), err.Error()), nil 65 | } 66 | // 处理映射关系 67 | for k, v := range userMap { 68 | userMap[k] = sr.Entries[0].GetAttributeValue(v.(string)) 69 | } 70 | jsonData, _ := json.Marshal(userMap) 71 | _ = json.Unmarshal(jsonData, &createUser) 72 | // 登录成功,将用户信息入库 73 | ok, userInfo := system.NewSysUser(sl.ctx).GetByUsername(request.Username) 74 | if ok { 75 | _ = system.NewSysUser(sl.ctx).Create(&createUser) 76 | ok, userInfo = system.NewSysUser(sl.ctx).GetByUsername(request.Username) 77 | if !ok && userInfo != nil { 78 | return nil, userInfo 79 | } 80 | } 81 | if userInfo != nil { 82 | return nil, userInfo 83 | } 84 | return errors.New("登录失败"), nil 85 | } 86 | 87 | func GetLoginUserResource(id int, ctx context.Context) (error, interface{}) { 88 | // 基础用户信息 LoginUser 89 | var user modeSystem.User 90 | if err := global.GORM.WithContext(ctx).Model(&modeSystem.User{}). 91 | Preload("Roles"). 92 | Preload("Roles.Menus"). 93 | Where("id = ?", id). 94 | First(&user).Error; err != nil { 95 | global.GeaLogger.Error("查询用户失败: ", err) 96 | return errors.New("查询用户失败"), nil 97 | } 98 | loginUser := new(response.LoginUser) 99 | 100 | if err := copier.Copy(&loginUser, user); err != nil { 101 | return global.OtherErr(errors.New("转换角色数据失败")), nil 102 | } 103 | for i := range user.Roles { 104 | loginUser.Roles = append(loginUser.Roles, user.Roles[i].ID) 105 | } 106 | // 菜单信息 107 | var menus []modeSystem.Menu 108 | for i := range user.Roles { 109 | menus = append(menus, user.Roles[i].Menus...) 110 | } 111 | afterRemoveMenu := removeMenu(menus) 112 | loginUser.Menus = buildMenuTree(afterRemoveMenu, 0) 113 | return nil, loginUser 114 | } 115 | 116 | func removeMenu(menus []modeSystem.Menu) (resMenus []modeSystem.Menu) { 117 | menuList := make(map[uint]modeSystem.Menu, 0) 118 | for _, item := range menus { 119 | if _, ok := menuList[item.ID]; !ok { 120 | menuList[item.ID] = item 121 | } 122 | } 123 | for _, item := range menuList { 124 | resMenus = append(resMenus, item) 125 | } 126 | return resMenus 127 | } 128 | 129 | func buildMenuTree(menus []modeSystem.Menu, parentID uint) []modeSystem.Menu { 130 | var tree []modeSystem.Menu 131 | for _, menu := range menus { 132 | if menu.ParentId == parentID { 133 | menu.Children = buildMenuTree(menus, menu.ID) 134 | tree = append(tree, menu) 135 | } 136 | } 137 | return tree 138 | } 139 | -------------------------------------------------------------------------------- /pkg/controller/system/apis.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/8 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "context" 12 | 13 | "github.com/jinzhu/copier" 14 | 15 | reqSystem "go-easy-admin/internal/model/request/system" 16 | "go-easy-admin/internal/model/system" 17 | "go-easy-admin/pkg/global" 18 | ) 19 | 20 | type SysApis interface { 21 | Create(req *reqSystem.CreateAPIsReq) error 22 | Delete(id int) error 23 | Update(id int, req *reqSystem.UpdateAPIsReq) error 24 | List() (error, interface{}) 25 | Get(id int) (error, *system.APIs) 26 | GetApiGroup() (error, []string) 27 | } 28 | type sysApis struct { 29 | tips string 30 | ctx context.Context 31 | } 32 | 33 | func NewSysApis(ctx context.Context) SysApis { 34 | return &sysApis{ctx: ctx, tips: "路由"} 35 | } 36 | 37 | func (sa *sysApis) Create(req *reqSystem.CreateAPIsReq) error { 38 | api := new(system.APIs) 39 | if err := copier.Copy(&api, req); err != nil { 40 | return global.OtherErr(err, "转换路由数据失败") 41 | } 42 | api.CreateBy = sa.ctx.Value("username").(string) 43 | if err := global.GORM.WithContext(sa.ctx).Create(api).Error; err != nil { 44 | return global.CreateErr(sa.tips, err) 45 | } 46 | return nil 47 | } 48 | 49 | func (sa *sysApis) Delete(id int) error { 50 | err, api := sa.Get(id) 51 | if err != nil { 52 | return err 53 | } 54 | if err = global.GORM.WithContext(sa.ctx).Delete(&api).Error; err != nil { 55 | return global.DeleteErr(sa.tips, err) 56 | } 57 | return NewSysRBAC(sa.ctx).DeleteByAPIsID(id) 58 | } 59 | 60 | func (sa *sysApis) Update(id int, req *reqSystem.UpdateAPIsReq) error { 61 | api := new(system.APIs) 62 | if err := copier.Copy(&api, req); err != nil { 63 | return global.OtherErr(err, "转换路由数据失败") 64 | } 65 | api.CreateBy = sa.ctx.Value("username").(string) 66 | if err := global.GORM.WithContext(sa.ctx).Model(&system.APIs{}).Where("id = ?", id).Updates(api).Error; err != nil { 67 | return global.UpdateErr(sa.tips, err) 68 | } 69 | _, resApi := sa.Get(id) 70 | return NewSysRBAC(sa.ctx).UpdateByAPI(resApi) 71 | 72 | } 73 | 74 | func (sa *sysApis) List() (error, interface{}) { 75 | var resApis []system.APIs 76 | if err := global.GORM.WithContext(sa.ctx).Model(&system.APIs{}). 77 | Find(&resApis).Error; err != nil { 78 | return global.GetErr(sa.tips, err), nil 79 | } 80 | return nil, &resApis 81 | } 82 | 83 | func (sa *sysApis) Get(id int) (error, *system.APIs) { 84 | api := new(system.APIs) 85 | if err := global.GORM.WithContext(sa.ctx).Model(&system.APIs{}).Where("id = ?", id).First(&api).Error; err != nil { 86 | return global.GetErr(sa.tips, err), nil 87 | } 88 | return nil, api 89 | } 90 | 91 | func (sa *sysApis) GetApiGroup() (error, []string) { 92 | var apiGroups []string 93 | if err := global.GORM.WithContext(sa.ctx).Model(&system.APIs{}).Distinct().Pluck("api_group", &apiGroups).Error; err != nil { 94 | return global.GetErr(sa.tips, err), nil 95 | } 96 | return nil, apiGroups 97 | } 98 | -------------------------------------------------------------------------------- /pkg/controller/system/ldap.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/6 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "context" 12 | "crypto/tls" 13 | "errors" 14 | 15 | "github.com/go-ldap/ldap/v3" 16 | "gorm.io/gorm" 17 | 18 | "go-easy-admin/internal/model/system" 19 | "go-easy-admin/pkg/global" 20 | ) 21 | 22 | type SysLdap interface { 23 | Create(req *system.Ldap) error 24 | Info() (error, *system.Ldap) 25 | Get() (error, *system.Ldap) 26 | Ping(req *system.Ldap) error 27 | } 28 | type sysLdap struct { 29 | tips string 30 | ctx context.Context 31 | } 32 | 33 | func NewSysLdap(ctx context.Context) SysLdap { 34 | return &sysLdap{ctx: ctx, tips: "Ldap"} 35 | } 36 | 37 | // 创建或更新 38 | 39 | func (sl *sysLdap) Create(req *system.Ldap) error { 40 | // 先判断是否存在 ,且只能存在一条,多条 以第一条为准 41 | lp := new(system.Ldap) 42 | if err := global.GORM.WithContext(sl.ctx).First(&lp).Error; err != nil { 43 | if errors.Is(err, gorm.ErrRecordNotFound) { 44 | // 创建 45 | req.CreateBy = sl.ctx.Value("username").(string) 46 | if err = global.GORM.WithContext(sl.ctx).Create(&req).Error; err != nil { 47 | return global.CreateErr(sl.tips, err) 48 | } 49 | return nil 50 | } 51 | // 更新 52 | } 53 | if err := global.GORM.WithContext(sl.ctx).Model(&lp).Updates(req).Error; err != nil { 54 | return global.UpdateErr(sl.tips, err) 55 | } 56 | return nil 57 | } 58 | 59 | // 获取 60 | 61 | func (sl *sysLdap) Info() (error, *system.Ldap) { 62 | lp := new(system.Ldap) 63 | if err := global.GORM.WithContext(sl.ctx).First(&lp).Error; err != nil { 64 | return global.GetErr(sl.tips, err), nil 65 | } 66 | lp.Password = "" 67 | return nil, lp 68 | } 69 | 70 | func (sl *sysLdap) Get() (error, *system.Ldap) { 71 | lp := new(system.Ldap) 72 | if err := global.GORM.WithContext(sl.ctx).Where("status = ?", 1).First(&lp).Error; err != nil { 73 | return global.GetErr(sl.tips, err), nil 74 | } 75 | return nil, lp 76 | } 77 | 78 | func (sl *sysLdap) Ping(req *system.Ldap) error { 79 | var ( 80 | ld *ldap.Conn 81 | err error 82 | ) 83 | if req.SSL == 1 { 84 | ld, err = ldap.DialURL("ldaps://"+req.Address, ldap.DialWithTLSConfig(&tls.Config{InsecureSkipVerify: true})) 85 | } else { 86 | ld, err = ldap.DialURL("ldap://" + req.Address) 87 | } 88 | if err != nil { 89 | return err 90 | } 91 | defer ld.Close() 92 | if ld != nil { 93 | if err = ld.Bind(req.DN, req.Password); err != nil { 94 | return global.OtherErr(errors.New("连接失败" + err.Error())) 95 | } 96 | } 97 | return nil 98 | } 99 | -------------------------------------------------------------------------------- /pkg/controller/system/menu.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/5 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "context" 12 | "errors" 13 | 14 | "github.com/jinzhu/copier" 15 | "gorm.io/gorm" 16 | 17 | reqSystem "go-easy-admin/internal/model/request/system" 18 | "go-easy-admin/internal/model/system" 19 | "go-easy-admin/pkg/global" 20 | ) 21 | 22 | type SysMenu interface { 23 | Create(req *reqSystem.CreateMenuReq) error 24 | Delete(id int) error 25 | Update(id int, req *reqSystem.CreateMenuReq) error 26 | List() (error, interface{}) 27 | Get(id int) (error, *system.Menu) 28 | } 29 | type sysMenu struct { 30 | tips string 31 | ctx context.Context 32 | } 33 | 34 | func NewSysMenu(ctx context.Context) SysMenu { 35 | return &sysMenu{ctx: ctx, tips: "菜单"} 36 | } 37 | 38 | func (sm *sysMenu) Create(req *reqSystem.CreateMenuReq) error { 39 | menu := new(system.Menu) 40 | if err := copier.Copy(&menu, req); err != nil { 41 | return global.OtherErr(errors.New("转换菜单数据失败"), err.Error()) 42 | } 43 | menu.CreateBy = sm.ctx.Value("username").(string) 44 | if err := global.GORM.WithContext(sm.ctx).Create(&menu).Error; err != nil { 45 | return global.CreateErr(sm.tips, err) 46 | } 47 | //if len(req.APIs) > 0 { 48 | // return sm.association(menu, req.APIs) 49 | //} 50 | return nil 51 | } 52 | 53 | // 级联删除菜单 54 | 55 | //var MenuSlice []*system.Menu 56 | 57 | func (sm *sysMenu) Delete(id int) error { 58 | menu := new(system.Menu) 59 | if err := global.GORM.WithContext(sm.ctx).Where("id = ?", id).First(&menu).Error; err != nil { 60 | // 菜单不存在 61 | if errors.Is(err, gorm.ErrRecordNotFound) { 62 | return global.NotFoundErr(sm.tips, err) 63 | } 64 | return err 65 | } 66 | // 判断是否存在子级菜单 67 | if hasChildren(menu) { 68 | return global.OtherErr(errors.New("存在子级菜单"), "请先删除子级菜单") 69 | } 70 | if err := global.GORM.WithContext(sm.ctx).Delete(menu).Error; err != nil { 71 | return global.DeleteErr(sm.tips, err) 72 | } 73 | // 清空关联 74 | sm.clear(menu) 75 | return nil 76 | } 77 | 78 | func (sm *sysMenu) Update(id int, req *reqSystem.CreateMenuReq) error { 79 | menu := new(system.Menu) 80 | if err := copier.Copy(&menu, req); err != nil { 81 | return global.OtherErr(errors.New("转换菜单数据失败"), err.Error()) 82 | } 83 | if err := global.GORM.WithContext(sm.ctx).Where("id = ?", id).Updates(&menu).Error; err != nil { 84 | return global.UpdateErr(sm.tips, err) 85 | } 86 | //err, m := sm.Get(id) 87 | //if err != nil { 88 | // return err 89 | //} 90 | //if len(req.APIs) > 0 { 91 | // return sm.association(m, req.APIs) 92 | //} 93 | return nil 94 | } 95 | 96 | func (sm *sysMenu) List() (error, interface{}) { 97 | var menus []system.Menu 98 | if err := global.GORM.WithContext(sm.ctx).Where("parent_id = ?", 0).Order("sort asc").Find(&menus).Error; err != nil { 99 | return global.GetErr(sm.tips, err), nil 100 | } 101 | for i := range menus { 102 | err := GetChildren(&menus[i]) 103 | if err != nil { 104 | return err, nil 105 | } 106 | } 107 | return nil, menus 108 | } 109 | func (sm *sysMenu) Get(id int) (error, *system.Menu) { 110 | var menu system.Menu 111 | if err := global.GORM.WithContext(sm.ctx).Where("id = ?", id).First(&menu).Error; err != nil { 112 | return global.GetErr(sm.tips, err), nil 113 | } 114 | if err := GetChildren(&menu); err != nil { 115 | return err, nil 116 | } 117 | return nil, &menu 118 | } 119 | 120 | // 获取子菜单 121 | 122 | func GetChildren(menu *system.Menu) error { 123 | if err := global.GORM.Where("parent_id = ?", menu.ID). 124 | Find(&menu.Children).Error; err != nil { 125 | return global.OtherErr(errors.New("获取子菜单失败"), err.Error()) 126 | } 127 | //MenuSlice = append(MenuSlice, menu) 128 | for i := range menu.Children { 129 | if err := GetChildren(&menu.Children[i]); err != nil { 130 | return err 131 | } 132 | } 133 | return nil 134 | } 135 | 136 | func hasChildren(menu *system.Menu) bool { 137 | var count int64 138 | if err := global.GORM.Where("parent_id = ?", menu.ID).Count(&count).Error; err != nil { 139 | if errors.Is(err, gorm.ErrRecordNotFound) { 140 | return true 141 | } 142 | return false 143 | } 144 | if count > 0 { 145 | return true 146 | } 147 | return false 148 | } 149 | 150 | func (sm *sysMenu) association(menu *system.Menu, apisIDs []int) error { 151 | var apis []system.APIs 152 | if err := global.GORM.WithContext(sm.ctx).Where("id IN (?)", apisIDs).Find(&apis).Error; err != nil { 153 | return global.OtherErr(errors.New("获取APIs失败"), err.Error()) 154 | } 155 | // 检查是否所有APIs都存在 156 | if len(apisIDs) != len(apis) { 157 | return global.OtherErr(errors.New("部分APIs不存在")) 158 | } 159 | if err := global.GORM.WithContext(sm.ctx).Model(menu).Association("APIs").Replace(apis); err != nil { 160 | return global.OtherErr(errors.New("关联APIs失败: " + err.Error())) 161 | } 162 | return nil 163 | } 164 | 165 | func (sm *sysMenu) clear(menus ...*system.Menu) { 166 | for _, menu := range menus { 167 | //_ = global.GORM.WithContext(sm.ctx).Model(&menu).Association("APIs").Clear() 168 | _ = global.GORM.WithContext(sm.ctx).Model(&menu).Association("Roles").Clear() 169 | } 170 | //MenuSlice = nil 171 | } 172 | -------------------------------------------------------------------------------- /pkg/controller/system/rbac.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/9 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "context" 12 | "fmt" 13 | "strconv" 14 | 15 | gormadapter "github.com/casbin/gorm-adapter/v3" 16 | 17 | "go-easy-admin/internal/model/system" 18 | "go-easy-admin/pkg/global" 19 | ) 20 | 21 | // casbin 权限 22 | 23 | type SysRBAC interface { 24 | Create(apiIDs []int, roleID int) error 25 | GetRbacByRoleID(roleID int) []int 26 | DeleteByAPIsID(apiID int) error 27 | UpdateByAPI(api *system.APIs) error 28 | } 29 | type sysRbac struct { 30 | tips string 31 | ctx context.Context 32 | } 33 | 34 | func NewSysRBAC(ctx context.Context) SysRBAC { 35 | return &sysRbac{ctx: ctx, tips: "权限"} 36 | } 37 | 38 | // 添加权限 39 | 40 | func (sr *sysRbac) Create(apiIDs []int, roleID int) error { 41 | var casbinRules []gormadapter.CasbinRule 42 | for _, id := range apiIDs { 43 | err, api := NewSysApis(sr.ctx).Get(id) 44 | if err != nil { 45 | continue 46 | } 47 | if api.Desc == "" { 48 | api.Desc = "系统添加描述信息" 49 | } 50 | casbinRules = append(casbinRules, gormadapter.CasbinRule{ 51 | Ptype: "p", 52 | V0: fmt.Sprintf("%d", roleID), 53 | V1: api.Path, 54 | V2: api.Method, 55 | V3: api.Desc, 56 | V4: sr.ctx.Value("username").(string), 57 | V5: fmt.Sprintf("%d", api.ID), 58 | }) 59 | } 60 | // 清空角色对应权限 61 | sr.DeleteByRoleID(roleID) 62 | if err := global.GORM.WithContext(sr.ctx).Create(&casbinRules).Error; err != nil { 63 | return err 64 | } 65 | freshRBAC() 66 | return nil 67 | 68 | } 69 | 70 | func (sr *sysRbac) UpdateByAPI(api *system.APIs) error { 71 | var updateCasbinRule = gormadapter.CasbinRule{ 72 | V1: api.Path, 73 | V2: api.Method, 74 | V3: api.Desc, 75 | } 76 | if err := global.GORM.Model(&gormadapter.CasbinRule{}).WithContext(sr.ctx). 77 | Where("v5 = ?", api.ID). 78 | Updates(&updateCasbinRule).Error; err != nil { 79 | return err 80 | } 81 | freshRBAC() 82 | return nil 83 | } 84 | 85 | func (sr *sysRbac) DeleteByRoleID(roleID int) { 86 | // 根据ID查找权限 87 | var casbinRules []gormadapter.CasbinRule 88 | global.GORM.Model(&casbinRules).WithContext(sr.ctx).Where("v0 = ?", roleID).Find(&casbinRules) 89 | if len(casbinRules) < 1 { 90 | return 91 | } 92 | for _, rule := range casbinRules { 93 | _, _ = global.CasbinCacheEnforcer.RemovePolicy(rule.V0, rule.V1, rule.V2, rule.V3, rule.V4) 94 | } 95 | return 96 | } 97 | 98 | func (sr *sysRbac) GetRbacByRoleID(roleID int) []int { 99 | var ( 100 | casbinRule []gormadapter.CasbinRule 101 | apiIDs []int 102 | ) 103 | global.GORM.Model(&casbinRule).WithContext(sr.ctx).Where("v0 = ?", roleID).Find(&casbinRule) 104 | if len(casbinRule) < 1 { 105 | return nil 106 | } 107 | for _, rbac := range casbinRule { 108 | id, _ := strconv.Atoi(rbac.V5) 109 | apiIDs = append(apiIDs, id) 110 | } 111 | return apiIDs 112 | } 113 | 114 | func (sr *sysRbac) DeleteByAPIsID(apiID int) error { 115 | var casbinRule gormadapter.CasbinRule 116 | if err := global.GORM.Model(&casbinRule).WithContext(sr.ctx).Where("v5 = ?", apiID).First(&casbinRule).Error; err != nil { 117 | return err 118 | } 119 | _, err := global.CasbinCacheEnforcer.RemovePolicy(casbinRule.V0, casbinRule.V1, casbinRule.V2, casbinRule.V3, casbinRule.V4, casbinRule.V5) 120 | freshRBAC() 121 | return err 122 | } 123 | 124 | func freshRBAC() { 125 | _ = global.CasbinCacheEnforcer.LoadPolicy() 126 | } 127 | -------------------------------------------------------------------------------- /pkg/controller/system/role.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/6 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "context" 12 | "errors" 13 | "github.com/jinzhu/copier" 14 | "gorm.io/gorm" 15 | 16 | reqSystem "go-easy-admin/internal/model/request/system" 17 | "go-easy-admin/internal/model/system" 18 | "go-easy-admin/pkg/global" 19 | ) 20 | 21 | type SysRole interface { 22 | Create(req *reqSystem.CreateRoleReq) error 23 | Delete(id int) error 24 | Update(id int, req *reqSystem.CreateRoleReq) error 25 | List() (error, interface{}) 26 | Get(id int) (error, *system.Role) 27 | } 28 | type sysRole struct { 29 | tips string 30 | ctx context.Context 31 | } 32 | 33 | func NewSysRole(ctx context.Context) SysRole { 34 | return &sysRole{ctx: ctx, tips: "角色"} 35 | } 36 | 37 | func (sr *sysRole) Create(req *reqSystem.CreateRoleReq) error { 38 | role := new(system.Role) 39 | if err := copier.Copy(&role, req); err != nil { 40 | return global.OtherErr(errors.New("转换角色数据失败")) 41 | } 42 | role.CreateBy = sr.ctx.Value("username").(string) 43 | if err := global.GORM.WithContext(sr.ctx).Create(&role).Error; err != nil { 44 | return global.CreateErr(sr.tips, err) 45 | } 46 | return sr.association(role, req.Users, req.Menus) 47 | } 48 | 49 | func (sr *sysRole) Delete(id int) error { 50 | role := new(system.Role) 51 | if err := global.GORM.WithContext(sr.ctx).First(&role, id).Error; err != nil { 52 | if errors.Is(err, gorm.ErrRecordNotFound) { 53 | return global.NotFoundErr(sr.tips, err) 54 | } 55 | return err 56 | } 57 | if err := global.GORM.WithContext(sr.ctx).Delete(&role, id).Error; err != nil { 58 | return global.DeleteErr(sr.tips, err) 59 | } 60 | sr.clear(role) 61 | return nil 62 | } 63 | 64 | func (sr *sysRole) Update(id int, req *reqSystem.CreateRoleReq) error { 65 | role := new(system.Role) 66 | if err := copier.Copy(&role, req); err != nil { 67 | return global.OtherErr(errors.New("转换角色数据失败")) 68 | } 69 | if err := global.GORM.WithContext(sr.ctx).Where("id = ?", id).Updates(&role).Error; err != nil { 70 | return global.UpdateErr(sr.tips, err) 71 | } 72 | err, r := sr.Get(id) 73 | if err != nil { 74 | return err 75 | } 76 | return sr.association(r, req.Users, req.Menus) 77 | } 78 | 79 | func (sr *sysRole) List() (error, interface{}) { 80 | var resRole []system.Role 81 | if err := global.GORM.WithContext(sr.ctx).Model(&system.Role{}). 82 | Preload("Users").Find(&resRole).Error; err != nil { 83 | return global.GetErr(sr.tips, err), nil 84 | } 85 | return nil, &resRole 86 | } 87 | func (sr *sysRole) Get(id int) (error, *system.Role) { 88 | var roles system.Role 89 | if err := global.GORM.WithContext(sr.ctx).Model(&system.Role{}).Where("id = ?", id).Preload("Users"). 90 | Preload("Menus").First(&roles).Error; err != nil { 91 | return global.GetErr(sr.tips, err), nil 92 | } 93 | return nil, &roles 94 | } 95 | 96 | func (sr *sysRole) association(role *system.Role, userIDs, menuIDs []int) error { 97 | var ( 98 | users []system.User 99 | menus []system.Menu 100 | ) 101 | if len(userIDs) > 0 { 102 | global.GORM.WithContext(sr.ctx).Where("id IN ?", userIDs).Find(&users) 103 | if len(userIDs) != len(users) { 104 | return global.OtherErr(errors.New("部分users不存在")) 105 | } 106 | if err := global.GORM.WithContext(sr.ctx).Model(role).Association("Users").Replace(users); err != nil { 107 | return global.OtherErr(err) 108 | } 109 | } 110 | if len(menuIDs) > 0 { 111 | global.GORM.WithContext(sr.ctx).Where("id IN ?", menuIDs).Find(&menus) 112 | if len(menuIDs) != len(menus) { 113 | return global.OtherErr(errors.New("部分menus不存在")) 114 | } 115 | if err := global.GORM.WithContext(sr.ctx).Model(role).Association("Menus").Replace(menus); err != nil { 116 | return global.OtherErr(err) 117 | } 118 | } 119 | return nil 120 | } 121 | 122 | func (sr *sysRole) clear(roles ...*system.Role) { 123 | for _, role := range roles { 124 | _ = global.GORM.WithContext(sr.ctx).Model(role).Association("Users").Clear() 125 | _ = global.GORM.WithContext(sr.ctx).Model(role).Association("Menus").Clear() 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /pkg/controller/system/user.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package system 9 | 10 | import ( 11 | "context" 12 | "errors" 13 | 14 | "github.com/jinzhu/copier" 15 | "gorm.io/gorm" 16 | 17 | reqSystem "go-easy-admin/internal/model/request/system" 18 | "go-easy-admin/internal/model/system" 19 | "go-easy-admin/pkg/global" 20 | ) 21 | 22 | type SysUser interface { 23 | Create(req *reqSystem.CreateUserReq) error 24 | Delete(id int) error 25 | Update(id int, req *reqSystem.UpdateUserReq) error 26 | List(userName string, limit, page int) (error, interface{}) 27 | Get(id int) (error, *system.User) 28 | GetByUsername(username string) (bool, *system.User) 29 | GetByUsernameAndPwd(username, password string) (bool, *system.User) 30 | } 31 | type sysUser struct { 32 | ctx context.Context 33 | } 34 | 35 | func NewSysUser(ctx context.Context) SysUser { 36 | return &sysUser{ctx: ctx} 37 | } 38 | 39 | func (su *sysUser) Create(req *reqSystem.CreateUserReq) error { 40 | user := new(system.User) 41 | if err := copier.Copy(&user, req); err != nil { 42 | global.GeaLogger.Error("转换用户数据失败: ", err) 43 | return errors.New("转换用户数据失败") 44 | } 45 | if v, ok := su.ctx.Value("username").(string); ok { 46 | user.CreateBy = v 47 | } else { 48 | user.CreateBy = "LDAP" 49 | } 50 | if err := global.GORM.WithContext(su.ctx).Create(user).Error; err != nil { 51 | global.GeaLogger.Error("创建用户失败: ", err) 52 | return errors.New("创建用户失败") 53 | } 54 | // 处理多对多关系 55 | if len(req.Roles) > 0 { 56 | if err := su.association(user, req.Roles); err != nil { 57 | return err 58 | } 59 | } 60 | return nil 61 | } 62 | 63 | func (su *sysUser) Delete(id int) error { 64 | var users []*system.User 65 | if err := global.GORM.WithContext(su.ctx).Where("id = ?", id).First(&users).Error; err != nil { 66 | global.GeaLogger.Error("删除用户失败: ", err) 67 | return errors.New("删除用户失败") 68 | } 69 | su.clear(users...) 70 | if err := global.GORM.WithContext(su.ctx).Delete(&users, id).Error; err != nil { 71 | global.GeaLogger.Error("删除用户失败: ", err) 72 | return errors.New("删除用户失败") 73 | } 74 | return nil 75 | } 76 | 77 | func (su *sysUser) Update(id int, req *reqSystem.UpdateUserReq) error { 78 | user := new(system.User) 79 | err, u := su.Get(id) 80 | if err != nil { 81 | return err 82 | } 83 | if err = copier.Copy(&user, req); err != nil { 84 | global.GeaLogger.Error("转换用户数据失败: ", err) 85 | return errors.New("转换用户数据失败") 86 | } 87 | if err = global.GORM.WithContext(su.ctx).Model(&system.User{}).Where("id = ?", id).Updates(&user).Error; err != nil { 88 | global.GeaLogger.Error("更新用户失败: ", err.Error) 89 | return errors.New("更新用户失败") 90 | } 91 | if len(req.Roles) > 0 { 92 | if err = su.association(u, req.Roles); err != nil { 93 | return err 94 | } 95 | } 96 | return nil 97 | } 98 | 99 | func (su *sysUser) List(userName string, limit, page int) (error, interface{}) { 100 | startSet := (page - 1) * limit 101 | resUser := new(struct { 102 | Items []system.User 103 | Total int64 104 | }) 105 | if err := global.GORM.WithContext(su.ctx).Model(&system.User{}).Where("username LIKE ?", "%"+userName+"%"). 106 | Count(&resUser.Total).Preload("Roles"). 107 | Limit(limit).Offset(startSet).Find(&resUser.Items).Error; err != nil { 108 | global.GeaLogger.Error("获取用户失败: ", err) 109 | return errors.New("获取用户失败"), nil 110 | } 111 | return nil, &resUser 112 | } 113 | 114 | func (su *sysUser) Get(id int) (error, *system.User) { 115 | var user system.User 116 | if err := global.GORM.WithContext(su.ctx).Model(system.User{}). 117 | Preload("Roles"). 118 | Preload("Roles.Menus"). 119 | Where("id = ?", id). 120 | First(&user).Error; err != nil { 121 | global.GeaLogger.Error("查询用户失败: ", err) 122 | return errors.New("查询用户失败"), nil 123 | } 124 | // 获取子菜单 125 | menuList := make([]system.Menu, 0) 126 | for i := range user.Roles { 127 | for j := range user.Roles[i].Menus { 128 | menuList = append(menuList, user.Roles[i].Menus[j]) 129 | } 130 | } 131 | return nil, &user 132 | } 133 | 134 | func (su *sysUser) GetByUsername(username string) (bool, *system.User) { 135 | var user *system.User 136 | if err := global.GORM.WithContext(su.ctx).Model(&system.User{}).Where("username = ?", username).First(&user).Error; err != nil { 137 | if errors.Is(err, gorm.ErrRecordNotFound) { 138 | return true, nil 139 | } 140 | return false, nil 141 | } 142 | return false, user 143 | } 144 | 145 | func (su *sysUser) GetByUsernameAndPwd(username, password string) (bool, *system.User) { 146 | var user *system.User 147 | if err := global.GORM.WithContext(su.ctx).Model(&system.User{}).Where("username = ? AND password = ?", username, password).First(&user).Error; err != nil { 148 | return false, nil 149 | } 150 | return true, user 151 | } 152 | 153 | func (su *sysUser) association(user *system.User, roleIDs []int) error { 154 | var roles []system.Role 155 | if err := global.GORM.WithContext(su.ctx).Where("id IN ?", roleIDs).Find(&roles).Error; err != nil { 156 | global.GeaLogger.Error("查询角色失败: ", err) 157 | return errors.New("查询角色失败") 158 | } 159 | // 检查是否所有传入的角色 ID 都存在 160 | if len(roles) != len(roleIDs) { 161 | global.GeaLogger.Error("部分角色不存在") 162 | return errors.New("部分角色不存在") 163 | } 164 | 165 | if err := global.GORM.WithContext(su.ctx).Model(user).Association("Roles").Replace(roles); err != nil { 166 | global.GeaLogger.Error("关联角色失败: ", err) 167 | return errors.New("关联角色失败") 168 | } 169 | return nil 170 | } 171 | 172 | func (su *sysUser) clear(users ...*system.User) { 173 | for _, user := range users { 174 | err := global.GORM.WithContext(su.ctx).Model(&user).Association("Roles").Clear() 175 | if err != nil { 176 | continue 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /pkg/global/cache.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/7 6 | */ 7 | 8 | package global 9 | 10 | import ( 11 | "sync" 12 | 13 | "github.com/go-ldap/ldap/v3" 14 | ) 15 | 16 | // 缓存相关 17 | 18 | type StoreInterface interface { 19 | } 20 | 21 | type system struct { 22 | LdapConfig *ldap.Conn 23 | } 24 | 25 | type store struct { 26 | rmx sync.RWMutex 27 | systemMap map[string]*system 28 | } 29 | 30 | var ( 31 | instance *store 32 | once sync.Once 33 | ) 34 | 35 | func NewStore() StoreInterface { 36 | once.Do(func() { 37 | instance = &store{ 38 | systemMap: make(map[string]*system), 39 | } 40 | }) 41 | return instance 42 | } 43 | 44 | func (s *store) SetCache(name string, system *system) { 45 | s.rmx.Lock() 46 | defer s.rmx.Unlock() 47 | s.systemMap[name] = system 48 | } 49 | 50 | func (s *store) DelCache(name string) bool { 51 | s.rmx.Lock() 52 | defer s.rmx.Unlock() 53 | if _, ok := s.systemMap[name]; !ok { 54 | return false 55 | } 56 | delete(s.systemMap, name) 57 | return true 58 | } 59 | 60 | func (s *store) UpdateCache(name string, system *system) bool { 61 | s.rmx.Lock() 62 | defer s.rmx.Unlock() 63 | if _, ok := s.systemMap[name]; !ok { 64 | s.SetCache(name, system) 65 | return true 66 | } 67 | s.systemMap[name] = system 68 | return true 69 | } 70 | 71 | func (s *store) GetLdapConfigCache(name string) (*ldap.Conn, bool) { 72 | s.rmx.RLock() 73 | defer s.rmx.RUnlock() 74 | if config, ok := s.systemMap[name]; ok { 75 | return config.LdapConfig, true 76 | } 77 | return nil, false 78 | } 79 | -------------------------------------------------------------------------------- /pkg/global/config.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package global 9 | 10 | import ( 11 | "fmt" 12 | "os" 13 | 14 | "github.com/fsnotify/fsnotify" 15 | "github.com/gin-gonic/gin" 16 | "github.com/spf13/viper" 17 | ) 18 | 19 | func InitConfig() { 20 | workingDir, _ := os.Getwd() 21 | viper.SetConfigName("config") 22 | viper.SetConfigType("yaml") 23 | viper.AddConfigPath(workingDir) 24 | // 监听配置变化,无需重启应用读取配置 25 | viper.WatchConfig() 26 | viper.OnConfigChange(func(in fsnotify.Event) { 27 | fmt.Println(in.Name, in.Op) 28 | readConfig() 29 | }) 30 | readConfig() 31 | } 32 | 33 | func readConfig() { 34 | err = viper.ReadInConfig() 35 | if err != nil { 36 | panic("加载配置文件错误") 37 | } 38 | switch viper.GetString("server.model") { 39 | case "release": 40 | gin.SetMode(gin.ReleaseMode) 41 | case "debug": 42 | gin.SetMode(gin.DebugMode) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /pkg/global/error.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/5 6 | */ 7 | 8 | package global 9 | 10 | import "errors" 11 | 12 | // 定义错误信息 13 | 14 | func CreateErr(filed string, err error) error { 15 | e := errors.New(filed + "创建失败") 16 | GeaLogger.Error(e, err) 17 | return e 18 | } 19 | 20 | func UpdateErr(filed string, err error) error { 21 | e := errors.New(filed + "更新失败") 22 | GeaLogger.Error(e, err) 23 | return e 24 | } 25 | 26 | func DeleteErr(filed string, err error) error { 27 | e := errors.New(filed + "删除失败") 28 | GeaLogger.Error(e, err) 29 | return e 30 | } 31 | func NotFoundErr(filed string, err error) error { 32 | e := errors.New(filed + "不存在") 33 | GeaLogger.Error(e, err) 34 | return e 35 | } 36 | 37 | func GetErr(filed string, err error) error { 38 | e := errors.New(filed + "获取失败") 39 | GeaLogger.Error(e, err) 40 | return e 41 | } 42 | 43 | func OtherErr(err error, s ...string) error { 44 | GeaLogger.Error(err, s) 45 | return err 46 | } 47 | -------------------------------------------------------------------------------- /pkg/global/logger.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package global 9 | 10 | import ( 11 | "fmt" 12 | "os" 13 | "path" 14 | 15 | "github.com/sirupsen/logrus" 16 | ) 17 | 18 | type MyFormatter struct{} 19 | 20 | var GeaLogger *logrus.Logger 21 | 22 | const ( 23 | red = 31 24 | yellow = 33 25 | blue = 36 26 | gray = 37 27 | ) 28 | 29 | func (f *MyFormatter) Format(entry *logrus.Entry) ([]byte, error) { 30 | level := entry.Level 31 | var levelColor int 32 | switch level { 33 | case logrus.TraceLevel, logrus.DebugLevel: 34 | levelColor = gray // Cyan 35 | case logrus.InfoLevel: 36 | levelColor = blue // Green 37 | case logrus.WarnLevel: 38 | levelColor = yellow // Yellow 39 | case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel: 40 | levelColor = red // Red 41 | default: 42 | levelColor = blue // Reset color 43 | } 44 | 45 | funcVal := entry.Caller.Function 46 | fileVal := fmt.Sprintf("%s:%d", path.Base(entry.Caller.File), entry.Caller.Line) 47 | msg := entry.Message 48 | time := entry.Time.Format("2006-01-02 15:04:05") 49 | return []byte(fmt.Sprintf("%s [ \033[%dm%s\033[0m ] [ %s ] [%s] %s\n", time, levelColor, level.String(), funcVal, fileVal, msg)), nil 50 | } 51 | 52 | func InitLog() { 53 | // 创建 Logrus 日志实例 54 | GeaLogger = logrus.New() 55 | GeaLogger.SetReportCaller(true) 56 | // 输出到标准输出 57 | GeaLogger.SetOutput(os.Stdout) 58 | // 使用自定义日志格式 59 | GeaLogger.SetFormatter(&MyFormatter{}) 60 | } 61 | -------------------------------------------------------------------------------- /pkg/global/logo.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package global 9 | 10 | import ( 11 | "fmt" 12 | 13 | "github.com/spf13/viper" 14 | ) 15 | 16 | func InitSysTips() { 17 | var logo = ` 18 | ____ ____\_ _____/____ _________.__. / _ \ __| _/_____ |__| ____ 19 | / ___\ / _ \| __)_\__ \ / ___< | |/ /_\ \ / __ |/ \| |/ \ 20 | / /_/ > <_> ) \/ __ \_\___ \ \___ / | \/ /_/ | Y Y \ | | \ 21 | \___ / \____/_______ (____ /____ >/ ____\____|__ /\____ |__|_| /__|___| / 22 | /_____/ \/ \/ \/ \/ \/ \/ \/ \/` 23 | var sys = ` 24 | Version: 1.0.0 25 | Author: AnRuo 26 | WebSite: https://www.kubesre.com/` 27 | var run = "Address: " + fmt.Sprintf("%s:%d", viper.GetString("server.address"), 28 | viper.GetInt("server.port")) 29 | fmt.Println(logo) 30 | fmt.Println(sys) 31 | fmt.Println(run) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /pkg/global/mysql.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package global 9 | 10 | import ( 11 | "fmt" 12 | 13 | "github.com/spf13/viper" 14 | "gorm.io/driver/mysql" 15 | "gorm.io/gorm" 16 | 17 | modelSystem "go-easy-admin/internal/model/system" 18 | ) 19 | 20 | var ( 21 | GORM *gorm.DB 22 | err error 23 | ) 24 | 25 | // 初始化数据库 26 | 27 | func InitMysql() { 28 | dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", 29 | viper.GetString("mysql.DbUser"), 30 | viper.GetString("mysql.DbPwd"), 31 | viper.GetString("mysql.DbHost"), 32 | viper.GetInt("mysql.DbPort"), 33 | viper.GetString("mysql.DbName")) 34 | GORM, err = gorm.Open(mysql.Open(dsn)) 35 | if err != nil { 36 | panic("数据库连接失败" + err.Error()) 37 | } 38 | if viper.GetInt("mysql.ActiveDebug") == 1 { 39 | GORM = GORM.Debug() 40 | } 41 | // 开启连接池 42 | db, _ := GORM.DB() 43 | db.SetMaxIdleConns(10) 44 | db.SetMaxOpenConns(100) 45 | if err = db.Ping(); err != nil { 46 | panic("数据库连接失败" + err.Error()) 47 | return 48 | } 49 | _ = GORM.AutoMigrate( 50 | modelSystem.User{}, 51 | modelSystem.Role{}, 52 | modelSystem.Menu{}, 53 | modelSystem.APIs{}, 54 | modelSystem.Ldap{}, 55 | ) 56 | 57 | } 58 | -------------------------------------------------------------------------------- /pkg/global/rbac.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/8 6 | */ 7 | 8 | package global 9 | 10 | import ( 11 | "github.com/casbin/casbin/v2" 12 | gormadapter "github.com/casbin/gorm-adapter/v3" 13 | ) 14 | 15 | var CasbinCacheEnforcer *casbin.SyncedCachedEnforcer 16 | 17 | func InitCasbinEnforcer() { 18 | e, err := MysqlCasbin() 19 | if err != nil { 20 | GeaLogger.Error("初始化casbin策略管理器失败:", err) 21 | panic(err) 22 | } 23 | e.EnableAutoSave(true) 24 | CasbinCacheEnforcer = e 25 | } 26 | 27 | // 定义casbin 28 | 29 | func MysqlCasbin() (*casbin.SyncedCachedEnforcer, error) { 30 | a, err := gormadapter.NewAdapterByDB(GORM) 31 | if err != nil { 32 | GeaLogger.Error("casbin adapter gorm failed: ", err) 33 | return nil, err 34 | } 35 | //e, err := casbin.NewEnforcer("rbac_model.conf", a) 36 | e, err := casbin.NewSyncedCachedEnforcer("rbac_model.conf", a) 37 | if err != nil { 38 | return nil, err 39 | } 40 | e.SetExpireTime(60 * 60) 41 | if err = e.LoadPolicy(); err != nil { 42 | return nil, err 43 | } 44 | return e, nil 45 | } 46 | -------------------------------------------------------------------------------- /pkg/global/response.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package global 9 | 10 | import ( 11 | "net/http" 12 | 13 | "github.com/gin-gonic/gin" 14 | ) 15 | 16 | // 接口返回内容格式 msg为提示信息, data为数据 17 | 18 | type BaseContext struct { 19 | ctx *gin.Context 20 | } 21 | 22 | // 返回格式 23 | 24 | type ReturnMsg struct { 25 | Code int `json:"code"` 26 | Msg string `json:"msg"` 27 | Data interface{} `json:"data"` 28 | } 29 | 30 | // 成功返回 31 | 32 | func ReturnContext(ctx *gin.Context) *BaseContext { 33 | return &BaseContext{ctx: ctx} 34 | } 35 | func (BaseContext *BaseContext) Successful(msg string, data interface{}) { 36 | resp := &ReturnMsg{ 37 | Code: 20000, 38 | Msg: msg, 39 | Data: data, 40 | } 41 | BaseContext.ctx.JSON(http.StatusOK, resp) 42 | } 43 | 44 | // 失败返回 45 | 46 | func (BaseContext *BaseContext) Failed(msg string, data interface{}) { 47 | resp := &ReturnMsg{ 48 | Code: 50000, 49 | Msg: msg, 50 | Data: data, 51 | } 52 | BaseContext.ctx.JSON(http.StatusOK, resp) 53 | } 54 | -------------------------------------------------------------------------------- /pkg/middles/auth.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/7 6 | */ 7 | 8 | package middles 9 | 10 | import ( 11 | "errors" 12 | "fmt" 13 | "net/http" 14 | "strings" 15 | "time" 16 | 17 | jwt "github.com/appleboy/gin-jwt/v2" 18 | "github.com/gin-gonic/gin" 19 | "github.com/spf13/viper" 20 | 21 | reqLogin "go-easy-admin/internal/model/request/login" 22 | "go-easy-admin/internal/model/system" 23 | "go-easy-admin/pkg/controller/login" 24 | "go-easy-admin/pkg/global" 25 | "go-easy-admin/pkg/utils" 26 | ) 27 | 28 | func InitAuth() (*jwt.GinJWTMiddleware, error) { 29 | authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{ 30 | Realm: viper.GetString("jwt.realm"), // jwt标识 31 | Key: []byte(viper.GetString("jwt.key")), // 服务端密钥 32 | Timeout: time.Hour * time.Duration(viper.GetInt("jwt.timeout")), // token过期时间 33 | MaxRefresh: time.Hour * time.Duration(viper.GetInt("jwt.maxRefresh")), // token最大刷新时间(RefreshToken过期时间=Timeout+MaxRefresh) 34 | PayloadFunc: payloadFunc, // 有效载荷处理 35 | IdentityHandler: identityHandler, // 解析Claims 36 | Authenticator: loginFunc, // 校验token的正确性, 处理登录逻辑 37 | Authorizator: authorizator, // 用户登录校验成功处理 38 | Unauthorized: unauthorized, // 用户登录校验失败处理 39 | LoginResponse: loginResponse, // 登录成功后的响应 40 | LogoutResponse: logoutResponse, // 登出后的响应 41 | RefreshResponse: refreshResponse, // 刷新token后的响应 42 | TokenLookup: "header: Authorization, query: token, cookie: jwt", // 自动在这几个地方寻找请求中的token 43 | TokenHeadName: "Bearer", // header名称 44 | TimeFunc: time.Now, 45 | }) 46 | return authMiddleware, err 47 | } 48 | 49 | // 登录1 50 | func loginFunc(ctx *gin.Context) (interface{}, error) { 51 | var loginUser reqLogin.ReqLogin 52 | if err := ctx.ShouldBind(&loginUser); err != nil { 53 | return "", jwt.ErrMissingLoginValues 54 | } 55 | path := strings.Split(ctx.Request.RequestURI, "?")[0] 56 | // 通用登录 57 | if !strings.Contains(path, "ldap") { 58 | var aesLogin = loginUser 59 | utils.TagAes(&aesLogin) 60 | err, user := login.NewSysLogin(ctx).GeneralLogin(&aesLogin) 61 | if err == nil { 62 | if user.(*system.User).Status != 1 { 63 | return nil, errors.New("该用户已被禁用") 64 | } 65 | return user, nil 66 | } 67 | 68 | } else { 69 | // ldap登录 70 | err, user := login.NewSysLogin(ctx).LdapLogin(&loginUser) 71 | if err == nil { 72 | if user.(*system.User).Status != 1 { 73 | return nil, errors.New("该用户已被禁用") 74 | } 75 | return user, nil 76 | 77 | } 78 | } 79 | return nil, errors.New("用户名或密码错误") 80 | } 81 | 82 | // 登录2 83 | func payloadFunc(data interface{}) jwt.MapClaims { 84 | if v, ok := data.(*system.User); ok { 85 | return jwt.MapClaims{ 86 | "id": v.ID, 87 | jwt.IdentityKey: v.ID, 88 | "username": v.Username, 89 | } 90 | } 91 | return jwt.MapClaims{} 92 | } 93 | 94 | func identityHandler(ctx *gin.Context) interface{} { 95 | claims := jwt.ExtractClaims(ctx) 96 | var jwtClaim system.User 97 | userID, _ := claims[jwt.IdentityKey].(float64) 98 | userNameStr := fmt.Sprintf("%s", claims["username"]) 99 | jwtClaim.ID = uint(userID) 100 | jwtClaim.Username = userNameStr 101 | return &jwtClaim 102 | } 103 | 104 | func authorizator(data interface{}, ctx *gin.Context) bool { 105 | if v, ok := data.(*system.User); ok { 106 | ctx.Set("username", v.Username) 107 | ctx.Set("id", v.ID) 108 | return true 109 | } 110 | return false 111 | } 112 | 113 | func unauthorized(ctx *gin.Context, code int, message string) { 114 | response := gin.H{ 115 | "code": code, 116 | "msg": "failed", 117 | "data": message, 118 | } 119 | ctx.JSON(http.StatusOK, response) 120 | return 121 | } 122 | 123 | // 登录3 124 | func loginResponse(ctx *gin.Context, code int, token string, expire time.Time) { 125 | global.ReturnContext(ctx).Successful("success", map[string]interface{}{ 126 | "token": token, 127 | "expires": expire.Format("2006-01-02 15:04:05"), 128 | }) 129 | return 130 | } 131 | 132 | func logoutResponse(ctx *gin.Context, code int) { 133 | global.ReturnContext(ctx).Successful("success", "退出成功") 134 | return 135 | } 136 | 137 | func refreshResponse(ctx *gin.Context, code int, token string, expire time.Time) { 138 | global.ReturnContext(ctx).Successful("success", map[string]interface{}{ 139 | "token": token, 140 | "expires": expire.Format("2006-01-02 15:04:05"), 141 | }) 142 | return 143 | } 144 | -------------------------------------------------------------------------------- /pkg/middles/cors.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package middles 9 | 10 | import ( 11 | "net/http" 12 | 13 | "github.com/gin-gonic/gin" 14 | ) 15 | 16 | func Cors() gin.HandlerFunc { 17 | return func(c *gin.Context) { 18 | method := c.Request.Method 19 | origin := c.Request.Header.Get("Origin") 20 | c.Header("Access-Control-Allow-Origin", origin) 21 | c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id") 22 | c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT") 23 | c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type") 24 | c.Header("Access-Control-Allow-Credentials", "true") 25 | 26 | // 放行所有OPTIONS方法 27 | if method == "OPTIONS" { 28 | c.AbortWithStatus(http.StatusNoContent) 29 | } 30 | // 处理请求 31 | c.Next() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /pkg/middles/limit.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/27 6 | */ 7 | 8 | package middles 9 | 10 | import ( 11 | "time" 12 | 13 | "github.com/gin-gonic/gin" 14 | "github.com/juju/ratelimit" 15 | 16 | "go-easy-admin/pkg/global" 17 | ) 18 | 19 | func RateLimitMiddle(fillInterval time.Duration, capacity int64) gin.HandlerFunc { 20 | bucket := ratelimit.NewBucket(fillInterval, capacity) 21 | return func(ctx *gin.Context) { 22 | if bucket.TakeAvailable(1) < 1 { 23 | global.ReturnContext(ctx).Failed("failed", "访问限流") 24 | ctx.Abort() 25 | return 26 | } 27 | ctx.Next() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /pkg/middles/log.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/2 6 | */ 7 | 8 | package middles 9 | 10 | import ( 11 | "fmt" 12 | "time" 13 | 14 | "github.com/gin-gonic/gin" 15 | ) 16 | 17 | func LogHandlerFunc() gin.HandlerFunc { 18 | return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string { 19 | // 自定义格式 20 | return fmt.Sprintf("[%s | method: %s | path: %s | host: %s | proto: %s | code: %d | %s | %s ]\n", 21 | param.TimeStamp.Format(time.RFC3339), 22 | param.Method, 23 | param.Path, 24 | param.ClientIP, 25 | param.Request.Proto, 26 | param.StatusCode, 27 | param.Latency, 28 | param.Request.UserAgent(), 29 | ) 30 | }) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/middles/rbac.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/8 6 | */ 7 | 8 | package middles 9 | 10 | import ( 11 | "fmt" 12 | "strconv" 13 | 14 | "github.com/gin-gonic/gin" 15 | 16 | "go-easy-admin/pkg/controller/system" 17 | "go-easy-admin/pkg/global" 18 | ) 19 | 20 | func RbacMiddle() gin.HandlerFunc { 21 | return func(ctx *gin.Context) { 22 | ctxUser := ctx.GetString("username") 23 | if ctxUser == "admin" { 24 | ctx.Next() 25 | } else { 26 | // 获取用户ID 27 | userID := ctx.Keys["id"] 28 | if userID == nil { 29 | global.ReturnContext(ctx).Failed("failed", "用户未登录") 30 | ctx.Abort() 31 | return 32 | } 33 | id, _ := strconv.Atoi(fmt.Sprintf("%d", userID)) 34 | // 获取用户信息 35 | // TODO 缓存角色信息 36 | err, userInfo := system.NewSysUser(ctx).Get(id) 37 | if err != nil { 38 | global.ReturnContext(ctx).Failed("failed", "用户不存在") 39 | ctx.Abort() 40 | return 41 | } 42 | var sub []uint 43 | for _, role := range userInfo.Roles { 44 | if role.Status != 1 { 45 | continue 46 | } 47 | sub = append(sub, role.ID) 48 | } 49 | // 获取请求路径 50 | obj := ctx.FullPath() 51 | // 获取请求方法 52 | act := ctx.Request.Method 53 | for _, s := range sub { 54 | success, _ := global.CasbinCacheEnforcer.Enforce(strconv.Itoa(int(s)), obj, act) 55 | if success { 56 | ctx.Next() 57 | return 58 | } 59 | } 60 | global.ReturnContext(ctx).Failed("failed", "权限不足") 61 | ctx.Abort() 62 | return 63 | 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /pkg/utils/aes.go: -------------------------------------------------------------------------------- 1 | /* 2 | @auth: AnRuo 3 | @source: 云原生运维圈 4 | @website: https://www.kubesre.com/ 5 | @time: 2024/8/7 6 | */ 7 | 8 | package utils 9 | 10 | import ( 11 | "bytes" 12 | "crypto/aes" 13 | "crypto/md5" 14 | "encoding/hex" 15 | "errors" 16 | "reflect" 17 | 18 | "github.com/spf13/viper" 19 | 20 | "go-easy-admin/pkg/global" 21 | ) 22 | 23 | func EncryptAES(origin string) (error, string) { 24 | if origin == "" { 25 | return errors.New("空字符串不加密"), "" 26 | } 27 | err, key := generateAESKey(viper.GetString("aes.key")) 28 | if err != nil { 29 | return global.OtherErr(errors.New("生成密钥失败"), err.Error()), "" 30 | } 31 | cipher, err := aes.NewCipher(key) 32 | if err != nil { 33 | return global.OtherErr(errors.New("加密失败"), err.Error()), "" 34 | } 35 | originBytes := []byte(origin) 36 | // 填充原始数据 37 | blockSize := cipher.BlockSize() 38 | padding := blockSize - len(originBytes)%blockSize 39 | paddingBytes := bytes.Repeat([]byte{byte(padding)}, padding) 40 | originBytes = append(originBytes, paddingBytes...) 41 | encrypted := make([]byte, len(originBytes)) 42 | cipher.Encrypt(encrypted, originBytes) 43 | return nil, hex.EncodeToString(encrypted) 44 | } 45 | func generateAESKey(key string) (error, []byte) { 46 | targetKeySize := 16 // 目标密钥长度为 16 字节 47 | hasher := md5.New() 48 | hasher.Write([]byte(key)) 49 | md5Hash := hasher.Sum(nil) 50 | generatedKey := md5Hash[:targetKeySize] 51 | return nil, generatedKey 52 | } 53 | 54 | // 根据tag加密 55 | 56 | func TagAes(input interface{}) { 57 | v := reflect.ValueOf(input).Elem() 58 | t := v.Type() 59 | for i := 0; i < v.NumField(); i++ { 60 | field := v.Field(i) 61 | fieldType := t.Field(i) 62 | 63 | // 检查 aes 标签 64 | if aesTag := fieldType.Tag.Get("aes"); aesTag == "true" { 65 | if field.Kind() == reflect.String { 66 | // 对字段进行加密操作 67 | err, encryptedValue := EncryptAES(field.String()) 68 | if err != nil { 69 | return 70 | } 71 | field.SetString(encryptedValue) 72 | } 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /rbac_model.conf: -------------------------------------------------------------------------------- 1 | [request_definition] 2 | r = sub, obj, act 3 | # r 代表请求(Request)。 4 | # sub 是请求的主体(Subject),即发起请求的用户或角色。 5 | # obj 是请求的对象(Object),即被访问的资源。 6 | # act 是请求的动作(Action),即对资源进行的操作(如读、写、删除等)。 7 | 8 | [policy_definition] 9 | p = sub, obj, act,desc,create_by,api_id 10 | 11 | # p 代表策略(Policy)。 12 | # sub 是策略的主体(Subject),与请求中的主体对应。 13 | # obj 是策略的对象(Object),与请求中的对象对应。 14 | # act 是策略的动作(Action),与请求中的动作对应。 15 | # desc 是策略的描述(Description),用于描述策略的额外信息(这个字段在匹配时不会用到,只是用于记录)。 16 | 17 | [role_definition] 18 | g = _, _ 19 | 20 | [policy_effect] 21 | e = some(where (p.eft == allow)) 22 | 23 | [matchers] 24 | m = r.sub == p.sub && (keyMatch2(r.obj, p.obj) || keyMatch(r.obj, p.obj)) && (r.act == p.act || p.act == "*") 25 | 26 | # m 代表匹配器(Matcher)。 27 | # r.sub == p.sub 表示请求的主体必须与策略的主体匹配。 28 | # (keyMatch2(r.obj, p.obj) || keyMatch(r.obj, p.obj)) 表示请求的对象必须与策略的对象匹配,使用了 keyMatch2 和 keyMatch 函数进行匹配。 29 | # keyMatch2 是一种路径匹配函数,支持带有通配符的路径匹配,例如:/foo/bar 可以匹配 /foo/*。 30 | # keyMatch 是一种基本的路径匹配函数,例如:/foo/bar 可以匹配 /foo/*。 31 | # (r.act == p.act || p.act == "*") 表示请求的动作必须与策略的动作匹配,或者策略的动作是通配符 *,表示所有动作。 -------------------------------------------------------------------------------- /scripts/sys_ldap.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : dev-192.168.70.211 5 | Source Server Type : MySQL 6 | Source Server Version : 50732 7 | Source Host : 192.168.70.211:3306 8 | Source Schema : go-easy-admin-v2 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 50732 12 | File Encoding : 65001 13 | 14 | Date: 12/08/2024 14:39:08 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for sys_ldap 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `sys_ldap`; 24 | CREATE TABLE `sys_ldap` ( 25 | `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, 26 | `created_at` datetime(3) NULL DEFAULT NULL, 27 | `updated_at` datetime(3) NULL DEFAULT NULL, 28 | `deleted_at` datetime(3) NULL DEFAULT NULL, 29 | `create_by` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '\'创建来源\'', 30 | `address` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 31 | `dn` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, 32 | `password` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, 33 | `ou` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, 34 | `filter` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 35 | `mapping` json NULL COMMENT '\'属性映射\'', 36 | `status` tinyint(1) NULL DEFAULT 2 COMMENT '\'状态(正常/禁用, 默认禁用)\'', 37 | `ssl` tinyint(1) NULL DEFAULT 2 COMMENT '\'状态(正常/禁用, 默认禁用)\'', 38 | `admin_user` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL, 39 | PRIMARY KEY (`id`) USING BTREE, 40 | INDEX `idx_sys_ldap_deleted_at`(`deleted_at`) USING BTREE 41 | ) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 42 | 43 | -- ---------------------------- 44 | -- Records of sys_ldap 45 | -- ---------------------------- 46 | INSERT INTO `sys_ldap` VALUES (1, '2024-08-07 01:23:36.831', '2024-08-07 01:30:45.501', NULL, 'admin', '192.168.10.12:389', 'ou=user,dc=ienglish,dc=cn', 'UwFaWxMHnDwNMXgJ', 'ou=user,dc=ienglish,dc=cn', '(&(objectClass=organizationalPerson)(uid=%s))', '{\"email\": \"mail\", \"phone\": \"telephoneNumber\", \"username\": \"cn\", \"nick_name\": \"sn\"}', 1, 2, 'cn=admin,dc=ienglish,dc=cn'); 47 | 48 | SET FOREIGN_KEY_CHECKS = 1; 49 | -------------------------------------------------------------------------------- /scripts/sys_role.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : dev-192.168.70.211 5 | Source Server Type : MySQL 6 | Source Server Version : 50732 7 | Source Host : 192.168.70.211:3306 8 | Source Schema : go-easy-admin-v2 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 50732 12 | File Encoding : 65001 13 | 14 | Date: 12/08/2024 15:47:43 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for sys_role 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `sys_role`; 24 | CREATE TABLE `sys_role` ( 25 | `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, 26 | `created_at` datetime(3) NULL DEFAULT NULL, 27 | `updated_at` datetime(3) NULL DEFAULT NULL, 28 | `deleted_at` datetime(3) NULL DEFAULT NULL, 29 | `create_by` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '\'创建来源\'', 30 | `name` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '\'角色名称\'', 31 | `desc` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '\'角色描述\'', 32 | `status` tinyint(1) NULL DEFAULT 1 COMMENT '\'用户状态(正常/禁用, 默认正常)\'', 33 | PRIMARY KEY (`id`) USING BTREE, 34 | INDEX `idx_sys_role_deleted_at`(`deleted_at`) USING BTREE 35 | ) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 36 | 37 | -- ---------------------------- 38 | -- Records of sys_role 39 | -- ---------------------------- 40 | INSERT INTO `sys_role` VALUES (1, '2024-08-12 15:13:00.291', '2024-08-12 15:13:00.291', NULL, 'admin', 'dev', '开发', 1); 41 | INSERT INTO `sys_role` VALUES (2, '2024-08-12 15:13:13.556', '2024-08-12 15:13:13.556', NULL, 'admin', 'test', '测试', 1); 42 | INSERT INTO `sys_role` VALUES (3, '2024-08-12 15:13:28.250', '2024-08-12 15:13:28.250', NULL, 'admin', 'admin', '管理员', 1); 43 | 44 | SET FOREIGN_KEY_CHECKS = 1; 45 | -------------------------------------------------------------------------------- /scripts/system_apis.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : dev-192.168.70.211 5 | Source Server Type : MySQL 6 | Source Server Version : 50732 7 | Source Host : 192.168.70.211:3306 8 | Source Schema : go-easy-admin-v2 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 50732 12 | File Encoding : 65001 13 | 14 | Date: 12/08/2024 15:47:31 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for system_apis 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `system_apis`; 24 | CREATE TABLE `system_apis` ( 25 | `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, 26 | `created_at` datetime(3) NULL DEFAULT NULL, 27 | `updated_at` datetime(3) NULL DEFAULT NULL, 28 | `deleted_at` datetime(3) NULL DEFAULT NULL, 29 | `create_by` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL COMMENT '\'创建来源\'', 30 | `path` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, 31 | `method` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, 32 | `desc` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, 33 | `api_group` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL, 34 | PRIMARY KEY (`id`) USING BTREE, 35 | INDEX `idx_system_apis_deleted_at`(`deleted_at`) USING BTREE 36 | ) ENGINE = InnoDB AUTO_INCREMENT = 41 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; 37 | 38 | -- ---------------------------- 39 | -- Records of system_apis 40 | -- ---------------------------- 41 | INSERT INTO `system_apis` VALUES (1, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/user/create', 'POST', '创建用户', '用户管理'); 42 | INSERT INTO `system_apis` VALUES (2, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/user/delete', 'POST', '删除用户', '用户管理'); 43 | INSERT INTO `system_apis` VALUES (3, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/user/update/:id', 'POST', '更新用户', '用户管理'); 44 | INSERT INTO `system_apis` VALUES (4, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/user/list', 'GET', '用户列表', '用户管理'); 45 | INSERT INTO `system_apis` VALUES (5, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/user/get/:id', 'GET', '用户详情', '用户管理'); 46 | INSERT INTO `system_apis` VALUES (6, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/menu/create', 'POST', '创建菜单', '菜单管理'); 47 | INSERT INTO `system_apis` VALUES (7, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/menu/delete/:id', 'POST', '删除菜单', '菜单管理'); 48 | INSERT INTO `system_apis` VALUES (8, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/menu/update/:id', 'POST', '更新菜单', '菜单管理'); 49 | INSERT INTO `system_apis` VALUES (9, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/menu/list', 'GET', '菜单列表', '菜单管理'); 50 | INSERT INTO `system_apis` VALUES (10, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/menu/get/:id', 'GET', '菜单详情', '菜单管理'); 51 | INSERT INTO `system_apis` VALUES (11, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/role/create', 'POST', '创建角色', '角色管理'); 52 | INSERT INTO `system_apis` VALUES (12, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/role/delete/:id', 'POST', '删除角色', '角色管理'); 53 | INSERT INTO `system_apis` VALUES (13, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/role/update/:id', 'POST', '更新角色', '角色管理'); 54 | INSERT INTO `system_apis` VALUES (14, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/role/list', 'GET', '角色列表', '角色管理'); 55 | INSERT INTO `system_apis` VALUES (15, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/role/get/:id', 'GET', '角色详情', '角色管理'); 56 | INSERT INTO `system_apis` VALUES (16, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/apis/create', 'POST', '创建路由', '路由管理'); 57 | INSERT INTO `system_apis` VALUES (17, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/apis/delete/:id', 'POST', '删除路由', '路由管理'); 58 | INSERT INTO `system_apis` VALUES (18, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/apis/update/:id', 'POST', '更新路由', '路由管理'); 59 | INSERT INTO `system_apis` VALUES (19, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/apis/list', 'GET', '路由列表', '路由管理'); 60 | INSERT INTO `system_apis` VALUES (20, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/apis/get/:id', 'GET', '路由详情', '路由管理'); 61 | INSERT INTO `system_apis` VALUES (21, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/apis/get/group', 'GET', '路由组', '路由管理'); 62 | INSERT INTO `system_apis` VALUES (22, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/ldap/create', 'POST', '创建LDAP', 'LDAP管理'); 63 | INSERT INTO `system_apis` VALUES (23, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/ldap/list', 'GET', 'LDAP列表', 'LDAP管理'); 64 | INSERT INTO `system_apis` VALUES (24, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/ldap/ping', 'POST', 'LDAP测试', 'LDAP管理'); 65 | INSERT INTO `system_apis` VALUES (25, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/rbac/create', 'POST', '创建授权', '授权管理'); 66 | INSERT INTO `system_apis` VALUES (26, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/rbac/delete/*', 'POST', '删除授权', '授权管理'); 67 | INSERT INTO `system_apis` VALUES (27, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/rbac/update/*', 'POST', '更新授权', '授权管理'); 68 | INSERT INTO `system_apis` VALUES (28, '2024-08-08 17:27:05.000', '2024-08-08 17:27:05.000', NULL, 'admin', '/sys/rbac/list', 'GET', '授权列表', '授权管理'); 69 | 70 | SET FOREIGN_KEY_CHECKS = 1; 71 | --------------------------------------------------------------------------------