generated from sendnrw/template_golang
update mit Notification
This commit is contained in:
156
main.go
156
main.go
@@ -14,6 +14,7 @@ import (
|
||||
"os/user"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -82,20 +83,158 @@ type InstalledApp struct {
|
||||
}
|
||||
|
||||
type DeviceInfo struct {
|
||||
InstanceID string `json:"instance_id"`
|
||||
Class string `json:"class"`
|
||||
ClassGUID string `json:"class_guid"`
|
||||
FriendlyName string `json:"friendly_name"`
|
||||
Manufacturer string `json:"manufacturer"`
|
||||
Status string `json:"status"`
|
||||
Present bool `json:"present"`
|
||||
InstanceID string `json:"instance_id"`
|
||||
Class string `json:"class"`
|
||||
ClassGUID string `json:"class_guid"`
|
||||
FriendlyName string `json:"friendly_name"`
|
||||
Manufacturer string `json:"manufacturer"`
|
||||
Status string `json:"status"`
|
||||
Present bool `json:"present"`
|
||||
}
|
||||
|
||||
type Notification struct {
|
||||
ID int64 `json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
Title string `json:"title"`
|
||||
Message string `json:"message"`
|
||||
TargetUser string `json:"target_user"` // "" = Broadcast an alle User
|
||||
}
|
||||
|
||||
var (
|
||||
notifyMu sync.Mutex
|
||||
notifySeq int64
|
||||
notifyRing []Notification
|
||||
)
|
||||
|
||||
// maximale Anzahl vorgehaltener Nachrichten (FIFO-Ring)
|
||||
const maxNotifications = 1000
|
||||
|
||||
var (
|
||||
mu sync.RWMutex
|
||||
lastCPUUsage float64
|
||||
)
|
||||
|
||||
func addNotification(n Notification) Notification {
|
||||
notifyMu.Lock()
|
||||
defer notifyMu.Unlock()
|
||||
|
||||
notifySeq++
|
||||
n.ID = notifySeq
|
||||
if n.CreatedAt.IsZero() {
|
||||
n.CreatedAt = time.Now()
|
||||
}
|
||||
|
||||
notifyRing = append(notifyRing, n)
|
||||
if len(notifyRing) > maxNotifications {
|
||||
// älteste abschneiden
|
||||
notifyRing = notifyRing[len(notifyRing)-maxNotifications:]
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// userName: z.B. "DOMAIN\\user" oder "user"
|
||||
// sinceID: letzte gesehene ID des Agents
|
||||
func getNotificationsForUser(userName string, sinceID int64) []Notification {
|
||||
notifyMu.Lock()
|
||||
defer notifyMu.Unlock()
|
||||
|
||||
userNameLower := strings.ToLower(strings.TrimSpace(userName))
|
||||
var res []Notification
|
||||
for _, n := range notifyRing {
|
||||
if n.ID <= sinceID {
|
||||
continue
|
||||
}
|
||||
// Broadcast
|
||||
if strings.TrimSpace(n.TargetUser) == "" {
|
||||
res = append(res, n)
|
||||
continue
|
||||
}
|
||||
// Ziel-User matchen (case-insensitive)
|
||||
if strings.EqualFold(n.TargetUser, userName) ||
|
||||
strings.EqualFold(n.TargetUser, userNameLower) {
|
||||
res = append(res, n)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type NotifyRequest struct {
|
||||
Title string `json:"title"`
|
||||
Message string `json:"message"`
|
||||
TargetUser string `json:"target_user"` // optional, "" = an alle
|
||||
}
|
||||
|
||||
func notifyFromServerHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
// optional: Authentifizierung per Token
|
||||
token := os.Getenv("NOTIFY_TOKEN")
|
||||
if token != "" && r.Header.Get("X-Notify-Token") != token {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
var req NotifyRequest
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "bad request: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
req.Title = strings.TrimSpace(req.Title)
|
||||
req.Message = strings.TrimSpace(req.Message)
|
||||
req.TargetUser = strings.TrimSpace(req.TargetUser)
|
||||
|
||||
if req.Message == "" {
|
||||
http.Error(w, "message must not be empty", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if req.Title == "" {
|
||||
req.Title = "Benachrichtigung"
|
||||
}
|
||||
|
||||
n := addNotification(Notification{
|
||||
Title: req.Title,
|
||||
Message: req.Message,
|
||||
TargetUser: req.TargetUser,
|
||||
})
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(n)
|
||||
}
|
||||
|
||||
func notificationsForAgentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
userName := strings.TrimSpace(r.URL.Query().Get("user"))
|
||||
if userName == "" {
|
||||
http.Error(w, "missing 'user' query parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
sinceStr := r.URL.Query().Get("since_id")
|
||||
var sinceID int64
|
||||
if sinceStr != "" {
|
||||
if v, err := strconv.ParseInt(sinceStr, 10, 64); err == nil {
|
||||
sinceID = v
|
||||
}
|
||||
}
|
||||
|
||||
// optional: Zugriff auf localhost beschränken
|
||||
host, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||
if host != "127.0.0.1" && host != "::1" {
|
||||
http.Error(w, "forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
notifs := getNotificationsForUser(userName, sinceID)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(notifs)
|
||||
}
|
||||
|
||||
func getPnpDevices() ([]DeviceInfo, error) {
|
||||
// PowerShell: aktuelle PnP-Geräte, inkl. ClassGuid
|
||||
cmd := exec.Command(
|
||||
@@ -781,6 +920,9 @@ func runHTTP(ctx context.Context) error {
|
||||
mux.HandleFunc("/api/summary", apiHandler)
|
||||
mux.HandleFunc("/api/devices", devicesHandler)
|
||||
|
||||
mux.HandleFunc("/api/notify", notifyFromServerHandler)
|
||||
mux.HandleFunc("/api/notifications", notificationsForAgentHandler)
|
||||
|
||||
addr := ":24000"
|
||||
|
||||
srv := &http.Server{
|
||||
|
||||
Reference in New Issue
Block a user