fix1
This commit is contained in:
@@ -3,12 +3,15 @@ package admin
|
||||
import (
|
||||
"context"
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.send.nrw/sendnrw/decent-webui/internal/blobfs"
|
||||
"git.send.nrw/sendnrw/decent-webui/internal/filesvc"
|
||||
"git.send.nrw/sendnrw/decent-webui/internal/mesh"
|
||||
)
|
||||
@@ -40,6 +43,7 @@ var (
|
||||
type Deps struct {
|
||||
Store filesvc.MeshStore
|
||||
Mesh *mesh.Node
|
||||
Blob blobfs.Store
|
||||
}
|
||||
|
||||
// Register hängt alle /admin Routen ein.
|
||||
@@ -52,7 +56,6 @@ func Register(mux *http.ServeMux, d Deps) {
|
||||
|
||||
// Partials
|
||||
mux.HandleFunc("/admin/items", func(w http.ResponseWriter, r *http.Request) {
|
||||
// Liste rendern (Pagination optional via ?next=)
|
||||
nextQ := strings.TrimSpace(r.URL.Query().Get("next"))
|
||||
var nextID filesvc.ID
|
||||
if nextQ != "" {
|
||||
@@ -61,12 +64,96 @@ func Register(mux *http.ServeMux, d Deps) {
|
||||
}
|
||||
}
|
||||
items, nextOut, _ := d.Store.List(r.Context(), nextID, 100)
|
||||
|
||||
type row struct {
|
||||
ID int64
|
||||
Name string
|
||||
UpdatedAt int64
|
||||
HasBlob bool
|
||||
Size int64
|
||||
}
|
||||
rows := make([]row, 0, len(items))
|
||||
for _, it := range items {
|
||||
meta, ok, _ := d.Blob.Stat(r.Context(), int64(it.ID))
|
||||
rows = append(rows, row{
|
||||
ID: int64(it.ID),
|
||||
Name: it.Name,
|
||||
UpdatedAt: it.UpdatedAt,
|
||||
HasBlob: ok,
|
||||
Size: meta.Size,
|
||||
})
|
||||
}
|
||||
|
||||
_ = tplItems.Execute(w, map[string]any{
|
||||
"Items": items,
|
||||
"Items": rows,
|
||||
"Next": nextOut,
|
||||
})
|
||||
})
|
||||
|
||||
// Upload (multipart/form-data, Feldname "file", optional name-Override)
|
||||
mux.HandleFunc("/admin/files/upload", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
if err := r.ParseMultipartForm(64 << 20); err != nil { // 64MB
|
||||
http.Error(w, "bad form", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
fh, hdr, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
http.Error(w, "missing file", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
defer fh.Close()
|
||||
name := strings.TrimSpace(r.FormValue("name"))
|
||||
if name == "" {
|
||||
name = hdr.Filename
|
||||
}
|
||||
|
||||
// 1) Metadatei anlegen (ID beziehen)
|
||||
it, err := d.Store.Create(r.Context(), name)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// 2) Blob speichern
|
||||
if _, err := d.Blob.Save(r.Context(), int64(it.ID), name, fh); err != nil {
|
||||
// zurückrollen (Tombstone)
|
||||
_, _ = d.Store.Delete(r.Context(), it.ID)
|
||||
http.Error(w, "save failed: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
_ = d.Mesh.SyncNow(r.Context()) // best-effort Push
|
||||
http.Redirect(w, r, "/admin/items", http.StatusSeeOther)
|
||||
})
|
||||
|
||||
// Download (Admin – BasicAuth schützt ggf.)
|
||||
mux.HandleFunc("/admin/files/", func(w http.ResponseWriter, r *http.Request) {
|
||||
// /admin/files/{id}/download
|
||||
parts := strings.Split(strings.TrimPrefix(r.URL.Path, "/admin/files/"), "/")
|
||||
if len(parts) != 2 || parts[1] != "download" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
id, err := strconv.ParseInt(parts[0], 10, 64)
|
||||
if err != nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
rc, meta, err := d.Blob.Open(r.Context(), id)
|
||||
if err != nil {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
defer rc.Close()
|
||||
w.Header().Set("Content-Type", meta.ContentType)
|
||||
w.Header().Set("Content-Length", strconv.FormatInt(meta.Size, 10))
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, meta.Name))
|
||||
_, _ = io.Copy(w, rc)
|
||||
})
|
||||
|
||||
mux.HandleFunc("/admin/peers", func(w http.ResponseWriter, r *http.Request) {
|
||||
peers := d.Mesh.PeerList()
|
||||
_ = tplPeers.Execute(w, map[string]any{
|
||||
|
||||
Reference in New Issue
Block a user