- Agent v2.1.2: WebSocket ShellService, ACK nach WS-Ankündigungen, shown_announcements Fix - Backend: shellServer.js mit WebSocket-Server (Shell + Announcement Push + RTC Signaling) - Backend: patch.controller.js Fix (command_id=0 Falsy-Bug beim Auto-Update) - Frontend: Remote Desktop Tab (WebRTC Beta) in AgentDetailPage für super_admin/admin - Frontend: PatchManagementPage auf v2.1.2 aktualisiert - WPF Notification: AllowsTransparency=False + kein DropShadowEffect (Dispatcher-Crash Fix) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
120 lines
4.0 KiB
C#
120 lines
4.0 KiB
C#
using ITNexusAgent.Models;
|
|
using Newtonsoft.Json;
|
|
using System.Diagnostics;
|
|
using System.Management;
|
|
using System.Text;
|
|
|
|
namespace ITNexusAgent.Services;
|
|
|
|
public class NotificationService
|
|
{
|
|
private readonly string _exePath;
|
|
private readonly string _dataDir;
|
|
private readonly string _shownIdsPath;
|
|
private readonly HashSet<int> _shownIds;
|
|
|
|
public NotificationService(string exePath, string dataDir)
|
|
{
|
|
_exePath = exePath;
|
|
_dataDir = dataDir;
|
|
_shownIdsPath = Path.Combine(dataDir, "shown_announcements.json");
|
|
_shownIds = LoadShownIds();
|
|
}
|
|
|
|
private HashSet<int> LoadShownIds()
|
|
{
|
|
try
|
|
{
|
|
if (File.Exists(_shownIdsPath))
|
|
{
|
|
var ids = JsonConvert.DeserializeObject<List<int>>(File.ReadAllText(_shownIdsPath));
|
|
return ids != null ? new HashSet<int>(ids) : [];
|
|
}
|
|
}
|
|
catch { }
|
|
return [];
|
|
}
|
|
|
|
public void RemoveShownId(int id)
|
|
{
|
|
if (_shownIds.Remove(id)) SaveShownIds();
|
|
}
|
|
|
|
private void SaveShownIds()
|
|
{
|
|
try { File.WriteAllText(_shownIdsPath, JsonConvert.SerializeObject(_shownIds.ToList())); }
|
|
catch { }
|
|
}
|
|
|
|
public void ShowAnnouncements(List<Announcement> announcements)
|
|
{
|
|
foreach (var ann in announcements)
|
|
{
|
|
if (_shownIds.Contains(ann.Id)) continue;
|
|
_shownIds.Add(ann.Id);
|
|
SaveShownIds();
|
|
ShowNotification(ann);
|
|
}
|
|
}
|
|
|
|
// Vollständigen eingeloggten User aus WMI holen (z.B. "WINKEL\gruessing" oder "AzureAD\gruessing")
|
|
public static string GetLoggedOnUser()
|
|
{
|
|
try
|
|
{
|
|
using var searcher = new ManagementObjectSearcher("SELECT UserName FROM Win32_ComputerSystem");
|
|
foreach (ManagementObject obj in searcher.Get())
|
|
{
|
|
var user = obj["UserName"]?.ToString();
|
|
if (!string.IsNullOrEmpty(user)) return user;
|
|
}
|
|
}
|
|
catch { }
|
|
return "";
|
|
}
|
|
|
|
private void ShowNotification(Announcement ann)
|
|
{
|
|
try
|
|
{
|
|
var fullUser = GetLoggedOnUser();
|
|
if (string.IsNullOrEmpty(fullUser)) return;
|
|
|
|
var json = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(ann)));
|
|
var taskName = $"ITNexus-Ann-{ann.Id}";
|
|
|
|
// Alten Task entfernen
|
|
Process.Start(new ProcessStartInfo("schtasks.exe", $"/delete /tn \"{taskName}\" /f")
|
|
{ CreateNoWindow = true })?.WaitForExit();
|
|
|
|
// Task mit Trigger weit in der Zukunft erstellen (damit er nicht abläuft vor /run)
|
|
var triggerTime = DateTime.Now.AddMinutes(60).ToString("HH:mm:ss");
|
|
// AzureAD-User (Entra/Hybrid-Join) können nicht per /ru aufgelöst werden → INTERACTIVE
|
|
var ruArg = fullUser.StartsWith("AzureAD\\", StringComparison.OrdinalIgnoreCase)
|
|
? "/ru \"INTERACTIVE\""
|
|
: $"/ru \"{fullUser}\"";
|
|
var args = $"/create /tn \"{taskName}\" /tr \"\\\"{_exePath}\\\" --notify {json}\" " +
|
|
$"/sc ONCE /st {triggerTime} {ruArg} /it /f";
|
|
var p = Process.Start(new ProcessStartInfo("schtasks.exe", args)
|
|
{ CreateNoWindow = true, RedirectStandardError = true, UseShellExecute = false });
|
|
p?.WaitForExit();
|
|
|
|
// Sofort ausführen
|
|
var run = Process.Start(new ProcessStartInfo("schtasks.exe", $"/run /tn \"{taskName}\"")
|
|
{ CreateNoWindow = true });
|
|
run?.WaitForExit();
|
|
|
|
// Task löschen damit der +60min Trigger nicht nochmal feuert
|
|
Thread.Sleep(3000);
|
|
Process.Start(new ProcessStartInfo("schtasks.exe", $"/delete /tn \"{taskName}\" /f")
|
|
{ CreateNoWindow = true })?.WaitForExit();
|
|
|
|
AgentWorker.Log($"NOTIFY: Task '{taskName}' für User '{fullUser}' gestartet (ExitCode={p?.ExitCode})");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
AgentWorker.Log($"NOTIFY ERROR: {ex.Message}");
|
|
}
|
|
}
|
|
}
|