This commit is contained in:
@@ -187,7 +187,7 @@ func Register(mux *http.ServeMux, d Deps) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Actions (HTMX POSTs)
|
// CREATE
|
||||||
mux.HandleFunc("/admin/items/create", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/admin/items/create", func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||||
@@ -196,12 +196,13 @@ func Register(mux *http.ServeMux, d Deps) {
|
|||||||
name := strings.TrimSpace(r.FormValue("name"))
|
name := strings.TrimSpace(r.FormValue("name"))
|
||||||
if name != "" {
|
if name != "" {
|
||||||
_, _ = d.Store.Create(r.Context(), name)
|
_, _ = d.Store.Create(r.Context(), name)
|
||||||
_ = d.Mesh.SyncNow(r.Context()) // prompt push (best effort)
|
_ = d.Mesh.SyncNow(r.Context())
|
||||||
}
|
}
|
||||||
// Nach Aktion Items partial zurückgeben (HTMX swap)
|
// statt Redirect:
|
||||||
http.Redirect(w, r, "/admin", http.StatusSeeOther)
|
renderItemsPartial(w, r, d)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// RENAME
|
||||||
mux.HandleFunc("/admin/items/rename", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/admin/items/rename", func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||||
@@ -213,21 +214,22 @@ func Register(mux *http.ServeMux, d Deps) {
|
|||||||
_, _ = d.Store.Rename(r.Context(), filesvc.ID(id), newName)
|
_, _ = d.Store.Rename(r.Context(), filesvc.ID(id), newName)
|
||||||
_ = d.Mesh.SyncNow(r.Context())
|
_ = d.Mesh.SyncNow(r.Context())
|
||||||
}
|
}
|
||||||
http.Redirect(w, r, "/admin", http.StatusSeeOther) //hier test nicht mehr /admin/items
|
renderItemsPartial(w, r, d)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// DELETE
|
||||||
mux.HandleFunc("/admin/items/delete", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/admin/items/delete", func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodPost {
|
if r.Method != http.MethodPost {
|
||||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
id64, err := strconv.ParseInt(r.FormValue("id"), 10, 64)
|
||||||
if id64, err := strconv.ParseInt(r.FormValue("id"), 10, 64); err == nil {
|
if err == nil {
|
||||||
_, _ = d.Store.Delete(r.Context(), filesvc.ID(id64))
|
_, _ = d.Store.Delete(r.Context(), filesvc.ID(id64))
|
||||||
_ = d.Blob.Delete(r.Context(), id64) // <— Blob löschen
|
_ = d.Blob.Delete(r.Context(), id64) // Blob wirklich löschen
|
||||||
_ = d.Mesh.SyncNow(r.Context())
|
_ = d.Mesh.SyncNow(r.Context())
|
||||||
}
|
}
|
||||||
http.Redirect(w, r, "/admin", http.StatusSeeOther)
|
renderItemsPartial(w, r, d)
|
||||||
})
|
})
|
||||||
|
|
||||||
mux.HandleFunc("/admin/mesh/syncnow", func(w http.ResponseWriter, r *http.Request) {
|
mux.HandleFunc("/admin/mesh/syncnow", func(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -258,3 +260,40 @@ func BasicAuth(user, pass string, next http.Handler) http.Handler {
|
|||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// rebuild & render items partial for HTMX swaps
|
||||||
|
func renderItemsPartial(w http.ResponseWriter, r *http.Request, d Deps) {
|
||||||
|
type row struct {
|
||||||
|
ID int64
|
||||||
|
Name string
|
||||||
|
UpdatedAt int64
|
||||||
|
HasBlob bool
|
||||||
|
Size int64
|
||||||
|
}
|
||||||
|
|
||||||
|
nextQ := strings.TrimSpace(r.URL.Query().Get("next"))
|
||||||
|
var nextID filesvc.ID
|
||||||
|
if nextQ != "" {
|
||||||
|
if n, err := strconv.ParseInt(nextQ, 10, 64); err == nil {
|
||||||
|
nextID = filesvc.ID(n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
items, nextOut, _ := d.Store.List(r.Context(), nextID, 100)
|
||||||
|
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
_ = tplItems.Execute(w, map[string]any{
|
||||||
|
"Items": rows,
|
||||||
|
"Next": nextOut,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div style="flex: 1 1 360px">
|
<div style="flex: 1 1 360px">
|
||||||
<form hx-post="/admin/items/create" hx-target="#items" hx-get="/admin/items" hx-swap="outerHTML">
|
<form hx-post="/admin/items/create" hx-target="#items" hx-swap="outerHTML">
|
||||||
<label>Neue leere Datei (nur Metadaten)</label>
|
<label>Neue leere Datei (nur Metadaten)</label>
|
||||||
<div style="display:flex; gap:8px; margin-top:6px">
|
<div style="display:flex; gap:8px; margin-top:6px">
|
||||||
<input type="text" name="name" placeholder="z.B. notes.txt" required>
|
<input type="text" name="name" placeholder="z.B. notes.txt" required>
|
||||||
@@ -42,17 +42,17 @@
|
|||||||
{{ end }}
|
{{ end }}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<form style="display:inline-flex; gap:6px"
|
<form style="display:inline-flex; gap:6px"
|
||||||
hx-post="/admin/items/rename"
|
hx-post="/admin/items/rename"
|
||||||
hx-target="#items" hx-get="/admin/items" hx-swap="outerHTML">
|
hx-target="#items" hx-swap="outerHTML">
|
||||||
<input type="hidden" name="id" value="{{ .ID }}">
|
<input type="hidden" name="id" value="{{ .ID }}">
|
||||||
<input type="text" name="name" placeholder="Neuer Name">
|
<input type="text" name="name" placeholder="Neuer Name">
|
||||||
<button class="btn" type="submit">Rename</button>
|
<button class="btn" type="submit">Rename</button>
|
||||||
</form>
|
</form>
|
||||||
<form style="display:inline"
|
<form style="display:inline"
|
||||||
hx-post="/admin/items/delete"
|
hx-post="/admin/items/delete"
|
||||||
hx-target="#items" hx-get="/admin/items" hx-swap="outerHTML"
|
hx-target="#items" hx-swap="outerHTML"
|
||||||
onsubmit="return confirm('Wirklich löschen (inkl. Blob)?');">
|
onsubmit="return confirm('Wirklich löschen (inkl. Blob)?');">
|
||||||
<input type="hidden" name="id" value="{{ .ID }}">
|
<input type="hidden" name="id" value="{{ .ID }}">
|
||||||
<button class="btn" type="submit">Delete</button>
|
<button class="btn" type="submit">Delete</button>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
Reference in New Issue
Block a user