U server process

This commit is contained in:
zhenorzz 2022-08-26 19:31:32 +08:00
parent fa6e63d550
commit 856104c268
14 changed files with 852 additions and 11 deletions

View File

@ -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
View 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
View 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, '');

View File

@ -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);

View File

@ -74,4 +74,8 @@ const (
FileCompare = 67
FileSync = 68
ProcessManager = 69
ShowServerProcessPage = 70
AddServerProcess = 71
EditServerProcess = 72
DeleteServerProcess = 73
)

View File

@ -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",

View File

@ -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
}
}

View 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

View File

@ -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",

View File

@ -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": "系统负载",

View File

@ -68,4 +68,8 @@ export default Object.freeze({
FileCompare: 67,
FileSync: 68,
ProcessManager: 69,
ShowServerProcessPage: 70,
AddServerProcess: 71,
EditServerProcess: 72,
DeleteServerProcess: 73,
})

View File

@ -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',

View File

@ -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() {

View 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>