Add ebook support
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -57,4 +60,38 @@ public class AppConfig {
|
||||
if (appName == null) appName = System.getenv("rateLimit");
|
||||
return (appName != null) ? Boolean.valueOf(appName) : false;
|
||||
}
|
||||
|
||||
@Bean(name = "RunningInDocker")
|
||||
public boolean runningInDocker() {
|
||||
return Files.exists(Paths.get("/.dockerenv"));
|
||||
}
|
||||
|
||||
@Bean(name = "bookFormatsInstalled")
|
||||
public boolean bookFormatsInstalled() {
|
||||
System.out.println("astirli " + applicationProperties.getSystem());
|
||||
System.out.println("astirli2 " + applicationProperties.getSystem().getCustomApplications());
|
||||
System.out.println(
|
||||
"astirli3 "
|
||||
+ applicationProperties
|
||||
.getSystem()
|
||||
.getCustomApplications()
|
||||
.isInstallBookFormats());
|
||||
return applicationProperties.getSystem().getCustomApplications().isInstallBookFormats();
|
||||
}
|
||||
|
||||
@Bean(name = "htmlFormatsInstalled")
|
||||
public boolean htmlFormatsInstalled() {
|
||||
System.out.println("astirli4 " + applicationProperties.getSystem());
|
||||
System.out.println("astirli5 " + applicationProperties.getSystem().getCustomApplications());
|
||||
System.out.println(
|
||||
"astirli6 "
|
||||
+ applicationProperties
|
||||
.getSystem()
|
||||
.getCustomApplications()
|
||||
.isInstallAdvancedHtmlToPDF());
|
||||
return applicationProperties
|
||||
.getSystem()
|
||||
.getCustomApplications()
|
||||
.isInstallAdvancedHtmlToPDF();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,14 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.annotation.DependsOn;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
|
||||
@Service
|
||||
@DependsOn({"bookFormatsInstalled"})
|
||||
public class EndpointConfiguration {
|
||||
private static final Logger logger = LoggerFactory.getLogger(EndpointConfiguration.class);
|
||||
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
|
||||
@@ -21,9 +24,14 @@ public class EndpointConfiguration {
|
||||
|
||||
private final ApplicationProperties applicationProperties;
|
||||
|
||||
private boolean bookFormatsInstalled;
|
||||
|
||||
@Autowired
|
||||
public EndpointConfiguration(ApplicationProperties applicationProperties) {
|
||||
public EndpointConfiguration(
|
||||
ApplicationProperties applicationProperties,
|
||||
@Qualifier("bookFormatsInstalled") boolean bookFormatsInstalled) {
|
||||
this.applicationProperties = applicationProperties;
|
||||
this.bookFormatsInstalled = bookFormatsInstalled;
|
||||
init();
|
||||
processEnvironmentConfigs();
|
||||
}
|
||||
@@ -145,6 +153,12 @@ public class EndpointConfiguration {
|
||||
addEndpointToGroup("CLI", "ocr-pdf");
|
||||
addEndpointToGroup("CLI", "html-to-pdf");
|
||||
addEndpointToGroup("CLI", "url-to-pdf");
|
||||
addEndpointToGroup("CLI", "book-to-pdf");
|
||||
addEndpointToGroup("CLI", "pdf-to-book");
|
||||
|
||||
// Calibre
|
||||
addEndpointToGroup("Calibre", "book-to-pdf");
|
||||
addEndpointToGroup("Calibre", "pdf-to-book");
|
||||
|
||||
// python
|
||||
addEndpointToGroup("Python", "extract-image-scans");
|
||||
@@ -215,7 +229,10 @@ public class EndpointConfiguration {
|
||||
private void processEnvironmentConfigs() {
|
||||
List<String> endpointsToRemove = applicationProperties.getEndpoints().getToRemove();
|
||||
List<String> groupsToRemove = applicationProperties.getEndpoints().getGroupsToRemove();
|
||||
|
||||
System.out.println("ASTIRLI7 bookFormatsInstalled=" + bookFormatsInstalled);
|
||||
if (!bookFormatsInstalled) {
|
||||
groupsToRemove.add("Calibre");
|
||||
}
|
||||
if (endpointsToRemove != null) {
|
||||
for (String endpoint : endpointsToRemove) {
|
||||
disableEndpoint(endpoint.trim());
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package stirling.software.SPDF.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import stirling.software.SPDF.model.ApplicationProperties;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor;
|
||||
import stirling.software.SPDF.utils.ProcessExecutor.ProcessExecutorResult;
|
||||
|
||||
@Component
|
||||
public class PostStartupProcesses {
|
||||
|
||||
@Autowired ApplicationProperties applicationProperties;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("RunningInDocker")
|
||||
private boolean runningInDocker;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bookFormatsInstalled")
|
||||
private boolean bookFormatsInstalled;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("htmlFormatsInstalled")
|
||||
private boolean htmlFormatsInstalled;
|
||||
|
||||
@PostConstruct
|
||||
public void runInstallCommandBasedOnEnvironment() throws IOException, InterruptedException {
|
||||
List<List<String>> commands = new ArrayList<>();
|
||||
System.out.println("astirli bookFormatsInstalled=" + bookFormatsInstalled);
|
||||
System.out.println("astirli htmlFormatsInstalled=" + htmlFormatsInstalled);
|
||||
// Checking for DOCKER_INSTALL_BOOK_FORMATS environment variable
|
||||
if (bookFormatsInstalled) {
|
||||
List<String> tmpList = new ArrayList<>();
|
||||
// Set up the timezone configuration commands
|
||||
tmpList.addAll(
|
||||
Arrays.asList(
|
||||
"sh",
|
||||
"-c",
|
||||
"echo 'tzdata tzdata/Areas select Europe' | debconf-set-selections; "
|
||||
+ "echo 'tzdata tzdata/Zones/Europe select Berlin' | debconf-set-selections"));
|
||||
commands.add(tmpList);
|
||||
|
||||
// Install calibre with DEBIAN_FRONTEND set to noninteractive
|
||||
tmpList = new ArrayList<>();
|
||||
tmpList.addAll(
|
||||
Arrays.asList(
|
||||
"sh",
|
||||
"-c",
|
||||
"DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends calibre"));
|
||||
commands.add(tmpList);
|
||||
}
|
||||
|
||||
// Checking for DOCKER_INSTALL_HTML_FORMATS environment variable
|
||||
if (htmlFormatsInstalled) {
|
||||
List<String> tmpList = new ArrayList<>();
|
||||
// Add -y flag for automatic yes to prompts and --no-install-recommends to reduce size
|
||||
tmpList.addAll(
|
||||
Arrays.asList(
|
||||
"apt-get", "install", "wkhtmltopdf", "-y", "--no-install-recommends"));
|
||||
commands.add(tmpList);
|
||||
}
|
||||
|
||||
if (!commands.isEmpty()) {
|
||||
// Run the command
|
||||
if (runningInDocker) {
|
||||
List<String> tmpList = new ArrayList<>();
|
||||
tmpList.addAll(Arrays.asList("apt-get", "update"));
|
||||
commands.add(0, tmpList);
|
||||
|
||||
for (List<String> list : commands) {
|
||||
ProcessExecutorResult returnCode =
|
||||
ProcessExecutor.getInstance(ProcessExecutor.Processes.INSTALL_APP, true)
|
||||
.runCommandWithOutputHandling(list);
|
||||
System.out.println("astirli RC for app installs " + returnCode.getRc());
|
||||
}
|
||||
} else {
|
||||
System.out.println(
|
||||
"astirli Not running inside Docker so skipping automated install process with command.");
|
||||
}
|
||||
|
||||
} else {
|
||||
if (runningInDocker) {
|
||||
System.out.println("astirli No custom apps to install.");
|
||||
} else {
|
||||
System.out.println("astirli No custom apps to install. and not docker");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,12 @@
|
||||
package stirling.software.SPDF.config.security;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.core.Authentication;
|
||||
@@ -18,6 +21,7 @@ import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponseWrapper;
|
||||
import stirling.software.SPDF.model.ApiKeyAuthenticationToken;
|
||||
|
||||
@Component
|
||||
@@ -31,14 +35,28 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||
@Qualifier("loginEnabled")
|
||||
public boolean loginEnabledValue;
|
||||
|
||||
@Value("${redirect.port:}") // Default to empty if not set
|
||||
private String redirectPort;
|
||||
|
||||
@Override
|
||||
protected void doFilterInternal(
|
||||
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||
throws ServletException, IOException {
|
||||
|
||||
// Custom response wrapper to modify the redirect location
|
||||
HttpServletResponseWrapper responseWrapper =
|
||||
new HttpServletResponseWrapper(response) {
|
||||
@Override
|
||||
public void sendRedirect(String location) throws IOException {
|
||||
// Modify the location to include the correct port
|
||||
String modifiedLocation = modifyLocation(location, request);
|
||||
super.sendRedirect(modifiedLocation);
|
||||
}
|
||||
};
|
||||
|
||||
if (!loginEnabledValue) {
|
||||
// If login is not enabled, just pass all requests without authentication
|
||||
filterChain.doFilter(request, response);
|
||||
filterChain.doFilter(request, responseWrapper);
|
||||
return;
|
||||
}
|
||||
String requestURI = request.getRequestURI();
|
||||
@@ -53,8 +71,8 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||
// provider for API keys.
|
||||
UserDetails userDetails = userService.loadUserByApiKey(apiKey);
|
||||
if (userDetails == null) {
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
response.getWriter().write("Invalid API Key.");
|
||||
responseWrapper.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
responseWrapper.getWriter().write("Invalid API Key.");
|
||||
return;
|
||||
}
|
||||
authentication =
|
||||
@@ -63,8 +81,8 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||
SecurityContextHolder.getContext().setAuthentication(authentication);
|
||||
} catch (AuthenticationException e) {
|
||||
// If API key authentication fails, deny the request
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
response.getWriter().write("Invalid API Key.");
|
||||
responseWrapper.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
responseWrapper.getWriter().write("Invalid API Key.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -76,18 +94,37 @@ public class UserAuthenticationFilter extends OncePerRequestFilter {
|
||||
String contextPath = request.getContextPath();
|
||||
|
||||
if ("GET".equalsIgnoreCase(method) && !(contextPath + "/login").equals(requestURI)) {
|
||||
response.sendRedirect(contextPath + "/login"); // redirect to the login page
|
||||
responseWrapper.sendRedirect(contextPath + "/login"); // redirect to the login page
|
||||
return;
|
||||
} else {
|
||||
response.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
response.getWriter()
|
||||
responseWrapper.setStatus(HttpStatus.UNAUTHORIZED.value());
|
||||
responseWrapper
|
||||
.getWriter()
|
||||
.write(
|
||||
"Authentication required. Please provide a X-API-KEY in request header.\nThis is found in Settings -> Account Settings -> API Key\nAlternativly you can disable authentication if this is unexpected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
filterChain.doFilter(request, response);
|
||||
filterChain.doFilter(request, responseWrapper);
|
||||
}
|
||||
|
||||
private String modifyLocation(String location, HttpServletRequest request) {
|
||||
if (!location.matches("https?://[^/]+:\\d+.*")
|
||||
&& redirectPort != null
|
||||
&& redirectPort.length() > 0) {
|
||||
try {
|
||||
int port = Integer.parseInt(redirectPort); // Parse the port
|
||||
URL url = new URL(location);
|
||||
String modifiedUrl =
|
||||
new URL(url.getProtocol(), url.getHost(), port, url.getFile()).toString();
|
||||
return modifiedUrl;
|
||||
} catch (MalformedURLException | NumberFormatException e) {
|
||||
// Log error and return the original location if URL parsing fails
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return location;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user