├── .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 |
--------------------------------------------------------------------------------