mirror of
https://gitee.com/goploy/goploy.git
synced 2024-11-29 18:57:59 +08:00
A deploy server mode
This commit is contained in:
parent
850d1b2fa9
commit
4e122724bc
@ -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()
|
||||
|
@ -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,
|
||||
|
@ -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';
|
@ -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',
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
11
web/environment.d.ts
vendored
Normal 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 {}
|
@ -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) => {
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Before Width: | Height: | Size: 867 B After Width: | Height: | Size: 867 B |
@ -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 |
@ -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"
|
||||
},
|
||||
|
@ -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": {
|
||||
|
@ -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 = () => {
|
||||
|
@ -72,4 +72,5 @@ export default Object.freeze({
|
||||
AddServerProcess: 71,
|
||||
EditServerProcess: 72,
|
||||
DeleteServerProcess: 73,
|
||||
ShowOperationLogPage: 74,
|
||||
})
|
||||
|
@ -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',
|
||||
|
@ -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') {
|
||||
|
@ -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
|
||||
|
228
web/src/views/log/operationLog.vue
Normal file
228
web/src/views/log/operationLog.vue
Normal 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>
|
@ -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()
|
||||
})
|
||||
|
@ -45,7 +45,7 @@
|
||||
<el-form-item
|
||||
:label="$t('role')"
|
||||
label-width="60px"
|
||||
prop="role"
|
||||
prop="roleId"
|
||||
style="margin-bottom: 5px"
|
||||
>
|
||||
<el-select
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user