feat: kubesphere 4.0 (#6115)
* feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> * feat: kubesphere 4.0 Signed-off-by: ci-bot <ci-bot@kubesphere.io> --------- Signed-off-by: ci-bot <ci-bot@kubesphere.io> Co-authored-by: ks-ci-bot <ks-ci-bot@example.com> Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
committed by
GitHub
parent
b5015ec7b9
commit
447a51f08b
139
pkg/kapis/static/handler.go
Normal file
139
pkg/kapis/static/handler.go
Normal file
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Please refer to the LICENSE file in the root directory of the project.
|
||||
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package static
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/klog/v2"
|
||||
|
||||
ksapi "kubesphere.io/kubesphere/pkg/api"
|
||||
"kubesphere.io/kubesphere/pkg/apiserver/rest"
|
||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||
)
|
||||
|
||||
const (
|
||||
imageCacheKeyPrefix = "static:images:"
|
||||
mimePNG = "image/png"
|
||||
mimeJPG = "image/jpeg"
|
||||
mimeSVG = "image/svg+xml"
|
||||
// https://mimesniff.spec.whatwg.org/#matching-an-image-type-pattern
|
||||
mimeICON = "image/x-icon"
|
||||
maxUploadSize = 2 * 1024 * 1024 // 2M
|
||||
)
|
||||
|
||||
var supportedMIMETypes = sets.NewString(mimePNG, mimeJPG, mimeSVG, mimeICON)
|
||||
|
||||
type handler struct {
|
||||
cache cache.Interface
|
||||
}
|
||||
|
||||
func init() {
|
||||
_ = mime.AddExtensionType(".ico", mimeICON)
|
||||
}
|
||||
|
||||
func NewHandler(cache cache.Interface) rest.Handler {
|
||||
return &handler{cache: cache}
|
||||
}
|
||||
func NewFakeHandler() rest.Handler {
|
||||
return &handler{}
|
||||
}
|
||||
|
||||
func (h *handler) uploadImage(req *restful.Request, resp *restful.Response) {
|
||||
req.Request.Body = http.MaxBytesReader(resp, req.Request.Body, maxUploadSize)
|
||||
if err := req.Request.ParseMultipartForm(maxUploadSize); err != nil {
|
||||
ksapi.HandleBadRequest(resp, req, fmt.Errorf("failed to parse multipart form: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
imagePart, existed := req.Request.MultipartForm.File["image"]
|
||||
if !existed || len(imagePart) == 0 {
|
||||
ksapi.HandleBadRequest(resp, req, fmt.Errorf("invalid form data"))
|
||||
return
|
||||
}
|
||||
|
||||
header := imagePart[0]
|
||||
ext := filepath.Ext(header.Filename)
|
||||
contentType := mime.TypeByExtension(ext)
|
||||
if contentType == "" {
|
||||
contentType = header.Header.Get("Content-Type")
|
||||
}
|
||||
|
||||
if !supportedMIMETypes.Has(contentType) {
|
||||
ksapi.HandleBadRequest(resp, req, fmt.Errorf("the provided file format is not allowed"))
|
||||
return
|
||||
}
|
||||
file, err := header.Open()
|
||||
if err != nil {
|
||||
ksapi.HandleInternalError(resp, req, err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
rawContent, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
ksapi.HandleInternalError(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
hasher := md5.New()
|
||||
hasher.Write(rawContent)
|
||||
hash := hasher.Sum(nil)
|
||||
|
||||
md5Hex := hex.EncodeToString(hash)
|
||||
fileName := fmt.Sprintf("%s%s", md5Hex, ext)
|
||||
key := fmt.Sprintf("%s%s", imageCacheKeyPrefix, fileName)
|
||||
|
||||
base64EncodedContent := base64.StdEncoding.EncodeToString(rawContent)
|
||||
if err = h.cache.Set(key, base64EncodedContent, cache.NeverExpire); err != nil {
|
||||
ksapi.HandleInternalError(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
result := map[string]string{"image": fileName}
|
||||
_ = resp.WriteEntity(result)
|
||||
}
|
||||
|
||||
func (h *handler) getImage(req *restful.Request, resp *restful.Response) {
|
||||
fileName := req.PathParameter("file")
|
||||
|
||||
base64EncodedData, err := h.cache.Get(fmt.Sprintf("%s%s", imageCacheKeyPrefix, fileName))
|
||||
if err != nil {
|
||||
if errors.Is(err, cache.ErrNoSuchKey) {
|
||||
ksapi.HandleNotFound(resp, req, fmt.Errorf("image %s not found", fileName))
|
||||
return
|
||||
}
|
||||
ksapi.HandleInternalError(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
rawContent, err := base64.StdEncoding.DecodeString(base64EncodedData)
|
||||
if err != nil {
|
||||
klog.Warningf("failed to decode base64 encoded image data: %s, %v", fileName, err)
|
||||
ksapi.HandleInternalError(resp, req, err)
|
||||
return
|
||||
}
|
||||
|
||||
contentType := mime.TypeByExtension(filepath.Ext(fileName))
|
||||
resp.Header().Set("Content-Type", contentType)
|
||||
resp.Header().Set("Cache-Control", "public, max-age=86400")
|
||||
resp.Header().Set("Expires", time.Now().Add(86400*time.Second).Format(time.RFC1123))
|
||||
|
||||
_, err = resp.Write(rawContent)
|
||||
if err != nil {
|
||||
klog.Warningf("failed to write image data: %s, %v", fileName, err)
|
||||
}
|
||||
}
|
||||
27
pkg/kapis/static/register.go
Normal file
27
pkg/kapis/static/register.go
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Please refer to the LICENSE file in the root directory of the project.
|
||||
* https://github.com/kubesphere/kubesphere/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package static
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful/v3"
|
||||
)
|
||||
|
||||
func (h *handler) AddToContainer(c *restful.Container) error {
|
||||
webservice := new(restful.WebService)
|
||||
webservice.Path("/static")
|
||||
webservice.Route(webservice.POST("/images").
|
||||
Doc("Upload image").
|
||||
Consumes("multipart/form-data").
|
||||
Produces(restful.MIME_JSON).
|
||||
To(h.uploadImage).
|
||||
Param(webservice.FormParameter("image", "Image content, support JPG, PNG, SVG; size limit is 2MB.")))
|
||||
webservice.Route(webservice.GET("/images/{file}").
|
||||
Doc("Get image file").
|
||||
To(h.getImage).
|
||||
Param(webservice.PathParameter("file", "File name of the image.")))
|
||||
c.Add(webservice)
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user