From 55e6299da0883c40740306060bdc466f409a8bd8 Mon Sep 17 00:00:00 2001 From: zhenorzz Date: Tue, 16 May 2023 17:37:19 +0800 Subject: [PATCH] rename file and edit file via sftp --- cmd/server/api/middleware/log.go | 91 ++++++++++++++-- cmd/server/api/server.go | 142 +++++++++++++++++++------ config/permission.go | 2 + database/1.14.0.sql | 5 +- database/goploy.sql | 2 + internal/model/sftp_log.go | 2 + web/components.d.ts | 1 + web/src/api/server.ts | 32 ++++++ web/src/lang/en.json | 4 +- web/src/lang/zh-cn.json | 4 +- web/src/permission.ts | 2 + web/src/views/monitor/index.vue | 2 +- web/src/views/server/sftp/explorer.vue | 60 ++++++++++- web/src/views/server/sftp/index.vue | 89 ++++++++++++++++ 14 files changed, 387 insertions(+), 51 deletions(-) diff --git a/cmd/server/api/middleware/log.go b/cmd/server/api/middleware/log.go index 9eea6d8..725f284 100644 --- a/cmd/server/api/middleware/log.go +++ b/cmd/server/api/middleware/log.go @@ -6,8 +6,9 @@ package middleware import ( "encoding/json" + "fmt" "github.com/zhenorzz/goploy/internal/log" - model2 "github.com/zhenorzz/goploy/internal/model" + "github.com/zhenorzz/goploy/internal/model" "github.com/zhenorzz/goploy/internal/server" "github.com/zhenorzz/goploy/internal/server/response" "strconv" @@ -26,7 +27,7 @@ func AddLoginLog(gp *server.Goploy, resp server.Response) { account = reqData.Account } - err := model2.LoginLog{ + err := model.LoginLog{ Account: account, RemoteAddr: gp.Request.RemoteAddr, UserAgent: gp.Request.UserAgent(), @@ -56,7 +57,7 @@ func AddOPLog(gp *server.Goploy, resp server.Response) { responseData = string(jsonBytes) } } - err := model2.OperationLog{ + err := model.OperationLog{ NamespaceID: gp.Namespace.ID, UserID: gp.UserInfo.ID, Router: gp.Request.Header.Get("Router"), @@ -82,13 +83,13 @@ func AddUploadLog(gp *server.Goploy, resp server.Response) { _ = file.Close() } - err := model2.SftpLog{ + err := model.SftpLog{ NamespaceID: gp.Namespace.ID, UserID: gp.UserInfo.ID, ServerID: serverID, RemoteAddr: gp.Request.RemoteAddr, UserAgent: gp.Request.UserAgent(), - Type: model2.SftpLogTypeUpload, + Type: model.SftpLogTypeUpload, Path: path, Reason: respJson.Message, }.AddRow() @@ -97,6 +98,74 @@ func AddUploadLog(gp *server.Goploy, resp server.Response) { } } +func AddEditLog(gp *server.Goploy, resp server.Response) { + var serverID int64 = 0 + var path = "" + respJson := resp.(response.JSON) + if respJson.Code != response.IllegalParam { + type ReqData struct { + ServerID int64 `json:"serverId"` + File string `json:"file"` + NewName string `json:"newName"` + CurrentName string `json:"currentName"` + } + var reqData ReqData + _ = json.Unmarshal(gp.Body, &reqData) + serverID = reqData.ServerID + path = reqData.File + } + + err := model.SftpLog{ + NamespaceID: gp.Namespace.ID, + UserID: gp.UserInfo.ID, + ServerID: serverID, + RemoteAddr: gp.Request.RemoteAddr, + UserAgent: gp.Request.UserAgent(), + Type: model.SftpLogTypeEdit, + Path: path, + Reason: respJson.Message, + }.AddRow() + if err != nil { + log.Error(err.Error()) + } +} + +func AddRenameLog(gp *server.Goploy, resp server.Response) { + var serverID int64 = 0 + var dir = "" + var currentName = "" + var newName = "" + respJson := resp.(response.JSON) + if respJson.Code != response.IllegalParam { + type ReqData struct { + ServerID int64 `json:"serverId"` + Dir string `json:"dir"` + NewName string `json:"newName"` + CurrentName string `json:"currentName"` + } + var reqData ReqData + _ = json.Unmarshal(gp.Body, &reqData) + serverID = reqData.ServerID + dir = reqData.Dir + currentName = reqData.CurrentName + newName = reqData.NewName + } + + err := model.SftpLog{ + NamespaceID: gp.Namespace.ID, + UserID: gp.UserInfo.ID, + ServerID: serverID, + RemoteAddr: gp.Request.RemoteAddr, + UserAgent: gp.Request.UserAgent(), + Type: model.SftpLogTypeRename, + Path: fmt.Sprintf("%s/%s->%s", dir, currentName, newName), + Reason: respJson.Message, + }.AddRow() + if err != nil { + log.Error(err.Error()) + } +} + func AddDeleteLog(gp *server.Goploy, resp server.Response) { var serverID int64 = 0 var path = "" @@ -112,13 +181,13 @@ func AddDeleteLog(gp *server.Goploy, resp server.Response) { path = reqData.File } - err := model2.SftpLog{ + err := model.SftpLog{ NamespaceID: gp.Namespace.ID, UserID: gp.UserInfo.ID, ServerID: serverID, RemoteAddr: gp.Request.RemoteAddr, UserAgent: gp.Request.UserAgent(), - Type: model2.SftpLogTypeDelete, + Type: model.SftpLogTypeDelete, Path: path, Reason: respJson.Message, }.AddRow() @@ -142,13 +211,13 @@ func AddDownloadLog(gp *server.Goploy, resp server.Response) { path = resp.(response.SftpFile).Filename } - err := model2.SftpLog{ + err := model.SftpLog{ NamespaceID: gp.Namespace.ID, UserID: gp.UserInfo.ID, ServerID: serverID, RemoteAddr: gp.Request.RemoteAddr, UserAgent: gp.Request.UserAgent(), - Type: model2.SftpLogTypeDownload, + Type: model.SftpLogTypeDownload, Path: path, Reason: msg, }.AddRow() @@ -172,13 +241,13 @@ func AddPreviewLog(gp *server.Goploy, resp server.Response) { path = resp.(response.SftpFile).Filename } - err := model2.SftpLog{ + err := model.SftpLog{ NamespaceID: gp.Namespace.ID, UserID: gp.UserInfo.ID, ServerID: serverID, RemoteAddr: gp.Request.RemoteAddr, UserAgent: gp.Request.UserAgent(), - Type: model2.SftpLogTypePreview, + Type: model.SftpLogTypePreview, Path: path, Reason: msg, }.AddRow() diff --git a/cmd/server/api/server.go b/cmd/server/api/server.go index e700a68..fcd00cd 100644 --- a/cmd/server/api/server.go +++ b/cmd/server/api/server.go @@ -13,7 +13,7 @@ import ( "github.com/zhenorzz/goploy/cmd/server/api/middleware" "github.com/zhenorzz/goploy/config" "github.com/zhenorzz/goploy/internal/log" - model2 "github.com/zhenorzz/goploy/internal/model" + "github.com/zhenorzz/goploy/internal/model" "github.com/zhenorzz/goploy/internal/pkg" "github.com/zhenorzz/goploy/internal/server" "github.com/zhenorzz/goploy/internal/server/response" @@ -43,6 +43,8 @@ func (s Server) Handler() []server.Route { server.NewRoute("/server/previewFile", http.MethodGet, s.PreviewFile).Permissions(config.SFTPPreviewFile).LogFunc(middleware.AddPreviewLog), server.NewRoute("/server/downloadFile", http.MethodGet, s.DownloadFile).Permissions(config.SFTPDownloadFile).LogFunc(middleware.AddDownloadLog), server.NewRoute("/server/uploadFile", http.MethodPost, s.UploadFile).Permissions(config.SFTPTransferFile).LogFunc(middleware.AddUploadLog), + server.NewRoute("/server/editFile", http.MethodPut, s.EditFile).Permissions(config.SFTPEditFile).LogFunc(middleware.AddEditLog), + server.NewRoute("/server/renameFile", http.MethodPut, s.RenameFile).Permissions(config.SFTPRenameFile).LogFunc(middleware.AddRenameLog), server.NewRoute("/server/deleteFile", http.MethodDelete, s.DeleteFile).Permissions(config.SFTPDeleteFile).LogFunc(middleware.AddDeleteLog), server.NewRoute("/server/transferFile", http.MethodPost, s.TransferFile).Permissions(config.SFTPUploadFile), server.NewRoute("/server/report", http.MethodGet, s.Report).Permissions(config.ShowServerMonitorPage), @@ -60,25 +62,25 @@ func (s Server) Handler() []server.Route { } func (Server) GetList(gp *server.Goploy) server.Response { - serverList, err := model2.Server{NamespaceID: gp.Namespace.ID}.GetList() + serverList, err := model.Server{NamespaceID: gp.Namespace.ID}.GetList() if err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } return response.JSON{ Data: struct { - Servers model2.Servers `json:"list"` + Servers model.Servers `json:"list"` }{Servers: serverList}, } } func (Server) GetOption(gp *server.Goploy) server.Response { - serverList, err := model2.Server{NamespaceID: gp.Namespace.ID}.GetAll() + serverList, err := model.Server{NamespaceID: gp.Namespace.ID}.GetAll() if err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } return response.JSON{ Data: struct { - Servers model2.Servers `json:"list"` + Servers model.Servers `json:"list"` }{Servers: serverList}, } } @@ -194,7 +196,7 @@ func (Server) Import(gp *server.Goploy) server.Response { wg.Add(1) go func() { errMsg := "" - srv := model2.Server{ + srv := model.Server{ NamespaceID: gp.Namespace.ID, } srv.Name = record[headerIdx["name"]] @@ -301,7 +303,7 @@ func (s Server) Add(gp *server.Goploy) server.Response { return response.JSON{Code: response.Error, Message: err.Error()} } - srv := model2.Server{ + srv := model.Server{ NamespaceID: reqData.NamespaceID, Name: reqData.Name, OS: reqData.OS, @@ -353,7 +355,7 @@ func (s Server) Edit(gp *server.Goploy) server.Response { if err := decodeJson(gp.Body, &reqData); err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } - srv := model2.Server{ + srv := model.Server{ ID: reqData.ID, NamespaceID: reqData.NamespaceID, Name: reqData.Name, @@ -388,7 +390,7 @@ func (Server) Toggle(gp *server.Goploy) server.Response { return response.JSON{Code: response.Error, Message: err.Error()} } - if err := (model2.Server{ID: reqData.ID, State: reqData.State}).ToggleRow(); err != nil { + if err := (model.Server{ID: reqData.ID, State: reqData.State}).ToggleRow(); err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } return response.JSON{} @@ -415,7 +417,7 @@ func (Server) InstallAgent(gp *server.Goploy) server.Response { for _, id := range reqData.IDs { go func(id int64) { - srv, err := (model2.Server{ID: id}).GetData() + srv, err := (model.Server{ID: id}).GetData() if err != nil { log.Error(fmt.Sprintf("Error on %d server, %s", id, err.Error())) return @@ -471,7 +473,7 @@ func (Server) PreviewFile(gp *server.Goploy) server.Response { if err != nil { return response.JSON{Code: response.Error, Message: "invalid server id"} } - srv, err := (model2.Server{ID: id}).GetData() + srv, err := (model.Server{ID: id}).GetData() if err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } @@ -488,7 +490,7 @@ func (Server) DownloadFile(gp *server.Goploy) server.Response { if err != nil { return response.JSON{Code: response.Error, Message: "invalid server id"} } - srv, err := (model2.Server{ID: id}).GetData() + srv, err := (model.Server{ID: id}).GetData() if err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } @@ -516,7 +518,7 @@ func (Server) UploadFile(gp *server.Goploy) server.Response { } defer file.Close() - srv, err := (model2.Server{ID: reqData.ID}).GetData() + srv, err := (model.Server{ID: reqData.ID}).GetData() if err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } @@ -547,6 +549,80 @@ func (Server) UploadFile(gp *server.Goploy) server.Response { return response.JSON{} } +func (Server) EditFile(gp *server.Goploy) server.Response { + type ReqData struct { + ServerID int64 `json:"serverId" validate:"gt=0"` + File string `json:"file" validate:"required"` + Content string `json:"content" validate:"required"` + } + var reqData ReqData + if err := decodeJson(gp.Body, &reqData); err != nil { + return response.JSON{Code: response.Error, Message: err.Error()} + } + + srv, err := (model.Server{ID: reqData.ServerID}).GetData() + if err != nil { + return response.JSON{Code: response.Error, Message: err.Error()} + } + client, err := srv.ToSSHConfig().Dial() + if err != nil { + return response.JSON{Code: response.Error, Message: err.Error()} + } + defer client.Close() + + sftpClient, err := sftp.NewClient(client) + if err != nil { + return response.JSON{Code: response.Error, Message: err.Error()} + } + defer sftpClient.Close() + + remoteFile, err := sftpClient.Create(reqData.File) + if err != nil { + return response.JSON{Code: response.Error, Message: err.Error()} + } + + _, err = remoteFile.Write([]byte(reqData.Content)) + if err != nil { + return response.JSON{Code: response.Error, Message: err.Error()} + } + + return response.JSON{} +} + +func (Server) RenameFile(gp *server.Goploy) server.Response { + type ReqData struct { + ServerID int64 `json:"serverId" validate:"gt=0"` + Dir string `json:"dir" validate:"required"` + NewName string `json:"newName" validate:"required"` + CurrentName string `json:"currentName" validate:"required"` + } + var reqData ReqData + if err := decodeJson(gp.Body, &reqData); err != nil { + return response.JSON{Code: response.Error, Message: err.Error()} + } + + srv, err := (model.Server{ID: reqData.ServerID}).GetData() + if err != nil { + return response.JSON{Code: response.Error, Message: err.Error()} + } + client, err := srv.ToSSHConfig().Dial() + if err != nil { + return response.JSON{Code: response.Error, Message: err.Error()} + } + defer client.Close() + + sftpClient, err := sftp.NewClient(client) + if err != nil { + return response.JSON{Code: response.Error, Message: err.Error()} + } + defer sftpClient.Close() + err = sftpClient.Rename(path.Join(reqData.Dir, reqData.CurrentName), path.Join(reqData.Dir, reqData.NewName)) + if err != nil { + return response.JSON{Code: response.Error, Message: err.Error()} + } + return response.JSON{} +} + func (Server) DeleteFile(gp *server.Goploy) server.Response { type ReqData struct { File string `json:"file" validate:"required"` @@ -557,7 +633,7 @@ func (Server) DeleteFile(gp *server.Goploy) server.Response { return response.JSON{Code: response.Error, Message: err.Error()} } - srv, err := (model2.Server{ID: reqData.ServerID}).GetData() + srv, err := (model.Server{ID: reqData.ServerID}).GetData() if err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } @@ -603,7 +679,7 @@ func (Server) TransferFile(gp *server.Goploy) server.Response { return response.JSON{Code: response.Error, Message: err.Error()} } - sourceServer, err := (model2.Server{ID: reqData.SourceServerID}).GetData() + sourceServer, err := (model.Server{ID: reqData.SourceServerID}).GetData() if err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } @@ -621,7 +697,7 @@ func (Server) TransferFile(gp *server.Goploy) server.Response { defer sftpClient.Close() for _, destServerID := range reqData.DestServerIDs { - destServer, err := (model2.Server{ID: destServerID}).GetData() + destServer, err := (model.Server{ID: destServerID}).GetData() if err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } @@ -734,7 +810,7 @@ func (Server) Report(gp *server.Goploy) server.Response { if len(datetimeRange) != 2 { return response.JSON{Code: response.Error, Message: "invalid datetime range"} } - serverAgentLogs, err := (model2.ServerAgentLog{ServerID: reqData.ServerID, Type: reqData.Type}).GetListBetweenTime(datetimeRange[0], datetimeRange[1]) + serverAgentLogs, err := (model.ServerAgentLog{ServerID: reqData.ServerID, Type: reqData.Type}).GetListBetweenTime(datetimeRange[0], datetimeRange[1]) if err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } @@ -753,7 +829,7 @@ func (Server) Report(gp *server.Goploy) server.Response { flagMap[serverAgentLog.Item] = Flag{Count: flagMap[serverAgentLog.Item].Count + 1} } - serverAgentMap := map[string]model2.ServerAgentLogs{} + serverAgentMap := map[string]model.ServerAgentLogs{} for _, serverAgentLog := range serverAgentLogs { flagMap[serverAgentLog.Item] = Flag{ Count: flagMap[serverAgentLog.Item].Count, @@ -769,20 +845,20 @@ func (Server) Report(gp *server.Goploy) server.Response { return response.JSON{ Data: struct { - ServerAgentMap map[string]model2.ServerAgentLogs `json:"map"` + ServerAgentMap map[string]model.ServerAgentLogs `json:"map"` }{ServerAgentMap: serverAgentMap}, } } func (Server) GetAllMonitor(gp *server.Goploy) server.Response { serverID, err := strconv.ParseInt(gp.URLQuery.Get("serverId"), 10, 64) - serverMonitorList, err := model2.ServerMonitor{ServerID: serverID}.GetAll() + serverMonitorList, err := model.ServerMonitor{ServerID: serverID}.GetAll() if err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } return response.JSON{ Data: struct { - List model2.ServerMonitors `json:"list"` + List model.ServerMonitors `json:"list"` }{List: serverMonitorList}, } } @@ -808,7 +884,7 @@ func (s Server) AddMonitor(gp *server.Goploy) server.Response { return response.JSON{Code: response.Error, Message: err.Error()} } - id, err := model2.ServerMonitor{ + id, err := model.ServerMonitor{ ServerID: reqData.ServerID, Item: reqData.Item, Formula: reqData.Formula, @@ -855,7 +931,7 @@ func (s Server) EditMonitor(gp *server.Goploy) server.Response { return response.JSON{Code: response.Error, Message: err.Error()} } - err := model2.ServerMonitor{ + err := model.ServerMonitor{ ID: reqData.ID, Item: reqData.Item, Formula: reqData.Formula, @@ -887,7 +963,7 @@ func (s Server) DeleteMonitor(gp *server.Goploy) server.Response { return response.JSON{Code: response.Error, Message: err.Error()} } - err := model2.ServerMonitor{ + err := model.ServerMonitor{ ID: reqData.ID, }.DeleteRow() @@ -899,13 +975,13 @@ func (s Server) DeleteMonitor(gp *server.Goploy) server.Response { } func (Server) GetProcessList(gp *server.Goploy) server.Response { - list, err := model2.ServerProcess{NamespaceID: gp.Namespace.ID}.GetList() + list, err := model.ServerProcess{NamespaceID: gp.Namespace.ID}.GetList() if err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } return response.JSON{ Data: struct { - List model2.ServerProcesses `json:"list"` + List model.ServerProcesses `json:"list"` }{List: list}, } } @@ -921,7 +997,7 @@ func (Server) AddProcess(gp *server.Goploy) server.Response { return response.JSON{Code: response.Error, Message: err.Error()} } - id, err := model2.ServerProcess{ + id, err := model.ServerProcess{ NamespaceID: gp.Namespace.ID, Name: reqData.Name, Items: reqData.Items, @@ -948,7 +1024,7 @@ func (Server) EditProcess(gp *server.Goploy) server.Response { if err := decodeJson(gp.Body, &reqData); err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } - err := model2.ServerProcess{ + err := model.ServerProcess{ ID: reqData.ID, Name: reqData.Name, Items: reqData.Items, @@ -969,7 +1045,7 @@ func (Server) DeleteProcess(gp *server.Goploy) server.Response { return response.JSON{Code: response.Error, Message: err.Error()} } - if err := (model2.ServerProcess{ID: reqData.ID}).DeleteRow(); err != nil { + if err := (model.ServerProcess{ID: reqData.ID}).DeleteRow(); err != nil { return response.JSON{Code: response.Error, Message: err.Error()} } @@ -1001,18 +1077,18 @@ func (Server) ExecProcess(gp *server.Goploy) server.Response { return response.JSON{Data: respData} } - serverProcess, err := model2.ServerProcess{ID: reqData.ID}.GetData() + serverProcess, err := model.ServerProcess{ID: reqData.ID}.GetData() if err != nil { respData.Stderr = err.Error() return response.JSON{Data: respData} } - srv, err := (model2.Server{ID: reqData.ServerID}).GetData() + srv, err := (model.Server{ID: reqData.ServerID}).GetData() if err != nil { respData.Stderr = err.Error() return response.JSON{Data: respData} } - var processItems model2.ServerProcessItems + var processItems model.ServerProcessItems if err := json.Unmarshal([]byte(serverProcess.Items), &processItems); err != nil { respData.Stderr = err.Error() return response.JSON{Data: respData} @@ -1080,7 +1156,7 @@ func (Server) ExecScript(gp *server.Goploy) server.Response { ServerID: serverId, } - srv, err := (model2.Server{ID: serverId}).GetData() + srv, err := (model.Server{ID: serverId}).GetData() if err != nil { serverResp.ExecRes = false serverResp.Stderr = err.Error() diff --git a/config/permission.go b/config/permission.go index d95dd35..1b26f4c 100644 --- a/config/permission.go +++ b/config/permission.go @@ -82,4 +82,6 @@ const ( SFTPTransferFile = 75 SFTPDeleteFile = 76 ShowServerScriptPage = 77 + SFTPRenameFile = 78 + SFTPEditFile = 79 ) diff --git a/database/1.14.0.sql b/database/1.14.0.sql index 7fb239c..ce84383 100644 --- a/database/1.14.0.sql +++ b/database/1.14.0.sql @@ -2,4 +2,7 @@ ALTER TABLE monitor ADD success_script text; ALTER TABLE monitor ADD fail_script text; ALTER TABLE monitor ADD success_server_id INT ( 10 ) UNSIGNED NOT NULL DEFAULT 0; ALTER TABLE monitor ADD fail_server_id INT ( 10 ) UNSIGNED NOT NULL DEFAULT 0; -ALTER TABLE project ADD tag VARCHAR ( 6382 ) NOT NULL DEFAULT ''; \ No newline at end of file +ALTER TABLE project ADD tag VARCHAR ( 6382 ) NOT NULL DEFAULT ''; + +INSERT IGNORE INTO `permission` (`id`, `pid`, `name`, `sort`, `description`) VALUES (78, 23, 'SFTPRenameFile', 0, ''); +INSERT IGNORE INTO `permission` (`id`, `pid`, `name`, `sort`, `description`) VALUES (79, 23, 'SFTPEditFile', 0, ''); diff --git a/database/goploy.sql b/database/goploy.sql index 69bce14..2f0cbe6 100644 --- a/database/goploy.sql +++ b/database/goploy.sql @@ -473,6 +473,8 @@ INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALU INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (75, 23, 'SFTPTransferFile', 0, ''); INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (76, 23, 'SFTPDeleteFile', 0, ''); INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (77, 23, 'ShowServerScriptPage', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (78, 23, 'SFTPRenameFile', 0, ''); +INSERT IGNORE INTO `permission`(`id`, `pid`, `name`, `sort`, `description`) VALUES (79, 23, 'SFTPEditFile', 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); diff --git a/internal/model/sftp_log.go b/internal/model/sftp_log.go index 50933a0..eae8626 100644 --- a/internal/model/sftp_log.go +++ b/internal/model/sftp_log.go @@ -32,6 +32,8 @@ const ( SftpLogTypeUpload = "UPLOAD" SftpLogTypeRead = "READ" SftpLogTypePreview = "PREVIEW" + SftpLogTypeRename = "RENAME" + SftpLogTypeEdit = "EDIT" SftpLogTypeDelete = "DELETE" ) diff --git a/web/components.d.ts b/web/components.d.ts index 80a17ab..75e3323 100644 --- a/web/components.d.ts +++ b/web/components.d.ts @@ -25,6 +25,7 @@ declare module '@vue/runtime-core' { ElDropdown: typeof import('element-plus/es')['ElDropdown'] ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] + ElEmpty: typeof import('element-plus/es')['ElEmpty'] ElForm: typeof import('element-plus/es')['ElForm'] ElFormItem: typeof import('element-plus/es')['ElFormItem'] ElIcon: typeof import('element-plus/es')['ElIcon'] diff --git a/web/src/api/server.ts b/web/src/api/server.ts index acb6d1d..abfbfd9 100644 --- a/web/src/api/server.ts +++ b/web/src/api/server.ts @@ -394,6 +394,37 @@ export class ServerExecProcess extends Request { } } +export class ServerRenameFile extends Request { + readonly url = '/server/renameFile' + readonly method = 'put' + readonly timeout = 0 + public param: { + serverId: number + dir: string + currentName: string + newName: string + } + constructor(param: ServerRenameFile['param']) { + super() + this.param = param + } +} + +export class ServerEditFile extends Request { + readonly url = '/server/editFile' + readonly method = 'put' + readonly timeout = 0 + public param: { + serverId: number + file: string + content: string + } + constructor(param: ServerEditFile['param']) { + super() + this.param = param + } +} + export class ServerDeleteFile extends Request { readonly url = '/server/deleteFile' readonly method = 'delete' @@ -407,6 +438,7 @@ export class ServerDeleteFile extends Request { this.param = param } } + export class ServerTransferFile extends Request { readonly url = '/server/transferFile' readonly method = 'post' diff --git a/web/src/lang/en.json b/web/src/lang/en.json index a52c639..bfa1fcc 100644 --- a/web/src/lang/en.json +++ b/web/src/lang/en.json @@ -90,6 +90,7 @@ "edit": "Edit", "copy": "Copy", "transfer": "Transfer", + "rename": "Rename", "approve": "Approve", "deny": "Deny", "preview": "Preview", @@ -305,7 +306,8 @@ "advance": "Advanced setting", "transferFile": "Transfer file", "sftpFileCount": "items", - "saveTemplate": "Save template" + "saveTemplate": "Save template", + "renameTips": "rename {name}" }, "monitorPage": { "defaultServer": "Follow Host", diff --git a/web/src/lang/zh-cn.json b/web/src/lang/zh-cn.json index 1a521a7..8f8113c 100644 --- a/web/src/lang/zh-cn.json +++ b/web/src/lang/zh-cn.json @@ -90,6 +90,7 @@ "edit": "编辑", "copy": "复制", "transfer": "传输", + "rename": "重命名", "approve": "同意", "deny": "拒绝", "preview": "预览", @@ -288,7 +289,8 @@ "advance": "高级选项", "transferFile": "传输文件", "sftpFileCount": "个项目", - "saveTemplate": "保存模板" + "saveTemplate": "保存模板", + "renameTips": "重命名{name}" }, "monitorPage": { "scriptMode": "脚本类型", diff --git a/web/src/permission.ts b/web/src/permission.ts index 6565225..ca6247a 100644 --- a/web/src/permission.ts +++ b/web/src/permission.ts @@ -76,4 +76,6 @@ export default Object.freeze({ SFTPTransferFile: 75, SFTPDeleteFile: 76, ShowServerScriptPage: 77, + SFTPRenameFile: 78, + SFTPEditFile: 79, }) diff --git a/web/src/views/monitor/index.vue b/web/src/views/monitor/index.vue index 53fc3f4..00195ce 100644 --- a/web/src/views/monitor/index.vue +++ b/web/src/views/monitor/index.vue @@ -287,7 +287,7 @@ diff --git a/web/src/views/server/sftp/explorer.vue b/web/src/views/server/sftp/explorer.vue index 0245f3f..62295a3 100644 --- a/web/src/views/server/sftp/explorer.vue +++ b/web/src/views/server/sftp/explorer.vue @@ -157,6 +157,13 @@ {{ $t('preview') }} + + {{ $t('edit') }} + + + {{ $t('rename') }} + { + fileListLoading.value = true + new ServerRenameFile({ + serverId: serverId.value, + dir: dir.value, + currentName: selectedFile.value.name, + newName: value, + }) + .request() + .then(() => { + const pos = fileList.value.findIndex( + (item) => item.name === selectedFile.value.name + ) + fileList.value[pos].name = value + }) + .finally(() => { + fileListLoading.value = false + }) + }) + .catch() +} + function transferFile() { emit('transfer-file', selectedFile.value) } diff --git a/web/src/views/server/sftp/index.vue b/web/src/views/server/sftp/index.vue index f0340f8..a1bf2af 100644 --- a/web/src/views/server/sftp/index.vue +++ b/web/src/views/server/sftp/index.vue @@ -56,6 +56,7 @@ :key="item.uuid" :server="item.server" @dir-change="handleDirChange" + @edit-file="handleEditFile" @transfer-file="handleTransferFile" > + + + + @@ -141,20 +172,36 @@ import explorer from './explorer.vue' import path from 'path-browserify' import type { ElForm } from 'element-plus' import { ElMessage } from 'element-plus' +import { VAceEditor } from 'vue3-ace-editor' +import * as ace from 'ace-builds/src-noconflict/ace' +import { getModeForPath } from 'ace-builds/src-noconflict/ext-modelist' +import { NamespaceKey, getNamespaceId } from '@/utils/namespace' import { ServerOption, ServerData, ServerSFTPFile, + ServerEditFile, ServerTransferFile, } from '@/api/server' +import { useDark } from '@vueuse/core' import { ref } from 'vue' interface sftp { uuid: number server: ServerData dir: string } +ace.config.set( + 'basePath', + 'https://cdn.jsdelivr.net/npm/ace-builds@' + ace.version + '/src-noconflict/' +) +ace.config.set( + 'themePath', + 'https://cdn.jsdelivr.net/npm/ace-builds@' + ace.version + '/src-noconflict/' +) +const isDark = useDark() const currentUUID = ref(0) const transferFileDialogVisible = ref(false) +const fileEditorDialogVisible = ref(false) const serverOption = ref([]) const serverId = ref('') const serverList = ref([]) @@ -171,6 +218,8 @@ const formProps = ref({ errorLog: '', }) +const fileContent = ref('') + getServerOption() function getServerOption() { @@ -212,6 +261,46 @@ function handleDirChange(dir: string) { selectedSFTP.value.dir = dir } +function handleEditFile(file: ServerSFTPFile) { + selectedFile.value = file + fileContent.value = '' + fileEditorDialogVisible.value = true + formProps.value.disabled = true + const f = path.normalize(`${selectedSFTP.value.dir}/${file.name}`) + fetch( + `${ + import.meta.env.VITE_APP_BASE_API + }/server/previewFile?${NamespaceKey}=${getNamespaceId()}&id=${ + selectedSFTP.value.server.id + }&file=${f}` + ) + .then((response) => response.text()) + .then((content) => { + fileContent.value = content + }) + .finally(() => { + formProps.value.disabled = false + }) +} + +function editFile() { + formProps.value.disabled = true + new ServerEditFile({ + serverId: selectedSFTP.value.server.id, + file: path.normalize( + `${selectedSFTP.value.dir}/${selectedFile.value.name}` + ), + content: fileContent.value, + }) + .request() + .then(() => { + ElMessage.success('Success') + }) + .finally(() => { + formProps.value.disabled = false + }) +} + function handleTransferFile(file: ServerSFTPFile) { formData.value.destDir = selectedSFTP.value.dir formData.value.sourceFile = path.normalize(