From 3a77eb1593804ad7f61cce14ead2c70f33978047 Mon Sep 17 00:00:00 2001 From: Patryk Hegenberg Date: Wed, 6 Dec 2023 17:24:33 +0100 Subject: [PATCH] backend: frontend: cleaned up the codebase and added more logging for debuging --- handlers/about_handler.go | 16 +++++++++++++--- handlers/add_monster_handler.go | 27 +++++++++++++++++++++++--- handlers/contact_handler.go | 9 ++++++++- handlers/form_handler.go | 31 ++++++++++++++++++++++++++---- handlers/main_handler.go | 11 +++++++++-- handlers/monster_table_handler.go | 9 ++++++++- handlers/submit_handler.go | 32 ++++++++++++++++++------------- main.go | 19 ++++++++++++++---- model/model.go | 31 ++++++++++++++++++++++++------ templates/about.html | 4 ++++ templates/contact.html | 1 - 11 files changed, 152 insertions(+), 38 deletions(-) diff --git a/handlers/about_handler.go b/handlers/about_handler.go index 12c02f8..fa7270c 100644 --- a/handlers/about_handler.go +++ b/handlers/about_handler.go @@ -7,18 +7,28 @@ import ( "net/http" ) +// AboutHandler returns an http.HandlerFunc that handles requests to the /about endpoint. +// It renders the about.html template and passes in the title "Dungeons & Dragons Monster Generator". func AboutHandler(content embed.FS) http.HandlerFunc { log.Print("AboutHandler called") + return func(w http.ResponseWriter, r *http.Request) { - tmpl, err := template.ParseFS(content, "templates/base.html", "templates/header.html", "templates/main.html", "templates/footer.html", "templates/about.html") + log.Print("AboutHandler request received") + + // Parse the template files + tmplFiles := []string{"templates/base.html", "templates/header.html", "templates/main.html", "templates/footer.html", "templates/about.html"} + tmpl, err := template.ParseFS(content, tmplFiles...) if err != nil { + log.Printf("Template parsing error: %v\n", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - err = tmpl.ExecuteTemplate(w, "about", map[string]interface{}{ + // Execute the template with the provided data + data := map[string]interface{}{ "Title": "Dungeons & Dragons Monster Generator", - }) + } + err = tmpl.ExecuteTemplate(w, "about", data) if err != nil { log.Printf("Template execution error: %v\n", err) http.Error(w, err.Error(), http.StatusInternalServerError) diff --git a/handlers/add_monster_handler.go b/handlers/add_monster_handler.go index bd84af3..8f08685 100644 --- a/handlers/add_monster_handler.go +++ b/handlers/add_monster_handler.go @@ -7,21 +7,28 @@ import ( "strconv" ) +// AddMonster is a http.HandlerFunc that adds a new monster to the Monsters slice. +// It expects a POST request with form data containing the details of the monster. +// The monster is then appended to the Monsters slice and a redirect response is sent. func AddMonster(Monsters *[]model.Monster) http.HandlerFunc { log.Print("AddMonster called") return func(w http.ResponseWriter, r *http.Request) { - // TODO + // Check if the request method is POST if r.Method != http.MethodPost { + log.Print("Method not allowed") http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } + // Parse the form data err := r.ParseForm() if err != nil { + log.Printf("Error parsing form data: %s", err.Error()) http.Error(w, err.Error(), http.StatusInternalServerError) return } + // Create a new monster with the form data monster := model.Monster{ Name: r.FormValue("name"), Source: r.FormValue("source"), @@ -73,19 +80,33 @@ func AddMonster(Monsters *[]model.Monster) http.HandlerFunc { }, }, } + + // Lock the Monsters slice, append the monster, and unlock the slice mu.Lock() defer mu.Unlock() *Monsters = append(*Monsters, monster) - log.Printf("Monster hinzugefügt. Anzahl der Monster jetzt: %d\n", len(*Monsters)) + + // Log the number of monsters and redirect to the monster table + log.Printf("Monster added. Number of monsters now: %d\n", len(*Monsters)) http.Redirect(w, r, "/monsterTable", http.StatusFound) } } -// parseInt konvertiert einen String zu einem Integer und gibt 0 zurück, wenn die Konvertierung fehlschlägt +// parseInt converts a string to an integer and returns 0 if the conversion fails func parseInt(s string) int { + // Add logging statement to print the input string + log.Println("Input string:", s) + + // Atoi is used to convert the string to an integer i, err := strconv.Atoi(s) + // If there is an error in the conversion, return 0 and log the error if err != nil { + log.Println("Conversion error:", err) return 0 } + // Log the converted integer + log.Println("Converted integer:", i) + + // Return the converted integer return i } diff --git a/handlers/contact_handler.go b/handlers/contact_handler.go index a683377..a01cad4 100644 --- a/handlers/contact_handler.go +++ b/handlers/contact_handler.go @@ -7,15 +7,22 @@ import ( "net/http" ) +// ContactHandler handles the contact page request. +// It takes the content embed.FS as a parameter and returns an http.HandlerFunc. +// The returned http.HandlerFunc renders the contact page using the provided templates. func ContactHandler(content embed.FS) http.HandlerFunc { - log.Print("ContactHandler called") return func(w http.ResponseWriter, r *http.Request) { + log.Print("ContactHandler called") + + // Parse the templates tmpl, err := template.ParseFS(content, "templates/base.html", "templates/header.html", "templates/main.html", "templates/footer.html", "templates/about.html", "templates/contact.html") if err != nil { + log.Printf("Template parsing error: %v\n", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } + // Execute the contact template err = tmpl.ExecuteTemplate(w, "contact", map[string]interface{}{ "Title": "Dungeons & Dragons Monster Generator", }) diff --git a/handlers/form_handler.go b/handlers/form_handler.go index 878ba66..20e0f05 100644 --- a/handlers/form_handler.go +++ b/handlers/form_handler.go @@ -8,25 +8,48 @@ import ( "net/http" ) +// FormHandler returns an http.HandlerFunc that handles form submissions. +// It takes the content embed.FS, a pointer to a slice of model.Monster, +// and a filename string as parameters. +// The function parses the template files from the content FS, +// executes the template with the provided data, and renders it as a response. func FormHandler(content embed.FS, monsters *[]model.Monster, filename string) http.HandlerFunc { log.Print("FormHandler called") + + // Lock the mutex to ensure exclusive access to the monsters slice. mu.Lock() defer mu.Unlock() + return func(w http.ResponseWriter, r *http.Request) { - tmpl, err := template.ParseFS(content, "templates/base.html", "templates/header.html", "templates/main.html", "templates/footer.html", "templates/monsterForm.html", "templates/monster.html", "templates/monsterTable.html") + log.Print("FormHandler handler called") + + // Parse the template files. + templateFiles := []string{ + "templates/base.html", + "templates/header.html", + "templates/main.html", + "templates/footer.html", + "templates/monsterForm.html", + "templates/monster.html", + "templates/monsterTable.html", + } + tmpl, err := template.ParseFS(content, templateFiles...) if err != nil { + log.Printf("Template parsing error: %v\n", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - err = tmpl.ExecuteTemplate(w, "base", map[string]interface{}{ + // Execute the template and render the response. + data := map[string]interface{}{ "Title": "Dungeons & Dragons Monster Generator", "Monsters": *monsters, - }) + } + err = tmpl.ExecuteTemplate(w, "base", data) if err != nil { log.Printf("Template execution error: %v\n", err) http.Error(w, err.Error(), http.StatusInternalServerError) } - log.Printf("Template mit %d Monstern gerendert\n", len(*monsters)) + log.Printf("Template rendered with %d Monsters\n", len(*monsters)) } } diff --git a/handlers/main_handler.go b/handlers/main_handler.go index 7f25b6a..f060e3b 100644 --- a/handlers/main_handler.go +++ b/handlers/main_handler.go @@ -8,19 +8,26 @@ import ( "net/http" ) -// MainHandler +// MainHandler handles the main HTTP request. +// It returns an http.HandlerFunc that renders the main page +// with the provided content and monsters. func MainHandler(content embed.FS, monsters *[]model.Monster) http.HandlerFunc { - log.Print("MainHandler called") return func(w http.ResponseWriter, r *http.Request) { + log.Print("MainHandler called") + + // Parse the templates from the embedded file system tmpl, err := template.ParseFS(content, "templates/main.html", "templates/monsterForm.html", "templates/monster.html", "templates/monsterTable.html", "templates/base.html") if err != nil { + log.Printf("Template parsing error: %v\n", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } + // Lock the mutex to ensure exclusive access to the monsters slice mu.Lock() defer mu.Unlock() + // Execute the main template with the provided data err = tmpl.ExecuteTemplate(w, "main", map[string]interface{}{ "Title": "Dungeons & Dragons Monster Generator", "Monsters": *monsters, diff --git a/handlers/monster_table_handler.go b/handlers/monster_table_handler.go index ebba144..4755386 100644 --- a/handlers/monster_table_handler.go +++ b/handlers/monster_table_handler.go @@ -8,15 +8,22 @@ import ( "net/http" ) +// MonsterTableHandler returns a http.HandlerFunc that handles requests to display a table of monsters. func MonsterTableHandler(content embed.FS, monsters *[]model.Monster) http.HandlerFunc { - log.Print("AboutHandler called") + log.Print("MonsterTableHandler called") + return func(w http.ResponseWriter, r *http.Request) { + log.Print("Handling request for monster table") + + // Parse the template files tmpl, err := template.ParseFS(content, "templates/base.html", "templates/header.html", "templates/main.html", "templates/footer.html", "templates/monsterTable.html", "templates/monster.html") if err != nil { + log.Printf("Template parsing error: %v\n", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } + // Execute the template and pass the necessary data err = tmpl.ExecuteTemplate(w, "monsterTable", map[string]interface{}{ "Title": "Dungeons & Dragons Monster Generator", "Monsters": *monsters, diff --git a/handlers/submit_handler.go b/handlers/submit_handler.go index 02a6fde..4a7b50e 100644 --- a/handlers/submit_handler.go +++ b/handlers/submit_handler.go @@ -13,53 +13,59 @@ import ( var mu sync.Mutex -// submitHandler verarbeitet die Formulardaten +// SubmitHandler processes the form data. func SubmitHandler(content embed.FS, chars *[]model.Character, Monsters *[]model.Monster, filename string) http.HandlerFunc { log.Print("SubmitHandler called") return func(w http.ResponseWriter, r *http.Request) { + log.Print("SubmitHandler called") if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } - // Formulardaten parsen + // Parse form data. err := r.ParseForm() if err != nil { + log.Printf("Error parsing form data: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - // Monster-Objekt erstellen + // Create monster object. filename := r.FormValue("filename") - // Charakter-Objekt erstellen oder aktualisieren + // Create or update character object. mu.Lock() defer mu.Unlock() char := model.GetOrCreateCharacter(filename, *chars) char.Monster = append(char.Monster, *Monsters...) - // Charakterdaten in JSON umwandeln + // Convert character data to JSON. charJSON, err := json.Marshal(char) if err != nil { + log.Printf("Error marshalling character data to JSON: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - // JSON-Daten in die Datei schreiben + // Write JSON data to file. err = model.WriteToFile(filename, charJSON) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - // Dateiinhalt lesen - fileContent, err := os.ReadFile(filename) - if err != nil { + log.Printf("Error writing JSON data to file: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - // Datei zum Download anbieten + // Read file contents. + fileContent, err := os.ReadFile(filename) + if err != nil { + log.Printf("Error reading file contents: %v", err) + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + // Offer file for download. w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename)) w.Header().Set("Content-Type", "application/json") w.Write(fileContent) diff --git a/main.go b/main.go index 2060623..d52656d 100644 --- a/main.go +++ b/main.go @@ -20,10 +20,14 @@ var ( Monsters []model.Monster ) +// main is the entry point of the program. func main() { filename := "" + + // Print the message indicating that 'static' has been included. log.Printf("Eingebunden is %v\n", static) + // Set up the HTTP handlers for different routes. http.HandleFunc("/", handlers.FormHandler(content, &Monsters, filename)) http.HandleFunc("/submit", handlers.SubmitHandler(content, &chars, &Monsters, filename)) http.Handle("/images/", http.StripPrefix("/images/", http.FileServer(http.FS(content)))) @@ -33,27 +37,34 @@ func main() { http.HandleFunc("/contact", handlers.ContactHandler(content)) http.HandleFunc("/monsterTable", handlers.MonsterTableHandler(content, &Monsters)) - // Lade die CSS-Datei + // Load the CSS file. css, err := loadCSS(static) if err != nil { log.Fatal(err) } - // Füge eine Route für die CSS-Datei hinzu + // Add a route for the CSS file. http.HandleFunc("/static/darkly_bulmawatch.css", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/css") w.Write([]byte(css)) }) + // Print the message indicating that the server has started. log.Print("Server gestartet, erreichbar unter http://localhost:8080") - http.ListenAndServe(":8080", nil) + + // Start the server and listen for incoming requests on port 8080. + log.Fatal(http.ListenAndServe(":8080", nil)) } -// loadCSS liest die CSS-Datei aus dem eingebetteten Dateisystem. +// loadCSS reads the CSS file from the embedded filesystem. +// It takes the content embed.FS as input. +// It returns the content of the CSS file as a string and an error if any. func loadCSS(content embed.FS) (string, error) { + // Read the CSS file "static/darkly_bulmawatch.css" from the embedded filesystem file, err := content.ReadFile("static/darkly_bulmawatch.css") if err != nil { return "", err } + // Convert the file content to a string and return return string(file), nil } diff --git a/model/model.go b/model/model.go index 1ad1bb0..fa6605e 100644 --- a/model/model.go +++ b/model/model.go @@ -2,6 +2,7 @@ package model import ( "fmt" + "log" "os" "time" ) @@ -89,32 +90,47 @@ type Source struct { ConvertedBy []string `json:"convertedBy"` } -// writeToFile schreibt Daten in eine Datei +// WriteToFile writes data to a file. +// It takes in a filename string and a data byte slice. +// It returns an error if there was an issue writing to the file, otherwise it returns nil. func WriteToFile(filename string, data []byte) error { + log.Println("Writing data to file:", filename) + + // Create a file with the given filename file, err := os.Create(filename) if err != nil { + log.Println("Error creating file:", err) return err } - defer file.Close() + defer func() { + if err := file.Close(); err != nil { + log.Println("Error closing file:", err) + } + }() - _, err = file.Write(data) + // Write the data to the file + n, err := file.Write(data) if err != nil { + log.Println("Error writing to file:", err) return err } + log.Printf("Successfully wrote %d bytes to file", n) return nil } -// getOrCreateCharacter gibt das aktuelle Charakterobjekt zurück oder erstellt ein neues +// getOrCreateCharacter returns the current character object or creates a new one func GetOrCreateCharacter(filename string, chars []Character) Character { + // Check if there is an empty character object for _, char := range chars { if char.Meta.DateLastModified == 0 { - // Ein leeres Charakterobjekt wurde gefunden + // Return the empty character object + log.Println("Returning existing character object") return char } } - // Erstelle ein neues Charakterobjekt + // Create a new character object now := time.Now().Unix() newChar := Character{ Meta: Meta{ @@ -134,7 +150,10 @@ func GetOrCreateCharacter(filename string, chars []Character) Character { Monster: []Monster{}, } + // Append the new character object to the list of characters chars = append(chars, newChar) + // Return the newly created character object + log.Println("Returning newly created character object") return newChar } diff --git a/templates/about.html b/templates/about.html index 9d29c17..6133b97 100644 --- a/templates/about.html +++ b/templates/about.html @@ -1,15 +1,19 @@ {{ define "about" }} +
+

About Us

+

Welcome to the Dungeons and Dragons Monster Generator website! We are a team of enthusiasts...

+ {{ end }} diff --git a/templates/contact.html b/templates/contact.html index 4b1dbea..ce0085d 100644 --- a/templates/contact.html +++ b/templates/contact.html @@ -13,7 +13,6 @@