188 lines
4.4 KiB
Go
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)
|
|
}
|