|
|
package server
|
|
|
|
|
|
import (
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
"log/slog"
|
|
|
"net/http"
|
|
|
"strconv"
|
|
|
|
|
|
"git.runcible.io/learning/ratchet/internal/model"
|
|
|
)
|
|
|
|
|
|
// TODO function should accept and a pointer to an interface allowing for mocking in tests.
|
|
|
func handleHome(logger *slog.Logger, tc *TemplateCache, snippetService *model.SnippetService) http.Handler {
|
|
|
return http.HandlerFunc(
|
|
|
func(w http.ResponseWriter, r *http.Request) {
|
|
|
// Retrieve Snippets from DB
|
|
|
snippets, err := snippetService.Lastest()
|
|
|
if err != err {
|
|
|
serverError(w, r, err)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
logger.Debug(fmt.Sprintf("%d snippets retrieved", len(snippets)))
|
|
|
|
|
|
// Old way. We want default data so
|
|
|
// data := templateData{
|
|
|
// Snippets: snippets,
|
|
|
// }
|
|
|
data := newTemplateData()
|
|
|
data.Snippets = snippets
|
|
|
|
|
|
renderTemplate(w, r, tc, http.StatusOK, "home.go.tmpl", data)
|
|
|
|
|
|
// // Initialize a slice containing the paths to the two files. It's important
|
|
|
// // to note that the file containing our base template must be the *first*
|
|
|
// // file in the slice.
|
|
|
// files := []string{
|
|
|
// "./ui/html/base.go.tmpl",
|
|
|
// "./ui/html/partials/nav.go.tmpl",
|
|
|
// "./ui/html/pages/home.go.tmpl",
|
|
|
// }
|
|
|
|
|
|
// read template file into template set.
|
|
|
// ts, err := template.ParseFiles(files...)
|
|
|
// ts, err := parseTemplateFiles(files...)
|
|
|
// if err != nil {
|
|
|
// serverError(w, r, err)
|
|
|
// return
|
|
|
// }
|
|
|
// // Write template content to response body
|
|
|
// err = ts.ExecuteTemplate(w, "base", data)
|
|
|
// if err != nil {
|
|
|
// // This is the older more verbose way of doing what RatchetServer.serverError does
|
|
|
// // rs.logger.Error(err.Error())
|
|
|
// // http.Error(w, "Internal Server Error", http.StatusInternalServerError)
|
|
|
// serverError(w, r, err)
|
|
|
// }
|
|
|
})
|
|
|
}
|
|
|
|
|
|
func handleSnippetView(logger *slog.Logger, tc *TemplateCache, snippetService *model.SnippetService) http.Handler {
|
|
|
return http.HandlerFunc(
|
|
|
func(w http.ResponseWriter, r *http.Request) {
|
|
|
id, err := strconv.Atoi(r.PathValue("id"))
|
|
|
if err != nil || id < 1 {
|
|
|
clientError(w, http.StatusNotFound)
|
|
|
return
|
|
|
}
|
|
|
// Set a new cache-control header. If an existing "Cache-Control" header exists
|
|
|
// it will be overwritten.
|
|
|
// w.Header().Set("Cache-Control", "public, max-age=31536000")
|
|
|
|
|
|
snippet, err := snippetService.Get(id)
|
|
|
if err != nil {
|
|
|
logger.Debug(fmt.Sprintf("Failed to retrieve an active record with id: %d", id))
|
|
|
if errors.Is(err, model.ErrNoRecord) {
|
|
|
clientError(w, http.StatusNotFound)
|
|
|
} else {
|
|
|
serverError(w, r, err)
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// files := []string{
|
|
|
// "./ui/html/base.go.tmpl",
|
|
|
// "./ui/html/partials/nav.go.tmpl",
|
|
|
// "./ui/html/pages/view.go.tmpl",
|
|
|
// }
|
|
|
|
|
|
// //ts, err := template.ParseFiles(files...)
|
|
|
// ts, err := parseTemplateFiles(files...)
|
|
|
// if err != nil {
|
|
|
// serverError(w, r, err)
|
|
|
// return
|
|
|
// }
|
|
|
|
|
|
// data := templateData{
|
|
|
// Snippet: snippet,
|
|
|
// }
|
|
|
|
|
|
// logger.Debug(fmt.Sprintf("created template: %s", ts.Name()))
|
|
|
|
|
|
// Any data that you pass as the final parameter to ts.ExecuteTemplate()
|
|
|
// is represented within your HTML templates by the . character (referred to as dot).
|
|
|
// In this specific case, the underlying type of dot will be a models.Snippet struct.
|
|
|
// When the underlying type of dot is a struct, you can render (or yield) the value
|
|
|
// of any exported field in your templates by postfixing dot with the field name
|
|
|
// field, we could yield the snippet title by writing {{.Title}} in our templates.
|
|
|
// err = ts.ExecuteTemplate(w, "base", data)
|
|
|
// if err != nil {
|
|
|
// serverError(w, r, err)
|
|
|
// }
|
|
|
// data := templateData{
|
|
|
// Snippet: snippet,
|
|
|
// }
|
|
|
data := newTemplateData()
|
|
|
data.Snippet = snippet
|
|
|
renderTemplate(w, r, tc, http.StatusOK, "view.go.tmpl", data)
|
|
|
})
|
|
|
}
|
|
|
|
|
|
func handleSnippetCreateGet() http.Handler {
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
w.Write([]byte("Create snippet form.."))
|
|
|
})
|
|
|
}
|
|
|
|
|
|
// snippetCreate handles display of the form used to create snippets
|
|
|
//
|
|
|
// curl -iL -d "" http://localhost:5001/snippet/create
|
|
|
func handleSnippetCreatePost(logger *slog.Logger, tc *TemplateCache, snippetService *model.SnippetService) http.Handler {
|
|
|
return http.HandlerFunc(
|
|
|
func(w http.ResponseWriter, r *http.Request) {
|
|
|
// example of a custom header. Must be done before calling WriteHeader
|
|
|
// or they will fail to take effect.
|
|
|
w.Header().Add("Server", "Dirp")
|
|
|
// Create some variables holding dummy data. We'll remove these later on
|
|
|
// during the build.
|
|
|
title := "O snail"
|
|
|
content := "O snail\nClimb Mount Fuji,\nBut slowly, slowly!\n\n– Kobayashi Issa"
|
|
|
expires := 7
|
|
|
|
|
|
id, err := snippetService.Insert(title, content, expires)
|
|
|
if err != nil {
|
|
|
serverError(w, r, err)
|
|
|
}
|
|
|
logger.Info(fmt.Sprintf("Inserted record. id: %d", id))
|
|
|
|
|
|
http.Redirect(w, r, fmt.Sprintf("/snippet/view/%d", id), http.StatusSeeOther)
|
|
|
})
|
|
|
}
|