sbsqlitessgcms/handler.go
2022-11-19 00:11:58 -07:00

188 lines
4.4 KiB
Go

package main
import (
"fmt"
"net/http"
"strconv"
h "gitlab.codemonkeysoftware.net/b/hatmill"
ha "gitlab.codemonkeysoftware.net/b/hatmill/attribute"
he "gitlab.codemonkeysoftware.net/b/hatmill/element"
"github.com/russross/blackfriday/v2"
)
const (
pathRoot = "/"
pathCreatePost = "/create-post"
pathDoCreatePost = "/create-post/do"
pathPreviewPost = "/preview-post"
)
type handler struct {
mux *http.ServeMux
store *Store
baseURL string
}
func NewHandler(store *Store, baseURL string) http.Handler {
h := &handler{
store: store,
mux: http.NewServeMux(),
baseURL: baseURL,
}
h.mux.HandleFunc(pathRoot, h.getPosts)
h.mux.HandleFunc(pathCreatePost, h.createPost)
h.mux.HandleFunc(pathDoCreatePost, h.doCreatePost)
h.mux.HandleFunc(pathPreviewPost, h.previewPost)
return h
}
func (hnd *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
hnd.mux.ServeHTTP(w, r)
}
func (hnd *handler) getPosts(w http.ResponseWriter, r *http.Request) {
result, err := hnd.store.GetPosts(r.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
var rows h.Terms
for _, post := range result.Posts {
previewURL := fmt.Sprintf("%s?%s=%d", pathPreviewPost, fieldNamePostID, post.Id)
rows = append(rows, he.Tr()(
he.Td()(h.Text(strconv.FormatInt(post.Id, 10))),
he.Td()(h.Text(post.CreatedAt)),
he.Td()(h.Text(post.UpdatedAt)),
he.Td()(h.Text(post.Author)),
he.Td()(h.Text(post.Title)),
he.Td()(
he.A(ha.Href(previewURL))(h.Text("Preview")),
),
))
}
html := he.Html()(
he.Head()(),
he.Body()(
he.H1()(h.Text("Posts")),
he.Table()(
he.Thead()(
he.Tr()(
he.Th()(h.Text("ID")),
he.Th()(h.Text("Created")),
he.Th()(h.Text("Updated")),
he.Th()(h.Text("Author")),
he.Th()(h.Text("Title")),
he.Th()(),
),
),
he.Tbody()(
rows...,
),
),
he.A(ha.Href(hnd.baseURL+pathCreatePost))(h.Text("Create post")),
),
)
_, err = h.WriteDocument(w, html)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
const fieldNamePostID = "post-id"
func (hnd *handler) previewPost(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Query().Get(fieldNamePostID)
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil {
http.Error(w, "invalid post ID: "+err.Error(), http.StatusBadRequest)
}
result, err := hnd.store.GetPost(r.Context(), GetPostQuery{PostID: id})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
renderedBody := string(blackfriday.Run([]byte(result.Body)))
html := he.Html()(
he.Head()(
he.Title()(h.Text(result.Title)),
),
he.Body()(
he.H1()(h.Text(result.Title)),
he.H2()(h.Text(result.Author)),
he.H3()(h.Text(fmt.Sprintf("%s (Updated %s)", result.CreatedAt, result.UpdatedAt))),
he.Main()(h.RawText(renderedBody)),
),
)
_, err = h.WriteDocument(w, html)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
const (
fieldNamePostTitle = "post-title"
fieldNamePostBody = "post-body"
fieldNamePostAuthor = "post-author"
)
func (hnd *handler) createPost(w http.ResponseWriter, r *http.Request) {
html := he.Html()(
he.Head()(),
he.Body()(
he.H1()(h.Text("Create post")),
he.Form(
ha.Action(hnd.baseURL+pathDoCreatePost),
ha.Method("POST"),
ha.Enctype("multipart/form-data"),
)(
he.Label(ha.For(fieldNamePostTitle))(h.Text("Title")),
he.Input(ha.Name(fieldNamePostTitle)),
he.Br(),
he.Label(ha.For(fieldNamePostAuthor))(h.Text("Author")),
he.Input(ha.Name(fieldNamePostAuthor)),
he.Br(),
he.Label(ha.For(fieldNamePostBody))(h.Text("Body")),
he.Textarea(ha.Name(fieldNamePostBody))(),
he.Br(),
he.Input(ha.Type("submit")),
),
),
)
_, err := h.WriteDocument(w, html)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func (hnd *handler) doCreatePost(w http.ResponseWriter, r *http.Request) {
err := r.ParseMultipartForm(0)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
cmd := CreatePostCommand{
Title: r.PostForm.Get(fieldNamePostTitle),
Author: r.PostForm.Get(fieldNamePostAuthor),
Body: r.PostForm.Get(fieldNamePostBody),
}
_, err = hnd.store.CreatePost(r.Context(), cmd)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, pathRoot, http.StatusFound)
}