diff --git a/application/src/main/java/run/halo/app/notification/endpoint/UserNotificationPreferencesEndpoint.java b/application/src/main/java/run/halo/app/notification/endpoint/UserNotificationPreferencesEndpoint.java
index 90948904d..9916d90ed 100644
--- a/application/src/main/java/run/halo/app/notification/endpoint/UserNotificationPreferencesEndpoint.java
+++ b/application/src/main/java/run/halo/app/notification/endpoint/UserNotificationPreferencesEndpoint.java
@@ -5,17 +5,20 @@ import static org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder
 import static org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder;
 import static org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder;
 
+import com.fasterxml.jackson.core.type.TypeReference;
 import io.swagger.v3.oas.annotations.enums.ParameterIn;
 import io.swagger.v3.oas.annotations.media.Schema;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.function.Function;
 import java.util.function.Supplier;
 import lombok.Data;
 import lombok.RequiredArgsConstructor;
 import lombok.experimental.Accessors;
+import org.apache.commons.lang3.StringUtils;
 import org.springdoc.webflux.core.fn.SpringdocRouteBuilder;
 import org.springframework.lang.NonNull;
 import org.springframework.stereotype.Component;
@@ -25,12 +28,15 @@ import org.springframework.web.reactive.function.server.ServerRequest;
 import org.springframework.web.reactive.function.server.ServerResponse;
 import reactor.core.publisher.Mono;
 import reactor.util.function.Tuples;
+import run.halo.app.core.extension.Role;
 import run.halo.app.core.extension.endpoint.CustomEndpoint;
 import run.halo.app.core.extension.notification.NotifierDescriptor;
 import run.halo.app.core.extension.notification.ReasonType;
 import run.halo.app.extension.Comparators;
 import run.halo.app.extension.GroupVersion;
+import run.halo.app.extension.MetadataUtil;
 import run.halo.app.extension.ReactiveExtensionClient;
+import run.halo.app.infra.utils.JsonUtils;
 import run.halo.app.notification.UserNotificationPreference;
 import run.halo.app.notification.UserNotificationPreferenceService;
 
@@ -135,10 +141,7 @@ public class UserNotificationPreferencesEndpoint implements CustomEndpoint {
 
     Mono<ReasonTypeNotifierMatrix> listReasonTypeNotifierMatrix(String username) {
         return client.list(ReasonType.class, null, Comparators.defaultComparator())
-            .map(reasonType -> new ReasonTypeInfo(reasonType.getMetadata().getName(),
-                reasonType.getSpec().getDisplayName(),
-                reasonType.getSpec().getDescription())
-            )
+            .map(ReasonTypeInfo::from)
             .collectList()
             .flatMap(reasonTypes -> client.list(NotifierDescriptor.class, null,
                     Comparators.defaultComparator())
@@ -188,7 +191,23 @@ public class UserNotificationPreferencesEndpoint implements CustomEndpoint {
         private boolean[][] stateMatrix;
     }
 
-    record ReasonTypeInfo(String name, String displayName, String description) {
+    record ReasonTypeInfo(String name, String displayName, String description,
+                          Set<String> uiPermissions) {
+
+        public static ReasonTypeInfo from(ReasonType reasonType) {
+            var uiPermissions = Optional.of(MetadataUtil.nullSafeAnnotations(reasonType))
+                .map(annotations -> annotations.get(Role.UI_PERMISSIONS_ANNO))
+                .filter(StringUtils::isNotBlank)
+                .map(uiPermissionStr -> JsonUtils.jsonToObject(uiPermissionStr,
+                    new TypeReference<Set<String>>() {
+                    })
+                )
+                .orElse(Set.of());
+            return new ReasonTypeInfo(reasonType.getMetadata().getName(),
+                reasonType.getSpec().getDisplayName(),
+                reasonType.getSpec().getDescription(),
+                uiPermissions);
+        }
     }
 
     record NotifierInfo(String name, String displayName, String description) {
diff --git a/application/src/main/resources/extensions/notification.yaml b/application/src/main/resources/extensions/notification.yaml
index 23429a95a..3fd11c919 100644
--- a/application/src/main/resources/extensions/notification.yaml
+++ b/application/src/main/resources/extensions/notification.yaml
@@ -64,6 +64,9 @@ apiVersion: notification.halo.run/v1alpha1
 kind: ReasonType
 metadata:
   name: new-comment-on-post
+  annotations:
+    rbac.authorization.halo.run/ui-permissions: |
+      [ "uc:posts:publish" ]
 spec:
   displayName: "我的文章收到新评论"
   description: "如果有读者在你的文章下方留下了新的评论,你将会收到一条通知,告诉你有新的评论。
@@ -90,6 +93,9 @@ apiVersion: notification.halo.run/v1alpha1
 kind: ReasonType
 metadata:
   name: new-comment-on-single-page
+  annotations:
+    rbac.authorization.halo.run/ui-permissions: |
+      [ "system:singlepages:manage" ]
 spec:
   displayName: "我的自定义页面收到新评论"
   description: "当你创建的自定义页面收到新评论时,你将会收到一条通知,告诉你有新的评论。"
diff --git a/console/packages/api-client/src/models/post-status.ts b/console/packages/api-client/src/models/post-status.ts
index dbc5bd93a..b59635bcc 100644
--- a/console/packages/api-client/src/models/post-status.ts
+++ b/console/packages/api-client/src/models/post-status.ts
@@ -58,6 +58,12 @@ export interface PostStatus {
    * @memberof PostStatus
    */
   lastModifyTime?: string;
+  /**
+   *
+   * @type {number}
+   * @memberof PostStatus
+   */
+  observedVersion?: number;
   /**
    *
    * @type {string}
diff --git a/console/packages/api-client/src/models/reason-type-info.ts b/console/packages/api-client/src/models/reason-type-info.ts
index 1008af2c5..422b2cabd 100644
--- a/console/packages/api-client/src/models/reason-type-info.ts
+++ b/console/packages/api-client/src/models/reason-type-info.ts
@@ -36,4 +36,10 @@ export interface ReasonTypeInfo {
    * @memberof ReasonTypeInfo
    */
   name?: string;
+  /**
+   *
+   * @type {Array<string>}
+   * @memberof ReasonTypeInfo
+   */
+  uiPermissions?: Array<string>;
 }
diff --git a/console/packages/api-client/src/models/single-page-status.ts b/console/packages/api-client/src/models/single-page-status.ts
index 29c74e359..6972c4237 100644
--- a/console/packages/api-client/src/models/single-page-status.ts
+++ b/console/packages/api-client/src/models/single-page-status.ts
@@ -58,6 +58,12 @@ export interface SinglePageStatus {
    * @memberof SinglePageStatus
    */
   lastModifyTime?: string;
+  /**
+   *
+   * @type {number}
+   * @memberof SinglePageStatus
+   */
+  observedVersion?: number;
   /**
    *
    * @type {string}
diff --git a/console/uc-src/modules/profile/tabs/NotificationPreferences.vue b/console/uc-src/modules/profile/tabs/NotificationPreferences.vue
index 9d9663554..32b3d457c 100644
--- a/console/uc-src/modules/profile/tabs/NotificationPreferences.vue
+++ b/console/uc-src/modules/profile/tabs/NotificationPreferences.vue
@@ -8,6 +8,7 @@ import { computed } from "vue";
 import { inject } from "vue";
 import { cloneDeep } from "lodash-es";
 import type { ReasonTypeNotifierRequest } from "@halo-dev/api-client";
+import HasPermission from "@/components/permission/HasPermission.vue";
 
 const queryClient = useQueryClient();
 
@@ -114,37 +115,41 @@ const {
           </tr>
         </thead>
         <tbody class="divide-y divide-gray-100 bg-white">
-          <tr
+          <template
             v-for="(reasonType, index) in data?.reasonTypes"
             :key="reasonType.name"
           >
-            <td
-              class="whitespace-nowrap px-4 py-3 text-sm font-medium text-gray-900"
-            >
-              {{ reasonType.displayName }}
-            </td>
-            <td
-              v-for="(notifier, notifierIndex) in data?.notifiers"
-              :key="notifier.name"
-              class="whitespace-nowrap px-4 py-3 text-sm text-gray-500"
-            >
-              <VSwitch
-                :model-value="data?.stateMatrix?.[index][notifierIndex]"
-                :loading="
-                  mutating &&
-                  variables?.reasonTypeIndex === index &&
-                  variables?.notifierIndex === notifierIndex
-                "
-                @change="
-                  mutate({
-                    state: !data?.stateMatrix?.[index][notifierIndex],
-                    reasonTypeIndex: index,
-                    notifierIndex: notifierIndex,
-                  })
-                "
-              />
-            </td>
-          </tr>
+            <HasPermission :permissions="reasonType.uiPermissions || []">
+              <tr>
+                <td
+                  class="whitespace-nowrap px-4 py-3 text-sm font-medium text-gray-900"
+                >
+                  {{ reasonType.displayName }}
+                </td>
+                <td
+                  v-for="(notifier, notifierIndex) in data?.notifiers"
+                  :key="notifier.name"
+                  class="whitespace-nowrap px-4 py-3 text-sm text-gray-500"
+                >
+                  <VSwitch
+                    :model-value="data?.stateMatrix?.[index][notifierIndex]"
+                    :loading="
+                      mutating &&
+                      variables?.reasonTypeIndex === index &&
+                      variables?.notifierIndex === notifierIndex
+                    "
+                    @change="
+                      mutate({
+                        state: !data?.stateMatrix?.[index][notifierIndex],
+                        reasonTypeIndex: index,
+                        notifierIndex: notifierIndex,
+                      })
+                    "
+                  />
+                </td>
+              </tr>
+            </HasPermission>
+          </template>
         </tbody>
       </table>
     </div>