mirror of
https://github.com/nabbar/golib.git
synced 2025-10-05 07:46:56 +08:00
201 lines
5.0 KiB
Go
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
|
|
}
|