Merge pull request #626 from trheyi/main

refactor: Update TemplateParser to support debug mode and DisableCache
This commit is contained in:
Max 2024-06-26 17:10:14 +08:00 committed by GitHub
commit 1fc9f9aab5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 138 additions and 141 deletions

View file

@ -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) {

View file

@ -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 {

View file

@ -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, "<html") {
html = fmt.Sprintf(`<!DOCTYPE html><html lang="en">%s</html>`, html)
html = fmt.Sprintf(`<!DOCTYPE html><html lang="en-us">%s</html>`, 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)
}

View file

@ -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