ref: d6fc20a67e1128c101617581bba6a17592699876
parent: f17f591d30d890670005da4e262ddd7f6e755841
author: Alex Musolino <alex@musolino.id.au>
date: Fri Jan 9 08:16:45 EST 2026
imgsrv.go: allow images tagged "public" to be accessed without auth
--- a/imgsrv.go
+++ b/imgsrv.go
@@ -7,6 +7,7 @@
"net/http"
"os"
"path"
+ "slices"
"sort"
"strings"
"sync"
@@ -26,6 +27,9 @@
}
func (h *YearIndexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {+ if !checkauth(w, r) {+ return
+ }
if r.URL.Path == "montage.jpg" { http.ServeFile(w, r, fmt.Sprintf("%s/montage.jpg", h.Idx.Path))return
@@ -66,12 +70,20 @@
Tags *Tags
}
+func imageNameFromJpeg(jpeg string) string {+ name, _ := strings.CutSuffix(strings.ToLower(jpeg), ".jpg")
+ for _, suffix := range []string{".big", ".medium", ".full", ".small", ".thumb"} {+ name, _ = strings.CutSuffix(name, suffix)
+ }
+ return name
+}
+
func (h *AlbumIndexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {relpath := strings.TrimPrefix(r.URL.Path, "/")
- switch relpath {- case "":
- fallthrough
- case "index.html":
+ if relpath == "" || relpath == "index.html" {+ if !checkauth(w, r) {+ return
+ }
type TplImgData struct {ID string
Prefix string
@@ -115,6 +127,9 @@
return
}
if strings.HasSuffix(relpath, ".html") {+ if !checkauth(w, r) {+ return
+ }
type TplData struct {Title string
UpText string
@@ -147,6 +162,12 @@
return
}
if strings.HasSuffix(strings.ToLower(relpath), ".jpg") {+ image := imageNameFromJpeg(relpath)
+ if !slices.Contains(h.Tags.TagsForImage(image), "public") {+ if !checkauth(w, r) {+ return
+ }
+ }
w.Header().Add("cache-control", "private, max-age=3600") http.ServeFile(w, r, fmt.Sprintf("%s/%s", h.Idx.Path, r.URL.Path))return
@@ -161,6 +182,9 @@
}
func (h *MainIndexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {+ if !checkauth(w, r) {+ return
+ }
type TplData struct {Title string
Years sort.StringSlice
@@ -280,11 +304,6 @@
Images map[string]struct{}}
-func (db *ImgDB) imageExists(img string) bool {- _, exists := db.Images[img]
- return exists
-}
-
func (db *ImgDB) nextMonth(y0 string, m0, step int) *AlbumIdx {var years []string
for y := range db.Years {@@ -614,12 +633,15 @@
}
func (h *TagApiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {+ if !checkauth(w, r) {+ return
+ }
if err := r.ParseForm(); err != nil {http.Error(w, "bad request", 400)
return
}
img := r.FormValue("image")- if !h.DB.imageExists(img) {+ if _, exists := h.DB.Images[img]; !exists {http.Error(w, "not found", 404)
return
}
@@ -657,6 +679,9 @@
}
func (h *TagIndexHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {+ if !checkauth(w, r) {+ return
+ }
parts := strings.Split(r.URL.Path, "/")
if len(parts) == 1 && !strings.HasSuffix(r.URL.Path, "/") { http.Redirect(w, r, fmt.Sprintf("/tags/%s/", parts[0]), http.StatusSeeOther)@@ -705,25 +730,15 @@
To uint
}
-type BasicAuthMux struct {- serveMux *http.ServeMux
-}
-
-func NewBasicAuthMux(serveMux *http.ServeMux) *BasicAuthMux {- return &BasicAuthMux{- serveMux: serveMux,
- }
-}
-
-func (basicAuthMux *BasicAuthMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {+func checkauth(w http.ResponseWriter, r *http.Request) bool {user, pass, ok := r.BasicAuth()
if !ok || user != "alex" || pass != "holyidentitytheftbatman!" { w.Header().Set("WWW-Authenticate", "Basic realm=PAIN")status := http.StatusUnauthorized
http.Error(w, http.StatusText(status), status)
- return
+ return false
}
- basicAuthMux.serveMux.ServeHTTP(w, r)
+ return true
}
func main() {@@ -763,5 +778,5 @@
mux.Handle(prefix, http.StripPrefix(prefix, &AlbumIndexHandler{idx, templates.Album, templates.Image, tags}))}
mux.Handle("/", &MainIndexHandler{db, templates.Main, tags})- log.Fatal(http.ListenAndServeTLS(":8080", "imgsrv.cert.pem", "imgsrv.key.pem", NewBasicAuthMux(mux)))+ log.Fatal(http.ListenAndServeTLS(":8080", "imgsrv.cert.pem", "imgsrv.key.pem", mux))}
--
⑨