mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 19:33:11 +02:00
java frontend (#5097)
# Description of Changes <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [ ] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [ ] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [ ] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --------- Co-authored-by: Reece <[email protected]> Co-authored-by: Reece Browne <[email protected]>
This commit is contained in:
co-authored by
Reece
Reece Browne
parent
c3456adc2b
commit
c2a63cf425
@@ -5,6 +5,7 @@ frontend/dist
|
|||||||
frontend/build
|
frontend/build
|
||||||
frontend/.vite
|
frontend/.vite
|
||||||
frontend/.tauri
|
frontend/.tauri
|
||||||
|
frontend/src-tauri/target
|
||||||
|
|
||||||
# Gradle build artifacts
|
# Gradle build artifacts
|
||||||
.gradle
|
.gradle
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ jobs:
|
|||||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./docker/embedded/Dockerfile
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:pr-${{ needs.check-comment.outputs.pr_number }}
|
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:pr-${{ needs.check-comment.outputs.pr_number }}
|
||||||
build-args: VERSION_TAG=alpha
|
build-args: VERSION_TAG=alpha
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
docker-rev: ["Dockerfile", "Dockerfile.ultra-lite", "Dockerfile.fat"]
|
docker-rev: ["docker/embedded/Dockerfile", "docker/embedded/Dockerfile.ultra-lite", "docker/embedded/Dockerfile.fat"]
|
||||||
steps:
|
steps:
|
||||||
- name: Harden Runner
|
- name: Harden Runner
|
||||||
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
|
uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1
|
||||||
@@ -301,7 +301,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
context: .
|
context: .
|
||||||
file: ./docker/backend/${{ matrix.docker-rev }}
|
file: ./${{ matrix.docker-rev }}
|
||||||
push: false
|
push: false
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- V2-master
|
- V2-master
|
||||||
|
- alljavadocker
|
||||||
|
|
||||||
# cancel in-progress jobs if a new job is triggered
|
# cancel in-progress jobs if a new job is triggered
|
||||||
# This is useful to avoid running multiple builds for the same branch if a new commit is pushed
|
# This is useful to avoid running multiple builds for the same branch if a new commit is pushed
|
||||||
@@ -93,10 +94,10 @@ jobs:
|
|||||||
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}
|
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
type=raw,value=latest
|
type=raw,value=latest
|
||||||
|
|
||||||
- name: Generate tags for latest (V2-demo branch - test)
|
- name: Generate tags for latest (alljavadocker branch - test)
|
||||||
id: meta-test
|
id: meta-test
|
||||||
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
|
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
|
||||||
if: github.ref == 'refs/heads/V2-demo'
|
if: github.ref == 'refs/heads/alljavadocker'
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/stirling-tools/stirling-pdf-test
|
ghcr.io/stirling-tools/stirling-pdf-test
|
||||||
@@ -110,7 +111,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
context: .
|
context: .
|
||||||
file: ./docker/Dockerfile.unified
|
file: ./docker/embedded/Dockerfile
|
||||||
push: true
|
push: true
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
@@ -149,10 +150,10 @@ jobs:
|
|||||||
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-fat
|
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-fat
|
||||||
type=raw,value=latest-fat
|
type=raw,value=latest-fat
|
||||||
|
|
||||||
- name: Generate tags for latest-fat (V2-demo branch - test)
|
- name: Generate tags for latest-fat (alljavadocker branch - test)
|
||||||
id: meta-fat-test
|
id: meta-fat-test
|
||||||
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
|
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
|
||||||
if: github.ref == 'refs/heads/V2-demo'
|
if: github.ref == 'refs/heads/alljavadocker'
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/stirling-tools/stirling-pdf-test
|
ghcr.io/stirling-tools/stirling-pdf-test
|
||||||
@@ -166,7 +167,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
context: .
|
context: .
|
||||||
file: ./docker/Dockerfile.unified
|
file: ./docker/embedded/Dockerfile.fat
|
||||||
push: true
|
push: true
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
@@ -203,10 +204,10 @@ jobs:
|
|||||||
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite
|
type=raw,value=${{ steps.versionNumber.outputs.versionNumber }}-ultra-lite
|
||||||
type=raw,value=latest-ultra-lite
|
type=raw,value=latest-ultra-lite
|
||||||
|
|
||||||
- name: Generate tags for ultra-lite (V2-demo branch - test)
|
- name: Generate tags for ultra-lite (alljavadocker branch - test)
|
||||||
id: meta-lite-test
|
id: meta-lite-test
|
||||||
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
|
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f # v5.8.0
|
||||||
if: github.ref == 'refs/heads/V2-demo'
|
if: github.ref == 'refs/heads/alljavadocker'
|
||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/stirling-tools/stirling-pdf-test
|
ghcr.io/stirling-tools/stirling-pdf-test
|
||||||
@@ -220,7 +221,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
context: .
|
context: .
|
||||||
file: ./docker/Dockerfile.unified-lite
|
file: ./docker/embedded/Dockerfile.ultra-lite
|
||||||
push: true
|
push: true
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./docker/embedded/Dockerfile
|
||||||
push: true
|
push: true
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
@@ -152,7 +152,7 @@ jobs:
|
|||||||
if: github.ref != 'refs/heads/main'
|
if: github.ref != 'refs/heads/main'
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile.ultra-lite
|
file: ./docker/embedded/Dockerfile.ultra-lite
|
||||||
push: true
|
push: true
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
@@ -183,7 +183,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
builder: ${{ steps.buildx.outputs.name }}
|
builder: ${{ steps.buildx.outputs.name }}
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile.fat
|
file: ./docker/embedded/Dockerfile.fat
|
||||||
push: true
|
push: true
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=max
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ jobs:
|
|||||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./docker/embedded/Dockerfile
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:test-${{ github.sha }}
|
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/test:test-${{ github.sha }}
|
||||||
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
build-args: VERSION_TAG=${{ steps.versionNumber.outputs.versionNumber }}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ public class RequestUriUtils {
|
|||||||
// Specific static files bundled with the frontend
|
// Specific static files bundled with the frontend
|
||||||
if (normalizedUri.equals("/robots.txt")
|
if (normalizedUri.equals("/robots.txt")
|
||||||
|| normalizedUri.equals("/favicon.ico")
|
|| normalizedUri.equals("/favicon.ico")
|
||||||
|
|| normalizedUri.equals("/manifest.json")
|
||||||
|| normalizedUri.equals("/site.webmanifest")
|
|| normalizedUri.equals("/site.webmanifest")
|
||||||
|| normalizedUri.equals("/manifest-classic.json")
|
|| normalizedUri.equals("/manifest-classic.json")
|
||||||
|| normalizedUri.equals("/index.html")) {
|
|| normalizedUri.equals("/index.html")) {
|
||||||
|
|||||||
+48
-7
@@ -1,20 +1,61 @@
|
|||||||
package stirling.software.SPDF.controller.web;
|
package stirling.software.SPDF.controller.web;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.http.MediaType;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
@Controller
|
@Controller
|
||||||
public class ReactRoutingController {
|
public class ReactRoutingController {
|
||||||
|
|
||||||
@GetMapping(
|
@Value("${server.servlet.context-path:/}")
|
||||||
"/{path:^(?!api|static|robots\\.txt|favicon\\.ico|pipeline|pdfjs|pdfjs-legacy|fonts|images|files|css|js)[^\\.]*$}")
|
private String contextPath;
|
||||||
public String forwardRootPaths() {
|
|
||||||
return "forward:/index.html";
|
@GetMapping(value = {"/", "/index.html"}, produces = MediaType.TEXT_HTML_VALUE)
|
||||||
|
public ResponseEntity<String> serveIndexHtml(HttpServletRequest request)
|
||||||
|
throws IOException {
|
||||||
|
ClassPathResource resource = new ClassPathResource("static/index.html");
|
||||||
|
|
||||||
|
try (InputStream inputStream = resource.getInputStream()) {
|
||||||
|
String html = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
|
||||||
|
// Replace %BASE_URL% with the actual context path for base href
|
||||||
|
String baseUrl = contextPath.endsWith("/") ? contextPath : contextPath + "/";
|
||||||
|
html = html.replace("%BASE_URL%", baseUrl);
|
||||||
|
// Also rewrite any existing <base> tag (Vite may have baked one in)
|
||||||
|
html =
|
||||||
|
html.replaceFirst(
|
||||||
|
"<base href=\\\"[^\\\"]*\\\"\\s*/?>",
|
||||||
|
"<base href=\\\"" + baseUrl + "\\\" />");
|
||||||
|
|
||||||
|
// Inject context path as a global variable for API calls
|
||||||
|
String contextPathScript =
|
||||||
|
"<script>window.STIRLING_PDF_API_BASE_URL = '" + baseUrl + "';</script>";
|
||||||
|
html = html.replace("</head>", contextPathScript + "</head>");
|
||||||
|
|
||||||
|
return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(html);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(
|
@GetMapping(
|
||||||
"/{path:^(?!api|static|pipeline|pdfjs|pdfjs-legacy|fonts|images|files|css|js)[^\\.]*}/{subpath:^(?!.*\\.).*$}")
|
"/{path:^(?!api|static|robots\\.txt|favicon\\.ico|manifest.*\\.json|pipeline|pdfjs|pdfjs-legacy|fonts|images|files|css|js|assets|locales|modern-logo|classic-logo|Login|og_images|samples)[^\\.]*$}")
|
||||||
public String forwardNestedPaths() {
|
public ResponseEntity<String> forwardRootPaths(HttpServletRequest request)
|
||||||
return "forward:/index.html";
|
throws IOException {
|
||||||
|
return serveIndexHtml(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping(
|
||||||
|
"/{path:^(?!api|static|pipeline|pdfjs|pdfjs-legacy|fonts|images|files|css|js|assets|locales|modern-logo|classic-logo|Login|og_images|samples)[^\\.]*}/{subpath:^(?!.*\\.).*$}")
|
||||||
|
public ResponseEntity<String> forwardNestedPaths(HttpServletRequest request)
|
||||||
|
throws IOException {
|
||||||
|
return serveIndexHtml(request);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-1
@@ -324,10 +324,14 @@ public class SecurityConfiguration {
|
|||||||
.authenticated());
|
.authenticated());
|
||||||
// Handle User/Password Logins
|
// Handle User/Password Logins
|
||||||
if (securityProperties.isUserPass()) {
|
if (securityProperties.isUserPass()) {
|
||||||
|
// v2: Authentication is handled via API (/api/v1/auth/login), not form login
|
||||||
|
// We configure form login to handle Spring Security redirects,
|
||||||
|
// but use /perform_login as the processing URL so /login remains a React route
|
||||||
http.formLogin(
|
http.formLogin(
|
||||||
formLogin ->
|
formLogin ->
|
||||||
formLogin
|
formLogin
|
||||||
.loginPage("/login")
|
.loginPage("/login") // Redirect here when unauthenticated
|
||||||
|
.loginProcessingUrl("/perform_login") // Process form posts here (not /login)
|
||||||
.successHandler(
|
.successHandler(
|
||||||
new CustomAuthenticationSuccessHandler(
|
new CustomAuthenticationSuccessHandler(
|
||||||
loginAttemptService,
|
loginAttemptService,
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
# Stirling-PDF Dockerfile - Full version with embedded frontend
|
||||||
|
# Single JAR contains both frontend and backend
|
||||||
|
|
||||||
|
# Stage 1: Build application with embedded frontend
|
||||||
|
FROM gradle:8.14-jdk21 AS build
|
||||||
|
|
||||||
|
# Install Node.js and npm for frontend build
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
||||||
|
&& apt-get install -y nodejs \
|
||||||
|
&& npm --version \
|
||||||
|
&& node --version \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Copy gradle files for dependency resolution
|
||||||
|
COPY build.gradle .
|
||||||
|
COPY settings.gradle .
|
||||||
|
COPY gradlew .
|
||||||
|
COPY gradle gradle/
|
||||||
|
COPY app/core/build.gradle core/.
|
||||||
|
COPY app/common/build.gradle common/.
|
||||||
|
COPY app/proprietary/build.gradle proprietary/.
|
||||||
|
RUN ./gradlew build -x spotlessApply -x spotlessCheck -x test -x sonarqube || return 0
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy entire project
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build JAR with embedded frontend (includes security features controlled at runtime)
|
||||||
|
RUN DISABLE_ADDITIONAL_FEATURES=false \
|
||||||
|
STIRLING_PDF_DESKTOP_UI=false \
|
||||||
|
./gradlew clean build -PbuildWithFrontend=true -x spotlessApply -x spotlessCheck -x test -x sonarqube
|
||||||
|
|
||||||
|
# Stage 2: Runtime image
|
||||||
|
FROM alpine:3.22.1
|
||||||
|
|
||||||
|
ARG VERSION_TAG
|
||||||
|
|
||||||
|
# Labels
|
||||||
|
LABEL org.opencontainers.image.title="Stirling-PDF"
|
||||||
|
LABEL org.opencontainers.image.description="Stirling-PDF with embedded frontend - Full version"
|
||||||
|
LABEL org.opencontainers.image.source="https://github.com/Stirling-Tools/Stirling-PDF"
|
||||||
|
LABEL org.opencontainers.image.licenses="MIT"
|
||||||
|
LABEL org.opencontainers.image.vendor="Stirling-Tools"
|
||||||
|
LABEL org.opencontainers.image.url="https://www.stirlingpdf.com"
|
||||||
|
LABEL org.opencontainers.image.documentation="https://docs.stirlingpdf.com"
|
||||||
|
LABEL maintainer="Stirling-Tools"
|
||||||
|
LABEL org.opencontainers.image.authors="Stirling-Tools"
|
||||||
|
LABEL org.opencontainers.image.version="${VERSION_TAG}"
|
||||||
|
LABEL org.opencontainers.image.keywords="PDF, manipulation, API, Spring Boot, React"
|
||||||
|
|
||||||
|
# Copy scripts and fonts
|
||||||
|
COPY scripts /scripts
|
||||||
|
COPY app/core/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||||
|
|
||||||
|
# Copy built JAR from build stage
|
||||||
|
COPY --from=build /app/app/core/build/libs/*.jar /app.jar
|
||||||
|
COPY --from=build /app/build/libs/restart-helper.jar /restart-helper.jar
|
||||||
|
|
||||||
|
# Environment Variables
|
||||||
|
ENV VERSION_TAG=$VERSION_TAG \
|
||||||
|
JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" \
|
||||||
|
JAVA_CUSTOM_OPTS="" \
|
||||||
|
HOME=/home/stirlingpdfuser \
|
||||||
|
PUID=1000 \
|
||||||
|
PGID=1000 \
|
||||||
|
UMASK=022 \
|
||||||
|
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
|
||||||
|
UNO_PATH=/usr/lib/libreoffice/program \
|
||||||
|
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc \
|
||||||
|
PATH=$PATH:/opt/venv/bin \
|
||||||
|
STIRLING_TEMPFILES_DIRECTORY=/tmp/stirling-pdf \
|
||||||
|
TMPDIR=/tmp/stirling-pdf \
|
||||||
|
TEMP=/tmp/stirling-pdf \
|
||||||
|
TMP=/tmp/stirling-pdf
|
||||||
|
|
||||||
|
# Install all dependencies
|
||||||
|
RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||||
|
echo "@community https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
|
||||||
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
|
||||||
|
apk upgrade --no-cache -a && \
|
||||||
|
apk add --no-cache \
|
||||||
|
ca-certificates \
|
||||||
|
tzdata \
|
||||||
|
tini \
|
||||||
|
bash \
|
||||||
|
curl \
|
||||||
|
shadow \
|
||||||
|
su-exec \
|
||||||
|
openssl \
|
||||||
|
openssl-dev \
|
||||||
|
openjdk21-jre \
|
||||||
|
# Doc conversion
|
||||||
|
gcompat \
|
||||||
|
libc6-compat \
|
||||||
|
libreoffice \
|
||||||
|
ghostscript \
|
||||||
|
fontforge \
|
||||||
|
# pdftohtml
|
||||||
|
poppler-utils \
|
||||||
|
# OCR MY PDF
|
||||||
|
unpaper \
|
||||||
|
tesseract-ocr-data-eng \
|
||||||
|
tesseract-ocr-data-chi_sim \
|
||||||
|
tesseract-ocr-data-deu \
|
||||||
|
tesseract-ocr-data-fra \
|
||||||
|
tesseract-ocr-data-por \
|
||||||
|
ocrmypdf \
|
||||||
|
# CV
|
||||||
|
py3-opencv \
|
||||||
|
python3 \
|
||||||
|
py3-pip \
|
||||||
|
py3-pillow@testing \
|
||||||
|
py3-pdf2image@testing && \
|
||||||
|
python3 -m venv /opt/venv && \
|
||||||
|
/opt/venv/bin/pip install --upgrade pip setuptools && \
|
||||||
|
/opt/venv/bin/pip install --no-cache-dir --upgrade unoserver weasyprint && \
|
||||||
|
ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \
|
||||||
|
ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \
|
||||||
|
ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \
|
||||||
|
mv /usr/share/tessdata /usr/share/tessdata-original && \
|
||||||
|
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders /tmp/stirling-pdf && \
|
||||||
|
fc-cache -f -v && \
|
||||||
|
chmod +x /scripts/* && \
|
||||||
|
# User permissions
|
||||||
|
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
|
||||||
|
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /pipeline /usr/share/fonts/opentype/noto /configs /customFiles /tmp/stirling-pdf && \
|
||||||
|
chown stirlingpdfuser:stirlingpdfgroup /app.jar /restart-helper.jar
|
||||||
|
|
||||||
|
EXPOSE 8080/tcp
|
||||||
|
|
||||||
|
# Set user and run command
|
||||||
|
ENTRYPOINT ["tini", "--", "/scripts/init.sh"]
|
||||||
|
CMD ["sh", "-c", "java -Dfile.encoding=UTF-8 -Djava.io.tmpdir=/tmp/stirling-pdf -jar /app.jar & /opt/venv/bin/unoserver --port 2003 --interface 127.0.0.1"]
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
# Stirling-PDF Dockerfile - Fat version with embedded frontend
|
||||||
|
# Single JAR contains both frontend and backend with extra fonts for air-gapped environments
|
||||||
|
|
||||||
|
# Stage 1: Build application with embedded frontend
|
||||||
|
FROM gradle:8.14-jdk21 AS build
|
||||||
|
|
||||||
|
# Install Node.js and npm for frontend build
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
||||||
|
&& apt-get install -y nodejs \
|
||||||
|
&& npm --version \
|
||||||
|
&& node --version \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Copy gradle files for dependency resolution
|
||||||
|
COPY build.gradle .
|
||||||
|
COPY settings.gradle .
|
||||||
|
COPY gradlew .
|
||||||
|
COPY gradle gradle/
|
||||||
|
COPY app/core/build.gradle core/.
|
||||||
|
COPY app/common/build.gradle common/.
|
||||||
|
COPY app/proprietary/build.gradle proprietary/.
|
||||||
|
RUN ./gradlew build -x spotlessApply -x spotlessCheck -x test -x sonarqube || return 0
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy entire project
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build JAR with embedded frontend (includes security features controlled at runtime)
|
||||||
|
RUN DISABLE_ADDITIONAL_FEATURES=false \
|
||||||
|
STIRLING_PDF_DESKTOP_UI=false \
|
||||||
|
./gradlew clean build -PbuildWithFrontend=true -x spotlessApply -x spotlessCheck -x test -x sonarqube
|
||||||
|
|
||||||
|
# Stage 2: Runtime image
|
||||||
|
FROM alpine:3.22.1
|
||||||
|
|
||||||
|
ARG VERSION_TAG
|
||||||
|
|
||||||
|
# Labels
|
||||||
|
LABEL org.opencontainers.image.title="Stirling-PDF Fat"
|
||||||
|
LABEL org.opencontainers.image.description="Stirling-PDF with embedded frontend - Fat version with extra fonts for air-gapped environments"
|
||||||
|
LABEL org.opencontainers.image.source="https://github.com/Stirling-Tools/Stirling-PDF"
|
||||||
|
LABEL org.opencontainers.image.licenses="MIT"
|
||||||
|
LABEL org.opencontainers.image.vendor="Stirling-Tools"
|
||||||
|
LABEL org.opencontainers.image.url="https://www.stirlingpdf.com"
|
||||||
|
LABEL org.opencontainers.image.documentation="https://docs.stirlingpdf.com"
|
||||||
|
LABEL maintainer="Stirling-Tools"
|
||||||
|
LABEL org.opencontainers.image.authors="Stirling-Tools"
|
||||||
|
LABEL org.opencontainers.image.version="${VERSION_TAG}"
|
||||||
|
LABEL org.opencontainers.image.keywords="PDF, manipulation, fat, air-gapped, API, Spring Boot, React"
|
||||||
|
|
||||||
|
# Copy scripts and fonts
|
||||||
|
COPY scripts /scripts
|
||||||
|
COPY app/core/src/main/resources/static/fonts/*.ttf /usr/share/fonts/opentype/noto/
|
||||||
|
|
||||||
|
# Copy built JAR from build stage
|
||||||
|
COPY --from=build /app/app/core/build/libs/*.jar /app.jar
|
||||||
|
COPY --from=build /app/build/libs/restart-helper.jar /restart-helper.jar
|
||||||
|
|
||||||
|
# Environment Variables
|
||||||
|
ENV VERSION_TAG=$VERSION_TAG \
|
||||||
|
JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" \
|
||||||
|
JAVA_CUSTOM_OPTS="" \
|
||||||
|
HOME=/home/stirlingpdfuser \
|
||||||
|
PUID=1000 \
|
||||||
|
PGID=1000 \
|
||||||
|
UMASK=022 \
|
||||||
|
FAT_DOCKER=true \
|
||||||
|
INSTALL_BOOK_AND_ADVANCED_HTML_OPS=false \
|
||||||
|
PYTHONPATH=/usr/lib/libreoffice/program:/opt/venv/lib/python3.12/site-packages \
|
||||||
|
UNO_PATH=/usr/lib/libreoffice/program \
|
||||||
|
URE_BOOTSTRAP=file:///usr/lib/libreoffice/program/fundamentalrc \
|
||||||
|
PATH=$PATH:/opt/venv/bin \
|
||||||
|
STIRLING_TEMPFILES_DIRECTORY=/tmp/stirling-pdf \
|
||||||
|
TMPDIR=/tmp/stirling-pdf \
|
||||||
|
TEMP=/tmp/stirling-pdf \
|
||||||
|
TMP=/tmp/stirling-pdf
|
||||||
|
|
||||||
|
# Install all dependencies plus extra fonts for air-gapped environments
|
||||||
|
RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||||
|
echo "@community https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
|
||||||
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
|
||||||
|
apk upgrade --no-cache -a && \
|
||||||
|
apk add --no-cache \
|
||||||
|
ca-certificates \
|
||||||
|
tzdata \
|
||||||
|
tini \
|
||||||
|
bash \
|
||||||
|
curl \
|
||||||
|
shadow \
|
||||||
|
su-exec \
|
||||||
|
openssl \
|
||||||
|
openssl-dev \
|
||||||
|
openjdk21-jre \
|
||||||
|
# Doc conversion
|
||||||
|
gcompat \
|
||||||
|
libc6-compat \
|
||||||
|
libreoffice \
|
||||||
|
ghostscript \
|
||||||
|
fontforge \
|
||||||
|
# pdftohtml
|
||||||
|
poppler-utils \
|
||||||
|
# OCR MY PDF
|
||||||
|
unpaper \
|
||||||
|
tesseract-ocr-data-eng \
|
||||||
|
tesseract-ocr-data-chi_sim \
|
||||||
|
tesseract-ocr-data-deu \
|
||||||
|
tesseract-ocr-data-fra \
|
||||||
|
tesseract-ocr-data-por \
|
||||||
|
ocrmypdf \
|
||||||
|
# Extra fonts for fat version
|
||||||
|
font-terminus font-dejavu font-noto font-noto-cjk font-awesome font-noto-extra font-liberation font-linux-libertine \
|
||||||
|
# CV
|
||||||
|
py3-opencv \
|
||||||
|
python3 \
|
||||||
|
py3-pip \
|
||||||
|
py3-pillow@testing \
|
||||||
|
py3-pdf2image@testing && \
|
||||||
|
python3 -m venv /opt/venv && \
|
||||||
|
/opt/venv/bin/pip install --upgrade pip setuptools && \
|
||||||
|
/opt/venv/bin/pip install --no-cache-dir --upgrade unoserver weasyprint && \
|
||||||
|
ln -s /usr/lib/libreoffice/program/uno.py /opt/venv/lib/python3.12/site-packages/ && \
|
||||||
|
ln -s /usr/lib/libreoffice/program/unohelper.py /opt/venv/lib/python3.12/site-packages/ && \
|
||||||
|
ln -s /usr/lib/libreoffice/program /opt/venv/lib/python3.12/site-packages/LibreOffice && \
|
||||||
|
mv /usr/share/tessdata /usr/share/tessdata-original && \
|
||||||
|
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders /tmp/stirling-pdf && \
|
||||||
|
fc-cache -f -v && \
|
||||||
|
chmod +x /scripts/* && \
|
||||||
|
# User permissions
|
||||||
|
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
|
||||||
|
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /pipeline /usr/share/fonts/opentype/noto /configs /customFiles /tmp/stirling-pdf && \
|
||||||
|
chown stirlingpdfuser:stirlingpdfgroup /app.jar /restart-helper.jar
|
||||||
|
|
||||||
|
EXPOSE 8080/tcp
|
||||||
|
|
||||||
|
# Set user and run command
|
||||||
|
ENTRYPOINT ["tini", "--", "/scripts/init.sh"]
|
||||||
|
CMD ["sh", "-c", "java -Dfile.encoding=UTF-8 -Djava.io.tmpdir=/tmp/stirling-pdf -jar /app.jar & /opt/venv/bin/unoserver --port 2003 --interface 127.0.0.1"]
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
# Stirling-PDF Dockerfile - Ultra-lite version with embedded frontend
|
||||||
|
# Single JAR contains both frontend and backend with minimal dependencies
|
||||||
|
|
||||||
|
# Stage 1: Build application with embedded frontend
|
||||||
|
FROM gradle:8.14-jdk21 AS build
|
||||||
|
|
||||||
|
# Install Node.js and npm for frontend build
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
curl \
|
||||||
|
&& curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
|
||||||
|
&& apt-get install -y nodejs \
|
||||||
|
&& npm --version \
|
||||||
|
&& node --version \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# Copy gradle files for dependency resolution
|
||||||
|
COPY build.gradle .
|
||||||
|
COPY settings.gradle .
|
||||||
|
COPY gradlew .
|
||||||
|
COPY gradle gradle/
|
||||||
|
COPY app/core/build.gradle core/.
|
||||||
|
COPY app/common/build.gradle common/.
|
||||||
|
COPY app/proprietary/build.gradle proprietary/.
|
||||||
|
RUN ./gradlew build -x spotlessApply -x spotlessCheck -x test -x sonarqube || return 0
|
||||||
|
|
||||||
|
# Set working directory
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy entire project
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Build ultra-lite JAR with embedded frontend (minimal features)
|
||||||
|
RUN DISABLE_ADDITIONAL_FEATURES=true \
|
||||||
|
STIRLING_PDF_DESKTOP_UI=false \
|
||||||
|
./gradlew clean build -PbuildWithFrontend=true -x spotlessApply -x spotlessCheck -x test -x sonarqube
|
||||||
|
|
||||||
|
# Stage 2: Runtime image
|
||||||
|
FROM alpine:3.22.1
|
||||||
|
|
||||||
|
ARG VERSION_TAG
|
||||||
|
|
||||||
|
# Labels
|
||||||
|
LABEL org.opencontainers.image.title="Stirling-PDF Ultra-Lite"
|
||||||
|
LABEL org.opencontainers.image.description="Stirling-PDF with embedded frontend - Ultra-lite version with minimal dependencies"
|
||||||
|
LABEL org.opencontainers.image.source="https://github.com/Stirling-Tools/Stirling-PDF"
|
||||||
|
LABEL org.opencontainers.image.licenses="MIT"
|
||||||
|
LABEL org.opencontainers.image.vendor="Stirling-Tools"
|
||||||
|
LABEL org.opencontainers.image.url="https://www.stirlingpdf.com"
|
||||||
|
LABEL org.opencontainers.image.documentation="https://docs.stirlingpdf.com"
|
||||||
|
LABEL maintainer="Stirling-Tools"
|
||||||
|
LABEL org.opencontainers.image.authors="Stirling-Tools"
|
||||||
|
LABEL org.opencontainers.image.version="${VERSION_TAG}"
|
||||||
|
LABEL org.opencontainers.image.keywords="PDF, manipulation, ultra-lite, API, Spring Boot, React"
|
||||||
|
|
||||||
|
# Copy scripts
|
||||||
|
COPY scripts/init-without-ocr.sh /scripts/init-without-ocr.sh
|
||||||
|
COPY scripts/installFonts.sh /scripts/installFonts.sh
|
||||||
|
|
||||||
|
# Copy built JAR from build stage
|
||||||
|
COPY --from=build /app/app/core/build/libs/*.jar /app.jar
|
||||||
|
COPY --from=build /app/build/libs/restart-helper.jar /restart-helper.jar
|
||||||
|
|
||||||
|
# Environment Variables
|
||||||
|
ENV VERSION_TAG=$VERSION_TAG \
|
||||||
|
JAVA_BASE_OPTS="-XX:+UnlockExperimentalVMOptions -XX:MaxRAMPercentage=75 -XX:InitiatingHeapOccupancyPercent=20 -XX:+G1PeriodicGCInvokesConcurrent -XX:G1PeriodicGCInterval=10000 -XX:+UseStringDeduplication -XX:G1PeriodicGCSystemLoadThreshold=70" \
|
||||||
|
JAVA_CUSTOM_OPTS="" \
|
||||||
|
HOME=/home/stirlingpdfuser \
|
||||||
|
PUID=1000 \
|
||||||
|
PGID=1000 \
|
||||||
|
UMASK=022 \
|
||||||
|
STIRLING_TEMPFILES_DIRECTORY=/tmp/stirling-pdf \
|
||||||
|
TMPDIR=/tmp/stirling-pdf \
|
||||||
|
TEMP=/tmp/stirling-pdf \
|
||||||
|
TMP=/tmp/stirling-pdf \
|
||||||
|
ENDPOINTS_GROUPS_TO_REMOVE=CLI
|
||||||
|
|
||||||
|
# Install minimal dependencies
|
||||||
|
RUN echo "@main https://dl-cdn.alpinelinux.org/alpine/edge/main" | tee -a /etc/apk/repositories && \
|
||||||
|
echo "@community https://dl-cdn.alpinelinux.org/alpine/edge/community" | tee -a /etc/apk/repositories && \
|
||||||
|
echo "@testing https://dl-cdn.alpinelinux.org/alpine/edge/testing" | tee -a /etc/apk/repositories && \
|
||||||
|
apk upgrade --no-cache -a && \
|
||||||
|
apk add --no-cache \
|
||||||
|
ca-certificates \
|
||||||
|
tzdata \
|
||||||
|
tini \
|
||||||
|
bash \
|
||||||
|
curl \
|
||||||
|
shadow \
|
||||||
|
su-exec \
|
||||||
|
openjdk21-jre && \
|
||||||
|
mkdir -p $HOME /configs /logs /customFiles /pipeline/watchedFolders /pipeline/finishedFolders /tmp/stirling-pdf && \
|
||||||
|
mkdir -p /usr/share/fonts/opentype/noto && \
|
||||||
|
chmod +x /scripts/*.sh && \
|
||||||
|
# User permissions
|
||||||
|
addgroup -S stirlingpdfgroup && adduser -S stirlingpdfuser -G stirlingpdfgroup && \
|
||||||
|
chown -R stirlingpdfuser:stirlingpdfgroup $HOME /scripts /configs /customFiles /pipeline /tmp/stirling-pdf && \
|
||||||
|
chown stirlingpdfuser:stirlingpdfgroup /app.jar /restart-helper.jar
|
||||||
|
|
||||||
|
EXPOSE 8080/tcp
|
||||||
|
|
||||||
|
# Set user and run command
|
||||||
|
ENTRYPOINT ["tini", "--", "/scripts/init-without-ocr.sh"]
|
||||||
|
CMD ["java", "-Dfile.encoding=UTF-8", "-Djava.io.tmpdir=/tmp/stirling-pdf", "-jar", "/app.jar"]
|
||||||
+4
-4
@@ -3,21 +3,21 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<base href="%BASE_URL%" />
|
<base href="%BASE_URL%" />
|
||||||
<link rel="icon" href="/modern-logo/favicon.ico" />
|
<link rel="icon" href="modern-logo/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<meta name="theme-color" content="#000000" />
|
<meta name="theme-color" content="#000000" />
|
||||||
<meta
|
<meta
|
||||||
name="description"
|
name="description"
|
||||||
content="The Free Adobe Acrobat alternative (10M+ Downloads)"
|
content="The Free Adobe Acrobat alternative (10M+ Downloads)"
|
||||||
/>
|
/>
|
||||||
<link rel="apple-touch-icon" href="/modern-logo/logo192.png" />
|
<link rel="apple-touch-icon" href="modern-logo/logo192.png" />
|
||||||
<link rel="manifest" href="/manifest.json" />
|
<link rel="manifest" href="manifest.json" />
|
||||||
|
|
||||||
<title>Stirling PDF</title>
|
<title>Stirling PDF</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/index.tsx"></script>
|
<script type="module" src="src/index.tsx"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -1,7 +1,24 @@
|
|||||||
|
|
||||||
// Base path from Vite config - build-time constant, normalized (no trailing slash)
|
// Base path - read from <base> tag at runtime to support dynamic subpaths
|
||||||
// When no subpath, use empty string instead of '.' to avoid relative path issues
|
// Falls back to Vite's BASE_URL for build-time subpaths
|
||||||
export const BASE_PATH = (import.meta.env.BASE_URL || '/').replace(/\/$/, '').replace(/^\.$/, '');
|
// Normalized: no trailing slash, empty string instead of '.' or '/'
|
||||||
|
function getBasePath(): string {
|
||||||
|
// Try to read from <base> tag first (runtime subpath support)
|
||||||
|
if (typeof document !== 'undefined') {
|
||||||
|
const baseElement = document.querySelector('base');
|
||||||
|
if (baseElement) {
|
||||||
|
const href = baseElement.getAttribute('href');
|
||||||
|
if (href && href !== '%BASE_URL%') {
|
||||||
|
return href.replace(/\/$/, '').replace(/^\.$/, '').replace(/^\/$/, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to Vite's BASE_URL (build-time subpath)
|
||||||
|
return (import.meta.env.BASE_URL || '/').replace(/\/$/, '').replace(/^\.$/, '').replace(/^\/$/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BASE_PATH = getBasePath();
|
||||||
|
|
||||||
// EmbedPDF needs time to remove annotations internally before a recreation runs.
|
// EmbedPDF needs time to remove annotations internally before a recreation runs.
|
||||||
// Without the buffer we occasionally end up with duplicate annotations or stale image data.
|
// Without the buffer we occasionally end up with duplicate annotations or stale image data.
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ export const TourOrchestrationProvider: React.FC<{ children: React.ReactNode }>
|
|||||||
|
|
||||||
const loadSampleFile = useCallback(async () => {
|
const loadSampleFile = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/samples/Sample.pdf');
|
const response = await fetch('samples/Sample.pdf');
|
||||||
const blob = await response.blob();
|
const blob = await response.blob();
|
||||||
const file = new File([blob], 'Sample.pdf', { type: 'application/pdf' });
|
const file = new File([blob], 'Sample.pdf', { type: 'application/pdf' });
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -335,7 +335,7 @@ export default function AdminGeneralSection() {
|
|||||||
label: (
|
label: (
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', padding: '0.25rem 0' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', padding: '0.25rem 0' }}>
|
||||||
<img
|
<img
|
||||||
src="/classic-logo/favicon.ico"
|
src="classic-logo/favicon.ico"
|
||||||
alt={t('admin.settings.general.logoStyle.classicAlt', 'Classic logo')}
|
alt={t('admin.settings.general.logoStyle.classicAlt', 'Classic logo')}
|
||||||
style={{ width: '24px', height: '24px' }}
|
style={{ width: '24px', height: '24px' }}
|
||||||
/>
|
/>
|
||||||
@@ -348,7 +348,7 @@ export default function AdminGeneralSection() {
|
|||||||
label: (
|
label: (
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', padding: '0.25rem 0' }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', padding: '0.25rem 0' }}>
|
||||||
<img
|
<img
|
||||||
src="/modern-logo/StirlingPDFLogoNoTextLight.svg"
|
src="modern-logo/StirlingPDFLogoNoTextLight.svg"
|
||||||
alt={t('admin.settings.general.logoStyle.modernAlt', 'Modern logo')}
|
alt={t('admin.settings.general.logoStyle.modernAlt', 'Modern logo')}
|
||||||
style={{ width: '24px', height: '24px' }}
|
style={{ width: '24px', height: '24px' }}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Reference in New Issue
Block a user