A deploy server mode

This commit is contained in:
zhenorzz 2022-09-16 11:16:01 +08:00
parent 850d1b2fa9
commit 4e122724bc
26 changed files with 520 additions and 179 deletions

View File

@ -305,6 +305,7 @@ func (Project) Add(gp *core.Goploy) core.Response {
AfterDeployScript string `json:"afterDeployScript"`
TransferType string `json:"transferType"`
TransferOption string `json:"transferOption"`
DeployServerMode string `json:"deployServerMode"`
ServerIDs []int64 `json:"serverIds"`
UserIDs []int64 `json:"userIds"`
NotifyType uint8 `json:"notifyType"`
@ -337,6 +338,7 @@ func (Project) Add(gp *core.Goploy) core.Response {
AfterDeployScript: reqData.AfterDeployScript,
TransferType: reqData.TransferType,
TransferOption: reqData.TransferOption,
DeployServerMode: reqData.DeployServerMode,
NotifyType: reqData.NotifyType,
NotifyTarget: reqData.NotifyTarget,
}.AddRow()
@ -391,6 +393,7 @@ func (Project) Edit(gp *core.Goploy) core.Response {
AfterDeployScript string `json:"afterDeployScript"`
TransferType string `json:"transferType"`
TransferOption string `json:"transferOption"`
DeployServerMode string `json:"deployServerMode"`
NotifyType uint8 `json:"notifyType"`
NotifyTarget string `json:"notifyTarget"`
}
@ -426,6 +429,7 @@ func (Project) Edit(gp *core.Goploy) core.Response {
AfterDeployScript: reqData.AfterDeployScript,
TransferType: reqData.TransferType,
TransferOption: reqData.TransferOption,
DeployServerMode: reqData.DeployServerMode,
NotifyType: reqData.NotifyType,
NotifyTarget: reqData.NotifyTarget,
}.EditRow()

View File

@ -31,6 +31,7 @@ type Project struct {
AfterDeployScript string `json:"afterDeployScript"`
TransferType string `json:"transferType"`
TransferOption string `json:"transferOption"`
DeployServerMode string `json:"deployServerMode"`
AutoDeploy uint8 `json:"autoDeploy"`
PublisherID int64 `json:"publisherId"`
PublisherName string `json:"publisherName"`
@ -96,6 +97,7 @@ func (p Project) AddRow() (int64, error) {
"after_deploy_script",
"transfer_type",
"transfer_option",
"deploy_server_mode",
"notify_type",
"notify_target",
).
@ -117,6 +119,7 @@ func (p Project) AddRow() (int64, error) {
p.AfterDeployScript,
p.TransferType,
p.TransferOption,
p.DeployServerMode,
p.NotifyType,
p.NotifyTarget,
).
@ -149,6 +152,7 @@ func (p Project) EditRow() error {
"after_deploy_script": p.AfterDeployScript,
"transfer_type": p.TransferType,
"transfer_option": p.TransferOption,
"deploy_server_mode": p.DeployServerMode,
"notify_type": p.NotifyType,
"notify_target": p.NotifyTarget,
}).
@ -260,6 +264,7 @@ func (p Project) GetList() (Projects, error) {
after_deploy_script,
transfer_type,
transfer_option,
deploy_server_mode,
auto_deploy,
notify_type,
notify_target,
@ -308,6 +313,7 @@ func (p Project) GetList() (Projects, error) {
&project.AfterDeployScript,
&project.TransferType,
&project.TransferOption,
&project.DeployServerMode,
&project.AutoDeploy,
&project.NotifyType,
&project.NotifyTarget,
@ -414,6 +420,7 @@ func (p Project) GetData() (Project, error) {
after_deploy_script,
transfer_type,
transfer_option,
deploy_server_mode,
auto_deploy,
deploy_state,
notify_type,
@ -445,6 +452,7 @@ func (p Project) GetData() (Project, error) {
&project.AfterDeployScript,
&project.TransferType,
&project.TransferOption,
&project.DeployServerMode,
&project.AutoDeploy,
&project.DeployState,
&project.NotifyType,

View File

@ -15,4 +15,8 @@ CREATE TABLE IF NOT EXISTS `operation_log` (
KEY `idx_request_time` (`request_time`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO `permission` (`id`, `pid`, `name`, `sort`, `description`) VALUES (74, 1, 'ShowOperationLogPage', 0, '');
INSERT INTO `permission` (`id`, `pid`, `name`, `sort`, `description`) VALUES (74, 1, 'ShowOperationLogPage', 0, '');
ALTER TABLE `project` ADD COLUMN `deploy_server_mode` varchar(255) NOT NULL DEFAULT '' COMMENT 'serial | parallel' AFTER `transfer_option`;
UPDATE `project` SET `deploy_server_mode` = 'parallel';

View File

@ -17,6 +17,7 @@ CREATE TABLE IF NOT EXISTS `project` (
`after_deploy_script` text NOT NULL COMMENT '',
`transfer_type` varchar(255) NOT NULL DEFAULT '',
`transfer_option` varchar(255) NOT NULL DEFAULT '',
`deploy_server_mode` varchar(255) NOT NULL DEFAULT '' COMMENT 'serial | parallel',
`auto_deploy` tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '0.disable 1.webhook',
`state` tinyint(4) UNSIGNED NOT NULL DEFAULT 1 COMMENT '0.disable 1.enable',
`deploy_state` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '0.not deploy 1.deploying 2.success 3.fail',

View File

@ -201,9 +201,23 @@ func parseGITLog(rawCommitLog string) []CommitInfo {
Author: commitRowSplit[1],
Timestamp: timestamp,
Message: commitRowSplit[3],
Tag: commitRowSplit[4],
Tag: extractTag(commitRowSplit[4]),
Diff: strings.Trim(commitRowSplit[5], "\n"),
})
}
return commitList
}
func extractTag(raw string) string {
if len(raw) < 3 {
return ""
}
raw = raw[2 : len(raw)-1]
for _, row := range strings.Split(raw, ",") {
if strings.Contains(row, "tag: ") {
raw = row[5:]
break
}
}
return raw
}

View File

@ -207,151 +207,156 @@ func (gsync Gsync) runAfterPullScript() (string, error) {
if output, err := handler.CombinedOutput(); err != nil {
return "", err
} else {
_ = os.Remove(scriptName)
_ = os.Remove(scriptFullName)
return string(output), nil
}
}
func (gsync Gsync) remoteSync(msgChIn chan<- syncMessage) {
for _, projectServer := range gsync.ProjectServers {
go func(projectServer model.ProjectServer) {
project := gsync.Project
publishTraceModel := model.PublishTrace{
Token: project.LastPublishToken,
ProjectID: project.ID,
ProjectName: project.Name,
PublisherID: gsync.UserInfo.ID,
PublisherName: gsync.UserInfo.Name,
Type: model.Deploy,
}
// write after deploy script for rsync
scriptName := fmt.Sprintf("goploy-after-deploy-p%d-s%d.%s", project.ID, projectServer.ServerID, utils.GetScriptExt(project.AfterDeployScriptMode))
if len(project.AfterDeployScript) != 0 {
scriptContent := ReplaceProjectVars(project.AfterDeployScript, project)
scriptContent = ReplaceProjectServerVars(scriptContent, projectServer)
_ = ioutil.WriteFile(path.Join(core.GetProjectPath(project.ID), scriptName), []byte(scriptContent), 0755)
}
var serverSync = func(projectServer model.ProjectServer) {
project := gsync.Project
publishTraceModel := model.PublishTrace{
Token: project.LastPublishToken,
ProjectID: project.ID,
ProjectName: project.Name,
PublisherID: gsync.UserInfo.ID,
PublisherName: gsync.UserInfo.Name,
Type: model.Deploy,
}
// write after deploy script for rsync
scriptName := fmt.Sprintf("goploy-after-deploy-p%d-s%d.%s", project.ID, projectServer.ServerID, utils.GetScriptExt(project.AfterDeployScriptMode))
if len(project.AfterDeployScript) != 0 {
scriptContent := ReplaceProjectVars(project.AfterDeployScript, project)
scriptContent = ReplaceProjectServerVars(scriptContent, projectServer)
_ = ioutil.WriteFile(path.Join(core.GetProjectPath(project.ID), scriptName), []byte(scriptContent), 0755)
}
transmitterEntity := transmitter.New(project, projectServer)
logCmd := transmitterEntity.String()
core.Log(core.TRACE, "projectID: "+strconv.FormatInt(project.ID, 10)+" "+logCmd)
ext, _ := json.Marshal(struct {
ServerID int64 `json:"serverId"`
ServerName string `json:"serverName"`
Command string `json:"command"`
}{projectServer.ServerID, projectServer.ServerName, logCmd})
publishTraceModel.Ext = string(ext)
transmitterEntity := transmitter.New(project, projectServer)
logCmd := transmitterEntity.String()
core.Log(core.TRACE, "projectID: "+strconv.FormatInt(project.ID, 10)+" "+logCmd)
ext, _ := json.Marshal(struct {
ServerID int64 `json:"serverId"`
ServerName string `json:"serverName"`
Command string `json:"command"`
}{projectServer.ServerID, projectServer.ServerName, logCmd})
publishTraceModel.Ext = string(ext)
if transmitterOutput, err := transmitterEntity.Exec(); err != nil {
core.Log(core.ERROR, fmt.Sprintf("projectID: %d transmit exec err: %s, output: %s", project.ID, err, transmitterOutput))
publishTraceModel.Detail = fmt.Sprintf("err: %s\noutput: %s", err, transmitterOutput)
publishTraceModel.State = model.Fail
publishTraceModel.AddRow()
msgChIn <- syncMessage{
serverName: projectServer.ServerName,
projectID: project.ID,
detail: err.Error(),
state: model.ProjectFail,
}
return
} else {
publishTraceModel.Detail = transmitterOutput
publishTraceModel.State = model.Success
publishTraceModel.AddRow()
if transmitterOutput, err := transmitterEntity.Exec(); err != nil {
core.Log(core.ERROR, fmt.Sprintf("projectID: %d transmit exec err: %s, output: %s", project.ID, err, transmitterOutput))
publishTraceModel.Detail = fmt.Sprintf("err: %s\noutput: %s", err, transmitterOutput)
publishTraceModel.State = model.Fail
publishTraceModel.AddRow()
msgChIn <- syncMessage{
serverName: projectServer.ServerName,
projectID: project.ID,
detail: err.Error(),
state: model.ProjectFail,
}
return
} else {
publishTraceModel.Detail = transmitterOutput
publishTraceModel.State = model.Success
publishTraceModel.AddRow()
}
var afterDeployCommands []string
cmdEntity := cmd.New(projectServer.ServerOS)
if len(project.SymlinkPath) != 0 {
destDir := path.Join(project.SymlinkPath, project.LastPublishToken)
afterDeployCommands = append(afterDeployCommands, cmdEntity.Symlink(destDir, project.Path))
}
var afterDeployCommands []string
cmdEntity := cmd.New(projectServer.ServerOS)
if len(project.SymlinkPath) != 0 {
destDir := path.Join(project.SymlinkPath, project.LastPublishToken)
afterDeployCommands = append(afterDeployCommands, cmdEntity.Symlink(destDir, project.Path))
}
if len(project.AfterDeployScript) != 0 {
afterDeployScriptPath := path.Join(project.Path, scriptName)
afterDeployCommands = append(afterDeployCommands, cmdEntity.Script(project.AfterDeployScriptMode, afterDeployScriptPath))
afterDeployCommands = append(afterDeployCommands, cmdEntity.Remove(afterDeployScriptPath))
}
// no symlink and deploy script
if len(afterDeployCommands) == 0 {
msgChIn <- syncMessage{
serverName: projectServer.ServerName,
projectID: project.ID,
state: model.ProjectSuccess,
}
return
}
completeAfterDeployCmd := strings.Join(afterDeployCommands, "&&")
publishTraceModel.Type = model.AfterDeploy
ext, _ = json.Marshal(struct {
ServerID int64 `json:"serverId"`
ServerName string `json:"serverName"`
Script string `json:"script"`
}{projectServer.ServerID, projectServer.ServerName, completeAfterDeployCmd})
publishTraceModel.Ext = string(ext)
client, err := projectServer.ToSSHConfig().Dial()
if err != nil {
core.Log(core.ERROR, err.Error())
publishTraceModel.Detail = err.Error()
publishTraceModel.State = model.Fail
publishTraceModel.AddRow()
msgChIn <- syncMessage{
serverName: projectServer.ServerName,
projectID: project.ID,
detail: err.Error(),
state: model.ProjectFail,
}
return
}
defer client.Close()
session, sessionErr := client.NewSession()
if sessionErr != nil {
core.Log(core.ERROR, sessionErr.Error())
publishTraceModel.Detail = sessionErr.Error()
publishTraceModel.State = model.Fail
publishTraceModel.AddRow()
msgChIn <- syncMessage{
serverName: projectServer.ServerName,
projectID: project.ID,
detail: sessionErr.Error(),
state: model.ProjectFail,
}
return
}
defer session.Close()
core.Log(core.TRACE, fmt.Sprintf("projectID: %d ssh exec: %s", project.ID, completeAfterDeployCmd))
if output, err := session.CombinedOutput(completeAfterDeployCmd); err != nil {
core.Log(core.ERROR, fmt.Sprintf("projectID: %d ssh exec err: %s, output: %s", project.ID, err, output))
publishTraceModel.Detail = fmt.Sprintf("err: %s\noutput: %s", err, output)
publishTraceModel.State = model.Fail
if _, err := publishTraceModel.AddRow(); err != nil {
core.Log(core.ERROR, "projectID: "+strconv.FormatInt(project.ID, 10)+" "+err.Error())
}
msgChIn <- syncMessage{
serverName: projectServer.ServerName,
projectID: project.ID,
detail: fmt.Sprintf("%s\noutput: %s", err.Error(), output),
state: model.ProjectFail,
}
return
} else {
publishTraceModel.Detail = string(output)
publishTraceModel.State = model.Success
if _, err := publishTraceModel.AddRow(); err != nil {
core.Log(core.ERROR, "projectID: "+strconv.FormatInt(project.ID, 10)+" "+err.Error())
}
}
if len(project.AfterDeployScript) != 0 {
afterDeployScriptPath := path.Join(project.Path, scriptName)
afterDeployCommands = append(afterDeployCommands, cmdEntity.Script(project.AfterDeployScriptMode, afterDeployScriptPath))
afterDeployCommands = append(afterDeployCommands, cmdEntity.Remove(afterDeployScriptPath))
}
// no symlink and deploy script
if len(afterDeployCommands) == 0 {
msgChIn <- syncMessage{
serverName: projectServer.ServerName,
projectID: project.ID,
state: model.ProjectSuccess,
}
return
}(projectServer)
}
completeAfterDeployCmd := strings.Join(afterDeployCommands, "&&")
publishTraceModel.Type = model.AfterDeploy
ext, _ = json.Marshal(struct {
ServerID int64 `json:"serverId"`
ServerName string `json:"serverName"`
Script string `json:"script"`
}{projectServer.ServerID, projectServer.ServerName, completeAfterDeployCmd})
publishTraceModel.Ext = string(ext)
client, err := projectServer.ToSSHConfig().Dial()
if err != nil {
core.Log(core.ERROR, err.Error())
publishTraceModel.Detail = err.Error()
publishTraceModel.State = model.Fail
publishTraceModel.AddRow()
msgChIn <- syncMessage{
serverName: projectServer.ServerName,
projectID: project.ID,
detail: err.Error(),
state: model.ProjectFail,
}
return
}
defer client.Close()
session, sessionErr := client.NewSession()
if sessionErr != nil {
core.Log(core.ERROR, sessionErr.Error())
publishTraceModel.Detail = sessionErr.Error()
publishTraceModel.State = model.Fail
publishTraceModel.AddRow()
msgChIn <- syncMessage{
serverName: projectServer.ServerName,
projectID: project.ID,
detail: sessionErr.Error(),
state: model.ProjectFail,
}
return
}
defer session.Close()
core.Log(core.TRACE, fmt.Sprintf("projectID: %d ssh exec: %s", project.ID, completeAfterDeployCmd))
if output, err := session.CombinedOutput(completeAfterDeployCmd); err != nil {
core.Log(core.ERROR, fmt.Sprintf("projectID: %d ssh exec err: %s, output: %s", project.ID, err, output))
publishTraceModel.Detail = fmt.Sprintf("err: %s\noutput: %s", err, output)
publishTraceModel.State = model.Fail
if _, err := publishTraceModel.AddRow(); err != nil {
core.Log(core.ERROR, "projectID: "+strconv.FormatInt(project.ID, 10)+" "+err.Error())
}
msgChIn <- syncMessage{
serverName: projectServer.ServerName,
projectID: project.ID,
detail: fmt.Sprintf("%s\noutput: %s", err.Error(), output),
state: model.ProjectFail,
}
return
} else {
publishTraceModel.Detail = string(output)
publishTraceModel.State = model.Success
if _, err := publishTraceModel.AddRow(); err != nil {
core.Log(core.ERROR, "projectID: "+strconv.FormatInt(project.ID, 10)+" "+err.Error())
}
}
msgChIn <- syncMessage{
serverName: projectServer.ServerName,
projectID: project.ID,
state: model.ProjectSuccess,
}
return
}
for _, projectServer := range gsync.ProjectServers {
if gsync.Project.DeployServerMode == "serial" {
serverSync(projectServer)
} else {
go serverSync(projectServer)
}
}
}

11
web/environment.d.ts vendored Normal file
View File

@ -0,0 +1,11 @@
declare global {
namespace NodeJS {
interface ProcessEnv {
VITE_APP_BASE_API: string
}
}
}
// If this file has no import/export statements (i.e. is a script)
// convert it into a module by adding an empty export statement.
export {}

View File

@ -19,6 +19,11 @@ service.interceptors.request.use(
(config: AxiosRequestConfig) => {
// do something before request is sent
config.headers[NamespaceKey] = getNamespaceId()
let href = window.location.hash.slice(1)
if (href.indexOf('?') > -1) {
href = href.substring(0, href.indexOf('?'))
}
config.headers['Router'] = href
return config
},
(error: AxiosError) => {

View File

@ -10,6 +10,19 @@ export interface LoginLogData {
loginTime: string
}
export interface OperationLogData {
[key: string]: any
id: number
namespaceId: number
userId: number
router: string
api: string
requestTime: string
requestData: string
responseTime: string
responseData: string
}
export interface SftpLogData {
id: number
namespaceId: number
@ -83,6 +96,46 @@ export class LoginLogTotal extends Request {
}
}
export class OperationLogList extends Request {
readonly url = '/log/getOperationLogList'
readonly method = 'get'
public pagination: Pagination
public param: {
username: string
router: string
api: string
}
public declare datagram: {
list: OperationLogData[]
}
constructor(param: OperationLogList['param'], pagination: Pagination) {
super()
this.pagination = pagination
this.param = { ...param, ...pagination }
}
}
export class OperationLogTotal extends Request {
readonly url = '/log/getOperationLogTotal'
readonly method = 'get'
public param: {
username: string
router: string
api: string
}
public declare datagram: Total
constructor(param: OperationLogTotal['param']) {
super()
this.param = param
}
}
export class SftpLogList extends Request {
readonly url = '/log/getSftpLogList'
readonly method = 'get'

View File

@ -98,8 +98,11 @@ export class MonitorCheck extends Request {
export class MonitorToggle extends Request {
readonly url = '/monitor/toggle'
readonly method = 'put'
public param: ID
constructor(param: ID) {
public param: {
id: number
state: number
}
constructor(param: MonitorToggle['param']) {
super()
this.param = param
}

View File

@ -21,6 +21,7 @@ export interface ProjectData {
afterDeployScript: string
transferType: string
transferOption: string
deployServerMode: string
autoDeploy: number
publisherId: number
publisherName: string
@ -162,6 +163,7 @@ export class ProjectAdd extends Request {
afterDeployScript: string
transferType: string
transferOption: string
deployServerMode: string
serverIds: number[]
userIds: number[]
notifyType: number
@ -193,6 +195,7 @@ export class ProjectEdit extends Request {
afterDeployScript: string
transferType: string
transferOption: string
deployServerMode: string
notifyType: number
notifyTarget: string
}

View File

Before

Width:  |  Height:  |  Size: 867 B

After

Width:  |  Height:  |  Size: 867 B

View File

@ -1 +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="1545633168327" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2047" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M896 896l-45.44-45.12A63.808 63.808 0 0 1 896 832a64 64 0 0 0 64-64V128a64 64 0 0 0-64-64H256a64 64 0 0 0-64 64v5.44c0 17.6-7.04 33.536-18.56 45.12L128 133.44V128A128 128 0 0 1 256 0h640a128 128 0 0 1 128 128v640a128 128 0 0 1-128 128zM64 256v640a64 64 0 0 0 64 64h640a64 64 0 0 0 64-64V256a64 64 0 0 0-64-64H128a64 64 0 0 0-64 64z m704-128a128 128 0 0 1 128 128v640a128 128 0 0 1-128 128H128A128 128 0 0 1 0 896V256a128 128 0 0 1 128-128h640z" p-id="2048"></path><path d="M160 256h384a32 32 0 0 1 0 64H160a32 32 0 0 1 0-64z m576 64a32 32 0 1 1 0-64 32 32 0 0 1 0 64zM64 384h768v64H64v-64z" p-id="2049"></path></svg>
<?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="1663145513732" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1455" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M891.19 652.72H635.52a31.21 31.21 0 0 0-30.1 22.61 100.24 100.24 0 0 1-192.89 0 31.23 31.23 0 0 0-30.1-22.61H132.81A61.81 61.81 0 0 0 71 714.53v182.66A61.81 61.81 0 0 0 132.81 959h758.38A61.81 61.81 0 0 0 953 897.19V714.53a61.81 61.81 0 0 0-61.81-61.81zM643.58 142.21v3.34c-0.67-2-0.67-2.67 0-3.34-0.67-1.32 0-2 0-3.32z" p-id="1456"></path><path d="M846.6 65H338c-18.64 0-34 17.3-34 37.94v501.25h135.22c0 42.6 30.62 77.21 67.9 77.21 37.94 0 67.89-34.61 67.89-77.21h305.54V102.94c0-21.3-15.31-37.94-33.95-37.94z m-399 168.35h289.49c13.86 0 24.12 11.33 24.72 24.72S750 282.8 737.09 282.8H447.62c-13.86 0-24.12-11.33-24.72-24.73s11.79-24.72 24.72-24.72zM737.39 455.5H447.92c-13.86 0-24.12-11.32-24.72-24.72s11.79-24.72 24.72-24.72h289.47c13.86 0 24.12 11.33 24.72 24.72s-11.79 24.72-24.72 24.72zM194.34 142.21h41.85v462H134.34v-402a60 60 0 0 1 60-60z" p-id="1457"></path></svg>

Before

Width:  |  Height:  |  Size: 1002 B

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -24,6 +24,8 @@
"serverId": "Server ID",
"serverName": "Server name",
"serverDescription": "Server description",
"serial": "Serial",
"parallel": "Parallel",
"process": "Process",
"template": "Template",
"package": "Package",
@ -299,6 +301,7 @@
"expandAll": "Expand all",
"collapseAll": "Collapse all",
"unmarkAll": "Unmark all",
"copyAll": "Copy all",
"tips":
"1.Hold down ALT and click label to achieve highlighting<br>2.Hold down SHIFT and click label to view the JSON path"
},

View File

@ -24,6 +24,8 @@
"serverId": "服务器ID",
"serverName": "服务器名称",
"serverDescription": "服务器描述",
"serial": "串行",
"parallel": "并行",
"process": "进程",
"template": "模板",
"package": "安装包",
@ -146,6 +148,7 @@
"member": "成员设置",
"log": "日志记录",
"loginLog": "登录日志",
"operationLog": "操作日志",
"publishLog": "构建日志",
"sftpLog": "SFTP日志",
"terminalLog": "终端日志"
@ -223,7 +226,8 @@
"ShowServerProcessPage": "查看服务器进程管理",
"AddServerProcess": "新增服务器进程",
"EditServerProcess": "编辑服务器进程",
"DeleteServerProcess": "删除服务器进程"
"DeleteServerProcess": "删除服务器进程",
"ShowOperationLogPage": "查看操作日志"
},
"tagsView": {
"refresh": "刷新",
@ -295,6 +299,7 @@
"expandAll": "展开所有",
"collapseAll": "收起所有",
"unmarkAll": "取消高亮",
"copyAll": "复制所有",
"tips": "1.按住ALT点击label可以实现高亮\n2.按住SHIFT可以查看JSON路径"
},
"projectPage": {

View File

@ -7,26 +7,29 @@
align="middle"
>
<el-row>
<el-button type="text" @click="switchJSONView">
<el-button type="primary" text @click="switchJSONView">
<svg-icon icon-class="switch" /> JSON Pretty
</el-button>
<el-button
v-show="json.view === 'pretty'"
type="text"
type="primary"
text
@click="expandAll"
>
{{ $t('JSONPage.expandAll') }}
</el-button>
<el-button
v-show="json.view === 'pretty'"
type="text"
type="primary"
text
@click="collapseAll"
>
{{ $t('JSONPage.collapseAll') }}
</el-button>
<el-button
v-show="json.view === 'pretty'"
type="text"
type="primary"
text
@click="unmarkAll"
>
{{ $t('JSONPage.unmarkAll') }}
@ -34,7 +37,7 @@
</el-row>
<el-row>
<el-tooltip class="item" effect="dark" placement="bottom-end">
<el-button type="text" icon="el-icon-question" />
<el-button type="primary" text :icon="QuestionFilled" />
<template #content>
<span style="white-space: pre-line">
{{ $t('JSONPage.tips') }}
@ -63,8 +66,9 @@
</template>
<script lang="ts" setup>
import './jsonTree.css'
import { jsonTree } from './jsonTree'
import '@/components/JSONTree/index.css'
import { jsonTree } from '@/components/JSONTree'
import { QuestionFilled } from '@element-plus/icons-vue'
import { ref, reactive } from 'vue'
defineProps({
modelValue: {
@ -75,7 +79,7 @@ defineProps({
const json = reactive({
view: 'raw',
input: '',
tree: {},
tree: {} as any,
})
const jsonPrettyString = ref()
const handleInput = () => {

View File

@ -72,4 +72,5 @@ export default Object.freeze({
AddServerProcess: 71,
EditServerProcess: 72,
DeleteServerProcess: 73,
ShowOperationLogPage: 74,
})

View File

@ -222,6 +222,16 @@ export default <RouteRecordRaw[]>[
permissions: [permission.ShowLoginLogPage],
},
},
{
path: 'operationLog',
name: 'OperationLog',
component: () => import('@/views/log/operationLog.vue'),
meta: {
title: 'operationLog',
icon: 'log',
permissions: [permission.ShowOperationLogPage],
},
},
{
path: 'publishLog',
name: 'PublishLog',

View File

@ -668,7 +668,7 @@ const rebuild = (data: PublishTraceData & PublishTraceExt) => {
)
.then(() => {
filterloading.value = true
new DeployRebuild({ projectId: data.projectId, token: data.token })
new DeployRebuild({ projectId: props.projectRow.id, token: data.token })
.request()
.then((response) => {
if (response.data === 'symlink') {

View File

@ -24,8 +24,8 @@
<RepoURL
style="font-size: 12px"
:url="projectRow.url"
:suffix="'/tree/' + scope.row.shortTag"
:text="scope.row.shortTag"
:suffix="'/tree/' + scope.row.tag"
:text="scope.row.tag"
>
</RepoURL>
</template>
@ -114,19 +114,7 @@ watch(
new RepositoryTagList({ id: props.projectRow.id })
.request()
.then((response) => {
tableData.value = response.data.list.map((element) => {
let shortTag = element.tag.replace(/[()]/g, '')
for (const tag of shortTag.split(',')) {
if (tag.indexOf('tag: ') !== -1) {
shortTag = tag.replace('tag: ', '').trim()
break
}
}
return Object.assign(element, {
projectId: props.projectRow.id,
shortTag: shortTag,
})
})
tableData.value = response.data.list
})
.finally(() => {
tableLoading.value = false

View File

@ -0,0 +1,228 @@
<template>
<el-row class="app-container">
<el-row class="app-bar" type="flex" justify="space-between">
<el-row>
<el-input
v-model="searchParam.username"
style="width: 200px"
placeholder="Filter the username"
/>
<el-input
v-model="searchParam.router"
style="width: 200px"
placeholder="Filter the router"
/>
<el-input
v-model="searchParam.api"
style="width: 200px"
placeholder="Filter the api"
/>
<el-button
:loading="tableLoading"
type="primary"
:icon="Search"
@click="searchList"
/>
</el-row>
</el-row>
<el-row class="app-table">
<el-table
v-loading="tableLoading"
stripe
highlight-current-row
height="100%"
:data="tableData"
>
<el-table-column prop="id" label="ID" width="100" />
<el-table-column prop="username" label="Username" width="150" />
<el-table-column prop="routerName" label="Router" min-width="230">
<template #default="scope">
{{ scope.row.routerName }}({{ scope.row.router }})
</template>
</el-table-column>
<el-table-column prop="api" label="API" min-width="200" />
<el-table-column label="Request Data" width="160" align="center">
<template #default="scope">
<el-button
type="primary"
text
@click="showJSON(scope.row.requestData)"
>
{{ $t('detail') }}
</el-button>
</template>
</el-table-column>
<el-table-column label="Response Data" width="160" align="center">
<template #default="scope">
<el-button
type="primary"
text
@click="showJSON(scope.row.responseData)"
>
{{ $t('detail') }}
</el-button>
</template>
</el-table-column>
<el-table-column
prop="requestTime"
label="Request time"
width="155"
align="center"
/>
<el-table-column
prop="responseTime"
label="Response time"
width="155"
align="center"
/>
</el-table>
</el-row>
<el-row type="flex" justify="end" class="app-page">
<el-pagination
:total="pagination.total"
:page-size="pagination.rows"
background
layout="total, prev, pager, next"
@current-change="handlePageChange"
/>
</el-row>
<el-dialog v-model="json.dialog" :width="'70%'">
<el-row
class="json-helper"
type="flex"
justify="space-between"
align="middle"
>
<el-row>
<el-button type="primary" text @click="expandAll">
{{ $t('JSONPage.expandAll') }}
</el-button>
<el-button type="primary" text @click="collapseAll">
{{ $t('JSONPage.collapseAll') }}
</el-button>
<el-button type="primary" text @click="unmarkAll">
{{ $t('JSONPage.unmarkAll') }}
</el-button>
<el-button type="primary" text @click="copyAll">
{{ $t('JSONPage.copyAll') }}
</el-button>
</el-row>
<el-row>
<el-tooltip class="item" effect="dark" placement="bottom-end">
<el-button type="primary" text :icon="QuestionFilled" />
<template #content>
<span style="white-space: pre-line">
{{ $t('JSONPage.tips') }}
</span>
</template>
</el-tooltip>
</el-row>
</el-row>
<div ref="jsonPrettyString" v-loading="json.loading" />
</el-dialog>
</el-row>
</template>
<script lang="ts">
export default { name: 'OperationLog' }
</script>
<script lang="ts" setup>
import '@/components/JSONTree/index.css'
import { jsonTree } from '@/components/JSONTree'
import { ElMessage } from 'element-plus'
import { QuestionFilled, Search } from '@element-plus/icons-vue'
import { copy } from '@/utils'
import { OperationLogList, OperationLogTotal } from '@/api/log'
import { ref, reactive, nextTick } from 'vue'
import { useStore } from 'vuex'
import { useI18n } from 'vue-i18n'
const store = useStore()
const { t } = useI18n()
const searchParam = ref({ username: '', router: '', api: '' })
const tableLoading = ref(false)
const tableData = ref<OperationLogList['datagram']['list']>([])
const pagination = ref({ page: 1, rows: 20, total: 0 })
const jsonPrettyString = ref()
const json = reactive({
str: '',
tree: {} as any,
dialog: false,
loading: false,
})
getList()
getTotal()
function searchList() {
pagination.value.page = 1
getList()
getTotal()
}
function getList() {
tableLoading.value = true
tableData.value = []
new OperationLogList(searchParam.value, pagination.value)
.request()
.then((response) => {
const routerObj: any = {}
for (const father of store.state.permission.routes) {
try {
for (const children of father.children) {
routerObj[father.path + '/' + children.path] = t(
'route.' + children.meta.title
)
}
} catch (error) {}
}
tableData.value = response.data.list.map((item) => {
item.routerName = routerObj[item.router] || item.router
return item
})
})
.finally(() => {
tableLoading.value = false
})
}
function getTotal() {
new OperationLogTotal(searchParam.value).request().then((response) => {
pagination.value.total = response.data.total
})
}
function handlePageChange(val = 1) {
pagination.value.page = val
getList()
}
function showJSON(jsonStr: string) {
json.str = jsonStr
json.dialog = true
json.loading = true
nextTick(() => {
jsonPrettyString.value.innerText = ''
try {
const data = JSON.parse(jsonStr)
json.tree = jsonTree.create(data, jsonPrettyString.value)
} catch (error) {
jsonPrettyString.value.innerText = error.message
}
json.loading = false
})
}
function expandAll() {
json.tree.expand()
}
function collapseAll() {
json.tree.collapse()
}
function unmarkAll() {
json.tree.unmarkAll()
}
function copyAll() {
copy(json.tree.toSourceJSON())
ElMessage.success('Success')
}
</script>

View File

@ -526,7 +526,7 @@ function handleToggle(data: MonitorData) {
}
)
.then(() => {
new MonitorToggle({ id: data.id }).request().then(() => {
new MonitorToggle({ id: data.id, state: 0 }).request().then(() => {
ElMessage.success(t('close'))
getList()
})
@ -535,7 +535,7 @@ function handleToggle(data: MonitorData) {
ElMessage.info('Cancel')
})
} else {
new MonitorToggle({ id: data.id }).request().then(() => {
new MonitorToggle({ id: data.id, state: 1 }).request().then(() => {
ElMessage.success(t('open'))
getList()
})

View File

@ -45,7 +45,7 @@
<el-form-item
:label="$t('role')"
label-width="60px"
prop="role"
prop="roleId"
style="margin-bottom: 5px"
>
<el-select

View File

@ -189,7 +189,7 @@
</el-tooltip>
</template>
<el-row type="flex" style="width: 100%">
<el-select v-model="formData.repoType" style="width: 65px">
<el-select v-model="formData.repoType" style="width: 70px">
<el-option label="git" value="git" />
<el-option label="svn" value="svn" />
<el-option label="ftp" value="ftp" />
@ -331,6 +331,13 @@
</el-row>
</el-form-item>
<el-form-item :label="$t('server')" prop="serverIds">
<el-radio-group
v-model="formData.deployServerMode"
style="margin-bottom: 5px"
>
<el-radio label="parallel">{{ $t('parallel') }}</el-radio>
<el-radio label="serial">{{ $t('serial') }}</el-radio>
</el-radio-group>
<el-select
v-model="formData.serverIds"
multiple
@ -981,6 +988,7 @@ const tempFormData = {
branch: '',
transferType: 'rsync',
transferOption: '-rtv --exclude .git',
deployServerMode: 'parallel',
serverIds: [] as number[],
userIds: [] as number[],
review: 0,
@ -1331,20 +1339,3 @@ function restoreFormData() {
formData.value = { ...tempFormData }
}
</script>
<style lang="scss">
@import '@/styles/mixin.scss';
.file-dialog {
.el-dialog__body {
padding-top: 20px;
}
.el-icon-upload {
font-size: 14px;
}
.file-form {
height: 520px;
overflow-y: auto;
@include scrollBar();
}
}
</style>