fix1
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
|
||||
// ← Passe diese Import-Pfade an dein go.mod an
|
||||
"git.send.nrw/sendnrw/decent-webui/internal/admin"
|
||||
"git.send.nrw/sendnrw/decent-webui/internal/blobfs"
|
||||
"git.send.nrw/sendnrw/decent-webui/internal/filesvc"
|
||||
"git.send.nrw/sendnrw/decent-webui/internal/mesh"
|
||||
)
|
||||
@@ -280,6 +282,73 @@ func fileRoutes(mux *http.ServeMux, store filesvc.MeshStore) {
|
||||
})
|
||||
}
|
||||
|
||||
// apiFiles wires upload/download endpoints
|
||||
func apiFiles(mux *http.ServeMux, store filesvc.MeshStore, blobs blobfs.Store, meshNode *mesh.Node) {
|
||||
// Multipart-Upload
|
||||
mux.HandleFunc("/api/v1/files/upload", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
w.WriteHeader(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
if err := r.ParseMultipartForm(128 << 20); err != nil { // 128MB
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "bad form"})
|
||||
return
|
||||
}
|
||||
fh, hdr, err := r.FormFile("file")
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "missing file"})
|
||||
return
|
||||
}
|
||||
defer fh.Close()
|
||||
name := strings.TrimSpace(r.FormValue("name"))
|
||||
if name == "" {
|
||||
name = hdr.Filename
|
||||
}
|
||||
|
||||
it, err := store.Create(r.Context(), name)
|
||||
if err != nil {
|
||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
meta, err := blobs.Save(r.Context(), int64(it.ID), name, fh)
|
||||
if err != nil {
|
||||
_, _ = store.Delete(r.Context(), it.ID)
|
||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
_ = meshNode.SyncNow(r.Context())
|
||||
writeJSON(w, http.StatusCreated, map[string]any{
|
||||
"file": it,
|
||||
"blob": meta,
|
||||
})
|
||||
})
|
||||
|
||||
// Download
|
||||
mux.HandleFunc("/api/v1/files/", func(w http.ResponseWriter, r *http.Request) {
|
||||
// /api/v1/files/{id}/download
|
||||
parts := strings.Split(strings.TrimPrefix(r.URL.Path, "/api/v1/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 := blobs.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)
|
||||
})
|
||||
}
|
||||
|
||||
/*** Mesh <-> Store Mapping (falls Typen getrennt sind) ***/
|
||||
|
||||
func toMeshSnapshot(s filesvc.Snapshot) mesh.Snapshot {
|
||||
@@ -345,13 +414,15 @@ func main() {
|
||||
root := http.NewServeMux()
|
||||
|
||||
// API (Bearer-Auth)
|
||||
blobs := blobfs.New(getenvDefault("DATA_DIR", "./data"))
|
||||
apiMux := http.NewServeMux()
|
||||
fileRoutes(apiMux, st)
|
||||
apiFiles(apiMux, st, blobs, mnode)
|
||||
root.Handle("/api/", authMiddleware(cfg.APIKey, apiMux))
|
||||
|
||||
// Admin-UI (optional BasicAuth via ADMIN_USER/ADMIN_PASS)
|
||||
adminRoot := http.NewServeMux()
|
||||
admin.Register(adminRoot, admin.Deps{Store: st, Mesh: mnode})
|
||||
admin.Register(adminRoot, admin.Deps{Store: st, Mesh: mnode, Blob: blobs})
|
||||
adminUser := os.Getenv("ADMIN_USER")
|
||||
adminPass := os.Getenv("ADMIN_PASS")
|
||||
if strings.TrimSpace(adminUser) != "" {
|
||||
|
||||
Reference in New Issue
Block a user