├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── favicon.ico
├── go.mod
├── go.sum
├── main.go
├── redis-manager-ui
├── .env.development
├── .env.production
├── .gitignore
├── .vscode
│ └── extensions.json
├── auto-imports.d.ts
├── components.d.ts
├── declaration.d.ts
├── index.html
├── package.json
├── pnpm-lock.yaml
├── public
│ ├── favicon.svg
│ └── vite.svg
├── src
│ ├── App.vue
│ ├── api
│ │ ├── board.ts
│ │ ├── cli.ts
│ │ ├── cloud.ts
│ │ ├── cluster.ts
│ │ ├── codis.ts
│ │ ├── history.ts
│ │ ├── rule.ts
│ │ ├── setting.ts
│ │ └── user.ts
│ ├── assets
│ │ ├── img
│ │ │ ├── favicon.ico
│ │ │ ├── github-mark.png
│ │ │ ├── logo.png
│ │ │ └── sun.jpeg
│ │ └── vue.svg
│ ├── components
│ │ ├── breadcrumb
│ │ │ └── Breadcrumb.vue
│ │ └── cli
│ │ │ └── Cli.vue
│ ├── main.ts
│ ├── pages
│ │ ├── command
│ │ │ └── Index.vue
│ │ ├── dashboard
│ │ │ └── Index.vue
│ │ ├── history
│ │ │ └── Index.vue
│ │ ├── home
│ │ │ └── Index.vue
│ │ ├── login
│ │ │ └── Index.vue
│ │ ├── redis
│ │ │ ├── aliredis
│ │ │ │ └── Index.vue
│ │ │ ├── cluster
│ │ │ │ └── Index.vue
│ │ │ ├── codis
│ │ │ │ └── Index.vue
│ │ │ └── txredis
│ │ │ │ └── Index.vue
│ │ ├── setting
│ │ │ ├── Index.vue
│ │ │ └── Rule.vue
│ │ └── user
│ │ │ └── Index.vue
│ ├── router
│ │ └── index.ts
│ ├── style.css
│ ├── utils
│ │ └── request
│ │ │ ├── http.ts
│ │ │ └── index.ts
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts
├── src
├── cfg
│ └── config.go
├── hsc
│ ├── code.go
│ └── msg.go
├── middleware
│ ├── alicloud
│ │ ├── alist.go
│ │ └── connect.go
│ ├── casbin
│ │ ├── casbinstruct.go
│ │ ├── initial.go
│ │ └── ruleop.go
│ ├── cluster
│ │ ├── cluster.go
│ │ └── nodes.go
│ ├── codisapi
│ │ ├── opapi.go
│ │ └── opstruct.go
│ ├── cosop
│ │ └── cosop.go
│ ├── httpapi
│ │ ├── apistruct.go
│ │ └── httpapi.go
│ ├── jwt
│ │ └── jwt.go
│ ├── logger
│ │ └── logger.go
│ ├── model
│ │ ├── aliresult.go
│ │ ├── cfg.go
│ │ ├── clusterresult.go
│ │ ├── codisv1.go
│ │ ├── path.go
│ │ ├── txresult.go
│ │ └── user.go
│ ├── mysql
│ │ ├── history.go
│ │ ├── initial.go
│ │ ├── mysqlstrcut.go
│ │ ├── opcfg.go
│ │ ├── opcloud.go
│ │ ├── opcluster.go
│ │ ├── opcodis.go
│ │ └── opuser.go
│ ├── opredis
│ │ ├── all.go
│ │ ├── analysisrdb.go
│ │ ├── base.go
│ │ ├── basecluster.go
│ │ ├── big.go
│ │ ├── click.go
│ │ ├── connect.go
│ │ ├── del.go
│ │ ├── hot.go
│ │ ├── lock.go
│ │ ├── opcodis.go
│ │ ├── opdilatation.go
│ │ ├── opshrinkage.go
│ │ ├── query.go
│ │ ├── redisstruct.go
│ │ ├── slow.go
│ │ └── telnet.go
│ ├── rcron
│ │ ├── cloudrefresh.go
│ │ └── clusterrefresh.go
│ ├── tools
│ │ ├── calculation.go
│ │ ├── capacity.go
│ │ ├── check.go
│ │ ├── codisauth.go
│ │ └── trans.go
│ ├── txcloud
│ │ ├── clist.go
│ │ ├── connect.go
│ │ └── opkey.go
│ ├── useride
│ │ ├── daoyou.go
│ │ ├── password.go
│ │ └── tocken.go
│ └── util
│ │ ├── cloud.go
│ │ ├── converge.go
│ │ ├── jwt.go
│ │ ├── pagination.go
│ │ ├── parameter.go
│ │ └── utilstruct.go
└── rhttp
│ ├── router.go
│ └── v1
│ ├── base.go
│ ├── board.go
│ ├── cfg.go
│ ├── cli.go
│ ├── cloud.go
│ ├── cluster.go
│ ├── codis.go
│ ├── home.go
│ ├── ophistory.go
│ ├── rule.go
│ ├── user.go
│ └── v1struct.go
├── start-docker.sh
├── website
├── assets
│ ├── Index-035ac3eb.css
│ ├── Index-06dbdf44.js
│ ├── Index-398d56ab.js
│ ├── Index-3bca26e6.js
│ ├── Index-425e1db0.js
│ ├── Index-64bee138.js
│ ├── Index-7aa9bc97.css
│ ├── Index-8878f285.js
│ ├── Index-88df60b4.js
│ ├── Index-967c1091.js
│ ├── Index-9ea35986.css
│ ├── Index-a2a246be.js
│ ├── Index-b3c9f82a.css
│ ├── Index-b6741412.css
│ ├── Index-bba6e235.css
│ ├── Index-bfef1db5.js
│ ├── Index-c6cadafd.js
│ ├── Index-c74390c1.css
│ ├── Index-d450f424.css
│ ├── Index-d4718653.css
│ ├── Index-dc641d83.css
│ ├── Index-f5a41eb2.css
│ ├── Rule-273f6791.css
│ ├── Rule-d9da8097.js
│ ├── cloud-3a6d1fb9.js
│ ├── cluster-7426750c.js
│ ├── codis-7a3bd4ff.js
│ ├── github-mark-367d5cb2.png
│ ├── index-04cccabd.js
│ ├── index-056ef9e9.js
│ ├── index-472d715e.css
│ ├── moment-fbc5633a.js
│ ├── setting-b5212552.js
│ ├── sun-80b81115.jpeg
│ └── user-d7605884.js
├── favicon.svg
├── index.html
└── vite.svg
└── yaml
├── dev.yaml
└── online.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Binaries for programs and plugins
2 | *.exe
3 | *.exe~
4 | *.dll
5 | *.so
6 | *.dylib
7 |
8 | # Test binary, built with `go test -c`
9 | *.test
10 |
11 | # Output of the go coverage tool, specifically when used with LiteIDE
12 | *.out
13 |
14 | .DS_Store
15 | # Dependency directories (remove the comment below to include it)
16 | # vendor/
17 |
18 | # server file
19 | logs
20 | yaml/config.yaml
21 | redis-manager
22 |
23 | # See http://help.github.com/ignore-files/ for more about ignoring files.
24 |
25 | # redis-manager-ui
26 | # Logs
27 | redis-manager-ui/logs
28 | redis-manager-ui/*.log
29 | redis-manager-ui/npm-debug.log*
30 | redis-manager-ui/yarn-debug.log*
31 | redis-manager-ui/yarn-error.log*
32 | redis-manager-ui/pnpm-debug.log*
33 | redis-manager-ui/lerna-debug.log*
34 |
35 | redis-manager-ui/node_modules
36 | redis-manager-ui/dist
37 | redis-manager-ui/dist-ssr
38 | redis-manager-ui/*.local
39 |
40 | # Editor directories and files
41 | redis-manager-ui/.vscode/*
42 | redis-manager-ui/!.vscode/extensions.json
43 | redis-manager-ui/.idea
44 | redis-manager-ui/.DS_Store
45 | redis-manager-ui/*.suo
46 | redis-manager-ui/*.ntvs*
47 | redis-manager-ui/*.njsproj
48 | redis-manager-ui/*.sln
49 | redis-manager-ui/*.sw?
50 |
51 | # Compiled output
52 | redis-manager-ui/dist/
53 | redis-manager-ui/tmp
54 | redis-manager-ui/out-tsc
55 | redis-manager-ui/bazel-out
56 |
57 | # Node
58 | redis-manager-ui/node_modules/
59 | redis-manager-ui/npm-debug.log
60 | redis-manager-ui/yarn-error.log
61 |
62 | # IDEs and editors
63 | redis-manager-ui/.idea/
64 | redis-manager-ui/.project
65 | redis-manager-ui/.classpath
66 | redis-manager-ui/.c9/
67 | redis-manager-ui/*.launch
68 | redis-manager-ui/.settings/
69 | redis-manager-ui/*.sublime-workspace
70 |
71 | # Visual Studio Code
72 | redis-manager-ui/.vscode/*
73 | redis-manager-ui/!.vscode/settings.json
74 | redis-manager-ui/!.vscode/tasks.json
75 | redis-manager-ui/!.vscode/launch.json
76 | redis-manager-ui/!.vscode/extensions.json
77 | redis-manager-ui/.history/*
78 |
79 | # Miscellaneous
80 | redis-manager-ui/.angular/cache
81 | redis-manager-ui/.sass-cache/
82 | redis-manager-ui/connect.lock
83 | redis-manager-ui/coverage
84 | redis-manager-ui/libpeerconnection.log
85 | redis-manager-ui/testem.log
86 | redis-manager-ui/typings
87 |
88 | # System files
89 | redis-manager-ui/.DS_Store
90 | redis-manager-ui/Thumbs.db
91 |
92 | redis-manager-ui/npm-debug.log*
93 | redis-manager-ui/yarn-debug.log*
94 | redis-manager-ui/yarn-error.log*
95 | redis-manager-ui/**/*.log
96 | redis-manager-ui/test/unit/coverage/
97 | redis-manager-ui/test/e2e/reports/
98 | redis-manager-ui/selenium-debug.log
99 | redis-manager-ui/tests/**/coverage/
100 | redis-manager-ui/tests/e2e/reports
101 | # Editor directories and files
102 | redis-manager-ui/*.suo
103 | redis-manager-ui/*.ntvs*
104 | redis-manager-ui/*.njsproj
105 | redis-manager-ui/*.sln
106 | redis-manager-ui/*.local
107 | redis-manager-ui/package-lock.json
108 | redis-manager-ui/yarn.lock
109 |
110 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM centos:7
2 |
3 | ENV LANG en_US.UTF-8
4 | ENV TZ Asia/Shanghai
5 |
6 | WORKDIR /data
7 |
8 | COPY ./redis-manager .
9 | COPY ./website ./website
10 | COPY ./yaml ./yaml
11 | CMD ["/data/redis-manager"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2021-present, 元匠信息科技(上海)有限公司
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
13 | all 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
21 | THE SOFTWARE
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # redis-manager
2 | 这是一个可以操作[redis cluster/codis/腾讯云redis/阿里云redis]的web管理平台。
3 | _________________
4 |
5 | ## 功能简介
6 | 1. **Cluster操作界面:** 支持Redis Cluster集群的添加,可以查看Redis Cluster的集群状态
7 | 2. **Codis操作界面:** 支持嵌入Codis Dashboard平台,可以查看codis平台信息,并且支持codis的扩缩容操作
8 | 3. **腾讯云Redis操作界面:** 支持腾讯云Redis的导入,可以查看腾讯Redis的基本信息
9 | 4. **阿里云Redis操作界面:** 支持阿里云Redis的导入,可以查看阿里Redis的基本信息(开发中...)
10 | 5. **数据查询界面:** 支持[string/list/hash/set/zset]类型的key的查询,以及查询[大key/热key/慢key/查询1万key]等功能,[阿里云redis暂时不支持]
11 | 6. **用户界面:** 支持用户的添加删除,可以管理平台用户
12 | 7. **系统设置界面:** 支持设置全局配置以及用户权限配置,可以管理平台系统配置
13 | 8. **历史记录界面:** 支持记载变更操作记录,方便审核回溯
14 |
15 |
16 | ## 项目启动
17 | ### 依赖语言
18 | - Golang1.19 + Vue3
19 | ### 环境依赖
20 | - mysql数据库 8.0版本以上
21 | - 云redis或者codis,或者单Redis,非cluster,2.8版本以上
22 | ### 启动
23 | - mysql需要创建 `redis_manager` 数据库:`create database redis_manager;`
24 | - 复制 yaml/dev.yaml 到 yaml/config.yaml
25 | - 编辑 yaml/config.yaml文件的[mysql、redis]配置
26 | - 命令行:`go run main.go`
27 | - docker模式启动: `sh start-docker.sh`
28 | - 浏览器访问:`http://127.0.0.1:8000`
29 |
30 | ## 项目依赖
31 | ### 大key查询依赖
32 | - 如果需要在自建Cluster和Codis上查询大key,则需要在每个redis机器上安装Agent,检测bgsave的dump文件,上传到cos里,才能进行使用;项目地址:[redis-agent](https://github.com/iguidao/redis-agent)
33 |
34 |
35 | ## 主要功能界面介绍
36 | 账号:iguidao
37 | 密码:123456
38 | ### 登陆
39 | > 账号密码登陆
40 |
41 |
42 | ### 概览
43 | > 展示集群基本信息
44 |
45 |
46 | ### Cluster集群页面
47 | > 展示Redis Cluster集群的信息
48 |
49 |
50 | ### Codis集群页面
51 | > 展示Codis集群信息
52 |
53 |
54 | ### 腾讯Redis集群界面
55 | > 展示腾讯Redis集群信息
56 |
>
57 |
58 | ### 阿里Redis集群界面
59 | > 展示阿里Redis集群信息[未测试]
60 |
61 |
62 | ### 数据查询界面
63 | > 进行redis的key操作,支持[查key/大key/热key/慢key/查询1万key/删key]功能
64 |
65 |
66 | ### 用户管理界面
67 | > 进行用户的创建删除,身份更改功能
68 |
69 |
70 | ### 系统设置界面
71 | > 进行全局配置
72 |
73 | > 进行权限配置
74 |
75 |
76 | ### 历史记录界面
77 | > 所有变更操作都会记录
78 |
79 |
80 | ## 联系方式
81 | mail: xiaohui920@sina.cn
82 |
83 |
84 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iguidao/redis-manager/43400bf548ffb68767ce7067679c6128fc2e155b/favicon.ico
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/iguidao/redis-manager/src/cfg"
5 | "github.com/iguidao/redis-manager/src/middleware/casbin"
6 | "github.com/iguidao/redis-manager/src/middleware/logger"
7 | "github.com/iguidao/redis-manager/src/middleware/model"
8 | "github.com/iguidao/redis-manager/src/middleware/mysql"
9 | "github.com/iguidao/redis-manager/src/middleware/rcron"
10 | "github.com/iguidao/redis-manager/src/rhttp"
11 | "github.com/robfig/cron"
12 | )
13 |
14 | func init() {
15 | if err := cfg.Init(""); err != nil {
16 | panic(err)
17 | }
18 | logger.SetupLogger()
19 | mysql.Connect(cfg.Get_Info_String("MYSQL"))
20 | mysql.Migrate()
21 | casbin.Connect()
22 | }
23 |
24 | func main() {
25 | c := cron.New()
26 | var calendarcrontime string
27 | calendarcrontime = mysql.DB.GetOneCfgValue(model.CLOUDREFRESH)
28 | if calendarcrontime == "" {
29 | calendarcrontime = "@every 10m"
30 | }
31 | c.AddFunc(calendarcrontime, func() {
32 | rcron.CloudRefresh()
33 | })
34 | c.Start()
35 | listen := cfg.Get_Info_String("addr")
36 | if listen == "" {
37 | listen = ":8000"
38 | }
39 | rhttp.NewServer().Run(listen)
40 | }
41 |
--------------------------------------------------------------------------------
/redis-manager-ui/.env.development:
--------------------------------------------------------------------------------
1 | VITE_MODE_NAME=development
2 | VITE_BASE_API='/api'
3 | VITE_APP_TITLE=Redis-Manager
--------------------------------------------------------------------------------
/redis-manager-ui/.env.production:
--------------------------------------------------------------------------------
1 | VITE_MODE_NAME=production
2 | VITE_BASE_API='/redis-manager'
3 | VITE_APP_TITLE=Redis-Manager
--------------------------------------------------------------------------------
/redis-manager-ui/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/redis-manager-ui/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
3 | }
4 |
--------------------------------------------------------------------------------
/redis-manager-ui/auto-imports.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /* prettier-ignore */
3 | // @ts-nocheck
4 | // Generated by unplugin-auto-import
5 | export {}
6 | declare global {
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/redis-manager-ui/components.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | /* prettier-ignore */
3 | // @ts-nocheck
4 | // Generated by unplugin-vue-components
5 | // Read more: https://github.com/vuejs/core/pull/3399
6 | import '@vue/runtime-core'
7 |
8 | export {}
9 |
10 | declare module '@vue/runtime-core' {
11 | export interface GlobalComponents {
12 | Breadcrumb: typeof import('./src/components/breadcrumb/Breadcrumb.vue')['default']
13 | ElAside: typeof import('element-plus/es')['ElAside']
14 | ElAvatar: typeof import('element-plus/es')['ElAvatar']
15 | ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
16 | ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
17 | ElButton: typeof import('element-plus/es')['ElButton']
18 | ElCard: typeof import('element-plus/es')['ElCard']
19 | ElCol: typeof import('element-plus/es')['ElCol']
20 | ElContainer: typeof import('element-plus/es')['ElContainer']
21 | ElDropdown: typeof import('element-plus/es')['ElDropdown']
22 | ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
23 | ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
24 | ElForm: typeof import('element-plus/es')['ElForm']
25 | ElFormItem: typeof import('element-plus/es')['ElFormItem']
26 | ElHeader: typeof import('element-plus/es')['ElHeader']
27 | ElIcon: typeof import('element-plus/es')['ElIcon']
28 | ElImage: typeof import('element-plus/es')['ElImage']
29 | ElInput: typeof import('element-plus/es')['ElInput']
30 | ElMain: typeof import('element-plus/es')['ElMain']
31 | ElMenu: typeof import('element-plus/es')['ElMenu']
32 | ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
33 | ElRow: typeof import('element-plus/es')['ElRow']
34 | ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
35 | Footer: typeof import('./src/components/footer/Footer.vue')['default']
36 | RouterLink: typeof import('vue-router')['RouterLink']
37 | RouterView: typeof import('vue-router')['RouterView']
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/redis-manager-ui/declaration.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'vue-json-viewer' {
2 | const vis: any;
3 | export default vis;
4 | }
--------------------------------------------------------------------------------
/redis-manager-ui/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Redis Manager
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/redis-manager-ui/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redis-manager-ui",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vue-tsc && vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "@element-plus/icons-vue": "^2.1.0",
13 | "axios": "^1.3.5",
14 | "clipboard": "^2.0.11",
15 | "element-plus": "^2.3.3",
16 | "moment": "^2.29.4",
17 | "pnpm": "^8.3.0",
18 | "vue": "^3.2.47",
19 | "vue-json-viewer": "^3.0.4",
20 | "vue-router": "^4.1.6"
21 | },
22 | "devDependencies": {
23 | "@vitejs/plugin-vue": "^4.1.0",
24 | "typescript": "^4.9.3",
25 | "unplugin-auto-import": "^0.15.2",
26 | "unplugin-vue-components": "^0.24.1",
27 | "vite": "^4.2.0",
28 | "vue-tsc": "^1.2.0"
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/redis-manager-ui/public/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
16 |
--------------------------------------------------------------------------------
/redis-manager-ui/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
8 |
10 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/api/board.ts:
--------------------------------------------------------------------------------
1 | import RequestHttp from '../utils/request'
2 |
3 | namespace Rboard {
4 |
5 | // 登录成功后返回的token
6 | export interface ResData {
7 | data: {
8 | aliredis: number;
9 | codis: number;
10 | txredis: number;
11 | cluster: number;
12 | }
13 | errorCode: number;
14 | msg: string;
15 | }
16 | }
17 | // 用户登录
18 | export const BoardDesc = () => {
19 | // 返回的数据格式可以和服务端约定
20 | return RequestHttp.get('/board/v1/desc');
21 | }
22 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/api/cli.ts:
--------------------------------------------------------------------------------
1 | import RequestHttp from '../utils/request'
2 |
3 | namespace Rcli {
4 | // 用户登录表单
5 | export interface CliReqForm {
6 | cache_type: string;
7 | cache_op: string;
8 | cluster_name: string;
9 | key_name: string;
10 | codis_url: string;
11 | group_name: string;
12 |
13 | }
14 | // 登录成功后返回的token
15 | export interface CliResData {
16 | data: {}
17 | errorCode: number;
18 | msg: string;
19 | }
20 | }
21 | // 用户登录
22 | export const cliRedisOpkey = (params: Rcli.CliReqForm) => {
23 | // 返回的数据格式可以和服务端约定
24 | return RequestHttp.post('/cli/v1/opkey', params);
25 | }
26 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/api/cloud.ts:
--------------------------------------------------------------------------------
1 | import RequestHttp from '../utils/request'
2 |
3 | namespace RCloud {
4 | export interface RegionReqForm {
5 | cloud: string;
6 | }
7 | export interface RegionResData {
8 | data: {
9 | region_list: [{
10 | Region: string;
11 | RegionName: string;
12 | RegionState: string;
13 | RegionId: string;
14 | RegionEndpoint: string;
15 | LocalName: string;
16 | }]
17 | }
18 | errorCode: number;
19 | msg: string;
20 | }
21 | export interface ListReqForm {
22 | cloud: string;
23 | region: string;
24 | }
25 | export interface ListResData {
26 | data: {
27 | redis_list: [
28 | {
29 | Cloud: string;
30 | InstanceName: string;
31 | InstanceId: string;
32 | PrivateIp: string;
33 | Port: string;
34 | Region: string;
35 | Createtime: string;
36 | Size: number;
37 | InstanceStatus: string;
38 | RedisShardSize: number;
39 | RedisShardNum: number;
40 | RedisReplicasNum: number;
41 | NoAuth: Boolean;
42 | PublicIp: string;
43 | }
44 | ]
45 | }
46 | errorCode: number;
47 | msg: string;
48 | }
49 | export interface PasswordReqForm {
50 | cloud: string;
51 | instanceid: string;
52 | password: string;
53 | }
54 | export interface PasswordResData {
55 | data: {}
56 | errorCode: number;
57 | msg: string;
58 | }
59 | }
60 | export const listCloudRegion = (params: RCloud.RegionReqForm) => {
61 | // 返回的数据格式可以和服务端约定
62 | return RequestHttp.get('/cloud/v1/region', {params: params});
63 | }
64 |
65 | export const listCloudRedis = (params: RCloud.ListReqForm) => {
66 | // 返回的数据格式可以和服务端约定
67 | return RequestHttp.get('/cloud/v1/list', {params: params});
68 | }
69 |
70 |
71 | export const changeCloudRedisPw = (params: RCloud.PasswordReqForm) => {
72 | // 返回的数据格式可以和服务端约定
73 | return RequestHttp.post('/cloud/v1/password', params);
74 | }
75 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/api/cluster.ts:
--------------------------------------------------------------------------------
1 | import RequestHttp from '../utils/request'
2 |
3 | // 新增codis平台
4 | namespace RCluster {
5 | export interface updateReqForm {
6 | name: string;
7 | nodes: string;
8 | password: string;
9 | }
10 | export interface updateResData {
11 | data: {}
12 | errorCode: number;
13 | msg: string;
14 | }
15 | export interface listResData {
16 | data: [{
17 | ID: number;
18 | CreatedAt: string;
19 | UpdatedAt: string;
20 | DeletedAt: string;
21 | Name: string;
22 | Nodes: string;
23 | Password: string;
24 | }]
25 | errorCode: number;
26 | msg: string;
27 | }
28 | export interface nodeReqForm {
29 | cluster_id: string;
30 | }
31 | export interface Node {
32 | CreateTime: string;
33 | CluserId: string;
34 | NodeId: string;
35 | Address: string;
36 | Flags: string;
37 | LinkState: string;
38 | RunStatus: string;
39 | SlotRange: string;
40 | SlotNumber: string;
41 | Children: Node[];
42 | }
43 | // 拿到配置数据
44 | export interface nodeResData {
45 | data: [{
46 | CreateTime: string;
47 | CluserId: string;
48 | NodeId: string;
49 | Address: string;
50 | Flags: string;
51 | LinkState: string;
52 | RunStatus: string;
53 | SlotRange: string;
54 | SlotNumber: string;
55 | Children: Node[];
56 | }]
57 | errorCode: number;
58 | msg: string;
59 | }
60 | export interface masterReqForm {
61 | cluster_id: string;
62 | }
63 | export interface masterResData {
64 | data: [{
65 | CreatedAt: string;
66 | CluserId: string;
67 | NodeId: string;
68 | Ip: string;
69 | Port: string;
70 | SlotRange: string;
71 | }]
72 | errorCode: number;
73 | msg: string;
74 | }
75 | }
76 | // 删除
77 | export const delClusterCfg = (params: RCluster.updateReqForm) => {
78 | return RequestHttp.delete('/cluster/v1/del', {params: params});
79 | }
80 |
81 | //新增
82 | export const addClusterCfg = (params: RCluster.updateReqForm) => {
83 | return RequestHttp.post('/cluster/v1/add', params);
84 | }
85 |
86 | // 配置获取
87 | export const listCluster = () => {
88 | // 返回的数据格式可以和服务端约定
89 | return RequestHttp.get('/cluster/v1/list');
90 | }
91 |
92 | // 配置获取
93 | export const listClusterNodes = (params: RCluster.nodeReqForm) => {
94 | // 返回的数据格式可以和服务端约定
95 | return RequestHttp.get('/cluster/v1/nodes', { params: params});
96 | }
97 |
98 | //获取master信息
99 | export const listClusterMaster = (params: RCluster.masterReqForm) => {
100 | return RequestHttp.get('/cluster/v1/masters', { params: params});
101 | }
--------------------------------------------------------------------------------
/redis-manager-ui/src/api/codis.ts:
--------------------------------------------------------------------------------
1 | import RequestHttp from '../utils/request'
2 |
3 | // 新增codis平台
4 | namespace Rcodis {
5 | export interface ReqForm {
6 | cname: string;
7 | curl: string;
8 | }
9 | export interface SettingResData {
10 | data: number
11 | errorCode: number;
12 | msg: string;
13 | }
14 | export interface ListResData {
15 | data: {
16 | lists: [{
17 | ID: number;
18 | CreatedAt: string;
19 | UpdatedAt: string;
20 | DeletedAt: string;
21 | Curl: string;
22 | Cname: string;
23 | }];
24 | total: number;
25 | }
26 | errorCode: number;
27 | msg: string;
28 | }
29 | export interface ListclusterReqForm {
30 | cname: string;
31 | curl: string;
32 | }
33 | export interface ListclusterResData {
34 | data: [string]
35 | errorCode: number;
36 | msg: string;
37 | }
38 | export interface ListgroupReqForm {
39 | cluster_name: string;
40 | curl: string;
41 | }
42 | export interface ListgroupResData {
43 | data: [string]
44 | errorCode: number;
45 | msg: string;
46 | }
47 | export interface opnodeReqForm {
48 | curl: string;
49 | cluster_name: string;
50 | add_proxy: string;
51 | add_server: string;
52 | del_proxy: number;
53 | del_group: number;
54 | op_type: string
55 | }
56 | export interface opnodeResData {
57 | data: string
58 | errorCode: number;
59 | msg: string;
60 | }
61 | }
62 | // 删除
63 | export const delCodisCfg = (params: Rcodis.ReqForm) => {
64 | return RequestHttp.delete('/cfg/v1/del', {params: params});
65 | }
66 |
67 | //新增
68 | export const addCodisCfg = (params: Rcodis.ReqForm) => {
69 | return RequestHttp.post('/codis/v1/add', params);
70 | }
71 |
72 | // 配置获取
73 | export const listCodis = () => {
74 | // 返回的数据格式可以和服务端约定
75 | return RequestHttp.get('/codis/v1/list');
76 | }
77 | //获取codis平台集群列表
78 | export const listCodisCluster = (params: Rcodis.ListclusterReqForm) => {
79 | // 返回的数据格式可以和服务端约定
80 | return RequestHttp.get('/codis/v1/cluster', {params: params});
81 | }
82 | //获取codis集群的group列表
83 | export const listCodisGroup = (params: Rcodis.ListgroupReqForm) => {
84 | // 返回的数据格式可以和服务端约定
85 | return RequestHttp.get('/codis/v1/group', {params: params});
86 | }
87 | //针对codis进行扩容缩容
88 | export const opCodisNode = (params: Rcodis.opnodeReqForm) => {
89 | return RequestHttp.post('/codis/v1/opnode', params);
90 | }
--------------------------------------------------------------------------------
/redis-manager-ui/src/api/history.ts:
--------------------------------------------------------------------------------
1 | import RequestHttp from '../utils/request'
2 |
3 | namespace Rhistory {
4 | // 拿到配置数据
5 | export interface HistoryResData {
6 | data: [
7 | {
8 | ID: number;
9 | CreatedAt: string;
10 | UpdatedAt: string;
11 | DeletedAt: string;
12 | UserId: number;
13 | OpInfo: string;
14 | OpParams: string;
15 | }
16 | ]
17 | errorCode: number;
18 | msg: string;
19 | }
20 | }
21 | // 配置获取
22 | export const list = () => {
23 | // 返回的数据格式可以和服务端约定
24 | return RequestHttp.get('/ophistory/v1/list');
25 | }
26 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/api/rule.ts:
--------------------------------------------------------------------------------
1 | import RequestHttp from '../utils/request'
2 |
3 | namespace Rruledata {
4 | export interface AddReqForm {
5 | identity: string;
6 | path: string;
7 | method: string;
8 | }
9 | export interface AddResData {
10 | data: {
11 | result: string;
12 | }
13 | errorCode: number;
14 | msg: string;
15 | }
16 | export interface ListResData {
17 | data: [{
18 | ID: number;
19 | identity: string;
20 | method: string;
21 | note: string;
22 | path: string;
23 | }]
24 | errorCode: number;
25 | msg: string;
26 | }
27 | export interface CfgResData {
28 | data: {
29 | method: [{
30 | label: string;
31 | value: string;
32 | }]
33 | url: [{
34 | label: string;
35 | value: string;
36 | }]
37 | }
38 | errorCode: number;
39 | msg: string;
40 | }
41 | }
42 | export const rulelist = () => {
43 | // 返回的数据格式可以和服务端约定
44 | return RequestHttp.get('/rule/v1/list');
45 | }
46 |
47 | export const ruledel = (params: Rruledata.AddReqForm) => {
48 | return RequestHttp.delete('/rule/v1/del', {params: params});
49 | }
50 |
51 | export const rulecfg = () => {
52 | return RequestHttp.get('/rule/v1/cfg');
53 | }
54 |
55 | export const ruleadd = (params: Rruledata.AddReqForm) => {
56 | return RequestHttp.post('/rule/v1/add', params);
57 | }
--------------------------------------------------------------------------------
/redis-manager-ui/src/api/setting.ts:
--------------------------------------------------------------------------------
1 | import RequestHttp from '../utils/request'
2 |
3 | namespace Rconfig {
4 | // 拿到配置数据
5 | export interface ListResData {
6 | data: {
7 | lists: [{
8 | ID: number;
9 | CreatedAt: string;
10 | UpdatedAt: string;
11 | DeletedAt: string;
12 | Key: string;
13 | Name: string;
14 | Value: string;
15 | }];
16 | total: number;
17 | }
18 | errorCode: number;
19 | msg: string;
20 | }
21 | export interface ResData {
22 | data: [{
23 | label: string;
24 | value: string;
25 | }]
26 | errorCode: number;
27 | msg: string;
28 | }
29 | export interface ReqForm {
30 | key: string;
31 | value: string;
32 | }
33 | }
34 | // 已配置获取
35 | export const listCfg = () => {
36 | // 返回的数据格式可以和服务端约定
37 | return RequestHttp.get('/cfg/v1/list');
38 | }
39 |
40 | // 默认配置获取
41 | export const listDefaultCfg = () => {
42 | // 返回的数据格式可以和服务端约定
43 | return RequestHttp.get('/cfg/v1/listdefault');
44 | }
45 |
46 | // 删除
47 | export const delCfg = (params: Rconfig.ReqForm) => {
48 | return RequestHttp.delete('/cfg/v1/del', {params: params});
49 | }
50 |
51 | //变更
52 | export const updateCfg = (params: Rconfig.ReqForm) => {
53 | return RequestHttp.post('/cfg/v1/update', params);
54 | }
--------------------------------------------------------------------------------
/redis-manager-ui/src/api/user.ts:
--------------------------------------------------------------------------------
1 | import RequestHttp from '../utils/request'
2 |
3 | namespace Ruser {
4 | // 用户登录表单
5 | export interface LoginReqForm {
6 | username: string;
7 | password: string;
8 | }
9 | // 登录成功后返回的token
10 | export interface LoginResData {
11 | data: {
12 | result: string;
13 | token: string;
14 | username: string;
15 | usertype: string;
16 | }
17 | errorCode: number;
18 | msg: string;
19 | }
20 | export interface PasswordReqForm {
21 | old: string;
22 | new: string;
23 | }
24 | export interface PasswordResData {
25 | data: {
26 | result: string;
27 | }
28 | errorCode: number;
29 | msg: string;
30 | }
31 | export interface listResData {
32 | data: [{
33 | ID: number;
34 | CreatedAt: string;
35 | UpdatedAt: string;
36 | UserName: string;
37 | Email: string;
38 | UserType: string;
39 | }]
40 | errorCode: number;
41 | msg: string;
42 | }
43 | export interface typeResData {
44 | data: [{
45 | label: string;
46 | value: string;
47 | }]
48 | errorCode: number;
49 | msg: string;
50 | }
51 | export interface createReqForm {
52 | username: string;
53 | password: string;
54 | mail: string;
55 | }
56 | export interface createResData {
57 | data: {
58 | result: string;
59 | }
60 | errorCode: number;
61 | msg: string;
62 | }
63 | export interface changeReqForm {
64 | username: string;
65 | usertype: string;
66 | }
67 | export interface changeResData {
68 | data: {
69 | result: string;
70 | }
71 | errorCode: number;
72 | msg: string;
73 | }
74 | export interface delReqForm {
75 | username: string;
76 | }
77 | export interface delResData {
78 | data: {
79 | result: string;
80 | }
81 | errorCode: number;
82 | msg: string;
83 | }
84 | }
85 | // 用户登录
86 | export const login = (params: Ruser.LoginReqForm) => {
87 | // 返回的数据格式可以和服务端约定
88 | return RequestHttp.post('/auth/v1/sign-in', params);
89 | }
90 |
91 | export const userpassword = (params: Ruser.PasswordReqForm) => {
92 | // 返回的数据格式可以和服务端约定
93 | return RequestHttp.post('/auth/v1/password', params);
94 | }
95 |
96 | export const userlist = () => {
97 | // 返回的数据格式可以和服务端约定
98 | return RequestHttp.get('/user/v1/list');
99 | }
100 |
101 | export const usertypelist = () => {
102 | // 返回的数据格式可以和服务端约定
103 | return RequestHttp.get('/user/v1/utype');
104 | }
105 |
106 | export const useradd = (params: Ruser.createReqForm) => {
107 | // 返回的数据格式可以和服务端约定
108 | return RequestHttp.post('/user/v1/add', params);
109 | }
110 |
111 | export const userchange = (params: Ruser.changeReqForm) => {
112 | // 返回的数据格式可以和服务端约定
113 | return RequestHttp.post('/user/v1/change', params);
114 | }
115 |
116 | export const userdel = (params: Ruser.delReqForm) => {
117 | // 返回的数据格式可以和服务端约定
118 | return RequestHttp.delete('/user/v1/del', {params: params});
119 | }
120 |
121 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/assets/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iguidao/redis-manager/43400bf548ffb68767ce7067679c6128fc2e155b/redis-manager-ui/src/assets/img/favicon.ico
--------------------------------------------------------------------------------
/redis-manager-ui/src/assets/img/github-mark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iguidao/redis-manager/43400bf548ffb68767ce7067679c6128fc2e155b/redis-manager-ui/src/assets/img/github-mark.png
--------------------------------------------------------------------------------
/redis-manager-ui/src/assets/img/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iguidao/redis-manager/43400bf548ffb68767ce7067679c6128fc2e155b/redis-manager-ui/src/assets/img/logo.png
--------------------------------------------------------------------------------
/redis-manager-ui/src/assets/img/sun.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iguidao/redis-manager/43400bf548ffb68767ce7067679c6128fc2e155b/redis-manager-ui/src/assets/img/sun.jpeg
--------------------------------------------------------------------------------
/redis-manager-ui/src/assets/vue.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/components/breadcrumb/Breadcrumb.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{item?.meta?.title}}
7 |
8 |
9 |
10 |
11 |
12 |
21 |
22 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/main.ts:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import './style.css'
3 | import App from './App.vue'
4 | import router from './router/index'
5 | import * as ElementPlusIconsVue from '@element-plus/icons-vue'
6 | import ElementPlus from 'element-plus'
7 | import 'element-plus/dist/index.css'
8 | import JsonViewer from 'vue-json-viewer'
9 | import.meta.env.MODE
10 |
11 | const app = createApp(App)
12 | for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
13 | app.component(key, component)
14 | }
15 | app.use(router).use(ElementPlus).use(JsonViewer).mount('#app')
16 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/pages/command/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
31 |
36 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/pages/dashboard/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | {{ descdata.txredis }}
8 | 0
9 | 腾讯Redis
10 |
11 |
12 |
13 | {{ descdata.aliredis }}
14 | 0
15 | 阿里Redis
16 |
17 |
18 |
19 | {{ descdata.codis }}
20 | 0
21 | 自建Codis
22 |
23 |
24 |
25 | {{ descdata.cluster }}
26 | 0
27 | 自建Cluster
28 |
29 |
30 |
31 |
32 |
34 |
35 |
36 |
37 |
38 |
57 |
58 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/pages/history/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
42 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/pages/login/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 登 录
7 | 欢迎登录Redis-Manager平台
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | 登录
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
67 |
68 |
119 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/router/index.ts:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
2 | const routes = [
3 | {
4 | path: '/',
5 | redirect: '/login'
6 | },
7 | {
8 | path: '/login',
9 | name: 'login',
10 | meta: {
11 | title: '登录'
12 | },
13 | component: () => import('../pages/login/Index.vue')
14 | },
15 |
16 | {
17 | path: '/home',
18 | meta: {
19 | title: 'Redis Manager'
20 | },
21 | component: () => import('../pages/home/Index.vue'),
22 | redirect: '/dashboard',
23 | children: [{
24 | path: '/dashboard',
25 | meta: {
26 | title: '概览'
27 | },
28 | component: () => import('../pages/dashboard/Index.vue')
29 | },
30 | {
31 | path: '/user/index',
32 | meta: {
33 | title: '用户管理'
34 | },
35 | component: () => import('../pages/user/Index.vue'),
36 | },
37 | {
38 | path: '/aliredis/index',
39 | meta: {
40 | title: '阿里redis'
41 | },
42 | component: () => import('../pages/redis/aliredis/Index.vue'),
43 | },
44 | {
45 | path: '/redis/index',
46 | meta: {
47 | title: '自建cluster'
48 | },
49 | component: () => import('../pages/redis/cluster/Index.vue'),
50 | },
51 | {
52 | path: '/codis/index',
53 | meta: {
54 | title: '自建codis'
55 | },
56 | component: () => import('../pages/redis/codis/Index.vue'),
57 | },
58 | {
59 | path: '/txredis/index',
60 | meta: {
61 | title: '腾讯redis'
62 | },
63 | component: () => import('../pages/redis/txredis/Index.vue'),
64 | },
65 | {
66 | path: '/command/query',
67 | meta: {
68 | title: '查询数据'
69 | },
70 | component: () => import('../pages/command/Index.vue'),
71 | },
72 | {
73 | path: '/history/index',
74 | meta: {
75 | title: '历史记录'
76 | },
77 | component: () => import('../pages/history/Index.vue'),
78 | },
79 | {
80 | path: '/setting/index',
81 | meta: {
82 | title: '系统设置'
83 | },
84 | component: () => import('../pages/setting/Index.vue'),
85 | },
86 | {
87 | path: '/setting/rule',
88 | meta: {
89 | title: '权限配置'
90 | },
91 | component: () => import('../pages/setting/Rule.vue'),
92 | },
93 | ]
94 | },
95 | ]
96 | const router = createRouter({
97 | history: createWebHistory(),
98 | // history: createWebHashHistory(),
99 | routes
100 | })
101 | // 挂载路由导航守卫:to表示将要访问的路径,from表示从哪里来,next是下一个要做的操作
102 | router.beforeEach((to, from, next) => {
103 | // 修改页面 title
104 | // if (to.meta.title) {
105 | // document.title = 'Redis-Manager平台 - ' + to.meta.title
106 | // }
107 | // 放行登录页面
108 | if (to.path === '/login') {
109 | return next()
110 | }
111 | // 获取token
112 | const token = sessionStorage.getItem('Authorization')
113 | if (!token) {
114 | return next('/login')
115 | } else {
116 | next()
117 | }
118 | return next()
119 | })
120 |
121 | // 导出路由
122 | export default router
--------------------------------------------------------------------------------
/redis-manager-ui/src/style.css:
--------------------------------------------------------------------------------
1 | /* :root {
2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
3 | line-height: 1.5;
4 | font-weight: 400;
5 |
6 | color-scheme: light dark;
7 | color: rgba(255, 255, 255, 0.87);
8 | background-color: #242424;
9 |
10 | font-synthesis: none;
11 | text-rendering: optimizeLegibility;
12 | -webkit-font-smoothing: antialiased;
13 | -moz-osx-font-smoothing: grayscale;
14 | -webkit-text-size-adjust: 100%;
15 | }
16 |
17 | a {
18 | font-weight: 500;
19 | color: #646cff;
20 | text-decoration: inherit;
21 | }
22 | a:hover {
23 | color: #535bf2;
24 | }
25 |
26 | body {
27 | margin: 0;
28 | display: flex;
29 | place-items: center;
30 | min-width: 320px;
31 | min-height: 100vh;
32 | }
33 |
34 | h1 {
35 | font-size: 3.2em;
36 | line-height: 1.1;
37 | }
38 |
39 | button {
40 | border-radius: 8px;
41 | border: 1px solid transparent;
42 | padding: 0.6em 1.2em;
43 | font-size: 1em;
44 | font-weight: 500;
45 | font-family: inherit;
46 | background-color: #1a1a1a;
47 | cursor: pointer;
48 | transition: border-color 0.25s;
49 | }
50 | button:hover {
51 | border-color: #646cff;
52 | }
53 | button:focus,
54 | button:focus-visible {
55 | outline: 4px auto -webkit-focus-ring-color;
56 | }
57 |
58 | .card {
59 | padding: 2em;
60 | }
61 |
62 | #app {
63 | max-width: 1280px;
64 | margin: 0 auto;
65 | padding: 2rem;
66 | text-align: center;
67 | }
68 |
69 | @media (prefers-color-scheme: light) {
70 | :root {
71 | color: #213547;
72 | background-color: #ffffff;
73 | }
74 | a:hover {
75 | color: #747bff;
76 | }
77 | button {
78 | background-color: #f9f9f9;
79 | }
80 | } */
81 |
--------------------------------------------------------------------------------
/redis-manager-ui/src/utils/request/http.ts:
--------------------------------------------------------------------------------
1 | import axiosInstance from './index'
2 |
3 | // 数据返回的接口
4 | // 定义请求响应参数,不含data
5 | interface Result {
6 | errorCode: number;
7 | msg: string
8 | }
9 |
10 | // 请求响应参数,包含data
11 | interface ResultData extends Result {
12 | data?: T;
13 | }
14 |
15 | class RequestHttp {
16 | get(url: string, params?: object): Promise> {
17 | return axiosInstance.get(url, {params});
18 | }
19 | post(url: string, params?: object): Promise> {
20 | return axiosInstance.post(url, params);
21 | }
22 | put(url: string, params?: object): Promise> {
23 | return axiosInstance.put(url, params);
24 | }
25 | delete(url: string, params?: object): Promise> {
26 | return axiosInstance.delete(url, {params});
27 | }
28 | }
29 | // 导出一个实例对象
30 | export default RequestHttp
--------------------------------------------------------------------------------
/redis-manager-ui/src/utils/request/index.ts:
--------------------------------------------------------------------------------
1 | import axios, { AxiosRequestConfig, AxiosRequestHeaders } from "axios";
2 | import {ElMessage} from 'element-plus'
3 |
4 |
5 | interface AdaptAxiosRequestConfig extends AxiosRequestConfig {
6 | headers: AxiosRequestHeaders
7 | }
8 |
9 | // config
10 | const axiosInstance = axios.create({
11 | baseURL: import.meta.env.VITE_BASE_API,
12 | // baseURL: "/api",
13 | timeout: 30000
14 | });
15 |
16 | // Interceptors
17 | axiosInstance.interceptors.request.use(
18 | (config): AdaptAxiosRequestConfig => {
19 | let token = sessionStorage.getItem('Authorization')
20 | if (token) {
21 | config.headers['Authorization'] = token
22 | }
23 | return config;
24 | },
25 | (error): any => {
26 | return Promise.reject(error);
27 | }
28 | );
29 |
30 | axiosInstance.interceptors.response.use(
31 | async (response): Promise => {
32 | return response;
33 | },
34 | async (error): Promise => {
35 | if (error && error.response) {
36 | const status = error.response.status
37 | switch (status) {
38 | case 400:
39 | ElMessage.error("请求错误");
40 | break;
41 | case 401:
42 | ElMessage.error("未授权,请重新登录");
43 | break;
44 | case 403:
45 | ElMessage.error("拒绝访问");
46 | break;
47 | case 404:
48 | ElMessage.error("请求错误,未找到相应的资源");
49 | break;
50 | case 408:
51 | ElMessage.error("请求超时");
52 | break;
53 | case 500:
54 | ElMessage.error("服务器内部错误");
55 | break;
56 | case 501:
57 | ElMessage.error("网络未实现");
58 | break;
59 | case 502:
60 | ElMessage.error("网络错误");
61 | break;
62 | case 503:
63 | ElMessage.error("服务不可用");
64 | break;
65 | case 504:
66 | ElMessage.error("网络超时");
67 | break;
68 | case 505:
69 | ElMessage.error("HTTP版本不支持该请求");
70 | break;
71 | default:
72 | ElMessage.error("请求失败");
73 | }
74 | } else {
75 | if (JSON.stringify(error).includes("timeout")) {
76 | ElMessage.error("服务器响应超时,请刷新页面");
77 | }
78 | ElMessage.error("连接服务器失败");
79 | }
80 | return Promise.reject(error);
81 | }
82 | );
83 |
84 | // 4.导出 axios 实例
85 | export default axiosInstance;
--------------------------------------------------------------------------------
/redis-manager-ui/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | interface ImportMetaEnv {
4 | readonly VITE_BASE_API: string
5 | // 更多环境变量...
6 | }
7 | interface ImportMeta {
8 | readonly env: ImportMetaEnv
9 | }
--------------------------------------------------------------------------------
/redis-manager-ui/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ESNext",
4 | "useDefineForClassFields": true,
5 | "module": "ESNext",
6 | "moduleResolution": "Node",
7 | "strict": true,
8 | "jsx": "preserve",
9 | "resolveJsonModule": true,
10 | "isolatedModules": true,
11 | "esModuleInterop": true,
12 | "lib": ["ESNext", "DOM"],
13 | "skipLibCheck": true,
14 | "noEmit": true
15 | },
16 | "include": ["src/**/*.ts", "src/**/*.d.ts", "**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "src/**/**/*.vue", "src/api/user.js"],
17 | "references": [{ "path": "./tsconfig.node.json" }]
18 | }
19 |
--------------------------------------------------------------------------------
/redis-manager-ui/tsconfig.node.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "module": "ESNext",
5 | "moduleResolution": "Node",
6 | "allowSyntheticDefaultImports": true
7 | },
8 | "include": ["vite.config.ts"]
9 | }
10 |
--------------------------------------------------------------------------------
/redis-manager-ui/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 | // import AutoImport from 'unplugin-auto-import/vite'
4 | // import Components from 'unplugin-vue-components/vite'
5 | // import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
6 |
7 | // https://vitejs.dev/config/
8 | export default defineConfig({
9 | plugins: [
10 | vue(),
11 | // AutoImport({
12 | // resolvers: [ElementPlusResolver()],
13 | // }),
14 | // Components({
15 | // resolvers: [ElementPlusResolver()],
16 | // }),
17 | ],
18 | server: {
19 | open: false,
20 | port: 3000,
21 | proxy: {
22 | '/api': {
23 | target: 'http://127.0.0.1:8000/redis-manager', //
24 | changeOrigin: true,
25 | rewrite: (path) => path.replace(/^\/api/, '')
26 | }
27 | },
28 | },
29 | })
30 |
--------------------------------------------------------------------------------
/src/cfg/config.go:
--------------------------------------------------------------------------------
1 | package cfg
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/fsnotify/fsnotify"
7 | "github.com/spf13/viper"
8 | )
9 |
10 | type Config struct {
11 | Name string
12 | }
13 |
14 | func Get_Info_Int(get_type string) int {
15 | switch get_type {
16 | case "allkeyfornum":
17 | rediscfg_allkeyfornum := viper.GetInt("rediscfg.allkeyfornum")
18 | return rediscfg_allkeyfornum
19 | case "locktime":
20 | rediscfg_locktime := viper.GetInt("rediscfg.locktime")
21 | return rediscfg_locktime
22 | case "biglocktime":
23 | rediscfg_biglocktime := viper.GetInt("rediscfg.biglocktime")
24 | return rediscfg_biglocktime
25 | case "checksize":
26 | rediscfg_checksize := viper.GetInt("rediscfg.checksize")
27 | return rediscfg_checksize
28 | default:
29 | return 0
30 | }
31 | }
32 |
33 | func Get_Info_String(get_type string) string {
34 | switch get_type {
35 | case "MYSQL":
36 | mysql_name := viper.GetString("mysql.name")
37 | mysql_addr := viper.GetString("mysql.addr")
38 | mysql_username := viper.GetString("mysql.username")
39 | mysql_password := viper.GetString("mysql.password")
40 | mysql_url := fmt.Sprintf("%s:%s@(%s)/%s?charset=utf8&parseTime=True&loc=Local", mysql_username, mysql_password, mysql_addr, mysql_name)
41 | return mysql_url
42 | case "REDIS":
43 | redis_addr := viper.GetString("redis.addr")
44 | redis_port := viper.GetString("redis.port")
45 | redis_url := fmt.Sprintf("%s:%s", redis_addr, redis_port)
46 | return redis_url
47 | case "redispw":
48 | local_redispw := viper.GetString("redis.password")
49 | return local_redispw
50 | case "addr":
51 | local_addr := viper.GetString("local.addr")
52 | return local_addr
53 | case "logapipath":
54 | local_logapipath := viper.GetString("local.logapipath")
55 | return local_logapipath
56 | case "logapppath":
57 | local_logapppath := viper.GetString("local.logapppath")
58 | return local_logapppath
59 | case "secretkey":
60 | rediscfg_secretkey := viper.GetString("local.secretkey")
61 | return rediscfg_secretkey
62 | case "cosaccesskey":
63 | cos_cosaccesskey := viper.GetString("cos.cosaccesskey")
64 | return cos_cosaccesskey
65 | case "cosaccesskeyid":
66 | cos_cosaccesskeyid := viper.GetString("cos.cosaccesskeyid")
67 | return cos_cosaccesskeyid
68 | case "cosendpointpub":
69 | cos_cosendpointpub := viper.GetString("cos.cosendpointpub")
70 | return cos_cosendpointpub
71 | default:
72 | return "noconfig"
73 | }
74 | }
75 |
76 | func Init(cfg string) error {
77 | c := Config{
78 | Name: cfg,
79 | }
80 | // 初始化配置文件
81 | if err := c.initConfig(); err != nil {
82 | return err
83 | }
84 | c.watchConfig()
85 |
86 | return nil
87 | }
88 |
89 | func (c *Config) initConfig() error {
90 | if c.Name != "" {
91 | // 如果指定了配置文件,则解析指定的配置文件
92 | viper.SetConfigFile(c.Name)
93 | } else {
94 | // 如果没有指定配置文件,则解析默认的配置文件
95 | viper.AddConfigPath("yaml")
96 | viper.SetConfigName("config")
97 | }
98 | // 设置配置文件格式为YAML
99 | viper.SetConfigType("yaml")
100 | // viper解析配置文件
101 | if err := viper.ReadInConfig(); err != nil {
102 | return err
103 | }
104 |
105 | return nil
106 | }
107 |
108 | // 监听配置文件是否改变,用于热更新
109 | func (c *Config) watchConfig() {
110 | viper.WatchConfig()
111 | viper.OnConfigChange(func(e fsnotify.Event) {
112 | fmt.Printf("Config file changed: %s\n", e.Name)
113 | })
114 | }
115 |
--------------------------------------------------------------------------------
/src/hsc/code.go:
--------------------------------------------------------------------------------
1 | package hsc
2 |
3 | // 提供状态码
4 | const (
5 | SUCCESS = 0
6 | ERROR = 500
7 | INVALID_PARAMS = 400
8 | NO_LOGIN = 401
9 | NOT_PROMISE = 403
10 | SERVER_ERROR = 544
11 |
12 | NOT_FOUND = 10001
13 | Method_FAILS = 10002
14 | ERROR_AUTH_CHECK_TOKEN_FAIL = 20001
15 | ERROR_AUTH_CHECK_TOKEN_TIMEOUT = 20002
16 | ERROR_AUTH_TOKEN = 20003
17 | ERROR_AUTH = 20004
18 | MESSAGE_RE = 400
19 | ERROR_NO_CONNEC = 50001
20 | ERROR_BACKGROUND = 50002
21 | ERROR_CLOUD_CONNECT = 50003
22 | ERROR_CLOUD_GET = 50004
23 | ERROR_WRITE_MYSQL = 50005
24 | WARN_CLICK_REPEATEDLY = 60000
25 | WARN_NO_USE = 60002
26 | WARN_BACKGROUND = 60004
27 | WARN_NOT_FOUND_CLOUD = 60005
28 | WARN_NOT_PROMISE_RULE = 60006
29 | WARN_USER_NAME_EXIST = 60007
30 | WARN_USER_MAIL_EXIST = 60008
31 | WARN_USER_PASSWORD_CHECK = 60009
32 | WARN_CODIS_NOT_CONNECT = 60010
33 | WARN_CODIS_IS_REBALANCE = 60011
34 | WARN_CODIS_NOT_OPTION = 60012
35 | WARN_CODIS_PROXY_MIN_NUMBER = 60013
36 | WARN_CODIS_GROUP_MIN_NUMBER = 60014
37 | WARN_CODIS_GROUP_MIN_CAPACITY = 60015
38 | WARN_CHECK_IPPORT_FAIL = 60016
39 | )
40 |
--------------------------------------------------------------------------------
/src/hsc/msg.go:
--------------------------------------------------------------------------------
1 | package hsc
2 |
3 | // 提供错误信息
4 | var MsgFlags = map[int]string{
5 | SUCCESS: "成功",
6 | ERROR: "服务器出现错误",
7 | NOT_FOUND: "没有找到请求",
8 | INVALID_PARAMS: "请求参数异常",
9 | NOT_PROMISE: "鉴权失败",
10 | Method_FAILS: "Method 请求错误",
11 | ERROR_AUTH_CHECK_TOKEN_FAIL: "Token 检查异常",
12 | ERROR_AUTH_CHECK_TOKEN_TIMEOUT: "Token 超时了",
13 | ERROR_AUTH_TOKEN: "Token 创建异常",
14 | ERROR_AUTH: "认证失败",
15 |
16 | ERROR_NO_CONNEC: "链接目标Redis异常",
17 | ERROR_CLOUD_CONNECT: "链接云服务异常",
18 | ERROR_CLOUD_GET: "获取云资源异常",
19 | ERROR_WRITE_MYSQL: "数据库操作异常",
20 | ERROR_BACKGROUND: "后台加载异常,请联系管理员",
21 | WARN_NO_USE: "功能会造成慢查询,暂时下线",
22 | WARN_CLICK_REPEATEDLY: "兄弟,你点的太快了,上一个还没结束,等一下哈!",
23 | WARN_BACKGROUND: "没有找到这些数据,后台已经再加载,请稍后重试一下。",
24 | WARN_NOT_FOUND_CLOUD: "暂时不支持这个云操作",
25 | WARN_NOT_PROMISE_RULE: "没有权限,请找管理员开放",
26 | WARN_USER_NAME_EXIST: "用户名已经存在",
27 | WARN_USER_MAIL_EXIST: "用户邮箱已经存在",
28 | WARN_USER_PASSWORD_CHECK: "旧密码不对",
29 | WARN_CODIS_NOT_CONNECT: "获取codis信息失败",
30 | WARN_CODIS_IS_REBALANCE: "codis正在rebalance",
31 | WARN_CODIS_NOT_OPTION: "没有这个codis操作",
32 | WARN_CODIS_PROXY_MIN_NUMBER: "codis的proxy最小是2个,不能再少了",
33 | WARN_CODIS_GROUP_MIN_NUMBER: "codis的group最小是1个,不能再少了",
34 | WARN_CODIS_GROUP_MIN_CAPACITY: "剩余codis的group容量不足80%了",
35 | WARN_CHECK_IPPORT_FAIL: "IP和端口健康检查失败",
36 | }
37 |
38 | func GetMsg(code int) string {
39 | msg, ok := MsgFlags[code]
40 | if ok {
41 | return msg
42 | }
43 |
44 | return MsgFlags[ERROR]
45 | }
46 |
--------------------------------------------------------------------------------
/src/middleware/alicloud/alist.go:
--------------------------------------------------------------------------------
1 | package alicloud
2 |
3 | import (
4 | r_kvstore20150101 "github.com/alibabacloud-go/r-kvstore-20150101/v3/client"
5 | util "github.com/alibabacloud-go/tea-utils/v2/service"
6 | "github.com/alibabacloud-go/tea/tea"
7 | "github.com/iguidao/redis-manager/src/middleware/logger"
8 | )
9 |
10 | func AliListRegion() (string, bool) {
11 | describeRegionsRequest := &r_kvstore20150101.DescribeRegionsRequest{}
12 | runtime := &util.RuntimeOptions{}
13 | response, err := AliRedisApi.DescribeRegionsWithOptions(describeRegionsRequest, runtime)
14 | if err != nil {
15 | logger.Error("Get ali region error: ", err)
16 | return "", false
17 | }
18 | return response.Body.GoString(), true
19 | }
20 |
21 | func AliListRedis(region string) (string, bool) {
22 | describeInstancesRequest := &r_kvstore20150101.DescribeInstancesRequest{
23 | RegionId: tea.String(region),
24 | }
25 | runtime := &util.RuntimeOptions{}
26 | response, err := AliRedisApi.DescribeInstancesWithOptions(describeInstancesRequest, runtime)
27 | if err != nil {
28 | logger.Error("Get ali region error: ", err)
29 | return "", false
30 | }
31 | return response.Body.GoString(), true
32 | }
33 |
--------------------------------------------------------------------------------
/src/middleware/alicloud/connect.go:
--------------------------------------------------------------------------------
1 | package alicloud
2 |
3 | import (
4 | openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
5 | r_kvstore20150101 "github.com/alibabacloud-go/r-kvstore-20150101/v3/client"
6 | "github.com/alibabacloud-go/tea/tea"
7 | "github.com/iguidao/redis-manager/src/middleware/logger"
8 | "github.com/iguidao/redis-manager/src/middleware/model"
9 | "github.com/iguidao/redis-manager/src/middleware/mysql"
10 | )
11 |
12 | var AliRedisApi *r_kvstore20150101.Client
13 |
14 | func CreateClient(accessKeyId *string, accessKeySecret *string) (_result *r_kvstore20150101.Client, _err error) {
15 | config := &openapi.Config{
16 | // 必填,您的 AccessKey ID
17 | AccessKeyId: accessKeyId,
18 | // 必填,您的 AccessKey Secret
19 | AccessKeySecret: accessKeySecret,
20 | }
21 |
22 | // 访问的域名
23 | config.Endpoint = tea.String(mysql.DB.GetOneCfgValue(model.ALIAPIURL))
24 | _result = &r_kvstore20150101.Client{}
25 | _result, _err = r_kvstore20150101.NewClient(config)
26 | return _result, _err
27 | }
28 |
29 | func AliRedisContent() bool {
30 | var err error
31 | // 工程代码泄露可能会导致AccessKey泄露,并威胁账号下所有资源的安全性。以下代码示例仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378661.html
32 | AliRedisApi, err = CreateClient(tea.String(mysql.DB.GetOneCfgValue(model.ALIACCESSKEYID)), tea.String(mysql.DB.GetOneCfgValue(model.ALIALIACCESSKEYSECRET)))
33 | if err != nil {
34 | logger.Error("conenct ali cloud redis error: ", err)
35 | return false
36 | }
37 | return true
38 | }
39 |
--------------------------------------------------------------------------------
/src/middleware/casbin/casbinstruct.go:
--------------------------------------------------------------------------------
1 | package casbin
2 |
3 | type CasbinRule struct {
4 | ID int `gorm:"primary_key"`
5 | Ptype string `gorm:"size:32;uniqueIndex:unique_index"`
6 | V0 string `gorm:"size:64;uniqueIndex:unique_index"`
7 | V1 string `gorm:"size:512;uniqueIndex:unique_index"`
8 | V2 string `gorm:"size:16;uniqueIndex:unique_index"`
9 | V3 string `gorm:"size:32;uniqueIndex:unique_index"`
10 | V4 string `gorm:"size:32;uniqueIndex:unique_index"`
11 | V5 string `gorm:"size:32;uniqueIndex:unique_index"`
12 | }
13 |
14 | func (CasbinRule) TableName() string {
15 | return "casbin_rule"
16 | }
17 |
--------------------------------------------------------------------------------
/src/middleware/casbin/initial.go:
--------------------------------------------------------------------------------
1 | package casbin
2 |
3 | import (
4 | "github.com/casbin/casbin/v2"
5 | cmodel "github.com/casbin/casbin/v2/model"
6 | gormadapter "github.com/casbin/gorm-adapter/v3"
7 | _ "github.com/go-sql-driver/mysql"
8 | "github.com/iguidao/redis-manager/src/middleware/logger"
9 | rmysql "github.com/iguidao/redis-manager/src/middleware/mysql"
10 | )
11 |
12 | var Enforcer *casbin.Enforcer
13 |
14 | func Connect() {
15 | var err error
16 | a, _ := gormadapter.NewAdapterByDBWithCustomTable(rmysql.DB.DB, &CasbinRule{})
17 | // a := gormadapter.NewAdapterByDB(db)
18 | m, err := cmodel.NewModelFromString(`
19 | [request_definition]
20 | r = sub, obj, act
21 |
22 | [policy_definition]
23 | p = sub, obj, act
24 |
25 | [policy_effect]
26 | e = some(where (p.eft == allow))
27 |
28 | [matchers]
29 | m = r.sub == p.sub && keyMatch(r.obj, p.obj) && regexMatch(r.act, p.act) || r.sub == "admin"
30 | `)
31 | if err != nil {
32 | logger.Error("error: model: ", err)
33 | }
34 |
35 | Enforcer, err = casbin.NewEnforcer(m, a)
36 | if err != nil {
37 | logger.Error("error: enforcer: ", err)
38 | }
39 | // 开启权限认证日志
40 | Enforcer.EnableLog(true)
41 | // Load the policy from DB.
42 | err = Enforcer.LoadPolicy()
43 | if err != nil {
44 | logger.Error("loadPolicy error")
45 | panic(err)
46 | }
47 | }
48 |
49 | // // DB as the mysql client
50 | // var DB *gorm.DB
51 |
52 | // // Connect create db connection
53 |
54 | // func Connect(dsn string) {
55 | // // Increase the column size to 512.
56 |
57 | // // db, _ := gorm.Open(...)
58 | // DB, err := gorm.Open(mysql.New(mysql.Config{
59 | // // DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
60 | // DSN: dsn,
61 | // DefaultStringSize: 512, // string 类型字段的默认长度
62 | // DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
63 | // DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
64 | // DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
65 | // SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
66 | // }), &gorm.Config{})
67 | // if err != nil {
68 | // logger.Error("Cannot open mysql database: ", err.Error())
69 | // panic(err)
70 | // }
71 | // // Initialize a Gorm adapter and use it in a Casbin enforcer:
72 | // // The adapter will use an existing gorm.DB instnace.
73 | // // a, _ := gormadapter.NewAdapterByDBWithCustomTable(db, &CasbinRule{})
74 | // a, _ := gormadapter.NewAdapterByDBWithCustomTable(DB, &CasbinRule{})
75 | // // a := gormadapter.NewAdapterByDB(db)
76 | // m, err := model.NewModelFromString(`
77 | // [request_definition]
78 | // r = sub, obj, act
79 |
80 | // [policy_definition]
81 | // p = sub, obj, act
82 |
83 | // [policy_effect]
84 | // e = some(where (p.eft == allow))
85 |
86 | // [matchers]
87 | // m = r.sub == p.sub && r.obj == p.obj && r.act == p.act || r.sub == "iguidao"
88 | // `)
89 | // if err != nil {
90 | // logger.Error("error: model: ", err)
91 | // }
92 |
93 | // Enforcer, err = casbin.NewEnforcer(m, a)
94 | // if err != nil {
95 | // logger.Error("error: enforcer: ", err)
96 | // }
97 | // // 开启权限认证日志
98 | // Enforcer.EnableLog(true)
99 | // // Load the policy from DB.
100 | // err = Enforcer.LoadPolicy()
101 | // if err != nil {
102 | // logger.Error("loadPolicy error")
103 | // panic(err)
104 | // }
105 |
106 | // }
107 |
--------------------------------------------------------------------------------
/src/middleware/casbin/ruleop.go:
--------------------------------------------------------------------------------
1 | package casbin
2 |
3 | import (
4 | "github.com/iguidao/redis-manager/src/middleware/logger"
5 | "github.com/iguidao/redis-manager/src/middleware/mysql"
6 | )
7 |
8 | func RuleCheck(identity, path, method string) bool {
9 | // 简单认证
10 | res, err := Enforcer.Enforce(identity, path, method)
11 | if err != nil {
12 | return false
13 | }
14 | return res
15 | }
16 |
17 | func RuleAdd(identity, path, method string) bool {
18 | res, err := Enforcer.AddPolicy(identity, path, method)
19 | if err != nil {
20 | logger.Error("create new rule error ", err)
21 | return false
22 | }
23 | return res
24 | }
25 |
26 | func RuleDel(identity, path, method string) bool {
27 | res, err := Enforcer.RemovePolicy(identity, path, method)
28 | if err != nil {
29 | return false
30 | }
31 | return res
32 | }
33 |
34 | func RuleGet() (casbinrule []CasbinRule) {
35 | mysql.DB.DB.Find(&casbinrule)
36 | // mysql.DB.DB.Offset(page).Limit(size).Find(&casbinrule)
37 | return
38 | }
39 |
--------------------------------------------------------------------------------
/src/middleware/cluster/cluster.go:
--------------------------------------------------------------------------------
1 | package cluster
2 |
3 | import (
4 | "strconv"
5 | "strings"
6 |
7 | "github.com/iguidao/redis-manager/src/middleware/logger"
8 | "github.com/iguidao/redis-manager/src/middleware/mysql"
9 | )
10 |
11 | func WriteCluster(clusterid int, v string) {
12 | var flags string
13 | var masterid string
14 | var linkstate string
15 | var slotrange string
16 | var slotnumber int
17 | nodelist := strings.Split(v, " ")
18 | nodeid := nodelist[0]
19 | nodeaddress := strings.Split(nodelist[1], "@")
20 | ip := strings.Split(nodeaddress[0], ":")[0]
21 | pord := strings.Split(nodeaddress[0], ":")[1]
22 | flags_info := nodelist[2]
23 | flags_list := strings.Split(flags_info, ",")
24 | if len(flags_list) == 1 {
25 | if flags_list[0] == "master" {
26 | flags = "master"
27 | linkstate = nodelist[7]
28 | slotrange = nodelist[8]
29 | slotnumber = getslotnumber(slotrange)
30 | } else {
31 | flags = "slave"
32 | masterid = nodelist[3]
33 | }
34 | } else {
35 | flags = flags_list[1]
36 | if flags == "master" {
37 | linkstate = nodelist[7]
38 | slotrange = nodelist[8]
39 | slotnumber = getslotnumber(slotrange)
40 | } else {
41 | masterid = nodelist[3]
42 | }
43 | }
44 | id, ok := mysql.DB.AddClusterNode(nodeid, ip, pord, flags, masterid, linkstate, slotrange, clusterid, slotnumber)
45 | if ok {
46 | logger.Info("write ", ip, " redis to mysql ok: ", id, " nodeid: ", nodeid)
47 | } else {
48 | logger.Error("write ", ip, " redis to mysql false: ", id, " nodeid: ", nodeid)
49 | }
50 | }
51 |
52 | func getslotnumber(slotrange string) int {
53 | var err error
54 | var start, end, slot int
55 | num := strings.Split(slotrange, "-")
56 | start, err = strconv.Atoi(num[0])
57 | if err != nil {
58 | return slot
59 | }
60 | end, err = strconv.Atoi(num[1])
61 | if err != nil {
62 | return slot
63 | }
64 | slot = end - start + 1
65 | return slot
66 | }
67 |
--------------------------------------------------------------------------------
/src/middleware/cluster/nodes.go:
--------------------------------------------------------------------------------
1 | package cluster
2 |
3 | import (
4 | "github.com/iguidao/redis-manager/src/middleware/model"
5 | "github.com/iguidao/redis-manager/src/middleware/mysql"
6 | )
7 |
8 | func ClusterConvergeTree(node mysql.ClusterNode) *model.ClusterNodeTables {
9 | root := &model.ClusterNodeTables{
10 | CreateTime: node.CreatedAt,
11 | CluserId: node.CluserId,
12 | Flags: node.Flags,
13 | Address: node.Ip + ":" + node.Port,
14 | LinkState: node.LinkState,
15 | RunStatus: node.RunStatus,
16 | NodeId: node.NodeId,
17 | SlotNumber: node.SlotNumber,
18 | SlotRange: node.SlotRange,
19 | }
20 | return root
21 | }
22 | func ClusterUpdateTree(mlist []*model.ClusterNodeTables, node mysql.ClusterNode) []*model.ClusterNodeTables {
23 | var res []*model.ClusterNodeTables
24 | for _, v := range mlist {
25 | if v.NodeId == node.MasterId {
26 | v.Children = append(v.Children, ClusterConvergeTree(node))
27 | res = append(res, v)
28 | } else {
29 | res = append(res, v)
30 | }
31 | }
32 | return res
33 | }
34 |
--------------------------------------------------------------------------------
/src/middleware/cosop/cosop.go:
--------------------------------------------------------------------------------
1 | package cosop
2 |
3 | import (
4 | "context"
5 | "net/http"
6 | "net/url"
7 | "os"
8 |
9 | "github.com/iguidao/redis-manager/src/middleware/logger"
10 | "github.com/iguidao/redis-manager/src/middleware/model"
11 | "github.com/iguidao/redis-manager/src/middleware/mysql"
12 | "github.com/tencentyun/cos-go-sdk-v5"
13 | )
14 |
15 | const (
16 | DefalutDisplayCount = 20
17 | )
18 |
19 | func CosGet(srcname, dstname string) bool {
20 | AccessKey := mysql.DB.GetOneCfgValue(model.TXCOSACCESSKEY)
21 | AccessKeyID := mysql.DB.GetOneCfgValue(model.TXCOSACCESSKEYID)
22 | EndpointPub := mysql.DB.GetOneCfgValue(model.TXCOSENDPOINTPUB)
23 | u, _ := url.Parse(EndpointPub)
24 | b := &cos.BaseURL{BucketURL: u}
25 |
26 | client := cos.NewClient(b, &http.Client{
27 | Transport: &cos.AuthorizationTransport{
28 | SecretID: AccessKeyID,
29 | SecretKey: AccessKey,
30 | },
31 | })
32 | if _, err := os.Stat(dstname); err == nil {
33 | logger.Error("Not download because file exists: ", dstname)
34 | return false
35 | }
36 | rsp, err := client.Object.Head(context.Background(), srcname, nil)
37 |
38 | if err != nil {
39 | if rsp.StatusCode == 404 {
40 | logger.Error("file not found on cos...")
41 | } else {
42 | logger.Error("get object meta data failed ,err:", err.Error())
43 | }
44 | return false
45 | }
46 | rsp, err = client.Object.GetToFile(context.Background(), srcname, dstname, nil)
47 | if err != nil {
48 | if rsp.StatusCode == 404 {
49 | logger.Error("download error,file not found,error:", err)
50 | } else {
51 | logger.Error("download error,", err)
52 | }
53 | return false
54 | }
55 | return true
56 | }
57 |
--------------------------------------------------------------------------------
/src/middleware/httpapi/apistruct.go:
--------------------------------------------------------------------------------
1 | package httpapi
2 |
--------------------------------------------------------------------------------
/src/middleware/httpapi/httpapi.go:
--------------------------------------------------------------------------------
1 | package httpapi
2 |
3 | import (
4 | "bytes"
5 | "io/ioutil"
6 | "net/http"
7 | "time"
8 | )
9 |
10 | func PostJson(url string, jsonStr []byte, HeaderData map[string]string) (bool, string) {
11 |
12 | req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
13 |
14 | if HeaderData != nil {
15 | for headerkey, headervalue := range HeaderData {
16 | // req.Header.Set("Content-Type", "application/json")
17 | req.Header.Set(headerkey, headervalue)
18 | }
19 | }
20 | client := &http.Client{Timeout: 10000 * time.Millisecond}
21 | resp, err := client.Do(req)
22 | if err != nil {
23 | errinfo := "Post请求失败:" + err.Error()
24 | return false, errinfo
25 | }
26 | if resp.StatusCode != 200 {
27 | errinfo := "Get请求失败,状态码:" + resp.Status
28 | return false, errinfo
29 | }
30 | defer resp.Body.Close()
31 |
32 | // statuscode := resp.StatusCode
33 | // hea := resp.Header
34 | body, _ := ioutil.ReadAll(resp.Body)
35 | return true, string(body)
36 |
37 | }
38 |
39 | func GetDefault(url string, UriData map[string]string, HeaderData map[string]string) (bool, string) {
40 | req, _ := http.NewRequest("GET", url, nil)
41 | if HeaderData != nil {
42 | for headerkey, headervalue := range HeaderData {
43 | // req.Header.Set("Content-Type", "application/json")
44 | req.Header.Set(headerkey, headervalue)
45 | }
46 | }
47 | if UriData != nil {
48 | q := req.URL.Query()
49 | for urikey, urivalue := range UriData {
50 | q.Add(urikey, urivalue)
51 | }
52 | req.URL.RawQuery = q.Encode()
53 | }
54 |
55 | client := &http.Client{Timeout: 10000 * time.Millisecond}
56 | resp, err := client.Do(req)
57 | if err != nil {
58 | errinfo := "Get请求失败" + err.Error()
59 | return false, errinfo
60 | }
61 | if resp.StatusCode != 200 {
62 | errinfo := "Get请求失败,状态码:" + resp.Status
63 | return false, errinfo
64 | }
65 | defer resp.Body.Close()
66 | // statuscode := resp.StatusCode
67 | // hea := resp.Header
68 | body, _ := ioutil.ReadAll(resp.Body)
69 | return true, string(body)
70 | }
71 | func PutDefault(url string, UriData map[string]string, HeaderData map[string]string) (bool, string) {
72 | req, _ := http.NewRequest("PUT", url, nil)
73 | if HeaderData != nil {
74 | for headerkey, headervalue := range HeaderData {
75 | // req.Header.Set("Content-Type", "application/json")
76 | req.Header.Set(headerkey, headervalue)
77 | }
78 | }
79 | if UriData != nil {
80 | q := req.URL.Query()
81 | for urikey, urivalue := range UriData {
82 | q.Add(urikey, urivalue)
83 | }
84 | req.URL.RawQuery = q.Encode()
85 | }
86 |
87 | client := &http.Client{Timeout: 10000 * time.Millisecond}
88 | resp, err := client.Do(req)
89 | if err != nil {
90 | errinfo := "Put请求失败" + err.Error()
91 | return false, errinfo
92 | }
93 | if resp.StatusCode != 200 {
94 | errinfo := "Put请求失败,状态码:" + resp.Status
95 | return false, errinfo
96 | }
97 | defer resp.Body.Close()
98 | // statuscode := resp.StatusCode
99 | // hea := resp.Header
100 | body, _ := ioutil.ReadAll(resp.Body)
101 | return true, string(body)
102 | }
103 |
104 | func DeleteDefault(url string, UriData map[string]string, HeaderData map[string]string) (bool, string) {
105 | req, _ := http.NewRequest("DELETE", url, nil)
106 | if HeaderData != nil {
107 | for headerkey, headervalue := range HeaderData {
108 | // req.Header.Set("Content-Type", "application/json")
109 | req.Header.Set(headerkey, headervalue)
110 | }
111 | }
112 | if UriData != nil {
113 | q := req.URL.Query()
114 | for urikey, urivalue := range UriData {
115 | q.Add(urikey, urivalue)
116 | }
117 | req.URL.RawQuery = q.Encode()
118 | }
119 |
120 | client := &http.Client{Timeout: 10000 * time.Millisecond}
121 | resp, err := client.Do(req)
122 | if err != nil {
123 | errinfo := "DELETE请求失败" + err.Error()
124 | return false, errinfo
125 | }
126 | if resp.StatusCode != 200 {
127 | errinfo := "DELETE请求失败,状态码:" + resp.Status
128 | return false, errinfo
129 | }
130 | defer resp.Body.Close()
131 | // statuscode := resp.StatusCode
132 | // hea := resp.Header
133 | body, _ := ioutil.ReadAll(resp.Body)
134 | return true, string(body)
135 | }
136 |
--------------------------------------------------------------------------------
/src/middleware/jwt/jwt.go:
--------------------------------------------------------------------------------
1 | package jwt
2 |
3 | import (
4 | "net/http"
5 | "strings"
6 | "time"
7 |
8 | "github.com/iguidao/redis-manager/src/hsc"
9 | "github.com/iguidao/redis-manager/src/middleware/casbin"
10 | "github.com/iguidao/redis-manager/src/middleware/util"
11 |
12 | "github.com/gin-gonic/gin"
13 | )
14 |
15 | func JWT() gin.HandlerFunc {
16 | return func(c *gin.Context) {
17 | var code int
18 | var token string
19 | var username string
20 | var usertype string
21 | var userid int
22 | Result := make(map[string]interface{})
23 | code = hsc.SUCCESS
24 | urlpath := c.Request.URL.Path
25 | method := c.Request.Method
26 | auth := c.Request.Header.Get("Authorization")
27 | if strings.Contains(auth, "Bearer ") {
28 | token = strings.Split(auth, "Bearer ")[1]
29 | }
30 | if token == "" {
31 | Result["result"] = "没有Authorization"
32 | code = hsc.NO_LOGIN
33 | } else {
34 | claims, err := util.ParseToken(token)
35 | if err != nil {
36 | Result["result"] = "Token鉴权失败"
37 | code = hsc.ERROR_AUTH_CHECK_TOKEN_FAIL
38 | } else if time.Now().Unix() > claims.ExpiresAt {
39 | Result["result"] = "Token已超时"
40 | code = hsc.ERROR_AUTH_CHECK_TOKEN_TIMEOUT
41 | } else {
42 | result := casbin.RuleCheck(claims.UserType, urlpath, method)
43 | if !result {
44 | code = hsc.WARN_NOT_PROMISE_RULE
45 | Result["result"] = "权限不够呀,找管理员开下权限!"
46 | } else {
47 | username = claims.UserName
48 | usertype = claims.UserType
49 | userid = claims.UserId
50 | }
51 | }
52 | }
53 | if code == hsc.WARN_NOT_PROMISE_RULE {
54 | c.JSON(http.StatusOK, gin.H{
55 | "errorCode": code,
56 | "msg": hsc.GetMsg(code),
57 | "data": Result,
58 | })
59 | c.Abort()
60 | return
61 | }
62 | if code != hsc.SUCCESS {
63 | c.JSON(http.StatusUnauthorized, gin.H{
64 | "errorCode": code,
65 | "msg": hsc.GetMsg(code),
66 | "data": Result,
67 | })
68 | c.Abort()
69 | return
70 | }
71 | c.Set("UserName", username)
72 | c.Set("UserType", usertype)
73 | c.Set("UserId", userid)
74 | c.Next()
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/middleware/logger/logger.go:
--------------------------------------------------------------------------------
1 | package logger
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "strings"
7 |
8 | "github.com/iguidao/redis-manager/src/cfg"
9 |
10 | "go.uber.org/zap"
11 | "go.uber.org/zap/zapcore"
12 | "gopkg.in/natefinch/lumberjack.v2"
13 | )
14 |
15 | // error logger
16 | var ErrorLogger *zap.SugaredLogger
17 |
18 | func SetupLogger() *zap.SugaredLogger {
19 | err := os.Mkdir("./logs/", os.ModePerm)
20 | if err != nil && !strings.Contains(err.Error(), "exists") {
21 | fmt.Println("create logs dir err, ", err.Error())
22 | }
23 | fileName := "./logs/" + cfg.Get_Info_String("logapppath")
24 | syncWriter := zapcore.AddSync(&lumberjack.Logger{
25 | Filename: fileName,
26 | MaxSize: 1024,
27 | LocalTime: true,
28 | Compress: true,
29 | })
30 | encoder := zap.NewDevelopmentEncoderConfig()
31 | encoder.EncodeTime = zapcore.ISO8601TimeEncoder
32 |
33 | var level zapcore.Level
34 | level = zap.DebugLevel
35 | core := zapcore.NewCore(
36 | zapcore.NewConsoleEncoder(encoder),
37 | zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout),
38 | syncWriter),
39 | level,
40 | )
41 |
42 | logger := zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1))
43 | ErrorLogger = logger.Sugar()
44 | return ErrorLogger
45 | }
46 |
47 | func Debug(args ...interface{}) {
48 | ErrorLogger.Debug(args...)
49 | }
50 |
51 | func Debugf(template string, args ...interface{}) {
52 | ErrorLogger.Debugf(template, args...)
53 | }
54 |
55 | func Info(args ...interface{}) {
56 | ErrorLogger.Info(args...)
57 | }
58 |
59 | func Infof(template string, args ...interface{}) {
60 | ErrorLogger.Infof(template, args...)
61 | }
62 |
63 | func Warn(args ...interface{}) {
64 | ErrorLogger.Warn(args...)
65 | }
66 |
67 | func Warnf(template string, args ...interface{}) {
68 | ErrorLogger.Warnf(template, args...)
69 | }
70 |
71 | func Error(args ...interface{}) {
72 | ErrorLogger.Error(args...)
73 | }
74 |
75 | func Errorf(template string, args ...interface{}) {
76 | ErrorLogger.Errorf(template, args...)
77 | }
78 |
79 | func DPanic(args ...interface{}) {
80 | ErrorLogger.DPanic(args...)
81 | }
82 |
83 | func DPanicf(template string, args ...interface{}) {
84 | ErrorLogger.DPanicf(template, args...)
85 | }
86 |
87 | func Panic(args ...interface{}) {
88 | ErrorLogger.Panic(args...)
89 | }
90 |
91 | func Panicf(template string, args ...interface{}) {
92 | ErrorLogger.Panicf(template, args...)
93 | }
94 |
95 | func Fatal(args ...interface{}) {
96 | ErrorLogger.Fatal(args...)
97 | }
98 |
99 | func Fatalf(template string, args ...interface{}) {
100 | ErrorLogger.Fatalf(template, args...)
101 | }
102 |
--------------------------------------------------------------------------------
/src/middleware/model/aliresult.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | // Region ali result
4 | type AliRegion struct {
5 | RequestId string `json:"RequestId"`
6 | RegionIds AliRegionRegionIds `json:"RegionIds"`
7 | }
8 | type AliRegionRegionIds struct {
9 | KVStoreRegion []AliRegionRegionIdsKVStoreRegion `json:"KVStoreRegion"`
10 | }
11 | type AliRegionRegionIdsKVStoreRegion struct {
12 | RegionId string `json:"RegionId"`
13 | RegionEndpoint string `json:"RegionEndpoint"`
14 | LocalName string `json:"LocalName"`
15 | }
16 |
17 | // redis ali result
18 | type AliRedis struct {
19 | RequestId string `json:"RequestId"`
20 | TotalCount int `json:"TotalCount"`
21 | PageSize int `json:"PageSize"`
22 | PageNumber int `json:"PageNumber"`
23 | Instances AliRedisInstances `json:"RegionIds"`
24 | }
25 | type AliRedisInstances struct {
26 | KVStoreInstance []AliRedisInstancesKVStoreInstance `json:"KVStoreInstance"`
27 | }
28 | type AliRedisInstancesKVStoreInstance struct {
29 | Connections int `json:"Connections"`
30 | EndTime string `json:"EndTime"`
31 | ResourceGroupId string `json:"ResourceGroupId"`
32 | EditionType string `json:"EditionType"`
33 | Config string `json:"Config"`
34 | Port int `json:"Port"`
35 | GlobalInstanceId string `json:"GlobalInstanceId"`
36 | HasRenewChangeOrder string `json:"HasRenewChangeOrder"`
37 | ConnectionDomain string `json:"ConnectionDomain"`
38 | Capacity int `json:"Capacity"`
39 | QPS int `json:"QPS"`
40 | NetworkType string `json:"NetworkType"`
41 | InstanceStatus string `json:"InstanceStatus"`
42 | PackageType string `json:"PackageType"`
43 | Bandwidth int `json:"Bandwidth"`
44 | InstanceType string `json:"InstanceType"`
45 | ArchitectureType string `json:"ArchitectureType"`
46 | EngineVersion string `json:"EngineVersion"`
47 | UserName string `json:"UserName"`
48 | ZoneId string `json:"ZoneId"`
49 | InstanceId string `json:"InstanceId"`
50 | CreateTime string `json:"CreateTime"`
51 | VSwitchId string `json:"VSwitchId"`
52 | InstanceClass string `json:"InstanceClass"`
53 | IsRds bool `json:"IsRds"`
54 | InstanceName string `json:"InstanceName"`
55 | VpcId string `json:"VpcId"`
56 | ChargeType string `json:"ChargeType"`
57 | NodeType string `json:"NodeType"`
58 | RegionId string `json:"RegionId"`
59 | ShardCount int `json:"ShardCount"`
60 | }
61 |
--------------------------------------------------------------------------------
/src/middleware/model/cfg.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | var (
4 | DefaultName = make(map[string]string)
5 | CC = "custom_config" // 自行配置的key
6 | CN = "用户自定义默认key" // 自定义key备注note
7 | TXSECRETID = "tx_secretid" // 腾讯SECRETID,账号需要开启[QCloudFinanceFullAccess、QcloudRedisFullAccess、QcloudMonitorFullAccess、QcloudDBBRAINFullAccess]权限
8 | TXSECRETKEY = "tx_secretkey" // 腾讯SECRETKEY
9 | TXAPIURL = "tx_redis_api_url" // 腾讯APIURL
10 | TXCOSACCESSKEY = "tx_cos_accesskey" // 腾讯COS的ACCESSKEY
11 | TXCOSACCESSKEYID = "tx_cos_accesskeyid" // 腾讯COS的ACCESSKEYID
12 | TXCOSENDPOINTPUB = "tx_cos_endpointpub" // 腾讯COS的ENDPOINTPUB
13 | ALIAPIURL = "ali_redis_api_url" // 阿里PIURL
14 | ALIACCESSKEYID = "ali_accesskeyid" // 阿里accessKeyId
15 | ALIALIACCESSKEYSECRET = "ali_accesskeysecret" // 阿里accessKeySecret
16 | BGSAVECOMMAND = "redis_bgsave" // bgsave命令的别名
17 | CLOUDREFRESH = "cloud_refresh" // 云redis定时更新时间,使用cron格式
18 | BOARDCODIS = "board_codis" // 是否启动自建codis
19 | BOARDTXREDIS = "board_txredis" // 是否启动腾讯redis
20 | BOARDALIREDIS = "board_aliredis" // 是否启动阿里redis
21 | BOARDCLUSTER = "board_cluster" // 是否启动自建redis
22 | CfgDefault = [...]string{TXSECRETID, TXSECRETKEY, TXAPIURL, TXCOSACCESSKEY, TXCOSACCESSKEYID, TXCOSENDPOINTPUB} // 默认key列表
23 | )
24 |
25 | func init() {
26 | DefaultName[TXSECRETID] = "腾讯SECRETID"
27 | DefaultName[TXSECRETKEY] = "腾讯TXSECRETKEY"
28 | DefaultName[TXAPIURL] = "腾讯REDIS的APIURL"
29 | DefaultName[TXCOSACCESSKEY] = "腾讯COS的ACCESSKEY"
30 | DefaultName[TXCOSACCESSKEYID] = "腾讯COS的ACCESSKEYID"
31 | DefaultName[TXCOSENDPOINTPUB] = "腾讯COS的ENDPOINTPUB"
32 | DefaultName[BGSAVECOMMAND] = "Redis命令bgsave别名"
33 | DefaultName[CLOUDREFRESH] = "云redis定时更新时间"
34 | DefaultName[ALIAPIURL] = "阿里REDIS的APIURL"
35 | DefaultName[ALIACCESSKEYID] = "阿里accessKeyId"
36 | DefaultName[ALIALIACCESSKEYSECRET] = "阿里accessKeySecret"
37 | }
38 |
--------------------------------------------------------------------------------
/src/middleware/model/clusterresult.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "time"
4 |
5 | type ClusterNodeTables struct {
6 | CreateTime time.Time
7 | CluserId int
8 | NodeId string
9 | Address string
10 | Flags string
11 | LinkState string
12 | RunStatus bool
13 | SlotRange string
14 | SlotNumber int
15 | Children []*ClusterNodeTables
16 | }
17 |
--------------------------------------------------------------------------------
/src/middleware/model/codisv1.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | // codis的proxy和server操作
4 | type CodisChangeNode struct {
5 | Curl string `json:"curl"`
6 | ClusterName string `json:"cluster_name"`
7 | AddProxy string `json:"add_proxy"`
8 | AddServer string `json:"add_server"`
9 | DelProxy int `json:"del_proxy"`
10 | DelGroup int `json:"del_group"`
11 | OpType string `json:"op_type"`
12 | }
13 |
--------------------------------------------------------------------------------
/src/middleware/model/path.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | var (
4 | DefaultPath = make(map[string]string)
5 | PATHCFG = "/redis-manager/cfg/v1"
6 | PATHBOARD = "/redis-manager/board/v1"
7 | PATHHISTORY = "/redis-manager/ophistory/v1"
8 | PATHCODIS = "/redis-manager/codis/v1"
9 | PATHCLOUD = "/redis-manager/cloud/v1"
10 | PATHCLUSTER = "/redis-manager/cluster/v1"
11 | PATHCLI = "/redis-manager/cli/v1"
12 | PATHUSER = "/redis-manager/user/v1"
13 | PATHRULE = "/redis-manager/rule/v1"
14 | PATHAUTH = "/redis-manager/auth/v1"
15 | DefaultMethod = make(map[string]string)
16 | METHODGET = "GET"
17 | METHODPOST = "POST"
18 | METHODDELETE = "DELETE"
19 | METHODPUT = "PUT"
20 | )
21 |
22 | func init() {
23 | DefaultPath[PATHCFG+"/*"] = "系统配置页面权限"
24 | DefaultPath[PATHBOARD+"/*"] = "概览页面权限"
25 | DefaultPath[PATHHISTORY+"/*"] = "历史记录页面权限"
26 | DefaultPath[PATHCODIS+"/*"] = "Redis集群/Codis页面权限"
27 | DefaultPath[PATHCLOUD+"/*"] = "Redis集群/腾讯和阿里Redis页面权限"
28 | DefaultPath[PATHCLUSTER+"/*"] = "Redis集群/Cluster页面权限"
29 | DefaultPath[PATHCLI+"/*"] = "数据查询页面权限"
30 | DefaultPath[PATHUSER+"/*"] = "用户管理/用户列表页面权限"
31 | DefaultPath[PATHRULE+"/*"] = "用户管理/权限管理页面权限"
32 | DefaultPath[PATHAUTH+"/*"] = "用户修改密码权限"
33 | DefaultMethod[METHODGET] = "读权限"
34 | DefaultMethod[METHODPOST] = "写权限"
35 | DefaultMethod[METHODDELETE] = "删除权限"
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/middleware/model/user.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | var (
4 | DefaultUser = make(map[string]string)
5 | USERTYPEADMIN = "admin" //管理员身份
6 | USERTYPEVISITOR = "visitor" //访客身份
7 | USERTYPEMEMBER = "member" //会员身份
8 | UserDefault = [...]string{USERTYPEADMIN, USERTYPEVISITOR, USERTYPEMEMBER} //列出员工身份
9 | )
10 |
11 | func init() {
12 | DefaultUser[USERTYPEADMIN] = "管理员"
13 | DefaultUser[USERTYPEVISITOR] = "访客"
14 | DefaultUser[USERTYPEMEMBER] = "会员"
15 | }
16 |
--------------------------------------------------------------------------------
/src/middleware/mysql/history.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import "github.com/iguidao/redis-manager/src/middleware/logger"
4 |
5 | func (m *MySQL) GetAllHistory() []OpHistory {
6 | var ophistory []OpHistory
7 | m.Find(&ophistory)
8 | return ophistory
9 | }
10 |
11 | // add cluster
12 | func (m *MySQL) AddHistory(userid int, opinfo, opparams string) (int, bool) {
13 | addcluster := &OpHistory{
14 | UserId: userid,
15 | OpInfo: opinfo,
16 | OpParams: opparams,
17 | }
18 | result := m.Create(&addcluster)
19 | if result.Error != nil {
20 | logger.Error("Mysql add history error:", result.Error)
21 | return 0, false
22 | }
23 | return addcluster.ID, true
24 | // return gdarticle.ID.String(), true
25 | }
26 |
--------------------------------------------------------------------------------
/src/middleware/mysql/initial.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import (
4 | "github.com/iguidao/redis-manager/src/middleware/logger"
5 | "gorm.io/driver/mysql"
6 | "gorm.io/gorm"
7 | )
8 |
9 | // MySQL refrence a mysql db
10 | type MySQL struct {
11 | *gorm.DB
12 | }
13 |
14 | // DB as the mysql client
15 | var DB MySQL
16 |
17 | // Connect create db connection
18 | func Connect(dsn string) {
19 | db, err := gorm.Open(mysql.New(mysql.Config{
20 | // DSN: "gorm:gorm@tcp(127.0.0.1:3306)/gorm?charset=utf8&parseTime=True&loc=Local", // DSN data source name
21 | DSN: dsn,
22 | DefaultStringSize: 512, // string 类型字段的默认长度
23 | DisableDatetimePrecision: true, // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
24 | DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
25 | DontSupportRenameColumn: true, // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
26 | SkipInitializeWithVersion: false, // 根据当前 MySQL 版本自动配置
27 |
28 | }), &gorm.Config{})
29 | if err != nil {
30 | logger.Error("Mysql Cannot open mysql database: ", err.Error())
31 | panic(err)
32 | }
33 | DB = MySQL{db}
34 |
35 | }
36 |
37 | // Migrate the db schema
38 | func Migrate() {
39 | logger.Info("Mysql start check data table exists...")
40 | if !DB.Migrator().HasTable(&UserInfo{}) {
41 | logger.Info("Mysql start create data table user migrate data schemas...")
42 | DB.AutoMigrate(&UserInfo{})
43 | logger.Info("Mysql Add User to account:iguidao , password:123456")
44 | DB.CreatUser("iguidao", "iguidao@iguidao.com", "tXfP0JhWJgtaNQc/DcHF78yeI73RRR+35uFNDx4cIVA=")
45 | }
46 | if !DB.Migrator().HasTable(&UserGroup{}) {
47 | logger.Info("Mysql start create data table user_group migrate data schemas...")
48 | DB.AutoMigrate(&UserGroup{})
49 | }
50 | if !DB.Migrator().HasTable(&GroupContain{}) {
51 | logger.Info("Mysql start create data table group_contain migrate data schemas...")
52 | DB.AutoMigrate(&GroupContain{})
53 | }
54 | if !DB.Migrator().HasTable(&CloudInfo{}) {
55 | logger.Info("Mysql start create data table cloud_info migrate data schemas...")
56 | DB.AutoMigrate(&CloudInfo{})
57 | }
58 | if !DB.Migrator().HasTable(&ClusterInfo{}) {
59 | logger.Info("Mysql start create data table cluster_info migrate data schemas...")
60 | DB.AutoMigrate(&ClusterInfo{})
61 | }
62 | if !DB.Migrator().HasTable(&ClusterNode{}) {
63 | logger.Info("Mysql start create data table redis_node migrate data schemas...")
64 | DB.AutoMigrate(&ClusterNode{})
65 | }
66 | if !DB.Migrator().HasTable(&OpHistory{}) {
67 | logger.Info("Mysql start create data table ophistory migrate data schemas...")
68 | DB.AutoMigrate(&OpHistory{})
69 | }
70 | if !DB.Migrator().HasTable(&CodisInfo{}) {
71 | logger.Info("Mysql start create data table CodisInfo migrate data schemas...")
72 | DB.AutoMigrate(&CodisInfo{})
73 | }
74 | if !DB.Migrator().HasTable(&Rconfig{}) {
75 | logger.Info("Mysql start create data table Rconfig migrate data schemas...")
76 | DB.AutoMigrate(&Rconfig{})
77 | }
78 | logger.Info("Mysql auto check data table done.")
79 | }
80 |
--------------------------------------------------------------------------------
/src/middleware/mysql/mysqlstrcut.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import (
4 | "time"
5 |
6 | "gorm.io/gorm"
7 | )
8 |
9 | // base
10 | type Base struct {
11 | ID int `gorm:"primary_key"`
12 | CreatedAt time.Time `gorm:"not null"`
13 | UpdatedAt time.Time
14 | DeletedAt gorm.DeletedAt `gorm:"index"`
15 | //创建索引`sql:"index"`
16 | }
17 |
18 | // 用户
19 | type UserInfo struct {
20 | Base
21 | UserName string `gorm:"not null;index;unique"`
22 | Password string `gorm:"not null"`
23 | Email string `gorm:"type:varchar(255)"`
24 | UserType string `gorm:"not null;index;type:varchar(50)"` //admin 管理员;visitor 访客;staff 员工
25 | Enable bool // 0是封禁False,1是可登录True
26 | }
27 |
28 | // 用户组
29 | type UserGroup struct {
30 | Base
31 | GroupName string `gorm:"not null;index;unique"`
32 | GroupDescribe string `gorm:"type:varchar(255)"`
33 | GroupType string `gorm:"not null;index;type:varchar(50)"` //admin 管理组;visitor 访客组;staff 员工组
34 | }
35 |
36 | // 组与用户的关系
37 | type GroupContain struct {
38 | Base
39 | GroupId int `gorm:"not null;index"`
40 | UserId int `gorm:"not null;index"`
41 | }
42 |
43 | // 集群信息
44 | type ClusterInfo struct {
45 | Base
46 | Name string `gorm:"not null;index;unique"`
47 | Nodes string `gorm:"type:varchar(255)"` //ip:port,ip:port
48 | Password string `gorm:"type:varchar(255)"`
49 | }
50 |
51 | // node信息
52 | type ClusterNode struct {
53 | Base
54 | CluserId int `gorm:"not null;index"` //集群ID
55 | NodeId string `gorm:"type:varchar(50);unique"` //node的ID
56 | Ip string `gorm:"type:varchar(50)"` //node的IP
57 | Port string `gorm:"type:varchar(25)"` //node的端口
58 | Flags string `gorm:"type:varchar(50)"` //node的身份
59 | MasterId string `gorm:"type:varchar(50)"` //如果是从的话master的ID
60 | LinkState string `gorm:"type:varchar(50)"` //链接状态
61 | RunStatus bool `gorm:"type:varchar(25)"` //运行状态
62 | SlotRange string `gorm:"type:varchar(50)"` //slot区间
63 | SlotNumber int `gorm:"type:varchar(25)"` //solt个数
64 | }
65 |
66 | // cloud redis信息
67 | type CloudInfo struct {
68 | Base
69 | Cloud string `gorm:"type:varchar(10)"` //云厂商
70 | InstanceId string `gorm:"not null;index;unique"` //实例ID
71 | InstanceName string `gorm:"type:varchar(100)"` //实例名称
72 | PrivateIp string `gorm:"type:varchar(20)"` //内网IP
73 | Port int `gorm:"type:varchar(10)"` //端口
74 | Region string `gorm:"type:varchar(20)"` //region
75 | Createtime string `gorm:"type:varchar(20)"` //创建时间
76 | Size int `gorm:"type:varchar(10)"` //实例大小
77 | InstanceStatus string `gorm:"type:varchar(10)"` //实例状态
78 | RedisShardSize int `gorm:"type:varchar(10)"` //分片大小
79 | RedisShardNum int `gorm:"type:varchar(10)"` //分练数量
80 | RedisReplicasNum int `gorm:"type:varchar(10)"` //副本个数
81 | NoAuth bool `gorm:"type:varchar(10)"` //是否需要密码
82 | PublicIp string `gorm:"type:varchar(20)"` //外网IP
83 | Password string `gorm:"type:varchar(50)"` //密码
84 | }
85 |
86 | // codis信息
87 | type CodisInfo struct {
88 | Base
89 | Curl string `gorm:"not null;index;unique"`
90 | Cname string `gorm:"type:varchar(50)"`
91 | }
92 |
93 | // config信息
94 | type Rconfig struct {
95 | Base
96 | Name string `gorm:"type:varchar(255)"`
97 | Key string `gorm:"not null:index:primary_key;unique"`
98 | Value string `gorm:"type:varchar(255)"`
99 | }
100 |
101 | // 操作历史
102 | type OpHistory struct {
103 | Base
104 | UserId int `gorm:"not null;index"`
105 | OpInfo string `gorm:"type:varchar(100)"` // 操作动作
106 | OpParams string `gorm:"type:text"` //操作参数属组或者对象
107 | }
108 |
109 | type Tabler interface {
110 | TableName() string
111 | }
112 |
113 | func (UserInfo) TableName() string {
114 | return "user_info"
115 | }
116 |
117 | func (UserGroup) TableName() string {
118 | return "user_group"
119 | }
120 |
121 | func (GroupContain) TableName() string {
122 | return "group_contain"
123 | }
124 |
125 | func (CloudInfo) TableName() string {
126 | return "cloud_info"
127 | }
128 |
129 | func (ClusterInfo) TableName() string {
130 | return "cluster_info"
131 | }
132 | func (CodisInfo) TableName() string {
133 | return "codis_info"
134 | }
135 |
136 | func (ClusterNode) TableName() string {
137 | return "cluster_node"
138 | }
139 |
140 | func (OpHistory) TableName() string {
141 | return "op_history"
142 | }
143 |
144 | func (Rconfig) TableName() string {
145 | return "rconfig"
146 | }
147 |
--------------------------------------------------------------------------------
/src/middleware/mysql/opcfg.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import (
4 | "github.com/iguidao/redis-manager/src/middleware/logger"
5 | )
6 |
7 | // add cfg
8 | func (m *MySQL) AddCfg(name, key, value string) (int, bool) {
9 | addcfginfo := &Rconfig{
10 | Key: key,
11 | Value: value,
12 | Name: name,
13 | }
14 | result := m.Create(&addcfginfo)
15 | if result.Error != nil {
16 | logger.Error("Mysql add cfg error:", result.Error)
17 | return 0, false
18 | }
19 | return addcfginfo.ID, true
20 | }
21 | func (m *MySQL) DelCfg(key string) bool {
22 | var cfg *Rconfig
23 | if err := m.Model(cfg).Where("`key` = ?", key).Delete(&cfg).Error; err != nil {
24 | logger.Error("Mysql del cfg error:", err)
25 | return false
26 | }
27 | return true
28 | }
29 |
30 | // update cfg
31 | func (m *MySQL) UpdateCfg(key, value string) bool {
32 | var cfg Rconfig
33 | result := m.Model(&cfg).Where("`key` = ?", key).Update("value", value)
34 | if result.Error != nil {
35 | logger.Error("Mysql result.Error: ", result.Error)
36 | }
37 | return result.Error == nil
38 | }
39 |
40 | // check cfg
41 | func (m *MySQL) ExistCfg(key string) bool {
42 | var cfg *Rconfig
43 | if err := m.Model(cfg).Where("`key` = ?", key).First(&cfg).Error; err != nil {
44 | logger.Error("Mysql exist cfg error:", err)
45 | return false
46 | }
47 | return true
48 | }
49 |
50 | // get all cfg
51 | func (m *MySQL) GetAllCfg() []Rconfig {
52 | var cfg []Rconfig
53 | m.Find(&cfg)
54 | return cfg
55 | }
56 |
57 | // get one cfg
58 | func (m *MySQL) GetOneCfg(key string) Rconfig {
59 | var cfg Rconfig
60 | m.Where("`key` = ?", key).First(&cfg)
61 | return cfg
62 | }
63 |
64 | // get one cfg value
65 | func (m *MySQL) GetOneCfgValue(key string) string {
66 | var cfg Rconfig
67 | m.Where("`key` = ?", key).First(&cfg)
68 | return cfg.Value
69 | }
70 |
--------------------------------------------------------------------------------
/src/middleware/mysql/opcluster.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import "github.com/iguidao/redis-manager/src/middleware/logger"
4 |
5 | // get all cluster
6 | func (m *MySQL) GetAllCluster() []ClusterInfo {
7 | var clusters []ClusterInfo
8 | m.Find(&clusters)
9 | return clusters
10 | }
11 | func (m *MySQL) GetClusterNumber() int64 {
12 | var clusters []ClusterInfo
13 | var count int64
14 | m.Model(clusters).Find(&clusters).Count(&count)
15 | return count
16 | }
17 | func (m *MySQL) GetClusterAddress(id string) (string, string) {
18 | var clusterinfo *ClusterInfo
19 | m.Where("id = ?", id).First(&clusterinfo)
20 | return clusterinfo.Nodes, clusterinfo.Password
21 | }
22 | func (m *MySQL) GetClusterPassword(id string) string {
23 | var clusterinfo *ClusterInfo
24 | m.Where("id = ?", id).First(&clusterinfo)
25 | return clusterinfo.Password
26 | }
27 |
28 | // add cluster
29 | func (m *MySQL) AddCluster(name, nodes, password string) (int, bool) {
30 | addcluster := &ClusterInfo{
31 | Name: name,
32 | Nodes: nodes,
33 | Password: password,
34 | }
35 | result := m.Create(&addcluster)
36 | if result.Error != nil {
37 | logger.Error("Mysql add cluster error:", result.Error)
38 | return 0, false
39 | }
40 | return addcluster.ID, true
41 | // return gdarticle.ID.String(), true
42 | }
43 |
44 | func (m *MySQL) GetClusterNode(cluster string) []ClusterNode {
45 | var nodes []ClusterNode
46 | m.Model(nodes).Where("cluser_id = ?", cluster).Find(&nodes)
47 | return nodes
48 | }
49 | func (m *MySQL) GetClusterNodeMaster(cluster string) []ClusterNode {
50 | var nodes []ClusterNode
51 | m.Model(nodes).Where("cluser_id = ? AND flags = ?", cluster, "master").Find(&nodes)
52 | return nodes
53 | }
54 |
55 | func (m *MySQL) GetClusterNodeMasterAddress(nodeid string) string {
56 | var nodes *ClusterNode
57 | m.Where("node_id = ?", nodeid).First(&nodes)
58 | return nodes.Ip + ":" + nodes.Port
59 | }
60 | func (m *MySQL) GetClusterNodeSlaverAddress(nodeid string) string {
61 | var nodes *ClusterNode
62 | m.Where("master_id = ?", nodeid).First(&nodes)
63 | return nodes.Ip + ":" + nodes.Port
64 | }
65 | func (m *MySQL) AddClusterNode(nodeid, ip, port, flags, masterid, linkstate, slotrange string, clusterid, slotnumber int) (int, bool) {
66 | addnode := &ClusterNode{
67 | CluserId: clusterid,
68 | NodeId: nodeid,
69 | Ip: ip,
70 | Port: port,
71 | Flags: flags,
72 | MasterId: masterid,
73 | LinkState: linkstate,
74 | SlotRange: slotrange,
75 | SlotNumber: slotnumber,
76 | }
77 | result := m.Create(&addnode)
78 | if result.Error != nil {
79 | logger.Error("Mysql add cluster node error:", result.Error)
80 | return 0, false
81 | }
82 | return addnode.ID, true
83 | // return gdarticle.ID.String(), true
84 | }
85 |
--------------------------------------------------------------------------------
/src/middleware/mysql/opcodis.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import "github.com/iguidao/redis-manager/src/middleware/logger"
4 |
5 | // add codis
6 |
7 | func (m *MySQL) AddCodis(curl, cname string) (int, bool) {
8 | addcodisinfo := &CodisInfo{
9 | Curl: curl,
10 | Cname: cname,
11 | }
12 | result := m.Create(&addcodisinfo)
13 | if result.Error != nil {
14 | logger.Error("Mysql add Codis error:", result.Error)
15 | return 0, false
16 | }
17 | return addcodisinfo.ID, true
18 | // return gdarticle.ID.String(), true
19 | }
20 |
21 | // get codis
22 | func (m *MySQL) GetAllCodis() []CodisInfo {
23 | var clusters []CodisInfo
24 | m.Find(&clusters)
25 | return clusters
26 | }
27 |
28 | func (m *MySQL) GetCodisNumber() int64 {
29 | var clusters []CodisInfo
30 | var count int64
31 | m.Find(&clusters).Count(&count)
32 | return count
33 | }
34 |
--------------------------------------------------------------------------------
/src/middleware/mysql/opuser.go:
--------------------------------------------------------------------------------
1 | package mysql
2 |
3 | import (
4 | "github.com/iguidao/redis-manager/src/middleware/logger"
5 | "github.com/iguidao/redis-manager/src/middleware/model"
6 | "gorm.io/gorm"
7 | )
8 |
9 | func (m *MySQL) GetAllUser() []UserInfo {
10 | var user []UserInfo
11 | m.Find(&user)
12 | return user
13 | }
14 |
15 | func (m *MySQL) UserInfo(username string) UserInfo {
16 | var user UserInfo
17 | m.Where("user_name = ?", username).First(&user)
18 | return user
19 | }
20 |
21 | func (m *MySQL) FindUserPassword(ruser string) (user UserInfo, err error) {
22 | err = m.Where("user_name = ?", ruser).Find(&user).Error
23 | if err != nil && err == gorm.ErrRecordNotFound {
24 | logger.Error("Mysql Find user password error:", err)
25 | }
26 | return
27 | }
28 |
29 | func (m *MySQL) FindEmail(email string) bool {
30 | var user UserInfo
31 | if m.Where("email = ?", email).First(&user).RowsAffected == 0 {
32 | return false
33 | }
34 | return true
35 | }
36 |
37 | func (m *MySQL) FindUser(ruser string) bool {
38 | var user UserInfo
39 | if m.Where("user_name = ?", ruser).First(&user).RowsAffected == 0 {
40 | return false
41 | }
42 | return true
43 | }
44 | func (m *MySQL) UpdateUserPassword(username string, password string) bool {
45 | var user *UserInfo
46 | if err := m.Model(user).Where("user_name = ?", username).Update("password", password).Error; err != nil {
47 | logger.Error("Mysql update user password error: ", err)
48 | return false
49 | }
50 | return true
51 | }
52 | func (m *MySQL) UpdateUserType(username string, usertype string) bool {
53 | var user *UserInfo
54 | if err := m.Model(user).Where("user_name = ?", username).Update("user_type", usertype).Error; err != nil {
55 | logger.Error("Mysql update user type error: ", err)
56 | return false
57 | }
58 | return true
59 | }
60 | func (m *MySQL) ExistUserId(id int) bool {
61 | var user *UserInfo
62 | if err := m.Model(user).Where("id = ?", id).First(&user).Error; err != nil {
63 | logger.Error("Mysql exist user id error: ", err)
64 | return false
65 | }
66 | return true
67 | }
68 | func (m *MySQL) ExistUserName(username string) bool {
69 | var user *UserInfo
70 | if err := m.Model(user).Where("user_name = ?", username).First(&user).Error; err != nil {
71 | logger.Error("Mysql exist user name error: ", err)
72 | return false
73 | }
74 | return true
75 | }
76 | func (m *MySQL) GetUserType(userid int) string {
77 | var user *UserInfo
78 | m.Where("id = ?", userid).First(&user)
79 | return user.UserType
80 | }
81 | func (m *MySQL) DelUser(userid int) bool {
82 | var user *UserInfo
83 | if err := m.Model(user).Where("id = ?", userid).Delete(&user).Error; err != nil {
84 | logger.Error("Mysql del user error:", err)
85 | return false
86 | }
87 | return true
88 | }
89 | func (m *MySQL) CreatUser(nick_name, email, password string) bool {
90 | var usertype string
91 | if nick_name == "iguidao" {
92 | usertype = model.USERTYPEADMIN
93 | } else {
94 | usertype = model.USERTYPEVISITOR
95 | }
96 | if result := m.Create(&UserInfo{
97 | UserName: nick_name,
98 | Email: email,
99 | Password: password,
100 | UserType: usertype,
101 | Enable: true,
102 | }); result.Error != nil {
103 | logger.Error("Mysql create mysql user fails", result.Error)
104 | return false
105 | }
106 | return true
107 | }
108 |
--------------------------------------------------------------------------------
/src/middleware/opredis/all.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | import (
4 | "sort"
5 |
6 | "github.com/iguidao/redis-manager/src/cfg"
7 | )
8 |
9 | func AllKey() []string {
10 | var keylist []string
11 | val, num, scanok := GetScanKey(0, 1000)
12 | if !scanok {
13 | return nil
14 | }
15 | keylist = append(keylist, val...)
16 | var fornum = 1
17 | for {
18 | val, num, scanok = GetScanKey(num, 1000)
19 | if !scanok {
20 | sort.Strings(keylist)
21 | return keylist
22 | }
23 | fornum++
24 | keylist = append(keylist, val...)
25 | if num == 0 || fornum >= cfg.Get_Info_Int("allkeyfornum") {
26 | break
27 | }
28 | }
29 | sort.Strings(keylist)
30 | return keylist
31 | }
32 |
--------------------------------------------------------------------------------
/src/middleware/opredis/analysisrdb.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | import (
4 | "encoding/json"
5 | "errors"
6 | "io"
7 | "os"
8 |
9 | "sort"
10 | "time"
11 |
12 | "github.com/iguidao/redis-manager/src/cfg"
13 | "github.com/iguidao/redis-manager/src/middleware/logger"
14 | "github.com/tommy351/rdb-go"
15 | )
16 |
17 | func Analysis(filename, serverip string) map[string]interface{} {
18 | checksize := cfg.Get_Info_Int("checksize")
19 | stringkeymap := make(map[string]int)
20 | listkeymap := make(map[string]int)
21 | hashkeymap := make(map[string]int)
22 | setkeymap := make(map[string]int)
23 | zsetkeymap := make(map[string]int)
24 | resultmap := make(map[string]interface{})
25 | file, err := os.Open(filename)
26 | if err != nil {
27 | panic(err)
28 | }
29 | defer file.Close()
30 | parser := rdb.NewParser(file)
31 |
32 | for {
33 | data, err := parser.Next()
34 | if errors.Is(err, io.EOF) {
35 | break
36 | }
37 | if err != nil {
38 | panic(err)
39 | }
40 | switch data := data.(type) {
41 | case *rdb.StringData:
42 | stringkeysize := len(data.Value)
43 | if stringkeysize > checksize {
44 | stringkeymap[data.Key] = stringkeysize
45 | }
46 | case *rdb.ListData:
47 | listkeysize := 0
48 | for _, value := range data.Value {
49 | listkeysize = listkeysize + len(value)
50 | }
51 | if listkeysize > checksize {
52 | listkeymap[data.Key] = listkeysize
53 | }
54 | case *rdb.HashData:
55 | hashkeysize := 0
56 | for _, value := range data.Value {
57 | hashkeysize = hashkeysize + len(value)
58 | }
59 | if hashkeysize > checksize {
60 | hashkeymap[data.Key] = hashkeysize
61 | }
62 | case *rdb.SetData:
63 | setkeysize := 0
64 | for _, value := range data.Value {
65 | setkeysize = setkeysize + len(value)
66 | }
67 | if setkeysize > checksize {
68 | setkeymap[data.Key] = setkeysize
69 | }
70 |
71 | case *rdb.SortedSetData:
72 | sortsetkeysize := 0
73 | for _, value := range data.Value {
74 | sortsetkeysize = sortsetkeysize + len(value.Value)
75 | }
76 | if sortsetkeysize > checksize {
77 | zsetkeymap[data.Key] = sortsetkeysize
78 | }
79 | }
80 | }
81 |
82 | resultmap["String-Big-Key-Top10"] = SortTopkey(stringkeymap)
83 | resultmap["List-Big-Key-Top10"] = SortTopkey(listkeymap)
84 | resultmap["hash-Big-Key-Top10"] = SortTopkey(hashkeymap)
85 | resultmap["set-Big-Key-Top10"] = SortTopkey(setkeymap)
86 | resultmap["check-time"] = time.Now().Format("2006-01-02 15:04:05")
87 | jsonBody, _ := json.Marshal(resultmap)
88 | _, ok := SetStringKey(serverip, string(jsonBody))
89 | if ok {
90 | logger.Info("bigkey ", serverip, "设置成功 ", string(jsonBody))
91 | }
92 | return resultmap
93 | }
94 |
95 | type keyintperoson struct {
96 | Name string
97 | Age int
98 | }
99 |
100 | func SortTopkey(keymap map[string]int) map[string]int {
101 | var lstPerson []keyintperoson
102 | resultkeymap := make(map[string]int)
103 | for k, v := range keymap {
104 | lstPerson = append(lstPerson, keyintperoson{k, v})
105 | }
106 |
107 | sort.Slice(lstPerson, func(i, j int) bool {
108 | return lstPerson[i].Age > lstPerson[j].Age // 降序
109 | })
110 | for i, v := range lstPerson {
111 | if i == 10 {
112 | break
113 | }
114 | resultkeymap[v.Name] = v.Age
115 | }
116 | return resultkeymap
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/src/middleware/opredis/basecluster.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | import (
4 | "strings"
5 |
6 | "github.com/iguidao/redis-manager/src/middleware/logger"
7 | )
8 |
9 | func CTypeKey(keyname string) (string, bool) {
10 | ok, err := CRD.Type(ctx, keyname).Result()
11 | if err != nil {
12 | logger.Error("Redis Set key: ", keyname, " Error: ", err)
13 | return "", false
14 | }
15 | return ok, true
16 | }
17 |
18 | // String key op
19 | func CGetStringKey(keyname string) (string, bool) {
20 | val, err := CRD.Get(ctx, keyname).Result()
21 | if err != nil {
22 | logger.Error("Redis Get key: ", keyname, " Error: ", err)
23 | return "", false
24 | }
25 | return val, true
26 | }
27 |
28 | func CGetListKey(keyname string) ([]string, bool) {
29 | lnum, err := CRD.LLen(ctx, keyname).Result()
30 | if err != nil {
31 | logger.Error("Redis LLEN key: ", keyname, " Error: ", err)
32 | return nil, false
33 | }
34 | // if lnum > 100 {
35 | // lnum = 100
36 | // }
37 | val, err := CRD.LRange(ctx, keyname, 0, lnum).Result()
38 | if err != nil {
39 | logger.Error("Redis LRANGE key: ", keyname, " Error: ", err)
40 | return nil, false
41 | }
42 | return val, true
43 | }
44 | func CGetHashKey(keyname string) (map[string]string, bool) {
45 | val, err := CRD.HGetAll(ctx, keyname).Result()
46 | if err != nil {
47 | logger.Error("Redis HGETALL key: ", keyname, " Error: ", err)
48 | return nil, false
49 | }
50 | return val, true
51 | }
52 | func CGetSetKey(keyname string) ([]string, bool) {
53 | val, err := CRD.SMembers(ctx, keyname).Result()
54 | if err != nil {
55 | logger.Error("Redis SMEMBERS key: ", keyname, " Error: ", err)
56 | return nil, false
57 | }
58 | return val, true
59 | }
60 | func CGetZsetKey(keyname string) ([]string, bool) {
61 | znum, err := CRD.ZCard(ctx, keyname).Result()
62 | if err != nil {
63 | logger.Error("Redis ZCARD key: ", keyname, " Error: ", err)
64 | return nil, false
65 | }
66 | // if znum > 100 {
67 | // znum = 100
68 | // }
69 | val, err := CRD.ZRange(ctx, keyname, 0, znum).Result()
70 | if err != nil {
71 | logger.Error("Redis ZRANGE key: ", keyname, " Error: ", err)
72 | return nil, false
73 | }
74 | return val, true
75 | }
76 | func CDelKey(keyname string) (int64, bool) {
77 | val, err := CRD.Del(ctx, keyname).Result()
78 | if err != nil {
79 | logger.Error("Redis Del key: ", keyname, " Error: ", err)
80 | return -1, false
81 | }
82 | return val, true
83 | }
84 | func CGetClusterNode() []string {
85 | clusternode := CRD.ClusterNodes(ctx)
86 | nodeinfo := strings.Split(clusternode.Val(), "\n")
87 | return nodeinfo
88 | }
89 |
--------------------------------------------------------------------------------
/src/middleware/opredis/big.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | import (
4 | "sort"
5 | "sync"
6 | )
7 |
8 | var (
9 | stringkeymap map[string]int64
10 | listkeymap map[string]int64
11 | hashkeymap map[string]int64
12 | setkeymap map[string]int64
13 | zsetkeymap map[string]int64
14 | wg sync.WaitGroup
15 | lock sync.Mutex
16 | )
17 |
18 | func BigKey() map[string]interface{} {
19 | stringkeymap = make(map[string]int64)
20 | listkeymap = make(map[string]int64)
21 | hashkeymap = make(map[string]int64)
22 | setkeymap = make(map[string]int64)
23 | zsetkeymap = make(map[string]int64)
24 | resultmap := make(map[string]interface{})
25 | // val, _ := BgsaveKey()
26 | val, num, scanok := GetScanKey(0, 1000)
27 | if !scanok {
28 | return nil
29 | }
30 | wg.Add(1)
31 | go Countkey(val)
32 | for {
33 | val, num, scanok = GetScanKey(num, 1000)
34 | if !scanok {
35 | break
36 | }
37 | if num != 0 {
38 | wg.Add(1)
39 | go Countkey(val)
40 | } else {
41 | break
42 | }
43 | }
44 | wg.Wait()
45 | resultmap["string-Top10"] = Sortkey(stringkeymap)
46 | resultmap["list-Top10"] = Sortkey(listkeymap)
47 | resultmap["hash-Top10"] = Sortkey(hashkeymap)
48 | resultmap["set-Top10"] = Sortkey(setkeymap)
49 | resultmap["zset-Top10"] = Sortkey(zsetkeymap)
50 | return resultmap
51 | }
52 |
53 | type keyperoson struct {
54 | Name string
55 | Age int64
56 | }
57 |
58 | func Sortkey(keymap map[string]int64) map[string]int64 {
59 | var lstPerson []keyperoson
60 | resultkeymap := make(map[string]int64)
61 | for k, v := range keymap {
62 | lstPerson = append(lstPerson, keyperoson{k, v})
63 | }
64 |
65 | sort.Slice(lstPerson, func(i, j int) bool {
66 | return lstPerson[i].Age > lstPerson[j].Age // 降序
67 | })
68 | for i, v := range lstPerson {
69 | if i == 10 {
70 | break
71 | }
72 | resultkeymap[v.Name] = v.Age
73 | }
74 | return resultkeymap
75 |
76 | }
77 |
78 | func Countkey(keylist []string) {
79 | cstringkeymap := make(map[string]int64)
80 | clistkeymap := make(map[string]int64)
81 | chashkeymap := make(map[string]int64)
82 | csetkeymap := make(map[string]int64)
83 | czsetkeymap := make(map[string]int64)
84 | for _, keyname := range keylist {
85 | keytype, ok := TypeKey(keyname)
86 | if !ok {
87 | continue
88 | }
89 | switch keytype {
90 | case "string":
91 | val, stringok := SizeStringKey(keyname)
92 | if stringok {
93 | cstringkeymap[keyname] = val
94 | }
95 | case "list":
96 | val, listok := SizeListKey(keyname)
97 | if listok {
98 | clistkeymap[keyname] = val
99 | }
100 | case "hash":
101 | val, hashok := SizeHashKey(keyname)
102 | if hashok {
103 | chashkeymap[keyname] = val
104 | }
105 | case "set":
106 | val, setok := SizeSetKey(keyname)
107 | if setok {
108 | csetkeymap[keyname] = val
109 | }
110 | case "zset":
111 | val, zsetok := SizeZsetKey(keyname)
112 | if zsetok {
113 | czsetkeymap[keyname] = val
114 | }
115 | case "none":
116 | continue
117 | default:
118 | continue
119 | }
120 | }
121 | stringkeymap = AppendMap(stringkeymap, cstringkeymap)
122 | listkeymap = AppendMap(listkeymap, clistkeymap)
123 | hashkeymap = AppendMap(hashkeymap, chashkeymap)
124 | setkeymap = AppendMap(setkeymap, csetkeymap)
125 | zsetkeymap = AppendMap(zsetkeymap, czsetkeymap)
126 | defer wg.Done()
127 | }
128 |
129 | func AppendMap(result, val map[string]int64) map[string]int64 {
130 | lock.Lock()
131 | for i, v := range val {
132 | if i != "" || v != 0 {
133 | result[i] = v
134 | }
135 | }
136 | lock.Unlock()
137 | return result
138 | }
139 |
--------------------------------------------------------------------------------
/src/middleware/opredis/click.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | import "github.com/iguidao/redis-manager/src/cfg"
4 |
5 | func BigKeyClick(clusterName, groupName, keyname string) (string, int) {
6 | // keyname := "Click-Bigkey-" + clusterName + "-" + groupName
7 | num, ok := IncrStringKey(keyname)
8 | if !ok {
9 | return "大key分析计数出错了执行出现了问题,请找管理员!!!", 0
10 | }
11 | if num == 1 {
12 | if !ExpireKey(keyname, cfg.Get_Info_Int("locktime")) {
13 | return "大key分析计数时间出错了执行出现了问题,请找管理员!!!", 0
14 | }
15 | }
16 | if num == 5 {
17 | return "成功激发隐藏功能,开始针对集群:" + clusterName + " 的组:" + groupName + " 执行大key分析操作!!!请等待,如若超过10min未查出结果,请找管理员..", 1
18 | }
19 | if num > 5 {
20 | return "嗯?已经在执行后台大key分析了,还点?别着急,请等待10min!,如若超过10min未查出结果,请找管理员..", 2
21 | }
22 | return "据听说1分钟点击5次查询,可以生成最新的大key分析数据", 3
23 | }
24 |
--------------------------------------------------------------------------------
/src/middleware/opredis/connect.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | import (
4 | "github.com/iguidao/redis-manager/src/middleware/logger"
5 |
6 | "github.com/go-redis/redis/v9"
7 | )
8 |
9 | // 单点和codis链接
10 | type ClientConnect struct {
11 | *redis.Client
12 | }
13 |
14 | var RD ClientConnect
15 |
16 | func ConnectRedis(addr, password string) bool {
17 | rd := redis.NewClient(&redis.Options{
18 | Addr: addr,
19 | Password: password, // no password set
20 | // DB: 0, // use default DB
21 | })
22 | RD = ClientConnect{rd}
23 | _, err := RD.Ping(ctx).Result()
24 | if err != nil {
25 | logger.Error("Redis Connect Error: ", err)
26 | return false
27 | }
28 | return true
29 | }
30 |
31 | // 集群链接
32 | type ClientClusterConnect struct {
33 | *redis.ClusterClient
34 | }
35 |
36 | var CRD ClientClusterConnect
37 |
38 | func ConnectRedisCluster(addr []string, password string) bool {
39 | rd := redis.NewClusterClient(&redis.ClusterOptions{
40 | Addrs: addr,
41 | Password: password,
42 | // DialTimeout: 200 * time.Microsecond,
43 | // ReadTimeout: 200 * time.Microsecond,
44 | // WriteTimeout: 200 * time.Microsecond,
45 | })
46 | CRD = ClientClusterConnect{rd}
47 | return true
48 | }
49 |
--------------------------------------------------------------------------------
/src/middleware/opredis/del.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | func DeleteKey(keyname string) string {
4 | val, stringok := DelKey(keyname)
5 | if stringok {
6 | if val == 1 {
7 | return "删除成功"
8 | }
9 | if val == -1 {
10 | return "删除失败"
11 | }
12 | if val == 0 {
13 | return "没有这个key"
14 | }
15 | }
16 | return "删除失败"
17 | }
18 |
19 | func CDeleteKey(keyname string) string {
20 | val, stringok := CDelKey(keyname)
21 | if stringok {
22 | if val == 1 {
23 | return "删除成功"
24 | }
25 | if val == -1 {
26 | return "删除失败"
27 | }
28 | if val == 0 {
29 | return "没有这个key"
30 | }
31 | }
32 | return "删除失败"
33 | }
34 |
--------------------------------------------------------------------------------
/src/middleware/opredis/hot.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | import (
4 | "context"
5 | "regexp"
6 | "strconv"
7 | "strings"
8 | "time"
9 |
10 | "github.com/iguidao/redis-manager/src/middleware/logger"
11 | )
12 |
13 | func HotKey(serverip, pw string) map[string]int {
14 | keydic := make(map[string]int)
15 |
16 | var monitor string
17 | var knowtime int64
18 | ch := make(chan string)
19 | timeout, cancel := context.WithTimeout(context.Background(), 3*time.Second)
20 | defer cancel()
21 | go func() {
22 | monitor, knowtime = TelnetCommond(serverip, "monitor", pw)
23 | ch <- "done"
24 | }()
25 |
26 | select {
27 | case res := <-ch:
28 | logger.Info("Telnet success: ", res)
29 | case <-timeout.Done():
30 | logger.Error("Telnet timout: ", timeout.Err())
31 | }
32 |
33 | re := regexp.MustCompile("(?m)[\r\n]+^.*\"PING\"|\"INFO\".*$")
34 | monitor = re.ReplaceAllString(monitor, "")
35 | str := "(?m)[\r\n]+^.*" + strconv.FormatInt(knowtime+1, 10) + ".*$"
36 | re = regexp.MustCompile(str)
37 | monitora := re.FindAllString(monitor, -1)
38 | for _, v := range monitora {
39 | vlist := strings.Split(v, " ")
40 | if len(vlist) > 4 {
41 | vstring := strings.Replace(vlist[4], " ", "", -1)
42 | vstring = strings.Replace(vstring, "\n", "", -1)
43 | vstring = strings.Replace(vstring, "\r", "", -1)
44 | vstring = strings.Replace(vstring, "\"", "", -1)
45 | vstring = strings.Replace(vstring, "\\", "", -1)
46 | if _, ok := keydic[vstring]; ok {
47 | keydic[vstring] = keydic[vstring] + 1
48 | } else {
49 | keydic[vstring] = 1
50 | }
51 | }
52 | }
53 | return keydic
54 | }
55 |
--------------------------------------------------------------------------------
/src/middleware/opredis/lock.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/iguidao/redis-manager/src/cfg"
7 | )
8 |
9 | func LockCheck(key string, keytime time.Duration) bool {
10 | if ConnectRedis(cfg.Get_Info_String("REDIS"), cfg.Get_Info_String("redispw")) {
11 | if LockOp("Lock-"+key, keytime) {
12 | return true
13 | }
14 | }
15 |
16 | return false
17 | }
18 |
19 | func LockRm(key string) bool {
20 | if ConnectRedis(cfg.Get_Info_String("REDIS"), cfg.Get_Info_String("redispw")) {
21 | if UnLockOp("Lock-" + key) {
22 | return true
23 | }
24 | }
25 | return false
26 | }
27 |
--------------------------------------------------------------------------------
/src/middleware/opredis/opdilatation.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/iguidao/redis-manager/src/middleware/codisapi"
7 | "github.com/iguidao/redis-manager/src/middleware/logger"
8 | "github.com/iguidao/redis-manager/src/middleware/model"
9 | )
10 |
11 | // Codis扩容操作
12 | func Cdilatation(codisnode model.CodisChangeNode, auth string, topom codisapi.Topom) map[string]interface{} {
13 | result := make(map[string]interface{})
14 | ok, uplist := UpClusterHost(codisnode, topom, auth)
15 |
16 | if !ok {
17 | logger.Error("Codis Opnode dilatation: codis add host fails")
18 | }
19 | time.Sleep(time.Duration(5) * time.Second)
20 | if !CodisRebalanceAll(codisnode.Curl, codisnode.ClusterName, auth) {
21 | logger.Error("Codis Opnode dilatation: codis Reabalance fails")
22 | }
23 | result["status"] = ok
24 | result["downlist"] = uplist
25 | return result
26 | }
27 |
--------------------------------------------------------------------------------
/src/middleware/opredis/opshrinkage.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | import (
4 | "github.com/iguidao/redis-manager/src/middleware/codisapi"
5 | "github.com/iguidao/redis-manager/src/middleware/logger"
6 | "github.com/iguidao/redis-manager/src/middleware/model"
7 | )
8 |
9 | // 缩容操作
10 | func Cshrinkage(codisnode model.CodisChangeNode, auth string, topom codisapi.Topom) map[string]interface{} {
11 | result := make(map[string]interface{})
12 | ok, downlist := DownClusterHost(codisnode, auth, topom.Stats)
13 | if !ok {
14 | logger.Error("Codis Opnode shrinkage: codis down host fails")
15 | }
16 | result["status"] = ok
17 | result["downlist"] = downlist
18 | return result
19 | }
20 |
--------------------------------------------------------------------------------
/src/middleware/opredis/query.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | import (
4 | "strings"
5 | )
6 |
7 | func CQueryKey(keyname string) QueryResult {
8 | keytype, ok := CTypeKey(keyname)
9 | var result QueryResult
10 | if ok {
11 | result.Value, result.Len = CQuery_value(keytype, keyname)
12 | ttl, tok := TtlKey(keyname)
13 | if tok {
14 | result.Ttl = ttl
15 | }
16 | result.Type = keytype
17 | }
18 | return result
19 | }
20 | func CQuery_value(keytype, keyname string) (interface{}, int) {
21 | switch keytype {
22 | case "string":
23 | val, stringok := CGetStringKey(keyname)
24 | if stringok {
25 | return val, strings.Count(val, "")
26 | }
27 | case "list":
28 | val, listok := CGetListKey(keyname)
29 | if listok {
30 | return val, len(val)
31 | }
32 | case "hash":
33 | val, hashok := CGetHashKey(keyname)
34 | if hashok {
35 | return val, len(val)
36 | }
37 | case "set":
38 | val, setok := CGetSetKey(keyname)
39 | if setok {
40 | return val, len(val)
41 | }
42 | case "zset":
43 | val, zsetok := CGetZsetKey(keyname)
44 | if zsetok {
45 | return val, len(val)
46 | }
47 | case "none":
48 | return "Not Found Key", 0
49 | default:
50 | return "This type " + keytype + " key, query is not supported", 0
51 | }
52 | return "Get Key Fail", 0
53 | }
54 |
55 | func QueryKey(keyname string) QueryResult {
56 | keytype, ok := TypeKey(keyname)
57 | var result QueryResult
58 | if ok {
59 | result.Value, result.Len = Query_value(keytype, keyname)
60 | ttl, tok := TtlKey(keyname)
61 | if tok {
62 | result.Ttl = ttl
63 | }
64 | result.Type = keytype
65 | }
66 | return result
67 | }
68 |
69 | func Query_value(keytype, keyname string) (interface{}, int) {
70 | switch keytype {
71 | case "string":
72 | val, stringok := GetStringKey(keyname)
73 | if stringok {
74 | return val, strings.Count(val, "")
75 | }
76 | case "list":
77 | val, listok := GetListKey(keyname)
78 | if listok {
79 | return val, len(val)
80 | }
81 | case "hash":
82 | val, hashok := GetHashKey(keyname)
83 | if hashok {
84 | return val, len(val)
85 | }
86 | case "set":
87 | val, setok := GetSetKey(keyname)
88 | if setok {
89 | return val, len(val)
90 | }
91 | case "zset":
92 | val, zsetok := GetZsetKey(keyname)
93 | if zsetok {
94 | return val, len(val)
95 | }
96 | case "none":
97 | return "Not Found Key", 0
98 | default:
99 | return "This type " + keytype + " key, query is not supported", 0
100 | }
101 | return "Get Key Fail", 0
102 | }
103 |
--------------------------------------------------------------------------------
/src/middleware/opredis/redisstruct.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | type QueryResult struct {
4 | Ttl string `json:"ttl"`
5 | Type string `json:"type"`
6 | Value interface{} `json:"value"`
7 | Len int `json:"len"`
8 | // Debug string `json:"debug"`
9 | }
10 |
--------------------------------------------------------------------------------
/src/middleware/opredis/slow.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | import (
4 | "github.com/go-redis/redis/v9"
5 | )
6 |
7 | func SlowKey() []redis.SlowLog {
8 | val, slowlogok := GetSlowLog()
9 | if !slowlogok {
10 | return nil
11 | }
12 | return val
13 | }
14 |
--------------------------------------------------------------------------------
/src/middleware/opredis/telnet.go:
--------------------------------------------------------------------------------
1 | package opredis
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/iguidao/redis-manager/src/middleware/logger"
7 | "github.com/reiver/go-telnet"
8 | )
9 |
10 | func ReaderTelnet(conn *telnet.Conn) (out string, knowtime int64) {
11 | var result string
12 | var buffer [100]byte
13 | recvData := buffer[:]
14 | knowtime = time.Now().Unix()
15 | for {
16 | comtime := time.Now().Unix()
17 | _, err := conn.Read(recvData)
18 | if nil != err {
19 | logger.Error("ReaderTelnet error: ", err)
20 | }
21 | result = result + string(recvData)
22 | if comtime-knowtime > 1 {
23 | break
24 | }
25 | }
26 |
27 | return result, knowtime
28 | }
29 | func SenderTelnet(conn *telnet.Conn, command string) {
30 | var crlfBuffer [2]byte = [2]byte{'\r', '\n'}
31 | crlf := crlfBuffer[:]
32 | conn.Write([]byte(command))
33 | conn.Write(crlf)
34 | }
35 |
36 | func TelnetCommond(ip, command, pw string) (string, int64) {
37 | conn, err := telnet.DialTo(ip)
38 | if nil != err {
39 | logger.Error("TelnetCommond error: ", err)
40 | }
41 | defer conn.Close()
42 | if pw != "" {
43 | SenderTelnet(conn, "auth "+pw)
44 | }
45 | SenderTelnet(conn, command)
46 | monitor, knowtime := ReaderTelnet(conn)
47 | return monitor, knowtime
48 | }
49 |
--------------------------------------------------------------------------------
/src/middleware/rcron/cloudrefresh.go:
--------------------------------------------------------------------------------
1 | package rcron
2 |
3 | import (
4 | "encoding/json"
5 |
6 | "github.com/iguidao/redis-manager/src/middleware/logger"
7 | "github.com/iguidao/redis-manager/src/middleware/model"
8 | "github.com/iguidao/redis-manager/src/middleware/mysql"
9 | "github.com/iguidao/redis-manager/src/middleware/txcloud"
10 | "github.com/iguidao/redis-manager/src/middleware/util"
11 | )
12 |
13 | func CloudRefresh() {
14 | logger.Info("定时任务:刷新云redis任务启动")
15 | cloudset := make(map[string]string)
16 | cloudinfo := mysql.DB.GetCloudRegion()
17 | if len(cloudinfo) == 0 {
18 | logger.Info("定时任务:数据库没有数据,不用获取最新的云redis数据")
19 | return
20 | }
21 | for _, v := range cloudinfo {
22 | cloudset[v.Region] = v.Cloud
23 | }
24 | for i, v := range cloudset {
25 | if v == "txredis" {
26 | if !txcloud.TxRedisContent(i) {
27 | logger.Error("定时任务:链接腾讯云redis失败")
28 | return
29 | } else {
30 | list, ok := txcloud.TxListRedis()
31 | var rlist model.TxL
32 | if ok {
33 | err := json.Unmarshal([]byte(list), &rlist)
34 | if err == nil {
35 | go util.TxWriteRedis(v, rlist)
36 | logger.Info("定时任务:开始更新腾讯云redis数据")
37 | } else {
38 | logger.Error("定时任务:json解析云redis数据失败", err)
39 | }
40 | } else {
41 | logger.Error("定时任务:获取云redis数据失败")
42 | }
43 | }
44 | }
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/middleware/rcron/clusterrefresh.go:
--------------------------------------------------------------------------------
1 | package rcron
2 |
3 | import "github.com/iguidao/redis-manager/src/middleware/logger"
4 |
5 | func ClusterRefresh() {
6 | logger.Info("定时任务:刷新cluster任务启动")
7 | }
8 |
--------------------------------------------------------------------------------
/src/middleware/tools/calculation.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | import "github.com/iguidao/redis-manager/src/middleware/codisapi"
4 |
5 | func CalculationGroup(slist []int, topomstats codisapi.TopomStats) int {
6 | var arrint []int
7 | for _, v := range topomstats.Group.Models {
8 | if !CheckInListInt(v.Id, slist) {
9 | arrint = append(arrint, v.Id)
10 | }
11 |
12 | }
13 | return CalculationArrMax(arrint)
14 | }
15 | func CalculationProxy(slist []int, topomstats codisapi.TopomStats) int {
16 | var arrint []int
17 | for _, v := range topomstats.Proxy.Models {
18 | if !CheckInListInt(v.Id, slist) {
19 | arrint = append(arrint, v.Id)
20 | }
21 | }
22 | return CalculationArrMax(arrint)
23 | }
24 | func CalculationArrMax(arrint []int) (max int) {
25 | max = arrint[0]
26 | for _, v := range arrint {
27 | if v > max {
28 | max = v
29 | }
30 | }
31 | return
32 | }
33 |
34 | func CheckInListInt(val int, slist []int) bool {
35 | for _, v := range slist {
36 | if v == val {
37 | return true
38 | }
39 | }
40 | return false
41 | }
42 |
43 | func CheckInListString(val string, slist []string) bool {
44 | for _, v := range slist {
45 | if v == val {
46 | return true
47 | }
48 | }
49 | return false
50 | }
51 |
52 | func DeleteListString(val string, slist []string) []string {
53 | var resultlist []string
54 | for _, v := range slist {
55 | if v != val {
56 | resultlist = append(resultlist, v)
57 | }
58 | }
59 | return resultlist
60 | }
61 |
62 | func DeleteListint(val int, slist []int) []int {
63 | var resultlist []int
64 | for _, v := range slist {
65 | if v != val {
66 | resultlist = append(resultlist, v)
67 | }
68 | }
69 | return resultlist
70 | }
71 |
--------------------------------------------------------------------------------
/src/middleware/tools/capacity.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 |
7 | "github.com/iguidao/redis-manager/src/middleware/codisapi"
8 | )
9 |
10 | func CapacityGroup(gn int, topom codisapi.Topom) bool {
11 | //计算内存大小
12 | var maxmemory, usememory, onemax, oneuse, maxnum, usenum int
13 | for _, v := range topom.Stats.Group.Stats {
14 | max, err := strconv.Atoi(v.Stats.Maxmemory)
15 | if err == nil {
16 | maxmemory = maxmemory + max
17 | maxnum = maxnum + 1
18 | }
19 | use, err := strconv.Atoi(v.Stats.Used_memory)
20 | if err == nil {
21 | usememory = usememory + use
22 | usenum = usenum + 1
23 | }
24 | }
25 | onemax = maxmemory / maxnum
26 | oneuse = usememory / usenum
27 | fenzi := ((oneuse*gn)/(len(topom.Stats.Group.Models)-gn) + oneuse)
28 | fenmu := onemax
29 | val, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", float64(fenzi)/float64(fenmu)), 64)
30 | if val < 0.8 {
31 | return true
32 | }
33 | return false
34 |
35 | }
36 |
37 | func CapacityProxy(pn int, topom codisapi.Topom) bool {
38 | // 计算proxy的qps
39 | var maxqps int
40 | for _, v := range topom.Stats.Proxy.Stats {
41 | maxqps = maxqps + v.Stats.Ops.Qps
42 | // log.Println(i, v.Stats.Ops.Qps)
43 | }
44 | val, _ := strconv.ParseFloat(fmt.Sprintf("%.2f", float64(maxqps)/float64(len(topom.Stats.Proxy.Models)-pn)), 64)
45 | if val < 40000 {
46 | return true
47 | }
48 | return false
49 | }
50 |
--------------------------------------------------------------------------------
/src/middleware/tools/check.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | import (
4 | "net"
5 | "time"
6 | )
7 |
8 | func CheckStringInArray(target string, str_array []string) bool {
9 | for _, element := range str_array {
10 | if target == element {
11 | return true
12 | }
13 | }
14 | return false
15 | }
16 |
17 | func IsIP(ip string) bool {
18 | address := net.ParseIP(ip)
19 | if address == nil {
20 | return false
21 | } else {
22 | return true
23 | }
24 | }
25 |
26 | func CheckIpPort(addr string, timeout int) bool {
27 | conn, err := net.DialTimeout("tcp", addr, time.Duration(timeout)*time.Millisecond)
28 | if err != nil {
29 | return false
30 | }
31 | defer conn.Close()
32 | return true
33 | }
34 |
--------------------------------------------------------------------------------
/src/middleware/tools/codisauth.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | import (
4 | "bytes"
5 | "crypto/sha256"
6 | "fmt"
7 | )
8 |
9 | func NewXAuth(segs ...string) string {
10 | t := &bytes.Buffer{}
11 | fmt.Fprintf(t, "Codis-XAuth")
12 | for _, s := range segs {
13 | fmt.Fprintf(t, "-[%s]", s)
14 | }
15 | b := sha256.Sum256(t.Bytes())
16 | return fmt.Sprintf("%x", b[:16])
17 | }
18 |
--------------------------------------------------------------------------------
/src/middleware/tools/trans.go:
--------------------------------------------------------------------------------
1 | package tools
2 |
3 | import (
4 | "encoding/json"
5 |
6 | "github.com/iguidao/redis-manager/src/middleware/logger"
7 | )
8 |
9 | func JsonToMap(jsonstr string) map[string]interface{} {
10 | m := make(map[string]interface{})
11 | err := json.Unmarshal([]byte(jsonstr), &m)
12 | if err != nil {
13 | logger.Error("json to map fail: ", jsonstr, " error: ", err)
14 | return nil
15 | }
16 | return m
17 | }
18 |
--------------------------------------------------------------------------------
/src/middleware/txcloud/clist.go:
--------------------------------------------------------------------------------
1 | package txcloud
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/iguidao/redis-manager/src/middleware/logger"
7 | "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
8 | cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
9 | tredis "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/redis/v20180412"
10 | )
11 |
12 | func TxListRedis() (string, bool) {
13 | // 实例化一个请求对象,每个接口都会对应一个request对象
14 | request := tredis.NewDescribeInstancesRequest()
15 | // 返回的resp是一个DescribeInstancesResponse的实例,与请求对象对应
16 | response, err := TxRedisApi.DescribeInstances(request)
17 | if _, ok := err.(*errors.TencentCloudSDKError); ok {
18 | logger.Error("An Redis API error has returned: ", err)
19 | return "", false
20 | }
21 | if err != nil {
22 | logger.Error("List Tx Cloud Redis Error: ", err)
23 | return "", false
24 | }
25 | // 输出json格式的字符串回包
26 | return response.ToJsonString(), true
27 | }
28 |
29 | func TxListRegion() (string, bool) {
30 | // 实例化一个请求对象,每个接口都会对应一个request对象
31 | request := cvm.NewDescribeRegionsRequest()
32 | // 返回的resp是一个DescribeRegionsResponse的实例,与请求对象对应
33 | response, err := TxCvmApi.DescribeRegions(request)
34 | if _, ok := err.(*errors.TencentCloudSDKError); ok {
35 | fmt.Printf("An Region API error has returned: %s", err)
36 | return "", false
37 | }
38 | if err != nil {
39 | logger.Error("List Tx Cloud Region Error: ", err)
40 | return "", false
41 | }
42 | // 输出json格式的字符串回包
43 | return response.ToJsonString(), true
44 | }
45 |
--------------------------------------------------------------------------------
/src/middleware/txcloud/connect.go:
--------------------------------------------------------------------------------
1 | package txcloud
2 |
3 | import (
4 | "github.com/iguidao/redis-manager/src/middleware/logger"
5 | "github.com/iguidao/redis-manager/src/middleware/model"
6 | "github.com/iguidao/redis-manager/src/middleware/mysql"
7 | "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
8 | "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
9 | cvm "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cvm/v20170312"
10 | dbbrain "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dbbrain/v20210527"
11 | tredis "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/redis/v20180412"
12 | )
13 |
14 | // DB as the mysql client
15 | var TxRedisApi *tredis.Client
16 |
17 | func TxRedisContent(region string) bool {
18 | var err error
19 | credential := common.NewCredential(
20 | mysql.DB.GetOneCfgValue(model.TXSECRETID),
21 | mysql.DB.GetOneCfgValue(model.TXSECRETKEY),
22 | )
23 | // 实例化一个client选项,可选的,没有特殊需求可以跳过
24 | cpf := profile.NewClientProfile()
25 | cpf.HttpProfile.Endpoint = mysql.DB.GetOneCfgValue(model.TXAPIURL)
26 | // 实例化要请求产品的client对象,clientProfile是可选的
27 | TxRedisApi, err = tredis.NewClient(credential, region, cpf)
28 | if err != nil {
29 | logger.Error("conenct tx cloud redis error: ", err)
30 | return false
31 | }
32 | return true
33 | }
34 |
35 | // CVM
36 | var TxCvmApi *cvm.Client
37 |
38 | func TxCvmContent() bool {
39 | var err error
40 | credential := common.NewCredential(
41 | mysql.DB.GetOneCfgValue(model.TXSECRETID),
42 | mysql.DB.GetOneCfgValue(model.TXSECRETKEY),
43 | )
44 | // 实例化一个client选项,可选的,没有特殊需求可以跳过
45 | cpf := profile.NewClientProfile()
46 | cpf.HttpProfile.Endpoint = "cvm.tencentcloudapi.com"
47 | // 实例化要请求产品的client对象,clientProfile是可选的
48 | TxCvmApi, err = cvm.NewClient(credential, "", cpf)
49 | if err != nil {
50 | logger.Error("conenct tx cloud region error: ", err)
51 | return false
52 | }
53 | return true
54 | }
55 |
56 | // Dbrain
57 | var TxDbrainApi *dbbrain.Client
58 |
59 | func TxDbrainContent(region string) bool {
60 | var err error
61 | credential := common.NewCredential(
62 | mysql.DB.GetOneCfgValue(model.TXSECRETID),
63 | mysql.DB.GetOneCfgValue(model.TXSECRETKEY),
64 | )
65 | // 实例化一个client选项,可选的,没有特殊需求可以跳过
66 | cpf := profile.NewClientProfile()
67 | cpf.HttpProfile.Endpoint = "dbbrain.tencentcloudapi.com"
68 | // 实例化要请求产品的client对象,clientProfile是可选的
69 | TxDbrainApi, err = dbbrain.NewClient(credential, region, cpf)
70 | if err != nil {
71 | logger.Error("conenct tx cloud region error: ", err)
72 | return false
73 | }
74 | return true
75 | }
76 |
--------------------------------------------------------------------------------
/src/middleware/txcloud/opkey.go:
--------------------------------------------------------------------------------
1 | package txcloud
2 |
3 | import (
4 | "fmt"
5 | "time"
6 |
7 | "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
8 | "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
9 | dbbrain "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dbbrain/v20210527"
10 | tredis "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/redis/v20180412"
11 | )
12 |
13 | func TxHostKey(instanceid string) (string, bool) {
14 |
15 | // 实例化一个请求对象,每个接口都会对应一个request对象
16 | request := tredis.NewDescribeInstanceMonitorHotKeyRequest()
17 |
18 | request.InstanceId = common.StringPtr(instanceid)
19 | request.SpanType = common.Int64Ptr(1)
20 |
21 | // 返回的resp是一个DescribeInstanceMonitorHotKeyResponse的实例,与请求对象对应
22 | response, err := TxRedisApi.DescribeInstanceMonitorHotKey(request)
23 | if _, ok := err.(*errors.TencentCloudSDKError); ok {
24 | fmt.Printf("An API error has returned: %s", err)
25 | return "", false
26 | }
27 | if err != nil {
28 | panic(err)
29 | }
30 | // 输出json格式的字符串回包
31 | return response.ToJsonString(), true
32 | }
33 |
34 | func TxProxySlowKey(instanceid, starttime, endtime string) (string, bool) {
35 | // todaynow := time.Now().Format("2006") + "-" + time.Now().Format("01") + "-" + time.Now().Format("02")
36 | request := tredis.NewDescribeProxySlowLogRequest()
37 |
38 | request.InstanceId = common.StringPtr(instanceid)
39 | request.BeginTime = common.StringPtr(starttime)
40 | request.EndTime = common.StringPtr(endtime)
41 |
42 | // 返回的resp是一个DescribeProxySlowLogResponse的实例,与请求对象对应
43 | response, err := TxRedisApi.DescribeProxySlowLog(request)
44 | if _, ok := err.(*errors.TencentCloudSDKError); ok {
45 | fmt.Printf("An API error has returned: %s", err)
46 | return "", false
47 | }
48 | if err != nil {
49 | panic(err)
50 | }
51 | // 输出json格式的字符串回包
52 | return response.ToJsonString(), true
53 | }
54 | func TxRedisSlowKey(instanceid, starttime, endtime string) (string, bool) {
55 |
56 | // 实例化一个请求对象,每个接口都会对应一个request对象
57 | request := tredis.NewDescribeSlowLogRequest()
58 |
59 | request.InstanceId = common.StringPtr(instanceid)
60 | request.BeginTime = common.StringPtr(starttime)
61 | request.EndTime = common.StringPtr(endtime)
62 |
63 | // 返回的resp是一个DescribeSlowLogResponse的实例,与请求对象对应
64 | response, err := TxRedisApi.DescribeSlowLog(request)
65 | if _, ok := err.(*errors.TencentCloudSDKError); ok {
66 | fmt.Printf("An API error has returned: %s", err)
67 | return "", false
68 | }
69 | if err != nil {
70 | panic(err)
71 | }
72 | // 输出json格式的字符串回包
73 | return response.ToJsonString(), true
74 | }
75 |
76 | func TxBigKey(instanceid string) (string, bool) {
77 | todaynow := time.Now().Format("2006") + "-" + time.Now().Format("01") + "-" + time.Now().Format("02")
78 |
79 | request := dbbrain.NewDescribeRedisTopBigKeysRequest()
80 |
81 | request.InstanceId = common.StringPtr(instanceid)
82 | request.Date = common.StringPtr(todaynow)
83 | request.Product = common.StringPtr("redis")
84 |
85 | // 返回的resp是一个DescribeRedisTopBigKeysResponse的实例,与请求对象对应
86 | response, err := TxDbrainApi.DescribeRedisTopBigKeys(request)
87 | if _, ok := err.(*errors.TencentCloudSDKError); ok {
88 | fmt.Printf("An API error has returned: %s", err)
89 | return "", false
90 | }
91 | if err != nil {
92 | panic(err)
93 | }
94 | // 输出json格式的字符串回包 return response.ToJsonString(), true
95 | return response.ToJsonString(), true
96 | }
97 |
--------------------------------------------------------------------------------
/src/middleware/useride/daoyou.go:
--------------------------------------------------------------------------------
1 | package useride
2 |
3 | import (
4 | "github.com/iguidao/redis-manager/src/middleware/logger"
5 | "github.com/iguidao/redis-manager/src/middleware/mysql"
6 | )
7 |
8 | func Gd_login(username, password string) bool {
9 | scrypt_password := Get_scrypt(password)
10 | //mysql_password := mysql.Get_user(phone)
11 | mysql_password, err := mysql.DB.FindUserPassword(username)
12 | if err != nil {
13 | logger.Error("数据库查询错误: ", err, username)
14 | return false
15 | }
16 |
17 | if scrypt_password != mysql_password.Password {
18 | return false
19 | } else {
20 | return true
21 | }
22 | //return "ok"
23 |
24 | }
25 |
26 | // func CacheUserinfo(token string, Phone int64) {
27 | // opredis.RegisterAuthRedis(token)
28 | // userinfo := mysql.DB.UserInfo(Phone)
29 | // userconver := util.UserConverge(userinfo)
30 | // jsonBody, _ := json.Marshal(userconver)
31 | // opredis.WriteUserRedis(string(jsonBody), userinfo.Base.ID)
32 | // }
33 | // func RefreshUserinfo(token, oldtoken string) {
34 | // opredis.RegisterAuthRedis(token)
35 | // opredis.DelAuthRedis(oldtoken)
36 | // }
37 |
38 | // // 查mysql用户信息存redis
39 | // func GetUserToRedis(id string) util.UserInfo {
40 | // userinfo := mysql.DB.UserIdInfo(id)
41 | // userconver := util.UserConverge(userinfo)
42 | // jsonBody, _ := json.Marshal(userconver)
43 | // opredis.WriteUserRedis(string(jsonBody), userinfo.Base.ID)
44 | // return userconver
45 | // }
46 |
--------------------------------------------------------------------------------
/src/middleware/useride/password.go:
--------------------------------------------------------------------------------
1 | package useride
2 |
3 | import (
4 | "bytes"
5 | "encoding/base64"
6 | "log"
7 |
8 | "golang.org/x/crypto/scrypt"
9 | )
10 |
11 | // 密码加密
12 | func Get_scrypt(password string) string {
13 | pw_new := string([]byte(password)[:3])
14 | user_src := []byte(pw_new)
15 | other_src := []byte("redis")
16 | all_src := [][]byte{user_src, other_src}
17 | salt := bytes.Join(all_src, []byte{})
18 | dk, err := scrypt.Key([]byte(password), salt, 32768, 8, 1, 32)
19 | if err != nil {
20 | log.Fatal(err)
21 | }
22 | user_password := base64.StdEncoding.EncodeToString(dk)
23 | return user_password
24 | }
25 |
--------------------------------------------------------------------------------
/src/middleware/useride/tocken.go:
--------------------------------------------------------------------------------
1 | package useride
2 |
3 | import (
4 | "time"
5 |
6 | "github.com/iguidao/redis-manager/src/cfg"
7 | "github.com/iguidao/redis-manager/src/middleware/logger"
8 |
9 | jwt_lib "github.com/golang-jwt/jwt/v4"
10 | )
11 |
12 | type Token struct {
13 | Token string `json:"token"`
14 | }
15 |
16 | func Token_Get() string {
17 | SecretKey := cfg.Get_Info_String("secretkey")
18 | token := jwt_lib.New(jwt_lib.GetSigningMethod("HS256"))
19 | claims := make(jwt_lib.MapClaims)
20 | claims["exp"] = time.Now().Add(time.Hour * time.Duration(1)).Unix()
21 | claims["iat"] = time.Now().Unix()
22 | token.Claims = claims
23 |
24 | tokenString, err := token.SignedString([]byte(SecretKey))
25 | if err != nil {
26 | logger.Error("Error while signing the token", err)
27 | }
28 |
29 | //response := Token{tokenString}
30 | return tokenString
31 | }
32 |
--------------------------------------------------------------------------------
/src/middleware/util/cloud.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "github.com/iguidao/redis-manager/src/middleware/logger"
5 | "github.com/iguidao/redis-manager/src/middleware/model"
6 | "github.com/iguidao/redis-manager/src/middleware/mysql"
7 | )
8 |
9 | func TxWriteRedis(cloud string, rlist model.TxL) {
10 | for _, v := range rlist.Response.InstanceSet {
11 | if !mysql.DB.ExistCloudredisId(cloud, v.InstanceId) {
12 | id, ok := mysql.DB.AddTxCloudRedis(cloud, v)
13 | if ok {
14 | logger.Info("write ", cloud, " redis to mysql ok: ", id, "instanceid: ", v.InstanceId)
15 | } else {
16 | logger.Error("write ", cloud, " redis to mysql false: ", id, "instanceid: ", v.InstanceId)
17 | }
18 | } else {
19 | ok := mysql.DB.UppdateTxCloudRedis(cloud, v)
20 | if ok {
21 | logger.Info("update ", cloud, " redis to mysql ok: ", "instanceid: ", v.InstanceId)
22 | } else {
23 | logger.Error("update ", cloud, " redis to mysql false: ", "instanceid: ", v.InstanceId)
24 | }
25 | }
26 | }
27 | }
28 |
29 | func AliWriteRedis(cloud string, rlist model.AliRedis) {
30 | for _, v := range rlist.Instances.KVStoreInstance {
31 | if !mysql.DB.ExistCloudredisId(cloud, v.InstanceId) {
32 | id, ok := mysql.DB.AddAliCloudRedis(cloud, v)
33 | if ok {
34 | logger.Info("write ", cloud, " redis to mysql ok: ", id, "instanceid: ", v.InstanceId)
35 | } else {
36 | logger.Error("write ", cloud, " redis to mysql false: ", id, "instanceid: ", v.InstanceId)
37 | }
38 | } else {
39 | ok := mysql.DB.UppdateAliCloudRedis(cloud, v)
40 | if ok {
41 | logger.Info("update ", cloud, " redis to mysql ok: ", "instanceid: ", v.InstanceId)
42 | } else {
43 | logger.Error("update ", cloud, " redis to mysql false: ", "instanceid: ", v.InstanceId)
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/middleware/util/converge.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | func ReturnDefaultModel(val string, list map[string]string) string {
4 | for i, v := range list {
5 | if val == i {
6 | return v
7 | }
8 | }
9 | return "自定义配置"
10 | }
11 |
12 | // func UserConverge(muserinfo mysql.RdUser) UserInfo {
13 | // var userinfo UserInfo
14 | // userinfo.Uid = muserinfo.Base.ID
15 | // userinfo.UserName = muserinfo.NickName
16 | // userinfo.Identity = muserinfo.Identity
17 | // // userinfo.Phone = muserinfo.UserPhone
18 | // userinfo.AvatarUrl = muserinfo.AvatarUrl
19 | // userinfo.CreatedAt = muserinfo.Base.CreatedAt
20 | // return userinfo
21 | // }
22 |
23 | // func claimsClaimsConverge(mclaims *Claims) UserJWTInfo {
24 | // var userinfo UserJWTInfo
25 | // userinfo.ID = mclaims.UserId
26 | // userinfo.UserName = mclaims.UserName
27 | // // userinfo.UserPhone = mclaims.UserPhone
28 | // // userinfo.CreatedAt = mclaims.CreaTime
29 | // return userinfo
30 | // }
31 |
--------------------------------------------------------------------------------
/src/middleware/util/jwt.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "github.com/iguidao/redis-manager/src/cfg"
5 | "github.com/iguidao/redis-manager/src/middleware/mysql"
6 |
7 | "errors"
8 | "time"
9 |
10 | jwt "github.com/golang-jwt/jwt/v4"
11 | )
12 |
13 | var jwtSecret = []byte(cfg.Get_Info_String("secretkey"))
14 |
15 | var (
16 | TokenExpired error = errors.New("Token is expired")
17 | TokenNotValidYet error = errors.New("Token not active yet")
18 | TokenMalformed error = errors.New("That's not even a token")
19 | TokenInvalid error = errors.New("Couldn't handle this token:")
20 | SignKey string = "cfun"
21 | )
22 |
23 | type Claims struct {
24 | UserId int `json:"userid"`
25 | UserName string `json:"username"`
26 | UserType string `json:"usertype"`
27 | // UserPhone int64 `json:"UserPhone"`
28 | // Password string `json:"Password"`
29 | // CreaTime time.Time `json:"CreaTime"`
30 | jwt.StandardClaims
31 | }
32 |
33 | // CreateToken 生成一个token
34 | func CreateToken(claims Claims) (string, error) {
35 | var SigningKey []byte
36 | token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
37 | return token.SignedString(SigningKey)
38 | }
39 |
40 | func GenerateToken(Username string, Password string) (string, string, error) {
41 |
42 | nowTime := time.Now()
43 | expireTime := nowTime.Add(168 * time.Hour)
44 | muserinfo := mysql.DB.UserInfo(Username)
45 | username := muserinfo.UserName
46 | // userid := strconv.Itoa()
47 | usertype := muserinfo.UserType
48 | // CreaTime := muserinfo.Base.CreatedAt
49 | claims := Claims{
50 | muserinfo.Base.ID,
51 | username,
52 | usertype,
53 | // UserPhone,
54 | // Password,
55 | // CreaTime,
56 | jwt.StandardClaims{
57 | ExpiresAt: expireTime.Unix(),
58 | Issuer: "redis-manager",
59 | },
60 | }
61 | token, err := CreateToken(claims)
62 | // tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
63 | // token, err := tokenClaims.SignedString(jwtSecret)
64 | return token, usertype, err
65 | }
66 |
67 | func ParseToken(token string) (*Claims, error) {
68 | tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
69 | return jwtSecret, nil
70 | })
71 | if tokenClaims != nil {
72 | if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
73 | return claims, nil
74 | }
75 | }
76 |
77 | return nil, err
78 | }
79 |
80 | func RefreshToken(tokenString string) (string, error) {
81 | // var username string
82 | jwt.TimeFunc = func() time.Time {
83 | return time.Unix(0, 0)
84 | }
85 | token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
86 | return jwtSecret, nil
87 | })
88 | if err != nil {
89 | return "", err
90 | }
91 | if claims, ok := token.Claims.(*Claims); ok && token.Valid {
92 | // userinfo = mysql.DB.UserInfo(claims.UserPhone)
93 | // username = userinfo.UserName
94 | jwt.TimeFunc = time.Now
95 | claims.StandardClaims.ExpiresAt = time.Now().Add(3 * time.Hour).Unix()
96 | ctoekn, err := CreateToken(*claims)
97 | return ctoekn, err
98 | }
99 | return "", TokenInvalid
100 | }
101 |
102 | func GetUserInfo(tokenString string) (UserJWTInfo, error) {
103 | // var username string
104 | var userinfo UserJWTInfo
105 | var err error
106 | jwt.TimeFunc = func() time.Time {
107 | return time.Unix(0, 0)
108 | }
109 | token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
110 | return jwtSecret, nil
111 | })
112 | if err != nil {
113 | return userinfo, err
114 | }
115 | if claims, ok := token.Claims.(*Claims); ok && token.Valid {
116 | userinfo = UserJWTInfo{
117 | claims.UserId,
118 | claims.UserName,
119 | claims.UserType,
120 | }
121 | // muserinfo := mysql.DB.UserInfo(claims.UserPhone)
122 | // userinfo = UserConverge(claims)
123 | return userinfo, err
124 | }
125 | return userinfo, err
126 | }
127 |
--------------------------------------------------------------------------------
/src/middleware/util/pagination.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "strconv"
5 |
6 | "github.com/gin-gonic/gin"
7 | )
8 |
9 | // GetPage get page parameters
10 | func GetPage(c *gin.Context) (int, int) {
11 | page, err := strconv.Atoi(c.DefaultQuery("page", "0"))
12 | if err != nil {
13 | panic(err)
14 | }
15 | size, err := strconv.Atoi(c.DefaultQuery("size", "10"))
16 | if err != nil {
17 | panic(err)
18 | }
19 |
20 | return page, size
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/src/middleware/util/parameter.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "regexp"
5 | )
6 |
7 | //email verify
8 | func VerifyEmailFormat(email string) bool {
9 | //pattern := `\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*` //匹配电子邮箱
10 | pattern := `^[0-9a-z][_.0-9a-z-]{0,31}@([0-9a-z][0-9a-z-]{0,30}[0-9a-z]\.){1,4}[a-z]{2,4}$`
11 |
12 | reg := regexp.MustCompile(pattern)
13 | return reg.MatchString(email)
14 | }
15 |
--------------------------------------------------------------------------------
/src/middleware/util/utilstruct.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import "time"
4 |
5 | // user info tbale
6 | type UserJWTInfo struct {
7 | Uid int
8 | UserName string
9 | UserType string
10 | // UserPhone int64
11 | // CreatedAt time.Time
12 | }
13 |
14 | // user info tbale
15 | type UserInfo struct {
16 | Uid int
17 | UserName string
18 | Identity string
19 | // UserPhone int64
20 | AvatarUrl string
21 | CreatedAt time.Time
22 | }
23 |
--------------------------------------------------------------------------------
/src/rhttp/router.go:
--------------------------------------------------------------------------------
1 | package rhttp
2 |
3 | import (
4 | "io"
5 | "io/ioutil"
6 | "os"
7 | "strings"
8 | "time"
9 |
10 | "github.com/gin-contrib/cors"
11 | "github.com/gin-contrib/static"
12 | "github.com/gin-gonic/gin"
13 | "github.com/iguidao/redis-manager/src/cfg"
14 | "github.com/iguidao/redis-manager/src/middleware/jwt"
15 | "github.com/iguidao/redis-manager/src/middleware/model"
16 | v1 "github.com/iguidao/redis-manager/src/rhttp/v1"
17 | )
18 |
19 | // NewServer return a configured http server of gin
20 | func NewServer() *gin.Engine {
21 | // 存储日志文件代码
22 | logpath := "./logs/" + cfg.Get_Info_String("logapipath")
23 | gin.DisableConsoleColor()
24 | f, _ := os.Create(logpath)
25 | gin.DefaultWriter = io.MultiWriter(f)
26 | r := gin.Default()
27 |
28 | // 跨域信息
29 | r.Use(cors.New(cors.Config{
30 | AllowAllOrigins: true,
31 | AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTION"},
32 | AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization"},
33 | AllowCredentials: true,
34 | MaxAge: 12 * time.Hour,
35 | }))
36 |
37 | r.Use(static.Serve("/", static.LocalFile("website", true)))
38 | r.NoRoute(func(c *gin.Context) {
39 | accept := c.Request.Header.Get("Accept")
40 | flag := strings.Contains(accept, "text/html")
41 | if flag {
42 | content, err := ioutil.ReadFile("dist/index.html")
43 | if (err) != nil {
44 | c.Writer.WriteHeader(404)
45 | c.Writer.WriteString("Not Found")
46 | return
47 | }
48 | c.Writer.WriteHeader(200)
49 | c.Writer.Header().Add("Accept", "text/html")
50 | c.Writer.Write((content))
51 | c.Writer.Flush()
52 | }
53 | })
54 | // vue配置
55 | // r.Static("/assets", "./website/assets")
56 | // r.LoadHTMLFiles("./website/index.html")
57 | home := r.Group("")
58 | {
59 | home.GET("/", v1.Home) //主页接口
60 | }
61 | base := r.Group("/redis-manager/base/v1")
62 | {
63 | base.GET("/health", v1.HealthCheck) //自检接口
64 | }
65 | login := r.Group("/redis-manager/auth/v1")
66 | {
67 | login.POST("/sign-in", v1.Login) //登陆接口
68 |
69 | }
70 | public := r.Group("/redis-manager/public/v1")
71 | {
72 | public.POST("/analysisrdb", v1.AnalysisRdb) //分析dump文件
73 | }
74 | auth := r.Group("/redis-manager/auth/v1")
75 | auth.Use(jwt.JWT())
76 | {
77 | auth.POST("/password", v1.ChangUserPassword) //更改用户密码
78 | auth.POST("/refresh", v1.Refresh) //刷新接口
79 | }
80 | board := r.Group(model.PATHBOARD)
81 | board.Use(jwt.JWT())
82 | {
83 | board.GET("/desc", v1.BoardDesc) //board页面
84 | }
85 | history := r.Group(model.PATHHISTORY)
86 | history.Use(jwt.JWT())
87 | {
88 | history.GET("/list", v1.OpHistory) //查看历史操作记录
89 | }
90 | cfg := r.Group(model.PATHCFG)
91 | cfg.Use(jwt.JWT())
92 | {
93 | cfg.POST("/update", v1.CfgUpdate) // 添加配置信息
94 | cfg.GET("/list", v1.CfgList) // 获取配置信息
95 | cfg.DELETE("/del", v1.CfgDelete) //删除配置
96 | cfg.POST("/adddefault", v1.CfgAddDefault) //添加默认key
97 | cfg.GET("/listdefault", v1.CfgListDefault) //返回默认配置key
98 | }
99 | codis := r.Group(model.PATHCODIS)
100 | codis.Use(jwt.JWT())
101 | {
102 | codis.POST("/add", v1.CodisAdd) //添加codis的平台地址
103 | codis.GET("/list", v1.CodisList) //列出有哪些平台地址
104 | codis.GET("/cluster", v1.CodisClusterList) //列出该平台地址有多少个集群
105 | codis.GET("/group", v1.CodisGroup) //列出该集群有多少个group
106 | codis.POST("/opnode", v1.CodisOpNode) //针对codis的proxy和server节点进行操作
107 | }
108 | cloud := r.Group(model.PATHCLOUD)
109 | cloud.Use(jwt.JWT())
110 | {
111 | cloud.GET("/region", v1.RegionList) //列出云的地域
112 | cloud.GET("/list", v1.CloudList) //列出云的集群列表
113 | cloud.POST("/password", v1.ChangeCloudPassword) //修改数据库保存密码
114 | cloud.POST("/size", v1.ChangeSize) //修改集群大小
115 | cloud.POST("/add", v1.CloudAdd) // 添加集群
116 | cloud.DELETE("/del", v1.CloudDel) //删除集群
117 | }
118 | cluster := r.Group(model.PATHCLUSTER)
119 | cluster.Use(jwt.JWT())
120 | {
121 | cluster.GET("/list", v1.ClusterList) //列出所有集群
122 | cluster.GET("/nodes", v1.NodeList) // 列出集群的node
123 | cluster.GET("/masters", v1.MasterList) //列出master地址
124 | cluster.POST("/add", v1.ClusterAdd) //添加集群
125 | }
126 | cli := r.Group(model.PATHCLI)
127 | cli.Use(jwt.JWT())
128 | {
129 | cli.POST("/opkey", v1.OpKey) //对key进行操作
130 | }
131 | user := r.Group(model.PATHUSER)
132 | user.Use(jwt.JWT())
133 | {
134 | user.POST("/add", v1.AddUser) //新增用户接口
135 | user.GET("/list", v1.ListUser) //列出所有用户
136 | user.DELETE("/del", v1.DelUser) //删除用户
137 | user.POST("/change", v1.ChangUserType) //更改用户属性
138 | user.GET("/utype", v1.ListUserType) //获取用户身份列表
139 | }
140 | rule := r.Group(model.PATHRULE)
141 | rule.Use(jwt.JWT())
142 | {
143 | rule.POST("/add", v1.AddRule) //添加规则
144 | rule.DELETE("/del", v1.DelRule) //删除规则
145 | rule.GET("/list", v1.AllRule) //查看所有规则
146 | rule.GET("/cfg", v1.GetRuleCfg) //查看默认配置
147 | }
148 |
149 | r.NoMethod(v1.MethodFails)
150 | r.NoRoute(v1.RouterNotFound)
151 | return r
152 | }
153 |
--------------------------------------------------------------------------------
/src/rhttp/v1/base.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | //"log"
5 |
6 | "io/ioutil"
7 | "net/http"
8 | "strings"
9 |
10 | "github.com/iguidao/redis-manager/src/hsc"
11 |
12 | "github.com/gin-gonic/gin"
13 | )
14 |
15 | func HealthCheck(c *gin.Context) {
16 | code := hsc.SUCCESS
17 | c.JSON(http.StatusOK, gin.H{
18 | "errorCode": code,
19 | "msg": hsc.GetMsg(code),
20 | "data": true,
21 | })
22 | // c.JSON(http.StatusOK, gin.H{"ok": true})
23 | }
24 |
25 | func HandleNotFound(c *gin.Context) {
26 | code := hsc.NOT_FOUND
27 | c.JSON(http.StatusOK, gin.H{
28 | "errorCode": code,
29 | "msg": hsc.GetMsg(code),
30 | "data": false,
31 | })
32 | }
33 |
34 | func MethodFails(c *gin.Context) {
35 | code := hsc.Method_FAILS
36 | c.JSON(http.StatusOK, gin.H{
37 | "errorCode": code,
38 | "msg": hsc.GetMsg(code),
39 | "data": false,
40 | })
41 | }
42 |
43 | func RouterNotFound(c *gin.Context) {
44 | accept := c.Request.Header.Get("Accept")
45 | flag := strings.Contains(accept, "text/html")
46 | if flag {
47 | content, err := ioutil.ReadFile("./website/index.html")
48 | if (err) != nil {
49 | c.Writer.WriteHeader(404)
50 | c.Writer.WriteString("Not Found")
51 | return
52 | }
53 | c.Writer.WriteHeader(200)
54 | c.Writer.Header().Add("Accept", "text/html")
55 | c.Writer.Write((content))
56 | c.Writer.Flush()
57 | }
58 | }
59 |
60 | func HttpTemplate(c *gin.Context) {
61 | data := make(map[string]interface{})
62 | code := hsc.SUCCESS
63 | c.JSON(http.StatusOK, gin.H{
64 | "errorCode": code,
65 | "msg": hsc.GetMsg(code),
66 | "data": data,
67 | })
68 | }
69 |
70 | func Cookie(c *gin.Context) {
71 | cookie, err := c.Cookie("gin_cookie")
72 | if err != nil {
73 | cookie = "NotSet"
74 | c.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
75 | }
76 | c.JSON(http.StatusOK, gin.H{"cookie": cookie})
77 | }
78 |
--------------------------------------------------------------------------------
/src/rhttp/v1/board.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/gin-gonic/gin"
7 | "github.com/iguidao/redis-manager/src/hsc"
8 | "github.com/iguidao/redis-manager/src/middleware/mysql"
9 | )
10 |
11 | func BoardDesc(c *gin.Context) {
12 | code := hsc.SUCCESS
13 | result := make(map[string]int64)
14 | result["aliredis"] = mysql.DB.GetCloudNumber("aliredis")
15 | result["txredis"] = mysql.DB.GetCloudNumber("txredis")
16 | result["codis"] = mysql.DB.GetCodisNumber()
17 | result["cluster"] = mysql.DB.GetClusterNumber()
18 | c.JSON(http.StatusOK, gin.H{
19 | "errorCode": code,
20 | "msg": hsc.GetMsg(code),
21 | "data": result,
22 | })
23 | }
24 |
--------------------------------------------------------------------------------
/src/rhttp/v1/cfg.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 |
7 | "github.com/gin-gonic/gin"
8 | "github.com/iguidao/redis-manager/src/hsc"
9 | "github.com/iguidao/redis-manager/src/middleware/logger"
10 | "github.com/iguidao/redis-manager/src/middleware/model"
11 | "github.com/iguidao/redis-manager/src/middleware/mysql"
12 | )
13 |
14 | func CfgListDefault(c *gin.Context) {
15 | code := hsc.SUCCESS
16 | listcfg := model.DefaultName
17 | var result []map[string]string
18 | for k, v := range listcfg {
19 | cfg := make(map[string]string)
20 | cfg["label"] = v
21 | cfg["value"] = k
22 | result = append(result, cfg)
23 | }
24 | c.JSON(http.StatusOK, gin.H{
25 | "errorCode": code,
26 | "msg": hsc.GetMsg(code),
27 | "data": result,
28 | })
29 | }
30 |
31 | func CfgAddDefault(c *gin.Context) {
32 | code := hsc.SUCCESS
33 | var cfg ConfigInfo
34 | err := c.BindJSON(&cfg)
35 | if err != nil {
36 | code = hsc.INVALID_PARAMS
37 | logger.Error("Config add Default error: ", err)
38 | } else {
39 | if mysql.DB.ExistCfg(model.CC) {
40 | custom := mysql.DB.GetOneCfg(model.CC)
41 | value := custom.Value + "," + cfg.Value
42 | if !mysql.DB.UpdateCfg(model.CC, value) {
43 | logger.Error("update cfg error")
44 | code = hsc.ERROR
45 | }
46 | } else {
47 | username, _ := c.Get("UserId")
48 | urlinfo := c.Request.URL
49 | jsonBody, _ := json.Marshal(cfg)
50 | method := c.Request.Method
51 | go mysql.DB.AddHistory(username.(int), method+":"+urlinfo.Path, string(jsonBody))
52 | _, ok := mysql.DB.AddCfg(model.CC, cfg.Value, model.CN)
53 | if !ok {
54 | logger.Error("add cfg error")
55 | code = hsc.ERROR
56 | }
57 | }
58 | }
59 |
60 | c.JSON(http.StatusOK, gin.H{
61 | "errorCode": code,
62 | "msg": hsc.GetMsg(code),
63 | "data": hsc.GetMsg(code),
64 | })
65 | }
66 |
67 | func CfgList(c *gin.Context) {
68 | code := hsc.SUCCESS
69 | result := make(map[string]interface{})
70 | cfglist := mysql.DB.GetAllCfg()
71 | result["lists"] = cfglist
72 | result["total"] = len(cfglist)
73 | c.JSON(http.StatusOK, gin.H{
74 | "errorCode": code,
75 | "msg": hsc.GetMsg(code),
76 | "data": result,
77 | })
78 | }
79 |
80 | func CfgUpdate(c *gin.Context) {
81 | code := hsc.SUCCESS
82 | var cfg ConfigInfo
83 | var result int
84 | var ok bool
85 | err := c.BindJSON(&cfg)
86 | if err != nil || cfg.Key == "" || cfg.Value == "" {
87 | code = hsc.INVALID_PARAMS
88 | logger.Error("Config add error: ", err)
89 | } else {
90 | username, _ := c.Get("UserId")
91 | urlinfo := c.Request.URL
92 | jsonBody, _ := json.Marshal(cfg)
93 | method := c.Request.Method
94 | go mysql.DB.AddHistory(username.(int), method+":"+urlinfo.Path, string(jsonBody))
95 | name := model.DefaultName[cfg.Key]
96 | if mysql.DB.ExistCfg(cfg.Key) {
97 | if !mysql.DB.UpdateCfg(cfg.Key, cfg.Value) {
98 | code = hsc.ERROR
99 | }
100 | } else {
101 | result, ok = mysql.DB.AddCfg(name, cfg.Key, cfg.Value)
102 | if !ok {
103 | code = hsc.ERROR
104 | }
105 | }
106 |
107 | }
108 | c.JSON(http.StatusOK, gin.H{
109 | "errorCode": code,
110 | "msg": hsc.GetMsg(code),
111 | "data": result,
112 | })
113 | }
114 |
115 | func CfgDelete(c *gin.Context) {
116 | code := hsc.SUCCESS
117 | key := c.Query("key")
118 | result := false
119 | if key != "" {
120 | username, _ := c.Get("UserId")
121 | urlinfo := c.Request.URL
122 | jsonBody, _ := json.Marshal(key)
123 | method := c.Request.Method
124 | go mysql.DB.AddHistory(username.(int), method+":"+urlinfo.Path, string(jsonBody))
125 | if !mysql.DB.DelCfg(key) {
126 | result = false
127 | code = hsc.SERVER_ERROR
128 | }
129 | } else {
130 | result = false
131 | code = hsc.INVALID_PARAMS
132 | }
133 | c.JSON(http.StatusOK, gin.H{
134 | "errorCode": code,
135 | "msg": hsc.GetMsg(code),
136 | "data": result,
137 | })
138 | }
139 |
--------------------------------------------------------------------------------
/src/rhttp/v1/cluster.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 | "strings"
7 |
8 | "github.com/gin-gonic/gin"
9 | "github.com/iguidao/redis-manager/src/hsc"
10 | "github.com/iguidao/redis-manager/src/middleware/cluster"
11 | "github.com/iguidao/redis-manager/src/middleware/logger"
12 | "github.com/iguidao/redis-manager/src/middleware/model"
13 | "github.com/iguidao/redis-manager/src/middleware/mysql"
14 | "github.com/iguidao/redis-manager/src/middleware/opredis"
15 | )
16 |
17 | func ClusterList(c *gin.Context) {
18 | code := hsc.SUCCESS
19 | result := mysql.DB.GetAllCluster()
20 | c.JSON(http.StatusOK, gin.H{
21 | "errorCode": code,
22 | "msg": hsc.GetMsg(code),
23 | "data": result,
24 | })
25 | // c.JSON(http.StatusOK, gin.H{"ok": true})
26 | }
27 | func NodeList(c *gin.Context) {
28 | code := hsc.SUCCESS
29 | var result []*model.ClusterNodeTables
30 | clusterid := c.Query("cluster_id")
31 | nodes := mysql.DB.GetClusterNode(clusterid)
32 | for _, v := range nodes {
33 | if v.Flags == "master" {
34 | result = append(result, cluster.ClusterConvergeTree(v))
35 | }
36 | }
37 | for _, v := range nodes {
38 | if v.Flags == "slave" {
39 | result = cluster.ClusterUpdateTree(result, v)
40 | }
41 | }
42 | c.JSON(http.StatusOK, gin.H{
43 | "errorCode": code,
44 | "msg": hsc.GetMsg(code),
45 | "data": result,
46 | })
47 | }
48 | func MasterList(c *gin.Context) {
49 | code := hsc.SUCCESS
50 | clusterid := c.Query("cluster_id")
51 | nodes := mysql.DB.GetClusterNodeMaster(clusterid)
52 | c.JSON(http.StatusOK, gin.H{
53 | "errorCode": code,
54 | "msg": hsc.GetMsg(code),
55 | "data": nodes,
56 | })
57 | }
58 | func ClusterAdd(c *gin.Context) {
59 | var clusterinfo AddCluster
60 | result := make(map[string]interface{})
61 | // staff_id, err := strconv.Atoi(UserId)
62 | err := c.BindJSON(&clusterinfo)
63 | code := hsc.ERROR
64 | if err != nil || clusterinfo.Name == "" || clusterinfo.Nodes == "" {
65 | logger.Error("Cluster add error: ", err)
66 | code = hsc.INVALID_PARAMS
67 | } else {
68 | username, _ := c.Get("UserId")
69 | urlinfo := c.Request.URL
70 | jsonBody, _ := json.Marshal(clusterinfo)
71 | method := c.Request.Method
72 | go mysql.DB.AddHistory(username.(int), method+":"+urlinfo.Path, string(jsonBody))
73 |
74 | address := strings.Split(clusterinfo.Nodes, ",")
75 | connectok := false
76 | if opredis.ConnectRedisCluster(address, clusterinfo.Password) {
77 | connectok = true
78 | }
79 | if connectok {
80 | id, ok := mysql.DB.AddCluster(clusterinfo.Name, clusterinfo.Nodes, clusterinfo.Password)
81 | if ok || id != 0 {
82 | nodeinfo := opredis.CGetClusterNode()
83 | for _, v := range nodeinfo {
84 | if len(v) != 0 {
85 | cluster.WriteCluster(id, v)
86 | }
87 | }
88 | code = hsc.SUCCESS
89 | } else {
90 | logger.Error("添加集群到 cluster info 失败")
91 | code = hsc.ERROR_WRITE_MYSQL
92 | }
93 | } else {
94 | logger.Error("链接目标redis异常")
95 | code = hsc.ERROR_NO_CONNEC
96 | }
97 |
98 | }
99 | c.JSON(http.StatusOK, gin.H{
100 | "errorCode": code,
101 | "msg": hsc.GetMsg(code),
102 | "data": result,
103 | })
104 | }
105 |
--------------------------------------------------------------------------------
/src/rhttp/v1/home.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/gin-gonic/gin"
7 | )
8 |
9 | // 首页
10 | func Home(c *gin.Context) {
11 | c.HTML(http.StatusOK, "index.html", gin.H{
12 | "title": "Redis Manager",
13 | })
14 | }
15 |
--------------------------------------------------------------------------------
/src/rhttp/v1/ophistory.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "net/http"
5 |
6 | "github.com/iguidao/redis-manager/src/hsc"
7 | "github.com/iguidao/redis-manager/src/middleware/mysql"
8 |
9 | "github.com/gin-gonic/gin"
10 | )
11 |
12 | func OpHistory(c *gin.Context) {
13 | code := hsc.SUCCESS
14 | result := mysql.DB.GetAllHistory()
15 | c.JSON(http.StatusOK, gin.H{
16 | "errorCode": code,
17 | "msg": hsc.GetMsg(code),
18 | "data": result,
19 | })
20 | // c.JSON(http.StatusOK, gin.H{"ok": true})
21 | }
22 |
--------------------------------------------------------------------------------
/src/rhttp/v1/rule.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | import (
4 | "encoding/json"
5 | "net/http"
6 |
7 | "github.com/gin-gonic/gin"
8 | "github.com/iguidao/redis-manager/src/hsc"
9 | "github.com/iguidao/redis-manager/src/middleware/casbin"
10 | "github.com/iguidao/redis-manager/src/middleware/model"
11 | "github.com/iguidao/redis-manager/src/middleware/mysql"
12 | "github.com/iguidao/redis-manager/src/middleware/util"
13 | )
14 |
15 | func AllRule(c *gin.Context) {
16 | code := hsc.SUCCESS
17 | var result []interface{}
18 | casbinlist := casbin.RuleGet()
19 | if len(casbinlist) != 0 {
20 | for _, v := range casbinlist {
21 | rinfo := make(map[string]string)
22 | rinfo["identity"] = v.V0
23 | rinfo["path"] = v.V1
24 | rinfo["method"] = v.V2
25 | rinfo["note"] = util.ReturnDefaultModel(v.V1, model.DefaultPath)
26 | result = append(result, rinfo)
27 | }
28 | }
29 | c.JSON(http.StatusOK, gin.H{
30 | "errorCode": code,
31 | "msg": hsc.GetMsg(code),
32 | "data": result,
33 | })
34 | }
35 | func GetRuleCfg(c *gin.Context) {
36 | code := hsc.SUCCESS
37 | result := make(map[string]interface{})
38 | var cfglist []map[string]string
39 | for k, v := range model.DefaultPath {
40 | cfg := make(map[string]string)
41 | cfg["label"] = v
42 | cfg["value"] = k
43 | cfglist = append(cfglist, cfg)
44 | }
45 | var methodlist []map[string]string
46 | for k, v := range model.DefaultMethod {
47 | method := make(map[string]string)
48 | method["label"] = v
49 | method["value"] = k
50 | methodlist = append(methodlist, method)
51 | }
52 | result["url"] = cfglist
53 | result["method"] = methodlist
54 | c.JSON(http.StatusOK, gin.H{
55 | "errorCode": code,
56 | "msg": hsc.GetMsg(code),
57 | "data": result,
58 | })
59 | }
60 | func AddRule(c *gin.Context) {
61 | data := make(map[string]interface{})
62 | code := hsc.SUCCESS
63 | var Policy CasbinPolicyJson
64 | err := c.BindJSON(&Policy)
65 | if err != nil {
66 | code = hsc.INVALID_PARAMS
67 | } else {
68 | username, _ := c.Get("UserId")
69 | urlinfo := c.Request.URL
70 | jsonBody, _ := json.Marshal(Policy)
71 | method := c.Request.Method
72 | go mysql.DB.AddHistory(username.(int), method+":"+urlinfo.Path, string(jsonBody))
73 | if !casbin.RuleAdd(Policy.Identity, Policy.Path, Policy.Method) {
74 | code = hsc.ERROR
75 | }
76 | }
77 | c.JSON(http.StatusOK, gin.H{
78 | "errorCode": code,
79 | "msg": hsc.GetMsg(code),
80 | "data": data,
81 | })
82 | }
83 |
84 | func DelRule(c *gin.Context) {
85 | data := make(map[string]interface{})
86 | code := hsc.SUCCESS
87 | ppath := c.Query("path")
88 | pmethod := c.Query("method")
89 | pidentity := c.Query("identity")
90 | if ppath == "" || pmethod == "" || pidentity == "" {
91 | code = hsc.INVALID_PARAMS
92 | } else {
93 | username, _ := c.Get("UserId")
94 | urlinfo := c.Request.URL
95 | jsonBody, _ := json.Marshal("Path:" + ppath + " method:" + pmethod + " identity:" + pidentity)
96 | method := c.Request.Method
97 | go mysql.DB.AddHistory(username.(int), method+":"+urlinfo.Path, string(jsonBody))
98 | if !casbin.RuleDel(pidentity, ppath, pmethod) {
99 | code = hsc.ERROR
100 | }
101 | }
102 | c.JSON(http.StatusOK, gin.H{
103 | "errorCode": code,
104 | "msg": hsc.GetMsg(code),
105 | "data": data,
106 | })
107 | }
108 |
109 | func RootCheck(c *gin.Context) {
110 | // var result auth.Result
111 | userinfo, ok := c.Get("UserName")
112 | if !ok {
113 | data := make(map[string]interface{})
114 | code := hsc.NOT_PROMISE
115 | c.JSON(http.StatusOK, gin.H{
116 | "errorCode": code,
117 | "msg": hsc.GetMsg(code),
118 | "data": data,
119 | })
120 | return
121 | }
122 |
123 | // 请求用户id
124 | userid := userinfo.(string)
125 | // 请求的path
126 | path := c.Request.URL.Path
127 | // 请求的方法
128 | method := c.Request.Method
129 | if !casbin.RuleCheck(userid, path, method) {
130 | data := make(map[string]interface{})
131 | code := hsc.ERROR
132 | c.JSON(http.StatusOK, gin.H{
133 | "errorCode": code,
134 | "msg": hsc.GetMsg(code),
135 | "data": data,
136 | })
137 | return
138 | }
139 | c.Next()
140 | }
141 |
--------------------------------------------------------------------------------
/src/rhttp/v1/v1struct.go:
--------------------------------------------------------------------------------
1 | package v1
2 |
3 | // codis信息的操作
4 | type CodisInfo struct {
5 | Curl string `json:"curl"`
6 | Cname string `json:"cname"`
7 | ClusterName string `json:"cluster_name"`
8 | }
9 |
10 | // config配置信息
11 | type ConfigInfo struct {
12 | // Name string `json:"name"`
13 | Key string `json:"key"`
14 | Value string `json:"value"`
15 | }
16 |
17 | // 操作缓存的指令
18 | type CliQuery struct {
19 | CacheType string `json:"cache_type"`
20 | CacheOp string `json:"cache_op"`
21 | ClusterName string `json:"cluster_name"`
22 | KeyName string `json:"key_name"`
23 | CodisUrl string `json:"codis_url"`
24 | GroupName string `json:"group_name"`
25 | Region string `json:"region"`
26 | InstanceId string `json:"instance_id"`
27 | ClusterId string `json:"cluster_id"`
28 | NodeId string `json:"node_id"`
29 | }
30 |
31 | // 分析大key
32 | type CliRdb struct {
33 | RdbName string `json:"rdbname"`
34 | ServerIp string `json:"serverip"`
35 | }
36 |
37 | // old
38 | type AddCluster struct {
39 | Name string `json:"name"`
40 | Nodes string `json:"nodes"`
41 | Password string `json:"password"`
42 | }
43 |
44 | // user
45 | type UserInfo struct {
46 | UserId int `json:"userid"`
47 | UserName string `json:"username"`
48 | Password string `json:"password"`
49 | Mail string `json:"mail"`
50 | UserType string `json:"usertype"`
51 | }
52 | type UserPassword struct {
53 | Old string `json:"old"`
54 | New string `json:"new"`
55 | }
56 |
57 | // casbin rule
58 | type CasbinPolicyJson struct {
59 | Identity string `json:"identity"`
60 | Path string `json:"path"`
61 | Method string `json:"method"`
62 | }
63 |
64 | // cloud
65 | type CloudPassword struct {
66 | Cloud string `json:"cloud"`
67 | Instanceid string `json:"instanceid"`
68 | Password string `json:"password"`
69 | }
70 |
71 | type TxShardCfg struct {
72 | Cloud string `json:"cloud"`
73 | TxShardType string `json:"txshardtype"`
74 | TxShardValue string `json:"txshardvalue"`
75 | }
76 |
77 | // cluster nodes table
78 |
--------------------------------------------------------------------------------
/start-docker.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | git pull
3 | CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o redis-manager main.go
4 |
5 | docker build -f Dockerfile -t redis-manager .
6 | rediswebmanager=`docker ps -a | grep redis-manager |wc -l`
7 |
8 | if [ ${rediswebmanager} == 1 ]
9 | then
10 | id=`docker ps -a| grep redis-manager| awk '{print $1}'`
11 | echo "docker stop $id"
12 | docker stop $id
13 | echo "docker rm $id"
14 | docker rm $id
15 | echo "docker run redis-manager"
16 | docker run -d -it -v logs:/data/logs -p 8000:8000 --restart=always --name redis-manager redis-manager
17 | else
18 | echo "docker run redis-manager"
19 | docker run -d -it -v logs:/data/logs -p 8000:8000 --restart=always --name redis-manager redis-manager
20 | fi
21 |
--------------------------------------------------------------------------------
/website/assets/Index-035ac3eb.css:
--------------------------------------------------------------------------------
1 | .content[data-v-88bc7fc3]{margin:20px 8px}
2 |
--------------------------------------------------------------------------------
/website/assets/Index-06dbdf44.js:
--------------------------------------------------------------------------------
1 | import{d as z,r as g,a as C,b as t,o as E,c as I,e as o,w as a,u as m,f as U,g as d,h as v,E as y,p as h,i as V,_ as b}from"./index-056ef9e9.js";import{l as N}from"./user-d7605884.js";import"./index-04cccabd.js";const T="/assets/github-mark-367d5cb2.png",k="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAABRBJREFUWEfNV1+IVFUY/31XJ2ZrXUmoHkpb1kQwdt093xiZRUpGD1pJFqYZpUEv0T9SH4zoQTRMgqzooUilNLWlslQoKlzDrXA8Z2dtAjdXCxTSJEh2VMbduV982x07O3vvzAhBnbd7zznf+Z3f9/v+HEKdg5nHA5gHYI6ItBNRC4CGaPsFETlORDkA+wDstdaercc01VrU3t4+JQiC1UQ0T0S+AfAlEVkAx6y153U/M18JYLKIMIB7iOguEdkbhuG6XC53tNoZiQAio2tEZBERbSgUCpv6+voGagHW+alTp45rbGxcLiIriWgngJfKYCv3xwIwxtxERLtE5DsiWlkvnZXG1W0isoGIbhORBc65/poApk+f3jF27NjPRWQ9gGcAjCeigyLyQ6lU2jI4OHgmnU7PV5qVdgBXATgDoDsIgm3ZbPZUDJAlANYPDQ3d19vb2+PPj2CAmdVgVxiGy0TkijFjxuytMNYXCW9SgisuAHjFWrsWQOiv6ejomBsEwWYAs621x8pzlwBEPj9IRGsPHTq0nZkNABXbZQ8R2eacexSA+JszmcxiEXkRwC1lTfgAXhORcc65J3XTzJkzG4rFYoGIgjgEIhIS0TkAjQBGaUlEnnfOvV651xjzDhENWGtf0LnhjVGo7SOim33BGWNOE9G1vhER2S8iawYGBg709/cXW1tbr06lUo8rc15e0C1n0+n0xO7u7hGREwnzpzAM52iIDgMwxmzWJGKt3egfxsxHNKq8fydbWlqaOzs7SzFCewjARxVglzrntsWsfVaTmXNuGUUZ7mihUJhcGefGmCwRZcoGRKTfOTclSRTMrGGmQi6PN621GkkjRpQnVIhTyBjzCID5zrnFMUi/BXBHvQCMMbuJaL5n531r7WNxgI0x2wHsUQbeFZFu59yWGMF8RURz6wXAzAcAzPLWv+WcezoBgOpmljKgNC+31v4Yw8CeqAANT1VzwYwZMyaGYai0pjw7T1lr344DwMytIrJJGfgDwMS4XM3MuwDc7xk4JyJPOOdUbJdivK2t7YZUKtUJ4Fbv9mEYhpNzudyvCQC0gJ1QAOettfoxahhjPiaiB2KmfgHwPQAFNImI7gSQ9teJyE7n3MNJgtX/enZVAMyst3qwmpGEuZNRtvutHgCJLjDG7CCiRZcDQER6iWihn++ruqCaCDVUiMin8ZSIdKl6ReT6ijT9s2bIpqamHV1dXUO1QPsiTAxDZv4QgJ8f8tba1sh/KwBs8A76s1gstuXz+RO1Dtd5Y8zfYcjMS0Tk3oREpGlUa3l5+ABUuMcBXOcpf39TU9PcehiI2N1dNRUzcyIAPTSTyayKGhf/0huttc9VY2FEKo7oiC1GxpitRKSpehQD+mPatGmNDQ0NysI1FSG4yjnnu2cEHmb+pxjpTJVy/AERLU0CkKCF8vItQRCsyWazCvDSiC3HkaERDUnETE0Azc3N6QkTJhwhohtjaNdseaxUKt1dzoixDUkEQEV1EMA6a62qXzPVVgCJLigfyMwLAHwa53cR+cQ5tzCyp4JeHduSRTfWdnyfNqU9PT1fM7MaVePl0W+tje0HjDHvaVGrAHF0cHDw9sOHD/9ebkpFZI7fno/q5cptORGpwvWwlwEM94X+bSpvy8xaBd8QkWXa84nIFxcvXlyRz+dPR83oqzXbco9S7Wo++zcfJlpV49Lz//Np5tP7nz1OY3ys7zx9ks2u9jyPitWeet+TfwG94//HwNlKlQAAAABJRU5ErkJggg==",A=l=>(h("data-v-8a551a48"),l=l(),V(),l),J={class:"login"},Q=A(()=>d("p",{class:"login_title"},"登 录",-1)),j=A(()=>d("p",{class:"login_desc"},"欢迎登录Redis-Manager平台",-1)),S=z({__name:"Index",setup(l){const s=g({username:"",password:""}),r=C(),f=g({username:[{required:!0,message:"账号不能为空",trigger:"blur"}],password:[{required:!0,message:"密码不能为空",trigger:"blur"}]}),_=async()=>{r&&r.value.validate(async c=>{if(c){const e=await N(s);console.log("res: ",e.data.data),e.data.errorCode===0?(sessionStorage.setItem("Authorization",e.data.data.token),sessionStorage.setItem("user_name",e.data.data.username),sessionStorage.setItem("user_type",e.data.data.usertype),v.push("/home")):y(e.data.msg)}else return!1})},w=()=>{window.open("https://github.com/iguidao/redis-manager","_blank")};return(c,e)=>{const p=t("el-image"),u=t("el-input"),i=t("el-form-item"),R=t("el-button"),x=t("el-form"),G=t("el-card");return E(),I("div",J,[o(G,{class:"login_card",shadow:"always"},{default:a(()=>[o(p,{class:"logo_image",src:m(k),fit:"cover"},null,8,["src"]),Q,j,o(x,{ref_key:"ruleFormRef",ref:r,model:s,rules:f},{default:a(()=>[o(i,{prop:"username"},{default:a(()=>[o(u,{placeholder:"请输入账号",modelValue:s.username,"onUpdate:modelValue":e[0]||(e[0]=n=>s.username=n),"prefix-icon":"User"},null,8,["modelValue"])]),_:1}),o(i,{prop:"password"},{default:a(()=>[o(u,{type:"password",placeholder:"请输入密码",modelValue:s.password,"onUpdate:modelValue":e[1]||(e[1]=n=>s.password=n),"prefix-icon":"Lock"},null,8,["modelValue"])]),_:1}),o(i,null,{default:a(()=>[o(R,{loading:!1,type:"primary",onClick:e[2]||(e[2]=n=>_())},{default:a(()=>[U("登录")]),_:1})]),_:1})]),_:1},8,["model","rules"])]),_:1}),d("div",null,[o(p,{class:"github_logo",src:m(T),fut:"civer",onClick:e[3]||(e[3]=n=>w())},null,8,["src"])])])}}});const D=b(S,[["__scopeId","data-v-8a551a48"]]);export{D as default};
2 |
--------------------------------------------------------------------------------
/website/assets/Index-398d56ab.js:
--------------------------------------------------------------------------------
1 | import{l as q,a as O,d as T,u as W}from"./setting-b5212552.js";import{h as j}from"./moment-fbc5633a.js";import{d as G,a as m,r as J,q as P,b as n,s as Q,o as v,c as V,e as t,w as l,f as _,v as R,l as x,g as h,F as X,k as Z,E as r,p as ee,i as te,_ as le}from"./index-056ef9e9.js";import"./index-04cccabd.js";const oe=p=>(ee("data-v-c3fd483f"),p=p(),te(),p),ae={class:"content"},ne=oe(()=>h("span",null,"系统配置",-1)),se={class:"dialog-footer"},re=G({__name:"Index",setup(p){const f=m(!1),b=m([]),g=(s,e)=>{const i=s[e.property];return i===void 0?"":j(i).utcOffset(8).format("YYYY-MM-DD HH:mm")},w=m([]),d=m(!1),C="100px",o=J({key:"",value:""}),y=async()=>{let s=(await q()).data;s.errorCode===0?(f.value=!1,b.value=s.data.lists):(f.value=!1,r.error(s.msg));let e=(await O()).data;e.errorCode===0?w.value=e.data:r.error(e.msg)},D=(s,e)=>{d.value=!0,o.key=e.Key,o.value=e.Value},I=async s=>{if(!s.Key)r.error("未获取到的删除的内容");else{o.key=s.Key;const e=await T(o);e.data.errorCode===0?(r.success("删除成功"),await y()):r.error(e.data.msg)}},$=async()=>{if(console.log(o),!o.key)r.error("未获取到的变更配置项");else if(!o.value)r.error("未获取到的变更配置值");else{const s=await W(o);s.data.errorCode===0?(r.success("变更成功"),o.key="",o.value="",await y()):r.error(s.data.msg),d.value=!1}},B=()=>{d.value=!1,o.key="",o.value=""};return P(async()=>{f.value=!0,await y()}),(s,e)=>{const i=n("el-col"),u=n("el-button"),E=n("el-row"),F=n("el-divider"),c=n("el-table-column"),K=n("el-popconfirm"),M=n("el-table"),N=n("el-card"),U=n("el-option"),Y=n("el-select"),k=n("el-form-item"),z=n("el-input"),S=n("el-form"),A=n("el-dialog"),H=Q("loading");return v(),V("div",ae,[t(N,{shadow:"never"},{default:l(()=>[t(E,null,{default:l(()=>[t(i,{span:2},{default:l(()=>[ne]),_:1}),t(i,{offset:18,span:3,style:{"min-width":"120px"}},{default:l(()=>[t(u,{size:"small",type:"primary",onClick:e[0]||(e[0]=a=>d.value=!0)},{default:l(()=>[_("添加集群")]),_:1})]),_:1})]),_:1}),t(F),R((v(),x(M,{data:b.value,stripe:"",style:{width:"100%"}},{default:l(()=>[t(c,{prop:"Name",label:"名称",width:"180"}),t(c,{prop:"Key",label:"key",width:"180"}),t(c,{prop:"Value",label:"value"}),t(c,{prop:"CreatedAt",formatter:g,label:"创建时间"}),t(c,{prop:"UpdatedAt",formatter:g,label:"修改时间"}),t(c,{label:"操作"},{default:l(a=>[t(u,{size:"small",onClick:L=>D(a.$index,a.row)},{default:l(()=>[_("修改")]),_:2},1032,["onClick"]),t(K,{title:"确定要删除吗?","confirm-button-text":"确认","cancel-button-text":"取消","confirm-button-type":"danger","cancel-button-type":"primary",onConfirm:L=>I(a.row)},{reference:l(()=>[t(u,{size:"small",type:"danger"},{default:l(()=>[_("删除")]),_:1})]),_:2},1032,["onConfirm"])]),_:1})]),_:1},8,["data"])),[[H,f.value]])]),_:1}),t(A,{modelValue:d.value,"onUpdate:modelValue":e[5]||(e[5]=a=>d.value=a),title:"配置变更",width:"30%","align-center":""},{footer:l(()=>[h("span",se,[t(u,{onClick:e[3]||(e[3]=a=>B())},{default:l(()=>[_("取消")]),_:1}),t(u,{type:"primary",onClick:e[4]||(e[4]=a=>$())},{default:l(()=>[_("确定")]),_:1})])]),default:l(()=>[t(S,{model:o},{default:l(()=>[t(k,{label:"配置项","label-width":C},{default:l(()=>[t(Y,{modelValue:o.key,"onUpdate:modelValue":e[1]||(e[1]=a=>o.key=a),placeholder:"请选择需要变更的配置"},{default:l(()=>[(v(!0),V(X,null,Z(w.value,a=>(v(),x(U,{key:a.value,label:a.label,value:a.value},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1}),t(k,{label:"配置值","label-width":C},{default:l(()=>[t(z,{modelValue:o.value,"onUpdate:modelValue":e[2]||(e[2]=a=>o.value=a),autocomplete:"off"},null,8,["modelValue"])]),_:1})]),_:1},8,["model"])]),_:1},8,["modelValue"])])}}});const _e=le(re,[["__scopeId","data-v-c3fd483f"]]);export{_e as default};
2 |
--------------------------------------------------------------------------------
/website/assets/Index-7aa9bc97.css:
--------------------------------------------------------------------------------
1 | .content[data-v-d5b3e431]{margin:20px 8px}.el-button--text[data-v-d5b3e431]{margin-right:15px}.el-select[data-v-d5b3e431],.el-input[data-v-d5b3e431]{width:300px}.dialog-footer button[data-v-d5b3e431]:first-child{margin-right:10px}
2 |
--------------------------------------------------------------------------------
/website/assets/Index-8878f285.js:
--------------------------------------------------------------------------------
1 | import{a as j,b as G,c as J,d as K,e as P}from"./user-d7605884.js";import{h as Q}from"./moment-fbc5633a.js";import{d as R,a as v,r as X,q as Z,b as n,s as ee,o as V,c as D,e as a,w as o,f as i,v as le,l as $,g as C,F as ae,k as te,E as r,p as oe,i as se,_ as ne}from"./index-056ef9e9.js";import"./index-04cccabd.js";const re=b=>(oe("data-v-d79bf60f"),b=b(),se(),b),de={class:"content"},ue=re(()=>C("span",null,"用户列表",-1)),ie={class:"dialog-footer"},me={class:"dialog-footer"},pe=R({__name:"Index",setup(b){const w=v(!1),x=v([]),k=v([]),U=(s,e)=>{const c=s[e.property];return c===void 0?"":Q(c).utcOffset(8).format("YYYY-MM-DD HH:mm")},m=v(!1),p=v(!1),f="100px",l=X({username:"",password:"",mail:"",usertype:""}),y=async()=>{let s=(await j()).data;s.errorCode===0?(w.value=!1,x.value=s.data):(w.value=!1,r.error(s.msg));let e=(await G()).data;e.errorCode===0?k.value=e.data:r.error(e.msg)},N=(s,e)=>{p.value=!0,l.username=e.UserName,l.usertype=e.UserType},A=async s=>{if(!s.ID)r.error("未获取到的删除的内容");else{l.username=s.UserName;const e=await J(l);e.data.errorCode===0?(r.success("删除成功"),await y()):r.error(e.data.msg)}},E=async()=>{if(console.log(l),!l.username)r.error("未获取到的变更用户");else if(!l.usertype)r.error("未获取到的变更值");else{const s=await K(l);s.data.errorCode===0?(r.success("变更成功"),l.username="",l.usertype="",await y()):r.error(s.data.msg),p.value=!1}},F=async()=>{if(console.log(l),!l.username)r.error("未获取到的用户名称");else if(!l.password)r.error("未获取到的用户密码");else if(!l.mail)r.error("未获取到的用户邮箱");else{const s=await P(l);s.data.errorCode===0?(r.success("变更成功"),l.username="",l.password="",l.mail="",await y()):r.error(s.data.msg),m.value=!1}},B=()=>{p.value=!1,l.username="",l.usertype=""},M=()=>{m.value=!1,l.username="",l.password="",l.mail=""};return Z(async()=>{w.value=!0,await y()}),(s,e)=>{const c=n("el-col"),d=n("el-button"),Y=n("el-row"),z=n("el-divider"),u=n("el-table-column"),S=n("el-popconfirm"),T=n("el-table"),H=n("el-card"),g=n("el-input"),_=n("el-form-item"),L=n("el-option"),q=n("el-select"),h=n("el-form"),I=n("el-dialog"),O=ee("loading");return V(),D("div",de,[a(H,{shadow:"never"},{default:o(()=>[a(Y,null,{default:o(()=>[a(c,{span:2},{default:o(()=>[ue]),_:1}),a(c,{offset:18,span:3,style:{"min-width":"120px"}},{default:o(()=>[a(d,{size:"small",type:"primary",onClick:e[0]||(e[0]=t=>m.value=!0)},{default:o(()=>[i("添加用户")]),_:1})]),_:1})]),_:1}),a(z),le((V(),$(T,{data:x.value,stripe:"",style:{width:"100%"}},{default:o(()=>[a(u,{prop:"ID",label:"用户ID",width:"180"}),a(u,{prop:"UserName",label:"用户名",width:"180"}),a(u,{prop:"UserType",label:"用户身份",width:"180"}),a(u,{prop:"Email",label:"邮箱"}),a(u,{prop:"CreatedAt",formatter:U,label:"创建时间"}),a(u,{prop:"UpdatedAt",formatter:U,label:"修改时间"}),a(u,{label:"操作"},{default:o(t=>[a(d,{size:"small",onClick:W=>N(t.$index,t.row)},{default:o(()=>[i("修改")]),_:2},1032,["onClick"]),a(S,{title:"确定要删除吗?","confirm-button-text":"确认","cancel-button-text":"取消","confirm-button-type":"danger","cancel-button-type":"primary",onConfirm:W=>A(t.row)},{reference:o(()=>[a(d,{size:"small",type:"danger"},{default:o(()=>[i("删除")]),_:1})]),_:2},1032,["onConfirm"])]),_:1})]),_:1},8,["data"])),[[O,w.value]])]),_:1}),a(I,{modelValue:p.value,"onUpdate:modelValue":e[5]||(e[5]=t=>p.value=t),title:"修改用户",width:"30%","align-center":""},{footer:o(()=>[C("span",ie,[a(d,{onClick:e[3]||(e[3]=t=>B())},{default:o(()=>[i("取消")]),_:1}),a(d,{type:"primary",onClick:e[4]||(e[4]=t=>E())},{default:o(()=>[i("确定")]),_:1})])]),default:o(()=>[a(h,{model:l},{default:o(()=>[a(_,{label:"用户名","label-width":f},{default:o(()=>[a(g,{modelValue:l.username,"onUpdate:modelValue":e[1]||(e[1]=t=>l.username=t),autocomplete:"off"},null,8,["modelValue"])]),_:1}),a(_,{label:"用户身份","label-width":f},{default:o(()=>[a(q,{modelValue:l.usertype,"onUpdate:modelValue":e[2]||(e[2]=t=>l.usertype=t),placeholder:"请选择需要变更的配置"},{default:o(()=>[(V(!0),D(ae,null,te(k.value,t=>(V(),$(L,{key:t.value,label:t.label,value:t.value},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1})]),_:1},8,["model"])]),_:1},8,["modelValue"]),a(I,{modelValue:m.value,"onUpdate:modelValue":e[11]||(e[11]=t=>m.value=t),title:"添加用户",width:"30%","align-center":""},{footer:o(()=>[C("span",me,[a(d,{onClick:e[9]||(e[9]=t=>M())},{default:o(()=>[i("取消")]),_:1}),a(d,{type:"primary",onClick:e[10]||(e[10]=t=>F())},{default:o(()=>[i("确定")]),_:1})])]),default:o(()=>[a(h,{model:l},{default:o(()=>[a(_,{label:"用户名称","label-width":f},{default:o(()=>[a(g,{modelValue:l.username,"onUpdate:modelValue":e[6]||(e[6]=t=>l.username=t),autocomplete:"off"},null,8,["modelValue"])]),_:1}),a(_,{label:"用户密码","label-width":f},{default:o(()=>[a(g,{modelValue:l.password,"onUpdate:modelValue":e[7]||(e[7]=t=>l.password=t),type:"password",autocomplete:"off"},null,8,["modelValue"])]),_:1}),a(_,{label:"用户邮箱","label-width":f},{default:o(()=>[a(g,{modelValue:l.mail,"onUpdate:modelValue":e[8]||(e[8]=t=>l.mail=t),autocomplete:"off"},null,8,["modelValue"])]),_:1})]),_:1},8,["model"])]),_:1},8,["modelValue"])])}}});const be=ne(pe,[["__scopeId","data-v-d79bf60f"]]);export{be as default};
2 |
--------------------------------------------------------------------------------
/website/assets/Index-9ea35986.css:
--------------------------------------------------------------------------------
1 | .content[data-v-9ad5b82c]{margin-top:20px;display:flex;flex-direction:column;height:70%}.main-info[data-v-9ad5b82c]{flex:1;display:flex;justify-content:space-around;background:rgb(255,255,255);min-width:700px;border-radius:8px}.info[data-v-9ad5b82c]{background:white;width:18%;height:80%;align-self:center;text-align:center;box-sizing:border-box;display:flex;align-items:center;justify-content:center}.info .num-info[data-v-9ad5b82c]{margin:10px 0}.info .desc[data-v-9ad5b82c]{font-size:10px;color:gray}.chart[data-v-9ad5b82c]{flex:1;margin-top:10px;display:flex;justify-content:space-between;border-radius:8px;width:100%;height:100%}.e-chart[data-v-9ad5b82c]{padding-top:10px;width:49%;height:auto;min-height:300px;min-width:300px}
2 |
--------------------------------------------------------------------------------
/website/assets/Index-a2a246be.js:
--------------------------------------------------------------------------------
1 | import{a as f}from"./index-04cccabd.js";import{h as i}from"./moment-fbc5633a.js";import{d as m,a as r,q as u,b as o,o as b,c as h,e as a,w as c,E as v,_ as w}from"./index-056ef9e9.js";const x=()=>f.get("/ophistory/v1/list"),I={class:"content"},y=m({__name:"Index",setup(g){const s=r([]);r("");const l=async()=>{let t=(await x()).data;t.errorCode===0?s.value=t.data:v.error(t.msg)},_=(t,n)=>{const e=t[n.property];return e===void 0?"":i(e).utcOffset(8).format("YYYY-MM-DD HH:mm:ss")};return u(async()=>{await l()}),(t,n)=>{const e=o("el-table-column"),d=o("el-table"),p=o("el-card");return b(),h("div",I,[a(p,{shadow:"never"},{default:c(()=>[a(d,{data:s.value,stripe:"",style:{width:"100%"}},{default:c(()=>[a(e,{prop:"CreatedAt",formatter:_,label:"时间",width:"170",sortable:""}),a(e,{prop:"UserId",label:"用户ID",width:"100"}),a(e,{prop:"OpInfo",label:"地址",width:"400"}),a(e,{prop:"OpParams",label:"事件"})]),_:1},8,["data"])]),_:1})])}}});const Y=w(y,[["__scopeId","data-v-0ccf9bff"]]);export{Y as default};
2 |
--------------------------------------------------------------------------------
/website/assets/Index-b3c9f82a.css:
--------------------------------------------------------------------------------
1 | .content[data-v-0d2aba58]{margin:20px 8px}.el-button--text[data-v-0d2aba58]{margin-right:15px}.el-select[data-v-0d2aba58],.el-input[data-v-0d2aba58]{width:300px}.dialog-footer button[data-v-0d2aba58]:first-child{margin-right:10px}
2 |
--------------------------------------------------------------------------------
/website/assets/Index-b6741412.css:
--------------------------------------------------------------------------------
1 | .content[data-v-c3fd483f]{margin:20px 8px}.el-button--text[data-v-c3fd483f]{margin-right:15px}.el-select[data-v-c3fd483f],.el-input[data-v-c3fd483f]{width:300px}.dialog-footer button[data-v-c3fd483f]:first-child{margin-right:10px}
2 |
--------------------------------------------------------------------------------
/website/assets/Index-bba6e235.css:
--------------------------------------------------------------------------------
1 | .content[data-v-0ccf9bff]{margin:20px 8px}
2 |
--------------------------------------------------------------------------------
/website/assets/Index-bfef1db5.js:
--------------------------------------------------------------------------------
1 | import{a as h}from"./index-04cccabd.js";import{d as f,a as m,q as v,b as u,o as e,c as o,g as a,e as c,w as _,t as r,E as y,p as x,i as k,_ as g}from"./index-056ef9e9.js";const I=()=>h.get("/board/v1/desc"),t=n=>(x("data-v-9ad5b82c"),n=n(),k(),n),b={class:"content"},w={class:"main-info"},C={key:0,class:"num-info"},B={key:1,class:"num-info"},S=t(()=>a("p",{class:"desc"},"腾讯Redis",-1)),E={key:0,class:"num-info"},D={key:1,class:"num-info"},M=t(()=>a("p",{class:"desc"},"阿里Redis",-1)),N={key:0,class:"num-info"},R={key:1,class:"num-info"},V=t(()=>a("p",{class:"desc"},"自建Codis",-1)),q={key:0,class:"num-info"},j={key:1,class:"num-info"},z=t(()=>a("p",{class:"desc"},"自建Cluster",-1)),A=t(()=>a("div",{class:"chart"},null,-1)),F=f({__name:"Index",setup(n){const s=m({}),p=async()=>{let i=(await I()).data;i.errorCode===0?s.value=i.data:y.error(i.msg)};return v(async()=>{await p()}),(i,G)=>{const d=u("el-button"),l=u("el-card");return e(),o("div",b,[a("div",w,[c(l,{class:"info"},{default:_(()=>[c(d,{type:"primary",icon:"el-icon-user-solid",circle:""}),s.value.txredis?(e(),o("h2",C,r(s.value.txredis),1)):(e(),o("h2",B,"0")),S]),_:1}),c(l,{class:"info"},{default:_(()=>[c(d,{type:"success",icon:"el-icon-s-data",circle:""}),s.value.aliredis?(e(),o("h2",E,r(s.value.aliredis),1)):(e(),o("h2",D,"0")),M]),_:1}),c(l,{class:"info"},{default:_(()=>[c(d,{type:"danger",icon:"el-icon-coin",circle:""}),s.value.codis?(e(),o("h2",N,r(s.value.codis),1)):(e(),o("h2",R,"0")),V]),_:1}),c(l,{class:"info"},{default:_(()=>[c(d,{type:"warning",icon:"el-icon-data-line",circle:""}),s.value.cluster?(e(),o("h2",q,r(s.value.cluster),1)):(e(),o("h2",j,"0")),z]),_:1})]),A])}}});const K=g(F,[["__scopeId","data-v-9ad5b82c"]]);export{K as default};
2 |
--------------------------------------------------------------------------------
/website/assets/Index-c6cadafd.js:
--------------------------------------------------------------------------------
1 | import{l as z,a as A,b as F}from"./cluster-7426750c.js";import{h as L}from"./moment-fbc5633a.js";import{d as $,a as f,r as S,q as H,b as d,s as T,o as n,c as p,g as w,e,w as l,f as V,v as q,l as G,n as O,E as i,p as W,i as j,_ as J}from"./index-056ef9e9.js";import"./index-04cccabd.js";const K=_=>(W("data-v-88bc7fc3"),_=_(),j(),_),Q={class:"content"},X=K(()=>w("span",null,"自建Cluster集群",-1)),Z={key:0},ee={key:0},te={key:1},le={key:1},oe={key:0},ae={class:"dialog-footer"},se=$({__name:"Index",setup(_){const m=f(!1),b="100px",k=f([]),x=f([]),u=f(!1),r=S({name:"",nodes:"",password:""}),v=S({cluster_id:""}),N=(s,t)=>{const c=s[t.property];return c===void 0?"":L(c).utcOffset(8).format("YYYY-MM-DD HH:mm")},h=async()=>{let s=(await z()).data;s.errorCode===0?(m.value=!1,k.value=s.data):(m.value=!1,i.error(s.msg))},D=async s=>{if(v.cluster_id=s.ID,!v.cluster_id)i.error("未获取到的添加cluster_id");else{const t=(await A(v)).data;t.errorCode===0?x.value=t.data:i.error(t.msg),u.value=!1}},P=async()=>{if(!r.name)i.error("未获取到的添加cluster集群的名称");else if(!r.nodes)i.error("未获取到的添加cluster集群的地址");else{const s=await F(r);s.data.errorCode===0?(i.success("添加成功"),await h()):i.error(s.data.msg),u.value=!1}};return H(async()=>{m.value=!0,await h()}),(s,t)=>{const c=d("el-col"),C=d("el-button"),B=d("el-row"),E=d("el-divider"),a=d("el-table-column"),I=d("el-table"),M=d("el-card"),y=d("el-input"),g=d("el-form-item"),R=d("el-form"),U=d("el-dialog"),Y=T("loading");return n(),p("div",Q,[w("div",null,[e(M,{shadow:"never"},{default:l(()=>[e(B,null,{default:l(()=>[e(c,{span:3},{default:l(()=>[X]),_:1}),e(c,{offset:18,span:3,style:{"min-width":"120px"}},{default:l(()=>[e(C,{size:"small",type:"primary",onClick:t[0]||(t[0]=o=>u.value=!0)},{default:l(()=>[V("添加集群")]),_:1})]),_:1})]),_:1}),e(E),q((n(),G(I,{data:k.value,stripe:"",style:{width:"100%"},lazy:"",onExpandChange:D},{default:l(()=>[e(a,{label:"查看",type:"expand",width:"60"},{default:l(o=>[e(I,{data:x.value,border:!0,style:{width:"100%"},"row-key":"id",lazy:"",load:h,"tree-props":{children:"Children",hasChildren:"hasChildren"}},{default:l(()=>[e(a,{prop:"Flags",label:"身份",width:"160"}),e(a,{prop:"Address",label:"地址",width:"160"}),e(a,{prop:"LinkState",label:"LinkState",width:"160"}),e(a,{prop:"RunStatus",label:"RunStatus",width:"160"}),e(a,{prop:"SlotRange",label:"SlotRange",width:"160"}),e(a,{prop:"SlotNumber",label:"SlotNumber",width:"160"}),e(a,{prop:"CreateTime",formatter:N,label:"创建时间",width:"160"})]),_:1},8,["data"])]),_:1}),e(a,{prop:"ID",label:"ID",width:"60"}),e(a,{prop:"Name",label:"名称",width:"100"}),e(a,{prop:"Nodes",label:"内网IP"}),e(a,{prop:"Password",label:"密码",width:"120"},{default:l(o=>[o.row.NoAuth?(n(),p("div",le,[o.row.Password===""?(n(),p("span",oe,"免密登陆")):O("",!0)])):(n(),p("div",Z,[o.row.Password===""?(n(),p("span",ee,"未设置密码")):(n(),p("span",te,"已设置密码"))]))]),_:1}),e(a,{prop:"CreatedAt",formatter:N,label:"创建时间",width:"150"})]),_:1},8,["data"])),[[Y,m.value]])]),_:1})]),w("div",null,[e(U,{modelValue:u.value,"onUpdate:modelValue":t[6]||(t[6]=o=>u.value=o),title:"添加集群",width:"30%","align-center":""},{footer:l(()=>[w("span",ae,[e(C,{onClick:t[4]||(t[4]=o=>u.value=!1)},{default:l(()=>[V("取消")]),_:1}),e(C,{type:"primary",onClick:t[5]||(t[5]=o=>P())},{default:l(()=>[V("Confirm")]),_:1})])]),default:l(()=>[e(R,{model:r},{default:l(()=>[e(g,{label:"集群名称","label-width":b},{default:l(()=>[e(y,{modelValue:r.name,"onUpdate:modelValue":t[1]||(t[1]=o=>r.name=o),placeholder:"Cluster Name",autocomplete:"off"},null,8,["modelValue"])]),_:1}),e(g,{label:"集群地址","label-width":b},{default:l(()=>[e(y,{modelValue:r.nodes,"onUpdate:modelValue":t[2]||(t[2]=o=>r.nodes=o),placeholder:"127.0.0.1:6379,127.0.0.1:6380",autocomplete:"off"},null,8,["modelValue"])]),_:1}),e(g,{label:"集群密码","label-width":b},{default:l(()=>[e(y,{modelValue:r.password,"onUpdate:modelValue":t[3]||(t[3]=o=>r.password=o),type:"password",placeholder:"Cluster Password",autocomplete:"off"},null,8,["modelValue"])]),_:1})]),_:1},8,["model"])]),_:1},8,["modelValue"])])])}}});const ue=J(se,[["__scopeId","data-v-88bc7fc3"]]);export{ue as default};
2 |
--------------------------------------------------------------------------------
/website/assets/Index-c74390c1.css:
--------------------------------------------------------------------------------
1 | .content[data-v-d79bf60f]{margin:20px 8px}.el-button--text[data-v-d79bf60f]{margin-right:15px}.el-select[data-v-d79bf60f],.el-input[data-v-d79bf60f]{width:300px}.dialog-footer button[data-v-d79bf60f]:first-child{margin-right:10px}
2 |
--------------------------------------------------------------------------------
/website/assets/Index-d450f424.css:
--------------------------------------------------------------------------------
1 | .content[data-v-ee0055ce]{margin:20px 8px}.home-container[data-v-9f6cd060]{position:absolute;height:100%;top:0px;left:0px;width:100%;background:#f2f3f5}.el-header[data-v-9f6cd060]{background:#ffffff;padding:0 10px;overflow:hidden}.grid-content[data-v-9f6cd060]{min-height:40px;display:flex;justify-content:space-between;align-items:center}.logo-wrapper[data-v-9f6cd060]{display:flex;justify-content:space-between;align-items:center;width:150px;cursor:pointer;padding-left:22px}.system_name[data-v-9f6cd060]{color:#000;font-size:18px}.el-aside[data-v-9f6cd060]{background:#545c64;width:auto!important}.el-menu-vertical-demo[data-v-9f6cd060]:not(.el-menu--collapse){width:200px;min-height:400px}.el-menu-item.is-active[data-v-9f6cd060]{color:#fff!important;font-size:15px;font-weight:700;background-color:#a7a7a7!important;border-radius:2px;height:50px;line-height:50px;box-sizing:border-box;margin:2px 5px 0 2px}
2 |
--------------------------------------------------------------------------------
/website/assets/Index-d4718653.css:
--------------------------------------------------------------------------------
1 | .login[data-v-8a551a48]{width:100%;height:100%;background-image:url(/assets/sun-80b81115.jpeg);position:absolute;left:0;top:0;background-size:100% 100%}.login_card[data-v-8a551a48]{position:absolute;left:0;right:0;top:0;bottom:0;margin:auto;width:20%;min-width:300px;height:450px;min-height:450px;border-radius:10px;text-align:center}.logo_image[data-v-8a551a48]{width:50px;height:50px;margin-top:40px}.login_title[data-v-8a551a48]{font-size:25px;font-weight:700}.login_desc[data-v-8a551a48]{letter-spacing:2px;color:#999a9a}.el-button[data-v-8a551a48]{width:100%}.github_logo[data-v-8a551a48]{width:40px;height:40px;position:fixed;bottom:60px;right:20px}
2 |
--------------------------------------------------------------------------------
/website/assets/Index-dc641d83.css:
--------------------------------------------------------------------------------
1 | .content[data-v-c36ae760]{margin:20px 8px}.el-button--text[data-v-c36ae760]{margin-right:15px}.el-select[data-v-c36ae760]{width:130px}.el-input[data-v-c36ae760]{width:300px}.dialog-footer button[data-v-c36ae760]:first-child{margin-right:10px}.el-link[data-v-c36ae760]{margin-right:8px}.el-link .el-icon--right.el-icon[data-v-c36ae760]{vertical-align:text-bottom}.codis_dashboard[data-v-c36ae760]{width:100%;height:70vh}
2 |
--------------------------------------------------------------------------------
/website/assets/Index-f5a41eb2.css:
--------------------------------------------------------------------------------
1 | .content[data-v-1b6ebbad]{margin:20px 8px}.el-button--text[data-v-1b6ebbad]{margin-right:15px}.el-select[data-v-1b6ebbad]{width:130px}.el-input[data-v-1b6ebbad]{width:300px}.cli_commend[data-v-1b6ebbad]{width:100%;height:70vh}.el-col[data-v-1b6ebbad]{border-radius:1px}.grid-content[data-v-1b6ebbad]{border-radius:1px;min-height:10px}.content[data-v-5dc9fb7f]{margin:20px 8px}
2 |
--------------------------------------------------------------------------------
/website/assets/Rule-273f6791.css:
--------------------------------------------------------------------------------
1 | .content[data-v-e7746445]{margin:20px 8px}.el-button--text[data-v-e7746445]{margin-right:15px}.el-select[data-v-e7746445],.el-input[data-v-e7746445]{width:300px}.dialog-footer button[data-v-e7746445]:first-child{margin-right:10px}
2 |
--------------------------------------------------------------------------------
/website/assets/Rule-d9da8097.js:
--------------------------------------------------------------------------------
1 | import{a as y}from"./index-04cccabd.js";import{b as A}from"./user-d7605884.js";import"./moment-fbc5633a.js";import{d as G,a as c,r as H,q as J,b as r,s as K,o as i,c as v,e as t,w as a,f as h,v as O,l as b,g as $,F as k,k as I,E as s,p as P,i as Q,_ as X}from"./index-056ef9e9.js";const Y=()=>y.get("/rule/v1/list"),Z=d=>y.delete("/rule/v1/del",{params:d}),ee=()=>y.get("/rule/v1/cfg"),te=d=>y.post("/rule/v1/add",d),le=d=>(P("data-v-e7746445"),d=d(),Q(),d),oe={class:"content"},ae=le(()=>$("span",null,"权限配置",-1)),ne={class:"dialog-footer"},re=G({__name:"Rule",setup(d){const m=c(!1),B=c([]),D=c([]),M=c([]),U=c([]),p=c(!1),g="100px",l=H({identity:"",path:"",method:""}),w=async()=>{let n=(await Y()).data;n.errorCode===0?(m.value=!1,B.value=n.data):(m.value=!1,s.error(n.msg));let o=(await A()).data;o.errorCode===0?D.value=o.data:s.error(o.msg);let u=(await ee()).data;u.errorCode===0?(M.value=u.data.url,U.value=u.data.method):s.error(u.msg)},E=async()=>{if(console.log(l),!l.identity)s.error("未获取到的添加的配置");else if(!l.path)s.error("未获取到的添加的配置");else if(!l.method)s.error("未获取到的添加的配置");else{const n=await te(l);n.data.errorCode===0?(s.success("添加成功"),l.identity="",l.path="",l.method="",await w()):s.error(n.data.msg),p.value=!1}},F=async n=>{if(!n.identity)s.error("未获取到的删除的配置");else if(!n.path)s.error("未获取到的删除的配置");else if(!n.method)s.error("未获取到的删除的配置");else{l.identity=n.identity,l.path=n.path,l.method=n.method;const o=await Z(l);o.data.errorCode===0?(s.success("删除成功"),await w()):s.error(o.data.msg)}},N=()=>{p.value=!1,l.identity="",l.path="",l.method=""};return J(async()=>{m.value=!0,await w()}),(n,o)=>{const u=r("el-col"),f=r("el-button"),R=r("el-row"),S=r("el-divider"),_=r("el-table-column"),z=r("el-popconfirm"),L=r("el-table"),q=r("el-card"),V=r("el-option"),C=r("el-select"),x=r("el-form-item"),T=r("el-form"),W=r("el-dialog"),j=K("loading");return i(),v("div",oe,[t(q,{shadow:"never"},{default:a(()=>[t(R,null,{default:a(()=>[t(u,{span:2},{default:a(()=>[ae]),_:1}),t(u,{offset:18,span:3,style:{"min-width":"120px"}},{default:a(()=>[t(f,{size:"small",type:"primary",onClick:o[0]||(o[0]=e=>p.value=!0)},{default:a(()=>[h("添加权限")]),_:1})]),_:1})]),_:1}),t(S),O((i(),b(L,{data:B.value,stripe:"",style:{width:"100%"}},{default:a(()=>[t(_,{prop:"identity",label:"身份",width:"180"}),t(_,{prop:"path",label:"接口"}),t(_,{prop:"method",label:"meth",width:"180"}),t(_,{prop:"note",label:"备注"}),t(_,{label:"操作"},{default:a(e=>[t(z,{title:"确定要删除吗?","confirm-button-text":"确认","cancel-button-text":"取消","confirm-button-type":"danger","cancel-button-type":"primary",onConfirm:se=>F(e.row)},{reference:a(()=>[t(f,{size:"small",type:"danger"},{default:a(()=>[h("删除")]),_:1})]),_:2},1032,["onConfirm"])]),_:1})]),_:1},8,["data"])),[[j,m.value]])]),_:1}),t(W,{modelValue:p.value,"onUpdate:modelValue":o[6]||(o[6]=e=>p.value=e),title:"权限添加",width:"30%","align-center":""},{footer:a(()=>[$("span",ne,[t(f,{onClick:o[4]||(o[4]=e=>N())},{default:a(()=>[h("取消")]),_:1}),t(f,{type:"primary",onClick:o[5]||(o[5]=e=>E())},{default:a(()=>[h("确定")]),_:1})])]),default:a(()=>[t(T,{model:l},{default:a(()=>[t(x,{label:"身份","label-width":g},{default:a(()=>[t(C,{modelValue:l.identity,"onUpdate:modelValue":o[1]||(o[1]=e=>l.identity=e),placeholder:"选择身份"},{default:a(()=>[(i(!0),v(k,null,I(D.value,e=>(i(),b(V,{key:e.value,label:e.label,value:e.value},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1}),t(x,{label:"接口","label-width":g},{default:a(()=>[t(C,{modelValue:l.path,"onUpdate:modelValue":o[2]||(o[2]=e=>l.path=e),placeholder:"选择接口"},{default:a(()=>[(i(!0),v(k,null,I(M.value,e=>(i(),b(V,{key:e.value,label:e.label,value:e.value},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1}),t(x,{label:"Method","label-width":g},{default:a(()=>[t(C,{modelValue:l.method,"onUpdate:modelValue":o[3]||(o[3]=e=>l.method=e),placeholder:"选择Method"},{default:a(()=>[(i(!0),v(k,null,I(U.value,e=>(i(),b(V,{key:e.value,label:e.label,value:e.value},null,8,["label","value"]))),128))]),_:1},8,["modelValue"])]),_:1})]),_:1},8,["model"])]),_:1},8,["modelValue"])])}}});const pe=X(re,[["__scopeId","data-v-e7746445"]]);export{pe as default};
2 |
--------------------------------------------------------------------------------
/website/assets/cloud-3a6d1fb9.js:
--------------------------------------------------------------------------------
1 | import{a as s}from"./index-04cccabd.js";const e=o=>s.get("/cloud/v1/region",{params:o}),n=o=>s.get("/cloud/v1/list",{params:o}),r=o=>s.post("/cloud/v1/password",o);export{e as a,r as c,n as l};
2 |
--------------------------------------------------------------------------------
/website/assets/cluster-7426750c.js:
--------------------------------------------------------------------------------
1 | import{a as t}from"./index-04cccabd.js";const r=s=>t.post("/cluster/v1/add",s),a=()=>t.get("/cluster/v1/list"),l=s=>t.get("/cluster/v1/nodes",{params:s}),u=s=>t.get("/cluster/v1/masters",{params:s});export{l as a,r as b,u as c,a as l};
2 |
--------------------------------------------------------------------------------
/website/assets/codis-7a3bd4ff.js:
--------------------------------------------------------------------------------
1 | import{a as o}from"./index-04cccabd.js";const r=s=>o.post("/codis/v1/add",s),d=()=>o.get("/codis/v1/list"),i=s=>o.get("/codis/v1/cluster",{params:s}),e=s=>o.get("/codis/v1/group",{params:s}),c=s=>o.post("/codis/v1/opnode",s);export{i as a,r as b,e as c,d as l,c as o};
2 |
--------------------------------------------------------------------------------
/website/assets/github-mark-367d5cb2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iguidao/redis-manager/43400bf548ffb68767ce7067679c6128fc2e155b/website/assets/github-mark-367d5cb2.png
--------------------------------------------------------------------------------
/website/assets/setting-b5212552.js:
--------------------------------------------------------------------------------
1 | import{a as t}from"./index-04cccabd.js";const a=()=>t.get("/cfg/v1/list"),f=()=>t.get("/cfg/v1/listdefault"),r=e=>t.delete("/cfg/v1/del",{params:e}),g=e=>t.post("/cfg/v1/update",e);export{f as a,r as d,a as l,g as u};
2 |
--------------------------------------------------------------------------------
/website/assets/sun-80b81115.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iguidao/redis-manager/43400bf548ffb68767ce7067679c6128fc2e155b/website/assets/sun-80b81115.jpeg
--------------------------------------------------------------------------------
/website/assets/user-d7605884.js:
--------------------------------------------------------------------------------
1 | import{a as e}from"./index-04cccabd.js";const r=s=>e.post("/auth/v1/sign-in",s),u=s=>e.post("/auth/v1/password",s),n=()=>e.get("/user/v1/list"),a=()=>e.get("/user/v1/utype"),o=s=>e.post("/user/v1/add",s),c=s=>e.post("/user/v1/change",s),d=s=>e.delete("/user/v1/del",{params:s});export{n as a,a as b,d as c,c as d,o as e,r as l,u};
2 |
--------------------------------------------------------------------------------
/website/favicon.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
16 |
--------------------------------------------------------------------------------
/website/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Redis Manager
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/website/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/yaml/dev.yaml:
--------------------------------------------------------------------------------
1 | local:
2 | addr: 0.0.0.0:8000
3 | runmode: debug
4 | pagesize: 10
5 | logapipath: "./logs/api.log"
6 | logapppath: "./logs/app.log"
7 |
8 | rediscfg:
9 | allkeyfornum: 10
10 | locktime: 60
11 | biglocktime: 600
12 | checksize: 4000
13 |
14 | mysql:
15 | name: redis_manager
16 | addr: 127.0.0.1:3308
17 | username: root
18 | password: 123456
19 |
20 | redis:
21 | addr: 10.2.74.82
22 | port: 6379
23 | password: xxxxxxxxxx
24 | db: 0
25 |
--------------------------------------------------------------------------------
/yaml/online.yaml:
--------------------------------------------------------------------------------
1 | local:
2 | addr: 0.0.0.0:8000
3 | runmode: debug
4 | pagesize: 10
5 | logapipath: "./logs/api.log"
6 | logapppath: "./logs/app.log"
7 |
8 | rediscfg:
9 | allkeyfornum: 10
10 | locktime: 60
11 | biglocktime: 600
12 | checksize: 4000
13 |
14 | mysql:
15 | name: dev_redis_manager
16 | addr: 127.0.0.1:3308
17 | username: root
18 | password: 123456
19 |
20 | redis:
21 | addr: 10.2.74.82
22 | port: 6379
23 | password: xxxxxxxxxx
24 | db: 0
25 |
--------------------------------------------------------------------------------