From 94cebfd8cc20d793df5ead8bc917aec39befe557 Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Wed, 15 May 2024 11:56:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20Redis=20=E6=94=AF=E6=8C=81=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E6=95=B0=E6=8D=AE=E5=BA=93=20(#5014)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/app/api/v1/database_redis.go | 25 ++- backend/app/api/v1/terminal.go | 36 +++- backend/app/service/database.go | 24 ++- backend/app/service/database_redis.go | 33 ++++ backend/router/ro_database.go | 2 + backend/utils/redis/redis.go | 26 +++ cmd/server/docs/docs.go | 25 ++- cmd/server/docs/swagger.json | 25 ++- cmd/server/docs/swagger.yaml | 17 +- frontend/src/api/modules/database.ts | 6 + frontend/src/lang/modules/en.ts | 1 + frontend/src/lang/modules/tw.ts | 1 + frontend/src/lang/modules/zh.ts | 1 + frontend/src/routers/modules/database.ts | 10 + frontend/src/store/interface/index.ts | 1 + frontend/src/store/modules/global.ts | 4 + .../src/views/database/redis/check/index.vue | 54 ++++++ frontend/src/views/database/redis/index.vue | 84 ++++++++- .../database/redis/remote/delete/index.vue | 100 ++++++++++ .../src/views/database/redis/remote/index.vue | 176 ++++++++++++++++++ .../database/redis/remote/operate/index.vue | 174 +++++++++++++++++ go.mod | 1 + go.sum | 5 + 23 files changed, 796 insertions(+), 35 deletions(-) create mode 100644 backend/utils/redis/redis.go create mode 100644 frontend/src/views/database/redis/check/index.vue create mode 100644 frontend/src/views/database/redis/remote/delete/index.vue create mode 100644 frontend/src/views/database/redis/remote/index.vue create mode 100644 frontend/src/views/database/redis/remote/operate/index.vue diff --git a/backend/app/api/v1/database_redis.go b/backend/app/api/v1/database_redis.go index 720879763108..60b086280474 100644 --- a/backend/app/api/v1/database_redis.go +++ b/backend/app/api/v1/database_redis.go @@ -16,7 +16,7 @@ import ( // @Param request body dto.OperationWithName true "request" // @Success 200 {object} dto.RedisStatus // @Security ApiKeyAuth -// @Router /databases/redis/status [get] +// @Router /databases/redis/status [post] func (b *BaseApi) LoadRedisStatus(c *gin.Context) { var req dto.OperationWithName if err := helper.CheckBind(&req, c); err != nil { @@ -38,7 +38,7 @@ func (b *BaseApi) LoadRedisStatus(c *gin.Context) { // @Param request body dto.OperationWithName true "request" // @Success 200 {object} dto.RedisConf // @Security ApiKeyAuth -// @Router /databases/redis/conf [get] +// @Router /databases/redis/conf [post] func (b *BaseApi) LoadRedisConf(c *gin.Context) { var req dto.OperationWithName if err := helper.CheckBind(&req, c); err != nil { @@ -60,7 +60,7 @@ func (b *BaseApi) LoadRedisConf(c *gin.Context) { // @Param request body dto.OperationWithName true "request" // @Success 200 {object} dto.RedisPersistence // @Security ApiKeyAuth -// @Router /databases/redis/persistence/conf [get] +// @Router /databases/redis/persistence/conf [post] func (b *BaseApi) LoadPersistenceConf(c *gin.Context) { var req dto.OperationWithName if err := helper.CheckBind(&req, c); err != nil { @@ -75,6 +75,25 @@ func (b *BaseApi) LoadPersistenceConf(c *gin.Context) { helper.SuccessWithData(c, data) } +func (b *BaseApi) CheckHasCli(c *gin.Context) { + helper.SuccessWithData(c, redisService.CheckHasCli()) +} + +// @Tags Database Redis +// @Summary Install redis-cli +// @Description 安装 redis cli +// @Success 200 +// @Security ApiKeyAuth +// @Router /databases/redis/install/cli [post] +func (b *BaseApi) InstallCli(c *gin.Context) { + if err := redisService.InstallCli(); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithOutData(c) +} + // @Tags Database Redis // @Summary Update redis conf // @Description 更新 redis 配置信息 diff --git a/backend/app/api/v1/terminal.go b/backend/app/api/v1/terminal.go index 6d2e68bde357..8569488a9188 100644 --- a/backend/app/api/v1/terminal.go +++ b/backend/app/api/v1/terminal.go @@ -3,6 +3,7 @@ package v1 import ( "encoding/base64" "encoding/json" + "fmt" "net/http" "strconv" "strings" @@ -88,23 +89,38 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) { return } name := c.Query("name") - redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Type: "redis", Name: name}) - if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) { - return - } - + from := c.Query("from") defer wsConn.Close() commands := []string{"redis-cli"} - if len(redisInfo.Password) != 0 { - commands = []string{"redis-cli", "-a", redisInfo.Password, "--no-auth-warning"} + database, err := databaseService.Get(name) + if wshandleError(wsConn, errors.WithMessage(err, "no such database in db")) { + return } - pidMap := loadMapFromDockerTop(redisInfo.Password) - itemCmds := append([]string{"exec", "-it", redisInfo.ContainerName}, commands...) + if from == "local" { + redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: "redis"}) + if wshandleError(wsConn, errors.WithMessage(err, "no such database in db")) { + return + } + name = redisInfo.ContainerName + if len(database.Password) != 0 { + commands = []string{"redis-cli", "-a", database.Password, "--no-auth-warning"} + } + } else { + itemPort := fmt.Sprintf("%v", database.Port) + commands = []string{"redis-cli", "-h", database.Address, "-p", itemPort} + if len(database.Password) != 0 { + commands = []string{"redis-cli", "-h", database.Address, "-p", itemPort, "-a", database.Password, "--no-auth-warning"} + } + name = "1Panel-redis-cli-tools" + } + + pidMap := loadMapFromDockerTop(name) + itemCmds := append([]string{"exec", "-it", name}, commands...) slave, err := terminal.NewCommand(itemCmds) if wshandleError(wsConn, err) { return } - defer killBash(redisInfo.ContainerName, strings.Join(commands, " "), pidMap) + defer killBash(name, strings.Join(commands, " "), pidMap) defer slave.Close() tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false) diff --git a/backend/app/service/database.go b/backend/app/service/database.go index 4ae131e5fe86..b27089bf8139 100644 --- a/backend/app/service/database.go +++ b/backend/app/service/database.go @@ -7,7 +7,8 @@ import ( "path" "github.com/1Panel-dev/1Panel/backend/utils/postgresql" - client2 "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client" + pg_client "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client" + redis_client "github.com/1Panel-dev/1Panel/backend/utils/redis" "github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/buserr" @@ -113,7 +114,7 @@ func (u *DatabaseService) LoadItems(dbType string) ([]dto.DatabaseItem, error) { func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool { switch req.Type { case constant.AppPostgresql: - _, err := postgresql.NewPostgresqlClient(client2.DBInfo{ + _, err := postgresql.NewPostgresqlClient(pg_client.DBInfo{ From: "remote", Address: req.Address, Port: req.Port, @@ -122,6 +123,13 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool { Timeout: 6, }) return err == nil + case constant.AppRedis: + _, err := redis_client.NewRedisClient(redis_client.DBInfo{ + Address: req.Address, + Port: req.Port, + Password: req.Password, + }) + return err == nil case "mysql", "mariadb": _, err := mysql.NewMysqlClient(client.DBInfo{ From: "remote", @@ -153,7 +161,7 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error { } switch req.Type { case constant.AppPostgresql: - if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{ + if _, err := postgresql.NewPostgresqlClient(pg_client.DBInfo{ From: "remote", Address: req.Address, Port: req.Port, @@ -163,6 +171,14 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error { }); err != nil { return err } + case constant.AppRedis: + if _, err := redis_client.NewRedisClient(redis_client.DBInfo{ + Address: req.Address, + Port: req.Port, + Password: req.Password, + }); err != nil { + return err + } case "mysql", "mariadb": if _, err := mysql.NewMysqlClient(client.DBInfo{ From: "remote", @@ -249,7 +265,7 @@ func (u *DatabaseService) Delete(req dto.DatabaseDelete) error { func (u *DatabaseService) Update(req dto.DatabaseUpdate) error { switch req.Type { case constant.AppPostgresql: - if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{ + if _, err := postgresql.NewPostgresqlClient(pg_client.DBInfo{ From: "remote", Address: req.Address, Port: req.Port, diff --git a/backend/app/service/database_redis.go b/backend/app/service/database_redis.go index d57cb5d1e572..3c91a2311883 100644 --- a/backend/app/service/database_redis.go +++ b/backend/app/service/database_redis.go @@ -1,6 +1,7 @@ package service import ( + "context" "encoding/json" "errors" "fmt" @@ -11,6 +12,8 @@ import ( "github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/utils/compose" + "github.com/1Panel-dev/1Panel/backend/utils/docker" + "github.com/docker/docker/api/types/container" _ "github.com/go-sql-driver/mysql" ) @@ -24,6 +27,9 @@ type IRedisService interface { LoadStatus(req dto.OperationWithName) (*dto.RedisStatus, error) LoadConf(req dto.OperationWithName) (*dto.RedisConf, error) LoadPersistenceConf(req dto.OperationWithName) (*dto.RedisPersistence, error) + + CheckHasCli() bool + InstallCli() error } func NewIRedisService() IRedisService { @@ -50,6 +56,33 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error { return nil } +func (u *RedisService) CheckHasCli() bool { + client, err := docker.NewDockerClient() + if err != nil { + return false + } + defer client.Close() + containerLists, err := client.ContainerList(context.Background(), container.ListOptions{}) + if err != nil { + return false + } + for _, item := range containerLists { + if strings.ReplaceAll(item.Names[0], "/", "") == "1Panel-redis-cli-tools" { + return true + } + } + return false +} + +func (u *RedisService) InstallCli() error { + item := dto.ContainerOperate{ + Name: "1Panel-redis-cli-tools", + Image: "redis:7.2.4", + Network: "1panel-network", + } + return NewIContainerService().ContainerCreate(item) +} + func (u *RedisService) ChangePassword(req dto.ChangeRedisPass) error { if err := updateInstallInfoInDB("redis", req.Database, "password", req.Value); err != nil { return err diff --git a/backend/router/ro_database.go b/backend/router/ro_database.go index 3cc26d9f866d..c0307651dcda 100644 --- a/backend/router/ro_database.go +++ b/backend/router/ro_database.go @@ -39,6 +39,8 @@ func (s *DatabaseRouter) InitRouter(Router *gin.RouterGroup) { cmdRouter.POST("/redis/status", baseApi.LoadRedisStatus) cmdRouter.POST("/redis/conf", baseApi.LoadRedisConf) cmdRouter.GET("/redis/exec", baseApi.RedisWsSsh) + cmdRouter.GET("/redis/check", baseApi.CheckHasCli) + cmdRouter.POST("/redis/install/cli", baseApi.InstallCli) cmdRouter.POST("/redis/password", baseApi.ChangeRedisPassword) cmdRouter.POST("/redis/conf/update", baseApi.UpdateRedisConf) cmdRouter.POST("/redis/persistence/update", baseApi.UpdateRedisPersistenceConf) diff --git a/backend/utils/redis/redis.go b/backend/utils/redis/redis.go new file mode 100644 index 000000000000..f4b28771ea73 --- /dev/null +++ b/backend/utils/redis/redis.go @@ -0,0 +1,26 @@ +package redis + +import ( + "fmt" + + "github.com/go-redis/redis" +) + +type DBInfo struct { + Address string `json:"address"` + Port uint `json:"port"` + Password string `json:"password"` +} + +func NewRedisClient(conn DBInfo) (*redis.Client, error) { + client := redis.NewClient(&redis.Options{ + Addr: fmt.Sprintf("%s:%v", conn.Address, conn.Port), + Password: conn.Password, + DB: 0, + }) + + if _, err := client.Ping().Result(); err != nil { + return client, err + } + return client, nil +} diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index 81b38e7333b1..ad4f4ddc6833 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -5110,7 +5110,7 @@ const docTemplate = `{ } }, "/databases/redis/conf": { - "get": { + "post": { "security": [ { "ApiKeyAuth": [] @@ -5185,6 +5185,25 @@ const docTemplate = `{ } } }, + "/databases/redis/install/cli": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "安装 redis cli", + "tags": [ + "Database Redis" + ], + "summary": "Install redis-cli", + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/databases/redis/password": { "post": { "security": [ @@ -5226,7 +5245,7 @@ const docTemplate = `{ } }, "/databases/redis/persistence/conf": { - "get": { + "post": { "security": [ { "ApiKeyAuth": [] @@ -5302,7 +5321,7 @@ const docTemplate = `{ } }, "/databases/redis/status": { - "get": { + "post": { "security": [ { "ApiKeyAuth": [] diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index be737759d4d7..f4f21730af61 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -5103,7 +5103,7 @@ } }, "/databases/redis/conf": { - "get": { + "post": { "security": [ { "ApiKeyAuth": [] @@ -5178,6 +5178,25 @@ } } }, + "/databases/redis/install/cli": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "安装 redis cli", + "tags": [ + "Database Redis" + ], + "summary": "Install redis-cli", + "responses": { + "200": { + "description": "OK" + } + } + } + }, "/databases/redis/password": { "post": { "security": [ @@ -5219,7 +5238,7 @@ } }, "/databases/redis/persistence/conf": { - "get": { + "post": { "security": [ { "ApiKeyAuth": [] @@ -5295,7 +5314,7 @@ } }, "/databases/redis/status": { - "get": { + "post": { "security": [ { "ApiKeyAuth": [] diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index 8c4a3a2715b8..c64f47278928 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -8349,7 +8349,7 @@ paths: tags: - Database Postgresql /databases/redis/conf: - get: + post: consumes: - application/json description: 获取 redis 配置信息 @@ -8396,6 +8396,17 @@ paths: formatEN: update the redis database configuration information formatZH: 更新 redis 数据库配置信息 paramKeys: [] + /databases/redis/install/cli: + post: + description: 安装 redis cli + responses: + "200": + description: OK + security: + - ApiKeyAuth: [] + summary: Install redis-cli + tags: + - Database Redis /databases/redis/password: post: consumes: @@ -8423,7 +8434,7 @@ paths: formatZH: 修改 redis 数据库密码 paramKeys: [] /databases/redis/persistence/conf: - get: + post: consumes: - application/json description: 获取 redis 持久化配置 @@ -8471,7 +8482,7 @@ paths: formatZH: redis 数据库持久化配置更新 paramKeys: [] /databases/redis/status: - get: + post: consumes: - application/json description: 获取 redis 状态信息 diff --git a/frontend/src/api/modules/database.ts b/frontend/src/api/modules/database.ts index 5ffe19f9fa50..c1d3456533d2 100644 --- a/frontend/src/api/modules/database.ts +++ b/frontend/src/api/modules/database.ts @@ -117,6 +117,12 @@ export const loadRedisConf = (database: string) => { export const redisPersistenceConf = (database: string) => { return http.post(`/databases/redis/persistence/conf`, { name: database }); }; +export const checkRedisCli = () => { + return http.get(`/databases/redis/check`); +}; +export const installRedisCli = () => { + return http.post(`/databases/redis/install/cli`, {}, TimeoutEnum.T_5M); +}; export const changeRedisPassword = (database: string, password: string) => { if (password) { password = Base64.encode(password); diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index a0de52d47840..75ae02050593 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -514,6 +514,7 @@ const message = { keyspaceMisses: 'Number of failed attempts to find the database key', hit: 'Find the database key hit ratio', latestForkUsec: 'The number of microseconds spent on the last fork() operation', + redisCliHelper: 'redis-cli service not detected, please enable the service first!', recoverHelper: 'Data is about to be overwritten with [{0}]. Do you want to continue?', submitIt: 'Overwrite the data', diff --git a/frontend/src/lang/modules/tw.ts b/frontend/src/lang/modules/tw.ts index ee91de046164..029175fcebd3 100644 --- a/frontend/src/lang/modules/tw.ts +++ b/frontend/src/lang/modules/tw.ts @@ -502,6 +502,7 @@ const message = { keyspaceMisses: '查找數據庫鍵失敗的次數', hit: '查找數據庫鍵命中率', latestForkUsec: '最近一次 fork() 操作耗費的微秒數', + redisCliHelper: '未檢測到 redis-cli 服務,請先啟用服務!', recoverHelper: '即將使用 [{0}] 對數據進行覆蓋,是否繼續?', submitIt: '覆蓋數據', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 6634dac6b94d..6a4f9f7c79b2 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -502,6 +502,7 @@ const message = { keyspaceMisses: '查找数据库键失败的次数', hit: '查找数据库键命中率', latestForkUsec: '最近一次 fork() 操作耗费的微秒数', + redisCliHelper: '未检测到 redis-cli 服务,请先启用服务!', recoverHelper: '即将使用 [{0}] 对数据进行覆盖,是否继续?', submitIt: '覆盖数据', diff --git a/frontend/src/routers/modules/database.ts b/frontend/src/routers/modules/database.ts index 58cf4c11188a..84787705b884 100644 --- a/frontend/src/routers/modules/database.ts +++ b/frontend/src/routers/modules/database.ts @@ -89,6 +89,16 @@ const databaseRouter = { requiresAuth: false, }, }, + { + path: 'redis/remote', + name: 'Redis-Remote', + component: () => import('@/views/database/redis/remote/index.vue'), + hidden: true, + meta: { + activeMenu: '/databases', + requiresAuth: false, + }, + }, ], }, ], diff --git a/frontend/src/store/interface/index.ts b/frontend/src/store/interface/index.ts index 9971ba43fdfd..1328a81904f4 100644 --- a/frontend/src/store/interface/index.ts +++ b/frontend/src/store/interface/index.ts @@ -28,6 +28,7 @@ export interface GlobalState { device: DeviceType; lastFilePath: string; currentDB: string; + currentRedisDB: string; showEntranceWarn: boolean; defaultNetwork: string; diff --git a/frontend/src/store/modules/global.ts b/frontend/src/store/modules/global.ts index cf75f9feac20..87a39d320257 100644 --- a/frontend/src/store/modules/global.ts +++ b/frontend/src/store/modules/global.ts @@ -32,6 +32,7 @@ const GlobalStore = defineStore({ device: DeviceType.Desktop, lastFilePath: '', currentDB: '', + currentRedisDB: '', showEntranceWarn: true, defaultNetwork: 'all', @@ -80,6 +81,9 @@ const GlobalStore = defineStore({ setCurrentDB(name: string) { this.currentDB = name; }, + setCurrentRedisDB(name: string) { + this.currentRedisDB = name; + }, setShowEntranceWarn(show: boolean) { this.showEntranceWarn = show; }, diff --git a/frontend/src/views/database/redis/check/index.vue b/frontend/src/views/database/redis/check/index.vue new file mode 100644 index 000000000000..96f1451a048a --- /dev/null +++ b/frontend/src/views/database/redis/check/index.vue @@ -0,0 +1,54 @@ + + diff --git a/frontend/src/views/database/redis/index.vue b/frontend/src/views/database/redis/index.vue index b2e9a356e087..383ee76d94b5 100644 --- a/frontend/src/views/database/redis/index.vue +++ b/frontend/src/views/database/redis/index.vue @@ -1,5 +1,13 @@ @@ -104,8 +120,10 @@ import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'; import { CheckAppInstalled, GetAppPort } from '@/api/modules/app'; import router from '@/routers'; import { GlobalStore } from '@/store'; -import { listDatabases } from '@/api/modules/database'; +import { listDatabases, checkRedisCli, installRedisCli } from '@/api/modules/database'; import { Database } from '@/api/interface/database'; +import { MsgSuccess } from '@/utils/message'; +import i18n from '@/lang'; const globalStore = GlobalStore(); const loading = ref(false); @@ -121,6 +139,8 @@ const terminalShow = ref(false); const redisCommandPort = ref(); const commandVisible = ref(false); +const redisCliExist = ref(); + const appKey = ref('redis'); const appName = ref(); const dbOptionsLocal = ref>([]); @@ -153,6 +173,12 @@ const goDashboard = async () => { const getAppDetail = (key: string) => { router.push({ name: 'AppAll', query: { install: key } }); }; +const goRemoteDB = async () => { + if (currentDB.value) { + globalStore.setCurrentRedisDB(currentDBName.value); + } + router.push({ name: 'Redis-Remote' }); +}; const loadDashboardPort = async () => { const res = await GetAppPort('redis-commander', ''); @@ -237,17 +263,39 @@ const reOpenTerminal = async () => { const initTerminal = async () => { loading.value = true; + if (currentDB.value.from === 'remote') { + if (!redisCliExist.value) { + loading.value = false; + return; + } + isRefresh.value = !isRefresh.value; + loading.value = false; + redisIsExist.value = true; + nextTick(() => { + terminalShow.value = true; + redisStatus.value = 'Running'; + terminalRef.value.acceptParams({ + endpoint: '/api/v1/databases/redis/exec', + args: `name=${currentDBName.value}&from=${currentDB.value.from}`, + error: '', + initCmd: '', + }); + }); + isRefresh.value = !isRefresh.value; + return; + } await CheckAppInstalled('redis', currentDBName.value) .then((res) => { redisIsExist.value = res.data.isExist; redisStatus.value = res.data.status; + console.log(redisStatus.value); loading.value = false; nextTick(() => { if (res.data.status === 'Running') { terminalShow.value = true; terminalRef.value.acceptParams({ endpoint: '/api/v1/databases/redis/exec', - args: `name=${currentDBName.value}`, + args: `name=${currentDBName.value}&from=${currentDB.value.from}`, error: '', initCmd: '', }); @@ -266,9 +314,27 @@ const closeTerminal = async (isKeepShow: boolean) => { terminalShow.value = isKeepShow; }; +const checkCliValid = async () => { + const res = await checkRedisCli(); + redisCliExist.value = res.data; +}; +const installCli = async () => { + loading.value = true; + await installRedisCli() + .then(() => { + loading.value = false; + redisCliExist.value = true; + MsgSuccess(i18n.global.t('commons.msg.operationSuccess')); + }) + .catch(() => { + loading.value = false; + }); +}; + onMounted(() => { loadDBOptions(); loadDashboardPort(); + checkCliValid(); }); const onBefore = () => { closeTerminal(false); diff --git a/frontend/src/views/database/redis/remote/delete/index.vue b/frontend/src/views/database/redis/remote/delete/index.vue new file mode 100644 index 000000000000..04f4b4a63794 --- /dev/null +++ b/frontend/src/views/database/redis/remote/delete/index.vue @@ -0,0 +1,100 @@ + + diff --git a/frontend/src/views/database/redis/remote/index.vue b/frontend/src/views/database/redis/remote/index.vue new file mode 100644 index 000000000000..977b83d81582 --- /dev/null +++ b/frontend/src/views/database/redis/remote/index.vue @@ -0,0 +1,176 @@ + + + diff --git a/frontend/src/views/database/redis/remote/operate/index.vue b/frontend/src/views/database/redis/remote/operate/index.vue new file mode 100644 index 000000000000..0212a1953a7c --- /dev/null +++ b/frontend/src/views/database/redis/remote/operate/index.vue @@ -0,0 +1,174 @@ + + + diff --git a/go.mod b/go.mod index b3ceedff456f..8f93b26da76b 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/go-acme/lego/v4 v4.15.0 github.com/go-gormigrate/gormigrate/v2 v2.1.1 github.com/go-playground/validator/v10 v10.18.0 + github.com/go-redis/redis v6.15.9+incompatible github.com/go-sql-driver/mysql v1.7.1 github.com/goh-chunlin/go-onedrive v1.1.1 github.com/golang-jwt/jwt/v4 v4.5.0 diff --git a/go.sum b/go.sum index a1e9f21942c4..577450831fec 100644 --- a/go.sum +++ b/go.sum @@ -328,6 +328,8 @@ github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiR github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U= github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= +github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= @@ -474,6 +476,7 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= @@ -1256,6 +1259,7 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= @@ -1265,6 +1269,7 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM= gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=