diff --git a/api-docs/openapi/v3_0/aggregated.json b/api-docs/openapi/v3_0/aggregated.json index 0c4adcb15..c11360092 100644 --- a/api-docs/openapi/v3_0/aggregated.json +++ b/api-docs/openapi/v3_0/aggregated.json @@ -7565,6 +7565,72 @@ ] } }, + "/apis/console.api.halo.run/v1alpha1/systemconfigs/{group}": { + "get": { + "description": "Get system config by group", + "operationId": "getSystemConfigByGroup", + "parameters": [ + { + "description": "Group of the system config", + "in": "path", + "name": "group", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "default": { + "content": { + "*/*": { + "schema": { + "type": "object" + } + }, + "application/json": {} + }, + "description": "default response" + } + }, + "tags": [ + "SystemConfigV1alpha1Console" + ] + }, + "put": { + "description": "Update system config by group", + "operationId": "updateSystemConfigByGroup", + "parameters": [ + { + "description": "Group of the system config", + "in": "path", + "name": "group", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "type": "object" + } + } + } + }, + "responses": { + "204 NO_CONTENT": { + "content": {}, + "description": "default response" + } + }, + "tags": [ + "SystemConfigV1alpha1Console" + ] + } + }, "/apis/console.api.migration.halo.run/v1alpha1/backup-files": { "get": { "description": "Get backup files from backup root.", diff --git a/api-docs/openapi/v3_0/apis_console.api_v1alpha1.json b/api-docs/openapi/v3_0/apis_console.api_v1alpha1.json index eaca77f80..691189a05 100644 --- a/api-docs/openapi/v3_0/apis_console.api_v1alpha1.json +++ b/api-docs/openapi/v3_0/apis_console.api_v1alpha1.json @@ -3267,6 +3267,72 @@ ] } }, + "/apis/console.api.halo.run/v1alpha1/systemconfigs/{group}": { + "get": { + "description": "Get system config by group", + "operationId": "getSystemConfigByGroup", + "parameters": [ + { + "description": "Group of the system config", + "in": "path", + "name": "group", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "default": { + "content": { + "*/*": { + "schema": { + "type": "object" + } + }, + "application/json": {} + }, + "description": "default response" + } + }, + "tags": [ + "SystemConfigV1alpha1Console" + ] + }, + "put": { + "description": "Update system config by group", + "operationId": "updateSystemConfigByGroup", + "parameters": [ + { + "description": "Group of the system config", + "in": "path", + "name": "group", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "*/*": { + "schema": { + "type": "object" + } + } + } + }, + "responses": { + "204 NO_CONTENT": { + "content": {}, + "description": "default response" + } + }, + "tags": [ + "SystemConfigV1alpha1Console" + ] + } + }, "/apis/console.api.migration.halo.run/v1alpha1/backup-files": { "get": { "description": "Get backup files from backup root.", diff --git a/application/src/main/java/run/halo/app/core/endpoint/SystemConfigEndpoint.java b/application/src/main/java/run/halo/app/core/endpoint/SystemConfigEndpoint.java new file mode 100644 index 000000000..d6ceb690f --- /dev/null +++ b/application/src/main/java/run/halo/app/core/endpoint/SystemConfigEndpoint.java @@ -0,0 +1,102 @@ +package run.halo.app.core.endpoint; + +import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder; +import static org.springdoc.core.fn.builders.content.Builder.contentBuilder; +import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder; +import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder; + +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import java.time.Duration; +import lombok.RequiredArgsConstructor; +import org.springdoc.webflux.core.fn.SpringdocRouteBuilder; +import org.springframework.dao.OptimisticLockingFailureException; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +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 reactor.core.publisher.Mono; +import reactor.util.retry.Retry; +import run.halo.app.core.extension.endpoint.CustomEndpoint; +import run.halo.app.extension.GroupVersion; +import run.halo.app.extension.ReactiveExtensionClient; +import run.halo.app.infra.SystemConfigurableEnvironmentFetcher; +import run.halo.app.infra.utils.JsonUtils; + +@Component +@RequiredArgsConstructor +public class SystemConfigEndpoint implements CustomEndpoint { + private final SystemConfigurableEnvironmentFetcher configurableEnvironmentFetcher; + private final ReactiveExtensionClient client; + + @Override + public RouterFunction endpoint() { + final var tag = "SystemConfigV1alpha1Console"; + return SpringdocRouteBuilder.route() + .GET("/systemconfigs/{group}", this::getConfigByGroup, + builder -> builder.operationId("getSystemConfigByGroup") + .description("Get system config by group") + .tag(tag) + .response(responseBuilder() + .content(contentBuilder() + .mediaType(MediaType.APPLICATION_JSON_VALUE) + ) + .implementation(ObjectNode.class)) + .parameter(parameterBuilder() + .in(ParameterIn.PATH) + .name("group") + .required(true) + .description("Group of the system config") + ) + ) + .PUT("/systemconfigs/{group}", this::updateConfigByGroup, + builder -> builder.operationId("updateSystemConfigByGroup") + .description("Update system config by group") + .tag(tag) + .parameter(parameterBuilder() + .in(ParameterIn.PATH) + .name("group") + .required(true) + .description("Group of the system config") + ) + .requestBody(requestBodyBuilder() + .implementation(ObjectNode.class) + ) + .response(responseBuilder() + .responseCode(String.valueOf(HttpStatus.NO_CONTENT)) + .implementation(Void.class) + ) + ) + .build(); + } + + private Mono updateConfigByGroup(ServerRequest request) { + final var group = request.pathVariable("group"); + return request.bodyToMono(ObjectNode.class) + .flatMap(objectNode -> configurableEnvironmentFetcher.getConfigMap() + .flatMap(configMap -> { + var data = configMap.getData(); + data.put(group, JsonUtils.objectToJson(objectNode)); + return client.update(configMap); + }) + ) + .retryWhen(Retry.backoff(5, Duration.ofMillis(100)) + .filter(OptimisticLockingFailureException.class::isInstance)) + .then(ServerResponse.noContent().build()); + } + + private Mono getConfigByGroup(ServerRequest request) { + final var group = request.pathVariable("group"); + return configurableEnvironmentFetcher.fetch(group, ObjectNode.class) + .switchIfEmpty(Mono.fromSupplier(JsonNodeFactory.instance::objectNode)) + .flatMap(json -> ServerResponse.ok().bodyValue(json)); + } + + @Override + public GroupVersion groupVersion() { + return new GroupVersion("console.api.halo.run", "v1alpha1"); + } +} diff --git a/application/src/main/resources/extensions/role-template-menu.yaml b/application/src/main/resources/extensions/role-template-menu.yaml index 24a4d71df..a94f33e35 100644 --- a/application/src/main/resources/extensions/role-template-menu.yaml +++ b/application/src/main/resources/extensions/role-template-menu.yaml @@ -14,6 +14,10 @@ rules: - apiGroups: [ "" ] resources: [ "menus", "menuitems" ] verbs: [ "*" ] + - apiGroups: [ "console.api.halo.run" ] + resources: [ "systemconfigs" ] + resourceNames: [ "menu" ] + verbs: [ "update" ] --- apiVersion: v1alpha1 kind: "Role" @@ -30,3 +34,7 @@ rules: - apiGroups: [ "" ] resources: [ "menus", "menuitems" ] verbs: [ "get", "list" ] + - apiGroups: [ "console.api.halo.run" ] + resources: [ "systemconfigs" ] + resourceNames: [ "menu" ] + verbs: [ "get" ] diff --git a/ui/console-src/modules/interface/menus/components/MenuList.vue b/ui/console-src/modules/interface/menus/components/MenuList.vue index 79925a01d..5d51168ea 100644 --- a/ui/console-src/modules/interface/menus/components/MenuList.vue +++ b/ui/console-src/modules/interface/menus/components/MenuList.vue @@ -1,7 +1,7 @@