2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 16:10:59 +08:00
Files
panel/app/http/controllers/ssh_controller.go
2023-11-11 03:19:06 +08:00

165 lines
4.1 KiB
Go

package controllers
import (
"bytes"
"context"
"sync"
"time"
"github.com/fasthttp/websocket"
"github.com/goravel/fiber"
"github.com/goravel/framework/contracts/http"
"github.com/goravel/framework/facades"
"github.com/spf13/cast"
"github.com/valyala/fasthttp"
"panel/app/models"
"panel/app/services"
"panel/pkg/ssh"
)
type SshController struct {
AuthMethod ssh.AuthMethod
setting services.Setting
}
func NewSshController() *SshController {
return &SshController{
AuthMethod: ssh.PASSWORD,
setting: services.NewSettingImpl(),
}
}
// GetInfo 获取 SSH 配置
func (r *SshController) GetInfo(ctx http.Context) http.Response {
host := r.setting.Get(models.SettingKeySshHost)
port := r.setting.Get(models.SettingKeySshPort)
user := r.setting.Get(models.SettingKeySshUser)
password := r.setting.Get(models.SettingKeySshPassword)
if len(host) == 0 || len(user) == 0 || len(password) == 0 {
return Error(ctx, http.StatusInternalServerError, "SSH 配置不完整")
}
return Success(ctx, http.Json{
"host": host,
"port": cast.ToInt(port),
"user": user,
"password": password,
})
}
// UpdateInfo 更新 SSH 配置
func (r *SshController) UpdateInfo(ctx http.Context) http.Response {
validator, err := ctx.Request().Validate(map[string]string{
"host": "required",
"port": "required",
"user": "required",
"password": "required",
})
if err != nil {
return Error(ctx, http.StatusUnprocessableEntity, err.Error())
}
if validator.Fails() {
return Error(ctx, http.StatusUnprocessableEntity, validator.Errors().One())
}
host := ctx.Request().Input("host")
port := ctx.Request().Input("port")
user := ctx.Request().Input("user")
password := ctx.Request().Input("password")
err = r.setting.Set(models.SettingKeySshHost, host)
if err != nil {
facades.Log().Info("[面板][SSH] 更新配置失败 ", err)
return ErrorSystem(ctx)
}
err = r.setting.Set(models.SettingKeySshPort, port)
if err != nil {
facades.Log().Info("[面板][SSH] 更新配置失败 ", err)
return ErrorSystem(ctx)
}
err = r.setting.Set(models.SettingKeySshUser, user)
if err != nil {
facades.Log().Info("[面板][SSH] 更新配置失败 ", err)
return ErrorSystem(ctx)
}
err = r.setting.Set(models.SettingKeySshPassword, password)
if err != nil {
facades.Log().Info("[面板][SSH] 更新配置失败 ", err)
return ErrorSystem(ctx)
}
return Success(ctx, nil)
}
// Session SSH 会话
func (r *SshController) Session(ctx http.Context) http.Response {
upGrader := websocket.FastHTTPUpgrader{
ReadBufferSize: 4096,
WriteBufferSize: 4096,
CheckOrigin: func(ctx *fasthttp.RequestCtx) bool {
return true
},
Subprotocols: []string{ctx.Request().Header("Sec-WebSocket-Protocol")},
}
config := ssh.SSHClientConfigPassword(
r.setting.Get(models.SettingKeySshHost)+":"+r.setting.Get(models.SettingKeySshPort),
r.setting.Get(models.SettingKeySshUser),
r.setting.Get(models.SettingKeySshPassword),
)
client, err := ssh.NewSSHClient(config)
err = upGrader.Upgrade(ctx.(*fiber.Context).Instance().Context(), func(conn *websocket.Conn) {
if err != nil {
_ = conn.WriteControl(websocket.CloseMessage,
[]byte(err.Error()), time.Now().Add(time.Second))
return
}
defer client.Close()
turn, err := ssh.NewTurn(conn, client)
if err != nil {
_ = conn.WriteControl(websocket.CloseMessage,
[]byte(err.Error()), time.Now().Add(time.Second))
return
}
defer turn.Close()
var bufPool = sync.Pool{
New: func() any {
return new(bytes.Buffer)
},
}
var logBuff = bufPool.Get().(*bytes.Buffer)
logBuff.Reset()
defer bufPool.Put(logBuff)
ctx2, cancel := context.WithCancel(context.Background())
wg := sync.WaitGroup{}
wg.Add(2)
go func() {
defer wg.Done()
err := turn.LoopRead(logBuff, ctx2)
if err != nil {
facades.Log().Info("[面板][SSH] 读取数据失败 ", err.Error())
}
}()
go func() {
defer wg.Done()
err := turn.SessionWait()
if err != nil {
facades.Log().Info("[面板][SSH] 会话失败 ", err.Error())
}
cancel()
}()
wg.Wait()
})
if err != nil {
facades.Log().Info("[面板][SSH] 建立连接失败 ", err)
return ErrorSystem(ctx)
}
return nil
}