From a583b133bc5d1f200aa0f42f6c1fa4d4e9ca619c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20Th=C3=A0nh=20=C4=90=E1=BA=A1t?= <91755733+lds217@users.noreply.github.com> Date: Fri, 1 Aug 2025 17:19:12 +0700 Subject: [PATCH] sync --- .../client/network/handler/ClientHandler.java | 1 - .../DashboardPage/DashboardPage.java | 25 +- .../DataDialog/MedicineManagementPanel.java | 54 ++- .../common/MedicineDialog/MedicineDialog.java | 76 ++-- .../common/packet/req/AddMedicineRequest.java | 2 +- .../packet/req/EditMedicineRequest.java | 2 +- src/main/java/BsK/server/Server.java | 28 +- src/main/java/BsK/server/ServerDashboard.java | 365 +++++++++--------- .../java/BsK/server/network/entity/User.java | 7 + .../server/network/handler/ServerHandler.java | 26 +- .../network/manager/SessionManager.java | 22 +- .../service/GoogleDriveServiceOAuth.java | 59 +++ src/main/resources/config.properties | 5 +- src/main/resources/database/BSK.db | Bin 1339392 -> 1339392 bytes 14 files changed, 403 insertions(+), 269 deletions(-) diff --git a/src/main/java/BsK/client/network/handler/ClientHandler.java b/src/main/java/BsK/client/network/handler/ClientHandler.java index 152e862..dcce267 100644 --- a/src/main/java/BsK/client/network/handler/ClientHandler.java +++ b/src/main/java/BsK/client/network/handler/ClientHandler.java @@ -49,7 +49,6 @@ public class ClientHandler extends SimpleChannelInboundHandler 0 ? Integer.parseInt(parts[0].trim()) : 0); + noonSpinner.setValue(parts.length > 1 ? Integer.parseInt(parts[1].trim()) : 0); + eveningSpinner.setValue(parts.length > 2 ? Integer.parseInt(parts[2].trim()) : 0); + noteField.setText(parts.length > 3 ? parts[3].trim() : ""); + } catch (NumberFormatException e) { + log.error("Could not parse preferred note: {}", preferredNote, e); + // Reset to default values in case of parsing error + morningSpinner.setValue(0); + noonSpinner.setValue(0); + eveningSpinner.setValue(0); + noteField.setText(""); + } + } else { + // No preferred note, set default values + morningSpinner.setValue(0); + noonSpinner.setValue(0); + eveningSpinner.setValue(0); + noteField.setText(""); + } + // --- END PARSING --- + // Update button states btnAdd.setEnabled(false); btnEdit.setEnabled(true); @@ -525,6 +554,14 @@ public class MedicineManagementPanel extends JPanel { medicineDescriptionField.setText(""); medicineUnitField.setText(""); medicinePriceField.setText(""); + + // --- ADDED RESET LOGIC --- + morningSpinner.setValue(0); + noonSpinner.setValue(0); + eveningSpinner.setValue(0); + noteField.setText(""); + // --- END ADD --- + chkIsSupplement.setSelected(false); chkIsDeleted.setSelected(false); medicineTable.clearSelection(); @@ -555,10 +592,7 @@ public class MedicineManagementPanel extends JPanel { medicineTableModel.addRow(new Object[]{ med.getId(), med.getName(), - med.getUnit(), - med.getSellingPrice(), supplementStatus, - med.getPreferredNote(), deletedStatus }); } diff --git a/src/main/java/BsK/client/ui/component/common/MedicineDialog/MedicineDialog.java b/src/main/java/BsK/client/ui/component/common/MedicineDialog/MedicineDialog.java index 7e8ed8a..ee61d8b 100644 --- a/src/main/java/BsK/client/ui/component/common/MedicineDialog/MedicineDialog.java +++ b/src/main/java/BsK/client/ui/component/common/MedicineDialog/MedicineDialog.java @@ -1,11 +1,5 @@ package BsK.client.ui.component.common.MedicineDialog; -import javax.swing.*; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; -import javax.swing.table.DefaultTableModel; -import javax.swing.table.TableColumnModel; - import BsK.client.network.handler.ClientHandler; import BsK.client.network.handler.ResponseListener; import BsK.common.entity.Medicine; @@ -17,25 +11,25 @@ import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.TableColumnModel; import java.awt.*; -import java.awt.event.KeyAdapter; -import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.WindowAdapter; -import java.awt.event.WindowEvent; +import java.awt.event.*; +import java.text.NumberFormat; // --- ADD THIS IMPORT --- import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.awt.event.ActionEvent; -import javax.swing.AbstractAction; -import javax.swing.KeyStroke; +import java.util.Locale; // --- ADD THIS IMPORT --- @Slf4j public class MedicineDialog extends JDialog { private JTextField medicineNameField; private JTextField medicineCompanyField; private JTextArea medicineDescriptionField; + private JCheckBox chkIsSupplement; // The new checkbox private JSpinner quantitySpinner; private JComboBox UnitComboBox; private JTextField priceField; @@ -69,10 +63,10 @@ public class MedicineDialog extends JDialog { void getMedInfoHandler(GetMedInfoResponse response) { logger.info("Received medicine data"); medicineData = response.getMedInfo(); - + // Get Medicine objects from the response medicines = response.getMedicines(); - + // We don't need to populate a main table anymore, just have the data ready. } @@ -87,7 +81,7 @@ public class MedicineDialog extends JDialog { this.originalPrescription = new String[0][0]; init(parent); } - + public MedicineDialog(final Frame parent, String[][] existingPrescription) { super(parent, "Đơn thuốc", true); this.medicinePrescription = existingPrescription != null ? existingPrescription : new String[0][0]; @@ -105,17 +99,17 @@ public class MedicineDialog extends JDialog { setSize(1200, 700); setLocationRelativeTo(parent); setResizable(true); - + // UI Fonts and Dimensions Font labelFont = new Font("Arial", Font.BOLD, 15); Font textFont = new Font("Arial", Font.PLAIN, 15); Font titleFont = new Font("Arial", Font.BOLD, 16); Dimension textFieldSize = new Dimension(100, 30); - + // Ensure modal behavior and proper parent relationship setModalityType(Dialog.ModalityType.APPLICATION_MODAL); setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this - + // Add window listener to handle minimize/restore events with parent addWindowListener(new WindowAdapter() { @Override @@ -124,7 +118,7 @@ public class MedicineDialog extends JDialog { parent.setState(Frame.ICONIFIED); } } - + @Override public void windowDeiconified(WindowEvent e) { if (parent != null && parent.getState() == Frame.ICONIFIED) { @@ -263,6 +257,16 @@ public class MedicineDialog extends JDialog { descriptionScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); medicineInfoPanel.add(descriptionScrollPane, gbc); gbc.weightx = 0.0; + + gbc.gridy++; + gbc.gridx = 0; + gbc.gridwidth = 2; + chkIsSupplement = new JCheckBox("Thực phẩm bổ sung"); + chkIsSupplement.setFont(labelFont); + chkIsSupplement.setEnabled(false); + chkIsSupplement.setOpaque(false); + medicineInfoPanel.add(chkIsSupplement, gbc); + gbc.gridwidth = 1; gbc.gridy = 0; gbc.gridx = 0; @@ -723,18 +727,21 @@ public class MedicineDialog extends JDialog { UnitComboBox.setSelectedItem(medicine.getUnit()); priceField.setText(medicine.getSellingPrice()); + // Update the new checkbox + chkIsSupplement.setSelected("1".equals(medicine.getSupplement())); + quantitySpinner.setValue(1); - String preferenceNote = medicine.getPreferredNote(); - if (preferenceNote != null && !preferenceNote.trim().isEmpty()) { - String[] parts = preferenceNote.split(",", 4); + String preferredNote = medicine.getPreferredNote(); + if (preferredNote != null && !preferredNote.trim().isEmpty()) { + String[] parts = preferredNote.split(",", 4); try { morningSpinner.setValue(parts.length > 0 ? Integer.parseInt(parts[0].trim()) : 0); noonSpinner.setValue(parts.length > 1 ? Integer.parseInt(parts[1].trim()) : 0); eveningSpinner.setValue(parts.length > 2 ? Integer.parseInt(parts[2].trim()) : 0); noteField.setText(parts.length > 3 ? parts[3].trim() : ""); } catch (NumberFormatException e) { - logger.error("Could not parse preference note: {}", preferenceNote, e); + logger.error("Could not parse preferred note: {}", preferredNote, e); // Reset to default values in case of parsing error morningSpinner.setValue(0); noonSpinner.setValue(0); @@ -742,7 +749,7 @@ public class MedicineDialog extends JDialog { noteField.setText(""); } } else { - // No preference note, set default values + // No preferred note, set default values morningSpinner.setValue(0); noonSpinner.setValue(0); eveningSpinner.setValue(0); @@ -760,6 +767,7 @@ public class MedicineDialog extends JDialog { UnitComboBox.setSelectedIndex(0); priceField.setText(""); totalField.setText(""); + chkIsSupplement.setSelected(false); // Reset the checkbox quantitySpinner.setValue(1); morningSpinner.setValue(0); noonSpinner.setValue(0); @@ -770,9 +778,19 @@ public class MedicineDialog extends JDialog { private void updateTotalField() { try { int quantity = (Integer) quantitySpinner.getValue(); - if (quantity < 0) { quantitySpinner.setValue(0); quantity = 0; } + if (quantity < 0) { + quantitySpinner.setValue(0); + quantity = 0; + } double price = priceField.getText().isEmpty() ? 0 : Double.parseDouble(priceField.getText()); - totalField.setText(String.format("%.0f", quantity * price)); + double totalValue = quantity * price; + + // Create formatter for Vietnamese locale + NumberFormat formatter = NumberFormat.getInstance(new Locale("vi", "VN")); + + // Format the total and set the text + totalField.setText(formatter.format(totalValue)); + } catch (NumberFormatException e) { totalField.setText("Giá trị không hợp lệ"); logger.error("Error parsing price for total calculation: " + priceField.getText(), e); diff --git a/src/main/java/BsK/common/packet/req/AddMedicineRequest.java b/src/main/java/BsK/common/packet/req/AddMedicineRequest.java index 8858e80..72057f4 100644 --- a/src/main/java/BsK/common/packet/req/AddMedicineRequest.java +++ b/src/main/java/BsK/common/packet/req/AddMedicineRequest.java @@ -12,7 +12,7 @@ public class AddMedicineRequest implements Packet{ private String description; private String unit; private Double price; - private String preferedNote; + private String preferredNote; private Boolean supplement; private Boolean deleted; } diff --git a/src/main/java/BsK/common/packet/req/EditMedicineRequest.java b/src/main/java/BsK/common/packet/req/EditMedicineRequest.java index b29e191..e99592e 100644 --- a/src/main/java/BsK/common/packet/req/EditMedicineRequest.java +++ b/src/main/java/BsK/common/packet/req/EditMedicineRequest.java @@ -13,7 +13,7 @@ public class EditMedicineRequest implements Packet { private String description; private String unit; private Double price; - private String preferedNote; + private String preferredNote; private Boolean supplement; private Boolean deleted; } diff --git a/src/main/java/BsK/server/Server.java b/src/main/java/BsK/server/Server.java index d6c5be6..32154d9 100644 --- a/src/main/java/BsK/server/Server.java +++ b/src/main/java/BsK/server/Server.java @@ -330,5 +330,31 @@ public class Server { parentGroup.shutdownGracefully(); childGroup.shutdownGracefully(); } - } } + + /** + * Initiates the backup of the main database file to Google Drive. + * This is intended to be called from the ServerDashboard. + * @throws IOException if the file cannot be found or the upload fails. + */ + public static void backupDatabaseToDrive() throws IOException { + ServerDashboard dashboard = ServerDashboard.getInstance(); + + if (!isGoogleDriveConnected() || googleDriveService == null) { + dashboard.addLog("❌ Cannot backup database: Google Drive is not connected."); + throw new IOException("Google Drive service is not available."); + } + + // Define the path to the database + String dbPath = "src/main/resources/database/BSK.db"; + java.io.File dbFile = new java.io.File(dbPath); + + if (!dbFile.exists()) { + dashboard.addLog("❌ Cannot backup database: File not found at " + dbPath); + throw new IOException("Database file not found: " + dbPath); + } + + // Call the service to perform the backup + googleDriveService.backupDatabaseFile(dbFile); + } +} diff --git a/src/main/java/BsK/server/ServerDashboard.java b/src/main/java/BsK/server/ServerDashboard.java index 85778b4..efcba66 100644 --- a/src/main/java/BsK/server/ServerDashboard.java +++ b/src/main/java/BsK/server/ServerDashboard.java @@ -1,16 +1,16 @@ package BsK.server; import javax.swing.*; +import javax.swing.border.TitledBorder; import javax.swing.table.DefaultTableModel; import javax.swing.text.*; import java.awt.*; -import java.awt.event.*; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Vector; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.OperatingSystemMXBean; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Vector; import BsK.server.network.entity.ClientConnection; import BsK.server.network.manager.SessionManager; import lombok.extern.slf4j.Slf4j; @@ -36,6 +36,9 @@ public class ServerDashboard extends JFrame { private JTable networkTable; private DefaultTableModel networkTableModel; + // --- NEW --- + private JButton backupDbButton; + public static ServerDashboard getInstance() { if (instance == null) { instance = new ServerDashboard(); @@ -49,97 +52,158 @@ public class ServerDashboard extends JFrame { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLocationRelativeTo(null); - // Create main panel with border layout - JPanel mainPanel = new JPanel(new BorderLayout()); + // Main container panel + JPanel mainPanel = new JPanel(new BorderLayout(10, 10)); mainPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); - // Status Panel (North) - JPanel topPanel = new JPanel(new GridLayout(3, 1, 0, 5)); - - // Server Status Panel - JPanel statusPanel = new JPanel(new GridLayout(1, 4, 10, 0)); - statusPanel.setBorder(BorderFactory.createTitledBorder("Server Status")); + // Add main components + mainPanel.add(createHeaderPanel(), BorderLayout.NORTH); + mainPanel.add(createMainContentPanel(), BorderLayout.CENTER); + add(mainPanel); + + // Start timers + startSystemStatsTimer(); + startNetworkStatsTimer(); + } + + private JPanel createHeaderPanel() { + JPanel headerPanel = new JPanel(new GridBagLayout()); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(0, 5, 0, 5); + gbc.weighty = 1.0; + + // Server Status Panel + JPanel statusPanel = new JPanel(new GridLayout(1, 3, 10, 0)); + statusPanel.setBorder(BorderFactory.createTitledBorder("Server Status")); statusLabel = new JLabel("Status: Starting...", SwingConstants.CENTER); portLabel = new JLabel("Port: --", SwingConstants.CENTER); clientsLabel = new JLabel("Connected Clients: 0", SwingConstants.CENTER); - googleDriveLabel = new JLabel("Google Drive: Checking...", SwingConstants.CENTER); - statusPanel.add(statusLabel); statusPanel.add(portLabel); statusPanel.add(clientsLabel); - statusPanel.add(googleDriveLabel); + gbc.gridx = 0; + gbc.weightx = 0.35; // MODIFIED: Adjusted weight + headerPanel.add(statusPanel, gbc); // System Info Panel - JPanel systemPanel = new JPanel(new GridLayout(1, 4, 10, 0)); + JPanel systemPanel = new JPanel(new GridLayout(1, 3, 10, 0)); systemPanel.setBorder(BorderFactory.createTitledBorder("System Information")); - memoryLabel = new JLabel("Memory Usage: --", SwingConstants.CENTER); cpuLabel = new JLabel("CPU Usage: --", SwingConstants.CENTER); JLabel osLabel = new JLabel("OS: " + System.getProperty("os.name"), SwingConstants.CENTER); - - // Google Drive settings button - googleDriveButton = new JButton("Retry Drive"); - googleDriveButton.addActionListener(e -> retryGoogleDriveConnection()); - googleDriveButton.setToolTipText("Retry Google Drive connection"); - systemPanel.add(memoryLabel); systemPanel.add(cpuLabel); systemPanel.add(osLabel); - systemPanel.add(googleDriveButton); + gbc.gridx = 1; + gbc.weightx = 0.35; // MODIFIED: Adjusted weight + headerPanel.add(systemPanel, gbc); - // Google Drive Settings Panel - JPanel driveSettingsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - driveSettingsPanel.setBorder(BorderFactory.createTitledBorder("Google Drive Settings")); + // Google Drive Panel + JPanel drivePanel = new JPanel(new BorderLayout(10, 0)); + drivePanel.setBorder(BorderFactory.createTitledBorder("Google Drive")); + googleDriveLabel = new JLabel("Status: Checking...", SwingConstants.CENTER); + googleDriveButton = new JButton("Retry"); + googleDriveButton.addActionListener(e -> retryGoogleDriveConnection()); + drivePanel.add(googleDriveLabel, BorderLayout.CENTER); + drivePanel.add(googleDriveButton, BorderLayout.EAST); + gbc.gridx = 2; + gbc.weightx = 0.2; // MODIFIED: Adjusted weight + headerPanel.add(drivePanel, gbc); - JLabel folderNameLabel = new JLabel("Root Folder Name:"); - JTextField driveRootFolderField = new JTextField(getCurrentDriveRootFolder(), 20); - JButton applyDriveSettingsButton = new JButton("Apply"); - applyDriveSettingsButton.addActionListener(e -> applyDriveSettings(driveRootFolderField.getText().trim())); - - driveSettingsPanel.add(folderNameLabel); - driveSettingsPanel.add(driveRootFolderField); - driveSettingsPanel.add(applyDriveSettingsButton); + // --- NEW: Actions Panel --- + JPanel actionsPanel = new JPanel(new BorderLayout()); + actionsPanel.setBorder(BorderFactory.createTitledBorder("Actions")); + backupDbButton = new JButton("Backup DB to Drive"); + backupDbButton.setToolTipText("Upload a timestamped copy of the database to Google Drive."); + backupDbButton.addActionListener(e -> performDatabaseBackup()); + actionsPanel.add(backupDbButton, BorderLayout.CENTER); + gbc.gridx = 3; + gbc.weightx = 0.1; // MODIFIED: Adjusted weight + headerPanel.add(actionsPanel, gbc); - topPanel.add(statusPanel); - topPanel.add(systemPanel); - topPanel.add(driveSettingsPanel); - // Create split pane for logs and network info - JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT); - splitPane.setResizeWeight(0.1); // Give more space to the top component + return headerPanel; + } + + // --- NEW: Method to handle the backup action --- + private void performDatabaseBackup() { + // Disable button to prevent multiple clicks + backupDbButton.setEnabled(false); + addLog("▶️ Starting database backup to Google Drive..."); - // Network Information Panel + // Run the backup in a background thread to not freeze the UI + new Thread(() -> { + try { + // Call the static method in the Server class + BsK.server.Server.backupDatabaseToDrive(); + + // Update UI on success + SwingUtilities.invokeLater(() -> { + addLog("✅ Database backup completed successfully."); + JOptionPane.showMessageDialog(this, + "Database backup was successfully uploaded to Google Drive.", + "Backup Successful", + JOptionPane.INFORMATION_MESSAGE); + }); + + } catch (Exception e) { + log.error("Database backup failed", e); + // Update UI on failure + SwingUtilities.invokeLater(() -> { + addLog("❌ Database backup failed: " + e.getMessage()); + JOptionPane.showMessageDialog(this, + "Failed to backup the database.\nError: " + e.getMessage() + "\n\nCheck logs for more details.", + "Backup Failed", + JOptionPane.ERROR_MESSAGE); + }); + } finally { + // Always re-enable the button on the UI thread + SwingUtilities.invokeLater(() -> backupDbButton.setEnabled(true)); + } + }).start(); + } + + private JSplitPane createMainContentPanel() { + // ... (This method remains unchanged) + // --- Log Panel --- + JPanel logPanel = new JPanel(new BorderLayout()); + logPanel.setBorder(BorderFactory.createTitledBorder("Server Logs")); + logArea = new JTextPane(); + logArea.setEditable(false); + logArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); + JScrollPane logScrollPane = new JScrollPane(logArea); + logPanel.add(createLogControlsPanel(), BorderLayout.NORTH); + logPanel.add(logScrollPane, BorderLayout.CENTER); + + // --- Network Panel --- JPanel networkPanel = new JPanel(new BorderLayout()); networkPanel.setBorder(BorderFactory.createTitledBorder("Network Information")); - - // Create table model with columns - String[] columnNames = {"Session ID", "IP Address", "Port", "Role", "Connected Time", "Last Activity", "Bytes Sent", "Bytes Received"}; + String[] columnNames = {"Session ID", "IP Address", "Port", "Role", "Connected Time", "Last Activity"}; networkTableModel = new DefaultTableModel(columnNames, 0) { @Override - public boolean isCellEditable(int row, int column) { - return false; - } + public boolean isCellEditable(int row, int column) { return false; } }; - networkTable = new JTable(networkTableModel); networkTable.setFillsViewportHeight(true); - JScrollPane networkScrollPane = new JScrollPane(networkTable); - networkPanel.add(networkScrollPane, BorderLayout.CENTER); + networkPanel.add(new JScrollPane(networkTable), BorderLayout.CENTER); - // Log Controls Panel - JPanel logControlsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + // --- Split Pane --- + JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, logPanel, networkPanel); + splitPane.setResizeWeight(0.80); // Give 80% of space to the logs - // Search field + return splitPane; + } + + private JPanel createLogControlsPanel() { + // ... (This method remains unchanged) + JPanel logControlsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); searchField = new JTextField(20); JButton searchButton = new JButton("Search"); searchButton.addActionListener(e -> searchInLogs()); - - // Clear logs button JButton clearButton = new JButton("Clear Logs"); clearButton.addActionListener(e -> clearLogs()); - - // Auto-scroll toggle autoScrollToggle = new JToggleButton("Auto-scroll", true); autoScrollToggle.addActionListener(e -> isAutoScrollEnabled = autoScrollToggle.isSelected()); @@ -150,52 +214,25 @@ public class ServerDashboard extends JFrame { logControlsPanel.add(clearButton); logControlsPanel.add(Box.createHorizontalStrut(20)); logControlsPanel.add(autoScrollToggle); - - // Log Area Panel - JPanel logPanel = new JPanel(new BorderLayout()); - logPanel.setBorder(BorderFactory.createTitledBorder("Server Logs")); - - logArea = new JTextPane(); - logArea.setEditable(false); - logArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); - JScrollPane logScrollPane = new JScrollPane(logArea); - - logPanel.add(logControlsPanel, BorderLayout.NORTH); - logPanel.add(logScrollPane, BorderLayout.CENTER); - - // Add components to split pane - splitPane.setTopComponent(logPanel); - splitPane.setBottomComponent(networkPanel); - - // Add components to main panel - mainPanel.add(topPanel, BorderLayout.NORTH); - mainPanel.add(splitPane, BorderLayout.CENTER); - - add(mainPanel); - - // Start system stats update timer - startSystemStatsTimer(); - // Start network stats update timer - startNetworkStatsTimer(); + return logControlsPanel; } private void startSystemStatsTimer() { + // ... (This method remains unchanged) MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean(); OperatingSystemMXBean osBean = ManagementFactory.getOperatingSystemMXBean(); statsTimer = new Timer(2000, e -> { - // Update memory usage long usedMemory = memoryBean.getHeapMemoryUsage().getUsed() / (1024 * 1024); long maxMemory = memoryBean.getHeapMemoryUsage().getMax() / (1024 * 1024); - SwingUtilities.invokeLater(() -> - memoryLabel.setText(String.format("Memory Usage: %d MB / %d MB", usedMemory, maxMemory)) + SwingUtilities.invokeLater(() -> + memoryLabel.setText(String.format("Memory: %d MB / %d MB", usedMemory, maxMemory)) ); - // Update CPU usage if available if (osBean instanceof com.sun.management.OperatingSystemMXBean) { double cpuLoad = ((com.sun.management.OperatingSystemMXBean) osBean).getSystemCpuLoad() * 100; - SwingUtilities.invokeLater(() -> - cpuLabel.setText(String.format("CPU Usage: %.1f%%", cpuLoad)) + SwingUtilities.invokeLater(() -> + cpuLabel.setText(String.format("CPU: %.1f%%", cpuLoad)) ); } }); @@ -203,16 +240,15 @@ public class ServerDashboard extends JFrame { } private void startNetworkStatsTimer() { + // ... (This method remains unchanged) networkStatsTimer = new Timer(1000, e -> refreshNetworkTable()); networkStatsTimer.start(); } public void refreshNetworkTable() { + // ... (This method remains unchanged) SwingUtilities.invokeLater(() -> { - // Clear existing rows networkTableModel.setRowCount(0); - - // Add current connections for (ClientConnection conn : SessionManager.getAllConnections()) { Vector row = new Vector<>(); row.add(conn.getSessionId()); @@ -221,21 +257,13 @@ public class ServerDashboard extends JFrame { row.add(conn.getUserRole()); row.add(conn.getConnectionDuration()); row.add(conn.getLastActivityDuration()); - row.add(formatBytes(conn.getBytesSent())); - row.add(formatBytes(conn.getBytesReceived())); networkTableModel.addRow(row); } }); } - private String formatBytes(long bytes) { - if (bytes < 1024) return bytes + " B"; - if (bytes < 1024 * 1024) return String.format("%.1f KB", bytes / 1024.0); - if (bytes < 1024 * 1024 * 1024) return String.format("%.1f MB", bytes / (1024.0 * 1024)); - return String.format("%.1f GB", bytes / (1024.0 * 1024 * 1024)); - } - private void searchInLogs() { + // ... (This method remains unchanged) String searchText = searchField.getText().toLowerCase(); if (searchText.isEmpty()) { return; @@ -248,9 +276,9 @@ public class ServerDashboard extends JFrame { int index = content.toLowerCase().indexOf(searchText); while (index >= 0) { logArea.getHighlighter().addHighlight( - index, - index + searchText.length(), - new DefaultHighlighter.DefaultHighlightPainter(Color.YELLOW) + index, + index + searchText.length(), + new DefaultHighlighter.DefaultHighlightPainter(Color.YELLOW) ); index = content.toLowerCase().indexOf(searchText, index + 1); } @@ -260,6 +288,7 @@ public class ServerDashboard extends JFrame { } private void clearLogs() { + // ... (This method remains unchanged) SwingUtilities.invokeLater(() -> { try { logArea.getDocument().remove(0, logArea.getDocument().getLength()); @@ -271,6 +300,7 @@ public class ServerDashboard extends JFrame { } public void updateStatus(String status, Color color) { + // ... (This method remains unchanged) SwingUtilities.invokeLater(() -> { statusLabel.setText("Status: " + status); statusLabel.setForeground(color); @@ -278,47 +308,48 @@ public class ServerDashboard extends JFrame { } public void updatePort(int port) { - SwingUtilities.invokeLater(() -> + // ... (This method remains unchanged) + SwingUtilities.invokeLater(() -> portLabel.setText("Port: " + port) ); } public static void incrementClients() { + // ... (This method remains unchanged) connectedClients++; updateClientCount(); } public static void decrementClients() { - connectedClients = Math.max(0, connectedClients - 1); // Ensure we don't go below 0 + // ... (This method remains unchanged) + connectedClients = Math.max(0, connectedClients - 1); updateClientCount(); } private static void updateClientCount() { + // ... (This method remains unchanged) if (instance != null) { SwingUtilities.invokeLater(() -> { instance.clientsLabel.setText("Connected Clients: " + connectedClients); - // Also update the network table since client count changed instance.refreshNetworkTable(); }); } } public void addLog(String message) { + // ... (This method remains unchanged) SwingUtilities.invokeLater(() -> { try { Document doc = logArea.getDocument(); String timestamp = timeFormat.format(new Date()); String formattedMessage = String.format("[%s] %s%n", timestamp, message); - - // Create style for new text + Style style = logArea.addStyle("Log Style", null); StyleConstants.setFontFamily(style, "Monospace"); StyleConstants.setFontSize(style, 12); - - // Insert the text at the end + doc.insertString(doc.getLength(), formattedMessage, style); - - // Scroll to bottom if auto-scroll is enabled + if (isAutoScrollEnabled) { logArea.setCaretPosition(doc.getLength()); } @@ -329,96 +360,60 @@ public class ServerDashboard extends JFrame { } public void updateGoogleDriveStatus(boolean connected, String statusMessage) { + // ... (This method remains unchanged) SwingUtilities.invokeLater(() -> { if (connected) { - googleDriveLabel.setText("Google Drive: ✅ " + statusMessage); - googleDriveLabel.setForeground(Color.GREEN); - googleDriveButton.setText("Test Drive"); + googleDriveLabel.setText("✅ " + statusMessage); + googleDriveLabel.setForeground(new Color(34, 139, 34)); // Forest Green + googleDriveButton.setText("Test"); googleDriveButton.setToolTipText("Test Google Drive connection"); } else { - googleDriveLabel.setText("Google Drive: ❌ " + statusMessage); + googleDriveLabel.setText("❌ " + statusMessage); googleDriveLabel.setForeground(Color.RED); - googleDriveButton.setText("Retry Drive"); + googleDriveButton.setText("Retry"); googleDriveButton.setToolTipText("Retry Google Drive connection"); } }); } private void retryGoogleDriveConnection() { + // ... (This method remains unchanged) SwingUtilities.invokeLater(() -> { - googleDriveLabel.setText("Google Drive: 🔄 Connecting..."); + googleDriveLabel.setText("🔄 Connecting..."); googleDriveLabel.setForeground(Color.ORANGE); googleDriveButton.setEnabled(false); - addLog("Retrying Google Drive connection..."); + addLog("Testing Google Drive connection..."); }); - // Run connection attempt in background thread + // Run connection attempt in a background thread new Thread(() -> { try { + // This method should attempt the connection and update the server's internal state. BsK.server.Server.retryGoogleDriveConnection(); - SwingUtilities.invokeLater(() -> { - googleDriveButton.setEnabled(true); - if (BsK.server.Server.isGoogleDriveConnected()) { - addLog("Google Drive connection successful ✅"); - } else { - addLog("Google Drive connection failed ❌"); - } - }); + + // After the attempt, get the result from the server's state + boolean isConnected = BsK.server.Server.isGoogleDriveConnected(); + String message = isConnected ? "Connection test successful" : "Connection test failed"; + + // *** THIS IS THE FIX *** + // Call the standard UI update method with the result. + updateGoogleDriveStatus(isConnected, message); + } catch (Exception e) { - SwingUtilities.invokeLater(() -> { - googleDriveButton.setEnabled(true); - addLog("Google Drive connection error: " + e.getMessage()); - }); - } - }).start(); - } + log.error("Google Drive connection test failed with an exception", e); + // Also update the UI in case of an exception + updateGoogleDriveStatus(false, "Connection error"); + addLog("Google Drive connection error: " + e.getMessage()); + } finally { + // ALWAYS re-enable the button on the UI thread + SwingUtilities.invokeLater(() -> googleDriveButton.setEnabled(true)); + } + }).start(); + } - private String getCurrentDriveRootFolder() { - // Get current root folder name from Server or return default - try { - if (BsK.server.Server.getGoogleDriveService() != null) { - return BsK.server.Server.getGoogleDriveRootFolderName(); - } - } catch (Exception e) { - log.warn("Could not get current drive root folder name", e); - } - return "BSK_Clinic_Patient_Files"; // Default value - } - - private void applyDriveSettings(String newRootFolderName) { - if (newRootFolderName == null || newRootFolderName.trim().isEmpty()) { - addLog("❌ Root folder name cannot be empty"); - return; - } - - // Sanitize folder name - String sanitizedName = newRootFolderName.replaceAll("[^a-zA-Z0-9._-]", "_"); - if (!sanitizedName.equals(newRootFolderName)) { - addLog("⚠️ Folder name sanitized to: " + sanitizedName); - } - - addLog("🔄 Updating Google Drive root folder to: " + sanitizedName); - - new Thread(() -> { - try { - // Update the root folder name in server - BsK.server.Server.updateGoogleDriveRootFolder(sanitizedName); - - SwingUtilities.invokeLater(() -> { - addLog("✅ Google Drive root folder updated successfully"); - addLog("💡 New patient folders will be created under: " + sanitizedName); - }); - } catch (Exception e) { - SwingUtilities.invokeLater(() -> { - addLog("❌ Failed to update Google Drive root folder: " + e.getMessage()); - }); - log.error("Error updating Google Drive root folder", e); - } - }).start(); - } - - @Override - public void dispose() { + @Override + public void dispose() { + // ... (This method remains unchanged) if (statsTimer != null) { statsTimer.stop(); } @@ -427,4 +422,4 @@ public class ServerDashboard extends JFrame { } super.dispose(); } -} \ No newline at end of file +} \ No newline at end of file diff --git a/src/main/java/BsK/server/network/entity/User.java b/src/main/java/BsK/server/network/entity/User.java index d03834e..7f55646 100644 --- a/src/main/java/BsK/server/network/entity/User.java +++ b/src/main/java/BsK/server/network/entity/User.java @@ -51,4 +51,11 @@ public class User { public boolean isAuthenticated() { return role != Role.GUEST; } + + public void resetAuthentication() { + this.role = Role.GUEST; + this.userId = -1; // Or some other indicator of not being logged in + SessionManager.updateUserRole(channel.id().asLongText(), this.role.toString(), this.userId); + log.info("User session {} has been de-authenticated.", sessionId); + } } diff --git a/src/main/java/BsK/server/network/handler/ServerHandler.java b/src/main/java/BsK/server/network/handler/ServerHandler.java index 879918e..a716914 100644 --- a/src/main/java/BsK/server/network/handler/ServerHandler.java +++ b/src/main/java/BsK/server/network/handler/ServerHandler.java @@ -58,7 +58,6 @@ public class ServerHandler extends SimpleChannelInboundHandlerMg-WM^M=ZEy*bVJ|xYJhPoIWDpqu000RMeE<)$4`&Y} z4&Dx#4p9yZ4Z;n5vk@?34Yz9z1nL2oXAcCKf>4G~0ftZlhEM~BPy~ih1%^-thENBF zPzZ)l35HM#hENNJPz;7p4Tew-hENZNP!P6I5fs1%2(bp~2G+M45E6_50dKdb5fa`6 z1PZ+XWRoHLO%{V#ZwD8HQ*Q?sD=PtpU36h$Y=cE_m%thl903KF-Vzcomk<~dj+5`< z5tpkQ5^xA|Wn^D&Z**morQ;xz$>SE6j~WsNmtPzbCbzK|5)1+a000B3hrt>Wx4{|{ RCO82>hv-rhx9Cz77#)$QVnYA` delta 446 zcmZp85ZLe_aDuepECvQfcOW)nU|`q@q_rpN7_-h|(5s8wn6g!#bvFM4{(#Mb33~jZ z!dA@M{MO9MjG@J;Me!-=@g=zh%##!Ktr@L0&(%*6;N`!;z|3FHz+cY4p8pO|k3WBm zIui$jI1sRkC#K|P<{=12Ge!;uMNw@@R`J5p%)Fe;JR?K%fQ-aEm&Ckug}~CvybJ}O z%)AUELj!XIQ^qNh$1h|!y7+qfAxxaSB41tu=xP=vW)22XAYc_O1-c9w1NEJ3eGwY$ z8q^ff7QomRz|ZQbOAgN(rl3FtWD42om1@B1l9+762Qag^mCK