// Package caseoverviewapi ist die HTTP-Bindung der workflow-eigenen // Akten-Uebersicht (VOR-05). Nutzeridentitaet kommt uebergangsweise aus // X-User-Id, wie bereits bei caseapi (VOR-01) und backchannelapi (VOR-04) // etabliert. package caseoverviewapi import ( "encoding/json" "errors" "net/http" "time" "gitea.perlbach24.de/scripte/nexarch/workflow/internal/caseobj" "gitea.perlbach24.de/scripte/nexarch/workflow/internal/caseoverview" "gitea.perlbach24.de/scripte/nexarch/workflow/internal/reminder" ) type Server struct { svc *caseoverview.Service now func() time.Time } func NewServer(svc *caseoverview.Service) *Server { return &Server{svc: svc, now: time.Now} } // RegisterRoutes bindet die Uebersichts-Routen. Alle Routen verlangen // X-User-Id, wie die uebrige workflow-eigene API. func (s *Server) RegisterRoutes(mux *http.ServeMux) { mux.HandleFunc("GET /cases", s.requireUser(s.handleList)) mux.HandleFunc("GET /cases/{id}/overview", s.requireUser(s.handleDetail)) mux.HandleFunc("GET /reminders/kanban", s.requireUser(s.handleKanban)) mux.HandleFunc("POST /reminders/{id}/move", s.requireUser(s.handleMove)) } func (s *Server) requireUser(next func(w http.ResponseWriter, r *http.Request)) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.Header.Get("X-User-Id") == "" { writeError(w, http.StatusUnauthorized, "X-User-Id fehlt") return } next(w, r) } } func (s *Server) handleList(w http.ResponseWriter, r *http.Request) { status := r.URL.Query().Get("status") responsible := r.URL.Query().Get("responsible") cases, err := s.svc.List(r.Context(), status, responsible) if err != nil { writeError(w, http.StatusInternalServerError, "akten konnten nicht gelesen werden") return } if cases == nil { cases = []caseobj.Case{} } writeJSON(w, http.StatusOK, cases) } func (s *Server) handleDetail(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") detail, err := s.svc.Detail(r.Context(), id) switch { case err == nil: writeJSON(w, http.StatusOK, detail) case errors.Is(err, caseobj.ErrNotFound): writeError(w, http.StatusNotFound, "akte nicht gefunden") default: writeError(w, http.StatusInternalServerError, "akte konnte nicht gelesen werden") } } func (s *Server) handleKanban(w http.ResponseWriter, r *http.Request) { items, err := s.svc.Kanban(r.Context(), s.now()) if err != nil { writeError(w, http.StatusInternalServerError, "wiedervorlagen konnten nicht gelesen werden") return } if items == nil { items = []caseoverview.KanbanItem{} } writeJSON(w, http.StatusOK, items) } type moveRequest struct { Target string `json:"target"` } func (s *Server) handleMove(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") var req moveRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "ungueltiger anfrage-koerper") return } err := s.svc.Move(r.Context(), id, caseoverview.KanbanColumn(req.Target)) switch { case err == nil: w.WriteHeader(http.StatusNoContent) case errors.Is(err, reminder.ErrNotFound): writeError(w, http.StatusNotFound, "wiedervorlage nicht gefunden") default: writeError(w, http.StatusInternalServerError, "wiedervorlage konnte nicht verschoben werden") } } func writeJSON(w http.ResponseWriter, status int, v any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) _ = json.NewEncoder(w).Encode(v) } func writeError(w http.ResponseWriter, status int, message string) { writeJSON(w, status, map[string]string{"error": message}) }