Browse Source

feat(index-status): add composable for monitoring cache index status using SSE

Jacky 2 months ago
parent
commit
2d3a5e2a16

+ 49 - 0
app/src/composables/useIndexStatus.ts

@@ -0,0 +1,49 @@
+import cacheIndex from '@/api/cache_index'
+import { SSE } from 'sse.js'
+import { useSSE } from './useSSE'
+
+/**
+ * Composable for monitoring cache index status
+ * Provides a way to track indexing/scanning status through SSE
+ */
+
+export interface IndexStatus {
+  isScanning: Ref<boolean>
+}
+
+/**
+ * Setup SSE connection to monitor indexing status
+ */
+export function setupIndexStatus() {
+  const { connect, disconnect, sseInstance } = useSSE()
+
+  const isScanning = ref(false)
+
+  disconnect()
+
+  const sse = cacheIndex.index_status()
+
+  if (sse instanceof SSE) {
+    connect({
+      url: '', // Not needed as we already have the SSE instance
+      token: '', // Not needed as we already have the SSE instance
+      onMessage: data => {
+        isScanning.value = data.scanning
+      },
+      onError: () => {
+        // Reconnection is handled by useSSE
+      },
+    })
+
+    // Manually assign the SSE instance since we're using a pre-created one
+    sseInstance.value = sse
+  }
+
+  provide('indexStatus', {
+    isScanning,
+  })
+}
+
+export function useIndexStatus(): IndexStatus {
+  return inject<IndexStatus>('indexStatus')!
+}

+ 0 - 2
app/src/composables/usePerformanceMetrics.ts

@@ -1,6 +1,4 @@
 import type { NginxPerformanceInfo } from '@/api/ngx'
-import type { Ref } from 'vue'
-import { computed } from 'vue'
 
 export function usePerformanceMetrics(nginxInfo: Ref<NginxPerformanceInfo | undefined>) {
   // Format numbers to a more readable form

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

@@ -1,6 +1,5 @@
 import type { SSEvent } from 'sse.js'
 import { SSE } from 'sse.js'
-import { onUnmounted, shallowRef } from 'vue'
 
 export interface SSEOptions {
   url: string

+ 3 - 0
app/src/layouts/BaseLayout.vue

@@ -1,6 +1,7 @@
 <script setup lang="ts">
 import settings from '@/api/settings'
 import PageHeader from '@/components/PageHeader/PageHeader.vue'
+import { setupIndexStatus } from '@/composables/useIndexStatus'
 import { useSettingsStore } from '@/pinia'
 import _ from 'lodash'
 import { storeToRefs } from 'pinia'
@@ -41,6 +42,8 @@ settings.get_server_name().then(r => {
   server_name.value = r.name
 })
 
+setupIndexStatus()
+
 const breadList = ref([])
 
 provide('breadList', breadList)

+ 1 - 1
app/src/views/certificate/components/RemoveCert.vue

@@ -88,7 +88,7 @@ function handleCancel() {
     </AButton>
 
     <AModal
-      v-model:visible="modalVisible"
+      v-model:open="modalVisible"
       :title="$gettext('Delete Certificate')"
       :confirm-loading="confirmLoading"
       :ok-button-props="{

+ 2 - 50
app/src/views/nginx_log/NginxLogList.vue

@@ -1,20 +1,16 @@
 <script setup lang="tsx">
 import type { CustomRender } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
 import type { Column } from '@/components/StdDesign/types'
-import type { SSE, SSEvent } from 'sse.js'
-import cacheIndex from '@/api/cache_index'
 import nginxLog from '@/api/nginx_log'
 import StdCurd from '@/components/StdDesign/StdDataDisplay/StdCurd.vue'
 import { input, select } from '@/components/StdDesign/StdDataEntry'
+import { useIndexStatus } from '@/composables/useIndexStatus'
 import { CheckCircleOutlined, LoadingOutlined } from '@ant-design/icons-vue'
 import { Tag } from 'ant-design-vue'
-import { onMounted, onUnmounted, ref } from 'vue'
-import { useRouter } from 'vue-router'
 
 const router = useRouter()
-const isScanning = ref(false)
+const { isScanning } = useIndexStatus()
 const stdCurdRef = ref()
-const sse = ref<SSE>()
 
 const columns: Column[] = [
   {
@@ -63,50 +59,6 @@ function viewLog(record: { type: string, path: string }) {
     },
   })
 }
-
-// Connect to SSE endpoint and setup handlers
-async function setupSSE() {
-  if (sse.value) {
-    sse.value.close()
-  }
-
-  sse.value = cacheIndex.index_status()
-
-  // Handle incoming messages
-  if (sse.value) {
-    sse.value.onmessage = (e: SSEvent) => {
-      try {
-        if (!e.data)
-          return
-
-        const data = JSON.parse(e.data)
-        isScanning.value = data.scanning
-
-        stdCurdRef.value.get_list()
-      }
-      catch (error) {
-        console.error('Error parsing SSE message:', error)
-      }
-    }
-
-    sse.value.onerror = () => {
-      // Reconnect on error
-      setTimeout(() => {
-        setupSSE()
-      }, 5000)
-    }
-  }
-}
-
-onMounted(() => {
-  setupSSE()
-})
-
-onUnmounted(() => {
-  if (sse.value) {
-    sse.value.close()
-  }
-})
 </script>
 
 <template>

+ 7 - 52
app/src/views/site/site_list/SiteList.vue

@@ -2,13 +2,12 @@
 import type { EnvGroup } from '@/api/env_group'
 import type { Site } from '@/api/site'
 import type { Column } from '@/components/StdDesign/types'
-import type { SSE, SSEvent } from 'sse.js'
-import cacheIndex from '@/api/cache_index'
 import env_group from '@/api/env_group'
 import site from '@/api/site'
 import EnvGroupTabs from '@/components/EnvGroupTabs/EnvGroupTabs.vue'
 import StdBatchEdit from '@/components/StdDesign/StdDataDisplay/StdBatchEdit.vue'
 import StdTable from '@/components/StdDesign/StdDataDisplay/StdTable.vue'
+import { useIndexStatus } from '@/composables/useIndexStatus'
 import { ConfigStatus } from '@/constants'
 import InspectConfig from '@/views/config/InspectConfig.vue'
 import columns from '@/views/site/site_list/columns'
@@ -20,15 +19,15 @@ const route = useRoute()
 const router = useRouter()
 
 const table = ref()
-const inspect_config = ref()
+const inspectConfig = ref()
 
 const envGroupId = ref(Number.parseInt(route.query.env_group_id as string) || 0)
 const envGroups = ref([]) as Ref<EnvGroup[]>
-const isScanning = ref(false)
-const sse = ref<SSE>()
+
+const { isScanning } = useIndexStatus()
 
 watch(route, () => {
-  inspect_config.value?.test()
+  inspectConfig.value?.test()
 })
 
 onMounted(async () => {
@@ -48,55 +47,11 @@ onMounted(async () => {
   }
 })
 
-onMounted(() => {
-  setupSSE()
-})
-
-// Connect to SSE endpoint and setup handlers
-async function setupSSE() {
-  if (sse.value) {
-    sse.value.close()
-  }
-
-  sse.value = cacheIndex.index_status()
-
-  // Handle incoming messages
-  if (sse.value) {
-    sse.value.onmessage = (e: SSEvent) => {
-      try {
-        if (!e.data)
-          return
-
-        const data = JSON.parse(e.data)
-        isScanning.value = data.scanning
-
-        table.value.get_list()
-      }
-      catch (error) {
-        console.error('Error parsing SSE message:', error)
-      }
-    }
-
-    sse.value.onerror = () => {
-      // Reconnect on error
-      setTimeout(() => {
-        setupSSE()
-      }, 5000)
-    }
-  }
-}
-
-onUnmounted(() => {
-  if (sse.value) {
-    sse.value.close()
-  }
-})
-
 function destroy(site_name: string) {
   site.destroy(site_name).then(() => {
     table.value.get_list()
     message.success($gettext('Delete site: %{site_name}', { site_name }))
-    inspect_config.value?.test()
+    inspectConfig.value?.test()
   })
 }
 
@@ -133,7 +88,7 @@ function handleBatchUpdated() {
         </template>
       </div>
     </template>
-    <InspectConfig ref="inspect_config" />
+    <InspectConfig ref="inspectConfig" />
 
     <EnvGroupTabs v-model:active-key="envGroupId" :env-groups="envGroups" />
 

+ 13 - 1
app/src/views/stream/StreamList.vue

@@ -10,10 +10,12 @@ import StdBatchEdit from '@/components/StdDesign/StdDataDisplay/StdBatchEdit.vue
 import StdTable from '@/components/StdDesign/StdDataDisplay/StdTable.vue'
 import { actualValueRender, datetime } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
 import { input, selector } from '@/components/StdDesign/StdDataEntry'
+import { useIndexStatus } from '@/composables/useIndexStatus'
 import { ConfigStatus } from '@/constants'
 import InspectConfig from '@/views/config/InspectConfig.vue'
 import envGroupColumns from '@/views/environments/group/columns'
 import StreamDuplicate from '@/views/stream/components/StreamDuplicate.vue'
+import { CheckCircleOutlined, LoadingOutlined } from '@ant-design/icons-vue'
 import { Badge, message } from 'ant-design-vue'
 
 const columns: Column[] = [{
@@ -169,12 +171,22 @@ function handleBatchUpdated() {
   table.value?.get_list()
   table.value?.resetSelection()
 }
+
+const { isScanning } = useIndexStatus()
 </script>
 
 <template>
   <ACard :title="$gettext('Manage Streams')">
     <template #extra>
-      <a @click="add">{{ $gettext('Add') }}</a>
+      <div class="flex items-center cursor-default">
+        <a class="mr-4" @click="add">{{ $gettext('Add') }}</a>
+        <template v-if="isScanning">
+          <LoadingOutlined class="mr-2" spin />{{ $gettext('Indexing...') }}
+        </template>
+        <template v-else>
+          <CheckCircleOutlined class="mr-2" />{{ $gettext('Indexed') }}
+        </template>
+      </div>
     </template>
 
     <InspectConfig ref="inspect_config" />