mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 19:33:11 +02:00
allow static overrides (#5258)
# 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) ### 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.
This commit is contained in:
@@ -33,14 +33,35 @@ public class WebMvcConfig implements WebMvcConfigurer {
|
|||||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
// Cache hashed assets (JS/CSS with content hashes) for 1 year
|
// Cache hashed assets (JS/CSS with content hashes) for 1 year
|
||||||
// These files have names like index-ChAS4tCC.js that change when content changes
|
// These files have names like index-ChAS4tCC.js that change when content changes
|
||||||
|
// Check customFiles/static first, then fall back to classpath
|
||||||
registry.addResourceHandler("/assets/**")
|
registry.addResourceHandler("/assets/**")
|
||||||
.addResourceLocations("classpath:/static/assets/")
|
.addResourceLocations(
|
||||||
|
"file:"
|
||||||
|
+ stirling.software.common.configuration.InstallationPathConfig
|
||||||
|
.getStaticPath()
|
||||||
|
+ "assets/",
|
||||||
|
"classpath:/static/assets/")
|
||||||
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS).cachePublic());
|
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS).cachePublic());
|
||||||
|
|
||||||
// Don't cache index.html - it needs to be fresh to reference latest hashed assets
|
// Don't cache index.html - it needs to be fresh to reference latest hashed assets
|
||||||
|
// Note: index.html is handled by ReactRoutingController for dynamic processing
|
||||||
registry.addResourceHandler("/index.html")
|
registry.addResourceHandler("/index.html")
|
||||||
.addResourceLocations("classpath:/static/")
|
.addResourceLocations(
|
||||||
|
"file:"
|
||||||
|
+ stirling.software.common.configuration.InstallationPathConfig
|
||||||
|
.getStaticPath(),
|
||||||
|
"classpath:/static/")
|
||||||
.setCacheControl(CacheControl.noCache().mustRevalidate());
|
.setCacheControl(CacheControl.noCache().mustRevalidate());
|
||||||
|
|
||||||
|
// Handle all other static resources (js, css, images, fonts, etc.)
|
||||||
|
// Check customFiles/static first for user overrides
|
||||||
|
registry.addResourceHandler("/**")
|
||||||
|
.addResourceLocations(
|
||||||
|
"file:"
|
||||||
|
+ stirling.software.common.configuration.InstallationPathConfig
|
||||||
|
.getStaticPath(),
|
||||||
|
"classpath:/static/")
|
||||||
|
.setCacheControl(CacheControl.maxAge(1, TimeUnit.HOURS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+43
-2
@@ -3,9 +3,14 @@ package stirling.software.SPDF.controller.web;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.core.io.ClassPathResource;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
|
import org.springframework.core.io.FileSystemResource;
|
||||||
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.stereotype.Controller;
|
import org.springframework.stereotype.Controller;
|
||||||
@@ -14,6 +19,11 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
import stirling.software.common.configuration.InstallationPathConfig;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
@Controller
|
@Controller
|
||||||
public class ReactRoutingController {
|
public class ReactRoutingController {
|
||||||
|
|
||||||
@@ -22,24 +32,44 @@ public class ReactRoutingController {
|
|||||||
|
|
||||||
private String cachedIndexHtml;
|
private String cachedIndexHtml;
|
||||||
private boolean indexHtmlExists = false;
|
private boolean indexHtmlExists = false;
|
||||||
|
private boolean useExternalIndexHtml = false;
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
// Only cache if index.html exists (production builds)
|
log.info("Static files custom path: {}", InstallationPathConfig.getStaticPath());
|
||||||
|
|
||||||
|
// Check for external index.html first (customFiles/static/)
|
||||||
|
Path externalIndexPath = Paths.get(InstallationPathConfig.getStaticPath(), "index.html");
|
||||||
|
log.debug("Checking for custom index.html at: {}", externalIndexPath);
|
||||||
|
if (Files.exists(externalIndexPath) && Files.isReadable(externalIndexPath)) {
|
||||||
|
log.info("Using custom index.html from: {}", externalIndexPath);
|
||||||
|
try {
|
||||||
|
this.cachedIndexHtml = processIndexHtml();
|
||||||
|
this.indexHtmlExists = true;
|
||||||
|
this.useExternalIndexHtml = true;
|
||||||
|
return;
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.warn("Failed to load custom index.html, falling back to classpath", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to classpath index.html
|
||||||
ClassPathResource resource = new ClassPathResource("static/index.html");
|
ClassPathResource resource = new ClassPathResource("static/index.html");
|
||||||
if (resource.exists()) {
|
if (resource.exists()) {
|
||||||
try {
|
try {
|
||||||
this.cachedIndexHtml = processIndexHtml();
|
this.cachedIndexHtml = processIndexHtml();
|
||||||
this.indexHtmlExists = true;
|
this.indexHtmlExists = true;
|
||||||
|
this.useExternalIndexHtml = false;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Failed to cache, will process on each request
|
// Failed to cache, will process on each request
|
||||||
|
log.warn("Failed to cache index.html", e);
|
||||||
this.indexHtmlExists = false;
|
this.indexHtmlExists = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String processIndexHtml() throws IOException {
|
private String processIndexHtml() throws IOException {
|
||||||
ClassPathResource resource = new ClassPathResource("static/index.html");
|
Resource resource = getIndexHtmlResource();
|
||||||
|
|
||||||
try (InputStream inputStream = resource.getInputStream()) {
|
try (InputStream inputStream = resource.getInputStream()) {
|
||||||
String html = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
String html = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
@@ -62,6 +92,17 @@ public class ReactRoutingController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Resource getIndexHtmlResource() throws IOException {
|
||||||
|
// Check external location first
|
||||||
|
Path externalIndexPath = Paths.get(InstallationPathConfig.getStaticPath(), "index.html");
|
||||||
|
if (Files.exists(externalIndexPath) && Files.isReadable(externalIndexPath)) {
|
||||||
|
return new FileSystemResource(externalIndexPath.toFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to classpath
|
||||||
|
return new ClassPathResource("static/index.html");
|
||||||
|
}
|
||||||
|
|
||||||
@GetMapping(
|
@GetMapping(
|
||||||
value = {"/", "/index.html"},
|
value = {"/", "/index.html"},
|
||||||
produces = MediaType.TEXT_HTML_VALUE)
|
produces = MediaType.TEXT_HTML_VALUE)
|
||||||
|
|||||||
Reference in New Issue
Block a user