Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: restructure RSS generation to support extension by other plugins #39

Merged
merged 1 commit into from
Dec 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/cd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ jobs:
with:
app-id: app-KhIVw
skip-node-setup: true
artifacts-path: 'app/build/libs'
1 change: 1 addition & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ jobs:
uses: halo-sigs/reusable-workflows/.github/workflows/plugin-ci.yaml@v1
with:
skip-node-setup: true
artifacts-path: 'app/build/libs'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,4 @@ application-local.properties

/admin-frontend/node_modules/
/workplace/
*/workplace/
104 changes: 95 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,92 @@

Halo 2.0 的 RSS 订阅链接生成插件

## 如何扩展 RSS 源

> 从 feed 插件 v1.4.0 版本开始,支持扩展 RSS 功能。

`feed` 插件提供了扩展点,允许其他插件扩展 RSS 源。

### 步骤 1:在插件中引入 feed 依赖

在你的插件项目中添加 `feed` 插件的依赖:

```groovy
dependencies {
// ...
compileOnly "run.halo.feed:api:{version}"
}
```

将 `{version}` 替换为实际的 `feed` 插件版本号。

### 步骤 2:实现 `RssRouteItem` 扩展点接口

创建一个类实现 `run.halo.feed.RssRouteItem` 接口,提供自定义的 RSS 数据源。例如:

```java
public class MomentRssProvider implements RssRouteItem {
// 实现具体的 RSS 提供逻辑
}
```

你可以参考 [PostRssProvider](./app/src/main/java/run/halo/feed/provider/PostRssProvider.java) 示例。

### 步骤 3:声明扩展点

在 `src/main/resources/extensions`
目录下,声明你的扩展。你可以参考 [ext-definition.yaml](app/src/main/resources/extensions/ext-definition.yaml) 文件来完成此步骤。

### 步骤 4:定义配置类并清理 RSS 缓存

在插件中定义一个配置类,使用 `@ConditionalOnClass` 注解确保只有在 `run.halo.feed.RssRouteItem` 类存在时才会创建对应的
Bean。同时,定义事件监听器来清理缓存。

`@ConditionalOnClass` 注解只能使用 name 属性来指定类全限定名,不支持使用 value 属性。

示例代码:

```java

@Configuration
@ConditionalOnClass(name = "run.halo.feed.RssRouteItem")
@RequiredArgsConstructor
public class RssAutoConfiguration {
private final ApplicationEventPublisher eventPublisher;

@Bean
public MomentRssProvider momentRssProvider() {
return new MomentRssProvider();
}

@Async
@EventListener({MomentUpdatedEvent.class, MomentDeletedEvent.class, ContextClosedEvent.class})
public void onMomentUpdatedOrDeleted() {
var rule = CacheClearRule.forExact("/feed/moments/rss.xml");
var event = RssCacheClearRequested.forRule(this, rule);
eventPublisher.publishEvent(event);
}
}
```

此配置确保了当 `RssRouteItem` 接口存在时,插件才会自动创建 `MomentRssProvider` 并监听相关事件来清理缓存。

### 步骤 5:声明插件依赖

在 `plugin.yaml` 文件中声明 `feed` 插件为可选依赖,确保当 `feed` 插件存在并启用时,插件能够自动注册 RSS 源。

```yaml
apiVersion: plugin.halo.run/v1alpha1
kind: Plugin
metadata:
name: moment
spec:
pluginDependencies:
"PluginFeed?": ">=1.4.0"
```

这样,当 `feed` 插件可用时,插件会自动注册自定义的 RSS 源。

## 开发环境

```bash
Expand All @@ -28,15 +114,15 @@ cd path/to/plugin-feed

```yaml
halo:
plugin:
runtime-mode: development
classes-directories:
- "build/classes"
- "build/resources"
lib-directories:
- "libs"
fixedPluginPath:
- "/path/to/plugin-feed"
plugin:
runtime-mode: development
classes-directories:
- "build/classes"
- "build/resources"
lib-directories:
- "libs"
fixedPluginPath:
- "/path/to/plugin-feed"
```

## 使用方式
Expand Down
76 changes: 76 additions & 0 deletions api/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
plugins {
id 'java-library'
id 'maven-publish'
id "io.freefair.lombok" version "8.0.0-rc2"
}

group = 'run.halo.feed'
version = rootProject.version

java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
withSourcesJar()
}

compileJava.options.encoding = "UTF-8"
compileTestJava.options.encoding = "UTF-8"
javadoc.options.encoding = "UTF-8"

dependencies {
api platform('run.halo.tools.platform:plugin:2.20.11')
compileOnly 'run.halo.app:api'
}

test {
useJUnitPlatform()
}

publishing {
publications {
mavenJava(MavenPublication) {
from components.java
artifact tasks.sourcesJar

artifactId = 'api'
version = project.hasProperty('version') ? project.property('version') : 'unspecified'

pom {
name = 'RSS'
description = '为站点生成 RSS 订阅链接'
url = 'https://www.halo.run/store/apps/app-KhIVw'

licenses {
license {
name = 'GPL-3.0'
url = 'https://github.com/halo-dev/plugin-feed/blob/main/LICENSE'
}
}

developers {
developer {
id = 'guqing'
name = 'guqing'
email = '[email protected]'
}
}

scm {
connection = 'scm:git:[email protected]:halo-dev/plugin-feed.git'
developerConnection = 'scm:git:[email protected]:halo-dev/plugin-feed.git'
url = 'https://github.com/halo-dev/plugin-feed'
}
}
}
}
repositories {
maven {
url = version.endsWith('-SNAPSHOT') ? 'https://s01.oss.sonatype.org/content/repositories/snapshots/' :
'https://s01.oss.sonatype.org/content/repositories/releases/'
credentials {
username = project.findProperty("ossr.user") ?: System.getenv("OSSR_USERNAME")
password = project.findProperty("ossr.password") ?: System.getenv("OSSR_PASSWORD")
}
}
}
}
40 changes: 40 additions & 0 deletions api/src/main/java/run/halo/feed/CacheClearRule.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package run.halo.feed;

import org.springframework.util.Assert;

public record CacheClearRule(Type type, String value) {
public CacheClearRule {
Assert.notNull(type, "Type cannot be null");
Assert.notNull(value, "Value cannot be null");
if (type == Type.EXACT && !value.startsWith("/")) {
throw new IllegalArgumentException("Exact value must start with /");
}
}

public static CacheClearRule forPrefix(String prefix) {
return new CacheClearRule(Type.PREFIX, prefix);
}

public static CacheClearRule forExact(String exact) {
return new CacheClearRule(Type.EXACT, exact);
}

public static CacheClearRule forContains(String contains) {
return new CacheClearRule(Type.CONTAINS, contains);
}

@Override
public String toString() {
return "CacheClearRule{" +
"type=" + type +
", value='" + value + '\'' +
'}';
}

public enum Type {
PREFIX,
EXACT,
CONTAINS
}
}

Loading
Loading