lots of stuff

This commit is contained in:
Anthony Stirling 2023-05-17 23:58:15 +01:00
parent 54e7998bf7
commit 320f56e473
10 changed files with 153 additions and 46 deletions

View file

@ -12,9 +12,7 @@ COPY build/libs/*.jar app.jar
EXPOSE 8080 EXPOSE 8080
# Set environment variables # Set environment variables
ENV APP_HOME_NAME="Stirling PDF" ENV GROUPS_TO_REMOVE=LibreOffice
#ENV APP_HOME_DESCRIPTION="Personal PDF Website!"
#ENV APP_NAVBAR_NAME="Stirling PDF"
# Run the application # Run the application
RUN chmod +x /scripts/init.sh RUN chmod +x /scripts/init.sh

14
Dockerfile-ultralite Normal file
View file

@ -0,0 +1,14 @@
# Build jbig2enc in a separate stage
FROM openjdk:17-jdk-slim
# Copy the application JAR file
COPY build/libs/*.jar app.jar
# Expose the application port
EXPOSE 8080
# Set environment variables
ENV GROUPS_TO_REMOVE=LibreOffice,CLI
# Run the application
CMD ["java", "-jar", "/app.jar"]

View file

@ -26,11 +26,11 @@ RUN git clone https://github.com/agl/jbig2enc && \
FROM openjdk:17-jdk-slim AS base FROM openjdk:17-jdk-slim AS base
RUN apt-get update && \ RUN apt-get update && \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
libreoffice-core \ libreoffice-core-nogui \
libreoffice-common \ libreoffice-common \
libreoffice-writer \ libreoffice-writer-nogui \
libreoffice-calc \ libreoffice-calc-nogui \
libreoffice-impress \ libreoffice-impress-nogui \
python3-uno \ python3-uno \
python3-pip \ python3-pip \
unoconv \ unoconv \

View file

@ -7,11 +7,12 @@ import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.slf4j.Logger;
@Service @Service
public class EndpointConfiguration { public class EndpointConfiguration {
private static final Logger logger = LoggerFactory.getLogger(EndpointConfiguration.class);
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>(); private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
private Map<String, Set<String>> endpointGroups = new ConcurrentHashMap<>(); private Map<String, Set<String>> endpointGroups = new ConcurrentHashMap<>();
@ -25,6 +26,7 @@ public class EndpointConfiguration {
} }
public void disableEndpoint(String endpoint) { public void disableEndpoint(String endpoint) {
logger.info("Disabling {}", endpoint);
endpointStatuses.put(endpoint, false); endpointStatuses.put(endpoint, false);
} }
@ -162,6 +164,7 @@ public class EndpointConfiguration {
addEndpointToGroup("Javascript", "pdf-organizer"); addEndpointToGroup("Javascript", "pdf-organizer");
addEndpointToGroup("Javascript", "sign"); addEndpointToGroup("Javascript", "sign");
addEndpointToGroup("Javascript", "compare"); addEndpointToGroup("Javascript", "compare");
} }
private void processEnvironmentConfigs() { private void processEnvironmentConfigs() {

View file

@ -17,12 +17,10 @@ public class EndpointInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception { throws Exception {
String requestURI = request.getRequestURI(); String requestURI = request.getRequestURI();
System.out.println("trying " + requestURI);
if (!endpointConfiguration.isEndpointEnabled(requestURI)) { if (!endpointConfiguration.isEndpointEnabled(requestURI)) {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "This endpoint is disabled"); response.sendError(HttpServletResponse.SC_FORBIDDEN, "This endpoint is disabled");
return false; return false;
} }
return true; return true;
} }
} }

View file

@ -18,7 +18,7 @@ public class MetricsConfig {
return new MeterFilter() { return new MeterFilter() {
@Override @Override
public MeterFilterReply accept(Meter.Id id) { public MeterFilterReply accept(Meter.Id id) {
if (id.getName().equals("http.requests") || id.getName().equals("health")) { if (id.getName().equals("http.requests")) {
return MeterFilterReply.NEUTRAL; return MeterFilterReply.NEUTRAL;
} }
return MeterFilterReply.DENY; return MeterFilterReply.DENY;

View file

@ -33,7 +33,7 @@ public class MetricsFilter extends OncePerRequestFilter {
String uri = request.getRequestURI(); String uri = request.getRequestURI();
// Ignore static resources // Ignore static resources
if (!(uri.startsWith("/css") || uri.startsWith("/js") || uri.startsWith("/images") || uri.endsWith(".ico") || uri.endsWith(".svg")|| uri.endsWith(".js"))) { if (!(uri.startsWith("/js") || uri.startsWith("/images") || uri.endsWith(".ico") || uri.endsWith(".css") || uri.endsWith(".svg")|| uri.endsWith(".js") || uri.contains("swagger") || uri.startsWith("/api"))) {
Counter counter = Counter.builder("http.requests") Counter counter = Counter.builder("http.requests")
.tag("uri", uri) .tag("uri", uri)
.tag("method", request.getMethod()) .tag("method", request.getMethod())

View file

@ -0,0 +1,80 @@
package stirling.software.SPDF.controller.web;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.swagger.v3.oas.annotations.Operation;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/v1")
public class MetricsController {
private final MeterRegistry meterRegistry;
public MetricsController(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
@GetMapping("/status")
@Operation(summary = "Application status and version",
description = "This endpoint returns the status of the application and its version number.")
public Map<String, String> getStatus() {
Map<String, String> status = new HashMap<>();
status.put("status", "UP");
status.put("version", getClass().getPackage().getImplementationVersion());
return status;
}
@GetMapping("/loads")
@Operation(summary = "GET request count",
description = "This endpoint returns the total count of GET requests or the count of GET requests for a specific endpoint.")
public Double getPageLoads(@RequestParam Optional<String> endpoint) {
try {
double count = 0.0;
for (Meter meter : meterRegistry.getMeters()) {
if (meter.getId().getName().equals("http.requests")) {
String method = meter.getId().getTag("method");
if (method != null && method.equals("GET")) {
if (meter instanceof Counter) {
count += ((Counter) meter).count();
}
}
}
}
return count;
} catch (Exception e) {
return -1.0;
}
}
@GetMapping("/requests")
@Operation(summary = "POST request count",
description = "This endpoint returns the total count of POST requests or the count of POST requests for a specific endpoint.")
public Double getTotalRequests(@RequestParam Optional<String> endpoint) {
try {
Counter counter;
if (endpoint.isPresent()) {
counter = meterRegistry.get("http.requests")
.tags("method", "POST", "uri", endpoint.get()).counter();
} else {
counter = meterRegistry.get("http.requests")
.tags("method", "POST").counter();
}
return counter.count();
} catch (Exception e) {
return -1.0;
}
}
}

View file

@ -22,5 +22,4 @@ server.servlet.context-path=${APP_ROOT_PATH:/}
spring.devtools.restart.enabled=true spring.devtools.restart.enabled=true
spring.devtools.livereload.enabled=true spring.devtools.livereload.enabled=true
spring.thymeleaf.encoding=UTF-8 spring.thymeleaf.encoding=UTF-8

View file

@ -151,11 +151,11 @@ function compareVersions(version1, version2) {
</a> </a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown"> <div class="dropdown-menu" aria-labelledby="navbarDropdown">
<!-- Existing menu items --> <!-- Existing menu items -->
<div th:replace="fragments/navbarEntry :: navbarEntry ('merge-pdfs', 'images/union.svg', 'home.merge.title', 'home.merge.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('merge-pdfs', 'images/union.svg', 'home.merge.title', 'home.merge.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('split-pdfs', 'images/layout-split.svg', 'home.split.title', 'home.split.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('split-pdfs', 'images/layout-split.svg', 'home.split.title', 'home.split.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ( 'pdf-organizer', 'images/sort-numeric-down.svg', 'home.pdfOrganiser.title', 'home.pdfOrganiser.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'pdf-organizer', 'images/sort-numeric-down.svg', 'home.pdfOrganiser.title', 'home.pdfOrganiser.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ( 'rotate-pdf', 'images/arrow-clockwise.svg', 'home.rotate.title', 'home.rotate.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'rotate-pdf', 'images/arrow-clockwise.svg', 'home.rotate.title', 'home.rotate.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ( 'remove-pages', 'images/file-earmark-x.svg', 'home.removePages.title', 'home.removePages.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ( 'remove-pages', 'images/file-earmark-x.svg', 'home.removePages.title', 'home.removePages.desc')}"></div>
</div> </div>
</li> </li>
@ -167,16 +167,16 @@ function compareVersions(version1, version2) {
</a> </a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown"> <div class="dropdown-menu" aria-labelledby="navbarDropdown">
<!-- Existing menu items --> <!-- Existing menu items -->
<div th:replace="fragments/navbarEntry :: navbarEntry ('img-to-pdf', 'images/image.svg', 'home.imageToPdf.title', 'home.imageToPdf.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('img-to-pdf', 'images/image.svg', 'home.imageToPdf.title', 'home.imageToPdf.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('file-to-pdf', 'images/file.svg', 'home.fileToPDF.title', 'home.fileToPDF.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('file-to-pdf', 'images/file.svg', 'home.fileToPDF.title', 'home.fileToPDF.desc')}"></div>
<hr class="dropdown-divider"> <hr class="dropdown-divider">
<div th:replace="fragments/navbarEntry :: navbarEntry ('pdf-to-img', 'images/image.svg', 'home.pdfToImage.title', 'home.pdfToImage.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-img', 'images/image.svg', 'home.pdfToImage.title', 'home.pdfToImage.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('pdf-to-word', 'images/file-earmark-word.svg', 'home.PDFToWord.title', 'home.PDFToWord.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-word', 'images/file-earmark-word.svg', 'home.PDFToWord.title', 'home.PDFToWord.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('pdf-to-presentation', 'images/file-earmark-ppt.svg', 'home.PDFToPresentation.title', 'home.PDFToPresentation.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-presentation', 'images/file-earmark-ppt.svg', 'home.PDFToPresentation.title', 'home.PDFToPresentation.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('pdf-to-text', 'images/filetype-txt.svg', 'home.PDFToText.title', 'home.PDFToText.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-text', 'images/filetype-txt.svg', 'home.PDFToText.title', 'home.PDFToText.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('pdf-to-html', 'images/filetype-html.svg', 'home.PDFToHTML.title', 'home.PDFToHTML.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-html', 'images/filetype-html.svg', 'home.PDFToHTML.title', 'home.PDFToHTML.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('pdf-to-xml', 'images/filetype-xml.svg', 'home.PDFToXML.title', 'home.PDFToXML.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-xml', 'images/filetype-xml.svg', 'home.PDFToXML.title', 'home.PDFToXML.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('pdf-to-pdfa', 'images/file-earmark-pdf.svg', 'home.pdfToPDFA.title', 'home.pdfToPDFA.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('pdf-to-pdfa', 'images/file-earmark-pdf.svg', 'home.pdfToPDFA.title', 'home.pdfToPDFA.desc')}"></div>
@ -191,10 +191,10 @@ function compareVersions(version1, version2) {
<img class="icon" src="images/shield-check.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> <span class="icon-text" th:text="#{navbar.security}"></span> <img class="icon" src="images/shield-check.svg" alt="icon" style="width: 16px; height: 16px; vertical-align: middle;"> <span class="icon-text" th:text="#{navbar.security}"></span>
</a> </a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown"> <div class="dropdown-menu" aria-labelledby="navbarDropdown">
<div th:replace="fragments/navbarEntry :: navbarEntry ('add-password', 'images/lock.svg', 'home.addPassword.title', 'home.addPassword.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('add-password', 'images/lock.svg', 'home.addPassword.title', 'home.addPassword.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('remove-password', 'images/unlock.svg', 'home.removePassword.title', 'home.removePassword.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('remove-password', 'images/unlock.svg', 'home.removePassword.title', 'home.removePassword.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('change-permissions', 'images/shield-lock.svg', 'home.permissions.title', 'home.permissions.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('change-permissions', 'images/shield-lock.svg', 'home.permissions.title', 'home.permissions.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('add-watermark', 'images/droplet.svg', 'home.watermark.title', 'home.watermark.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('add-watermark', 'images/droplet.svg', 'home.watermark.title', 'home.watermark.desc')}"></div>
</div> </div>
</li> </li>
@ -207,18 +207,17 @@ function compareVersions(version1, version2) {
</a> </a>
<div class="dropdown-menu" aria-labelledby="navbarDropdown"> <div class="dropdown-menu" aria-labelledby="navbarDropdown">
<div th:replace="fragments/navbarEntry :: navbarEntry ('ocr-pdf', 'images/search.svg', 'home.ocr.title', 'home.ocr.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('ocr-pdf', 'images/search.svg', 'home.ocr.title', 'home.ocr.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('add-image', 'images/file-earmark-richtext.svg', 'home.addImage.title', 'home.addImage.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('add-image', 'images/file-earmark-richtext.svg', 'home.addImage.title', 'home.addImage.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('compress-pdf', 'images/file-zip.svg', 'home.compressPdfs.title', 'home.compressPdfs.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('compress-pdf', 'images/file-zip.svg', 'home.compressPdfs.title', 'home.compressPdfs.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('extract-images', 'images/images.svg', 'home.extractImages.title', 'home.extractImages.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('extract-images', 'images/images.svg', 'home.extractImages.title', 'home.extractImages.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('change-metadata', 'images/clipboard-data.svg', 'home.changeMetadata.title', 'home.changeMetadata.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('change-metadata', 'images/clipboard-data.svg', 'home.changeMetadata.title', 'home.changeMetadata.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('extract-image-scans', 'images/scanner.svg', 'home.ScannerImageSplit.title', 'home.ScannerImageSplit.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('extract-image-scans', 'images/scanner.svg', 'home.ScannerImageSplit.title', 'home.ScannerImageSplit.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('sign', 'images/sign.svg', 'home.sign.title', 'home.sign.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('sign', 'images/sign.svg', 'home.sign.title', 'home.sign.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('flatten', 'images/flatten.svg', 'home.flatten.title', 'home.flatten.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('flatten', 'images/flatten.svg', 'home.flatten.title', 'home.flatten.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('repair', 'images/wrench.svg', 'home.repair.title', 'home.repair.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('repair', 'images/wrench.svg', 'home.repair.title', 'home.repair.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('remove-blanks', 'images/blank-file.svg', 'home.removeBlanks.title', 'home.removeBlanks.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('remove-blanks', 'images/blank-file.svg', 'home.removeBlanks.title', 'home.removeBlanks.desc')}"></div>
<div th:replace="fragments/navbarEntry :: navbarEntry ('compare', 'images/scales.svg', 'home.compare.title', 'home.compare.desc')"></div> <div th:replace="~{fragments/navbarEntry :: navbarEntry ('compare', 'images/scales.svg', 'home.compare.title', 'home.compare.desc')}"></div>
</div> </div>
</li> </li>
@ -351,9 +350,13 @@ function compareVersions(version1, version2) {
<div class="modal-body"> <div class="modal-body">
<div class="d-flex justify-content-between align-items-center mb-3"> <div class="d-flex justify-content-between align-items-center mb-3">
<p class="mb-0" th:utext="#{settings.appVersion} + ' ' + ${@appVersion}"></p> <p class="mb-0" th:utext="#{settings.appVersion} + ' ' + ${@appVersion}"></p>
<a href="swagger-ui/index.html" target="_blank">
<button type="button" class="btn btn-sm btn-outline-primary"> API </button>
</a>
<a href="https://github.com/Frooodle/Stirling-PDF/releases" target="_blank"> <a href="https://github.com/Frooodle/Stirling-PDF/releases" target="_blank">
<button type="button" class="btn btn-sm btn-outline-primary" id="update-btn" th:utext="#{settings.update}"></button> <button type="button" class="btn btn-sm btn-outline-primary" id="update-btn" th:utext="#{settings.update}"></button>
</a> </a>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -375,6 +378,7 @@ function compareVersions(version1, version2) {
<label class="custom-control-label" for="boredWaiting" th:text="#{bored}"></label> <label class="custom-control-label" for="boredWaiting" th:text="#{bored}"></label>
</div> </div>
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{close}"></button> <button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{close}"></button>
@ -385,6 +389,17 @@ function compareVersions(version1, version2) {
<script> <script>
$(document).ready(function() {
$(".nav-item.dropdown").each(function() {
var $dropdownMenu = $(this).find(".dropdown-menu");
if ($dropdownMenu.children().length <= 2 && $dropdownMenu.children("hr.dropdown-divider").length === $dropdownMenu.children().length) {
$(this).prev('.nav-item.nav-item-separator').remove();
$(this).remove();
}
});
});
// Get the download option from local storage, or set it to 'sameWindow' if it doesn't exist // Get the download option from local storage, or set it to 'sameWindow' if it doesn't exist