From 3ab261d77723c696f7a88caacef79ebd4bb65da7 Mon Sep 17 00:00:00 2001 From: Florian Walther Date: Fri, 6 Feb 2026 12:34:10 +0100 Subject: [PATCH] added templates, more solid DarkMode --- Dockerfile | 7 +- main.go | 584 +++---------------------------------------- static/style.css | 171 +++++++++++++ templates/base.html | 48 ++++ templates/help.html | 35 +++ templates/index.html | 43 ++++ 6 files changed, 342 insertions(+), 546 deletions(-) create mode 100644 static/style.css create mode 100644 templates/base.html create mode 100644 templates/help.html create mode 100644 templates/index.html diff --git a/Dockerfile b/Dockerfile index c461d23..3418428 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,9 +16,14 @@ FROM scratch # Kopiere die gebaute Binärdatei COPY --from=builder /app/password-generator /password-generator +# Kopiere die templates +COPY templates/ /templates/ + +# Kopiere die static files +COPY static/ /static/ + # Setze die Umgebungsvariable für die Ports ENV PORT=8080 # Starte die Anwendung CMD ["/password-generator"] - diff --git a/main.go b/main.go index 0f11aa3..76ce59b 100644 --- a/main.go +++ b/main.go @@ -2,582 +2,76 @@ package main import ( "crypto/rand" - "fmt" + "html/template" "log" "net/http" ) const ( passwordLength = 32 - // Zeichensatz mit 62 möglichen Zeichen pro Position chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" - - // Zeichensatz mit 58 möglichen Zeichen pro Position - // Verwechslungsanfällige Zeichen (0, O, 1, l, I) sind nicht enthalten. - // - // ## Security Note: ################################################ - // Der reduzierte Zeichensatz setzt den Keyspace von 10^57 auf 10^56 herab. - // Die Entropie wird von ~192.6 Bit auf ~190.6 Bit herabgesetzt. - // Solange die Passwortlänge von 32 Zeichen beibehalten wird ist der - // Sicherheitsverlust durch einen reduzierten Zeichensatz akzeptabel, - // weil der Keyspace immer noch so groß ist dass ein erraten praktisch - // unmöglich ist. - // - //const chars = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789" - ) +//var tmpl *template.Template +var templates = make(map[string]*template.Template) + +func loadTemplates() { + templates["index.html"] = template.Must(template.ParseFiles("templates/base.html", "templates/index.html")) + templates["help.html"] = template.Must(template.ParseFiles("templates/base.html", "templates/help.html")) + log.Printf("Alle Templates erfolgreich geladen") +} + func generatePassword() string { password := make([]byte, passwordLength) _, err := rand.Read(password) if err != nil { log.Fatal(err) } - for i := 0; i < passwordLength; i++ { password[i] = chars[int(password[i])%len(chars)] } - return string(password) } func passwordHandler(w http.ResponseWriter, r *http.Request) { - log.Printf("APIHandler called from %s\n", r.RemoteAddr) password := generatePassword() - fmt.Fprint(w, password) + w.Header().Set("Content-Type", "text/plain") + w.Write([]byte(password)) +} + +func indexHandler(w http.ResponseWriter, r *http.Request) { + log.Printf("call indexHandler: Request %s %s\n", r.Method, r.URL) + password := generatePassword() + data := struct { + Password string + }{ + Password: password, + } + err := templates["index.html"].ExecuteTemplate(w, "base.html", data) + if err != nil { + log.Printf("Fehler beim Rendern des Templates: %v", err) + http.Error(w, "Interner Serverfehler", http.StatusInternalServerError) + } } -// new help handler func helpHandler(w http.ResponseWriter, r *http.Request) { - log.Printf("helpHandler called from %s\n", r.RemoteAddr) - helpHTML := ` - - - - Hilfe - - - - -
-

Hilfe: API-Endpunkt

-

- Diese Anwendung bietet einen API-Endpunkt, um Passwörter direkt über die Kommandozeile abzurufen. - Der Endpunkt gibt das Passwort im Plain-Text-Format zurück. -

-

Endpunkt:

-

http://localhost:8080/api/password

-

Beispiele:

-

Mac/Linux (Terminal):

-
echo $(curl -s http://localhost:8080/api/password)
-

Windows (PowerShell):

-
Invoke-RestMethod -Uri http://localhost:8080/api/password
-

Windows (cmd):

-
curl http://localhost:8080/api/password
-

- Zurück zur Passwort-Generierung -

-
- - - - - ` - w.Header().Set("Content-Type", "text/html; charset=utf-8") - fmt.Fprint(w, helpHTML) -} - - -func webHandler(w http.ResponseWriter, r *http.Request) { - log.Printf("webHandler called from %s\n", r.RemoteAddr) - password := generatePassword() -html := fmt.Sprintf( - ` - - - Passwort-Generator - - - - -
- API - - - code -

Generiertes Passwort

-
%s
-
- - -
-
✓ Kopiert!
-
- - - - - `, - password, -) - - w.Header().Set("Content-Type", "text/html; charset=utf-8") - fmt.Fprint(w, html) + log.Printf("call helpHandler\n") + err := templates["help.html"].ExecuteTemplate(w, "base.html", nil) + if err != nil { + log.Printf("Fehler beim Rendern des Templates: %v", err) + http.Error(w, "Interner Serverfehler", http.StatusInternalServerError) + } } func main() { - http.HandleFunc("/", webHandler) + loadTemplates() + fs := http.FileServer(http.Dir("static")) + http.Handle("/static/", http.StripPrefix("/static/", fs)) + + http.HandleFunc("/", indexHandler) http.HandleFunc("/api/password", passwordHandler) http.HandleFunc("/help", helpHandler) + log.Println("Server läuft auf http://localhost:8080") - log.Println("Plain-Text-Passwort: curl http://localhost:8080/api/password") log.Fatal(http.ListenAndServe(":8080", nil)) } diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..717ead8 --- /dev/null +++ b/static/style.css @@ -0,0 +1,171 @@ +:root { + --bg-color: #f5f5f5; + --text-color: #333; + --container-bg: white; + --button-bg: #007BFF; + --button-hover: #0056b3; + --copy-button-bg: #4CAF50; + --copy-button-hover: #45a049; + --password-bg: #f0f0f0; + --border-color: #ddd; + --shadow-color: rgba(0, 0, 0, 0.1); +} + +.dark { + --bg-color: #121212; + --text-color: #e0e0e0; + --container-bg: #1e1e1e; + --button-bg: #2a7df4; + --button-hover: #1a5fb4; + --copy-button-bg: #4caf60; + --copy-button-hover: #3d8b40; + --password-bg: #2d2d2d; + --border-color: #444; + --shadow-color: rgba(0, 0, 0, 0.3); +} + +body { + font-family: 'Helvetica Neue', Arial, sans-serif; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + margin: 0; + background-color: var(--bg-color); + color: var(--text-color); + transition: background-color 0.3s, color 0.3s; +} + +.container { + text-align: center; + background: var(--container-bg); + padding: 2rem; + border-radius: 8px; + box-shadow: 0 2px 10px var(--shadow-color); + width: 50%; + min-width: 600px; + position: relative; +} + +#password { + font-family: 'Courier New', Courier, monospace; + font-size: 1.2rem; + letter-spacing: 1px; + margin: 1rem auto; + padding: 0.8rem; + background: var(--password-bg); + border-radius: 4px; + border: 1px solid var(--border-color); + width: 90%; + word-break: break-all; + color: var(--text-color); +} + +.buttons { + display: flex; + justify-content: center; + gap: 0.5rem; +} + +.copy-button { + background: var(--copy-button-bg); + color: white; + border: none; + padding: 0.6rem 1.2rem; + font-size: 1rem; + border-radius: 4px; + cursor: pointer; + transition: background 0.2s; +} + +.copy-button:hover { + background: var(--copy-button-hover); +} + +.renew-button { + background: var(--button-bg); + color: white; + border: none; + padding: 0.6rem 1.2rem; + font-size: 1rem; + border-radius: 4px; + cursor: pointer; + transition: background 0.2s; +} + +.renew-button:hover { + background: var(--button-hover); +} + +.code-link { + position: absolute; + top: 1rem; + left: 1rem; + font-size: 1.2rem; + color: var(--text-color); + opacity: 0.7; + text-decoration: none; +} + +.code-link:hover { + opacity: 1; +} +.help-link { + position: absolute; + top: 1rem; + right: 1rem; + font-size: 1.2rem; + color: var(--text-color); + opacity: 0.7; + text-decoration: none; +} + +.help-link:hover { + opacity: 1; +} + +#toast { + visibility: hidden; + min-width: 150px; + background-color: var(--copy-button-bg); + color: white; + text-align: center; + border-radius: 4px; + padding: 0.5rem; + position: fixed; + top: 20px; + right: 20px; + z-index: 1; + font-size: 0.9rem; + box-shadow: 0 2px 10px var(--shadow-color); +} + +#theme-toggle { + position: absolute; + top: 1rem; + left: 1rem; + background: transparent; + border: none; + color: var(--text-color); + font-size: 1.2rem; + cursor: pointer; +} + +pre { + font-family: 'Courier New', Courier, monospace; + background: var(--password-bg); + padding: 0.8rem; + border-radius: 4px; + color: var(--text-color); + border: 1px solid var(--border-color); +} + +a { + color: var(--button-bg); + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..920a6a4 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,48 @@ + + + + + + {{ block "title" . }}Passwort-Generator{{ end }} + + + {{ block "head" . }}{{ end }} + + + + {{ block "body" . }}{{end}} + + diff --git a/templates/help.html b/templates/help.html new file mode 100644 index 0000000..eb08eb6 --- /dev/null +++ b/templates/help.html @@ -0,0 +1,35 @@ +{{ define "help.html" }} + +{{ block "title" . }}Hilfe{{ end }} + +{{ block "head" . }} + +{{ end }} + +{{ block "body" . }} +
+

Hilfe: API-Endpunkt

+

Diese Anwendung bietet einen API-Endpunkt, um Passwörter direkt über die Kommandozeile abzurufen.

+

Endpunkt:

+

+

Beispiele:

+

Mac/Linux (Terminal):

+

+    

Windows (PowerShell):

+

+    

Windows (cmd):

+

+    

Zurück zur Passwort-Generierung

+
+{{ end }} +{{ end }} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..3d756ed --- /dev/null +++ b/templates/index.html @@ -0,0 +1,43 @@ +{{ define "index.html" }} + +{{ block "title" . }}Passwort-Generator{{ end }} + +{{ block "head" . }} + +{{ end }} + +{{ block "body" . }} +
+ ? + Sourcecode +

Generiertes Passwort

+
{{ .Password }}
+
+ + +
+
✓ Kopiert!
+
+{{ end }} +{{ end }}