From c7ed0325006322f7b958ffa7094fc0f153921360 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Fri, 16 Jan 2026 00:59:10 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=9B=B4=E5=A4=9A?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E7=8E=AF=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/ace/wire_gen.go | 6 +- go.sum | 7 + internal/data/environment.go | 38 +++- internal/http/request/environment_common.go | 24 +++ internal/route/http.go | 190 ++++++++++-------- internal/service/environment_go.go | 90 +++++++++ internal/service/environment_java.go | 48 +++++ internal/service/environment_nodejs.go | 89 ++++++++ internal/service/environment_php.go | 8 +- internal/service/environment_python.go | 89 ++++++++ internal/service/service.go | 4 + pkg/api/environment.go | 1 - web/src/api/panel/environment/go/index.ts | 11 + web/src/api/panel/environment/java/index.ts | 6 + web/src/api/panel/environment/nodejs/index.ts | 11 + web/src/api/panel/environment/python/index.ts | 11 + web/src/views/environment/GoView.vue | 90 +++++++++ web/src/views/environment/JavaView.vue | 36 ++++ web/src/views/environment/NodejsView.vue | 90 +++++++++ web/src/views/environment/PHPView.vue | 24 ++- web/src/views/environment/PythonView.vue | 92 +++++++++ web/src/views/environment/route.ts | 48 +++++ 22 files changed, 913 insertions(+), 100 deletions(-) create mode 100644 internal/http/request/environment_common.go create mode 100644 internal/service/environment_go.go create mode 100644 internal/service/environment_java.go create mode 100644 internal/service/environment_nodejs.go create mode 100644 internal/service/environment_python.go create mode 100644 web/src/api/panel/environment/go/index.ts create mode 100644 web/src/api/panel/environment/java/index.ts create mode 100644 web/src/api/panel/environment/nodejs/index.ts create mode 100644 web/src/api/panel/environment/python/index.ts create mode 100644 web/src/views/environment/GoView.vue create mode 100644 web/src/views/environment/JavaView.vue create mode 100644 web/src/views/environment/NodejsView.vue create mode 100644 web/src/views/environment/PythonView.vue diff --git a/cmd/ace/wire_gen.go b/cmd/ace/wire_gen.go index 701e9e6b..8904a963 100644 --- a/cmd/ace/wire_gen.go +++ b/cmd/ace/wire_gen.go @@ -96,7 +96,11 @@ func initWeb() (*app.Web, error) { certAccountService := service.NewCertAccountService(certAccountRepo) appService := service.NewAppService(locale, appRepo, cacheRepo, settingRepo) environmentService := service.NewEnvironmentService(locale, environmentRepo, taskRepo) + environmentGoService := service.NewEnvironmentGoService(locale, environmentRepo) + environmentJavaService := service.NewEnvironmentJavaService(locale, environmentRepo) + environmentNodejsService := service.NewEnvironmentNodejsService(locale, environmentRepo) environmentPHPService := service.NewEnvironmentPHPService(locale, config, environmentRepo, taskRepo) + environmentPythonService := service.NewEnvironmentPythonService(locale, environmentRepo) cronService := service.NewCronService(cronRepo) processService := service.NewProcessService() safeRepo := data.NewSafeRepo(logger) @@ -152,7 +156,7 @@ func initWeb() (*app.Web, error) { s3fsApp := s3fs.NewApp(locale) supervisorApp := supervisor.NewApp(locale) loader := bootstrap.NewLoader(apacheApp, codeserverApp, dockerApp, fail2banApp, frpApp, giteaApp, mariadbApp, memcachedApp, minioApp, mysqlApp, nginxApp, openrestyApp, perconaApp, phpmyadminApp, podmanApp, postgresqlApp, pureftpdApp, redisApp, rsyncApp, s3fsApp, supervisorApp) - http := route.NewHttp(config, userService, userTokenService, homeService, taskService, websiteService, projectService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, environmentService, environmentPHPService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, logService, monitorService, settingService, systemctlService, toolboxSystemService, toolboxBenchmarkService, toolboxSSHService, toolboxDiskService, toolboxLogService, webHookService, templateService, loader) + http := route.NewHttp(config, userService, userTokenService, homeService, taskService, websiteService, projectService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, environmentService, environmentGoService, environmentJavaService, environmentNodejsService, environmentPHPService, environmentPythonService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, logService, monitorService, settingService, systemctlService, toolboxSystemService, toolboxBenchmarkService, toolboxSSHService, toolboxDiskService, toolboxLogService, webHookService, templateService, loader) wsService := service.NewWsService(locale, config, logger, sshRepo) ws := route.NewWs(wsService) mux, err := bootstrap.NewRouter(locale, middlewares, http, ws) diff --git a/go.sum b/go.sum index 0adac8a1..ffac5f75 100644 --- a/go.sum +++ b/go.sum @@ -125,6 +125,8 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.2.0 h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= github.com/google/wire v0.7.0 h1:JxUKI6+CVBgCO2WToKy/nQk0sS+amI9z9EjVmdaocj4= github.com/google/wire v0.7.0/go.mod h1:n6YbUQD9cPKTnHXEBN2DXlOp/mVADhVErcMFb0v3J18= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= @@ -276,6 +278,7 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= @@ -386,6 +389,8 @@ golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -459,6 +464,8 @@ golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= diff --git a/internal/data/environment.go b/internal/data/environment.go index de20a81f..e5b5538f 100644 --- a/internal/data/environment.go +++ b/internal/data/environment.go @@ -36,7 +36,11 @@ func NewEnvironmentRepo(t *gotext.Locale, conf *config.Config, cache biz.CacheRe func (r *environmentRepo) Types() []types.LV { return []types.LV{ + {Label: "Go", Value: "go"}, + {Label: "Java", Value: "java"}, + {Label: "Node.js", Value: "nodejs"}, {Label: "PHP", Value: "php"}, + {Label: "Python", Value: "python"}, } } @@ -72,8 +76,16 @@ func (r *environmentRepo) IsInstalled(typ, slug string) bool { path := filepath.Join(app.Root, "server", typ, slug) var binFile string switch typ { + case "go": + binFile = filepath.Join(path, "bin", "go") + case "java": + binFile = filepath.Join(path, "bin", "java") + case "nodejs": + binFile = filepath.Join(path, "bin", "node") case "php": binFile = filepath.Join(path, "bin", "php") + case "python": + binFile = filepath.Join(path, "bin", "python3") default: return false } @@ -102,17 +114,33 @@ func (r *environmentRepo) InstalledVersion(typ, slug string) string { } var basePath = filepath.Join(app.Root, "server", typ, slug) + var version string + var err error switch typ { + case "go": + // go version go1.21.0 linux/amd64 -> 1.21.0 + version, err = shell.Exec(filepath.Join(basePath, "bin", "go") + " version | awk '{print $3}' | sed 's/go//'") + case "java": + // openjdk version "17.0.8" 2023-07-18 LTS -> 17.0.8 + version, err = shell.Exec(filepath.Join(basePath, "bin", "java") + " -version 2>&1 | head -n 1 | awk -F'\"' '{print $2}'") + case "nodejs": + // v20.10.0 -> 20.10.0 + version, err = shell.Exec(filepath.Join(basePath, "bin", "node") + " -v | sed 's/v//'") case "php": - version, err := shell.Exec(filepath.Join(basePath, "bin", "php") + " -v | head -n 1 | awk '{print $2}'") - if err != nil { - return "" - } - return version + // PHP 8.3.0 (cli) -> 8.3.0 + version, err = shell.Exec(filepath.Join(basePath, "bin", "php") + " -v | head -n 1 | awk '{print $2}'") + case "python": + // Python 3.11.5 -> 3.11.5 + version, err = shell.Exec(filepath.Join(basePath, "bin", "python3") + " --version | awk '{print $2}'") default: return "" } + + if err != nil { + return "" + } + return version } func (r *environmentRepo) HasUpdate(typ, slug string) bool { diff --git a/internal/http/request/environment_common.go b/internal/http/request/environment_common.go new file mode 100644 index 00000000..e967f4a0 --- /dev/null +++ b/internal/http/request/environment_common.go @@ -0,0 +1,24 @@ +package request + +// EnvironmentSlug 环境版本请求(通用) +type EnvironmentSlug struct { + Slug string `json:"slug"` +} + +// EnvironmentProxy Go 代理设置请求 +type EnvironmentProxy struct { + Slug string `json:"slug"` + Proxy string `form:"proxy" json:"proxy" validate:"required"` +} + +// EnvironmentRegistry Node.js 镜像设置请求 +type EnvironmentRegistry struct { + Slug string `json:"slug"` + Registry string `form:"registry" json:"registry" validate:"required"` +} + +// EnvironmentMirror Python 镜像设置请求 +type EnvironmentMirror struct { + Slug string `json:"slug"` + Mirror string `form:"mirror" json:"mirror" validate:"required"` +} diff --git a/internal/route/http.go b/internal/route/http.go index 4cb627cd..53d05ab8 100644 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -16,46 +16,50 @@ import ( ) type Http struct { - conf *config.Config - user *service.UserService - userToken *service.UserTokenService - home *service.HomeService - task *service.TaskService - website *service.WebsiteService - project *service.ProjectService - database *service.DatabaseService - databaseServer *service.DatabaseServerService - databaseUser *service.DatabaseUserService - backup *service.BackupService - cert *service.CertService - certDNS *service.CertDNSService - certAccount *service.CertAccountService - app *service.AppService - environment *service.EnvironmentService - environmentPHP *service.EnvironmentPHPService - cron *service.CronService - process *service.ProcessService - safe *service.SafeService - firewall *service.FirewallService - ssh *service.SSHService - container *service.ContainerService - containerCompose *service.ContainerComposeService - containerNetwork *service.ContainerNetworkService - containerImage *service.ContainerImageService - containerVolume *service.ContainerVolumeService - file *service.FileService - log *service.LogService - monitor *service.MonitorService - setting *service.SettingService - systemctl *service.SystemctlService - toolboxSystem *service.ToolboxSystemService - toolboxBenchmark *service.ToolboxBenchmarkService - toolboxSSH *service.ToolboxSSHService - toolboxDisk *service.ToolboxDiskService - toolboxLog *service.ToolboxLogService - webhook *service.WebHookService - template *service.TemplateService - apps *apploader.Loader + conf *config.Config + user *service.UserService + userToken *service.UserTokenService + home *service.HomeService + task *service.TaskService + website *service.WebsiteService + project *service.ProjectService + database *service.DatabaseService + databaseServer *service.DatabaseServerService + databaseUser *service.DatabaseUserService + backup *service.BackupService + cert *service.CertService + certDNS *service.CertDNSService + certAccount *service.CertAccountService + app *service.AppService + environment *service.EnvironmentService + environmentGo *service.EnvironmentGoService + environmentJava *service.EnvironmentJavaService + environmentNodejs *service.EnvironmentNodejsService + environmentPHP *service.EnvironmentPHPService + environmentPython *service.EnvironmentPythonService + cron *service.CronService + process *service.ProcessService + safe *service.SafeService + firewall *service.FirewallService + ssh *service.SSHService + container *service.ContainerService + containerCompose *service.ContainerComposeService + containerNetwork *service.ContainerNetworkService + containerImage *service.ContainerImageService + containerVolume *service.ContainerVolumeService + file *service.FileService + log *service.LogService + monitor *service.MonitorService + setting *service.SettingService + systemctl *service.SystemctlService + toolboxSystem *service.ToolboxSystemService + toolboxBenchmark *service.ToolboxBenchmarkService + toolboxSSH *service.ToolboxSSHService + toolboxDisk *service.ToolboxDiskService + toolboxLog *service.ToolboxLogService + webhook *service.WebHookService + template *service.TemplateService + apps *apploader.Loader } func NewHttp( @@ -75,7 +79,11 @@ func NewHttp( certAccount *service.CertAccountService, app *service.AppService, environment *service.EnvironmentService, + environmentGo *service.EnvironmentGoService, + environmentJava *service.EnvironmentJavaService, + environmentNodejs *service.EnvironmentNodejsService, environmentPHP *service.EnvironmentPHPService, + environmentPython *service.EnvironmentPythonService, cron *service.CronService, process *service.ProcessService, safe *service.SafeService, @@ -101,46 +109,50 @@ func NewHttp( apps *apploader.Loader, ) *Http { return &Http{ - conf: conf, - user: user, - userToken: userToken, - home: home, - task: task, - website: website, - project: project, - database: database, - databaseServer: databaseServer, - databaseUser: databaseUser, - backup: backup, - cert: cert, - certDNS: certDNS, - certAccount: certAccount, - app: app, - environment: environment, - environmentPHP: environmentPHP, - cron: cron, - process: process, - safe: safe, - firewall: firewall, - ssh: ssh, - container: container, - containerCompose: containerCompose, - containerNetwork: containerNetwork, - containerImage: containerImage, - containerVolume: containerVolume, - file: file, - log: log, - monitor: monitor, - setting: setting, - systemctl: systemctl, - toolboxSystem: toolboxSystem, - toolboxBenchmark: toolboxBenchmark, - toolboxSSH: toolboxSSH, - toolboxDisk: toolboxDisk, - toolboxLog: toolboxLog, - webhook: webhook, - template: template, - apps: apps, + conf: conf, + user: user, + userToken: userToken, + home: home, + task: task, + website: website, + project: project, + database: database, + databaseServer: databaseServer, + databaseUser: databaseUser, + backup: backup, + cert: cert, + certDNS: certDNS, + certAccount: certAccount, + app: app, + environment: environment, + environmentGo: environmentGo, + environmentJava: environmentJava, + environmentNodejs: environmentNodejs, + environmentPHP: environmentPHP, + environmentPython: environmentPython, + cron: cron, + process: process, + safe: safe, + firewall: firewall, + ssh: ssh, + container: container, + containerCompose: containerCompose, + containerNetwork: containerNetwork, + containerImage: containerImage, + containerVolume: containerVolume, + file: file, + log: log, + monitor: monitor, + setting: setting, + systemctl: systemctl, + toolboxSystem: toolboxSystem, + toolboxBenchmark: toolboxBenchmark, + toolboxSSH: toolboxSSH, + toolboxDisk: toolboxDisk, + toolboxLog: toolboxLog, + webhook: webhook, + template: template, + apps: apps, } } @@ -306,6 +318,19 @@ func (route *Http) Register(r *chi.Mux) { r.Get("/uninstall", route.environment.Uninstall) r.Put("/update", route.environment.Update) r.Get("/is_installed", route.environment.IsInstalled) + r.Route("/go", func(r chi.Router) { + r.Post("/{slug}/set_cli", route.environmentGo.SetCli) + r.Get("/{slug}/proxy", route.environmentGo.GetProxy) + r.Post("/{slug}/proxy", route.environmentGo.SetProxy) + }) + r.Route("/java", func(r chi.Router) { + r.Post("/{slug}/set_cli", route.environmentJava.SetCli) + }) + r.Route("/nodejs", func(r chi.Router) { + r.Post("/{slug}/set_cli", route.environmentNodejs.SetCli) + r.Get("/{slug}/registry", route.environmentNodejs.GetRegistry) + r.Post("/{slug}/registry", route.environmentNodejs.SetRegistry) + }) r.Route("/php", func(r chi.Router) { r.Post("/{version}/set_cli", route.environmentPHP.SetCli) r.Get("/{version}/phpinfo", route.environmentPHP.PHPInfo) @@ -322,6 +347,11 @@ func (route *Http) Register(r *chi.Mux) { r.Post("/{version}/modules", route.environmentPHP.InstallModule) r.Delete("/{version}/modules", route.environmentPHP.UninstallModule) }) + r.Route("/python", func(r chi.Router) { + r.Post("/{slug}/set_cli", route.environmentPython.SetCli) + r.Get("/{slug}/mirror", route.environmentPython.GetMirror) + r.Post("/{slug}/mirror", route.environmentPython.SetMirror) + }) }) r.Route("/cron", func(r chi.Router) { diff --git a/internal/service/environment_go.go b/internal/service/environment_go.go new file mode 100644 index 00000000..df5246cb --- /dev/null +++ b/internal/service/environment_go.go @@ -0,0 +1,90 @@ +package service + +import ( + "fmt" + "net/http" + "strings" + + "github.com/leonelquinteros/gotext" + + "github.com/acepanel/panel/internal/app" + "github.com/acepanel/panel/internal/biz" + "github.com/acepanel/panel/internal/http/request" + "github.com/acepanel/panel/pkg/shell" +) + +type EnvironmentGoService struct { + t *gotext.Locale + environmentRepo biz.EnvironmentRepo +} + +func NewEnvironmentGoService(t *gotext.Locale, environmentRepo biz.EnvironmentRepo) *EnvironmentGoService { + return &EnvironmentGoService{ + t: t, + environmentRepo: environmentRepo, + } +} + +func (s *EnvironmentGoService) SetCli(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.EnvironmentSlug](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + if !s.environmentRepo.IsInstalled("go", req.Slug) { + Error(w, http.StatusUnprocessableEntity, s.t.Get("Go-%s is not installed", req.Slug)) + return + } + + binPath := fmt.Sprintf("%s/server/go/%s/bin", app.Root, req.Slug) + if _, err = shell.Execf("ln -sf %s/go /usr/local/bin/go", binPath); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + if _, err = shell.Execf("ln -sf %s/gofmt /usr/local/bin/gofmt", binPath); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + Success(w, nil) +} + +func (s *EnvironmentGoService) GetProxy(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.EnvironmentSlug](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + if !s.environmentRepo.IsInstalled("go", req.Slug) { + Error(w, http.StatusUnprocessableEntity, s.t.Get("Go-%s is not installed", req.Slug)) + return + } + + goBin := fmt.Sprintf("%s/server/go/%s/bin/go", app.Root, req.Slug) + proxy, err := shell.Execf("%s env GOPROXY", goBin) + if err != nil { + proxy = "https://proxy.golang.org,direct" + } + + Success(w, strings.TrimSpace(proxy)) +} + +func (s *EnvironmentGoService) SetProxy(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.EnvironmentProxy](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + if !s.environmentRepo.IsInstalled("go", req.Slug) { + Error(w, http.StatusUnprocessableEntity, s.t.Get("Go-%s is not installed", req.Slug)) + return + } + + goBin := fmt.Sprintf("%s/server/go/%s/bin/go", app.Root, req.Slug) + if _, err = shell.Execf("%s env -w GOPROXY=%s", goBin, req.Proxy); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + Success(w, nil) +} diff --git a/internal/service/environment_java.go b/internal/service/environment_java.go new file mode 100644 index 00000000..975ee9ad --- /dev/null +++ b/internal/service/environment_java.go @@ -0,0 +1,48 @@ +package service + +import ( + "fmt" + "net/http" + + "github.com/leonelquinteros/gotext" + + "github.com/acepanel/panel/internal/app" + "github.com/acepanel/panel/internal/biz" + "github.com/acepanel/panel/internal/http/request" + "github.com/acepanel/panel/pkg/shell" +) + +type EnvironmentJavaService struct { + t *gotext.Locale + environmentRepo biz.EnvironmentRepo +} + +func NewEnvironmentJavaService(t *gotext.Locale, environmentRepo biz.EnvironmentRepo) *EnvironmentJavaService { + return &EnvironmentJavaService{ + t: t, + environmentRepo: environmentRepo, + } +} + +func (s *EnvironmentJavaService) SetCli(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.EnvironmentSlug](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + if !s.environmentRepo.IsInstalled("java", req.Slug) { + Error(w, http.StatusUnprocessableEntity, s.t.Get("Java-%s is not installed", req.Slug)) + return + } + + binPath := fmt.Sprintf("%s/server/java/%s/bin", app.Root, req.Slug) + binaries := []string{"java", "javac", "jar", "jshell"} + for _, bin := range binaries { + if _, err = shell.Execf("ln -sf %s/%s /usr/local/bin/%s", binPath, bin, bin); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + } + + Success(w, nil) +} diff --git a/internal/service/environment_nodejs.go b/internal/service/environment_nodejs.go new file mode 100644 index 00000000..79d1297d --- /dev/null +++ b/internal/service/environment_nodejs.go @@ -0,0 +1,89 @@ +package service + +import ( + "fmt" + "net/http" + "strings" + + "github.com/leonelquinteros/gotext" + + "github.com/acepanel/panel/internal/app" + "github.com/acepanel/panel/internal/biz" + "github.com/acepanel/panel/internal/http/request" + "github.com/acepanel/panel/pkg/shell" +) + +type EnvironmentNodejsService struct { + t *gotext.Locale + environmentRepo biz.EnvironmentRepo +} + +func NewEnvironmentNodejsService(t *gotext.Locale, environmentRepo biz.EnvironmentRepo) *EnvironmentNodejsService { + return &EnvironmentNodejsService{ + t: t, + environmentRepo: environmentRepo, + } +} + +func (s *EnvironmentNodejsService) SetCli(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.EnvironmentSlug](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + if !s.environmentRepo.IsInstalled("nodejs", req.Slug) { + Error(w, http.StatusUnprocessableEntity, s.t.Get("Node.js-%s is not installed", req.Slug)) + return + } + + binPath := fmt.Sprintf("%s/server/nodejs/%s/bin", app.Root, req.Slug) + binaries := []string{"node", "npm", "npx", "corepack"} + for _, bin := range binaries { + if _, err = shell.Execf("ln -sf %s/%s /usr/local/bin/%s", binPath, bin, bin); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + } + + Success(w, nil) +} + +func (s *EnvironmentNodejsService) GetRegistry(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.EnvironmentSlug](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + if !s.environmentRepo.IsInstalled("nodejs", req.Slug) { + Error(w, http.StatusUnprocessableEntity, s.t.Get("Node.js-%s is not installed", req.Slug)) + return + } + + npmBin := fmt.Sprintf("%s/server/nodejs/%s/bin/npm", app.Root, req.Slug) + registry, err := shell.Execf("%s config get --global registry", npmBin) + if err != nil { + registry = "https://registry.npmjs.org/" + } + + Success(w, strings.TrimSpace(registry)) +} + +func (s *EnvironmentNodejsService) SetRegistry(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.EnvironmentRegistry](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + if !s.environmentRepo.IsInstalled("nodejs", req.Slug) { + Error(w, http.StatusUnprocessableEntity, s.t.Get("Node.js-%s is not installed", req.Slug)) + return + } + + npmBin := fmt.Sprintf("%s/server/nodejs/%s/bin/npm", app.Root, req.Slug) + if _, err = shell.Execf("%s config set --global registry %s", npmBin, req.Registry); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + Success(w, nil) +} diff --git a/internal/service/environment_php.go b/internal/service/environment_php.go index b227bb20..7ad4b4ac 100644 --- a/internal/service/environment_php.go +++ b/internal/service/environment_php.go @@ -88,13 +88,13 @@ func (s *EnvironmentPHPService) GetConfig(w http.ResponseWriter, r *http.Request return } - config, err := io.Read(fmt.Sprintf("%s/server/php/%d/etc/php.ini", app.Root, req.Version)) + ini, err := io.Read(fmt.Sprintf("%s/server/php/%d/etc/php.ini", app.Root, req.Version)) if err != nil { Error(w, http.StatusInternalServerError, "%v", err) return } - Success(w, config) + Success(w, ini) } func (s *EnvironmentPHPService) UpdateConfig(w http.ResponseWriter, r *http.Request) { @@ -127,13 +127,13 @@ func (s *EnvironmentPHPService) GetFPMConfig(w http.ResponseWriter, r *http.Requ return } - config, err := io.Read(fmt.Sprintf("%s/server/php/%d/etc/php-fpm.conf", app.Root, req.Version)) + ini, err := io.Read(fmt.Sprintf("%s/server/php/%d/etc/php-fpm.conf", app.Root, req.Version)) if err != nil { Error(w, http.StatusInternalServerError, "%v", err) return } - Success(w, config) + Success(w, ini) } func (s *EnvironmentPHPService) UpdateFPMConfig(w http.ResponseWriter, r *http.Request) { diff --git a/internal/service/environment_python.go b/internal/service/environment_python.go new file mode 100644 index 00000000..082ca82e --- /dev/null +++ b/internal/service/environment_python.go @@ -0,0 +1,89 @@ +package service + +import ( + "fmt" + "net/http" + "strings" + + "github.com/leonelquinteros/gotext" + + "github.com/acepanel/panel/internal/app" + "github.com/acepanel/panel/internal/biz" + "github.com/acepanel/panel/internal/http/request" + "github.com/acepanel/panel/pkg/shell" +) + +type EnvironmentPythonService struct { + t *gotext.Locale + environmentRepo biz.EnvironmentRepo +} + +func NewEnvironmentPythonService(t *gotext.Locale, environmentRepo biz.EnvironmentRepo) *EnvironmentPythonService { + return &EnvironmentPythonService{ + t: t, + environmentRepo: environmentRepo, + } +} + +func (s *EnvironmentPythonService) SetCli(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.EnvironmentSlug](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + if !s.environmentRepo.IsInstalled("python", req.Slug) { + Error(w, http.StatusUnprocessableEntity, s.t.Get("Python-%s is not installed", req.Slug)) + return + } + + binPath := fmt.Sprintf("%s/server/python/%s/bin", app.Root, req.Slug) + binaries := []string{"python3", "pip3"} + for _, bin := range binaries { + if _, err = shell.Execf("ln -sf %s/%s /usr/local/bin/%s", binPath, bin, bin); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + } + + Success(w, nil) +} + +func (s *EnvironmentPythonService) GetMirror(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.EnvironmentSlug](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + if !s.environmentRepo.IsInstalled("python", req.Slug) { + Error(w, http.StatusUnprocessableEntity, s.t.Get("Python-%s is not installed", req.Slug)) + return + } + + pipBin := fmt.Sprintf("%s/server/python/%s/bin/pip3", app.Root, req.Slug) + mirror, err := shell.Execf("%s config --global get global.index-url", pipBin) + if err != nil { + mirror = "https://pypi.org/simple" + } + + Success(w, strings.TrimSpace(mirror)) +} + +func (s *EnvironmentPythonService) SetMirror(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.EnvironmentMirror](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + if !s.environmentRepo.IsInstalled("python", req.Slug) { + Error(w, http.StatusUnprocessableEntity, s.t.Get("Python-%s is not installed", req.Slug)) + return + } + + pipBin := fmt.Sprintf("%s/server/python/%s/bin/pip3", app.Root, req.Slug) + if _, err = shell.Execf("%s config --global set global.index-url %s", pipBin, req.Mirror); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + Success(w, nil) +} diff --git a/internal/service/service.go b/internal/service/service.go index bab22d84..48ecb04d 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -20,7 +20,11 @@ var ProviderSet = wire.NewSet( NewDatabaseServerService, NewDatabaseUserService, NewEnvironmentService, + NewEnvironmentGoService, + NewEnvironmentJavaService, + NewEnvironmentNodejsService, NewEnvironmentPHPService, + NewEnvironmentPythonService, NewFileService, NewFirewallService, NewHomeService, diff --git a/pkg/api/environment.go b/pkg/api/environment.go index 53b87467..fcacf870 100644 --- a/pkg/api/environment.go +++ b/pkg/api/environment.go @@ -8,7 +8,6 @@ type Environment struct { Name string `json:"name"` Version string `json:"version"` Description string `json:"description"` - Order int `json:"order"` } type Environments []*Environment diff --git a/web/src/api/panel/environment/go/index.ts b/web/src/api/panel/environment/go/index.ts new file mode 100644 index 00000000..48788c1c --- /dev/null +++ b/web/src/api/panel/environment/go/index.ts @@ -0,0 +1,11 @@ +import { http } from '@/utils' + +export default { + // 设为 CLI 版本 + setCli: (slug: string): any => http.Post(`/environment/go/${slug}/set_cli`), + // 获取代理 + getProxy: (slug: string): any => http.Get(`/environment/go/${slug}/proxy`), + // 设置代理 + setProxy: (slug: string, proxy: string): any => + http.Post(`/environment/go/${slug}/proxy`, { proxy }) +} diff --git a/web/src/api/panel/environment/java/index.ts b/web/src/api/panel/environment/java/index.ts new file mode 100644 index 00000000..4ffa2d1c --- /dev/null +++ b/web/src/api/panel/environment/java/index.ts @@ -0,0 +1,6 @@ +import { http } from '@/utils' + +export default { + // 设为 CLI 版本 + setCli: (slug: string): any => http.Post(`/environment/java/${slug}/set_cli`) +} diff --git a/web/src/api/panel/environment/nodejs/index.ts b/web/src/api/panel/environment/nodejs/index.ts new file mode 100644 index 00000000..2250927a --- /dev/null +++ b/web/src/api/panel/environment/nodejs/index.ts @@ -0,0 +1,11 @@ +import { http } from '@/utils' + +export default { + // 设为 CLI 版本 + setCli: (slug: string): any => http.Post(`/environment/nodejs/${slug}/set_cli`), + // 获取镜像 + getRegistry: (slug: string): any => http.Get(`/environment/nodejs/${slug}/registry`), + // 设置镜像 + setRegistry: (slug: string, registry: string): any => + http.Post(`/environment/nodejs/${slug}/registry`, { registry }) +} diff --git a/web/src/api/panel/environment/python/index.ts b/web/src/api/panel/environment/python/index.ts new file mode 100644 index 00000000..147e0a59 --- /dev/null +++ b/web/src/api/panel/environment/python/index.ts @@ -0,0 +1,11 @@ +import { http } from '@/utils' + +export default { + // 设为 CLI 版本 + setCli: (slug: string): any => http.Post(`/environment/python/${slug}/set_cli`), + // 获取镜像 + getMirror: (slug: string): any => http.Get(`/environment/python/${slug}/mirror`), + // 设置镜像 + setMirror: (slug: string, mirror: string): any => + http.Post(`/environment/python/${slug}/mirror`, { mirror }) +} diff --git a/web/src/views/environment/GoView.vue b/web/src/views/environment/GoView.vue new file mode 100644 index 00000000..00e5eaca --- /dev/null +++ b/web/src/views/environment/GoView.vue @@ -0,0 +1,90 @@ + + + diff --git a/web/src/views/environment/JavaView.vue b/web/src/views/environment/JavaView.vue new file mode 100644 index 00000000..7bf18169 --- /dev/null +++ b/web/src/views/environment/JavaView.vue @@ -0,0 +1,36 @@ + + + diff --git a/web/src/views/environment/NodejsView.vue b/web/src/views/environment/NodejsView.vue new file mode 100644 index 00000000..8bfcbbf4 --- /dev/null +++ b/web/src/views/environment/NodejsView.vue @@ -0,0 +1,90 @@ + + + diff --git a/web/src/views/environment/PHPView.vue b/web/src/views/environment/PHPView.vue index 404a9991..2093daad 100644 --- a/web/src/views/environment/PHPView.vue +++ b/web/src/views/environment/PHPView.vue @@ -191,16 +191,22 @@ const handleUninstallModule = async (module: string) => { - PHP {{ slug }} + + + + - - - {{ $gettext('Set as CLI Default Version') }} - - - {{ $gettext('View PHPInfo') }} - - diff --git a/web/src/views/environment/PythonView.vue b/web/src/views/environment/PythonView.vue new file mode 100644 index 00000000..a2179227 --- /dev/null +++ b/web/src/views/environment/PythonView.vue @@ -0,0 +1,92 @@ + + + diff --git a/web/src/views/environment/route.ts b/web/src/views/environment/route.ts index ba397fd2..6f53a99d 100644 --- a/web/src/views/environment/route.ts +++ b/web/src/views/environment/route.ts @@ -8,6 +8,42 @@ export default { isHidden: true, component: Layout, children: [ + { + name: 'environment-go', + path: 'go/:slug', + isHidden: true, + component: () => import('./GoView.vue'), + meta: { + title: 'Go', + icon: 'mdi:language-go', + role: ['admin'], + requireAuth: true + } + }, + { + name: 'environment-java', + path: 'java/:slug', + isHidden: true, + component: () => import('./JavaView.vue'), + meta: { + title: 'Java', + icon: 'mdi:language-java', + role: ['admin'], + requireAuth: true + } + }, + { + name: 'environment-nodejs', + path: 'nodejs/:slug', + isHidden: true, + component: () => import('./NodejsView.vue'), + meta: { + title: 'Node.js', + icon: 'mdi:nodejs', + role: ['admin'], + requireAuth: true + } + }, { name: 'environment-php', path: 'php/:slug', @@ -19,6 +55,18 @@ export default { role: ['admin'], requireAuth: true } + }, + { + name: 'environment-python', + path: 'python/:slug', + isHidden: true, + component: () => import('./PythonView.vue'), + meta: { + title: 'Python', + icon: 'mdi:language-python', + role: ['admin'], + requireAuth: true + } } ] } as RouteType