Add role template for managing cache (#4153)

#### What type of PR is this?

/kind feature
/area core
/milestone 2.7.x

#### What this PR does / why we need it:

Add role template for managing cache. Anyone with role `role-template-manage-cache` can see the `Refresh Page Cache` button.

#### Which issue(s) this PR fixes:

Fixes https://github.com/halo-dev/halo/issues/4143

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

```release-note
None
```
pull/4155/head
John Niang 2023-06-29 15:48:12 +08:00 committed by GitHub
parent 9a0c52fb2a
commit 4685bf4052
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 63 additions and 45 deletions

View File

@ -2,14 +2,16 @@ package run.halo.app.cache;
import static io.swagger.v3.oas.annotations.enums.ParameterIn.PATH; import static io.swagger.v3.oas.annotations.enums.ParameterIn.PATH;
import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder; import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder;
import static org.springdoc.webflux.core.fn.SpringdocRouteBuilder.route;
import static org.springframework.http.HttpStatus.NO_CONTENT; import static org.springframework.http.HttpStatus.NO_CONTENT;
import org.springdoc.core.fn.builders.apiresponse.Builder; import org.springdoc.core.fn.builders.apiresponse.Builder;
import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
import org.springframework.cache.CacheManager; import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;
import run.halo.app.core.extension.endpoint.CustomEndpoint; import run.halo.app.core.extension.endpoint.CustomEndpoint;
@Component @Component
@ -23,21 +25,11 @@ public class CacheEndpoint implements CustomEndpoint {
@Override @Override
public RouterFunction<ServerResponse> endpoint() { public RouterFunction<ServerResponse> endpoint() {
return SpringdocRouteBuilder return route()
.route() .DELETE("/caches/{name}", this::evictCache, builder -> builder
.POST("/caches/{name}/invalidation", request -> {
var cacheName = request.pathVariable("name");
if (cacheManager.getCacheNames().contains(cacheName)) {
var cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.invalidate();
}
}
return ServerResponse.noContent().build();
}, builder -> builder
.tag("v1alpha1/Cache") .tag("v1alpha1/Cache")
.operationId("InvalidCache") .operationId("EvictCache")
.description("Invalidate a cache.") .description("Evict a cache.")
.parameter(parameterBuilder() .parameter(parameterBuilder()
.name("name") .name("name")
.in(PATH) .in(PATH)
@ -49,4 +41,14 @@ public class CacheEndpoint implements CustomEndpoint {
.build(); .build();
} }
private Mono<ServerResponse> evictCache(ServerRequest request) {
var cacheName = request.pathVariable("name");
if (cacheManager.getCacheNames().contains(cacheName)) {
var cache = cacheManager.getCache(cacheName);
if (cache != null) {
cache.invalidate();
}
}
return ServerResponse.accepted().build();
}
} }

View File

@ -0,0 +1,15 @@
apiVersion: v1alpha1
kind: "Role"
metadata:
name: role-template-manage-cache
labels:
halo.run/role-template: "true"
annotations:
rbac.authorization.halo.run/module: "Cache Management"
rbac.authorization.halo.run/display-name: "Cache Manage"
rbac.authorization.halo.run/ui-permissions: |
["system:caches:manage"]
rules:
- apiGroups: ["api.console.halo.run"]
resources: ["caches"]
verbs: ["delete"]

View File

@ -46,19 +46,19 @@ export const V1alpha1CacheApiAxiosParamCreator = function (
) { ) {
return { return {
/** /**
* Invalidate a cache. * Evict a cache.
* @param {string} name Cache name * @param {string} name Cache name
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
invalidCache: async ( evictCache: async (
name: string, name: string,
options: AxiosRequestConfig = {} options: AxiosRequestConfig = {}
): Promise<RequestArgs> => { ): Promise<RequestArgs> => {
// verify required parameter 'name' is not null or undefined // verify required parameter 'name' is not null or undefined
assertParamExists("invalidCache", "name", name); assertParamExists("evictCache", "name", name);
const localVarPath = const localVarPath =
`/apis/api.console.halo.run/v1alpha1/caches/{name}/invalidation`.replace( `/apis/api.console.halo.run/v1alpha1/caches/{name}`.replace(
`{${"name"}}`, `{${"name"}}`,
encodeURIComponent(String(name)) encodeURIComponent(String(name))
); );
@ -70,7 +70,7 @@ export const V1alpha1CacheApiAxiosParamCreator = function (
} }
const localVarRequestOptions = { const localVarRequestOptions = {
method: "POST", method: "DELETE",
...baseOptions, ...baseOptions,
...options, ...options,
}; };
@ -111,18 +111,18 @@ export const V1alpha1CacheApiFp = function (configuration?: Configuration) {
V1alpha1CacheApiAxiosParamCreator(configuration); V1alpha1CacheApiAxiosParamCreator(configuration);
return { return {
/** /**
* Invalidate a cache. * Evict a cache.
* @param {string} name Cache name * @param {string} name Cache name
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
async invalidCache( async evictCache(
name: string, name: string,
options?: AxiosRequestConfig options?: AxiosRequestConfig
): Promise< ): Promise<
(axios?: AxiosInstance, basePath?: string) => AxiosPromise<void> (axios?: AxiosInstance, basePath?: string) => AxiosPromise<void>
> { > {
const localVarAxiosArgs = await localVarAxiosParamCreator.invalidCache( const localVarAxiosArgs = await localVarAxiosParamCreator.evictCache(
name, name,
options options
); );
@ -148,32 +148,32 @@ export const V1alpha1CacheApiFactory = function (
const localVarFp = V1alpha1CacheApiFp(configuration); const localVarFp = V1alpha1CacheApiFp(configuration);
return { return {
/** /**
* Invalidate a cache. * Evict a cache.
* @param {V1alpha1CacheApiInvalidCacheRequest} requestParameters Request parameters. * @param {V1alpha1CacheApiEvictCacheRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
*/ */
invalidCache( evictCache(
requestParameters: V1alpha1CacheApiInvalidCacheRequest, requestParameters: V1alpha1CacheApiEvictCacheRequest,
options?: AxiosRequestConfig options?: AxiosRequestConfig
): AxiosPromise<void> { ): AxiosPromise<void> {
return localVarFp return localVarFp
.invalidCache(requestParameters.name, options) .evictCache(requestParameters.name, options)
.then((request) => request(axios, basePath)); .then((request) => request(axios, basePath));
}, },
}; };
}; };
/** /**
* Request parameters for invalidCache operation in V1alpha1CacheApi. * Request parameters for evictCache operation in V1alpha1CacheApi.
* @export * @export
* @interface V1alpha1CacheApiInvalidCacheRequest * @interface V1alpha1CacheApiEvictCacheRequest
*/ */
export interface V1alpha1CacheApiInvalidCacheRequest { export interface V1alpha1CacheApiEvictCacheRequest {
/** /**
* Cache name * Cache name
* @type {string} * @type {string}
* @memberof V1alpha1CacheApiInvalidCache * @memberof V1alpha1CacheApiEvictCache
*/ */
readonly name: string; readonly name: string;
} }
@ -186,18 +186,18 @@ export interface V1alpha1CacheApiInvalidCacheRequest {
*/ */
export class V1alpha1CacheApi extends BaseAPI { export class V1alpha1CacheApi extends BaseAPI {
/** /**
* Invalidate a cache. * Evict a cache.
* @param {V1alpha1CacheApiInvalidCacheRequest} requestParameters Request parameters. * @param {V1alpha1CacheApiEvictCacheRequest} requestParameters Request parameters.
* @param {*} [options] Override http request option. * @param {*} [options] Override http request option.
* @throws {RequiredError} * @throws {RequiredError}
* @memberof V1alpha1CacheApi * @memberof V1alpha1CacheApi
*/ */
public invalidCache( public evictCache(
requestParameters: V1alpha1CacheApiInvalidCacheRequest, requestParameters: V1alpha1CacheApiEvictCacheRequest,
options?: AxiosRequestConfig options?: AxiosRequestConfig
) { ) {
return V1alpha1CacheApiFp(this.configuration) return V1alpha1CacheApiFp(this.configuration)
.invalidCache(requestParameters.name, options) .evictCache(requestParameters.name, options)
.then((request) => request(this.axios, this.basePath)); .then((request) => request(this.axios, this.basePath));
} }
} }

View File

@ -124,7 +124,7 @@ core:
dialog_title: Do you want to refresh the search engine index? dialog_title: Do you want to refresh the search engine index?
dialog_content: This operation will recreate search engine indexes for all published posts. dialog_content: This operation will recreate search engine indexes for all published posts.
success_message: Refresh search engine index successfully. success_message: Refresh search engine index successfully.
refresh_page_cache: evict_page_cache:
title: Refresh Page Cache title: Refresh Page Cache
dialog_title: Do you want to refresh the page cache? dialog_title: Do you want to refresh the page cache?
dialog_content: This operation will clear the cache for all pages. dialog_content: This operation will clear the cache for all pages.

View File

@ -124,7 +124,7 @@ core:
dialog_title: 确定要刷新搜索引擎索引吗? dialog_title: 确定要刷新搜索引擎索引吗?
dialog_content: 此操作会对所有已发布的文章重新创建搜索引擎索引。 dialog_content: 此操作会对所有已发布的文章重新创建搜索引擎索引。
success_message: 刷新成功 success_message: 刷新成功
refresh_page_cache: evict_page_cache:
title: 刷新页面缓存 title: 刷新页面缓存
dialog_title: 确定要刷新页面缓存吗? dialog_title: 确定要刷新页面缓存吗?
dialog_content: 此操作会清空所有页面的缓存。 dialog_content: 此操作会清空所有页面的缓存。

View File

@ -124,7 +124,7 @@ core:
dialog_title: 確定要刷新搜尋引擎索引嗎? dialog_title: 確定要刷新搜尋引擎索引嗎?
dialog_content: 此操作會對所有已發布的文章重新創建搜尋引擎索引。 dialog_content: 此操作會對所有已發布的文章重新創建搜尋引擎索引。
success_message: 刷新成功 success_message: 刷新成功
refresh_page_cache: evict_page_cache:
title: 重新整理頁面快取 title: 重新整理頁面快取
dialog_title: 確定要重新整理頁面快取嗎? dialog_title: 確定要重新整理頁面快取嗎?
dialog_content: 這個操作會清空所有頁面的快取。 dialog_content: 這個操作會清空所有頁面的快取。

View File

@ -160,28 +160,29 @@ const actions: Action[] = [
{ {
icon: markRaw(IconDatabase2Line), icon: markRaw(IconDatabase2Line),
title: t( title: t(
"core.dashboard.widgets.presets.quicklink.actions.refresh_page_cache.title" "core.dashboard.widgets.presets.quicklink.actions.evict_page_cache.title"
), ),
action: () => { action: () => {
Dialog.warning({ Dialog.warning({
title: t( title: t(
"core.dashboard.widgets.presets.quicklink.actions.refresh_page_cache.dialog_title" "core.dashboard.widgets.presets.quicklink.actions.evict_page_cache.dialog_title"
), ),
description: t( description: t(
"core.dashboard.widgets.presets.quicklink.actions.refresh_page_cache.dialog_content" "core.dashboard.widgets.presets.quicklink.actions.evict_page_cache.dialog_content"
), ),
confirmText: t("core.common.buttons.confirm"), confirmText: t("core.common.buttons.confirm"),
cancelText: t("core.common.buttons.cancel"), cancelText: t("core.common.buttons.cancel"),
onConfirm: async () => { onConfirm: async () => {
await apiClient.cache.invalidCache({ name: "page" }); await apiClient.cache.evictCache({ name: "page" });
Toast.success( Toast.success(
t( t(
"core.dashboard.widgets.presets.quicklink.actions.refresh_page_cache.success_message" "core.dashboard.widgets.presets.quicklink.actions.evict_page_cache.success_message"
) )
); );
}, },
}); });
}, },
permissions: ["system:caches:manage"],
}, },
]; ];
</script> </script>