2
0
mirror of https://github.com/acepanel/panel.git synced 2026-02-04 07:57:21 +08:00

feat: 文件远程下载

This commit is contained in:
耗子
2024-10-20 19:20:39 +08:00
parent f9233bd36b
commit d51d11f460
4 changed files with 107 additions and 21 deletions

View File

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

View File

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

View File

@@ -31,8 +31,8 @@ export default {
copy: (source: string, target: string): Promise<AxiosResponse<any>> =>
request.post('/file/copy', { source, target }),
// 远程下载
remoteDownload: (url: string, path: string): Promise<AxiosResponse<any>> =>
request.post('/file/remoteDownload', { url, path }),
remoteDownload: (path: string, url: string): Promise<AxiosResponse<any>> =>
request.post('/file/remoteDownload', { path, url }),
// 获取文件信息
info: (path: string): Promise<AxiosResponse<any>> =>
request.get('/file/info', { params: { path } }),

View File

@@ -14,32 +14,52 @@ const compress = defineModel<boolean>('compress', { type: Boolean, required: tru
const permission = defineModel<boolean>('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 */
}
}
)
</script>
<template>
@@ -112,12 +149,12 @@ const bulkDelete = () => {
{ label: '文件', value: 'file' },
{ label: '文件夹', value: 'folder' }
]"
@update:value="showNew"
@update:value="showCreate"
>
<n-button type="primary"> 创建 </n-button>
</n-popselect>
<n-button @click="upload = true"> 上传 </n-button>
<n-button style="display: none"> 远程下载 </n-button>
<n-button @click="download = true"> 远程下载 </n-button>
<div ml-auto>
<n-flex>
<n-button v-if="marked.length" secondary type="primary" @click="handlePaste">
@@ -139,7 +176,7 @@ const bulkDelete = () => {
</div>
</n-flex>
<n-modal
v-model:show="newModal"
v-model:show="create"
preset="card"
title="创建"
style="width: 60vw"
@@ -148,12 +185,33 @@ const bulkDelete = () => {
:segmented="false"
>
<n-space vertical>
<n-form :model="newModel">
<n-form :model="createModel">
<n-form-item label="名称">
<n-input v-model:value="newModel.path" />
<n-input v-model:value="createModel.path" />
</n-form-item>
</n-form>
<n-button type="info" block @click="handleNew">提交</n-button>
<n-button type="info" block @click="handleCreate">提交</n-button>
</n-space>
</n-modal>
<n-modal
v-model:show="download"
preset="card"
title="远程下载"
style="width: 60vw"
size="huge"
:bordered="false"
:segmented="false"
>
<n-space vertical>
<n-form :model="downloadModel">
<n-form-item label="下载链接">
<n-input :input-props="{ type: 'url' }" v-model:value="downloadModel.url" />
</n-form-item>
<n-form-item label="保存文件名">
<n-input v-model:value="downloadModel.path" />
</n-form-item>
</n-form>
<n-button type="info" block @click="handleDownload">提交</n-button>
</n-space>
</n-modal>
<upload-modal v-model:show="upload" v-model:path="path" />