docs: update plugin development document

Signed-off-by: Ryan Wang <i@ryanc.cc>
This commit is contained in:
Ryan Wang
2022-07-15 18:03:10 +08:00
parent a058a08907
commit cb8508bb5f

251
README.md
View File

@@ -1,6 +1,6 @@
# halo-plugin-template # plugin-template
A Halo plugin template for backend development Halo 2.0 插件开发快速开始模板WIP
## 如何开发一个 Halo 插件 ## 如何开发一个 Halo 插件
@@ -16,59 +16,82 @@ A Halo plugin template for backend development
├── LICENSE ├── LICENSE
├── README.md ├── README.md
├── admin-frontend ├── admin-frontend
│   ├── ... │   ├── README.md
├── build │   ├── env.d.ts
│   ├── classes │   ├── package.json
│   ├── libs │   ├── pnpm-lock.yaml
│   │   ├── halo-plugin-template-0.0.1-SNAPSHOT-plain.jar │   ├── src
│   ├── resources │   │   ├── assets
│   │   │   └── logo.svg
│   │   ├── components
│   │   │   └── HelloWorld.vue
│   │   ├── index.ts # Admin Frontend Entry file
│   │   ├── styles
│   │   │   └── index.css
│   │   └── views
│   │   └── DefaultView.vue # Views Component
│   ├── tsconfig.app.json
│   ├── tsconfig.config.json
│   ├── tsconfig.json
│   ├── tsconfig.vitest.json
│   └── vite.config.ts
├── build.gradle ├── build.gradle
├── gradle
│   └── wrapper
│   ├── gradle-wrapper.jar
│   └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── lib ├── lib
│   └── halo-2.0.0-SNAPSHOT-plain.jar │   └── halo-2.0.0-SNAPSHOT-plain.jar
├── settings.gradle ├── settings.gradle
└── src └── src
└── main └── main
├── java ├── java
│   └── io │   └── run
│   └── github │   └── halo
│   └── guqing
│   └── template │   └── template
│   ├── Apple.java │   ├── Apple.java
│   ├── ApplePlugin.java │   ├── ApplesController.java
│   └── ApplesController.java │   └── TemplatePlugin.java # Main Class
└── resources └── resources
├── admin ├── admin
│   ├── main.js │   ├── main.js # Admin Frontend Entry file(production build)
│   └── style.css │   └── style.css
├── extensions ├── extensions
│   ├── apple.yaml │   ├── apple.yaml
│   ├── reverseProxy.yaml │   ├── reverseProxy.yaml # Reverse Proxy Config
│   ── roleTemplate.yaml │   ── roleTemplate.yaml # Role Template Config
├── plugin.yaml │   └── settings.yaml # Settings Config
├── plugin.yaml # Plugin Config
└── static └── static
├── ... ├── image.jpeg
├── some.txt
└── test.html
``` ```
对于以上目录树: 对于以上目录树:
- `admin-frontend` 插件前端项目目录,为一个 vue 项目 - `admin-frontend` 插件前端项目目录,为一个 Vue 项目,技术栈为 Vue 3 + Vite其中已经预配置好了构建策略。
- `build`:插件后端构建目录,`build/libs` 下的 jar 包为最终插件产物 - `build`:插件后端构建目录,`build/libs` 下的 jar 包为最终插件产物
- `lib`:为临时的 halo 依赖,为了使用 halo 中提供的类在 `build.gradle` 中作为编译时依赖引入 `compileOnly files("lib/halo-2.0.0-SNAPSHOT-plain.jar")`,待 `2.0` 正式发布会将其发布到 `maven` 中央仓库,便可通过 `gradle `依赖; - `lib`:为临时的 Halo 依赖,为了使用 Halo 中提供的类在 `build.gradle` 中作为编译时依赖引入 `compileOnly files("lib/halo-2.0.0-SNAPSHOT-plain.jar")`
,待 `2.0` 正式发布会将其发布到 `maven` 中央仓库,便可通过 `gradle` 依赖。
- `src`: 为插件后端源码目录 - `src` 为插件后端源码目录
- `Apple.java` 为自定义模型 - `Apple.java` 为自定义模型
```java ```java
@GVK(group = "apple.guqing.xyz", kind = "Apple", @GVK(group = "apple.guqing.xyz", kind = "Apple",
version = "v1alpha1", singular = "apple", plural = "apples") version = "v1alpha1", singular = "apple", plural = "apples")
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class Apple extends AbstractExtension {} public class Apple extends AbstractExtension {
}
``` ```
关键在于标注 `@GVK `注解和 `extends AbstractExtension`,当如此定义了一个模型后,插件启动时会根据 `@GVK` 配置自动生成`CRUD``RESTful API`,以此为例子会生成如下`APIs` 关键在于标注 `@GVK` 注解和 `extends AbstractExtension`,当如此定义了一个模型后,插件启动时会根据 `@GVK` 配置自动生成`CRUD`的 `RESTful API`
,以此为例子会生成如下 `APIs`
```text ```http
GET /apis/apple.guqing.xyz/v1alpha1/apples GET /apis/apple.guqing.xyz/v1alpha1/apples
POST /apis/apple.guqing.xyz/v1alpha1/apples POST /apis/apple.guqing.xyz/v1alpha1/apples
@@ -82,9 +105,13 @@ DELETE /apis/apple.guqing.xyz/v1alpha1/apples/{name}
生成规则见:[Halo extension RFC](https://github.com/halo-dev/rfcs/tree/main/extension) 生成规则见:[Halo extension RFC](https://github.com/halo-dev/rfcs/tree/main/extension)
- `ApplePlugin.java`:插件生命周期入口,它继承 `BasePlugin`,可以通过`getApplicationContext()`方法获取到 `SchemeManager`,然后在 `start()` 方法中注册自定义模型,这一步必不可少,所有定义的自定义模型都需要在此注册,并在 `stop()` 生命周期方法中清理资源。 - `TemplatePlugin.java`:插件生命周期入口,它继承 `BasePlugin`,可以通过 `getApplicationContext()` 方法获取到 `SchemeManager`,然后在 `start()`
方法中注册自定义模型,这一步必不可少,所有定义的自定义模型都需要在此注册,并在 `stop()` 生命周期方法中清理资源。
```java ```java
public class TemplatePlugin extends BasePlugin {
// ...
@Override @Override
public void start() { public void start() {
schemeManager.register(Apple.class); schemeManager.register(Apple.class);
@@ -92,10 +119,12 @@ public void start() {
@Override @Override
public void stop() { public void stop() {
// TODO 优化取消注册自定义模型的写法
Scheme scheme = schemeManager.get(Apple.class); Scheme scheme = schemeManager.get(Apple.class);
schemeManager.unregister(scheme); schemeManager.unregister(scheme);
} }
// ...
}
``` ```
注意:该类不能标注 `@Component` 等能将其声明为 `Spring Bean` 的注解 注意:该类不能标注 `@Component` 等能将其声明为 `Spring Bean` 的注解
@@ -103,6 +132,7 @@ public void stop() {
- `ApplesController.java`:如果根据模型自动生成的 `CURD RESTful APIs` 无法满足业务需要,可以写常规 `Controller` 来自定义 `APIs`,示例: - `ApplesController.java`:如果根据模型自动生成的 `CURD RESTful APIs` 无法满足业务需要,可以写常规 `Controller` 来自定义 `APIs`,示例:
```java ```java
@ApiVersion("v1alpha1") @ApiVersion("v1alpha1")
@RestController @RestController
@RequestMapping("colors") @RequestMapping("colors")
@@ -115,9 +145,9 @@ public class ApplesController {
} }
``` ```
插件定义 `Controller` 必须要标注 `@ApiVersion` 注解,否则启动时会报错。如果定义了这样的 `Controller` ,插件启动后会生成如下的 `APIs` 插件定义 `Controller` 必须要标注 `@ApiVersion` 注解,否则启动时会报错。如果定义了这样的 `Controller` ,插件启动后会生成如下的 `APIs`
```text ```http
GET /api/v1alpha1/plugins/apples/colors GET /api/v1alpha1/plugins/apples/colors
``` ```
@@ -125,8 +155,8 @@ GET /api/v1alpha1/plugins/apples/colors
其中: 其中:
- `version`:来自 `@ApiVersion("v1alpha1") `的 value详情参考[Halo plugin API composition](https://github.com/halo-dev/rfcs/blob/main/plugin/pluggable-design.md#api-%E6%9E%84%E6%88%90%E8%AE%A8%E8%AE%BA) - `version`:来自 `@ApiVersion("v1alpha1")` 的
value详情参考[Halo plugin API composition](https://github.com/halo-dev/rfcs/blob/main/plugin/pluggable-design.md#api-%E6%9E%84%E6%88%90%E8%AE%A8%E8%AE%BA)
- `plugin-name`:值来自 `plugin.yaml` 中的 `metadata.name` 属性 - `plugin-name`:值来自 `plugin.yaml` 中的 `metadata.name` 属性
- `mapping-in-class`:来自标注在类上的 `@RequestMapping("colors")` - `mapping-in-class`:来自标注在类上的 `@RequestMapping("colors")`
- `mapping-in-method`:来自标注在方法上的 `@GetMapping` - `mapping-in-method`:来自标注在方法上的 `@GetMapping`
@@ -134,6 +164,7 @@ GET /api/v1alpha1/plugins/apples/colors
插件还允许使用 `@Service`、`@Component` 注解将其声明为一个 `Spring Bean`,便可通过依赖注入使用 `Spring Bean`,示例: 插件还允许使用 `@Service`、`@Component` 注解将其声明为一个 `Spring Bean`,便可通过依赖注入使用 `Spring Bean`,示例:
```java ```java
@Service @Service
public class ColorService { public class ColorService {
public String getColor(String appleName) { public String getColor(String appleName) {
@@ -146,6 +177,7 @@ public class ColorService {
@RequestMapping("colors") @RequestMapping("colors")
public class ApplesController { public class ApplesController {
private final ColorService colorService; private final ColorService colorService;
// 构造器注入,当然也同样允许 @Autowired 注入 和 setter 方法注入 // 构造器注入,当然也同样允许 @Autowired 注入 和 setter 方法注入
public ApplesController(ColorService colorService) { public ApplesController(ColorService colorService) {
this.colorService = colorService; this.colorService = colorService;
@@ -154,27 +186,10 @@ public class ApplesController {
``` ```
- `resources`:目录为插件资源目录 - `resources`:目录为插件资源目录
- `admin` 目录下为插件前端打包后的产物存放目录,固定为 `main.js` 和 `style.css `两个文件
`admin` 目录下为插件前端打包后的产物存放目录,固定为 `main.js``style.css `两个文件 - `extensions` 存放自定义模型资源配置
- `plugin.yaml`为插件描述配置
`extensions` 存放自定义模型资源配置 - `static` 为静态资源示例目录
`plugin.yaml`为插件描述配置
`static` 为静态资源示例目录
├── admin
│ ├── main.js
│ └── style.css
├── extensions
│ ├── apple.yaml
│ ├── reverseProxy.yaml
│ └── roleTemplate.yaml
├── plugin.yaml
└── static
├── image.jpeg
├── some.txt
└── test.html
插件启动时会加载 `extensions` 目录的 `yaml` 保存到数据库,`apple.yaml` 为本项目自定义的模型的数据示例,当启用插件后调用 插件启动时会加载 `extensions` 目录的 `yaml` 保存到数据库,`apple.yaml` 为本项目自定义的模型的数据示例,当启用插件后调用
@@ -250,38 +265,128 @@ rules:
更多详情参考:[Halo security RFC](https://github.com/halo-dev/rfcs/blob/main/identity/002-security.md) 更多详情参考:[Halo security RFC](https://github.com/halo-dev/rfcs/blob/main/identity/002-security.md)
### 打包 ### 开发环境
#### 打包前端项目 #### 环境要求
1.`admin-frontend` 目录下执行 - openJDK 17
- NodeJS 16+
- pnpm 7+
#### 拉取 Halo 相关项目源码
```bash
mkdir ./halo-dev
mkdir ./halo-dev/dev-plugins # 存放插件源码
```
```bash
cd ./halo-dev
git clone https://github.com/halo-dev/halo --branch next
```
```bash
git clone https://github.com/halo-dev/halo-admin --branch next
```
```bash
cd ./dev-plugins
git clone https://github.com/halo-sigs/plugin-template
```
#### Halo 配置文件修改
修改 halo/src/resources/application-dev.yaml
```yaml
halo:
security:
initializer:
super-admin-username: admin
super-admin-password: P@88w0rd
oauth2:
jwt:
jwsAlgorithm: rs512
public-key-location: classpath:app.pub
private-key-location: classpath:app.key
plugin:
runtime-mode: development # development, deployment
classes-directories:
- "build/classes"
- "build/resources"
lib-directories:
- "libs"
plugins-root: /Users/ryanwang/Workspace/github/ruibaby/halo-dev/dev-plugins # 修改为上方存放插件源码的实际目录
initial-extension-locations: # 初始化资源加载配置,需要配置当前开发中插件的资源目录或者文件
- "/Users/ryanwang/Workspace/github/ruibaby/halo-dev/dev-plugins/plugin-template/src/main/resources/plugin.yaml"
```
#### 编译插件
下载前端依赖:
```bash
cd ./halo-dev/dev-plugins/plugin-template
./gradlew.bat pnpmInstall
# or macOS/Linux
./gradlew pnpmInstall
```
构建:
```bash
./gradlew.bat build
# or macOS/Linux
./gradlew build
```
#### 启动 Halo
```bash
cd ./halo-dev/halo
./graldew.bat bootRun --args="--spring.profiles.active=dev"
# or macOS/Linux
./gradlew bootRun --args="--spring.profiles.active=dev"
```
或者在 IntelliJ IDEA 中运行 Application 启动类。但注意需要配置好 `spring.profiles.active` 为 dev。
#### 启动 Halo Admin
```bash
cd ./halo-dev/halo-admin
```shell
pnpm install pnpm install
pnpm dev
``` ```
2. 开发时执行 #### 访问后台
```shell 在浏览器中访问 https://localhost:3000 即可,登录用户名和密码为上方 `application-dev.yaml` 配置中的 `super-admin-username`
pnpm run dev 和 `super-admin-password`。
```
3. 打包时执行 然后在左侧菜单中选择 `插件`,即可查看所有插件的状态。
```shell #### 开发
pnpm run build
```
#### 构建后端 修改前端代码或者后端代码,然后运行 `./gradlew.bat build` 或者 `./gradlew build`macOS/Linux即可构建插件无需重启 Halo。但修改配置文件后需要 build 插件以及重启 Halo。
1. 开发时可以使用 `command + F9 / ctrl + F9` ### 构建生产产物
2. 生产时:点击 `gradle``build`
或者执行
``` ```
./gradlew -x build ./gradlew -x build
``` ```
然后只需复制例如`build/libs/halo-plugin-template-0.0.1-SNAPSHOT-plain.jar``jar` 包即可使用 然后只需复制例如`build/libs/plugin-template-0.0.1-SNAPSHOT-plain.jar` 的 `jar` 包即可使用