lots of stuff
This commit is contained in:
parent
54e7998bf7
commit
320f56e473
10 changed files with 153 additions and 46 deletions
|
@ -12,9 +12,7 @@ COPY build/libs/*.jar app.jar
|
|||
EXPOSE 8080
|
||||
|
||||
# Set environment variables
|
||||
ENV APP_HOME_NAME="Stirling PDF"
|
||||
#ENV APP_HOME_DESCRIPTION="Personal PDF Website!"
|
||||
#ENV APP_NAVBAR_NAME="Stirling PDF"
|
||||
ENV GROUPS_TO_REMOVE=LibreOffice
|
||||
|
||||
# Run the application
|
||||
RUN chmod +x /scripts/init.sh
|
||||
|
|
14
Dockerfile-ultralite
Normal file
14
Dockerfile-ultralite
Normal 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"]
|
|
@ -26,11 +26,11 @@ RUN git clone https://github.com/agl/jbig2enc && \
|
|||
FROM openjdk:17-jdk-slim AS base
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
libreoffice-core \
|
||||
libreoffice-core-nogui \
|
||||
libreoffice-common \
|
||||
libreoffice-writer \
|
||||
libreoffice-calc \
|
||||
libreoffice-impress \
|
||||
libreoffice-writer-nogui \
|
||||
libreoffice-calc-nogui \
|
||||
libreoffice-impress-nogui \
|
||||
python3-uno \
|
||||
python3-pip \
|
||||
unoconv \
|
||||
|
|
|
@ -7,11 +7,12 @@ import java.util.Map.Entry;
|
|||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
@Service
|
||||
public class EndpointConfiguration {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(EndpointConfiguration.class);
|
||||
private Map<String, Boolean> endpointStatuses = new ConcurrentHashMap<>();
|
||||
private Map<String, Set<String>> endpointGroups = new ConcurrentHashMap<>();
|
||||
|
||||
|
@ -25,6 +26,7 @@ public class EndpointConfiguration {
|
|||
}
|
||||
|
||||
public void disableEndpoint(String endpoint) {
|
||||
logger.info("Disabling {}", endpoint);
|
||||
endpointStatuses.put(endpoint, false);
|
||||
}
|
||||
|
||||
|
@ -162,6 +164,7 @@ public class EndpointConfiguration {
|
|||
addEndpointToGroup("Javascript", "pdf-organizer");
|
||||
addEndpointToGroup("Javascript", "sign");
|
||||
addEndpointToGroup("Javascript", "compare");
|
||||
|
||||
}
|
||||
|
||||
private void processEnvironmentConfigs() {
|
||||
|
|
|
@ -17,12 +17,10 @@ public class EndpointInterceptor implements HandlerInterceptor {
|
|||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
|
||||
throws Exception {
|
||||
String requestURI = request.getRequestURI();
|
||||
System.out.println("trying " + requestURI);
|
||||
if (!endpointConfiguration.isEndpointEnabled(requestURI)) {
|
||||
response.sendError(HttpServletResponse.SC_FORBIDDEN, "This endpoint is disabled");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -18,7 +18,7 @@ public class MetricsConfig {
|
|||
return new MeterFilter() {
|
||||
@Override
|
||||
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.DENY;
|
||||
|
|
|
@ -33,7 +33,7 @@ public class MetricsFilter extends OncePerRequestFilter {
|
|||
String uri = request.getRequestURI();
|
||||
|
||||
// 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")
|
||||
.tag("uri", uri)
|
||||
.tag("method", request.getMethod())
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -22,5 +22,4 @@ server.servlet.context-path=${APP_ROOT_PATH:/}
|
|||
spring.devtools.restart.enabled=true
|
||||
spring.devtools.livereload.enabled=true
|
||||
|
||||
spring.thymeleaf.encoding=UTF-8
|
||||
|
||||
spring.thymeleaf.encoding=UTF-8
|
|
@ -151,11 +151,11 @@ function compareVersions(version1, version2) {
|
|||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<!-- 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 ('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 ( '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 ('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 ( '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 ( 'remove-pages', 'images/file-earmark-x.svg', 'home.removePages.title', 'home.removePages.desc')}"></div>
|
||||
|
||||
</div>
|
||||
</li>
|
||||
|
@ -167,16 +167,16 @@ function compareVersions(version1, version2) {
|
|||
</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown">
|
||||
<!-- 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 ('file-to-pdf', 'images/file.svg', 'home.fileToPDF.title', 'home.fileToPDF.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>
|
||||
<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-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-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-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-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-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-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-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>
|
||||
</a>
|
||||
<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 ('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 ('add-watermark', 'images/droplet.svg', 'home.watermark.title', 'home.watermark.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 ('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>
|
||||
</li>
|
||||
|
@ -207,18 +207,17 @@ function compareVersions(version1, version2) {
|
|||
|
||||
</a>
|
||||
<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 ('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 ('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 ('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 ('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 ('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 ('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 ('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 ('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 ('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 ('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 ('compare', 'images/scales.svg', 'home.compare.title', 'home.compare.desc')}"></div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
|
@ -351,9 +350,13 @@ function compareVersions(version1, version2) {
|
|||
<div class="modal-body">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<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">
|
||||
<button type="button" class="btn btn-sm btn-outline-primary" id="update-btn" th:utext="#{settings.update}"></button>
|
||||
</a>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
@ -375,6 +378,7 @@ function compareVersions(version1, version2) {
|
|||
<label class="custom-control-label" for="boredWaiting" th:text="#{bored}"></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal" th:text="#{close}"></button>
|
||||
|
@ -385,6 +389,17 @@ function compareVersions(version1, version2) {
|
|||
|
||||
|
||||
<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
|
||||
|
|
Loading…
Reference in a new issue