From 9d07197d06fbf251e6119d561ae6057e01fc7298 Mon Sep 17 00:00:00 2001 From: jbergner Date: Thu, 18 Dec 2025 07:15:24 +0100 Subject: [PATCH] init --- go.mod | 3 + main.go | 350 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 353 insertions(+) create mode 100644 go.mod create mode 100644 main.go diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..b44db17 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module git.send.nrw/sendnrw/pcinfosender + +go 1.25.3 diff --git a/main.go b/main.go new file mode 100644 index 0000000..878e3bc --- /dev/null +++ b/main.go @@ -0,0 +1,350 @@ +//go:build windows + +package main + +import ( + "bytes" + "encoding/json" + "html/template" + "log" + "net/http" + "os" + "strconv" + "strings" + "time" +) + +type NotifyRequest struct { + Title string `json:"title"` + Message string `json:"message"` + TargetUser string `json:"target_user"` +} + +type PageData struct { + ClientHost string + Port string + Title string + Message string + TargetUser string + + Status string + Error string +} + +var ( + tpl *template.Template + notifyToken string + defaultPort = "24000" + listenAddr = ":8088" + defaultTitle = "Benachrichtigung" +) + +func main() { + notifyToken = os.Getenv("NOTIFY_TOKEN") + + if v := os.Getenv("SENDER_LISTEN_ADDR"); v != "" { + listenAddr = v + } + if v := os.Getenv("DEFAULT_PORT"); v != "" { + defaultPort = v + } + + var err error + tpl, err = template.New("index").Parse(pageHTML) + if err != nil { + log.Fatalf("template parse error: %v", err) + } + + http.HandleFunc("/", indexHandler) + + log.Printf("Notification-Sender Web UI läuft auf http://localhost%s/", listenAddr) + log.Fatalf("ListenAndServe: %v", http.ListenAndServe(listenAddr, nil)) +} + +func indexHandler(w http.ResponseWriter, r *http.Request) { + switch r.Method { + case http.MethodGet: + render(w, PageData{ + Port: defaultPort, + }) + case http.MethodPost: + if err := r.ParseForm(); err != nil { + render(w, PageData{ + Error: "Formular konnte nicht gelesen werden: " + err.Error(), + Port: defaultPort, + }) + return + } + + data := PageData{ + ClientHost: strings.TrimSpace(r.FormValue("client_host")), + Port: strings.TrimSpace(r.FormValue("port")), + Title: strings.TrimSpace(r.FormValue("title")), + Message: strings.TrimSpace(r.FormValue("message")), + TargetUser: strings.TrimSpace(r.FormValue("target_user")), + } + + if data.Port == "" { + data.Port = defaultPort + } + + if data.ClientHost == "" { + data.Error = "Client-Host/IP darf nicht leer sein." + render(w, data) + return + } + if data.Message == "" { + data.Error = "Nachricht darf nicht leer sein." + render(w, data) + return + } + if data.Title == "" { + data.Title = defaultTitle + } + + if err := sendNotification(data); err != nil { + data.Error = "Fehler beim Senden: " + err.Error() + } else { + data.Status = "Benachrichtigung wurde gesendet." + } + + render(w, data) + default: + http.Error(w, "method not allowed", http.StatusMethodNotAllowed) + } +} + +func render(w http.ResponseWriter, data PageData) { + w.Header().Set("Content-Type", "text/html; charset=utf-8") + if err := tpl.Execute(w, data); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } +} + +func sendNotification(d PageData) error { + // Port validieren (nur zur Sicherheit, falls jemand Unsinn einträgt) + if _, err := strconv.Atoi(d.Port); err != nil { + return err + } + + // URL bauen: + // Wenn ClientHost schon ein Schema enthält (http://...), nutzen wir das, + // sonst "http://:/api/notify" + var url string + if strings.Contains(d.ClientHost, "://") { + url = strings.TrimRight(d.ClientHost, "/") + "/api/notify" + } else { + url = "http://" + d.ClientHost + ":" + d.Port + "/api/notify" + } + + bodyStruct := NotifyRequest{ + Title: d.Title, + Message: d.Message, + TargetUser: d.TargetUser, + } + body, err := json.Marshal(bodyStruct) + if err != nil { + return err + } + + req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader(body)) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + if notifyToken != "" { + req.Header.Set("X-Notify-Token", notifyToken) + } + + client := &http.Client{ + Timeout: 5 * time.Second, + } + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode >= 300 { + return &httpError{Status: resp.Status} + } + return nil +} + +type httpError struct { + Status string +} + +func (e *httpError) Error() string { + return "HTTP-Status vom Client: " + e.Status +} + +const pageHTML = ` + + + +Notification Sender + + + + +
+

Notification Sender

+

Sende Benachrichtigungen an deine GoSysInfo-Clients (POST /api/notify).

+
+
+
+
+ + +
+
+ + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + + + {{if .Status}} +
{{.Status}}
+ {{end}} + {{if .Error}} +
{{.Error}}
+ {{end}} +
+
+ Hinweis: Dieses Tool sendet JSON an http://<Client>:<Port>/api/notify und nutzt optional den Header X-Notify-Token, wenn die Umgebungsvariable NOTIFY_TOKEN gesetzt ist. + UI-Service +
+
+
+ +`