fix: redis 改密并持久化

pull/74/head
ssongliu 2022-12-12 14:06:39 +08:00 committed by ssongliu
parent 7eea08c842
commit 62d0d3fc2c
14 changed files with 200 additions and 79 deletions

View File

@ -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 {

View File

@ -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))
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)

View File

@ -64,6 +64,9 @@ export const loadRedisConf = () => {
export const RedisPersistenceConf = () => {
return http.get<Database.RedisPersistenceConf>(`/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);
};

View File

@ -17,7 +17,7 @@ export const useDeleteData = <P = any, R = any>(
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,

View File

@ -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',

View File

@ -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: '',

View File

@ -4,7 +4,7 @@
<AppStatus :app-key="'mysql'" style="margin-top: 20px" @setting="onSetting" @is-exist="checkExist" />
<Setting ref="settingRef" style="margin-top: 20px" />
<el-card width="30%" v-if="mysqlStatus != 'Running' && !isOnSetting" class="mask-prompt">
<el-card width="30%" v-if="mysqlStatus != 'Running' && !isOnSetting && mysqlIsExist" class="mask-prompt">
<span style="font-size: 14px">{{ $t('database.mysqlBadStatus') }}</span>
<el-button type="primary" link style="font-size: 14px; margin-bottom: 5px" @click="onSetting">
{{ $t('database.setting') }}

View File

@ -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();

View File

@ -29,25 +29,33 @@
<span class="input-help">{{ $t('database.portHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('setting.password')" prop="requirepass">
<el-input type="password" show-password clearable v-model="form.requirepass" />
<el-input type="password" show-password clearable v-model="form.requirepass">
<template #append>
<el-button @click="onSavePassword()" icon="Collection">
{{ $t('commons.button.save') }}
</el-button>
</template>
</el-input>
<span class="input-help">{{ $t('database.requirepassHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('database.timeout')" prop="timeout">
<el-input clearable type="number" v-model.number="form.timeout" />
<span class="input-help">{{ $t('database.timeoutHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('database.maxclients')" prop="maxclients">
<el-input clearable type="number" v-model.number="form.maxclients" />
</el-form-item>
<el-form-item :label="$t('database.maxmemory')" prop="maxmemory">
<el-input clearable type="number" v-model.number="form.maxmemory" />
<span class="input-help">{{ $t('database.maxmemoryHelper') }}</span>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSave(formRef)">
{{ $t('commons.button.save') }}
</el-button>
</el-form-item>
<div v-if="redisStatus === 'Running'">
<el-form-item :label="$t('database.timeout')" prop="timeout">
<el-input clearable type="number" v-model.number="form.timeout" />
<span class="input-help">{{ $t('database.timeoutHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('database.maxclients')" prop="maxclients">
<el-input clearable type="number" v-model.number="form.maxclients" />
</el-form-item>
<el-form-item :label="$t('database.maxmemory')" prop="maxmemory">
<el-input clearable type="number" v-model.number="form.maxmemory" />
<span class="input-help">{{ $t('database.maxmemoryHelper') }}</span>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSave(formRef)">
{{ $t('commons.button.save') }}
</el-button>
</el-form-item>
</div>
</el-col>
</el-row>
</el-form>
@ -63,7 +71,7 @@
theme="cobalt"
:styleActiveLine="true"
:extensions="extensions"
v-model="mysqlConf"
v-model="redisConf"
:readOnly="true"
/>
<el-button
@ -88,6 +96,7 @@
<ConfirmDialog ref="confirmDialogRef" @confirm="onSubmitSave"></ConfirmDialog>
<ConfirmDialog ref="confirmDialogRef2" @confirm="onChangePort(formRef)"></ConfirmDialog>
<ConfirmDialog ref="confirmDialogRef3" @confirm="onChangePassword()"></ConfirmDialog>
</div>
</template>
@ -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<FormInstance>();
const mysqlConf = ref();
const redisConf = ref();
const confirmDialogRef = ref();
const confirmDialogRef2 = ref();
const settingShow = ref<boolean>(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({

View File

@ -28,11 +28,7 @@
</el-radio-group>
</el-form-item>
</el-form>
<el-button
type="primary"
@click="onSave(formRef, 'aof')"
style="bottom: 10px; width: 90px"
>
<el-button type="primary" @click="onSave(formRef, 'aof')" style="bottom: 10px">
{{ $t('commons.button.save') }}
</el-button>
</el-col>
@ -52,7 +48,7 @@
<td width="32%">
<el-input type="number" v-model="row.second"></el-input>
</td>
<td width="55px">
<td width="60px">
<span>{{ $t('database.rdbHelper1') }}</span>
</td>
<td width="32%">
@ -76,7 +72,7 @@
<div>
<span style="margin-left: 2px; margin-top: 5px">{{ $t('database.rdbHelper3') }}</span>
</div>
<el-button type="primary" @click="onSave(undefined, 'rbd')" style="margin-top: 10px; width: 90px">
<el-button type="primary" @click="onSave(undefined, 'rbd')" style="margin-top: 10px">
{{ $t('commons.button.save') }}
</el-button>
</el-card>
@ -152,11 +148,16 @@ const rules = reactive({
});
const formRef = ref<FormInstance>();
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;

View File

@ -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;