taggart

Simple golang tagging filesystem webapp
Log | Files | Refs

commit abc6ee37e48263f3dc67fa380aa525fe24e64ab5
parent 90399ee9f584aa96309e6889029f1c45fcafbba6
Author: breadcat <breadcat@users.noreply.github.com>
Date:   Fri, 19 Sep 2025 18:46:11 +0100

Add remote file upload functionality

Diffstat:
Mmain.go | 75++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mtemplates/upload.html | 7+++++++
2 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/main.go b/main.go @@ -9,6 +9,8 @@ import ( "os" "path/filepath" "strings" + "io" + "net/url" _ "github.com/mattn/go-sqlite3" ) @@ -80,12 +82,12 @@ func main() { http.HandleFunc("/", listFilesHandler) http.HandleFunc("/upload", uploadHandler) + http.HandleFunc("/upload-url", uploadFromURLHandler) http.HandleFunc("/file/", fileRouter) http.HandleFunc("/tags", tagsHandler) http.HandleFunc("/tag/", tagFilterHandler) http.HandleFunc("/untagged", untaggedFilesHandler) - http.Handle("/uploads/", http.StripPrefix("/uploads/", http.FileServer(http.Dir("uploads")))) http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static")))) @@ -93,6 +95,77 @@ func main() { http.ListenAndServe(":8080", nil) } +// Upload file from URL +func uploadFromURLHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Redirect(w, r, "/upload", http.StatusSeeOther) + return + } + + fileURL := r.FormValue("fileurl") + if fileURL == "" { + http.Error(w, "No URL provided", http.StatusBadRequest) + return + } + + // Validate URL + parsedURL, err := url.ParseRequestURI(fileURL) + if err != nil || !(parsedURL.Scheme == "http" || parsedURL.Scheme == "https") { + http.Error(w, "Invalid URL", http.StatusBadRequest) + return + } + + // Download the file + resp, err := http.Get(fileURL) + if err != nil || resp.StatusCode != http.StatusOK { + http.Error(w, "Failed to download file", http.StatusBadRequest) + return + } + defer resp.Body.Close() + + // Determine filename from URL + parts := strings.Split(parsedURL.Path, "/") + filename := parts[len(parts)-1] + if filename == "" { + filename = "file_from_url" + } + + dstPath := filepath.Join("uploads", filename) + + // Avoid overwriting existing files + for i := 1; ; i++ { + if _, err := os.Stat(dstPath); os.IsNotExist(err) { + break + } + ext := filepath.Ext(filename) + name := strings.TrimSuffix(filename, ext) + dstPath = filepath.Join("uploads", fmt.Sprintf("%s_%d%s", name, i, ext)) + } + + dst, err := os.Create(dstPath) + if err != nil { + http.Error(w, "Failed to save file", http.StatusInternalServerError) + return + } + defer dst.Close() + + _, err = io.Copy(dst, resp.Body) + if err != nil { + http.Error(w, "Failed to save file", http.StatusInternalServerError) + return + } + + // Add to database + res, err := db.Exec("INSERT INTO files (filename, path) VALUES (?, ?)", filepath.Base(dstPath), dstPath) + if err != nil { + http.Error(w, "Failed to record file", http.StatusInternalServerError) + return + } + + id, _ := res.LastInsertId() + http.Redirect(w, r, fmt.Sprintf("/file/%d", id), http.StatusSeeOther) +} + // List all files, plus untagged files func listFilesHandler(w http.ResponseWriter, r *http.Request) { // Tagged files diff --git a/templates/upload.html b/templates/upload.html @@ -9,6 +9,13 @@ <input type="file" name="file"> <button type="submit">Upload</button> </form> + +<h2>Upload from URL</h2> +<form method="post" action="/upload-url"> + File URL: <input type="url" name="fileurl" required style="width:400px"><br> + <button type="submit">Add File</button> +</form> + <a href="/">Back to files</a> </body> </html> \ No newline at end of file