Преглед на файлове

refactor: improve SSE connection handling and clean up notification component

Jacky преди 5 дни
родител
ревизия
a39ec77992

+ 8 - 31
app/src/components/Notification/Notification.vue

@@ -1,17 +1,16 @@
 <script setup lang="ts">
 import type { Notification } from '@/api/notification'
 import type { CustomRender } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
-import type { SSEvent } from 'sse.js'
 import type { Ref } from 'vue'
 import notificationApi from '@/api/notification'
 import { detailRender } from '@/components/Notification/detailRender'
+import { useSSE } from '@/composables/useSSE'
 import { NotificationTypeT } from '@/constants'
 import { useUserStore } from '@/pinia'
 import { BellOutlined, CheckCircleOutlined, CloseCircleOutlined, DeleteOutlined, InfoCircleOutlined, WarningOutlined } from '@ant-design/icons-vue'
 import { message, notification } from 'ant-design-vue'
 import dayjs from 'dayjs'
 import relativeTime from 'dayjs/plugin/relativeTime'
-import { SSE } from 'sse.js'
 import notifications from './notifications'
 
 defineProps<{
@@ -22,32 +21,15 @@ dayjs.extend(relativeTime)
 
 const loading = ref(false)
 
-const { token, unreadCount } = storeToRefs(useUserStore())
+const { unreadCount } = storeToRefs(useUserStore())
 
 const data = ref([]) as Ref<Notification[]>
 
-const sse = shallowRef(newSSE())
-
-function reconnect() {
-  setTimeout(() => {
-    sse.value = newSSE()
-  }, 5000)
-}
-
-function newSSE() {
-  const s = new SSE('api/notifications/live', {
-    headers: {
-      Authorization: token.value,
-    },
-  })
-
-  s.onmessage = (e: SSEvent) => {
-    const data = JSON.parse(e.data)
-    // data.type may be 0
-    if (data.type === undefined || data.type === null || data.type === '') {
-      return
-    }
+const { connect } = useSSE()
 
+connect({
+  url: '/api/notifications/live',
+  onMessage: (data: Notification) => {
     const typeTrans = {
       0: 'error',
       1: 'warning',
@@ -59,13 +41,8 @@ function newSSE() {
       message: $gettext(data.title),
       description: detailRender({ text: data.details, record: data } as CustomRender),
     })
-  }
-
-  // reconnect
-  s.onerror = reconnect
-
-  return s
-}
+  },
+})
 
 function init() {
   loading.value = true

+ 1 - 3
app/src/components/SelfCheck/store.ts

@@ -6,7 +6,6 @@ import frontendTasks from './tasks/frontend'
 export const useSelfCheckStore = defineStore('selfCheck', () => {
   const data = ref<TaskReport[]>([])
 
-  const requestError = ref(false)
   const loading = ref(false)
 
   async function __check() {
@@ -42,7 +41,6 @@ export const useSelfCheckStore = defineStore('selfCheck', () => {
     }
     catch (error) {
       console.error(error)
-      requestError.value = true
     }
     finally {
       loading.value = false
@@ -72,7 +70,7 @@ export const useSelfCheckStore = defineStore('selfCheck', () => {
   }
 
   const hasError = computed(() => {
-    return requestError.value || data.value?.some(item => item.status === ReportStatus.Error)
+    return data.value?.some(item => item.status === ReportStatus.Error)
   })
 
   return { data, loading, fixing, hasError, check, fix }

+ 3 - 1
app/src/composables/useSSE.ts

@@ -25,7 +25,9 @@ export function useSSE() {
    * Connect to SSE service
    */
   function connect(options: SSEOptions) {
-    disconnect()
+    if (token.value) {
+      return
+    }
 
     const {
       url,

+ 5 - 2
app/src/layouts/BaseLayout.vue

@@ -1,7 +1,7 @@
 <script setup lang="ts">
 import settings from '@/api/settings'
 import PageHeader from '@/components/PageHeader/PageHeader.vue'
-import { useSettingsStore } from '@/pinia'
+import { useSettingsStore, useUserStore } from '@/pinia'
 import { throttle } from 'lodash'
 import { storeToRefs } from 'pinia'
 import FooterLayout from './FooterLayout.vue'
@@ -44,10 +44,13 @@ settings.get_server_name().then(r => {
 const breadList = ref([])
 
 provide('breadList', breadList)
+
+const userStore = useUserStore()
+const { token } = storeToRefs(userStore)
 </script>
 
 <template>
-  <ALayout class="full-screen-wrapper min-h-screen">
+  <ALayout :key="token" class="full-screen-wrapper min-h-screen">
     <div class="drawer-sidebar">
       <ADrawer
         v-model:open="drawerVisible"

+ 1 - 1
app/src/lib/http/interceptors.ts

@@ -131,7 +131,7 @@ export function setupResponseInterceptor() {
           case 403:
             user.logout()
             await router.push('/login')
-            break
+            return
         }
       }
 

+ 6 - 0
app/src/views/certificate/CertificateList/Certificate.vue

@@ -1,6 +1,7 @@
 <script setup lang="tsx">
 import cert from '@/api/cert'
 import StdTable from '@/components/StdDesign/StdDataDisplay/StdTable.vue'
+import { useGlobalStore } from '@/pinia'
 import { CloudUploadOutlined, SafetyCertificateOutlined } from '@ant-design/icons-vue'
 import RemoveCert from '../components/RemoveCert.vue'
 import WildcardCertificate from '../components/WildcardCertificate.vue'
@@ -8,6 +9,10 @@ import certColumns from './certColumns'
 
 const refWildcard = ref()
 const refTable = ref()
+
+const globalStore = useGlobalStore()
+
+const { processingStatus } = storeToRefs(globalStore)
 </script>
 
 <template>
@@ -25,6 +30,7 @@ const refTable = ref()
       <AButton
         type="link"
         size="small"
+        :disabled="processingStatus.auto_cert_processing"
         @click="() => refWildcard.open()"
       >
         <SafetyCertificateOutlined />