WIP Bindata stuff

This commit is contained in:
Noah 2020-10-28 18:56:09 -07:00
parent 445fffdf2b
commit 6a36c0ed76
5 changed files with 141 additions and 37 deletions

View File

@ -18,6 +18,14 @@ build:
gofmt -w . gofmt -w .
go build $(LDFLAGS) -i -o bin/blog cmd/blog/main.go go build $(LDFLAGS) -i -o bin/blog cmd/blog/main.go
# `make bindata` to make the bindata module.
# `make bindata-dev` for debug mode module for editing files locally.
.PHONY: bindata bindata-dev
bindata:
go-bindata -pkg root -prefix root/ -o src/root/bundle.go root/...
bindata-dev:
go-bindata -debug -pkg root -prefix root/ -o src/root/bundle.go root/...
# `make run` to run it in debug mode. # `make run` to run it in debug mode.
.PHONY: run .PHONY: run
run: run:

View File

@ -3,7 +3,9 @@ package blog
import ( import (
"html/template" "html/template"
"io/ioutil" "io/ioutil"
"mime"
"net/http" "net/http"
"path/filepath"
"strings" "strings"
"github.com/kirsle/blog/src/controllers/posts" "github.com/kirsle/blog/src/controllers/posts"
@ -11,6 +13,7 @@ import (
"github.com/kirsle/blog/src/markdown" "github.com/kirsle/blog/src/markdown"
"github.com/kirsle/blog/src/render" "github.com/kirsle/blog/src/render"
"github.com/kirsle/blog/src/responses" "github.com/kirsle/blog/src/responses"
"github.com/kirsle/blog/src/root"
) )
// PageHandler is the catch-all route handler, for serving static web pages. // PageHandler is the catch-all route handler, for serving static web pages.
@ -31,7 +34,7 @@ func (b *Blog) PageHandler(w http.ResponseWriter, r *http.Request) {
} }
// Search for a file that matches their URL. // Search for a file that matches their URL.
filepath, err := render.ResolvePath(path) fp, err := render.ResolvePath(path)
if err != nil { if err != nil {
// See if it resolves as a blog entry. // See if it resolves as a blog entry.
err = postctl.ViewPost(w, r, strings.TrimLeft(path, "/")) err = postctl.ViewPost(w, r, strings.TrimLeft(path, "/"))
@ -43,19 +46,32 @@ func (b *Blog) PageHandler(w http.ResponseWriter, r *http.Request) {
} }
// Is it a template file? // Is it a template file?
if strings.HasSuffix(filepath.URI, ".gohtml") { if strings.HasSuffix(fp.URI, ".gohtml") {
render.Template(w, r, filepath.URI, nil) render.Template(w, r, fp.URI, nil)
return return
} }
// Is it a Markdown file? // Is it a Markdown file?
if strings.HasSuffix(filepath.URI, ".md") || strings.HasSuffix(filepath.URI, ".markdown") { if strings.HasSuffix(fp.URI, ".md") || strings.HasSuffix(fp.URI, ".markdown") {
source, err := ioutil.ReadFile(filepath.Absolute) var source []byte
if len(fp.BindataKey) > 0 {
data, err := root.Asset(fp.BindataKey)
if err != nil {
responses.Error(w, r, "Couldn't read bindata key: "+fp.BindataKey)
return
}
source = data
} else {
data, err := ioutil.ReadFile(fp.Absolute)
if err != nil { if err != nil {
responses.Error(w, r, "Couldn't read Markdown source!") responses.Error(w, r, "Couldn't read Markdown source!")
return return
} }
source = data
}
// Render it to HTML and find out its title. // Render it to HTML and find out its title.
body := string(source) body := string(source)
html := markdown.RenderTrustedMarkdown(body) html := markdown.RenderTrustedMarkdown(body)
@ -64,10 +80,22 @@ func (b *Blog) PageHandler(w http.ResponseWriter, r *http.Request) {
render.Template(w, r, ".markdown", map[string]interface{}{ render.Template(w, r, ".markdown", map[string]interface{}{
"Title": title, "Title": title,
"HTML": template.HTML(html), "HTML": template.HTML(html),
"MarkdownPath": filepath.URI, "MarkdownPath": fp.URI,
}) })
return return
} }
http.ServeFile(w, r, filepath.Absolute) // It's a regular static file we can serve directly.
{
// Check if we have bindata for it.
if fp.BindataKey != "" {
data, _ := root.Asset(fp.BindataKey)
w.Header().Set("Content-Type", mime.TypeByExtension(filepath.Ext(fp.URI)))
w.Write(data)
return
}
// Try the filesystem.
http.ServeFile(w, r, fp.Absolute)
}
} }

View File

@ -8,11 +8,12 @@ import (
"net/url" "net/url"
"strings" "strings"
"github.com/kirsle/blog/models/comments"
"github.com/kirsle/blog/models/settings"
"github.com/kirsle/blog/src/log" "github.com/kirsle/blog/src/log"
"github.com/kirsle/blog/src/markdown" "github.com/kirsle/blog/src/markdown"
"github.com/kirsle/blog/src/render" "github.com/kirsle/blog/src/render"
"github.com/kirsle/blog/models/comments" "github.com/kirsle/blog/src/root"
"github.com/kirsle/blog/models/settings"
"github.com/microcosm-cc/bluemonday" "github.com/microcosm-cc/bluemonday"
gomail "gopkg.in/gomail.v2" gomail "gopkg.in/gomail.v2"
) )
@ -51,10 +52,21 @@ func SendEmail(email Email) {
// Render the template to HTML. // Render the template to HTML.
var html bytes.Buffer var html bytes.Buffer
t := template.New(tmpl.Basename) t := template.New(tmpl.Basename)
t, err = template.ParseFiles(tmpl.Absolute)
// Load it from bindata or filesystem.
if tmpl.BindataKey != "" {
log.Debug("Parse %s from bindata", tmpl.BindataKey)
asset, _ := root.Asset(tmpl.BindataKey)
t, err = t.Parse(string(asset))
} else {
log.Debug("Parse %s from file", tmpl.Absolute)
t, err = t.ParseFiles(tmpl.Absolute)
}
if err != nil { if err != nil {
log.Error("SendEmail: template parsing error: %s", err.Error()) log.Error("SendEmail: template parsing error: %s", err.Error())
} }
err = t.ExecuteTemplate(&html, tmpl.Basename, email) err = t.ExecuteTemplate(&html, tmpl.Basename, email)
if err != nil { if err != nil {
log.Error("SendEmail: template execution error: %s", err.Error()) log.Error("SendEmail: template execution error: %s", err.Error())

View File

@ -8,6 +8,7 @@ import (
"strings" "strings"
"github.com/kirsle/blog/src/log" "github.com/kirsle/blog/src/log"
"github.com/kirsle/blog/src/root"
) )
// Blog configuration bindings. // Blog configuration bindings.
@ -37,6 +38,10 @@ type Filepath struct {
Basename string Basename string
Relative string // Relative path including document root (i.e. "root/about.html") Relative string // Relative path including document root (i.e. "root/about.html")
Absolute string // Absolute path on disk (i.e. "/opt/blog/root/about.html") Absolute string // Absolute path on disk (i.e. "/opt/blog/root/about.html")
// If file was resolved to embedded bindata, this is the bindata key name.
// Zero value means it resolved to a file on filesystem.
BindataKey string
} }
func (f Filepath) String() string { func (f Filepath) String() string {
@ -55,39 +60,78 @@ func ResolvePath(path string) (Filepath, error) {
// If you need to debug this function, edit this block. // If you need to debug this function, edit this block.
debug := func(tmpl string, args ...interface{}) { debug := func(tmpl string, args ...interface{}) {
if false { if true { // edit this to enable
log.Debug(tmpl, args...) log.Debug(tmpl, args...)
} }
} }
debug("Resolving filepath for URI: %s", path) debug("ResolvePath(%s) called", path)
for _, root := range []string{*UserRoot, *DocumentRoot} {
if len(root) == 0 { if len(*UserRoot) > 0 {
continue debug("1. Resolving filepath for URI in user root: %s", path)
}
// Resolve the file path. // Resolve the file path.
relPath := filepath.Join(root, path) relPath := filepath.Join(*UserRoot, path)
absPath, err := filepath.Abs(relPath) absPath, err := filepath.Abs(relPath)
basename := filepath.Base(relPath) basename := filepath.Base(relPath)
if err != nil { if err != nil {
log.Error("%v", err) log.Error("%v", err)
} }
debug("Expected filepath: %s", absPath) debug(" Expected filepath: %s", absPath)
// Found an exact hit? // Found an exact hit?
if stat, err := os.Stat(absPath); !os.IsNotExist(err) && !stat.IsDir() { if stat, err := os.Stat(absPath); !os.IsNotExist(err) && !stat.IsDir() {
debug("Exact filepath found: %s", absPath) debug(" + Exact filepath found: %s", absPath)
return Filepath{path, basename, relPath, absPath}, nil return Filepath{
URI: path,
Basename: basename,
Relative: relPath,
Absolute: absPath,
}, nil
} }
// Try some supported suffixes. // Try some supported suffixes.
for _, suffix := range hiddenSuffixes { for _, suffix := range hiddenSuffixes {
test := absPath + suffix test := absPath + suffix
if stat, err := os.Stat(test); !os.IsNotExist(err) && !stat.IsDir() { if stat, err := os.Stat(test); !os.IsNotExist(err) && !stat.IsDir() {
debug("Filepath found via suffix %s: %s", suffix, test) debug(" + Filepath found via suffix %s: %s", suffix, test)
return Filepath{path + suffix, basename + suffix, relPath + suffix, test}, nil return Filepath{
URI: path + suffix,
Basename: basename + suffix,
Relative: relPath + suffix,
Absolute: test,
}, nil
}
}
}
debug("2. Not found in filesystem, checking bindata for: %s", path)
{
// Exact hit?
if _, err := root.Asset(path); err == nil {
debug(" Found in bindata as: %s", path)
return Filepath{
URI: path,
Basename: filepath.Base(path),
Relative: path,
Absolute: path,
BindataKey: path,
}, nil
}
// Try some supported suffixes.
for _, suffix := range hiddenSuffixes {
test := path + suffix
if _, err := root.Asset(test); err == nil {
debug(" Filepath found via suffix %s: %s", suffix, test)
return Filepath{
URI: test,
Basename: filepath.Base(test),
Relative: test,
Absolute: test,
BindataKey: test,
}, nil
} }
} }
} }

View File

@ -8,13 +8,14 @@ import (
"time" "time"
gorilla "github.com/gorilla/sessions" gorilla "github.com/gorilla/sessions"
"github.com/kirsle/blog/models/settings"
"github.com/kirsle/blog/models/users"
"github.com/kirsle/blog/src/log" "github.com/kirsle/blog/src/log"
"github.com/kirsle/blog/src/middleware" "github.com/kirsle/blog/src/middleware"
"github.com/kirsle/blog/src/middleware/auth" "github.com/kirsle/blog/src/middleware/auth"
"github.com/kirsle/blog/src/root"
"github.com/kirsle/blog/src/sessions" "github.com/kirsle/blog/src/sessions"
"github.com/kirsle/blog/src/types" "github.com/kirsle/blog/src/types"
"github.com/kirsle/blog/models/settings"
"github.com/kirsle/blog/models/users"
) )
// Vars is an interface to implement by the templates to pass their own custom // Vars is an interface to implement by the templates to pass their own custom
@ -113,19 +114,19 @@ func Template(w io.Writer, r *http.Request, path string, data interface{}) error
// Get the layout template. // Get the layout template.
if !isPartial { if !isPartial {
templateName = "layout" templateName = "layout"
layout, err = ResolvePath(".layout") layout, err = ResolvePath(".layout.gohtml")
if err != nil { if err != nil {
log.Error("RenderTemplate(%s): layout template not found", path) log.Error("RenderTemplate(%s): layout template not found", path)
return err return err
} }
} else { } else {
templateName = filepath.Basename templateName = filepath.Absolute
} }
// The comment entry partial. // The comment entry partial.
commentEntry, err := ResolvePath("comments/entry.partial") commentEntry, err := ResolvePath("comments/entry.partial.gohtml")
if err != nil { if err != nil {
log.Error("RenderTemplate(%s): comments/entry.partial not found") log.Error("RenderTemplate: comments/entry.partial not found")
return err return err
} }
@ -135,17 +136,28 @@ func Template(w io.Writer, r *http.Request, path string, data interface{}) error
// and allows the filepath template to set the page title. // and allows the filepath template to set the page title.
var templates []string var templates []string
if !isPartial { if !isPartial {
templates = append(templates, layout.Absolute) templates = append(templates, layout.Absolute, commentEntry.Absolute, filepath.Absolute)
} }
t, err = t.ParseFiles(append(templates, commentEntry.Absolute, filepath.Absolute)...)
for _, filename := range templates {
if asset, err2 := root.Asset(filename); err2 == nil {
log.Debug("Parse %s from bindata", filename)
t, err = t.Parse(string(asset))
} else {
log.Debug("Parse %s from file", filename)
t, err = t.ParseFiles(filename)
}
if err != nil { if err != nil {
log.Error(err.Error()) log.Error(err.Error())
return err return err
} }
}
err = t.ExecuteTemplate(w, templateName, v) err = t.ExecuteTemplate(w, templateName, v)
if err != nil { if err != nil {
log.Error("Template parsing error: %s", err) log.Error("Template parsing error(tmpl name: %s; ): %s", err)
return err return err
} }