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.

140 lines
3.9 KiB
Go

package ratchet
import (
"fmt"
"html/template"
"log/slog"
"net/http"
"os"
"strconv"
"strings"
)
type RatchetServer struct {
http.Handler
//Services used by HTTP routes
UserService UserService
}
func parseLogLevel(levelStr string) slog.Level {
switch strings.ToUpper(levelStr) {
case "DEBUG":
return slog.LevelDebug
case "INFO":
return slog.LevelInfo
case "WARN":
return slog.LevelWarn
case "ERROR":
return slog.LevelError
default:
return slog.LevelInfo // Default level
}
}
func InitLogging(level string) {
// Use os.Stderr
//
// Stderr is used for diagnostics and logging. Stdout is used for program
// output. Stderr also have greater likely hood of being seen if a programs
// output is being redirected.
parsedLogLevel := parseLogLevel(level)
loggerHandler := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: parsedLogLevel, AddSource: true})
logger := slog.New(loggerHandler)
slog.SetDefault(logger)
}
func NewRatchetServer() *RatchetServer {
r := new(RatchetServer)
// 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 /{$}", home)
router.HandleFunc("GET /snippet/view/{id}", snippetView)
router.HandleFunc("GET /snippet/create", snippetCreate)
// FYI The http.HandlerFunc() adapter works by automatically adding a ServeHTTP() method to
// the passed function
router.HandleFunc("POST /snippet/create", snippetCreatePost)
// Mux Router implements the Handler interface. AKA it has a ServeHTTP receiver.
r.Handler = router
return r
}
func home(w http.ResponseWriter, r *http.Request) {
// TODO middleware should be able to print out these lines for all routes
slog.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 {
slog.Error(err.Error())
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
return
}
// Write template content to response body
err = ts.ExecuteTemplate(w, "base", nil)
if err != nil {
slog.Error(err.Error())
http.Error(w, "Internal Server Error", http.StatusInternalServerError)
}
}
func snippetView(w http.ResponseWriter, r *http.Request) {
id, err := strconv.Atoi(r.PathValue("id"))
if err != nil || id < 1 {
http.NotFound(w, r)
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 snippetCreate(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Displaying snippetCreate form..."))
}
// snippetCreate handles display of the form used to create snippets
func 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")
w.WriteHeader(http.StatusCreated)
w.Write([]byte("Created snippet..."))
}