├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .idea ├── .gitignore ├── kubemanage.iml ├── modules.xml └── vcs.xml ├── LICENSE ├── README.md ├── README_en.md ├── cmd ├── app │ ├── config │ │ ├── config.go │ │ └── viper.go │ ├── options │ │ └── options.go │ └── server.go └── main.go ├── config.yaml ├── controller ├── api │ ├── api.go │ └── api_router.go ├── authority │ ├── authority.go │ ├── authority_router.go │ └── casbin.go ├── kubeController │ ├── configmap.go │ ├── daemonset.go │ ├── deployment.go │ ├── ingress.go │ ├── kubeRouter.go │ ├── namespace.go │ ├── node.go │ ├── persistentVolumeClaim.go │ ├── persistentvolume.go │ ├── pod.go │ ├── secret.go │ ├── service.go │ ├── statefulset.go │ └── workflow.go ├── menu │ ├── menu.go │ └── menu_router.go ├── operation │ ├── operation.go │ └── operation_router.go ├── other │ └── swagger.go └── user │ ├── user.go │ └── user_router.go ├── dao ├── api │ └── api.go ├── authority │ ├── authority.go │ └── sys_authority_menus.go ├── factory.go ├── menu │ └── basemenu.go ├── model │ ├── authorities_menus.go │ ├── authority.go │ ├── casbin.go │ ├── common.go │ ├── db_initializer.go │ ├── init.go │ ├── operation.go │ ├── sys_api.go │ ├── sys_authority_menu.go │ ├── sys_base_menu.go │ ├── user.go │ └── workflow.go ├── operation │ └── operation.go ├── user │ └── user.go └── workflow │ └── workflow.go ├── docs ├── docs.go ├── swagger.json └── swagger.yaml ├── dto ├── authority.go ├── casbin.go ├── common.go ├── kubeDto │ ├── configmap.go │ ├── daemonset.go │ ├── deployment.go │ ├── ingress.go │ ├── monitor.go │ ├── namespace.go │ ├── node.go │ ├── persistentVolumeClaim.go │ ├── persistentvolume.go │ ├── pod.go │ ├── secret.go │ ├── service.go │ ├── statefulset.go │ └── workflow.go ├── menu.go ├── operation.go └── user.go ├── go.mod ├── go.sum ├── img ├── cm_detail.jpg ├── cmdb │ ├── host.png │ └── webshell.png ├── dashboard.jpg ├── deployment.jpg ├── namespace.jpg ├── node.jpg ├── operation.png ├── pod.jpg ├── pod_log.jpg ├── pod_ter.jpg ├── rbac │ ├── api_rbac.png │ └── menu_rbac.png ├── service.jpg ├── system_state.png ├── user.png └── wordflow.jpg ├── middleware ├── casbin_rbac.go ├── cors.go ├── jwt.go ├── limiter.go ├── log.go ├── middleware.go ├── operation.go ├── recovery.go ├── response.go └── translation.go ├── pkg ├── const.go ├── core │ └── kubemanage │ │ └── v1 │ │ ├── cloud.go │ │ ├── interface.go │ │ ├── kube │ │ ├── configmap.go │ │ ├── daemonset.go │ │ ├── dataselector.go │ │ ├── deployment.go │ │ ├── ingress.go │ │ ├── init.go │ │ ├── namespace.go │ │ ├── node.go │ │ ├── persistentVolumeClaim.go │ │ ├── persistentvolume.go │ │ ├── pod.go │ │ ├── pod_v1.go │ │ ├── secret.go │ │ ├── service.go │ │ └── statefulset.go │ │ ├── setup.go │ │ ├── sys │ │ ├── api.go │ │ ├── authority.go │ │ ├── casbin.go │ │ ├── menu.go │ │ ├── operation.go │ │ └── user.go │ │ ├── system.go │ │ └── workflow.go ├── globalError │ └── global_error.go ├── jwt.go ├── logger │ ├── interface.go │ └── zap.go ├── params.go ├── password.go ├── source │ ├── initializer.go │ ├── mysqlinitHandler.go │ └── systemInitailzer.go ├── types │ └── termisnal.go └── utils │ ├── auth.go │ ├── print.go │ ├── public.go │ └── signal.go └── router └── router.go /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | env: 12 | GO_VERSION: '1.17.5' 13 | 14 | jobs: 15 | 16 | golang-lint: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | with: 22 | submodules: true 23 | 24 | - name: Set up Golang 25 | uses: actions/setup-go@v2 26 | with: 27 | go-version: ${{ env.GO_VERSION }} 28 | 29 | - name: Build the kubemanage binariy 30 | run: go build -v ./... 31 | 32 | - name: Run kubemanage unit test 33 | run: go test -v ./... 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### JetBrains template 2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 3 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 4 | 5 | # User-specific stuff 6 | .idea 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/**/usage.statistics.xml 10 | .idea/**/dictionaries 11 | .idea/**/shelf 12 | 13 | # Generated files 14 | .idea/**/contentModel.xml 15 | 16 | # Sensitive or high-churn files 17 | .idea/**/dataSources/ 18 | .idea/**/dataSources.ids 19 | .idea/**/dataSources.local.xml 20 | .idea/**/sqlDataSources.xml 21 | .idea/**/dynamic.xml 22 | .idea/**/uiDesigner.xml 23 | .idea/**/dbnavigator.xml 24 | 25 | # Gradle 26 | .idea/**/gradle.xml 27 | .idea/**/libraries 28 | 29 | # Gradle and Maven with auto-import 30 | # When using Gradle or Maven with auto-import, you should exclude module files, 31 | # since they will be recreated, and may cause churn. Uncomment if using 32 | # auto-import. 33 | # .idea/artifacts 34 | # .idea/compiler.xml 35 | # .idea/jarRepositories.xml 36 | # .idea/modules.xml 37 | # .idea/*.iml 38 | # .idea/modules 39 | # *.iml 40 | # *.ipr 41 | 42 | # CMake 43 | cmake-build-*/ 44 | 45 | # Mongo Explorer plugin 46 | .idea/**/mongoSettings.xml 47 | 48 | # File-based project format 49 | *.iws 50 | 51 | # IntelliJ 52 | out/ 53 | 54 | # mpeltonen/sbt-idea plugin 55 | .idea_modules/ 56 | 57 | # JIRA plugin 58 | atlassian-ide-plugin.xml 59 | 60 | # Cursive Clojure plugin 61 | .idea/replstate.xml 62 | 63 | # Crashlytics plugin (for Android Studio and IntelliJ) 64 | com_crashlytics_export_strings.xml 65 | crashlytics.properties 66 | crashlytics-build.properties 67 | fabric.properties 68 | 69 | # Editor-based Rest Client 70 | .idea/httpRequests 71 | 72 | # Android studio 3.1+ serialized cache file 73 | .idea/caches/build_file_checksums.ser 74 | 75 | ### Go template 76 | # Binaries for programs and plugins 77 | *.exe 78 | *.exe~ 79 | *.dll 80 | *.so 81 | *.dylib 82 | 83 | # Test binary, built with `go test -c` 84 | *.test 85 | 86 | # Output of the go coverage tool, specifically when used with LiteIDE 87 | *.log 88 | *.out 89 | 90 | # Dependency directories (remove the comment below to include it) 91 | # vendor/ 92 | 93 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Editor-based HTTP Client requests 5 | /httpRequests/ 6 | # Datasource local storage ignored files 7 | /dataSources/ 8 | /dataSources.local.xml 9 | -------------------------------------------------------------------------------- /.idea/kubemanage.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 noovertime7 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 简体中文 | [English](./README_en.md) 2 | # kubemanage 3 |

4 | 5 | golang 6 | 7 | 8 | gin 9 | 10 | 11 | gorm 12 | 13 | 14 | vue 15 | 16 |

17 | 18 | > kubemanage是一个简单易用的K8S管理平台,前端使用vue3,后端使用gin+gorm,对于初学k8s开发的同学来说,是一个很方便练手的项目,也可以作为企业二次开发的模板 19 | 20 | 技术栈选型当下最主流框架,后端使用Gin+GORM,前端使用vite+pinia+VUE3(v3版本)开发,前后端分离开发模式,使用client-go与K8S交互,使用Casbin与动态路由实现RBAC的权限体系 21 | 22 | 前端项目地址 https://github.com/noovertime7/kubemanage-web 23 | 24 | V3版本前端项目地址(开发中) https://gitee.com/noovertime/kubemanage-web.git 25 | ## 开始部署 26 | ### 初始化数据库 27 | 需要手动创建数据库,数据表与数据会通过`DBInitializer`自动初始化 28 | 29 | ```sql 30 | CREATE DATABASE kubemanage; 31 | ``` 32 | ### 运行工程 33 | 前端 34 | ```shell 35 | git clone https://github.com/noovertime7/kubemanage-web.git 36 | 37 | cd kubemanage-web 38 | 39 | npm install 40 | 41 | npm run serve 42 | ``` 43 | 后端 44 | 45 | 注意:请确保用户名/./kube 文件夹下存在k8s的kubeconfig文件,后面重构为多集群注册模式,容器部署,前端使用V3版本,后端请切换到V3分支 46 | 47 | 开始前请设置配置文件环境变量`KUBEMANAGE-CONFIG`,或通过命令行参数`configFile`指定,配置文件优先级: 默认配置 < 环境变量< 命令行 48 | 49 | ``` 50 | git clone https://github.com/noovertime7/kubemanage.git 51 | 52 | cd kubemanage 53 | 54 | go mod tidy 55 | 56 | go run cmd/main.go 57 | ``` 58 | 默认用户名密码 admin/kubemanage 59 | 60 | ## 现有特性 61 | 62 | - 支持RBAC的权限管理,根据角色配置菜单权限与接口权限 63 | - 支持资产管理,多主机同步连接,互不影响 64 | - 本地Kubernetes集群的管理 65 | - 接口调用操作审计功能 66 | 67 | ## Roadmap 68 | 69 | - 支持多集群管理 70 | - 支持应用一键发布 71 | - 在线工单审批系统 72 | 73 | ## Issue 规范 74 | - issue 仅用于提交 Bug 或 Feature 以及设计相关的内容,其它内容可能会被直接关闭。 75 | 76 | - 在提交 issue 之前,请搜索相关内容是否已被提出。 77 | 78 | ## Pull Request 规范 79 | - 请先 fork 一份到自己的项目下,在自己项目下新建分支。 80 | 81 | - commit 信息要以`feat(model): 描述信息` 的形式填写,例如 `fix(user): fix xxx bug / feat(user): add xxx`。 82 | 83 | - 如果是修复 bug,请在 PR 中给出描述信息。 84 | 85 | - 合并代码需要两名维护人员参与:一人进行 review 后 approve,另一人再次 review,通过后即可合并。 86 | 87 | ## 生成APi文档 88 | 89 | 使用swag生成api文档 90 | 91 | PS: 请使用最新版本的swag工具,建议拉取最新代码后自行编译,否则会`swag init`初始化失败 92 | 93 | ```shell 94 | swag init --pd -d ./cmd,docs 95 | ``` 96 | 97 | 成功生成后访问 `http://127.0.0.1:6180/swagger/index.html` 98 | 99 | ## 效果演示 100 | 101 | 集群详情(v3版本) 102 | ![image](https://github.com/noovertime7/kubemanage/assets/100392073/c1a02d86-4523-418c-8d4f-e93393bd2569) 103 | 104 | 首页 105 | ![首页](./img/dashboard.jpg?raw=true) 106 | 107 | 操作审计(v3版本) 108 | ![](./img/operation.png) 109 | 110 | 接口与菜单的RBAC控制(v3版本) 111 | ![](./img/rbac/api_rbac.png) 112 | ![](./img/rbac/menu_rbac.png) 113 | 114 | 用户管理(v3版本) 115 | ![](./img/user.png) 116 | 117 | 服务状态(v3版本) 118 | ![](./img/system_state.png) 119 | 120 | CMDB主机管理(v3版本) 121 | ![](./img/cmdb/host.png) 122 | 123 | CMDB网页终端(v3版本) 124 | ![](./img/cmdb/webshell.png) 125 | 126 | 工作流 127 | ![工作流](./img/wordflow.jpg?raw=true) 128 | 129 | deployment 130 | ![image](https://github.com/noovertime7/kubemanage/assets/100392073/d9d1cc92-1f35-445f-af16-3e3804e9ede0) 131 | 132 | ![image](https://github.com/noovertime7/kubemanage/assets/100392073/c6b6344e-afe2-41e0-b68b-fdc5da6c8a8a) 133 | 134 | 135 | ![deployment](./img/deployment.jpg?raw=true) 136 | 137 | pod 138 | ![首页](./img/pod.jpg?raw=true) 139 | 140 | POD日志 141 | ![POD 日志](./img/pod_log.jpg?raw=true) 142 | 143 | POD终端 144 | ![POD 终端](./img/pod_ter.jpg?raw=true) 145 | 146 | service 147 | ![service](./img/service.jpg?raw=true) 148 | 149 | configmap 150 | ![configmap](./img/cm_detail.jpg?raw=true) 151 | 152 | node 153 | ![node](./img/node.jpg?raw=true) 154 | -------------------------------------------------------------------------------- /README_en.md: -------------------------------------------------------------------------------- 1 | English | [简体中文](./README.md) 2 | # kubemanage 3 | Kubemanage is a simple and easy to use K8S management platform. The front end uses vue3 and the back end uses gin+gorm. It is a very convenient project for beginners of K8S development, and can also be used as a template for enterprise secondary development 4 | 5 | Front end project address https://github.com/noovertime7/kubemanage-web 6 | ## Start Deployment 7 | ### Initialize database 8 | The database needs to be created manually, and the data table and data will be initialized automatically through the 'DBInitializer' 9 | ```sql 10 | CREATE DATABASE kubemanage; 11 | ``` 12 | ### Operation engineering 13 | front 14 | ```shell 15 | git clone https://github.com/noovertime7/kubemanage-web.git 16 | cd kubemanage-web 17 | npm install 18 | npm run serve 19 | ``` 20 | back-end 21 | Note: Please ensure the username// The kubeconfig file of k8s exists in the kube folder. Later, it will be changed to use crd and container deployment 22 | 23 | Before starting, please set the configuration file environment variable KubeManageConfigFile="configuration file location", configuration file priority: default configuration 0 { 49 | return fmt.Errorf("%q does not take any arguments, got %q", cmd.CommandPath(), args) 50 | } 51 | } 52 | return nil 53 | }, 54 | } 55 | 56 | // 绑定命令行参数 57 | opts.BindFlags(cmd) 58 | return cmd 59 | } 60 | 61 | func Run(opt *options.Options) error { 62 | // 打印logo 63 | utils.PrintLogo() 64 | // 设置核心应用接口 65 | v1.Setup(opt) 66 | //初始化K8s client TODO 未来移除 67 | InitLocalK8s() 68 | // 初始化 APIs 路由 69 | router.InstallRouters(opt) 70 | // 启动优雅服务 71 | runServer(opt) 72 | return nil 73 | } 74 | 75 | func InitLocalK8s() { 76 | //初始化K8s client 77 | if err := kube.K8s.Init(); err != nil { 78 | utils.Must(err) 79 | } 80 | } 81 | 82 | // 优雅启动貔貅服务 83 | func runServer(opt *options.Options) { 84 | srv := &http.Server{ 85 | Addr: fmt.Sprintf("%s", config.SysConfig.Default.ListenAddr), 86 | Handler: opt.GinEngine, 87 | } 88 | 89 | // Initializing the server in a goroutine so that it won't block the graceful shutdown handling below 90 | go func() { 91 | logger.LG.Info("Success", zap.String("starting kubemanage server running on", config.SysConfig.Default.ListenAddr)) 92 | if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { 93 | logger.LG.Fatal("failed to listen kubemanage server: ", zap.Error(err)) 94 | } 95 | }() 96 | 97 | // Wait for interrupt signal to gracefully shut down the server with a timeout of 5 seconds. 98 | quit := utils.SetupSignalHandler() 99 | <-quit 100 | logger.LG.Info("shutting kubemanage server down ...") 101 | 102 | // The context is used to inform the server it has 5 seconds to finish the request 103 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 104 | defer cancel() 105 | 106 | if err := srv.Shutdown(ctx); err != nil { 107 | logger.LG.Fatal("kubemanage server forced to shutdown: ", zap.Error(err)) 108 | os.Exit(1) 109 | } 110 | logger.LG.Info("kubemanage server exit successful") 111 | } 112 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/cmd/app" 6 | "os" 7 | ) 8 | 9 | func main() { 10 | gin.SetMode(gin.ReleaseMode) 11 | 12 | cmd := app.NewServerCommand() 13 | if err := cmd.Execute(); err != nil { 14 | os.Exit(1) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /config.yaml: -------------------------------------------------------------------------------- 1 | default: 2 | listenAddr: ":6180" 3 | webSocketListenAddr: "" 4 | JWTSecret: "kubemanage" 5 | expireTime: 10 6 | 7 | mysql: 8 | host: "127.0.0.1" 9 | port: "3306" 10 | user: "root" 11 | password: "chenteng" 12 | name: "kubemanage" 13 | maxOpenConns: 100 14 | maxLifetime: 20 15 | maxIdleConns: 10 16 | 17 | log: 18 | level: "debug" ## 日志等级 19 | filename: "kubemanage.log" # 日志文件位置 20 | max_size: 200 # 日志文件最大大小(MB) 21 | max_age: 30 # 保留旧日志文件的最大天数 22 | max_backups: 7 # 最大保留日志个数 -------------------------------------------------------------------------------- /controller/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | 6 | "github.com/noovertime7/kubemanage/middleware" 7 | v1 "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1" 8 | "github.com/noovertime7/kubemanage/pkg/globalError" 9 | ) 10 | 11 | // GetApiList 12 | // @Tags API 13 | // @Summary 获取API列表 14 | // @Security ApiKeyAuth 15 | // @accept application/json 16 | // @Produce application/json 17 | // @Param data body dto.Empty 18 | // @Success 200 {object} middleware.Response{data=[]model.SysApi,msg=string} "获取API列表" 19 | // @Router /api/sysApi/getAPiList [get] 20 | func (a *apiController) GetApiList(ctx *gin.Context) { 21 | data, err := v1.CoreV1.System().Api().GetApiList(ctx) 22 | if err != nil { 23 | v1.Log.ErrorWithCode(globalError.GetError, err) 24 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.GetError, err)) 25 | } 26 | middleware.ResponseSuccess(ctx, data) 27 | } 28 | -------------------------------------------------------------------------------- /controller/api/api_router.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | type apiController struct{} 6 | 7 | func NewApiRouter(ginGroup *gin.RouterGroup) { 8 | api := &apiController{} 9 | api.initRoutes(ginGroup) 10 | } 11 | 12 | func (a *apiController) initRoutes(ginGroup *gin.RouterGroup) { 13 | apiRoute := ginGroup.Group("/sysApi") 14 | apiRoute.GET("/getAPiList", a.GetApiList) 15 | } 16 | -------------------------------------------------------------------------------- /controller/authority/authority.go: -------------------------------------------------------------------------------- 1 | package authority 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | 6 | "github.com/noovertime7/kubemanage/dto" 7 | "github.com/noovertime7/kubemanage/middleware" 8 | v1 "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1" 9 | "github.com/noovertime7/kubemanage/pkg/globalError" 10 | ) 11 | 12 | func (a *authorityController) GetAuthorityList(ctx *gin.Context) { 13 | params := &dto.PageInfo{} 14 | if err := params.BindingValidParams(ctx); err != nil { 15 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 16 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 17 | return 18 | } 19 | data, err := v1.CoreV1.System().Authority().GetAuthorityList(ctx, *params) 20 | if err != nil { 21 | v1.Log.ErrorWithCode(globalError.GetError, err) 22 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.GetError, err)) 23 | return 24 | } 25 | middleware.ResponseSuccess(ctx, data) 26 | } 27 | -------------------------------------------------------------------------------- /controller/authority/authority_router.go: -------------------------------------------------------------------------------- 1 | package authority 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | type authorityController struct{} 8 | 9 | func NewCasbinRouter(ginEngine *gin.RouterGroup) { 10 | cas := authorityController{} 11 | cas.initRoutes(ginEngine) 12 | } 13 | 14 | func (a *authorityController) initRoutes(ginEngine *gin.RouterGroup) { 15 | casRoute := ginEngine.Group("/authority") 16 | { 17 | casRoute.GET("/getPolicyPathByAuthorityId", a.GetPolicyPathByAuthorityId) 18 | casRoute.POST("/updateCasbinByAuthority", a.UpdateCasbinByAuthorityId) 19 | casRoute.GET("/getAuthorityList", a.GetAuthorityList) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /controller/authority/casbin.go: -------------------------------------------------------------------------------- 1 | package authority 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | 6 | "github.com/noovertime7/kubemanage/dto" 7 | "github.com/noovertime7/kubemanage/middleware" 8 | "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1" 9 | "github.com/noovertime7/kubemanage/pkg/globalError" 10 | ) 11 | 12 | // GetPolicyPathByAuthorityId 13 | // @Tags Casbin 14 | // @Summary 获取权限列表 15 | // @Security ApiKeyAuth 16 | // @accept application/json 17 | // @Produce application/json 18 | // @Param data body dto.CasbinInReceive true "权限id, 权限模型列表" 19 | // @Success 200 {object} middleware.Response{data=dto.CasbinInfo,msg=string} "获取权限列表,返回包括casbin详情列表" 20 | // @Router /api/authority/getPolicyPathByAuthorityId [get] 21 | func (a *authorityController) GetPolicyPathByAuthorityId(ctx *gin.Context) { 22 | rule := &dto.CasbinInReceive{} 23 | if err := rule.BindingValidParams(ctx); err != nil { 24 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 25 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 26 | return 27 | } 28 | middleware.ResponseSuccess(ctx, v1.CoreV1.System().CasbinService().GetPolicyPathByAuthorityId(rule.AuthorityId)) 29 | } 30 | 31 | // UpdateCasbinByAuthorityId 32 | // @Tags Casbin 33 | // @Summary 通过角色更新接口权限 34 | // @Security ApiKeyAuth 35 | // @accept application/json 36 | // @Produce application/json 37 | // @Param data body dto.UpdateCasbinInput true "权限id, 权限模型列表" 38 | // @Success 200 {object} middleware.Response{msg=string} "通过角色更新接口权限" 39 | // @Router /api/authority/updateCasbinByAuthority [post] 40 | func (a *authorityController) UpdateCasbinByAuthorityId(ctx *gin.Context) { 41 | params := &dto.UpdateCasbinInput{} 42 | if err := params.BindingValidParams(ctx); err != nil { 43 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 44 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 45 | return 46 | } 47 | if err := v1.CoreV1.System().CasbinService().UpdateCasbin(params.AuthorityId, params.CasbinInfo); err != nil { 48 | v1.Log.ErrorWithCode(globalError.UpdateError, err) 49 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.UpdateError, err)) 50 | return 51 | } 52 | middleware.ResponseSuccess(ctx, "") 53 | } 54 | -------------------------------------------------------------------------------- /controller/kubeController/kubeRouter.go: -------------------------------------------------------------------------------- 1 | package kubeController 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | type kubeRouter struct{} 8 | 9 | func NewKubeRouter(ginEngine *gin.RouterGroup) { 10 | k := kubeRouter{} 11 | k.initRoutes(ginEngine) 12 | } 13 | 14 | func (k *kubeRouter) initRoutes(ginEngine *gin.RouterGroup) { 15 | k8sRoute := ginEngine.Group("/k8s") 16 | { 17 | k8sRoute.POST("/deployment/create", Deployment.CreateDeployment) 18 | k8sRoute.DELETE("/deployment/del", Deployment.DeleteDeployment) 19 | k8sRoute.PUT("/deployment/update", Deployment.UpdateDeployment) 20 | k8sRoute.GET("/deployment/list", Deployment.GetDeploymentList) 21 | k8sRoute.GET("/deployment/detail", Deployment.GetDeploymentDetail) 22 | k8sRoute.PUT("/deployment/restart", Deployment.RestartDeployment) 23 | k8sRoute.GET("/deployment/scale", Deployment.ScaleDeployment) 24 | k8sRoute.GET("/deployment/numnp", Deployment.GetDeploymentNumPreNS) 25 | } 26 | { 27 | k8sRoute.GET("/pod/list", Pod.GetPods) 28 | k8sRoute.GET("/pod/detail", Pod.GetPodDetail) 29 | k8sRoute.DELETE("/pod/del", Pod.DeletePod) 30 | k8sRoute.PUT("/pod/update", Pod.UpdatePod) 31 | k8sRoute.GET("/pod/container", Pod.GetPodContainer) 32 | k8sRoute.GET("/pod/log", Pod.GetPodLog) 33 | k8sRoute.GET("/pod/numnp", Pod.GetPodNumPreNp) 34 | k8sRoute.GET("/pod/webshell", Pod.WebShell) 35 | } 36 | { 37 | k8sRoute.DELETE("/daemonset/del", DaemonSet.DeleteDaemonSet) 38 | k8sRoute.PUT("/daemonset/update", DaemonSet.UpdateDaemonSet) 39 | k8sRoute.GET("/daemonset/list", DaemonSet.GetDaemonSetList) 40 | k8sRoute.GET("/daemonset/detail", DaemonSet.GetDaemonSetDetail) 41 | } 42 | { 43 | k8sRoute.DELETE("/statefulset/del", StatefulSet.DeleteStatefulSet) 44 | k8sRoute.PUT("/statefulset/update", StatefulSet.UpdateStatefulSet) 45 | k8sRoute.GET("/statefulset/list", StatefulSet.GetStatefulSetList) 46 | k8sRoute.GET("/statefulset/detail", StatefulSet.GetStatefulSetDetail) 47 | } 48 | { 49 | k8sRoute.GET("/node/list", Node.GetNodeList) 50 | k8sRoute.GET("/node/detail", Node.GetNodeDetail) 51 | } 52 | 53 | { 54 | k8sRoute.PUT("/namespace/create", NameSpace.CreateNameSpace) 55 | k8sRoute.DELETE("/namespace/del", NameSpace.DeleteNameSpace) 56 | k8sRoute.GET("/namespace/list", NameSpace.GetNameSpaceList) 57 | k8sRoute.GET("/namespace/detail", NameSpace.GetNameSpaceDetail) 58 | } 59 | 60 | { 61 | k8sRoute.DELETE("/persistentvolume/del", PersistentVolume.DeletePersistentVolume) 62 | k8sRoute.GET("/persistentvolume/list", PersistentVolume.GetPersistentVolumeList) 63 | k8sRoute.GET("/persistentvolume/detail", PersistentVolume.GetPersistentVolumeDetail) 64 | } 65 | 66 | { 67 | k8sRoute.POST("/service/create", ServiceController.CreateService) 68 | k8sRoute.DELETE("/service/del", ServiceController.DeleteService) 69 | k8sRoute.PUT("/service/update", ServiceController.UpdateService) 70 | k8sRoute.GET("/service/list", ServiceController.GetServiceList) 71 | k8sRoute.GET("/service/detail", ServiceController.GetServiceDetail) 72 | k8sRoute.GET("/service/numnp", ServiceController.GetServicePerNS) 73 | } 74 | 75 | { 76 | k8sRoute.PUT("/ingress/create", IngressController.CreateIngress) 77 | k8sRoute.DELETE("/ingress/del", IngressController.DeleteIngress) 78 | k8sRoute.PUT("/ingress/update", IngressController.UpdateIngress) 79 | k8sRoute.GET("/ingress/list", IngressController.GetIngressList) 80 | k8sRoute.GET("/ingress/detail", IngressController.GetIngressDetail) 81 | k8sRoute.GET("/ingress/numnp", IngressController.GetIngressNumPreNp) 82 | } 83 | 84 | { 85 | k8sRoute.DELETE("/configmap/del", Configmap.DeleteConfigmap) 86 | k8sRoute.PUT("/configmap/update", Configmap.UpdateConfigmap) 87 | k8sRoute.GET("/configmap/list", Configmap.GetConfigmapList) 88 | k8sRoute.GET("/configmap/detail", Configmap.GetConfigmapDetail) 89 | } 90 | 91 | { 92 | k8sRoute.DELETE("/persistentvolumeclaim/del", PersistentVolumeClaim.DeletePersistentVolumeClaim) 93 | k8sRoute.PUT("/persistentvolumeclaim/update", PersistentVolumeClaim.UpdatePersistentVolumeClaim) 94 | k8sRoute.GET("/persistentvolumeclaim/list", PersistentVolumeClaim.GetPersistentVolumeClaimList) 95 | k8sRoute.GET("/persistentvolumeclaim/detail", PersistentVolumeClaim.GetPersistentVolumeClaimDetail) 96 | } 97 | 98 | { 99 | k8sRoute.DELETE("/secret/del", Secret.DeleteSecret) 100 | k8sRoute.PUT("/secret/update", Secret.UpdateSecret) 101 | k8sRoute.GET("/secret/list", Secret.GetSecretList) 102 | k8sRoute.GET("/secret/detail", Secret.GetSecretDetail) 103 | } 104 | 105 | { 106 | k8sRoute.POST("/workflow/create", WorkFlow.CreateWorkFlow) 107 | k8sRoute.DELETE("/workflow/del", WorkFlow.DeleteWorkflow) 108 | k8sRoute.GET("/workflow/list", WorkFlow.GetWorkflowList) 109 | k8sRoute.GET("/workflow/id", WorkFlow.GetWorkflowByID) 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /controller/kubeController/namespace.go: -------------------------------------------------------------------------------- 1 | package kubeController 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/dto/kubeDto" 6 | "github.com/noovertime7/kubemanage/middleware" 7 | "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1" 8 | "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1/kube" 9 | "github.com/noovertime7/kubemanage/pkg/globalError" 10 | ) 11 | 12 | var NameSpace namespace 13 | 14 | type namespace struct{} 15 | 16 | // CreateNameSpace 创建namespace 17 | // ListPage godoc 18 | // @Summary 创建namespace 19 | // @Description 创建namespace 20 | // @Tags NameSpace 21 | // @ID /api/k8s/namespace/create 22 | // @Accept json 23 | // @Produce json 24 | // @Param name query string true "namespace名称" 25 | // @Success 200 {object} middleware.Response"{"code": 200, msg="","data": "创建成功}" 26 | // @Router /api/k8s/namespace/create [put] 27 | func (n *namespace) CreateNameSpace(ctx *gin.Context) { 28 | params := &kubeDto.NameSpaceNameInput{} 29 | if err := params.BindingValidParams(ctx); err != nil { 30 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 31 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 32 | return 33 | } 34 | if err := kube.NameSpace.CreateNameSpace(params.Name); err != nil { 35 | v1.Log.ErrorWithCode(globalError.CreateError, err) 36 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.CreateError, err)) 37 | return 38 | } 39 | middleware.ResponseSuccess(ctx, "创建成功") 40 | } 41 | 42 | // DeleteNameSpace 删除namespace 43 | // ListPage godoc 44 | // @Summary 删除namespace 45 | // @Description 删除namespace 46 | // @Tags NameSpace 47 | // @ID /api/k8s/namespace/del 48 | // @Accept json 49 | // @Produce json 50 | // @Param name query string true "namespace名称" 51 | // @Success 200 {object} middleware.Response"{"code": 200, msg="","data": "删除成功}" 52 | // @Router /api/k8s/namespace/del [delete] 53 | func (n *namespace) DeleteNameSpace(ctx *gin.Context) { 54 | params := &kubeDto.NameSpaceNameInput{} 55 | if err := params.BindingValidParams(ctx); err != nil { 56 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 57 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 58 | return 59 | } 60 | if err := kube.NameSpace.DeleteNameSpace(params.Name); err != nil { 61 | v1.Log.ErrorWithCode(globalError.DeleteError, err) 62 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.DeleteError, err)) 63 | return 64 | } 65 | middleware.ResponseSuccess(ctx, "删除成功") 66 | } 67 | 68 | // GetNameSpaceList 获取NameSpace列表 69 | // ListPage godoc 70 | // @Summary 获取NameSpace列表 71 | // @Description 获取NameSpace列表 72 | // @Tags NameSpace 73 | // @ID /api/k8s/namespace/list 74 | // @Accept json 75 | // @Produce json 76 | // @Param filter_name query string false "过滤" 77 | // @Param page query int false "页码" 78 | // @Param limit query int false "分页限制" 79 | // @Success 200 {object} middleware.Response"{"code": 200, msg="","data": service.NameSpaceResp}" 80 | // @Router /api/k8s/namespace/list [get] 81 | func (n *namespace) GetNameSpaceList(ctx *gin.Context) { 82 | params := &kubeDto.NameSpaceListInput{} 83 | if err := params.BindingValidParams(ctx); err != nil { 84 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 85 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 86 | return 87 | } 88 | data, err := kube.NameSpace.GetNameSpaces(params.FilterName, params.Limit, params.Page) 89 | if err != nil { 90 | v1.Log.ErrorWithCode(globalError.GetError, err) 91 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.GetError, err)) 92 | return 93 | } 94 | middleware.ResponseSuccess(ctx, data) 95 | } 96 | 97 | // GetNameSpaceDetail 获取NameSpace详情 98 | // ListPage godoc 99 | // @Summary 获取NameSpace详情 100 | // @Description 获取NameSpace详情 101 | // @Tags NameSpace 102 | // @ID /api/k8s/namespace/detail 103 | // @Accept json 104 | // @Produce json 105 | // @Param name query string true "namespace名称" 106 | // @Success 200 {object} middleware.Response"{"code": 200, msg="","data":data }" 107 | // @Router /api/k8s/namespace/detail [get] 108 | func (n *namespace) GetNameSpaceDetail(ctx *gin.Context) { 109 | params := &kubeDto.NameSpaceNameInput{} 110 | if err := params.BindingValidParams(ctx); err != nil { 111 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 112 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 113 | return 114 | } 115 | data, err := kube.NameSpace.GetNameSpacesDetail(params.Name) 116 | if err != nil { 117 | v1.Log.ErrorWithCode(globalError.GetError, err) 118 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.GetError, err)) 119 | return 120 | } 121 | middleware.ResponseSuccess(ctx, data) 122 | } 123 | -------------------------------------------------------------------------------- /controller/kubeController/node.go: -------------------------------------------------------------------------------- 1 | package kubeController 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/dto/kubeDto" 6 | "github.com/noovertime7/kubemanage/middleware" 7 | "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1" 8 | "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1/kube" 9 | "github.com/noovertime7/kubemanage/pkg/globalError" 10 | ) 11 | 12 | var Node node 13 | 14 | type node struct{} 15 | 16 | // GetNodeList 获取Node列表 17 | // ListPage godoc 18 | // @Summary 获取Node列表 19 | // @Description 获取Node列表 20 | // @Tags Node 21 | // @ID /api/k8s/node/list 22 | // @Accept json 23 | // @Produce json 24 | // @Param filter_name query string false "过滤" 25 | // @Param page query int false "页码" 26 | // @Param limit query int false "分页限制" 27 | // @Success 200 {object} middleware.Response"{"code": 200, msg="","data": service.NameSpaceResp}" 28 | // @Router /api/k8s/node/list [get] 29 | func (n *node) GetNodeList(ctx *gin.Context) { 30 | params := &kubeDto.NodeListInput{} 31 | if err := params.BindingValidParams(ctx); err != nil { 32 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 33 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 34 | return 35 | } 36 | data, err := kube.Node.GetNodes(params.FilterName, params.Limit, params.Page) 37 | if err != nil { 38 | v1.Log.ErrorWithCode(globalError.GetError, err) 39 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.GetError, err)) 40 | return 41 | } 42 | middleware.ResponseSuccess(ctx, data) 43 | } 44 | 45 | // GetNodeDetail 获取Node详情 46 | // ListPage godoc 47 | // @Summary 获取Node详情 48 | // @Description 获取Node详情 49 | // @Tags Node 50 | // @ID /api/k8s/node/detail 51 | // @Accept json 52 | // @Produce json 53 | // @Param name query string true "node名称" 54 | // @Success 200 {object} middleware.Response"{"code": 200, msg="","data":data }" 55 | // @Router /api/k8s/node/detail [get] 56 | func (n *node) GetNodeDetail(ctx *gin.Context) { 57 | params := &kubeDto.NodeNameInput{} 58 | if err := params.BindingValidParams(ctx); err != nil { 59 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 60 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 61 | return 62 | } 63 | data, err := kube.Node.GetNodeDetail(params.Name) 64 | if err != nil { 65 | v1.Log.ErrorWithCode(globalError.GetError, err) 66 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.GetError, err)) 67 | return 68 | } 69 | middleware.ResponseSuccess(ctx, data) 70 | } 71 | -------------------------------------------------------------------------------- /controller/kubeController/persistentvolume.go: -------------------------------------------------------------------------------- 1 | package kubeController 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/dto/kubeDto" 6 | "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1" 7 | 8 | "github.com/noovertime7/kubemanage/middleware" 9 | "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1/kube" 10 | "github.com/noovertime7/kubemanage/pkg/globalError" 11 | ) 12 | 13 | var PersistentVolume persistentVolume 14 | 15 | type persistentVolume struct{} 16 | 17 | // DeletePersistentVolume 删除persistentVolume 18 | // ListPage godoc 19 | // @Summary 删除persistentVolume 20 | // @Description 删除persistentVolume 21 | // @Tags PersistentVolume 22 | // @ID /api/k8s/spersistentvolume/del 23 | // @Accept json 24 | // @Produce json 25 | // @Param name query string true "persistentvolume名称" 26 | // @Success 200 {object} middleware.Response"{"code": 200, msg="","data": "删除成功}" 27 | // @Router /api/k8s/spersistentvolume/del [delete] 28 | func (n *persistentVolume) DeletePersistentVolume(ctx *gin.Context) { 29 | params := &kubeDto.PersistentVolumeNameInput{} 30 | if err := params.BindingValidParams(ctx); err != nil { 31 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 32 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 33 | return 34 | } 35 | if err := kube.PersistentVolume.DeletePersistentVolume(params.Name); err != nil { 36 | v1.Log.ErrorWithCode(globalError.GetError, err) 37 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.GetError, err)) 38 | return 39 | } 40 | middleware.ResponseSuccess(ctx, "删除成功") 41 | } 42 | 43 | // GetPersistentVolumeList 获取PV列表 44 | // ListPage godoc 45 | // @Summary 获取PV列表 46 | // @Description 获取PV列表 47 | // @Tags PersistentVolume 48 | // @ID /api/k8s/persistentvolume/list 49 | // @Accept json 50 | // @Produce json 51 | // @Param filter_name query string false "过滤" 52 | // @Param page query int false "页码" 53 | // @Param limit query int false "分页限制" 54 | // @Success 200 {object} middleware.Response"{"code": 200, msg="","data": service.PersistentVolumeResp}" 55 | // @Router /api/k8s/persistentvolume/list [get] 56 | func (n *persistentVolume) GetPersistentVolumeList(ctx *gin.Context) { 57 | params := &kubeDto.PersistentVolumeListInput{} 58 | if err := params.BindingValidParams(ctx); err != nil { 59 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 60 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 61 | return 62 | } 63 | data, err := kube.PersistentVolume.GetPersistentVolumes(params.FilterName, params.Limit, params.Page) 64 | if err != nil { 65 | v1.Log.ErrorWithCode(globalError.GetError, err) 66 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.GetError, err)) 67 | return 68 | } 69 | middleware.ResponseSuccess(ctx, data) 70 | } 71 | 72 | // GetPersistentVolumeDetail 获取PV的详细信息 73 | // ListPage godoc 74 | // @Summary 获取PV的详细信息 75 | // @Description 获取PV的详细信息 76 | // @Tags PersistentVolume 77 | // @ID /api/k8s/persistentvolume/detail 78 | // @Accept json 79 | // @Produce json 80 | // @Param name query string true "persistentVolume名称" 81 | // @Success 200 {object} middleware.Response"{"code": 200, msg="","data": *coreV1.PersistentVolume}" 82 | // @Router /api/k8s/persistentvolume/detail [get] 83 | func (n *persistentVolume) GetPersistentVolumeDetail(ctx *gin.Context) { 84 | params := &kubeDto.PersistentVolumeNameInput{} 85 | if err := params.BindingValidParams(ctx); err != nil { 86 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 87 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 88 | return 89 | } 90 | data, err := kube.PersistentVolume.GetPersistentVolumesDetail(params.Name) 91 | if err != nil { 92 | v1.Log.ErrorWithCode(globalError.GetError, err) 93 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.GetError, err)) 94 | return 95 | } 96 | middleware.ResponseSuccess(ctx, data) 97 | } 98 | -------------------------------------------------------------------------------- /controller/kubeController/workflow.go: -------------------------------------------------------------------------------- 1 | package kubeController 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/dto/kubeDto" 6 | "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1" 7 | 8 | "github.com/noovertime7/kubemanage/middleware" 9 | "github.com/noovertime7/kubemanage/pkg/globalError" 10 | ) 11 | 12 | var WorkFlow workflow 13 | 14 | type workflow struct{} 15 | 16 | // CreateWorkFlow 创建workflow 17 | // ListPage godoc 18 | // @Summary 创建workflow 19 | // @Description 创建workflow 20 | // @Tags Workflow 21 | // @ID /api/k8s/workflow/create 22 | // @Accept json 23 | // @Produce json 24 | // @Param body body kubernetes.WorkFlowCreateInput true "body" 25 | // @Success 200 {object} middleware.Response"{"code": 200, msg="","data": "创建成功}" 26 | // @Router /api/k8s/workflow/create [post] 27 | func (w *workflow) CreateWorkFlow(ctx *gin.Context) { 28 | params := &kubeDto.WorkFlowCreateInput{} 29 | if err := params.BindingValidParams(ctx); err != nil { 30 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 31 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 32 | return 33 | } 34 | if err := v1.CoreV1.WorkFlow().Save(ctx, params); err != nil { 35 | v1.Log.ErrorWithCode(globalError.CreateError, err) 36 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.CreateError, err)) 37 | return 38 | } 39 | middleware.ResponseSuccess(ctx, "创建成功") 40 | } 41 | 42 | // DeleteWorkflow 删除Workflow 43 | // ListPage godoc 44 | // @Summary 删除Workflow 45 | // @Description 删除Workflow 46 | // @Tags Workflow 47 | // @ID /api/k8s/workflow/del 48 | // @Accept json 49 | // @Produce json 50 | // @Param ID query int true "Workflow ID" 51 | // @Success 200 {object} middleware.Response"{"code": 200, msg="","data": "删除成功}" 52 | // @Router /api/k8s/workflow/del [delete] 53 | func (w *workflow) DeleteWorkflow(ctx *gin.Context) { 54 | params := &kubeDto.WorkFlowIDInput{} 55 | if err := params.BindingValidParams(ctx); err != nil { 56 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 57 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 58 | return 59 | } 60 | if err := v1.CoreV1.WorkFlow().Delete(ctx, params.ID); err != nil { 61 | v1.Log.ErrorWithCode(globalError.DeleteError, err) 62 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.DeleteError, err)) 63 | return 64 | } 65 | middleware.ResponseSuccess(ctx, "删除成功") 66 | } 67 | 68 | // GetWorkflowList 查看Configmap列表 69 | // ListPage godoc 70 | // @Summary 查看Configmap列表 71 | // @Description 查看Configmap列表 72 | // @Tags Workflow 73 | // @ID /api/k8s/workflow/list 74 | // @Accept json 75 | // @Produce json 76 | // @Param filter_name query string false "过滤" 77 | // @Param page query int false "页码" 78 | // @Param limit query int false "分页限制" 79 | // @Success 200 {object} middleware.Response"{"code": 200, msg="","data": }" 80 | // @Router /api/k8s/workflow/list [get] 81 | func (w *workflow) GetWorkflowList(ctx *gin.Context) { 82 | params := &kubeDto.WorkFlowListInput{} 83 | if err := params.BindingValidParams(ctx); err != nil { 84 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 85 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 86 | return 87 | } 88 | data, err := v1.CoreV1.WorkFlow().FindList(ctx, params) 89 | if err != nil { 90 | v1.Log.ErrorWithCode(globalError.GetError, err) 91 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.GetError, err)) 92 | return 93 | } 94 | middleware.ResponseSuccess(ctx, data) 95 | } 96 | 97 | // GetWorkflowByID 根据ID查看workflow 98 | // ListPage godoc 99 | // @Summary 根据ID查看workflow 100 | // @Description 根据ID查看workflow 101 | // @Tags Workflow 102 | // @ID /api/k8s/workflow/id 103 | // @Accept json 104 | // @Produce json 105 | // @Param ID query int true "Workflow ID" 106 | // @Success 200 {object} middleware.Response"{"code": 200, msg="","data": }" 107 | // @Router /api/k8s/workflow/id [get] 108 | func (w *workflow) GetWorkflowByID(ctx *gin.Context) { 109 | params := &kubeDto.WorkFlowIDInput{} 110 | if err := params.BindingValidParams(ctx); err != nil { 111 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 112 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 113 | return 114 | } 115 | data, err := v1.CoreV1.WorkFlow().Find(ctx, params) 116 | if err != nil { 117 | v1.Log.ErrorWithCode(globalError.GetError, err) 118 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.GetError, err)) 119 | return 120 | } 121 | middleware.ResponseSuccess(ctx, data) 122 | } 123 | -------------------------------------------------------------------------------- /controller/menu/menu.go: -------------------------------------------------------------------------------- 1 | package menu 2 | 3 | import ( 4 | "fmt" 5 | "github.com/gin-gonic/gin" 6 | "github.com/noovertime7/kubemanage/dto" 7 | "github.com/noovertime7/kubemanage/middleware" 8 | "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1" 9 | "github.com/noovertime7/kubemanage/pkg/globalError" 10 | "github.com/noovertime7/kubemanage/pkg/utils" 11 | ) 12 | 13 | // GetMenusByAuthID 14 | // @Tags AuthorityMenu 15 | // @Summary 获取用户动态路由 16 | // @Security ApiKeyAuth 17 | // @Produce application/json 18 | // @Param data body dto.Empty true "空" 19 | // @Success 200 {object} middleware.Response{data=dto.SysBaseMenusResponse,msg=string} "获取用户动态路由,返回包括系统菜单详情列表" 20 | // @Router /api/menu/:authID/getMenuByAuthID [get] 21 | func (m *menuController) GetMenusByAuthID(ctx *gin.Context) { 22 | authID, err := utils.ParseUint(ctx.Param("authID")) 23 | if err != nil || authID == 0 { 24 | v1.Log.ErrorWithCode(globalError.ParamBindError, fmt.Errorf("authID empty")) 25 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, fmt.Errorf("authID empty"))) 26 | } 27 | menus, err := v1.CoreV1.System().Menu().GetMenuByAuthorityID(ctx, authID) 28 | if err != nil { 29 | v1.Log.ErrorWithCode(globalError.GetError, err) 30 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.GetError, err)) 31 | return 32 | } 33 | middleware.ResponseSuccess(ctx, &dto.SysMenusResponse{Menus: menus}) 34 | } 35 | 36 | // GetBaseMenus 37 | // @Tags AuthorityMenu 38 | // @Summary 获取用户动态路由 39 | // @Security ApiKeyAuth 40 | // @Produce application/json 41 | // @Param data body dto.Empty true "空" 42 | // @Success 200 {object} middleware.Response{data=dto.SysBaseMenusResponse,msg=string} "获取系统菜单详情列表" 43 | // @Router /api/menu/getBaseMenuTree [get] 44 | func (m *menuController) GetBaseMenus(ctx *gin.Context) { 45 | menus, err := v1.CoreV1.System().Menu().GetBassMenu(ctx) 46 | if err != nil { 47 | v1.Log.ErrorWithCode(globalError.GetError, err) 48 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.GetError, err)) 49 | return 50 | } 51 | middleware.ResponseSuccess(ctx, menus) 52 | } 53 | 54 | // AddBaseMenu 55 | // @Tags Menu 56 | // @Summary 新增菜单 57 | // @Security ApiKeyAuth 58 | // @accept application/json 59 | // @Produce application/json 60 | // @Param data body model.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记" 61 | // @Success 200 {object} middleware.Response{msg=string} "新增菜单" 62 | // @Router /api/menu/add_base_menu [post] 63 | func (m *menuController) AddBaseMenu(ctx *gin.Context) { 64 | params := &dto.AddSysMenusInput{} 65 | if err := params.BindingValidParams(ctx); err != nil { 66 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 67 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 68 | } 69 | if err := v1.CoreV1.System().Menu().AddBaseMenu(ctx, params); err != nil { 70 | v1.Log.ErrorWithCode(globalError.CreateError, err) 71 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.CreateError, err)) 72 | return 73 | } 74 | middleware.ResponseSuccess(ctx, "添加成功") 75 | } 76 | 77 | // AddMenuAuthority 78 | // @Tags AuthorityMenu 79 | // @Summary 增加menu和角色关联关系 80 | // @Security ApiKeyAuth 81 | // @accept application/json 82 | // @Produce application/json 83 | // @Param data body dto.AddMenuAuthorityInput true "角色ID" 84 | // @Success 200 {object} response.Response{msg=string} "增加menu和角色关联关系" 85 | // @Router /api/menu/add_menu_authority [post] 86 | func (m *menuController) AddMenuAuthority(ctx *gin.Context) { 87 | params := &dto.AddMenuAuthorityInput{} 88 | if err := params.BindingValidParams(ctx); err != nil { 89 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 90 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 91 | } 92 | if err := v1.CoreV1.System().Menu().AddMenuAuthority(ctx, params.Menus, params.AuthorityId); err != nil { 93 | v1.Log.ErrorWithCode(globalError.ServerError, err) 94 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ServerError, err)) 95 | } 96 | middleware.ResponseSuccess(ctx, "添加成功") 97 | } 98 | -------------------------------------------------------------------------------- /controller/menu/menu_router.go: -------------------------------------------------------------------------------- 1 | package menu 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | type menuController struct{} 6 | 7 | func NewMenuRouter(ginEngine *gin.RouterGroup) { 8 | menu := menuController{} 9 | menu.initRoutes(ginEngine) 10 | } 11 | 12 | func (m *menuController) initRoutes(ginEngine *gin.RouterGroup) { 13 | menuRoute := ginEngine.Group("/menu") 14 | menu := &menuController{} 15 | { 16 | menuRoute.GET("/:authID/getMenuByAuthID", menu.GetMenusByAuthID) 17 | menuRoute.GET("/getBaseMenuTree", menu.GetBaseMenus) 18 | menuRoute.POST("/add_base_menu", menu.AddBaseMenu) 19 | menuRoute.POST("/add_menu_authority", menu.AddMenuAuthority) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /controller/operation/operation.go: -------------------------------------------------------------------------------- 1 | package operation 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1" 6 | 7 | "github.com/noovertime7/kubemanage/dto" 8 | "github.com/noovertime7/kubemanage/middleware" 9 | "github.com/noovertime7/kubemanage/pkg/globalError" 10 | "github.com/noovertime7/kubemanage/pkg/utils" 11 | ) 12 | 13 | // GetOperationRecordList 14 | // @Tags SysOperationRecord 15 | // @Summary 分页获取SysOperationRecord列表 16 | // @Security ApiKeyAuth 17 | // @accept application/json 18 | // @Produce application/json 19 | // @Param data query dto.OperationListInput true "页码, 每页大小, 搜索条件" 20 | // @Success 200 {object} middleware.Response{data=dto.OperationListOutPut,msg=string} "分页获取SysOperationRecord列表,返回包括列表,总数,页码,每页数量" 21 | // @Router /api/operation/get_operations [get] 22 | func (o *operationController) GetOperationRecordList(ctx *gin.Context) { 23 | params := &dto.OperationListInput{} 24 | if err := params.BindingValidParams(ctx); err != nil { 25 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 26 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 27 | return 28 | } 29 | data, err := v1.CoreV1.System().Operation().GetPageList(ctx, params) 30 | if err != nil { 31 | v1.Log.ErrorWithErr("查询失败", err) 32 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.GetError, err)) 33 | return 34 | } 35 | middleware.ResponseSuccess(ctx, data) 36 | } 37 | 38 | // DeleteOperationRecord 39 | // @Tags SysOperationRecord 40 | // @Summary 删除SysOperationRecord 41 | // @Security ApiKeyAuth 42 | // @accept application/json 43 | // @Produce application/json 44 | // @Param data body dto.Empty 45 | // @Success 200 {object} middleware.Response{msg=string} "删除SysOperationRecord" 46 | // @Router /api/operation/{id}/delete_operation [delete] 47 | func (o *operationController) DeleteOperationRecord(ctx *gin.Context) { 48 | recordId, err := utils.ParseInt(ctx.Param("id")) 49 | if err != nil { 50 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 51 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 52 | } 53 | if err := v1.CoreV1.System().Operation().DeleteRecord(ctx, recordId); err != nil { 54 | v1.Log.ErrorWithCode(globalError.DeleteError, err) 55 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.DeleteError, err)) 56 | } 57 | middleware.ResponseSuccess(ctx, "删除成功") 58 | } 59 | 60 | // DeleteOperationRecords 61 | // @Tags SysOperationRecord 62 | // @Summary 删除SysOperationRecord 63 | // @Security ApiKeyAuth 64 | // @accept application/json 65 | // @Produce application/json 66 | // @Param data body dto.IdsReq 67 | // @Success 200 {object} middleware.Response{msg=string} "删除SysOperationRecord" 68 | // @Router /api/operation/delete_operations [delete] 69 | func (o *operationController) DeleteOperationRecords(ctx *gin.Context) { 70 | params := &dto.IdsReq{} 71 | if err := params.BindingValidParams(ctx); err != nil { 72 | v1.Log.ErrorWithCode(globalError.ParamBindError, err) 73 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.ParamBindError, err)) 74 | return 75 | } 76 | if err := v1.CoreV1.System().Operation().DeleteRecords(ctx, params.Ids); err != nil { 77 | v1.Log.ErrorWithErr("批量删除失败", err) 78 | middleware.ResponseError(ctx, globalError.NewGlobalError(globalError.DeleteError, err)) 79 | return 80 | } 81 | middleware.ResponseSuccess(ctx, "删除成功") 82 | } 83 | -------------------------------------------------------------------------------- /controller/operation/operation_router.go: -------------------------------------------------------------------------------- 1 | package operation 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | type operationController struct{} 8 | 9 | func NewOperationRouter(ginEngine *gin.RouterGroup) { 10 | opera := operationController{} 11 | opera.initRoutes(ginEngine) 12 | } 13 | 14 | func (o *operationController) initRoutes(ginEngine *gin.RouterGroup) { 15 | operaRoute := ginEngine.Group("/operation") 16 | opera := &operationController{} 17 | { 18 | operaRoute.GET("/get_operations", opera.GetOperationRecordList) 19 | operaRoute.DELETE("/:id/delete_operation", opera.DeleteOperationRecord) 20 | operaRoute.POST("/delete_operations", opera.DeleteOperationRecords) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /controller/other/swagger.go: -------------------------------------------------------------------------------- 1 | package other 2 | 3 | // @title Swagger Example API 4 | // @version 1.0 5 | // @description This is a sample server celler server. 6 | // @termsOfService http://swagger.io/terms/ 7 | 8 | // @contact.name API Support 9 | // @contact.url http://www.swagger.io/support 10 | // @contact.email support@swagger.io 11 | 12 | // @license.name Apache 2.0 13 | // @license.url http://www.apache.org/licenses/LICENSE-2.0.html 14 | 15 | // @host localhost:8080 16 | // @BasePath /api/v1 17 | // @query.collection.format multi 18 | 19 | // @securityDefinitions.basic BasicAuth 20 | 21 | // @securityDefinitions.apikey ApiKeyAuth 22 | // @in header 23 | // @name Authorization 24 | 25 | // @securitydefinitions.oauth2.application OAuth2Application 26 | // @tokenUrl https://example.com/oauth/token 27 | // @scope.write Grants write access 28 | // @scope.admin Grants read and write access to administrative information 29 | 30 | // @securitydefinitions.oauth2.implicit OAuth2Implicit 31 | // @authorizationurl https://example.com/oauth/authorize 32 | // @scope.write Grants write access 33 | // @scope.admin Grants read and write access to administrative information 34 | 35 | // @securitydefinitions.oauth2.password OAuth2Password 36 | // @tokenUrl https://example.com/oauth/token 37 | // @scope.read Grants read access 38 | // @scope.write Grants write access 39 | // @scope.admin Grants read and write access to administrative information 40 | 41 | // @securitydefinitions.oauth2.accessCode OAuth2AccessCode 42 | // @tokenUrl https://example.com/oauth/token 43 | // @authorizationurl https://example.com/oauth/authorize 44 | // @scope.admin Grants read and write access to administrative information 45 | 46 | // @x-extension-openapi {"example": "value on a json format"} 47 | 48 | import ( 49 | "github.com/gin-gonic/gin" 50 | "github.com/noovertime7/kubemanage/docs" 51 | swaggerFiles "github.com/swaggo/files" 52 | ginSwagger "github.com/swaggo/gin-swagger" 53 | ) 54 | 55 | func NewSwaggarRoute(ginEngine *gin.RouterGroup) { 56 | // programmatically set swagger info 57 | docs.SwaggerInfo.Title = "Kubemanage API" 58 | docs.SwaggerInfo.Description = "Kubemanage" 59 | docs.SwaggerInfo.Version = "1.0" 60 | docs.SwaggerInfo.Host = "127.0.0.1:6180" 61 | docs.SwaggerInfo.BasePath = "" 62 | docs.SwaggerInfo.Schemes = []string{"http", "https"} 63 | ginEngine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) 64 | } 65 | -------------------------------------------------------------------------------- /controller/user/user_router.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import "github.com/gin-gonic/gin" 4 | 5 | type userController struct{} 6 | 7 | func NewUserRouter(ginEngine *gin.RouterGroup) { 8 | u := userController{} 9 | u.initRoutes(ginEngine) 10 | } 11 | 12 | func (u *userController) initRoutes(ginEngine *gin.RouterGroup) { 13 | userRoute := ginEngine.Group("/user") 14 | user := &userController{} 15 | { 16 | userRoute.POST("/login", user.Login) 17 | userRoute.GET("/loginout", user.LoginOut) 18 | userRoute.GET("/getinfo", user.GetUserInfo) 19 | userRoute.PUT("/:id/set_auth", user.SetUserAuthority) 20 | userRoute.DELETE("/:id/delete_user", user.DeleteUser) 21 | userRoute.POST("/:id/change_pwd", user.ChangePassword) 22 | userRoute.PUT("/:id/reset_pwd", user.ResetPassword) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /dao/api/api.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | "context" 5 | 6 | "gorm.io/gorm" 7 | 8 | "github.com/noovertime7/kubemanage/dao/model" 9 | ) 10 | 11 | type APi interface { 12 | FindList(ctx context.Context, search model.SysApi) ([]model.SysApi, error) 13 | } 14 | 15 | var _ APi = &api{} 16 | 17 | func NewApi(db *gorm.DB) APi { 18 | return &api{db: db} 19 | } 20 | 21 | type api struct { 22 | db *gorm.DB 23 | } 24 | 25 | func (a *api) FindList(ctx context.Context, search model.SysApi) ([]model.SysApi, error) { 26 | var out []model.SysApi 27 | return out, a.db.WithContext(ctx).Where(&search).Find(&out).Error 28 | } 29 | -------------------------------------------------------------------------------- /dao/authority/authority.go: -------------------------------------------------------------------------------- 1 | package authority 2 | 3 | import ( 4 | "context" 5 | "github.com/noovertime7/kubemanage/dao/model" 6 | "github.com/noovertime7/kubemanage/dto" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | type Authority interface { 11 | Find(ctx context.Context, authInfo *model.SysAuthority) (*model.SysAuthority, error) 12 | FindList(ctx context.Context, authInfo *model.SysAuthority) ([]*model.SysAuthority, error) 13 | Save(ctx context.Context, authInfo *model.SysAuthority) error 14 | Updates(ctx context.Context, authInfo *model.SysAuthority) error 15 | 16 | SetMenuAuthority(ctx context.Context, authInfo *model.SysAuthority) error 17 | PageList(ctx context.Context, params dto.PageInfo) ([]model.SysAuthority, int64, error) 18 | } 19 | 20 | var _ Authority = &authority{} 21 | 22 | type authority struct { 23 | db *gorm.DB 24 | } 25 | 26 | func NewAuthority(db *gorm.DB) *authority { 27 | return &authority{db: db} 28 | } 29 | 30 | func (a *authority) Find(ctx context.Context, authInfo *model.SysAuthority) (*model.SysAuthority, error) { 31 | var out *model.SysAuthority 32 | return out, a.db.WithContext(ctx).Where(authInfo).Find(out).Error 33 | } 34 | 35 | func (a *authority) FindList(ctx context.Context, authInfo *model.SysAuthority) ([]*model.SysAuthority, error) { 36 | var out []*model.SysAuthority 37 | return out, a.db.WithContext(ctx).Where(&authInfo).Find(&out).Error 38 | } 39 | 40 | func (a *authority) PageList(ctx context.Context, params dto.PageInfo) ([]model.SysAuthority, int64, error) { 41 | var total int64 = 0 42 | limit := params.PageSize 43 | offset := params.PageSize * (params.Page - 1) 44 | query := a.db.WithContext(ctx) 45 | var list []model.SysAuthority 46 | // 如果有条件搜索 下方会自动创建搜索语句 47 | if params.Keyword != "" { 48 | query = query.Where("authority_name = ?", params.Keyword) 49 | } 50 | if err := query.Find(&list).Count(&total).Error; err != nil { 51 | return nil, 0, err 52 | } 53 | 54 | if err := query.Order("authority_id desc").Limit(limit).Offset(offset).Find(&list).Error; err != nil { 55 | return nil, 0, err 56 | } 57 | return list, total, nil 58 | } 59 | 60 | func (a *authority) Save(ctx context.Context, authInfo *model.SysAuthority) error { 61 | return a.db.WithContext(ctx).Create(authInfo).Error 62 | } 63 | 64 | func (a *authority) Updates(ctx context.Context, authInfo *model.SysAuthority) error { 65 | return a.db.WithContext(ctx).Updates(authInfo).Error 66 | } 67 | 68 | // SetMenuAuthority 菜单与角色绑定 69 | func (a *authority) SetMenuAuthority(ctx context.Context, authInfo *model.SysAuthority) error { 70 | var s model.SysAuthority 71 | a.db.WithContext(ctx).Preload("SysBaseMenus").First(&s, "authority_id = ?", authInfo.AuthorityId) 72 | return a.db.WithContext(ctx).Model(&s).Association("SysBaseMenus").Replace(&authInfo.SysBaseMenus) 73 | } 74 | -------------------------------------------------------------------------------- /dao/authority/sys_authority_menus.go: -------------------------------------------------------------------------------- 1 | package authority 2 | 3 | import ( 4 | "context" 5 | "github.com/noovertime7/kubemanage/dao/model" 6 | "gorm.io/gorm" 7 | ) 8 | 9 | type AuthorityMenu interface { 10 | FindList(ctx context.Context, in *model.SysAuthorityMenu) ([]*model.SysAuthorityMenu, error) 11 | } 12 | 13 | var _ AuthorityMenu = &authorityMenu{} 14 | 15 | type authorityMenu struct { 16 | db *gorm.DB 17 | } 18 | 19 | func NewAuthorityMenu(db *gorm.DB) *authorityMenu { 20 | return &authorityMenu{db: db} 21 | } 22 | 23 | func (a authorityMenu) FindList(ctx context.Context, in *model.SysAuthorityMenu) ([]*model.SysAuthorityMenu, error) { 24 | var out []*model.SysAuthorityMenu 25 | return out, a.db.WithContext(ctx).Where(&in).Find(&out).Error 26 | } 27 | -------------------------------------------------------------------------------- /dao/factory.go: -------------------------------------------------------------------------------- 1 | package dao 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | 6 | "github.com/noovertime7/kubemanage/dao/api" 7 | "github.com/noovertime7/kubemanage/dao/authority" 8 | "github.com/noovertime7/kubemanage/dao/menu" 9 | "github.com/noovertime7/kubemanage/dao/operation" 10 | "github.com/noovertime7/kubemanage/dao/user" 11 | "github.com/noovertime7/kubemanage/dao/workflow" 12 | ) 13 | 14 | // ShareDaoFactory 数据库抽象工厂 包含所有数据操作接口 15 | type ShareDaoFactory interface { 16 | GetDB() *gorm.DB 17 | WorkFlow() workflow.WorkFlowInterface 18 | User() user.User 19 | Api() api.APi 20 | Authority() authority.Authority 21 | AuthorityMenu() authority.AuthorityMenu 22 | BaseMenu() menu.BaseMenu 23 | Opera() operation.Operation 24 | } 25 | 26 | func NewShareDaoFactory(db *gorm.DB) ShareDaoFactory { 27 | return &shareDaoFactory{db: db} 28 | } 29 | 30 | var _ ShareDaoFactory = &shareDaoFactory{} 31 | 32 | type shareDaoFactory struct { 33 | db *gorm.DB 34 | } 35 | 36 | func (s *shareDaoFactory) GetDB() *gorm.DB { 37 | return s.db 38 | } 39 | 40 | func (s *shareDaoFactory) WorkFlow() workflow.WorkFlowInterface { 41 | return workflow.NewWorkFlow(s.db) 42 | } 43 | 44 | func (s *shareDaoFactory) User() user.User { 45 | return user.NewUser(s.db) 46 | } 47 | 48 | func (s *shareDaoFactory) Api() api.APi { 49 | return api.NewApi(s.db) 50 | } 51 | 52 | func (s *shareDaoFactory) Authority() authority.Authority { 53 | return authority.NewAuthority(s.db) 54 | } 55 | 56 | func (s *shareDaoFactory) AuthorityMenu() authority.AuthorityMenu { 57 | return authority.NewAuthorityMenu(s.db) 58 | } 59 | 60 | func (s *shareDaoFactory) BaseMenu() menu.BaseMenu { 61 | return menu.NewBaseMenu(s.db) 62 | } 63 | 64 | func (s *shareDaoFactory) Opera() operation.Operation { 65 | return operation.NewOperation(s.db) 66 | } 67 | -------------------------------------------------------------------------------- /dao/menu/basemenu.go: -------------------------------------------------------------------------------- 1 | package menu 2 | 3 | import ( 4 | "context" 5 | "github.com/noovertime7/kubemanage/dao/model" 6 | "gorm.io/gorm" 7 | ) 8 | 9 | type BaseMenu interface { 10 | Find(ctx context.Context, in *model.SysBaseMenu) (*model.SysBaseMenu, error) 11 | FindIn(ctx context.Context, in []string) ([]*model.SysBaseMenu, error) 12 | FindList(ctx context.Context, in *model.SysBaseMenu) ([]model.SysBaseMenu, error) 13 | Save(ctx context.Context, in *model.SysBaseMenu) error 14 | Updates(ctx context.Context, in *model.SysBaseMenu) error 15 | } 16 | 17 | type baseMenu struct { 18 | db *gorm.DB 19 | } 20 | 21 | func NewBaseMenu(db *gorm.DB) *baseMenu { 22 | return &baseMenu{db: db} 23 | } 24 | 25 | func (b baseMenu) Find(ctx context.Context, in *model.SysBaseMenu) (*model.SysBaseMenu, error) { 26 | var out *model.SysBaseMenu 27 | return out, b.db.WithContext(ctx).Where(in).Find(&out).Error 28 | } 29 | 30 | func (b baseMenu) FindIn(ctx context.Context, in []string) ([]*model.SysBaseMenu, error) { 31 | //做一下排序 32 | var out []*model.SysBaseMenu 33 | return out, b.db.WithContext(ctx).Where("id in (?)", in).Order("sort").Find(&out).Error 34 | } 35 | 36 | func (b baseMenu) FindList(ctx context.Context, in *model.SysBaseMenu) ([]model.SysBaseMenu, error) { 37 | var out []model.SysBaseMenu 38 | return out, b.db.WithContext(ctx).Order("sort").Where(in).Find(&out).Error 39 | } 40 | 41 | func (b baseMenu) Save(ctx context.Context, in *model.SysBaseMenu) error { 42 | return b.db.WithContext(ctx).Create(in).Error 43 | } 44 | 45 | func (b baseMenu) Updates(ctx context.Context, in *model.SysBaseMenu) error { 46 | return b.db.WithContext(ctx).Updates(in).Error 47 | } 48 | -------------------------------------------------------------------------------- /dao/model/authorities_menus.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | "gorm.io/gorm" 7 | ) 8 | 9 | func init() { 10 | RegisterInitializer(MenuAuthorityOrder, &MenuAuthority{}) 11 | } 12 | 13 | type MenuAuthority struct{} 14 | 15 | func (i MenuAuthority) TableName() string { 16 | return "sys_menu_authorities" 17 | } 18 | 19 | func (i *MenuAuthority) MigrateTable(ctx context.Context, db *gorm.DB) error { 20 | //数据由gorm填充不需要手动迁移 21 | return nil 22 | } 23 | 24 | func (i *MenuAuthority) InitData(ctx context.Context, db *gorm.DB) error { 25 | var ( 26 | adminRole = SysAuthorityEntities[0] 27 | userRole = SysAuthorityEntities[1] 28 | userSubRole = SysAuthorityEntities[2] 29 | err error 30 | ok bool 31 | ) 32 | ok, err = i.IsInitData(ctx, db) 33 | if err != nil || ok { 34 | return err 35 | } 36 | // admin 拥有全部菜单权限 37 | if err = db.Model(&adminRole).Association("SysBaseMenus").Append(SysBaseMenuEntities); err != nil { 38 | return err 39 | } 40 | 41 | // userRole cmdb菜单 42 | menu8881 := SysBaseMenuEntities[5:7] 43 | menu8881 = append(menu8881, SysBaseMenuEntities[0]) 44 | menu8881 = append(menu8881, SysBaseMenuEntities[1]) 45 | if err = db.Model(&userRole).Association("SysBaseMenus").Replace(menu8881); err != nil { 46 | return err 47 | } 48 | 49 | // userSubRole 50 | if err = db.Model(&userSubRole).Association("SysBaseMenus").Replace(SysBaseMenuEntities[:6]); err != nil { 51 | return err 52 | } 53 | if err = db.Model(&userSubRole).Association("SysBaseMenus").Append(SysBaseMenuEntities[5:6]); err != nil { 54 | return err 55 | } 56 | return nil 57 | } 58 | 59 | func (i *MenuAuthority) IsInitData(ctx context.Context, db *gorm.DB) (bool, error) { 60 | auth := &SysAuthority{} 61 | if err := db.WithContext(ctx).Model(auth).Where("authority_id = ?", pkg.AdminDefaultAuth).Preload("SysBaseMenus").Find(auth).Error; err != nil { 62 | return false, err 63 | } 64 | return len(auth.SysBaseMenus) > 0, nil 65 | } 66 | 67 | func (i *MenuAuthority) TableCreated(ctx context.Context, db *gorm.DB) bool { 68 | return false // always replace 69 | } 70 | -------------------------------------------------------------------------------- /dao/model/authority.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | "github.com/pkg/errors" 6 | "gorm.io/gorm" 7 | ) 8 | 9 | func init() { 10 | RegisterInitializer(SysAuthorityOrder, &SysAuthority{}) 11 | } 12 | 13 | type SysAuthority struct { 14 | CommonModel 15 | AuthorityId uint `json:"authorityId" gorm:"not null;unique;primary_key;comment:角色ID;size:90"` // 角色ID 16 | AuthorityName string `json:"authorityName" gorm:"comment:角色名"` // 角色名 17 | ParentId uint `json:"parentId" gorm:"comment:父角色ID"` // 父角色ID 18 | DataAuthorityId []*SysAuthority `json:"dataAuthorityId" gorm:"many2many:sys_data_authority_id;"` 19 | Children []SysAuthority `json:"children" gorm:"-"` 20 | SysBaseMenus []SysBaseMenu `json:"menus" gorm:"many2many:sys_authority_menus;"` 21 | Users []SysUser `json:"-" gorm:"many2many:sys_user_authority;"` 22 | DefaultRouter string `json:"defaultRouter" gorm:"comment:默认菜单;default:dashboard"` // 默认菜单(默认dashboard) 23 | } 24 | 25 | func (s *SysAuthority) MigrateTable(ctx context.Context, db *gorm.DB) error { 26 | return db.WithContext(ctx).AutoMigrate(&s) 27 | } 28 | 29 | func (s *SysAuthority) InitData(ctx context.Context, db *gorm.DB) error { 30 | ok, err := s.IsInitData(ctx, db) 31 | if err != nil || ok { 32 | return err 33 | } 34 | if err := db.Model(&SysAuthorityEntities[0]).Association("DataAuthorityId").Replace( 35 | []*SysAuthority{ 36 | {AuthorityId: 111}, 37 | {AuthorityId: 222}, 38 | {AuthorityId: 2221}, 39 | }); err != nil { 40 | return errors.Wrapf(err, "%s表数据初始化失败!", 41 | db.Model(&SysAuthorityEntities[0]).Association("DataAuthorityId").Relationship.JoinTable.Name) 42 | } 43 | if err := db.Model(&SysAuthorityEntities[1]).Association("DataAuthorityId").Replace( 44 | []*SysAuthority{ 45 | {AuthorityId: 222}, 46 | {AuthorityId: 2221}, 47 | }); err != nil { 48 | return errors.Wrapf(err, "%s表数据初始化失败!", 49 | db.Model(&SysAuthorityEntities[1]).Association("DataAuthorityId").Relationship.JoinTable.Name) 50 | } 51 | return nil 52 | } 53 | 54 | func (s *SysAuthority) IsInitData(ctx context.Context, db *gorm.DB) (bool, error) { 55 | var out *SysAuthority 56 | if err := db.WithContext(ctx).Where("authority_id = '111' ").Find(&out).Error; err != nil { 57 | return false, nil 58 | } 59 | return out.AuthorityId != 0, nil 60 | } 61 | 62 | func (s *SysAuthority) TableCreated(ctx context.Context, db *gorm.DB) bool { 63 | return db.WithContext(ctx).Migrator().HasTable(&s) 64 | } 65 | 66 | func (*SysAuthority) TableName() string { 67 | return "sys_authorities" 68 | } 69 | -------------------------------------------------------------------------------- /dao/model/casbin.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | "github.com/pkg/errors" 6 | "gorm.io/gorm" 7 | 8 | adapter "github.com/casbin/gorm-adapter/v3" 9 | ) 10 | 11 | func init() { 12 | RegisterInitializer(CasbinInitOrder, &casbinDateBase{}) 13 | } 14 | 15 | type casbinDateBase struct{} 16 | 17 | func (c casbinDateBase) TableName() string { 18 | var entity adapter.CasbinRule 19 | return entity.TableName() 20 | } 21 | 22 | func (c casbinDateBase) MigrateTable(ctx context.Context, db *gorm.DB) error { 23 | return db.WithContext(ctx).AutoMigrate(&adapter.CasbinRule{}) 24 | } 25 | 26 | func (c casbinDateBase) InitData(ctx context.Context, db *gorm.DB) error { 27 | ok, err := c.IsInitData(ctx, db) 28 | if err != nil || ok { 29 | return err 30 | } 31 | return db.WithContext(ctx).Create(CasbinApi).Error 32 | } 33 | 34 | func (c casbinDateBase) IsInitData(ctx context.Context, db *gorm.DB) (bool, error) { 35 | // TODO 管理员用户判断登陆接口是否具有权限 36 | if errors.Is(gorm.ErrRecordNotFound, db.WithContext(ctx).Where(adapter.CasbinRule{Ptype: "p", V0: "222", V1: "/api/user/login", V2: "POST"}). 37 | First(&adapter.CasbinRule{}).Error) { // 判断是否存在数据 38 | return false, nil 39 | } 40 | return true, nil 41 | } 42 | 43 | func (c casbinDateBase) TableCreated(ctx context.Context, db *gorm.DB) bool { 44 | return db.WithContext(ctx).Migrator().HasTable(&adapter.CasbinRule{}) 45 | } 46 | -------------------------------------------------------------------------------- /dao/model/common.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | "time" 6 | ) 7 | 8 | type CommonModel struct { 9 | CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` 10 | UpdatedAt time.Time `json:"updated_at" gorm:"column:updated_at"` 11 | DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` 12 | } 13 | -------------------------------------------------------------------------------- /dao/model/db_initializer.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | // DBInitializer 用于初始化数据表与数据 9 | type DBInitializer interface { 10 | TableName() string 11 | MigrateTable(ctx context.Context, db *gorm.DB) error 12 | InitData(ctx context.Context, db *gorm.DB) error 13 | IsInitData(ctx context.Context, db *gorm.DB) (bool, error) 14 | TableCreated(ctx context.Context, db *gorm.DB) bool 15 | } 16 | 17 | var InitializerList []*OrderedInitializer 18 | 19 | // OrderedInitializer 组合一个顺序字段,以供排序 20 | type OrderedInitializer struct { 21 | Order int 22 | DBInitializer 23 | } 24 | 25 | func RegisterInitializer(order int, c DBInitializer) { 26 | InitializerList = append(InitializerList, &OrderedInitializer{ 27 | Order: order, 28 | DBInitializer: c, 29 | }) 30 | InitializerList = bubbleSort(InitializerList) 31 | } 32 | 33 | // 冒泡排序,根据order选择初始化顺序 34 | func bubbleSort(s []*OrderedInitializer) []*OrderedInitializer { 35 | n := len(s) 36 | for i := 0; i < n-1; i++ { 37 | for j := 0; j < n-i-1; j++ { 38 | if s[j].Order > s[j+1].Order { 39 | s[j], s[j+1] = s[j+1], s[j] 40 | } 41 | } 42 | } 43 | return s 44 | } 45 | -------------------------------------------------------------------------------- /dao/model/operation.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "gorm.io/gorm" 8 | ) 9 | 10 | func init() { 11 | RegisterInitializer(OperatorationOrder, &SysOperationRecord{}) 12 | } 13 | 14 | type SysOperationRecord struct { 15 | ID int `gorm:"column:id;primary_key;AUTO_INCREMENT;not null" json:"id"` 16 | Ip string `json:"ip" form:"ip" gorm:"column:ip;comment:请求ip"` // 请求ip 17 | Method string `json:"method" form:"method" gorm:"column:method;comment:请求方法"` // 请求方法 18 | Path string `json:"path" form:"path" gorm:"column:path;comment:请求路径"` // 请求路径 19 | Status int `json:"status" form:"status" gorm:"column:status;comment:请求状态"` // 请求状态 20 | Latency time.Duration `json:"latency" form:"latency" gorm:"column:latency;comment:延迟" swaggertype:"string"` // 延迟 21 | Agent string `json:"agent" form:"agent" gorm:"column:agent;comment:代理"` // 代理 22 | ErrorMessage string `json:"error_message" form:"error_message" gorm:"column:error_message;comment:错误信息"` // 错误信息 23 | Body string `json:"body" form:"body" gorm:"type:text;column:body;comment:请求Body"` // 请求Body 24 | Resp string `json:"resp" form:"resp" gorm:"type:text;column:resp;comment:响应Body"` // 响应Body 25 | UserID int `json:"user_id" form:"user_id" gorm:"column:user_id;comment:用户id"` // 用户id 26 | User SysUser `json:"user"` 27 | CommonModel 28 | } 29 | 30 | func (s *SysOperationRecord) MigrateTable(ctx context.Context, db *gorm.DB) error { 31 | return db.WithContext(ctx).AutoMigrate(s) 32 | } 33 | 34 | func (s *SysOperationRecord) InitData(ctx context.Context, db *gorm.DB) error { 35 | // 审计表,不需要初始化数据 36 | return nil 37 | } 38 | 39 | func (s *SysOperationRecord) IsInitData(ctx context.Context, db *gorm.DB) (bool, error) { 40 | return true, nil 41 | } 42 | 43 | func (s *SysOperationRecord) TableCreated(ctx context.Context, db *gorm.DB) bool { 44 | return db.WithContext(ctx).Migrator().HasTable(&s) 45 | } 46 | 47 | func (s *SysOperationRecord) TableName() string { 48 | return "sys_operation_record" 49 | } 50 | -------------------------------------------------------------------------------- /dao/model/sys_api.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | func init() { 9 | RegisterInitializer(SysApisInitOrder, &SysApi{}) 10 | } 11 | 12 | type SysApi struct { 13 | ID int `gorm:"column:id;primary_key;AUTO_INCREMENT;not null" json:"id"` 14 | Path string `json:"path" gorm:"comment:api路径"` // api路径 15 | Description string `json:"description" gorm:"comment:api中文描述"` // api中文描述 16 | ApiGroup string `json:"apiGroup" gorm:"comment:api组"` // api组 17 | Method string `json:"method" gorm:"default:POST;comment:方法"` // 方法:创建POST(默认)|查看GET|更新PUT|删除DELETE 18 | CommonModel 19 | } 20 | 21 | func (a *SysApi) MigrateTable(ctx context.Context, db *gorm.DB) error { 22 | return db.WithContext(ctx).AutoMigrate(a) 23 | } 24 | 25 | func (a *SysApi) InitData(ctx context.Context, db *gorm.DB) error { 26 | ok, err := a.IsInitData(ctx, db) 27 | if err != nil || ok { 28 | return err 29 | } 30 | return db.WithContext(ctx).Create(SysApis).Error 31 | } 32 | 33 | func (a *SysApi) IsInitData(ctx context.Context, db *gorm.DB) (bool, error) { 34 | var out *SysApi 35 | // TODO 验证方式统一优化 36 | if err := db.WithContext(ctx).Where("path = '/api/user/login' ").Find(&out).Error; err != nil { 37 | return false, nil 38 | } 39 | return out.ID != 0, nil 40 | } 41 | 42 | func (a *SysApi) TableCreated(ctx context.Context, db *gorm.DB) bool { 43 | return db.WithContext(ctx).Migrator().HasTable(a) 44 | } 45 | 46 | func (*SysApi) TableName() string { 47 | return "sys_apis" 48 | } 49 | -------------------------------------------------------------------------------- /dao/model/sys_authority_menu.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type SysMenu struct { 4 | SysBaseMenu 5 | MenuId string `json:"menuId" gorm:"comment:菜单ID"` 6 | AuthorityId uint `json:"-" gorm:"comment:角色ID"` 7 | Children []SysMenu `json:"children" gorm:"-"` 8 | } 9 | 10 | type SysAuthorityMenu struct { 11 | MenuId string `json:"menuId" gorm:"comment:菜单ID;column:sys_base_menu_id"` 12 | AuthorityId string `json:"-" gorm:"comment:角色ID;column:sys_authority_authority_id"` 13 | } 14 | 15 | func (s SysAuthorityMenu) TableName() string { 16 | return "sys_authority_menus" 17 | } 18 | -------------------------------------------------------------------------------- /dao/model/sys_base_menu.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | "github.com/pkg/errors" 6 | "gorm.io/gorm" 7 | ) 8 | 9 | type SysBaseMenu struct { 10 | ID int `gorm:"column:id;primary_key;AUTO_INCREMENT;not null" json:"id"` 11 | MenuLevel uint `json:"-"` 12 | ParentId string `json:"parentId" gorm:"comment:父菜单ID"` // 父菜单ID 13 | Path string `json:"path" gorm:"comment:路由path"` // 路由path 14 | Name string `json:"name" gorm:"comment:路由name"` // 路由name 15 | Hidden bool `json:"hidden" gorm:"comment:是否在列表隐藏"` // 是否在列表隐藏 16 | Disabled bool `json:"disabled" gorm:"comment:是否禁止修改菜单"` // 是否在列表隐藏 17 | Sort int `json:"sort" gorm:"comment:排序标记"` // 排序标记 18 | Children []SysBaseMenu `json:"children" gorm:"-"` 19 | Meta `json:"meta" gorm:"embedded;comment:附加属性"` // 附加属性 20 | SysAuthoritys []SysAuthority `json:"authoritys" gorm:"many2many:sys_authority_menus;"` 21 | CommonModel 22 | } 23 | 24 | type Meta struct { 25 | ActiveName string `json:"activeName" gorm:"comment:高亮菜单"` 26 | KeepAlive bool `json:"keepAlive" gorm:"comment:是否缓存"` // 是否缓存 27 | Title string `json:"title" gorm:"comment:菜单名"` // 菜单名 28 | Icon string `json:"icon" gorm:"comment:菜单图标"` // 菜单图标 29 | CloseTab bool `json:"closeTab" gorm:"comment:自动关闭tab"` // 自动关闭tab 30 | } 31 | 32 | func init() { 33 | RegisterInitializer(SysBaseMenuOrder, &SysBaseMenu{}) 34 | } 35 | 36 | func (m *SysBaseMenu) MigrateTable(ctx context.Context, db *gorm.DB) error { 37 | return db.WithContext(ctx).AutoMigrate(&m) 38 | } 39 | 40 | func (m *SysBaseMenu) IsInitData(ctx context.Context, db *gorm.DB) (bool, error) { 41 | var out *SysBaseMenu 42 | if err := db.WithContext(ctx).Where("path = 'dashboard' ").Find(&out).Error; err != nil { 43 | return false, nil 44 | } 45 | return out.ID != 0, nil 46 | } 47 | 48 | func (m *SysBaseMenu) InitData(ctx context.Context, db *gorm.DB) error { 49 | ok, err := m.IsInitData(ctx, db) 50 | if err != nil || ok { 51 | return err 52 | } 53 | if !ok { 54 | if err := db.WithContext(ctx).Create(&SysBaseMenuEntities).Error; err != nil { 55 | menu := SysBaseMenu{} 56 | return errors.Wrap(err, menu.TableName()+"表数据初始化失败!") 57 | } 58 | } 59 | return nil 60 | } 61 | 62 | func (m *SysBaseMenu) TableCreated(ctx context.Context, db *gorm.DB) bool { 63 | return db.WithContext(ctx).Migrator().HasTable(&m) 64 | } 65 | 66 | func (*SysBaseMenu) TableName() string { 67 | return "sys_base_menus" 68 | } 69 | -------------------------------------------------------------------------------- /dao/model/user.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | uuid "github.com/satori/go.uuid" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | type SysUser struct { 11 | ID int `gorm:"column:id;primary_key;AUTO_INCREMENT;not null" json:"id"` 12 | UUID uuid.UUID `json:"uuid" gorm:"index;comment:用户UUID"` // 用户UUID 13 | UserName string `json:"userName" gorm:"index;comment:用户登录名"` // 用户登录名 14 | Password string `json:"-" gorm:"comment:用户登录密码"` // 用户登录密码 15 | NickName string `json:"nickName" gorm:"default:系统用户;comment:用户昵称"` // 用户昵称 16 | SideMode string `json:"sideMode" gorm:"default:dark;comment:用户侧边主题"` // 用户侧边主题 17 | Avatar string `json:"avatar" gorm:"default:https://qmplusimg.henrongyi.top/gva_header.jpg;comment:用户头像"` // 用户头像 18 | BaseColor string `json:"baseColor" gorm:"default:#fff;comment:基础颜色"` // 基础颜色 19 | ActiveColor string `json:"activeColor" gorm:"default:#1890ff;comment:活跃颜色"` // 活跃颜色 20 | AuthorityId uint `json:"authorityId" gorm:"default:2222;comment:用户角色ID"` // 用户角色ID 21 | Authority SysAuthority `json:"authority" gorm:"foreignKey:AuthorityId;references:AuthorityId;comment:用户角色"` 22 | Authorities []SysAuthority `json:"authorities" gorm:"many2many:sys_user_authority;"` 23 | Phone string `json:"phone" gorm:"comment:用户手机号"` // 用户手机号 24 | Email string `json:"email" gorm:"comment:用户邮箱"` // 用户邮箱 25 | Enable int `json:"enable" gorm:"default:1;comment:用户是否被冻结 1正常 2冻结"` //用户是否被冻结 1正常 2冻结 26 | Status sql.NullInt64 `gorm:"column:status;type:int(11);comment:0离线;1在线" json:"status"` 27 | CommonModel 28 | } 29 | 30 | func init() { 31 | RegisterInitializer(SysUserOrder, &SysUser{}) 32 | } 33 | 34 | func (u *SysUser) TableName() string { 35 | return "sys_users" 36 | } 37 | 38 | func (u *SysUser) InitData(ctx context.Context, db *gorm.DB) error { 39 | ok, err := u.IsInitData(ctx, db) 40 | if err != nil || ok { 41 | return err 42 | } 43 | if err := db.WithContext(ctx).Create(SysUserEntities).Error; err != nil { 44 | return err 45 | } 46 | //管理员用户 47 | if err := db.Model(&SysUserEntities[0]).Association("Authorities").Replace(SysAuthorityEntities); err != nil { 48 | return err 49 | } 50 | //普通用户 51 | if err := db.Model(&SysUserEntities[1]).Association("Authorities").Replace(SysAuthorityEntities[:1]); err != nil { 52 | return err 53 | } 54 | return nil 55 | } 56 | 57 | // IsInitData 判断是否admin初始化成功 58 | func (u *SysUser) IsInitData(ctx context.Context, db *gorm.DB) (bool, error) { 59 | adminOk, err := u.isAdminInit(ctx, db) 60 | if err != nil { 61 | return false, nil 62 | } 63 | return adminOk, nil 64 | } 65 | 66 | func (u *SysUser) isAdminInit(ctx context.Context, db *gorm.DB) (bool, error) { 67 | var out *SysUser 68 | if err := db.WithContext(ctx).Where("user_name = 'admin' ").Find(&out).Error; err != nil { 69 | return false, err 70 | } 71 | return out.ID != 0, nil 72 | } 73 | 74 | func (u *SysUser) MigrateTable(ctx context.Context, db *gorm.DB) error { 75 | return db.WithContext(ctx).AutoMigrate(&u) 76 | } 77 | 78 | func (u *SysUser) TableCreated(ctx context.Context, db *gorm.DB) bool { 79 | return db.WithContext(ctx).Migrator().HasTable(u) 80 | } 81 | -------------------------------------------------------------------------------- /dao/model/workflow.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "context" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | func init() { 9 | RegisterInitializer(WorkFlowOrder, &Workflow{}) 10 | } 11 | 12 | type Workflow struct { 13 | ID int `gorm:"column:id;primary_key;AUTO_INCREMENT;not null" json:"id"` 14 | Name string `json:"name" gorm:"column:name"` 15 | NameSpace string `json:"namespace" gorm:"column:namespace"` 16 | Replicas int32 `json:"replicas" gorm:"column:replicas"` 17 | Deployment string `json:"deployment" gorm:"column:deployment"` 18 | Service string `json:"service" gorm:"column:service"` 19 | Ingress string `json:"ingress" gorm:"column:ingress"` 20 | ServiceType string `json:"service_type" gorm:"column:service_type"` 21 | CommonModel 22 | } 23 | 24 | func (w *Workflow) MigrateTable(ctx context.Context, db *gorm.DB) error { 25 | return db.WithContext(ctx).AutoMigrate(&w) 26 | } 27 | 28 | func (w *Workflow) IsInitData(ctx context.Context, db *gorm.DB) (bool, error) { 29 | return true, nil 30 | } 31 | 32 | func (w *Workflow) InitData(ctx context.Context, db *gorm.DB) error { 33 | return nil 34 | } 35 | 36 | func (w *Workflow) TableCreated(ctx context.Context, db *gorm.DB) bool { 37 | return db.WithContext(ctx).Migrator().HasTable(w) 38 | } 39 | 40 | func (w *Workflow) TableName() string { 41 | return "t_workflow" 42 | } 43 | 44 | func GetWorkflowTableName() string { 45 | temp := &Workflow{} 46 | return temp.TableName() 47 | } 48 | -------------------------------------------------------------------------------- /dao/operation/operation.go: -------------------------------------------------------------------------------- 1 | package operation 2 | 3 | import ( 4 | "context" 5 | 6 | "gorm.io/gorm" 7 | 8 | "github.com/noovertime7/kubemanage/dao/model" 9 | "github.com/noovertime7/kubemanage/dto" 10 | ) 11 | 12 | type Operation interface { 13 | Find(ctx context.Context, in *model.SysOperationRecord) (*model.SysOperationRecord, error) 14 | PageList(ctx context.Context, params *dto.OperationListInput) ([]*model.SysOperationRecord, int64, error) 15 | Save(ctx context.Context, in *model.SysOperationRecord) error 16 | Delete(ctx context.Context, in *model.SysOperationRecord) error 17 | DeleteList(ctx context.Context, in []int) error 18 | } 19 | 20 | type operation struct { 21 | db *gorm.DB 22 | } 23 | 24 | func (o *operation) Find(ctx context.Context, in *model.SysOperationRecord) (*model.SysOperationRecord, error) { 25 | out := &model.SysOperationRecord{} 26 | return out, o.db.WithContext(ctx).Where(in).Find(&out).Error 27 | } 28 | 29 | func (o *operation) Save(ctx context.Context, in *model.SysOperationRecord) error { 30 | return o.db.WithContext(ctx).Create(in).Error 31 | } 32 | 33 | func (o *operation) Delete(ctx context.Context, in *model.SysOperationRecord) error { 34 | return o.db.WithContext(ctx).Delete(in).Error 35 | } 36 | 37 | func (o *operation) DeleteList(ctx context.Context, in []int) error { 38 | return o.db.WithContext(ctx).Delete(&[]model.SysOperationRecord{}, "id in (?)", in).Error 39 | } 40 | 41 | func NewOperation(db *gorm.DB) *operation { 42 | return &operation{db: db} 43 | } 44 | 45 | func (o *operation) PageList(ctx context.Context, params *dto.OperationListInput) ([]*model.SysOperationRecord, int64, error) { 46 | var total int64 = 0 47 | limit := params.PageSize 48 | offset := params.PageSize * (params.Page - 1) 49 | query := o.db.WithContext(ctx) 50 | var list []*model.SysOperationRecord 51 | // 如果有条件搜索 下方会自动创建搜索语句 52 | if params.Method != "" { 53 | query = query.Where("method = ?", params.Method) 54 | } 55 | if params.Path != "" { 56 | query = query.Where("path = ?", params.Path) 57 | } 58 | if params.Status != 0 { 59 | query = query.Where("status = ?", params.Status) 60 | } 61 | 62 | if err := query.Find(&list).Count(&total).Error; err != nil { 63 | return nil, 0, err 64 | } 65 | 66 | if err := query.Order("id desc").Limit(limit).Offset(offset).Preload("User").Find(&list).Error; err != nil { 67 | return nil, 0, err 68 | } 69 | return list, total, nil 70 | } 71 | -------------------------------------------------------------------------------- /dao/user/user.go: -------------------------------------------------------------------------------- 1 | package user 2 | 3 | import ( 4 | "context" 5 | "github.com/noovertime7/kubemanage/dao/model" 6 | "github.com/pkg/errors" 7 | "gorm.io/gorm" 8 | ) 9 | 10 | type User interface { 11 | Find(ctx context.Context, userInfo *model.SysUser) (*model.SysUser, error) 12 | Save(ctx context.Context, userInfo *model.SysUser) error 13 | Updates(ctx context.Context, userInfo *model.SysUser) error 14 | Delete(ctx context.Context, userInfo *model.SysUser) error 15 | } 16 | 17 | var _ User = &user{} 18 | 19 | type user struct { 20 | db *gorm.DB 21 | } 22 | 23 | func NewUser(db *gorm.DB) *user { 24 | return &user{db: db} 25 | } 26 | 27 | func (u *user) Find(ctx context.Context, userInfo *model.SysUser) (*model.SysUser, error) { 28 | user := &model.SysUser{} 29 | if err := u.db.WithContext(ctx).Preload("Authorities").Preload("Authority").Where(userInfo).Find(user).Error; err != nil { 30 | if err == gorm.ErrRecordNotFound { 31 | return nil, gorm.ErrRecordNotFound 32 | } 33 | return nil, err 34 | } 35 | return user, nil 36 | } 37 | 38 | func (u *user) Save(ctx context.Context, userInfo *model.SysUser) error { 39 | return u.db.WithContext(ctx).Save(userInfo).Error 40 | } 41 | 42 | func (u *user) Updates(ctx context.Context, userInfo *model.SysUser) error { 43 | if userInfo.ID == 0 { 44 | return errors.New("id not set") 45 | } 46 | return u.db.WithContext(ctx).Updates(userInfo).Error 47 | } 48 | 49 | func (u *user) Delete(ctx context.Context, userInfo *model.SysUser) error { 50 | return u.db.WithContext(ctx).Delete(userInfo).Error 51 | } 52 | -------------------------------------------------------------------------------- /dao/workflow/workflow.go: -------------------------------------------------------------------------------- 1 | package workflow 2 | 3 | import ( 4 | "context" 5 | "github.com/noovertime7/kubemanage/dao/model" 6 | "github.com/noovertime7/kubemanage/dto/kubeDto" 7 | "github.com/pkg/errors" 8 | "gorm.io/gorm" 9 | "time" 10 | ) 11 | 12 | type WorkFlowInterface interface { 13 | Save(ctx context.Context, obj *model.Workflow) error 14 | Updates(ctx context.Context, obj *model.Workflow) error 15 | Find(ctx context.Context, id int) (*model.Workflow, error) 16 | FindList(ctx context.Context, search *model.Workflow) ([]*model.Workflow, error) 17 | PageList(ctx context.Context, params *kubeDto.WorkFlowListInput) ([]*model.Workflow, int, error) 18 | Delete(ctx context.Context, wid int) error 19 | } 20 | 21 | type workflow struct { 22 | db *gorm.DB 23 | } 24 | 25 | func NewWorkFlow(db *gorm.DB) WorkFlowInterface { 26 | return &workflow{db: db} 27 | } 28 | 29 | func (w *workflow) PageList(ctx context.Context, params *kubeDto.WorkFlowListInput) ([]*model.Workflow, int, error) { 30 | var total int64 = 0 31 | var list []*model.Workflow 32 | offset := (params.Page - 1) * params.Limit 33 | query := w.db.WithContext(ctx).Table(model.GetWorkflowTableName()) 34 | query.Find(&list).Count(&total) 35 | if params.FilterName != "" { 36 | query = query.Where("( name like ?)", "%"+params.FilterName+"%") 37 | } 38 | if err := query.Limit(params.Limit).Offset(offset).Order("id desc").Find(&list).Error; err != nil && err != gorm.ErrRecordNotFound { 39 | return nil, 0, err 40 | } 41 | return list, int(total), nil 42 | } 43 | 44 | func (w *workflow) Save(ctx context.Context, obj *model.Workflow) error { 45 | now := time.Now() 46 | obj.UpdatedAt = now 47 | return w.db.WithContext(ctx).Save(obj).Error 48 | } 49 | 50 | func (w *workflow) Updates(ctx context.Context, obj *model.Workflow) error { 51 | if obj.ID == 0 { 52 | return errors.New("id no set") 53 | } 54 | return w.db.WithContext(ctx).Updates(obj).Error 55 | } 56 | 57 | func (w *workflow) Find(ctx context.Context, id int) (*model.Workflow, error) { 58 | out := &model.Workflow{} 59 | return out, w.db.WithContext(ctx).Where("id = ?", id).First(out).Error 60 | } 61 | 62 | func (w *workflow) FindList(ctx context.Context, search *model.Workflow) ([]*model.Workflow, error) { 63 | var res []*model.Workflow 64 | return res, w.db.WithContext(ctx).Where(&search).Find(&res).Error 65 | } 66 | 67 | func (w *workflow) Delete(ctx context.Context, wid int) error { 68 | return w.db.WithContext(ctx).Where("id = ?", wid).Delete(&model.Workflow{}).Error 69 | } 70 | -------------------------------------------------------------------------------- /dto/authority.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import "github.com/noovertime7/kubemanage/dao/model" 4 | 5 | type AuthorityList struct { 6 | PageInfo 7 | Total int64 `json:"total"` 8 | AuthorityListItem []model.SysAuthority `json:"list"` 9 | } 10 | -------------------------------------------------------------------------------- /dto/casbin.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | ) 7 | 8 | // CasbinInfo Casbin info structure 9 | type CasbinInfo struct { 10 | Path string `form:"path" json:"path"` // 路径 11 | Method string ` form:"method" json:"method"` // 方法 12 | } 13 | 14 | // UpdateCasbinInput 通过角色id更改接口权限 15 | type UpdateCasbinInput struct { 16 | AuthorityId uint `form:"authorityId" json:"authorityId"` // 权限id 17 | CasbinInfo []CasbinInfo `json:"casbinInfos"` 18 | } 19 | 20 | // CasbinInReceive Casbin structure for input parameters 21 | type CasbinInReceive struct { 22 | AuthorityId uint `form:"authorityId" json:"authorityId"` // 权限id 23 | } 24 | 25 | // BindingValidParams 绑定并校验参数 26 | func (a *CasbinInReceive) BindingValidParams(ctx *gin.Context) error { 27 | return pkg.DefaultGetValidParams(ctx, a) 28 | } 29 | 30 | // BindingValidParams 绑定并校验参数 31 | func (a *UpdateCasbinInput) BindingValidParams(ctx *gin.Context) error { 32 | return pkg.DefaultGetValidParams(ctx, a) 33 | } 34 | -------------------------------------------------------------------------------- /dto/common.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | ) 7 | 8 | // Empty 用于swag生成不需要传参的api 9 | type Empty struct{} 10 | 11 | // PageInfo Paging common input parameter structure 12 | type PageInfo struct { 13 | Page int `json:"page" form:"page"` // 页码 14 | PageSize int `json:"pageSize" form:"pageSize"` // 每页大小 15 | Keyword string `json:"keyword" form:"keyword"` //关键字 16 | } 17 | 18 | type IdsReq struct { 19 | Ids []int `json:"ids" form:"ids"` 20 | } 21 | 22 | // BindingValidParams 绑定并校验参数 23 | func (a *IdsReq) BindingValidParams(ctx *gin.Context) error { 24 | return pkg.DefaultGetValidParams(ctx, a) 25 | } 26 | 27 | func (a *PageInfo) BindingValidParams(ctx *gin.Context) error { 28 | return pkg.DefaultGetValidParams(ctx, a) 29 | } 30 | -------------------------------------------------------------------------------- /dto/kubeDto/configmap.go: -------------------------------------------------------------------------------- 1 | package kubeDto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | ) 7 | 8 | type ConfigmapNameNS struct { 9 | Name string `json:"name" form:"name" comment:"配置卷名称" validate:"required"` 10 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 11 | } 12 | 13 | type ConfigmapUpdateInput struct { 14 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 15 | Content string `json:"content" form:"content" validate:"required" comment:"更新内容"` 16 | } 17 | 18 | type ConfigmapListInput struct { 19 | FilterName string `json:"filter_name" form:"filter_name" validate:"" comment:"过滤名"` 20 | NameSpace string `json:"namespace" form:"namespace" validate:"" comment:"命名空间"` 21 | Limit int `json:"limit" form:"limit" validate:"" comment:"分页限制"` 22 | Page int `json:"page" form:"page" validate:"" comment:"页码"` 23 | } 24 | 25 | func (params *ConfigmapNameNS) BindingValidParams(c *gin.Context) error { 26 | return pkg.DefaultGetValidParams(c, params) 27 | } 28 | 29 | func (params *ConfigmapUpdateInput) BindingValidParams(c *gin.Context) error { 30 | return pkg.DefaultGetValidParams(c, params) 31 | } 32 | 33 | func (params *ConfigmapListInput) BindingValidParams(c *gin.Context) error { 34 | return pkg.DefaultGetValidParams(c, params) 35 | } 36 | -------------------------------------------------------------------------------- /dto/kubeDto/daemonset.go: -------------------------------------------------------------------------------- 1 | package kubeDto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | ) 7 | 8 | type DaemonSetNameNS struct { 9 | Name string `json:"name" form:"name" comment:"有状态控制器名称" validate:"required"` 10 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 11 | } 12 | 13 | type DaemonSetUpdateInput struct { 14 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 15 | Content string `json:"content" validate:"required" comment:"更新内容"` 16 | } 17 | 18 | type DaemonSetListInput struct { 19 | FilterName string `json:"filter_name" form:"filter_name" validate:"" comment:"过滤名"` 20 | NameSpace string `json:"namespace" form:"namespace" validate:"" comment:"命名空间"` 21 | Limit int `json:"limit" form:"limit" validate:"" comment:"分页限制"` 22 | Page int `json:"page" form:"page" validate:"" comment:"页码"` 23 | } 24 | 25 | func (params *DaemonSetNameNS) BindingValidParams(c *gin.Context) error { 26 | return pkg.DefaultGetValidParams(c, params) 27 | } 28 | 29 | func (params *DaemonSetUpdateInput) BindingValidParams(c *gin.Context) error { 30 | return pkg.DefaultGetValidParams(c, params) 31 | } 32 | 33 | func (params *DaemonSetListInput) BindingValidParams(c *gin.Context) error { 34 | return pkg.DefaultGetValidParams(c, params) 35 | } 36 | -------------------------------------------------------------------------------- /dto/kubeDto/deployment.go: -------------------------------------------------------------------------------- 1 | package kubeDto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | ) 7 | 8 | type DeploymentNameNS struct { 9 | Name string `json:"deployment_name" form:"deployment_name" comment:"无状态控制器名称" validate:"required"` 10 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 11 | } 12 | 13 | type DeployCreateInput struct { 14 | //DeploymentNameNS 15 | Name string `json:"name" form:"name" comment:"无状态控制器名称" validate:"required"` 16 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 17 | Replicas int32 `json:"replicas" validate:"required" comment:"副本数"` 18 | Image string `json:"image" validate:"required" comment:"镜像名"` 19 | Labels map[string]string `json:"label" validate:"" comment:"标签"` 20 | Cpu string `json:"cpu" validate:"" comment:"Cpu限制"` 21 | Memory string `json:"memory" validate:"" comment:"内存限制"` 22 | ContainerPort int32 `json:"container_port" validate:"" comment:"容器端口"` 23 | HealthCheck bool `json:"health_check" validate:"" comment:"健康检查开关"` 24 | HealthPath string `json:"health_path" validate:"" comment:"Http健康检查路径"` 25 | } 26 | 27 | type UpdateDeployInput struct { 28 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 29 | Content string `json:"content" form:"content" validate:"required" comment:"更新内容"` 30 | } 31 | 32 | type DeployScaleInput struct { 33 | Name string `json:"deployment_name" form:"deployment_name" comment:"无状态控制器名称" validate:"required"` 34 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 35 | ScaleNum int `json:"scale_num" form:"scale_num" comment:"期望副本数" validate:"required"` 36 | } 37 | 38 | type DeployListInput struct { 39 | FilterName string `json:"filter_name" form:"filter_name" validate:"" comment:"过滤名"` 40 | NameSpace string `json:"namespace" form:"namespace" validate:"" comment:"命名空间"` 41 | Limit int `json:"limit" form:"limit" validate:"" comment:"分页限制"` 42 | Page int `json:"page" form:"page" validate:"" comment:"页码"` 43 | } 44 | 45 | func (params *DeployListInput) BindingValidParams(c *gin.Context) error { 46 | return pkg.DefaultGetValidParams(c, params) 47 | } 48 | 49 | func (params *UpdateDeployInput) BindingValidParams(c *gin.Context) error { 50 | return pkg.DefaultGetValidParams(c, params) 51 | } 52 | 53 | func (params *DeployCreateInput) BindingValidParams(c *gin.Context) error { 54 | return pkg.DefaultGetValidParams(c, params) 55 | } 56 | 57 | func (params *DeployScaleInput) BindingValidParams(c *gin.Context) error { 58 | return pkg.DefaultGetValidParams(c, params) 59 | } 60 | 61 | func (params *DeploymentNameNS) BindingValidParams(c *gin.Context) error { 62 | return pkg.DefaultGetValidParams(c, params) 63 | } 64 | -------------------------------------------------------------------------------- /dto/kubeDto/ingress.go: -------------------------------------------------------------------------------- 1 | package kubeDto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | nwV1 "k8s.io/api/networking/v1" 7 | ) 8 | 9 | type IngressCreteInput struct { 10 | Name string `json:"name"` 11 | NameSpace string `json:"namespace"` 12 | Label map[string]string `json:"label"` 13 | Hosts map[string][]*HttpPath `json:"hosts"` 14 | } 15 | 16 | type HttpPath struct { 17 | Path string `json:"path"` 18 | PathType nwV1.PathType `json:"path_type"` 19 | ServiceName string `json:"service_name"` 20 | ServicePort int32 `json:"service_port"` 21 | } 22 | 23 | type IngressNameNS struct { 24 | Name string `json:"name" form:"name" comment:"服务名称" validate:"required"` 25 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 26 | } 27 | 28 | type IngressUpdateInput struct { 29 | Content string `json:"content" form:"content" validate:"required" comment:"更新内容"` 30 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 31 | } 32 | 33 | type IngressListInput struct { 34 | FilterName string `json:"filter_name" form:"filter_name" validate:"" comment:"过滤名"` 35 | NameSpace string `json:"namespace" form:"namespace" validate:"" comment:"命名空间"` 36 | Limit int `json:"limit" form:"limit" validate:"" comment:"分页限制"` 37 | Page int `json:"page" form:"page" validate:"" comment:"页码"` 38 | } 39 | 40 | func (params *IngressCreteInput) BindingValidParams(c *gin.Context) error { 41 | return pkg.DefaultGetValidParams(c, params) 42 | } 43 | 44 | func (params *IngressNameNS) BindingValidParams(c *gin.Context) error { 45 | return pkg.DefaultGetValidParams(c, params) 46 | } 47 | 48 | func (params *IngressUpdateInput) BindingValidParams(c *gin.Context) error { 49 | return pkg.DefaultGetValidParams(c, params) 50 | } 51 | 52 | func (params *IngressListInput) BindingValidParams(c *gin.Context) error { 53 | return pkg.DefaultGetValidParams(c, params) 54 | } 55 | -------------------------------------------------------------------------------- /dto/kubeDto/monitor.go: -------------------------------------------------------------------------------- 1 | package kubeDto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | ) 7 | 8 | type ImageListInput struct { 9 | ClusterName string `json:"cluster_name" form:"cluster_name" validate:"required"` 10 | } 11 | 12 | type ImageListOut struct { 13 | Total int `json:"total"` 14 | List []ImageListItem `json:"list"` 15 | } 16 | 17 | type ImageListItem struct { 18 | ClusterName string `json:"cluster_name"` 19 | NameSpace string `json:"name_space"` 20 | AppName string `json:"app_name"` 21 | Image string `json:"image"` 22 | } 23 | 24 | func (params *ImageListInput) BindingValidParams(c *gin.Context) error { 25 | return pkg.DefaultGetValidParams(c, params) 26 | } 27 | -------------------------------------------------------------------------------- /dto/kubeDto/namespace.go: -------------------------------------------------------------------------------- 1 | package kubeDto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | ) 7 | 8 | type NameSpaceNameInput struct { 9 | Name string `json:"name" form:"name" comment:"命名空间名称" validate:"required"` 10 | } 11 | 12 | type NameSpaceListInput struct { 13 | FilterName string `json:"filter_name" form:"filter_name" validate:"" comment:"过滤名"` 14 | Limit int `json:"limit" form:"limit" validate:"" comment:"分页限制"` 15 | Page int `json:"page" form:"page" validate:"" comment:"页码"` 16 | } 17 | 18 | func (params *NameSpaceListInput) BindingValidParams(c *gin.Context) error { 19 | return pkg.DefaultGetValidParams(c, params) 20 | } 21 | 22 | func (params *NameSpaceNameInput) BindingValidParams(c *gin.Context) error { 23 | return pkg.DefaultGetValidParams(c, params) 24 | } 25 | -------------------------------------------------------------------------------- /dto/kubeDto/node.go: -------------------------------------------------------------------------------- 1 | package kubeDto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | ) 7 | 8 | type NodeNameInput struct { 9 | Name string `json:"name" form:"name" comment:"Node名称" validate:"required"` 10 | } 11 | 12 | type NodeListInput struct { 13 | FilterName string `json:"filter_name" form:"filter_name" validate:"" comment:"过滤名"` 14 | Limit int `json:"limit" form:"limit" validate:"" comment:"分页限制"` 15 | Page int `json:"page" form:"page" validate:"" comment:"页码"` 16 | } 17 | 18 | func (params *NodeNameInput) BindingValidParams(c *gin.Context) error { 19 | return pkg.DefaultGetValidParams(c, params) 20 | } 21 | 22 | func (params *NodeListInput) BindingValidParams(c *gin.Context) error { 23 | return pkg.DefaultGetValidParams(c, params) 24 | } 25 | -------------------------------------------------------------------------------- /dto/kubeDto/persistentVolumeClaim.go: -------------------------------------------------------------------------------- 1 | package kubeDto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | ) 7 | 8 | type PersistentVolumeClaimNameNS struct { 9 | Name string `json:"name" form:"name" comment:"配置卷名称" validate:"required"` 10 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 11 | } 12 | 13 | type PersistentVolumeClaimUpdateInput struct { 14 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 15 | Content string `json:"content" form:"content" validate:"required" comment:"更新内容"` 16 | } 17 | 18 | type PersistentVolumeClaimListInput struct { 19 | FilterName string `json:"filter_name" form:"filter_name" validate:"" comment:"过滤名"` 20 | NameSpace string `json:"namespace" form:"namespace" validate:"" comment:"命名空间"` 21 | Limit int `json:"limit" form:"limit" validate:"" comment:"分页限制"` 22 | Page int `json:"page" form:"page" validate:"" comment:"页码"` 23 | } 24 | 25 | func (params *PersistentVolumeClaimNameNS) BindingValidParams(c *gin.Context) error { 26 | return pkg.DefaultGetValidParams(c, params) 27 | } 28 | 29 | func (params *PersistentVolumeClaimUpdateInput) BindingValidParams(c *gin.Context) error { 30 | return pkg.DefaultGetValidParams(c, params) 31 | } 32 | 33 | func (params *PersistentVolumeClaimListInput) BindingValidParams(c *gin.Context) error { 34 | return pkg.DefaultGetValidParams(c, params) 35 | } 36 | -------------------------------------------------------------------------------- /dto/kubeDto/persistentvolume.go: -------------------------------------------------------------------------------- 1 | package kubeDto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | ) 7 | 8 | type PersistentVolumeNameInput struct { 9 | Name string `json:"name" form:"name" comment:"命名空间名称" validate:"required"` 10 | } 11 | 12 | type PersistentVolumeListInput struct { 13 | FilterName string `json:"filter_name" form:"filter_name" validate:"" comment:"过滤名"` 14 | Limit int `json:"limit" form:"limit" validate:"" comment:"分页限制"` 15 | Page int `json:"page" form:"page" validate:"" comment:"页码"` 16 | } 17 | 18 | func (params *PersistentVolumeListInput) BindingValidParams(c *gin.Context) error { 19 | return pkg.DefaultGetValidParams(c, params) 20 | } 21 | 22 | func (params *PersistentVolumeNameInput) BindingValidParams(c *gin.Context) error { 23 | return pkg.DefaultGetValidParams(c, params) 24 | } 25 | -------------------------------------------------------------------------------- /dto/kubeDto/pod.go: -------------------------------------------------------------------------------- 1 | package kubeDto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | 6 | "github.com/noovertime7/kubemanage/pkg" 7 | ) 8 | 9 | type PodListInput struct { 10 | FilterName string `json:"filter_name" form:"filter_name" validate:"" comment:"过滤名"` 11 | NameSpace string `json:"namespace" form:"namespace" validate:"" comment:"命名空间"` 12 | Limit int `json:"limit" form:"limit" validate:"" comment:"分页限制"` 13 | Page int `json:"page" form:"page" validate:"" comment:"页码"` 14 | } 15 | 16 | // WebShellOptions ws API 参数定义 17 | type WebShellOptions struct { 18 | Namespace string `form:"namespace"` 19 | Pod string `form:"pod_name"` 20 | Container string `form:"container_name"` 21 | } 22 | 23 | func (params *WebShellOptions) BindingValidParams(c *gin.Context) error { 24 | return pkg.DefaultGetValidParams(c, params) 25 | } 26 | 27 | func (params *PodListInput) BindingValidParams(c *gin.Context) error { 28 | return pkg.DefaultGetValidParams(c, params) 29 | } 30 | 31 | type PodNameNsInput struct { 32 | PodName string `json:"pod_name" form:"pod_name" comment:"POD名称" validate:"required"` 33 | NameSpace string `json:"name_space" form:"namespace" comment:"命名空间" validate:"required"` 34 | } 35 | 36 | type PodUpdateInput struct { 37 | PodName string `json:"pod_name" form:"pod_name" comment:"POD名称" validate:"required"` 38 | NameSpace string `json:"name_space" form:"namespace" comment:"命名空间" validate:"required"` 39 | Content string `json:"content" form:"content" comment:"内容" validate:"required"` 40 | } 41 | 42 | type PodGetLogInput struct { 43 | PodName string `json:"pod_name" form:"pod_name" comment:"POD名称" validate:"required"` 44 | NameSpace string `json:"name_space" form:"namespace" comment:"命名空间" validate:"required"` 45 | ContainerName string `json:"container_name" form:"container_name" comment:"容器名称" validate:"required"` 46 | } 47 | 48 | func (params *PodNameNsInput) BindingValidParams(c *gin.Context) error { 49 | return pkg.DefaultGetValidParams(c, params) 50 | } 51 | 52 | func (params *PodGetLogInput) BindingValidParams(c *gin.Context) error { 53 | return pkg.DefaultGetValidParams(c, params) 54 | } 55 | 56 | func (params *PodUpdateInput) BindingValidParams(c *gin.Context) error { 57 | return pkg.DefaultGetValidParams(c, params) 58 | } 59 | -------------------------------------------------------------------------------- /dto/kubeDto/secret.go: -------------------------------------------------------------------------------- 1 | package kubeDto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | ) 7 | 8 | type SecretNameNS struct { 9 | Name string `json:"name" form:"name" comment:"有状态控制器名称" validate:"required"` 10 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 11 | } 12 | 13 | type SecretUpdateInput struct { 14 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 15 | Content string `json:"content" form:"content" validate:"required" comment:"更新内容"` 16 | } 17 | 18 | type SecretListInput struct { 19 | FilterName string `json:"filter_name" form:"filter_name" validate:"" comment:"过滤名"` 20 | NameSpace string `json:"namespace" form:"namespace" validate:"" comment:"命名空间"` 21 | Limit int `json:"limit" form:"limit" validate:"" comment:"分页限制"` 22 | Page int `json:"page" form:"page" validate:"" comment:"页码"` 23 | } 24 | 25 | func (params *SecretNameNS) BindingValidParams(c *gin.Context) error { 26 | return pkg.DefaultGetValidParams(c, params) 27 | } 28 | 29 | func (params *SecretUpdateInput) BindingValidParams(c *gin.Context) error { 30 | return pkg.DefaultGetValidParams(c, params) 31 | } 32 | 33 | func (params *SecretListInput) BindingValidParams(c *gin.Context) error { 34 | return pkg.DefaultGetValidParams(c, params) 35 | } 36 | -------------------------------------------------------------------------------- /dto/kubeDto/service.go: -------------------------------------------------------------------------------- 1 | package kubeDto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | ) 7 | 8 | type ServiceCreateInput struct { 9 | Name string `json:"name"` 10 | NameSpace string `json:"namespace"` 11 | Type string `json:"type"` 12 | ContainerPort int32 `json:"container_port"` 13 | Port int32 `json:"port"` 14 | NodePort int32 `json:"node_port"` 15 | Label map[string]string `json:"label"` 16 | } 17 | 18 | type ServiceNameNS struct { 19 | Name string `json:"name" form:"name" comment:"服务名称" validate:"required"` 20 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 21 | } 22 | 23 | type ServiceUpdateInput struct { 24 | Content string `json:"content" validate:"required" comment:"更新内容"` 25 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 26 | } 27 | 28 | type ServiceListInput struct { 29 | FilterName string `json:"filter_name" form:"filter_name" validate:"" comment:"过滤名"` 30 | NameSpace string `json:"namespace" form:"namespace" validate:"" comment:"命名空间"` 31 | Limit int `json:"limit" form:"limit" validate:"" comment:"分页限制"` 32 | Page int `json:"page" form:"page" validate:"" comment:"页码"` 33 | } 34 | 35 | func (params *ServiceNameNS) BindingValidParams(c *gin.Context) error { 36 | return pkg.DefaultGetValidParams(c, params) 37 | } 38 | 39 | func (params *ServiceCreateInput) BindingValidParams(c *gin.Context) error { 40 | return pkg.DefaultGetValidParams(c, params) 41 | } 42 | 43 | func (params *ServiceUpdateInput) BindingValidParams(c *gin.Context) error { 44 | return pkg.DefaultGetValidParams(c, params) 45 | } 46 | 47 | func (params *ServiceListInput) BindingValidParams(c *gin.Context) error { 48 | return pkg.DefaultGetValidParams(c, params) 49 | } 50 | -------------------------------------------------------------------------------- /dto/kubeDto/statefulset.go: -------------------------------------------------------------------------------- 1 | package kubeDto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | ) 7 | 8 | type StatefulSetNameNS struct { 9 | Name string `json:"name" form:"name" comment:"有状态控制器名称" validate:"required"` 10 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 11 | } 12 | 13 | type StatefulSetUpdateInput struct { 14 | NameSpace string `json:"namespace" form:"namespace" comment:"命名空间" validate:"required"` 15 | Content string `json:"content" form:"content" validate:"required" comment:"更新内容"` 16 | } 17 | 18 | type StatefulSetListInput struct { 19 | FilterName string `json:"filter_name" form:"filter_name" validate:"" comment:"过滤名"` 20 | NameSpace string `json:"namespace" form:"namespace" validate:"" comment:"命名空间"` 21 | Limit int `json:"limit" form:"limit" validate:"" comment:"分页限制"` 22 | Page int `json:"page" form:"page" validate:"" comment:"页码"` 23 | } 24 | 25 | func (params *StatefulSetNameNS) BindingValidParams(c *gin.Context) error { 26 | return pkg.DefaultGetValidParams(c, params) 27 | } 28 | 29 | func (params *StatefulSetUpdateInput) BindingValidParams(c *gin.Context) error { 30 | return pkg.DefaultGetValidParams(c, params) 31 | } 32 | 33 | func (params *StatefulSetListInput) BindingValidParams(c *gin.Context) error { 34 | return pkg.DefaultGetValidParams(c, params) 35 | } 36 | -------------------------------------------------------------------------------- /dto/kubeDto/workflow.go: -------------------------------------------------------------------------------- 1 | package kubeDto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | ) 7 | 8 | type WorkFlowListInput struct { 9 | FilterName string `json:"filter_name" form:"filter_name" validate:"" comment:"过滤名"` 10 | Limit int `json:"limit" form:"limit" validate:"" comment:"分页限制"` 11 | Page int `json:"page" form:"page" validate:"" comment:"页码"` 12 | } 13 | 14 | type WorkFlowCreateInput struct { 15 | Name string `json:"name"` 16 | NameSpace string `json:"namespace"` 17 | Replicas int32 `json:"replicas"` 18 | Deployment string `json:"deployment"` 19 | Image string `json:"image"` 20 | Label map[string]string `json:"label"` 21 | Cpu string `json:"cpu"` 22 | Memory string `json:"memory"` 23 | ContainerPort int32 `json:"container_port"` 24 | HealthPath string `json:"health_path"` 25 | HealthCheck bool `json:"healthCheck"` 26 | Type string `json:"type"` 27 | Port int32 `json:"port"` 28 | NodePort int32 `json:"node_port"` 29 | Hosts map[string][]*HttpPath `json:"hosts"` 30 | } 31 | 32 | type WorkFlowIDInput struct { 33 | ID int `json:"id" form:"id"` 34 | } 35 | 36 | func (params *WorkFlowCreateInput) BindingValidParams(c *gin.Context) error { 37 | return pkg.DefaultGetValidParams(c, params) 38 | } 39 | 40 | func (params *WorkFlowListInput) BindingValidParams(c *gin.Context) error { 41 | return pkg.DefaultGetValidParams(c, params) 42 | } 43 | 44 | func (params *WorkFlowIDInput) BindingValidParams(c *gin.Context) error { 45 | return pkg.DefaultGetValidParams(c, params) 46 | } 47 | -------------------------------------------------------------------------------- /dto/menu.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | 6 | "github.com/noovertime7/kubemanage/dao/model" 7 | "github.com/noovertime7/kubemanage/pkg" 8 | ) 9 | 10 | type AddSysMenusInput struct { 11 | ParentId string `json:"parentId" comment:"父菜单ID" validate:"required"` // 父菜单ID 12 | Name string `json:"name" comment:"路由name" validate:"required"` // 路由name 13 | Path string `json:"path" comment:"路由path" validate:"required"` // 路由path 14 | Disabled bool `json:"disabled" comment:"是否禁用" validate:"required"` // 是否在列表隐藏 15 | Hidden bool `json:"hidden" comment:"是否在列表隐藏" validate:"required"` // 是否在列表隐藏 16 | Sort int `json:"sort" comment:"排序标记" validate:"required"` // 排序标记 17 | model.Meta 18 | } 19 | 20 | type SysMenusResponse struct { 21 | Menus []model.SysMenu `json:"menus"` 22 | } 23 | 24 | type SysBaseMenusResponse struct { 25 | Menus []model.SysBaseMenu `json:"menus"` 26 | } 27 | 28 | type SysBaseMenuResponse struct { 29 | Menu model.SysBaseMenu `json:"menu"` 30 | } 31 | 32 | type AddMenuAuthorityInput struct { 33 | Menus []model.SysBaseMenu `json:"menus"` 34 | AuthorityId uint `json:"authorityId" validate:"required"` // 角色ID 35 | } 36 | 37 | // BindingValidParams 绑定并校验参数 38 | func (a *AddSysMenusInput) BindingValidParams(ctx *gin.Context) error { 39 | return pkg.DefaultGetValidParams(ctx, a) 40 | } 41 | 42 | // BindingValidParams 绑定并校验参数 43 | func (a *AddMenuAuthorityInput) BindingValidParams(ctx *gin.Context) error { 44 | return pkg.DefaultGetValidParams(ctx, a) 45 | } 46 | -------------------------------------------------------------------------------- /dto/operation.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | 6 | "github.com/noovertime7/kubemanage/dao/model" 7 | "github.com/noovertime7/kubemanage/pkg" 8 | ) 9 | 10 | type OperationListInput struct { 11 | PageInfo 12 | Method string `json:"method" form:"method" ` // 请求方法 13 | Path string `json:"path" form:"path" ` // 请求路径 14 | Status int `json:"status" form:"status" ` // 请求状态 15 | } 16 | 17 | type OperationListOutPut struct { 18 | Total int64 `json:"total"` 19 | OperationList []*model.SysOperationRecord `json:"list"` 20 | PageInfo 21 | } 22 | 23 | // BindingValidParams 绑定并校验参数 24 | func (o *OperationListInput) BindingValidParams(ctx *gin.Context) error { 25 | return pkg.DefaultGetValidParams(ctx, o) 26 | } 27 | -------------------------------------------------------------------------------- /dto/user.go: -------------------------------------------------------------------------------- 1 | package dto 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/dao/model" 6 | "github.com/noovertime7/kubemanage/pkg" 7 | ) 8 | 9 | type AdminLoginInput struct { 10 | UserName string `form:"username" json:"username" comment:"用户名" validate:"required" example:"用户名"` 11 | Password string `form:"password" json:"password" comment:"密码" validate:"required" example:"密码"` 12 | } 13 | 14 | type AdminLoginOut struct { 15 | Token string `form:"token" json:"token" comment:"token" example:"token"` 16 | } 17 | 18 | type UserInfoOut struct { 19 | User model.SysUser `json:"user"` 20 | Menus []model.SysMenu `json:"menus"` 21 | RuleNames []string `json:"ruleNames"` 22 | } 23 | 24 | type SetUserAuth struct { 25 | AuthorityId uint `json:"authorityId"` // 角色ID 26 | } 27 | 28 | type ChangeUserPwdInput struct { 29 | OldPwd string `json:"old_pwd" form:"old_pwd" comment:"原密码" validate:"required"` 30 | NewPwd string `json:"new_pwd" form:"new_pwd" comment:"new_pwd" validate:"required"` 31 | } 32 | 33 | // BindingValidParams 绑定并校验参数 34 | func (a *ChangeUserPwdInput) BindingValidParams(ctx *gin.Context) error { 35 | return pkg.DefaultGetValidParams(ctx, a) 36 | } 37 | 38 | // BindingValidParams 绑定并校验参数 39 | func (a *SetUserAuth) BindingValidParams(ctx *gin.Context) error { 40 | return pkg.DefaultGetValidParams(ctx, a) 41 | } 42 | 43 | // BindingValidParams 绑定并校验参数 44 | func (a *AdminLoginInput) BindingValidParams(ctx *gin.Context) error { 45 | return pkg.DefaultGetValidParams(ctx, a) 46 | } 47 | -------------------------------------------------------------------------------- /img/cm_detail.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/cm_detail.jpg -------------------------------------------------------------------------------- /img/cmdb/host.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/cmdb/host.png -------------------------------------------------------------------------------- /img/cmdb/webshell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/cmdb/webshell.png -------------------------------------------------------------------------------- /img/dashboard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/dashboard.jpg -------------------------------------------------------------------------------- /img/deployment.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/deployment.jpg -------------------------------------------------------------------------------- /img/namespace.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/namespace.jpg -------------------------------------------------------------------------------- /img/node.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/node.jpg -------------------------------------------------------------------------------- /img/operation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/operation.png -------------------------------------------------------------------------------- /img/pod.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/pod.jpg -------------------------------------------------------------------------------- /img/pod_log.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/pod_log.jpg -------------------------------------------------------------------------------- /img/pod_ter.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/pod_ter.jpg -------------------------------------------------------------------------------- /img/rbac/api_rbac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/rbac/api_rbac.png -------------------------------------------------------------------------------- /img/rbac/menu_rbac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/rbac/menu_rbac.png -------------------------------------------------------------------------------- /img/service.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/service.jpg -------------------------------------------------------------------------------- /img/system_state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/system_state.png -------------------------------------------------------------------------------- /img/user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/user.png -------------------------------------------------------------------------------- /img/wordflow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/noovertime7/kubemanage/10cedd820104fb18400d386907ecd54ec0bec553/img/wordflow.jpg -------------------------------------------------------------------------------- /middleware/casbin_rbac.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | 7 | "github.com/gin-gonic/gin" 8 | 9 | "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1" 10 | "github.com/noovertime7/kubemanage/pkg/globalError" 11 | "github.com/noovertime7/kubemanage/pkg/utils" 12 | ) 13 | 14 | // CasbinHandler 拦截器 15 | func CasbinHandler() gin.HandlerFunc { 16 | return func(c *gin.Context) { 17 | if AlwaysAllowPath.Has(c.Request.URL.Path) { 18 | return 19 | } 20 | waitUse, err := utils.GetClaims(c) 21 | if err != nil { 22 | ResponseError(c, globalError.NewGlobalError(globalError.ServerError, err)) 23 | c.Abort() 24 | return 25 | } 26 | // 获取请求的PATH 27 | obj := c.Request.URL.Path 28 | // 获取请求方法 29 | act := c.Request.Method 30 | // 获取用户的角色 31 | sub := strconv.Itoa(int(waitUse.AuthorityId)) 32 | e := v1.CoreV1.System().CasbinService().Casbin() // 判断策略中是否存在 33 | success, _ := e.Enforce(sub, obj, act) 34 | if success { 35 | c.Next() 36 | } else { 37 | ResponseError(c, globalError.NewGlobalError(globalError.AuthErr, fmt.Errorf("角色ID %d 请求 %s %s 无权限", waitUse.AuthorityId, act, obj))) 38 | c.Abort() 39 | return 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /middleware/cors.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/gin-contrib/cors" 7 | "github.com/gin-gonic/gin" 8 | ) 9 | 10 | // Cores 处理跨域请求,支持options访问 11 | func Cores() gin.HandlerFunc { 12 | c := cors.Config{ 13 | AllowAllOrigins: true, 14 | AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "PATCH"}, 15 | AllowHeaders: []string{"Content-Type", "Access-Token", "Authorization"}, 16 | MaxAge: 6 * time.Hour, 17 | } 18 | 19 | return cors.New(c) 20 | } 21 | -------------------------------------------------------------------------------- /middleware/jwt.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg/globalError" 6 | "github.com/noovertime7/kubemanage/pkg/utils" 7 | ) 8 | 9 | // JWTAuth jwt认证函数 10 | func JWTAuth() gin.HandlerFunc { 11 | return func(context *gin.Context) { 12 | if AlwaysAllowPath.Has(context.Request.URL.Path) { 13 | return 14 | } 15 | 16 | if len(context.Request.URL.String()) == 15 && context.Request.URL.String()[0:15] == "/api/user/login" { 17 | context.Next() 18 | return 19 | } 20 | // 处理验证逻辑 21 | claims, err := utils.GetClaims(context) 22 | if err != nil { 23 | ResponseError(context, globalError.NewGlobalError(globalError.AuthorizationError, err)) 24 | context.Abort() 25 | return 26 | } 27 | // 继续交由下一个路由处理,并将解析出的信息传递下去 28 | context.Set("claims", claims) 29 | context.Next() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /middleware/limiter.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Pixiu Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package middleware 18 | 19 | import ( 20 | "fmt" 21 | "github.com/gin-gonic/gin" 22 | "github.com/noovertime7/kubemanage/pkg/globalError" 23 | "golang.org/x/time/rate" 24 | ) 25 | 26 | func Limiter() gin.HandlerFunc { 27 | 28 | // 初始化一个限速器,每秒产生 1000 个令牌,桶的大小为 1000 个 29 | // 初始化状态桶是满的 30 | 31 | limiter := rate.NewLimiter(1000, 1000) 32 | 33 | return func(c *gin.Context) { 34 | if !limiter.Allow() { 35 | ResponseError(c, globalError.NewGlobalError(globalError.ServerError, fmt.Errorf("系统繁忙,请稍后重试"))) 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /middleware/log.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg/logger" 6 | "go.uber.org/zap" 7 | "time" 8 | ) 9 | 10 | // Logger 接收gin框架默认的日志 11 | func Logger() gin.HandlerFunc { 12 | return func(c *gin.Context) { 13 | start := time.Now() 14 | path := c.Request.URL.Path 15 | query := c.Request.URL.RawQuery 16 | c.Next() 17 | 18 | cost := time.Since(start) 19 | logger.LG.Info(path, 20 | zap.Int("status", c.Writer.Status()), 21 | zap.String("method", c.Request.Method), 22 | zap.String("path", path), 23 | zap.String("query", query), 24 | zap.String("ip", c.ClientIP()), 25 | zap.String("user-agent", c.Request.UserAgent()), 26 | zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()), 27 | zap.String("host", c.Request.Host), 28 | zap.Duration("cost", cost), 29 | ) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /middleware/middleware.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | "k8s.io/apimachinery/pkg/util/sets" 7 | ) 8 | 9 | var AlwaysAllowPath sets.String 10 | 11 | func InstallMiddlewares(ginEngine *gin.RouterGroup) { 12 | // 初始化可忽略的请求路径 13 | AlwaysAllowPath = sets.NewString(pkg.LoginURL, pkg.LogoutURL, pkg.WebShellURL) 14 | ginEngine.Use(Logger(), Cores(), Limiter(), Recovery(true), TranslationMiddleware(), JWTAuth(), CasbinHandler()) 15 | } 16 | -------------------------------------------------------------------------------- /middleware/operation.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1" 7 | "io" 8 | "net/http" 9 | "net/url" 10 | "strings" 11 | "sync" 12 | "time" 13 | 14 | "github.com/gin-gonic/gin" 15 | "go.uber.org/zap" 16 | 17 | "github.com/noovertime7/kubemanage/dao/model" 18 | "github.com/noovertime7/kubemanage/pkg/logger" 19 | "github.com/noovertime7/kubemanage/pkg/utils" 20 | ) 21 | 22 | var respPool sync.Pool 23 | 24 | func init() { 25 | respPool.New = func() interface{} { 26 | return make([]byte, 1024) 27 | } 28 | } 29 | 30 | func OperationRecord() gin.HandlerFunc { 31 | return func(c *gin.Context) { 32 | if AlwaysAllowPath.Has(c.Request.URL.Path) { 33 | return 34 | } 35 | var ( 36 | err error 37 | userId int 38 | body []byte 39 | ) 40 | //如果请求不是get请求,从body中获取数据 41 | if c.Request.Method != http.MethodGet { 42 | var err error 43 | body, err = io.ReadAll(c.Request.Body) 44 | if err != nil { 45 | logger.LG.Error("read body from request error:", zap.Error(err)) 46 | } else { 47 | c.Request.Body = io.NopCloser(bytes.NewBuffer(body)) 48 | } 49 | } else { 50 | query := c.Request.URL.RawQuery 51 | query, _ = url.QueryUnescape(query) 52 | split := strings.Split(query, "&") 53 | m := make(map[string]string) 54 | for _, v := range split { 55 | kv := strings.Split(v, "=") 56 | if len(kv) == 2 { 57 | m[kv[0]] = kv[1] 58 | } 59 | } 60 | if len(m) > 0 { 61 | body, err = json.Marshal(&m) 62 | if err != nil { 63 | logger.LG.Error("marshal body error:", zap.Error(err)) 64 | body = []byte{} 65 | } 66 | } 67 | } 68 | claims, err := utils.GetClaims(c) 69 | if err != nil { 70 | logger.LG.Error("get claims from token err:", zap.Error(err)) 71 | return 72 | } 73 | userId = claims.ID 74 | record := model.SysOperationRecord{ 75 | Ip: c.ClientIP(), 76 | Method: c.Request.Method, 77 | Path: c.Request.URL.Path, 78 | Agent: c.Request.UserAgent(), 79 | Body: string(body), 80 | UserID: userId, 81 | } 82 | //if len(record.Body) > 1024 { 83 | // // 截断 84 | // newBody := respPool.Get().([]byte) 85 | // copy(newBody, record.Body) 86 | // record.Body = string(newBody) 87 | // defer respPool.Put(newBody[:0]) 88 | //} 89 | writer := responseBodyWriter{ 90 | ResponseWriter: c.Writer, 91 | body: &bytes.Buffer{}, 92 | } 93 | c.Writer = writer 94 | now := time.Now() 95 | 96 | c.Next() 97 | 98 | latency := time.Since(now) 99 | record.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String() 100 | record.Status = c.Writer.Status() 101 | record.Latency = latency 102 | record.Resp = writer.body.String() 103 | // 104 | //if len(record.Resp) > 1024 { 105 | // // 截断 106 | // newBody := respPool.Get().([]byte) 107 | // copy(newBody, record.Resp) 108 | // record.Resp = string(newBody) 109 | // defer respPool.Put(newBody[:0]) 110 | //} 111 | 112 | if err := v1.CoreV1.System().Operation().CreateOperationRecord(c, &record); err != nil { 113 | logger.LG.Error("create operation record error:", zap.Error(err)) 114 | } 115 | } 116 | } 117 | 118 | type responseBodyWriter struct { 119 | gin.ResponseWriter 120 | body *bytes.Buffer 121 | } 122 | 123 | func (r responseBodyWriter) Write(b []byte) (int, error) { 124 | r.body.Write(b) 125 | return r.ResponseWriter.Write(b) 126 | } 127 | -------------------------------------------------------------------------------- /middleware/recovery.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg/logger" 6 | "net" 7 | "net/http" 8 | "net/http/httputil" 9 | "os" 10 | "runtime/debug" 11 | "strings" 12 | 13 | "go.uber.org/zap" 14 | ) 15 | 16 | // Recovery 使用自定义日志库替换原有recover中间件 17 | func Recovery(stack bool) gin.HandlerFunc { 18 | return func(c *gin.Context) { 19 | defer func() { 20 | if err := recover(); err != nil { 21 | // Check for a broken connection, as it is not really a 22 | // condition that warrants a panic stack trace. 23 | var brokenPipe bool 24 | if ne, ok := err.(*net.OpError); ok { 25 | if se, ok := ne.Err.(*os.SyscallError); ok { 26 | if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") { 27 | brokenPipe = true 28 | } 29 | } 30 | } 31 | 32 | httpRequest, _ := httputil.DumpRequest(c.Request, false) 33 | if brokenPipe { 34 | logger.LG.Error(c.Request.URL.Path, 35 | zap.Any("error", err), 36 | zap.String("request", string(httpRequest)), 37 | ) 38 | // If the connection is dead, we can't write a status to it. 39 | c.Error(err.(error)) // nolint: errcheck 40 | c.Abort() 41 | return 42 | } 43 | 44 | if stack { 45 | logger.LG.Error("[Recovery from panic]", 46 | zap.Any("error", err), 47 | zap.String("request", string(httpRequest)), 48 | zap.String("stack", string(debug.Stack())), 49 | ) 50 | } else { 51 | logger.LG.Error("[Recovery from panic]", 52 | zap.Any("error", err), 53 | zap.String("request", string(httpRequest)), 54 | ) 55 | } 56 | c.AbortWithStatus(http.StatusInternalServerError) 57 | } 58 | }() 59 | c.Next() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /middleware/response.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/gin-gonic/gin" 6 | "github.com/noovertime7/kubemanage/pkg/globalError" 7 | "github.com/pkg/errors" 8 | "net/http" 9 | ) 10 | 11 | type ResponseCode int 12 | 13 | type Response struct { 14 | Code ResponseCode `json:"code"` 15 | Msg string `json:"msg"` 16 | RealErr string `json:"real_err"` 17 | Data interface{} `json:"data"` 18 | } 19 | 20 | func ResponseSuccess(c *gin.Context, data interface{}) { 21 | resp := &Response{Code: http.StatusOK, Msg: "", Data: data} 22 | tempMsg, ok := data.(string) 23 | if ok && tempMsg == "" { 24 | resp.Msg = "操作成功" 25 | } 26 | c.JSON(200, resp) 27 | response, _ := json.Marshal(resp) 28 | c.Set("response", string(response)) 29 | } 30 | 31 | func ResponseError(c *gin.Context, err error) { 32 | //判断错误类型 33 | // As - 获取错误的具体实现 34 | var code ResponseCode 35 | var myError = new(globalError.GlobalError) 36 | if errors.As(err, &myError) { 37 | code = ResponseCode(myError.Code) 38 | } 39 | resp := &Response{Code: code, Msg: err.Error(), RealErr: myError.RealErrorMessage, Data: ""} 40 | c.JSON(200, resp) 41 | response, _ := json.Marshal(resp) 42 | c.Set("response", string(response)) 43 | } 44 | -------------------------------------------------------------------------------- /middleware/translation.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/go-playground/locales/en" 6 | "github.com/go-playground/locales/zh" 7 | "github.com/go-playground/universal-translator" 8 | "github.com/noovertime7/kubemanage/pkg" 9 | "gopkg.in/go-playground/validator.v9" 10 | en_translations "gopkg.in/go-playground/validator.v9/translations/en" 11 | zh_translations "gopkg.in/go-playground/validator.v9/translations/zh" 12 | "reflect" 13 | ) 14 | 15 | // 设置Translation 16 | func TranslationMiddleware() gin.HandlerFunc { 17 | return func(c *gin.Context) { 18 | //参照:https://github.com/go-playground/validator/blob/v9/_examples/translations/main.go 19 | 20 | //设置支持语言 21 | enLan := en.New() 22 | zhLan := zh.New() 23 | 24 | //设置国际化翻译器 25 | uni := ut.New(zhLan, zhLan, enLan) 26 | val := validator.New() 27 | 28 | //根据参数取翻译器实例 29 | locale := c.DefaultQuery("locale", "zh") 30 | trans, _ := uni.GetTranslator(locale) 31 | 32 | //翻译器注册到validator 33 | switch locale { 34 | case "en": 35 | err := en_translations.RegisterDefaultTranslations(val, trans) 36 | if err != nil { 37 | return 38 | } 39 | val.RegisterTagNameFunc(func(fld reflect.StructField) string { 40 | return fld.Tag.Get("en_comment") 41 | }) 42 | break 43 | default: 44 | err := zh_translations.RegisterDefaultTranslations(val, trans) 45 | if err != nil { 46 | return 47 | } 48 | val.RegisterTagNameFunc(func(fld reflect.StructField) string { 49 | return fld.Tag.Get("comment") 50 | }) 51 | 52 | ////自定义验证方法 53 | ////https://github.com/go-playground/validator/blob/v9/_examples/custom-validation/main.go 54 | //val.RegisterValidation("is-validuser", func(fl validator.FieldLevel) bool { 55 | // return fl.Field().String() == "admin" 56 | //}) 57 | // 58 | ////自定义验证器 59 | ////https://github.com/go-playground/validator/blob/v9/_examples/translations/main.go 60 | //val.RegisterTranslation("is-validuser", trans, func(ut ut.Translator) error { 61 | // return ut.Add("is-validuser", "{0} 填写不正确哦", true) 62 | //}, func(ut ut.Translator, fe validator.FieldError) string { 63 | // t, _ := ut.T("is-validuser", fe.Field()) 64 | // return t 65 | //}) 66 | break 67 | } 68 | c.Set(pkg.TranslatorKey, trans) 69 | c.Set(pkg.ValidatorKey, val) 70 | c.Next() 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /pkg/const.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import "strconv" 4 | 5 | const ( 6 | ValidatorKey = "ValidatorKey" 7 | TranslatorKey = "TranslatorKey" 8 | ) 9 | 10 | const ( 11 | LoginURL = "/api/user/login" 12 | LogoutURL = "/api/user/logout" 13 | WebShellURL = "/api/k8s/pod/webshell" 14 | ) 15 | 16 | var ( 17 | AdminDefaultAuth uint = 111 18 | AdminDefaultAuthStr = strconv.Itoa(int(AdminDefaultAuth)) 19 | UserDefaultAuth uint = 222 20 | UserDefaultAuthStr = strconv.Itoa(int(UserDefaultAuth)) 21 | UserSubDefaultAuth uint = 2221 22 | UserSubDefaultAuthStr = strconv.Itoa(int(UserSubDefaultAuth)) 23 | ) 24 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/cloud.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "github.com/noovertime7/kubemanage/dao" 5 | "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1/kube" 6 | ) 7 | 8 | type CloudGetter interface { 9 | Cloud() CloudInterface 10 | } 11 | 12 | type CloudInterface interface { 13 | kube.PodsGetter 14 | } 15 | 16 | type cloud struct { 17 | app *KubeManage 18 | factory dao.ShareDaoFactory 19 | } 20 | 21 | func (c *cloud) Pods(cloud string) kube.PodInterface { 22 | // TODO 临时添加,需要重构 23 | return kube.NewPods(nil, "", c.factory) 24 | } 25 | 26 | func NewCloud(c *KubeManage) CloudInterface { 27 | return &cloud{ 28 | app: c, 29 | factory: c.Factory, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/interface.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "github.com/noovertime7/kubemanage/cmd/app/config" 5 | "github.com/noovertime7/kubemanage/dao" 6 | "github.com/noovertime7/kubemanage/pkg/logger" 7 | ) 8 | 9 | type CoreService interface { 10 | WorkFlowServiceGetter 11 | CloudGetter 12 | SystemGetter 13 | } 14 | 15 | func New(cfg *config.Config, factory dao.ShareDaoFactory) CoreService { 16 | return &KubeManage{ 17 | Cfg: cfg, 18 | Factory: factory, 19 | } 20 | } 21 | 22 | type Logger interface { 23 | logger.Logger 24 | } 25 | 26 | type KubeManage struct { 27 | Cfg *config.Config 28 | Factory dao.ShareDaoFactory 29 | } 30 | 31 | func (c *KubeManage) WorkFlow() WorkFlowService { 32 | return NewWorkFlow(c) 33 | } 34 | 35 | func (c *KubeManage) Cloud() CloudInterface { 36 | return NewCloud(c) 37 | } 38 | 39 | func (c *KubeManage) System() SystemInterface { 40 | return NewSystem(c) 41 | } 42 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/kube/configmap.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | 7 | coreV1 "k8s.io/api/core/v1" 8 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | var Configmap configmap 12 | 13 | type configmap struct{} 14 | 15 | type ConfigmapResp struct { 16 | Total int `json:"total"` 17 | Items []coreV1.ConfigMap `json:"items"` 18 | } 19 | 20 | type ConfigmapNp struct { 21 | NameSpace string `json:"namespace"` 22 | ConfigmapNum int `json:"configmap_num"` 23 | } 24 | 25 | func (d *configmap) toCells(Configmaps []coreV1.ConfigMap) []DataCell { 26 | cells := make([]DataCell, len(Configmaps)) 27 | for i := range Configmaps { 28 | cells[i] = configmapCell(Configmaps[i]) 29 | } 30 | return cells 31 | } 32 | 33 | func (d *configmap) FromCells(cells []DataCell) []coreV1.ConfigMap { 34 | Configmaps := make([]coreV1.ConfigMap, len(cells)) 35 | for i := range cells { 36 | Configmaps[i] = coreV1.ConfigMap(cells[i].(configmapCell)) 37 | } 38 | return Configmaps 39 | } 40 | 41 | func (d *configmap) GetConfigmaps(filterName, namespace string, limit, page int) (*ConfigmapResp, error) { 42 | ConfigmapList, err := K8s.ClientSet.CoreV1().ConfigMaps(namespace).List(context.TODO(), metaV1.ListOptions{}) 43 | if err != nil { 44 | return nil, err 45 | } 46 | selectableData := &dataSelector{ 47 | GenericDataList: d.toCells(ConfigmapList.Items), 48 | DataSelect: &DataSelectQuery{ 49 | Filter: &FilterQuery{Name: filterName}, 50 | Paginatite: &PaginateQuery{ 51 | Limit: limit, 52 | Page: page, 53 | }, 54 | }, 55 | } 56 | filterd := selectableData.Filter() 57 | total := len(filterd.GenericDataList) 58 | data := filterd.Sort().Paginate() 59 | Configmaps := d.FromCells(data.GenericDataList) 60 | return &ConfigmapResp{ 61 | Total: total, 62 | Items: Configmaps, 63 | }, nil 64 | } 65 | 66 | func (d *configmap) GetConfigmapDetail(name, namespace string) (*coreV1.ConfigMap, error) { 67 | data, err := K8s.ClientSet.CoreV1().ConfigMaps(namespace).Get(context.TODO(), name, metaV1.GetOptions{}) 68 | if err != nil { 69 | return nil, err 70 | } 71 | return data, nil 72 | } 73 | 74 | func (d *configmap) DeleteConfigmap(name, namespace string) error { 75 | return K8s.ClientSet.CoreV1().ConfigMaps(namespace).Delete(context.TODO(), name, metaV1.DeleteOptions{}) 76 | } 77 | 78 | func (d *configmap) UpdateConfigmap(content, namespace string) error { 79 | var Configmap = &coreV1.ConfigMap{} 80 | if err := json.Unmarshal([]byte(content), Configmap); err != nil { 81 | return err 82 | } 83 | if _, err := K8s.ClientSet.CoreV1().ConfigMaps(namespace).Update(context.TODO(), Configmap, metaV1.UpdateOptions{}); err != nil { 84 | return err 85 | } 86 | return nil 87 | } 88 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/kube/daemonset.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | 7 | appsV1 "k8s.io/api/apps/v1" 8 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | var DaemonSet daemonSet 12 | 13 | type daemonSet struct { 14 | } 15 | 16 | type DaemonSetResp struct { 17 | Total int `json:"total"` 18 | Items []appsV1.DaemonSet `json:"items"` 19 | } 20 | 21 | type DaemonSetNp struct { 22 | NameSpace string `json:"namespace"` 23 | DaemonSetNum int `json:"daemonset_num"` 24 | } 25 | 26 | func (d *daemonSet) toCells(daemonsets []appsV1.DaemonSet) []DataCell { 27 | cells := make([]DataCell, len(daemonsets)) 28 | for i := range daemonsets { 29 | cells[i] = daemonSetCell(daemonsets[i]) 30 | } 31 | return cells 32 | } 33 | 34 | func (d *daemonSet) FromCells(cells []DataCell) []appsV1.DaemonSet { 35 | daemonSets := make([]appsV1.DaemonSet, len(cells)) 36 | for i := range cells { 37 | daemonSets[i] = appsV1.DaemonSet(cells[i].(daemonSetCell)) 38 | } 39 | return daemonSets 40 | } 41 | 42 | func (d *daemonSet) GetDaemonSets(filterName, namespace string, limit, page int) (*DaemonSetResp, error) { 43 | daemonSetList, err := K8s.ClientSet.AppsV1().DaemonSets(namespace).List(context.TODO(), metaV1.ListOptions{}) 44 | if err != nil { 45 | return nil, err 46 | } 47 | selectableData := &dataSelector{ 48 | GenericDataList: d.toCells(daemonSetList.Items), 49 | DataSelect: &DataSelectQuery{ 50 | Filter: &FilterQuery{Name: filterName}, 51 | Paginatite: &PaginateQuery{ 52 | Limit: limit, 53 | Page: page, 54 | }, 55 | }, 56 | } 57 | filterd := selectableData.Filter() 58 | total := len(filterd.GenericDataList) 59 | data := filterd.Sort().Paginate() 60 | daemonSets := d.FromCells(data.GenericDataList) 61 | return &DaemonSetResp{ 62 | Total: total, 63 | Items: daemonSets, 64 | }, nil 65 | } 66 | 67 | func (d *daemonSet) GetDaemonSetDetail(name, namespace string) (*appsV1.DaemonSet, error) { 68 | data, err := K8s.ClientSet.AppsV1().DaemonSets(namespace).Get(context.TODO(), name, metaV1.GetOptions{}) 69 | if err != nil { 70 | return nil, err 71 | } 72 | return data, nil 73 | } 74 | 75 | func (d *daemonSet) DeleteDaemonSet(name, namespace string) error { 76 | return K8s.ClientSet.AppsV1().DaemonSets(namespace).Delete(context.TODO(), name, metaV1.DeleteOptions{}) 77 | } 78 | 79 | func (d *daemonSet) UpdateDaemonSet(content, namespace string) error { 80 | var daemonset = &appsV1.DaemonSet{} 81 | if err := json.Unmarshal([]byte(content), daemonset); err != nil { 82 | return err 83 | } 84 | if _, err := K8s.ClientSet.AppsV1().DaemonSets(namespace).Update(context.TODO(), daemonset, metaV1.UpdateOptions{}); err != nil { 85 | return err 86 | } 87 | return nil 88 | } 89 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/kube/init.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "flag" 5 | "github.com/noovertime7/kubemanage/pkg/logger" 6 | "os" 7 | "path/filepath" 8 | 9 | "k8s.io/client-go/kubernetes" 10 | "k8s.io/client-go/rest" 11 | "k8s.io/client-go/tools/clientcmd" 12 | ) 13 | 14 | var K8s k8s 15 | 16 | type k8s struct { 17 | Config *rest.Config 18 | ClientSet *kubernetes.Clientset 19 | } 20 | 21 | func (k *k8s) Init() error { 22 | var err error 23 | var config *rest.Config 24 | var kubeConfig *string 25 | 26 | if home := homeDir(); home != "" { 27 | kubeConfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file") 28 | } else { 29 | kubeConfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file") 30 | } 31 | flag.Parse() 32 | 33 | // 使用 ServiceAccount 创建集群配置(InCluster模式) 34 | if config, err = rest.InClusterConfig(); err != nil { 35 | // 使用 KubeConfig 文件创建集群配置 36 | if config, err = clientcmd.BuildConfigFromFlags("", *kubeConfig); err != nil { 37 | return err 38 | } 39 | } 40 | 41 | // 创建 clientSet 42 | clientSet, err := kubernetes.NewForConfig(config) 43 | if err != nil { 44 | return err 45 | } 46 | log := logger.New() 47 | log.Info("获取k8s clientSet 成功") 48 | k.ClientSet = clientSet 49 | k.Config = config 50 | return nil 51 | } 52 | 53 | func homeDir() string { 54 | if h := os.Getenv("HOME"); h != "" { 55 | return h 56 | } 57 | return os.Getenv("USERPROFILE") // windows 58 | } 59 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/kube/namespace.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "context" 5 | "github.com/pkg/errors" 6 | coreV1 "k8s.io/api/core/v1" 7 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | var NameSpace namespace 11 | 12 | type namespace struct{} 13 | 14 | type NameSpaceResp struct { 15 | Total int `json:"total"` 16 | Items []coreV1.Namespace `json:"items"` 17 | } 18 | 19 | func (n *namespace) toCells(nodes []coreV1.Namespace) []DataCell { 20 | cells := make([]DataCell, len(nodes)) 21 | for i := range nodes { 22 | cells[i] = namespaceCell(nodes[i]) 23 | } 24 | return cells 25 | } 26 | 27 | func (n *namespace) FromCells(cells []DataCell) []coreV1.Namespace { 28 | nodes := make([]coreV1.Namespace, len(cells)) 29 | for i := range cells { 30 | nodes[i] = coreV1.Namespace(cells[i].(namespaceCell)) 31 | } 32 | return nodes 33 | } 34 | 35 | func (n *namespace) GetNameSpaces(filterName string, limit, page int) (nodesResp *NameSpaceResp, err error) { 36 | NamespaceList, err := K8s.ClientSet.CoreV1().Namespaces().List(context.TODO(), metaV1.ListOptions{}) 37 | if err != nil { 38 | return nil, errors.New("获取Pod列表失败") 39 | } 40 | //实例化dataSelector结构体,组装数据 41 | selectableData := &dataSelector{ 42 | GenericDataList: n.toCells(NamespaceList.Items), 43 | DataSelect: &DataSelectQuery{ 44 | Filter: &FilterQuery{Name: filterName}, 45 | Paginatite: &PaginateQuery{limit, page}, 46 | }, 47 | } 48 | //先过滤 49 | filtered := selectableData.Filter() 50 | total := len(filtered.GenericDataList) 51 | //排序、分页 52 | data := filtered.Sort().Paginate() 53 | //将dataCell类型转换为coreV1.Pod 54 | namespaces := n.FromCells(data.GenericDataList) 55 | return &NameSpaceResp{ 56 | total, 57 | namespaces, 58 | }, nil 59 | } 60 | 61 | // GetNameSpacesDetail 获取Node详情 62 | func (n *namespace) GetNameSpacesDetail(Name string) (*coreV1.Namespace, error) { 63 | namespacesRes, err := K8s.ClientSet.CoreV1().Namespaces().Get(context.TODO(), Name, metaV1.GetOptions{}) 64 | if err != nil { 65 | return nil, err 66 | } 67 | return namespacesRes, nil 68 | } 69 | 70 | func (n *namespace) CreateNameSpace(name string) error { 71 | ns := &coreV1.Namespace{ 72 | TypeMeta: metaV1.TypeMeta{}, 73 | ObjectMeta: metaV1.ObjectMeta{ 74 | Name: name, 75 | }, 76 | Spec: coreV1.NamespaceSpec{}, 77 | Status: coreV1.NamespaceStatus{}, 78 | } 79 | if _, err := K8s.ClientSet.CoreV1().Namespaces().Create(context.TODO(), ns, metaV1.CreateOptions{}); err != nil { 80 | return err 81 | } 82 | return nil 83 | } 84 | 85 | func (n *namespace) DeleteNameSpace(name string) error { 86 | return K8s.ClientSet.CoreV1().Namespaces().Delete(context.TODO(), name, metaV1.DeleteOptions{}) 87 | } 88 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/kube/node.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "context" 5 | 6 | coreV1 "k8s.io/api/core/v1" 7 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | var Node node 11 | 12 | type node struct{} 13 | 14 | type NodeResp struct { 15 | Total int `json:"total"` 16 | Items []coreV1.Node `json:"items"` 17 | } 18 | 19 | func (n *node) toCells(nodes []coreV1.Node) []DataCell { 20 | cells := make([]DataCell, len(nodes)) 21 | for i := range nodes { 22 | cells[i] = nodeCell(nodes[i]) 23 | } 24 | return cells 25 | } 26 | 27 | func (n *node) FromCells(cells []DataCell) []coreV1.Node { 28 | nodes := make([]coreV1.Node, len(cells)) 29 | for i := range cells { 30 | nodes[i] = coreV1.Node(cells[i].(nodeCell)) 31 | } 32 | return nodes 33 | } 34 | 35 | func (n *node) GetNodes(filterName string, limit, page int) (nodesResp *NodeResp, err error) { 36 | nodeList, err := K8s.ClientSet.CoreV1().Nodes().List(context.TODO(), metaV1.ListOptions{}) 37 | if err != nil { 38 | return nil, err 39 | } 40 | //实例化dataSelector结构体,组装数据 41 | selectableData := &dataSelector{ 42 | GenericDataList: n.toCells(nodeList.Items), 43 | DataSelect: &DataSelectQuery{ 44 | Filter: &FilterQuery{Name: filterName}, 45 | Paginatite: &PaginateQuery{limit, page}, 46 | }, 47 | } 48 | //先过滤 49 | filtered := selectableData.Filter() 50 | total := len(filtered.GenericDataList) 51 | //排序、分页 52 | data := filtered.Sort().Paginate() 53 | //将dataCell类型转换为coreV1.Pod 54 | nodes := n.FromCells(data.GenericDataList) 55 | return &NodeResp{ 56 | total, 57 | nodes, 58 | }, nil 59 | } 60 | 61 | // GetNodeDetail 获取Node详情 62 | func (n *node) GetNodeDetail(Name string) (*coreV1.Node, error) { 63 | nodeRes, err := K8s.ClientSet.CoreV1().Nodes().Get(context.TODO(), Name, metaV1.GetOptions{}) 64 | if err != nil { 65 | return nil, err 66 | } 67 | return nodeRes, nil 68 | } 69 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/kube/persistentVolumeClaim.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | coreV1 "k8s.io/api/core/v1" 7 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | var PersistentVolumeClaim persistentVolumeClaim 11 | 12 | type persistentVolumeClaim struct { 13 | } 14 | 15 | type PersistentVolumeClaimResp struct { 16 | Total int `json:"total"` 17 | Items []coreV1.PersistentVolumeClaim `json:"items"` 18 | } 19 | 20 | type PersistentVolumeClaimNp struct { 21 | NameSpace string `json:"namespace"` 22 | PersistentVolumeClaimNum int `json:"PersistentVolumeClaim_num"` 23 | } 24 | 25 | func (d *persistentVolumeClaim) toCells(PersistentVolumeClaims []coreV1.PersistentVolumeClaim) []DataCell { 26 | cells := make([]DataCell, len(PersistentVolumeClaims)) 27 | for i := range PersistentVolumeClaims { 28 | cells[i] = persistentVolumeClaimCell(PersistentVolumeClaims[i]) 29 | } 30 | return cells 31 | } 32 | 33 | func (d *persistentVolumeClaim) FromCells(cells []DataCell) []coreV1.PersistentVolumeClaim { 34 | PersistentVolumeClaims := make([]coreV1.PersistentVolumeClaim, len(cells)) 35 | for i := range cells { 36 | PersistentVolumeClaims[i] = coreV1.PersistentVolumeClaim(cells[i].(persistentVolumeClaimCell)) 37 | } 38 | return PersistentVolumeClaims 39 | } 40 | 41 | func (d *persistentVolumeClaim) DeletePersistentVolumeClaim(name, namespace string) error { 42 | return K8s.ClientSet.CoreV1().PersistentVolumeClaims(namespace).Delete(context.TODO(), name, metaV1.DeleteOptions{}) 43 | } 44 | 45 | func (d *persistentVolumeClaim) UpdatePersistentVolumeClaim(content, namespace string) error { 46 | var PersistentVolumeClaim = &coreV1.PersistentVolumeClaim{} 47 | if err := json.Unmarshal([]byte(content), PersistentVolumeClaim); err != nil { 48 | return err 49 | } 50 | if _, err := K8s.ClientSet.CoreV1().PersistentVolumeClaims(namespace).Update(context.TODO(), PersistentVolumeClaim, metaV1.UpdateOptions{}); err != nil { 51 | return err 52 | } 53 | return nil 54 | } 55 | 56 | func (d *persistentVolumeClaim) GetPersistentVolumeClaims(filterName, namespace string, limit, page int) (*PersistentVolumeClaimResp, error) { 57 | PersistentVolumeClaimList, err := K8s.ClientSet.CoreV1().PersistentVolumeClaims(namespace).List(context.TODO(), metaV1.ListOptions{}) 58 | if err != nil { 59 | return nil, err 60 | } 61 | selectableData := &dataSelector{ 62 | GenericDataList: d.toCells(PersistentVolumeClaimList.Items), 63 | DataSelect: &DataSelectQuery{ 64 | Filter: &FilterQuery{Name: filterName}, 65 | Paginatite: &PaginateQuery{ 66 | Limit: limit, 67 | Page: page, 68 | }, 69 | }, 70 | } 71 | filterd := selectableData.Filter() 72 | total := len(filterd.GenericDataList) 73 | data := filterd.Sort().Paginate() 74 | PersistentVolumeClaims := d.FromCells(data.GenericDataList) 75 | return &PersistentVolumeClaimResp{ 76 | Total: total, 77 | Items: PersistentVolumeClaims, 78 | }, nil 79 | } 80 | 81 | func (d *persistentVolumeClaim) GetPersistentVolumeClaimDetail(name, namespace string) (*coreV1.PersistentVolumeClaim, error) { 82 | data, err := K8s.ClientSet.CoreV1().PersistentVolumeClaims(namespace).Get(context.TODO(), name, metaV1.GetOptions{}) 83 | if err != nil { 84 | return nil, err 85 | } 86 | return data, nil 87 | } 88 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/kube/persistentvolume.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/pkg/errors" 7 | coreV1 "k8s.io/api/core/v1" 8 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | var PersistentVolume persistentVolume 12 | 13 | type persistentVolume struct{} 14 | 15 | type PersistentVolumeResp struct { 16 | Total int `json:"total"` 17 | Items []coreV1.PersistentVolume `json:"items"` 18 | } 19 | 20 | func (n *persistentVolume) toCells(pvs []coreV1.PersistentVolume) []DataCell { 21 | cells := make([]DataCell, len(pvs)) 22 | for i := range pvs { 23 | cells[i] = persistentvolumesCell(pvs[i]) 24 | } 25 | return cells 26 | } 27 | 28 | func (n *persistentVolume) FromCells(cells []DataCell) []coreV1.PersistentVolume { 29 | nodes := make([]coreV1.PersistentVolume, len(cells)) 30 | for i := range cells { 31 | nodes[i] = coreV1.PersistentVolume(cells[i].(persistentvolumesCell)) 32 | } 33 | return nodes 34 | } 35 | 36 | func (n *persistentVolume) GetPersistentVolumes(filterName string, limit, page int) (*PersistentVolumeResp, error) { 37 | PersistentVolumeList, err := K8s.ClientSet.CoreV1().PersistentVolumes().List(context.TODO(), metaV1.ListOptions{}) 38 | if err != nil { 39 | return nil, errors.New("获取Pod列表失败") 40 | } 41 | //实例化dataSelector结构体,组装数据 42 | selectableData := &dataSelector{ 43 | GenericDataList: n.toCells(PersistentVolumeList.Items), 44 | DataSelect: &DataSelectQuery{ 45 | Filter: &FilterQuery{Name: filterName}, 46 | Paginatite: &PaginateQuery{limit, page}, 47 | }, 48 | } 49 | //先过滤 50 | filtered := selectableData.Filter() 51 | total := len(filtered.GenericDataList) 52 | //排序、分页 53 | data := filtered.Sort().Paginate() 54 | //将dataCell类型转换为coreV1.Pod 55 | PersistentVolumes := n.FromCells(data.GenericDataList) 56 | return &PersistentVolumeResp{ 57 | total, 58 | PersistentVolumes, 59 | }, nil 60 | } 61 | 62 | // GetPersistentVolumesDetail 获取PersistentVolume详情 63 | func (n *persistentVolume) GetPersistentVolumesDetail(Name string) (*coreV1.PersistentVolume, error) { 64 | PersistentVolumesRes, err := K8s.ClientSet.CoreV1().PersistentVolumes().Get(context.TODO(), Name, metaV1.GetOptions{}) 65 | if err != nil { 66 | return nil, err 67 | } 68 | return PersistentVolumesRes, nil 69 | } 70 | 71 | func (n *persistentVolume) DeletePersistentVolume(name string) error { 72 | return K8s.ClientSet.CoreV1().PersistentVolumes().Delete(context.TODO(), name, metaV1.DeleteOptions{}) 73 | } 74 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/kube/pod.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "net/http" 5 | 6 | coreV1 "k8s.io/api/core/v1" 7 | "k8s.io/client-go/kubernetes" 8 | "k8s.io/client-go/kubernetes/scheme" 9 | "k8s.io/client-go/tools/remotecommand" 10 | 11 | "github.com/noovertime7/kubemanage/dao" 12 | "github.com/noovertime7/kubemanage/dto/kubeDto" 13 | "github.com/noovertime7/kubemanage/pkg/logger" 14 | "github.com/noovertime7/kubemanage/pkg/types" 15 | ) 16 | 17 | type PodsGetter interface { 18 | Pods(cloud string) PodInterface 19 | } 20 | 21 | type PodInterface interface { 22 | WebShellHandler(webShellOptions *kubeDto.WebShellOptions, w http.ResponseWriter, r *http.Request) error 23 | } 24 | 25 | type pods struct { 26 | client *kubernetes.Clientset 27 | cloud string 28 | factory dao.ShareDaoFactory 29 | } 30 | 31 | func NewPods(c *kubernetes.Clientset, cloud string, factory dao.ShareDaoFactory) *pods { 32 | return &pods{ 33 | client: c, 34 | cloud: cloud, 35 | factory: factory, 36 | } 37 | } 38 | 39 | func (c *pods) WebShellHandler(webShellOptions *kubeDto.WebShellOptions, w http.ResponseWriter, r *http.Request) error { 40 | log := logger.New() 41 | session, err := types.NewTerminalSession(w, r) 42 | if err != nil { 43 | return err 44 | } 45 | // 处理关闭 46 | defer func() { 47 | _ = session.Close() 48 | }() 49 | 50 | // 组装 POST 请求 51 | req := K8s.ClientSet.CoreV1().RESTClient().Post(). 52 | Resource("pods"). 53 | Name(webShellOptions.Pod). 54 | Namespace(webShellOptions.Namespace). 55 | SubResource("exec"). 56 | VersionedParams(&coreV1.PodExecOptions{ 57 | Container: webShellOptions.Container, 58 | Command: []string{"/bin/sh"}, 59 | Stderr: true, 60 | Stdin: true, 61 | Stdout: true, 62 | TTY: true, 63 | }, scheme.ParameterCodec) 64 | 65 | // remotecommand 主要实现了http 转 SPDY 添加X-Stream-Protocol-Version相关header 并发送请求 66 | executor, err := remotecommand.NewSPDYExecutor(K8s.Config, "POST", req.URL()) 67 | if err != nil { 68 | log.ErrorWithErr("remotecommand pod error", err) 69 | return err 70 | } 71 | // 与 kubelet 建立 stream 连接 72 | if err = executor.Stream(remotecommand.StreamOptions{ 73 | Stdout: session, 74 | Stdin: session, 75 | Stderr: session, 76 | TerminalSizeQueue: session, 77 | Tty: true, 78 | }); err != nil { 79 | log.ErrorWithErr("exec pod error", err) 80 | _, _ = session.Write([]byte("exec pod command failed," + err.Error())) 81 | // 标记关闭terminal 82 | session.Done() 83 | } 84 | return nil 85 | } 86 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/kube/pod_v1.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "encoding/json" 7 | "io" 8 | 9 | coreV1 "k8s.io/api/core/v1" 10 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | ) 12 | 13 | var Pod pod 14 | 15 | type pod struct{} 16 | 17 | type PodsResp struct { 18 | Total int `json:"total"` 19 | Items []coreV1.Pod `json:"items"` 20 | } 21 | 22 | type PodsNp struct { 23 | Namespace string `json:"namespace"` 24 | PodNum int `json:"pod_num"` 25 | } 26 | 27 | // GetPods 获取pod列表支持、过滤、排序以及分页 28 | func (p *pod) GetPods(filterName, namespace string, limit, page int) (podsResp *PodsResp, err error) { 29 | podlist, err := K8s.ClientSet.CoreV1().Pods(namespace).List(context.TODO(), metaV1.ListOptions{}) 30 | if err != nil { 31 | return nil, err 32 | } 33 | //实例化dataSelector结构体,组装数据 34 | selectableData := &dataSelector{ 35 | GenericDataList: p.toCells(podlist.Items), 36 | DataSelect: &DataSelectQuery{ 37 | Filter: &FilterQuery{Name: filterName}, 38 | Paginatite: &PaginateQuery{limit, page}, 39 | }, 40 | } 41 | //先过滤 42 | filtered := selectableData.Filter() 43 | total := len(filtered.GenericDataList) 44 | //排序、分页 45 | data := filtered.Sort().Paginate() 46 | //将dataCell类型转换为coreV1.Pod 47 | pods := p.FromCells(data.GenericDataList) 48 | return &PodsResp{ 49 | total, 50 | pods, 51 | }, nil 52 | } 53 | 54 | // GetPodDetail 获取Pod详情 55 | func (p *pod) GetPodDetail(podName, namespace string) (pod *coreV1.Pod, err error) { 56 | podRes, err := K8s.ClientSet.CoreV1().Pods(namespace).Get(context.TODO(), podName, metaV1.GetOptions{}) 57 | if err != nil { 58 | return nil, err 59 | } 60 | return podRes, nil 61 | } 62 | 63 | // DeletePod 删除Pod 64 | func (p *pod) DeletePod(podName, namespace string) error { 65 | err := K8s.ClientSet.CoreV1().Pods(namespace).Delete(context.TODO(), podName, metaV1.DeleteOptions{}) 66 | if err != nil { 67 | return err 68 | } 69 | return nil 70 | } 71 | 72 | // UpdatePod 更新Pod 73 | func (p *pod) UpdatePod(namespace, content string) error { 74 | var pod = &coreV1.Pod{} 75 | //将json反序列换为pod类型 76 | if err := json.Unmarshal([]byte(content), pod); err != nil { 77 | return err 78 | } 79 | _, err := K8s.ClientSet.CoreV1().Pods(namespace).Update(context.TODO(), pod, metaV1.UpdateOptions{}) 80 | if err != nil { 81 | return err 82 | } 83 | return nil 84 | } 85 | 86 | // GetPodContainer 获取Pod容器名 87 | func (p *pod) GetPodContainer(podName, namespace string) (containers []string, err error) { 88 | pod, err := p.GetPodDetail(podName, namespace) 89 | if err != nil { 90 | return nil, err 91 | } 92 | //从pod中获取containers 93 | for _, container := range pod.Spec.Containers { 94 | containers = append(containers, container.Name) 95 | } 96 | return containers, nil 97 | } 98 | 99 | // GetPodLog 获取容器日志 100 | func (p *pod) GetPodLog(containerName, podName, namespace string) (log string, err error) { 101 | //设置日志的配置,容器名,获取的内容的配置 102 | lineLimit := int64(100) 103 | op := &coreV1.PodLogOptions{ 104 | Container: containerName, 105 | TailLines: &lineLimit, 106 | } 107 | //获取request的实例 108 | req := K8s.ClientSet.CoreV1().Pods(namespace).GetLogs(podName, op) 109 | //发起stream连接,得到response.body 110 | podLogs, err := req.Stream(context.TODO()) 111 | if err != nil { 112 | return "", err 113 | } 114 | defer func(podLogs io.ReadCloser) { 115 | err := podLogs.Close() 116 | if err != nil { 117 | 118 | } 119 | }(podLogs) 120 | //将body写入缓冲区,转换为可读的string类型 121 | buf := new(bytes.Buffer) 122 | if _, err := io.Copy(buf, podLogs); err != nil { 123 | return "", err 124 | } 125 | return buf.String(), nil 126 | } 127 | 128 | // GetPodNumPerNp 获取namespace下的Pod数量 129 | func (p *pod) GetPodNumPerNp() (podsNps []*PodsNp, err error) { 130 | namespaceList, err := K8s.ClientSet.CoreV1().Namespaces().List(context.TODO(), metaV1.ListOptions{}) 131 | if err != nil { 132 | return nil, err 133 | } 134 | for _, namespace := range namespaceList.Items { 135 | podList, err := K8s.ClientSet.CoreV1().Pods(namespace.Name).List(context.TODO(), metaV1.ListOptions{}) 136 | if err != nil { 137 | return nil, err 138 | } 139 | //组装数据 140 | podsNp := &PodsNp{ 141 | Namespace: namespace.Name, 142 | PodNum: len(podList.Items), 143 | } 144 | podsNps = append(podsNps, podsNp) 145 | } 146 | return podsNps, nil 147 | } 148 | 149 | // 类型转换的方法 coreV1.pod => DataCell,DataCell => coreV1.pod 150 | func (p *pod) toCells(pods []coreV1.Pod) []DataCell { 151 | cells := make([]DataCell, len(pods)) 152 | for i := range pods { 153 | cells[i] = podCell(pods[i]) 154 | } 155 | return cells 156 | } 157 | 158 | func (p *pod) FromCells(cells []DataCell) []coreV1.Pod { 159 | pods := make([]coreV1.Pod, len(cells)) 160 | for i := range cells { 161 | pods[i] = coreV1.Pod(cells[i].(podCell)) 162 | } 163 | return pods 164 | } 165 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/kube/secret.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | 7 | coreV1 "k8s.io/api/core/v1" 8 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | var Secret secret 12 | 13 | type secret struct { 14 | } 15 | 16 | type SecretResp struct { 17 | Total int `json:"total"` 18 | Items []coreV1.Secret `json:"items"` 19 | } 20 | 21 | type SecretNp struct { 22 | NameSpace string `json:"namespace"` 23 | SecretNum int `json:"secret_num"` 24 | } 25 | 26 | func (d *secret) toCells(secrets []coreV1.Secret) []DataCell { 27 | cells := make([]DataCell, len(secrets)) 28 | for i := range secrets { 29 | cells[i] = secretCell(secrets[i]) 30 | } 31 | return cells 32 | } 33 | 34 | func (d *secret) FromCells(cells []DataCell) []coreV1.Secret { 35 | secrets := make([]coreV1.Secret, len(cells)) 36 | for i := range cells { 37 | secrets[i] = coreV1.Secret(cells[i].(secretCell)) 38 | } 39 | return secrets 40 | } 41 | 42 | func (d *secret) GetSecrets(filterName, namespace string, limit, page int) (*SecretResp, error) { 43 | SecretsList, err := K8s.ClientSet.CoreV1().Secrets(namespace).List(context.TODO(), metaV1.ListOptions{}) 44 | if err != nil { 45 | return nil, err 46 | } 47 | selectableData := &dataSelector{ 48 | GenericDataList: d.toCells(SecretsList.Items), 49 | DataSelect: &DataSelectQuery{ 50 | Filter: &FilterQuery{Name: filterName}, 51 | Paginatite: &PaginateQuery{ 52 | Limit: limit, 53 | Page: page, 54 | }, 55 | }, 56 | } 57 | filterd := selectableData.Filter() 58 | total := len(filterd.GenericDataList) 59 | data := filterd.Sort().Paginate() 60 | secrets := d.FromCells(data.GenericDataList) 61 | return &SecretResp{ 62 | Total: total, 63 | Items: secrets, 64 | }, nil 65 | } 66 | 67 | func (d *secret) GetSecretsDetail(name, namespace string) (*coreV1.Secret, error) { 68 | data, err := K8s.ClientSet.CoreV1().Secrets(namespace).Get(context.TODO(), name, metaV1.GetOptions{}) 69 | if err != nil { 70 | return nil, err 71 | } 72 | return data, nil 73 | } 74 | 75 | func (d *secret) DeleteSecrets(name, namespace string) error { 76 | return K8s.ClientSet.CoreV1().Secrets(namespace).Delete(context.TODO(), name, metaV1.DeleteOptions{}) 77 | } 78 | 79 | func (d *secret) UpdateSecrets(content, namespace string) error { 80 | var secret = &coreV1.Secret{} 81 | if err := json.Unmarshal([]byte(content), secret); err != nil { 82 | return err 83 | } 84 | if _, err := K8s.ClientSet.CoreV1().Secrets(namespace).Update(context.TODO(), secret, metaV1.UpdateOptions{}); err != nil { 85 | return err 86 | } 87 | return nil 88 | } 89 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/kube/service.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | 7 | coreV1 "k8s.io/api/core/v1" 8 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | "k8s.io/apimachinery/pkg/util/intstr" 10 | 11 | "github.com/noovertime7/kubemanage/dto/kubeDto" 12 | ) 13 | 14 | var Service service 15 | 16 | type service struct{} 17 | 18 | type serviceResp struct { 19 | Total int `json:"total"` 20 | Items []coreV1.Service `json:"items"` 21 | } 22 | 23 | type serviceNp struct { 24 | NameSpace string `json:"namespace"` 25 | ServiceNum int `json:"service_num"` 26 | } 27 | 28 | func (s *service) toCells(services []coreV1.Service) []DataCell { 29 | cells := make([]DataCell, len(services)) 30 | for i := range services { 31 | cells[i] = serviceCell(services[i]) 32 | } 33 | return cells 34 | } 35 | 36 | func (s *service) FromCells(cells []DataCell) []coreV1.Service { 37 | services := make([]coreV1.Service, len(cells)) 38 | for i := range cells { 39 | services[i] = coreV1.Service(cells[i].(serviceCell)) 40 | } 41 | return services 42 | } 43 | 44 | func (s *service) CreateService(data *kubeDto.ServiceCreateInput) error { 45 | service := &coreV1.Service{ 46 | ObjectMeta: metaV1.ObjectMeta{ 47 | Name: data.Name, 48 | Namespace: data.NameSpace, 49 | Labels: data.Label, 50 | }, 51 | Spec: coreV1.ServiceSpec{ 52 | Type: coreV1.ServiceType(data.Type), 53 | Ports: []coreV1.ServicePort{ 54 | { 55 | Name: "http", 56 | Port: data.Port, 57 | Protocol: "TCP", 58 | TargetPort: intstr.IntOrString{ 59 | Type: 0, 60 | IntVal: data.ContainerPort, 61 | }, 62 | }, 63 | }, 64 | Selector: data.Label, 65 | }, 66 | Status: coreV1.ServiceStatus{}, 67 | } 68 | if data.NodePort != 0 && data.Type == "NodePort" { 69 | service.Spec.Ports[0].NodePort = data.NodePort 70 | } 71 | //创建service 72 | if _, err := K8s.ClientSet.CoreV1().Services(data.NameSpace).Create(context.TODO(), service, metaV1.CreateOptions{}); err != nil { 73 | return err 74 | } 75 | return nil 76 | } 77 | 78 | func (s *service) DeleteService(name, namespace string) error { 79 | return K8s.ClientSet.CoreV1().Services(namespace).Delete(context.TODO(), name, metaV1.DeleteOptions{}) 80 | } 81 | 82 | func (s *service) UpdateService(namespace, content string) error { 83 | var Service = &coreV1.Service{} 84 | if err := json.Unmarshal([]byte(content), Service); err != nil { 85 | return err 86 | } 87 | if _, err := K8s.ClientSet.CoreV1().Services(namespace).Update(context.TODO(), Service, metaV1.UpdateOptions{}); err != nil { 88 | return err 89 | } 90 | return nil 91 | } 92 | 93 | func (s *service) GetServiceList(filterName, namespace string, limit, page int) (*serviceResp, error) { 94 | ServiceList, err := K8s.ClientSet.CoreV1().Services(namespace).List(context.TODO(), metaV1.ListOptions{}) 95 | if err != nil { 96 | return nil, err 97 | } 98 | //实例化dataSelector结构体,组装数据 99 | selectableData := &dataSelector{ 100 | GenericDataList: s.toCells(ServiceList.Items), 101 | DataSelect: &DataSelectQuery{ 102 | Filter: &FilterQuery{Name: filterName}, 103 | Paginatite: &PaginateQuery{limit, page}, 104 | }, 105 | } 106 | //先过滤 107 | filtered := selectableData.Filter() 108 | total := len(filtered.GenericDataList) 109 | //排序、分页 110 | data := filtered.Sort().Paginate() 111 | //将dataCell类型转换为coreV1.Pod 112 | Services := s.FromCells(data.GenericDataList) 113 | return &serviceResp{ 114 | total, 115 | Services, 116 | }, nil 117 | } 118 | 119 | func (s *service) GetServiceDetail(name, namespace string) (*coreV1.Service, error) { 120 | data, err := K8s.ClientSet.CoreV1().Services(namespace).Get(context.TODO(), name, metaV1.GetOptions{}) 121 | if err != nil { 122 | return nil, err 123 | } 124 | return data, nil 125 | } 126 | 127 | func (s *service) GetServiceNp() ([]*serviceNp, error) { 128 | namespaceList, err := K8s.ClientSet.CoreV1().Namespaces().List(context.TODO(), metaV1.ListOptions{}) 129 | if err != nil { 130 | return nil, err 131 | } 132 | var services []*serviceNp 133 | for _, namespace := range namespaceList.Items { 134 | serviceList, err := K8s.ClientSet.CoreV1().Services(namespace.Name).List(context.TODO(), metaV1.ListOptions{}) 135 | if err != nil { 136 | return nil, err 137 | } 138 | //组装数据 139 | ServiceNp := &serviceNp{ 140 | NameSpace: namespace.Name, 141 | ServiceNum: len(serviceList.Items), 142 | } 143 | services = append(services, ServiceNp) 144 | } 145 | return services, nil 146 | } 147 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/kube/statefulset.go: -------------------------------------------------------------------------------- 1 | package kube 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | appsV1 "k8s.io/api/apps/v1" 7 | metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | var StatefulSet statefulSet 11 | 12 | type statefulSet struct{} 13 | 14 | type statefulSetResp struct { 15 | Total int `json:"total"` 16 | Items []appsV1.StatefulSet `json:"items"` 17 | } 18 | 19 | type StatefulSetNp struct { 20 | NameSpace string `json:"namespace"` 21 | DaemonSetNum int `json:"daemonset_num"` 22 | } 23 | 24 | func (d *statefulSet) toCells(statefulSets []appsV1.StatefulSet) []DataCell { 25 | cells := make([]DataCell, len(statefulSets)) 26 | for i := range statefulSets { 27 | cells[i] = statefulSetCell(statefulSets[i]) 28 | } 29 | return cells 30 | } 31 | 32 | func (d *statefulSet) FromCells(cells []DataCell) []appsV1.StatefulSet { 33 | statefulSets := make([]appsV1.StatefulSet, len(cells)) 34 | for i := range cells { 35 | statefulSets[i] = appsV1.StatefulSet(cells[i].(statefulSetCell)) 36 | } 37 | return statefulSets 38 | } 39 | 40 | func (d *statefulSet) GetStatefulSets(filterName, namespace string, limit, page int) (*statefulSetResp, error) { 41 | statefulSetList, err := K8s.ClientSet.AppsV1().StatefulSets(namespace).List(context.TODO(), metaV1.ListOptions{}) 42 | if err != nil { 43 | return nil, err 44 | } 45 | selectableData := &dataSelector{ 46 | GenericDataList: d.toCells(statefulSetList.Items), 47 | DataSelect: &DataSelectQuery{ 48 | Filter: &FilterQuery{Name: filterName}, 49 | Paginatite: &PaginateQuery{ 50 | Limit: limit, 51 | Page: page, 52 | }, 53 | }, 54 | } 55 | filterd := selectableData.Filter() 56 | total := len(filterd.GenericDataList) 57 | data := filterd.Sort().Paginate() 58 | statefulSets := d.FromCells(data.GenericDataList) 59 | return &statefulSetResp{ 60 | Total: total, 61 | Items: statefulSets, 62 | }, nil 63 | } 64 | 65 | func (d *statefulSet) GetStatefulSetDetail(name, namespace string) (*appsV1.StatefulSet, error) { 66 | data, err := K8s.ClientSet.AppsV1().StatefulSets(namespace).Get(context.TODO(), name, metaV1.GetOptions{}) 67 | if err != nil { 68 | return nil, err 69 | } 70 | return data, nil 71 | } 72 | 73 | func (d *statefulSet) DeleteStatefulSet(name, namespace string) error { 74 | return K8s.ClientSet.AppsV1().StatefulSets(namespace).Delete(context.TODO(), name, metaV1.DeleteOptions{}) 75 | } 76 | 77 | func (d *statefulSet) UpdateStatefulSet(content, namespace string) error { 78 | var statefulSet = &appsV1.StatefulSet{} 79 | if err := json.Unmarshal([]byte(content), statefulSet); err != nil { 80 | return err 81 | } 82 | if _, err := K8s.ClientSet.AppsV1().StatefulSets(namespace).Update(context.TODO(), statefulSet, metaV1.UpdateOptions{}); err != nil { 83 | return err 84 | } 85 | return nil 86 | } 87 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/setup.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "github.com/noovertime7/kubemanage/cmd/app/config" 5 | "github.com/noovertime7/kubemanage/cmd/app/options" 6 | "github.com/noovertime7/kubemanage/pkg/logger" 7 | ) 8 | 9 | var CoreV1 CoreService 10 | 11 | var Log Logger 12 | 13 | // Setup 完成核心应用接口的设置 14 | func Setup(o *options.Options) { 15 | Log = logger.New() 16 | CoreV1 = New(config.SysConfig, o.Factory) 17 | } 18 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/sys/api.go: -------------------------------------------------------------------------------- 1 | package sys 2 | 3 | import ( 4 | "context" 5 | "github.com/noovertime7/kubemanage/dao" 6 | "github.com/noovertime7/kubemanage/dao/model" 7 | ) 8 | 9 | type APIServiceGetter interface { 10 | Api() APIService 11 | } 12 | 13 | type APIService interface { 14 | GetApiList(ctx context.Context) ([]model.SysApi, error) 15 | } 16 | 17 | var _ APIService = &apiService{} 18 | 19 | func NewApiService(factory dao.ShareDaoFactory) APIService { 20 | return &apiService{factory: factory} 21 | } 22 | 23 | type apiService struct { 24 | factory dao.ShareDaoFactory 25 | } 26 | 27 | func (a *apiService) GetApiList(ctx context.Context) ([]model.SysApi, error) { 28 | // 不做任何限制查询全量数据 29 | var search model.SysApi 30 | return a.factory.Api().FindList(ctx, search) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/sys/authority.go: -------------------------------------------------------------------------------- 1 | package sys 2 | 3 | import ( 4 | "context" 5 | "github.com/noovertime7/kubemanage/dto" 6 | 7 | "github.com/noovertime7/kubemanage/dao" 8 | "github.com/noovertime7/kubemanage/dao/model" 9 | ) 10 | 11 | type AuthorityGetter interface { 12 | Authority() Authority 13 | } 14 | 15 | type Authority interface { 16 | SetMenuAuthority(ctx context.Context, auth *model.SysAuthority) error 17 | GetAuthorityList(ctx context.Context, pageInfo dto.PageInfo) (*dto.AuthorityList, error) 18 | } 19 | 20 | type authority struct { 21 | factory dao.ShareDaoFactory 22 | } 23 | 24 | func NewAuthority(factory dao.ShareDaoFactory) *authority { 25 | return &authority{factory: factory} 26 | } 27 | 28 | func (a *authority) SetMenuAuthority(ctx context.Context, auth *model.SysAuthority) error { 29 | return a.factory.Authority().SetMenuAuthority(ctx, auth) 30 | } 31 | 32 | func (a *authority) GetAuthorityList(ctx context.Context, pageInfo dto.PageInfo) (*dto.AuthorityList, error) { 33 | list, total, err := a.factory.Authority().PageList(ctx, pageInfo) 34 | if err != nil { 35 | return nil, err 36 | } 37 | return &dto.AuthorityList{ 38 | PageInfo: pageInfo, 39 | Total: total, 40 | AuthorityListItem: list, 41 | }, nil 42 | } 43 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/sys/casbin.go: -------------------------------------------------------------------------------- 1 | package sys 2 | 3 | import ( 4 | "strconv" 5 | "sync" 6 | 7 | "github.com/casbin/casbin/v2" 8 | "github.com/casbin/casbin/v2/model" 9 | gormadapter "github.com/casbin/gorm-adapter/v3" 10 | "github.com/pkg/errors" 11 | 12 | "github.com/noovertime7/kubemanage/dao" 13 | "github.com/noovertime7/kubemanage/dto" 14 | ) 15 | 16 | type CasbinServiceGetter interface { 17 | CasbinService() CasbinService 18 | } 19 | 20 | type CasbinService interface { 21 | UpdateCasbin(AuthorityID uint, casbinInfos []dto.CasbinInfo) error 22 | UpdateCasbinApi(oldPath string, newPath string, oldMethod string, newMethod string) error 23 | GetPolicyPathByAuthorityId(AuthorityID uint) (pathMaps []dto.CasbinInfo) 24 | Casbin() *casbin.CachedEnforcer 25 | } 26 | 27 | type casbinService struct { 28 | factory dao.ShareDaoFactory 29 | } 30 | 31 | func NewCasbinService(factory dao.ShareDaoFactory) CasbinService { 32 | return &casbinService{factory: factory} 33 | } 34 | 35 | func (c *casbinService) UpdateCasbin(AuthorityID uint, casbinInfos []dto.CasbinInfo) error { 36 | authorityId := strconv.Itoa(int(AuthorityID)) 37 | c.ClearCasbin(0, authorityId) 38 | var rules [][]string 39 | for _, v := range casbinInfos { 40 | rules = append(rules, []string{authorityId, v.Path, v.Method}) 41 | } 42 | e := c.Casbin() 43 | success, _ := e.AddPolicies(rules) 44 | if !success { 45 | return errors.New("存在相同api,添加失败,请联系管理员") 46 | } 47 | err := e.InvalidateCache() 48 | if err != nil { 49 | return err 50 | } 51 | return nil 52 | } 53 | 54 | func (c *casbinService) UpdateCasbinApi(oldPath string, newPath string, oldMethod string, newMethod string) error { 55 | err := c.factory.GetDB().Model(&gormadapter.CasbinRule{}).Where("v1 = ? AND v2 = ?", oldPath, oldMethod).Updates(map[string]interface{}{ 56 | "v1": newPath, 57 | "v2": newMethod, 58 | }).Error 59 | e := c.Casbin() 60 | err = e.InvalidateCache() 61 | if err != nil { 62 | return err 63 | } 64 | return err 65 | } 66 | 67 | func (c *casbinService) GetPolicyPathByAuthorityId(AuthorityID uint) (pathMaps []dto.CasbinInfo) { 68 | e := c.Casbin() 69 | authorityId := strconv.Itoa(int(AuthorityID)) 70 | list := e.GetFilteredPolicy(0, authorityId) 71 | for _, v := range list { 72 | pathMaps = append(pathMaps, dto.CasbinInfo{ 73 | Path: v[1], 74 | Method: v[2], 75 | }) 76 | } 77 | return pathMaps 78 | } 79 | 80 | func (c *casbinService) ClearCasbin(v int, p ...string) bool { 81 | e := c.Casbin() 82 | success, _ := e.RemoveFilteredPolicy(v, p...) 83 | return success 84 | } 85 | 86 | var ( 87 | cachedEnforcer *casbin.CachedEnforcer 88 | once sync.Once 89 | ) 90 | 91 | func (c *casbinService) Casbin() *casbin.CachedEnforcer { 92 | once.Do(func() { 93 | a, _ := gormadapter.NewAdapterByDB(c.factory.GetDB()) 94 | text := ` 95 | [request_definition] 96 | r = sub, obj, act 97 | 98 | [policy_definition] 99 | p = sub, obj, act 100 | 101 | [role_definition] 102 | g = _, _ 103 | 104 | [policy_effect] 105 | e = some(where (p.eft == allow)) 106 | 107 | [matchers] 108 | m = g(r.sub, p.sub) && keyMatch2(r.obj, p.obj) && regexMatch(r.act, p.act) || r.sub == "111" 109 | ` 110 | m, err := model.NewModelFromString(text) 111 | if err != nil { 112 | return 113 | } 114 | cachedEnforcer, _ = casbin.NewCachedEnforcer(m, a) 115 | cachedEnforcer.SetExpireTime(60 * 60) 116 | _ = cachedEnforcer.LoadPolicy() 117 | }) 118 | return cachedEnforcer 119 | } 120 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/sys/menu.go: -------------------------------------------------------------------------------- 1 | package sys 2 | 3 | import ( 4 | "context" 5 | "strconv" 6 | 7 | "github.com/pkg/errors" 8 | "gorm.io/gorm" 9 | 10 | "github.com/noovertime7/kubemanage/dao" 11 | "github.com/noovertime7/kubemanage/dao/model" 12 | "github.com/noovertime7/kubemanage/dto" 13 | ) 14 | 15 | type MenuGetter interface { 16 | Menu() MenuService 17 | } 18 | 19 | type MenuService interface { 20 | GetMenuByAuthorityID(ctx context.Context, authorityId uint) ([]model.SysMenu, error) 21 | GetBassMenu(ctx context.Context) ([]model.SysBaseMenu, error) 22 | AddBaseMenu(ctx context.Context, in *dto.AddSysMenusInput) error 23 | AddMenuAuthority(ctx context.Context, menus []model.SysBaseMenu, authorityId uint) error 24 | } 25 | 26 | type menuService struct { 27 | factory dao.ShareDaoFactory 28 | } 29 | 30 | func NewMenuService(factory dao.ShareDaoFactory) *menuService { 31 | return &menuService{factory: factory} 32 | } 33 | 34 | // GetBassMenu 获取全量的菜单 35 | func (m *menuService) GetBassMenu(ctx context.Context) ([]model.SysBaseMenu, error) { 36 | treeMap, err := m.getBaseMenuTreeMap(ctx) 37 | if err != nil { 38 | return nil, err 39 | } 40 | menus := treeMap["0"] 41 | for i := 0; i < len(menus); i++ { 42 | if err := m.getBaseChildrenList(&menus[i], treeMap); err != nil { 43 | return nil, err 44 | } 45 | } 46 | return menus, nil 47 | } 48 | 49 | func (m *menuService) GetMenuByAuthorityID(ctx context.Context, authorityId uint) ([]model.SysMenu, error) { 50 | menuTree, err := m.getMenuTree(ctx, authorityId) 51 | if err != nil { 52 | return nil, err 53 | } 54 | //parent_id = 0 ,代表所有跟路由 55 | menus := menuTree["0"] 56 | for i := 0; i < len(menus); i++ { 57 | err = m.getChildrenList(&menus[i], menuTree) 58 | } 59 | return menus, nil 60 | } 61 | 62 | // AddBaseMenu 添加基础路由 63 | func (m *menuService) AddBaseMenu(ctx context.Context, in *dto.AddSysMenusInput) error { 64 | menuInfo := &model.SysBaseMenu{ 65 | ParentId: in.ParentId, 66 | Name: in.Name, 67 | Path: in.Path, 68 | Hidden: in.Hidden, 69 | Sort: in.Sort, 70 | Meta: in.Meta, 71 | } 72 | menu, err := m.factory.BaseMenu().Find(ctx, menuInfo) 73 | if !errors.Is(err, gorm.ErrRecordNotFound) && menu.ID != 0 { 74 | return errors.New("存在重复名称菜单,请修改菜单名称") 75 | } 76 | return m.factory.BaseMenu().Save(ctx, menuInfo) 77 | } 78 | 79 | // AddMenuAuthority 为角色增加menu树 80 | func (m *menuService) AddMenuAuthority(ctx context.Context, menus []model.SysBaseMenu, authorityId uint) error { 81 | auth := &model.SysAuthority{AuthorityId: authorityId, SysBaseMenus: menus} 82 | return m.factory.Authority().SetMenuAuthority(ctx, auth) 83 | } 84 | 85 | func (m *menuService) getMenuTree(ctx context.Context, authorityId uint) (map[string][]model.SysMenu, error) { 86 | var allMenus []model.SysMenu 87 | treeMap := make(map[string][]model.SysMenu) 88 | SysAuthorityMenu := &model.SysAuthorityMenu{AuthorityId: strconv.Itoa(int(authorityId))} 89 | authorityMenus, err := m.factory.AuthorityMenu().FindList(ctx, SysAuthorityMenu) 90 | if err != nil { 91 | return nil, err 92 | } 93 | var MenuIds []string 94 | for i := range authorityMenus { 95 | MenuIds = append(MenuIds, authorityMenus[i].MenuId) 96 | } 97 | baseMenus, err := m.factory.BaseMenu().FindIn(ctx, MenuIds) 98 | if err != nil { 99 | return nil, err 100 | } 101 | for i := range baseMenus { 102 | allMenus = append(allMenus, model.SysMenu{ 103 | SysBaseMenu: *baseMenus[i], 104 | AuthorityId: authorityId, 105 | MenuId: strconv.Itoa(baseMenus[i].ID), 106 | }) 107 | } 108 | for _, v := range allMenus { 109 | treeMap[v.ParentId] = append(treeMap[v.ParentId], v) 110 | } 111 | return treeMap, nil 112 | } 113 | 114 | func (m *menuService) getChildrenList(menu *model.SysMenu, treeMap map[string][]model.SysMenu) error { 115 | // treeMap中包含所有路由 116 | menu.Children = treeMap[menu.MenuId] 117 | for i := 0; i < len(menu.Children); i++ { 118 | if err := m.getChildrenList(&menu.Children[i], treeMap); err != nil { 119 | return err 120 | } 121 | } 122 | return nil 123 | } 124 | 125 | func (m *menuService) getBaseChildrenList(menu *model.SysBaseMenu, treeMap map[string][]model.SysBaseMenu) (err error) { 126 | menu.Children = treeMap[strconv.Itoa(menu.ID)] 127 | for i := 0; i < len(menu.Children); i++ { 128 | err = m.getBaseChildrenList(&menu.Children[i], treeMap) 129 | } 130 | return err 131 | } 132 | 133 | func (m *menuService) getBaseMenuTreeMap(ctx context.Context) (treeMap map[string][]model.SysBaseMenu, err error) { 134 | var menuDB *model.SysBaseMenu 135 | treeMap = make(map[string][]model.SysBaseMenu) 136 | allMenus, err := m.factory.BaseMenu().FindList(ctx, menuDB) 137 | if err != nil { 138 | return nil, err 139 | } 140 | for _, v := range allMenus { 141 | treeMap[v.ParentId] = append(treeMap[v.ParentId], v) 142 | } 143 | return treeMap, err 144 | } 145 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/sys/operation.go: -------------------------------------------------------------------------------- 1 | package sys 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | 6 | "github.com/noovertime7/kubemanage/dao" 7 | "github.com/noovertime7/kubemanage/dao/model" 8 | "github.com/noovertime7/kubemanage/dto" 9 | ) 10 | 11 | type OperationServiceGetter interface { 12 | Operation() OperationService 13 | } 14 | 15 | type OperationService interface { 16 | CreateOperationRecord(ctx *gin.Context, record *model.SysOperationRecord) error 17 | DeleteRecord(ctx *gin.Context, id int) error 18 | DeleteRecords(ctx *gin.Context, ids []int) error 19 | GetPageList(ctx *gin.Context, in *dto.OperationListInput) (*dto.OperationListOutPut, error) 20 | } 21 | 22 | type operationService struct { 23 | factory dao.ShareDaoFactory 24 | } 25 | 26 | func NewOperationService(factory dao.ShareDaoFactory) *operationService { 27 | return &operationService{factory: factory} 28 | } 29 | 30 | func (o *operationService) CreateOperationRecord(ctx *gin.Context, record *model.SysOperationRecord) error { 31 | return o.factory.Opera().Save(ctx, record) 32 | } 33 | 34 | func (o *operationService) DeleteRecord(ctx *gin.Context, id int) error { 35 | record := &model.SysOperationRecord{ID: id} 36 | return o.factory.Opera().Delete(ctx, record) 37 | } 38 | 39 | func (o *operationService) DeleteRecords(ctx *gin.Context, ids []int) error { 40 | return o.factory.Opera().DeleteList(ctx, ids) 41 | } 42 | 43 | func (o *operationService) GetPageList(ctx *gin.Context, in *dto.OperationListInput) (*dto.OperationListOutPut, error) { 44 | list, total, err := o.factory.Opera().PageList(ctx, in) 45 | if err != nil { 46 | return nil, err 47 | } 48 | return &dto.OperationListOutPut{OperationList: list, Total: total, PageInfo: in.PageInfo}, nil 49 | } 50 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/sys/user.go: -------------------------------------------------------------------------------- 1 | package sys 2 | 3 | import ( 4 | "database/sql" 5 | "github.com/gin-gonic/gin" 6 | 7 | "github.com/noovertime7/kubemanage/dao" 8 | "github.com/noovertime7/kubemanage/dao/model" 9 | "github.com/noovertime7/kubemanage/dto" 10 | "github.com/noovertime7/kubemanage/pkg" 11 | "github.com/pkg/errors" 12 | ) 13 | 14 | type UserServiceGetter interface { 15 | User() UserService 16 | } 17 | 18 | type UserService interface { 19 | Login(ctx *gin.Context, userInfo *dto.AdminLoginInput) (string, error) 20 | LoginOut(ctx *gin.Context, uid int) error 21 | GetUserInfo(ctx *gin.Context, uid int, aid uint) (*dto.UserInfoOut, error) 22 | SetUserAuth(ctx *gin.Context, uid int, aid uint) error 23 | DeleteUser(ctx *gin.Context, uid int) error 24 | ChangePassword(ctx *gin.Context, uid int, info *dto.ChangeUserPwdInput) error 25 | ResetPassword(ctx *gin.Context, uid int) error 26 | } 27 | 28 | type userService struct { 29 | Menu MenuService 30 | Casbin CasbinService 31 | factory dao.ShareDaoFactory 32 | } 33 | 34 | func NewUserService(factory dao.ShareDaoFactory) *userService { 35 | return &userService{ 36 | factory: factory, 37 | Menu: NewMenuService(factory), 38 | Casbin: NewCasbinService(factory), 39 | } 40 | } 41 | 42 | var _ UserService = &userService{} 43 | 44 | func (u *userService) Login(ctx *gin.Context, userInfo *dto.AdminLoginInput) (string, error) { 45 | user, err := u.factory.User().Find(ctx, &model.SysUser{UserName: userInfo.UserName}) 46 | if err != nil { 47 | return "", err 48 | } 49 | 50 | if !pkg.CheckPassword(userInfo.Password, user.Password) { 51 | return "", errors.New("密码错误,请重新输入") 52 | } 53 | 54 | token, err := pkg.JWTToken.GenerateToken(pkg.BaseClaims{ 55 | UUID: user.UUID, 56 | ID: user.ID, 57 | Username: user.UserName, 58 | NickName: user.NickName, 59 | AuthorityId: user.AuthorityId, 60 | }) 61 | if err != nil { 62 | return "", err 63 | } 64 | return token, nil 65 | } 66 | 67 | func (u *userService) LoginOut(ctx *gin.Context, uid int) error { 68 | user := &model.SysUser{ID: uid, Status: sql.NullInt64{Int64: 0, Valid: true}} 69 | return u.factory.User().Updates(ctx, user) 70 | } 71 | 72 | func (u *userService) GetUserInfo(ctx *gin.Context, uid int, aid uint) (*dto.UserInfoOut, error) { 73 | user, err := u.factory.User().Find(ctx, &model.SysUser{ID: uid}) 74 | if err != nil { 75 | return nil, err 76 | } 77 | menus, err := u.Menu.GetMenuByAuthorityID(ctx, aid) 78 | if err != nil { 79 | return nil, err 80 | } 81 | var outRules []string 82 | rules := u.Casbin.GetPolicyPathByAuthorityId(aid) 83 | for _, rule := range rules { 84 | item := rule.Path + "," + rule.Method 85 | outRules = append(outRules, item) 86 | } 87 | return &dto.UserInfoOut{ 88 | User: *user, 89 | Menus: menus, 90 | RuleNames: outRules, 91 | }, nil 92 | } 93 | 94 | func (u *userService) SetUserAuth(ctx *gin.Context, uid int, aid uint) error { 95 | user := &model.SysUser{ID: uid, AuthorityId: aid} 96 | return u.factory.User().Updates(ctx, user) 97 | } 98 | 99 | func (u *userService) DeleteUser(ctx *gin.Context, uid int) error { 100 | user := &model.SysUser{ID: uid} 101 | return u.factory.User().Delete(ctx, user) 102 | } 103 | 104 | func (u *userService) ChangePassword(ctx *gin.Context, uid int, info *dto.ChangeUserPwdInput) error { 105 | userDB := &model.SysUser{ID: uid} 106 | user, err := u.factory.User().Find(ctx, userDB) 107 | if err != nil { 108 | return err 109 | } 110 | 111 | if !pkg.CheckPassword(info.OldPwd, user.Password) { 112 | return errors.New("原密码错误,请重新输入") 113 | } 114 | 115 | //生成新密码 116 | user.Password, err = pkg.GenSaltPassword(info.NewPwd) 117 | if err != nil { 118 | return err 119 | } 120 | return u.factory.User().Updates(ctx, user) 121 | } 122 | 123 | func (u *userService) ResetPassword(ctx *gin.Context, uid int) error { 124 | newPwd, err := pkg.GenSaltPassword("kubemanage") 125 | if err != nil { 126 | return err 127 | } 128 | user := &model.SysUser{ID: uid, Password: newPwd} 129 | return u.factory.User().Updates(ctx, user) 130 | } 131 | -------------------------------------------------------------------------------- /pkg/core/kubemanage/v1/system.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "github.com/noovertime7/kubemanage/dao" 5 | "github.com/noovertime7/kubemanage/pkg/core/kubemanage/v1/sys" 6 | ) 7 | 8 | type SystemGetter interface { 9 | System() SystemInterface 10 | } 11 | 12 | // SystemInterface 顶层抽象 包括系统相关接口 13 | type SystemInterface interface { 14 | sys.UserServiceGetter 15 | sys.MenuGetter 16 | sys.CasbinServiceGetter 17 | sys.AuthorityGetter 18 | sys.OperationServiceGetter 19 | sys.APIServiceGetter 20 | } 21 | 22 | var _ SystemInterface = &system{} 23 | 24 | func NewSystem(app *KubeManage) SystemInterface { 25 | return &system{app: app, factory: app.Factory} 26 | } 27 | 28 | type system struct { 29 | app *KubeManage 30 | factory dao.ShareDaoFactory 31 | } 32 | 33 | func (s *system) User() sys.UserService { 34 | return sys.NewUserService(s.factory) 35 | } 36 | 37 | func (s *system) Menu() sys.MenuService { 38 | return sys.NewMenuService(s.factory) 39 | } 40 | 41 | func (s *system) CasbinService() sys.CasbinService { 42 | return sys.NewCasbinService(s.factory) 43 | } 44 | 45 | func (s *system) Authority() sys.Authority { 46 | return sys.NewAuthority(s.factory) 47 | } 48 | 49 | func (s *system) Operation() sys.OperationService { 50 | return sys.NewOperationService(s.factory) 51 | } 52 | 53 | func (s *system) Api() sys.APIService { 54 | return sys.NewApiService(s.factory) 55 | } 56 | -------------------------------------------------------------------------------- /pkg/globalError/global_error.go: -------------------------------------------------------------------------------- 1 | package globalError 2 | 3 | type GlobalError struct { 4 | Code int `json:"code"` // 业务码 5 | Message string `json:"message"` // 业务码 6 | RealErrorMessage string `json:"err_msg"` 7 | } 8 | 9 | func (e *GlobalError) Error() string { 10 | return e.Message 11 | } 12 | 13 | // 2、定义errorCode 14 | const ( 15 | AuthErr = 405 16 | 17 | ServerError = 10101 // Internal Server Error 18 | ParamBindError = 10102 // 参数信息有误 19 | AuthorizationError = 10103 // 签名信息有误 20 | CallHTTPError = 10104 // 调用第三方HTTP接口失败 21 | ResubmitMsg = 10105 // 请勿重复提交 22 | 23 | GetError = 20101 24 | CreateError = 20102 25 | DeleteError = 20103 26 | UpdateError = 20104 27 | 28 | LoginErr = 30101 29 | LogoutErr = 30102 30 | ) 31 | 32 | // 3、定义errorCode对应的文本信息 33 | var codeTag = map[int]string{ 34 | AuthErr: "权限不足,请联系管理员", 35 | 36 | ServerError: "Internal Server Error", 37 | ParamBindError: "参数信息有误", 38 | AuthorizationError: "签名信息有误", 39 | CallHTTPError: "调用第三方 HTTP 接口失败", 40 | ResubmitMsg: "请勿重复提交", 41 | 42 | GetError: "查询失败", 43 | CreateError: "添加失败", 44 | UpdateError: "修改失败", 45 | DeleteError: "删除失败", 46 | 47 | LoginErr: "登录失败", 48 | LogoutErr: "注销失败", 49 | } 50 | 51 | func GetErrorMsg(code int) string { 52 | return codeTag[code] 53 | } 54 | 55 | // NewGlobalError 4、新建自定义error实例化 56 | func NewGlobalError(code int, err error) error { 57 | // 初次调用得用Wrap方法,进行实例化 58 | return &GlobalError{ 59 | Code: code, 60 | Message: codeTag[code], 61 | RealErrorMessage: err.Error(), 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /pkg/jwt.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "errors" 5 | jwt "github.com/dgrijalva/jwt-go" 6 | uuid "github.com/satori/go.uuid" 7 | "time" 8 | ) 9 | 10 | var JWTToken jwtToken 11 | 12 | // 定义jwtToken结构体 13 | type jwtToken struct { 14 | secret string 15 | } 16 | 17 | func RegisterJwt(secret string) { 18 | JWTToken.secret = secret 19 | } 20 | 21 | type BaseClaims struct { 22 | UUID uuid.UUID 23 | ID int 24 | Username string 25 | NickName string 26 | AuthorityId uint 27 | } 28 | 29 | // CustomClaims 自定义token中携带的信息 30 | type CustomClaims struct { 31 | BaseClaims 32 | jwt.StandardClaims 33 | } 34 | 35 | // GenerateToken 生成token函数方法 36 | func (j *jwtToken) GenerateToken(baseClaims BaseClaims) (string, error) { 37 | nowTime := time.Now() 38 | expireTime := nowTime.Add(24 * time.Hour) 39 | claims := CustomClaims{ 40 | baseClaims, 41 | jwt.StandardClaims{ 42 | NotBefore: time.Now().Unix() - 1000, // 签名生效时间 43 | ExpiresAt: expireTime.Unix(), 44 | Issuer: "kubemanage", // 签名的发行者 45 | }, 46 | } 47 | tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) 48 | token, err := tokenClaims.SignedString([]byte(j.secret)) 49 | return token, err 50 | } 51 | 52 | // ParseToken 解析token函数 53 | func (j *jwtToken) ParseToken(tokenString string) (claims *CustomClaims, err error) { 54 | // 使用jwt.ParseWithClaims方法解析token,这个token是前端传给我们的,获得一个*Token类型的对象 55 | token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) { 56 | return []byte(j.secret), nil 57 | }) 58 | if err != nil { 59 | // 处理token解析后的各种错误 60 | if ve, ok := err.(*jwt.ValidationError); ok { 61 | if ve.Errors == jwt.ValidationErrorExpired { 62 | return nil, errors.New("登录已过期,请重新登录") 63 | } else { 64 | return nil, errors.New("token不可用," + err.Error()) 65 | } 66 | } 67 | } 68 | // 转换为*CustomClaims类型并返回 69 | if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid { 70 | // 如果解析成功并且token是可用的 71 | return claims, nil 72 | } 73 | return nil, errors.New("解析token失败") 74 | } 75 | -------------------------------------------------------------------------------- /pkg/logger/interface.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "go.uber.org/zap" 5 | 6 | "github.com/noovertime7/kubemanage/pkg/globalError" 7 | ) 8 | 9 | type Logger interface { 10 | Info(msg interface{}) 11 | Infof(template string, args ...interface{}) 12 | Warn(msg interface{}) 13 | Warnf(template string, args ...interface{}) 14 | Error(msg interface{}) 15 | ErrorWithCode(code int, err error) 16 | ErrorWithErr(msg string, err error) 17 | } 18 | 19 | func New() Logger { 20 | return logger{} 21 | } 22 | 23 | type logger struct{} 24 | 25 | func (logger) Info(msg interface{}) { 26 | LG.Sugar().Info(msg) 27 | } 28 | 29 | func (logger) Infof(template string, args ...interface{}) { 30 | LG.Sugar().Infof(template, args) 31 | } 32 | func (logger) Warn(msg interface{}) { 33 | LG.Sugar().Warn(msg) 34 | } 35 | 36 | func (logger) Warnf(template string, args ...interface{}) { 37 | LG.Sugar().Warnf(template, args) 38 | } 39 | 40 | func (logger) ErrorWithCode(code int, err error) { 41 | msg := globalError.GetErrorMsg(code) 42 | LG.Error(msg, zap.Error(err)) 43 | } 44 | 45 | func (logger) Error(msg interface{}) { 46 | LG.Sugar().Error(msg) 47 | } 48 | 49 | func (logger) ErrorWithErr(msg string, err error) { 50 | LG.Error(msg, zap.Error(err)) 51 | } 52 | -------------------------------------------------------------------------------- /pkg/logger/zap.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "os" 5 | "time" 6 | 7 | "github.com/natefinch/lumberjack" 8 | "go.uber.org/zap" 9 | "go.uber.org/zap/zapcore" 10 | 11 | "github.com/noovertime7/kubemanage/cmd/app/config" 12 | ) 13 | 14 | var LG *zap.Logger 15 | 16 | // InitLogger 初始化lg 17 | func InitLogger() (err error) { 18 | cfg := config.SysConfig.Log 19 | writeSyncer := getLogWriter(cfg.Filename, cfg.MaxSize, cfg.MaxBackups, cfg.MaxAge) 20 | encoder := getEncoder() 21 | var l = new(zapcore.Level) 22 | err = l.UnmarshalText([]byte(cfg.Level)) 23 | if err != nil { 24 | return 25 | } 26 | var core zapcore.Core 27 | if cfg.Level == "debug" { 28 | // 进入开发模式,日志输出到终端 29 | config := zap.NewDevelopmentEncoderConfig() 30 | // 设置日志颜色 31 | config.EncodeLevel = zapcore.LowercaseColorLevelEncoder 32 | // 设置自定义时间格式 33 | config.EncodeTime = getCustomTimeEncoder 34 | consoleEncoder := zapcore.NewConsoleEncoder(config) 35 | core = zapcore.NewTee( 36 | zapcore.NewCore(encoder, writeSyncer, l), 37 | zapcore.NewCore(consoleEncoder, zapcore.Lock(os.Stdout), zapcore.DebugLevel), 38 | ) 39 | } else { 40 | core = zapcore.NewCore(encoder, writeSyncer, l) 41 | } 42 | 43 | LG = zap.New(core, zap.AddCaller()) 44 | 45 | zap.ReplaceGlobals(LG) 46 | zap.L().Info("init logger success") 47 | return 48 | } 49 | 50 | func getEncoder() zapcore.Encoder { 51 | encoderConfig := zap.NewProductionEncoderConfig() 52 | encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder 53 | encoderConfig.TimeKey = "time" 54 | encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder 55 | encoderConfig.EncodeDuration = zapcore.SecondsDurationEncoder 56 | encoderConfig.EncodeCaller = zapcore.ShortCallerEncoder 57 | return zapcore.NewJSONEncoder(encoderConfig) 58 | } 59 | 60 | func getLogWriter(filename string, maxSize, maxBackup, maxAge int) zapcore.WriteSyncer { 61 | lumberJackLogger := &lumberjack.Logger{ 62 | Filename: filename, 63 | MaxSize: maxSize, 64 | MaxBackups: maxBackup, 65 | MaxAge: maxAge, 66 | } 67 | return zapcore.AddSync(lumberJackLogger) 68 | } 69 | 70 | // CustomTimeEncoder 自定义日志输出时间格式 71 | func getCustomTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { 72 | enc.AppendString("[kubemanage] " + t.Format("2006/01/02 - 15:04:05.000")) 73 | } 74 | -------------------------------------------------------------------------------- /pkg/params.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/go-playground/universal-translator" 6 | "github.com/pkg/errors" 7 | "gopkg.in/go-playground/validator.v9" 8 | "strings" 9 | ) 10 | 11 | func DefaultGetValidParams(c *gin.Context, params interface{}) error { 12 | if err := c.ShouldBind(params); err != nil { 13 | return err 14 | } 15 | //获取验证器 16 | valid, err := GetValidator(c) 17 | if err != nil { 18 | return err 19 | } 20 | //获取翻译器 21 | trans, err := GetTranslation(c) 22 | if err != nil { 23 | return err 24 | } 25 | err = valid.Struct(params) 26 | if err != nil { 27 | errs := err.(validator.ValidationErrors) 28 | sliceErrs := []string{} 29 | for _, e := range errs { 30 | sliceErrs = append(sliceErrs, e.Translate(trans)) 31 | } 32 | return errors.New(strings.Join(sliceErrs, ",")) 33 | } 34 | return nil 35 | } 36 | 37 | func GetValidator(c *gin.Context) (*validator.Validate, error) { 38 | val, ok := c.Get(ValidatorKey) 39 | if !ok { 40 | return nil, errors.New("未设置验证器") 41 | } 42 | validatorObj, ok := val.(*validator.Validate) 43 | if !ok { 44 | return nil, errors.New("获取验证器失败") 45 | } 46 | 47 | return validatorObj, nil 48 | } 49 | 50 | func GetTranslation(c *gin.Context) (ut.Translator, error) { 51 | trans, ok := c.Get(TranslatorKey) 52 | if !ok { 53 | return nil, errors.New("未设置翻译器") 54 | } 55 | translator, ok := trans.(ut.Translator) 56 | if !ok { 57 | return nil, errors.New("获取翻译器失败") 58 | } 59 | return translator, nil 60 | } 61 | -------------------------------------------------------------------------------- /pkg/password.go: -------------------------------------------------------------------------------- 1 | package pkg 2 | 3 | import ( 4 | "golang.org/x/crypto/bcrypt" 5 | ) 6 | 7 | func GenSaltPassword(password string) (string, error) { 8 | bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14) 9 | return string(bytes), err 10 | } 11 | 12 | func CheckPassword(password, hashPassword string) bool { 13 | err := bcrypt.CompareHashAndPassword([]byte(hashPassword), []byte(password)) 14 | return err == nil 15 | } 16 | -------------------------------------------------------------------------------- /pkg/source/initializer.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "context" 5 | "github.com/pkg/errors" 6 | "gorm.io/gorm" 7 | ) 8 | 9 | type SubInitializer interface { 10 | InitializerName() string // 不一定代表单独一个表,所以改成了更宽泛的语义 11 | MigrateTable(ctx context.Context, db *gorm.DB) error 12 | InitializeData(ctx context.Context, db *gorm.DB) error 13 | TableCreated(ctx context.Context, db *gorm.DB) bool 14 | } 15 | 16 | type InitHandler interface { 17 | InitTables(ctx context.Context, inits initSlice) error // 建表 handler 18 | } 19 | 20 | type initSlice []SubInitializer 21 | 22 | var initializers initSlice 23 | 24 | // RegisterInit 注册要执行的初始化过程,会在 InitDB() 时调用 25 | func RegisterInit(i SubInitializer) { 26 | if initializers == nil { 27 | initializers = initSlice{} 28 | } 29 | initializers = append(initializers, i) 30 | } 31 | 32 | type initDBService struct { 33 | db *gorm.DB 34 | } 35 | 36 | func NewInitDBService(db *gorm.DB) *initDBService { 37 | return &initDBService{db: db} 38 | } 39 | 40 | func (i *initDBService) InitDB() error { 41 | if len(initializers) == 0 { 42 | return errors.New("无可用初始化过程,请检查初始化是否已执行完成") 43 | } 44 | // TODO support more database 45 | handler := newMysqlInitHandler(i.db) 46 | return handler.InitTables(context.TODO(), initializers) 47 | } 48 | -------------------------------------------------------------------------------- /pkg/source/mysqlinitHandler.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "context" 5 | "gorm.io/gorm" 6 | ) 7 | 8 | type mysqlInitHandler struct { 9 | db *gorm.DB 10 | } 11 | 12 | var _ InitHandler = &mysqlInitHandler{} 13 | 14 | func newMysqlInitHandler(db *gorm.DB) InitHandler { 15 | return &mysqlInitHandler{db: db} 16 | } 17 | 18 | func (m *mysqlInitHandler) InitTables(ctx context.Context, inits initSlice) error { 19 | if err := m.createTables(ctx, inits); err != nil { 20 | return err 21 | } 22 | if err := m.createDatas(ctx, inits); err != nil { 23 | return err 24 | } 25 | return nil 26 | } 27 | 28 | func (m *mysqlInitHandler) createTables(ctx context.Context, inits initSlice) error { 29 | for _, init := range inits { 30 | if init.TableCreated(ctx, m.db) { 31 | continue 32 | } 33 | if err := init.MigrateTable(ctx, m.db); err != nil { 34 | return err 35 | } 36 | } 37 | return nil 38 | } 39 | 40 | func (m *mysqlInitHandler) createDatas(ctx context.Context, inits initSlice) error { 41 | for _, init := range inits { 42 | if err := init.InitializeData(ctx, m.db); err != nil { 43 | return err 44 | } 45 | } 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /pkg/source/systemInitailzer.go: -------------------------------------------------------------------------------- 1 | package source 2 | 3 | import ( 4 | "context" 5 | "github.com/noovertime7/kubemanage/dao/model" 6 | "gorm.io/gorm" 7 | "strings" 8 | ) 9 | 10 | func init() { 11 | RegisterInit(&SystemInitTable{}) 12 | } 13 | 14 | type SystemInitTable struct { 15 | } 16 | 17 | func (s *SystemInitTable) InitializerName() string { 18 | return strings.ToUpper("SystemInitTable") 19 | } 20 | 21 | func (s *SystemInitTable) MigrateTable(ctx context.Context, db *gorm.DB) error { 22 | for _, initializer := range model.InitializerList { 23 | if err := initializer.MigrateTable(ctx, db); err != nil { 24 | return err 25 | } 26 | } 27 | return nil 28 | } 29 | 30 | func (s *SystemInitTable) InitializeData(ctx context.Context, db *gorm.DB) error { 31 | for _, initializer := range model.InitializerList { 32 | if err := initializer.InitData(ctx, db); err != nil { 33 | return err 34 | } 35 | } 36 | return nil 37 | } 38 | 39 | func (s *SystemInitTable) TableCreated(ctx context.Context, db *gorm.DB) bool { 40 | yes := true 41 | for _, initializer := range model.InitializerList { 42 | yes = yes && db.Migrator().HasTable(initializer.TableName()) 43 | } 44 | return yes 45 | } 46 | -------------------------------------------------------------------------------- /pkg/types/termisnal.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Pixiu Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package types 18 | 19 | import ( 20 | "encoding/json" 21 | "fmt" 22 | "net/http" 23 | "time" 24 | 25 | "github.com/gorilla/websocket" 26 | "k8s.io/client-go/tools/remotecommand" 27 | ) 28 | 29 | // TerminalMessage 定义了终端和容器 shell 交互内容的格式 Operation 是操作类型 30 | // Data 是具体数据内容 Rows和Cols 可以理解为终端的行数和列数,也就是宽、高 31 | type TerminalMessage struct { 32 | Operation string `json:"operation"` 33 | Data string `json:"data"` 34 | Rows uint16 `json:"rows"` 35 | Cols uint16 `json:"cols"` 36 | } 37 | 38 | // TerminalSession 定义 TerminalSession 结构体,实现 PtyHandler 接口 // wsConn 是 websocket 连接 // sizeChan 用来定义终端输入和输出的宽和高 // doneChan 用于标记退出终端 39 | type TerminalSession struct { 40 | wsConn *websocket.Conn 41 | sizeChan chan remotecommand.TerminalSize 42 | doneChan chan struct{} 43 | } 44 | 45 | // NewTerminalSession 该方法用于升级 http 协议至 websocket,并new一个 TerminalSession 类型的对象返回 46 | func NewTerminalSession(w http.ResponseWriter, r *http.Request) (*TerminalSession, error) { 47 | // 初始化 Upgrader 类型的对象,用于http协议升级为 websocket 协议 48 | upgrader := &websocket.Upgrader{ 49 | HandshakeTimeout: time.Second * 2, 50 | // 检测请求来源 51 | CheckOrigin: func(r *http.Request) bool { 52 | return true 53 | }, 54 | Subprotocols: []string{r.Header.Get("Sec-WebSocket-Protocol")}, 55 | } 56 | conn, err := upgrader.Upgrade(w, r, nil) 57 | if err != nil { 58 | return nil, err 59 | } 60 | session := &TerminalSession{ 61 | wsConn: conn, 62 | sizeChan: make(chan remotecommand.TerminalSize), 63 | doneChan: make(chan struct{}), 64 | } 65 | 66 | return session, nil 67 | } 68 | 69 | // 用于读取web端的输入,接收web端输入的指令内容 70 | func (t *TerminalSession) Read(p []byte) (int, error) { 71 | _, message, err := t.wsConn.ReadMessage() 72 | if err != nil { 73 | return copy(p, "\u0004"), err 74 | } 75 | // 反序列化 76 | var msg TerminalMessage 77 | if err = json.Unmarshal(message, &msg); err != nil { 78 | return copy(p, "\u0004"), err 79 | } 80 | // 逻辑判断 81 | switch msg.Operation { 82 | // 如果是标准输入 83 | case "stdin": 84 | return copy(p, msg.Data), nil 85 | // 窗口调整大小 86 | case "resize": 87 | t.sizeChan <- remotecommand.TerminalSize{Width: msg.Cols, Height: msg.Rows} 88 | return 0, nil 89 | // ping 无内容交互 90 | case "ping": 91 | return 0, nil 92 | default: 93 | return copy(p, "\u0004"), fmt.Errorf("unknown message type") 94 | } 95 | } 96 | 97 | // 写数据的方法,拿到 api-server 的返回内容,向web端输出 98 | func (t *TerminalSession) Write(p []byte) (int, error) { 99 | msg, err := json.Marshal(TerminalMessage{ 100 | Operation: "stdout", 101 | Data: string(p), 102 | }) 103 | if err != nil { 104 | return 0, err 105 | } 106 | if err = t.wsConn.WriteMessage(websocket.TextMessage, msg); err != nil { 107 | return 0, err 108 | } 109 | return len(p), nil 110 | } 111 | 112 | // Done 标记关闭doneChan,关闭后触发退出终端 113 | func (t *TerminalSession) Done() { 114 | close(t.doneChan) 115 | } 116 | 117 | // Close 用于关闭websocket连接 118 | func (t *TerminalSession) Close() error { 119 | return t.wsConn.Close() 120 | } 121 | 122 | // Next 获取web端是否resize,以及是否退出终端 123 | func (t *TerminalSession) Next() *remotecommand.TerminalSize { 124 | select { 125 | case size := <-t.sizeChan: 126 | return &size 127 | case <-t.doneChan: 128 | return nil 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /pkg/utils/auth.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "github.com/noovertime7/kubemanage/pkg" 6 | "github.com/pkg/errors" 7 | ) 8 | 9 | func GetClaims(c *gin.Context) (*pkg.CustomClaims, error) { 10 | token := c.Request.Header.Get("token") 11 | if token == "" { 12 | return nil, errors.New("请求未携带token,无权限访问") 13 | } 14 | // 解析token内容 15 | claims, err := pkg.JWTToken.ParseToken(token) 16 | if err != nil { 17 | return nil, err 18 | } 19 | return claims, err 20 | } 21 | 22 | // GetUserAuthorityId 从Gin的Context中获取从jwt解析出来的用户角色id 23 | func GetUserAuthorityId(c *gin.Context) (uint, error) { 24 | if claims, exists := c.Get("claims"); !exists { 25 | if cl, err := GetClaims(c); err != nil { 26 | return 0, err 27 | } else { 28 | return cl.AuthorityId, nil 29 | } 30 | } else { 31 | waitUse := claims.(*pkg.CustomClaims) 32 | return waitUse.AuthorityId, nil 33 | } 34 | } 35 | 36 | // GetUserInfo 从Gin的Context中获取从jwt解析出来的用户角色id 37 | func GetUserInfo(c *gin.Context) *pkg.CustomClaims { 38 | if claims, exists := c.Get("claims"); !exists { 39 | if cl, err := GetClaims(c); err != nil { 40 | return nil 41 | } else { 42 | return cl 43 | } 44 | } else { 45 | waitUse := claims.(*pkg.CustomClaims) 46 | return waitUse 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pkg/utils/print.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/fatih/color" 7 | ) 8 | 9 | var ( 10 | Blue = color.New(color.FgHiBlue, color.Bold).SprintFunc() 11 | ) 12 | 13 | // PrintLogo 服务启动打印信息 14 | func PrintLogo() { 15 | fmt.Println(Blue(` 16 | /$$ /$$ /$$ /$$ /$$ 17 | | $$ /$$/ | $$ | $$$ /$$$ 18 | | $$ /$$/ /$$ /$$| $$$$$$$ /$$$$$$ | $$$$ /$$$$ /$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$ /$$$$$$ 19 | | $$$$$/ | $$ | $$| $$__ $$ /$$__ $$| $$ $$/$$ $$ |____ $$| $$__ $$ |____ $$ /$$__ $$ /$$__ $$ 20 | | $$ $$ | $$ | $$| $$ \ $$| $$$$$$$$| $$ $$$| $$ /$$$$$$$| $$ \ $$ /$$$$$$$| $$ \ $$| $$$$$$$$ 21 | | $$\ $$ | $$ | $$| $$ | $$| $$_____/| $$\ $ | $$ /$$__ $$| $$ | $$ /$$__ $$| $$ | $$| $$_____/ 22 | | $$ \ $$| $$$$$$/| $$$$$$$/| $$$$$$$| $$ \/ | $$| $$$$$$$| $$ | $$| $$$$$$$| $$$$$$$| $$$$$$$ 23 | |__/ \__/ \______/ |_______/ \_______/|__/ |__/ \_______/|__/ |__/ \_______/ \____ $$ \_______/ 24 | /$$ \ $$ 25 | | $$$$$$/ 26 | \______/ 27 | `)) 28 | } 29 | 30 | func Must(err error) { 31 | panic(err) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/utils/public.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "strconv" 4 | 5 | // ParseInt64 将字符串转换为 int64 6 | func ParseInt64(s string) (int64, error) { 7 | if len(s) == 0 { 8 | return 0, nil 9 | } 10 | return strconv.ParseInt(s, 10, 64) 11 | } 12 | 13 | // ParseUint 将字符串转换为 uint 14 | func ParseUint(s string) (uint, error) { 15 | v, err := ParseInt(s) 16 | if err != nil { 17 | return 0, err 18 | } 19 | return uint(v), err 20 | } 21 | 22 | // ParseInt 将字符串转换为 int64 23 | func ParseInt(s string) (int, error) { 24 | if len(s) == 0 { 25 | return 0, nil 26 | } 27 | return strconv.Atoi(s) 28 | } 29 | -------------------------------------------------------------------------------- /pkg/utils/signal.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "os/signal" 7 | ) 8 | 9 | var shutdownSignals = []os.Signal{os.Interrupt} 10 | var onlyOneSignalHandler = make(chan struct{}) 11 | var shutdownHandler chan os.Signal 12 | 13 | // SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned 14 | // which is closed on one of these signals. If a second signal is caught, the program 15 | // is terminated with exit code 1. 16 | // Only one of SetupSignalContext and SetupSignalHandler should be called, and only can 17 | // be called once. 18 | func SetupSignalHandler() <-chan struct{} { 19 | return SetupSignalContext().Done() 20 | } 21 | 22 | // SetupSignalContext is same as SetupSignalHandler, but a context.Context is returned. 23 | // Only one of SetupSignalContext and SetupSignalHandler should be called, and only can 24 | // be called once. 25 | func SetupSignalContext() context.Context { 26 | close(onlyOneSignalHandler) // panics when called twice 27 | 28 | shutdownHandler = make(chan os.Signal, 2) 29 | 30 | ctx, cancel := context.WithCancel(context.Background()) 31 | signal.Notify(shutdownHandler, shutdownSignals...) 32 | go func() { 33 | <-shutdownHandler 34 | cancel() 35 | <-shutdownHandler 36 | os.Exit(1) // second signal. Exit directly. 37 | }() 38 | 39 | return ctx 40 | } 41 | 42 | // RequestShutdown emulates a received event that is considered as shutdown signal (SIGTERM/SIGINT) 43 | // This returns whether a handler was notified 44 | func RequestShutdown() bool { 45 | if shutdownHandler != nil { 46 | select { 47 | case shutdownHandler <- shutdownSignals[0]: 48 | return true 49 | default: 50 | } 51 | } 52 | return false 53 | } 54 | -------------------------------------------------------------------------------- /router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | 6 | "github.com/noovertime7/kubemanage/cmd/app/options" 7 | "github.com/noovertime7/kubemanage/controller/api" 8 | "github.com/noovertime7/kubemanage/controller/authority" 9 | "github.com/noovertime7/kubemanage/controller/kubeController" 10 | "github.com/noovertime7/kubemanage/controller/menu" 11 | "github.com/noovertime7/kubemanage/controller/operation" 12 | "github.com/noovertime7/kubemanage/controller/other" 13 | "github.com/noovertime7/kubemanage/controller/user" 14 | "github.com/noovertime7/kubemanage/middleware" 15 | ) 16 | 17 | func InstallRouters(opt *options.Options) { 18 | apiGroup := opt.GinEngine.Group("/api") 19 | middleware.InstallMiddlewares(apiGroup) 20 | //安装不需要操作记录路由 21 | { 22 | api.NewApiRouter(apiGroup) 23 | operation.NewOperationRouter(apiGroup) 24 | user.NewUserRouter(apiGroup) 25 | } 26 | installOperationRouters(apiGroup) 27 | } 28 | 29 | func installOperationRouters(apiGroup *gin.RouterGroup) { 30 | // 需要操作记录 31 | apiGroup.Use(middleware.OperationRecord()) 32 | { 33 | other.NewSwaggarRoute(apiGroup) 34 | kubeController.NewKubeRouter(apiGroup) 35 | menu.NewMenuRouter(apiGroup) 36 | authority.NewCasbinRouter(apiGroup) 37 | } 38 | } 39 | --------------------------------------------------------------------------------