mirror of https://github.com/halo-dev/halo
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 ```pull/2460/head
parent
95b7a273f8
commit
d40626b07b
|
@ -1,8 +1,12 @@
|
||||||
package run.halo.app.config;
|
package run.halo.app.config;
|
||||||
|
|
||||||
import static org.springframework.util.ResourceUtils.FILE_URL_PREFIX;
|
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 com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
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.lang.NonNull;
|
||||||
import org.springframework.web.reactive.config.ResourceHandlerRegistry;
|
import org.springframework.web.reactive.config.ResourceHandlerRegistry;
|
||||||
import org.springframework.web.reactive.config.WebFluxConfigurer;
|
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.RouterFunction;
|
||||||
|
import org.springframework.web.reactive.function.server.ServerRequest;
|
||||||
import org.springframework.web.reactive.function.server.ServerResponse;
|
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.ViewResolutionResultHandler;
|
||||||
import org.springframework.web.reactive.result.view.ViewResolver;
|
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.CustomEndpoint;
|
||||||
import run.halo.app.core.extension.endpoint.CustomEndpointsBuilder;
|
import run.halo.app.core.extension.endpoint.CustomEndpointsBuilder;
|
||||||
import run.halo.app.infra.properties.HaloProperties;
|
import run.halo.app.infra.properties.HaloProperties;
|
||||||
|
@ -30,9 +39,13 @@ public class WebFluxConfig implements WebFluxConfigurer {
|
||||||
|
|
||||||
private final HaloProperties haloProp;
|
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.objectMapper = objectMapper;
|
||||||
this.haloProp = haloProp;
|
this.haloProp = haloProp;
|
||||||
|
this.applicationContext = applicationContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
@ -69,10 +82,45 @@ public class WebFluxConfig implements WebFluxConfigurer {
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
RouterFunction<ServerResponse> consoleIndexRedirection() {
|
||||||
|
return route(GET("/console")
|
||||||
|
.or(GET("/console/index"))
|
||||||
|
.or(GET("/console/index.html")),
|
||||||
|
this::redirectConsole)
|
||||||
|
.and(route(GET("/console/"),
|
||||||
|
this::serveConsoleIndex
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mono<ServerResponse> 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<ServerResponse> redirectConsole(ServerRequest request) {
|
||||||
|
return ServerResponse.permanentRedirect(URI.create("/console/")).build();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||||
var attachmentsRoot = haloProp.getWorkDir().resolve("attachments");
|
var attachmentsRoot = haloProp.getWorkDir().resolve("attachments");
|
||||||
registry.addResourceHandler("/upload/**")
|
registry.addResourceHandler("/upload/**")
|
||||||
.addResourceLocations(FILE_URL_PREFIX + attachmentsRoot + "/");
|
.addResourceLocations(FILE_URL_PREFIX + attachmentsRoot + "/");
|
||||||
|
|
||||||
|
// For console project
|
||||||
|
registry.addResourceHandler("/console/**")
|
||||||
|
.addResourceLocations(haloProp.getConsole().getLocation())
|
||||||
|
.resourceChain(true)
|
||||||
|
.addResolver(new EncodedResourceResolver())
|
||||||
|
.addResolver(new PathResourceResolver());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
package run.halo.app.infra.properties;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ConsoleProperties {
|
||||||
|
|
||||||
|
private String location = "classpath:/console/";
|
||||||
|
|
||||||
|
}
|
|
@ -39,4 +39,7 @@ public class HaloProperties {
|
||||||
|
|
||||||
@Valid
|
@Valid
|
||||||
private final SecurityProperties security = new SecurityProperties();
|
private final SecurityProperties security = new SecurityProperties();
|
||||||
|
|
||||||
|
@Valid
|
||||||
|
private final ConsoleProperties console = new ConsoleProperties();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
server:
|
server:
|
||||||
port: 8090
|
port: 8090
|
||||||
|
compression:
|
||||||
|
enabled: true
|
||||||
spring:
|
spring:
|
||||||
output:
|
output:
|
||||||
ansi:
|
ansi:
|
||||||
|
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
console index
|
Loading…
Reference in New Issue