diff --git a/go.mod b/go.mod index dc80a2dc2..aeaf74133 100644 --- a/go.mod +++ b/go.mod @@ -105,6 +105,7 @@ require ( github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect go.mongodb.org/mongo-driver v1.13.0 // indirect golang.org/x/arch v0.6.0 // indirect + golang.org/x/image v0.14.0 // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.18.0 // indirect golang.org/x/oauth2 v0.14.0 // indirect diff --git a/go.sum b/go.sum index 824b77dba..c5945c76f 100644 --- a/go.sum +++ b/go.sum @@ -261,8 +261,9 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo= golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= +golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4= +golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= diff --git a/sui/api/api.go b/sui/api/api.go index 11e18a1ad..b4a2f99f0 100644 --- a/sui/api/api.go +++ b/sui/api/api.go @@ -168,7 +168,7 @@ var dsl = []byte(` "method": "GET", "guard": "-", "process": "sui.Template.Asset", - "in": ["$param.id", "$param.template_id", "$param.path"], + "in": ["$param.id", "$param.template_id", "$param.path", "$query.w", "$query.h"], "out": { "status": 200, "body": "?:content", @@ -185,7 +185,7 @@ var dsl = []byte(` "guard": "query-jwt", "method": "GET", "process": "sui.Page.Asset", - "in": ["$param.id", "$param.template_id", "$param.path"], + "in": ["$param.id", "$param.template_id", "$param.path", "$query.w", "$query.h"], "out": { "status": 200, "body": "?:content", diff --git a/sui/api/process.go b/sui/api/process.go index 3372ba01b..a751c85c0 100644 --- a/sui/api/process.go +++ b/sui/api/process.go @@ -102,7 +102,9 @@ func TemplateAsset(process *process.Process) interface{} { exception.New(err.Error(), 500).Throw() } - asset, err := tmpl.Asset(process.ArgsString(2)) + w := process.ArgsInt(3, 0) + h := process.ArgsInt(4, 0) + asset, err := tmpl.Asset(process.ArgsString(2), uint(w), uint(h)) if err != nil { exception.New(err.Error(), 404).Throw() } diff --git a/sui/core/interfaces.go b/sui/core/interfaces.go index 20bc9911f..cc69999fe 100644 --- a/sui/core/interfaces.go +++ b/sui/core/interfaces.go @@ -39,7 +39,7 @@ type ITemplate interface { Locales() []SelectOption Themes() []SelectOption - Asset(file string) (*Asset, error) + Asset(file string, width, height uint) (*Asset, error) AssetUpload(reader io.Reader, name string) (string, error) MediaSearch(query url.Values, page int, pageSize int) (MediaSearchResult, error) diff --git a/sui/storages/local/template.go b/sui/storages/local/template.go index e53e5421c..a33893166 100644 --- a/sui/storages/local/template.go +++ b/sui/storages/local/template.go @@ -3,7 +3,6 @@ package local import ( "fmt" "io" - "math" "net/url" "os" "path/filepath" @@ -54,17 +53,32 @@ func (tmpl *Template) Themes() []core.SelectOption { // MediaSearch search the asset func (tmpl *Template) MediaSearch(query url.Values, page int, pageSize int) (core.MediaSearchResult, error) { res := core.MediaSearchResult{Data: []core.Media{}, Page: page, PageSize: pageSize} + keyword := query.Get("keyword") + types := query["types"] + if types == nil { + types = []string{"image", "video", "audio"} + } + exts := tmpl.mediaExts(types) + path := filepath.Join(tmpl.Root, "__assets", "upload") + files, total, pagecnt, err := tmpl.local.fs.List(path, exts, page, pageSize, func(s string) bool { + if keyword == "" { + return true + } + return strings.Contains(s, keyword) + }) + + if err != nil { + return res, err + } + + for _, file := range files { - total := 124 - pagecnt := int(math.Ceil(float64(total) / float64(pageSize))) - for i := 0; i < pageSize; i++ { - test := fmt.Sprintf("https://plus.unsplash.com/premium_photo-1671641797903-fd39ec702b16?auto=format&fit=crop&q=80&w=2334&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%%3D%%3D&id=%d", (page-1)*pageSize+i) - thumb := fmt.Sprintf("https://plus.unsplash.com/premium_photo-1671641797903-fd39ec702b16?auto=format&fit=crop&q=80&w=100&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%%3D%%3D&id=%d", (page-1)*pageSize+i) + file = strings.TrimPrefix(file, filepath.Join(tmpl.Root, "__assets", "upload")) res.Data = append(res.Data, core.Media{ - ID: test, - URL: test, - Thumb: thumb, - Type: "image", + ID: file, + URL: filepath.Join("@assets", "upload", file), + Thumb: filepath.Join("@assets", "upload", file), + Type: tmpl.mediaType(file), Width: 100, Height: 100, }) @@ -86,6 +100,57 @@ func (tmpl *Template) MediaSearch(query url.Values, page int, pageSize int) (cor return res, nil } +func (tmpl *Template) mediaExts(types []string) []string { + exts := []string{} + for _, typ := range types { + switch typ { + + case "image": + exts = append(exts, []string{".jpg", ".jpeg", ".png"}...) + break + + case "video": + exts = append(exts, []string{".mp4"}...) + break + + case "audio": + exts = append(exts, []string{".mp3"}...) + break + } + } + + return exts +} + +func (tmpl *Template) mediaType(file string) string { + ext := strings.ToLower(filepath.Ext(file)) + switch ext { + + case ".jpg": + return "image" + + case ".jpeg": + return "image" + + case ".png": + return "image" + + case ".gif": + return "image" + + case ".bmp": + return "image" + + case ".mp4": + return "video" + + case ".mp3": + return "audio" + } + + return "file" +} + // AssetUpload upload the asset func (tmpl *Template) AssetUpload(reader io.Reader, name string) (string, error) { @@ -101,11 +166,15 @@ func (tmpl *Template) AssetUpload(reader io.Reader, name string) (string, error) } // Asset get the asset -func (tmpl *Template) Asset(file string) (*core.Asset, error) { +func (tmpl *Template) Asset(file string, width, height uint) (*core.Asset, error) { file = filepath.Join(tmpl.Root, "__assets", file) if exist, _ := tmpl.local.fs.Exists(file); exist { + if width > 0 || height > 0 { + return tmpl.assetThumb(file, width, height) + } + content, err := tmpl.local.fs.ReadFile(file) if err != nil { return nil, err @@ -145,3 +214,26 @@ func (tmpl *Template) Asset(file string) (*core.Asset, error) { return nil, fmt.Errorf("Asset %s not found", file) } + +func (tmpl *Template) assetThumb(file string, width, height uint) (*core.Asset, error) { + + cacheFile := filepath.Join(tmpl.Root, "__assets", ".cache", fmt.Sprintf("%dx%d", width, height), file) + exist, _ := tmpl.local.fs.Exists(cacheFile) + if !exist { + err := tmpl.local.fs.Resize(file, cacheFile, width, height) + if err != nil { + return nil, err + } + } + + typ, err := tmpl.local.fs.MimeType(file) + if err != nil { + return nil, err + } + + content, err := tmpl.local.fs.ReadFile(cacheFile) + if err != nil { + return nil, err + } + return &core.Asset{Type: typ, Content: content}, nil +} diff --git a/sui/storages/local/template_test.go b/sui/storages/local/template_test.go index c376d8dd5..99f3a7613 100644 --- a/sui/storages/local/template_test.go +++ b/sui/storages/local/template_test.go @@ -59,7 +59,7 @@ func TestTemplateAsset(t *testing.T) { t.Fatalf("GetTemplate error: %v", err) } - asset, err := tmpl.Asset("/css/tailwind.css") + asset, err := tmpl.Asset("/css/tailwind.css", 0, 0) if err != nil { t.Fatalf("Asset error: %v", err) }