From a8250500fc56916bb3a4d640e45152900312a6f6 Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Thu, 4 May 2023 15:40:38 +0800 Subject: [PATCH] refactor: add system initialization check and redirect to console if not initialized (#3892) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind improvement /area core /milestone 2.5.2 #### What this PR does / why we need it: 添加系统初始化检查,如果未初始化则重定向到控制台。 此检查只针对首页,当用户访问首页时检查到未初始化则跳转到 Console 让用户初始化以优化没有数据时的访问体验。 SetupStateCache 用于缓存系统初始化状态,当数据库状态改变时会更新缓存以优化性能,避免每次访问首页都查询数据。 #### Which issue(s) this PR fixes: A part of #3230 #### Does this PR introduce a user-facing change? ```release-note 添加系统初始化检查,如果未初始化则重定向到控制台 ``` --- .../run/halo/app/infra/SetupStateCache.java | 96 +++++++++++++++ .../InitializeRedirectionWebFilter.java | 57 +++++++++ .../InitializeRedirectionWebFilterTest.java | 109 ++++++++++++++++++ .../ThemeMessageResolverIntegrationTest.java | 5 + 4 files changed, 267 insertions(+) create mode 100644 application/src/main/java/run/halo/app/infra/SetupStateCache.java create mode 100644 application/src/main/java/run/halo/app/security/InitializeRedirectionWebFilter.java create mode 100644 application/src/test/java/run/halo/app/security/InitializeRedirectionWebFilterTest.java diff --git a/application/src/main/java/run/halo/app/infra/SetupStateCache.java b/application/src/main/java/run/halo/app/infra/SetupStateCache.java new file mode 100644 index 000000000..b98995343 --- /dev/null +++ b/application/src/main/java/run/halo/app/infra/SetupStateCache.java @@ -0,0 +1,96 @@ +package run.halo.app.infra; + +import io.micrometer.common.util.StringUtils; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; +import lombok.Data; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Component; +import run.halo.app.extension.ConfigMap; +import run.halo.app.extension.ExtensionClient; +import run.halo.app.extension.controller.Controller; +import run.halo.app.extension.controller.ControllerBuilder; +import run.halo.app.extension.controller.Reconciler; +import run.halo.app.infra.utils.JsonUtils; + +/** + *
A cache that caches system setup state.
+ * when setUp state changed, the cache will be updated. + * + * @author guqing + * @since 2.5.2 + */ +@Component +public class SetupStateCache implements ReconcilerGets system setup state.
+ * Never return null. + * + * @returntrue
if system is initialized, false
otherwise.
+ */
+ @NonNull
+ @Override
+ public Boolean get() {
+ return valueCache.get();
+ }
+
+ @Override
+ public Result reconcile(Request request) {
+ if (!SYSTEM_STATES_CONFIGMAP.equals(request.name())) {
+ return Result.doNotRetry();
+ }
+ valueCache.cache(isInitialized());
+ return Result.doNotRetry();
+ }
+
+ @Override
+ public Controller setupWith(ControllerBuilder builder) {
+ return builder
+ .extension(new ConfigMap())
+ .build();
+ }
+
+ /**
+ * Check if system is initialized.
+ *
+ * @return true
if system is initialized, false
otherwise.
+ */
+ private boolean isInitialized() {
+ return client.fetch(ConfigMap.class, SYSTEM_STATES_CONFIGMAP)
+ .filter(configMap -> configMap.getData() != null)
+ .map(ConfigMap::getData)
+ .flatMap(map -> Optional.ofNullable(map.get(SystemStates.GROUP))
+ .filter(StringUtils::isNotBlank)
+ .map(value -> JsonUtils.jsonToObject(value, SystemStates.class).getIsSetup())
+ )
+ .orElse(false);
+ }
+
+ @Data
+ static class SystemStates {
+ static final String GROUP = "states";
+ Boolean isSetup;
+ }
+
+ static class InternalValueCache {
+ private final AtomicBoolean value = new AtomicBoolean(false);
+
+ public boolean cache(boolean newValue) {
+ return value.getAndSet(newValue);
+ }
+
+ public boolean get() {
+ return value.get();
+ }
+ }
+}
diff --git a/application/src/main/java/run/halo/app/security/InitializeRedirectionWebFilter.java b/application/src/main/java/run/halo/app/security/InitializeRedirectionWebFilter.java
new file mode 100644
index 000000000..e7f9b8386
--- /dev/null
+++ b/application/src/main/java/run/halo/app/security/InitializeRedirectionWebFilter.java
@@ -0,0 +1,57 @@
+package run.halo.app.security;
+
+import java.net.URI;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpMethod;
+import org.springframework.lang.NonNull;
+import org.springframework.security.web.server.DefaultServerRedirectStrategy;
+import org.springframework.security.web.server.ServerRedirectStrategy;
+import org.springframework.security.web.server.util.matcher.PathPatternParserServerWebExchangeMatcher;
+import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
+import org.springframework.stereotype.Component;
+import org.springframework.util.Assert;
+import org.springframework.web.server.ServerWebExchange;
+import org.springframework.web.server.WebFilter;
+import org.springframework.web.server.WebFilterChain;
+import reactor.core.publisher.Mono;
+import run.halo.app.infra.SetupStateCache;
+
+/**
+ * A web filter that will redirect user to set up page if system is not initialized.
+ *
+ * @author guqing
+ * @since 2.5.2
+ */
+@Component
+@RequiredArgsConstructor
+public class InitializeRedirectionWebFilter implements WebFilter {
+ private final URI location = URI.create("/console");
+ private final ServerWebExchangeMatcher redirectMatcher =
+ new PathPatternParserServerWebExchangeMatcher("/", HttpMethod.GET);
+
+ private final SetupStateCache setupStateCache;
+
+ private ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();
+
+ @Override
+ @NonNull
+ public Mono