main.go (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
package main import ( "context" "crypto" "database/sql" "fmt" "html/template" "log" "net/http" "reflect" "strings" "time" "git.nergen.net/nergen.net-guestbook/sqlc_gb" "github.com/gomarkdown/markdown" "github.com/gomarkdown/markdown/html" "github.com/gomarkdown/markdown/parser" _ "github.com/mattn/go-sqlite3" "github.com/microcosm-cc/bluemonday" ) var templates map[string]*template.Template var ctx context.Context var queries *sqlc_gb.Queries var p *bluemonday.Policy func init() { if templates == nil { templates = make(map[string]*template.Template) } // Do this once for each unique policy, and use the policy for the life of the program // Policy creation/editing is not safe to use in multiple goroutines // p = bluemonday.UGCPolicy() // The policy can then be used to sanitize lots of input and it is safe to use the policy // in multiple goroutines p = bluemonday.NewPolicy() p.AllowElements("b", "code", "strong", "p", "blockquote", "pre", "br", "dt", "dl", "dd", "h1", "h2", "h3", "h4", "em", "sup", "div", "ol", "ul", "li") templates["index.html"] = template.Must(template.New("boilerplate.gohtml").Funcs(template.FuncMap{ "safeHTML": func(b string) template.HTML { return template.HTML(b) }, }).ParseFiles("templates/guestbook-body.gohtml", "templates/boilerplate.gohtml")) } func main() { h1 := func(w http.ResponseWriter, r *http.Request) { posts, err := queries.GetPosts(ctx) if err != nil { log.Fatal(err) } templates["index.html"].Execute(w, posts) } h2 := func(w http.ResponseWriter, r *http.Request) { msg_markdown := r.PostFormValue("msg") msg_html := string(mdToHTML([]byte(msg_markdown))) msg_sanitized_html := p.Sanitize(msg_html) if msg_html != msg_sanitized_html { fmt.Println("message was sanitized") } if strings.TrimSpace(msg_sanitized_html) == "<p></p>" { fmt.Println("message turned out empty") return } post, err := queries.AddPost(ctx, sqlc_gb.AddPostParams{ Msg: msg_sanitized_html, // Msg: msg_html, Stamp: time.Now(), }) if err != nil { log.Fatal(err) } templates["index.html"].ExecuteTemplate(w, "rendered-post", post) } db, err := sql.Open("sqlite3", "gb.db") if err != nil { log.Fatal(err) } defer db.Close() ctx = context.Background() queries = sqlc_gb.New(db) if err = queries.TableCreate(ctx); err != nil { log.Fatal(err) } http.HandleFunc("/", h1) http.HandleFunc("/add-post/", h2) log.Fatal(http.ListenAndServe(":8000", nil)) } func mdToHTML(md []byte) []byte { // create markdown parser with extensions extensions := parser.CommonExtensions | parser.NoEmptyLineBeforeBlock | parser.HardLineBreak | parser.Footnotes p := parser.NewWithExtensions(extensions) doc := p.Parse(md) // create HTML renderer with extensions htmlFlags := html.CommonFlags | html.HrefTargetBlank opts := html.RendererOptions{Flags: htmlFlags} renderer := html.NewRenderer(opts) return markdown.Render(doc, renderer) } func Hash(objs ...interface{}) string { digester := crypto.MD5.New() for _, ob := range objs { fmt.Fprint(digester, reflect.TypeOf(ob)) fmt.Fprint(digester, ob) } return fmt.Sprintf("%x", digester.Sum(nil)) } |