taggart

Simple golang tagging filesystem webapp
Log | Files | Refs

commit 12418b12ae7d6e565f21359426570278cec3f0fa
parent 6d52b2946655a5506db31e28fe93f1c5a19c5c58
Author: breadcat <breadcat@users.noreply.github.com>
Date:   Mon, 22 Sep 2025 16:17:44 +0100

Raw URL copy/paste function

Diffstat:
Mmain.go | 41++++++++++++++++++++++++++++++++++-------
Mtemplates/file.html | 41++++++++++++++++++++++++++++++++++++++++-
2 files changed, 74 insertions(+), 8 deletions(-)

diff --git a/main.go b/main.go @@ -45,6 +45,8 @@ type TagDisplay struct { type PageData struct { Title string Data interface{} + IP string + Port string } func main() { @@ -351,6 +353,22 @@ func uploadHandler(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, fmt.Sprintf("/file/%d", id), http.StatusSeeOther) } +// raw local IP for raw address +func getLocalIP() (string, error) { + addrs, err := net.InterfaceAddrs() + if err != nil { + return "", err + } + for _, addr := range addrs { + if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { + if ipnet.IP.To4() != nil { + return ipnet.IP.String(), nil + } + } + } + return "", fmt.Errorf("no connected network interface found") +} + // Router for file operations, tag deletion, rename, and delete func fileRouter(w http.ResponseWriter, r *http.Request) { parts := strings.Split(r.URL.Path, "/") @@ -552,13 +570,22 @@ func fileHandler(w http.ResponseWriter, r *http.Request) { return } - pageData := PageData{ - Title: f.Filename, - Data: struct { - File File - Categories []string - }{f, cats}, - } + // IP and port for raw URL + ip, _ := getLocalIP() + port := strings.TrimPrefix(config.ServerPort, ":") + // Escape filename for copy/paste + escaped := url.PathEscape(f.Filename) + + pageData := PageData{ + Title: f.Filename, + Data: struct { + File File + Categories []string + EscapedFilename string + }{f, cats, escaped}, + IP: ip, + Port: port, + } tmpl.ExecuteTemplate(w, "file.html", pageData) } diff --git a/templates/file.html b/templates/file.html @@ -1,5 +1,4 @@ {{template "_header" .}} -File {{.Data.File.Filename}} <h2>File: {{.Data.File.Filename}}</h2> {{if hasAnySuffix .Data.File.Filename ".jpg" ".jpeg" ".png" ".gif" ".webp"}} @@ -12,6 +11,46 @@ File {{.Data.File.Filename}} <a href="/uploads/{{.Data.File.Filename}}">Download file</a><br> {{end}} +<h3>Raw URL</h3> +<pre id="raw-url">http://{{.IP}}:{{.Port}}/uploads/{{.Data.EscapedFilename}}</pre> +<button id="copy-btn" style="margin-top: 5px;">Copy to Clipboard</button> +<span id="copy-status" style="margin-left: 10px;"></span> + +<script> +document.getElementById("copy-btn").addEventListener("click", function() { + const text = document.getElementById("raw-url").textContent.trim(); + const status = document.getElementById("copy-status"); + + // Fallback approach using a temporary textarea + const textarea = document.createElement("textarea"); + textarea.value = text; + textarea.style.position = "fixed"; // prevent scrolling + textarea.style.left = "-9999px"; // off-screen + document.body.appendChild(textarea); + textarea.focus(); + textarea.select(); + + let successful = false; + try { + successful = document.execCommand("copy"); + } catch (err) { + successful = false; + } + + document.body.removeChild(textarea); + + if (successful) { + status.textContent = "✓ Copied!"; + status.style.color = "green"; + } else { + status.textContent = "✗ Copy failed"; + status.style.color = "red"; + } +}); +</script> + + + <h3>File Actions</h3> <form method="post" action="/file/{{.Data.File.ID}}/rename" style="display:inline-block; margin-right: 20px;"> New filename: <input type="text" name="newfilename" value="{{.Data.File.Filename}}" required style="width:300px"><br>