From f8f47b51e9a009e6664f48e3802a62dff51be254 Mon Sep 17 00:00:00 2001 From: CityFun <31820853+zhengkunwang223@users.noreply.github.com> Date: Mon, 11 Aug 2025 18:16:44 +0800 Subject: [PATCH] feat: Configure restart policy for application installation support (#9953) Refs https://github.com/1Panel-dev/1Panel/issues/9895 --- agent/app/dto/request/app.go | 1 + agent/app/service/app_install.go | 1 + agent/app/service/app_utils.go | 26 +- agent/utils/docker/compose.go | 64 +-- frontend/src/api/interface/app.ts | 1 + frontend/src/api/modules/app.ts | 9 +- .../src/views/app-store/detail/form/index.vue | 400 ++++++++++++++++++ .../views/app-store/detail/install/index.vue | 311 +++----------- .../app-store/installed/detail/index.vue | 67 +-- .../app-store/installed/ignore/index.vue | 1 - .../src/views/database/mysql/check/index.vue | 1 - .../views/website/website/create/index.vue | 134 +----- 12 files changed, 550 insertions(+), 466 deletions(-) create mode 100644 frontend/src/views/app-store/detail/form/index.vue diff --git a/agent/app/dto/request/app.go b/agent/app/dto/request/app.go index 997c8956e..3fd50b060 100644 --- a/agent/app/dto/request/app.go +++ b/agent/app/dto/request/app.go @@ -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 { diff --git a/agent/app/service/app_install.go b/agent/app/service/app_install.go index 721359436..f5e57fa3b 100644 --- a/agent/app/service/app_install.go +++ b/agent/app/service/app_install.go @@ -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 diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index 8e236b5fa..868cade0d 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -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 { diff --git a/agent/utils/docker/compose.go b/agent/utils/docker/compose.go index 8a628a758..1e68c738a 100644 --- a/agent/utils/docker/compose.go +++ b/agent/utils/docker/compose.go @@ -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 { diff --git a/frontend/src/api/interface/app.ts b/frontend/src/api/interface/app.ts index 9cd98aefe..67b8f47af 100644 --- a/frontend/src/api/interface/app.ts +++ b/frontend/src/api/interface/app.ts @@ -276,6 +276,7 @@ export namespace App { type: string; webUI: string; specifyIP: string; + restartPolicy: string; } export interface IgnoredApp { diff --git a/frontend/src/api/modules/app.ts b/frontend/src/api/modules/app.ts index eb31ac938..ed1f23652 100644 --- a/frontend/src/api/modules/app.ts +++ b/frontend/src/api/modules/app.ts @@ -15,12 +15,9 @@ export const searchApp = (req: App.AppReq) => { return http.post('apps/search', req); }; -export const getAppByKey = (key: string) => { - return http.get('apps/' + key); -}; - -export const getAppByKeyWithNode = (key: string, node: string) => { - return http.get('apps/' + key + `?operateNode=${node}`); +export const getAppByKey = (key: string, node?: string) => { + const params = node ? `?operateNode=${node}` : ''; + return http.get('apps/' + key + `${params}`); }; export const getAppTags = () => { diff --git a/frontend/src/views/app-store/detail/form/index.vue b/frontend/src/views/app-store/detail/form/index.vue new file mode 100644 index 000000000..e83ff3095 --- /dev/null +++ b/frontend/src/views/app-store/detail/form/index.vue @@ -0,0 +1,400 @@ + + + diff --git a/frontend/src/views/app-store/detail/install/index.vue b/frontend/src/views/app-store/detail/install/index.vue index 7d8a5ffe0..5fbd7c416 100644 --- a/frontend/src/views/app-store/detail/install/index.vue +++ b/frontend/src/views/app-store/detail/install/index.vue @@ -1,120 +1,12 @@