diff --git a/config/setup/markdown.go b/config/setup/markdown.go index 8e296ef4f..bfe96300b 100644 --- a/config/setup/markdown.go +++ b/config/setup/markdown.go @@ -2,11 +2,12 @@ package setup import ( "net/http" + "path" + "path/filepath" "github.com/mholt/caddy/middleware" "github.com/mholt/caddy/middleware/markdown" "github.com/russross/blackfriday" - "path/filepath" ) // Markdown configures a new Markdown middleware instance. @@ -37,6 +38,7 @@ func markdownParse(c *Controller) ([]markdown.Config, error) { Renderer: blackfriday.HtmlRenderer(0, "", ""), Templates: make(map[string]string), StaticFiles: make(map[string]string), + StaticDir: path.Join(c.Root, markdown.StaticDir), } // Get the path scope diff --git a/middleware/markdown/markdown.go b/middleware/markdown/markdown.go index f4eab69e5..d7bdcc399 100644 --- a/middleware/markdown/markdown.go +++ b/middleware/markdown/markdown.go @@ -31,6 +31,16 @@ type Markdown struct { IndexFiles []string } +// Helper function to check if a file is an index file +func (m Markdown) IsIndexFile(file string) bool { + for _, f := range m.IndexFiles { + if f == file { + return true + } + } + return false +} + // Config stores markdown middleware configurations. type Config struct { // Markdown renderer @@ -53,6 +63,9 @@ type Config struct { // Static files StaticFiles map[string]string + + // Static files directory + StaticDir string } // ServeHTTP implements the http.Handler interface. @@ -77,18 +90,37 @@ func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error return http.StatusNotFound, nil } + fs, err := f.Stat() + if err != nil { + return http.StatusNotFound, nil + } + + // if static site is generated, attempt to use it + if filepath, ok := m.StaticFiles[fpath]; ok { + if fs1, err := os.Stat(filepath); err == nil { + // if markdown has not been modified + // since static page generation, + // serve the static page + if fs.ModTime().UnixNano() < fs1.ModTime().UnixNano() { + if html, err := ioutil.ReadFile(filepath); err == nil { + w.Write(html) + return http.StatusOK, nil + } + } + } + } + body, err := ioutil.ReadAll(f) if err != nil { return http.StatusInternalServerError, err } - html, err := Process(md, fpath, body) + html, err := md.process(m, fpath, body) if err != nil { return http.StatusInternalServerError, err } - w.Write([]byte(html)) - + w.Write(html) return http.StatusOK, nil } } diff --git a/middleware/markdown/process.go b/middleware/markdown/process.go index 207b87337..a9fa702a2 100644 --- a/middleware/markdown/process.go +++ b/middleware/markdown/process.go @@ -9,12 +9,19 @@ import ( "text/template" "github.com/russross/blackfriday" + "log" + "os" "strings" ) -// Process the contents of a page. +const ( + DefaultTemplate = "defaultTemplate" + StaticDir = ".caddy_static" +) + +// process the contents of a page. // It parses the metadata if any and uses the template if found -func Process(c Config, fpath string, b []byte) ([]byte, error) { +func (md Markdown) process(c Config, fpath string, b []byte) ([]byte, error) { metadata, markdown, err := extractMetadata(b) if err != nil { return nil, err @@ -42,7 +49,7 @@ func Process(c Config, fpath string, b []byte) ([]byte, error) { // set it as body for template metadata.Variables["body"] = string(markdown) - return processTemplate(c, fpath, tmpl, metadata) + return md.processTemplate(c, fpath, tmpl, metadata) } // extractMetadata extracts metadata content from a page. @@ -104,7 +111,7 @@ func findParser(line []byte) MetadataParser { return nil } -func processTemplate(c Config, fpath string, tmpl []byte, metadata Metadata) ([]byte, error) { +func (md Markdown) processTemplate(c Config, fpath string, tmpl []byte, metadata Metadata) ([]byte, error) { // if template is specified // replace parse the template if tmpl != nil { @@ -119,6 +126,14 @@ func processTemplate(c Config, fpath string, tmpl []byte, metadata Metadata) ([] if err = t.Execute(b, metadata.Variables); err != nil { return nil, err } + + // generate static page + if err = md.generatePage(c, fpath, b.Bytes()); err != nil { + // if static page generation fails, + // nothing fatal, only log the error. + log.Println(err) + } + return b.Bytes(), nil } @@ -154,6 +169,42 @@ func defaultTemplate(c Config, metadata Metadata, fpath string) []byte { return html } +func (md Markdown) generatePage(c Config, fpath string, content []byte) error { + // should not happen + // must be set on init + if c.StaticDir == "" { + return fmt.Errorf("Static directory not set") + } + + // if static directory is not existing, create it + if _, err := os.Stat(c.StaticDir); err != nil { + err := os.MkdirAll(c.StaticDir, os.FileMode(0755)) + if err != nil { + return err + } + } + + filePath := filepath.Join(c.StaticDir, fpath) + + // If it is index file, use the directory instead + if md.IsIndexFile(filepath.Base(fpath)) { + filePath, _ = filepath.Split(filePath) + } + if err := os.MkdirAll(filePath, os.FileMode(0755)); err != nil { + return err + } + + // generate index.html file in the directory + filePath = filepath.Join(filePath, "index.html") + err := ioutil.WriteFile(filePath, content, os.FileMode(0755)) + if err != nil { + return err + } + + c.StaticFiles[fpath] = filePath + return nil +} + const ( htmlTemplate = ` @@ -169,6 +220,4 @@ const ( ` cssTemplate = `` jsTemplate = `` - - DefaultTemplate = "defaultTemplate" )