diff --git a/.gitignore b/.gitignore index 9fdf7ac..cde0123 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,2 @@ dist/ -test_data/ diff --git a/README.md b/README.md deleted file mode 100644 index 34428ea..0000000 --- a/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# ddServer - -This is a simple Webserver, which helps zou creating monsters for a dungeons & dragons game - -## Installation -Either clone the repo and build it your self. -- You need to have go installed for that - -Or download the latest release and start your server with -```bash -./ddServer -``` diff --git a/go.mod b/go.mod index dfe5d47..8a6ca08 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,3 @@ module ddServer go 1.21.4 - -require github.com/stretchr/testify v1.8.4 - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/go.sum b/go.sum deleted file mode 100644 index fa4b6e6..0000000 --- a/go.sum +++ /dev/null @@ -1,10 +0,0 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/handlers/about_handler.go b/handlers/about_handler.go index 1d628e0..12c02f8 100644 --- a/handlers/about_handler.go +++ b/handlers/about_handler.go @@ -7,31 +7,21 @@ 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) { - 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...) + tmpl, err := template.ParseFS(content, "templates/base.html", "templates/header.html", "templates/main.html", "templates/footer.html", "templates/about.html") if err != nil { - log.Printf("Template parsing error: %v\n", err) - http.Error(w, err.Error(), http.StatusBadRequest) + http.Error(w, err.Error(), http.StatusInternalServerError) return } - // Execute the template with the provided data - data := map[string]any{ + err = tmpl.ExecuteTemplate(w, "about", 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.StatusBadRequest) + http.Error(w, err.Error(), http.StatusInternalServerError) } } } diff --git a/handlers/add_monster_handler.go b/handlers/add_monster_handler.go index 38cb1a1..bd84af3 100644 --- a/handlers/add_monster_handler.go +++ b/handlers/add_monster_handler.go @@ -5,140 +5,87 @@ import ( "log" "net/http" "strconv" - "strings" ) -// 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) { - // Check if the request method is POST + // TODO 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.StatusNoContent) + http.Error(w, err.Error(), http.StatusInternalServerError) return } - // Create a new monster with the form data - monster := parseMonster(r) - - // Lock the Monsters slice, append the monster, and unlock the slice + monster := model.Monster{ + Name: r.FormValue("name"), + Source: r.FormValue("source"), + Size: []string{r.FormValue("size")}, + Type: r.FormValue("type"), + Alignment: []string{r.FormValue("alignment")}, + AC: []model.AC{ + { + AC: parseInt(r.FormValue("ac")), + From: []string{r.FormValue("acFrom")}, + }, + }, + HP: model.HP{ + Average: parseInt(r.FormValue("hpAverage")), + Formula: r.FormValue("hpFormula"), + }, + Speed: model.Speed{ + Walk: parseInt(r.FormValue("speed")), + }, + Str: parseInt(r.FormValue("str")), + Dex: parseInt(r.FormValue("dex")), + Con: parseInt(r.FormValue("con")), + Int: parseInt(r.FormValue("int")), + Wis: parseInt(r.FormValue("wis")), + Cha: parseInt(r.FormValue("cha")), + Save: model.Save{ + Dex: r.FormValue("saveDex"), + Con: r.FormValue("saveCon"), + Wis: r.FormValue("saveWis"), + }, + Skill: model.Skill{ + Perception: r.FormValue("perception"), + Stealth: r.FormValue("stealth"), + }, + DamageRes: []string{r.FormValue("damageRes")}, + Senses: []string{r.FormValue("senses")}, + Languages: []string{r.FormValue("languages")}, + CR: r.FormValue("cr"), + Traits: []model.Trait{ + { + Name: r.FormValue("traitName"), + Entries: []string{r.FormValue("traitEntry")}, + }, + }, + Actions: []model.Action{ + { + Name: r.FormValue("actionName"), + Entries: []string{r.FormValue("actionEntry")}, + }, + }, + } mu.Lock() defer mu.Unlock() *Monsters = append(*Monsters, monster) - - // Log the number of monsters and redirect to the monster table - log.Printf("Monster added. Number of monsters now: %d\n", len(*Monsters)) + log.Printf("Monster hinzugefügt. Anzahl der Monster jetzt: %d\n", len(*Monsters)) http.Redirect(w, r, "/monsterTable", http.StatusFound) } } -// parseInt converts a string to an integer and returns 0 if the conversion fails +// parseInt konvertiert einen String zu einem Integer und gibt 0 zurück, wenn die Konvertierung fehlschlägt 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 } - -// parseMonster parses the Monster from monsterForm.html and return it. -func parseMonster(r *http.Request) model.Monster { - return model.Monster{ - Name: r.FormValue("name"), - Source: r.FormValue("source"), - Size: []string{r.FormValue("size")}, - Type: strings.ToLower(r.FormValue("type")), - Alignment: []string{r.FormValue("alignment")}, - AC: []model.AC{ - { - AC: parseInt(r.FormValue("ac")), - From: []string{r.FormValue("acFrom")}, - }, - }, - HP: model.HP{ - Average: parseInt(r.FormValue("hpAverage")), - Formula: r.FormValue("hpFormula"), - }, - Speed: model.Speed{ - Walk: parseInt(r.FormValue("walk")), - Burrow: parseInt(r.FormValue("burrow")), - Fly: parseInt(r.FormValue("fly")), - Swim: parseInt(r.FormValue("swim")), - Climb: parseInt(r.FormValue("climb")), - }, - Str: parseInt(r.FormValue("str")), - Dex: parseInt(r.FormValue("dex")), - Con: parseInt(r.FormValue("con")), - Int: parseInt(r.FormValue("int")), - Wis: parseInt(r.FormValue("wis")), - Cha: parseInt(r.FormValue("cha")), - Save: model.Save{ - Dex: r.FormValue("saveDex"), - Con: r.FormValue("saveCon"), - Wis: r.FormValue("saveWis"), - Str: r.FormValue("saveStr"), - Cha: r.FormValue("saveCha"), - Int: r.FormValue("saveInt"), - }, - Skill: model.Skill{ - Perception: r.FormValue("perception"), - Stealth: r.FormValue("stealth"), - Acrobatics: r.FormValue("acrobatics"), - AnimalHandling: r.FormValue("animalHandling"), - Arcana: r.FormValue("arcana"), - Athletics: r.FormValue("athletics"), - Deception: r.FormValue("deception"), - History: r.FormValue("history"), - Insight: r.FormValue("insight"), - Intimidation: r.FormValue("intimidation"), - Investigation: r.FormValue("investigation"), - Medicine: r.FormValue("medicine"), - Nature: r.FormValue("nature"), - Performance: r.FormValue("performance"), - Persuasion: r.FormValue("persuasion"), - SleightOfHand: r.FormValue("sleightOfHand"), - Survival: r.FormValue("survival"), - Religion: r.FormValue("religion"), - }, - Resist: []string{r.FormValue("resist")}, - ConditionImmune: []string{r.FormValue("conditionImmune")}, - Immune: []string{r.FormValue("immune")}, - Vulnerable: []string{r.FormValue("vulnerable")}, - Senses: []string{r.FormValue("senses")}, - Languages: []string{r.FormValue("languages")}, - CR: r.FormValue("cr"), - Traits: []model.Trait{ - { - Name: r.FormValue("traitName"), - Entries: []string{r.FormValue("traitEntry")}, - }, - }, - Actions: []model.Action{ - { - Name: r.FormValue("actionName"), - Entries: []string{r.FormValue("actionEntry")}, - }, - }, - } -} diff --git a/handlers/contact_handler.go b/handlers/contact_handler.go index 611abc2..a683377 100644 --- a/handlers/contact_handler.go +++ b/handlers/contact_handler.go @@ -7,23 +7,16 @@ 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]any{ + err = tmpl.ExecuteTemplate(w, "contact", map[string]interface{}{ "Title": "Dungeons & Dragons Monster Generator", }) if err != nil { diff --git a/handlers/form_handler.go b/handlers/form_handler.go index 1232463..878ba66 100644 --- a/handlers/form_handler.go +++ b/handlers/form_handler.go @@ -8,49 +8,25 @@ 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) http.HandlerFunc { +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) { - 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/skills.html", - "templates/monsterTable.html", - } - tmpl, err := template.ParseFS(content, templateFiles...) + 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") if err != nil { - log.Printf("Template parsing error: %v\n", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - // Execute the template and render the response. - data := map[string]any{ + err = tmpl.ExecuteTemplate(w, "base", 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 rendered with %d Monsters\n", len(*monsters)) + log.Printf("Template mit %d Monstern gerendert\n", len(*monsters)) } } diff --git a/handlers/main_handler.go b/handlers/main_handler.go index e4b575e..7f25b6a 100644 --- a/handlers/main_handler.go +++ b/handlers/main_handler.go @@ -8,27 +8,20 @@ import ( "net/http" ) -// MainHandler handles the main HTTP request. -// It returns an http.HandlerFunc that renders the main page -// with the provided content and monsters. +// MainHandler 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]any{ + 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 f227962..ebba144 100644 --- a/handlers/monster_table_handler.go +++ b/handlers/monster_table_handler.go @@ -8,23 +8,16 @@ 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("MonsterTableHandler called") - + log.Print("AboutHandler 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]any{ + err = tmpl.ExecuteTemplate(w, "monsterTable", map[string]interface{}{ "Title": "Dungeons & Dragons Monster Generator", "Monsters": *monsters, }) diff --git a/handlers/skill_calculation_handler.go b/handlers/skill_calculation_handler.go deleted file mode 100644 index 48935cf..0000000 --- a/handlers/skill_calculation_handler.go +++ /dev/null @@ -1,142 +0,0 @@ -package handlers - -import ( - "embed" - "html/template" - "log" - "net/http" - "strconv" -) - -// SkillCalculationHandler ist ein http.HandlerFunc, der von htmx getriggert wird, -// wenn der Benutzer Einträge in bestimmten Feldern macht, und dann die Skill-Felder befüllt. -func SkillCalculationHandler(content embed.FS) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - log.Print("SkillCalculationHandler called") - - // Überprüfen Sie, ob die Anfrage eine POST-Anfrage ist. - if r.Method != http.MethodPost { - log.Print("Method not allowed") - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - - // Parse Formulardaten. - err := r.ParseForm() - if err != nil { - log.Printf("Error parsing form data: %v", err) - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - tmplFiles := []string{"templates/base.html", "templates/header.html", "templates/skills.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.StatusBadRequest) - return - } - - str := parseFieldValue(r.FormValue("str")) - dex := parseFieldValue(r.FormValue("dex")) - int := parseFieldValue(r.FormValue("int")) - cha := parseFieldValue(r.FormValue("cha")) - wis := parseFieldValue(r.FormValue("wis")) - cr := parseFieldValue(r.FormValue("cr")) - crBonus := calcBonus(cr) - - skillValues := map[string]string{ - "acrobatics": strconv.Itoa(calcAbilityScore(dex) + crBonus), - "animalHandling": strconv.Itoa(calcAbilityScore(wis) + crBonus), - "arcana": strconv.Itoa(calcAbilityScore(int) + crBonus), - "athletics": strconv.Itoa(calcAbilityScore(str) + crBonus), - "deception": strconv.Itoa(calcAbilityScore(cha) + crBonus), - "history": strconv.Itoa(calcAbilityScore(int) + crBonus), - "insight": strconv.Itoa(calcAbilityScore(wis) + crBonus), - "intimidation": strconv.Itoa(calcAbilityScore(cha) + crBonus), - "investigation": strconv.Itoa(calcAbilityScore(int) + crBonus), - "medicine": strconv.Itoa(calcAbilityScore(wis) + crBonus), - "nature": strconv.Itoa(calcAbilityScore(int) + crBonus), - "perception": strconv.Itoa(calcAbilityScore(wis) + crBonus), - "performance": strconv.Itoa(calcAbilityScore(cha) + crBonus), - "persuasion": strconv.Itoa(calcAbilityScore(cha) + crBonus), - "religion": strconv.Itoa(calcAbilityScore(int) + crBonus), - "sleightOfHand": strconv.Itoa(calcAbilityScore(dex) + crBonus), - "stealth": strconv.Itoa(calcAbilityScore(dex) + crBonus), - "survival": strconv.Itoa(calcAbilityScore(wis) + crBonus), - } - - err = tmpl.ExecuteTemplate(w, "skills", skillValues) - if err != nil { - log.Printf("Template execution error: %v\n", err) - http.Error(w, err.Error(), http.StatusBadRequest) - } - } -} - -func calcBonus(cr int) int { - if cr >= 0 && cr < 5 { - return 2 - } else if cr >= 5 && cr < 9 { - return 3 - } else if cr >= 9 && cr < 14 { - return 4 - } else if cr >= 14 && cr < 18 { - return 5 - } else if cr >= 18 && cr < 21 { - return 6 - } else if cr >= 21 && cr < 25 { - return 7 - } else if cr >= 25 && cr < 28 { - return 8 - } else if cr >= 28 && cr < 31 { - return 9 - } else { - return 0 - } -} - -func calcAbilityScore(val int) int { - if val < 2 { - return -5 - } else if val >= 2 && val < 4 { - return -4 - } else if val >= 4 && val < 6 { - return -3 - } else if val >= 6 && val < 8 { - return -2 - } else if val >= 8 && val < 10 { - return -1 - } else if val >= 10 && val < 12 { - return 0 - } else if val >= 12 && val < 14 { - return 1 - } else if val >= 14 && val < 16 { - return 2 - } else if val >= 16 && val < 18 { - return 3 - } else if val >= 18 && val < 20 { - return 4 - } else if val >= 20 && val < 22 { - return 5 - } else if val >= 22 && val < 24 { - return 6 - } else if val >= 24 && val < 26 { - return 7 - } else if val >= 26 && val < 28 { - return 8 - } else if val >= 28 && val < 30 { - return 9 - } else { - return 10 - } -} - -func parseFieldValue(value string) int { - val, err := strconv.Atoi(value) - if err != nil { - log.Printf("Error converting field value to integer: %v", err) - return 0 - } - return val -} diff --git a/handlers/submit_handler.go b/handlers/submit_handler.go index 4a7b50e..02a6fde 100644 --- a/handlers/submit_handler.go +++ b/handlers/submit_handler.go @@ -13,59 +13,53 @@ import ( var mu sync.Mutex -// SubmitHandler processes the form data. +// submitHandler verarbeitet die Formulardaten 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 } - // Parse form data. + // Formulardaten parsen err := r.ParseForm() if err != nil { - log.Printf("Error parsing form data: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - // Create monster object. + // Monster-Objekt erstellen filename := r.FormValue("filename") - // Create or update character object. + // Charakter-Objekt erstellen oder aktualisieren mu.Lock() defer mu.Unlock() char := model.GetOrCreateCharacter(filename, *chars) char.Monster = append(char.Monster, *Monsters...) - // Convert character data to JSON. + // Charakterdaten in JSON umwandeln 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 } - // Write JSON data to file. + // JSON-Daten in die Datei schreiben err = model.WriteToFile(filename, charJSON) if err != nil { - log.Printf("Error writing JSON data to file: %v", err) http.Error(w, err.Error(), http.StatusInternalServerError) return } - - // Read file contents. + // Dateiinhalt lesen 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. + // Datei zum Download anbieten 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 d163e79..2060623 100644 --- a/main.go +++ b/main.go @@ -20,54 +20,40 @@ var ( Monsters []model.Monster ) -// main is the entry point of the program. func main() { filename := "" - - // Create a new ServeMux instance - routes := http.NewServeMux() - - // Register the handlers for different routes - routes.HandleFunc("/", handlers.FormHandler(content, &Monsters)) - routes.HandleFunc("/submit", handlers.SubmitHandler(content, &chars, &Monsters, filename)) - routes.Handle("/images/", http.StripPrefix("/images/", http.FileServer(http.FS(content)))) - routes.HandleFunc("/addMonster", handlers.AddMonster(&Monsters)) - routes.HandleFunc("/main", handlers.MainHandler(content, &Monsters)) - routes.HandleFunc("/about", handlers.AboutHandler(content)) - routes.HandleFunc("/contact", handlers.ContactHandler(content)) - routes.HandleFunc("/monsterTable", handlers.MonsterTableHandler(content, &Monsters)) - routes.HandleFunc("/calculate-skills", handlers.SkillCalculationHandler(content)) - // Print the message indicating that 'static' has been included. log.Printf("Eingebunden is %v\n", static) - // Load the CSS file. + 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)))) + http.HandleFunc("/addMonster", handlers.AddMonster(&Monsters)) + http.HandleFunc("/main", handlers.MainHandler(content, &Monsters)) + http.HandleFunc("/about", handlers.AboutHandler(content)) + http.HandleFunc("/contact", handlers.ContactHandler(content)) + http.HandleFunc("/monsterTable", handlers.MonsterTableHandler(content, &Monsters)) + + // Lade die CSS-Datei css, err := loadCSS(static) if err != nil { log.Fatal(err) } - // Add a route for the CSS file - routes.HandleFunc("/static/darkly_bulmawatch.css", func(w http.ResponseWriter, r *http.Request) { + // Füge eine Route für die CSS-Datei hinzu + 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") - - // Start the server and listen for incoming requests on port 8080. - log.Fatal(http.ListenAndServe(":8080", routes)) + http.ListenAndServe(":8080", nil) } -// 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. +// loadCSS liest die CSS-Datei aus dem eingebetteten Dateisystem. 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/main_test.go b/main_test.go deleted file mode 100644 index d3acb55..0000000 --- a/main_test.go +++ /dev/null @@ -1,176 +0,0 @@ -package main - -import ( - "ddServer/handlers" - "log" - "net/http" - "net/http/httptest" - "net/url" - "os" - "path/filepath" - "strings" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestMain(t *testing.T) { - // Test case 1: Check if the root route ("/") returns the expected response. - t.Run("Root Route", func(t *testing.T) { - req, _ := http.NewRequest("GET", "/", nil) - rr := httptest.NewRecorder() - handler := http.HandlerFunc(handlers.FormHandler(content, &Monsters)) - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) - }) - - // Test case 2: Check if the "/submit" route returns the expected response. - t.Run("Submit Route", func(t *testing.T) { - dir, err := filepath.Abs("test_data") - if err != nil { - log.Fatal(err) - } - - filename := filepath.Join(dir, "monster.json") - EnsureDirExists(dir) - - formData := url.Values{ - "filename": {filename}, - "name": {"Monster Name"}, - "source": {"Monster Source"}, - "size": {"Monster Size"}, - "type": {"Monster Type"}, - "alignment": {"Monster Alignment"}, - "ac": {"15"}, // Beispielwert für AC - "acFrom": {"Natural Armor"}, // Beispielwert für AC From - "hpAverage": {"30"}, // Beispielwert für HP Average - "hpFormula": {"2d10+5"}, // Beispielwert für HP Formula - "speed": {"30"}, // Beispielwert für Speed - "str": {"16"}, // Beispielwert für Str - "dex": {"14"}, // Beispielwert für Dex - "con": {"18"}, // Beispielwert für Con - "int": {"10"}, // Beispielwert für Int - "wis": {"12"}, // Beispielwert für Wis - "cha": {"8"}, // Beispielwert für Cha - "saveDex": {"+2"}, // Beispielwert für Save Dex - "saveCon": {"+4"}, // Beispielwert für Save Con - "saveWis": {"+1"}, // Beispielwert für Save Wis - "perception": {"+3"}, // Beispielwert für Perception - "stealth": {"+2"}, // Beispielwert für Stealth - "damageRes": {"Fire, Cold"}, // Beispielwert für Damage Resistances - "senses": {"Darkvision"}, // Beispielwert für Senses - "languages": {"Common"}, // Beispielwert für Languages - "cr": {"2"}, // Beispielwert für CR - "traitName": {"Trait Name"}, // Beispielwert für Trait Name - "traitEntry": {"Trait Entry"}, // Beispielwert für Trait Entry - "actionName": {"Action Name"}, // Beispielwert für Action Name - "actionEntry": {"Action Entry"}, // Beispielwert für Action Entry - } - - log.Println("Writing data to file:", filename) - - req, _ := http.NewRequest("POST", "/submit", strings.NewReader(formData.Encode())) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - rr := httptest.NewRecorder() - handler := http.HandlerFunc(handlers.SubmitHandler(content, &chars, &Monsters, filename)) - handler.ServeHTTP(rr, req) - - assert.Equal(t, http.StatusOK, rr.Code) - }) - - // Test case 3: Check if the "/images/" route returns the expected response. - t.Run("Images Route", func(t *testing.T) { - req, _ := http.NewRequest("GET", "/images/", nil) - rr := httptest.NewRecorder() - handler := http.StripPrefix("/images/", http.FileServer(http.FS(content))) - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) - }) - - // Test case 4: Check if the "/addMonster" route returns the expected response. - t.Run("AddMonster Route", func(t *testing.T) { - dir, err := filepath.Abs("test_data") - if err != nil { - log.Fatal(err) - } - - filename := filepath.Join(dir, "monster.json") - EnsureDirExists(dir) - formData := url.Values{ - "filename": {filename}, - "name": {"Monster Name"}, - "source": {"Monster Source"}, - "size": {"Monster Size"}, - "type": {"Monster Type"}, - "alignment": {"Monster Alignment"}, - "ac": {"15"}, // Beispielwert für AC - "acFrom": {"Natural Armor"}, // Beispielwert für AC From - "hpAverage": {"30"}, // Beispielwert für HP Average - "hpFormula": {"2d10+5"}, // Beispielwert für HP Formula - "speed": {"30"}, // Beispielwert für Speed - "str": {"16"}, // Beispielwert für Str - "dex": {"14"}, // Beispielwert für Dex - "con": {"18"}, // Beispielwert für Con - "int": {"10"}, // Beispielwert für Int - "wis": {"12"}, // Beispielwert für Wis - "cha": {"8"}, // Beispielwert für Cha - "saveDex": {"+2"}, // Beispielwert für Save Dex - "saveCon": {"+4"}, // Beispielwert für Save Con - "saveWis": {"+1"}, // Beispielwert für Save Wis - "perception": {"+3"}, // Beispielwert für Perception - "stealth": {"+2"}, // Beispielwert für Stealth - "damageRes": {"Fire, Cold"}, // Beispielwert für Damage Resistances - "senses": {"Darkvision"}, // Beispielwert für Senses - "languages": {"Common"}, // Beispielwert für Languages - "cr": {"2"}, // Beispielwert für CR - "traitName": {"Trait Name"}, // Beispielwert für Trait Name - "traitEntry": {"Trait Entry"}, // Beispielwert für Trait Entry - "actionName": {"Action Name"}, // Beispielwert für Action Name - "actionEntry": {"Action Entry"}, // Beispielwert für Action Entry - } - req, _ := http.NewRequest("POST", "/addMonster", strings.NewReader(formData.Encode())) - req.Header.Set("Content-Type", "application/x-www-form-urlencoded") - - rr := httptest.NewRecorder() - handler := http.HandlerFunc(handlers.SubmitHandler(content, &chars, &Monsters, filename)) - handler.ServeHTTP(rr, req) - - assert.Equal(t, http.StatusOK, rr.Code) - }) - - // Test case 5: Check if the "/main" route returns the expected response. - t.Run("Main Route", func(t *testing.T) { - req, _ := http.NewRequest("GET", "/main", nil) - rr := httptest.NewRecorder() - handler := http.HandlerFunc(handlers.MainHandler(content, &Monsters)) - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) - }) - - // Test case 6: Check if the "/about" route returns the expected response. - t.Run("About Route", func(t *testing.T) { - req, _ := http.NewRequest("GET", "/about", nil) - rr := httptest.NewRecorder() - handler := http.HandlerFunc(handlers.AboutHandler(content)) - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) - }) - - // Test case 7: Check if the "/contact" route returns the expected response. - t.Run("Contact Route", func(t *testing.T) { - req, _ := http.NewRequest("GET", "/contact", nil) - rr := httptest.NewRecorder() - handler := http.HandlerFunc(handlers.ContactHandler(content)) - handler.ServeHTTP(rr, req) - assert.Equal(t, http.StatusOK, rr.Code) - }) -} - -func EnsureDirExists(dir string) error { - err := os.MkdirAll(dir, os.ModePerm) - if err != nil { - log.Println("Error creating directory:", err) - } - return err -} diff --git a/model/model.go b/model/model.go index 0c8ec00..1ad1bb0 100644 --- a/model/model.go +++ b/model/model.go @@ -2,38 +2,34 @@ package model import ( "fmt" - "log" "os" "time" ) // Monster struct für die Daten des Monsters type Monster struct { - Save Save `json:"save"` - Skill Skill `json:"skill"` - HP HP `json:"hp"` - Source string `json:"source"` - CR string `json:"cr"` - Type string `json:"type"` - Name string `json:"name"` - Vulnerable []string `json:"vulnerable"` - ConditionImmune []string `json:"conditionImmune"` - Resist []string `json:"resist"` - Immune []string `json:"immune"` - Traits []Trait `json:"trait"` - AC []AC `json:"ac"` - Alignment []string `json:"alignment"` - Senses []string `json:"senses"` - Languages []string `json:"languages"` - Size []string `json:"size"` - Actions []Action `json:"action"` - Speed Speed `json:"speed"` - Str int `json:"str"` - Dex int `json:"dex"` - Con int `json:"con"` - Int int `json:"int"` - Wis int `json:"wis"` - Cha int `json:"cha"` + Save Save `json:"save"` + Skill Skill `json:"skill"` + HP HP `json:"hp"` + Source string `json:"source"` + CR string `json:"cr"` + Type string `json:"type"` + Name string `json:"name"` + DamageRes []string `json:"damageResistances"` + Traits []Trait `json:"trait"` + AC []AC `json:"ac"` + Alignment []string `json:"alignment"` + Senses []string `json:"senses"` + Languages []string `json:"languages"` + Size []string `json:"size"` + Actions []Action `json:"action"` + Speed Speed `json:"speed"` + Str int `json:"str"` + Dex int `json:"dex"` + Con int `json:"con"` + Int int `json:"int"` + Wis int `json:"wis"` + Cha int `json:"cha"` } type AC struct { @@ -47,41 +43,18 @@ type HP struct { } type Speed struct { - Walk int `json:"walk"` - Burrow int `json:"burrow"` - Climb int `json:"climb"` - Fly int `json:"fly"` - Swim int `json:"swim"` + Walk int `json:"walk"` } type Save struct { Dex string `json:"dex"` Con string `json:"con"` Wis string `json:"wis"` - Cha string `json:"cha"` - Str string `json:"str"` - Int string `json:"int"` } type Skill struct { - Stealth string `json:"stealth"` - Acrobatics string `json:"acrobatics"` - AnimalHandling string `json:"animalHandling"` - Arcana string `json:"arcana"` - Athletics string `json:"athletics"` - Deception string `json:"deception"` - History string `json:"history"` - Insight string `json:"insight"` - Intimidation string `json:"intimidation"` - Investigation string `json:"investigation"` - Medicine string `json:"medicine"` - Nature string `json:"nature"` - Perception string `json:"perception"` - Performance string `json:"performance"` - Persuasion string `json:"persuasion"` - SleightOfHand string `json:"sleightOfHand"` - Survival string `json:"survival"` - Religion string `json:"religion"` + Perception string `json:"perception"` + Stealth string `json:"stealth"` } type Trait struct { @@ -116,47 +89,32 @@ type Source struct { ConvertedBy []string `json:"convertedBy"` } -// 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. +// writeToFile schreibt Daten in eine Datei 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 func() { - if err := file.Close(); err != nil { - log.Println("Error closing file:", err) - } - }() + defer file.Close() - // Write the data to the file - n, err := file.Write(data) + _, 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 returns the current character object or creates a new one +// getOrCreateCharacter gibt das aktuelle Charakterobjekt zurück oder erstellt ein neues func GetOrCreateCharacter(filename string, chars []Character) Character { - // Check if there is an empty character object for _, char := range chars { if char.Meta.DateLastModified == 0 { - // Return the empty character object - log.Println("Returning existing character object") + // Ein leeres Charakterobjekt wurde gefunden return char } } - // Create a new character object + // Erstelle ein neues Charakterobjekt now := time.Now().Unix() newChar := Character{ Meta: Meta{ @@ -176,10 +134,7 @@ 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 6133b97..9d29c17 100644 --- a/templates/about.html +++ b/templates/about.html @@ -1,19 +1,15 @@ {{ 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 ce0085d..4b1dbea 100644 --- a/templates/contact.html +++ b/templates/contact.html @@ -13,6 +13,7 @@ diff --git a/templates/main.html b/templates/main.html index ed4581f..eb95065 100644 --- a/templates/main.html +++ b/templates/main.html @@ -1,109 +1,83 @@ {{ define "main" }}
-
-
-
-

Monster Form

+
+
+
+

Monster Form

+
+
+
+
+ +
-
- -
- -
-
-
-
-
- -
-
- {{ template "monsterform" . }} - - - +
+
+
+
-
+
+ {{ template "monsterform" . }} + + + +
+
-
-
-
-

Existing Monsters

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {{ template "monsterTable" }} -
NameSourceSizeTypeAlignmentACAC FormHP AverageHP FormulaWalkSwimBurrowClimbFlyStrDexConIntWisChaSave DexSave ConSave WisSave StrSave ConSave ChaPerceptionStealthAcrobaticsAnimalHandlingArcanaAthleticsDeceptionHistoryInsightIntimidationInvestigationMedicineNaturePerformancePersuasionSleightOfHandSurvivalReligionDamage ResistanceDamage ImmuneVulnerableCondition ImmuneSensesLanguagesCRTrait NameTrait EntryAction NameAction Entry
-
-
+
+
+
+

Existing Monsters

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ template "monsterTable" }} +
NameSourceSizeTypeAlignmentACAC FormHP AverageHP FormulaSpeedStrDexConIntWisChaSave DexSave ConSavve WisPerceptionStealthDamage ResistanceSensesLanguagesCRTrait NameTrait EntryAction NameAction Entry
+
+
{{ end }} diff --git a/templates/monster.html b/templates/monster.html index d3a59d6..2cd8b84 100644 --- a/templates/monster.html +++ b/templates/monster.html @@ -10,10 +10,6 @@ {{.HP.Average}} {{.HP.Formula}} {{.Speed.Walk}} - {{.Speed.Swim}} - {{.Speed.Burrow}} - {{.Speed.Climb}} - {{.Speed.Fly}} {{.Str}} {{.Dex}} {{.Con}} @@ -23,31 +19,9 @@ {{.Save.Dex}} {{.Save.Con}} {{.Save.Wis}} - {{.Save.Str}} - {{.Save.Con}} - {{.Save.Cha}} {{.Skill.Perception}} {{.Skill.Stealth}} - {{.Skill.Acrobatics}} - {{.Skill.AnimalHandling}} - {{.Skill.Arcana}} - {{.Skill.Athletics}} - {{.Skill.Deception}} - {{.Skill.History}} - {{.Skill.Insight}} - {{.Skill.Intimidation}} - {{.Skill.Investigation}} - {{.Skill.Medicine}} - {{.Skill.Nature}} - {{.Skill.Performance}} - {{.Skill.Persuasion}} - {{.Skill.SleightOfHand}} - {{.Skill.Survival}} - {{.Skill.Religion}} - {{range .Resist}}{{.}}{{end}} - {{range .Immune}}{{.}}{{end}} - {{range .Vulnerable}}{{.}}{{end}} - {{range .ConditionImmune}}{{.}}{{end}} + {{range .DamageRes}}{{.}}{{end}} {{range .Senses}}{{.}}{{end}} {{range .Languages}}{{.}}{{end}} {{.CR}} diff --git a/templates/monsterForm.html b/templates/monsterForm.html index ca69873..51b6be1 100644 --- a/templates/monsterForm.html +++ b/templates/monsterForm.html @@ -20,15 +20,7 @@
-
- -
+
@@ -87,57 +79,12 @@
-
-
-

- Speed -

-
-
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
+
+
+
+ +
+
@@ -147,8 +94,7 @@
- +
@@ -156,8 +102,7 @@
- +
@@ -165,18 +110,7 @@
- -
-
-
-
-
- -
- +
@@ -186,8 +120,7 @@
- +
@@ -195,8 +128,7 @@
- +
@@ -204,117 +136,63 @@
- +
-
+
+
+
- +
- +
-
-
-
-

- Save -

-
-
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
+
+
+ +
+ +
+
+
+
+
+ +
+
-
-
- {{ template "skills" }}
- +
- +
+
+
- +
- +
+
+
- +
- -
-
-
-
-
- -
- +
@@ -339,6 +217,16 @@
+
+
+
+ +
+ +
+
+
+
diff --git a/templates/skills.html b/templates/skills.html deleted file mode 100644 index 9322260..0000000 --- a/templates/skills.html +++ /dev/null @@ -1,185 +0,0 @@ -{{ define "skills" }} -
-

- Skill -

-
-
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
- -
- -
-
-
-
-
-
-{{ end }}