init commit for ddServer
First commit with basic functionality for create a json-file from Input in the html form.
This commit is contained in:
commit
c251b553a1
3 changed files with 384 additions and 0 deletions
104
forms.html
Normal file
104
forms.html
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>D&D Monster Form</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/daisyui@2.31.0/dist/full.css" rel="stylesheet" type="text/css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
font-family: 'Arial', sans-serif;
|
||||||
|
transition: background-color 0.3s, color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-box {
|
||||||
|
max-width: 700px;
|
||||||
|
width: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-100 h-screen flex items-center justify-center">
|
||||||
|
|
||||||
|
<div data-theme="dark" class="bg-white p-6 rounded-lg shadow-md w-full max-w-lg">
|
||||||
|
<div class="navbar bg-base-100">
|
||||||
|
<a class="btn btn-ghost text-xl">Dungeons & Dragons</a>
|
||||||
|
</div>
|
||||||
|
<div class="container mx-auto card w-96 bg-base-100 shadow-xl">
|
||||||
|
<div class="card-body form-box">
|
||||||
|
<h2 class="card-title">Monster Form</h2>
|
||||||
|
<form action="/submit" method="post" class="grid grid-columns-2 gap-4 space-y-4 grid h-screen place-items-center">
|
||||||
|
<label for="filename">Filename:</label>
|
||||||
|
<input type="text" name="filename" required placeholder="Dateiname" class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="name">Monster Name:</label>
|
||||||
|
<input type="text" name="name" required placeholder="Type here" class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="source">Monster Source:</label>
|
||||||
|
<input type="text" name="source" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="size">Size:</label>
|
||||||
|
<input type="text" name="size" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="type">Type:</label>
|
||||||
|
<input type="text" name="type" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="alignment">Alignment:</label>
|
||||||
|
<input type="text" name="alignment" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="ac">AC:</label>
|
||||||
|
<input type="number" name="ac" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="acFrom">AC From:</label>
|
||||||
|
<input type="text" name="acFrom" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="hpAverage">HP Average:</label>
|
||||||
|
<input type="number" name="hpAverage" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="hpFormula">HP Formula:</label>
|
||||||
|
<input type="text" name="hpFormula" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="speed">Speed:</label>
|
||||||
|
<input type="number" name="speed" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="str">Str:</label>
|
||||||
|
<input type="number" name="str" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="dex">Dex:</label>
|
||||||
|
<input type="number" name="dex" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="con">Con:</label>
|
||||||
|
<input type="number" name="con" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="int">Int:</label>
|
||||||
|
<input type="number" name="int" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="wis">Wis:</label>
|
||||||
|
<input type="number" name="wis" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="cha">Cha:</label>
|
||||||
|
<input type="number" name="cha" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="saveDex">Save Dex:</label>
|
||||||
|
<input type="text" name="saveDex" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="saveCon">Save Con:</label>
|
||||||
|
<input type="text" name="saveCon" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="saveWis">Save Wis:</label>
|
||||||
|
<input type="text" name="saveWis" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="perception">Perception:</label>
|
||||||
|
<input type="text" name="perception" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="stealth">Stealth:</label>
|
||||||
|
<input type="text" name="stealth" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="damageRes">Damage Resistances:</label>
|
||||||
|
<input type="text" name="damageRes" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="senses">Senses:</label>
|
||||||
|
<input type="text" name="senses" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="languages">Languages:</label>
|
||||||
|
<input type="text" name="languages" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="cr">CR:</label>
|
||||||
|
<input type="text" name="cr" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="traitName">Trait Name:</label>
|
||||||
|
<input type="text" name="traitName" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="traitEntry">Trait Entry:</label>
|
||||||
|
<input type="text" name="traitEntry" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="actionName">Action Name:</label>
|
||||||
|
<input type="text" name="actionName" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<label for="actionEntry">Action Entry:</label>
|
||||||
|
<input type="text" name="actionEntry" required class="input input-bordered w-full max-w-xs"><br>
|
||||||
|
<input type="submit" value="Submit" class="btn">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
3
go.mod
Normal file
3
go.mod
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
module ddServer
|
||||||
|
|
||||||
|
go 1.21.4
|
||||||
277
main.go
Normal file
277
main.go
Normal file
|
|
@ -0,0 +1,277 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Monster struct für die Daten des Monsters
|
||||||
|
type Monster struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Source string `json:"source"`
|
||||||
|
Size []string `json:"size"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Alignment []string `json:"alignment"`
|
||||||
|
AC []AC `json:"ac"`
|
||||||
|
HP HP `json:"hp"`
|
||||||
|
Speed Speed `json:"speed"`
|
||||||
|
Save Save `json:"save"`
|
||||||
|
Skill Skill `json:"skill"`
|
||||||
|
DamageRes []string `json:"damageResistances"`
|
||||||
|
Senses []string `json:"senses"`
|
||||||
|
Languages []string `json:"languages"`
|
||||||
|
CR string `json:"cr"`
|
||||||
|
Traits []Trait `json:"trait"`
|
||||||
|
Actions []Action `json:"action"`
|
||||||
|
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 {
|
||||||
|
AC int `json:"ac"`
|
||||||
|
From []string `json:"from"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HP struct {
|
||||||
|
Average int `json:"average"`
|
||||||
|
Formula string `json:"formula"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Speed struct {
|
||||||
|
Walk int `json:"walk"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Save struct {
|
||||||
|
Dex string `json:"dex"`
|
||||||
|
Con string `json:"con"`
|
||||||
|
Wis string `json:"wis"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Skill struct {
|
||||||
|
Perception string `json:"perception"`
|
||||||
|
Stealth string `json:"stealth"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Trait struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Entries []string `json:"entries"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Action struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Entries []string `json:"entries"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Character struct für die Daten des Charakters
|
||||||
|
type Character struct {
|
||||||
|
Meta Meta `json:"_meta"`
|
||||||
|
Monster []Monster `json:"monster"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Meta struct für Meta-Informationen
|
||||||
|
type Meta struct {
|
||||||
|
Sources []Source `json:"sources"`
|
||||||
|
DateAdded int64 `json:"dateAdded"`
|
||||||
|
DateLastModified int64 `json:"dateLastModified"`
|
||||||
|
DateLastModifiedHash string `json:"_dateLastModifiedHash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Source struct {
|
||||||
|
Json string `json:"json"`
|
||||||
|
Abbreviation string `json:"abbreviation"`
|
||||||
|
Authors []string `json:"authors"`
|
||||||
|
ConvertedBy []string `json:"convertedBy"`
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
mu sync.Mutex
|
||||||
|
chars []Character
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
filename := ""
|
||||||
|
|
||||||
|
http.HandleFunc("/", formHandler(filename))
|
||||||
|
http.HandleFunc("/submit", submitHandler(filename))
|
||||||
|
|
||||||
|
fmt.Println("Server gestartet, erreichbar unter http://localhost:8080")
|
||||||
|
http.ListenAndServe(":8080", nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// formHandler zeigt das Formular an
|
||||||
|
func formHandler(filename string) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
tmpl, err := template.ParseFiles("forms.html")
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl.Execute(w, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// submitHandler verarbeitet die Formulardaten
|
||||||
|
func submitHandler(filename string) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.Method != http.MethodPost {
|
||||||
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Formulardaten parsen
|
||||||
|
err := r.ParseForm()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Monster-Objekt erstellen
|
||||||
|
filename := r.FormValue("filename")
|
||||||
|
monster := Monster{
|
||||||
|
Name: r.FormValue("name"),
|
||||||
|
Source: r.FormValue("source"),
|
||||||
|
Size: []string{r.FormValue("size")},
|
||||||
|
Type: r.FormValue("type"),
|
||||||
|
Alignment: []string{r.FormValue("alignment")},
|
||||||
|
AC: []AC{
|
||||||
|
{
|
||||||
|
AC: parseInt(r.FormValue("ac")),
|
||||||
|
From: []string{r.FormValue("acFrom")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
HP: HP{
|
||||||
|
Average: parseInt(r.FormValue("hpAverage")),
|
||||||
|
Formula: r.FormValue("hpFormula"),
|
||||||
|
},
|
||||||
|
Speed: 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: Save{
|
||||||
|
Dex: r.FormValue("saveDex"),
|
||||||
|
Con: r.FormValue("saveCon"),
|
||||||
|
Wis: r.FormValue("saveWis"),
|
||||||
|
},
|
||||||
|
Skill: 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: []Trait{
|
||||||
|
{
|
||||||
|
Name: r.FormValue("traitName"),
|
||||||
|
Entries: []string{r.FormValue("traitEntry")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Actions: []Action{
|
||||||
|
{
|
||||||
|
Name: r.FormValue("actionName"),
|
||||||
|
Entries: []string{r.FormValue("actionEntry")},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Charakter-Objekt erstellen oder aktualisieren
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
|
||||||
|
char := getOrCreateCharacter(filename)
|
||||||
|
char.Monster = append(char.Monster, monster)
|
||||||
|
|
||||||
|
// Charakterdaten in JSON umwandeln
|
||||||
|
charJSON, err := json.Marshal(char)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON-Daten in die Datei schreiben
|
||||||
|
err = writeToFile(filename, charJSON)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erfolgsmeldung anzeigen
|
||||||
|
fmt.Fprintf(w, "Monsterdaten erfolgreich gespeichert in %s: %s", filename, charJSON)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeToFile schreibt Daten in eine Datei
|
||||||
|
func writeToFile(filename string, data []byte) error {
|
||||||
|
file, err := os.Create(filename)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
_, err = file.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOrCreateCharacter gibt das aktuelle Charakterobjekt zurück oder erstellt ein neues
|
||||||
|
func getOrCreateCharacter(filename string) Character {
|
||||||
|
for _, char := range chars {
|
||||||
|
if char.Meta.DateLastModified == 0 {
|
||||||
|
// Ein leeres Charakterobjekt wurde gefunden
|
||||||
|
return char
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erstelle ein neues Charakterobjekt
|
||||||
|
now := time.Now().Unix()
|
||||||
|
newChar := Character{
|
||||||
|
Meta: Meta{
|
||||||
|
Sources: []Source{
|
||||||
|
{
|
||||||
|
Json: "Malgorgon",
|
||||||
|
Abbreviation: "MG",
|
||||||
|
Authors: []string{"Krzysztof"},
|
||||||
|
ConvertedBy: []string{"Krzysztof"},
|
||||||
|
Version: "unknown",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DateAdded: now,
|
||||||
|
DateLastModified: now,
|
||||||
|
DateLastModifiedHash: fmt.Sprintf("%x", now),
|
||||||
|
},
|
||||||
|
Monster: []Monster{},
|
||||||
|
}
|
||||||
|
|
||||||
|
chars = append(chars, newChar)
|
||||||
|
|
||||||
|
return newChar
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseInt konvertiert einen String zu einem Integer und gibt 0 zurück, wenn die Konvertierung fehlschlägt
|
||||||
|
func parseInt(s string) int {
|
||||||
|
i, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return i
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue