mirror of
https://gitee.com/goploy/goploy.git
synced 2024-11-30 03:07:59 +08:00
U server process
This commit is contained in:
parent
fa6e63d550
commit
856104c268
@ -46,6 +46,11 @@ func (s Server) Routes() []core.Route {
|
||||
core.NewRoute("/server/addMonitor", http.MethodPost, s.AddMonitor).Permissions(permission.AddServerWarningRule),
|
||||
core.NewRoute("/server/editMonitor", http.MethodPut, s.EditMonitor).Permissions(permission.EditServerWarningRule),
|
||||
core.NewRoute("/server/deleteMonitor", http.MethodDelete, s.DeleteMonitor).Permissions(permission.DeleteServerWarningRule),
|
||||
core.NewRoute("/server/getProcessList", http.MethodGet, s.GetProcessList).Permissions(permission.ShowServerProcessPage),
|
||||
core.NewRoute("/server/addProcess", http.MethodPost, s.AddProcess).Permissions(permission.AddServerProcess),
|
||||
core.NewRoute("/server/editProcess", http.MethodPut, s.EditProcess).Permissions(permission.EditServerProcess),
|
||||
core.NewRoute("/server/deleteProcess", http.MethodDelete, s.DeleteProcess).Permissions(permission.DeleteServerProcess),
|
||||
core.NewRoute("/server/execProcess", http.MethodPost, s.ExecProcess).Permissions(permission.ShowServerProcessPage),
|
||||
}
|
||||
}
|
||||
|
||||
@ -715,3 +720,165 @@ func (s Server) DeleteMonitor(gp *core.Goploy) core.Response {
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Server) GetProcessList(gp *core.Goploy) core.Response {
|
||||
type ReqData struct {
|
||||
ServerID int64 `json:"serverId" validate:"gt=0"`
|
||||
}
|
||||
|
||||
var reqData ReqData
|
||||
if err := decodeQuery(gp.URLQuery, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
list, err := model.ServerProcess{ServerID: reqData.ServerID}.GetListByServerID()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
List model.ServerProcesses `json:"list"`
|
||||
}{List: list},
|
||||
}
|
||||
}
|
||||
|
||||
func (Server) AddProcess(gp *core.Goploy) core.Response {
|
||||
type ReqData struct {
|
||||
ServerID int64 `json:"serverId" validate:"gt=0"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Status string `json:"status"`
|
||||
Start string `json:"start"`
|
||||
Stop string `json:"stop"`
|
||||
Restart string `json:"restart"`
|
||||
}
|
||||
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
id, err := model.ServerProcess{
|
||||
ServerID: reqData.ServerID,
|
||||
Name: reqData.Name,
|
||||
Status: reqData.Status,
|
||||
Start: reqData.Start,
|
||||
Stop: reqData.Stop,
|
||||
Restart: reqData.Restart,
|
||||
}.AddRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
|
||||
}
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ID int64 `json:"id"`
|
||||
}{ID: id},
|
||||
}
|
||||
}
|
||||
|
||||
func (Server) EditProcess(gp *core.Goploy) core.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
Status string `json:"status"`
|
||||
Start string `json:"start"`
|
||||
Stop string `json:"stop"`
|
||||
Restart string `json:"restart"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
err := model.ServerProcess{
|
||||
ID: reqData.ID,
|
||||
Name: reqData.Name,
|
||||
Status: reqData.Status,
|
||||
Start: reqData.Start,
|
||||
Stop: reqData.Stop,
|
||||
Restart: reqData.Restart,
|
||||
}.EditRow()
|
||||
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Server) DeleteProcess(gp *core.Goploy) core.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
if err := (model.ServerProcess{ID: reqData.ID}).DeleteRow(); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
return response.JSON{}
|
||||
}
|
||||
|
||||
func (Server) ExecProcess(gp *core.Goploy) core.Response {
|
||||
type ReqData struct {
|
||||
ID int64 `json:"id" validate:"gt=0"`
|
||||
Command string `json:"command" validate:"required"`
|
||||
}
|
||||
var reqData ReqData
|
||||
if err := decodeJson(gp.Body, &reqData); err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
serverProcess, err := model.ServerProcess{ID: reqData.ID}.GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
server, err := (model.Server{ID: serverProcess.ServerID}).GetData()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
|
||||
script := ""
|
||||
switch reqData.Command {
|
||||
case "status":
|
||||
script = serverProcess.Status
|
||||
case "start":
|
||||
script = serverProcess.Start
|
||||
case "stop":
|
||||
script = serverProcess.Stop
|
||||
case "restart":
|
||||
script = serverProcess.Restart
|
||||
default:
|
||||
return response.JSON{Code: response.Error, Message: "Command error"}
|
||||
}
|
||||
if script == "" {
|
||||
return response.JSON{Code: response.Error, Message: "Command empty"}
|
||||
}
|
||||
|
||||
client, err := server.ToSSHConfig().Dial()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return response.JSON{Code: response.Error, Message: err.Error()}
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
var sshOutbuf, sshErrbuf bytes.Buffer
|
||||
session.Stdout = &sshOutbuf
|
||||
session.Stderr = &sshErrbuf
|
||||
err = session.Run(script)
|
||||
core.Log(core.TRACE, fmt.Sprintf("%s exec cmd %s, result %t, stdout: %s, stderr: %s", gp.UserInfo.Name, script, err == nil, sshOutbuf.String(), sshErrbuf.String()))
|
||||
return response.JSON{
|
||||
Data: struct {
|
||||
ExecRes bool `json:"execRes"`
|
||||
Stdout string `json:"stdout"`
|
||||
Stderr string `json:"stderr"`
|
||||
}{ExecRes: err == nil, Stdout: sshOutbuf.String(), Stderr: sshErrbuf.String()},
|
||||
}
|
||||
}
|
||||
|
120
model/ServerProcessModel.go
Normal file
120
model/ServerProcessModel.go
Normal file
@ -0,0 +1,120 @@
|
||||
// Copyright 2022 The Goploy Authors. All rights reserved.
|
||||
// Use of this source code is governed by a GPLv3-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package model
|
||||
|
||||
import (
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
)
|
||||
|
||||
const serverProcessTable = "`server_process`"
|
||||
|
||||
type ServerProcess struct {
|
||||
ID int64 `json:"id"`
|
||||
ServerID int64 `json:"serverId"`
|
||||
Name string `json:"name"`
|
||||
Start string `json:"start"`
|
||||
Stop string `json:"stop"`
|
||||
Status string `json:"status"`
|
||||
Restart string `json:"restart"`
|
||||
InsertTime string `json:"insertTime,omitempty"`
|
||||
UpdateTime string `json:"updateTime,omitempty"`
|
||||
}
|
||||
|
||||
type ServerProcesses []ServerProcess
|
||||
|
||||
func (sp ServerProcess) GetData() (ServerProcess, error) {
|
||||
var serverProcess ServerProcess
|
||||
err := sq.
|
||||
Select("id, server_id, name, start, stop, status, restart").
|
||||
From(serverProcessTable).
|
||||
Where(sq.Eq{"id": sp.ID}).
|
||||
OrderBy("id DESC").
|
||||
RunWith(DB).
|
||||
QueryRow().
|
||||
Scan(&serverProcess.ID,
|
||||
&serverProcess.ServerID,
|
||||
&serverProcess.Name,
|
||||
&serverProcess.Start,
|
||||
&serverProcess.Stop,
|
||||
&serverProcess.Status,
|
||||
&serverProcess.Restart)
|
||||
if err != nil {
|
||||
return serverProcess, err
|
||||
}
|
||||
return serverProcess, nil
|
||||
}
|
||||
|
||||
func (sp ServerProcess) GetListByServerID() (ServerProcesses, error) {
|
||||
rows, err := sq.
|
||||
Select("id, server_id, name, start, stop, status, restart, insert_time, update_time").
|
||||
From(serverProcessTable).
|
||||
Where(sq.Eq{"server_id": sp.ServerID}).
|
||||
OrderBy("id DESC").
|
||||
RunWith(DB).
|
||||
Query()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
serverProcesses := ServerProcesses{}
|
||||
for rows.Next() {
|
||||
var serverProcess ServerProcess
|
||||
|
||||
if err := rows.Scan(
|
||||
&serverProcess.ID,
|
||||
&serverProcess.ServerID,
|
||||
&serverProcess.Name,
|
||||
&serverProcess.Start,
|
||||
&serverProcess.Stop,
|
||||
&serverProcess.Status,
|
||||
&serverProcess.Restart,
|
||||
&serverProcess.InsertTime,
|
||||
&serverProcess.UpdateTime,
|
||||
); err != nil {
|
||||
return serverProcesses, err
|
||||
}
|
||||
serverProcesses = append(serverProcesses, serverProcess)
|
||||
}
|
||||
return serverProcesses, nil
|
||||
}
|
||||
|
||||
func (sp ServerProcess) AddRow() (int64, error) {
|
||||
result, err := sq.
|
||||
Insert(serverProcessTable).
|
||||
Columns("server_id", "name", "start", "stop", "status", "restart").
|
||||
Values(sp.ServerID, sp.Name, sp.Start, sp.Stop, sp.Status, sp.Restart).
|
||||
RunWith(DB).
|
||||
Exec()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
id, err := result.LastInsertId()
|
||||
return id, err
|
||||
}
|
||||
|
||||
func (sp ServerProcess) EditRow() error {
|
||||
_, err := sq.
|
||||
Update(serverProcessTable).
|
||||
SetMap(sq.Eq{
|
||||
"name": sp.Name,
|
||||
"start": sp.Start,
|
||||
"stop": sp.Stop,
|
||||
"status": sp.Status,
|
||||
"restart": sp.Restart,
|
||||
}).
|
||||
Where(sq.Eq{"id": sp.ID}).
|
||||
RunWith(DB).
|
||||
Exec()
|
||||
return err
|
||||
}
|
||||
|
||||
func (sp ServerProcess) DeleteRow() error {
|
||||
_, err := sq.
|
||||
Delete(serverProcessTable).
|
||||
Where(sq.Eq{"id": sp.ID}).
|
||||
RunWith(DB).
|
||||
Exec()
|
||||
return err
|
||||
}
|
4
model/sql/1.10.0.sql
Normal file
4
model/sql/1.10.0.sql
Normal file
@ -0,0 +1,4 @@
|
||||
INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (70, 23, 'ShowServerProcessPage', 0, '');
|
||||
INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (71, 23, 'AddServerProcess', 0, '');
|
||||
INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (72, 23, 'EditServerProcess', 0, '');
|
||||
INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (73, 23, 'DeleteServerProcess', 0, '');
|
@ -420,6 +420,10 @@ INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALU
|
||||
INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (67, 56, 'FileCompare', 0, '');
|
||||
INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (68, 56, 'FileSync', 0, '');
|
||||
INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (69, 56, 'ProcessManager', 0, '');
|
||||
INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (70, 23, 'ShowServerProcessPage', 0, '');
|
||||
INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (71, 23, 'AddServerProcess', 0, '');
|
||||
INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (72, 23, 'EditServerProcess', 0, '');
|
||||
INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (73, 23, 'DeleteServerProcess', 0, '');
|
||||
INSERT IGNORE INTO `role_permission`(`role_id`, `permission_id`) VALUES (1, 13);
|
||||
INSERT IGNORE INTO `role_permission`(`role_id`, `permission_id`) VALUES (1, 14);
|
||||
INSERT IGNORE INTO `role_permission`(`role_id`, `permission_id`) VALUES (1, 15);
|
||||
|
@ -74,4 +74,8 @@ const (
|
||||
FileCompare = 67
|
||||
FileSync = 68
|
||||
ProcessManager = 69
|
||||
ShowServerProcessPage = 70
|
||||
AddServerProcess = 71
|
||||
EditServerProcess = 72
|
||||
DeleteServerProcess = 73
|
||||
)
|
||||
|
@ -4,8 +4,8 @@
|
||||
"license": "GPLv3",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"server": "cd ../ && go run main.go --asset-dir=./"
|
||||
"server": "cd ../ && go run main.go --asset-dir=./",
|
||||
"build": "vite build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@chenfengyuan/vue-qrcode": "^2.0.0-beta",
|
||||
|
@ -292,3 +292,98 @@ export class ServerMonitorDelete extends Request {
|
||||
this.param = param
|
||||
}
|
||||
}
|
||||
|
||||
export interface ServerProcessData {
|
||||
[key: string]: any
|
||||
id: number
|
||||
serverId: number
|
||||
name: string
|
||||
start: string
|
||||
stop: string
|
||||
status: string
|
||||
restart: string
|
||||
InsertTime: string
|
||||
UpdateTime: string
|
||||
}
|
||||
|
||||
export class ServerProcessList extends Request {
|
||||
readonly url = '/server/getProcessList'
|
||||
readonly method = 'get'
|
||||
public param: {
|
||||
serverId: number
|
||||
}
|
||||
|
||||
public declare datagram: {
|
||||
list: ServerProcessData[]
|
||||
}
|
||||
constructor(param: ServerProcessList['param']) {
|
||||
super()
|
||||
this.param = param
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerProcessAdd extends Request {
|
||||
readonly url = '/server/addProcess'
|
||||
readonly method = 'post'
|
||||
public param: {
|
||||
name: string
|
||||
start: string
|
||||
stop: string
|
||||
status: string
|
||||
restart: string
|
||||
}
|
||||
public declare datagram: ID
|
||||
constructor(param: ServerProcessAdd['param']) {
|
||||
super()
|
||||
this.param = param
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerProcessEdit extends Request {
|
||||
readonly url = '/server/editProcess'
|
||||
readonly method = 'put'
|
||||
public param: {
|
||||
id: number
|
||||
name: string
|
||||
start: string
|
||||
stop: string
|
||||
status: string
|
||||
restart: string
|
||||
}
|
||||
constructor(param: ServerProcessEdit['param']) {
|
||||
super()
|
||||
this.param = param
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerProcessDelete extends Request {
|
||||
readonly url = '/server/deleteProcess'
|
||||
readonly method = 'delete'
|
||||
public param: {
|
||||
id: number
|
||||
}
|
||||
constructor(param: ServerProcessDelete['param']) {
|
||||
super()
|
||||
this.param = param
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerExecProcess extends Request {
|
||||
readonly url = '/server/execProcess'
|
||||
readonly method = 'post'
|
||||
readonly timeout = 0
|
||||
public param: {
|
||||
id: number
|
||||
serverId: number
|
||||
command: string
|
||||
}
|
||||
public declare datagram: {
|
||||
execRes: boolean
|
||||
stdout: string
|
||||
stderr: string
|
||||
}
|
||||
constructor(param: ServerExecProcess['param']) {
|
||||
super()
|
||||
this.param = param
|
||||
}
|
||||
}
|
||||
|
1
web/src/icons/svg/processManage.svg
Normal file
1
web/src/icons/svg/processManage.svg
Normal file
@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1661485574631" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1617" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M265.6 64c5.94 0 11.638 2.364 15.84 6.572A22.456 22.456 0 0 1 288 86.437v179.497c0 5.951-2.36 11.658-6.56 15.866a22.381 22.381 0 0 1-15.84 6.572h-44.8v190.715h134.4c12.371 0 22.4 10.046 22.4 22.437V546.4c0 12.391-10.029 22.437-22.4 22.437H220.8v258.027h134.4c12.371 0 22.4 10.046 22.4 22.437v44.875c0 12.391-10.029 22.437-22.4 22.437H153.6a22.381 22.381 0 0 1-15.84-6.572 22.456 22.456 0 0 1-6.56-15.865V288.372H86.4a22.381 22.381 0 0 1-15.84-6.572A22.456 22.456 0 0 1 64 265.934V86.437c0-5.95 2.36-11.658 6.56-15.865A22.381 22.381 0 0 1 86.4 64h179.2z m672 370.213c5.94 0 11.638 2.364 15.84 6.572A22.456 22.456 0 0 1 960 456.65v134.623c0 5.95-2.36 11.658-6.56 15.866a22.381 22.381 0 0 1-15.84 6.571l-14.495-0.002C883.037 552.88 814.198 512.743 736 512.743S588.963 552.88 548.895 613.708l-59.295 0.002a22.381 22.381 0 0 1-15.84-6.571 22.456 22.456 0 0 1-6.56-15.866V456.65c0-5.95 2.36-11.658 6.56-15.865a22.381 22.381 0 0 1 15.84-6.572h448z m0-347.776c5.94 0 11.638 2.364 15.84 6.572a22.456 22.456 0 0 1 6.56 15.865v134.623c0 5.95-2.36 11.658-6.56 15.866a22.381 22.381 0 0 1-15.84 6.571H400a22.381 22.381 0 0 1-15.84-6.571 22.456 22.456 0 0 1-6.56-15.866V108.874c0-12.391 10.029-22.437 22.4-22.437h537.6z" p-id="1618"></path><path d="M925.862 750.614c-1.536-8.481-9.76-17.114-18.29-19.025l-6.378-1.45c-14.985-4.51-28.28-14.366-36.733-28.882-8.453-14.593-10.452-31.173-6.763-46.376l1.998-5.96c2.536-8.252-0.769-19.635-7.453-25.29 0 0-5.995-5.042-22.902-14.745-16.907-9.627-24.207-12.301-24.207-12.301-8.223-2.98-19.75-0.153-25.744 6.189l-4.458 4.737c-11.374 10.696-26.667 17.19-43.572 17.19s-32.351-6.569-43.726-17.343l-4.305-4.585c-5.918-6.34-17.52-9.166-25.744-6.188 0 0-7.377 2.674-24.284 12.3-16.907 9.78-22.824 14.822-22.824 14.822-6.685 5.578-9.99 16.885-7.454 25.213l1.846 6.036c3.612 15.204 1.69 31.708-6.763 46.3-8.454 14.591-21.898 24.527-36.959 28.959L555 731.589c-8.453 1.91-16.753 10.468-18.29 19.025 0 0-1.382 7.64-1.382 27.046 0 19.407 1.382 27.047 1.382 27.047 1.537 8.557 9.76 17.114 18.29 19.024l5.995 1.375c15.062 4.433 28.587 14.365 37.04 29.034 8.454 14.593 10.453 31.173 6.764 46.376l-1.767 5.883c-2.535 8.252 0.77 19.635 7.454 25.29 0 0 5.994 5.042 22.9 14.746 16.907 9.703 24.208 12.3 24.208 12.3 8.223 2.98 19.75 0.153 25.744-6.189l4.226-4.508c11.45-10.773 26.82-17.343 43.804-17.343a63.547 63.547 0 0 1 43.803 17.419l4.226 4.508c5.918 6.341 17.52 9.17 25.744 6.189 0 0 7.377-2.674 24.284-12.3 16.907-9.704 22.824-14.746 22.824-14.746 6.685-5.578 9.99-16.961 7.453-25.29l-1.846-6.113c-3.611-15.127-1.69-31.63 6.764-46.147 8.453-14.594 21.978-24.601 37.04-29.034l5.995-1.374c8.453-1.91 16.754-10.468 18.29-19.025 0 0 1.383-7.64 1.383-27.046-0.08-19.484-1.464-27.125-1.464-27.125v0.003zM731.365 855.362c-43.111 0-78.154-34.762-78.154-77.702 0-42.862 34.965-77.626 78.154-77.626 43.111 0 78.153 34.763 78.153 77.702-0.078 42.863-35.043 77.626-78.153 77.626z" p-id="1619"></path></svg>
|
After Width: | Height: | Size: 3.2 KiB |
@ -137,6 +137,7 @@
|
||||
"serverTerminal": "Terminal",
|
||||
"serverSFTP": "SFTP",
|
||||
"serverAgent": "Monitor",
|
||||
"serverProcess": "Process",
|
||||
"template": "Template",
|
||||
"crontab": "Crontab",
|
||||
"serverCron": "Cron",
|
||||
@ -219,7 +220,11 @@
|
||||
"DeployTask": "Can deploy task",
|
||||
"FileCompare": "File compare",
|
||||
"FileSync": "File sync",
|
||||
"ProcessManager": "Manage process"
|
||||
"ProcessManager": "Manage process",
|
||||
"ShowServerProcessPage": "See the server process page",
|
||||
"AddServerProcess": "Add a server process",
|
||||
"EditServerProcess": "Edit the server process",
|
||||
"DeleteServerProcess": "Delete the server process"
|
||||
},
|
||||
"tagsView": {
|
||||
"refresh": "Refresh",
|
||||
@ -268,6 +273,7 @@
|
||||
"diskIO": "Disk IO",
|
||||
"addMonitor": "Create monitor",
|
||||
"monitorList": "Monitor list",
|
||||
"deleteTips": "This action will delete the {name}, continue?",
|
||||
"removeCronTips": "This action will delete the crontab, continue?",
|
||||
"removeMonitorTips": "This action will delete the monitor({item}), continue?",
|
||||
"item": "Item",
|
||||
|
@ -137,8 +137,8 @@
|
||||
"serverTerminal": "Terminal",
|
||||
"serverSFTP": "SFTP",
|
||||
"serverAgent": "服务器监控",
|
||||
"serverProcess": "进程管理",
|
||||
"serverCron": "定时任务",
|
||||
"template": "模板设置",
|
||||
"crontab": "Crontab管理",
|
||||
"namespace": "空间管理",
|
||||
"namespaceSetting": "空间设置",
|
||||
@ -219,7 +219,11 @@
|
||||
"DeployTask": "定时构建",
|
||||
"FileCompare": "文件对比",
|
||||
"FileSync": "文件同步",
|
||||
"ProcessManager": "进程管理"
|
||||
"ProcessManager": "进程管理",
|
||||
"ShowServerProcessPage": "查看服务器进程管理",
|
||||
"AddServerProcess": "新增服务器进程",
|
||||
"EditServerProcess": "编辑服务器进程",
|
||||
"DeleteServerProcess": "删除服务器进程"
|
||||
},
|
||||
"tagsView": {
|
||||
"refresh": "刷新",
|
||||
@ -257,6 +261,7 @@
|
||||
"copyPubTips": "复制成功,请粘贴到目标服务器~/.ssh/authorized_keys里面",
|
||||
"testConnection": "测试连接",
|
||||
"removeServerTips": "此操作将删除服务器({serverName}), 是否继续?",
|
||||
"deleteTips": "此操作将永久删除({name}), 是否继续?",
|
||||
"cpuUsage": "CPU使用率",
|
||||
"ramUsage": "RAM使用率",
|
||||
"loadavg": "系统负载",
|
||||
|
@ -68,4 +68,8 @@ export default Object.freeze({
|
||||
FileCompare: 67,
|
||||
FileSync: 68,
|
||||
ProcessManager: 69,
|
||||
ShowServerProcessPage: 70,
|
||||
AddServerProcess: 71,
|
||||
EditServerProcess: 72,
|
||||
DeleteServerProcess: 73,
|
||||
})
|
||||
|
@ -126,6 +126,16 @@ export default <RouteRecordRaw[]>[
|
||||
permissions: [permission.ShowServerMonitorPage],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'process',
|
||||
name: 'ServerProcess',
|
||||
component: () => import('@/views/server/process/index.vue'),
|
||||
meta: {
|
||||
title: 'serverProcess',
|
||||
icon: 'processManage',
|
||||
permissions: [permission.ShowServerProcessPage],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'cron',
|
||||
name: 'ServerCron',
|
||||
|
@ -297,11 +297,15 @@ function handleEdit(data: CronData) {
|
||||
}
|
||||
|
||||
function handleRemove(data: CronData) {
|
||||
ElMessageBox.confirm(t('serverPage.removeUserTips'), t('tips'), {
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning',
|
||||
})
|
||||
ElMessageBox.confirm(
|
||||
t('serverPage.deleteTips', { name: data.command }),
|
||||
t('tips'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
new CronRemove({ id: data.id }).request().then(() => {
|
||||
getList()
|
||||
@ -324,7 +328,6 @@ function onExpressionChange() {
|
||||
|
||||
function handlePageChange(val = 1) {
|
||||
pagination.value.page = val
|
||||
getList()
|
||||
}
|
||||
|
||||
function submit() {
|
||||
|
418
web/src/views/server/process/index.vue
Normal file
418
web/src/views/server/process/index.vue
Normal file
@ -0,0 +1,418 @@
|
||||
<template>
|
||||
<el-row class="app-container">
|
||||
<el-row class="app-bar" type="flex" justify="space-between">
|
||||
<el-col :span="16">
|
||||
<el-select
|
||||
v-model="serverId"
|
||||
placeholder="Select server"
|
||||
style="width: 160px"
|
||||
filterable
|
||||
@change="selectServer"
|
||||
>
|
||||
<el-option
|
||||
v-for="server in serverOption"
|
||||
:key="server.id"
|
||||
:label="server.name"
|
||||
:value="server.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col v-if="serverId !== ''" :span="8" style="text-align: right">
|
||||
<el-button
|
||||
style="margin-left: 10px"
|
||||
:loading="tableLoading"
|
||||
type="primary"
|
||||
:icon="Refresh"
|
||||
@click="refresList"
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
:icon="Plus"
|
||||
:permissions="[pms.AddServerProcess]"
|
||||
@click="handleAdd"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row class="app-table">
|
||||
<el-table
|
||||
ref="table"
|
||||
v-loading="tableLoading"
|
||||
height="100%"
|
||||
highlight-current-row
|
||||
:data="tablePage.list"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column type="expand">
|
||||
<template #default="{}">
|
||||
<div style="padding: 0 20px">
|
||||
<el-row style="margin-left: 4px">
|
||||
{{ $t('deployPage.execRes') }}:
|
||||
<span
|
||||
:class="commandRes.execRes ? 'exec-success' : 'exec-fail'"
|
||||
style="padding-left: 5px"
|
||||
>
|
||||
{{ commandRes.execRes }}
|
||||
</span>
|
||||
</el-row>
|
||||
<el-row style="white-space: pre-wrap">
|
||||
stdout: {{ commandRes.stdout }}
|
||||
</el-row>
|
||||
<el-row style="white-space: pre-wrap">
|
||||
stderr:{{ commandRes.stderr }}
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="name"
|
||||
:label="$t('name')"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column prop="status" label="Status" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
:loading="selectedItem['id'] === scope.row.id"
|
||||
type="primary"
|
||||
text
|
||||
@click="handleProcessCmd(scope.row, 'status')"
|
||||
>
|
||||
status
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="start" label="Start" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
:loading="selectedItem['id'] === scope.row.id"
|
||||
type="success"
|
||||
text
|
||||
@click="handleProcessCmd(scope.row, 'start')"
|
||||
>
|
||||
start
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="restart" label="restart" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
:loading="selectedItem['id'] === scope.row.id"
|
||||
type="warning"
|
||||
text
|
||||
@click="handleProcessCmd(scope.row, 'restart')"
|
||||
>
|
||||
restart
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="stop" label="stop" align="center">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
:loading="selectedItem['id'] === scope.row.id"
|
||||
type="danger"
|
||||
text
|
||||
@click="handleProcessCmd(scope.row, 'stop')"
|
||||
>
|
||||
stop
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="operation"
|
||||
:label="$t('op')"
|
||||
width="130"
|
||||
align="center"
|
||||
:fixed="$store.state.app.device === 'mobile' ? false : 'right'"
|
||||
>
|
||||
<template #default="scope">
|
||||
<Button
|
||||
type="primary"
|
||||
:icon="Edit"
|
||||
:permissions="[pms.EditCron]"
|
||||
@click="handleEdit(scope.row)"
|
||||
/>
|
||||
<Button
|
||||
type="danger"
|
||||
:icon="Delete"
|
||||
:permissions="[pms.DeleteCron]"
|
||||
@click="handleRemove(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-row>
|
||||
<el-row type="flex" justify="end" class="app-page">
|
||||
<el-pagination
|
||||
:total="tablePage.total"
|
||||
:page-size="pagination.rows"
|
||||
background
|
||||
layout="total, prev, pager, next"
|
||||
@current-change="handlePageChange"
|
||||
/>
|
||||
</el-row>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:fullscreen="$store.state.app.device === 'mobile'"
|
||||
:title="$t('setting')"
|
||||
>
|
||||
<el-form
|
||||
ref="form"
|
||||
v-loading="formProps.loading"
|
||||
:rules="formRules"
|
||||
:model="formData"
|
||||
label-width="80px"
|
||||
:label-position="
|
||||
$store.state.app.device === 'desktop' ? 'right' : 'top'
|
||||
"
|
||||
>
|
||||
<el-form-item :label="$t('name')" prop="name">
|
||||
<el-input v-model="formData.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Status">
|
||||
<el-input v-model="formData.status" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Start">
|
||||
<el-input v-model="formData.start" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Stop">
|
||||
<el-input v-model="formData.stop" />
|
||||
</el-form-item>
|
||||
<el-form-item label="Restart">
|
||||
<el-input v-model="formData.restart" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">
|
||||
{{ $t('cancel') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
:disabled="formProps.disabled"
|
||||
type="primary"
|
||||
@click="submit"
|
||||
>
|
||||
{{ $t('confirm') }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-row>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
export default { name: 'ServerProcess' }
|
||||
</script>
|
||||
<script lang="ts" setup>
|
||||
import pms from '@/permission'
|
||||
import Button from '@/components/Permission/Button.vue'
|
||||
import { Refresh, Plus, Edit, Delete } from '@element-plus/icons-vue'
|
||||
import {
|
||||
ServerOption,
|
||||
ServerProcessList,
|
||||
ServerProcessAdd,
|
||||
ServerProcessEdit,
|
||||
ServerProcessDelete,
|
||||
ServerProcessData,
|
||||
ServerExecProcess,
|
||||
} from '@/api/server'
|
||||
import type { ElForm } from 'element-plus'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
const { t } = useI18n({ useScope: 'global' })
|
||||
const serverId = ref('')
|
||||
const dialogVisible = ref(false)
|
||||
const serverOption = ref<ServerOption['datagram']['list']>([])
|
||||
const table = ref()
|
||||
const tableLoading = ref(false)
|
||||
const tableData = ref<ServerProcessList['datagram']['list']>([])
|
||||
const pagination = ref({ page: 1, rows: 20 })
|
||||
const selectedItem = ref<ServerProcessData>({})
|
||||
const form = ref<InstanceType<typeof ElForm>>()
|
||||
const tempFormData = {
|
||||
id: 0,
|
||||
serverId: 0,
|
||||
name: '',
|
||||
status: '',
|
||||
start: '',
|
||||
stop: '',
|
||||
restart: '',
|
||||
}
|
||||
const formData = ref(tempFormData)
|
||||
const formProps = ref({
|
||||
loading: false,
|
||||
disabled: false,
|
||||
})
|
||||
const formRules = <InstanceType<typeof ElForm>['rules']>{
|
||||
name: [{ required: true, message: 'Name required', trigger: 'blur' }],
|
||||
}
|
||||
|
||||
getServerOption()
|
||||
|
||||
function selectServer() {
|
||||
getList()
|
||||
}
|
||||
|
||||
function getServerOption() {
|
||||
new ServerOption().request().then((response) => {
|
||||
serverOption.value = response.data.list
|
||||
})
|
||||
}
|
||||
|
||||
function getList() {
|
||||
tableLoading.value = true
|
||||
tableData.value = []
|
||||
new ServerProcessList({ serverId: Number(serverId.value) })
|
||||
.request()
|
||||
.then((response) => {
|
||||
tableData.value = response.data.list
|
||||
})
|
||||
.finally(() => {
|
||||
tableLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const tablePage = computed(() => {
|
||||
let _tableData = tableData.value
|
||||
return {
|
||||
list: _tableData.slice(
|
||||
(pagination.value.page - 1) * pagination.value.rows,
|
||||
pagination.value.page * pagination.value.rows
|
||||
),
|
||||
total: _tableData.length,
|
||||
}
|
||||
})
|
||||
|
||||
function refresList() {
|
||||
pagination.value.page = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
restoreFormData()
|
||||
formData.value.serverId = Number(serverId.value)
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function handleEdit(data: ServerProcessData) {
|
||||
formData.value = data
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
function handleRemove(data: ServerProcessData) {
|
||||
ElMessageBox.confirm(
|
||||
t('serverPage.deleteTips', { name: data.name }),
|
||||
t('tips'),
|
||||
{
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning',
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
new ServerProcessDelete({ id: data.id }).request().then(() => {
|
||||
getList()
|
||||
ElMessage.success('Success')
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.info('Cancel')
|
||||
})
|
||||
}
|
||||
const commandRes = ref<ServerExecProcess['datagram']>({
|
||||
execRes: true,
|
||||
stdout: '',
|
||||
stderr: '',
|
||||
})
|
||||
|
||||
const handleProcessCmd = (data: ServerProcessData, command: string) => {
|
||||
ElMessageBox.confirm(t('deployPage.execTips', { command }), t('tips'), {
|
||||
confirmButtonText: t('confirm'),
|
||||
cancelButtonText: t('cancel'),
|
||||
type: 'warning',
|
||||
})
|
||||
.then(() => {
|
||||
selectedItem.value = data
|
||||
new ServerExecProcess({
|
||||
id: data.id,
|
||||
serverId: data.serverId,
|
||||
command,
|
||||
})
|
||||
.request()
|
||||
.then((response) => {
|
||||
commandRes.value = response.data
|
||||
table.value.toggleRowExpansion(data, true)
|
||||
})
|
||||
.finally(() => {
|
||||
selectedItem.value = {}
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
ElMessage.info('Cancel')
|
||||
})
|
||||
}
|
||||
|
||||
function handlePageChange(val = 1) {
|
||||
pagination.value.page = val
|
||||
}
|
||||
|
||||
function submit() {
|
||||
form.value?.validate((valid) => {
|
||||
if (valid) {
|
||||
if (formData.value.id === 0) {
|
||||
add()
|
||||
} else {
|
||||
edit()
|
||||
}
|
||||
return Promise.resolve(true)
|
||||
} else {
|
||||
return Promise.reject(false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function add() {
|
||||
formProps.value.disabled = true
|
||||
new ServerProcessAdd(formData.value)
|
||||
.request()
|
||||
.then(() => {
|
||||
getList()
|
||||
ElMessage.success('Success')
|
||||
})
|
||||
.finally(() => {
|
||||
formProps.value.disabled = dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function edit() {
|
||||
formProps.value.disabled = true
|
||||
new ServerProcessEdit(formData.value)
|
||||
.request()
|
||||
.then(() => {
|
||||
getList()
|
||||
ElMessage.success('Success')
|
||||
})
|
||||
.finally(() => {
|
||||
formProps.value.disabled = dialogVisible.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function restoreFormData() {
|
||||
formData.value = { ...tempFormData }
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '@/styles/mixin.scss';
|
||||
.template-dialog {
|
||||
padding-right: 10px;
|
||||
height: 400px;
|
||||
overflow-y: auto;
|
||||
@include scrollBar();
|
||||
}
|
||||
|
||||
.exec-success {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.exec-fail {
|
||||
color: #f56c6c;
|
||||
}
|
||||
</style>
|
Loading…
Reference in New Issue
Block a user