From b0e94e3d73589bc604e77d27d423224b5a652638 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 23 Nov 2023 17:16:51 +0800 Subject: [PATCH] [add] sui page render (dev) --- sui/api/request.go | 57 +++++++++++++++++++++++++++-- sui/core/compile.go | 15 ++++---- sui/core/request.go | 73 +++++++++++++++++++++++++++++++++++++ sui/core/sui.go | 5 +-- sui/core/types.go | 5 ++- sui/storages/local/build.go | 25 +++++-------- 6 files changed, 148 insertions(+), 32 deletions(-) diff --git a/sui/api/request.go b/sui/api/request.go index f2a134d1b..bc38f9053 100644 --- a/sui/api/request.go +++ b/sui/api/request.go @@ -2,11 +2,14 @@ package api import ( "fmt" + "net/url" + "os" "path/filepath" "strings" "github.com/gin-gonic/gin" "github.com/yaoapp/gou/application" + "github.com/yaoapp/kun/log" "github.com/yaoapp/yao/sui/core" ) @@ -37,7 +40,7 @@ func NewRequestContext(c *gin.Context) (*Request, int, error) { Body: body, Payload: payload, Referer: c.Request.Referer(), - Headers: c.Request.Header, + Headers: url.Values(c.Request.Header), Params: params, }, }, 200, nil @@ -45,7 +48,53 @@ func NewRequestContext(c *gin.Context) (*Request, int, error) { // Render is the response for the page API. func (r *Request) Render() (string, int, error) { - return r.File, 200, nil + + c := core.GetCache(r.File) + if c == nil { + // Read the file + content, err := application.App.Read(r.File) + if err != nil { + return "", 404, err + } + + doc, err := core.NewDocument(content) + if err != nil { + return "", 500, err + } + + dataText := "" + dataSel := doc.Find("script[name=data]") + if dataSel != nil && dataSel.Length() > 0 { + dataText = dataSel.Text() + dataSel.Remove() + } + + html, err := doc.Html() + if err != nil { + return "", 500, fmt.Errorf("parse error, please re-complie the page %s", err.Error()) + } + + // Save to The Cache + c = core.SetCache(r.File, html, dataText) + log.Trace("The page %s is cached", r.File) + } + + var err error + data := core.Data{} + if c.Data != "" { + data, err = r.Request.ExecString(c.Data) + if err != nil { + return "", 500, fmt.Errorf("data error, please re-complie the page %s", err.Error()) + } + } + + parser := core.NewTemplateParser(data, nil) + html, err := parser.Render(c.HTML) + if err != nil { + return "", 500, fmt.Errorf("render error, please re-complie the page %s", err.Error()) + } + + return html, 200, nil } func parserPath(c *gin.Context) (string, map[string]string, error) { @@ -57,7 +106,7 @@ func parserPath(c *gin.Context) (string, map[string]string, error) { return "", nil, fmt.Errorf("no route matchers") } - fileParts := []string{application.App.Root(), "public"} + fileParts := []string{string(os.PathSeparator), "public"} // Match the sui matchers := core.RouteExactMatchers[parts[0]] @@ -101,7 +150,7 @@ func parserPath(c *gin.Context) (string, map[string]string, error) { } else if matcher.Regex != nil { if matcher.Regex.MatchString(part) { - file := matcher.Ref.(string) + file := matcher.Ref key := strings.TrimRight(strings.TrimLeft(file, "["), "]") params[key] = part fileParts = append(fileParts, file) diff --git a/sui/core/compile.go b/sui/core/compile.go index 6ac2e74f6..c3fc0955f 100644 --- a/sui/core/compile.go +++ b/sui/core/compile.go @@ -1,7 +1,6 @@ package core import ( - "fmt" "regexp" "github.com/evanw/esbuild/pkg/api" @@ -30,13 +29,13 @@ func (page *Page) Compile(option *BuildOption) (string, error) { ) } - // add the route data - doc.Find("body").AppendHtml(`\n") + // // add the route data + // doc.Find("body").AppendHtml(`\n") html, err := doc.Html() if err != nil { diff --git a/sui/core/request.go b/sui/core/request.go index 468a14e20..1f1841662 100644 --- a/sui/core/request.go +++ b/sui/core/request.go @@ -9,6 +9,15 @@ import ( "github.com/yaoapp/kun/any" ) +// Cache the cache +type Cache struct { + Data string + HTML string +} + +// Caches the caches +var Caches = map[string]*Cache{} + // ExecString get the data func (r *Request) ExecString(data string) (Data, error) { var res Data @@ -50,6 +59,39 @@ func (r *Request) Exec(m Data) error { func (r *Request) execValue(value interface{}) (interface{}, error) { switch v := value.(type) { case string: + + if strings.HasPrefix(v, "$query.") { + key := strings.TrimLeft(v, "$query.") + if r.Query.Has(key) { + return r.Query.Get(key), nil + } + return "", nil + } + + if strings.HasPrefix(v, "$header.") { + key := strings.TrimLeft(v, "$header.") + if r.Headers.Has(key) { + return r.Headers.Get(key), nil + } + return "", nil + } + + if strings.HasPrefix(v, "$param.") { + key := strings.TrimLeft(v, "$param.") + if value, has := r.Params[key]; has { + return value, nil + } + return "", nil + } + + if strings.HasPrefix(v, "$payload.") { + key := strings.TrimLeft(v, "$payload.") + if value, has := r.Payload[key]; has { + return value, nil + } + return "", nil + } + if strings.HasPrefix(v, "$") { return r.call(strings.TrimLeft(v, "$")) } @@ -127,6 +169,10 @@ func (r *Request) call(p interface{}) (interface{}, error) { return nil, err } + if r.Sid != "" { + process.WithSID(r.Sid) + } + return process.Exec() } @@ -183,3 +229,30 @@ func (r *Request) parseArgs(args []interface{}) ([]interface{}, error) { return args, nil } + +// SetCache set the cache +func SetCache(file string, html string, data string) *Cache { + Caches[file] = &Cache{ + Data: data, + HTML: html, + } + return Caches[file] +} + +// GetCache get the cache +func GetCache(file string) *Cache { + if cache, has := Caches[file]; has { + return cache + } + return nil +} + +// RemoveCache remove the cache +func RemoveCache(file string) { + delete(Caches, file) +} + +// CleanCache clean the cache +func CleanCache() { + Caches = map[string]*Cache{} +} diff --git a/sui/core/sui.go b/sui/core/sui.go index 139aa222f..7845e4c45 100644 --- a/sui/core/sui.go +++ b/sui/core/sui.go @@ -30,7 +30,6 @@ func (sui *DSL) WithSid(sid string) { // PublicRootMatcher returns the public root matcher func (sui *DSL) PublicRootMatcher() *Matcher { - var ref SUI = sui pub := sui.GetPublic() if varRe.MatchString(pub.Root) { if pub.Matcher != "" { @@ -41,9 +40,9 @@ func (sui *DSL) PublicRootMatcher() *Matcher { } return &Matcher{Regex: re} } - return &Matcher{Regex: RouteRegexp, Ref: ref} + return &Matcher{Regex: RouteRegexp} } - return &Matcher{Exact: pub.Root, Ref: ref} + return &Matcher{Exact: pub.Root} } // PublicRoot returns the public root path diff --git a/sui/core/types.go b/sui/core/types.go index e797afc37..04a8db22d 100644 --- a/sui/core/types.go +++ b/sui/core/types.go @@ -147,8 +147,9 @@ type Request struct { Payload map[string]interface{} `json:"payload,omitempty"` Query url.Values `json:"query,omitempty"` Params map[string]string `json:"params,omitempty"` - Headers map[string][]string `json:"headers,omitempty"` + Headers url.Values `json:"headers,omitempty"` Body interface{} `json:"body,omitempty"` + Sid string `json:"sid,omitempty"` Theme string `json:"theme,omitempty"` Locale string `json:"locale,omitempty"` } @@ -266,7 +267,7 @@ type Matcher struct { Regex *regexp.Regexp `json:"regex,omitempty"` Exact string `json:"exact,omitempty"` Parent string `json:"-"` - Ref interface{} `json:"-"` + Ref string `json:"-"` } // DocumentDefault is the default document diff --git a/sui/storages/local/build.go b/sui/storages/local/build.go index 7afcd7485..bc598cb5c 100644 --- a/sui/storages/local/build.go +++ b/sui/storages/local/build.go @@ -99,28 +99,23 @@ func (page *Page) publicFile() string { log.Error("publicFile: Get the public root error: %s. use %s", err.Error(), page.tmpl.local.DSL.Public.Root) root = page.tmpl.local.DSL.Public.Root } - return filepath.Join(application.App.Root(), "public", root, page.Route) + return filepath.Join("/", "public", root, page.Route) } // writeHTMLTo write the html to file func (page *Page) writeHTML(html []byte) error { htmlFile := fmt.Sprintf("%s.sui", page.publicFile()) - dir := filepath.Dir(htmlFile) + htmlFileAbs := filepath.Join(application.App.Root(), htmlFile) + dir := filepath.Dir(htmlFileAbs) if exist, _ := os.Stat(dir); exist == nil { os.MkdirAll(dir, os.ModePerm) } - return os.WriteFile(htmlFile, html, 0644) -} - -// writeHTMLTo write the html to file -func (page *Page) writeData() error { - if page.Codes.DATA.Code == "" { - return nil - } - dataFile := fmt.Sprintf("%s.json", page.publicFile()) - dir := filepath.Dir(dataFile) - if exist, _ := os.Stat(dir); exist == nil { - os.MkdirAll(dir, os.ModePerm) + err := os.WriteFile(htmlFileAbs, html, 0644) + if err != nil { + return err } - return os.WriteFile(dataFile, []byte(page.Codes.DATA.Code), 0644) + + core.RemoveCache(htmlFile) + log.Trace("The page %s is removed", htmlFile) + return nil }