diff --git a/Protest/Front/issues.js b/Protest/Front/issues.js index a53f46f9..2885974b 100644 --- a/Protest/Front/issues.js +++ b/Protest/Front/issues.js @@ -16,23 +16,32 @@ class Issues extends List { static SEVERITY_COLOR = { 1 : "var(--clr-dark)", - 2 : "var(--clr-warning)", + 2 : "rgb(240,140,8)", 3 : "var(--clr-error)", 4 : "var(--clr-critical)", }; + static CATEGORY_ICON = { + "Password" : "url(mono/lock.svg)", + "Printer component" : "url(mono/printer.svg)", + "CPU usage" : "url(mono/cpu.svg)", + "Ram usage" : "url(mono/ram.svg)", + "Disk capacity" : "url(mono/ssd.svg)", + "Disk IO" : "url(mono/hdd.svg)", + }; + constructor() { super(); this.AddCssDependencies("list.css"); - const columns = ["issue", "target", "category", "severity", "source"]; + const columns = ["severity", "issue", "target", "category", "source"]; this.SetupColumns(columns); - this.columnsElements[0].style.width = "40%"; + this.columnsElements[0].style.width = "10%"; - this.columnsElements[1].style.left = "40%"; - this.columnsElements[1].style.width = "15%"; + this.columnsElements[1].style.left = "10%"; + this.columnsElements[1].style.width = "45%"; this.columnsElements[2].style.left = "55%"; this.columnsElements[2].style.width = "15%"; @@ -129,6 +138,15 @@ class Issues extends List { this.InflateElement(element, this.link.data[key]); this.link.length++; + + if (this.link) { + this.counter.textContent = this.list.childNodes.length === this.link.length + ? this.link.length + : `${this.list.childNodes.length} / ${this.link.length}`; + } + else { + this.counter.textContent = "0"; + } } ScanDialog(entry=null, isRunning=false) { @@ -165,6 +183,8 @@ class Issues extends List { dialog.Close(); }; + + setTimeout(()=> okButton.focus(), 200); } InflateElement(element, entry) { //overrides @@ -175,7 +195,7 @@ class Issues extends List { icon.style.maskRepeat = "no-repeat"; icon.style.maskImage = Issues.SEVERITY_ICON[entry.severity.v] ?? "url(mono/critical.svg)"; icon.style.backgroundColor = Issues.SEVERITY_COLOR[entry.severity.v] ?? "var(--clr-dark)"; - icon.style.filter = "brightness(0.8)"; + icon.style.filter = "brightness(0.85)"; element.appendChild(icon); for (let i = 0; i < this.columnsElements.length; i++) { @@ -183,7 +203,6 @@ class Issues extends List { const propertyName = this.columnsElements[i].textContent; let value; - if (propertyName === "severity") { value = Issues.SEVERITY_TEXT[entry[this.columnsElements[i].textContent].v]; icon.style.left = this.columnsElements[i].style.left; @@ -202,6 +221,16 @@ class Issues extends List { newAttr.style.left = `calc(28px + ${this.columnsElements[i].style.left})`; newAttr.style.width = `calc(${this.columnsElements[i].style.width} - 28px)`; } + else if (propertyName === "category") { + newAttr.style.left = this.columnsElements[i].style.left; + newAttr.style.width = this.columnsElements[i].style.width; + + newAttr.style.paddingLeft = "24px"; + newAttr.style.backgroundImage = Issues.CATEGORY_ICON[value] ?? "none"; + newAttr.style.backgroundSize = "20px 20px"; + newAttr.style.backgroundPosition = "0px 50%"; + newAttr.style.backgroundRepeat = "no-repeat"; + } else { newAttr.style.left = this.columnsElements[i].style.left; newAttr.style.width = this.columnsElements[i].style.width; diff --git a/Protest/Front/list.js b/Protest/Front/list.js index 6e435c40..f4bee8b9 100644 --- a/Protest/Front/list.js +++ b/Protest/Front/list.js @@ -580,9 +580,9 @@ class List extends Window { } if (this.link) { - this.counter.textContent = this.list.childNodes.length === this.link.length ? - this.link.length : - `${this.list.childNodes.length} / ${this.link.length}`; + this.counter.textContent = this.list.childNodes.length === this.link.length + ? this.link.length + : `${this.list.childNodes.length} / ${this.link.length}`; } else { this.counter.textContent = "0"; diff --git a/Protest/Front/root.css b/Protest/Front/root.css index 6c3f497a..f9e09d48 100644 --- a/Protest/Front/root.css +++ b/Protest/Front/root.css @@ -32,7 +32,7 @@ --clr-select: rgb(232,118,0); --clr-transparent: rgba(255,102,0,.6); --clr-critical: rgb(240,16,16); - --clr-error: rgb(240,64,24); + --clr-error: rgb(240,80,24); --clr-orange: rgb(232,118,0); --clr-warning: rgb(255,186,0); --clr-light: rgb(224,224,224); diff --git a/Protest/Protocols/Snmp.Polling.cs b/Protest/Protocols/Snmp.Polling.cs index 532b6456..0021c94e 100644 --- a/Protest/Protocols/Snmp.Polling.cs +++ b/Protest/Protocols/Snmp.Polling.cs @@ -337,6 +337,10 @@ public static (IList, SnmpProfiles.Profile) SnmpQueryTrialAndError(IPA } public static IList SnmpQuery(IPAddress target, SnmpProfiles.Profile profile, string[] oids, SnmpOperation operation) { + if (profile is null) { + return null; + } + if (profile.version == 3) { try { IList result = result = Protocols.Snmp.Polling.SnmpRequestV3( diff --git a/Protest/Tools/LiveStats.cs b/Protest/Tools/LiveStats.cs index bb4ee6eb..a1b0ca08 100644 --- a/Protest/Tools/LiveStats.cs +++ b/Protest/Tools/LiveStats.cs @@ -202,7 +202,7 @@ public static async void DeviceStats(HttpListenerContext ctx) { && firstReply.Status == IPStatus.Success && entry.attributes.TryGetValue("type", out Database.Attribute _type) && entry.attributes.TryGetValue("snmp profile", out Database.Attribute _snmpProfile)) { - SnmpQuery(ws, mutex, firstAlive, _type?.value.ToLower(), _snmpProfile.value); + SnmpQuery(ws, mutex, entry.filename, firstAlive, _type?.value.ToLower(), _snmpProfile.value); } if (OperatingSystem.IsWindows() && _hostname?.value?.Length > 0) { @@ -380,8 +380,7 @@ private static void WmiQuery(WebSocket ws, object mutex, string firstAlive, ref catch { } } - private static void SnmpQuery(WebSocket ws, object mutex, string firstAlive, string type, string snmpProfileGuid) { - + private static void SnmpQuery(WebSocket ws, object mutex, string file, string firstAlive, string type, string snmpProfileGuid) { if (!SnmpProfiles.FromGuid(snmpProfileGuid, out SnmpProfiles.Profile profile)) { return; } @@ -424,7 +423,7 @@ private static void SnmpQuery(WebSocket ws, object mutex, string firstAlive, str WsWriteText(ws, $"{{\"info\":\"Total jobs: {Data.EscapeJsonText(snmpPrinterJobs)}\",\"source\":\"SNMP\"}}", mutex); } - if (Issues.CheckPrinterComponent(ipAddress, profile, out Issues.Issue[] issues)) { + if (Issues.CheckPrinterComponent(file, ipAddress, profile, out Issues.Issue[] issues)) { for (int i = 0; i < issues.Length; i++) { WsWriteText(ws, issues[i].ToJsonBytes(), mutex); } diff --git a/Protest/Workers/Issues.cs b/Protest/Workers/Issues.cs index 06c59bbf..2dc55bd2 100644 --- a/Protest/Workers/Issues.cs +++ b/Protest/Workers/Issues.cs @@ -32,8 +32,8 @@ public struct Issue { public string target; public string category; public string source; - //public string file; public bool isUser; + public string file; public long timestamp; } @@ -45,7 +45,7 @@ public static byte[] ToJsonBytes(this Issue issue) => JsonSerializer.SerializeTo { "target", issue.target }, { "category", issue.category}, { "source", issue.source }, - //{ "file", issue.file}, + { "file", issue.file}, }); public static byte[] List() { @@ -116,6 +116,8 @@ public static async void WebSocketHandler(HttpListenerContext ctx) { int lastIssuesCount = 0; long lastTimestamp = -1; + await Task.Delay(1_000); + try { while (ws.State == WebSocketState.Open && task is not null) { if (!Auth.IsAuthenticatedAndAuthorized(ctx, "/ws/issues")) { @@ -123,8 +125,6 @@ public static async void WebSocketHandler(HttpListenerContext ctx) { return; } - await Task.Delay(5_000); - if (lastIssuesCount == issues.Count) { continue; } @@ -147,8 +147,9 @@ public static async void WebSocketHandler(HttpListenerContext ctx) { lastTimestamp = filtered.Max(o => o.timestamp); } - } + await Task.Delay(5_000); + } } catch { } finally { @@ -163,7 +164,6 @@ public static async void WebSocketHandler(HttpListenerContext ctx) { private static void Scan() { ScanUsers(); - Thread.Sleep(1000); ScanDevices(); } @@ -185,8 +185,8 @@ private static void ScanDevices() { public static void ScanDevice(KeyValuePair device) { device.Value.attributes.TryGetValue("type", out Database.Attribute typeAttribute); - device.Value.attributes.TryGetValue("ip", out Database.Attribute ipAttribute); - device.Value.attributes.TryGetValue("hostname", out Database.Attribute hostnameAttribute); + //device.Value.attributes.TryGetValue("ip", out Database.Attribute ipAttribute); + //device.Value.attributes.TryGetValue("hostname", out Database.Attribute hostnameAttribute); device.Value.attributes.TryGetValue("operating system", out Database.Attribute osAttribute); if (CheckPasswordStrength(device.Value, false, out Issue? issue) && issue.HasValue) { @@ -196,14 +196,14 @@ public static void ScanDevice(KeyValuePair device) { if (osAttribute?.value.Contains("windows", StringComparison.OrdinalIgnoreCase) == true) { } - else if (Data.PRINTER_TYPES.Contains(typeAttribute?.value)) { - if (CheckPrinterComponent(device.Value, out Issue[] printerIssues) && issue.HasValue) { + else if (Data.PRINTER_TYPES.Contains(typeAttribute?.value, StringComparer.OrdinalIgnoreCase)) { + if (CheckPrinterComponent(device.Value, out Issue[] printerIssues) && printerIssues is not null) { for (int i = 0; i < printerIssues.Length; i++) { issues.Add(printerIssues[i]); } } } - else if (Data.SWITCH_TYPES.Contains(typeAttribute?.value)) { + else if (Data.SWITCH_TYPES.Contains(typeAttribute?.value, StringComparer.OrdinalIgnoreCase)) { } } @@ -243,6 +243,7 @@ public static bool CheckPasswordStrength(Database.Entry entry, bool isUser, out category = "Password", source = "Internal check", isUser = isUser, + file = entry.filename, timestamp = DateTime.UtcNow.Ticks }; return true; @@ -254,43 +255,43 @@ public static bool CheckPasswordStrength(Database.Entry entry, bool isUser, out } public static bool CheckDiskCapacity(string target, double percent, string diskCaption, out Issue? issue) { - string message = $"Free space is {percent}% on disk {Data.EscapeJsonText(diskCaption)}"; + string message = $"{percent}% free space on disk {Data.EscapeJsonText(diskCaption)}"; if (percent <= 1) { issue = new Issue { - severity = SeverityLevel.critical, - target = target, - message = message, - category = "Disk drive", - source = "WMI", - isUser = false, - timestamp = DateTime.UtcNow.Ticks, + severity = SeverityLevel.critical, + target = target, + message = message, + category = "Disk drive", + source = "WMI", + isUser = false, + timestamp = DateTime.UtcNow.Ticks, }; return true; } if (percent <= 5) { issue = new Issue { - severity = SeverityLevel.error, - target = target, - message = message, - category = "Disk drive", - source = "WMI", - isUser = false, - timestamp = DateTime.UtcNow.Ticks, + severity = SeverityLevel.error, + target = target, + message = message, + category = "Disk drive", + source = "WMI", + isUser = false, + timestamp = DateTime.UtcNow.Ticks, }; return true; } if (percent < 15) { issue = new Issue { - severity = SeverityLevel.warning, - target = target, - message = message, - category = "Disk drive", - source = "WMI", - isUser = false, - timestamp = DateTime.UtcNow.Ticks, + severity = SeverityLevel.warning, + target = target, + message = message, + category = "Disk drive", + source = "WMI", + isUser = false, + timestamp = DateTime.UtcNow.Ticks, }; return true; } @@ -300,7 +301,6 @@ public static bool CheckDiskCapacity(string target, double percent, string diskC } public static bool CheckPrinterComponent(Database.Entry entry, out Issue[] issuse) { - if (!entry.attributes.TryGetValue("snmp profile", out Database.Attribute snmpGuidAttribute)) { issuse = null; return false; @@ -332,14 +332,16 @@ public static bool CheckPrinterComponent(Database.Entry entry, out Issue[] issus return false; } - return CheckPrinterComponent(ipAddress, profile, out issuse); + return CheckPrinterComponent(entry.filename, ipAddress, profile, out issuse); } - public static bool CheckPrinterComponent(IPAddress ipAddress, SnmpProfiles.Profile profile, out Issue[] issues) { + public static bool CheckPrinterComponent(string file, IPAddress ipAddress, SnmpProfiles.Profile profile, out Issue[] issues) { Dictionary componentName = Protocols.Snmp.Polling.ParseResponse(Protocols.Snmp.Polling.SnmpQuery(ipAddress, profile, new string[] { Protocols.Snmp.Oid.PRINTER_TONERS }, Protocols.Snmp.Polling.SnmpOperation.Walk)); Dictionary componentMax = Protocols.Snmp.Polling.ParseResponse(Protocols.Snmp.Polling.SnmpQuery(ipAddress, profile, new string[] { Protocols.Snmp.Oid.PRINTER_TONERS_MAX }, Protocols.Snmp.Polling.SnmpOperation.Walk)); Dictionary componentCurrent = Protocols.Snmp.Polling.ParseResponse(Protocols.Snmp.Polling.SnmpQuery(ipAddress, profile, new string[] { Protocols.Snmp.Oid.PRINTER_TONER_CURRENT }, Protocols.Snmp.Polling.SnmpOperation.Walk)); + Console.WriteLine(profile?.name); + if (componentName is not null && componentCurrent is not null && componentMax is not null && componentName.Count == componentCurrent.Count && componentCurrent.Count == componentMax.Count) { @@ -363,28 +365,19 @@ public static bool CheckPrinterComponent(IPAddress ipAddress, SnmpProfiles.Profi componentNameArray[i][1] = componentNameArray[i][1].TrimStart(' ', '!', '\"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '{', '|', '}', '~'); int used = 100 * current / max; - if (used < 5) { + + if (used < 15) { arrays.Add(new Issue { - severity = SeverityLevel.error, + severity = used < 5 ? SeverityLevel.error : SeverityLevel.warning, message = $"{used}% {componentNameArray[i][1]}", target = ipAddress.ToString(), category = "Printer component", source = "SNMP", isUser = false, + file = file, timestamp = DateTime.UtcNow.Ticks }); } - else if (used < 15) { - arrays.Add(new Issue { - severity = SeverityLevel.warning, - message = $"{used}% {componentNameArray[i][1]}", - target = ipAddress.ToString(), - category = "Printer component", - source = "SNMP", - isUser = false, - timestamp = DateTime.UtcNow.Ticks - }); - } } if (arrays.Count > 0) {