package api import ( "bufio" "encoding/json" "fmt" "io" "log/slog" "net/http" "strconv" "git.clustercockpit.org/moebiusband/go-http-skeleton/internal/repository" ) type ErrorResponse struct { // Statustext of Errorcode Status string `json:"status"` Error string `json:"error"` // Error Message } func MountApiEndpoints(r *http.ServeMux) { r.HandleFunc("POST /api/news/", createNewsItem) r.HandleFunc("GET /api/news/", readNewsItems) r.HandleFunc("GET /api/news/{id}", readNewsItem) r.HandleFunc("PATCH /api/news/", updateNewsItem) r.HandleFunc("DELETE /api/news/{id}", deleteNewsItem) } func handleError(err error, statusCode int, rw http.ResponseWriter) { slog.Warn("rest error", "error", err) rw.Header().Add("Content-Type", "application/json") rw.WriteHeader(statusCode) json.NewEncoder(rw).Encode(ErrorResponse{ Status: http.StatusText(statusCode), Error: err.Error(), }) } func decode(r io.Reader, val any) error { dec := json.NewDecoder(r) dec.DisallowUnknownFields() return dec.Decode(val) } func createNewsItem(rw http.ResponseWriter, r *http.Request) { req := repository.CreateNewsEntryParams{} if err := decode(r.Body, &req); err != nil { handleError(fmt.Errorf("parsing request body failed: %w", err), http.StatusBadRequest, rw) return } repo := repository.GetRepository() err := repo.CreateNewsEntry(r.Context(), req) if err != nil { handleError(err, http.StatusBadRequest, rw) return } } func readNewsItems(rw http.ResponseWriter, r *http.Request) { repo := repository.GetRepository() items, err := repo.ListNews(r.Context()) if err != nil { handleError(err, http.StatusBadRequest, rw) return } slog.Debug("/api/news returned", "newscount", len(items)) rw.Header().Add("Content-Type", "application/json") bw := bufio.NewWriter(rw) defer bw.Flush() if err := json.NewEncoder(bw).Encode(items); err != nil { handleError(err, http.StatusInternalServerError, rw) return } } func readNewsItem(rw http.ResponseWriter, r *http.Request) { repo := repository.GetRepository() id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) if err != nil { handleError(err, http.StatusBadRequest, rw) return } item, err := repo.GetNewsEntry(r.Context(), id) if err != nil { handleError(err, http.StatusBadRequest, rw) return } rw.Header().Add("Content-Type", "application/json") bw := bufio.NewWriter(rw) defer bw.Flush() if err := json.NewEncoder(bw).Encode(item); err != nil { handleError(err, http.StatusInternalServerError, rw) return } } func updateNewsItem(rw http.ResponseWriter, r *http.Request) { req := repository.UpdateNewsEntryParams{} if err := decode(r.Body, &req); err != nil { handleError(fmt.Errorf("parsing request body failed: %w", err), http.StatusBadRequest, rw) return } repo := repository.GetRepository() err := repo.UpdateNewsEntry(r.Context(), req) if err != nil { handleError(err, http.StatusBadRequest, rw) return } } func deleteNewsItem(rw http.ResponseWriter, r *http.Request) { repo := repository.GetRepository() id, err := strconv.ParseInt(r.PathValue("id"), 10, 64) if err != nil { handleError(err, http.StatusBadRequest, rw) return } err = repo.DeleteNewsEntry(r.Context(), id) if err != nil { handleError(err, http.StatusBadRequest, rw) return } }