From c2fec0a03039536021429d200f88f4d78b522f2b Mon Sep 17 00:00:00 2001 From: Anthony Stirling <77850077+Frooodle@users.noreply.github.com> Date: Tue, 16 May 2023 22:44:53 +0100 Subject: [PATCH] stats and conditionals --- Dockerfile-lite | 22 +++ DockerfileBase-lite | 57 ++++++ build.gradle | 5 + .../SPDF/config/EndpointConfiguration.java | 168 ++++++++++++++++++ .../SPDF/config/EndpointInterceptor.java | 28 +++ .../software/SPDF/config/MetricsConfig.java | 28 +++ .../software/SPDF/config/MetricsFilter.java | 50 ++++++ .../software/SPDF/config/WebMvcConfig.java | 18 ++ src/main/resources/application.properties | 1 + .../resources/templates/fragments/card.html | 2 +- .../resources/templates/fragments/navbar.html | 148 ++++----------- .../templates/fragments/navbarEntry.html | 6 + src/main/resources/templates/home.html | 2 + 13 files changed, 420 insertions(+), 115 deletions(-) create mode 100644 Dockerfile-lite create mode 100644 DockerfileBase-lite create mode 100644 src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java create mode 100644 src/main/java/stirling/software/SPDF/config/EndpointInterceptor.java create mode 100644 src/main/java/stirling/software/SPDF/config/MetricsConfig.java create mode 100644 src/main/java/stirling/software/SPDF/config/MetricsFilter.java create mode 100644 src/main/java/stirling/software/SPDF/config/WebMvcConfig.java create mode 100644 src/main/resources/templates/fragments/navbarEntry.html diff --git a/Dockerfile-lite b/Dockerfile-lite new file mode 100644 index 00000000..1d12a545 --- /dev/null +++ b/Dockerfile-lite @@ -0,0 +1,22 @@ +# Build jbig2enc in a separate stage +FROM frooodle/stirling-pdf-base:beta2 + +# Create scripts folder and copy local scripts +RUN mkdir /scripts +COPY ./scripts/* /scripts/ + +# Copy the application JAR file +COPY build/libs/*.jar app.jar + +# Expose the application port +EXPOSE 8080 + +# Set environment variables +ENV APP_HOME_NAME="Stirling PDF" +#ENV APP_HOME_DESCRIPTION="Personal PDF Website!" +#ENV APP_NAVBAR_NAME="Stirling PDF" + +# Run the application +RUN chmod +x /scripts/init.sh +ENTRYPOINT ["/scripts/init.sh"] +CMD ["java", "-jar", "/app.jar"] diff --git a/DockerfileBase-lite b/DockerfileBase-lite new file mode 100644 index 00000000..ebe3c31b --- /dev/null +++ b/DockerfileBase-lite @@ -0,0 +1,57 @@ +# Build jbig2enc in a separate stage +FROM debian:bullseye-slim as jbig2enc_builder + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + git \ + automake \ + autoconf \ + libtool \ + libleptonica-dev \ + pkg-config \ + ca-certificates \ + zlib1g-dev \ + make \ + g++ + +RUN git clone https://github.com/agl/jbig2enc && \ + cd jbig2enc && \ + ./autogen.sh && \ + ./configure && \ + make && \ + make install + + +# Main stage +FROM openjdk:17-jdk-slim AS base +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + python3-uno \ + python3-pip \ + unoconv \ + pngquant \ + unpaper \ + ocrmypdf && \ + rm -rf /var/lib/apt/lists/* && \ + mkdir /usr/share/tesseract-ocr-original && \ + cp -r /usr/share/tesseract-ocr/* /usr/share/tesseract-ocr-original && \ + rm -rf /usr/share/tesseract-ocr + +# Python packages stage +FROM base AS python-packages +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + build-essential \ + libffi-dev \ + libssl-dev \ + zlib1g-dev \ + libjpeg-dev && \ + pip install --upgrade pip && \ + pip install --no-cache-dir \ + opencv-python-headless && \ + rm -rf /var/lib/apt/lists/* + +# Final stage: Copy necessary files from the previous stage +FROM base +COPY --from=python-packages /usr/local /usr/local +COPY --from=jbig2enc_builder /usr/local/bin/jbig2 /usr/local/bin/jbig2 \ No newline at end of file diff --git a/build.gradle b/build.gradle index ff8a5f94..61999152 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,11 @@ dependencies { //general PDF implementation 'org.apache.pdfbox:pdfbox:2.0.28' + + + implementation 'org.springframework.boot:spring-boot-starter-actuator' + implementation 'io.micrometer:micrometer-core' + developmentOnly("org.springframework.boot:spring-boot-devtools") } diff --git a/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java new file mode 100644 index 00000000..b0aa3ed6 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java @@ -0,0 +1,168 @@ +package stirling.software.SPDF.config; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.stereotype.Service; + +@Service +public class EndpointConfiguration { + + private Map endpointStatuses = new ConcurrentHashMap<>(); + private Map> endpointGroups = new ConcurrentHashMap<>(); + + public EndpointConfiguration() { + init(); + processEnvironmentConfigs(); + } + + public void enableEndpoint(String endpoint) { + endpointStatuses.put(endpoint, true); + } + + public void disableEndpoint(String endpoint) { + endpointStatuses.put(endpoint, false); + } + + public boolean isEndpointEnabled(String endpoint) { + if (endpoint.startsWith("/")) { + endpoint = endpoint.substring(1); + } + return endpointStatuses.getOrDefault(endpoint, true); + } + + public void addEndpointToGroup(String group, String endpoint) { + endpointGroups.computeIfAbsent(group, k -> new HashSet<>()).add(endpoint); + } + + public void enableGroup(String group) { + Set endpoints = endpointGroups.get(group); + if (endpoints != null) { + for (String endpoint : endpoints) { + enableEndpoint(endpoint); + } + } + } + + public void disableGroup(String group) { + Set endpoints = endpointGroups.get(group); + if (endpoints != null) { + for (String endpoint : endpoints) { + disableEndpoint(endpoint); + } + } + } + + public void init() { + // Adding endpoints to "PageOps" group + addEndpointToGroup("PageOps", "remove-pages"); + addEndpointToGroup("PageOps", "merge-pdfs"); + addEndpointToGroup("PageOps", "split-pdfs"); + addEndpointToGroup("PageOps", "pdf-organizer"); + addEndpointToGroup("PageOps", "rotate-pdf"); + + // Adding endpoints to "Convert" group + addEndpointToGroup("Convert", "pdf-to-img"); + addEndpointToGroup("Convert", "img-to-pdf"); + addEndpointToGroup("Convert", "pdf-to-pdfa"); + addEndpointToGroup("Convert", "file-to-pdf"); + addEndpointToGroup("Convert", "xlsx-to-pdf"); + addEndpointToGroup("Convert", "pdf-to-word"); + addEndpointToGroup("Convert", "pdf-to-presentation"); + addEndpointToGroup("Convert", "pdf-to-text"); + addEndpointToGroup("Convert", "pdf-to-html"); + addEndpointToGroup("Convert", "pdf-to-xml"); + + // Adding endpoints to "Security" group + addEndpointToGroup("Security", "add-password"); + addEndpointToGroup("Security", "remove-password"); + addEndpointToGroup("Security", "change-permissions"); + addEndpointToGroup("Security", "add-watermark"); + + // Adding endpoints to "Other" group + addEndpointToGroup("Other", "ocr-pdf"); + addEndpointToGroup("Other", "add-image"); + addEndpointToGroup("Other", "compress-pdf"); + addEndpointToGroup("Other", "extract-images"); + addEndpointToGroup("Other", "change-metadata"); + addEndpointToGroup("Other", "extract-image-scans"); + addEndpointToGroup("Other", "sign"); + addEndpointToGroup("Other", "flatten"); + addEndpointToGroup("Other", "repair"); + addEndpointToGroup("Other", "remove-blanks"); + addEndpointToGroup("Other", "compare"); + + + + + + + + //CLI + addEndpointToGroup("CLI", "compress-pdf"); + addEndpointToGroup("CLI", "extract-image-scans"); + addEndpointToGroup("CLI", "remove-blanks"); + addEndpointToGroup("CLI", "repair"); + addEndpointToGroup("CLI", "pdf-to-pdfa"); + addEndpointToGroup("CLI", "file-to-pdf"); + addEndpointToGroup("CLI", "xlsx-to-pdf"); + addEndpointToGroup("CLI", "pdf-to-word"); + addEndpointToGroup("CLI", "pdf-to-presentation"); + addEndpointToGroup("CLI", "pdf-to-text"); + addEndpointToGroup("CLI", "pdf-to-html"); + addEndpointToGroup("CLI", "pdf-to-xml"); + + //python + addEndpointToGroup("Python", "extract-image-scans"); + addEndpointToGroup("Python", "remove-blanks"); + + + + //openCV + addEndpointToGroup("OpenCV", "extract-image-scans"); + addEndpointToGroup("OpenCV", "remove-blanks"); + + //LibreOffice + addEndpointToGroup("LibreOffice", "repair"); + addEndpointToGroup("LibreOffice", "file-to-pdf"); + addEndpointToGroup("LibreOffice", "xlsx-to-pdf"); + addEndpointToGroup("LibreOffice", "pdf-to-word"); + addEndpointToGroup("LibreOffice", "pdf-to-presentation"); + addEndpointToGroup("LibreOffice", "pdf-to-text"); + addEndpointToGroup("LibreOffice", "pdf-to-html"); + addEndpointToGroup("LibreOffice", "pdf-to-xml"); + + + //OCRmyPDF + addEndpointToGroup("OCRmyPDF", "compress-pdf"); + addEndpointToGroup("OCRmyPDF", "pdf-to-pdfa"); + + disableEndpoint("remove-pages"); + disableEndpoint("compress-pdf"); + } + + private void processEnvironmentConfigs() { + String endpointsToRemove = System.getenv("ENDPOINTS_TO_REMOVE"); + String groupsToRemove = System.getenv("GROUPS_TO_REMOVE"); + + if (endpointsToRemove != null) { + String[] endpoints = endpointsToRemove.split(","); + for (String endpoint : endpoints) { + disableEndpoint(endpoint.trim()); + } + } + + if (groupsToRemove != null) { + String[] groups = groupsToRemove.split(","); + for (String group : groups) { + disableGroup(group.trim()); + } + } + } + +} + diff --git a/src/main/java/stirling/software/SPDF/config/EndpointInterceptor.java b/src/main/java/stirling/software/SPDF/config/EndpointInterceptor.java new file mode 100644 index 00000000..4e560148 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/EndpointInterceptor.java @@ -0,0 +1,28 @@ +package stirling.software.SPDF.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@Component +public class EndpointInterceptor implements HandlerInterceptor { + + @Autowired + private EndpointConfiguration endpointConfiguration; + + @Override + 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; + } +} + diff --git a/src/main/java/stirling/software/SPDF/config/MetricsConfig.java b/src/main/java/stirling/software/SPDF/config/MetricsConfig.java new file mode 100644 index 00000000..c80acba4 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/MetricsConfig.java @@ -0,0 +1,28 @@ +package stirling.software.SPDF.config; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.config.MeterFilter; +import io.micrometer.core.instrument.config.MeterFilterReply; + +@Configuration +public class MetricsConfig { + + @Bean + public MeterFilter meterFilter() { + return new MeterFilter() { + @Override + public MeterFilterReply accept(Meter.Id id) { + if (id.getName().equals("http.requests") || id.getName().equals("health")) { + return MeterFilterReply.NEUTRAL; + } + return MeterFilterReply.DENY; + } + }; + } +} \ No newline at end of file diff --git a/src/main/java/stirling/software/SPDF/config/MetricsFilter.java b/src/main/java/stirling/software/SPDF/config/MetricsFilter.java new file mode 100644 index 00000000..57b9272c --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/MetricsFilter.java @@ -0,0 +1,50 @@ +package stirling.software.SPDF.config; + +import java.io.IOException; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.Meter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.config.MeterFilter; +import io.micrometer.core.instrument.config.MeterFilterReply; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@Component +public class MetricsFilter extends OncePerRequestFilter { + + private final MeterRegistry meterRegistry; + + @Autowired + public MetricsFilter(MeterRegistry meterRegistry) { + this.meterRegistry = meterRegistry; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + 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"))) { + Counter counter = Counter.builder("http.requests") + .tag("uri", uri) + .tag("method", request.getMethod()) + .register(meterRegistry); + + counter.increment(); + } + + filterChain.doFilter(request, response); + } + + + +} diff --git a/src/main/java/stirling/software/SPDF/config/WebMvcConfig.java b/src/main/java/stirling/software/SPDF/config/WebMvcConfig.java new file mode 100644 index 00000000..610b11c0 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/WebMvcConfig.java @@ -0,0 +1,18 @@ +package stirling.software.SPDF.config; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + @Autowired + private EndpointInterceptor endpointInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(endpointInterceptor); + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e52ba67a..2e577acb 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -23,3 +23,4 @@ spring.devtools.restart.enabled=true spring.devtools.livereload.enabled=true spring.thymeleaf.encoding=UTF-8 + diff --git a/src/main/resources/templates/fragments/card.html b/src/main/resources/templates/fragments/card.html index eef6c82e..faa816a4 100644 --- a/src/main/resources/templates/fragments/card.html +++ b/src/main/resources/templates/fragments/card.html @@ -1,4 +1,4 @@ -
+
Icon diff --git a/src/main/resources/templates/fragments/navbar.html b/src/main/resources/templates/fragments/navbar.html index ff921f52..d4d0a3cf 100644 --- a/src/main/resources/templates/fragments/navbar.html +++ b/src/main/resources/templates/fragments/navbar.html @@ -151,26 +151,12 @@ function compareVersions(version1, version2) { @@ -181,56 +167,18 @@ function compareVersions(version1, version2) { @@ -243,18 +191,11 @@ function compareVersions(version1, version2) { icon @@ -266,39 +207,18 @@ function compareVersions(version1, version2) { diff --git a/src/main/resources/templates/fragments/navbarEntry.html b/src/main/resources/templates/fragments/navbarEntry.html new file mode 100644 index 00000000..6fd9ba9e --- /dev/null +++ b/src/main/resources/templates/fragments/navbarEntry.html @@ -0,0 +1,6 @@ + diff --git a/src/main/resources/templates/home.html b/src/main/resources/templates/home.html index 97d311bf..5689eaab 100644 --- a/src/main/resources/templates/home.html +++ b/src/main/resources/templates/home.html @@ -88,6 +88,8 @@ filter: invert(0.2) sepia(2) saturate(50) hue-rotate(190deg);
+ +