caddy/middleware/markdown/markdown.go

156 lines
3.4 KiB
Go
Raw Normal View History

2015-03-17 01:45:51 +08:00
// Package markdown is middleware to render markdown files as HTML
// on-the-fly.
package markdown
import (
"io/ioutil"
"log"
2015-03-17 01:45:51 +08:00
"net/http"
"os"
2015-03-17 01:45:51 +08:00
"strings"
"sync"
2015-03-17 01:45:51 +08:00
"github.com/mholt/caddy/middleware"
"github.com/russross/blackfriday"
)
// Markdown implements a layer of middleware that serves
// markdown as HTML.
2015-03-17 01:45:51 +08:00
type Markdown struct {
// Server root
Root string
// Jail the requests to site root with a mock file system
FileSys http.FileSystem
2015-03-17 01:45:51 +08:00
// Next HTTP handler in the chain
Next middleware.Handler
2015-03-17 01:45:51 +08:00
// The list of markdown configurations
2015-05-05 01:49:49 +08:00
Configs []Config
// The list of index files to try
IndexFiles []string
}
2015-05-25 10:52:34 +08:00
// IsIndexFile checks to see if a file is an index file
func (md Markdown) IsIndexFile(file string) bool {
for _, f := range md.IndexFiles {
if f == file {
return true
}
}
return false
}
2015-05-05 01:49:49 +08:00
// Config stores markdown middleware configurations.
type Config struct {
2015-03-17 01:45:51 +08:00
// Markdown renderer
Renderer blackfriday.Renderer
// Base path to match
PathScope string
// List of extensions to consider as markdown files
Extensions []string
// List of style sheets to load for each markdown file
Styles []string
// List of JavaScript files to load for each markdown file
Scripts []string
// Map of registered templates
Templates map[string]string
// Map of request URL to static files generated
StaticFiles map[string]string
// Links to all markdown pages ordered by date.
Links []PageLink
// Directory to store static files
StaticDir string
sync.RWMutex
2015-03-17 01:45:51 +08:00
}
// ServeHTTP implements the http.Handler interface.
func (md Markdown) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
for i := range md.Configs {
m := &md.Configs[i]
if !middleware.Path(r.URL.Path).Matches(m.PathScope) {
continue
}
fpath := r.URL.Path
if idx, ok := middleware.IndexFile(md.FileSys, fpath, md.IndexFiles); ok {
fpath = idx
}
2015-03-17 01:45:51 +08:00
for _, ext := range m.Extensions {
if strings.HasSuffix(fpath, ext) {
f, err := md.FileSys.Open(fpath)
2015-03-17 01:45:51 +08:00
if err != nil {
if os.IsPermission(err) {
return http.StatusForbidden, err
}
return http.StatusNotFound, nil
2015-03-17 01:45:51 +08:00
}
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
}
2015-05-25 10:52:34 +08:00
if os.IsPermission(err) {
return http.StatusForbidden, err
}
return http.StatusNotFound, nil
}
}
}
if m.StaticDir != "" {
// Markdown modified or new. Update links.
if err := GenerateLinks(md, m); err != nil {
log.Println(err)
}
}
body, err := ioutil.ReadAll(f)
if err != nil {
return http.StatusInternalServerError, err
}
ctx := middleware.Context{
Root: md.FileSys,
Req: r,
URL: r.URL,
}
html, err := md.Process(*m, fpath, body, ctx)
if err != nil {
return http.StatusInternalServerError, err
}
w.Write(html)
return http.StatusOK, nil
2015-03-17 01:45:51 +08:00
}
}
}
// Didn't qualify to serve as markdown; pass-thru
return md.Next.ServeHTTP(w, r)
2015-03-17 01:45:51 +08:00
}