rename file and edit file via sftp

This commit is contained in:
zhenorzz 2023-05-16 17:37:19 +08:00
parent 4fcd89ca25
commit 55e6299da0
14 changed files with 387 additions and 51 deletions

View File

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

View File

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

View File

@ -82,4 +82,6 @@ const (
SFTPTransferFile = 75
SFTPDeleteFile = 76
ShowServerScriptPage = 77
SFTPRenameFile = 78
SFTPEditFile = 79
)

View File

@ -3,3 +3,6 @@ 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 '';
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, '');

View File

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

View File

@ -32,6 +32,8 @@ const (
SftpLogTypeUpload = "UPLOAD"
SftpLogTypeRead = "READ"
SftpLogTypePreview = "PREVIEW"
SftpLogTypeRename = "RENAME"
SftpLogTypeEdit = "EDIT"
SftpLogTypeDelete = "DELETE"
)

1
web/components.d.ts vendored
View File

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

View File

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

View File

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

View File

@ -90,6 +90,7 @@
"edit": "编辑",
"copy": "复制",
"transfer": "传输",
"rename": "重命名",
"approve": "同意",
"deny": "拒绝",
"preview": "预览",
@ -288,7 +289,8 @@
"advance": "高级选项",
"transferFile": "传输文件",
"sftpFileCount": "个项目",
"saveTemplate": "保存模板"
"saveTemplate": "保存模板",
"renameTips": "重命名{name}"
},
"monitorPage": {
"scriptMode": "脚本类型",

View File

@ -76,4 +76,6 @@ export default Object.freeze({
SFTPTransferFile: 75,
SFTPDeleteFile: 76,
ShowServerScriptPage: 77,
SFTPRenameFile: 78,
SFTPEditFile: 79,
})

View File

@ -287,7 +287,7 @@
<VAceEditor
v-model:value="formProps.script"
lang="sh"
theme="github"
:theme="isDark ? 'one_dark' : 'github'"
style="height: 360px; width: 100%"
:options="{ newLineMode: 'unix' }"
/>

View File

@ -157,6 +157,13 @@
{{ $t('preview') }}
</Link>
</el-dropdown-item>
<DropdownItem
v-if="selectedFile['isDir'] === false"
:permissions="[permission.SFTPEditFile]"
@click="editFile"
>
{{ $t('edit') }}
</DropdownItem>
<el-dropdown-item
v-if="selectedFile['isDir'] === false"
style="padding: 0"
@ -171,6 +178,12 @@
{{ $t('download') }}
</Link>
</el-dropdown-item>
<DropdownItem
:permissions="[permission.SFTPRenameFile]"
@click="rename"
>
{{ $t('rename') }}
</DropdownItem>
<DropdownItem
v-if="selectedFile['isDir'] === false"
:permissions="[permission.SFTPDeleteFile]"
@ -223,14 +236,18 @@ import path from 'path-browserify'
import { humanSize, parseTime } from '@/utils'
import { NamespaceKey, getNamespaceId } from '@/utils/namespace'
import type { ElUpload } from 'element-plus'
import { ServerData, ServerSFTPFile, ServerDeleteFile } from '@/api/server'
import {
ServerData,
ServerSFTPFile,
ServerRenameFile,
ServerDeleteFile,
} from '@/api/server'
import { ElMessage, ElMessageBox } from 'element-plus'
import { HttpResponse } from '@/api/types'
import { ref, PropType, computed, onBeforeUnmount } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const emit = defineEmits(['dir-change', 'transfer-file'])
const emit = defineEmits(['dir-change', 'edit-file', 'transfer-file'])
const props = defineProps({
uuid: {
type: Number,
@ -443,6 +460,43 @@ function deleteFile() {
.catch()
}
function editFile() {
emit('edit-file', selectedFile.value)
}
function rename() {
ElMessageBox.prompt(
t('serverPage.renameTips', { name: selectedFile.value.name }),
t('rename'),
{
confirmButtonText: t('confirm'),
cancelButtonText: t('cancel'),
inputPattern: /.+/,
inputErrorMessage: 'Name required',
}
)
.then(({ value }) => {
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)
}

View File

@ -56,6 +56,7 @@
:key="item.uuid"
:server="item.server"
@dir-change="handleDirChange"
@edit-file="handleEditFile"
@transfer-file="handleTransferFile"
></explorer>
<el-dialog
@ -130,6 +131,36 @@
</el-button>
</template>
</el-dialog>
<el-dialog
v-model="fileEditorDialogVisible"
:fullscreen="$store.state.app.device === 'mobile'"
:title="selectedFile['name']"
:close-on-click-modal="false"
>
<VAceEditor
ref="fileEditor"
v-model:value="fileContent"
v-loading="formProps.disabled"
:lang="getModeForPath(selectedFile['name']).name"
:theme="isDark ? 'one_dark' : 'github'"
style="height: 360px; width: 100%"
/>
<template #footer>
<el-button
:disabled="formProps.disabled"
@click="fileEditorDialogVisible = false"
>
{{ $t('cancel') }}
</el-button>
<el-button
type="primary"
:loading="formProps.disabled"
@click="editFile"
>
{{ $t('confirm') }}
</el-button>
</template>
</el-dialog>
</el-row>
</template>
@ -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<ServerOption['datagram']['list']>([])
const serverId = ref('')
const serverList = ref<sftp[]>([])
@ -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(