mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-11-22 00:38:34 +08:00
feat: Configure restart policy for application installation support (#9953)
Refs https://github.com/1Panel-dev/1Panel/issues/9895
This commit is contained in:
parent
b735178fcd
commit
f8f47b51e9
12 changed files with 550 additions and 466 deletions
|
|
@ -40,6 +40,7 @@ type AppContainerConfig struct {
|
|||
WebUI string `json:"webUI"`
|
||||
Type string `json:"type"`
|
||||
SpecifyIP string `json:"specifyIP"`
|
||||
RestartPolicy string `json:"restartPolicy" validate:"omitempty,oneof=always unless-stopped no on-failure"`
|
||||
}
|
||||
|
||||
type AppInstalledSearch struct {
|
||||
|
|
|
|||
|
|
@ -788,6 +788,7 @@ func (a *AppInstallService) GetParams(id uint) (*response.AppConfig, error) {
|
|||
}
|
||||
res.AppContainerConfig = config
|
||||
res.HostMode = isHostModel(install.DockerCompose)
|
||||
res.RestartPolicy = getRestartPolicy(install.DockerCompose)
|
||||
res.WebUI = install.WebUI
|
||||
res.Type = install.App.Type
|
||||
return &res, nil
|
||||
|
|
|
|||
|
|
@ -357,7 +357,7 @@ func deleteAppInstall(deleteReq request.AppInstallDelete) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
images, err := composeV2.GetDockerComposeImagesV2(content, []byte(install.DockerCompose))
|
||||
images, err := composeV2.GetImagesFromDockerCompose(content, []byte(install.DockerCompose))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -540,6 +540,9 @@ func handleUpgradeCompose(install model.AppInstall, detail model.AppDetail) (map
|
|||
if oldServiceValue["deploy"] != nil {
|
||||
serviceValue["deploy"] = oldServiceValue["deploy"]
|
||||
}
|
||||
if oldServiceValue["restart"] != nil {
|
||||
serviceValue["restart"] = oldServiceValue["restart"]
|
||||
}
|
||||
servicesMap[install.ServiceName] = serviceValue
|
||||
composeMap["services"] = servicesMap
|
||||
return composeMap, nil
|
||||
|
|
@ -659,7 +662,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error {
|
|||
if req.DockerCompose != "" {
|
||||
composeContent = []byte(req.DockerCompose)
|
||||
}
|
||||
images, err := composeV2.GetDockerComposeImagesV2(content, composeContent)
|
||||
images, err := composeV2.GetImagesFromDockerCompose(content, composeContent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -1658,6 +1661,14 @@ func addDockerComposeCommonParam(composeMap map[string]interface{}, serviceName
|
|||
deploy["resources"] = resource
|
||||
serviceValue["deploy"] = deploy
|
||||
|
||||
if req.RestartPolicy != "" {
|
||||
if req.RestartPolicy == "on-failure" {
|
||||
serviceValue["restart"] = "on-failure:5"
|
||||
} else {
|
||||
serviceValue["restart"] = req.RestartPolicy
|
||||
}
|
||||
}
|
||||
|
||||
ports, ok := serviceValue["ports"].([]interface{})
|
||||
if ok {
|
||||
for i, port := range ports {
|
||||
|
|
@ -1760,6 +1771,17 @@ func isHostModel(dockerCompose string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func getRestartPolicy(yml string) string {
|
||||
var project docker.ComposeProject
|
||||
if err := yaml.Unmarshal([]byte(yml), &project); err != nil {
|
||||
return ""
|
||||
}
|
||||
for _, service := range project.Services {
|
||||
return service.Restart
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getMajorVersion(version string) string {
|
||||
parts := strings.Split(version, ".")
|
||||
if len(parts) >= 2 {
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ type Service struct {
|
|||
Image string `yaml:"image"`
|
||||
Environment Environment `yaml:"environment"`
|
||||
Volumes []string `json:"volumes"`
|
||||
Restart string `json:"restart"`
|
||||
}
|
||||
|
||||
type Environment struct {
|
||||
|
|
@ -92,69 +93,6 @@ func (e *Environment) UnmarshalYAML(value *yaml.Node) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func replaceEnvVariables(input string, envVars map[string]string) string {
|
||||
for key, value := range envVars {
|
||||
placeholder := fmt.Sprintf("${%s}", key)
|
||||
input = strings.ReplaceAll(input, placeholder, value)
|
||||
}
|
||||
return input
|
||||
}
|
||||
func GetDockerComposeImagesV2(env, yml []byte) ([]string, error) {
|
||||
var (
|
||||
compose ComposeProject
|
||||
err error
|
||||
images []string
|
||||
)
|
||||
err = yaml.Unmarshal(yml, &compose)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
envMap, err := godotenv.UnmarshalBytes(env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, service := range compose.Services {
|
||||
image := replaceEnvVariables(service.Image, envMap)
|
||||
images = append(images, image)
|
||||
}
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func GetDockerComposeImages(projectName string, env, yml []byte) ([]string, error) {
|
||||
var (
|
||||
configFiles []types.ConfigFile
|
||||
images []string
|
||||
imagesMap = make(map[string]struct{})
|
||||
)
|
||||
configFiles = append(configFiles, types.ConfigFile{
|
||||
Filename: "docker-compose.yml",
|
||||
Content: yml},
|
||||
)
|
||||
envMap, err := godotenv.UnmarshalBytes(env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
details := types.ConfigDetails{
|
||||
ConfigFiles: configFiles,
|
||||
Environment: envMap,
|
||||
}
|
||||
|
||||
project, err := loader.LoadWithContext(context.Background(), details, func(options *loader.Options) {
|
||||
options.SetProjectName(projectName, true)
|
||||
options.ResolvePaths = true
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, service := range project.AllServices() {
|
||||
imagesMap[service.Image] = struct{}{}
|
||||
}
|
||||
for image := range imagesMap {
|
||||
images = append(images, image)
|
||||
}
|
||||
return images, nil
|
||||
}
|
||||
|
||||
func GetImagesFromDockerCompose(env, yml []byte) ([]string, error) {
|
||||
envVars, err := loadEnvFile(env)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -276,6 +276,7 @@ export namespace App {
|
|||
type: string;
|
||||
webUI: string;
|
||||
specifyIP: string;
|
||||
restartPolicy: string;
|
||||
}
|
||||
|
||||
export interface IgnoredApp {
|
||||
|
|
|
|||
|
|
@ -15,12 +15,9 @@ export const searchApp = (req: App.AppReq) => {
|
|||
return http.post<App.AppResPage>('apps/search', req);
|
||||
};
|
||||
|
||||
export const getAppByKey = (key: string) => {
|
||||
return http.get<App.AppDTO>('apps/' + key);
|
||||
};
|
||||
|
||||
export const getAppByKeyWithNode = (key: string, node: string) => {
|
||||
return http.get<App.AppDTO>('apps/' + key + `?operateNode=${node}`);
|
||||
export const getAppByKey = (key: string, node?: string) => {
|
||||
const params = node ? `?operateNode=${node}` : '';
|
||||
return http.get<App.AppDTO>('apps/' + key + `${params}`);
|
||||
};
|
||||
|
||||
export const getAppTags = () => {
|
||||
|
|
|
|||
400
frontend/src/views/app-store/detail/form/index.vue
Normal file
400
frontend/src/views/app-store/detail/form/index.vue
Normal file
|
|
@ -0,0 +1,400 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
:title="$t('app.hostModeHelper')"
|
||||
class="common-prompt"
|
||||
:closable="false"
|
||||
type="warning"
|
||||
v-if="isHostMode"
|
||||
/>
|
||||
<el-alert
|
||||
:title="$t('app.memoryRequiredHelper', [computeSizeFromMB(memoryRequired)])"
|
||||
class="common-prompt"
|
||||
:closable="false"
|
||||
type="warning"
|
||||
v-if="memoryRequired > 0"
|
||||
/>
|
||||
|
||||
<el-form
|
||||
v-loading="loading"
|
||||
@submit.prevent
|
||||
ref="formRef"
|
||||
label-position="top"
|
||||
:model="formData"
|
||||
label-width="150px"
|
||||
:rules="formRules"
|
||||
:validate-on-rule-change="false"
|
||||
>
|
||||
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||
<el-input v-model.trim="formData.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('app.version')" prop="version">
|
||||
<el-select v-model="formData.version" @change="handleVersionChange">
|
||||
<el-option
|
||||
v-for="(version, index) in appVersions"
|
||||
:key="index"
|
||||
:label="version"
|
||||
:value="version"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<Params
|
||||
:key="paramKey"
|
||||
v-if="showParams"
|
||||
v-model:form="formData.params"
|
||||
v-model:params="installParams"
|
||||
v-model:rules="formRules.params"
|
||||
:propStart="'params.'"
|
||||
/>
|
||||
|
||||
<el-form-item prop="advanced">
|
||||
<el-checkbox v-model="formData.advanced" :label="$t('app.advanced')" size="large" />
|
||||
</el-form-item>
|
||||
|
||||
<div v-if="formData.advanced">
|
||||
<el-form-item :label="$t('app.containerName')" prop="containerName">
|
||||
<el-input v-model.trim="formData.containerName" :placeholder="$t('app.containerNameHelper')" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="allowPort" v-if="!isHostMode">
|
||||
<el-checkbox v-model="formData.allowPort" :label="$t('app.allowPort')" size="large" />
|
||||
<span class="input-help">{{ $t('app.allowPortHelper') }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('app.specifyIP')" v-if="formData.allowPort" prop="specifyIP">
|
||||
<el-input v-model="formData.specifyIP" />
|
||||
<span class="input-help">{{ $t('app.specifyIPHelper') }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item :label="$t('container.restartPolicy')" prop="restartPolicy">
|
||||
<el-select v-model="formData.restartPolicy" class="p-w-300">
|
||||
<el-option :label="$t('container.no')" value="no"></el-option>
|
||||
<el-option :label="$t('container.always')" value="always"></el-option>
|
||||
<el-option :label="$t('container.onFailure')" value="on-failure"></el-option>
|
||||
<el-option :label="$t('container.unlessStopped')" value="unless-stopped"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:label="$t('container.cpuQuota')"
|
||||
prop="cpuQuota"
|
||||
:rules="checkNumberRange(0, limits.cpu)"
|
||||
>
|
||||
<el-input type="number" class="!w-2/5" v-model.number="formData.cpuQuota" maxlength="5">
|
||||
<template #append>{{ $t('app.cpuCore') }}</template>
|
||||
</el-input>
|
||||
<span class="input-help">
|
||||
{{ $t('container.limitHelper', [limits.cpu]) }}{{ $t('commons.units.core') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:label="$t('container.memoryLimit')"
|
||||
prop="memoryLimit"
|
||||
:rules="checkNumberRange(0, limits.memory)"
|
||||
>
|
||||
<el-input class="!w-2/5" v-model.number="formData.memoryLimit" maxlength="10">
|
||||
<template #append>
|
||||
<el-select
|
||||
v-model="formData.memoryUnit"
|
||||
placeholder="Select"
|
||||
class="p-w-100"
|
||||
@change="changeUnit"
|
||||
>
|
||||
<el-option label="MB" value="M" />
|
||||
<el-option label="GB" value="G" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
<span class="input-help">
|
||||
{{ $t('container.limitHelper', [limits.memory]) }}{{ formData.memoryUnit }}B
|
||||
</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item pro="gpuConfig" v-if="gpuSupport">
|
||||
<el-checkbox v-model="formData.gpuConfig" :label="$t('app.gpuConfig')" size="large" />
|
||||
<span class="input-help">{{ $t('app.gpuConfigHelper') }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item pro="pullImage">
|
||||
<el-checkbox v-model="formData.pullImage" :label="$t('app.pullImage')" size="large" />
|
||||
<span class="input-help">{{ $t('app.pullImageHelper') }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="editCompose">
|
||||
<el-checkbox v-model="formData.editCompose" :label="$t('app.editCompose')" size="large" />
|
||||
<span class="input-help">{{ $t('app.editComposeHelper') }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<div v-if="formData.editCompose">
|
||||
<CodemirrorPro v-model="formData.dockerCompose" mode="yaml" />
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="AppInstallForm">
|
||||
import { App } from '@/api/interface/app';
|
||||
import { getAppByKey, getAppDetail, getAppInstalledByID } from '@/api/modules/app';
|
||||
import { Rules, checkNumberRange } from '@/global/form-rules';
|
||||
import { FormInstance, FormRules } from 'element-plus';
|
||||
import { reactive, ref, watch } from 'vue';
|
||||
import Params from '../params/index.vue';
|
||||
import { Container } from '@/api/interface/container';
|
||||
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
|
||||
import { computeSizeFromMB } from '@/utils/util';
|
||||
import { loadResourceLimit } from '@/api/modules/container';
|
||||
|
||||
interface ClusterProps {
|
||||
key: string;
|
||||
node: string;
|
||||
masterNode: string;
|
||||
appInstallID: number;
|
||||
masterVersion: string;
|
||||
masterNodeAddr: string;
|
||||
role: string;
|
||||
}
|
||||
interface Props {
|
||||
loading?: boolean;
|
||||
modelValue?: any;
|
||||
}
|
||||
|
||||
const limits = ref<Container.ResourceLimit>({
|
||||
cpu: null as number,
|
||||
memory: null as number,
|
||||
});
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
loading: false,
|
||||
});
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:modelValue', value: any): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const formRef = ref<FormInstance>();
|
||||
const paramKey = ref(1);
|
||||
const isHostMode = ref(false);
|
||||
const memoryRequired = ref(0);
|
||||
const gpuSupport = ref(false);
|
||||
const installParams = ref<App.AppParams>();
|
||||
const oldMemory = ref<number>(0);
|
||||
const showParams = ref(false);
|
||||
const currentApp = ref<any>({});
|
||||
const appVersions = ref<string[]>([]);
|
||||
const operateNode = ref();
|
||||
const env = ref();
|
||||
const masterNodeAddr = ref();
|
||||
|
||||
const formRules = ref<FormRules>({
|
||||
name: [Rules.appName],
|
||||
params: [],
|
||||
version: [Rules.requiredSelect],
|
||||
containerName: [Rules.containerName],
|
||||
cpuQuota: [Rules.requiredInput, checkNumberRange(0, 99999)],
|
||||
memoryLimit: [Rules.requiredInput, checkNumberRange(0, 9999999999)],
|
||||
specifyIP: [Rules.ipv4orV6],
|
||||
restartPolicy: [Rules.requiredSelect],
|
||||
});
|
||||
|
||||
const initFormData = () => ({
|
||||
appDetailId: 0,
|
||||
params: {},
|
||||
name: '',
|
||||
advanced: true,
|
||||
cpuQuota: 0,
|
||||
memoryLimit: 0,
|
||||
memoryUnit: 'M',
|
||||
containerName: '',
|
||||
allowPort: false,
|
||||
editCompose: false,
|
||||
dockerCompose: '',
|
||||
version: '',
|
||||
appID: '',
|
||||
pullImage: true,
|
||||
taskID: '',
|
||||
gpuConfig: false,
|
||||
specifyIP: '',
|
||||
restartPolicy: 'always',
|
||||
});
|
||||
|
||||
const formData = reactive(props.modelValue || initFormData());
|
||||
|
||||
watch(
|
||||
formData,
|
||||
(newVal) => {
|
||||
emit('update:modelValue', newVal);
|
||||
},
|
||||
{ deep: true },
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
Object.assign(formData, newVal);
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true },
|
||||
);
|
||||
|
||||
const changeUnit = () => {
|
||||
if (formData.memoryUnit == 'M') {
|
||||
limits.value.memory = oldMemory.value;
|
||||
} else {
|
||||
limits.value.memory = Number((oldMemory.value / 1024).toFixed(2));
|
||||
}
|
||||
};
|
||||
|
||||
const handleVersionChange = async (version: string) => {
|
||||
await getVersionDetail(version);
|
||||
};
|
||||
|
||||
const getVersionDetail = async (version: string) => {
|
||||
try {
|
||||
const res = await getAppDetail(currentApp.value.id, version, 'app', operateNode.value);
|
||||
formData.appDetailId = res.data.id;
|
||||
formData.dockerCompose = res.data.dockerCompose;
|
||||
isHostMode.value = res.data.hostMode;
|
||||
if (env.value) {
|
||||
installParams.value = addMasterParams(res.data.params);
|
||||
} else {
|
||||
installParams.value = res.data.params;
|
||||
}
|
||||
paramKey.value++;
|
||||
memoryRequired.value = res.data.memoryRequired;
|
||||
gpuSupport.value = res.data.gpuSupport;
|
||||
showParams.value = true;
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const initForm = async (appKey: string) => {
|
||||
formData.name = appKey;
|
||||
const res = await getAppByKey(appKey);
|
||||
currentApp.value = res.data;
|
||||
appVersions.value = currentApp.value.versions;
|
||||
if (appVersions.value.length > 0) {
|
||||
const defaultVersion = appVersions.value[0];
|
||||
formData.version = defaultVersion;
|
||||
getVersionDetail(defaultVersion);
|
||||
}
|
||||
};
|
||||
|
||||
const getMasterAppInstall = async (appInstallID: number, masterNode: string) => {
|
||||
try {
|
||||
const res = await getAppInstalledByID(appInstallID, masterNode);
|
||||
env.value = res.data.env;
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
const addMasterParams = (appParams: App.AppParams) => {
|
||||
for (const key in appParams.formFields) {
|
||||
const field = appParams.formFields[key];
|
||||
if (field?.envKey === 'MASTER_ROOT_PASSWORD') {
|
||||
field.default = env.value['PANEL_DB_ROOT_PASSWORD']
|
||||
? env.value['PANEL_DB_ROOT_PASSWORD']
|
||||
: env.value['PANEL_REDIS_ROOT_PASSWORD'];
|
||||
}
|
||||
if (field?.envKey === 'PANEL_DB_ROOT_PASSWORD') {
|
||||
field.default = env.value['PANEL_DB_ROOT_PASSWORD'];
|
||||
}
|
||||
if (field?.envKey === 'REPLICATION_USER') {
|
||||
field.default = env.value['REPLICATION_USER'];
|
||||
}
|
||||
if (field?.envKey === 'REPLICATION_PASSWORD') {
|
||||
field.default = env.value['REPLICATION_PASSWORD'];
|
||||
}
|
||||
if (field?.envKey === 'MASTER_PORT') {
|
||||
field.default = env.value['PANEL_APP_PORT_HTTP'];
|
||||
}
|
||||
if (field?.envKey === 'MASTER_HOST' && masterNodeAddr.value != '127.0.0.1') {
|
||||
field.default = masterNodeAddr.value;
|
||||
}
|
||||
}
|
||||
return appParams;
|
||||
};
|
||||
|
||||
const initClusterForm = async (props: ClusterProps) => {
|
||||
if (props.appInstallID && props.masterNode) {
|
||||
getMasterAppInstall(props.appInstallID, props.masterNode);
|
||||
}
|
||||
masterNodeAddr.value = props.masterNodeAddr;
|
||||
operateNode.value = props.node;
|
||||
const res = await getAppByKey(props.key, props.node);
|
||||
currentApp.value = res.data;
|
||||
appVersions.value = currentApp.value.versions;
|
||||
if (appVersions.value.length > 0) {
|
||||
appVersions.value = appVersions.value.filter((v: string) => {
|
||||
return v.includes(props.role) && v.includes(props.masterVersion);
|
||||
});
|
||||
const defaultVersion = appVersions.value[0];
|
||||
formData.version = defaultVersion;
|
||||
getVersionDetail(defaultVersion);
|
||||
}
|
||||
formData.name = props.key + '-' + props.role;
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
if (formRef.value) {
|
||||
formRef.value.clearValidate();
|
||||
formRef.value.resetFields();
|
||||
}
|
||||
Object.assign(formData, initFormData());
|
||||
isHostMode.value = false;
|
||||
memoryRequired.value = 0;
|
||||
gpuSupport.value = false;
|
||||
showParams.value = false;
|
||||
};
|
||||
|
||||
const validate = async (): Promise<boolean> => {
|
||||
if (!formRef.value) return false;
|
||||
try {
|
||||
const isValid = await formRef.value.validate();
|
||||
return isValid;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const clearValidate = () => {
|
||||
if (formRef.value) {
|
||||
formRef.value.clearValidate();
|
||||
}
|
||||
};
|
||||
|
||||
const getFormData = () => {
|
||||
return { ...formData };
|
||||
};
|
||||
|
||||
const setFormData = (data: any) => {
|
||||
Object.assign(formData, data);
|
||||
};
|
||||
|
||||
const loadLimit = async () => {
|
||||
const res = await loadResourceLimit();
|
||||
limits.value = res.data;
|
||||
limits.value.memory = Number((limits.value.memory / 1024 / 1024).toFixed(2));
|
||||
oldMemory.value = limits.value.memory;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadLimit();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
formRef,
|
||||
formData,
|
||||
initForm,
|
||||
resetForm,
|
||||
validate,
|
||||
clearValidate,
|
||||
getFormData,
|
||||
setFormData,
|
||||
isHostMode: () => isHostMode.value,
|
||||
initClusterForm,
|
||||
});
|
||||
</script>
|
||||
|
|
@ -1,120 +1,12 @@
|
|||
<template>
|
||||
<DrawerPro v-model="open" :header="$t('commons.button.install')" @close="handleClose" size="large">
|
||||
<el-alert
|
||||
:title="$t('app.hostModeHelper')"
|
||||
class="common-prompt"
|
||||
:closable="false"
|
||||
type="warning"
|
||||
v-if="isHostMode"
|
||||
/>
|
||||
<el-alert
|
||||
:title="$t('app.memoryRequiredHelper', [computeSizeFromMB(memoryRequired)])"
|
||||
class="common-prompt"
|
||||
:closable="false"
|
||||
type="warning"
|
||||
v-if="memoryRequired > 0"
|
||||
/>
|
||||
<el-form
|
||||
v-loading="loading"
|
||||
@submit.prevent
|
||||
ref="paramForm"
|
||||
label-position="top"
|
||||
:model="req"
|
||||
label-width="150px"
|
||||
:rules="rules"
|
||||
:validate-on-rule-change="false"
|
||||
>
|
||||
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||
<el-input v-model.trim="req.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('app.version')" prop="version">
|
||||
<el-select v-model="req.version" @change="getDetail(req.version)">
|
||||
<el-option
|
||||
v-for="(version, index) in appVersions"
|
||||
:key="index"
|
||||
:label="version"
|
||||
:value="version"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<Params
|
||||
:key="paramKey"
|
||||
v-if="open"
|
||||
v-model:form="req.params"
|
||||
v-model:params="installData.params"
|
||||
v-model:rules="rules.params"
|
||||
:propStart="'params.'"
|
||||
></Params>
|
||||
<el-form-item prop="advanced">
|
||||
<el-checkbox v-model="req.advanced" :label="$t('app.advanced')" size="large" />
|
||||
</el-form-item>
|
||||
<div v-if="req.advanced">
|
||||
<el-form-item :label="$t('app.containerName')" prop="containerName">
|
||||
<el-input v-model.trim="req.containerName" :placeholder="$t('app.containerNameHelper')"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="allowPort" v-if="!isHostMode">
|
||||
<el-checkbox v-model="req.allowPort" :label="$t('app.allowPort')" size="large" />
|
||||
<span class="input-help">{{ $t('app.allowPortHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('app.specifyIP')" v-if="req.allowPort" prop="specifyIP">
|
||||
<el-input v-model="req.specifyIP"></el-input>
|
||||
<span class="input-help">{{ $t('app.specifyIPHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('container.cpuQuota')"
|
||||
prop="cpuQuota"
|
||||
:rules="checkNumberRange(0, limits.cpu)"
|
||||
>
|
||||
<el-input type="number" style="width: 40%" v-model.number="req.cpuQuota" maxlength="5">
|
||||
<template #append>{{ $t('app.cpuCore') }}</template>
|
||||
</el-input>
|
||||
<span class="input-help">
|
||||
{{ $t('container.limitHelper', [limits.cpu]) }}{{ $t('commons.units.core') }}
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('container.memoryLimit')"
|
||||
prop="memoryLimit"
|
||||
:rules="checkNumberRange(0, limits.memory)"
|
||||
>
|
||||
<el-input style="width: 40%" v-model.number="req.memoryLimit" maxlength="10">
|
||||
<template #append>
|
||||
<el-select
|
||||
v-model="req.memoryUnit"
|
||||
placeholder="Select"
|
||||
style="width: 85px"
|
||||
@change="changeUnit"
|
||||
>
|
||||
<el-option label="MB" value="M" />
|
||||
<el-option label="GB" value="G" />
|
||||
</el-select>
|
||||
</template>
|
||||
</el-input>
|
||||
<span class="input-help">
|
||||
{{ $t('container.limitHelper', [limits.memory]) }}{{ req.memoryUnit }}B
|
||||
</span>
|
||||
</el-form-item>
|
||||
<el-form-item pro="gpuConfig" v-if="gpuSupport">
|
||||
<el-checkbox v-model="req.gpuConfig" :label="$t('app.gpuConfig')" size="large" />
|
||||
<span class="input-help">{{ $t('app.gpuConfigHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item pro="pullImage">
|
||||
<el-checkbox v-model="req.pullImage" :label="$t('app.pullImage')" size="large" />
|
||||
<span class="input-help">{{ $t('app.pullImageHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item prop="editCompose">
|
||||
<el-checkbox v-model="req.editCompose" :label="$t('app.editCompose')" size="large" />
|
||||
<span class="input-help">{{ $t('app.editComposeHelper') }}</span>
|
||||
</el-form-item>
|
||||
<div v-if="req.editCompose">
|
||||
<CodemirrorPro v-model="req.dockerCompose" mode="yaml"></CodemirrorPro>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
<AppInstallForm ref="installFormRef" v-model="formData" :loading="loading" />
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose" :disabled="loading">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button type="primary" @click="submit(paramForm)" :disabled="loading">
|
||||
<el-button @click="handleClose" :disabled="loading">
|
||||
{{ $t('commons.button.cancel') }}
|
||||
</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :disabled="loading">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
|
|
@ -123,49 +15,26 @@
|
|||
<TaskLog ref="taskLogRef" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="appInstall">
|
||||
import { App } from '@/api/interface/app';
|
||||
import { getAppByKey, getAppDetail, installApp } from '@/api/modules/app';
|
||||
import { Rules, checkNumberRange } from '@/global/form-rules';
|
||||
import { FormInstance, FormRules } from 'element-plus';
|
||||
import { onMounted, reactive, ref } from 'vue';
|
||||
<script lang="ts" setup name="AppInstallPage">
|
||||
import { ref, reactive } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import Params from '../params/index.vue';
|
||||
import i18n from '@/lang';
|
||||
import AppInstallForm from '@/views/app-store/detail/form/index.vue';
|
||||
import { installApp } from '@/api/modules/app';
|
||||
import { MsgError } from '@/utils/message';
|
||||
import { Container } from '@/api/interface/container';
|
||||
import { loadResourceLimit } from '@/api/modules/container';
|
||||
import CodemirrorPro from '@/components/codemirror-pro/index.vue';
|
||||
import TaskLog from '@/components/log/task/index.vue';
|
||||
import { newUUID } from '@/utils/util';
|
||||
import { computeSizeFromMB } from '@/utils/util';
|
||||
import { routerToName } from '@/utils/router';
|
||||
|
||||
interface InstallRrops {
|
||||
params?: App.AppParams;
|
||||
app: any;
|
||||
}
|
||||
const installData = ref<InstallRrops>({
|
||||
app: {},
|
||||
});
|
||||
import TaskLog from '@/components/log/task/index.vue';
|
||||
import i18n from '@/lang';
|
||||
|
||||
const router = useRouter();
|
||||
const open = ref(false);
|
||||
const rules = ref<FormRules>({
|
||||
name: [Rules.appName],
|
||||
params: [],
|
||||
version: [Rules.requiredSelect],
|
||||
containerName: [Rules.containerName],
|
||||
cpuQuota: [Rules.requiredInput, checkNumberRange(0, 99999)],
|
||||
memoryLimit: [Rules.requiredInput, checkNumberRange(0, 9999999999)],
|
||||
specifyIP: [Rules.ipv4orV6],
|
||||
});
|
||||
const loading = ref(false);
|
||||
const paramForm = ref<FormInstance>();
|
||||
const form = ref<{ [key: string]: any }>({});
|
||||
const initData = () => ({
|
||||
const installFormRef = ref<InstanceType<typeof AppInstallForm>>();
|
||||
const taskLogRef = ref();
|
||||
|
||||
const formData = reactive({
|
||||
appDetailId: 0,
|
||||
params: form.value,
|
||||
params: {},
|
||||
name: '',
|
||||
advanced: true,
|
||||
cpuQuota: 0,
|
||||
|
|
@ -182,139 +51,73 @@ const initData = () => ({
|
|||
gpuConfig: false,
|
||||
specifyIP: '',
|
||||
});
|
||||
const req = reactive(initData());
|
||||
const limits = ref<Container.ResourceLimit>({
|
||||
cpu: null as number,
|
||||
memory: null as number,
|
||||
});
|
||||
const oldMemory = ref<number>(0);
|
||||
const appVersions = ref<string[]>([]);
|
||||
|
||||
const handleClose = () => {
|
||||
open.value = false;
|
||||
resetForm();
|
||||
installFormRef.value?.resetForm();
|
||||
if (router.currentRoute.value.query.install) {
|
||||
routerToName('AppAll');
|
||||
}
|
||||
};
|
||||
const paramKey = ref(1);
|
||||
const isHostMode = ref(false);
|
||||
const taskLogRef = ref();
|
||||
const memoryRequired = ref(0);
|
||||
const gpuSupport = ref(false);
|
||||
|
||||
const changeUnit = () => {
|
||||
if (req.memoryUnit == 'M') {
|
||||
limits.value.memory = oldMemory.value;
|
||||
const handleSubmit = async () => {
|
||||
const isValid = await installFormRef.value?.validate();
|
||||
if (!isValid) return;
|
||||
|
||||
const submitData = installFormRef.value?.getFormData();
|
||||
|
||||
if (submitData.editCompose && submitData.dockerCompose === '') {
|
||||
MsgError(i18n.global.t('app.composeNullErr'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (submitData.cpuQuota < 0) {
|
||||
submitData.cpuQuota = 0;
|
||||
}
|
||||
if (submitData.memoryLimit < 0) {
|
||||
submitData.memoryLimit = 0;
|
||||
}
|
||||
|
||||
const isHostMode = installFormRef.value?.isHostMode();
|
||||
if (!isHostMode && !submitData.allowPort) {
|
||||
ElMessageBox.confirm(i18n.global.t('app.installWarn'), i18n.global.t('app.checkTitle'), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
}).then(async () => {
|
||||
await install(submitData);
|
||||
});
|
||||
} else {
|
||||
limits.value.memory = Number((oldMemory.value / 1024).toFixed(2));
|
||||
await install(submitData);
|
||||
}
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
if (paramForm.value) {
|
||||
paramForm.value.clearValidate();
|
||||
paramForm.value.resetFields();
|
||||
}
|
||||
isHostMode.value = false;
|
||||
Object.assign(req, initData());
|
||||
};
|
||||
|
||||
const acceptParams = async (props: InstallRrops) => {
|
||||
resetForm();
|
||||
if (props.app.versions != undefined) {
|
||||
installData.value = props;
|
||||
} else {
|
||||
const res = await getAppByKey(props.app.key);
|
||||
installData.value.app = res.data;
|
||||
}
|
||||
|
||||
const app = installData.value.app;
|
||||
appVersions.value = app.versions;
|
||||
if (appVersions.value.length > 0) {
|
||||
req.version = appVersions.value[0];
|
||||
getDetail(appVersions.value[0]);
|
||||
}
|
||||
|
||||
req.name = props.app.key;
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
const getDetail = async (version: string) => {
|
||||
const install = async (submitData: any) => {
|
||||
loading.value = true;
|
||||
const taskID = newUUID();
|
||||
submitData.taskID = taskID;
|
||||
|
||||
try {
|
||||
const res = await getAppDetail(installData.value.app.id, version, 'app');
|
||||
req.appDetailId = res.data.id;
|
||||
req.dockerCompose = res.data.dockerCompose;
|
||||
isHostMode.value = res.data.hostMode;
|
||||
installData.value.params = res.data.params;
|
||||
paramKey.value++;
|
||||
memoryRequired.value = res.data.memoryRequired;
|
||||
gpuSupport.value = res.data.gpuSupport;
|
||||
await installApp(submitData);
|
||||
handleClose();
|
||||
openTaskLog(taskID);
|
||||
} catch (error) {
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const submit = async (formEl: FormInstance | undefined) => {
|
||||
if (!formEl) return;
|
||||
await formEl.validate((valid) => {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
if (req.editCompose && req.dockerCompose == '') {
|
||||
MsgError(i18n.global.t('app.composeNullErr'));
|
||||
return;
|
||||
}
|
||||
if (req.cpuQuota < 0) {
|
||||
req.cpuQuota = 0;
|
||||
}
|
||||
if (req.memoryLimit < 0) {
|
||||
req.memoryLimit = 0;
|
||||
}
|
||||
if (!isHostMode.value && !req.allowPort) {
|
||||
ElMessageBox.confirm(i18n.global.t('app.installWarn'), i18n.global.t('app.checkTitle'), {
|
||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
}).then(async () => {
|
||||
install();
|
||||
});
|
||||
} else {
|
||||
install();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const openTaskLog = (taskID: string) => {
|
||||
taskLogRef.value.openWithTaskID(taskID);
|
||||
};
|
||||
|
||||
const install = () => {
|
||||
loading.value = true;
|
||||
const taskID = newUUID();
|
||||
req.taskID = taskID;
|
||||
installApp(req)
|
||||
.then(() => {
|
||||
handleClose();
|
||||
openTaskLog(taskID);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
const loadLimit = async () => {
|
||||
const res = await loadResourceLimit();
|
||||
limits.value = res.data;
|
||||
limits.value.memory = Number((limits.value.memory / 1024 / 1024).toFixed(2));
|
||||
oldMemory.value = limits.value.memory;
|
||||
const acceptParams = async (props: { app: any; params?: any }) => {
|
||||
open.value = true;
|
||||
await nextTick();
|
||||
installFormRef.value?.resetForm();
|
||||
installFormRef.value?.initForm(props.app.key);
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
loadLimit();
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -91,6 +91,14 @@
|
|||
<el-input v-model="paramModel.specifyIP"></el-input>
|
||||
<span class="input-help">{{ $t('app.specifyIPHelper') }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.restartPolicy')" prop="restartPolicy">
|
||||
<el-select v-model="paramModel.restartPolicy" class="p-w-300">
|
||||
<el-option :label="$t('container.no')" value="no"></el-option>
|
||||
<el-option :label="$t('container.always')" value="always"></el-option>
|
||||
<el-option :label="$t('container.onFailure')" value="on-failure"></el-option>
|
||||
<el-option :label="$t('container.unlessStopped')" value="unless-stopped"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.cpuQuota')" prop="cpuQuota">
|
||||
<el-input type="number" class="!w-2/5" v-model.number="paramModel.cpuQuota" maxlength="5">
|
||||
<template #append>{{ $t('app.cpuCore') }}</template>
|
||||
|
|
@ -159,7 +167,7 @@ const loading = ref(false);
|
|||
const params = ref<EditForm[]>();
|
||||
const edit = ref(false);
|
||||
const paramForm = ref<FormInstance>();
|
||||
const paramModel = ref<any>({
|
||||
const paramModel = reactive<any>({
|
||||
params: {},
|
||||
});
|
||||
const rules = reactive({
|
||||
|
|
@ -167,8 +175,9 @@ const rules = reactive({
|
|||
cpuQuota: [Rules.requiredInput, checkNumberRange(0, 999)],
|
||||
memoryLimit: [Rules.requiredInput, checkNumberRange(0, 9999999999)],
|
||||
containerName: [Rules.containerName],
|
||||
restartPolicy: [Rules.requiredSelect],
|
||||
});
|
||||
const submitModel = ref<any>({
|
||||
const submitModel = reactive<any>({
|
||||
webUI: '',
|
||||
});
|
||||
const appType = ref('');
|
||||
|
|
@ -208,10 +217,10 @@ function checkPort(port: string) {
|
|||
}
|
||||
|
||||
const acceptParams = async (props: ParamProps) => {
|
||||
submitModel.value.installId = props.id;
|
||||
submitModel.installId = props.id;
|
||||
params.value = [];
|
||||
paramData.value.id = props.id;
|
||||
paramModel.value.params = {};
|
||||
paramModel.params = {};
|
||||
edit.value = false;
|
||||
await get();
|
||||
open.value = true;
|
||||
|
|
@ -224,14 +233,14 @@ const handleClose = () => {
|
|||
};
|
||||
const editParam = () => {
|
||||
params.value.forEach((param: EditForm) => {
|
||||
paramModel.value.params[param.key] = param.value;
|
||||
paramModel.params[param.key] = param.value;
|
||||
});
|
||||
edit.value = !edit.value;
|
||||
};
|
||||
|
||||
const changeAllowPort = () => {
|
||||
if (paramModel.value.allowPort) {
|
||||
paramModel.value.specifyIP = '';
|
||||
if (paramModel.allowPort) {
|
||||
paramModel.specifyIP = '';
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -271,15 +280,16 @@ const get = async () => {
|
|||
}
|
||||
});
|
||||
}
|
||||
paramModel.value.memoryLimit = res.data.memoryLimit;
|
||||
paramModel.value.cpuQuota = res.data.cpuQuota;
|
||||
paramModel.value.memoryUnit = res.data.memoryUnit !== '' ? res.data.memoryUnit : 'MB';
|
||||
paramModel.value.allowPort = res.data.allowPort;
|
||||
paramModel.value.containerName = res.data.containerName;
|
||||
paramModel.value.advanced = false;
|
||||
paramModel.value.dockerCompose = res.data.dockerCompose;
|
||||
paramModel.value.isHostMode = res.data.hostMode;
|
||||
paramModel.value.specifyIP = res.data.specifyIP;
|
||||
paramModel.memoryLimit = res.data.memoryLimit;
|
||||
paramModel.cpuQuota = res.data.cpuQuota;
|
||||
paramModel.memoryUnit = res.data.memoryUnit !== '' ? res.data.memoryUnit : 'MB';
|
||||
paramModel.allowPort = res.data.allowPort;
|
||||
paramModel.containerName = res.data.containerName;
|
||||
paramModel.advanced = false;
|
||||
paramModel.dockerCompose = res.data.dockerCompose;
|
||||
paramModel.isHostMode = res.data.hostMode;
|
||||
paramModel.specifyIP = res.data.specifyIP;
|
||||
paramModel.restartPolicy = res.data.restartPolicy || 'no';
|
||||
appConfigUpdate.value.webUI = res.data.webUI;
|
||||
if (res.data.webUI != '') {
|
||||
const httpConfig = splitHttp(res.data.webUI);
|
||||
|
|
@ -304,22 +314,23 @@ const submit = async (formEl: FormInstance) => {
|
|||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
||||
type: 'info',
|
||||
}).then(async () => {
|
||||
submitModel.value.params = paramModel.value.params;
|
||||
if (paramModel.value.advanced) {
|
||||
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;
|
||||
if (paramModel.value.editCompose) {
|
||||
submitModel.value.editCompose = paramModel.value.editCompose;
|
||||
submitModel.value.dockerCompose = paramModel.value.dockerCompose;
|
||||
submitModel.params = paramModel.params;
|
||||
if (paramModel.advanced) {
|
||||
submitModel.advanced = paramModel.advanced;
|
||||
submitModel.memoryLimit = paramModel.memoryLimit;
|
||||
submitModel.cpuQuota = paramModel.cpuQuota;
|
||||
submitModel.memoryUnit = paramModel.memoryUnit;
|
||||
submitModel.allowPort = paramModel.allowPort;
|
||||
submitModel.containerName = paramModel.containerName;
|
||||
if (paramModel.editCompose) {
|
||||
submitModel.editCompose = paramModel.editCompose;
|
||||
submitModel.dockerCompose = paramModel.dockerCompose;
|
||||
}
|
||||
submitModel.restartPolicy = paramModel.restartPolicy;
|
||||
}
|
||||
try {
|
||||
loading.value = true;
|
||||
await updateAppInstallParams(submitModel.value);
|
||||
await updateAppInstallParams(submitModel);
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.updateSuccess'));
|
||||
handleClose();
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ const getApps = async () => {
|
|||
try {
|
||||
const res = await getIgnoredApp();
|
||||
apps.value = res.data;
|
||||
console.log(apps.value);
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ let open = ref(false);
|
|||
|
||||
const acceptParams = (props: InstallProps) => {
|
||||
installData.value = props.items;
|
||||
console.log('acceptParams', props.items);
|
||||
open.value = true;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -100,12 +100,10 @@
|
|||
</el-form-item>
|
||||
<div v-if="website.appType == 'new'">
|
||||
<el-form-item :label="$t('app.app')" prop="appinstall.appId">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-select
|
||||
v-model="website.appinstall.appId"
|
||||
@change="changeApp()"
|
||||
class="p-w-200"
|
||||
class="p-w-300"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
|
|
@ -115,33 +113,8 @@
|
|||
:value="app.id"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-select
|
||||
v-model="website.appinstall.version"
|
||||
@change="getDetail(website.appinstall.version)"
|
||||
class="p-w-200"
|
||||
>
|
||||
<el-option
|
||||
v-for="(version, index) in appVersions"
|
||||
:key="index"
|
||||
:label="version"
|
||||
:value="version"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.table.name')" prop="appinstall.name">
|
||||
<el-input v-model.trim="website.appinstall.name"></el-input>
|
||||
</el-form-item>
|
||||
<Params
|
||||
:key="paramKey"
|
||||
v-model:form="website.appinstall.params"
|
||||
v-model:rules="rules.appinstall.params"
|
||||
:params="appParams"
|
||||
:propStart="'appinstall.params.'"
|
||||
></Params>
|
||||
<AppInstallForm ref="installFormRef" v-model="website.appinstall" :loading="loading" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="website.type === 'subsite'">
|
||||
|
|
@ -246,49 +219,6 @@
|
|||
{{ $t('website.runtimePortWarn') }}
|
||||
</el-text>
|
||||
</div>
|
||||
<el-form-item prop="advanced" v-if="website.type === 'deployment' && website.appType === 'new'">
|
||||
<el-checkbox v-model="website.appinstall.advanced" :label="$t('app.advanced')" size="large" />
|
||||
</el-form-item>
|
||||
|
||||
<div v-if="website.appinstall.advanced">
|
||||
<el-form-item :label="$t('app.containerName')" prop="containerName">
|
||||
<el-input
|
||||
v-model.trim="website.appinstall.containerName"
|
||||
:placeholder="$t('app.containerNameHelper')"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.cpuQuota')" prop="appinstall.cpuQuota">
|
||||
<el-input
|
||||
type="number"
|
||||
style="width: 40%"
|
||||
v-model.number="website.appinstall.cpuQuota"
|
||||
maxlength="5"
|
||||
>
|
||||
<template #append>{{ $t('app.cpuCore') }}</template>
|
||||
</el-input>
|
||||
<span class="input-help">{{ $t('container.limitHelper', [99999]) }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('container.memoryLimit')" prop="appinstall.memoryLimit">
|
||||
<el-input style="width: 40%" v-model.number="website.appinstall.memoryLimit" maxlength="10">
|
||||
<template #append>
|
||||
<el-select
|
||||
v-model="website.appinstall.memoryUnit"
|
||||
placeholder="Select"
|
||||
class="pre-select"
|
||||
>
|
||||
<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', ['9999999999']) }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item prop="allowPort" v-if="website.type === 'deployment'">
|
||||
<el-checkbox v-model="website.appinstall.allowPort" :label="$t('app.allowPort')" size="large" />
|
||||
<span class="input-help">{{ $t('app.allowPortHelper') }}</span>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<DomainCreate v-model:form="website" @gengerate="websiteForm.clearValidate()"></DomainCreate>
|
||||
<el-form-item prop="IPV6">
|
||||
<el-checkbox v-model="website.IPV6" :label="$t('website.ipv6')" size="large" />
|
||||
|
|
@ -521,7 +451,7 @@
|
|||
|
||||
<script lang="ts" setup name="CreateWebSite">
|
||||
import { App } from '@/api/interface/app';
|
||||
import { getAppByKey, getAppDetail, searchApp, getAppInstalled } from '@/api/modules/app';
|
||||
import { searchApp, getAppInstalled } from '@/api/modules/app';
|
||||
import {
|
||||
createWebsite,
|
||||
getWebsiteOptions,
|
||||
|
|
@ -534,7 +464,6 @@ import { Rules, checkNumberRange } from '@/global/form-rules';
|
|||
import i18n from '@/lang';
|
||||
import { ElForm, FormInstance } from 'element-plus';
|
||||
import { reactive, ref } from 'vue';
|
||||
import Params from '@/views/app-store/detail/params/index.vue';
|
||||
import Check from '../check/index.vue';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
import { getAgentGroupList } from '@/api/modules/group';
|
||||
|
|
@ -550,6 +479,7 @@ import { Website } from '@/api/interface/website';
|
|||
import DomainCreate from '@/views/website/website/domain-create/index.vue';
|
||||
import { getPathByType } from '@/api/modules/files';
|
||||
import { getWebsiteTypes } from '@/global/mimetype';
|
||||
import AppInstallForm from '@/views/app-store/detail/form/index.vue';
|
||||
|
||||
const websiteForm = ref<FormInstance>();
|
||||
|
||||
|
|
@ -646,10 +576,6 @@ const appReq = reactive({
|
|||
pageSize: 100,
|
||||
});
|
||||
const apps = ref<App.App[]>([]);
|
||||
const appVersions = ref<string[]>([]);
|
||||
const appDetail = ref<App.AppDetail>();
|
||||
const appParams = ref<App.AppParams>();
|
||||
const paramKey = ref(1);
|
||||
const preCheckRef = ref();
|
||||
const staticPath = ref('');
|
||||
const runtimeResource = ref('appstore');
|
||||
|
|
@ -671,6 +597,7 @@ const parentWebsites = ref();
|
|||
const dirs = ref([]);
|
||||
const runtimePorts = ref([]);
|
||||
const WebsiteTypes = getWebsiteTypes();
|
||||
const installFormRef = ref();
|
||||
|
||||
const handleClose = () => {
|
||||
open.value = false;
|
||||
|
|
@ -747,7 +674,7 @@ const searchAppList = () => {
|
|||
if (res.data.items.length > 0) {
|
||||
website.value.appinstall.appId = res.data.items[0].id;
|
||||
website.value.appinstall.appkey = res.data.items[0].key;
|
||||
getApp();
|
||||
changeApp();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -756,30 +683,11 @@ const changeApp = () => {
|
|||
apps.value.forEach((app) => {
|
||||
if (app.id === website.value.appinstall.appId) {
|
||||
website.value.appinstall.appkey = app.key;
|
||||
getApp();
|
||||
installFormRef.value.initForm(app.key);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getApp = () => {
|
||||
getAppByKey(website.value.appinstall.appkey).then((res) => {
|
||||
appVersions.value = res.data.versions;
|
||||
if (res.data.versions.length > 0) {
|
||||
website.value.appinstall.version = res.data.versions[0];
|
||||
getDetail(res.data.versions[0]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const getDetail = (version: string) => {
|
||||
getAppDetail(website.value.appinstall.appId, version, 'app').then((res) => {
|
||||
website.value.appinstall.appDetailId = res.data.id;
|
||||
appDetail.value = res.data;
|
||||
appParams.value = res.data.params;
|
||||
paramKey.value++;
|
||||
});
|
||||
};
|
||||
|
||||
const changeRuntimeType = () => {
|
||||
runtimeReq.value.type = website.value.runtimeType;
|
||||
website.value.appinstall.advanced = false;
|
||||
|
|
@ -893,6 +801,10 @@ const submit = async (formEl: FormInstance | undefined) => {
|
|||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
if (website.value.type == 'deployment' && website.value.appType === 'new') {
|
||||
const isValid = await installFormRef.value?.validate();
|
||||
if (!isValid) return;
|
||||
}
|
||||
if (website.value.type === 'runtime' && website.value.runtimeType !== 'php' && website.value.port == 0) {
|
||||
MsgError(i18n.global.t('website.runtimePortWarn'));
|
||||
return;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue