mirror of
https://github.com/tiennm99/BSK-All-in-One-Clinic-Management-System.git
synced 2026-10-11 03:12:53 +00:00
[update] Remove unused Times New Roman font dependency from pom.xml. Enhance Client and ClientHandler classes with improved network resilience features, including IdleStateHandler for better timeout management and user-friendly error dialogs for connection issues. Update Server class to include IdleStateHandler for improved tolerance of network delays.
This commit is contained in:
1 parent
ae96a1a4da
commit
4ebce2b32a
9 files changed
+642
-13
No files matched your search
Binary file not shown.
@@ -246,12 +246,5 @@
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.45.1.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.yourcompany</groupId>
|
||||
<artifactId>jasper-fonts-timesnewroman</artifactId>
|
||||
<version>1.0</version>
|
||||
<scope>system</scope>
|
||||
<systemPath>${project.basedir}/src/main/resources/fonts/timesnewroman-fonts.jar</systemPath>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -13,6 +13,7 @@ import io.netty.handler.codec.http.HttpClientCodec;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -45,11 +46,13 @@ public class Client {
|
||||
1. Máy chủ chưa được khởi động
|
||||
2. Địa chỉ hoặc cổng không chính xác
|
||||
3. Tường lửa đang chặn kết nối
|
||||
4. Mạng internet/WiFi bị ngắt kết nối
|
||||
|
||||
Giải pháp:
|
||||
1. Kiểm tra xem máy chủ đã được khởi động chưa
|
||||
2. Kiểm tra lại cấu hình trong file config.properties
|
||||
3. Tắt ứng dụng và khởi động lại
|
||||
1. Kiểm tra kết nối internet/WiFi
|
||||
2. Kiểm tra xem máy chủ đã được khởi động chưa
|
||||
3. Kiểm tra lại cấu hình trong file config.properties
|
||||
4. Tắt ứng dụng và khởi động lại
|
||||
|
||||
Chi tiết lỗi: %s
|
||||
""",
|
||||
@@ -186,16 +189,37 @@ public class Client {
|
||||
true,
|
||||
new DefaultHttpHeaders(),
|
||||
50 * 1024 * 1024))
|
||||
// Add IdleStateHandler with longer timeouts for network resilience
|
||||
// Read timeout: 60 seconds, Write timeout: 30 seconds
|
||||
.addLast(new IdleStateHandler(60, 30, 0))
|
||||
.addLast("ws", ClientHandler.INSTANCE);
|
||||
}
|
||||
});
|
||||
log.info("Connecting to {}:{}", serverAddress, 1999);
|
||||
Channel channel = bootstrap.connect(serverAddress, 1999).sync().channel();
|
||||
|
||||
// Store the channel for potential reconnection
|
||||
ClientHandler.ctx = channel.pipeline().get(ClientHandler.class).ctx;
|
||||
|
||||
// Wait for channel to close, but handle it gracefully
|
||||
channel.closeFuture().sync();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Connection error", e);
|
||||
showConnectionErrorDialog(serverAddress, 1999, e);
|
||||
|
||||
// Show more specific error message for different types of connection failures
|
||||
if (e instanceof java.net.ConnectException) {
|
||||
showConnectionErrorDialog(serverAddress, 1999,
|
||||
new Exception("Không thể kết nối đến máy chủ. Máy chủ có thể chưa được khởi động hoặc địa chỉ/cổng không đúng."));
|
||||
} else if (e instanceof java.net.UnknownHostException) {
|
||||
showConnectionErrorDialog(serverAddress, 1999,
|
||||
new Exception("Không tìm thấy máy chủ. Kiểm tra lại địa chỉ máy chủ và kết nối mạng."));
|
||||
} else if (e instanceof java.net.SocketTimeoutException) {
|
||||
showConnectionErrorDialog(serverAddress, 1999,
|
||||
new Exception("Hết thời gian chờ kết nối. Kiểm tra kết nối mạng và thử lại."));
|
||||
} else {
|
||||
showConnectionErrorDialog(serverAddress, 1999, e);
|
||||
}
|
||||
return;
|
||||
} finally {
|
||||
try {
|
||||
|
||||
@@ -13,10 +13,13 @@ import io.netty.handler.codec.http.websocketx.*;
|
||||
import io.netty.handler.timeout.IdleState;
|
||||
import io.netty.handler.timeout.IdleStateEvent;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@@ -25,6 +28,12 @@ public class ClientHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
public static ChannelHandlerContext ctx;
|
||||
public static TextWebSocketFrame frame;
|
||||
public static final ClientHandler INSTANCE = new ClientHandler();
|
||||
|
||||
// Network resilience tracking
|
||||
private static volatile boolean internetIssueDialogShown = false;
|
||||
private static volatile long lastNetworkIssueTime = 0;
|
||||
private static final long NETWORK_ISSUE_DIALOG_COOLDOWN = 30000; // 30 seconds
|
||||
private static volatile boolean dialogCurrentlyShowing = false;
|
||||
|
||||
private static final Map<Class<?>, List<ResponseListener<?>>> listeners = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -113,6 +122,14 @@ public class ClientHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
if (evt instanceof IdleStateEvent) {
|
||||
IdleStateEvent event = (IdleStateEvent) evt;
|
||||
if (event.state() == IdleState.WRITER_IDLE) {
|
||||
// Send ping to keep connection alive
|
||||
log.debug("Sending ping to server");
|
||||
ctx.writeAndFlush(new PingWebSocketFrame());
|
||||
} else if (event.state() == IdleState.READER_IDLE) {
|
||||
// Server hasn't responded in a while - show internet issue dialog but don't close immediately
|
||||
log.warn("Server not responding - possible network issues");
|
||||
showInternetIssueDialog();
|
||||
// Try to send a ping to test connection
|
||||
ctx.writeAndFlush(new PingWebSocketFrame());
|
||||
}
|
||||
}
|
||||
@@ -120,12 +137,213 @@ public class ClientHandler extends SimpleChannelInboundHandler<TextWebSocketFram
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||
if (cause instanceof IOException) {
|
||||
log.error("Connection closed");
|
||||
log.error("Connection exception: {}", cause.getMessage());
|
||||
|
||||
// Check if it's a network-related exception that might be temporary
|
||||
if (cause instanceof IOException ||
|
||||
cause instanceof java.nio.channels.ClosedChannelException ||
|
||||
cause instanceof java.net.SocketException) {
|
||||
|
||||
// Show internet issue dialog instead of immediately closing
|
||||
showInternetIssueDialogBlocking();
|
||||
|
||||
// Only close after multiple failures or if explicitly requested
|
||||
log.warn("Network exception occurred, showing warning dialog");
|
||||
} else {
|
||||
// For other exceptions, show the connection lost dialog and exit
|
||||
showConnectionLostDialog();
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
|
||||
log.warn("Channel became inactive - connection lost");
|
||||
|
||||
// Show internet issue dialog and give it time to display
|
||||
showInternetIssueDialogBlocking();
|
||||
|
||||
// Add a small delay to ensure dialog shows before channel cleanup
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
super.channelInactive(ctx);
|
||||
// The dialog will handle the exit decision
|
||||
}
|
||||
|
||||
private void showInternetIssueDialog() {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
|
||||
// Only show dialog if cooldown period has passed to avoid spam
|
||||
if (internetIssueDialogShown && (currentTime - lastNetworkIssueTime) < NETWORK_ISSUE_DIALOG_COOLDOWN) {
|
||||
return;
|
||||
}
|
||||
|
||||
internetIssueDialogShown = true;
|
||||
lastNetworkIssueTime = currentTime;
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
String message = """
|
||||
Phát hiện vấn đề kết nối mạng!
|
||||
|
||||
Tình trạng:
|
||||
• Mạng internet chậm hoặc không ổn định
|
||||
• Một số yêu cầu có thể bị trễ hoặc thất bại
|
||||
• Ứng dụng sẽ tiếp tục hoạt động nhưng có thể chậm
|
||||
|
||||
Nguyên nhân có thể:
|
||||
1. Kết nối WiFi/Internet không ổn định
|
||||
2. Tốc độ mạng chậm
|
||||
3. Gián đoạn mạng tạm thời
|
||||
|
||||
Giải pháp:
|
||||
1. Kiểm tra kết nối internet/WiFi
|
||||
2. Thử thao tác lại nếu có lỗi
|
||||
3. Đợi mạng ổn định hơn
|
||||
4. Khởi động lại ứng dụng nếu cần thiết
|
||||
|
||||
Ứng dụng sẽ tiếp tục hoạt động...
|
||||
""";
|
||||
|
||||
int option = JOptionPane.showOptionDialog(
|
||||
null,
|
||||
message,
|
||||
"Cảnh Báo - Vấn Đề Mạng",
|
||||
JOptionPane.YES_NO_OPTION,
|
||||
JOptionPane.WARNING_MESSAGE,
|
||||
null,
|
||||
new String[]{"Tiếp Tục", "Đóng Ứng Dụng"},
|
||||
"Tiếp Tục"
|
||||
);
|
||||
|
||||
if (option == 1) { // User chose to close application
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
// Reset the flag after some time to allow showing the dialog again if needed
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Thread.sleep(NETWORK_ISSUE_DIALOG_COOLDOWN);
|
||||
internetIssueDialogShown = false;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}).start();
|
||||
});
|
||||
}
|
||||
|
||||
private void showInternetIssueDialogBlocking() {
|
||||
long currentTime = System.currentTimeMillis();
|
||||
|
||||
// Only show dialog if cooldown period has passed to avoid spam
|
||||
if ((internetIssueDialogShown && (currentTime - lastNetworkIssueTime) < NETWORK_ISSUE_DIALOG_COOLDOWN) || dialogCurrentlyShowing) {
|
||||
return;
|
||||
}
|
||||
|
||||
internetIssueDialogShown = true;
|
||||
dialogCurrentlyShowing = true;
|
||||
lastNetworkIssueTime = currentTime;
|
||||
|
||||
// Show dialog directly on current thread to ensure it displays before app can exit
|
||||
try {
|
||||
// Force the dialog to show on the EDT and block until it's handled
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
// If already on EDT, show directly
|
||||
showNetworkIssueDialog();
|
||||
} else {
|
||||
// If on another thread, switch to EDT and wait
|
||||
SwingUtilities.invokeAndWait(this::showNetworkIssueDialog);
|
||||
}
|
||||
} catch (InterruptedException | InvocationTargetException e) {
|
||||
dialogCurrentlyShowing = false;
|
||||
System.err.println("Error showing dialog: " + e.getMessage());
|
||||
// Show a simple message as fallback
|
||||
System.err.println("NETWORK ISSUE DETECTED - Please restart the application");
|
||||
}
|
||||
|
||||
dialogCurrentlyShowing = false;
|
||||
|
||||
// Reset the flag after some time to allow showing the dialog again if needed
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Thread.sleep(NETWORK_ISSUE_DIALOG_COOLDOWN);
|
||||
internetIssueDialogShown = false;
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}, "NetworkDialogReset").start();
|
||||
}
|
||||
|
||||
private void showNetworkIssueDialog() {
|
||||
String message = """
|
||||
Phát hiện vấn đề kết nối mạng!
|
||||
|
||||
Tình trạng:
|
||||
• Mạng internet chậm hoặc không ổn định
|
||||
• Một số yêu cầu có thể bị trễ hoặc thất bại
|
||||
• Ứng dụng sẽ tiếp tục hoạt động nhưng có thể chậm
|
||||
|
||||
Nguyên nhân có thể:
|
||||
1. Kết nối WiFi/Internet không ổn định
|
||||
2. Tốc độ mạng chậm
|
||||
3. Gián đoạn mạng tạm thời
|
||||
|
||||
Giải pháp:
|
||||
1. Kiểm tra kết nối internet/WiFi
|
||||
2. Thử thao tác lại nếu có lỗi
|
||||
3. Đợi mạng ổn định hơn
|
||||
4. Khởi động lại ứng dụng nếu cần thiết
|
||||
|
||||
Ứng dụng sẽ tiếp tục hoạt động...
|
||||
""";
|
||||
|
||||
int option = JOptionPane.showOptionDialog(
|
||||
null,
|
||||
message,
|
||||
"Cảnh Báo - Vấn Đề Mạng",
|
||||
JOptionPane.YES_NO_OPTION,
|
||||
JOptionPane.WARNING_MESSAGE,
|
||||
null,
|
||||
new String[]{"Tiếp Tục", "Đóng Ứng Dụng"},
|
||||
"Tiếp Tục"
|
||||
);
|
||||
|
||||
if (option == 1) { // User chose to close application
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void showConnectionLostDialog() {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
String message = """
|
||||
Mất kết nối với máy chủ!
|
||||
|
||||
Nguyên nhân có thể:
|
||||
1. Mạng internet bị ngắt kết nối
|
||||
2. WiFi bị mất kết nối
|
||||
3. Máy chủ đã ngừng hoạt động
|
||||
4. Lỗi mạng tạm thời
|
||||
|
||||
Giải pháp:
|
||||
1. Kiểm tra kết nối internet/WiFi
|
||||
2. Khởi động lại ứng dụng
|
||||
3. Liên hệ quản trị viên nếu vấn đề tiếp tục
|
||||
|
||||
Ứng dụng sẽ đóng sau khi nhấn OK.
|
||||
""";
|
||||
|
||||
JOptionPane.showMessageDialog(
|
||||
null,
|
||||
message,
|
||||
"Cảnh Báo - Mất Kết Nối",
|
||||
JOptionPane.WARNING_MESSAGE
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> void notifyListener(ResponseListener<?> listener, T response) {
|
||||
try {
|
||||
|
||||
@@ -17,6 +17,7 @@ import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
|
||||
import io.netty.handler.stream.ChunkedWriteHandler;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
import java.awt.Color;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -293,6 +294,9 @@ public class Server {
|
||||
.addLast(new ChunkedWriteHandler())
|
||||
.addLast(new HttpObjectAggregator(50 * 1024 * 1024))
|
||||
.addLast(new WebSocketServerProtocolHandler("/", null, true, 50 * 1024 * 1024))
|
||||
// Add IdleStateHandler with longer timeout to be more tolerant of network issues
|
||||
// Reader timeout: 90 seconds (longer than client's 60s to allow for network delays)
|
||||
.addLast(new IdleStateHandler(90, 0, 0))
|
||||
.addLast(new ServerHandler());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
@echo off
|
||||
REM BSK Client Update Script Launcher
|
||||
REM This batch file runs the PowerShell update script with proper execution policy
|
||||
|
||||
echo BSK Client Update Launcher
|
||||
echo =========================
|
||||
echo.
|
||||
|
||||
REM Change to the script directory
|
||||
cd /d "%~dp0"
|
||||
|
||||
REM Run the PowerShell script with execution policy bypass
|
||||
powershell -ExecutionPolicy Bypass -File ".\update_client_script.ps1"
|
||||
|
||||
REM Keep the window open if there's an error
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo An error occurred. Press any key to exit...
|
||||
pause >nul
|
||||
)
|
||||
@@ -0,0 +1,175 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Checks for a new version of the client JAR application, downloads it, and replaces the old version.
|
||||
|
||||
.DESCRIPTION
|
||||
This script is dedicated to updating the client application.
|
||||
It reads a local version_client.json file to determine the current version.
|
||||
It then fetches a remote latest_client.json file from a specified URL to see if a newer version is available.
|
||||
If an update is found, it stops the running client, downloads the new JAR,
|
||||
backs it up, installs the new one, and restarts the client.
|
||||
|
||||
.NOTES
|
||||
Author: Dat Le
|
||||
Date: 2025-09-07
|
||||
Version: 1.1.0 (Client Only)
|
||||
#>
|
||||
|
||||
# --- CONFIGURATION ---
|
||||
# IMPORTANT: This URL should now point to your 'latest_client.json' file on GitHub.
|
||||
$remoteVersionInfoUrl = "https://github.com/lds217/BSK-All-in-One-Clinic-Management-System/releases/download/v1.1.0/latest.json"
|
||||
|
||||
# --- SCRIPT START ---
|
||||
$ProgressPreference = 'Continue'
|
||||
|
||||
# Get the directory where this script is located.
|
||||
$scriptPath = $PSScriptRoot
|
||||
# Get the directory where this script is located.
|
||||
$scriptPath = $PSScriptRoot
|
||||
|
||||
# Define the paths for the local files (now client-specific)
|
||||
$localVersionFile = Join-Path $scriptPath "version_client.json"
|
||||
$jarFileName = "client.jar"
|
||||
$localJarFile = Join-Path $scriptPath $jarFileName
|
||||
$backupJarFile = Join-Path $scriptPath "$($jarFileName).old"
|
||||
|
||||
# --- Step 1: Read the local version information ---
|
||||
Write-Host "Reading local client version info from '$localVersionFile'..."
|
||||
if (-not (Test-Path $localVersionFile)) {
|
||||
Write-Error "Error: Local version file not found at '$localVersionFile'. Cannot proceed."
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
|
||||
try {
|
||||
$localVersionInfo = Get-Content $localVersionFile | ConvertFrom-Json
|
||||
Write-Host " - Local Client Version: $($localVersionInfo.currentVersion)"
|
||||
}
|
||||
catch {
|
||||
Write-Error "Error: Could not read or parse the local version_client.json file. Is it valid JSON?"
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
# --- Step 2: Fetch the remote version information ---
|
||||
Write-Host "Fetching remote client version info from '$remoteVersionInfoUrl'..."
|
||||
try {
|
||||
$remoteVersionInfo = Invoke-RestMethod -Uri $remoteVersionInfoUrl
|
||||
Write-Host " - Latest Remote Version: $($remoteVersionInfo.latestVersion)"
|
||||
}
|
||||
catch {
|
||||
Write-Error "Error: Failed to fetch or parse remote version info. Check the URL and your internet connection."
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
# --- Step 3: Compare versions ---
|
||||
if ([version]$localVersionInfo.currentVersion -ge [version]$remoteVersionInfo.latestVersion) {
|
||||
Write-Host "`nResult: Your client application is up-to-date! (Version $($localVersionInfo.currentVersion))"
|
||||
}
|
||||
else {
|
||||
Write-Host "`nResult: New version available! (Version $($remoteVersionInfo.latestVersion)). Starting update process."
|
||||
Write-Host "Release Notes: $($remoteVersionInfo.releaseNotes)"
|
||||
|
||||
# Get the correct download URL (now simplified)
|
||||
$downloadUrl = $remoteVersionInfo.files.client
|
||||
if (-not $downloadUrl) {
|
||||
Write-Error "Error: 'downloadUrl' not found in remote JSON."
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Download URL: $downloadUrl"
|
||||
|
||||
# --- Step 4: Stop the application ---
|
||||
Write-Host "`nStopping application..."
|
||||
try {
|
||||
# This command finds the java process running 'client.jar' and stops it.
|
||||
Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like "*$jarFileName*" } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
|
||||
Write-Host " - Application stopped successfully."
|
||||
Start-Sleep -Seconds 3 # Give the OS time to release file locks
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Could not automatically stop the application. Please stop it manually before continuing."
|
||||
Read-Host "Press Enter to continue after stopping the application"
|
||||
}
|
||||
|
||||
|
||||
# --- Step 5: Download and Replace ---
|
||||
$tempDownloadPath = Join-Path $scriptPath "client.jar.download"
|
||||
|
||||
Write-Host "`nDownloading new version..."
|
||||
try {
|
||||
# First, get the file size for progress calculation
|
||||
$response = Invoke-WebRequest -Uri $downloadUrl -Method Head
|
||||
$totalSize = [int64]$response.Headers.'Content-Length'
|
||||
|
||||
Write-Host "File size: $([math]::Round($totalSize/1MB, 2)) MB"
|
||||
Write-Host "Starting download with progress indicator..."
|
||||
|
||||
# Use Start-BitsTransfer for better progress display if available
|
||||
if (Get-Command Start-BitsTransfer -ErrorAction SilentlyContinue) {
|
||||
Start-BitsTransfer -Source $downloadUrl -Destination $tempDownloadPath -DisplayName "Downloading client.jar" -Description "BSK Client Update"
|
||||
}
|
||||
else {
|
||||
# Fallback: Custom progress implementation
|
||||
$webClient = New-Object System.Net.WebClient
|
||||
|
||||
# Register progress event
|
||||
Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -Action {
|
||||
$percent = $Event.SourceEventArgs.ProgressPercentage
|
||||
$received = $Event.SourceEventArgs.BytesReceived
|
||||
$total = $Event.SourceEventArgs.TotalBytesToReceive
|
||||
$speed = [math]::Round($received / 1MB, 2)
|
||||
|
||||
Write-Progress -Activity "Downloading client.jar" -Status "$percent% Complete" -PercentComplete $percent -CurrentOperation "Downloaded: $speed MB / $([math]::Round($total/1MB, 2)) MB"
|
||||
} | Out-Null
|
||||
|
||||
try {
|
||||
$webClient.DownloadFile($downloadUrl, $tempDownloadPath)
|
||||
Write-Progress -Activity "Downloading client.jar" -Completed
|
||||
}
|
||||
finally {
|
||||
$webClient.Dispose()
|
||||
Get-EventSubscriber | Unregister-Event
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Download completed successfully!"
|
||||
}
|
||||
catch {
|
||||
Write-Error "Error: Download failed. Please check the URL and your connection."
|
||||
if (Test-Path $tempDownloadPath) { Remove-Item $tempDownloadPath }
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "`nInstalling update..."
|
||||
if (Test-Path $localJarFile) {
|
||||
Write-Host " - Backing up old version to '$backupJarFile'..."
|
||||
Move-Item -Path $localJarFile -Destination $backupJarFile -Force
|
||||
}
|
||||
|
||||
Write-Host " - Installing new version..."
|
||||
Move-Item -Path $tempDownloadPath -Destination $localJarFile -Force
|
||||
|
||||
|
||||
# --- Step 6: Update the local version file ---
|
||||
Write-Host " - Updating local version number..."
|
||||
# Create a new JSON object to ensure the file is clean
|
||||
@{ currentVersion = $remoteVersionInfo.latestVersion } | ConvertTo-Json -Depth 1 | Set-Content $localVersionFile
|
||||
|
||||
|
||||
# --- Step 7: Restart the application ---
|
||||
# !! CRITICAL !! You MUST customize this step.
|
||||
Write-Host "`nRestarting application (you may need to customize this step)..."
|
||||
# Example: Start-Process "java" -ArgumentList "-jar", $localJarFile
|
||||
# Example: Start-Process ".\start_client.bat"
|
||||
|
||||
Write-Host "`nUpdate complete! Now running version $($remoteVersionInfo.latestVersion)."
|
||||
}
|
||||
|
||||
# Pause at the end
|
||||
Read-Host "`nPress Enter to exit."
|
||||
@@ -0,0 +1,20 @@
|
||||
@echo off
|
||||
REM BSK Server Update Script Launcher
|
||||
REM This batch file runs the PowerShell update script with proper execution policy
|
||||
|
||||
echo BSK Server Update Launcher
|
||||
echo ========================
|
||||
echo.
|
||||
|
||||
REM Change to the script directory
|
||||
cd /d "%~dp0"
|
||||
|
||||
REM Run the PowerShell script with execution policy bypass
|
||||
powershell -ExecutionPolicy Bypass -File ".\update_server_script.ps1"
|
||||
|
||||
REM Keep the window open if there's an error
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo An error occurred. Press any key to exit...
|
||||
pause >nul
|
||||
)
|
||||
@@ -0,0 +1,175 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Checks for a new version of the server JAR application, downloads it, and replaces the old version.
|
||||
|
||||
.DESCRIPTION
|
||||
This script is dedicated to updating the server application.
|
||||
It reads a local version_server.json file to determine the current version.
|
||||
It then fetches a remote latest_server.json file from a specified URL to see if a newer version is available.
|
||||
If an update is found, it stops the running server, downloads the new JAR,
|
||||
backs it up, installs the new one, and restarts the server.
|
||||
|
||||
.NOTES
|
||||
Author: Dat Le
|
||||
Date: 2025-09-07
|
||||
Version: 1.1.0 (Server Only)
|
||||
#>
|
||||
|
||||
# --- CONFIGURATION ---
|
||||
# IMPORTANT: This URL should now point to your 'latest_server.json' file on GitHub.
|
||||
$remoteVersionInfoUrl = "https://github.com/lds217/BSK-All-in-One-Clinic-Management-System/releases/download/v1.1.0/latest.json"
|
||||
|
||||
# --- SCRIPT START ---
|
||||
$ProgressPreference = 'Continue'
|
||||
|
||||
# Get the directory where this script is located.
|
||||
$scriptPath = $PSScriptRoot
|
||||
# Get the directory where this script is located.
|
||||
$scriptPath = $PSScriptRoot
|
||||
|
||||
# Define the paths for the local files (now server-specific)
|
||||
$localVersionFile = Join-Path $scriptPath "version_server.json"
|
||||
$jarFileName = "server.jar"
|
||||
$localJarFile = Join-Path $scriptPath $jarFileName
|
||||
$backupJarFile = Join-Path $scriptPath "$($jarFileName).old"
|
||||
|
||||
# --- Step 1: Read the local version information ---
|
||||
Write-Host "Reading local server version info from '$localVersionFile'..."
|
||||
if (-not (Test-Path $localVersionFile)) {
|
||||
Write-Error "Error: Local version file not found at '$localVersionFile'. Cannot proceed."
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
|
||||
try {
|
||||
$localVersionInfo = Get-Content $localVersionFile | ConvertFrom-Json
|
||||
Write-Host " - Local Server Version: $($localVersionInfo.currentVersion)"
|
||||
}
|
||||
catch {
|
||||
Write-Error "Error: Could not read or parse the local version_server.json file. Is it valid JSON?"
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
# --- Step 2: Fetch the remote version information ---
|
||||
Write-Host "Fetching remote server version info from '$remoteVersionInfoUrl'..."
|
||||
try {
|
||||
$remoteVersionInfo = Invoke-RestMethod -Uri $remoteVersionInfoUrl
|
||||
Write-Host " - Latest Remote Version: $($remoteVersionInfo.latestVersion)"
|
||||
}
|
||||
catch {
|
||||
Write-Error "Error: Failed to fetch or parse remote version info. Check the URL and your internet connection."
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
# --- Step 3: Compare versions ---
|
||||
if ([version]$localVersionInfo.currentVersion -ge [version]$remoteVersionInfo.latestVersion) {
|
||||
Write-Host "`nResult: Your server application is up-to-date! (Version $($localVersionInfo.currentVersion))"
|
||||
}
|
||||
else {
|
||||
Write-Host "`nResult: New version available! (Version $($remoteVersionInfo.latestVersion)). Starting update process."
|
||||
Write-Host "Release Notes: $($remoteVersionInfo.releaseNotes)"
|
||||
|
||||
# Get the correct download URL (now simplified)
|
||||
$downloadUrl = $remoteVersionInfo.files.server
|
||||
if (-not $downloadUrl) {
|
||||
Write-Error "Error: 'downloadUrl' not found in remote JSON."
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Download URL: $downloadUrl"
|
||||
|
||||
# --- Step 4: Stop the application ---
|
||||
Write-Host "`nStopping application..."
|
||||
try {
|
||||
# This command finds the java process running 'server.jar' and stops it.
|
||||
Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like "*$jarFileName*" } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force }
|
||||
Write-Host " - Application stopped successfully."
|
||||
Start-Sleep -Seconds 3 # Give the OS time to release file locks
|
||||
}
|
||||
catch {
|
||||
Write-Warning "Could not automatically stop the application. Please stop it manually before continuing."
|
||||
Read-Host "Press Enter to continue after stopping the application"
|
||||
}
|
||||
|
||||
|
||||
# --- Step 5: Download and Replace ---
|
||||
$tempDownloadPath = Join-Path $scriptPath "server.jar.download"
|
||||
|
||||
Write-Host "`nDownloading new version..."
|
||||
try {
|
||||
# First, get the file size for progress calculation
|
||||
$response = Invoke-WebRequest -Uri $downloadUrl -Method Head
|
||||
$totalSize = [int64]$response.Headers.'Content-Length'
|
||||
|
||||
Write-Host "File size: $([math]::Round($totalSize/1MB, 2)) MB"
|
||||
Write-Host "Starting download with progress indicator..."
|
||||
|
||||
# Use Start-BitsTransfer for better progress display if available
|
||||
if (Get-Command Start-BitsTransfer -ErrorAction SilentlyContinue) {
|
||||
Start-BitsTransfer -Source $downloadUrl -Destination $tempDownloadPath -DisplayName "Downloading server.jar" -Description "BSK Server Update"
|
||||
}
|
||||
else {
|
||||
# Fallback: Custom progress implementation
|
||||
$webClient = New-Object System.Net.WebClient
|
||||
|
||||
# Register progress event
|
||||
Register-ObjectEvent -InputObject $webClient -EventName DownloadProgressChanged -Action {
|
||||
$percent = $Event.SourceEventArgs.ProgressPercentage
|
||||
$received = $Event.SourceEventArgs.BytesReceived
|
||||
$total = $Event.SourceEventArgs.TotalBytesToReceive
|
||||
$speed = [math]::Round($received / 1MB, 2)
|
||||
|
||||
Write-Progress -Activity "Downloading server.jar" -Status "$percent% Complete" -PercentComplete $percent -CurrentOperation "Downloaded: $speed MB / $([math]::Round($total/1MB, 2)) MB"
|
||||
} | Out-Null
|
||||
|
||||
try {
|
||||
$webClient.DownloadFile($downloadUrl, $tempDownloadPath)
|
||||
Write-Progress -Activity "Downloading server.jar" -Completed
|
||||
}
|
||||
finally {
|
||||
$webClient.Dispose()
|
||||
Get-EventSubscriber | Unregister-Event
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Download completed successfully!"
|
||||
}
|
||||
catch {
|
||||
Write-Error "Error: Download failed. Please check the URL and your connection."
|
||||
if (Test-Path $tempDownloadPath) { Remove-Item $tempDownloadPath }
|
||||
Read-Host "Press Enter to exit"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "`nInstalling update..."
|
||||
if (Test-Path $localJarFile) {
|
||||
Write-Host " - Backing up old version to '$backupJarFile'..."
|
||||
Move-Item -Path $localJarFile -Destination $backupJarFile -Force
|
||||
}
|
||||
|
||||
Write-Host " - Installing new version..."
|
||||
Move-Item -Path $tempDownloadPath -Destination $localJarFile -Force
|
||||
|
||||
|
||||
# --- Step 6: Update the local version file ---
|
||||
Write-Host " - Updating local version number..."
|
||||
# Create a new JSON object to ensure the file is clean
|
||||
@{ currentVersion = $remoteVersionInfo.latestVersion } | ConvertTo-Json -Depth 1 | Set-Content $localVersionFile
|
||||
|
||||
|
||||
# --- Step 7: Restart the application ---
|
||||
# !! CRITICAL !! You MUST customize this step.
|
||||
Write-Host "`nRestarting application (you may need to customize this step)..."
|
||||
# Example: Start-Process "java" -ArgumentList "-jar", $localJarFile
|
||||
# Example: Start-Process ".\start_server.bat"
|
||||
|
||||
Write-Host "`nUpdate complete! Now running version $($remoteVersionInfo.latestVersion)."
|
||||
}
|
||||
|
||||
# Pause at the end
|
||||
Read-Host "`nPress Enter to exit."
|
||||
Reference in new issue
Block a user