mirror of
https://github.com/caddyserver/caddy.git
synced 2024-11-25 09:40:13 +08:00
markdown: only update template when file changed (#1909)
Signed-off-by: Tw <tw19881113@gmail.com>
This commit is contained in:
parent
689591ef01
commit
5cca9cc18e
|
@ -68,8 +68,13 @@ type Config struct {
|
|||
// Template(s) to render with
|
||||
Template *template.Template
|
||||
|
||||
// a pair of template's name and its underlying file path
|
||||
TemplateFiles map[string]string
|
||||
// a pair of template's name and its underlying file information
|
||||
TemplateFiles map[string]*cachedFileInfo
|
||||
}
|
||||
|
||||
type cachedFileInfo struct {
|
||||
path string
|
||||
fi os.FileInfo
|
||||
}
|
||||
|
||||
// ServeHTTP implements the http.Handler interface.
|
||||
|
|
|
@ -62,7 +62,7 @@ func markdownParse(c *caddy.Controller) ([]*Config, error) {
|
|||
Extensions: make(map[string]struct{}),
|
||||
Template: GetDefaultTemplate(),
|
||||
IndexFiles: []string{},
|
||||
TemplateFiles: make(map[string]string),
|
||||
TemplateFiles: make(map[string]*cachedFileInfo),
|
||||
}
|
||||
|
||||
// Get the path scope
|
||||
|
@ -133,7 +133,9 @@ func loadParams(c *caddy.Controller, mdc *Config) error {
|
|||
return c.Errf("default template parse error: %v", err)
|
||||
}
|
||||
|
||||
mdc.TemplateFiles[""] = fpath
|
||||
mdc.TemplateFiles[""] = &cachedFileInfo{
|
||||
path: fpath,
|
||||
}
|
||||
return nil
|
||||
case 2:
|
||||
fpath := filepath.ToSlash(filepath.Clean(cfg.Root + string(filepath.Separator) + tArgs[1]))
|
||||
|
@ -142,7 +144,9 @@ func loadParams(c *caddy.Controller, mdc *Config) error {
|
|||
return c.Errf("template parse error: %v", err)
|
||||
}
|
||||
|
||||
mdc.TemplateFiles[tArgs[0]] = fpath
|
||||
mdc.TemplateFiles[tArgs[0]] = &cachedFileInfo{
|
||||
path: fpath,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case "templatedir":
|
||||
|
@ -164,7 +168,9 @@ func loadParams(c *caddy.Controller, mdc *Config) error {
|
|||
return c.Errf("glob %q failed: %v", pattern, err)
|
||||
}
|
||||
for _, path := range paths {
|
||||
mdc.TemplateFiles[filepath.Base(path)] = path
|
||||
mdc.TemplateFiles[filepath.Base(path)] = &cachedFileInfo{
|
||||
path: path,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
|
|
|
@ -77,7 +77,7 @@ func TestMarkdownParse(t *testing.T) {
|
|||
Styles: []string{"/resources/css/blog.css"},
|
||||
Scripts: []string{"/resources/js/blog.js"},
|
||||
Template: GetDefaultTemplate(),
|
||||
TemplateFiles: make(map[string]string),
|
||||
TemplateFiles: make(map[string]*cachedFileInfo),
|
||||
}}},
|
||||
{`markdown /blog {
|
||||
ext .md
|
||||
|
@ -88,8 +88,8 @@ func TestMarkdownParse(t *testing.T) {
|
|||
".md": {},
|
||||
},
|
||||
Template: setDefaultTemplate("./testdata/tpl_with_include.html"),
|
||||
TemplateFiles: map[string]string{
|
||||
"": "testdata/tpl_with_include.html",
|
||||
TemplateFiles: map[string]*cachedFileInfo{
|
||||
"": {path: "testdata/tpl_with_include.html"},
|
||||
},
|
||||
}}},
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ package markdown
|
|||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"sync"
|
||||
"text/template"
|
||||
|
||||
"github.com/mholt/caddy/caddyhttp/httpserver"
|
||||
|
@ -41,6 +43,8 @@ func (d Data) Include(filename string, args ...interface{}) (string, error) {
|
|||
return httpserver.ContextInclude(filename, d, d.Root)
|
||||
}
|
||||
|
||||
var templateUpdateMu sync.RWMutex
|
||||
|
||||
// execTemplate executes a template given a requestPath, template, and metadata
|
||||
func execTemplate(c *Config, mdata metadata.Metadata, meta map[string]string, files []FileInfo, ctx httpserver.Context) ([]byte, error) {
|
||||
mdData := Data{
|
||||
|
@ -51,18 +55,43 @@ func execTemplate(c *Config, mdata metadata.Metadata, meta map[string]string, fi
|
|||
Meta: meta,
|
||||
Files: files,
|
||||
}
|
||||
|
||||
templateName := mdata.Template
|
||||
// reload template on every request for now
|
||||
// TODO: cache templates by a general plugin
|
||||
if templateFile, ok := c.TemplateFiles[templateName]; ok {
|
||||
err := SetTemplate(c.Template, templateName, templateFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
updateTemplate := func() error {
|
||||
templateUpdateMu.Lock()
|
||||
defer templateUpdateMu.Unlock()
|
||||
|
||||
templateFile, ok := c.TemplateFiles[templateName]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
currentFileInfo, err := os.Lstat(templateFile.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !fileChanged(currentFileInfo, templateFile.fi) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// update template due to file changes
|
||||
err = SetTemplate(c.Template, templateName, templateFile.path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
templateFile.fi = currentFileInfo
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := updateTemplate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := new(bytes.Buffer)
|
||||
templateUpdateMu.RLock()
|
||||
defer templateUpdateMu.RUnlock()
|
||||
if err := c.Template.ExecuteTemplate(b, templateName, mdData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -70,6 +99,21 @@ func execTemplate(c *Config, mdata metadata.Metadata, meta map[string]string, fi
|
|||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
func fileChanged(new, old os.FileInfo) bool {
|
||||
// never checked before
|
||||
if old == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if new.Size() != old.Size() ||
|
||||
new.Mode() != old.Mode() ||
|
||||
new.ModTime() != old.ModTime() {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetTemplate reads in the template with the filename provided. If the file does not exist or is not parsable, it will return an error.
|
||||
func SetTemplate(t *template.Template, name, filename string) error {
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user