This website is served on the google app engine, using code roughly equivalent to the following:
package main
import (
"log"
"os"
"context"
"strings"
"time"
"net/http"
"io/ioutil"
"github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html"
"cloud.google.com/go/storage"
"google.golang.org/api/iterator"
)
func get(client *storage.Client, s string) ([]byte, error) {
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
rc, err := client.Bucket("haha").Object(s).NewReader(ctx)
if err != nil {
return nil, err
}
defer rc.Close()
data, err := ioutil.ReadAll(rc)
if err != nil {
return nil, err
}
return data, nil
}
func list(client *storage.Client) ([]byte, error) {
var mdlist string
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, time.Second)
defer cancel()
query := &storage.Query{Prefix: "blag/"}
it := client.Bucket("haha").Objects(ctx, query)
it.Next()
for {
attrs, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
return nil, err
}
mdlist = mdlist + "- [" + strings.TrimSuffix(strings.TrimPrefix(attrs.Name, "blag/"), ".md") + "]" +
"(" + strings.TrimSuffix(attrs.Name, ".md") + ")\n"
}
return []byte(mdlist), nil
}
func main() {
p := os.Getenv("PORT")
if p == "" {
p = "8080"
}
ctx := context.Background()
client, err := storage.NewClient(ctx)
if err != nil {
log.Fatal(err)
}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
if (r.URL.Path == "/" || strings.HasPrefix(r.URL.Path, "/blag")) {
page := strings.TrimLeft(r.URL.Path, "/")
if (page == "") {
page = "about"
}
var data []byte
var err error
if (page == "blag") {
// fetch all blog files
data, err = list(client)
} else {
data, err = get(client, page+".md")
}
if err != nil {
w.WriteHeader(404)
w.Write([]byte("not found"))
return
}
renderopts := html.RendererOptions{
Title: page,
Flags: html.CommonFlags | html.CompletePage,
Head: []byte(`<style>blockquote,h1,h2,h3,h4,h5,h6,p{margin:0;padding:0}body{font-family:"Helvetica Neue",Helvetica,"Hiragino Sans GB",Arial,sans-serif;font-size:13px;line-height:18px;color:#737373;background-color:#fff;margin:10px 13px 10px 13px}table{margin:10px 0 15px 0;border-collapse:collapse}td,th{border:1px solid #ddd;padding:3px 10px}th{padding:5px 10px}a{color:#0069d6}a:hover{color:#0050a3;text-decoration:none}a img{border:none}p{margin-bottom:9px}h1,h2,h3,h4,h5,h6{color:#404040;line-height:36px}h1{margin-bottom:18px;font-size:30px}h2{font-size:24px}h3{font-size:18px}h4{font-size:16px}h5{font-size:14px}h6{font-size:13px}hr{margin:0 0 19px;border:0;border-bottom:1px solid #ccc}blockquote{padding:13px 13px 21px 15px;margin-bottom:18px;font-family:georgia,serif;font-style:italic}blockquote:before{content:"\201C";font-size:40px;margin-left:-10px;font-family:georgia,serif;color:#eee}blockquote p{font-size:14px;font-weight:300;line-height:18px;margin-bottom:0;font-style:italic}code,pre{font-family:Monaco,Andale Mono,Courier New,monospace}code{background-color:#fee9cc;color:rgba(0,0,0,.75);padding:1px 3px;font-size:12px;-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px}pre{display:block;padding:14px;margin:0 0 18px;line-height:16px;font-size:11px;border:1px solid #d9d9d9;white-space:pre-wrap;word-wrap:break-word}pre code{background-color:#fff;color:#737373;font-size:11px;padding:0}sup{font-size:.83em;vertical-align:super;line-height:0}*{-webkit-print-color-adjust:exact}@media screen and (min-width:914px){body{width:854px;margin:10px auto}}@media print{body,code,h1,h2,h3,h4,h5,h6,pre code{color:#000}pre,table{page-break-inside:avoid}}</style>`),
}
renderer := html.NewRenderer(renderopts)
html := markdown.ToHTML(data, nil, renderer)
w.Write(html)
return
}
if (strings.HasPrefix(r.URL.Path, "/img")) {
sp := strings.TrimLeft(r.URL.Path, "/")
data, err := get(client, sp)
if err != nil {
w.WriteHeader(404)
return
}
w.Write(data)
return
}
w.WriteHeader(404)
w.Write([]byte("not found"))
return
})
if err := http.ListenAndServe(":"+p, nil); err != nil {
log.Fatal(err)
}
}