diff --git a/java-api/src/main/java/cz/kamma/processmonitor/Main.java b/java-api/src/main/java/cz/kamma/processmonitor/Main.java index f5b1bf8..2eff174 100644 --- a/java-api/src/main/java/cz/kamma/processmonitor/Main.java +++ b/java-api/src/main/java/cz/kamma/processmonitor/Main.java @@ -190,6 +190,61 @@ public class Main { } } + private String extractMainName(String processName, java.util.List allProcessNames) { + if (processName == null || processName.isBlank()) { + return null; + } + // Odstranit .exe + String name = processName; + int dotIndex = name.lastIndexOf('.'); + if (dotIndex > 0) { + name = name.substring(0, dotIndex); + } + + // Seřadit všechny názvy podle délky (od nejkratšího) + java.util.List sortedNames = new java.util.ArrayList<>(allProcessNames); + sortedNames.sort((a, b) -> { + int lenA = a != null ? a.length() : 0; + int lenB = b != null ? b.length() : 0; + return Integer.compare(lenA, lenB); + }); + + // Pro každý název najít jeho unikátní základ + // Pokud nějaký název začíná kratším názvem, odstraníme ten delší + java.util.Set baseNames = new java.util.TreeSet<>(String.CASE_INSENSITIVE_ORDER); + + for (String procName : sortedNames) { + if (procName == null || procName.isBlank()) continue; + String cleanName = procName; + int dotIdx = cleanName.lastIndexOf('.'); + if (dotIdx > 0) { + cleanName = cleanName.substring(0, dotIdx); + } + + // Zkontrolovat, zda tento název začíná nějakým již přidaným základem + boolean foundBase = false; + for (String base : baseNames) { + if (cleanName.toLowerCase().startsWith(base.toLowerCase())) { + foundBase = true; + break; + } + } + + if (!foundBase) { + baseNames.add(cleanName); + } + } + + // Najít základ pro tento konkrétní název + for (String base : baseNames) { + if (name.toLowerCase().startsWith(base.toLowerCase())) { + return base.trim(); + } + } + + return name.trim(); + } + private StatsResponse getStats(String machine, String process, String status, String from, String to) throws SQLException { StringBuilder sql = new StringBuilder("SELECT id, machine_name, status, detected_at, process_name FROM process_heartbeat WHERE 1=1"); java.util.List params = new java.util.ArrayList<>(); @@ -224,15 +279,29 @@ public class Main { stmt.setString(i + 1, params.get(i)); } + // Nejprve načíst všechny unikátní názvy procesů pro určení hlavního názvu + java.util.List allProcessNames = new java.util.ArrayList<>(); + try (PreparedStatement allStmt = connection.prepareStatement( + "SELECT DISTINCT process_name FROM process_heartbeat WHERE process_name IS NOT NULL")) { + try (java.sql.ResultSet rs = allStmt.executeQuery()) { + while (rs.next()) { + allProcessNames.add(rs.getString(1)); + } + } + } + java.util.List records = new java.util.ArrayList<>(); try (java.sql.ResultSet rs = stmt.executeQuery()) { while (rs.next()) { + String processName = rs.getString(5); + String mainName = extractMainName(processName, allProcessNames); records.add(new Record( rs.getLong(1), rs.getString(2), rs.getString(3), rs.getTimestamp(4), - rs.getString(5) + processName, + mainName )); } } @@ -240,6 +309,21 @@ public class Main { return new StatsResponse(records); } } + + private LastRecordTimeResponse getLastRecordTime() throws SQLException { + String sql = "SELECT detected_at FROM process_heartbeat ORDER BY detected_at DESC LIMIT 1"; + + try (Connection connection = DriverManager.getConnection(config.jdbcUrl, config.dbUser, config.dbPassword); + PreparedStatement stmt = connection.prepareStatement(sql); + java.sql.ResultSet rs = stmt.executeQuery()) { + + if (rs.next()) { + Timestamp ts = rs.getTimestamp(1); + return new LastRecordTimeResponse(ts.toInstant().toString()); + } + return new LastRecordTimeResponse(null); + } + } } private static final class FilterOptions { @@ -260,13 +344,15 @@ public class Main { String status; String detected_at; String process_name; + String main_name; - Record(long id, String machine_name, String status, Timestamp detected_at, String process_name) { + Record(long id, String machine_name, String status, Timestamp detected_at, String process_name, String main_name) { this.id = id; this.machine_name = machine_name; this.status = status; this.detected_at = detected_at != null ? detected_at.toInstant().toString() : null; this.process_name = process_name; + this.main_name = main_name; } } @@ -278,6 +364,14 @@ public class Main { } } + private static final class LastRecordTimeResponse { + String lastRecordTime; + + LastRecordTimeResponse(String lastRecordTime) { + this.lastRecordTime = lastRecordTime; + } + } + private static final class HeartbeatRequest { private String machine_name; private String status; @@ -419,6 +513,9 @@ public class Main { if ("filters".equals(type)) { String response = GSON.toJson(database.getFilterOptions()); sendJson(exchange, 200, response); + } else if ("lastRecordTime".equals(type)) { + String response = GSON.toJson(database.getLastRecordTime()); + sendJson(exchange, 200, response); } else if ("stats".equals(type)) { String machine = getParam(query, "machine"); String process = getParam(query, "process"); diff --git a/java-api/src/main/resources/dashboard.html b/java-api/src/main/resources/dashboard.html index d6c0fe9..c4d2430 100644 --- a/java-api/src/main/resources/dashboard.html +++ b/java-api/src/main/resources/dashboard.html @@ -27,12 +27,12 @@ align-items: center; } .header h1 { - margin-bottom: 10px; + margin-bottom: 5px; } .header-content { flex: 1; } - .logout-btn { + .logout-btn { background: rgba(255,255,255,0.2); border: 1px solid rgba(255,255,255,0.3); color: white; @@ -46,7 +46,7 @@ background: rgba(255,255,255,0.3); } .container { - max-width: 1400px; + max-width: 600px; margin: 0 auto; padding: 20px; } @@ -54,11 +54,10 @@ background: white; padding: 20px; border-radius: 8px; - margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 15px; + grid-template-columns: 1fr 1fr; + gap: 10px; } .filter-group { display: flex; @@ -103,19 +102,14 @@ .filter-buttons button.reset:hover { background: #777; } - .charts-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(500px, 1fr)); - gap: 20px; - margin-bottom: 20px; - } .chart-container { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); + margin-top: 20px; } - .chart-container h3 { + .chart-container h3 { margin-bottom: 15px; color: #333; } @@ -123,27 +117,18 @@ position: relative; height: 300px; } - .stats-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 15px; - margin-bottom: 20px; - } - .stat-card { + .info-panel { background: white; - padding: 20px; + padding: 15px 20px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); - } - .stat-card h4 { + margin-top: 20px; font-size: 14px; color: #666; - margin-bottom: 10px; } - .stat-card .value { - font-size: 28px; - font-weight: bold; - color: #667eea; + .info-panel span { + font-weight: 600; + color: #333; } .error { background: #fee; @@ -157,72 +142,51 @@
-

📊 Process Monitor Dashboard

-

Real-time monitoring of processes across machines

+

Process Monitor Dashboard

-
-
+
-
+
-
+
-
+
-
+
-
- -
-
-

Celkem záznamů

-
-
-
-
-

Procesy UP

-
-
-
-
-

Procesy DOWN

-
-
-
-
-

Dostupnost

-
-
-
+
+ Poslední záznam: Načítání...
-
-
-

Doba procesů vs. čas bez nalezených procesů

-
- -
+
+

Doba procesů

+
+
-
+
diff --git a/java-api/src/main/resources/error.html b/java-api/src/main/resources/error.html deleted file mode 100644 index faa6163..0000000 --- a/java-api/src/main/resources/error.html +++ /dev/null @@ -1,61 +0,0 @@ - - - - - - Process Monitor - Chyba - - - -
-

⚠️ Chyba

-

%MESSAGE%

- Zpět na přihlášení -
- - diff --git a/java-api/src/main/resources/login.html b/java-api/src/main/resources/login.html deleted file mode 100644 index 83561cd..0000000 --- a/java-api/src/main/resources/login.html +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - Process Monitor - Login - - - - - - -