Skip to content

Commit

Permalink
Merge branch 'halo-dev:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
LIlGG authored Nov 6, 2023
2 parents 58798e5 + 4ea2014 commit 3a7e6df
Show file tree
Hide file tree
Showing 96 changed files with 2,665 additions and 813 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,5 @@ application-local.properties
!application/src/test/resources/themes/*.zip
!application/src/main/resources/themes/*.zip
application/src/main/resources/console/
application/src/main/resources/uc/
application/src/main/resources/presets/
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
## 快速开始

```bash
docker run -it -d --name halo -p 8090:8090 -v ~/.halo2:/root/.halo2 halohub/halo:2.9
docker run -it -d --name halo -p 8090:8090 -v ~/.halo2:/root/.halo2 halohub/halo:2.10
```

以上仅作为体验使用,详细部署文档请查阅:<https://docs.halo.run/getting-started/install/docker-compose>
Expand Down
4 changes: 2 additions & 2 deletions application/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
id 'org.springframework.boot' version '3.1.4'
id 'org.springframework.boot' version '3.1.5'
id 'io.spring.dependency-management' version '1.1.0'
id "com.gorylenko.gradle-git-properties" version "2.3.2"
id "checkstyle"
Expand Down Expand Up @@ -78,7 +78,7 @@ tasks.register('downloadPluginPresets', Download) {
'https://github.com/halo-dev/plugin-search-widget/releases/download/v1.2.0/plugin-search-widget-1.2.0.jar',
'https://github.com/halo-dev/plugin-sitemap/releases/download/v1.1.1/plugin-sitemap-1.1.1.jar',
'https://github.com/halo-dev/plugin-feed/releases/download/v1.2.0/plugin-feed-1.2.0.jar',
'https://github.com/halo-dev/plugin-app-store/releases/download/v1.0.0-beta.1/plugin-app-store-1.0.0-beta.1.jar'
'https://github.com/halo-dev/plugin-app-store/releases/download/v1.0.0-beta.2/plugin-app-store-1.0.0-beta.2.jar'
])
dest 'src/main/resources/presets/plugins'
}
39 changes: 32 additions & 7 deletions application/src/main/java/run/halo/app/config/WebFluxConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,13 @@
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.console.ConsoleProxyFilter;
import run.halo.app.console.ProxyFilter;
import run.halo.app.console.WebSocketRequestPredicate;
import run.halo.app.core.extension.endpoint.CustomEndpoint;
import run.halo.app.core.extension.endpoint.CustomEndpointsBuilder;
Expand Down Expand Up @@ -104,11 +103,21 @@ RouterFunction<ServerResponse> consoleIndexRedirection() {
.and(path("/console/**").and(path("/console/assets/**").negate()))
.and(accept(MediaType.TEXT_HTML))
.and(new WebSocketRequestPredicate().negate());
return route(consolePredicate, this::serveConsoleIndex);
return route(consolePredicate,
request -> this.serveIndex(haloProp.getConsole().getLocation() + "index.html"));
}

private Mono<ServerResponse> serveConsoleIndex(ServerRequest request) {
var indexLocation = haloProp.getConsole().getLocation() + "index.html";
@Bean
RouterFunction<ServerResponse> ucIndexRedirect() {
var consolePredicate = method(HttpMethod.GET)
.and(path("/uc/**").and(path("/uc/assets/**").negate()))
.and(accept(MediaType.TEXT_HTML))
.and(new WebSocketRequestPredicate().negate());
return route(consolePredicate,
request -> this.serveIndex(haloProp.getUc().getLocation() + "index.html"));
}

private Mono<ServerResponse> serveIndex(String indexLocation) {
var indexResource = applicationContext.getResource(indexLocation);
try {
return ServerResponse.ok()
Expand Down Expand Up @@ -142,6 +151,15 @@ public void addResourceHandlers(ResourceHandlerRegistry registry) {
.addResolver(new EncodedResourceResolver())
.addResolver(new PathResourceResolver());

// For uc assets
registry.addResourceHandler("/uc/assets/**")
.addResourceLocations(haloProp.getUc().getLocation() + "assets/")
.setCacheControl(cacheControl)
.setUseLastModified(useLastModified)
.resourceChain(true)
.addResolver(new EncodedResourceResolver())
.addResolver(new PathResourceResolver());

// Additional resource mappings
var staticResources = haloProp.getAttachment().getResourceMappings();
staticResources.forEach(staticResource -> {
Expand Down Expand Up @@ -172,7 +190,14 @@ public void addResourceHandlers(ResourceHandlerRegistry registry) {

@ConditionalOnProperty(name = "halo.console.proxy.enabled", havingValue = "true")
@Bean
ConsoleProxyFilter consoleProxyFilter() {
return new ConsoleProxyFilter(haloProp);
ProxyFilter consoleProxyFilter() {
return new ProxyFilter("/console/**", haloProp.getConsole().getProxy());
}


@ConditionalOnProperty(name = "halo.uc.proxy.enabled", havingValue = "true")
@Bean
ProxyFilter ucProxyFilter() {
return new ProxyFilter("/uc/**", haloProp.getUc().getProxy());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,26 @@
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;
import run.halo.app.infra.properties.ProxyProperties;

@Slf4j
public class ConsoleProxyFilter implements WebFilter {
public class ProxyFilter 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/**");
public ProxyFilter(String pattern, ProxyProperties proxyProperties) {
this.proxyProperties = proxyProperties;
var consoleMatcher = ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, pattern);
consoleMatcher = new AndServerWebExchangeMatcher(consoleMatcher,
new NegatedServerWebExchangeMatcher(new WebSocketServerWebExchangeMatcher()));
this.consoleMatcher = consoleMatcher;
this.webClient = WebClient.create(proxyProperties.getEndpoint().toString());
log.info("Initialized ConsoleProxyFilter to proxy console");
log.debug("Initialized ProxyFilter to proxy {} to endpoint {}", pattern,
proxyProperties.getEndpoint());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package run.halo.app.content.comment;

import io.micrometer.common.util.StringUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import run.halo.app.content.NotificationReasonConst;
import run.halo.app.core.extension.content.Comment;
Expand Down Expand Up @@ -56,13 +58,21 @@ public void subscribeNewReplyReasonForReply(Reply reply) {
void subscribeReply(Subscription.ReasonSubject reasonSubject,
Identity identity) {
var subscriber = createSubscriber(identity);
if (subscriber == null) {
return;
}
var interestReason = new Subscription.InterestReason();
interestReason.setReasonType(NotificationReasonConst.SOMEONE_REPLIED_TO_YOU);
interestReason.setSubject(reasonSubject);
notificationCenter.subscribe(subscriber, interestReason).block();
}

@Nullable
private Subscription.Subscriber createSubscriber(Identity author) {
if (StringUtils.isBlank(author.name())) {
return null;
}

Subscription.Subscriber subscriber;
if (author.isEmail()) {
subscriber = subscriberEmailResolver.ofEmail(author.name());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@
import java.util.function.Predicate;
import java.util.function.Supplier;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.pf4j.PluginState;
import org.reactivestreams.Publisher;
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
import org.springframework.beans.factory.DisposableBean;
Expand Down Expand Up @@ -201,6 +203,27 @@ public RouterFunction<ServerResponse> endpoint() {
.response(responseBuilder()
.implementation(Plugin.class))
)
.PUT("plugins/{name}/plugin-state", this::changePluginRunningState,
builder -> builder.operationId("ChangePluginRunningState")
.description("Change the running state of a plugin by name.")
.tag(tag)
.parameter(parameterBuilder()
.name("name")
.in(ParameterIn.PATH)
.required(true)
.implementation(String.class)
)
.requestBody(requestBodyBuilder()
.required(true)
.content(contentBuilder()
.mediaType(MediaType.APPLICATION_JSON_VALUE)
.schema(schemaBuilder()
.implementation(RunningStateRequest.class))
)
)
.response(responseBuilder()
.implementation(Plugin.class))
)
.GET("plugins", this::list, builder -> {
builder.operationId("ListPlugins")
.tag(tag)
Expand Down Expand Up @@ -255,6 +278,56 @@ public RouterFunction<ServerResponse> endpoint() {
.build();
}

Mono<ServerResponse> changePluginRunningState(ServerRequest request) {
final var name = request.pathVariable("name");
return request.bodyToMono(RunningStateRequest.class)
.flatMap(runningState -> {
final var enable = runningState.isEnable();
return client.get(Plugin.class, name)
.flatMap(plugin -> {
plugin.getSpec().setEnabled(enable);
return client.update(plugin);
})
.flatMap(plugin -> {
if (runningState.isAsync()) {
return Mono.just(plugin);
}
return waitForPluginToMeetExpectedState(name, p -> {
// when enabled = true,excepted phase = started || failed
// when enabled = false,excepted phase = !started
var phase = p.statusNonNull().getPhase();
if (enable) {
return PluginState.STARTED.equals(phase)
|| PluginState.FAILED.equals(phase);
}
return !PluginState.STARTED.equals(phase);
});
});
})
.flatMap(plugin -> ServerResponse.ok().bodyValue(plugin));
}

Mono<Plugin> waitForPluginToMeetExpectedState(String name, Predicate<Plugin> predicate) {
return Mono.defer(() -> client.get(Plugin.class, name)
.map(plugin -> {
if (predicate.test(plugin)) {
return plugin;
}
throw new IllegalStateException("Plugin " + name + " is not in expected state");
})
)
.retryWhen(Retry.backoff(10, Duration.ofMillis(100))
.filter(IllegalStateException.class::isInstance)
);
}

@Data
@Schema(name = "PluginRunningStateRequest")
static class RunningStateRequest {
private boolean enable;
private boolean async;
}

private Mono<ServerResponse> fetchJsBundle(ServerRequest request) {
Optional<String> versionOption = request.queryParam("v");
return versionOption.map(s ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import java.time.Duration;
import java.util.Objects;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.fn.builders.schema.Builder;
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
Expand Down Expand Up @@ -42,9 +42,10 @@
*/
@Slf4j
@Component
@AllArgsConstructor
@RequiredArgsConstructor
public class PostEndpoint implements CustomEndpoint {

private int maxAttemptsWaitForPublish = 10;
private final PostService postService;
private final ReactiveExtensionClient client;

Expand Down Expand Up @@ -243,7 +244,7 @@ private Mono<Post> awaitPostPublished(String postName) {
})
.switchIfEmpty(Mono.error(
() -> new RetryException("Retry to check post publish status"))))
.retryWhen(Retry.fixedDelay(10, Duration.ofMillis(200))
.retryWhen(Retry.backoff(maxAttemptsWaitForPublish, Duration.ofMillis(100))
.filter(t -> t instanceof RetryException));
}

Expand Down Expand Up @@ -278,4 +279,11 @@ Mono<ServerResponse> listPost(ServerRequest request) {
return postService.listPost(postQuery)
.flatMap(listedPosts -> ServerResponse.ok().bodyValue(listedPosts));
}

/**
* Convenient for testing, to avoid waiting too long for post published when testing.
*/
public void setMaxAttemptsWaitForPublish(int maxAttempts) {
this.maxAttemptsWaitForPublish = maxAttempts;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.Optional;
import java.util.Set;
import lombok.AllArgsConstructor;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.springframework.context.ApplicationEvent;
Expand Down Expand Up @@ -102,12 +103,20 @@ public Result reconcile(Request request) {
post.getMetadata().setAnnotations(annotations);
}

if (!annotations.containsKey(Post.PUBLISHED_LABEL)) {
labels.put(Post.PUBLISHED_LABEL, BooleanUtils.FALSE);
}

var status = post.getStatus();
if (status == null) {
status = new Post.PostStatus();
post.setStatus(status);
}

if (post.isPublished() && post.getSpec().getPublishTime() == null) {
post.getSpec().setPublishTime(Instant.now());
}

// calculate the sha256sum
var configSha256sum = Hashing.sha256().hashString(post.getSpec().toString(), UTF_8)
.toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ void subscribeNewCommentNotification(SinglePage page) {

private void reconcileSpec(String name) {
client.fetch(SinglePage.class, name).ifPresent(page -> {
if (page.isPublished() && page.getSpec().getPublishTime() == null) {
page.getSpec().setPublishTime(Instant.now());
}

// un-publish if necessary
if (page.isPublished() && Objects.equals(false, page.getSpec().getPublish())) {
unPublish(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public Mono<Boolean> contains(Collection<String> source, Collection<String> cand
if (source.contains(SuperAdminInitializer.SUPER_ROLE_NAME)) {
return Mono.just(true);
}
return listDependencies(new HashSet<>(source), shouldFilterHidden(true))
return listDependencies(new HashSet<>(source), shouldFilterHidden(false))
.map(role -> role.getMetadata().getName())
.collect(Collectors.toSet())
.map(roleNames -> roleNames.containsAll(candidates));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StreamUtils;
import org.springframework.web.server.ServerWebInputException;
import reactor.core.Exceptions;
import reactor.core.publisher.Flux;
Expand Down Expand Up @@ -179,9 +178,15 @@ public Flux<DataBuffer> uglifyCssBundle() {
return BundleResourceUtils.getJsBundleResource(pluginManager, pluginName,
BundleResourceUtils.CSS_BUNDLE);
})
.flatMap(resource -> DataBufferUtils.read(resource,
DefaultDataBufferFactory.sharedInstance, StreamUtils.BUFFER_SIZE)
);
.flatMap(resource -> {
try {
return DataBufferUtils.read(resource, DefaultDataBufferFactory.sharedInstance,
(int) resource.contentLength());
} catch (IOException e) {
log.error("Failed to read plugin css bundle resource", e);
return Flux.empty();
}
});
}

@Override
Expand Down
Loading

0 comments on commit 3a7e6df

Please sign in to comment.