mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-09-12 17:52:13 +02:00
Fix PATH order for python3 calls, improve merge memory
This commit is contained in:
parent
905fb67766
commit
dfd567b803
@ -34,7 +34,7 @@ ENV DOCKER_ENABLE_SECURITY=false \
|
|||||||
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
|
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
|
||||||
UNO_PATH=/usr/lib/libreoffice/program \
|
UNO_PATH=/usr/lib/libreoffice/program \
|
||||||
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc \
|
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc \
|
||||||
PATH=/opt/venv/bin:$PATH
|
PATH=$PATH:/opt/venv/bin
|
||||||
|
|
||||||
|
|
||||||
# JDK for app
|
# JDK for app
|
||||||
|
@ -43,7 +43,7 @@ ENV DOCKER_ENABLE_SECURITY=false \
|
|||||||
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
|
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
|
||||||
UNO_PATH=/usr/lib/libreoffice/program \
|
UNO_PATH=/usr/lib/libreoffice/program \
|
||||||
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc \
|
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc \
|
||||||
PATH=/opt/venv/bin:$PATH
|
PATH=$PATH:/opt/venv/bin
|
||||||
|
|
||||||
|
|
||||||
# JDK for app
|
# JDK for app
|
||||||
|
@ -78,7 +78,6 @@ public class KeygenLicenseVerifier {
|
|||||||
// Remove all newlines
|
// Remove all newlines
|
||||||
encodedPayload = encodedPayload.replaceAll("\\r?\\n", "");
|
encodedPayload = encodedPayload.replaceAll("\\r?\\n", "");
|
||||||
|
|
||||||
|
|
||||||
byte[] payloadBytes = Base64.getDecoder().decode(encodedPayload);
|
byte[] payloadBytes = Base64.getDecoder().decode(encodedPayload);
|
||||||
String payload = new String(payloadBytes);
|
String payload = new String(payloadBytes);
|
||||||
|
|
||||||
|
@ -290,7 +290,7 @@ public class EndpointConfiguration {
|
|||||||
disableGroup("enterprise");
|
disableGroup("enterprise");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!applicationProperties.getSystem().getEnableUrlToPDF()) {
|
if (!applicationProperties.getSystem().getEnableUrlToPDF()) {
|
||||||
disableEndpoint("url-to-pdf");
|
disableEndpoint("url-to-pdf");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,8 +125,7 @@ public class MergeController {
|
|||||||
public ResponseEntity<byte[]> mergePdfs(@ModelAttribute MergePdfsRequest form)
|
public ResponseEntity<byte[]> mergePdfs(@ModelAttribute MergePdfsRequest form)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
List<File> filesToDelete = new ArrayList<>(); // List of temporary files to delete
|
List<File> filesToDelete = new ArrayList<>(); // List of temporary files to delete
|
||||||
ByteArrayOutputStream docOutputstream =
|
File mergedTempFile = null;
|
||||||
new ByteArrayOutputStream(); // Stream for the merged document
|
|
||||||
PDDocument mergedDocument = null;
|
PDDocument mergedDocument = null;
|
||||||
|
|
||||||
boolean removeCertSign = form.isRemoveCertSign();
|
boolean removeCertSign = form.isRemoveCertSign();
|
||||||
@ -139,21 +138,24 @@ public class MergeController {
|
|||||||
form.getSortType())); // Sort files based on the given sort type
|
form.getSortType())); // Sort files based on the given sort type
|
||||||
|
|
||||||
PDFMergerUtility mergerUtility = new PDFMergerUtility();
|
PDFMergerUtility mergerUtility = new PDFMergerUtility();
|
||||||
|
long totalSize = 0;
|
||||||
for (MultipartFile multipartFile : files) {
|
for (MultipartFile multipartFile : files) {
|
||||||
|
totalSize += multipartFile.getSize();
|
||||||
File tempFile =
|
File tempFile =
|
||||||
GeneralUtils.convertMultipartFileToFile(
|
GeneralUtils.convertMultipartFileToFile(
|
||||||
multipartFile); // Convert MultipartFile to File
|
multipartFile); // Convert MultipartFile to File
|
||||||
filesToDelete.add(tempFile); // Add temp file to the list for later deletion
|
filesToDelete.add(tempFile); // Add temp file to the list for later deletion
|
||||||
mergerUtility.addSource(tempFile); // Add source file to the merger utility
|
mergerUtility.addSource(tempFile); // Add source file to the merger utility
|
||||||
}
|
}
|
||||||
mergerUtility.setDestinationStream(
|
|
||||||
docOutputstream); // Set the output stream for the merged document
|
|
||||||
mergerUtility.mergeDocuments(null); // Merge the documents
|
|
||||||
|
|
||||||
byte[] mergedPdfBytes = docOutputstream.toByteArray(); // Get merged document bytes
|
mergedTempFile = File.createTempFile("merged-", ".pdf");
|
||||||
|
mergerUtility.setDestinationFileName(mergedTempFile.getAbsolutePath());
|
||||||
|
|
||||||
|
mergerUtility.mergeDocuments(
|
||||||
|
pdfDocumentFactory.getStreamCacheFunction(totalSize)); // Merge the documents
|
||||||
|
|
||||||
// Load the merged PDF document
|
// Load the merged PDF document
|
||||||
mergedDocument = pdfDocumentFactory.load(mergedPdfBytes);
|
mergedDocument = pdfDocumentFactory.load(mergedTempFile);
|
||||||
|
|
||||||
// Remove signatures if removeCertSign is true
|
// Remove signatures if removeCertSign is true
|
||||||
if (removeCertSign) {
|
if (removeCertSign) {
|
||||||
@ -180,21 +182,23 @@ public class MergeController {
|
|||||||
String mergedFileName =
|
String mergedFileName =
|
||||||
files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
files[0].getOriginalFilename().replaceFirst("[.][^.]+$", "")
|
||||||
+ "_merged_unsigned.pdf";
|
+ "_merged_unsigned.pdf";
|
||||||
return WebResponseUtils.bytesToWebResponse(
|
return WebResponseUtils.boasToWebResponse(
|
||||||
baos.toByteArray(), mergedFileName); // Return the modified PDF
|
baos, mergedFileName); // Return the modified PDF
|
||||||
|
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
log.error("Error in merge pdf process", ex);
|
log.error("Error in merge pdf process", ex);
|
||||||
throw ex;
|
throw ex;
|
||||||
} finally {
|
} finally {
|
||||||
|
if (mergedDocument != null) {
|
||||||
|
mergedDocument.close(); // Close the merged document
|
||||||
|
}
|
||||||
for (File file : filesToDelete) {
|
for (File file : filesToDelete) {
|
||||||
if (file != null) {
|
if (file != null) {
|
||||||
Files.deleteIfExists(file.toPath()); // Delete temporary files
|
Files.deleteIfExists(file.toPath()); // Delete temporary files
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
docOutputstream.close();
|
if (mergedTempFile != null) {
|
||||||
if (mergedDocument != null) {
|
Files.deleteIfExists(mergedTempFile.toPath());
|
||||||
mergedDocument.close(); // Close the merged document
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,9 +37,12 @@ public class ConvertWebsiteToPDF {
|
|||||||
private final CustomPDFDocumentFactory pdfDocumentFactory;
|
private final CustomPDFDocumentFactory pdfDocumentFactory;
|
||||||
private final RuntimePathConfig runtimePathConfig;
|
private final RuntimePathConfig runtimePathConfig;
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public ConvertWebsiteToPDF(
|
public ConvertWebsiteToPDF(
|
||||||
CustomPDFDocumentFactory pdfDocumentFactory, RuntimePathConfig runtimePathConfig, ApplicationProperties applicationProperties) {
|
CustomPDFDocumentFactory pdfDocumentFactory,
|
||||||
|
RuntimePathConfig runtimePathConfig,
|
||||||
|
ApplicationProperties applicationProperties) {
|
||||||
this.pdfDocumentFactory = pdfDocumentFactory;
|
this.pdfDocumentFactory = pdfDocumentFactory;
|
||||||
this.runtimePathConfig = runtimePathConfig;
|
this.runtimePathConfig = runtimePathConfig;
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
@ -55,8 +58,7 @@ public class ConvertWebsiteToPDF {
|
|||||||
throws IOException, InterruptedException {
|
throws IOException, InterruptedException {
|
||||||
String URL = request.getUrlInput();
|
String URL = request.getUrlInput();
|
||||||
|
|
||||||
|
if (!applicationProperties.getSystem().getEnableUrlToPDF()) {
|
||||||
if(!applicationProperties.getSystem().getEnableUrlToPDF()) {
|
|
||||||
throw new IllegalArgumentException("This endpoint has been disabled by the admin.");
|
throw new IllegalArgumentException("This endpoint has been disabled by the admin.");
|
||||||
}
|
}
|
||||||
// Validate the URL format
|
// Validate the URL format
|
||||||
|
@ -22,6 +22,7 @@ import jakarta.annotation.PostConstruct;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import stirling.software.SPDF.config.EndpointInspector;
|
||||||
import stirling.software.SPDF.config.StartupApplicationListener;
|
import stirling.software.SPDF.config.StartupApplicationListener;
|
||||||
import stirling.software.SPDF.model.ApplicationProperties;
|
import stirling.software.SPDF.model.ApplicationProperties;
|
||||||
|
|
||||||
@ -32,15 +33,17 @@ import stirling.software.SPDF.model.ApplicationProperties;
|
|||||||
public class MetricsController {
|
public class MetricsController {
|
||||||
|
|
||||||
private final ApplicationProperties applicationProperties;
|
private final ApplicationProperties applicationProperties;
|
||||||
|
|
||||||
private final MeterRegistry meterRegistry;
|
private final MeterRegistry meterRegistry;
|
||||||
|
private final EndpointInspector endpointInspector;
|
||||||
private boolean metricsEnabled;
|
private boolean metricsEnabled;
|
||||||
|
|
||||||
public MetricsController(
|
public MetricsController(
|
||||||
ApplicationProperties applicationProperties, MeterRegistry meterRegistry) {
|
ApplicationProperties applicationProperties,
|
||||||
|
MeterRegistry meterRegistry,
|
||||||
|
EndpointInspector endpointInspector) {
|
||||||
this.applicationProperties = applicationProperties;
|
this.applicationProperties = applicationProperties;
|
||||||
this.meterRegistry = meterRegistry;
|
this.meterRegistry = meterRegistry;
|
||||||
|
this.endpointInspector = endpointInspector;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@ -208,16 +211,40 @@ public class MetricsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private double getRequestCount(String method, Optional<String> endpoint) {
|
private double getRequestCount(String method, Optional<String> endpoint) {
|
||||||
double count =
|
return meterRegistry.find("http.requests").tag("method", method).counters().stream()
|
||||||
meterRegistry.find("http.requests").tag("method", method).counters().stream()
|
|
||||||
.filter(
|
.filter(
|
||||||
counter ->
|
counter -> {
|
||||||
!endpoint.isPresent()
|
String uri = counter.getId().getTag("uri");
|
||||||
|| endpoint.get()
|
|
||||||
.equals(counter.getId().getTag("uri")))
|
// Apply filtering logic - Skip if uri is null
|
||||||
|
if (uri == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For POST requests, only include if they start with /api/v1
|
||||||
|
if ("POST".equals(method) && !uri.contains("api/v1")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri.contains(".txt")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For GET requests, validate if we have a list of valid endpoints
|
||||||
|
final boolean validateGetEndpoints =
|
||||||
|
endpointInspector.getValidGetEndpoints().size() != 0;
|
||||||
|
if ("GET".equals(method)
|
||||||
|
&& validateGetEndpoints
|
||||||
|
&& !endpointInspector.isValidGetEndpoint(uri)) {
|
||||||
|
log.debug("Skipping invalid GET endpoint: {}", uri);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter for specific endpoint if provided
|
||||||
|
return !endpoint.isPresent() || endpoint.get().equals(uri);
|
||||||
|
})
|
||||||
.mapToDouble(Counter::count)
|
.mapToDouble(Counter::count)
|
||||||
.sum();
|
.sum();
|
||||||
return count;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<EndpointCount> getEndpointCounts(String method) {
|
private List<EndpointCount> getEndpointCounts(String method) {
|
||||||
@ -229,23 +256,72 @@ public class MetricsController {
|
|||||||
.forEach(
|
.forEach(
|
||||||
counter -> {
|
counter -> {
|
||||||
String uri = counter.getId().getTag("uri");
|
String uri = counter.getId().getTag("uri");
|
||||||
|
|
||||||
|
// Skip if uri is null
|
||||||
|
if (uri == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For POST requests, only include if they start with /api/v1
|
||||||
|
if ("POST".equals(method) && !uri.contains("api/v1")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri.contains(".txt")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For GET requests, validate if we have a list of valid endpoints
|
||||||
|
final boolean validateGetEndpoints =
|
||||||
|
endpointInspector.getValidGetEndpoints().size() != 0;
|
||||||
|
if ("GET".equals(method)
|
||||||
|
&& validateGetEndpoints
|
||||||
|
&& !endpointInspector.isValidGetEndpoint(uri)) {
|
||||||
|
log.debug("Skipping invalid GET endpoint: {}", uri);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
counts.merge(uri, counter.count(), Double::sum);
|
counts.merge(uri, counter.count(), Double::sum);
|
||||||
});
|
});
|
||||||
List<EndpointCount> result =
|
|
||||||
counts.entrySet().stream()
|
return counts.entrySet().stream()
|
||||||
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue()))
|
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue()))
|
||||||
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
|
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private double getUniqueUserCount(String method, Optional<String> endpoint) {
|
private double getUniqueUserCount(String method, Optional<String> endpoint) {
|
||||||
Set<String> uniqueUsers = new HashSet<>();
|
Set<String> uniqueUsers = new HashSet<>();
|
||||||
meterRegistry.find("http.requests").tag("method", method).counters().stream()
|
meterRegistry.find("http.requests").tag("method", method).counters().stream()
|
||||||
.filter(
|
.filter(
|
||||||
counter ->
|
counter -> {
|
||||||
!endpoint.isPresent()
|
String uri = counter.getId().getTag("uri");
|
||||||
|| endpoint.get().equals(counter.getId().getTag("uri")))
|
|
||||||
|
// Skip if uri is null
|
||||||
|
if (uri == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For POST requests, only include if they start with /api/v1
|
||||||
|
if ("POST".equals(method) && !uri.contains("api/v1")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uri.contains(".txt")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For GET requests, validate if we have a list of valid endpoints
|
||||||
|
final boolean validateGetEndpoints =
|
||||||
|
endpointInspector.getValidGetEndpoints().size() != 0;
|
||||||
|
if ("GET".equals(method)
|
||||||
|
&& validateGetEndpoints
|
||||||
|
&& !endpointInspector.isValidGetEndpoint(uri)) {
|
||||||
|
log.debug("Skipping invalid GET endpoint: {}", uri);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !endpoint.isPresent() || endpoint.get().equals(uri);
|
||||||
|
})
|
||||||
.forEach(
|
.forEach(
|
||||||
counter -> {
|
counter -> {
|
||||||
String session = counter.getId().getTag("session");
|
String session = counter.getId().getTag("session");
|
||||||
@ -270,12 +346,10 @@ public class MetricsController {
|
|||||||
uniqueUsers.computeIfAbsent(uri, k -> new HashSet<>()).add(session);
|
uniqueUsers.computeIfAbsent(uri, k -> new HashSet<>()).add(session);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
List<EndpointCount> result =
|
return uniqueUsers.entrySet().stream()
|
||||||
uniqueUsers.entrySet().stream()
|
|
||||||
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue().size()))
|
.map(entry -> new EndpointCount(entry.getKey(), entry.getValue().size()))
|
||||||
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
|
.sorted(Comparator.comparing(EndpointCount::getCount).reversed())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping("/uptime")
|
@GetMapping("/uptime")
|
||||||
|
@ -139,7 +139,7 @@ public class CustomPDFDocumentFactory {
|
|||||||
* Determine the appropriate caching strategy based on file size and available memory. This
|
* Determine the appropriate caching strategy based on file size and available memory. This
|
||||||
* common method is used by both password and non-password loading paths.
|
* common method is used by both password and non-password loading paths.
|
||||||
*/
|
*/
|
||||||
private StreamCacheCreateFunction getStreamCacheFunction(long contentSize) {
|
public StreamCacheCreateFunction getStreamCacheFunction(long contentSize) {
|
||||||
long maxMemory = Runtime.getRuntime().maxMemory();
|
long maxMemory = Runtime.getRuntime().maxMemory();
|
||||||
long freeMemory = Runtime.getRuntime().freeMemory();
|
long freeMemory = Runtime.getRuntime().freeMemory();
|
||||||
long totalMemory = Runtime.getRuntime().totalMemory();
|
long totalMemory = Runtime.getRuntime().totalMemory();
|
||||||
|
@ -72,10 +72,6 @@ premium:
|
|||||||
author: username
|
author: username
|
||||||
creator: Stirling-PDF
|
creator: Stirling-PDF
|
||||||
producer: Stirling-PDF
|
producer: Stirling-PDF
|
||||||
enterpriseFeatures:
|
|
||||||
persistentMetrics:
|
|
||||||
enabled: false
|
|
||||||
retentionDays: 90
|
|
||||||
|
|
||||||
legal:
|
legal:
|
||||||
termsAndConditions: https://www.stirlingpdf.com/terms-and-conditions # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder
|
termsAndConditions: https://www.stirlingpdf.com/terms-and-conditions # URL to the terms and conditions of your application (e.g. https://example.com/terms). Empty string to disable or filename to load from local file in static folder
|
||||||
|
@ -2,7 +2,6 @@ package stirling.software.SPDF.controller.api.converters;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -29,13 +28,14 @@ public class ConvertWebsiteToPdfTest {
|
|||||||
MockitoAnnotations.openMocks(this);
|
MockitoAnnotations.openMocks(this);
|
||||||
applicationProperties = new ApplicationProperties();
|
applicationProperties = new ApplicationProperties();
|
||||||
applicationProperties.getSystem().setEnableUrlToPDF(true);
|
applicationProperties.getSystem().setEnableUrlToPDF(true);
|
||||||
convertWebsiteToPDF = new ConvertWebsiteToPDF(mockPdfDocumentFactory, runtimePathConfig, applicationProperties);
|
convertWebsiteToPDF =
|
||||||
|
new ConvertWebsiteToPDF(
|
||||||
|
mockPdfDocumentFactory, runtimePathConfig, applicationProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_exemption_is_thrown_when_invalid_url_format_provided() {
|
public void test_exemption_is_thrown_when_invalid_url_format_provided() {
|
||||||
|
|
||||||
|
|
||||||
String invalid_format_Url = "invalid-url";
|
String invalid_format_Url = "invalid-url";
|
||||||
|
|
||||||
UrlToPdfRequest request = new UrlToPdfRequest();
|
UrlToPdfRequest request = new UrlToPdfRequest();
|
||||||
|
Loading…
Reference in New Issue
Block a user