From 0a5105b9d0193aa23428726c3efa9984dc05eaaf Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 26 Jun 2024 17:09:41 +0800 Subject: [PATCH] refactor: Update TemplateParser to support debug mode and DisableCache --- sui/api/request.go | 205 +++++++++++++++++--------------------------- sui/core/editor.go | 2 +- sui/core/parser.go | 16 ++-- sui/core/request.go | 56 ++++++++++-- 4 files changed, 138 insertions(+), 141 deletions(-) diff --git a/sui/api/request.go b/sui/api/request.go index e22b1da2..64880a2b 100644 --- a/sui/api/request.go +++ b/sui/api/request.go @@ -8,6 +8,7 @@ import ( "regexp" "strings" + "github.com/fatih/color" "github.com/gin-gonic/gin" jsoniter "github.com/json-iterator/go" "github.com/yaoapp/gou/application" @@ -76,10 +77,19 @@ func NewRequestContext(c *gin.Context) (*Request, int, error) { // Render is the response for the page API. func (r *Request) Render() (string, int, error) { - c := core.GetCache(r.File) - c = nil // disable cache @todo disable cache on development - // if c == nil { - if true { + // Read content from cache + var c *core.Cache = nil + if !r.Request.DisableCache() { + c = core.GetCache(r.File) + } + + if c == nil { + + // The page is not cached + message := fmt.Sprintf("[SUI] The page %s is not cached. file=%s DisableCache=%v", r.Request.URL.Path, r.File, r.Request.DisableCache()) + fmt.Println(color.YellowString(message)) + log.Warn(message) + // Read the file content, err := application.App.Read(r.File) if err != nil { @@ -136,7 +146,6 @@ func (r *Request) Render() (string, int, error) { } // Save to The Cache - // c = core.SetCache(r.File, html, dataText, globalDataText) c = &core.Cache{ Data: dataText, Global: globalDataText, @@ -145,54 +154,16 @@ func (r *Request) Render() (string, int, error) { GuardRedirect: guardRedirect, Config: configText, } - log.Trace("The page %s is cached", r.File) + core.SetCache(r.File, c) + log.Trace("[SUI] The page %s is cached file=%s", r.Request.URL.Path, r.File) } // Guard the page - if c.Guard != "" && r.context != nil { - - if guard, has := Guards[c.Guard]; has { - err := guard(r) - if err != nil { - - // Redirect the page (should refector before release) - if c.GuardRedirect != "" { - redirect := c.GuardRedirect - 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()) - } - } - - if c.Global != "" { - global, err := r.Request.ExecString(c.Global) - if err != nil { - return "", 500, fmt.Errorf("global data error, please re-complie the page %s", err.Error()) - } - data["$global"] = global - } - - redirect, _ = data.Replace(redirect) - return "", 302, fmt.Errorf("%s", redirect) - } - - // Return the error - ex := exception.Err(err, 403) - return "", ex.Code, fmt.Errorf("%s", ex.Message) - } - } else { - // Process the guard - err := r.processGuard(c.Guard) - if err != nil { - ex := exception.Err(err, 403) - return "", ex.Code, fmt.Errorf("%s", ex.Message) - } - } + code, err := r.Guard(c) + if err != nil { + return "", code, err } - var err error data := core.Data{} if c.Data != "" { data, err = r.Request.ExecString(c.Data) @@ -210,18 +181,16 @@ func (r *Request) Render() (string, int, error) { } // Set the page request data - data["$payload"] = r.Request.Payload - data["$query"] = r.Request.Query - data["$param"] = r.Request.Params - data["$cookie"] = r.Request.Cookies() - data["$url"] = r.Request.URL - - printData := false - if r.Query != nil && r.Query.Has("__sui_print_data") { - printData = true + r.Request.WithData(data) + option := core.ParserOption{ + Theme: data["$theme"].(string), + Lang: data["$lang"].(string), + Debug: r.Request.DebugMode(), + Request: true, } - parser := core.NewTemplateParser(data, &core.ParserOption{PrintData: printData, Request: true}) + // Parse the template + parser := core.NewTemplateParser(data, &option) html, err := parser.Render(c.HTML) if err != nil { return "", 500, fmt.Errorf("render error, please re-complie the page %s", err.Error()) @@ -230,6 +199,58 @@ func (r *Request) Render() (string, int, error) { return html, 200, nil } +// Guard the page +func (r *Request) Guard(c *core.Cache) (int, error) { + + // Guard not set + if c.Guard == "" || r.context == nil { + return 200, nil + } + + // Built-in guard + if guard, has := Guards[c.Guard]; has { + err := guard(r) + if err != nil { + // Redirect the page (should refector before release) + if c.GuardRedirect != "" { + redirect := c.GuardRedirect + 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()) + } + } + + if c.Global != "" { + global, err := r.Request.ExecString(c.Global) + if err != nil { + return 500, fmt.Errorf("global data error, please re-complie the page %s", err.Error()) + } + data["$global"] = global + } + + redirect, _ = data.Replace(redirect) + return 302, fmt.Errorf("%s", redirect) + } + + // Return the error + ex := exception.Err(err, 403) + return ex.Code, fmt.Errorf("%s", ex.Message) + } + return 200, nil + } + + // Developer custom guard + err := r.processGuard(c.Guard) + if err != nil { + ex := exception.Err(err, 403) + return ex.Code, fmt.Errorf("%s", ex.Message) + } + + return 200, nil +} + func parserPath(c *gin.Context) (string, map[string]string, error) { params := map[string]string{} @@ -265,74 +286,6 @@ func parserPath(c *gin.Context) (string, map[string]string, error) { } } return filename, params, nil - - // // Match the sui - // matchers := core.RouteExactMatchers[parts[0]] - // if matchers == nil { - // for matcher, reMatchers := range core.RouteMatchers { - // matched := matcher.FindStringSubmatch(parts[0]) - // if len(matched) > 0 { - // matchers = reMatchers - // fileParts = append(fileParts, matched[0]) - // break - // } - // } - // } - - // // No matchers - // if matchers == nil { - // if len(parts) < 1 { - // return "", nil, fmt.Errorf("path parts error: %s", strings.Join(parts, "/")) - // } - - // fileParts = append(fileParts, parts...) - // return filepath.Join(fileParts...) + ".sui", params, nil - // } - - // // Match the page parts - // for i, part := range parts[1:] { - // if len(matchers) < i+1 { - // return "", nil, fmt.Errorf("matchers length error %d < %d", len(matchers), i+1) - // } - - // parent := "" - // if i > 0 { - // parent = parts[i] - // } - // matched := false - // for _, matcher := range matchers[i] { - - // // Filter the parent - // if matcher.Parent != "" && matcher.Parent != parent { - // continue - // } - - // if matcher.Exact == part { - // fileParts = append(fileParts, matcher.Exact) - // matched = true - // break - - // } else if matcher.Regex != nil { - // if matcher.Regex.MatchString(part) { - // file := matcher.Ref - // key := strings.TrimRight(strings.TrimLeft(file, "["), "]") - // params[key] = part - // fileParts = append(fileParts, file) - // matched = true - // break - // } - // } - // } - - // if !matched { - // return "", nil, fmt.Errorf("route does not match") - // } - // } - // return filepath.Join(fileParts...) + ".sui", params, nil -} - -func params(c *gin.Context) map[string]string { - return nil } func payload(c *gin.Context) (map[string]interface{}, interface{}, error) { diff --git a/sui/core/editor.go b/sui/core/editor.go index 94fde331..8459940a 100644 --- a/sui/core/editor.go +++ b/sui/core/editor.go @@ -111,7 +111,7 @@ func (res *ResponseEditorRender) Render(data map[string]interface{}) error { } var err error - parser := NewTemplateParser(data, &ParserOption{Editor: true, PrintData: true}) + parser := NewTemplateParser(data, &ParserOption{Editor: true, Debug: true}) res.HTML, err = parser.Render(res.HTML) if err != nil { diff --git a/sui/core/parser.go b/sui/core/parser.go index 8897a6b3..6f06d7a6 100644 --- a/sui/core/parser.go +++ b/sui/core/parser.go @@ -29,10 +29,12 @@ type Mapping struct { // ParserOption parser option type ParserOption struct { - Editor bool `json:"editor,omitempty"` - Preview bool `json:"preview,omitempty"` - PrintData bool `json:"print_data,omitempty"` - Request bool `json:"request,omitempty"` + Editor bool `json:"editor,omitempty"` + Preview bool `json:"preview,omitempty"` + Debug bool `json:"debug,omitempty"` + Request bool `json:"request,omitempty"` + Theme string `json:"theme,omitempty"` + Lang string `json:"lang,omitempty"` } var keepWords = map[string]bool{ @@ -66,7 +68,7 @@ func NewTemplateParser(data Data, option *ParserOption) *TemplateParser { func (parser *TemplateParser) Render(html string) (string, error) { if !strings.Contains(html, "%s`, html) + html = fmt.Sprintf(`%s`, html) } doc, err := NewDocumentString(html) @@ -85,7 +87,7 @@ func (parser *TemplateParser) Render(html string) (string, error) { // Print the data jsPrintData := "" - if parser.option != nil && parser.option.PrintData { + if parser.option != nil && parser.option.Debug { jsPrintData = "console.log(__sui_data);\n" } @@ -121,7 +123,7 @@ func (parser *TemplateParser) Render(html string) (string, error) { // Remove the sui-hide attribute doc.Find("[sui-hide]").Remove() - // Remove All comments + // Remove All comments and unsed nodes parser.tidy(doc.Selection) } diff --git a/sui/core/request.go b/sui/core/request.go index fe099699..208c8852 100644 --- a/sui/core/request.go +++ b/sui/core/request.go @@ -54,6 +54,53 @@ func (r *Request) Cookies() map[string]string { return cookies } +// DebugMode get the debug mode +func (r *Request) DebugMode() bool { + debug := false + if r.Query != nil && (r.Query.Has("__sui_print_data") || r.Query.Has("__debug")) { + debug = true + } + return debug +} + +// DisableCache get the disable cache +func (r *Request) DisableCache() bool { + disable := false + if (r.Query != nil && r.Query.Has("__debug") || r.Query.Has("__sui_disable_cache")) || (r.Headers != nil && r.Headers.Get("Cache-Control") == "no-cache") { + disable = true + } + return disable +} + +// WithData set the data +func (r *Request) WithData(data Data) *Request { + cookies := r.Cookies() + data["$payload"] = r.Payload + data["$query"] = r.Query + data["$param"] = r.Params + data["$cookie"] = cookies + data["$url"] = r.URL + data["$theme"] = GetTheme(cookies) + data["$lang"] = GetLang(cookies) + return r +} + +// GetLang get the lang +func GetLang(cookies map[string]string) string { + if lang, has := cookies["lang"]; has { + return lang + } + return "" +} + +// GetTheme get the theme +func GetTheme(cookies map[string]string) string { + if theme, has := cookies["color-theme"]; has { + return theme + } + return "" +} + // ExecString get the data func (r *Request) ExecString(data string) (Data, error) { var res Data @@ -314,13 +361,8 @@ func (url ReqeustURL) Map() Data { } // SetCache set the cache -func SetCache(file string, html string, data string, global string) *Cache { - Caches[file] = &Cache{ - Data: data, - HTML: html, - Global: global, - } - return Caches[file] +func SetCache(file string, cache *Cache) { + Caches[file] = cache } // GetCache get the cache