package kernel import ( _ "embed" "bytes" "compress/gzip" "encoding/json" "net/http" "strings" "sync" . "github.com/mickael-kerjean/filestash/server/pkg/config" . "github.com/mickael-kerjean/filestash/server/pkg/core" . "github.com/mickael-kerjean/filestash/server/pkg/env" . "github.com/mickael-kerjean/filestash/server/pkg/utils" ) const IndentSize = " " type APISuccessResult struct { Status string `json:"status"` Result interface{} `json:"result,omitempty"` } type APISuccessResults struct { Status string `json:"status"` Results interface{} `json:"results"` } type APISuccessResultsWithMetadata struct { Status string `json:"status"` Results interface{} `json:"results"` Metadata interface{} `json:"permissions,omitempty"` } type APIErrorMessage struct { Status string `json:"status"` Message string `json:"message,omitempty"` } func SendSuccessResult(res http.ResponseWriter, data interface{}) { encoder := json.NewEncoder(res) encoder.SetEscapeHTML(false) encoder.SetIndent("", IndentSize) encoder.Encode(APISuccessResult{"ok", data}) } func SendSuccessResultWithEtagAndGzip(res http.ResponseWriter, req *http.Request, data interface{}) { var dataToSend []byte var err error if dataToSend, err = json.MarshalIndent(APISuccessResult{"ok", data}, "", IndentSize); err != nil { dataToSend = []byte("{}") } mode := "normal" if strings.Contains(req.Header.Get("Accept-Encoding"), "gzip") == true { mode = "gzip" } hash := QuickHash(mode+string(dataToSend), 20) if req.Header.Get("If-None-Match") == hash { res.WriteHeader(http.StatusNotModified) return } head := res.Header() head.Set("Etag", hash) if mode == "gzip" { head.Set("Content-Encoding", "gzip") var b bytes.Buffer w, _ := gzip.NewWriterLevel(&b, 1) w.Write(dataToSend) w.Close() dataToSend = b.Bytes() } res.Write(dataToSend) } func SendSuccessResults(res http.ResponseWriter, data interface{}) { SendRaw(res, APISuccessResults{"ok", data}) } func SendSuccessResultsWithMetadata(res http.ResponseWriter, data interface{}, p interface{}) { SendRaw(res, APISuccessResultsWithMetadata{"ok", data, p}) } func SendErrorResult(res http.ResponseWriter, err error) { obj, ok := err.(interface{ Status() int }) if ok == true { res.WriteHeader(obj.Status()) } else { res.WriteHeader(http.StatusInternalServerError) } m := func(r string) string { if r == "" { return r } return strings.ToUpper(string(r[0])) + string(r[1:]) }(err.Error()) SendRaw(res, APIErrorMessage{"error", m}) } type jsonEncoder struct { buf bytes.Buffer *json.Encoder } var encoderPool = sync.Pool{ New: func() any { p := &jsonEncoder{} p.Encoder = json.NewEncoder(&p.buf) p.SetEscapeHTML(false) p.SetIndent("", IndentSize) return p }, } func SendRaw(res http.ResponseWriter, data interface{}) { p := encoderPool.Get().(*jsonEncoder) p.buf.Reset() if p.Encode(data) == nil { res.Write(p.buf.Bytes()) } if p.buf.Cap() <= 1_000_000 { encoderPool.Put(p) } } func Page(stuff string) string { if strings.Contains(stuff, "") { stuff += "" } return ` ` + strings.NewReplacer("{", "{", "}", "}").Replace(Config.Get("general.name").String()) + ` ` + stuff + ` ` } func RedirectPage(url string) string { return ` ` } //go:embed static/404.html var HtmlPage404 []byte func NotFoundHandler(ctx *App, res http.ResponseWriter, req *http.Request) { if strings.Contains(req.Header.Get("Accept"), "text/html") { res.WriteHeader(http.StatusNotFound) res.Write(HtmlPage404) return } SendErrorResult(res, ErrNotFound) }