Files
volumen/internal/admin/media_routes.go
T
2026-09-29 23:47:27 +02:00

83 lines
2.3 KiB
Go

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
package admin
import (
"fmt"
"net/http"
"strings"
"sourcedock.dev/petrbalvin/volumen/internal/store"
)
func (a *Admin) registerMediaRoutes(mux *http.ServeMux) {
mux.HandleFunc("GET /admin/media", a.requireLogin(a.handleMediaLibrary))
mux.HandleFunc("POST /admin/media/{name}/delete", a.requireLogin(a.handleMediaDelete))
}
func (a *Admin) handleMediaLibrary(w http.ResponseWriter, r *http.Request) {
data := a.pageData(r)
items := a.deps.Store.ListMedia()
// A profile photo of any account is not library content. Avatars
// stored since the avatar directory existed never reach this list
// at all; the filter keeps the flat photos of installations that
// predate it out of the tiles the same way.
photos := make(map[string]bool)
for _, user := range a.deps.Users.All() {
if user.Photo != "" {
photos[user.Photo] = true
}
}
if len(photos) > 0 {
filtered := items[:0]
for _, item := range items {
if !photos[item.URL] {
filtered = append(filtered, item)
}
}
items = filtered
}
data.MediaItems = mediaRows(items)
data.MediaTotal = humanSize(totalSize(items))
data.Crumbs = []Crumb{{Label: "Media", IsLast: true, UI: true}}
a.renderPage(w, r, "media.html", data, http.StatusOK)
}
// totalSize sums the byte sizes of the media library.
func totalSize(items []store.Media) int64 {
var total int64
for _, item := range items {
total += item.Size
}
return total
}
// humanSize formats a byte count for the library summary: kilobytes
// below a megabyte (rounded up, so nothing reads as zero), megabytes
// above it, empty for an empty library.
func humanSize(total int64) string {
switch {
case total <= 0:
return ""
case total < 1024*1024:
kb := (total + 1023) / 1024
return fmt.Sprintf("%d kB", kb)
default:
return fmt.Sprintf("%.1f MB", float64(total)/(1024*1024))
}
}
func (a *Admin) handleMediaDelete(w http.ResponseWriter, r *http.Request) {
if !a.requireCSRF(w, r) {
return
}
name := r.PathValue("name")
if !a.deps.Store.DeleteMedia("/media/" + strings.TrimPrefix(name, "/")) {
http.Error(w, "File not found", http.StatusNotFound)
return
}
a.record(r, "media.deleted", fmt.Sprintf("/media/%s", name), nil)
http.Redirect(w, r, "/admin/media", http.StatusSeeOther)
}