diff --git a/cmd/ace/wire_gen.go b/cmd/ace/wire_gen.go index 64e72e1a..b83d2901 100644 --- a/cmd/ace/wire_gen.go +++ b/cmd/ace/wire_gen.go @@ -118,6 +118,7 @@ func initWeb() (*app.Web, error) { systemctlService := service.NewSystemctlService(locale) toolboxSystemService := service.NewToolboxSystemService(locale) toolboxBenchmarkService := service.NewToolboxBenchmarkService(locale) + toolboxSSHService := service.NewToolboxSSHService(locale) webHookRepo := data.NewWebHookRepo(locale, db) webHookService := service.NewWebHookService(webHookRepo) codeserverApp := codeserver.NewApp() @@ -141,7 +142,7 @@ func initWeb() (*app.Web, error) { s3fsApp := s3fs.NewApp(locale) supervisorApp := supervisor.NewApp(locale) loader := bootstrap.NewLoader(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, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, environmentService, environmentPHPService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, toolboxSystemService, toolboxBenchmarkService, webHookService, loader) + http := route.NewHttp(config, userService, userTokenService, homeService, taskService, websiteService, databaseService, databaseServerService, databaseUserService, backupService, certService, certDNSService, certAccountService, appService, environmentService, environmentPHPService, cronService, processService, safeService, firewallService, sshService, containerService, containerComposeService, containerNetworkService, containerImageService, containerVolumeService, fileService, monitorService, settingService, systemctlService, toolboxSystemService, toolboxBenchmarkService, toolboxSSHService, webHookService, 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 2b43e86e..72e7e4b4 100644 --- a/go.sum +++ b/go.sum @@ -118,6 +118,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= @@ -267,6 +269,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/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs= @@ -375,6 +378,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= @@ -447,6 +452,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= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= diff --git a/internal/http/request/toolbox_ssh.go b/internal/http/request/toolbox_ssh.go new file mode 100644 index 00000000..1ec9456b --- /dev/null +++ b/internal/http/request/toolbox_ssh.go @@ -0,0 +1,26 @@ +package request + +// ToolboxSSHPort SSH 端口设置 +type ToolboxSSHPort struct { + Port uint `form:"port" json:"port" validate:"required|min:1|max:65535"` +} + +// ToolboxSSHPasswordAuth SSH 密码认证设置 +type ToolboxSSHPasswordAuth struct { + Enabled bool `form:"enabled" json:"enabled"` +} + +// ToolboxSSHPubKeyAuth SSH 密钥认证设置 +type ToolboxSSHPubKeyAuth struct { + Enabled bool `form:"enabled" json:"enabled"` +} + +// ToolboxSSHRootLogin Root 登录设置 +type ToolboxSSHRootLogin struct { + Mode string `form:"mode" json:"mode" validate:"required|in:yes,no,without-password,prohibit-password"` +} + +// ToolboxSSHRootPassword Root 密码设置 +type ToolboxSSHRootPassword struct { + Password string `form:"password" json:"password" validate:"required|password"` +} diff --git a/internal/route/http.go b/internal/route/http.go index 49831ff1..b4b21103 100644 --- a/internal/route/http.go +++ b/internal/route/http.go @@ -48,6 +48,7 @@ type Http struct { systemctl *service.SystemctlService toolboxSystem *service.ToolboxSystemService toolboxBenchmark *service.ToolboxBenchmarkService + toolboxSSH *service.ToolboxSSHService webhook *service.WebHookService apps *apploader.Loader } @@ -85,6 +86,7 @@ func NewHttp( systemctl *service.SystemctlService, toolboxSystem *service.ToolboxSystemService, toolboxBenchmark *service.ToolboxBenchmarkService, + toolboxSSH *service.ToolboxSSHService, webhook *service.WebHookService, apps *apploader.Loader, ) *Http { @@ -121,6 +123,7 @@ func NewHttp( systemctl: systemctl, toolboxSystem: toolboxSystem, toolboxBenchmark: toolboxBenchmark, + toolboxSSH: toolboxSSH, webhook: webhook, apps: apps, } @@ -438,13 +441,26 @@ func (route *Http) Register(r *chi.Mux) { r.Post("/hostname", route.toolboxSystem.UpdateHostname) r.Get("/hosts", route.toolboxSystem.GetHosts) r.Post("/hosts", route.toolboxSystem.UpdateHosts) - r.Post("/root_password", route.toolboxSystem.UpdateRootPassword) }) r.Route("/toolbox_benchmark", func(r chi.Router) { r.Post("/test", route.toolboxBenchmark.Test) }) + r.Route("/toolbox_ssh", func(r chi.Router) { + r.Get("/info", route.toolboxSSH.GetInfo) + r.Post("/start", route.toolboxSSH.Start) + r.Post("/stop", route.toolboxSSH.Stop) + r.Post("/restart", route.toolboxSSH.Restart) + r.Post("/port", route.toolboxSSH.UpdatePort) + r.Post("/password_auth", route.toolboxSSH.UpdatePasswordAuth) + r.Post("/pubkey_auth", route.toolboxSSH.UpdatePubKeyAuth) + r.Post("/root_login", route.toolboxSSH.UpdateRootLogin) + r.Post("/root_password", route.toolboxSSH.UpdateRootPassword) + r.Get("/root_key", route.toolboxSSH.GetRootKey) + r.Post("/root_key", route.toolboxSSH.GenerateRootKey) + }) + r.Route("/webhook", func(r chi.Router) { r.Get("/", route.webhook.List) r.Post("/", route.webhook.Create) diff --git a/internal/service/service.go b/internal/service/service.go index 50ac3f25..756e74c9 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -37,5 +37,6 @@ var ProviderSet = wire.NewSet( NewWebsiteService, NewToolboxSystemService, NewToolboxBenchmarkService, + NewToolboxSSHService, NewWsService, ) diff --git a/internal/service/toolbox_ssh.go b/internal/service/toolbox_ssh.go new file mode 100644 index 00000000..7440d7c5 --- /dev/null +++ b/internal/service/toolbox_ssh.go @@ -0,0 +1,339 @@ +package service + +import ( + "net/http" + "regexp" + "strings" + + "github.com/leonelquinteros/gotext" + "github.com/libtnb/chix" + "github.com/spf13/cast" + + "github.com/acepanel/panel/internal/http/request" + "github.com/acepanel/panel/pkg/io" + "github.com/acepanel/panel/pkg/shell" + "github.com/acepanel/panel/pkg/systemctl" +) + +type ToolboxSSHService struct { + t *gotext.Locale +} + +func NewToolboxSSHService(t *gotext.Locale) *ToolboxSSHService { + return &ToolboxSSHService{ + t: t, + } +} + +// GetInfo 获取 SSH 信息 +func (s *ToolboxSSHService) GetInfo(w http.ResponseWriter, r *http.Request) { + // 读取 sshd_config + sshdConfig, err := io.Read("/etc/ssh/sshd_config") + if err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to read sshd_config: %v", err)) + return + } + + // 获取 SSH 服务状态 + status, err := systemctl.Status("sshd") + if err != nil { + // 尝试 ssh 服务名 + status, err = systemctl.Status("ssh") + if err != nil { + status = false + } + } + + // 解析端口 + port := 22 + portMatch := regexp.MustCompile(`(?m)^Port\s+(\d+)`).FindStringSubmatch(sshdConfig) + if len(portMatch) >= 2 { + port = cast.ToInt(portMatch[1]) + } + + // 解析密码认证 + passwordAuth := true + passwordAuthMatch := regexp.MustCompile(`(?m)^PasswordAuthentication\s+(\S+)`).FindStringSubmatch(sshdConfig) + if len(passwordAuthMatch) >= 2 { + passwordAuth = strings.ToLower(passwordAuthMatch[1]) == "yes" + } + + // 解析密钥认证 + pubKeyAuth := true + pubKeyAuthMatch := regexp.MustCompile(`(?m)^PubkeyAuthentication\s+(\S+)`).FindStringSubmatch(sshdConfig) + if len(pubKeyAuthMatch) >= 2 { + pubKeyAuth = strings.ToLower(pubKeyAuthMatch[1]) == "yes" + } + + // 解析 Root 登录设置 + rootLogin := "yes" + rootLoginMatch := regexp.MustCompile(`(?m)^PermitRootLogin\s+(\S+)`).FindStringSubmatch(sshdConfig) + if len(rootLoginMatch) >= 2 { + rootLogin = strings.ToLower(rootLoginMatch[1]) + } + + Success(w, chix.M{ + "status": status, + "port": port, + "password_auth": passwordAuth, + "pubkey_auth": pubKeyAuth, + "root_login": rootLogin, + }) +} + +// Start 启动 SSH 服务 +func (s *ToolboxSSHService) Start(w http.ResponseWriter, r *http.Request) { + err := systemctl.Start("sshd") + if err != nil { + err = systemctl.Start("ssh") + if err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to start SSH service: %v", err)) + return + } + } + Success(w, nil) +} + +// Stop 停止 SSH 服务 +func (s *ToolboxSSHService) Stop(w http.ResponseWriter, r *http.Request) { + err := systemctl.Stop("sshd") + if err != nil { + err = systemctl.Stop("ssh") + if err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to stop SSH service: %v", err)) + return + } + } + Success(w, nil) +} + +// Restart 重启 SSH 服务 +func (s *ToolboxSSHService) Restart(w http.ResponseWriter, r *http.Request) { + err := systemctl.Restart("sshd") + if err != nil { + err = systemctl.Restart("ssh") + if err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to restart SSH service: %v", err)) + return + } + } + Success(w, nil) +} + +// UpdatePort 修改 SSH 端口 +func (s *ToolboxSSHService) UpdatePort(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ToolboxSSHPort](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + if err = s.updateSSHConfig("Port", cast.ToString(req.Port)); err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to update SSH port: %v", err)) + return + } + + // 重启 SSH 服务 + if err = s.restartSSH(); err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to restart SSH service: %v", err)) + return + } + + Success(w, nil) +} + +// UpdatePasswordAuth 设置密码认证 +func (s *ToolboxSSHService) UpdatePasswordAuth(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ToolboxSSHPasswordAuth](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + value := "no" + if req.Enabled { + value = "yes" + } + + if err = s.updateSSHConfig("PasswordAuthentication", value); err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to update password authentication: %v", err)) + return + } + + if err = s.restartSSH(); err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to restart SSH service: %v", err)) + return + } + + Success(w, nil) +} + +// UpdatePubKeyAuth 设置密钥认证 +func (s *ToolboxSSHService) UpdatePubKeyAuth(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ToolboxSSHPubKeyAuth](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + value := "no" + if req.Enabled { + value = "yes" + } + + if err = s.updateSSHConfig("PubkeyAuthentication", value); err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to update pubkey authentication: %v", err)) + return + } + + if err = s.restartSSH(); err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to restart SSH service: %v", err)) + return + } + + Success(w, nil) +} + +// UpdateRootLogin 设置 Root 登录 +func (s *ToolboxSSHService) UpdateRootLogin(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ToolboxSSHRootLogin](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + if err = s.updateSSHConfig("PermitRootLogin", req.Mode); err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to update root login setting: %v", err)) + return + } + + if err = s.restartSSH(); err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to restart SSH service: %v", err)) + return + } + + Success(w, nil) +} + +// UpdateRootPassword 修改 Root 密码 +func (s *ToolboxSSHService) UpdateRootPassword(w http.ResponseWriter, r *http.Request) { + req, err := Bind[request.ToolboxSSHRootPassword](r) + if err != nil { + Error(w, http.StatusUnprocessableEntity, "%v", err) + return + } + + password := strings.ReplaceAll(req.Password, `'`, `\'`) + if _, err = shell.Execf(`yes '%s' | passwd root`, password); err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to update root password: %v", err)) + return + } + + Success(w, nil) +} + +// GetRootKey 获取 Root 私钥 +func (s *ToolboxSSHService) GetRootKey(w http.ResponseWriter, r *http.Request) { + var privateKey string + + // 优先尝试 ed25519 密钥 + if io.Exists("/root/.ssh/id_ed25519") { + privateKey, _ = io.Read("/root/.ssh/id_ed25519") + } else if io.Exists("/root/.ssh/id_rsa") { + privateKey, _ = io.Read("/root/.ssh/id_rsa") + } + + Success(w, strings.TrimSpace(privateKey)) +} + +// GenerateRootKey 生成 Root 密钥对 +func (s *ToolboxSSHService) GenerateRootKey(w http.ResponseWriter, r *http.Request) { + // 确保 .ssh 目录存在 + if _, err := shell.Execf("mkdir -p /root/.ssh && chmod 700 /root/.ssh"); err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to create .ssh directory: %v", err)) + return + } + + // 优先生成 ED25519 密钥对 + keyType := "ed25519" + if _, err := shell.Execf(`yes 'y' | ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 -N ""`); err != nil { + // 不行再生成 RSA 密钥 + keyType = "rsa" + if _, err = shell.Execf(`yes 'y' | ssh-keygen -t rsa -b 4096 -f /root/.ssh/id_rsa -N ""`); err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to generate SSH key: %v", err)) + return + } + } + + // 读取生成的密钥 + var pubKey, privateKey string + var err error + if keyType == "ed25519" { + pubKey, err = io.Read("/root/.ssh/id_ed25519.pub") + if err == nil { + privateKey, _ = io.Read("/root/.ssh/id_ed25519") + } + } else { + pubKey, err = io.Read("/root/.ssh/id_rsa.pub") + if err == nil { + privateKey, _ = io.Read("/root/.ssh/id_rsa") + } + } + + if err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to read generated key: %v", err)) + return + } + + // 将公钥添加到 authorized_keys + pubKey = strings.TrimSpace(pubKey) + privateKey = strings.TrimSpace(privateKey) + authorizedKeysPath := "/root/.ssh/authorized_keys" + authorizedKeys, _ := io.Read(authorizedKeysPath) + + // 检查公钥是否已存在 + if !strings.Contains(authorizedKeys, pubKey) { + if authorizedKeys != "" && !strings.HasSuffix(authorizedKeys, "\n") { + authorizedKeys += "\n" + } + authorizedKeys += pubKey + "\n" + if err = io.Write(authorizedKeysPath, authorizedKeys, 0600); err != nil { + Error(w, http.StatusInternalServerError, s.t.Get("failed to update authorized_keys: %v", err)) + return + } + } + + _ = s.restartSSH() + + Success(w, privateKey) +} + +// updateSSHConfig 更新 SSH 配置项 +func (s *ToolboxSSHService) updateSSHConfig(key, value string) error { + sshdConfig, err := io.Read("/etc/ssh/sshd_config") + if err != nil { + return err + } + + // 检查配置项是否存在(包括注释的) + configRegex := regexp.MustCompile(`(?m)^#?\s*` + key + `\s+.*$`) + + if configRegex.MatchString(sshdConfig) { + // 替换现有配置 + sshdConfig = configRegex.ReplaceAllString(sshdConfig, key+" "+value) + } else { + // 添加新配置 + sshdConfig = sshdConfig + "\n" + key + " " + value + "\n" + } + + return io.Write("/etc/ssh/sshd_config", sshdConfig, 0600) +} + +// restartSSH 重启 SSH 服务 +func (s *ToolboxSSHService) restartSSH() error { + err := systemctl.Restart("sshd") + if err != nil { + err = systemctl.Restart("ssh") + } + return err +} diff --git a/internal/service/toolbox_system.go b/internal/service/toolbox_system.go index f50ad006..db96e5e6 100644 --- a/internal/service/toolbox_system.go +++ b/internal/service/toolbox_system.go @@ -305,20 +305,3 @@ func (s *ToolboxSystemService) UpdateHosts(w http.ResponseWriter, r *http.Reques Success(w, nil) } - -// UpdateRootPassword 设置 root 密码 -func (s *ToolboxSystemService) UpdateRootPassword(w http.ResponseWriter, r *http.Request) { - req, err := Bind[request.ToolboxSystemPassword](r) - if err != nil { - Error(w, http.StatusUnprocessableEntity, "%v", err) - return - } - - req.Password = strings.ReplaceAll(req.Password, `'`, `\'`) - if _, err = shell.Execf(`yes '%s' | passwd root`, req.Password); err != nil { - Error(w, http.StatusInternalServerError, "%v", s.t.Get("failed to set root password: %v", err)) - return - } - - Success(w, nil) -} diff --git a/web/src/api/panel/toolbox-ssh/index.ts b/web/src/api/panel/toolbox-ssh/index.ts new file mode 100644 index 00000000..7db5fae3 --- /dev/null +++ b/web/src/api/panel/toolbox-ssh/index.ts @@ -0,0 +1,28 @@ +import { http } from '@/utils' + +export default { + // 获取 SSH 信息 + info: (): any => http.Get('/toolbox_ssh/info'), + // 启动 SSH 服务 + start: (): any => http.Post('/toolbox_ssh/start'), + // 停止 SSH 服务 + stop: (): any => http.Post('/toolbox_ssh/stop'), + // 重启 SSH 服务 + restart: (): any => http.Post('/toolbox_ssh/restart'), + // 设置 SSH 端口 + updatePort: (port: number): any => http.Post('/toolbox_ssh/port', { port }), + // 设置密码认证 + updatePasswordAuth: (enabled: boolean): any => + http.Post('/toolbox_ssh/password_auth', { enabled }), + // 设置密钥认证 + updatePubkeyAuth: (enabled: boolean): any => http.Post('/toolbox_ssh/pubkey_auth', { enabled }), + // 设置 Root 登录 + updateRootLogin: (mode: string): any => http.Post('/toolbox_ssh/root_login', { mode }), + // 设置 Root 密码 + updateRootPassword: (password: string): any => + http.Post('/toolbox_ssh/root_password', { password }), + // 获取 Root 公钥 + rootKey: (): any => http.Get('/toolbox_ssh/root_key'), + // 生成 Root 密钥对 + generateRootKey: (): any => http.Post('/toolbox_ssh/root_key') +} diff --git a/web/src/views/toolbox/IndexView.vue b/web/src/views/toolbox/IndexView.vue index 806d6635..ca016a10 100644 --- a/web/src/views/toolbox/IndexView.vue +++ b/web/src/views/toolbox/IndexView.vue @@ -5,6 +5,7 @@ defineOptions({ import BenchmarkView from '@/views/toolbox/BenchmarkView.vue' import ProcessView from '@/views/toolbox/ProcessView.vue' +import SSHView from '@/views/toolbox/SSHView.vue' import SystemView from '@/views/toolbox/SystemView.vue' import WebHookView from '@/views/toolbox/WebHookView.vue' import { useGettext } from 'vue3-gettext' @@ -19,6 +20,7 @@ const current = ref('process') + @@ -26,6 +28,7 @@ const current = ref('process') + diff --git a/web/src/views/toolbox/SSHView.vue b/web/src/views/toolbox/SSHView.vue new file mode 100644 index 00000000..276c3a52 --- /dev/null +++ b/web/src/views/toolbox/SSHView.vue @@ -0,0 +1,393 @@ + + + + + diff --git a/web/src/views/toolbox/SystemView.vue b/web/src/views/toolbox/SystemView.vue index d29c28ea..cb07f7a1 100644 --- a/web/src/views/toolbox/SystemView.vue +++ b/web/src/views/toolbox/SystemView.vue @@ -21,7 +21,6 @@ const hosts = ref('') const timezone = ref('') const timezones = ref([]) const time = ref(DateTime.now().toMillis()) -const rootPassword = ref('') useRequest(system.dns()).onSuccess(({ data }) => { dns1.value = data[0] @@ -65,12 +64,6 @@ const handleUpdateHost = async () => { }) } -const handleUpdateRootPassword = () => { - useRequest(system.updateRootPassword(rootPassword.value)).onSuccess(() => { - window.$message.success($gettext('Saved successfully')) - }) -} - const handleUpdateTime = async () => { await Promise.all([ useRequest(system.updateTime(String(DateTime.fromMillis(time.value).toISO()))), @@ -180,15 +173,5 @@ const handleSyncTime = () => { - - - - - - - - {{ $gettext('Save') }} - -