mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-18 03:16:30 +08:00
feat: 编辑应用增加高级设置 (#1062)
This commit is contained in:
parent
80e22ffc82
commit
36db30471c
11 changed files with 174 additions and 44 deletions
|
@ -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 {
|
||||
|
|
|
@ -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"`
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -21,6 +21,11 @@ const (
|
|||
|
||||
AppResourceLocal = "local"
|
||||
AppResourceRemote = "remote"
|
||||
|
||||
CPUS = "CPUS"
|
||||
MemoryLimit = "MEMORY_LIMIT"
|
||||
HostIP = "HOST_IP"
|
||||
ContainerName = "CONTAINER_NAME"
|
||||
)
|
||||
|
||||
type AppOperate string
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) => {
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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: '网站',
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Reference in a new issue