mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 07:57:21 +08:00
feat: 文件远程下载
This commit is contained in:
@@ -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"`
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 } }),
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user