mirror of https://github.com/halo-dev/halo
Support proxying console in server side (#2535)
#### What type of PR is this? /kind feature /area core /milestone 2.0 #### What this PR does / why we need it: Support proxying console in server side. We just need to configure the properties as following: ```yaml halo: console: proxy: endpoint: http://localhost:3000/ enabled: true ``` Before starting Halo server, please start the console first at port 3000 by checkouting https://github.com/halo-dev/console/pull/638. Now we can have a try to access console via <http://localhost:8090/console/>. Please note that this feature should be only for development environment instead of production environment. #### Todos - [ ] Proxy WebSocket for hmr of Vite #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/2530 #### Does this PR introduce a user-facing change? ```release-note None ```pull/2536/head
parent
af8860ffb6
commit
dbaa087936
|
@ -8,6 +8,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
|||
import java.net.URI;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -28,6 +29,7 @@ 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.console.ConsoleProxyFilter;
|
||||
import run.halo.app.core.extension.endpoint.CustomEndpoint;
|
||||
import run.halo.app.core.extension.endpoint.CustomEndpointsBuilder;
|
||||
import run.halo.app.infra.properties.HaloProperties;
|
||||
|
@ -123,4 +125,10 @@ public class WebFluxConfig implements WebFluxConfigurer {
|
|||
.addResolver(new PathResourceResolver());
|
||||
}
|
||||
|
||||
|
||||
@ConditionalOnProperty(name = "halo.console.proxy.enabled", havingValue = "true")
|
||||
@Bean
|
||||
ConsoleProxyFilter consoleProxyFilter() {
|
||||
return new ConsoleProxyFilter(haloProp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
package run.halo.app.console;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.web.server.util.matcher.AndServerWebExchangeMatcher;
|
||||
import org.springframework.security.web.server.util.matcher.MediaTypeServerWebExchangeMatcher;
|
||||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
|
||||
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatchers;
|
||||
import org.springframework.web.reactive.function.BodyExtractors;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebFilter;
|
||||
import org.springframework.web.server.WebFilterChain;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
import reactor.core.publisher.Mono;
|
||||
import run.halo.app.infra.properties.ConsoleProperties.ProxyProperties;
|
||||
import run.halo.app.infra.properties.HaloProperties;
|
||||
|
||||
@Slf4j
|
||||
public class ConsoleProxyFilter implements WebFilter {
|
||||
|
||||
private final ProxyProperties proxyProperties;
|
||||
|
||||
private final ServerWebExchangeMatcher consoleMatcher;
|
||||
|
||||
private final WebClient webClient;
|
||||
|
||||
public ConsoleProxyFilter(HaloProperties haloProperties) {
|
||||
this.proxyProperties = haloProperties.getConsole().getProxy();
|
||||
var consoleMatcher = ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, "/console/**");
|
||||
consoleMatcher = new AndServerWebExchangeMatcher(consoleMatcher,
|
||||
new MediaTypeServerWebExchangeMatcher(MediaType.TEXT_HTML));
|
||||
this.consoleMatcher = consoleMatcher;
|
||||
this.webClient = WebClient.create(proxyProperties.getEndpoint().toString());
|
||||
log.info("Initialized ConsoleProxyFilter to proxy console");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
|
||||
return consoleMatcher.matches(exchange)
|
||||
.filter(ServerWebExchangeMatcher.MatchResult::isMatch)
|
||||
.switchIfEmpty(chain.filter(exchange).then(Mono.empty()))
|
||||
.map(matchResult -> {
|
||||
var request = exchange.getRequest();
|
||||
return UriComponentsBuilder.fromUriString(
|
||||
request.getPath().pathWithinApplication().value())
|
||||
.queryParams(request.getQueryParams())
|
||||
.build()
|
||||
.toUriString();
|
||||
})
|
||||
.doOnNext(uri -> {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Proxy {} to {}", uri, proxyProperties.getEndpoint());
|
||||
}
|
||||
})
|
||||
.flatMap(uri -> webClient.get()
|
||||
.uri(uri)
|
||||
.headers(httpHeaders -> httpHeaders.addAll(exchange.getRequest().getHeaders()))
|
||||
.exchangeToMono(clientResponse -> {
|
||||
var response = exchange.getResponse();
|
||||
// set headers
|
||||
response.getHeaders().putAll(clientResponse.headers().asHttpHeaders());
|
||||
// set cookies
|
||||
response.getCookies().putAll(clientResponse.cookies());
|
||||
// set status code
|
||||
response.setStatusCode(clientResponse.statusCode());
|
||||
var body = clientResponse.body(BodyExtractors.toDataBuffers());
|
||||
return exchange.getResponse().writeAndFlushWith(Mono.just(body));
|
||||
}));
|
||||
}
|
||||
}
|
|
@ -1,5 +1,7 @@
|
|||
package run.halo.app.infra.properties;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import java.net.URI;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
|
@ -7,4 +9,20 @@ public class ConsoleProperties {
|
|||
|
||||
private String location = "classpath:/console/";
|
||||
|
||||
@Valid
|
||||
private ProxyProperties proxy = new ProxyProperties();
|
||||
|
||||
@Data
|
||||
public static class ProxyProperties {
|
||||
|
||||
/**
|
||||
* Console endpoint in development environment to be proxied. e.g.: http://localhost:8090/
|
||||
*/
|
||||
private URI endpoint;
|
||||
|
||||
/**
|
||||
* Indicates if the proxy behaviour is enabled. Default is false
|
||||
*/
|
||||
private boolean enabled = false;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue