@@ -15,22 +15,16 @@ Devtools 是使用 Java 开发的一个 [Gradle](https://gradle.org/) 插件,
15
15
16
16
``` groovy
17
17
plugins {
18
- // ...
19
- id "run.halo.plugin.devtools" version "0.2 .0"
18
+ // ...
19
+ id "run.halo.plugin.devtools" version "0.3 .0"
20
20
}
21
21
```
22
22
23
23
## 使用说明
24
24
25
- 当在项目中引入了 ` devtools ` 之后,就可以使用一些额外的构建任务:
25
+ 当在项目中引入了 ` devtools ` 之后,就可以使用一些额外的构建任务来辅助插件的开发,参考 [ 构建任务详解 ] ( #任务 ) 。
26
26
27
- - ` haloServer ` :此构建任务用于启动 Halo 服务并自动将依赖此 ` devtools ` 的 Halo 插件项目以开发模式加载到 Halo 服务中。
28
- - ` watch ` :此构建任务用于监视 Halo 插件项目的变化并自动重新加载到 Halo 服务中
29
- - ` reloadPlugin ` :此构建任务用于重载插件,如果你此时使用的是 ` haloServer ` 运行的插件,改动代码后可以运行此任务来重载插件代码使用新的改动被应用。
30
-
31
- 一个可能的使用场景:
32
-
33
- 正在开发 ` plugin-starter ` 插件,此时想测试插件的功能如看到默认提供的菜单项,你可以通过 ` haloServer ` 将插件运行起来:
27
+ 例如,正在开发 ` plugin-starter ` 插件时,可以通过 ` haloServer ` 任务启动 Halo 服务,来测试插件功能:
34
28
35
29
``` shell
36
30
./gradlew haloServer
@@ -39,44 +33,45 @@ plugins {
39
33
看到如下日志时表示 Halo 服务已经启动成功:
40
34
41
35
``` shell
42
- Halo 初始化成功,访问: http://localhost:8090/console
43
- 用户名:admin
44
- 密码:admin
36
+ =======================================================================
37
+ > Halo 启动成功!
38
+ 访问地址:http://localhost:8090/console? language=zh-CN
39
+ 用户名:admin
40
+ 密码:admin
41
+ API 文档:http://localhost:8090/swagger-ui.html
42
+ 插件开发文档:https://docs.halo.run/developer-guide/plugin/introduction
43
+ =======================================================================
45
44
```
46
45
47
- 然后改动了某行代码需要使其生效,可以继续保持 ` haloServer ` 的运行,然后执行 :
46
+ 修改代码后,无需停止服务,只需执行 :
48
47
49
48
``` shell
50
- ./gradlew reloadPlugin
49
+ ./gradlew reload
51
50
```
52
51
53
- 来时新的改动应用到现有服务上。
54
-
55
- 但如果你使用的 ` watch ` 任务启动插件则不需要执行 ` reloadPlugin ` 任务,它会监听文件的改动自动重载插件。
52
+ 即可应用改动。如果使用 watch 任务启动插件,则不需要执行 ` reload ` ,它会自动监听并重载插件。
56
53
57
54
## 配置
58
55
59
- 在 ` build.gradle ` 文件中作出配置可以更改 ` devtools ` 的行为 :
56
+ 可通过 ` build.gradle ` 文件中的 ` halo {} ` 块自定义 Devtools 启动 Halo 服务必要配置,示例如下 :
60
57
61
58
``` groovy
62
59
halo {
63
- version = '2.20'
64
- superAdminUsername = 'admin'
65
- superAdminPassword = 'admin'
66
- externalUrl = 'http://localhost:8090'
67
- docker {
68
- // windows 默认为 npipe:////./pipe/docker_engine
69
- url = 'unix:///var/run/docker.sock'
70
- apiVersion = '1.42'
71
- }
72
- port = 8090
73
- debug = true
74
- debugPort = 5005
60
+ version = '2.20'
61
+ superAdminUsername = 'admin'
62
+ superAdminPassword = 'admin'
63
+ externalUrl = 'http://localhost:8090'
64
+ docker {
65
+ // windows 默认为 npipe:////./pipe/docker_engine
66
+ url = 'unix:///var/run/docker.sock'
67
+ apiVersion = '1.42'
68
+ }
69
+ port = 8090
70
+ debug = true
71
+ debugPort = 5005
75
72
}
76
73
```
77
74
78
- ` halo {} ` 这个配置对象下面用于配置 Halo 服务器的一些信息,所有配置的默认值如上所示,你可以直接使用默认值而不进行任何配置。
79
-
80
75
- ` version ` :表示要使用的 Halo 版本,随着插件 API 的更新你可能需要更高的 Halo 版本来运行插件,可自行更改。
81
76
- ` superAdminUsername ` : Halo 的超级管理员用户名,当你启动插件时会自动根据此配置和 ` superAdminPassword ` 为你初始化 Halo 的超级管理员账户。
82
77
- ` superAdminPassword ` :Halo 的超级管理员用户密码。
@@ -100,13 +95,302 @@ halo {
100
95
由于 Halo 2.20.0 版本更改了初始化和登录流程,如果 ` halo.version ` 指定 ` 2.20.x ` 版本需要将 ` run.halo.plugin.devtools ` 版本升级到 ` 0.2.0 ` 及以上。
101
96
:::
102
97
98
+ ## 任务
99
+
100
+ 本插件提供了 ` haloServer ` 和 ` watch ` 两个任务,使用它们的前提条件是需要在本地配置 Docker 环境。
101
+
102
+ ### 环境要求
103
+
104
+ - ** Windows 和 Mac 用户** :可以直接安装 [ Docker Desktop] ( https://www.docker.com/products/docker-desktop ) 。
105
+ - ** Linux 用户** :请参考 [ Docker 官方文档] ( https://docs.docker.com/engine/install/ ) 安装 Docker。
106
+
107
+ 确保 Docker 服务已启动后,即可运行 ` haloServer ` 和 ` watch ` 任务。
108
+
109
+ ### 工作目录
110
+
111
+ 这两个任务会将 Halo 的工作目录挂载到插件项目的 ` workplace ` 目录下,以确保在重启任务时数据不会丢失。
112
+
113
+ ### 自定义配置
114
+
115
+ 如果需要修改 Halo 的配置,您可以在 ` workplace ` 目录下创建一个 ` config ` 目录,并添加一个 ` application.yaml ` 文件。在该文件中,您可以覆盖 Halo 的默认配置。例如:
116
+
117
+ ``` yaml
118
+ # workplace/config/application.yaml
119
+ logging :
120
+ level :
121
+ run.halo.app : DEBUG
122
+ ` ` `
123
+
124
+ 更多配置项请参考 [Halo 配置列表](../../../getting-started/install/config.md#配置列表)。
125
+
126
+ ### haloServer 任务
127
+
128
+ 使用方式:
129
+
130
+ ` ` ` shell
131
+ ./gradlew haloServer
132
+ ```
133
+
134
+ 此任务用于启动 Halo 服务并自动将使用此 Gradle 插件的 Halo 插件项目以开发模式加载到 Halo 服务中,当你修改了插件的代码后,可以通过 ` reload ` 任务使更改生效。
135
+
136
+ #### haloServer 任务默认配置
137
+
138
+ ` haloServer ` 任务具有以下默认配置用于连接和操作 Halo 服务:
139
+
140
+ ``` groovy
141
+ halo {
142
+ version = '2.9.1'
143
+ superAdminUsername = 'admin'
144
+ superAdminPassword = 'admin'
145
+ externalUrl = 'http://localhost:8090'
146
+ docker {
147
+ // Windows 用户默认使用 npipe:////./pipe/docker_engine
148
+ url = 'unix:///var/run/docker.sock'
149
+ apiVersion = '1.42'
150
+ }
151
+ }
152
+ ```
153
+
154
+ 如需修改,可以在 ` build.gradle ` 文件中进行配置。
155
+
156
+ ### reload 任务
157
+
158
+ 使用方式:
159
+
160
+ ``` shell
161
+ ./gradlew reload
162
+ ```
163
+
164
+ 此任务用于重新加载当前正在开发的插件,修改代码后执行此任务以应用更改。
165
+
166
+ 该任务基于以下配置调用 Halo API 重新加载插件:
167
+
168
+ ``` groovy
169
+ halo {
170
+ // ...
171
+ superAdminUsername = 'admin'
172
+ superAdminPassword = 'admin'
173
+ externalUrl = 'http://localhost:8090'
174
+ }
175
+ ```
176
+
177
+ #### watch 任务
178
+
179
+ 使用方式:
180
+
181
+ ``` shell
182
+ ./gradlew watch
183
+ ```
184
+
185
+ 此任务用于监视 Halo 插件项目的变化并自动重新加载到 Halo 服务中。
186
+ 默认只监听 ` src/main/java ` 和 ` src/main/resources ` 目录下的文件变化,如果需要监听其他目录,可以在项目的 ` build.gradle ` 中添加如下配置:
187
+
188
+ ``` groovy
189
+ haloPlugin {
190
+ watchDomains {
191
+ // consoleSource 为自定义的名称,可以随意取
192
+ consoleSource {
193
+ // 监听 console/src/ 目录下的文件变化
194
+ files files('console/src/')
195
+ }
196
+ // ... 可以添加多个
197
+ }
198
+ }
199
+ ```
200
+
201
+ ### 生成 API client
202
+
203
+ #### 什么是 API client
204
+
205
+ API client 是一种工具或库,旨在简化前端应用程序与后端服务器之间的通信,尤其是在使用 RESTful API 或 GraphQL API 的情况下。
206
+ 它提供了一种简洁且类型安全的方式来调用服务器端的 API,并处理请求和响应。
207
+
208
+ 在 TypeScript 环境中,使用 API client 有以下几个优点:
209
+
210
+ - 自动化 HTTP 请求:API 客户端封装了 HTTP 请求的细节,如构建 URL、设置请求头、处理查询参数等。开发者只需调用客户端提供的函数即可发送请求。
211
+
212
+ - 类型安全:通过结合 OpenAPI 等规范生成的 TypeScript 类型定义,API 客户端可以确保请求和响应的数据类型在编译时就能得到验证。这可以帮助减少运行时的错误,并提高代码的可读性和可维护性。
213
+
214
+ - 统一的错误处理:API 客户端可以提供统一的错误处理机制,比如自动重试、错误日志记录等,这样开发者无需在每个 API 调用中重复编写相同的错误处理逻辑。
215
+
216
+ - 提高开发效率:通过使用 API 客户端,开发者可以专注于业务逻辑的实现,而不用关心底层的 HTTP 细节。这不仅提高了开发效率,还减少了代码冗余。
217
+
218
+ #### 如何生成 API client
219
+
220
+ 本插件提供了一个 ` generateApiClient ` 任务,用于为插件项目生成 API client,生成规则基于 OpenAPI 规范来自动生成客户端代码。
221
+
222
+ 能生成 API 客户端代码的前提是插件项目中需要对自定义的 API 进行文档声明如:
223
+
224
+ ``` java
225
+ final var tag = " CommentV1alpha1Console" ;
226
+ return SpringdocRouteBuilder . route()
227
+ . GET (" comments" , this :: listComments, builder - > {
228
+ builder. operationId(" ListComments" )
229
+ .description(" List comments." )
230
+ .tag(tag)
231
+ .response(responseBuilder()
232
+ .implementation(ListResult . generateGenericClass(ListedComment . class))
233
+ );
234
+ CommentQuery . buildParameters(builder);
235
+ })
236
+ .build();
237
+ ```
238
+
239
+ 或者是在插件中定义了自定义模型,自定义模型自动生成的 CRUD APIs 是已经支持的。
240
+
241
+ 以下是如何配置和使用 ` generateApiClient ` 的详细步骤:
242
+
243
+ ##### 配置 ` generateApiClient `
244
+
245
+ 在 build.gradle 文件中,使用 haloPlugin 块来配置 OpenAPI 文档生成和 API 客户端生成的相关设置:
246
+
247
+ ``` groovy
248
+ haloPlugin {
249
+ openApi {
250
+ // outputDir = file("$rootDir/api-docs/openapi/v3_0") // 指定 OpenAPI 文档的输出目录默认输出到 build 目录下,不建议修改,除非需要提交到代码仓库
251
+ groupingRules {
252
+ // 定义 API 分组规则,用于为插件项目中的 APIs 分组然后只对此分组生成 API 客户端代码
253
+ // 定义了一个名为 extensionApis 的分组,task 会通过 /v3/api-docs/extensionApis 访问到 api docs 然后生成 API 客户端代码
254
+ // extensionApis 名称可以替换为其他名称,但需要与 groupedApiMappings 中的名称一致
255
+ extensionApis {
256
+ // 分组显示名称,避免与其他分组重名建议替换 {your-plugin-name} 为插件名称
257
+ displayName = 'Extension API for {your-plugin-name}'
258
+ // 分组的 API 规则用于匹配插件项目中的 API 将其划分到此分组,它是一个 Ant 风格的路径匹配规则可以写多个
259
+ pathsToMatch = ['/apis/staticpage.halo.run/v1alpha1/**']
260
+ }
261
+ }
262
+ groupedApiMappings = [
263
+ // 这里为固定写法,照搬即可,除非是 groupingRules 中 extensionApis 的名字修改了
264
+ '/v3/api-docs/extensionApis': 'extensionApis.json'
265
+ ]
266
+ generator {
267
+ // 指定 API 客户端代码的输出目录如 console 或 ui
268
+ outputDir = file("${projectDir}/console/src/api/generated")
269
+
270
+ // 定制生成,以下是默认配置可以不需要添加到 build.gradle 中
271
+ additionalProperties = [
272
+ useES6: true,
273
+ useSingleRequestParameter: true,
274
+ withSeparateModelsAndApi: true,
275
+ apiPackage: "api",
276
+ modelPackage: "models"
277
+ ]
278
+ // 类型映射,用于将 OpenAPI 中的类型映射到 TypeScript 中的类型,以下是默认配置可以不需要添加到 build.gradle 中
279
+ typeMappings = [
280
+ set: "Array"
281
+ ]
282
+ }
283
+ }
284
+ }
285
+ ```
286
+
287
+ ##### 执行 ` generateApiClient `
288
+
289
+ 在项目目录中执行以下命令即可生成 API 客户端代码到指定目录:
290
+
291
+ ``` shell
292
+ ./gradlew generateApiClient
293
+ ```
294
+
295
+ 然后在 ` openApi.generator.outputDir ` 目录创建一个 ` index.ts ` 文件并创建实例,以瞬间插件为例
296
+
297
+ ``` typescript
298
+ // console/src/api/index.ts
299
+ // 先引入 axiosInstance 用于请求
300
+ import { axiosInstance } from " @halo-dev/api-client" ;
301
+ // 这里导入的是声明 API doc 时指定的 tag 名称,如上文中定义的 CommentV1alpha1Console
302
+ import {
303
+ ConsoleApiMomentHaloRunV1alpha1MomentApi ,
304
+ MomentV1alpha1Api ,
305
+ UcApiMomentHaloRunV1alpha1MomentApi ,
306
+ } from " ./generated" ;
307
+
308
+ // MomentV1alpha1Api 是自定义模型生成的 API tag 这里创建了一个 momentsCoreApiClient 实例
309
+ const momentsCoreApiClient = {
310
+ moment: new MomentV1alpha1Api (undefined , " " , axiosInstance ),
311
+ };
312
+
313
+ // ConsoleApiMomentHaloRunV1alpha1MomentApi 是用于在 console 端调用的 APIs 的 tag,这里创建了一个 momentsConsoleApiClient 实例用于在 console 端调用
314
+ const momentsConsoleApiClient = {
315
+ moment: new ConsoleApiMomentHaloRunV1alpha1MomentApi (
316
+ undefined ,
317
+ " " ,
318
+ axiosInstance
319
+ ),
320
+ };
321
+
322
+ // 用于在个人中心调用的 APIs,单独创建一个 momentsUcApiClient 实例
323
+ const momentsUcApiClient = {
324
+ moment: new UcApiMomentHaloRunV1alpha1MomentApi (undefined , " " , axiosInstance ),
325
+ };
326
+ // 导出实例
327
+ export { momentsConsoleApiClient , momentsCoreApiClient , momentsUcApiClient };
328
+ ```
329
+
330
+ 使用定义的实例:
331
+
332
+ ``` typescript
333
+ import { momentsConsoleApiClient } from " @/api" ;
334
+
335
+ // 查询瞬间的标签
336
+ const { data } = await momentsConsoleApiClient .moment .listTags ({
337
+ name: props .keyword ?.value ,
338
+ });
339
+ ```
340
+
341
+ ::: tip
342
+ 它会先执行 ` generateOpenApiDocs ` 任务根据配置访问 ` /v3/api-docs/extensionApis ` 获取 OpenAPI 文档,
343
+ 并将 OpenAPI 的 Schema 文件保存到 ` openApi.outputDir ` 目录下,然后再由 ` generateApiClient ` 任务根据 Schema 文件生成 API 客户端代码到 ` openApi.generator.outputDir ` 目录下。
344
+ :::
345
+
346
+ ::: warning
347
+ 执行 ` generateApiClient ` 任务时会先删除 ` openApi.generator.outputDir ` 下的所有文件,因此建议将 API client 的输出目录设置为一个独立的目录,以避免误删其他文件。
348
+ :::
349
+
350
+ ### generateRoleTemplates 任务
351
+
352
+ 在 Halo 插件开发中,权限管理是一个关键问题,尤其是配置[ 角色模板] ( https://docs.halo.run/developer-guide/plugin/security/rbac#%E8%A7%92%E8%89%B2%E6%A8%A1%E6%9D%BF ) 时,角色的 ` rules ` 部分往往让开发者感到困惑。具体来说,如何区分资源、apiGroup、verb 等概念是许多开发者的痛点。
353
+
354
+ ` generateRoleTemplates ` Task 的出现正是为了简化这一过程,该任务能够根据 [ 配置 Generate Api Client] ( #配置-generateapiclient ) 中的配置获取到 OpenAPI docs 的 JSON 文件,并自动生成 Halo 的 Role YAML 文件,让开发者可以专注于自己的业务逻辑,而不是纠结于复杂的角色 ` rules ` 配置。
355
+
356
+ 在生成的 ` roleTemplate.yaml ` 文件中,rules 部分是基于 OpenAPI docs 中 API 资源和请求方式自动生成的,覆盖了可能的操作。
357
+ 然而,在实际的生产环境中,Role 通常会根据具体的需求被划分为不同的权限级别,例如:
358
+
359
+ - 查看权限的角色模板:通常只包含对资源的读取权限,如 get、list、watch 等。
360
+ - 管理权限的角色模板:则可能包含创建、修改、删除等权限,如 create、update、delete。
361
+
362
+ > watch verb 是对于 WebSocket API,不会在 roleTemplates.yaml 中体现为 watch,而是体现为 list,因此需要开发者根据实际情况进行调整。
363
+
364
+ 因此,生成的 YAML 文件只是一个基础模板,涵盖了所有可用的操作。开发者需要根据自己的实际需求,对这些 rules 进行调整。比如,针对只需要查看资源的场景,开发者可以从生成的 YAML 中删除` 修改 ` 和` 删除 ` 相关的操作,保留读取权限。
365
+ 而对于需要管理资源的场景,可以保留` 创建 ` 、` 更新 ` 和` 删除 ` 权限,对于角色模板的依赖关系和聚合关系,开发者也可以根据实际情况进行调整。
366
+
367
+ 通过这种方式,开发者可以使用生成的 YAML 文件作为基础,快速定制出符合不同场景的权限配置,而不必从头开始编写复杂的规则以减少出错的可能性。
368
+
369
+ #### 如何使用
370
+
371
+ 在 build.gradle 文件中,使用 haloPlugin 块来配置 OpenAPI 文档生成和 Role 模板生成的相关设置:
372
+
373
+ ``` groovy
374
+ haloPlugin {
375
+ openApi {
376
+ // 参考配置 generateApiClient 中的配置
377
+ }
378
+ }
379
+ ```
380
+
381
+ 在项目目录中执行以下命令即可生成 ` roleTemplates.yaml ` 文件到 ` worplace ` 目录:
382
+
383
+ ``` shell
384
+ ./gradlew generateRoleTemplates
385
+ ```
386
+
103
387
## 调试后端代码
104
388
105
389
如果你想调试后端代码,可以在 ` build.gradle ` 中配置
106
390
107
391
``` groovy
108
392
halo {
109
- debug = true
393
+ debug = true
110
394
}
111
395
```
112
396
0 commit comments