-
-
Notifications
You must be signed in to change notification settings - Fork 9.4k
/
ThemeConfiguration.java
89 lines (80 loc) · 3.65 KB
/
ThemeConfiguration.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package run.halo.app.theme;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.accept;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
import java.io.IOException;
import java.nio.file.Path;
import java.time.Instant;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.web.WebProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.MediaType;
import org.springframework.security.web.server.context.ServerSecurityContextRepository;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.thymeleaf.extras.springsecurity6.dialect.SpringSecurityDialect;
import reactor.core.publisher.Mono;
import run.halo.app.infra.ThemeRootGetter;
import run.halo.app.infra.utils.FileUtils;
import run.halo.app.theme.dialect.HaloSpringSecurityDialect;
import run.halo.app.theme.dialect.LinkExpressionObjectDialect;
/**
* @author guqing
* @since 2.0.0
*/
@Configuration
public class ThemeConfiguration {
private final ThemeRootGetter themeRoot;
public ThemeConfiguration(ThemeRootGetter themeRoot) {
this.themeRoot = themeRoot;
}
@Bean
public RouterFunction<ServerResponse> themeAssets(WebProperties webProperties) {
var cacheProperties = webProperties.getResources().getCache();
return route(
GET("/themes/{themeName}/assets/{*resource}").and(accept(MediaType.TEXT_PLAIN)),
request -> {
var themeName = request.pathVariable("themeName");
var resource = request.pathVariable("resource");
resource = StringUtils.removeStart(resource, "/");
var fsRes = new FileSystemResource(getThemeAssetsPath(themeName, resource));
if (!fsRes.exists()) {
return ServerResponse.notFound().build();
}
var bodyBuilder = ServerResponse.ok()
.cacheControl(cacheProperties.getCachecontrol().toHttpCacheControl());
try {
if (cacheProperties.isUseLastModified()) {
var lastModified = Instant.ofEpochMilli(fsRes.lastModified());
return request.checkNotModified(lastModified)
.switchIfEmpty(Mono.defer(() -> bodyBuilder.lastModified(lastModified)
.body(BodyInserters.fromResource(fsRes))));
}
return bodyBuilder.body(BodyInserters.fromResource(fsRes));
} catch (IOException e) {
return Mono.error(e);
}
});
}
Path getThemeAssetsPath(String themeName, String resource) {
Path basePath = themeRoot.get()
.resolve(themeName)
.resolve("templates")
.resolve("assets");
Path result = basePath.resolve(resource);
FileUtils.checkDirectoryTraversal(basePath, result);
return result;
}
@Bean
LinkExpressionObjectDialect linkExpressionObjectDialect() {
return new LinkExpressionObjectDialect();
}
@Bean
SpringSecurityDialect springSecurityDialect(
ServerSecurityContextRepository securityContextRepository) {
return new HaloSpringSecurityDialect(securityContextRepository);
}
}