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 @@
+
+
+
+
+
+
+
+ Go {{ slug }}
+
+
+
+ {{ $gettext('Set as CLI Default Version') }}
+
+
+
+
+
+
+
+
+ {{ $gettext('GOPROXY is used to configure the Go module proxy. Using a domestic mirror can speed up dependency downloads.') }}
+
+
+
+
+
+
+ {{ $gettext('Save') }}
+
+
+
+
+
+
+
+
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 @@
+
+
+
+
+
+
+
+ Java {{ slug }} (Amazon Corretto)
+
+
+
+ {{ $gettext('Set as CLI Default Version') }}
+
+
+
+ {{ $gettext('Amazon Corretto is a no-cost, multiplatform, production-ready distribution of the Open Java Development Kit (OpenJDK).') }}
+
+
+
+
+
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 @@
+
+
+
+
+
+
+
+ Node.js {{ slug }}
+
+
+
+ {{ $gettext('Set as CLI Default Version') }}
+
+
+
+
+
+
+
+
+ {{ $gettext('npm registry is used to configure the npm package source. Using a domestic mirror can speed up package downloads.') }}
+
+
+
+
+
+
+ {{ $gettext('Save') }}
+
+
+
+
+
+
+
+
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 }}
+
+
+ PHP {{ slug }}
+
+
+
+
+ {{ $gettext('Set as CLI Default Version') }}
+
+
+ {{ $gettext('View PHPInfo') }}
+
+
+
+
-
-
- {{ $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 @@
+
+
+
+
+
+
+
+ Python {{ slug }}
+
+
+
+ {{ $gettext('Set as CLI Default Version') }}
+
+
+
+
+
+
+
+
+ {{ $gettext('pip mirror is used to configure the Python package source. Using a domestic mirror can speed up package downloads.') }}
+
+
+
+
+
+
+ {{ $gettext('Save') }}
+
+
+
+
+
+
+
+
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