mirror of https://github.com/halo-dev/halo
fix: not clearing the template engine cache after upgrading the theme (#2970)
#### What type of PR is this? /kind improvement #### What this PR does / why we need it: 通过在模板引擎管理器里添加clearCache方法,在升级主题后进行缓存刷新,让新模板内容生效。 #### Which issue(s) this PR fixes: Fixes #2953 #### Special notes for your reviewer: #### Does this PR introduce a user-facing change? ```release-note NONE ```pull/3003/head
parent
64550d235f
commit
efc940df99
|
@ -37,6 +37,7 @@ import run.halo.app.extension.ReactiveExtensionClient;
|
|||
import run.halo.app.extension.router.IListRequest;
|
||||
import run.halo.app.extension.router.QueryParamBuildUtil;
|
||||
import run.halo.app.infra.ThemeRootGetter;
|
||||
import run.halo.app.theme.TemplateEngineManager;
|
||||
|
||||
/**
|
||||
* Endpoint for managing themes.
|
||||
|
@ -54,11 +55,14 @@ public class ThemeEndpoint implements CustomEndpoint {
|
|||
|
||||
private final ThemeService themeService;
|
||||
|
||||
private final TemplateEngineManager templateEngineManager;
|
||||
|
||||
public ThemeEndpoint(ReactiveExtensionClient client, ThemeRootGetter themeRoot,
|
||||
ThemeService themeService) {
|
||||
ThemeService themeService, TemplateEngineManager templateEngineManager) {
|
||||
this.client = client;
|
||||
this.themeRoot = themeRoot;
|
||||
this.themeService = themeService;
|
||||
this.templateEngineManager = templateEngineManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -180,6 +184,9 @@ public class ThemeEndpoint implements CustomEndpoint {
|
|||
return Mono.error(e);
|
||||
}
|
||||
})
|
||||
.flatMap((updatedTheme) -> templateEngineManager.clearCache(
|
||||
updatedTheme.getMetadata().getName())
|
||||
.thenReturn(updatedTheme))
|
||||
.flatMap(updatedTheme -> ServerResponse.ok()
|
||||
.bodyValue(updatedTheme));
|
||||
}
|
||||
|
|
|
@ -7,12 +7,14 @@ import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties;
|
|||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.ConcurrentLruCache;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.thymeleaf.TemplateEngine;
|
||||
import org.thymeleaf.dialect.IDialect;
|
||||
import org.thymeleaf.spring6.ISpringWebFluxTemplateEngine;
|
||||
import org.thymeleaf.spring6.dialect.SpringStandardDialect;
|
||||
import org.thymeleaf.standard.expression.IStandardVariableExpressionEvaluator;
|
||||
import org.thymeleaf.templateresolver.FileTemplateResolver;
|
||||
import org.thymeleaf.templateresolver.ITemplateResolver;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.infra.ExternalUrlSupplier;
|
||||
import run.halo.app.infra.exception.NotFoundException;
|
||||
import run.halo.app.theme.dialect.HaloProcessorDialect;
|
||||
|
@ -47,14 +49,17 @@ public class TemplateEngineManager {
|
|||
|
||||
private final ObjectProvider<IDialect> dialects;
|
||||
|
||||
private final ThemeResolver themeResolver;
|
||||
|
||||
public TemplateEngineManager(ThymeleafProperties thymeleafProperties,
|
||||
ExternalUrlSupplier externalUrlSupplier,
|
||||
ObjectProvider<ITemplateResolver> templateResolvers,
|
||||
ObjectProvider<IDialect> dialects) {
|
||||
ObjectProvider<IDialect> dialects, ThemeResolver themeResolver) {
|
||||
this.thymeleafProperties = thymeleafProperties;
|
||||
this.externalUrlSupplier = externalUrlSupplier;
|
||||
this.templateResolvers = templateResolvers;
|
||||
this.dialects = dialects;
|
||||
this.themeResolver = themeResolver;
|
||||
engineCache = new ConcurrentLruCache<>(CACHE_SIZE_LIMIT, this::templateEngineGenerator);
|
||||
}
|
||||
|
||||
|
@ -77,6 +82,16 @@ public class TemplateEngineManager {
|
|||
}
|
||||
}
|
||||
|
||||
public Mono<Void> clearCache(String themeName) {
|
||||
return themeResolver.getThemeContext(themeName)
|
||||
.doOnNext(themeContext -> {
|
||||
TemplateEngine templateEngine =
|
||||
(TemplateEngine) engineCache.get(themeContext);
|
||||
templateEngine.clearTemplateCache();
|
||||
})
|
||||
.then();
|
||||
}
|
||||
|
||||
private ISpringWebFluxTemplateEngine templateEngineGenerator(ThemeContext theme) {
|
||||
var engine = new SpringWebFluxTemplateEngine();
|
||||
engine.setEnableSpringELCompiler(thymeleafProperties.isEnableSpringElCompiler());
|
||||
|
|
|
@ -6,6 +6,7 @@ import org.apache.commons.lang3.StringUtils;
|
|||
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafProperties;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.Assert;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.infra.SystemConfigurableEnvironmentFetcher;
|
||||
import run.halo.app.infra.SystemSetting.Theme;
|
||||
|
@ -26,6 +27,22 @@ public class ThemeResolver {
|
|||
|
||||
private final ThymeleafProperties thymeleafProperties;
|
||||
|
||||
public Mono<ThemeContext> getThemeContext(String themeName) {
|
||||
Assert.hasText(themeName, "Theme name cannot be empty");
|
||||
var path = FilePathUtils.combinePath(haloProperties.getWorkDir().toString(),
|
||||
THEME_WORK_DIR, themeName);
|
||||
return Mono.just(ThemeContext.builder().name(themeName).path(path))
|
||||
.flatMap(builder -> environmentFetcher.fetch(Theme.GROUP, Theme.class)
|
||||
.mapNotNull(Theme::getActive)
|
||||
.map(activatedTheme -> {
|
||||
boolean active = StringUtils.equals(activatedTheme, themeName);
|
||||
return builder.active(active);
|
||||
})
|
||||
.defaultIfEmpty(builder.active(false))
|
||||
)
|
||||
.map(ThemeContext.ThemeContextBuilder::build);
|
||||
}
|
||||
|
||||
public Mono<ThemeContext> getTheme(ServerHttpRequest request) {
|
||||
return environmentFetcher.fetch(Theme.GROUP, Theme.class)
|
||||
.map(Theme::getActive)
|
||||
|
|
|
@ -51,6 +51,7 @@ class ThemeReconcilerTest {
|
|||
@Mock
|
||||
private HaloProperties haloProperties;
|
||||
|
||||
@Mock
|
||||
private File defaultTheme;
|
||||
|
||||
private Path tempDirectory;
|
||||
|
|
|
@ -4,6 +4,7 @@ import static org.mockito.ArgumentMatchers.any;
|
|||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.isA;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.web.reactive.function.BodyInserters.fromMultipartData;
|
||||
|
@ -32,6 +33,7 @@ import reactor.core.publisher.Mono;
|
|||
import run.halo.app.core.extension.Theme;
|
||||
import run.halo.app.extension.Metadata;
|
||||
import run.halo.app.infra.ThemeRootGetter;
|
||||
import run.halo.app.theme.TemplateEngineManager;
|
||||
|
||||
/**
|
||||
* Tests for {@link ThemeEndpoint}.
|
||||
|
@ -48,6 +50,9 @@ class ThemeEndpointTest {
|
|||
@Mock
|
||||
ThemeService themeService;
|
||||
|
||||
@Mock
|
||||
TemplateEngineManager templateEngineManager;
|
||||
|
||||
@InjectMocks
|
||||
ThemeEndpoint themeEndpoint;
|
||||
|
||||
|
@ -108,6 +113,9 @@ class ThemeEndpointTest {
|
|||
when(themeService.upgrade(eq("default"), isA(InputStream.class)))
|
||||
.thenReturn(Mono.just(newTheme));
|
||||
|
||||
when(templateEngineManager.clearCache(eq("default")))
|
||||
.thenReturn(Mono.empty());
|
||||
|
||||
webTestClient.post()
|
||||
.uri("/themes/default/upgrade")
|
||||
.body(fromMultipartData(bodyBuilder.build()))
|
||||
|
@ -115,6 +123,8 @@ class ThemeEndpointTest {
|
|||
.expectStatus().isOk();
|
||||
|
||||
verify(themeService).upgrade(eq("default"), isA(InputStream.class));
|
||||
|
||||
verify(templateEngineManager, times(1)).clearCache(eq("default"));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue