From bdfe930ab01bffe70d26953aec2e6dc54ea83f10 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 24 Aug 2025 09:20:21 +0000 Subject: [PATCH] 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. --- frontend/src/api/files.ts | 8 +++ frontend/src/i18n/en.json | 1 + frontend/src/views/files/FileListing.vue | 27 +++++++++ http/download.go | 73 ++++++++++++++++++++++++ http/http.go | 1 + 5 files changed, 110 insertions(+) create mode 100644 http/download.go diff --git a/frontend/src/api/files.ts b/frontend/src/api/files.ts index d9ee12d6..499d2527 100644 --- a/frontend/src/api/files.ts +++ b/frontend/src/api/files.ts @@ -213,6 +213,14 @@ export function getSubtitlesURL(file: ResourceItem) { 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) { url = removePrefix(url); diff --git a/frontend/src/i18n/en.json b/frontend/src/i18n/en.json index 7de128ed..3085a05f 100644 --- a/frontend/src/i18n/en.json +++ b/frontend/src/i18n/en.json @@ -11,6 +11,7 @@ "create": "Create", "delete": "Delete", "download": "Download", + "downloadFromUrl": "Download from URL", "file": "File", "folder": "Folder", "fullScreen": "Toggle full screen", diff --git a/frontend/src/views/files/FileListing.vue b/frontend/src/views/files/FileListing.vue index 3f052d54..ccd88b8c 100644 --- a/frontend/src/views/files/FileListing.vue +++ b/frontend/src/views/files/FileListing.vue @@ -72,6 +72,13 @@ :label="t('buttons.upload')" @action="uploadFunc" /> + { }); }; +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 () => { layoutStore.closeHovers(); diff --git a/http/download.go b/http/download.go new file mode 100644 index 00000000..849e34fa --- /dev/null +++ b/http/download.go @@ -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) +}) diff --git a/http/http.go b/http/http.go index 2d87535f..f1da1160 100644 --- a/http/http.go +++ b/http/http.go @@ -70,6 +70,7 @@ func NewHandler( 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(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")