You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
4.0 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package server
import (
"database/sql"
"fmt"
"html/template"
"log/slog"
"net/http"
"strconv"
"git.runcible.io/learning/ratchet/internal/domain/user"
"git.runcible.io/learning/ratchet/internal/model"
)
type RatchetServer struct {
http.Handler
logger *slog.Logger
//Services used by HTTP routes
snippetService *model.SnippetService
UserService user.UserService
}
func NewRatchetServer(logger *slog.Logger, db *sql.DB) *RatchetServer {
rs := new(RatchetServer)
rs.logger = logger
rs.snippetService = &model.SnippetService{DB: db}
// TODO implement middleware that disables directory listings
fileServer := http.FileServer(http.Dir("./ui/static/"))
router := http.NewServeMux()
// Subtree pattern for static assets
router.Handle("GET /static/", http.StripPrefix("/static/", fileServer))
// /{$} is used to prevent subtree path patterns from acting like a wildcard
// resulting in this route requiring an exact match on "/" only
// You can only include one HTTP method in a route pattern if you choose
// GET will match GET & HEAD http request methods
router.HandleFunc("GET /{$}", rs.home)
router.HandleFunc("GET /snippet/view/{id}", rs.snippetView)
router.HandleFunc("GET /snippet/create", rs.snippetCreate)
// FYI The http.HandlerFunc() adapter works by automatically adding a ServeHTTP() method to
// the passed function
router.HandleFunc("POST /snippet/create", rs.snippetCreatePost)
// Mux Router implements the Handler interface. AKA it has a ServeHTTP receiver.
rs.Handler = router
return rs
}
func (rs *RatchetServer) home(w http.ResponseWriter, r *http.Request) {
// TODO middleware should be able to print out these lines for all routes
rs.logger.Info("request received", "method", "GET", "path", "/")
w.Header().Add("Server", "Go")
// 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...)
if err != nil {
rs.serverError(w, r, err)
return
}
// Write template content to response body
err = ts.ExecuteTemplate(w, "base", nil)
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)
rs.serverError(w, r, err)
}
}
func (rs *RatchetServer) snippetView(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil || id < 1 {
// http.NotFound(w, r)
rs.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")
// msg := fmt.Sprintf("Snippet %d...", id)
// w.Write([]byte(msg))
// we can rely on the Write() interface to use a differnent
// function to write out our response
fmt.Fprintf(w, "Snippet %d...", id)
}
// snippetCreate handles display of the form used to create snippets
func (rs *RatchetServer) snippetCreate(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:5000/snippet/create
func (rs *RatchetServer) snippetCreatePost(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 := rs.snippetService.Insert(title, content, expires)
if err != nil {
rs.serverError(w, r, err)
}
http.Redirect(w, r, fmt.Sprintf("/snippet/view/%d", id), http.StatusSeeOther)
}