feat: 完成数据库端口修改功能

This commit is contained in:
ssongliu 2022-11-08 14:34:41 +08:00 committed by ssongliu
parent 85419b6dd4
commit 8431f49c47
14 changed files with 152 additions and 11 deletions

View File

@ -1,11 +1,13 @@
package v1
import (
"strconv"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/gin-gonic/gin"
"strconv"
)
func (b *BaseApi) SearchApp(c *gin.Context) {
@ -180,3 +182,22 @@ func (b *BaseApi) GetUpdateVersions(c *gin.Context) {
helper.SuccessWithData(c, versions)
}
func (b *BaseApi) ChangeAppPort(c *gin.Context) {
var req dto.PortUpdate
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 := appService.ChangeAppPort(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}

View File

@ -2,6 +2,7 @@ package dto
import (
"encoding/json"
"github.com/1Panel-dev/1Panel/backend/app/model"
)
@ -78,6 +79,12 @@ type AppInstallOperate struct {
Operate AppOperate `json:"operate" validate:"required"`
}
type PortUpdate struct {
Key string `json:"key"`
Name string `json:"name"`
Port int64 `json:"port"`
}
type AppService struct {
Label string `json:"label"`
Value string `json:"value"`

View File

@ -151,6 +151,7 @@ type RedisConfUpdateByFile struct {
type RedisConf struct {
Name string `json:"name"`
Port int64 `json:"port"`
ContainerName string `json:"containerName"`
Timeout string `json:"timeout"`
Maxclients string `json:"maxclients"`

View File

@ -22,7 +22,7 @@ type IMysqlRepo interface {
LoadRunningVersion(keys []string) ([]string, error)
LoadBaseInfoByName(name string) (*RootInfo, error)
LoadRedisBaseInfo() (*RootInfo, error)
UpdateDatabasePassword(id uint, vars map[string]interface{}) error
UpdateDatabaseInfo(id uint, vars map[string]interface{}) error
}
func NewIMysqlRepo() IMysqlRepo {
@ -181,7 +181,7 @@ func (u *MysqlRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.DatabaseMysql{}).Where("id = ?", id).Updates(vars).Error
}
func (u *MysqlRepo) UpdateDatabasePassword(id uint, vars map[string]interface{}) error {
func (u *MysqlRepo) UpdateDatabaseInfo(id uint, vars map[string]interface{}) error {
if err := global.DB.Model(&model.AppInstall{}).Where("id = ?", id).Updates(vars).Error; err != nil {
return err
}

View File

@ -5,6 +5,12 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"strconv"
"strings"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/1Panel-dev/1Panel/backend/app/model"
"github.com/1Panel-dev/1Panel/backend/app/repo"
@ -16,9 +22,6 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/files"
"golang.org/x/net/context"
"gopkg.in/yaml.v3"
"os"
"path"
"strings"
)
type AppService struct {
@ -197,6 +200,58 @@ func (a AppService) OperateInstall(req dto.AppInstallOperate) error {
return appInstallRepo.Save(&install)
}
func (a AppService) ChangeAppPort(req dto.PortUpdate) error {
var (
files []string
newFiles []string
)
app, err := mysqlRepo.LoadBaseInfoByName(req.Name)
if err != nil {
return err
}
ComposeDir := fmt.Sprintf("%s/%s/%s", constant.AppInstallDir, req.Key, req.Name)
ComposeFile := fmt.Sprintf("%s/%s/%s/docker-compose.yml", constant.AppInstallDir, req.Key, req.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_APP_PORT_HTTP=") {
newFiles = append(newFiles, fmt.Sprintf("PANEL_APP_PORT_HTTP=%v", req.Port))
} else {
newFiles = append(newFiles, line)
}
}
file, err := os.OpenFile(path, os.O_WRONLY, 0666)
if err != nil {
return err
}
defer file.Close()
_, err = file.WriteString(strings.Join(newFiles, "\n"))
if err != nil {
return err
}
if err := mysqlRepo.UpdateDatabaseInfo(app.ID, map[string]interface{}{
"env": strings.ReplaceAll(app.Env, strconv.FormatInt(app.Port, 10), strconv.FormatInt(req.Port, 10)),
}); err != nil {
return err
}
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 (a AppService) Install(name string, appDetailId uint, params map[string]interface{}) error {
httpPort, err := checkPort("PANEL_APP_PORT_HTTP", params)

View File

@ -224,7 +224,7 @@ func (u *MysqlService) ChangeInfo(info dto.ChangeDBInfo) error {
}
}
}
_ = mysqlRepo.UpdateDatabasePassword(app.ID, map[string]interface{}{
_ = mysqlRepo.UpdateDatabaseInfo(app.ID, map[string]interface{}{
"param": strings.ReplaceAll(app.Param, app.Password, info.Value),
"env": strings.ReplaceAll(app.Env, app.Password, info.Value),
})

View File

@ -47,7 +47,7 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "maxclients", req.Maxclients); err != nil {
return err
}
if err := mysqlRepo.UpdateDatabasePassword(redisInfo.ID, map[string]interface{}{
if err := mysqlRepo.UpdateDatabaseInfo(redisInfo.ID, map[string]interface{}{
"param": strings.ReplaceAll(redisInfo.Param, redisInfo.Password, req.Requirepass),
"env": strings.ReplaceAll(redisInfo.Env, redisInfo.Password, req.Requirepass),
}); err != nil {
@ -133,6 +133,7 @@ func (u *RedisService) LoadConf() (*dto.RedisConf, error) {
var item dto.RedisConf
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
}

View File

@ -26,6 +26,7 @@ func (a *AppRouter) InitAppRouter(Router *gin.RouterGroup) {
appRouter.POST("/installed/sync", baseApi.SyncInstalled)
appRouter.POST("/installed/backups", baseApi.SearchInstalledBackup)
appRouter.POST("/installed/backups/del", baseApi.DeleteAppBackup)
appRouter.POST("/installed/port/change", baseApi.ChangeAppPort)
appRouter.GET("/services/:key", baseApi.GetServices)
}
}

View File

@ -62,6 +62,12 @@ export namespace App {
params: any;
}
export interface ChangePort {
key: string;
name: string;
port: number;
}
export interface AppInstalled extends CommonModel {
name: string;
appId: string;

View File

@ -150,6 +150,7 @@ export namespace Database {
}
export interface RedisConf {
name: string;
port: number;
timeout: number;
maxclients: number;
requirepass: string;

View File

@ -22,6 +22,10 @@ export const InstallApp = (install: App.AppInstall) => {
return http.post<any>('apps/install', install);
};
export const ChangePort = (params: App.ChangePort) => {
return http.post<any>('apps/installed/port/change', params);
};
export const GetAppInstalled = (info: ReqPage) => {
return http.post<ResPage<App.AppInstalled>>('apps/installed', info);
};

View File

@ -171,6 +171,7 @@ export default {
baseSetting: '基础设置',
remoteConnHelper: 'root 帐号远程连接 mysql 有安全风险开启需谨慎',
confChange: '配置修改',
portHelper: '该端口为容器对外暴露端口修改需要单独保存并且重启容器',
currentStatus: '当前状态',
runTime: '启动时间',

View File

@ -104,6 +104,7 @@ import {
updateMysqlConfByFile,
updateMysqlDBInfo,
} from '@/api/modules/database';
import { ChangePort } from '@/api/modules/app';
import { Rules } from '@/global/form-rules';
import i18n from '@/lang';
@ -152,6 +153,16 @@ const onSave = async (formEl: FormInstance | undefined, key: string, val: any) =
if (!result) {
return;
}
if (key === 'port') {
let params = {
key: baseInfo.mysqlKey,
name: mysqlName.value,
port: val,
};
await ChangePort(params);
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
return;
}
let changeForm = {
id: 0,
mysqlName: mysqlName.value,

View File

@ -9,8 +9,15 @@
<el-row style="margin-top: 20px">
<el-col :span="1"><br /></el-col>
<el-col :span="10">
<el-form-item :label="$t('setting.port')" prop="port">
<el-input clearable type="number" v-model.number="form.port" />
<el-form-item :label="$t('setting.port')" prop="port" :rules="Rules.port">
<el-input clearable type="number" v-model.number="form.port">
<template #append>
<el-button @click="onChangePort(formRef)" icon="Collection">
{{ $t('commons.button.save') }}
</el-button>
</template>
</el-input>
<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" />
@ -71,6 +78,7 @@ import ConfirmDialog from '@/components/confirm-dialog/index.vue';
import { loadRedisConf, updateRedisConf, updateRedisConfByFile } from '@/api/modules/database';
import i18n from '@/lang';
import { Rules } from '@/global/form-rules';
import { ChangePort } from '@/api/modules/app';
const extensions = [javascript(), oneDark];
const confShowType = ref('base');
@ -78,7 +86,7 @@ const confShowType = ref('base');
const restartNow = ref(false);
const form = reactive({
name: '',
port: 3306,
port: 6379,
requirepass: '',
timeout: 0,
maxclients: 0,
@ -105,6 +113,29 @@ const onClose = (): void => {
settingShow.value = false;
};
const onChangePort = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
const result = await formEl.validateField('port', callback);
if (!result) {
return;
}
let params = {
key: 'redis',
name: form.name,
port: form.port,
};
await ChangePort(params);
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
return;
};
function callback(error: any) {
if (error) {
return error.message;
} else {
return;
}
}
const onSave = async (formEl: FormInstance | undefined) => {
if (confShowType.value === 'all') {
onSaveFile();
@ -149,6 +180,7 @@ const loadform = async () => {
form.maxclients = Number(res.data?.maxclients);
form.requirepass = res.data?.requirepass;
form.maxmemory = Number(res.data?.maxmemory);
form.port = Number(res.data?.port);
loadMysqlConf(`/opt/1Panel/data/apps/redis/${form.name}/conf/redis.conf`);
};