12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970 | // Command repo-render-preview serves a rendered site over HTTP, for
// previewing it locally. It is a plain file server: no caching rules, no
// login for the private site, and of the security headers only the
// sandbox on the repositories' own files. A real web server
// serves the site in production (see examples/httpd.conf).
//
// repo-render-preview [--listen ADDR] [--private PRIVATE_HTDOCS] HTDOCS
package main
import (
"flag"
"fmt"
"log"
"net/http"
"os"
"path"
"path/filepath"
"strings"
)
func main() {
listen := flag.String("listen", "127.0.0.1:8080", "address to listen on")
private := flag.String("private", "", "the config's private_htdocs, served at /private/")
flag.Usage = func() {
fmt.Fprintln(flag.CommandLine.Output(), "usage: repo-render-preview [--listen ADDR] [--private PRIVATE_HTDOCS] HTDOCS")
flag.PrintDefaults()
}
flag.Parse()
if flag.NArg() != 1 {
flag.Usage()
os.Exit(2)
}
log.Printf("serving %s at http://%s/", flag.Arg(0), *listen)
log.Fatal(http.ListenAndServe(*listen, handler(flag.Arg(0), *private)))
}
// handler serves htdocs, and private under /private/. A page's URL has no
// .html: /code/r/src is the file code/r/src.html, as is /code/r/src/.
func handler(htdocs, private string) http.Handler {
pub := http.FileServer(http.Dir(htdocs))
var priv http.Handler = http.NotFoundHandler()
if private != "" {
priv = http.FileServer(http.Dir(private))
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
p := path.Clean("/" + r.URL.Path)
root, files := htdocs, pub
if p == "/private" || strings.HasPrefix(p, "/private/") {
root, files = private, priv
}
page := strings.TrimPrefix(p, "/private")
if strings.HasPrefix(page, "/raw/") || strings.HasPrefix(page, "/svg/") {
// The repositories' own files, which may be HTML: kept from
// running as part of the site, as httpd.conf keeps them.
w.Header().Set("Content-Security-Policy", "sandbox")
}
if root != "" && (strings.HasPrefix(page, "/code/") || strings.HasPrefix(page, "/types/")) {
// Not http.ServeFile, which redirects .../index.html: the page
// of a file named index.
if f, err := os.Open(filepath.Join(root, filepath.FromSlash(p)+".html")); err == nil {
defer func() { _ = f.Close() }() // read only
if st, err := f.Stat(); err == nil && st.Mode().IsRegular() {
http.ServeContent(w, r, "page.html", st.ModTime(), f)
return
}
}
}
files.ServeHTTP(w, r)
})
}
|