From 0b4aa6cf5733961fa6c1e8d88f68df5d56e5cf22 Mon Sep 17 00:00:00 2001 From: zhengkunwang223 <1paneldev@sina.com> Date: Thu, 16 Oct 2025 16:56:45 +0800 Subject: [PATCH] feat: support 1pctl app init command --- agent/cmd/server/cmd/app.go | 154 +++++++++++++++++ {agent => core}/cmd/server/app/app_config.go | 0 {agent => core}/cmd/server/app/app_config.yml | 10 +- {agent => core}/cmd/server/app/app_param.yml | 0 {agent => core}/cmd/server/app/logo.png | Bin core/cmd/server/cmd/app.go | 159 ++++++++++++++++++ 6 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 agent/cmd/server/cmd/app.go rename {agent => core}/cmd/server/app/app_config.go (100%) rename {agent => core}/cmd/server/app/app_config.yml (81%) rename {agent => core}/cmd/server/app/app_param.yml (100%) rename {agent => core}/cmd/server/app/logo.png (100%) create mode 100644 core/cmd/server/cmd/app.go diff --git a/agent/cmd/server/cmd/app.go b/agent/cmd/server/cmd/app.go new file mode 100644 index 000000000000..e492e450477c --- /dev/null +++ b/agent/cmd/server/cmd/app.go @@ -0,0 +1,154 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/1Panel-dev/1Panel/agent/cmd/server/app" + "github.com/1Panel-dev/1Panel/agent/i18n" + "github.com/1Panel-dev/1Panel/agent/utils/files" + "io" + + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + appKey string + appVersion string +) + +func init() { + appCmd.SetHelpFunc(func(c *cobra.Command, s []string) { + i18n.UseI18nForCmd(language) + loadAppHelper() + }) + initCmd.SetHelpFunc(func(c *cobra.Command, s []string) { + i18n.UseI18nForCmd(language) + loadAppInitHelper() + }) + + initCmd.Flags().StringVarP(&appKey, "key", "k", "", "") + initCmd.Flags().StringVarP(&appVersion, "version", "v", "", "") + appCmd.AddCommand(initCmd) + RootCmd.AddCommand(appCmd) +} + +var appCmd = &cobra.Command{ + Use: "app", +} + +var initCmd = &cobra.Command{ + Use: "init", + RunE: func(cmd *cobra.Command, args []string) error { + i18n.UseI18nForCmd(language) + if !isRoot() { + fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl app init"})) + return nil + } + if len(args) > 0 { + appKey = args[0] + if len(args) > 1 { + appVersion = args[1] + } + } + if appKey == "" { + fmt.Println(i18n.GetMsgByKeyForCmd("AppMissKey")) + return nil + } + if appVersion == "" { + fmt.Println(i18n.GetMsgByKeyForCmd("AppMissVersion")) + return nil + } + fileOp := files.NewFileOp() + appKeyPath := fmt.Sprintf("./%s", appKey) + if err := createFolder(fileOp, appKeyPath); err != nil { + return err + } + configYamlPath := fmt.Sprintf("%s/data.yml", appKeyPath) + if err := createFile(fileOp, configYamlPath); err != nil { + return err + } + if err := writeFile(fileOp, configYamlPath, bytes.NewReader(app.Config)); err != nil { + return err + } + readMePath := fmt.Sprintf("%s/README.md", appKeyPath) + if err := createFile(fileOp, readMePath); err != nil { + return err + } + logoPath := fmt.Sprintf("%s/logo.png", appKeyPath) + if err := createFile(fileOp, logoPath); err != nil { + return err + } + if err := writeFile(fileOp, logoPath, bytes.NewReader(app.Logo)); err != nil { + return err + } + versionPath := fmt.Sprintf("%s/%s", appKeyPath, appVersion) + if fileOp.Stat(versionPath) { + return errors.New(i18n.GetMsgByKeyForCmd("AppVersionExist")) + } + if err := createFolder(fileOp, versionPath); err != nil { + return err + } + versionParamPath := fmt.Sprintf("%s/%s", versionPath, "data.yml") + if err := createFile(fileOp, versionParamPath); err != nil { + return err + } + if err := writeFile(fileOp, versionParamPath, bytes.NewReader(app.Param)); err != nil { + return err + } + dockerComposeYamlPath := fmt.Sprintf("%s/%s", versionPath, "docker-compose.yml") + if err := createFile(fileOp, dockerComposeYamlPath); err != nil { + return err + } + fmt.Println(i18n.GetMsgByKeyForCmd("AppCreateSuccessful")) + return nil + }, +} + +func createFile(fileOp files.FileOp, filePath string) error { + if fileOp.Stat(filePath) { + return nil + } + if err := fileOp.CreateFile(filePath); err != nil { + fmt.Println(i18n.GetMsgWithMapForCmd("AppCreateFileErr", map[string]interface{}{"name": filePath, "err": err.Error()})) + return err + } + return nil +} + +func createFolder(fileOp files.FileOp, dirPath string) error { + if fileOp.Stat(dirPath) { + return nil + } + if err := fileOp.CreateDir(dirPath, 0755); err != nil { + fmt.Println(i18n.GetMsgWithMapForCmd("AppCreateDirErr", map[string]interface{}{"name": dirPath, "err": err.Error()})) + return err + } + return nil +} + +func writeFile(fileOp files.FileOp, filePath string, in io.Reader) error { + if err := fileOp.WriteFile(filePath, in, 0755); err != nil { + fmt.Println(i18n.GetMsgWithMapForCmd("AppWriteErr", map[string]interface{}{"name": filePath, "err": err.Error()})) + return err + } + return nil +} + +func loadAppHelper() { + fmt.Println(i18n.GetMsgByKeyForCmd("AppCommands")) + fmt.Println("\nUsage:\n 1panel app [command]\n\nAvailable Commands:") + fmt.Println("\n init " + i18n.GetMsgByKeyForCmd("AppInit")) + fmt.Println("\nFlags:\n -h, --help help for app") + fmt.Println(" -k, --key string " + i18n.GetMsgByKeyForCmd("AppKeyVal")) + fmt.Println(" -v, --version string " + i18n.GetMsgByKeyForCmd("AppVersion")) + fmt.Println("\nUse \"1panel app [command] --help\" for more information about a command.") +} + +func loadAppInitHelper() { + fmt.Println(i18n.GetMsgByKeyForCmd("AppInit")) + fmt.Println("\nUsage:\n 1panel app init [flags]") + fmt.Println("\nFlags:\n -h, --help help for app") + fmt.Println(" -k, --key string " + i18n.GetMsgByKeyForCmd("AppKeyVal")) + fmt.Println(" -v, --version string " + i18n.GetMsgByKeyForCmd("AppVersion")) +} diff --git a/agent/cmd/server/app/app_config.go b/core/cmd/server/app/app_config.go similarity index 100% rename from agent/cmd/server/app/app_config.go rename to core/cmd/server/app/app_config.go diff --git a/agent/cmd/server/app/app_config.yml b/core/cmd/server/app/app_config.yml similarity index 81% rename from agent/cmd/server/app/app_config.yml rename to core/cmd/server/app/app_config.yml index 3a2ec0827f37..31dbd25775d9 100644 --- a/agent/cmd/server/app/app_config.yml +++ b/core/cmd/server/app/app_config.yml @@ -10,4 +10,12 @@ additionalProperties: limit: #应用安装数量限制,0 代表无限制 website: #官网地址 github: #github 地址 - document: #文档地址 \ No newline at end of file + description: + en: + zh: #应用中文描述,不要超过30个字 + zh-Hant: + ja: + ms: + pt-br: + ru: + ko: \ No newline at end of file diff --git a/agent/cmd/server/app/app_param.yml b/core/cmd/server/app/app_param.yml similarity index 100% rename from agent/cmd/server/app/app_param.yml rename to core/cmd/server/app/app_param.yml diff --git a/agent/cmd/server/app/logo.png b/core/cmd/server/app/logo.png similarity index 100% rename from agent/cmd/server/app/logo.png rename to core/cmd/server/app/logo.png diff --git a/core/cmd/server/cmd/app.go b/core/cmd/server/cmd/app.go new file mode 100644 index 000000000000..3ea281674d85 --- /dev/null +++ b/core/cmd/server/cmd/app.go @@ -0,0 +1,159 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/1Panel-dev/1Panel/core/cmd/server/app" + "github.com/1Panel-dev/1Panel/core/i18n" + "io" + "os" + + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +var ( + appKey string + appVersion string +) + +func init() { + appCmd.SetHelpFunc(func(c *cobra.Command, s []string) { + i18n.UseI18nForCmd(language) + loadAppHelper() + }) + initCmd.SetHelpFunc(func(c *cobra.Command, s []string) { + i18n.UseI18nForCmd(language) + loadAppInitHelper() + }) + initCmd.Flags().StringVarP(&appKey, "key", "k", "", "") + initCmd.Flags().StringVarP(&appVersion, "version", "v", "", "") + appCmd.AddCommand(initCmd) + RootCmd.AddCommand(appCmd) +} + +var appCmd = &cobra.Command{ + Use: "app", +} + +var initCmd = &cobra.Command{ + Use: "init", + RunE: func(cmd *cobra.Command, args []string) error { + i18n.UseI18nForCmd(language) + if !isRoot() { + fmt.Println(i18n.GetMsgWithMapForCmd("SudoHelper", map[string]interface{}{"cmd": "sudo 1pctl app init"})) + return nil + } + if len(args) > 0 { + appKey = args[0] + if len(args) > 1 { + appVersion = args[1] + } + } + if appKey == "" { + fmt.Println(i18n.GetMsgByKeyForCmd("AppMissKey")) + return nil + } + if appVersion == "" { + fmt.Println(i18n.GetMsgByKeyForCmd("AppMissVersion")) + return nil + } + appKeyPath := fmt.Sprintf("./%s", appKey) + if err := createFolder(appKeyPath); err != nil { + return err + } + configYamlPath := fmt.Sprintf("%s/data.yml", appKeyPath) + if err := createFile(configYamlPath); err != nil { + return err + } + if err := writeFile(configYamlPath, bytes.NewReader(app.Config)); err != nil { + return err + } + readMePath := fmt.Sprintf("%s/README.md", appKeyPath) + if err := createFile(readMePath); err != nil { + return err + } + logoPath := fmt.Sprintf("%s/logo.png", appKeyPath) + if err := createFile(logoPath); err != nil { + return err + } + if err := writeFile(logoPath, bytes.NewReader(app.Logo)); err != nil { + return err + } + versionPath := fmt.Sprintf("%s/%s", appKeyPath, appVersion) + if _, err := os.Stat(versionPath); err == nil { + return errors.New(i18n.GetMsgByKeyForCmd("AppVersionExist")) + } + if err := createFolder(versionPath); err != nil { + return err + } + versionParamPath := fmt.Sprintf("%s/%s", versionPath, "data.yml") + if err := createFile(versionParamPath); err != nil { + return err + } + if err := writeFile(versionParamPath, bytes.NewReader(app.Param)); err != nil { + return err + } + dockerComposeYamlPath := fmt.Sprintf("%s/%s", versionPath, "docker-compose.yml") + if err := createFile(dockerComposeYamlPath); err != nil { + return err + } + fmt.Println(i18n.GetMsgByKeyForCmd("AppCreateSuccessful")) + return nil + }, +} + +func createFile(filePath string) error { + if _, err := os.Stat(filePath); err == nil { + return nil + } + file, err := os.Create(filePath) + if err != nil { + fmt.Println(i18n.GetMsgWithMapForCmd("AppCreateFileErr", map[string]interface{}{"name": filePath, "err": err.Error()})) + return err + } + defer file.Close() + return nil +} + +func createFolder(dirPath string) error { + if _, err := os.Stat(dirPath); err == nil { + return nil + } + if err := os.MkdirAll(dirPath, 0755); err != nil { + fmt.Println(i18n.GetMsgWithMapForCmd("AppCreateDirErr", map[string]interface{}{"name": dirPath, "err": err.Error()})) + return err + } + return nil +} + +func writeFile(filePath string, in io.Reader) error { + data, err := io.ReadAll(in) + if err != nil { + fmt.Println(i18n.GetMsgWithMapForCmd("AppWriteErr", map[string]interface{}{"name": filePath, "err": err.Error()})) + return err + } + if err := os.WriteFile(filePath, data, 0755); err != nil { + fmt.Println(i18n.GetMsgWithMapForCmd("AppWriteErr", map[string]interface{}{"name": filePath, "err": err.Error()})) + return err + } + return nil +} + +func loadAppHelper() { + fmt.Println(i18n.GetMsgByKeyForCmd("AppCommands")) + fmt.Println("\nUsage:\n 1panel app [command]\n\nAvailable Commands:") + fmt.Println("\n init " + i18n.GetMsgByKeyForCmd("AppInit")) + fmt.Println("\nFlags:\n -h, --help help for app") + fmt.Println(" -k, --key string " + i18n.GetMsgByKeyForCmd("AppKeyVal")) + fmt.Println(" -v, --version string " + i18n.GetMsgByKeyForCmd("AppVersion")) + fmt.Println("\nUse \"1panel app [command] --help\" for more information about a command.") +} + +func loadAppInitHelper() { + fmt.Println(i18n.GetMsgByKeyForCmd("AppInit")) + fmt.Println("\nUsage:\n 1panel app init [flags]") + fmt.Println("\nFlags:\n -h, --help help for app") + fmt.Println(" -k, --key string " + i18n.GetMsgByKeyForCmd("AppKeyVal")) + fmt.Println(" -v, --version string " + i18n.GetMsgByKeyForCmd("AppVersion")) +}