83 lines
1.8 KiB
Go
83 lines
1.8 KiB
Go
|
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)
|
||
|
}
|