This commit is contained in:
Anthony Stirling 2025-07-25 17:34:16 +02:00 committed by GitHub
commit 3762560cb4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 205 additions and 996 deletions

View File

@ -17,7 +17,7 @@ spotless {
dependencies {
api 'org.springframework.boot:spring-boot-starter-web'
api 'org.springframework.boot:spring-boot-starter-aop'
api 'org.springframework.boot:spring-boot-starter-thymeleaf'
// api 'org.springframework.boot:spring-boot-starter-thymeleaf' // Deprecated - UI moved to React frontend
api 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20240325.1'
api 'com.fathzer:javaluator:3.0.6'
api 'com.posthog.java:posthog:1.2.0'

View File

@ -10,7 +10,6 @@ import java.util.Properties;
import java.util.function.Predicate;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
@ -19,9 +18,7 @@ import org.springframework.context.annotation.Scope;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.ClassUtils;
import org.thymeleaf.spring6.SpringTemplateEngine;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@ -51,6 +48,7 @@ public class AppConfig {
@Value("${server.port:8080}")
private String serverPort;
/* Commented out Thymeleaf template engine bean - to be removed when frontend migration is complete
@Bean
@ConditionalOnProperty(name = "system.customHTMLFiles", havingValue = "true")
public SpringTemplateEngine templateEngine(ResourceLoader resourceLoader) {
@ -58,6 +56,7 @@ public class AppConfig {
templateEngine.addTemplateResolver(new FileFallbackTemplateResolver(resourceLoader));
return templateEngine;
}
*/
@Bean(name = "loginEnabled")
public boolean loginEnabled() {

View File

@ -1,19 +1,8 @@
package stirling.software.common.configuration;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
/* Commented out entire FileFallbackTemplateResolver class - Thymeleaf dependency removed
* This class will be removed when frontend migration to React is complete
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.thymeleaf.IEngineConfiguration;
import org.thymeleaf.templateresolver.AbstractConfigurableTemplateResolver;
import org.thymeleaf.templateresource.FileTemplateResource;
import org.thymeleaf.templateresource.ITemplateResource;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.model.InputStreamTemplateResource;
@Slf4j
public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateResolver {
@ -57,3 +46,4 @@ public class FileFallbackTemplateResolver extends AbstractConfigurableTemplateRe
return null;
}
}
*/

View File

@ -1,11 +1,8 @@
package stirling.software.common.model;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
/* Commented out entire InputStreamTemplateResource class - Thymeleaf dependency removed
* This class will be removed when frontend migration to React is complete
import org.thymeleaf.templateresource.ITemplateResource;
public class InputStreamTemplateResource implements ITemplateResource {
private InputStream inputStream;
@ -42,3 +39,4 @@ public class InputStreamTemplateResource implements ITemplateResource {
return inputStream != null;
}
}
*/

View File

@ -10,7 +10,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
@ -23,7 +22,6 @@ import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import stirling.software.SPDF.UI.WebBrowser;
import stirling.software.common.configuration.AppConfig;
import stirling.software.common.configuration.ConfigInitializer;
import stirling.software.common.configuration.InstallationPathConfig;
@ -47,21 +45,20 @@ public class SPDFApplication {
private final AppConfig appConfig;
private final Environment env;
private final ApplicationProperties applicationProperties;
private final WebBrowser webBrowser;
// private final WebBrowser webBrowser; // Removed - desktop UI eliminated
public SPDFApplication(
AppConfig appConfig,
Environment env,
ApplicationProperties applicationProperties,
@Autowired(required = false) WebBrowser webBrowser) {
AppConfig appConfig, Environment env, ApplicationProperties applicationProperties) {
this.appConfig = appConfig;
this.env = env;
this.applicationProperties = applicationProperties;
this.webBrowser = webBrowser;
// this.webBrowser = webBrowser; // Removed - desktop UI eliminated
}
public static void main(String[] args) throws IOException, InterruptedException {
SpringApplication app = new SpringApplication(SPDFApplication.class);
app.setLazyInitialization(true); // Optimize startup time
Properties props = new Properties();
@ -151,28 +148,31 @@ public class SPDFApplication {
serverPortStatic = serverPort;
String url = baseUrl + ":" + getStaticPort() + contextPath;
if (webBrowser != null
&& Boolean.parseBoolean(System.getProperty("STIRLING_PDF_DESKTOP_UI", "false"))) {
webBrowser.initWebUI(url);
} else {
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
boolean browserOpen = browserOpenEnv != null && "true".equalsIgnoreCase(browserOpenEnv);
if (browserOpen) {
try {
String os = System.getProperty("os.name").toLowerCase();
Runtime rt = Runtime.getRuntime();
// Desktop UI initialization removed - webBrowser dependency eliminated
// Keep backwards compatibility for STIRLING_PDF_DESKTOP_UI system property
if (Boolean.parseBoolean(System.getProperty("STIRLING_PDF_DESKTOP_UI", "false"))) {
log.info("Desktop UI mode enabled, but WebBrowser functionality has been removed");
// webBrowser.initWebUI(url); // Removed - desktop UI eliminated
}
if (os.contains("win")) {
// For Windows
SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url);
} else if (os.contains("mac")) {
SystemCommand.runCommand(rt, "open " + url);
} else if (os.contains("nix") || os.contains("nux")) {
SystemCommand.runCommand(rt, "xdg-open " + url);
}
} catch (IOException e) {
log.error("Error opening browser: {}", e.getMessage());
// Standard browser opening logic
String browserOpenEnv = env.getProperty("BROWSER_OPEN");
boolean browserOpen = browserOpenEnv != null && "true".equalsIgnoreCase(browserOpenEnv);
if (browserOpen) {
try {
String os = System.getProperty("os.name").toLowerCase();
Runtime rt = Runtime.getRuntime();
if (os.contains("win")) {
// For Windows
SystemCommand.runCommand(rt, "rundll32 url.dll,FileProtocolHandler " + url);
} else if (os.contains("mac")) {
SystemCommand.runCommand(rt, "open " + url);
} else if (os.contains("nix") || os.contains("nux")) {
SystemCommand.runCommand(rt, "xdg-open " + url);
}
} catch (IOException e) {
log.error("Error opening browser: {}", e.getMessage());
}
}
}
@ -189,9 +189,10 @@ public class SPDFApplication {
@PreDestroy
public void cleanup() {
if (webBrowser != null) {
webBrowser.cleanup();
}
// webBrowser cleanup removed - desktop UI eliminated
// if (webBrowser != null) {
// webBrowser.cleanup();
// }
}
private static void printStartupLogs() {

View File

@ -1,7 +0,0 @@
package stirling.software.SPDF.UI;
public interface WebBrowser {
void initWebUI(String url);
void cleanup();
}

View File

@ -1,497 +0,0 @@
package stirling.software.SPDF.UI.impl;
import java.awt.AWTException;
import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.WindowEvent;
import java.awt.event.WindowStateListener;
import java.io.File;
import java.io.InputStream;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import org.cef.CefApp;
import org.cef.CefClient;
import org.cef.CefSettings;
import org.cef.browser.CefBrowser;
import org.cef.callback.CefBeforeDownloadCallback;
import org.cef.callback.CefDownloadItem;
import org.cef.callback.CefDownloadItemCallback;
import org.cef.handler.CefDownloadHandlerAdapter;
import org.cef.handler.CefLoadHandlerAdapter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import me.friwi.jcefmaven.CefAppBuilder;
import me.friwi.jcefmaven.EnumProgress;
import me.friwi.jcefmaven.MavenCefAppHandlerAdapter;
import me.friwi.jcefmaven.impl.progress.ConsoleProgressHandler;
import stirling.software.SPDF.UI.WebBrowser;
import stirling.software.common.configuration.InstallationPathConfig;
import stirling.software.common.util.UIScaling;
@Component
@Slf4j
@ConditionalOnProperty(
name = "STIRLING_PDF_DESKTOP_UI",
havingValue = "true",
matchIfMissing = false)
public class DesktopBrowser implements WebBrowser {
private static CefApp cefApp;
private static CefClient client;
private static CefBrowser browser;
private static JFrame frame;
private static LoadingWindow loadingWindow;
private static volatile boolean browserInitialized = false;
private static TrayIcon trayIcon;
private static SystemTray systemTray;
public DesktopBrowser() {
SwingUtilities.invokeLater(
() -> {
loadingWindow = new LoadingWindow(null, "Initializing...");
loadingWindow.setVisible(true);
});
}
public void initWebUI(String url) {
CompletableFuture.runAsync(
() -> {
try {
CefAppBuilder builder = new CefAppBuilder();
configureCefSettings(builder);
builder.setProgressHandler(createProgressHandler());
builder.setInstallDir(
new File(InstallationPathConfig.getClientWebUIPath()));
// Build and initialize CEF
cefApp = builder.build();
client = cefApp.createClient();
// Set up download handler
setupDownloadHandler();
// Create browser and frame on EDT
SwingUtilities.invokeAndWait(
() -> {
browser = client.createBrowser(url, false, false);
setupMainFrame();
setupLoadHandler();
// Force initialize UI after 7 seconds if not already done
Timer timeoutTimer =
new Timer(
2500,
e -> {
log.warn(
"Loading timeout reached. Forcing"
+ " UI transition.");
if (!browserInitialized) {
// Force UI initialization
forceInitializeUI();
}
});
timeoutTimer.setRepeats(false);
timeoutTimer.start();
});
} catch (Exception e) {
log.error("Error initializing JCEF browser: ", e);
cleanup();
}
});
}
private void configureCefSettings(CefAppBuilder builder) {
CefSettings settings = builder.getCefSettings();
String basePath = InstallationPathConfig.getClientWebUIPath();
log.info("basePath " + basePath);
settings.cache_path = new File(basePath + "cache").getAbsolutePath();
settings.root_cache_path = new File(basePath + "root_cache").getAbsolutePath();
// settings.browser_subprocess_path = new File(basePath +
// "subprocess").getAbsolutePath();
// settings.resources_dir_path = new File(basePath + "resources").getAbsolutePath();
// settings.locales_dir_path = new File(basePath + "locales").getAbsolutePath();
settings.log_file = new File(basePath, "debug.log").getAbsolutePath();
settings.persist_session_cookies = true;
settings.windowless_rendering_enabled = false;
settings.log_severity = CefSettings.LogSeverity.LOGSEVERITY_INFO;
builder.setAppHandler(
new MavenCefAppHandlerAdapter() {
@Override
public void stateHasChanged(org.cef.CefApp.CefAppState state) {
log.info("CEF state changed: " + state);
if (state == CefApp.CefAppState.TERMINATED) {
System.exit(0);
}
}
});
}
private void setupDownloadHandler() {
client.addDownloadHandler(
new CefDownloadHandlerAdapter() {
@Override
public boolean onBeforeDownload(
CefBrowser browser,
CefDownloadItem downloadItem,
String suggestedName,
CefBeforeDownloadCallback callback) {
callback.Continue("", true);
return true;
}
@Override
public void onDownloadUpdated(
CefBrowser browser,
CefDownloadItem downloadItem,
CefDownloadItemCallback callback) {
if (downloadItem.isComplete()) {
log.info("Download completed: " + downloadItem.getFullPath());
} else if (downloadItem.isCanceled()) {
log.info("Download canceled: " + downloadItem.getFullPath());
}
}
});
}
private ConsoleProgressHandler createProgressHandler() {
return new ConsoleProgressHandler() {
@Override
public void handleProgress(EnumProgress state, float percent) {
Objects.requireNonNull(state, "state cannot be null");
SwingUtilities.invokeLater(
() -> {
if (loadingWindow != null) {
switch (state) {
case LOCATING:
loadingWindow.setStatus("Locating Files...");
loadingWindow.setProgress(0);
break;
case DOWNLOADING:
if (percent >= 0) {
loadingWindow.setStatus(
String.format(
"Downloading additional files: %.0f%%",
percent));
loadingWindow.setProgress((int) percent);
}
break;
case EXTRACTING:
loadingWindow.setStatus("Extracting files...");
loadingWindow.setProgress(60);
break;
case INITIALIZING:
loadingWindow.setStatus("Initializing UI...");
loadingWindow.setProgress(80);
break;
case INITIALIZED:
loadingWindow.setStatus("Finalising startup...");
loadingWindow.setProgress(90);
break;
}
}
});
}
};
}
private void setupMainFrame() {
frame = new JFrame("Stirling-PDF");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.setUndecorated(true);
frame.setOpacity(0.0f);
JPanel contentPane = new JPanel(new BorderLayout());
contentPane.setDoubleBuffered(true);
contentPane.add(browser.getUIComponent(), BorderLayout.CENTER);
frame.setContentPane(contentPane);
frame.addWindowListener(
new java.awt.event.WindowAdapter() {
@Override
public void windowClosing(java.awt.event.WindowEvent windowEvent) {
cleanup();
System.exit(0);
}
});
frame.setSize(UIScaling.scaleWidth(1280), UIScaling.scaleHeight(800));
frame.setLocationRelativeTo(null);
loadIcon();
}
private void setupLoadHandler() {
final long initStartTime = System.currentTimeMillis();
log.info("Setting up load handler at: {}", initStartTime);
client.addLoadHandler(
new CefLoadHandlerAdapter() {
@Override
public void onLoadingStateChange(
CefBrowser browser,
boolean isLoading,
boolean canGoBack,
boolean canGoForward) {
log.debug(
"Loading state change - isLoading: {}, canGoBack: {}, canGoForward:"
+ " {}, browserInitialized: {}, Time elapsed: {}ms",
isLoading,
canGoBack,
canGoForward,
browserInitialized,
System.currentTimeMillis() - initStartTime);
if (!isLoading && !browserInitialized) {
log.info(
"Browser finished loading, preparing to initialize UI"
+ " components");
browserInitialized = true;
SwingUtilities.invokeLater(
() -> {
try {
if (loadingWindow != null) {
log.info("Starting UI initialization sequence");
// Close loading window first
loadingWindow.setVisible(false);
loadingWindow.dispose();
loadingWindow = null;
log.info("Loading window disposed");
// Then setup the main frame
frame.setVisible(false);
frame.dispose();
frame.setOpacity(1.0f);
frame.setUndecorated(false);
frame.pack();
frame.setSize(
UIScaling.scaleWidth(1280),
UIScaling.scaleHeight(800));
frame.setLocationRelativeTo(null);
log.debug("Frame reconfigured");
// Show the main frame
frame.setVisible(true);
frame.requestFocus();
frame.toFront();
log.info("Main frame displayed and focused");
// Focus the browser component
Timer focusTimer =
new Timer(
100,
e -> {
try {
browser.getUIComponent()
.requestFocus();
log.info(
"Browser component"
+ " focused");
} catch (Exception ex) {
log.error(
"Error focusing"
+ " browser",
ex);
}
});
focusTimer.setRepeats(false);
focusTimer.start();
}
} catch (Exception e) {
log.error("Error during UI initialization", e);
// Attempt cleanup on error
if (loadingWindow != null) {
loadingWindow.dispose();
loadingWindow = null;
}
if (frame != null) {
frame.setVisible(true);
frame.requestFocus();
}
}
});
}
}
});
}
private void setupTrayIcon(Image icon) {
if (!SystemTray.isSupported()) {
log.warn("System tray is not supported");
return;
}
try {
systemTray = SystemTray.getSystemTray();
// Create popup menu
PopupMenu popup = new PopupMenu();
// Create menu items
MenuItem showItem = new MenuItem("Show");
showItem.addActionListener(
e -> {
frame.setVisible(true);
frame.setState(Frame.NORMAL);
});
MenuItem exitItem = new MenuItem("Exit");
exitItem.addActionListener(
e -> {
cleanup();
System.exit(0);
});
// Add menu items to popup menu
popup.add(showItem);
popup.addSeparator();
popup.add(exitItem);
// Create tray icon
trayIcon = new TrayIcon(icon, "Stirling-PDF", popup);
trayIcon.setImageAutoSize(true);
// Add double-click behavior
trayIcon.addActionListener(
e -> {
frame.setVisible(true);
frame.setState(Frame.NORMAL);
});
// Add tray icon to system tray
systemTray.add(trayIcon);
// Modify frame behavior to minimize to tray
frame.addWindowStateListener(
new WindowStateListener() {
public void windowStateChanged(WindowEvent e) {
if (e.getNewState() == Frame.ICONIFIED) {
frame.setVisible(false);
}
}
});
} catch (AWTException e) {
log.error("Error setting up system tray icon", e);
}
}
private void loadIcon() {
try {
Image icon = null;
String[] iconPaths = {"/static/favicon.ico"};
for (String path : iconPaths) {
if (icon != null) break;
try {
try (InputStream is = getClass().getResourceAsStream(path)) {
if (is != null) {
icon = ImageIO.read(is);
break;
}
}
} catch (Exception e) {
log.debug("Could not load icon from " + path, e);
}
}
if (icon != null) {
frame.setIconImage(icon);
setupTrayIcon(icon);
} else {
log.warn("Could not load icon from any source");
}
} catch (Exception e) {
log.error("Error loading icon", e);
}
}
@PreDestroy
public void cleanup() {
if (browser != null) browser.close(true);
if (client != null) client.dispose();
if (cefApp != null) cefApp.dispose();
if (loadingWindow != null) loadingWindow.dispose();
}
public static void forceInitializeUI() {
try {
if (loadingWindow != null) {
log.info("Forcing start of UI initialization sequence");
// Close loading window first
loadingWindow.setVisible(false);
loadingWindow.dispose();
loadingWindow = null;
log.info("Loading window disposed");
// Then setup the main frame
frame.setVisible(false);
frame.dispose();
frame.setOpacity(1.0f);
frame.setUndecorated(false);
frame.pack();
frame.setSize(UIScaling.scaleWidth(1280), UIScaling.scaleHeight(800));
frame.setLocationRelativeTo(null);
log.debug("Frame reconfigured");
// Show the main frame
frame.setVisible(true);
frame.requestFocus();
frame.toFront();
log.info("Main frame displayed and focused");
// Focus the browser component if available
if (browser != null) {
Timer focusTimer =
new Timer(
100,
e -> {
try {
browser.getUIComponent().requestFocus();
log.info("Browser component focused");
} catch (Exception ex) {
log.error(
"Error focusing browser during force ui"
+ " initialization.",
ex);
}
});
focusTimer.setRepeats(false);
focusTimer.start();
}
}
} catch (Exception e) {
log.error("Error during Forced UI initialization.", e);
// Attempt cleanup on error
if (loadingWindow != null) {
loadingWindow.dispose();
loadingWindow = null;
}
if (frame != null) {
frame.setVisible(true);
frame.setOpacity(1.0f);
frame.setUndecorated(false);
frame.requestFocus();
}
}
}
}

View File

@ -1,351 +0,0 @@
package stirling.software.SPDF.UI.impl;
import java.awt.*;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.imageio.ImageIO;
import javax.swing.*;
import io.github.pixee.security.BoundedLineReader;
import lombok.extern.slf4j.Slf4j;
import stirling.software.common.util.UIScaling;
@Slf4j
public class LoadingWindow extends JDialog {
private final JProgressBar progressBar;
private final JLabel statusLabel;
private final JPanel mainPanel;
private final JLabel brandLabel;
private long startTime;
private Timer stuckTimer;
private long stuckThreshold = 4000;
private long timeAt90Percent = -1;
private volatile Process explorerProcess;
private static final boolean IS_WINDOWS =
System.getProperty("os.name").toLowerCase().contains("win");
public LoadingWindow(Frame parent, String initialUrl) {
super(parent, "Initializing Stirling-PDF", true);
startTime = System.currentTimeMillis();
log.info("Creating LoadingWindow - initialization started at: {}", startTime);
// Initialize components
mainPanel = new JPanel();
mainPanel.setBackground(Color.WHITE);
mainPanel.setBorder(BorderFactory.createEmptyBorder(20, 30, 20, 30));
mainPanel.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
// Configure GridBagConstraints
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(5, 5, 5, 5);
gbc.weightx = 1.0;
gbc.weighty = 0.0;
// Add icon
try {
try (InputStream is = getClass().getResourceAsStream("/static/favicon.ico")) {
if (is != null) {
Image img = ImageIO.read(is);
if (img != null) {
Image scaledImg = UIScaling.scaleIcon(img, 48, 48);
JLabel iconLabel = new JLabel(new ImageIcon(scaledImg));
iconLabel.setHorizontalAlignment(SwingConstants.CENTER);
gbc.gridy = 0;
mainPanel.add(iconLabel, gbc);
log.info("Icon loaded and scaled successfully");
}
}
}
} catch (Exception e) {
log.error("Failed to load icon", e);
}
// URL Label with explicit size
brandLabel = new JLabel(initialUrl);
brandLabel.setHorizontalAlignment(SwingConstants.CENTER);
brandLabel.setPreferredSize(new Dimension(300, 25));
brandLabel.setText("Stirling-PDF");
gbc.gridy = 1;
mainPanel.add(brandLabel, gbc);
// Status label with explicit size
statusLabel = new JLabel("Initializing...");
statusLabel.setHorizontalAlignment(SwingConstants.CENTER);
statusLabel.setPreferredSize(new Dimension(300, 25));
gbc.gridy = 2;
mainPanel.add(statusLabel, gbc);
// Progress bar with explicit size
progressBar = new JProgressBar(0, 100);
progressBar.setStringPainted(true);
progressBar.setPreferredSize(new Dimension(300, 25));
gbc.gridy = 3;
mainPanel.add(progressBar, gbc);
// Set dialog properties
setContentPane(mainPanel);
setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
setResizable(false);
setUndecorated(false);
// Set size and position
setSize(UIScaling.scaleWidth(400), UIScaling.scaleHeight(200));
setLocationRelativeTo(parent);
setAlwaysOnTop(true);
setProgress(0);
setStatus("Starting...");
log.info(
"LoadingWindow initialization completed in {}ms",
System.currentTimeMillis() - startTime);
}
private void checkAndRefreshExplorer() {
if (!IS_WINDOWS) {
return;
}
if (timeAt90Percent == -1) {
timeAt90Percent = System.currentTimeMillis();
stuckTimer =
new Timer(
1000,
e -> {
long currentTime = System.currentTimeMillis();
if (currentTime - timeAt90Percent > stuckThreshold) {
try {
log.debug(
"Attempting Windows explorer refresh due to 90% stuck state");
String currentDir = System.getProperty("user.dir");
// Store current explorer PIDs before we start new one
Set<String> existingPids = new HashSet<>();
ProcessBuilder listExplorer =
new ProcessBuilder(
"cmd",
"/c",
"wmic",
"process",
"where",
"name='explorer.exe'",
"get",
"ProcessId",
"/format:csv");
Process process = listExplorer.start();
BufferedReader reader =
new BufferedReader(
new InputStreamReader(
process.getInputStream()));
String line;
while ((line =
BoundedLineReader.readLine(
reader, 5_000_000))
!= null) {
if (line.matches(".*\\d+.*")) { // Contains numbers
String[] parts = line.trim().split(",");
if (parts.length >= 2) {
existingPids.add(
parts[parts.length - 1].trim());
}
}
}
process.waitFor(2, TimeUnit.SECONDS);
// Start new explorer
ProcessBuilder pb =
new ProcessBuilder(
"cmd",
"/c",
"start",
"/min",
"/b",
"explorer.exe",
currentDir);
pb.redirectErrorStream(true);
explorerProcess = pb.start();
// Schedule cleanup
Timer cleanupTimer =
new Timer(
2000,
cleanup -> {
try {
// Find new explorer processes
ProcessBuilder findNewExplorer =
new ProcessBuilder(
"cmd",
"/c",
"wmic",
"process",
"where",
"name='explorer.exe'",
"get",
"ProcessId",
"/format:csv");
Process newProcess =
findNewExplorer.start();
BufferedReader newReader =
new BufferedReader(
new InputStreamReader(
newProcess
.getInputStream()));
String newLine;
while ((newLine =
BoundedLineReader
.readLine(
newReader,
5_000_000))
!= null) {
if (newLine.matches(
".*\\d+.*")) {
String[] parts =
newLine.trim()
.split(",");
if (parts.length >= 2) {
String pid =
parts[
parts.length
- 1]
.trim();
if (!existingPids
.contains(
pid)) {
log.debug(
"Found new explorer.exe with PID: "
+ pid);
ProcessBuilder
killProcess =
new ProcessBuilder(
"taskkill",
"/PID",
pid,
"/F");
killProcess
.redirectErrorStream(
true);
Process killResult =
killProcess
.start();
killResult.waitFor(
2,
TimeUnit
.SECONDS);
log.debug(
"Explorer process terminated: "
+ pid);
}
}
}
}
newProcess.waitFor(
2, TimeUnit.SECONDS);
} catch (Exception ex) {
log.error(
"Error cleaning up Windows explorer process",
ex);
}
});
cleanupTimer.setRepeats(false);
cleanupTimer.start();
stuckTimer.stop();
} catch (Exception ex) {
log.error("Error refreshing Windows explorer", ex);
}
}
});
stuckTimer.setRepeats(true);
stuckTimer.start();
}
}
public void setProgress(final int progress) {
SwingUtilities.invokeLater(
() -> {
try {
int validProgress = Math.min(Math.max(progress, 0), 100);
log.info(
"Setting progress to {}% at {}ms since start",
validProgress, System.currentTimeMillis() - startTime);
// Log additional details when near 90%
if (validProgress >= 85 && validProgress <= 95) {
log.info(
"Near 90% progress - Current status: {}, Window visible: {}, "
+ "Progress bar responding: {}, Memory usage: {}MB",
statusLabel.getText(),
isVisible(),
progressBar.isEnabled(),
Runtime.getRuntime().totalMemory() / (1024 * 1024));
// Add thread state logging
Thread currentThread = Thread.currentThread();
log.info(
"Current thread state - Name: {}, State: {}, Priority: {}",
currentThread.getName(),
currentThread.getState(),
currentThread.getPriority());
if (validProgress >= 90 && validProgress < 95) {
checkAndRefreshExplorer();
} else {
// Reset the timer if we move past 95%
if (validProgress >= 95) {
if (stuckTimer != null) {
stuckTimer.stop();
}
timeAt90Percent = -1;
}
}
}
progressBar.setValue(validProgress);
progressBar.setString(validProgress + "%");
mainPanel.revalidate();
mainPanel.repaint();
} catch (Exception e) {
log.error("Error updating progress to " + progress, e);
}
});
}
public void setStatus(final String status) {
log.info(
"Status update at {}ms - Setting status to: {}",
System.currentTimeMillis() - startTime,
status);
SwingUtilities.invokeLater(
() -> {
try {
String validStatus = status != null ? status : "";
statusLabel.setText(validStatus);
// Log UI state when status changes
log.info(
"UI State - Window visible: {}, Progress: {}%, Status: {}",
isVisible(), progressBar.getValue(), validStatus);
mainPanel.revalidate();
mainPanel.repaint();
} catch (Exception e) {
log.error("Error updating status to: " + status, e);
}
});
}
@Override
public void dispose() {
log.info("LoadingWindow disposing after {}ms", System.currentTimeMillis() - startTime);
super.dispose();
}
}

View File

@ -2,7 +2,6 @@ package stirling.software.SPDF.controller.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.Hidden;
@ -14,42 +13,48 @@ import stirling.software.common.util.CheckProgramInstall;
@Tag(name = "Convert", description = "Convert APIs")
public class ConverterWebController {
@GetMapping("/img-to-pdf")
@Deprecated
// @GetMapping("/img-to-pdf")
@Hidden
public String convertImgToPdfForm(Model model) {
model.addAttribute("currentPage", "img-to-pdf");
return "convert/img-to-pdf";
}
@GetMapping("/html-to-pdf")
@Deprecated
// @GetMapping("/html-to-pdf")
@Hidden
public String convertHTMLToPdfForm(Model model) {
model.addAttribute("currentPage", "html-to-pdf");
return "convert/html-to-pdf";
}
@GetMapping("/markdown-to-pdf")
@Deprecated
// @GetMapping("/markdown-to-pdf")
@Hidden
public String convertMarkdownToPdfForm(Model model) {
model.addAttribute("currentPage", "markdown-to-pdf");
return "convert/markdown-to-pdf";
}
@GetMapping("/pdf-to-markdown")
@Deprecated
// @GetMapping("/pdf-to-markdown")
@Hidden
public String convertPdfToMarkdownForm(Model model) {
model.addAttribute("currentPage", "pdf-to-markdown");
return "convert/pdf-to-markdown";
}
@GetMapping("/url-to-pdf")
@Deprecated
// @GetMapping("/url-to-pdf")
@Hidden
public String convertURLToPdfForm(Model model) {
model.addAttribute("currentPage", "url-to-pdf");
return "convert/url-to-pdf";
}
@GetMapping("/file-to-pdf")
@Deprecated
// @GetMapping("/file-to-pdf")
@Hidden
public String convertToPdfForm(Model model) {
model.addAttribute("currentPage", "file-to-pdf");
@ -58,7 +63,8 @@ public class ConverterWebController {
// PDF TO......
@GetMapping("/pdf-to-img")
@Deprecated
// @GetMapping("/pdf-to-img")
@Hidden
public String pdfToimgForm(Model model) {
boolean isPython = CheckProgramInstall.isPythonAvailable();
@ -67,7 +73,8 @@ public class ConverterWebController {
return "convert/pdf-to-img";
}
@GetMapping("/pdf-to-html")
@Deprecated
// @GetMapping("/pdf-to-html")
@Hidden
public ModelAndView pdfToHTML() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-html");
@ -75,7 +82,8 @@ public class ConverterWebController {
return modelAndView;
}
@GetMapping("/pdf-to-presentation")
@Deprecated
// @GetMapping("/pdf-to-presentation")
@Hidden
public ModelAndView pdfToPresentation() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-presentation");
@ -83,7 +91,8 @@ public class ConverterWebController {
return modelAndView;
}
@GetMapping("/pdf-to-text")
@Deprecated
// @GetMapping("/pdf-to-text")
@Hidden
public ModelAndView pdfToText() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-text");
@ -91,7 +100,8 @@ public class ConverterWebController {
return modelAndView;
}
@GetMapping("/pdf-to-word")
@Deprecated
// @GetMapping("/pdf-to-word")
@Hidden
public ModelAndView pdfToWord() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-word");
@ -99,7 +109,8 @@ public class ConverterWebController {
return modelAndView;
}
@GetMapping("/pdf-to-xml")
@Deprecated
// @GetMapping("/pdf-to-xml")
@Hidden
public ModelAndView pdfToXML() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-xml");
@ -107,7 +118,8 @@ public class ConverterWebController {
return modelAndView;
}
@GetMapping("/pdf-to-csv")
@Deprecated
// @GetMapping("/pdf-to-csv")
@Hidden
public ModelAndView pdfToCSV() {
ModelAndView modelAndView = new ModelAndView("convert/pdf-to-csv");
@ -115,14 +127,16 @@ public class ConverterWebController {
return modelAndView;
}
@GetMapping("/pdf-to-pdfa")
@Deprecated
// @GetMapping("/pdf-to-pdfa")
@Hidden
public String pdfToPdfAForm(Model model) {
model.addAttribute("currentPage", "pdf-to-pdfa");
return "convert/pdf-to-pdfa";
}
@GetMapping("/eml-to-pdf")
@Deprecated
// @GetMapping("/eml-to-pdf")
@Hidden
public String convertEmlToPdfForm(Model model) {
model.addAttribute("currentPage", "eml-to-pdf");

View File

@ -14,7 +14,6 @@ import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
@ -53,7 +52,8 @@ public class GeneralWebController {
this.runtimePathConfig = runtimePathConfig;
}
@GetMapping("/pipeline")
@Deprecated
// @GetMapping("/pipeline")
@Hidden
public String pipelineForm(Model model) {
model.addAttribute("currentPage", "pipeline");
@ -103,91 +103,104 @@ public class GeneralWebController {
return "pipeline";
}
@GetMapping("/merge-pdfs")
@Deprecated
// @GetMapping("/merge-pdfs")
@Hidden
public String mergePdfForm(Model model) {
model.addAttribute("currentPage", "merge-pdfs");
return "merge-pdfs";
}
@GetMapping("/split-pdf-by-sections")
@Deprecated
// @GetMapping("/split-pdf-by-sections")
@Hidden
public String splitPdfBySections(Model model) {
model.addAttribute("currentPage", "split-pdf-by-sections");
return "split-pdf-by-sections";
}
@GetMapping("/split-pdf-by-chapters")
@Deprecated
// @GetMapping("/split-pdf-by-chapters")
@Hidden
public String splitPdfByChapters(Model model) {
model.addAttribute("currentPage", "split-pdf-by-chapters");
return "split-pdf-by-chapters";
}
@GetMapping("/view-pdf")
@Deprecated
// @GetMapping("/view-pdf")
@Hidden
public String ViewPdfForm2(Model model) {
model.addAttribute("currentPage", "view-pdf");
return "view-pdf";
}
@GetMapping("/edit-table-of-contents")
@Deprecated
// @GetMapping("/edit-table-of-contents")
@Hidden
public String editTableOfContents(Model model) {
model.addAttribute("currentPage", "edit-table-of-contents");
return "edit-table-of-contents";
}
@GetMapping("/multi-tool")
@Deprecated
// @GetMapping("/multi-tool")
@Hidden
public String multiToolForm(Model model) {
model.addAttribute("currentPage", "multi-tool");
return "multi-tool";
}
@GetMapping("/remove-pages")
@Deprecated
// @GetMapping("/remove-pages")
@Hidden
public String pageDeleter(Model model) {
model.addAttribute("currentPage", "remove-pages");
return "remove-pages";
}
@GetMapping("/pdf-organizer")
@Deprecated
// @GetMapping("/pdf-organizer")
@Hidden
public String pageOrganizer(Model model) {
model.addAttribute("currentPage", "pdf-organizer");
return "pdf-organizer";
}
@GetMapping("/extract-page")
@Deprecated
// @GetMapping("/extract-page")
@Hidden
public String extractPages(Model model) {
model.addAttribute("currentPage", "extract-page");
return "extract-page";
}
@GetMapping("/pdf-to-single-page")
@Deprecated
// @GetMapping("/pdf-to-single-page")
@Hidden
public String pdfToSinglePage(Model model) {
model.addAttribute("currentPage", "pdf-to-single-page");
return "pdf-to-single-page";
}
@GetMapping("/rotate-pdf")
@Deprecated
// @GetMapping("/rotate-pdf")
@Hidden
public String rotatePdfForm(Model model) {
model.addAttribute("currentPage", "rotate-pdf");
return "rotate-pdf";
}
@GetMapping("/split-pdfs")
@Deprecated
// @GetMapping("/split-pdfs")
@Hidden
public String splitPdfForm(Model model) {
model.addAttribute("currentPage", "split-pdfs");
return "split-pdfs";
}
@GetMapping("/sign")
@Deprecated
// @GetMapping("/sign")
@Hidden
public String signForm(Model model) {
String username = "";
@ -202,28 +215,32 @@ public class GeneralWebController {
return "sign";
}
@GetMapping("/multi-page-layout")
@Deprecated
// @GetMapping("/multi-page-layout")
@Hidden
public String multiPageLayoutForm(Model model) {
model.addAttribute("currentPage", "multi-page-layout");
return "multi-page-layout";
}
@GetMapping("/scale-pages")
@Deprecated
// @GetMapping("/scale-pages")
@Hidden
public String scalePagesFrom(Model model) {
model.addAttribute("currentPage", "scale-pages");
return "scale-pages";
}
@GetMapping("/split-by-size-or-count")
@Deprecated
// @GetMapping("/split-by-size-or-count")
@Hidden
public String splitBySizeOrCount(Model model) {
model.addAttribute("currentPage", "split-by-size-or-count");
return "split-by-size-or-count";
}
@GetMapping("/overlay-pdf")
@Deprecated
// @GetMapping("/overlay-pdf")
@Hidden
public String overlayPdf(Model model) {
model.addAttribute("currentPage", "overlay-pdf");
@ -296,21 +313,24 @@ public class GeneralWebController {
}
}
@GetMapping("/crop")
@Deprecated
// @GetMapping("/crop")
@Hidden
public String cropForm(Model model) {
model.addAttribute("currentPage", "crop");
return "crop";
}
@GetMapping("/auto-split-pdf")
@Deprecated
// @GetMapping("/auto-split-pdf")
@Hidden
public String autoSPlitPDFForm(Model model) {
model.addAttribute("currentPage", "auto-split-pdf");
return "auto-split-pdf";
}
@GetMapping("/remove-image-pdf")
@Deprecated
// @GetMapping("/remove-image-pdf")
@Hidden
public String removeImagePdfForm(Model model) {
model.addAttribute("currentPage", "remove-image-pdf");

View File

@ -32,14 +32,16 @@ public class HomeWebController {
private final ApplicationProperties applicationProperties;
@GetMapping("/about")
@Deprecated
// @GetMapping("/about")
@Hidden
public String gameForm(Model model) {
model.addAttribute("currentPage", "about");
return "about";
}
@GetMapping("/licenses")
@Deprecated
// @GetMapping("/licenses")
@Hidden
public String licensesForm(Model model) {
model.addAttribute("currentPage", "licenses");
@ -56,12 +58,14 @@ public class HomeWebController {
return "licenses";
}
@GetMapping("/releases")
@Deprecated
// @GetMapping("/releases")
public String getReleaseNotes(Model model) {
return "releases";
}
@GetMapping("/")
@Deprecated
// @GetMapping("/")
public String home(Model model) {
model.addAttribute("currentPage", "home");
String showSurvey = System.getenv("SHOW_SURVEY");
@ -70,12 +74,14 @@ public class HomeWebController {
return "home";
}
@GetMapping("/home")
@Deprecated
// @GetMapping("/home")
public String root(Model model) {
return "redirect:/";
}
@GetMapping("/home-legacy")
@Deprecated
// @GetMapping("/home-legacy")
public String redirectHomeLegacy() {
return "redirect:/";
}

View File

@ -7,7 +7,6 @@ import java.util.List;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.servlet.ModelAndView;
import io.swagger.v3.oas.annotations.Hidden;
@ -25,21 +24,24 @@ public class OtherWebController {
private final ApplicationProperties applicationProperties;
@GetMapping("/compress-pdf")
@Deprecated
// @GetMapping("/compress-pdf")
@Hidden
public String compressPdfForm(Model model) {
model.addAttribute("currentPage", "compress-pdf");
return "misc/compress-pdf";
}
@GetMapping("/replace-and-invert-color-pdf")
@Deprecated
// @GetMapping("/replace-and-invert-color-pdf")
@Hidden
public String replaceAndInvertColorPdfForm(Model model) {
model.addAttribute("currentPage", "replace-invert-color-pdf");
return "misc/replace-color";
}
@GetMapping("/extract-image-scans")
@Deprecated
// @GetMapping("/extract-image-scans")
@Hidden
public ModelAndView extractImageScansForm() {
ModelAndView modelAndView = new ModelAndView("misc/extract-image-scans");
@ -49,70 +51,80 @@ public class OtherWebController {
return modelAndView;
}
@GetMapping("/show-javascript")
@Deprecated
// @GetMapping("/show-javascript")
@Hidden
public String extractJavascriptForm(Model model) {
model.addAttribute("currentPage", "show-javascript");
return "misc/show-javascript";
}
@GetMapping("/stamp")
@Deprecated
// @GetMapping("/stamp")
@Hidden
public String stampForm(Model model) {
model.addAttribute("currentPage", "stamp");
return "misc/stamp";
}
@GetMapping("/add-page-numbers")
@Deprecated
// @GetMapping("/add-page-numbers")
@Hidden
public String addPageNumbersForm(Model model) {
model.addAttribute("currentPage", "add-page-numbers");
return "misc/add-page-numbers";
}
@GetMapping("/scanner-effect")
@Deprecated
//@GetMapping("/scanner-effect")
@Hidden
public String scannerEffectForm(Model model) {
model.addAttribute("currentPage", "scanner-effect");
return "misc/scanner-effect";
}
@GetMapping("/extract-images")
@Deprecated
// @GetMapping("/extract-images")
@Hidden
public String extractImagesForm(Model model) {
model.addAttribute("currentPage", "extract-images");
return "misc/extract-images";
}
@GetMapping("/flatten")
@Deprecated
// @GetMapping("/flatten")
@Hidden
public String flattenForm(Model model) {
model.addAttribute("currentPage", "flatten");
return "misc/flatten";
}
@GetMapping("/change-metadata")
@Deprecated
// @GetMapping("/change-metadata")
@Hidden
public String addWatermarkForm(Model model) {
model.addAttribute("currentPage", "change-metadata");
return "misc/change-metadata";
}
@GetMapping("/unlock-pdf-forms")
@Deprecated
// @GetMapping("/unlock-pdf-forms")
@Hidden
public String unlockPDFForms(Model model) {
model.addAttribute("currentPage", "unlock-pdf-forms");
return "misc/unlock-pdf-forms";
}
@GetMapping("/compare")
@Deprecated
// @GetMapping("/compare")
@Hidden
public String compareForm(Model model) {
model.addAttribute("currentPage", "compare");
return "misc/compare";
}
@GetMapping("/print-file")
@Deprecated
// @GetMapping("/print-file")
@Hidden
public String printFileForm(Model model) {
model.addAttribute("currentPage", "print-file");
@ -133,7 +145,8 @@ public class OtherWebController {
.toList();
}
@GetMapping("/ocr-pdf")
@Deprecated
// @GetMapping("/ocr-pdf")
@Hidden
public ModelAndView ocrPdfPage() {
ModelAndView modelAndView = new ModelAndView("misc/ocr-pdf");
@ -143,56 +156,64 @@ public class OtherWebController {
return modelAndView;
}
@GetMapping("/add-image")
@Deprecated
// @GetMapping("/add-image")
@Hidden
public String overlayImage(Model model) {
model.addAttribute("currentPage", "add-image");
return "misc/add-image";
}
@GetMapping("/adjust-contrast")
@Deprecated
// @GetMapping("/adjust-contrast")
@Hidden
public String contrast(Model model) {
model.addAttribute("currentPage", "adjust-contrast");
return "misc/adjust-contrast";
}
@GetMapping("/repair")
@Deprecated
// @GetMapping("/repair")
@Hidden
public String repairForm(Model model) {
model.addAttribute("currentPage", "repair");
return "misc/repair";
}
@GetMapping("/remove-blanks")
@Deprecated
// @GetMapping("/remove-blanks")
@Hidden
public String removeBlanksForm(Model model) {
model.addAttribute("currentPage", "remove-blanks");
return "misc/remove-blanks";
}
@GetMapping("/remove-annotations")
@Deprecated
// @GetMapping("/remove-annotations")
@Hidden
public String removeAnnotationsForm(Model model) {
model.addAttribute("currentPage", "remove-annotations");
return "misc/remove-annotations";
}
@GetMapping("/auto-crop")
@Deprecated
// @GetMapping("/auto-crop")
@Hidden
public String autoCropForm(Model model) {
model.addAttribute("currentPage", "auto-crop");
return "misc/auto-crop";
}
@GetMapping("/auto-rename")
@Deprecated
// @GetMapping("/auto-rename")
@Hidden
public String autoRenameForm(Model model) {
model.addAttribute("currentPage", "auto-rename");
return "misc/auto-rename";
}
@GetMapping("/add-attachments")
@Deprecated
// @GetMapping("/add-attachments")
@Hidden
public String attachmentsForm(Model model) {
model.addAttribute("currentPage", "add-attachments");

View File

@ -2,7 +2,6 @@ package stirling.software.SPDF.controller.web;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -11,76 +10,87 @@ import io.swagger.v3.oas.annotations.tags.Tag;
@Tag(name = "Security", description = "Security APIs")
public class SecurityWebController {
@GetMapping("/auto-redact")
@Deprecated
// @GetMapping("/auto-redact")
@Hidden
public String autoRedactForm(Model model) {
model.addAttribute("currentPage", "auto-redact");
return "security/auto-redact";
}
@GetMapping("/redact")
@Deprecated
// @GetMapping("/redact")
public String redactForm(Model model) {
model.addAttribute("currentPage", "redact");
return "security/redact";
}
@GetMapping("/add-password")
@Deprecated
// @GetMapping("/add-password")
@Hidden
public String addPasswordForm(Model model) {
model.addAttribute("currentPage", "add-password");
return "security/add-password";
}
@GetMapping("/change-permissions")
@Deprecated
// @GetMapping("/change-permissions")
@Hidden
public String permissionsForm(Model model) {
model.addAttribute("currentPage", "change-permissions");
return "security/change-permissions";
}
@GetMapping("/remove-password")
@Deprecated
// @GetMapping("/remove-password")
@Hidden
public String removePasswordForm(Model model) {
model.addAttribute("currentPage", "remove-password");
return "security/remove-password";
}
@GetMapping("/add-watermark")
@Deprecated
// @GetMapping("/add-watermark")
@Hidden
public String addWatermarkForm(Model model) {
model.addAttribute("currentPage", "add-watermark");
return "security/add-watermark";
}
@GetMapping("/cert-sign")
@Deprecated
// @GetMapping("/cert-sign")
@Hidden
public String certSignForm(Model model) {
model.addAttribute("currentPage", "cert-sign");
return "security/cert-sign";
}
@GetMapping("/validate-signature")
@Deprecated
// @GetMapping("/validate-signature")
@Hidden
public String certSignVerifyForm(Model model) {
model.addAttribute("currentPage", "validate-signature");
return "security/validate-signature";
}
@GetMapping("/remove-cert-sign")
@Deprecated
// @GetMapping("/remove-cert-sign")
@Hidden
public String certUnSignForm(Model model) {
model.addAttribute("currentPage", "remove-cert-sign");
return "security/remove-cert-sign";
}
@GetMapping("/sanitize-pdf")
@Deprecated
// @GetMapping("/sanitize-pdf")
@Hidden
public String sanitizeForm(Model model) {
model.addAttribute("currentPage", "sanitize-pdf");
return "security/sanitize-pdf";
}
@GetMapping("/get-info-on-pdf")
@Deprecated
// @GetMapping("/get-info-on-pdf")
@Hidden
public String getInfo(Model model) {
model.addAttribute("currentPage", "get-info-on-pdf");

View File

@ -9,7 +9,7 @@ import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
import org.thymeleaf.util.StringUtils;
import org.springframework.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
@ -40,7 +40,7 @@ public class SignatureService {
List<SignatureFile> signatures = new ArrayList<>();
// Get signatures from user's personal folder
if (!StringUtils.isEmptyOrWhitespace(username)) {
if (StringUtils.hasText(username)) {
Path userFolder = Paths.get(SIGNATURE_BASE_PATH, username);
if (Files.exists(userFolder)) {
try {

View File

@ -25,7 +25,7 @@ server.servlet.context-path=${SYSTEM_ROOTURIPATH:/}
spring.devtools.restart.enabled=true
spring.devtools.livereload.enabled=true
spring.devtools.restart.exclude=stirling.software.proprietary.security/**
spring.thymeleaf.encoding=UTF-8
# spring.thymeleaf.encoding=UTF-8 # Disabled - React frontend replaces Thymeleaf
spring.web.resources.mime-mappings.webmanifest=application/manifest+json
spring.mvc.async.request-timeout=${SYSTEM_CONNECTIONTIMEOUTMILLISECONDS:1200000}
@ -35,6 +35,8 @@ spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=false
spring.jpa.hibernate.ddl-auto=update
spring.jpa.defer-datasource-initialization=true
spring.jpa.show-sql=false
server.servlet.session.timeout:30m
# Change the default URL path for OpenAPI JSON
springdoc.api-docs.path=/v1/api-docs

View File

@ -23,6 +23,8 @@ dependencies {
api 'org.springframework:spring-webmvc'
api 'org.springframework.session:spring-session-core'
api "org.springframework.security:spring-security-core:$springSecuritySamlVersion"
api "org.springframework.security:spring-security-web:$springSecuritySamlVersion"
api "org.springframework.security:spring-security-config:$springSecuritySamlVersion"
api "org.springframework.security:spring-security-saml2-service-provider:$springSecuritySamlVersion"
api 'org.springframework.boot:spring-boot-starter-jetty'
api 'org.springframework.boot:spring-boot-starter-security'
@ -35,7 +37,7 @@ dependencies {
// https://mvnrepository.com/artifact/com.bucket4j/bucket4j_jdk17
implementation 'org.bouncycastle:bcprov-jdk18on:1.81'
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE'
// implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.1.3.RELEASE' // Removed - UI moved to React frontend
api 'io.micrometer:micrometer-registry-prometheus'
implementation 'com.unboundid.product.scim2:scim2-sdk-client:4.0.0'
runtimeOnly 'com.h2database:h2:2.3.232' // Don't upgrade h2database

View File

@ -6,7 +6,6 @@ import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -24,8 +23,9 @@ public class DatabaseWebController {
private final DatabaseService databaseService;
@Deprecated
@PreAuthorize("hasRole('ROLE_ADMIN')")
@GetMapping("/database")
// @GetMapping("/database")
public String database(HttpServletRequest request, Model model, Authentication authentication) {
String error = request.getParameter("error");
String confirmed = request.getParameter("infoMessage");

View File

@ -8,7 +8,6 @@ import java.util.Map;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@ -35,7 +34,8 @@ public class TeamWebController {
private final SessionRepository sessionRepository;
private final UserRepository userRepository;
@GetMapping
@Deprecated
// @GetMapping
@PreAuthorize("hasRole('ROLE_ADMIN')")
public String listTeams(HttpServletRequest request, Model model) {
// Get teams with user counts using a DTO projection
@ -86,7 +86,8 @@ public class TeamWebController {
return "accounts/teams";
}
@GetMapping("/{id}")
@Deprecated
// @GetMapping("/{id}")
@PreAuthorize("hasRole('ROLE_ADMIN')")
public String viewTeamDetails(
HttpServletRequest request, @PathVariable("id") Long id, Model model) {