all repos — nergen.net-guestbook.git @ 4b116bb12defb8023960aa877082983ebc1b3a96

Unnamed repository; edit this file 'description' to name the repository.

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))
}