handler_html.go — HTML Handlers
Server-rendered handlers for the browser UI. Parses templates at startup, renders the list page, and handles form submissions with Post/Redirect/Get.
package main
import (
"html/template"
"log/slog"
"net/http"
"strconv"
"time"
)
type PageData struct {
Title string
Bookmarks []Bookmark
Count int
}
func parseTemplates() *template.Template {
funcMap := template.FuncMap{
"formatTime": func(t time.Time) string {
return t.Format("Jan 02, 2006")
},
}
return template.Must(
template.New("layout.html").Funcs(funcMap).ParseFiles(
"templates/layout.html",
"templates/list.html",
),
)
}
func handleBookmarksList(tmpl *template.Template, store *BookmarkStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
bookmarks, err := store.List(r.Context())
if err != nil {
slog.Error("list bookmarks", "err", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
data := PageData{
Title: "Bookmarks",
Bookmarks: bookmarks,
Count: len(bookmarks),
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")
if err := tmpl.Execute(w, data); err != nil {
slog.Error("template execute", "err", err)
}
}
}
func handleBookmarksCreate(store *BookmarkStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
title := r.FormValue("title")
url := r.FormValue("url")
if title == "" || url == "" {
http.Error(w, "title and url are required", http.StatusBadRequest)
return
}
if _, err := store.Create(r.Context(), url, title); err != nil {
slog.Error("create bookmark", "err", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/", http.StatusSeeOther)
}
}
func handleBookmarksDelete(store *BookmarkStore) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil {
http.Error(w, "invalid id", http.StatusBadRequest)
return
}
if err := store.Delete(r.Context(), id); err != nil {
slog.Error("delete bookmark", "err", err)
http.Error(w, "internal error", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/", http.StatusSeeOther)
}
}