|  |  | @ -1,52 +1,26 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  | package ratchet |  |  |  | package server | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | import ( |  |  |  | import ( | 
			
		
	
		
		
			
				
					
					|  |  |  | 	"fmt" |  |  |  | 	"fmt" | 
			
		
	
		
		
			
				
					
					|  |  |  | 	"html/template" |  |  |  | 	"html/template" | 
			
		
	
		
		
			
				
					
					|  |  |  | 	"log/slog" |  |  |  | 	"log/slog" | 
			
		
	
		
		
			
				
					
					|  |  |  | 	"net/http" |  |  |  | 	"net/http" | 
			
		
	
		
		
			
				
					
					|  |  |  | 	"os" |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | 	"strconv" |  |  |  | 	"strconv" | 
			
		
	
		
		
			
				
					
					|  |  |  | 	"strings" |  |  |  | 
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 	"git.runcible.io/learning/ratchet/internal/domain/user" | 
			
		
	
		
		
			
				
					
					|  |  |  | ) |  |  |  | ) | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | type RatchetServer struct { |  |  |  | type RatchetServer struct { | 
			
		
	
		
		
			
				
					
					|  |  |  | 	http.Handler |  |  |  | 	http.Handler | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 	logger *slog.Logger | 
			
		
	
		
		
			
				
					
					|  |  |  | 	//Services used by HTTP routes
 |  |  |  | 	//Services used by HTTP routes
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	UserService UserService |  |  |  | 	UserService user.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) { |  |  |  | func NewRatchetServer(logger *slog.Logger) *RatchetServer { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 	// Use os.Stderr
 |  |  |  | 	rs := new(RatchetServer) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 	//
 |  |  |  | 	rs.logger = logger | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 	// 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
 |  |  |  | 	// TODO implement middleware that disables directory listings
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	fileServer := http.FileServer(http.Dir("./ui/static/")) |  |  |  | 	fileServer := http.FileServer(http.Dir("./ui/static/")) | 
			
		
	
		
		
			
				
					
					|  |  |  | 	router := http.NewServeMux() |  |  |  | 	router := http.NewServeMux() | 
			
		
	
	
		
		
			
				
					|  |  | @ -58,22 +32,22 @@ func NewRatchetServer() *RatchetServer { | 
			
		
	
		
		
			
				
					
					|  |  |  | 	// resulting in this route requiring an exact match on "/" only
 |  |  |  | 	// resulting in this route requiring an exact match on "/" only
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	// You can only include one HTTP method in a route pattern if you choose
 |  |  |  | 	// You can only include one HTTP method in a route pattern if you choose
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	// GET will match GET & HEAD http request methods
 |  |  |  | 	// GET will match GET & HEAD http request methods
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	router.HandleFunc("GET /{$}", home) |  |  |  | 	router.HandleFunc("GET /{$}", rs.home) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 	router.HandleFunc("GET /snippet/view/{id}", snippetView) |  |  |  | 	router.HandleFunc("GET /snippet/view/{id}", rs.snippetView) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 	router.HandleFunc("GET /snippet/create", snippetCreate) |  |  |  | 	router.HandleFunc("GET /snippet/create", rs.snippetCreate) | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	// FYI The http.HandlerFunc() adapter works by automatically adding a ServeHTTP() method to
 |  |  |  | 	// FYI The http.HandlerFunc() adapter works by automatically adding a ServeHTTP() method to
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	// the passed function
 |  |  |  | 	// the passed function
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	router.HandleFunc("POST /snippet/create", snippetCreatePost) |  |  |  | 	router.HandleFunc("POST /snippet/create", rs.snippetCreatePost) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	// Mux Router implements the Handler interface. AKA it has a ServeHTTP receiver.
 |  |  |  | 	// Mux Router implements the Handler interface. AKA it has a ServeHTTP receiver.
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	r.Handler = router |  |  |  | 	rs.Handler = router | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 	return r |  |  |  | 	return rs | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | func home(w http.ResponseWriter, r *http.Request) { |  |  |  | func (rs *RatchetServer) home(w http.ResponseWriter, r *http.Request) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 	// TODO middleware should be able to print out these lines for all routes
 |  |  |  | 	// TODO middleware should be able to print out these lines for all routes
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	slog.Info("request received", "method", "GET", "path", "/") |  |  |  | 	rs.logger.Info("request received", "method", "GET", "path", "/") | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	w.Header().Add("Server", "Go") |  |  |  | 	w.Header().Add("Server", "Go") | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -89,23 +63,25 @@ func home(w http.ResponseWriter, r *http.Request) { | 
			
		
	
		
		
			
				
					
					|  |  |  | 	// read template file into template set.
 |  |  |  | 	// read template file into template set.
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	ts, err := template.ParseFiles(files...) |  |  |  | 	ts, err := template.ParseFiles(files...) | 
			
		
	
		
		
			
				
					
					|  |  |  | 	if err != nil { |  |  |  | 	if err != nil { | 
			
		
	
		
		
			
				
					
					|  |  |  | 		slog.Error(err.Error()) |  |  |  | 		rs.serverError(w, r, err) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		http.Error(w, "Internal Server Error", http.StatusInternalServerError) |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 		return |  |  |  | 		return | 
			
		
	
		
		
			
				
					
					|  |  |  | 	} |  |  |  | 	} | 
			
		
	
		
		
			
				
					
					|  |  |  | 	// Write template content to response body
 |  |  |  | 	// Write template content to response body
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	err = ts.ExecuteTemplate(w, "base", nil) |  |  |  | 	err = ts.ExecuteTemplate(w, "base", nil) | 
			
		
	
		
		
			
				
					
					|  |  |  | 	if err != nil { |  |  |  | 	if err != nil { | 
			
		
	
		
		
			
				
					
					|  |  |  | 		slog.Error(err.Error()) |  |  |  | 		// This is the older more verbose way of doing what RatchetServer.serverError does
 | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | 		http.Error(w, "Internal Server Error", http.StatusInternalServerError) |  |  |  | 		// rs.logger.Error(err.Error())
 | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		// http.Error(w, "Internal Server Error", http.StatusInternalServerError)
 | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		rs.serverError(w, r, err) | 
			
		
	
		
		
			
				
					
					|  |  |  | 	} |  |  |  | 	} | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | func snippetView(w http.ResponseWriter, r *http.Request) { |  |  |  | func (rs *RatchetServer) snippetView(w http.ResponseWriter, r *http.Request) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	id, err := strconv.Atoi(r.PathValue("id")) |  |  |  | 	id, err := strconv.Atoi(r.PathValue("id")) | 
			
		
	
		
		
			
				
					
					|  |  |  | 	if err != nil || id < 1 { |  |  |  | 	if err != nil || id < 1 { | 
			
		
	
		
		
			
				
					
					|  |  |  | 		http.NotFound(w, r) |  |  |  | 		// http.NotFound(w, r)
 | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | 		rs.clientError(w, http.StatusNotFound) | 
			
		
	
		
		
			
				
					
					|  |  |  | 		return |  |  |  | 		return | 
			
		
	
		
		
			
				
					
					|  |  |  | 	} |  |  |  | 	} | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
	
		
		
			
				
					|  |  | @ -124,12 +100,12 @@ func snippetView(w http.ResponseWriter, r *http.Request) { | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | // snippetCreate handles display of the form used to create snippets
 |  |  |  | // snippetCreate handles display of the form used to create snippets
 | 
			
		
	
		
		
			
				
					
					|  |  |  | func snippetCreate(w http.ResponseWriter, r *http.Request) { |  |  |  | func (rs *RatchetServer) snippetCreate(w http.ResponseWriter, r *http.Request) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 	w.Write([]byte("Displaying snippetCreate form...")) |  |  |  | 	w.Write([]byte("Displaying snippetCreate form...")) | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  | 
 |  |  |  | 
 | 
			
		
	
		
		
			
				
					
					|  |  |  | // snippetCreate handles display of the form used to create snippets
 |  |  |  | // snippetCreate handles display of the form used to create snippets
 | 
			
		
	
		
		
			
				
					
					|  |  |  | func snippetCreatePost(w http.ResponseWriter, r *http.Request) { |  |  |  | func (rs *RatchetServer) snippetCreatePost(w http.ResponseWriter, r *http.Request) { | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | 	// example of a custom header. Must be done before calling WriteHeader
 |  |  |  | 	// example of a custom header. Must be done before calling WriteHeader
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	// or they will fail to take effect.
 |  |  |  | 	// or they will fail to take effect.
 | 
			
		
	
		
		
			
				
					
					|  |  |  | 	w.Header().Add("Server", "Dirp") |  |  |  | 	w.Header().Add("Server", "Dirp") |