package http import ( "encoding/json" "io" "io/fs" "net/http" "os" "github.com/pkg/errors" "gitlab.com/wpetit/goweb/logger" ) const ( ErrCodeForbidden = "forbidden" ErrCodeInternalError = "internal-error" ErrCodeBadRequest = "bad-request" ErrCodeNotFound = "not-found" ) type jsonErrorResponse struct { Error jsonErr `json:"error"` } type jsonErr struct { Code string `json:"code"` } func JSONError(w http.ResponseWriter, status int, code string) { w.Header().Add("Content-Type", "application/json") w.WriteHeader(status) encoder := json.NewEncoder(w) response := jsonErrorResponse{ Error: jsonErr{ Code: code, }, } if err := encoder.Encode(response); err != nil { panic(errors.WithStack(err)) } } func ServeFile(w http.ResponseWriter, r *http.Request, fs fs.FS, path string) { ctx := logger.With(r.Context(), logger.F("path", path)) file, err := fs.Open(path) if err != nil { if errors.Is(err, os.ErrNotExist) { http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) return } logger.Error(ctx, "error while opening fs file", logger.CapturedE(errors.WithStack(err))) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } defer func() { if err := file.Close(); err != nil { logger.Error(ctx, "error while closing fs file", logger.CapturedE(errors.WithStack(err))) } }() info, err := file.Stat() if err != nil { logger.Error(ctx, "error while retrieving fs file stat", logger.CapturedE(errors.WithStack(err))) http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) return } reader, ok := file.(io.ReadSeeker) if !ok { return } http.ServeContent(w, r, path, info.ModTime(), reader) }