#### What type of PR is this?
/kind feature
/area core
/milestone 2.0
#### What this PR does / why we need it:
This PR mainly implement full-text search of posts and provide extension point for other search engine.
Meanwhile, I implement ExtensionGetter to get implemention(s) of extension point from system ConfigMap.
But there still are something to do here:
- [x] Udpate documents when posts are published or posts are becoming unpublic.
- [x] Delete documents when posts are unpublished or deleted.
Because I'm waiting for https://github.com/halo-dev/halo/pull/2659 got merged.
I create two endpoints:
1. For full-text search of post
```bash
curl -X 'GET' \
'http://localhost:8090/apis/api.halo.run/v1alpha1/indices/post?keyword=halo&limit=10000&highlightPreTag=%3CB%3E&highlightPostTag=%3C%2FB%3E' \
-H 'accept: */*'
```
1. For refreshing indices
```bash
curl -X 'POST' \
'http://localhost:8090/apis/api.console.halo.run/v1alpha1/indices/post' \
-H 'accept: */*' \
-d ''
```
#### Which issue(s) this PR fixes:
Fixes #https://github.com/halo-dev/halo/issues/2637
#### Special notes for your reviewer:
#### Does this PR introduce a user-facing change?
```release-note
提供文章全文搜索功能并支持搜索引擎扩展
```
#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
#### What this PR does / why we need it:
Bump Spring Boot to 3.0.0-RC2.
See https://github.com/spring-projects/spring-boot/releases/tag/v3.0.0-RC2 for more.
#### Does this PR introduce a user-facing change?
```release-note
升级 Spring Boot 至 3.0.0-RC2
```
#### What type of PR is this?
/kind feature
/milestone 2.0
/area core
#### What this PR does / why we need it:
主题端提供判断插件是否已经启用的方法
#### Which issue(s) this PR fixes:
Fixes#2677
#### Special notes for your reviewer:
how to test it?
在主题端使用 `${pluginFinder.available(your-plugin-name)}` 来查看某个插件是否已经启用,如果是则得到 true ,否则 false,如果插件没有安装也得到 false
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
主题端支持获取插件启用状态
```
#### What type of PR is this?
/kind improvement
/milestone 2.0
/area core
/kind api-change
#### What this PR does / why we need it:
- 通过修改 `spec.publish=true` 且 `spec.releasedSnapshot = spec.headSnapshot` 来实现通过 reconciler 异步发布
- Snapshot 中的 subjectRef 类型改为了 Ref 类型,因为之前的 Snapshot.SubjectRef 只包含了 kind 和 name 可能会冲突
#### Which issue(s) this PR fixes:
Fixes#2650
#### Special notes for your reviewer:
how to test it?
1. 通过 `POST /apis/console.halo.run/v1alpha1/posts` 创建文章并将 `spec.publish=true`,创建后查询可以看到 `spec.baseSnapshot`、`spec.headSnapshot`、`spec.releasedSnapshot` 三个值都相等,且 `status.phase=PUBLISHED`(此过程相当于创建即发布没有保存过程)
2. 先通过 `POST /apis/console.halo.run/v1alpha1/posts` 创建一篇草稿(`spec.publish=false`),在获取它并设置 `spec.publish=true` ,更新后期望文章为发布状态 `spec.headSnapshot`, `spec.releasedSnapshot` 都不等于空且等于 `spec.baseSnapshot`),且 `spec.version=1`(此过程相当于先保存后发布且之前从未发布过)
3. 在步骤2的基础上修改`spec.releasedSnapshot=spec.headSnapshot`并更新,期望 `spec.version=2`且`spec.releasedSnapshot` 对应的 Snapshot 数据具有 publishTime(此过程相当于发布后编辑内容在发布的场景)
4. 自定义页面亦如是
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
重构文章发布以解决创建与发布 API 几乎同时调用时无法成功发布文章的问题
```
#### What type of PR is this?
/kind bug
/area core
/milestone 2.0
#### What this PR does / why we need it:
修复日志 while 循环导致 cpu 占用高的问题
#### Special notes for your reviewer:
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
None
```
#### What type of PR is this?
/kind bug
/kind improvement
/area core
/milestone 2.0
#### What this PR does / why we need it:
修复 Halo 异常停止时日志服务线程无法中断的问题
#### Special notes for your reviewer:
how to test it?
使用 mysql 启动 halo 但不启动 mysql,此时 halo 会无法链接 mysql
期望现象:Halo 服务终止,异常现象:Halo 抛异常但 Netty 服务器不会停止
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
修复 Halo 异常停止时日志服务线程无法中断的问题
```
#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
#### What this PR does / why we need it:
插件 logo 支持配置外部 URL 或相对于 resources 的路径
Console 端展示插件 logo 时使用的字段需要使用 status.logo 而非 spec.logo
#### Which issue(s) this PR fixes:
Fixes#2651
#### Special notes for your reviewer:
how to test it?
1. 插件配置 logo 为外部 url 例如 https://guqing.xyz/avatar
2. 安装此插件后不会注册 ReverseProxy 规则
3. logo 配置为相对于 resources 的路径例如:/logo.png
4. 安装此插件后可以访问到 /plugins/{your-plugin-name}/assets/logo.png
5. 开发模式启动插件后,修改了 plugin.yaml 中的 spec.logo 则也会更新 ReverseProxy 的 rule
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
插件 Logo 支持配置外部 URL 或相对于 resources 的路径
```
#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
#### What this PR does / why we need it:
如果配置了 halo.externalUrl 则 permalink 生成时会自动加上 external url
#### Which issue(s) this PR fixes:
Fixes #
#### Special notes for your reviewer:
how to test it?
1. 配置 `halo.externalUrl` 并启动
2. 创建几篇文章、标签、分类、自定义页面
3. console 端能看到访问路径自动带上了 external url 并且通过访问路径能正确访问到相应资源
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
文章/分类/标签的访问路径自动追加外部访问地址
```
#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
#### What this PR does / why we need it:
菜单和分类数列表加强健壮性,防止因为 children 字段存在垃圾数据时导致 NPE 问题
#### Which issue(s) this PR fixes:
a part of #2643
#### Special notes for your reviewer:
how to test it?
1. 创建一个层级结构菜单,比如
```
A
|-B
|-C
```
2. 删除菜单 C 然后访问主题端首页不会发生以下异常信息
```
Caused by: java.lang.NullPointerException: Cannot invoke "run.halo.app.theme.finders.vo.MenuItemVo.setParentName(String)" because "childNode" is null
```
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
修复获取菜单和分类列表时会出现 NPE 的问题
```
#### What type of PR is this?
/kind bug
/area core
/milestone 2.0
#### What this PR does / why we need it:
修复文章和自定义页面的 labels 在文章标记为删除状态时没有更新的问题
#### Which issue(s) this PR fixes:
A part of #2647
#### Special notes for your reviewer:
how to test it?
1. 创建文章
2. 由于没有发布,此时访问文章的 permalink 访问不到
3. 发布文章,可以正常访问
4. 更新文章的 `spec.deleted=true`,在访问文章期望404,并且 `metadata.labels` 中的数据正确
5. 自定义页面亦如是
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
None
```
#### What type of PR is this?
/kind feature
/area core
/milestone 2.0
#### What this PR does / why we need it:
支持通过文章名称获取上一篇和下一篇文章数据
本 PR 通过实现一个固定大小的滑动窗口数据结构来通过传入的文章名称获取上一篇和下一篇文章
例如当具有一系列文章名为 [a, b, c, d, e, f, g]
查询文章名为 d 的文章的上一篇和下一篇则使用一个固定大小为 3 的滑动窗口, 示意图如下
<img width="526" alt="image" src="https://user-images.githubusercontent.com/38999863/198243133-20f77431-1107-4526-9f4f-6a11c68204e7.png">
通过窗口右移来调整当窗口圈住所需元素 D 时且 元素 D 的位置不是窗口的最后一个元素时得到一个想要的窗口
此时:
1. 如果 D 位于 window 中的第一个元素则 D 没有上一篇
2. 如果 D 位于 window 中间则 window 的第一个元素为 D 的上一篇,最后一个元素为下一篇
3. 如果 D 位于 window 的最后一个位置,则 D 没有下一篇
#### Which issue(s) this PR fixes:
Fixes#2635
#### Special notes for your reviewer:
how to test it?
1. 创建几篇文章然后发布
4. 能通过 `${postFinder.cursor(your-post-name)}` 来获取到带有上一页下一页的文章数据
5. 当文章处于第一条时没有上一页,当文章处于最后一页时没有下一页(可以通过`${postCursor.hasPrevious()}` 和 `${postCursor.hasNext()}` 判断按钮是否展示)
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
支持通过文章名称获取上一篇和下一篇文章数据
```
#### What type of PR is this?
/kind documentation
#### What this PR does / why we need it:
Bump image tag in docker command.
#### Does this PR introduce a user-facing change?
```release-note
None
```
#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
#### What this PR does / why we need it:
permalink 路由匹配时自动忽略末尾分隔符
例如
/archives 匹配到 /archives
/archives/ 匹配到 /archives
#### Special notes for your reviewer:
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
None
```
#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
#### What this PR does / why we need it:
ReverseProxyRouterFunctionRegistry 的注册方法之前使用了一个 LinkedMultiValueMap<String, String>,相当于是一个 Map<String, List<String>>,当插件启动后扫描到 ReverseProxy 资源保存到数据库会触发 ReverseProxyReconciler 调用`ReverseProxyRouterFunctionRegistry#register`,如果此时插件的 PluginApplicationContext 还没有被创建好,register 时会在中途因为获取不到 PluginApplicationContext 而失败然后 requeue,而 LinkedMultiValueMap 此时已经保存了一条记录 requeue后再执行 LinkedMultiValueMap(即`Map<String, List<String>>`) 这个 value 是一个 List 需要更多的判断来防止重复,需要用一个 Map<String, Set<String>>这样的结构来保证 requeue 时 ReverseProxyRouterFunctionRegistry 中集合结构之前的数据一致性这样更方便,所以将从 spring 的 LinkedMultiValueMap 切换到 guava 提供的 LinkedHashMultimap(它的结构是Map<K, Set<V>)
#### Does this PR introduce a user-facing change?
```release-note
None
```
#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
#### What this PR does / why we need it:
Please refer to <https://github.com/jasync-sql/jasync-sql/releases/tag/2.1.7>.
#### Does this PR introduce a user-facing change?
```release-note
None
```
#### What type of PR is this?
/kind improvement
/milestone 2.0
#### What this PR does / why we need it:
在 dev 的配置文件中禁用 Thymeleaf 的模板缓存。
#### Special notes for your reviewer:
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
在 dev 的配置文件中禁用 Thymeleaf 的模板缓存。
```
#### What type of PR is this?
/kind feature
/area core
/milestone 2.0
#### What this PR does / why we need it:
Provide an endpoint to upgrade plugin by uploading a new jar file.
#### Which issue(s) this PR fixes:
Fixes https://github.com/halo-dev/halo/issues/2551
#### Special notes for your reviewer:
1. Install an old plugin
2. Update the plugin and package again
3. Upgrade the plugin and see the result
#### Does this PR introduce a user-facing change?
```release-note
提供插件更新功能
```
#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
#### What this PR does / why we need it:
渲染模板页面时将被渲染的模板名称填充到试图模型中作为 templateId
为什么需要它:
1. thymeleaf 渲染模板可以使用 fragment,此时在 thymeleaf 的 IElementTagProcessor 等处理器中获取到的 template name不一定是例如 post.html这样的名称
2. 比如渲染文章模板 post.html 而 #2569 计划将要在 theme.yaml 中通过配置允许用户选择文章所渲染的模板,因为无法确定渲染模板的标志,例如有些处理器想在文章页插入 head 这样的需求就需要知道哪个模板是文章页。see also [issuecomment-1215135195](https://github.com/halo-dev/halo/issues/2322#issuecomment-1215135195)
所以目前想到的是通过在 render 时填充一个 templateId 到 view model 中标识模板身份
#### Which issue(s) this PR fixes:
Fixes#2621
#### Special notes for your reviewer:
how to test it?
见 issue #2621
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
修复设置了文章的 htmlMetas 字段但没有在页面的 head 注入标签的问题
```
#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
#### What this PR does / why we need it:
重构 PluginReconciler,避免因为每一段逻辑的执行时间太长而增加乐观锁错误的发生概率
修复判断逻辑问题导致启动时因为发生乐关锁错误造成没有填充 entry 和 stylesheet 字段的问题
#### Which issue(s) this PR fixes:
Fixes#2616
#### Special notes for your reviewer:
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
None
```
#### What type of PR is this?
/kind feature
/area core
/milestone 2.0
#### What this PR does / why we need it:
提供文章归档页
#### Which issue(s) this PR fixes:
Fixes#2548
#### Special notes for your reviewer:
#### Does this PR introduce a user-facing change?
```release-note
为主题提供文章归档页
```
#### What type of PR is this?
/kind bug
/milestone 2.0
#### What this PR does / why we need it:
修复文章、自定义页面、评论聚合查询接口排序不固定的问题。
#### Which issue(s) this PR fixes:
Fixes https://github.com/halo-dev/halo/issues/2622
#### Special notes for your reviewer:
测试方式:检查 Console 端的文章、自定义页面、评论、回复的排序是否正常,需要多次刷新检查。
```release-note
修复文章、自定义页面、评论聚合查询接口排序不固定的问题。
```
#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
#### What this PR does / why we need it:
修复插件卸载时没有连同删除插件 JAR 文件的问题
#### Which issue(s) this PR fixes:
Fixes#2552
#### Special notes for your reviewer:
how to test it?
1. 以 deployment 模式安装一个插件
2. 插件的名称在插件目录是以 {pluginName}-{version}.jar 的方式命名的
3. 卸载插件时会连同删除插件的 JAR 文件
4. 开发模式下不会删除插件文件
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
修复插件卸载时没有连同删除插件 JAR 文件的问题
```
#### What type of PR is this?
/kind bug
/area core
/milestone 2.0
#### What this PR does / why we need it:
当删除文章或自定义页面时级联删除内容快照和评论及计数器
see also #2602
#### Which issue(s) this PR fixes:
Fixes#2599
#### Special notes for your reviewer:
how to test it?
1. 新建一篇文章并创建一些评论(需要安装评论插件 [plugin-comment-widget](https://github.com/halo-sigs/plugin-comment-widget))
2. 逻辑删除文章时评论不会被删除,真实删除则会
3. 新建一个文章,再删除它,然后查询 snaphost 模型看关联文章的数据有没有被删除
4. 自定义页面同上
5. 删除文章或自定义页面时会删除对应的 Counter 记录(`GET /apis/metrics.halo.run/v1alpha1/counters`)
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
修复删除文章或自定义页面时没有级联删除内容快照和评论的问题
```
#### What type of PR is this?
/kind bug
/area core
/milestone 2.0
#### What this PR does / why we need it:
Use CopyOnWriteArrayList on SchemeManager to prevent concurrent modification when link plugin is installed.
#### How to test?
1. Install link plugin
2. Restart Halo
3. Delete any extensions
4. Check the result
#### Does this PR introduce a user-facing change?
```release-note
修复数据一直处于删除中的错误
```
#### What type of PR is this?
/kind feature
/area core
/milestone 2.0
#### What this PR does / why we need it:
提供主题端站点统计信息查询器
#### Which issue(s) this PR fixes:
Fixes #
#### Special notes for your reviewer:
how to test it?
在任意主题页面使用如下语法
```
<p th:text="${siteStatsFinder.getStats()}"></p>
```
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
提供主题端站点统计信息查询器
```
#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
#### What this PR does / why we need it:
修改仪表盘统计返回类型的名称避类名相同时 swagger api 的 schema 被覆盖
#### Which issue(s) this PR fixes:
Fixes #
#### Special notes for your reviewer:
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
None
```
#### What type of PR is this?
/kind improvement
/area core
#### What this PR does / why we need it:
Remove custom schema validation and make field metadata.name of extension required. So that the API client generated by `openapi-gen` will be more consistent than before.
#### Does this PR introduce a user-facing change?
```release-note
None
```
#### What type of PR is this?
/kind bug
/area core
/milestone 2.0
#### What this PR does / why we need it:
修复分类树查询
#### Which issue(s) this PR fixes:
Fixes#2532
#### Special notes for your reviewer:
how to test it?
1. 新建分类并,并拖动构建一个树形
2. 在主题端通过 `categoryFinder.listAsTree()` 查看结果是否正确
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
修复分类树状数据查询
```
#### What type of PR is this?
/kind bug
/area core
/milestone 2.0
#### What this PR does / why we need it:
修复主题列表数据类型转换问题
#### Which issue(s) this PR fixes:
Fixes #
#### Special notes for your reviewer:
how to test it?
访问如下 API 不报错即可
```
curl http://localhost:8090/apis/api.console.halo.run/v1alpha1/themes?uninstalled=false
```
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
None
```
#### What type of PR is this?
/kind feature
/area core
/milestone 2.0
#### What this PR does / why we need it:
See https://github.com/halo-dev/halo/issues/2309 for more.
#### Which issue(s) this PR fixes:
Fixes https://github.com/halo-dev/halo/issues/2309
#### Special notes for your reviewer:
#### Does this PR introduce a user-facing change?
```release-note
新增 generateName 字段用于自动生成自定义模型名称
```
#### What type of PR is this?
/kind feature
/milestone 2.0
/area core
/kind api-change
#### What this PR does / why we need it:
新增 API 用于查询未安装的主题
#### Which issue(s) this PR fixes:
Fixes#2554
#### Special notes for your reviewer:
how to test it?
1. 安装几个主题
2. 直接解压几个主题到 work dir 的 themes 目录
3. 使用以下 endpoint 查询未安装的主题,期望获得所有未安装主题的 themes.yaml 信息
```
/apis/api.console.halo.run/v1alpha1/themes?uninstalled=true
```
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
支持扫描主题目录下未安装的主题
```
#### What type of PR is this?
/kind feature
/milestone 2.0
/area core
#### What this PR does / why we need it:
允许插件通过实现 TemplateHeadProcessor 接口来修改主题模板的 head 标签
#### Which issue(s) this PR fixes:
how to test it?
1. 克隆 https://github.com/halo-sigs/plugin-umami
2. build 一个 jar 包作为插件使用
3. 配置 plugin-umami 后能在主题页的 head 标签看到一个用于 umami 统计的 script 标签
Fixes #
#### Special notes for your reviewer:
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
None
```
#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
#### What this PR does / why we need it:
优化主题端菜单项查询
#### Which issue(s) this PR fixes:
Fixes#2564
#### Special notes for your reviewer:
how to test it?
使用 menuFinder.getDefault() 方法测试多级菜单项的排序及新增
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
修复主题端菜单查询数据错误问题
```
#### What type of PR is this?
/kind feature
/area core
/milestone 2.0
#### What this PR does / why we need it:
模板渲染自动填充 Favicon 到 head 标签
#### Which issue(s) this PR fixes:
Fixes#2581
#### Special notes for your reviewer:
how to test it?
在 console 系统设置 -> 基础设置 填写 Favicon 后,到主题端能到看它
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
支持设置 Favicon
```
#### What type of PR is this?
/kind bug
/area core
/milestone 2.0
/kind api-change
#### What this PR does / why we need it:
1. Disable CSRF token check for RESTful APIs but login and logout APIs.
2. Enable CORS check for login and logout APIs
#### Which issue(s) this PR fixes:
Fixes https://github.com/halo-dev/halo/issues/2571
#### How to test?
1. Install a valid theme and create a sample post
2. View the post at theme end
3. Check the response of counter API
#### Does this PR introduce a user-facing change?
```release-note
禁用对 RESTful API 的 CSRF 检查
```
#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
#### What this PR does / why we need it:
对反向代理规则注册增加重复注册检查
场景:
ReverseProxyRouterFunctionRegistry 中有一个 pluginIdReverseProxyMap 记录了 插件名称和插件的 ReverseProxy 名称对应关系
当重复 fake-plugin -> test-reverse-proxy 时,pluginIdReverseProxyMap 的 value 不会去重,因此需要增加重复注册检查
#### Which issue(s) this PR fixes:
Fixes #
#### Special notes for your reviewer:
how to test it?
本 PR 不需要测试,已经对上述场景添加了单元测试
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
None
```
#### What type of PR is this?
/kind improvement
/area core
/milestone 2.0
/kind api-change
#### What this PR does / why we need it:
重构插件 JsBundle 文件加载方式及路由规则
- 将插件静态资源的访问路由规则从 /assets/{plugin-name}/** 改为 /plugins/{plugin-name}/assets/** 与主题静态资源规则结构一致
- 默认在 Halo 中提供 /plugins/{plugin-name}/assets/console/** 路由以确保插件都能加载到最基础的 JsBundle 文件
#### Which issue(s) this PR fixes:
Fixes#2555
#### Special notes for your reviewer:
how to test it?
1. 安装并启用一个插件能访问到 `/plugins/{plugin-name}/assets/console/main.js` 和 `/plugins/{plugin-name}/assets/console/style.css` 即为功能正确
2. 在插件的 extensions 目录创建一个 reverse proxy 的自定义模型 yaml 资源,并使用此插件,插件反向代理规则能正确访问到文件即为功能正确
/cc @halo-dev/sig-halo
#### Does this PR introduce a user-facing change?
```release-note
重构插件 JsBundle 文件加载方式及路由规则
```