mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-10-06 13:27:43 +08:00
fix(container): Fix cpu surge when the container terminal executes exit (#8096)
Some checks failed
SonarCloud Scan / SonarCloud (push) Failing after -19s
Some checks failed
SonarCloud Scan / SonarCloud (push) Failing after -19s
Refs #7574
This commit is contained in:
parent
766beae4e1
commit
402b41100b
19 changed files with 92 additions and 256 deletions
|
@ -10,7 +10,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/service"
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"github.com/1Panel-dev/1Panel/backend/global"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
"github.com/1Panel-dev/1Panel/backend/utils/cmd"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/copier"
|
"github.com/1Panel-dev/1Panel/backend/utils/copier"
|
||||||
|
@ -74,7 +73,7 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
|
||||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||||
|
@ -96,193 +95,105 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
source := c.Query("source")
|
||||||
|
var containerID string
|
||||||
|
var initCmd string
|
||||||
|
switch source {
|
||||||
|
case "redis":
|
||||||
|
containerID, initCmd, err = loadRedisInitCmd(c)
|
||||||
|
case "ollama":
|
||||||
|
containerID, initCmd, err = loadOllamaInitCmd(c)
|
||||||
|
case "container":
|
||||||
|
containerID, initCmd, err = loadContainerInitCmd(c)
|
||||||
|
default:
|
||||||
|
if wshandleError(wsConn, fmt.Errorf("not support such source %s", source)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if wshandleError(wsConn, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pidMap := loadMapFromDockerTop(containerID)
|
||||||
|
slave, err := terminal.NewCommand("clear && " + initCmd)
|
||||||
|
if wshandleError(wsConn, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer killBash(containerID, strings.ReplaceAll(initCmd, fmt.Sprintf("docker exec -it %s ", containerID), ""), pidMap)
|
||||||
|
defer slave.Close()
|
||||||
|
|
||||||
|
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
||||||
|
if wshandleError(wsConn, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
quitChan := make(chan bool, 3)
|
||||||
|
tty.Start(quitChan)
|
||||||
|
go slave.Wait(quitChan)
|
||||||
|
|
||||||
|
<-quitChan
|
||||||
|
|
||||||
|
global.LOG.Info("websocket finished")
|
||||||
|
if wshandleError(wsConn, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadRedisInitCmd(c *gin.Context) (string, string, error) {
|
||||||
name := c.Query("name")
|
name := c.Query("name")
|
||||||
from := c.Query("from")
|
from := c.Query("from")
|
||||||
commands := []string{"redis-cli"}
|
commands := "redis-cli"
|
||||||
database, err := databaseService.Get(name)
|
database, err := databaseService.Get(name)
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "no such database in db")) {
|
if err != nil {
|
||||||
return
|
return "", "", fmt.Errorf("no such database in db, err: %v", err)
|
||||||
}
|
}
|
||||||
if from == "local" {
|
if from == "local" {
|
||||||
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: "redis"})
|
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: "redis"})
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "no such database in db")) {
|
if err != nil {
|
||||||
return
|
return "", "", fmt.Errorf("no such app in db, err: %v", err)
|
||||||
}
|
}
|
||||||
name = redisInfo.ContainerName
|
name = redisInfo.ContainerName
|
||||||
if len(database.Password) != 0 {
|
if len(database.Password) != 0 {
|
||||||
commands = []string{"redis-cli", "-a", database.Password, "--no-auth-warning"}
|
commands = "redis-cli -a " + database.Password + " --no-auth-warning"
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
itemPort := fmt.Sprintf("%v", database.Port)
|
commands = fmt.Sprintf("redis-cli -h %s -p %v", database.Address, database.Port)
|
||||||
commands = []string{"redis-cli", "-h", database.Address, "-p", itemPort}
|
|
||||||
if len(database.Password) != 0 {
|
if len(database.Password) != 0 {
|
||||||
commands = []string{"redis-cli", "-h", database.Address, "-p", itemPort, "-a", database.Password, "--no-auth-warning"}
|
commands = fmt.Sprintf("redis-cli -h %s -p %v -a %s --no-auth-warning", database.Address, database.Port, database.Password)
|
||||||
}
|
}
|
||||||
name = "1Panel-redis-cli-tools"
|
name = "1Panel-redis-cli-tools"
|
||||||
}
|
}
|
||||||
|
return name, fmt.Sprintf("docker exec -it %s %s", name, commands), nil
|
||||||
pidMap := loadMapFromDockerTop(name)
|
|
||||||
itemCmds := append([]string{"exec", "-it", name}, commands...)
|
|
||||||
slave, err := terminal.NewCommand(itemCmds)
|
|
||||||
if wshandleError(wsConn, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer killBash(name, strings.Join(commands, " "), pidMap)
|
|
||||||
defer slave.Close()
|
|
||||||
|
|
||||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
|
||||||
if wshandleError(wsConn, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
quitChan := make(chan bool, 3)
|
|
||||||
tty.Start(quitChan)
|
|
||||||
go slave.Wait(quitChan)
|
|
||||||
|
|
||||||
<-quitChan
|
|
||||||
|
|
||||||
global.LOG.Info("websocket finished")
|
|
||||||
if wshandleError(wsConn, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) OllamaWsSsh(c *gin.Context) {
|
func loadOllamaInitCmd(c *gin.Context) (string, string, error) {
|
||||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer wsConn.Close()
|
|
||||||
|
|
||||||
if global.CONF.System.IsDemo {
|
|
||||||
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
name := c.Query("name")
|
name := c.Query("name")
|
||||||
if cmd.CheckIllegal(name) {
|
if cmd.CheckIllegal(name) {
|
||||||
if wshandleError(wsConn, errors.New(" The command contains illegal characters.")) {
|
return "", "", fmt.Errorf("ollama model %s contains illegal characters", name)
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
container, err := service.LoadContainerName()
|
ollamaInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: "", Type: "ollama"})
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, " load container name for ollama failed")) {
|
if err != nil {
|
||||||
return
|
return "", "", fmt.Errorf("no such app in db, err: %v", err)
|
||||||
}
|
|
||||||
commands := []string{"ollama", "run", name}
|
|
||||||
|
|
||||||
pidMap := loadMapFromDockerTop(container)
|
|
||||||
fmt.Println("pidMap")
|
|
||||||
for k, v := range pidMap {
|
|
||||||
fmt.Println(k, v)
|
|
||||||
}
|
|
||||||
itemCmds := append([]string{"exec", "-it", container}, commands...)
|
|
||||||
slave, err := terminal.NewCommand(itemCmds)
|
|
||||||
if wshandleError(wsConn, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer killBash(container, strings.Join(commands, " "), pidMap)
|
|
||||||
defer slave.Close()
|
|
||||||
|
|
||||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
|
||||||
if wshandleError(wsConn, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
quitChan := make(chan bool, 3)
|
|
||||||
tty.Start(quitChan)
|
|
||||||
go slave.Wait(quitChan)
|
|
||||||
|
|
||||||
<-quitChan
|
|
||||||
|
|
||||||
global.LOG.Info("websocket finished")
|
|
||||||
if wshandleError(wsConn, err) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
containerName := ollamaInfo.ContainerName
|
||||||
|
return containerName, fmt.Sprintf("docker exec -it %s ollama run %s", containerName, name), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) ContainerWsSsh(c *gin.Context) {
|
func loadContainerInitCmd(c *gin.Context) (string, string, error) {
|
||||||
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
|
||||||
if err != nil {
|
|
||||||
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer wsConn.Close()
|
|
||||||
|
|
||||||
if global.CONF.System.IsDemo {
|
|
||||||
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
containerID := c.Query("containerid")
|
containerID := c.Query("containerid")
|
||||||
command := c.Query("command")
|
command := c.Query("command")
|
||||||
user := c.Query("user")
|
user := c.Query("user")
|
||||||
if len(command) == 0 || len(containerID) == 0 {
|
|
||||||
if wshandleError(wsConn, errors.New("error param of command or containerID")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cmds := []string{"exec", containerID, command}
|
|
||||||
if len(user) != 0 {
|
|
||||||
cmds = []string{"exec", "-u", user, containerID, command}
|
|
||||||
}
|
|
||||||
if cmd.CheckIllegal(user, containerID, command) {
|
if cmd.CheckIllegal(user, containerID, command) {
|
||||||
if wshandleError(wsConn, errors.New(" The command contains illegal characters.")) {
|
return "", "", fmt.Errorf("the command contains illegal characters. command: %s, user: %s, containerID: %s", command, user, containerID)
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
stdout, err := cmd.ExecWithCheck("docker", cmds...)
|
if len(command) == 0 || len(containerID) == 0 {
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, stdout)) {
|
return "", "", fmt.Errorf("error param of command: %s or containerID: %s", command, containerID)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
command = fmt.Sprintf("docker exec -it %s %s", containerID, command)
|
||||||
commands := []string{"exec", "-it", containerID, command}
|
|
||||||
if len(user) != 0 {
|
if len(user) != 0 {
|
||||||
commands = []string{"exec", "-it", "-u", user, containerID, command}
|
command = fmt.Sprintf("docker exec -it -u %s %s %s", user, containerID, command)
|
||||||
}
|
|
||||||
pidMap := loadMapFromDockerTop(containerID)
|
|
||||||
slave, err := terminal.NewCommand(commands)
|
|
||||||
if wshandleError(wsConn, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer killBash(containerID, command, pidMap)
|
|
||||||
defer slave.Close()
|
|
||||||
|
|
||||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, true)
|
|
||||||
if wshandleError(wsConn, err) {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
quitChan := make(chan bool, 3)
|
return containerID, command, nil
|
||||||
tty.Start(quitChan)
|
|
||||||
go slave.Wait(quitChan)
|
|
||||||
|
|
||||||
<-quitChan
|
|
||||||
|
|
||||||
global.LOG.Info("websocket finished")
|
|
||||||
if wshandleError(wsConn, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func wshandleError(ws *websocket.Conn, err error) bool {
|
func wshandleError(ws *websocket.Conn, err error) bool {
|
||||||
|
|
|
@ -15,7 +15,6 @@ func (a *AIToolsRouter) InitRouter(Router *gin.RouterGroup) {
|
||||||
|
|
||||||
baseApi := v1.ApiGroupApp.BaseApi
|
baseApi := v1.ApiGroupApp.BaseApi
|
||||||
{
|
{
|
||||||
aiToolsRouter.GET("/ollama/exec", baseApi.OllamaWsSsh)
|
|
||||||
aiToolsRouter.POST("/ollama/close", baseApi.CloseOllamaModel)
|
aiToolsRouter.POST("/ollama/close", baseApi.CloseOllamaModel)
|
||||||
aiToolsRouter.POST("/ollama/model", baseApi.CreateOllamaModel)
|
aiToolsRouter.POST("/ollama/model", baseApi.CreateOllamaModel)
|
||||||
aiToolsRouter.POST("/ollama/model/recreate", baseApi.RecreateOllamaModel)
|
aiToolsRouter.POST("/ollama/model/recreate", baseApi.RecreateOllamaModel)
|
||||||
|
|
|
@ -15,7 +15,7 @@ func (s *ContainerRouter) InitRouter(Router *gin.RouterGroup) {
|
||||||
Use(middleware.PasswordExpired())
|
Use(middleware.PasswordExpired())
|
||||||
baseApi := v1.ApiGroupApp.BaseApi
|
baseApi := v1.ApiGroupApp.BaseApi
|
||||||
{
|
{
|
||||||
baRouter.GET("/exec", baseApi.ContainerWsSsh)
|
baRouter.GET("/exec", baseApi.ContainerWsSSH)
|
||||||
baRouter.GET("/stats/:id", baseApi.ContainerStats)
|
baRouter.GET("/stats/:id", baseApi.ContainerStats)
|
||||||
|
|
||||||
baRouter.POST("", baseApi.ContainerCreate)
|
baRouter.POST("", baseApi.ContainerCreate)
|
||||||
|
|
|
@ -38,7 +38,6 @@ func (s *DatabaseRouter) InitRouter(Router *gin.RouterGroup) {
|
||||||
cmdRouter.POST("/redis/persistence/conf", baseApi.LoadPersistenceConf)
|
cmdRouter.POST("/redis/persistence/conf", baseApi.LoadPersistenceConf)
|
||||||
cmdRouter.POST("/redis/status", baseApi.LoadRedisStatus)
|
cmdRouter.POST("/redis/status", baseApi.LoadRedisStatus)
|
||||||
cmdRouter.POST("/redis/conf", baseApi.LoadRedisConf)
|
cmdRouter.POST("/redis/conf", baseApi.LoadRedisConf)
|
||||||
cmdRouter.GET("/redis/exec", baseApi.RedisWsSsh)
|
|
||||||
cmdRouter.GET("/redis/check", baseApi.CheckHasCli)
|
cmdRouter.GET("/redis/check", baseApi.CheckHasCli)
|
||||||
cmdRouter.POST("/redis/install/cli", baseApi.InstallCli)
|
cmdRouter.POST("/redis/install/cli", baseApi.InstallCli)
|
||||||
cmdRouter.POST("/redis/password", baseApi.ChangeRedisPassword)
|
cmdRouter.POST("/redis/password", baseApi.ChangeRedisPassword)
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
package ssh
|
package ssh
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
gossh "golang.org/x/crypto/ssh"
|
gossh "golang.org/x/crypto/ssh"
|
||||||
|
@ -82,58 +79,6 @@ func (c *ConnInfo) Close() {
|
||||||
_ = c.Client.Close()
|
_ = c.Client.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
type SshConn struct {
|
|
||||||
StdinPipe io.WriteCloser
|
|
||||||
ComboOutput *wsBufferWriter
|
|
||||||
Session *gossh.Session
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *ConnInfo) NewSshConn(cols, rows int) (*SshConn, error) {
|
|
||||||
sshSession, err := c.Client.NewSession()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
stdinP, err := sshSession.StdinPipe()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
comboWriter := new(wsBufferWriter)
|
|
||||||
sshSession.Stdout = comboWriter
|
|
||||||
sshSession.Stderr = comboWriter
|
|
||||||
|
|
||||||
modes := gossh.TerminalModes{
|
|
||||||
gossh.ECHO: 1,
|
|
||||||
gossh.TTY_OP_ISPEED: 14400,
|
|
||||||
gossh.TTY_OP_OSPEED: 14400,
|
|
||||||
}
|
|
||||||
if err := sshSession.RequestPty("xterm", rows, cols, modes); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if err := sshSession.Shell(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &SshConn{StdinPipe: stdinP, ComboOutput: comboWriter, Session: sshSession}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SshConn) Close() {
|
|
||||||
if s.Session != nil {
|
|
||||||
s.Session.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type wsBufferWriter struct {
|
|
||||||
buffer bytes.Buffer
|
|
||||||
mu sync.Mutex
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *wsBufferWriter) Write(p []byte) (int, error) {
|
|
||||||
w.mu.Lock()
|
|
||||||
defer w.mu.Unlock()
|
|
||||||
return w.buffer.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func makePrivateKeySigner(privateKey []byte, passPhrase []byte) (gossh.Signer, error) {
|
func makePrivateKeySigner(privateKey []byte, passPhrase []byte) (gossh.Signer, error) {
|
||||||
if len(passPhrase) != 0 {
|
if len(passPhrase) != 0 {
|
||||||
return gossh.ParsePrivateKeyWithPassphrase(privateKey, passPhrase)
|
return gossh.ParsePrivateKeyWithPassphrase(privateKey, passPhrase)
|
||||||
|
|
|
@ -25,14 +25,24 @@ type LocalCommand struct {
|
||||||
pty *os.File
|
pty *os.File
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCommand(commands []string) (*LocalCommand, error) {
|
func NewCommand(initCmd string) (*LocalCommand, error) {
|
||||||
cmd := exec.Command("docker", commands...)
|
cmd := exec.Command("bash")
|
||||||
|
if term := os.Getenv("TERM"); term != "" {
|
||||||
|
cmd.Env = append(os.Environ(), "TERM="+term)
|
||||||
|
} else {
|
||||||
|
cmd.Env = append(os.Environ(), "TERM=xterm")
|
||||||
|
}
|
||||||
|
|
||||||
pty, err := pty.Start(cmd)
|
pty, err := pty.Start(cmd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "failed to start command")
|
return nil, errors.Wrapf(err, "failed to start command")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(initCmd) != 0 {
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
_, _ = pty.Write([]byte(initCmd + "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
lcmd := &LocalCommand{
|
lcmd := &LocalCommand{
|
||||||
closeSignal: DefaultCloseSignal,
|
closeSignal: DefaultCloseSignal,
|
||||||
closeTimeout: DefaultCloseTimeout,
|
closeTimeout: DefaultCloseTimeout,
|
||||||
|
|
|
@ -123,8 +123,6 @@ const message = {
|
||||||
},
|
},
|
||||||
msg: {
|
msg: {
|
||||||
noneData: 'No data available',
|
noneData: 'No data available',
|
||||||
disConn:
|
|
||||||
'Please click the disconnect button directly to terminate the terminal connection, avoiding the use of exit commands like {0}.',
|
|
||||||
delete: `This operation delete can't be undone. Do you want to continue?`,
|
delete: `This operation delete can't be undone. Do you want to continue?`,
|
||||||
clean: `This operation clean can't be undone. Do you want to continue?`,
|
clean: `This operation clean can't be undone. Do you want to continue?`,
|
||||||
deleteTitle: 'Delete',
|
deleteTitle: 'Delete',
|
||||||
|
|
|
@ -122,8 +122,6 @@ const message = {
|
||||||
},
|
},
|
||||||
msg: {
|
msg: {
|
||||||
noneData: '利用可能なデータはありません',
|
noneData: '利用可能なデータはありません',
|
||||||
disConn:
|
|
||||||
'端末接続を切断するには、{0} のような終了コマンドを使用せずに、直接切断ボタンをクリックしてください',
|
|
||||||
delete: `この操作削除は元に戻すことはできません。続けたいですか?`,
|
delete: `この操作削除は元に戻すことはできません。続けたいですか?`,
|
||||||
clean: `この操作は取り消すことはできません。続けたいですか?`,
|
clean: `この操作は取り消すことはできません。続けたいですか?`,
|
||||||
deleteTitle: '消去',
|
deleteTitle: '消去',
|
||||||
|
|
|
@ -123,8 +123,6 @@ const message = {
|
||||||
},
|
},
|
||||||
msg: {
|
msg: {
|
||||||
noneData: '데이터가 없습니다',
|
noneData: '데이터가 없습니다',
|
||||||
disConn:
|
|
||||||
'종료 명령어인 {0} 등을 사용하지 않고 직접 연결 끊기 버튼을 클릭하여 터미널 연결을 종료해 주십시오.',
|
|
||||||
delete: `이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?`,
|
delete: `이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?`,
|
||||||
clean: `이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?`,
|
clean: `이 작업은 되돌릴 수 없습니다. 계속하시겠습니까?`,
|
||||||
deleteTitle: '삭제',
|
deleteTitle: '삭제',
|
||||||
|
|
|
@ -123,8 +123,6 @@ const message = {
|
||||||
},
|
},
|
||||||
msg: {
|
msg: {
|
||||||
noneData: 'Tiada data tersedia',
|
noneData: 'Tiada data tersedia',
|
||||||
disConn:
|
|
||||||
'Sila klik butang putus sambungan secara langsung untuk menamatkan sambungan terminal, mengelakkan penggunaan arahan keluar seperti {0}.',
|
|
||||||
delete: 'Operasi ini tidak boleh diundur. Adakah anda mahu meneruskan?',
|
delete: 'Operasi ini tidak boleh diundur. Adakah anda mahu meneruskan?',
|
||||||
clean: 'Operasi ini tidak boleh diundur. Adakah anda mahu meneruskan?',
|
clean: 'Operasi ini tidak boleh diundur. Adakah anda mahu meneruskan?',
|
||||||
deleteTitle: 'Padam',
|
deleteTitle: 'Padam',
|
||||||
|
|
|
@ -123,8 +123,6 @@ const message = {
|
||||||
},
|
},
|
||||||
msg: {
|
msg: {
|
||||||
noneData: 'Nenhum dado disponível',
|
noneData: 'Nenhum dado disponível',
|
||||||
disConn:
|
|
||||||
'Por favor, clique diretamente no botão de desconexão para encerrar a conexão do terminal, evitando o uso de comandos de saída como {0}.',
|
|
||||||
delete: 'Esta operação de exclusão não pode ser desfeita. Deseja continuar?',
|
delete: 'Esta operação de exclusão não pode ser desfeita. Deseja continuar?',
|
||||||
clean: 'Esta operação de limpeza não pode ser desfeita. Deseja continuar?',
|
clean: 'Esta operação de limpeza não pode ser desfeita. Deseja continuar?',
|
||||||
deleteTitle: 'Excluir',
|
deleteTitle: 'Excluir',
|
||||||
|
|
|
@ -124,8 +124,6 @@ const message = {
|
||||||
},
|
},
|
||||||
msg: {
|
msg: {
|
||||||
noneData: 'Нет данных',
|
noneData: 'Нет данных',
|
||||||
disConn:
|
|
||||||
'Пожалуйста, нажмите кнопку отключения, чтобы разорвать соединение с терминалом, избегая использования команд выхода, таких как {0}.',
|
|
||||||
delete: 'Эта операция удаления не может быть отменена. Хотите продолжить?',
|
delete: 'Эта операция удаления не может быть отменена. Хотите продолжить?',
|
||||||
clean: 'Эта операция очистки не может быть отменена. Хотите продолжить?',
|
clean: 'Эта операция очистки не может быть отменена. Хотите продолжить?',
|
||||||
deleteTitle: 'Удалить',
|
deleteTitle: 'Удалить',
|
||||||
|
|
|
@ -123,7 +123,6 @@ const message = {
|
||||||
},
|
},
|
||||||
msg: {
|
msg: {
|
||||||
noneData: '暫無資料',
|
noneData: '暫無資料',
|
||||||
disConn: '請直接點選斷開按鈕斷開終端連接,避免使用 {0} 等退出指令。',
|
|
||||||
delete: '刪除 操作不可復原,是否繼續?',
|
delete: '刪除 操作不可復原,是否繼續?',
|
||||||
clean: '清空 操作不可復原,是否繼續?',
|
clean: '清空 操作不可復原,是否繼續?',
|
||||||
deleteTitle: '刪除',
|
deleteTitle: '刪除',
|
||||||
|
|
|
@ -121,7 +121,6 @@ const message = {
|
||||||
Rollbacking: '快照回滚中,请稍候...',
|
Rollbacking: '快照回滚中,请稍候...',
|
||||||
},
|
},
|
||||||
msg: {
|
msg: {
|
||||||
disConn: '请直接点击断开按钮断开终端连接,避免使用 {0} 等退出命令',
|
|
||||||
noneData: '暂无数据',
|
noneData: '暂无数据',
|
||||||
delete: '删除 操作不可回滚,是否继续?',
|
delete: '删除 操作不可回滚,是否继续?',
|
||||||
clean: '清空 操作不可回滚,是否继续?',
|
clean: '清空 操作不可回滚,是否继续?',
|
||||||
|
|
|
@ -16,12 +16,7 @@
|
||||||
</template>
|
</template>
|
||||||
</DrawerHeader>
|
</DrawerHeader>
|
||||||
</template>
|
</template>
|
||||||
<el-alert type="error" :closable="false">
|
<Terminal class="mt-2" style="height: calc(100vh - 175px)" ref="terminalRef"></Terminal>
|
||||||
<template #title>
|
|
||||||
<span>{{ $t('commons.msg.disConn', ['/bye exit']) }}</span>
|
|
||||||
</template>
|
|
||||||
</el-alert>
|
|
||||||
<Terminal class="mt-2" style="height: calc(100vh - 225px)" ref="terminalRef"></Terminal>
|
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
|
@ -67,8 +62,8 @@ const loadTooltip = () => {
|
||||||
const initTerm = () => {
|
const initTerm = () => {
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
terminalRef.value.acceptParams({
|
terminalRef.value.acceptParams({
|
||||||
endpoint: '/api/v1/ai/ollama/exec',
|
endpoint: '/api/v1/containers/exec',
|
||||||
args: `name=${itemName.value}`,
|
args: `source=ollama&name=${itemName.value}`,
|
||||||
error: '',
|
error: '',
|
||||||
initCmd: '',
|
initCmd: '',
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,12 +10,7 @@
|
||||||
<template #header>
|
<template #header>
|
||||||
<DrawerHeader :header="$t('container.containerTerminal')" :resource="title" :back="handleClose" />
|
<DrawerHeader :header="$t('container.containerTerminal')" :resource="title" :back="handleClose" />
|
||||||
</template>
|
</template>
|
||||||
<el-alert type="error" :closable="false">
|
<el-form ref="formRef" :model="form" label-position="top">
|
||||||
<template #title>
|
|
||||||
<span>{{ $t('commons.msg.disConn', ['exit']) }}</span>
|
|
||||||
</template>
|
|
||||||
</el-alert>
|
|
||||||
<el-form ref="formRef" class="mt-2" :model="form" label-position="top">
|
|
||||||
<el-form-item :label="$t('commons.table.user')" prop="user">
|
<el-form-item :label="$t('commons.table.user')" prop="user">
|
||||||
<el-input placeholder="root" clearable v-model="form.user" />
|
<el-input placeholder="root" clearable v-model="form.user" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
@ -51,7 +46,7 @@
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button v-else @click="onClose()">{{ $t('commons.button.disconnect') }}</el-button>
|
<el-button v-else @click="onClose()">{{ $t('commons.button.disconnect') }}</el-button>
|
||||||
<Terminal
|
<Terminal
|
||||||
style="height: calc(100vh - 355px); margin-top: 18px"
|
style="height: calc(100vh - 312px); margin-top: 18px"
|
||||||
ref="terminalRef"
|
ref="terminalRef"
|
||||||
v-if="terminalOpen"
|
v-if="terminalOpen"
|
||||||
></Terminal>
|
></Terminal>
|
||||||
|
@ -104,7 +99,7 @@ const initTerm = (formEl: FormInstance | undefined) => {
|
||||||
await nextTick();
|
await nextTick();
|
||||||
terminalRef.value!.acceptParams({
|
terminalRef.value!.acceptParams({
|
||||||
endpoint: '/api/v1/containers/exec',
|
endpoint: '/api/v1/containers/exec',
|
||||||
args: `containerid=${form.containerID}&user=${form.user}&command=${form.command}`,
|
args: `source=container&containerid=${form.containerID}&user=${form.user}&command=${form.command}`,
|
||||||
error: '',
|
error: '',
|
||||||
initCmd: '',
|
initCmd: '',
|
||||||
});
|
});
|
||||||
|
|
|
@ -309,8 +309,8 @@ const initTerminal = async () => {
|
||||||
terminalShow.value = true;
|
terminalShow.value = true;
|
||||||
redisStatus.value = 'Running';
|
redisStatus.value = 'Running';
|
||||||
terminalRef.value.acceptParams({
|
terminalRef.value.acceptParams({
|
||||||
endpoint: '/api/v1/databases/redis/exec',
|
endpoint: '/api/v1/containers/exec',
|
||||||
args: `name=${currentDBName.value}&from=${currentDB.value.from}`,
|
args: `source=redis&name=${currentDBName.value}&from=${currentDB.value.from}`,
|
||||||
error: '',
|
error: '',
|
||||||
initCmd: '',
|
initCmd: '',
|
||||||
});
|
});
|
||||||
|
@ -327,8 +327,8 @@ const initTerminal = async () => {
|
||||||
if (res.data.status === 'Running') {
|
if (res.data.status === 'Running') {
|
||||||
terminalShow.value = true;
|
terminalShow.value = true;
|
||||||
terminalRef.value.acceptParams({
|
terminalRef.value.acceptParams({
|
||||||
endpoint: '/api/v1/databases/redis/exec',
|
endpoint: '/api/v1/containers/exec',
|
||||||
args: `name=${currentDBName.value}&from=${currentDB.value.from}`,
|
args: `source=redis&name=${currentDBName.value}&from=${currentDB.value.from}`,
|
||||||
error: '',
|
error: '',
|
||||||
initCmd: '',
|
initCmd: '',
|
||||||
});
|
});
|
||||||
|
|
3
go.mod
3
go.mod
|
@ -58,6 +58,7 @@ require (
|
||||||
golang.org/x/sys v0.29.0
|
golang.org/x/sys v0.29.0
|
||||||
golang.org/x/term v0.28.0
|
golang.org/x/term v0.28.0
|
||||||
golang.org/x/text v0.21.0
|
golang.org/x/text v0.21.0
|
||||||
|
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38
|
||||||
gopkg.in/ini.v1 v1.67.0
|
gopkg.in/ini.v1 v1.67.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
gorm.io/gorm v1.25.7
|
gorm.io/gorm v1.25.7
|
||||||
|
@ -218,7 +219,6 @@ require (
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/stretchr/testify v1.10.0 // indirect
|
github.com/stretchr/testify v1.10.0 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cloudapp v1.0.1105 // indirect
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1105 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1105 // indirect
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1065 // indirect
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.1065 // indirect
|
||||||
github.com/therootcompany/xz v1.0.1 // indirect
|
github.com/therootcompany/xz v1.0.1 // indirect
|
||||||
|
@ -259,7 +259,6 @@ require (
|
||||||
golang.org/x/sync v0.10.0 // indirect
|
golang.org/x/sync v0.10.0 // indirect
|
||||||
golang.org/x/time v0.8.0 // indirect
|
golang.org/x/time v0.8.0 // indirect
|
||||||
golang.org/x/tools v0.28.0 // indirect
|
golang.org/x/tools v0.28.0 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20241118233622-e639e219e697 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect
|
||||||
google.golang.org/grpc v1.67.1 // indirect
|
google.golang.org/grpc v1.67.1 // indirect
|
||||||
|
|
3
go.sum
3
go.sum
|
@ -960,10 +960,7 @@ github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+z
|
||||||
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
|
github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo=
|
||||||
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
|
github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg=
|
||||||
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
|
github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cloudapp v1.0.1105 h1:ikgsQkFcKDzOJFbxIcSCdu7oj8GQBfwJohBpYHWeSco=
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/cloudapp v1.0.1105/go.mod h1:/GMcTQRH1+iDTX8RSl+G79doAWcabhpt+xmv3V9P3p0=
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.563/go.mod h1:7sCQWVkxcsR38nffDW057DRGk8mUjK1Ing/EFOK8s8Y=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1065 h1:krcqtAmexnHHBm/4ge4tr2b1cn/a7JGBESVGoZYXQAE=
|
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1065/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1065/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1105 h1:lCs0dmezU6/8JcfNwEaam1Pm1RS/5MhXvNhf/X2y65s=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1105 h1:lCs0dmezU6/8JcfNwEaam1Pm1RS/5MhXvNhf/X2y65s=
|
||||||
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1105/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.1105/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
|
||||||
|
|
Loading…
Add table
Reference in a new issue