feat: add system info getter for plugin (#7103)

#### What type of PR is this?
/kind improvement
/area core
/milestone 2.20.x

#### What this PR does / why we need it:
为插件提供 SystemInfoGetter 用于获取站点基本信息

站点标题描述和系统版本这些信息对于插件来说是很有必要的避免插件要直接查询 system ConfigMap 来获取,如 RSS 和 通知器扩展等插件都会需要用到

```json
{
  "title" : "guqing's blog",
  "subtitle" : "副标题",
  "logo" : "/upload/myavatar.png",
  "favicon" : "/upload/myavatar.png",
  "url" : "http://localhost:8090",
  "version" : {
    "majorVersion" : 2,
    "minorVersion" : 20,
    "normalVersion" : "2.20.10",
    "preRelease" : true,
    "publicApiStable" : true,
    "patchVersion" : 10,
    "preReleaseVersion" : "SNAPSHOT",
    "buildMetadata" : "",
    "stable" : false
  },
  "seo" : {
    "blockSpiders" : false,
    "keywords" : "keyword1,keyword2",
    "description" : "站点描述"
  },
  "locale" : "zh_CN_#Hans",
  "timeZone" : "Asia/Shanghai",
  "activatedThemeName" : "theme-earth"
}
```

#### Does this PR introduce a user-facing change?

```release-note
开发者相关:为插件提供 SystemInfoGetter 用于获取站点基本信息
```
pull/7111/head
guqing 2024-12-04 15:53:10 +08:00 committed by GitHub
parent fef06edcd8
commit ead667683c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 123 additions and 0 deletions

View File

@ -0,0 +1,40 @@
package run.halo.app.infra;
import com.github.zafarkhaja.semver.Version;
import java.net.URL;
import java.util.Locale;
import java.util.TimeZone;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class SystemInfo {
private String title;
private String subtitle;
private String logo;
private String favicon;
private URL url;
private Version version;
private SeoProp seo;
private Locale locale;
private TimeZone timeZone;
private String activatedThemeName;
@Data
@Accessors(chain = true)
public static class SeoProp {
private boolean blockSpiders;
private String keywords;
private String description;
}
}

View File

@ -0,0 +1,7 @@
package run.halo.app.infra;
import java.util.function.Supplier;
import reactor.core.publisher.Mono;
public interface SystemInfoGetter extends Supplier<Mono<SystemInfo>> {
}

View File

@ -0,0 +1,71 @@
package run.halo.app.infra;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Locale;
import java.util.TimeZone;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@Component
@RequiredArgsConstructor
public class SystemInfoGetterImpl implements SystemInfoGetter {
private final SystemConfigurableEnvironmentFetcher environmentFetcher;
private final SystemVersionSupplier systemVersionSupplier;
private final ExternalUrlSupplier externalUrlSupplier;
private final ServerProperties serverProperties;
private final WebFluxProperties webFluxProperties;
@Override
public Mono<SystemInfo> get() {
var systemInfo = new SystemInfo()
.setVersion(systemVersionSupplier.get())
.setUrl(getExternalUrl())
// TODO populate locale and timezone from system settings in the future
.setLocale(Locale.getDefault())
.setTimeZone(TimeZone.getDefault());
var basicMono =
environmentFetcher.fetch(SystemSetting.Basic.GROUP, SystemSetting.Basic.class)
.doOnNext(basic -> systemInfo.setTitle(basic.getTitle())
.setSubtitle(basic.getSubtitle())
.setLogo(basic.getLogo())
.setFavicon(basic.getFavicon())
);
var seoMono = environmentFetcher.fetch(SystemSetting.Seo.GROUP, SystemSetting.Seo.class)
.doOnNext(seo -> systemInfo.setSeo(new SystemInfo.SeoProp()
.setBlockSpiders(BooleanUtils.isTrue(seo.blockSpiders))
.setKeywords(seo.getKeywords())
.setDescription(seo.getDescription())
));
var themeMono =
environmentFetcher.fetch(SystemSetting.Theme.GROUP, SystemSetting.Theme.class)
.doOnNext(theme -> systemInfo.setActivatedThemeName(theme.getActive()));
return Mono.when(basicMono, seoMono, themeMono)
.thenReturn(systemInfo);
}
private URL getExternalUrl() {
var url = externalUrlSupplier.getRaw();
if (url != null) {
return url;
}
var port = serverProperties.getPort();
var basePath = StringUtils.defaultIfBlank(webFluxProperties.getBasePath(), "/");
try {
var uriStr = "http://localhost:" + port + basePath;
return URI.create(StringUtils.removeEnd(uriStr, "/")).toURL();
} catch (MalformedURLException e) {
// Should not happen
throw new RuntimeException("Cannot create URL from server properties.", e);
}
}
}

View File

@ -17,6 +17,7 @@ import run.halo.app.extension.ReactiveExtensionClient;
import run.halo.app.infra.BackupRootGetter;
import run.halo.app.infra.ExternalLinkProcessor;
import run.halo.app.infra.ExternalUrlSupplier;
import run.halo.app.infra.SystemInfoGetter;
import run.halo.app.notification.NotificationCenter;
import run.halo.app.notification.NotificationReasonEmitter;
import run.halo.app.plugin.extensionpoint.ExtensionGetter;
@ -97,6 +98,10 @@ public enum SharedApplicationContextFactory {
.ifUnique(userDetailsService ->
beanFactory.registerSingleton("userDetailsService", userDetailsService)
);
rootContext.getBeanProvider(SystemInfoGetter.class)
.ifUnique(systemInfoGetter ->
beanFactory.registerSingleton("systemInfoGetter", systemInfoGetter)
);
// TODO add more shared instance here
sharedContext.refresh();