Files
golib/static/static.go
Nicolas JUHEL c9b6a9d1ee Fix linter
2020-08-17 11:10:47 +02:00

201 lines
5.0 KiB
Go

/*
* MIT License
*
* Copyright (c) 2019 Nicolas JUHEL
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
package static
import (
"bytes"
"fmt"
"mime"
"net/http"
"path/filepath"
"strings"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/render"
"github.com/gobuffalo/packr"
"github.com/nabbar/golib/errors"
"github.com/nabbar/golib/logger"
"github.com/nabbar/golib/router"
)
const (
FileIndex = "index.html"
)
type staticHandler struct {
box packr.Box
download []string
prefix string
head gin.HandlerFunc
debug bool
index bool
allDwnld bool
}
type Static interface {
Register(register router.RegisterRouter)
RegisterInGroup(group string, register router.RegisterRouterInGroup)
SetDownloadAll()
SetDownload(file string)
IsDownload(file string) bool
Has(file string) bool
Find(file string) ([]byte, error)
Health() errors.Error
Get(c *gin.Context)
}
func cleanPath(p string) string {
return filepath.Clean(filepath.Join("/", strings.TrimLeft(p, "/")))
}
func cleanJoinPath(p, e string) string {
return cleanPath(filepath.Join(strings.TrimLeft(p, "/"), e))
}
func NewStatic(hasIndex bool, prefix string, box packr.Box, Header gin.HandlerFunc) Static {
return &staticHandler{
box: box,
debug: false,
index: hasIndex,
prefix: cleanPath(prefix),
head: Header,
download: make([]string, 0),
}
}
func (s *staticHandler) Register(register router.RegisterRouter) {
if s.prefix == "/" {
for _, f := range s.box.List() {
register(http.MethodGet, cleanJoinPath(s.prefix, f), s.Get)
}
} else {
register(http.MethodGet, s.prefix, s.Get)
register(http.MethodGet, cleanJoinPath(s.prefix, "/*file"), s.Get)
}
}
func (s *staticHandler) RegisterInGroup(group string, register router.RegisterRouterInGroup) {
if s.prefix == "/" {
for _, f := range s.box.List() {
register(group, http.MethodGet, cleanJoinPath(s.prefix, f), s.head, s.Get)
}
} else {
register(group, http.MethodGet, s.prefix, s.head, s.Get)
register(group, http.MethodGet, cleanJoinPath(s.prefix, "/*file"), s.head, s.Get)
}
}
func (s *staticHandler) print() {
if s.debug {
return
}
for _, f := range s.box.List() {
logger.DebugLevel.Logf("Embedded file : %s", f)
}
s.debug = true
}
func (s *staticHandler) Health() errors.Error {
s.print()
if len(s.box.List()) < 1 {
return EMPTY_PACKED.Error(nil)
}
if s.index && !s.box.Has("index.html") && !s.box.Has("index.htm") {
return INDEX_NOT_FOUND.Error(nil)
}
return nil
}
func (s *staticHandler) Has(file string) bool {
return s.box.Has(file)
}
func (s *staticHandler) Find(file string) ([]byte, error) {
return s.box.Find(file)
}
func (s *staticHandler) Get(c *gin.Context) {
partPath := strings.SplitN(c.Request.URL.Path, s.prefix, 2)
requestPath := partPath[1]
requestPath = strings.TrimLeft(requestPath, "./")
requestPath = strings.Trim(requestPath, "/")
calledFile := filepath.Base(requestPath)
if requestPath == "" || requestPath == "/" {
if s.index {
calledFile = FileIndex
requestPath = FileIndex
} else {
_ = c.AbortWithError(http.StatusNotFound, INDEX_REQUESTED_NOT_SET.Error(nil))
return
}
}
if obj, err := s.box.Find(requestPath); !logger.ErrorLevel.LogErrorCtxf(logger.DebugLevel, "find file '%s' error for request '%s%s' :", err, calledFile, s.prefix, requestPath) {
head := router.NewHeaders()
if s.allDwnld || s.IsDownload(requestPath) {
head.Add("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", calledFile))
}
c.Render(http.StatusOK, render.Reader{
ContentLength: int64(len(obj)),
ContentType: mime.TypeByExtension(filepath.Ext(calledFile)),
Headers: head.Header(),
Reader: bytes.NewReader(obj),
})
} else {
_ = c.AbortWithError(http.StatusNotFound, FILE_NOT_FOUND.Error(nil))
}
}
func (s *staticHandler) SetDownload(file string) {
s.download = append(s.download, file)
}
func (s *staticHandler) SetDownloadAll() {
s.allDwnld = true
}
func (s *staticHandler) IsDownload(file string) bool {
for _, f := range s.download {
if f == file {
return true
}
}
return false
}