From d51d11f460dd0e525f2dcd853d7d2b5430160991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=80=97=E5=AD=90?= Date: Sun, 20 Oct 2024 19:20:39 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=87=E4=BB=B6=E8=BF=9C=E7=A8=8B?= =?UTF-8?q?=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/http/request/file.go | 5 ++ internal/service/file.go | 27 +++++++++- web/src/api/panel/file/index.ts | 4 +- web/src/views/file/ToolBar.vue | 92 +++++++++++++++++++++++++++------ 4 files changed, 107 insertions(+), 21 deletions(-) diff --git a/internal/http/request/file.go b/internal/http/request/file.go index 54df46e6..477236f3 100644 --- a/internal/http/request/file.go +++ b/internal/http/request/file.go @@ -37,6 +37,11 @@ type FileCopy struct { Force bool `form:"force" json:"force"` } +type FileRemoteDownload struct { + Path string `form:"path" json:"path" validate:"required"` + URL string `form:"url" json:"url" validate:"required"` +} + type FilePermission struct { Path string `form:"path" json:"path" validate:"required"` Mode string `form:"mode" json:"mode" validate:"required"` diff --git a/internal/service/file.go b/internal/service/file.go index 24f87cb2..350f95f9 100644 --- a/internal/service/file.go +++ b/internal/service/file.go @@ -17,6 +17,8 @@ import ( "github.com/go-rat/chix" "github.com/spf13/cast" + "github.com/TheTNB/panel/internal/biz" + "github.com/TheTNB/panel/internal/data" "github.com/TheTNB/panel/internal/http/request" "github.com/TheTNB/panel/pkg/io" "github.com/TheTNB/panel/pkg/os" @@ -25,10 +27,13 @@ import ( ) type FileService struct { + taskRepo biz.TaskRepo } func NewFileService() *FileService { - return &FileService{} + return &FileService{ + taskRepo: data.NewTaskRepo(), + } } func (s *FileService) Create(w http.ResponseWriter, r *http.Request) { @@ -225,7 +230,25 @@ func (s *FileService) Download(w http.ResponseWriter, r *http.Request) { } func (s *FileService) RemoteDownload(w http.ResponseWriter, r *http.Request) { - // TODO: 未实现 + req, err := Bind[request.FileRemoteDownload](r) + if err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + timestamp := time.Now().Format("20060102150405") + task := new(biz.Task) + task.Name = "下载远程文件" + task.Status = biz.TaskStatusWaiting + task.Shell = fmt.Sprintf(`wget -o /tmp/remote-download-%s.log -O '%s' '%s'`, timestamp, req.Path, req.URL) + task.Log = fmt.Sprintf("/tmp/remote-download-%s.log", timestamp) + + if err = s.taskRepo.Push(task); err != nil { + Error(w, http.StatusInternalServerError, "%v", err) + return + } + + Success(w, nil) } func (s *FileService) Info(w http.ResponseWriter, r *http.Request) { diff --git a/web/src/api/panel/file/index.ts b/web/src/api/panel/file/index.ts index 78a5a705..7a059630 100644 --- a/web/src/api/panel/file/index.ts +++ b/web/src/api/panel/file/index.ts @@ -31,8 +31,8 @@ export default { copy: (source: string, target: string): Promise> => request.post('/file/copy', { source, target }), // 远程下载 - remoteDownload: (url: string, path: string): Promise> => - request.post('/file/remoteDownload', { url, path }), + remoteDownload: (path: string, url: string): Promise> => + request.post('/file/remoteDownload', { path, url }), // 获取文件信息 info: (path: string): Promise> => request.get('/file/info', { params: { path } }), diff --git a/web/src/views/file/ToolBar.vue b/web/src/views/file/ToolBar.vue index ccc7d0ee..958b79dd 100644 --- a/web/src/views/file/ToolBar.vue +++ b/web/src/views/file/ToolBar.vue @@ -14,32 +14,52 @@ const compress = defineModel('compress', { type: Boolean, required: tru const permission = defineModel('permission', { type: Boolean, required: true }) const upload = ref(false) -const newModal = ref(false) -const newModel = ref({ +const create = ref(false) +const createModel = ref({ dir: false, path: '' }) +const download = ref(false) +const downloadModel = ref({ + path: '', + url: '' +}) -const showNew = (value: string) => { - newModel.value.dir = value !== 'file' - newModel.value.path = '' - newModal.value = true +const showCreate = (value: string) => { + createModel.value.dir = value !== 'file' + createModel.value.path = '' + create.value = true } -const handleNew = () => { - if (!checkName(newModel.value.path)) { +const handleCreate = () => { + if (!checkName(createModel.value.path)) { window.$message.error('名称不合法') return } - const fullPath = path.value + '/' + newModel.value.path - file.create(fullPath, newModel.value.dir).then(() => { - newModal.value = false + const fullPath = path.value + '/' + createModel.value.path + file.create(fullPath, createModel.value.dir).then(() => { + create.value = false window.$message.success('创建成功') EventBus.emit('file:refresh') }) } +const handleDownload = () => { + if (!checkName(downloadModel.value.path)) { + window.$message.error('名称不合法') + return + } + + file + .remoteDownload(path.value + '/' + downloadModel.value.path, downloadModel.value.url) + .then(() => { + download.value = false + window.$message.success('下载任务创建成功') + EventBus.emit('file:refresh') + }) +} + const handleCopy = () => { if (!selected.value.length) { window.$message.error('请选择要复制的文件/文件夹') @@ -103,6 +123,23 @@ const bulkDelete = () => { }) } } + +// 自动填充下载文件名 +watch( + () => downloadModel.value.url, + (newUrl) => { + if (!newUrl) return + try { + const url = new URL(newUrl) + const path = url.pathname.split('/').pop() + if (path) { + downloadModel.value.path = decodeURIComponent(path) + } + } catch (error) { + /* empty */ + } + } +)