package handlers import ( "fmt" "io" "net/http" "mgit.msbls.de/m/KanzlAI-mGMT/internal/auth" "mgit.msbls.de/m/KanzlAI-mGMT/internal/services" "github.com/google/uuid" ) const maxUploadSize = 50 << 20 // 50 MB type DocumentHandler struct { svc *services.DocumentService } func NewDocumentHandler(svc *services.DocumentService) *DocumentHandler { return &DocumentHandler{svc: svc} } func (h *DocumentHandler) ListByCase(w http.ResponseWriter, r *http.Request) { tenantID, ok := auth.TenantFromContext(r.Context()) if !ok { writeError(w, http.StatusForbidden, "missing tenant") return } caseID, err := uuid.Parse(r.PathValue("id")) if err != nil { writeError(w, http.StatusBadRequest, "invalid case ID") return } docs, err := h.svc.ListByCase(r.Context(), tenantID, caseID) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusOK, map[string]any{ "documents": docs, "total": len(docs), }) } func (h *DocumentHandler) Upload(w http.ResponseWriter, r *http.Request) { tenantID, ok := auth.TenantFromContext(r.Context()) if !ok { writeError(w, http.StatusForbidden, "missing tenant") return } userID, _ := auth.UserFromContext(r.Context()) caseID, err := uuid.Parse(r.PathValue("id")) if err != nil { writeError(w, http.StatusBadRequest, "invalid case ID") return } r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize) if err := r.ParseMultipartForm(maxUploadSize); err != nil { writeError(w, http.StatusBadRequest, "file too large or invalid multipart form") return } file, header, err := r.FormFile("file") if err != nil { writeError(w, http.StatusBadRequest, "missing file field") return } defer file.Close() title := r.FormValue("title") if title == "" { title = header.Filename } contentType := header.Header.Get("Content-Type") if contentType == "" { contentType = "application/octet-stream" } input := services.CreateDocumentInput{ Title: title, DocType: r.FormValue("doc_type"), Filename: header.Filename, ContentType: contentType, Size: int(header.Size), Data: file, } doc, err := h.svc.Create(r.Context(), tenantID, caseID, userID, input) if err != nil { if err.Error() == "case not found" { writeError(w, http.StatusNotFound, "case not found") return } writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusCreated, doc) } func (h *DocumentHandler) Download(w http.ResponseWriter, r *http.Request) { tenantID, ok := auth.TenantFromContext(r.Context()) if !ok { writeError(w, http.StatusForbidden, "missing tenant") return } docID, err := uuid.Parse(r.PathValue("docId")) if err != nil { writeError(w, http.StatusBadRequest, "invalid document ID") return } body, contentType, title, err := h.svc.Download(r.Context(), tenantID, docID) if err != nil { if err.Error() == "document not found" || err.Error() == "document has no file" { writeError(w, http.StatusNotFound, err.Error()) return } writeError(w, http.StatusInternalServerError, err.Error()) return } defer body.Close() w.Header().Set("Content-Type", contentType) w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, title)) io.Copy(w, body) } func (h *DocumentHandler) GetMeta(w http.ResponseWriter, r *http.Request) { tenantID, ok := auth.TenantFromContext(r.Context()) if !ok { writeError(w, http.StatusForbidden, "missing tenant") return } docID, err := uuid.Parse(r.PathValue("docId")) if err != nil { writeError(w, http.StatusBadRequest, "invalid document ID") return } doc, err := h.svc.GetByID(r.Context(), tenantID, docID) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } if doc == nil { writeError(w, http.StatusNotFound, "document not found") return } writeJSON(w, http.StatusOK, doc) } func (h *DocumentHandler) Delete(w http.ResponseWriter, r *http.Request) { tenantID, ok := auth.TenantFromContext(r.Context()) if !ok { writeError(w, http.StatusForbidden, "missing tenant") return } userID, _ := auth.UserFromContext(r.Context()) docID, err := uuid.Parse(r.PathValue("docId")) if err != nil { writeError(w, http.StatusBadRequest, "invalid document ID") return } if err := h.svc.Delete(r.Context(), tenantID, docID, userID); err != nil { writeError(w, http.StatusNotFound, "document not found") return } writeJSON(w, http.StatusOK, map[string]string{"status": "deleted"}) }