From d40626b07ba1d5d3db09b1807898aa244a38fe0b Mon Sep 17 00:00:00 2001 From: John Niang Date: Thu, 22 Sep 2022 22:22:13 +0800 Subject: [PATCH] Support configuring console location to access console (#2453) #### What type of PR is this? /kind feature /area core /milestone 2.0 #### What this PR does / why we need it: Support configuring console location to access console. By default, `classpath:/console/` location will be used. If anyone want to debug locally, you can configure the console properties as following: ```yaml halo: console: location: file:/home/xxx/path/to/halo-admin/dist/ # The trailing slash is required or it will be treated with a file instead of a folder. ``` #### Does this PR introduce a user-facing change? ```release-note None ``` --- .../run/halo/app/config/WebFluxConfig.java | 50 ++++++++++++++++++- .../infra/properties/ConsoleProperties.java | 10 ++++ .../app/infra/properties/HaloProperties.java | 3 ++ src/main/resources/application.yaml | 2 + .../halo/app/config/WebFluxConfigTest.java | 41 +++++++++++++++ src/test/resources/console/index.html | 1 + 6 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 src/main/java/run/halo/app/infra/properties/ConsoleProperties.java create mode 100644 src/test/java/run/halo/app/config/WebFluxConfigTest.java create mode 100644 src/test/resources/console/index.html diff --git a/src/main/java/run/halo/app/config/WebFluxConfig.java b/src/main/java/run/halo/app/config/WebFluxConfig.java index ae55dfc1e..0658f4afe 100644 --- a/src/main/java/run/halo/app/config/WebFluxConfig.java +++ b/src/main/java/run/halo/app/config/WebFluxConfig.java @@ -1,8 +1,12 @@ package run.halo.app.config; import static org.springframework.util.ResourceUtils.FILE_URL_PREFIX; +import static org.springframework.web.reactive.function.server.RequestPredicates.GET; +import static org.springframework.web.reactive.function.server.RouterFunctions.route; import com.fasterxml.jackson.databind.ObjectMapper; +import java.net.URI; +import java.time.Instant; import java.util.List; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; @@ -15,10 +19,15 @@ import org.springframework.http.codec.json.Jackson2JsonEncoder; import org.springframework.lang.NonNull; import org.springframework.web.reactive.config.ResourceHandlerRegistry; import org.springframework.web.reactive.config.WebFluxConfigurer; +import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.server.RouterFunction; +import org.springframework.web.reactive.function.server.ServerRequest; import org.springframework.web.reactive.function.server.ServerResponse; +import org.springframework.web.reactive.resource.EncodedResourceResolver; +import org.springframework.web.reactive.resource.PathResourceResolver; import org.springframework.web.reactive.result.view.ViewResolutionResultHandler; import org.springframework.web.reactive.result.view.ViewResolver; +import reactor.core.publisher.Mono; import run.halo.app.core.extension.endpoint.CustomEndpoint; import run.halo.app.core.extension.endpoint.CustomEndpointsBuilder; import run.halo.app.infra.properties.HaloProperties; @@ -30,9 +39,13 @@ public class WebFluxConfig implements WebFluxConfigurer { private final HaloProperties haloProp; - public WebFluxConfig(ObjectMapper objectMapper, HaloProperties haloProp) { + private final ApplicationContext applicationContext; + + public WebFluxConfig(ObjectMapper objectMapper, HaloProperties haloProp, + ApplicationContext applicationContext) { this.objectMapper = objectMapper; this.haloProp = haloProp; + this.applicationContext = applicationContext; } @Bean @@ -69,10 +82,45 @@ public class WebFluxConfig implements WebFluxConfigurer { return builder.build(); } + @Bean + RouterFunction consoleIndexRedirection() { + return route(GET("/console") + .or(GET("/console/index")) + .or(GET("/console/index.html")), + this::redirectConsole) + .and(route(GET("/console/"), + this::serveConsoleIndex + )); + } + + private Mono serveConsoleIndex(ServerRequest request) { + var indexLocation = haloProp.getConsole().getLocation() + "index.html"; + var indexResource = applicationContext.getResource(indexLocation); + try { + return ServerResponse.ok() + .lastModified(Instant.ofEpochMilli(indexResource.lastModified())) + .body(BodyInserters.fromResource(indexResource)); + } catch (Throwable e) { + return Mono.error(e); + } + } + + private Mono redirectConsole(ServerRequest request) { + return ServerResponse.permanentRedirect(URI.create("/console/")).build(); + } + @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { var attachmentsRoot = haloProp.getWorkDir().resolve("attachments"); registry.addResourceHandler("/upload/**") .addResourceLocations(FILE_URL_PREFIX + attachmentsRoot + "/"); + + // For console project + registry.addResourceHandler("/console/**") + .addResourceLocations(haloProp.getConsole().getLocation()) + .resourceChain(true) + .addResolver(new EncodedResourceResolver()) + .addResolver(new PathResourceResolver()); } + } diff --git a/src/main/java/run/halo/app/infra/properties/ConsoleProperties.java b/src/main/java/run/halo/app/infra/properties/ConsoleProperties.java new file mode 100644 index 000000000..5d42c6275 --- /dev/null +++ b/src/main/java/run/halo/app/infra/properties/ConsoleProperties.java @@ -0,0 +1,10 @@ +package run.halo.app.infra.properties; + +import lombok.Data; + +@Data +public class ConsoleProperties { + + private String location = "classpath:/console/"; + +} diff --git a/src/main/java/run/halo/app/infra/properties/HaloProperties.java b/src/main/java/run/halo/app/infra/properties/HaloProperties.java index 5f0538f0b..66d2124bb 100644 --- a/src/main/java/run/halo/app/infra/properties/HaloProperties.java +++ b/src/main/java/run/halo/app/infra/properties/HaloProperties.java @@ -39,4 +39,7 @@ public class HaloProperties { @Valid private final SecurityProperties security = new SecurityProperties(); + + @Valid + private final ConsoleProperties console = new ConsoleProperties(); } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 0386c6da2..d5fc67d27 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,5 +1,7 @@ server: port: 8090 + compression: + enabled: true spring: output: ansi: diff --git a/src/test/java/run/halo/app/config/WebFluxConfigTest.java b/src/test/java/run/halo/app/config/WebFluxConfigTest.java new file mode 100644 index 000000000..a8f26a8cb --- /dev/null +++ b/src/test/java/run/halo/app/config/WebFluxConfigTest.java @@ -0,0 +1,41 @@ +package run.halo.app.config; + +import java.util.List; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.web.reactive.server.WebTestClient; + +@SpringBootTest(properties = "halo.console.location=classpath:/console/") +@AutoConfigureWebTestClient +class WebFluxConfigTest { + + @Autowired + WebTestClient webClient; + + @Nested + class ConsoleRequest { + + @Test + void shouldRedirect() { + List.of("/console", "/console/index", "/console/index.html") + .forEach(index -> { + webClient.get().uri(index) + .exchange() + .expectStatus().isPermanentRedirect() + .expectHeader().location("/console/"); + }); + } + + @Test + void shouldRequestConsoleIndex() { + webClient.get().uri("/console/") + .exchange() + .expectStatus().isOk() + .expectBody(String.class).isEqualTo("console index\n"); + } + } + +} \ No newline at end of file diff --git a/src/test/resources/console/index.html b/src/test/resources/console/index.html new file mode 100644 index 000000000..f4d6c3d1f --- /dev/null +++ b/src/test/resources/console/index.html @@ -0,0 +1 @@ +console index