diff --git a/backend/app/api/v1/database_redis.go b/backend/app/api/v1/database_redis.go index d43e10806..adff49430 100644 --- a/backend/app/api/v1/database_redis.go +++ b/backend/app/api/v1/database_redis.go @@ -66,6 +66,23 @@ func (b *BaseApi) UpdateRedisConf(c *gin.Context) { helper.SuccessWithData(c, nil) } +func (b *BaseApi) ChangeRedisPassword(c *gin.Context) { + var req dto.ChangeDBInfo + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := redisService.ChangePassword(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + func (b *BaseApi) UpdateRedisPersistenceConf(c *gin.Context) { var req dto.RedisConfPersistenceUpdate if err := c.ShouldBindJSON(&req); err != nil { diff --git a/backend/app/service/app_install.go b/backend/app/service/app_install.go index 529625c96..9dab46b5e 100644 --- a/backend/app/service/app_install.go +++ b/backend/app/service/app_install.go @@ -284,7 +284,7 @@ func (a AppInstallService) ChangeAppPort(req dto.PortUpdate) error { newFiles = append(newFiles, line) } } - file, err := os.OpenFile(path, os.O_WRONLY, 0666) + file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0666) if err != nil { return err } @@ -480,21 +480,26 @@ func updateInstallInfoInDB(appKey, param string, value interface{}) { if param != "password" && param != "port" { return } - appInstall, _ := appInstallRepo.LoadBaseInfoByKey(appKey) - if appInstall.ID == 0 { + appInstall, err := appInstallRepo.LoadBaseInfoByKey(appKey) + if err != nil { return } oldVal, newVal := "", "" if param == "password" { oldVal = fmt.Sprintf("\"PANEL_DB_ROOT_PASSWORD\":\"%v\"", appInstall.Password) newVal = fmt.Sprintf("\"PANEL_DB_ROOT_PASSWORD\":\"%v\"", value) + _ = appInstallRepo.BatchUpdateBy(map[string]interface{}{ + "param": strings.ReplaceAll(appInstall.Param, oldVal, newVal), + "env": strings.ReplaceAll(appInstall.Env, oldVal, newVal), + }, commonRepo.WithByID(appInstall.ID)) } if param == "port" { oldVal = fmt.Sprintf("\"PANEL_APP_PORT_HTTP\":%v", appInstall.Port) newVal = fmt.Sprintf("\"PANEL_APP_PORT_HTTP\":%v", value) + _ = appInstallRepo.BatchUpdateBy(map[string]interface{}{ + "param": strings.ReplaceAll(appInstall.Param, oldVal, newVal), + "env": strings.ReplaceAll(appInstall.Env, oldVal, newVal), + "http_port": value, + }, commonRepo.WithByID(appInstall.ID)) } - _ = appInstallRepo.BatchUpdateBy(map[string]interface{}{ - "param": strings.ReplaceAll(appInstall.Param, oldVal, newVal), - "env": strings.ReplaceAll(appInstall.Env, oldVal, newVal), - }, commonRepo.WithByID(appInstall.ID)) } diff --git a/backend/app/service/database_mysql.go b/backend/app/service/database_mysql.go index 314e70532..fcd068597 100644 --- a/backend/app/service/database_mysql.go +++ b/backend/app/service/database_mysql.go @@ -240,7 +240,7 @@ func (u *MysqlService) DeleteCheck(id uint) ([]string, error) { return appInUsed, err } - apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithResourceId(app.ID), appInstallResourceRepo.WithLinkId(db.ID)) + apps, _ := appInstallResourceRepo.GetBy(appInstallResourceRepo.WithLinkId(app.ID), appInstallResourceRepo.WithResourceId(db.ID)) for _, app := range apps { appInstall, _ := appInstallRepo.GetFirst(commonRepo.WithByID(app.AppInstallId)) if appInstall.ID != 0 { @@ -431,7 +431,7 @@ func (u *MysqlService) UpdateVariables(updatas []dto.MysqlVariablesUpdate) error files = updateMyCnf(files, group, info.Param, loadSizeUnit(info.Value)) } - file, err := os.OpenFile(path, os.O_WRONLY, 0666) + file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0666) if err != nil { return err } diff --git a/backend/app/service/database_redis.go b/backend/app/service/database_redis.go index 7e4478b02..b79924ffb 100644 --- a/backend/app/service/database_redis.go +++ b/backend/app/service/database_redis.go @@ -22,6 +22,7 @@ type RedisService struct{} type IRedisService interface { UpdateConf(req dto.RedisConfUpdate) error UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error + ChangePassword(info dto.ChangeDBInfo) error LoadStatus() (*dto.RedisStatus, error) LoadConf() (*dto.RedisConf, error) @@ -48,12 +49,6 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error { return err } - updateInstallInfoInDB("redis", "password", req.Requirepass) - updateInstallInfoInDB("phpmyadmin", "password", req.Requirepass) - - if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "requirepass", req.Requirepass); err != nil { - return err - } if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "maxmemory", req.Maxmemory); err != nil { return err } @@ -67,6 +62,57 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error { return nil } +func (u *RedisService) ChangePassword(req dto.ChangeDBInfo) error { + var ( + files []string + newFiles []string + ) + + redisInfo, err := appInstallRepo.LoadBaseInfoByKey("redis") + if err != nil { + return err + } + ComposeDir := fmt.Sprintf("%s/redis/%s", constant.AppInstallDir, redisInfo.Name) + ComposeFile := fmt.Sprintf("%s/redis/%s/docker-compose.yml", constant.AppInstallDir, redisInfo.Name) + path := fmt.Sprintf("%s/.env", ComposeDir) + lineBytes, err := ioutil.ReadFile(path) + if err != nil { + return err + } else { + files = strings.Split(string(lineBytes), "\n") + } + for _, line := range files { + if strings.HasPrefix(line, "PANEL_DB_ROOT_PASSWORD=") { + newFiles = append(newFiles, fmt.Sprintf("PANEL_DB_ROOT_PASSWORD=%v", req.Value)) + } else { + newFiles = append(newFiles, line) + } + } + file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0666) + if err != nil { + return err + } + defer file.Close() + _, err = file.WriteString(strings.Join(newFiles, "\n")) + if err != nil { + return err + } + + updateInstallInfoInDB("redis", "password", req.Value) + updateInstallInfoInDB("redis-commander", "password", req.Value) + + stdout, err := compose.Down(ComposeFile) + if err != nil { + return errors.New(stdout) + } + stdout, err = compose.Up(ComposeFile) + if err != nil { + return errors.New(stdout) + } + + return nil +} + func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error { redisInfo, err := appInstallRepo.LoadBaseInfoByKey("redis") if err != nil { @@ -132,18 +178,10 @@ func (u *RedisService) LoadConf() (*dto.RedisConf, error) { item.ContainerName = redisInfo.ContainerName item.Name = redisInfo.Name item.Port = redisInfo.Port - if item.Timeout, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "timeout"); err != nil { - return nil, err - } - if item.Maxclients, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "maxclients"); err != nil { - return nil, err - } - if item.Requirepass, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "requirepass"); err != nil { - return nil, err - } - if item.Maxmemory, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "maxmemory"); err != nil { - return nil, err - } + item.Requirepass = redisInfo.Password + item.Timeout, _ = configGetStr(redisInfo.ContainerName, redisInfo.Password, "timeout") + item.Maxclients, _ = configGetStr(redisInfo.ContainerName, redisInfo.Password, "maxclients") + item.Maxmemory, _ = configGetStr(redisInfo.ContainerName, redisInfo.Password, "maxmemory") return &item, nil } diff --git a/backend/router/ro_database.go b/backend/router/ro_database.go index 60f3b87f9..4524093a5 100644 --- a/backend/router/ro_database.go +++ b/backend/router/ro_database.go @@ -42,6 +42,7 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) { cmdRouter.GET("/redis/status", baseApi.LoadRedisStatus) cmdRouter.GET("/redis/conf", baseApi.LoadRedisConf) cmdRouter.GET("/redis/exec", baseApi.RedisExec) + cmdRouter.POST("/redis/password", baseApi.ChangeRedisPassword) cmdRouter.POST("/redis/backup", baseApi.RedisBackup) cmdRouter.POST("/redis/recover", baseApi.RedisRecover) cmdRouter.POST("/redis/backup/records", baseApi.RedisBackupList) diff --git a/frontend/src/api/modules/database.ts b/frontend/src/api/modules/database.ts index 9a34c1f01..f248ebe22 100644 --- a/frontend/src/api/modules/database.ts +++ b/frontend/src/api/modules/database.ts @@ -64,6 +64,9 @@ export const loadRedisConf = () => { export const RedisPersistenceConf = () => { return http.get(`/databases/redis/persistence/conf`); }; +export const changeRedisPassword = (params: Database.ChangeInfo) => { + return http.post(`/databases/redis/password`, params); +}; export const updateRedisPersistenceConf = (params: Database.RedisConfPersistenceUpdate) => { return http.post(`/databases/redis/conf/update/persistence`, params); }; diff --git a/frontend/src/hooks/use-delete-data.ts b/frontend/src/hooks/use-delete-data.ts index 2ce242746..647788c1b 100644 --- a/frontend/src/hooks/use-delete-data.ts +++ b/frontend/src/hooks/use-delete-data.ts @@ -17,7 +17,7 @@ export const useDeleteData =

( confirmType: HandleData.MessageType = 'error', ) => { return new Promise((resolve, reject) => { - ElMessageBox.confirm(i18n.global.t(`${message}`) + '?', i18n.global.t('commons.msg.deleteTitle'), { + ElMessageBox.confirm(i18n.global.t(`${message}`), i18n.global.t('commons.msg.deleteTitle'), { confirmButtonText: i18n.global.t('commons.button.confirm'), cancelButtonText: i18n.global.t('commons.button.cancel'), closeOnClickModal: false, diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts index 6af126e84..e18ca83eb 100644 --- a/frontend/src/lang/modules/en.ts +++ b/frontend/src/lang/modules/en.ts @@ -307,7 +307,8 @@ export default { timeoutHelper: 'Idle connection timeout period. 0 indicates that the connection is on continuously', maxclients: 'Max clients', requirepass: 'Password', - requirepassHelper: 'Leaving a blank indicates that no password has been set', + requirepassHelper: + 'Leave this blank to indicate that no password has been set. Changes need to be saved separately and the container restarted!', databases: 'Number of databases', maxmemory: 'Maximum memory usage', maxmemoryHelper: '0 indicates no restriction', diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index c458a5119..dda49eee2 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -320,7 +320,7 @@ export default { timeoutHelper: '空闲连接超时时间,0表示不断开', maxclients: '最大连接数', requirepass: '密码', - requirepassHelper: '留空代表没有设置密码', + requirepassHelper: '留空代表没有设置密码,修改需要单独保存并且重启容器!', databases: '数据库数量', maxmemory: '最大内存使用', maxmemoryHelper: '0 表示不做限制', @@ -764,7 +764,7 @@ export default { name: '名称', description: '描述', delete: '删除', - deleteWarn: '删除操作会把所有数据和备份一并删除,此操作不可回滚,是否继续?', + deleteWarn: '删除操作会把所有数据和备份一并删除,此操作不可回滚,是否继续?', syncSuccess: '同步成功', canUpdate: '可升级', backup: '备份', diff --git a/frontend/src/views/database/mysql/index.vue b/frontend/src/views/database/mysql/index.vue index 5a71b2bbe..f8c499fb7 100644 --- a/frontend/src/views/database/mysql/index.vue +++ b/frontend/src/views/database/mysql/index.vue @@ -4,7 +4,7 @@ - + {{ $t('database.mysqlBadStatus') }} 【 {{ $t('database.setting') }} 】 diff --git a/frontend/src/views/database/redis/index.vue b/frontend/src/views/database/redis/index.vue index 1561e5687..4d962ff2c 100644 --- a/frontend/src/views/database/redis/index.vue +++ b/frontend/src/views/database/redis/index.vue @@ -48,6 +48,7 @@ const terminalRef = ref(); const settingRef = ref(); const isOnSetting = ref(false); const redisIsExist = ref(false); +const redisSattus = ref(); const redisCommandPort = ref(); const commandVisiable = ref(false); @@ -55,7 +56,7 @@ const commandVisiable = ref(false); const onSetting = async () => { isOnSetting.value = true; terminalRef.value.onClose(); - settingRef.value!.acceptParams(); + settingRef.value!.acceptParams({ status: redisSattus.value }); }; const goRouter = async (path: string) => { @@ -79,6 +80,7 @@ const loadDashboardPort = async () => { const checkExist = (data: App.CheckInstalled) => { redisIsExist.value = data.isExist; + redisSattus.value = data.status; if (redisIsExist.value) { loadDashboardPort(); terminalRef.value.acceptParams(); diff --git a/frontend/src/views/database/redis/setting/index.vue b/frontend/src/views/database/redis/setting/index.vue index 362e32c1f..a8d26d623 100644 --- a/frontend/src/views/database/redis/setting/index.vue +++ b/frontend/src/views/database/redis/setting/index.vue @@ -29,25 +29,33 @@ {{ $t('database.portHelper') }} - + + + {{ $t('database.requirepassHelper') }} - - - {{ $t('database.timeoutHelper') }} - - - - - - - {{ $t('database.maxmemoryHelper') }} - - - - {{ $t('commons.button.save') }} - - +

+ + + {{ $t('database.timeoutHelper') }} + + + + + + + {{ $t('database.maxmemoryHelper') }} + + + + {{ $t('commons.button.save') }} + + +
@@ -63,7 +71,7 @@ theme="cobalt" :styleActiveLine="true" :extensions="extensions" - v-model="mysqlConf" + v-model="redisConf" :readOnly="true" /> + @@ -102,7 +111,7 @@ import { LoadFile } from '@/api/modules/files'; import ConfirmDialog from '@/components/confirm-dialog/index.vue'; import Status from '@/views/database/redis/setting/status/index.vue'; import Persistence from '@/views/database/redis/setting/persistence/index.vue'; -import { loadRedisConf, updateRedisConf, updateRedisConfByFile } from '@/api/modules/database'; +import { changeRedisPassword, loadRedisConf, updateRedisConf, updateRedisConfByFile } from '@/api/modules/database'; import i18n from '@/lang'; import { Rules } from '@/global/form-rules'; import { ChangePort } from '@/api/modules/app'; @@ -131,18 +140,25 @@ const activeName = ref('1'); const statusRef = ref(); const persistenceRef = ref(); +const redisStatus = ref(); + const formRef = ref(); -const mysqlConf = ref(); +const redisConf = ref(); const confirmDialogRef = ref(); const confirmDialogRef2 = ref(); const settingShow = ref(false); -const acceptParams = (): void => { +interface DialogProps { + status: string; +} + +const acceptParams = (prop: DialogProps): void => { + redisStatus.value = prop.status; settingShow.value = true; loadform(); - statusRef.value!.acceptParams(); - persistenceRef.value!.acceptParams(); + statusRef.value!.acceptParams({ status: prop.status }); + persistenceRef.value!.acceptParams({ status: prop.status }); }; const onClose = (): void => { settingShow.value = false; @@ -157,6 +173,31 @@ const onSavePort = async () => { confirmDialogRef2.value!.acceptParams(params); }; +const confirmDialogRef3 = ref(); +const onSavePassword = async () => { + let params = { + header: i18n.global.t('database.confChange'), + operationInfo: i18n.global.t('database.restartNowHelper1'), + submitInputInfo: i18n.global.t('database.restartNow'), + }; + confirmDialogRef3.value!.acceptParams(params); +}; +const onChangePassword = async () => { + loading.value = true; + let param = { + id: 0, + value: form.requirepass, + }; + changeRedisPassword(param) + .then(() => { + loading.value = false; + ElMessage.success(i18n.global.t('commons.msg.operationSuccess')); + }) + .finally(() => { + loading.value = false; + }); +}; + const onChangePort = async (formEl: FormInstance | undefined) => { if (!formEl) return; const result = await formEl.validateField('port', callback); @@ -188,7 +229,7 @@ function callback(error: any) { const onChangeMode = async () => { if (confShowType.value === 'all') { - loadMysqlConf(); + loadredisConf(); } else { loadform(); } @@ -231,7 +272,7 @@ const onSaveFile = async () => { const onSubmitSave = async () => { let param = { - file: mysqlConf.value, + file: redisConf.value, restartNow: true, }; await updateRedisConfByFile(param); @@ -239,19 +280,26 @@ const onSubmitSave = async () => { }; const loadform = async () => { - const res = await loadRedisConf(); - form.name = res.data?.name; - form.timeout = Number(res.data?.timeout); - form.maxclients = Number(res.data?.maxclients); - form.requirepass = res.data?.requirepass; - form.maxmemory = Number(res.data?.maxmemory); - form.port = Number(res.data?.port); + loading.value = true; + await loadRedisConf() + .then((res) => { + loading.value = false; + form.name = res.data?.name; + form.timeout = Number(res.data?.timeout); + form.maxclients = Number(res.data?.maxclients); + form.requirepass = res.data?.requirepass; + form.maxmemory = Number(res.data?.maxmemory); + form.port = Number(res.data?.port); + }) + .catch(() => { + loading.value = false; + }); }; -const loadMysqlConf = async () => { +const loadredisConf = async () => { let path = `/opt/1Panel/data/apps/redis/${form.name}/conf/redis.conf`; const res = await LoadFile({ path: path }); - mysqlConf.value = res.data; + redisConf.value = res.data; }; defineExpose({ diff --git a/frontend/src/views/database/redis/setting/persistence/index.vue b/frontend/src/views/database/redis/setting/persistence/index.vue index f565f0b3d..49a42efeb 100644 --- a/frontend/src/views/database/redis/setting/persistence/index.vue +++ b/frontend/src/views/database/redis/setting/persistence/index.vue @@ -28,11 +28,7 @@ - + {{ $t('commons.button.save') }} @@ -52,7 +48,7 @@ - + {{ $t('database.rdbHelper1') }} @@ -76,7 +72,7 @@
{{ $t('database.rdbHelper3') }}
- + {{ $t('commons.button.save') }} @@ -152,11 +148,16 @@ const rules = reactive({ }); const formRef = ref(); +interface DialogProps { + status: string; +} const persistenceShow = ref(false); -const acceptParams = (): void => { +const acceptParams = (prop: DialogProps): void => { persistenceShow.value = true; - loadform(); - loadBackupRecords(); + if (prop.status === 'Running') { + loadform(); + loadBackupRecords(); + } }; const onClose = (): void => { persistenceShow.value = false; diff --git a/frontend/src/views/database/redis/setting/status/index.vue b/frontend/src/views/database/redis/setting/status/index.vue index b2fa1700f..b70f7e60e 100644 --- a/frontend/src/views/database/redis/setting/status/index.vue +++ b/frontend/src/views/database/redis/setting/status/index.vue @@ -100,9 +100,14 @@ const redisStatus = reactive({ const statusShow = ref(false); -const acceptParams = (): void => { +interface DialogProps { + status: string; +} +const acceptParams = (prop: DialogProps): void => { statusShow.value = true; - loadStatus(); + if (prop.status === 'Running') { + loadStatus(); + } }; const onClose = (): void => { statusShow.value = false;