diff --git a/.github/workflows/push-docker.yml b/.github/workflows/push-docker.yml index 593c43a9..1ffb470e 100644 --- a/.github/workflows/push-docker.yml +++ b/.github/workflows/push-docker.yml @@ -38,7 +38,7 @@ jobs: with: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_API }} - + - name: Login to GitHub Container Registry uses: docker/login-action@v2.1.0 with: @@ -50,26 +50,46 @@ jobs: id: meta uses: docker/metadata-action@v4.4.0 with: - images: | - ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf - ghcr.io/${{ github.repository_owner }}/s-pdf - tags: | - ${{ steps.versionNumber.outputs.versionNumber }}${{ github.ref == 'refs/heads/main' && '-alpha' || '' }} - type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }} + images: | + ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf + ghcr.io/${{ github.repository_owner }}/s-pdf + tags: | + ${{ steps.versionNumber.outputs.versionNumber }} + type=raw,value=latest,enable=${{ github.ref == 'refs/heads/master' }} + type=raw,value=alpha,enable=${{ github.ref == 'refs/heads/main' }} - name: Set up QEMU uses: docker/setup-qemu-action@v2.1.0 - + - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2.5.0 - - - name: Build and push + + - name: Build and push main Dockerfile uses: docker/build-push-action@v4.0.0 with: context: . + dockerfile: ./Dockerfile push: true cache-from: type=gha cache-to: type=gha,mode=max tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} platforms: linux/amd64,linux/arm64/v8 + + - name: Build and push Dockerfile-ultralite + uses: docker/build-push-action@v4.0.0 + with: + context: . + dockerfile: ./Dockerfile-ultralite + push: true + cache-from: type=gha + cache-to: type=gha,mode=max + tags: | + ${{ secrets.DOCKER_HUB_USERNAME }}/s-pdf:ultra-light-latest + ghcr.io/${{ github.repository_owner }}/s-pdf:ultra-light-latest + labels: | + ${{ steps.meta.outputs.labels }} + type=raw,value=ultra-light-latest,enable=${{ github.ref == 'refs/heads/master' }} + type=raw,value=ultra-light-alpha,enable=${{ github.ref == 'refs/heads/main' }} + platforms: linux/amd64,linux/arm64/v8 + diff --git a/Dockerfile-ultralite b/Dockerfile-ultralite new file mode 100644 index 00000000..2c87caf7 --- /dev/null +++ b/Dockerfile-ultralite @@ -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"] diff --git a/DockerfileBase b/DockerfileBase index fc5399af..876b5a10 100644 --- a/DockerfileBase +++ b/DockerfileBase @@ -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 \ diff --git a/README.md b/README.md index a0ca985a..864788b8 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,11 @@ This is a powerful locally hosted web based PDF manipulation tool using docker that allows you to perform various operations on PDF files, such as splitting merging, converting, reorganizing, adding images, rotating, compressing, and more. This locally hosted web application started as a 100% ChatGPT-made application and has evolved to include a wide range of features to handle all your PDF needs. +Stirling PDF makes no outbount calls for any record keeping or tracking. + +All files and PDFs are either purely client side, in server memory only during the execution of the task or within a temporay file only for execution of the task. +Any file which has been downloaded by the user will have already been deleted from the server by that time. + Feel free to request any features or bug fixes either in github issues or our [Discord](https://discord.gg/Cn8pWhQRxZ) @@ -42,6 +47,7 @@ Feel free to request any features or bug fixes either in github issues or our [D - Parallel file processing and downloads - API for integration with external scripts +For a overview of the tasks and the technology each uses please view [groups.md](https://github.com/Frooodle/Stirling-PDF/blob/main/Groups.md) Hosted instance/demo of the app can be seen [here](https://pdf.adminforge.de/) hosted by the team at adminforge.de ## Technologies used @@ -106,14 +112,16 @@ Please view https://github.com/Frooodle/Stirling-PDF/blob/main/HowToUseOCR.md ## Want to add your own language? Stirling PDF currently supports -- English -- Arabic (العربية) -- German (Deutsch) -- French (Français) -- Spanish (Español) -- Chinese (简体中文) -- Catalan (Català) -- Italian (Italiano) +- English (English) (en_GB) +- Arabic (العربية) (ar_AR) +- German (Deutsch) (de_DE) +- French (Français) (fr_FR) +- Spanish (Español) (es_ES) +- Chinese (简体中文) (it_IT) +- Catalan (Català) (zh_CN) +- Italian (Italiano) (ca_CA) +- Swedish (Svenska) (sv_SE) +- Polish (Polski) (pl_PL) If you want to add your own language to Stirling-PDF please refer https://github.com/Frooodle/Stirling-PDF/blob/main/HowToAddNewLanguage.md @@ -133,10 +141,34 @@ Simply use environment variables APP_HOME_NAME, APP_HOME_DESCRIPTION and APP_NAV If running Java directly, you can also pass these as properties using -D arguments. Using the same method you can also change -- The default language by providing APP_LOCALE with values like de-DE fr-FR or ar-AR to select your default language (Will always default to English on invalid locale) -- Enable/Disable search engine visiblility with ALLOW_GOOGLE_VISIBILITY with true / false values. Default disable visibility. + +- The default language by providing APP_LOCALE with values like de-DE fr-FR or ar-AR (Note the - character not _ ) to select your default language (Will always default to English on invalid locale) Current accepted locales can be seen above in the Want to add your own language section +- Enable/Disable search engine visiblility with ALLOW_GOOGLE_VISIBILITY with true / false values. Default disable visiblility. - Change root URI for Stirling-PDF ie change server.com/ to server.com/pdf-app by running APP_ROOT_PATH as pdf-app +- Disable and remove endpoints and functionality from Stirling-PDF. Currently the endpoints ENDPOINTS_TO_REMOVE and GROUPS_TO_REMOVE can include comma seperated lists of endpoints and groups to disable as example ENDPOINTS_TO_REMOVE=img-to-pdf,remove-pages would disable both image to pdf and remove pages, GROUPS_TO_REMOVE=LibreOffice Would disable all things that use LibreOffice. You can see a list of all endpoints and groups [here](https://github.com/Frooodle/Stirling-PDF/blob/main/Groups.md) + ## API For those wanting to use Stirling-PDFs backend API to link with their own custom scripting to edit PDFs you can view all existing API documentation [here](https://app.swaggerhub.com/apis-docs/Frooodle/Stirling-PDF/) or navigate to /swagger-ui/index.html of your stirling-pdf instance for your versions documentation + + +## FAQ + +### Q1: Where is the authentication for Stirling PDF +There is no Auth within Stirling PDF and there is none planned. This feature will not be added. Instead its recommended to use trusted and secure auth software like Authentik or Authelia. + +### Q2: What are your planned features +- Crop +- Progress bar/Tracking +- Annotations (Sign/draw logic directly on page) +- Full custom logic pipelines to combine multiple operations together. +- Folder support with auto scanning to perform operations on +- Redact text +- Add page numbers +- Auto rename (Renames file based on file title text) +- URL to PDF +- Change contrast + +### Q3: Why is my application download .htm files +This is a issue caused commonly by your NGINX congifuration. The default file upload size for NGINX is 1MB, you need to add the following in your Nginx sites-available file. client_max_body_size SIZE; Where "SIZE" is 50M for example for 50MB files. \ No newline at end of file diff --git a/build.gradle b/build.gradle index 75d985c4..3d8100e9 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/groups.md b/groups.md new file mode 100644 index 00000000..9feb2e27 --- /dev/null +++ b/groups.md @@ -0,0 +1,32 @@ +Operation | PageOps | Convert | Security | Other | CLI | Python | OpenCV | LibreOffice | OCRmyPDF | Java | Javascript +--------------------|---------|---------|----------|-------|------|--------|--------|-------------|--------- |-------- |----------- +remove-pages | X | | | | | | | | | X | +merge-pdfs | X | | | | | | | | | X | +split-pdfs | X | | | | | | | | | X | +pdf-organizer | X | | | | | | | | | X | X +rotate-pdf | X | | | | | | | | | X | +pdf-to-img | | X | | | | | | | | X | +img-to-pdf | | X | | | | | | | | X | +pdf-to-pdfa | | X | | | X | | | | X | | +file-to-pdf | | X | | | X | | | X | | | +xlsx-to-pdf | | X | | | X | | | X | | | +pdf-to-word | | X | | | X | | | X | | | +pdf-to-presentation | | X | | | X | | | X | | | +pdf-to-text | | X | | | X | | | X | | | +pdf-to-html | | X | | | X | | | X | | | +pdf-to-xml | | X | | | X | | | X | | | +add-password | | | X | | | | | | | X | +remove-password | | | X | | | | | | | X | +change-permissions | | | X | | | | | | | X | +add-watermark | | | X | | | | | | | X | +ocr-pdf | | | | X | X | | | | X | | +add-image | | | | X | | | | | | X | +compress-pdf | | | | X | X | | | | X +extract-images | | | | X | | | | | | X | +change-metadata | | | | X | | | | | | X | +extract-image-scans | | | | X | X | X | X | | | | +sign | | | | X | | | | | | | X +flatten | | | | X | | | | | | | +repair | | | | X | X | | | X | | | +remove-blanks | | | | X | X | X | X | | | | +compare | | | | X | | | | | | | X 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..a98edbb2 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/EndpointConfiguration.java @@ -0,0 +1,192 @@ +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.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 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) { + logger.info("Disabling {}", 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"); + addEndpointToGroup("CLI", "ocr-pdf"); + + //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"); + addEndpointToGroup("OCRmyPDF", "ocr-pdf"); + + //Java + addEndpointToGroup("Java", "merge-pdfs"); + addEndpointToGroup("Java", "remove-pages"); + addEndpointToGroup("Java", "split-pdfs"); + addEndpointToGroup("Java", "pdf-organizer"); + addEndpointToGroup("Java", "rotate-pdf"); + addEndpointToGroup("Java", "pdf-to-img"); + addEndpointToGroup("Java", "img-to-pdf"); + addEndpointToGroup("Java", "add-password"); + addEndpointToGroup("Java", "remove-password"); + addEndpointToGroup("Java", "change-permissions"); + addEndpointToGroup("Java", "add-watermark"); + addEndpointToGroup("Java", "add-image"); + addEndpointToGroup("Java", "extract-images"); + addEndpointToGroup("Java", "change-metadata"); + + + //Javascript + addEndpointToGroup("Javascript", "pdf-organizer"); + addEndpointToGroup("Javascript", "sign"); + addEndpointToGroup("Javascript", "compare"); + + } + + 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..5befd511 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/config/EndpointInterceptor.java @@ -0,0 +1,26 @@ +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(); + if (!endpointConfiguration.isEndpointEnabled(requestURI)) { + response.sendError(HttpServletResponse.SC_FORBIDDEN, "This endpoint is disabled"); + return false; + } + return true; + } +} \ No newline at end of file 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..928a3868 --- /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")) { + 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..2407b649 --- /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("/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()) + .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/java/stirling/software/SPDF/controller/web/MetricsController.java b/src/main/java/stirling/software/SPDF/controller/web/MetricsController.java new file mode 100644 index 00000000..97d6db93 --- /dev/null +++ b/src/main/java/stirling/software/SPDF/controller/web/MetricsController.java @@ -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 getStatus() { + Map 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 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 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; + } + + } + +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e52ba67a..be16c7ec 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -23,3 +23,6 @@ spring.devtools.restart.enabled=true spring.devtools.livereload.enabled=true spring.thymeleaf.encoding=UTF-8 + +server.connection-timeout=${CONNECTION_TIMEOUT:5m} +spring.mvc.async.request-timeout=${ASYNC_CONNECTION_TIMEOUT:300000} diff --git a/src/main/resources/messages_sv_SE.properties.txt b/src/main/resources/messages_sv_SE.properties similarity index 100% rename from src/main/resources/messages_sv_SE.properties.txt rename to src/main/resources/messages_sv_SE.properties 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 @@ -
+