feat: 编辑应用增加高级设置 (#1062)

This commit is contained in:
zhengkunwang223 2023-05-17 15:46:29 +08:00 committed by GitHub
parent 80e22ffc82
commit 36db30471c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 174 additions and 44 deletions

View file

@ -55,8 +55,14 @@ type AppInstalledOperate struct {
}
type AppInstalledUpdate struct {
InstallId uint `json:"installId" validate:"required"`
Params map[string]interface{} `json:"params" validate:"required"`
InstallId uint `json:"installId" validate:"required"`
Params map[string]interface{} `json:"params" validate:"required"`
Advanced bool `json:"advanced"`
CpuQuota float64 `json:"cpuQuota"`
MemoryLimit float64 `json:"memoryLimit"`
MemoryUnit string `json:"memoryUnit"`
ContainerName string `json:"containerName"`
AllowPort bool `json:"allowPort"`
}
type PortUpdate struct {

View file

@ -13,8 +13,6 @@ type AppRes struct {
}
type AppUpdateRes struct {
//Version string `json:"version"`
//DownloadPath string `json:"downloadPath"`
CanUpdate bool `json:"canUpdate"`
AppStoreLastModified int `json:"appStoreLastModified"`
List dto.AppList `json:"list"`
@ -84,3 +82,12 @@ type AppParam struct {
Required bool `json:"required"`
Multiple bool `json:"multiple"`
}
type AppConfig struct {
Params []AppParam `json:"params"`
CpuQuota float64 `json:"cpuQuota"`
MemoryLimit float64 `json:"memoryLimit"`
MemoryUnit string `json:"memoryUnit"`
ContainerName string `json:"containerName"`
AllowPort bool `json:"allowPort"`
}

View file

@ -292,7 +292,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
if index > 0 {
continue
}
req.Params["CONTAINER_NAME"] = containerName
req.Params[constant.ContainerName] = containerName
appInstall.ServiceName = serviceName
appInstall.ContainerName = containerName
index++
@ -311,13 +311,13 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
},
},
}
req.Params["CPUS"] = "0"
req.Params[constant.CPUS] = "0"
if req.CpuQuota > 0 {
req.Params["CPUS"] = req.CpuQuota
req.Params[constant.CPUS] = req.CpuQuota
}
req.Params["MEMORY_LIMIT"] = "0"
req.Params[constant.MemoryLimit] = "0"
if req.MemoryLimit > 0 {
req.Params["MEMORY_LIMIT"] = strconv.FormatFloat(req.MemoryLimit, 'f', -1, 32) + req.MemoryUnit
req.Params[constant.MemoryLimit] = strconv.FormatFloat(req.MemoryLimit, 'f', -1, 32) + req.MemoryUnit
}
serviceValue["deploy"] = deploy
}
@ -328,7 +328,7 @@ func (a AppService) Install(ctx context.Context, req request.AppInstallCreate) (
if req.AllowPort {
allowHost = "0.0.0.0"
}
req.Params["HOST_IP"] = allowHost
req.Params[constant.HostIP] = allowHost
for i, port := range ports {
portStr, portOK := port.(string)
if !portOK {

View file

@ -9,6 +9,7 @@ import (
"os"
"path"
"reflect"
"regexp"
"strconv"
"strings"
@ -46,7 +47,7 @@ type IAppInstallService interface {
SyncAll(systemInit bool) error
GetServices(key string) ([]response.AppService, error)
GetUpdateVersions(installId uint) ([]dto.AppVersion, error)
GetParams(id uint) ([]response.AppParam, error)
GetParams(id uint) (*response.AppConfig, error)
ChangeAppPort(req request.PortUpdate) error
GetDefaultConfigByKey(key string) (string, error)
DeleteCheck(installId uint) ([]dto.AppResource, error)
@ -261,6 +262,25 @@ func (a *AppInstallService) Update(req request.AppInstalledUpdate) error {
}
}
if req.Advanced {
if req.ContainerName != "" {
req.Params[constant.ContainerName] = req.ContainerName
}
req.Params[constant.CPUS] = "0"
if req.CpuQuota > 0 {
req.Params[constant.CPUS] = req.CpuQuota
}
req.Params[constant.MemoryLimit] = "0"
if req.MemoryLimit > 0 {
req.Params[constant.MemoryLimit] = strconv.FormatFloat(req.MemoryLimit, 'f', -1, 32) + req.MemoryUnit
}
allowHost := "127.0.0.1"
if req.AllowPort {
allowHost = "0.0.0.0"
}
req.Params[constant.HostIP] = allowHost
}
envPath := path.Join(installed.GetPath(), ".env")
oldEnvMaps, err := godotenv.Read(envPath)
if err != nil {
@ -473,11 +493,12 @@ func (a *AppInstallService) GetDefaultConfigByKey(key string) (string, error) {
return string(contentByte), nil
}
func (a *AppInstallService) GetParams(id uint) ([]response.AppParam, error) {
func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
var (
res []response.AppParam
params []response.AppParam
appForm dto.AppForm
envs = make(map[string]interface{})
res response.AppConfig
)
install, err := appInstallRepo.GetFirst(commonRepo.WithByID(id))
if err != nil {
@ -519,10 +540,35 @@ func (a *AppInstallService) GetParams(id uint) ([]response.AppParam, error) {
}
appParam.Values = form.Values
}
res = append(res, appParam)
params = append(params, appParam)
}
}
return res, nil
res.ContainerName = envs[constant.ContainerName].(string)
res.AllowPort = envs[constant.HostIP].(string) == "0.0.0.0"
numStr, ok := envs[constant.CPUS].(string)
if ok {
num, err := strconv.ParseFloat(numStr, 64)
if err == nil {
res.CpuQuota = num
}
}
num64, ok := envs[constant.CPUS].(float64)
if ok {
res.CpuQuota = num64
}
re := regexp.MustCompile(`(\d+(?:\.\d+)?)\s*([KMGT]?B)`)
matches := re.FindStringSubmatch(envs[constant.MemoryLimit].(string))
if len(matches) == 3 {
num, err := strconv.ParseFloat(matches[1], 64)
if err == nil {
unit := matches[2]
res.MemoryLimit = num
res.MemoryUnit = unit
}
}
res.Params = params
return &res, nil
}
func syncById(installId uint) error {

View file

@ -21,6 +21,11 @@ const (
AppResourceLocal = "local"
AppResourceRemote = "remote"
CPUS = "CPUS"
MemoryLimit = "MEMORY_LIMIT"
HostIP = "HOST_IP"
ContainerName = "CONTAINER_NAME"
)
type AppOperate string

View file

@ -176,4 +176,13 @@ export namespace App {
required?: boolean;
multiple?: boolean;
}
export interface AppConfig {
params: InstallParams[];
cpuQuota: number;
memoryLimit: number;
memoryUnit: string;
containerName: string;
allowPort: boolean;
}
}

View file

@ -83,7 +83,7 @@ export const GetAppDefaultConfig = (key: string) => {
};
export const GetAppInstallParams = (id: number) => {
return http.get<App.InstallParams[]>(`apps/installed/params/${id}`);
return http.get<App.AppConfig>(`apps/installed/params/${id}`);
};
export const UpdateAppInstallParams = (req: any) => {

View file

@ -155,6 +155,7 @@ const message = {
paramUrlAndPort: 'The format is http(s)://(domain name/ip):(port)',
nginxDoc: 'Only supports English case, numbers, and .',
appName: 'Support English, numbers, - and _, length 2-30, and cannot start and end with -_',
conatinerName: 'Supports letters, numbers, underscores, hyphens and dots, cannot end with hyphen- or dot.',
},
res: {
paramError: 'The request failed, please try again later!',
@ -259,12 +260,6 @@ const message = {
free: 'Free',
percent: 'Percent',
app: 'Recommended Apps',
haloInfo: 'Easy to use and powerful open source website tools',
deInfo: 'Open source data visualization tool available to everyone',
jsInfo: 'The popular Open source fortress machine',
msInfo: 'One-stop open source continuous testing platform',
koInfo: 'Open source lightweight Kubernetes distribution',
kubepiInfo: 'Modern open source Kubernetes panel',
goInstall: 'Go install',
networkCard: 'NetworkCard',
@ -1050,6 +1045,15 @@ const message = {
updateWarn: 'Update parameters need to rebuild the application, Do you want to continue? ',
busPort: 'Service Port',
syncStart: 'Start syncing! Please refresh the app store later',
advanced: 'Advanced Settings',
cpuCore: 'Number of cores',
containerName: 'Container Name',
conatinerNameHelper: 'can be empty, it will be automatically generated',
allowPort: 'Port external access',
allowPortHelper:
'Allowing external port access will release the firewall port, please do not release the php operating environment',
appInstallWarn:
'The application does not release the external access port by default, you can choose to release it in the advanced settings',
},
website: {
website: 'Website',

View file

@ -272,12 +272,6 @@ const message = {
free: '可用',
percent: '使用率',
app: '推荐应用',
haloInfo: '好用又强大的开源建站工具',
deInfo: '人人可用的开源数据可视化分析工具',
jsInfo: '广受欢迎的开源堡垒机',
msInfo: '一站式开源持续测试平台',
koInfo: '开源的轻量级 Kubernetes 发行版',
kubepiInfo: '现代化的开源 Kubernetes 面板',
goInstall: '去安装',
networkCard: '网卡',
@ -1059,6 +1053,7 @@ const message = {
conatinerNameHelper: '可以为空为空自动生成',
allowPort: '端口外部访问',
allowPortHelper: '允许外部端口访问会放开防火墙端口php运行环境请勿放开',
appInstallWarn: '应用默认不放开外部访问端口可以在高级设置中选择放开',
},
website: {
website: '网站',

View file

@ -12,6 +12,9 @@
<el-row v-loading="loading">
<el-col :span="22" :offset="1">
<el-alert type="info" :closable="false">
<p>{{ $t('app.appInstallWarn') }}</p>
</el-alert>
<el-form
@submit.prevent
ref="paramForm"

View file

@ -23,10 +23,10 @@
<el-input
v-if="p.type == 'number'"
type="number"
v-model.number="paramModel[p.key]"
v-model.number="paramModel.params[p.key]"
:disabled="!p.edit"
></el-input>
<el-select v-model="paramModel[p.key]" v-else-if="p.type == 'select'">
<el-select v-model="paramModel.params[p.key]" v-else-if="p.type == 'select'">
<el-option
v-for="value in p.values"
:key="value.label"
@ -35,7 +35,45 @@
:disabled="!p.edit"
></el-option>
</el-select>
<el-input v-else v-model.trim="paramModel[p.key]" :disabled="!p.edit"></el-input>
<el-input v-else v-model.trim="paramModel.params[p.key]" :disabled="!p.edit"></el-input>
</el-form-item>
</div>
<el-form-item prop="advanced">
<el-checkbox v-model="paramModel.advanced" :label="$t('app.advanced')" size="large" />
</el-form-item>
<div v-if="paramModel.advanced">
<el-form-item :label="$t('app.containerName')" prop="containerName">
<el-input
v-model.trim="paramModel.containerName"
:placeholder="$t('app.conatinerNameHelper')"
></el-input>
</el-form-item>
<el-form-item :label="$t('container.cpuQuota')" prop="cpuQuota">
<el-input
type="number"
style="width: 40%"
v-model.number="paramModel.cpuQuota"
maxlength="5"
>
<template #append>{{ $t('app.cpuCore') }}</template>
</el-input>
<span class="input-help">{{ $t('container.limitHelper') }}</span>
</el-form-item>
<el-form-item :label="$t('container.memoryLimit')" prop="memoryLimit">
<el-input style="width: 40%" v-model.number="paramModel.memoryLimit" maxlength="10">
<template #append>
<el-select v-model="paramModel.memoryUnit" placeholder="Select" style="width: 85px">
<el-option label="KB" value="K" />
<el-option label="MB" value="M" />
<el-option label="GB" value="G" />
</el-select>
</template>
</el-input>
<span class="input-help">{{ $t('container.limitHelper') }}</span>
</el-form-item>
<el-form-item prop="allowPort">
<el-checkbox v-model="paramModel.allowPort" :label="$t('app.allowPort')" size="large" />
<span class="input-help">{{ $t('app.allowPortHelper') }}</span>
</el-form-item>
</div>
</el-form>
@ -73,15 +111,19 @@ interface EditForm extends App.InstallParams {
default: any;
}
let open = ref(false);
let loading = ref(false);
const open = ref(false);
const loading = ref(false);
const params = ref<EditForm[]>();
let edit = ref(false);
const edit = ref(false);
const paramForm = ref<FormInstance>();
let paramModel = ref<any>({});
let rules = reactive({});
let submitModel = ref<any>({});
let canEdit = ref(false);
const paramModel = ref<any>({
params: {},
});
const rules = reactive({
params: {},
});
const submitModel = ref<any>({});
const canEdit = ref(false);
const acceptParams = async (props: ParamProps) => {
canEdit.value = false;
@ -98,7 +140,7 @@ const handleClose = () => {
};
const editParam = () => {
params.value.forEach((param: EditForm) => {
paramModel.value[param.key] = param.value;
paramModel.value.params[param.key] = param.value;
});
edit.value = !edit.value;
};
@ -107,8 +149,9 @@ const get = async () => {
try {
loading.value = true;
const res = await GetAppInstallParams(Number(paramData.value.id));
if (res.data && res.data.length > 0) {
res.data.forEach((d) => {
const configParams = res.data.params || [];
if (configParams && configParams.length > 0) {
configParams.forEach((d) => {
if (d.edit) {
canEdit.value = true;
}
@ -128,12 +171,18 @@ const get = async () => {
values: d.values,
showValue: d.showValue,
});
rules[d.key] = [Rules.requiredInput];
rules.params[d.key] = [Rules.requiredInput];
if (d.rule) {
rules[d.key].push(Rules[d.rule]);
rules.params[d.key].push(Rules[d.rule]);
}
});
}
paramModel.value.memoryLimit = res.data.memoryLimit;
paramModel.value.cpuQuota = res.data.cpuQuota;
paramModel.value.memoryUnit = res.data.memoryUnit;
paramModel.value.allowPort = res.data.allowPort;
paramModel.value.containerName = res.data.containerName;
paramModel.value.advanced = false;
} catch (error) {
} finally {
loading.value = false;
@ -160,7 +209,13 @@ const submit = async (formEl: FormInstance) => {
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
}).then(async () => {
submitModel.value.params = paramModel.value;
submitModel.value.params = paramModel.value.params;
submitModel.value.advanced = paramModel.value.advanced;
submitModel.value.memoryLimit = paramModel.value.memoryLimit;
submitModel.value.cpuQuota = paramModel.value.cpuQuota;
submitModel.value.memoryUnit = paramModel.value.memoryUnit;
submitModel.value.allowPort = paramModel.value.allowPort;
submitModel.value.containerName = paramModel.value.containerName;
try {
loading.value = true;
await UpdateAppInstallParams(submitModel.value);