2020-04-08 18:03:12 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2020-04-10 15:39:43 +00:00
|
|
|
"bytes"
|
2020-04-08 18:03:12 +00:00
|
|
|
"html/template"
|
2020-04-10 15:39:43 +00:00
|
|
|
"net/url"
|
2020-04-08 18:03:12 +00:00
|
|
|
"strings"
|
|
|
|
|
|
|
|
"code.hackerspace.pl/hscloud/devtools/hackdoc/config"
|
|
|
|
|
2020-04-10 19:20:53 +00:00
|
|
|
"github.com/gabriel-vasile/mimetype"
|
2020-04-08 18:03:12 +00:00
|
|
|
"github.com/golang/glog"
|
|
|
|
"gopkg.in/russross/blackfriday.v2"
|
|
|
|
)
|
|
|
|
|
2020-04-10 15:39:43 +00:00
|
|
|
// renderMarkdown renders markdown to HTML, replacing all relative (intra-hackdoc) links with version that have ref set.
|
|
|
|
func renderMarkdown(input []byte, ref string) []byte {
|
|
|
|
r := blackfriday.NewHTMLRenderer(blackfriday.HTMLRendererParameters{
|
|
|
|
Flags: blackfriday.CommonHTMLFlags,
|
|
|
|
})
|
|
|
|
|
|
|
|
parser := blackfriday.New(blackfriday.WithRenderer(r), blackfriday.WithExtensions(blackfriday.CommonExtensions))
|
|
|
|
ast := parser.Parse(input)
|
|
|
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
ast.Walk(func(node *blackfriday.Node, entering bool) blackfriday.WalkStatus {
|
|
|
|
if ref != "" && entering && node.Type == blackfriday.Link {
|
|
|
|
dest := string(node.Destination)
|
|
|
|
u, err := url.Parse(dest)
|
|
|
|
if err == nil && !u.IsAbs() {
|
|
|
|
q := u.Query()
|
|
|
|
q["ref"] = []string{ref}
|
|
|
|
u.RawQuery = q.Encode()
|
|
|
|
node.Destination = []byte(u.String())
|
2020-04-10 19:20:53 +00:00
|
|
|
glog.Infof("link fix %q -> %q", dest, u.String())
|
2020-04-10 15:39:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return r.RenderNode(&buf, node, entering)
|
|
|
|
})
|
|
|
|
return buf.Bytes()
|
|
|
|
}
|
|
|
|
|
2020-04-10 19:20:53 +00:00
|
|
|
type pathPart struct {
|
|
|
|
Label string
|
|
|
|
Path string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *request) handleFile(path string, cfg *config.Config) {
|
2020-04-10 15:39:43 +00:00
|
|
|
data, err := r.source.ReadFile(r.ctx, path)
|
2020-04-08 18:03:12 +00:00
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("ReadFile(%q): %w", err)
|
2020-04-10 15:39:43 +00:00
|
|
|
r.handle500()
|
2020-04-08 18:03:12 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-10 19:20:53 +00:00
|
|
|
// TODO(q3k): do MIME detection instead.
|
|
|
|
if strings.HasSuffix(path, ".md") {
|
|
|
|
rendered := renderMarkdown([]byte(data), r.ref)
|
|
|
|
|
|
|
|
r.logRequest("serving markdown at %s, cfg %+v", path, cfg)
|
|
|
|
|
|
|
|
// TODO(q3k): allow markdown files to override which template to load
|
|
|
|
tmpl, ok := cfg.Templates["default"]
|
|
|
|
if !ok {
|
|
|
|
glog.Errorf("No default template found for %s", path)
|
|
|
|
// TODO(q3k): implement fallback template
|
|
|
|
r.w.Write(rendered)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
pathInDepot := strings.TrimPrefix(path, "//")
|
|
|
|
pathParts := []pathPart{
|
|
|
|
{Label: "//", Path: "/"},
|
|
|
|
}
|
|
|
|
parts := strings.Split(pathInDepot, "/")
|
|
|
|
fullPath := ""
|
|
|
|
for i, p := range parts {
|
|
|
|
label := p
|
|
|
|
if i != len(parts)-1 {
|
|
|
|
label = label + "/"
|
|
|
|
}
|
|
|
|
fullPath += "/" + p
|
|
|
|
pathParts = append(pathParts, pathPart{Label: label, Path: fullPath})
|
|
|
|
}
|
2020-04-08 18:03:12 +00:00
|
|
|
|
2020-04-10 19:20:53 +00:00
|
|
|
vars := map[string]interface{}{
|
|
|
|
"Rendered": template.HTML(rendered),
|
|
|
|
"Title": path,
|
|
|
|
"Path": path,
|
|
|
|
"PathInDepot": pathInDepot,
|
|
|
|
"PathParts": pathParts,
|
|
|
|
"HackdocURL": flagHackdocURL,
|
|
|
|
"WebLinks": r.source.WebLinks(pathInDepot),
|
|
|
|
}
|
|
|
|
err = tmpl.Execute(r.w, vars)
|
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("Could not execute template for %s: %v", err)
|
|
|
|
}
|
2020-04-08 18:03:12 +00:00
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-04-10 19:20:53 +00:00
|
|
|
// Just serve the file.
|
|
|
|
mime := mimetype.Detect(data)
|
|
|
|
r.w.Header().Set("Content-Type", mime.String())
|
|
|
|
r.w.Write(data)
|
2020-04-08 18:03:12 +00:00
|
|
|
}
|