I added a new button to the file listing view, a new API function to call the backend, and the corresponding logic to handle the user interaction.
This commit is contained in:
parent
280fa562a6
commit
bdfe930ab0
@ -213,6 +213,14 @@ export function getSubtitlesURL(file: ResourceItem) {
|
|||||||
return file.subtitles?.map((d) => createURL("api/subtitle" + d, params));
|
return file.subtitles?.map((d) => createURL("api/subtitle" + d, params));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function downloadFromURL(url: string, path: string) {
|
||||||
|
const res = await fetchURL(`/api/downloads`, {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify({ url, path }),
|
||||||
|
});
|
||||||
|
return res.json();
|
||||||
|
}
|
||||||
|
|
||||||
export async function usage(url: string, signal: AbortSignal) {
|
export async function usage(url: string, signal: AbortSignal) {
|
||||||
url = removePrefix(url);
|
url = removePrefix(url);
|
||||||
|
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
"create": "Create",
|
"create": "Create",
|
||||||
"delete": "Delete",
|
"delete": "Delete",
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
|
"downloadFromUrl": "Download from URL",
|
||||||
"file": "File",
|
"file": "File",
|
||||||
"folder": "Folder",
|
"folder": "Folder",
|
||||||
"fullScreen": "Toggle full screen",
|
"fullScreen": "Toggle full screen",
|
||||||
|
|||||||
@ -72,6 +72,13 @@
|
|||||||
:label="t('buttons.upload')"
|
:label="t('buttons.upload')"
|
||||||
@action="uploadFunc"
|
@action="uploadFunc"
|
||||||
/>
|
/>
|
||||||
|
<action
|
||||||
|
v-if="headerButtons.upload"
|
||||||
|
icon="file_download"
|
||||||
|
id="download-url-button"
|
||||||
|
:label="t('buttons.downloadFromUrl')"
|
||||||
|
@action="downloadUrl"
|
||||||
|
/>
|
||||||
<action icon="info" :label="t('buttons.info')" show="info" />
|
<action icon="info" :label="t('buttons.info')" show="info" />
|
||||||
<action
|
<action
|
||||||
icon="check_circle"
|
icon="check_circle"
|
||||||
@ -894,6 +901,26 @@ const download = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const downloadUrl = () => {
|
||||||
|
layoutStore.showHover({
|
||||||
|
prompt: "url",
|
||||||
|
confirm: (event: Event, url: string) => {
|
||||||
|
event.preventDefault();
|
||||||
|
layoutStore.closeHovers();
|
||||||
|
|
||||||
|
if (url === "") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
api.downloadFromURL(url, route.path)
|
||||||
|
.then(() => {
|
||||||
|
fileStore.reload = true;
|
||||||
|
})
|
||||||
|
.catch($showError);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const switchView = async () => {
|
const switchView = async () => {
|
||||||
layoutStore.closeHovers();
|
layoutStore.closeHovers();
|
||||||
|
|
||||||
|
|||||||
73
http/download.go
Normal file
73
http/download.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package http
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/filebrowser/filebrowser/v2/files"
|
||||||
|
)
|
||||||
|
|
||||||
|
type downloadBody struct {
|
||||||
|
URL string `json:"url"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var urlDownloadHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||||
|
if !d.user.Perm.Create {
|
||||||
|
return http.StatusForbidden, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var body downloadBody
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&body)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusBadRequest, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if body.URL == "" || body.Path == "" {
|
||||||
|
return http.StatusBadRequest, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = url.ParseRequestURI(body.URL)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusBadRequest, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fileName := path.Base(body.URL)
|
||||||
|
filePath := path.Join(body.Path, fileName)
|
||||||
|
|
||||||
|
if !d.Check(filePath) {
|
||||||
|
return http.StatusForbidden, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.Get(body.URL)
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
err = d.RunHook(func() error {
|
||||||
|
_, writeErr := writeFile(d.user.Fs, filePath, resp.Body, d.settings.FileMode, d.settings.DirMode)
|
||||||
|
return writeErr
|
||||||
|
}, "upload", filePath, "", d.user)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
_ = d.user.Fs.RemoveAll(filePath)
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := files.NewFileInfo(&files.FileOptions{
|
||||||
|
Fs: d.user.Fs,
|
||||||
|
Path: filePath,
|
||||||
|
Modify: d.user.Perm.Modify,
|
||||||
|
Expand: false,
|
||||||
|
ReadHeader: d.server.TypeDetectionByHeader,
|
||||||
|
Checker: d,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return http.StatusInternalServerError, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return renderJSON(w, r, file)
|
||||||
|
})
|
||||||
@ -70,6 +70,7 @@ func NewHandler(
|
|||||||
api.PathPrefix("/tus").Handler(monkey(tusHeadHandler(), "/api/tus")).Methods("HEAD", "GET")
|
api.PathPrefix("/tus").Handler(monkey(tusHeadHandler(), "/api/tus")).Methods("HEAD", "GET")
|
||||||
api.PathPrefix("/tus").Handler(monkey(tusPatchHandler(), "/api/tus")).Methods("PATCH")
|
api.PathPrefix("/tus").Handler(monkey(tusPatchHandler(), "/api/tus")).Methods("PATCH")
|
||||||
api.PathPrefix("/tus").Handler(monkey(tusDeleteHandler(), "/api/tus")).Methods("DELETE")
|
api.PathPrefix("/tus").Handler(monkey(tusDeleteHandler(), "/api/tus")).Methods("DELETE")
|
||||||
|
api.PathPrefix("/downloads").Handler(monkey(urlDownloadHandler, "/api/downloads")).Methods("POST")
|
||||||
|
|
||||||
api.PathPrefix("/usage").Handler(monkey(diskUsage, "/api/usage")).Methods("GET")
|
api.PathPrefix("/usage").Handler(monkey(diskUsage, "/api/usage")).Methods("GET")
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user