-
Notifications
You must be signed in to change notification settings - Fork 0
/
utilities.go
154 lines (121 loc) · 4.19 KB
/
utilities.go
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package main
import (
"database/sql"
"fmt"
"html/template"
"log"
"net/http"
"os"
"github.com/gomarkdown/markdown"
"github.com/gomarkdown/markdown/html"
"github.com/gomarkdown/markdown/parser"
"github.com/joho/godotenv"
_ "github.com/lib/pq"
"github.com/microcosm-cc/bluemonday"
"github.com/go-chi/chi/middleware"
"github.com/go-chi/chi/v5"
)
type App struct {
templates *template.Template
db *sql.DB
log *log.Logger
}
type contextKey string
const userIDKey contextKey = "userID"
// initializeApp initializes the app database and routes and starts the HTTP server on the given port
func startApp() {
mux := chi.NewMux()
//Recommended default middleware stack
mux.Use(middleware.RequestID)
mux.Use(middleware.RealIP)
mux.Use(middleware.Logger)
mux.Use(middleware.Recoverer)
//Custom middleware
mux.Use(checkAuthentication)
//Select port from environment variable or default to :3000
port := os.Getenv("PORT")
if port == "" {
port = "3000"
}
//Load .env if one exists
godotenv.Load()
//Find URI for Postgres connection
postgresUri := os.Getenv("GONOTE_POSTGRES_URI")
if postgresUri == "" {
log.Fatalln("Could not find Postgres connection URI in environment variable")
}
//Open DB using postgres driver
db, err := sql.Open("postgres", postgresUri)
if err != nil {
log.Fatalln("Could not connect to Postgres database")
}
defer db.Close()
//Parse all templates
templates := template.Must(template.ParseGlob("templates/*/*.html"))
//Create new app struct to pass db connection
app := &App{
templates: templates,
db: db,
log: log.Default(),
}
//Mount routers and utility handlers
mux.Mount("/", app.frontendRouter())
mux.Mount("/api", app.apiRouter())
mux.Get("/freshtoast", app.handleEmptyToast)
//Add static file server, pattern from Alex Edwards
fs := http.FileServer(http.Dir("./static"))
mux.Handle("/static/*", http.StripPrefix("/static/", fs))
fmt.Println("Listening on port " + port)
http.ListenAndServe(":"+port, mux)
}
// sendToast takes a ResponseWriter and message string and sends back a toast
// notification to the client front end using the toast template
func sendToast(w http.ResponseWriter, message string) {
toastTemplate, err := template.ParseFiles("templates/components/toast.html")
if err != nil {
fmt.Println("Error parsing toast.html")
}
toastTemplate.Execute(w, message)
}
// sendErrorToast takes a ResponseWriter and message string and sends back am error toast
// notification to the client front end using the toast template
func (app *App) sendErrorToast(w http.ResponseWriter, errorMessage string) {
app.templates.ExecuteTemplate(w, "error_toast", errorMessage)
}
// handleEmptyToast is called after a toast message has timed out on the front end
// it responds with an empty toast message to serve as a placeholder for the next
// toast message.
func (app *App) handleEmptyToast(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("<div id=\"toast\"></div>"))
}
// frontendRouter returns a router with the appropriate pages for the front end registered as paths
func (app *App) frontendRouter() *chi.Mux {
router := chi.NewRouter()
router.Get("/", app.handleIndex)
router.Get("/login", app.handleLoginPage)
router.Get("/register", app.handleRegisterPage)
router.Get("/notes", app.handleNotesPage)
router.Get("/notes/{id}", app.handleIndividualNotePage)
router.Get("/sharelink/{id}", app.handleSharelinkPage)
return router
}
// apiRouter returns a router with the appropriately mounted routes for the API
func (app *App) apiRouter() *chi.Mux {
router := chi.NewRouter()
router.Mount("/notes", app.noteRouter())
router.Mount("/auth", app.authRouter())
router.Mount("/sharelink", app.sharelinkRouter())
return router
}
func mdToHTML(md string) string {
extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock
p := parser.NewWithExtensions(extensions)
doc := p.Parse([]byte(md))
htmlFlags := html.CommonFlags | html.HrefTargetBlank
opts := html.RendererOptions{Flags: htmlFlags}
renderer := html.NewRenderer(opts)
unsafeHTMLDoc := markdown.Render(doc, renderer)
policy := bluemonday.UGCPolicy()
safeHTML := policy.SanitizeBytes(unsafeHTMLDoc)
return string(safeHTML)
}