NginxDashBoard.vue 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. <script setup lang="ts">
  2. import ngx from '@/api/ngx'
  3. import { useNginxPerformance } from '@/composables/useNginxPerformance'
  4. import { useSSE } from '@/composables/useSSE'
  5. import { NginxStatus } from '@/constants'
  6. import { useUserStore } from '@/pinia'
  7. import { useGlobalStore } from '@/pinia/moudule/global'
  8. import { ClockCircleOutlined, ReloadOutlined } from '@ant-design/icons-vue'
  9. import ConnectionMetricsCard from './components/ConnectionMetricsCard.vue'
  10. import PerformanceStatisticsCard from './components/PerformanceStatisticsCard.vue'
  11. import PerformanceTablesCard from './components/PerformanceTablesCard.vue'
  12. import ProcessDistributionCard from './components/ProcessDistributionCard.vue'
  13. import ResourceUsageCard from './components/ResourceUsageCard.vue'
  14. // Global state
  15. const global = useGlobalStore()
  16. const { nginxStatus: status } = storeToRefs(global)
  17. const { token } = storeToRefs(useUserStore())
  18. // Use performance data composable
  19. const {
  20. loading,
  21. nginxInfo,
  22. error,
  23. formattedUpdateTime,
  24. updateLastUpdateTime,
  25. fetchInitialData,
  26. stubStatusEnabled,
  27. stubStatusLoading,
  28. stubStatusError,
  29. } = useNginxPerformance()
  30. // SSE connection
  31. const { connect, disconnect } = useSSE()
  32. // Toggle stub_status module status
  33. async function toggleStubStatus() {
  34. try {
  35. stubStatusLoading.value = true
  36. stubStatusError.value = ''
  37. const response = await ngx.toggle_stub_status(!stubStatusEnabled.value)
  38. if (response.stub_status_enabled !== undefined) {
  39. stubStatusEnabled.value = response.stub_status_enabled
  40. }
  41. if (response.error) {
  42. stubStatusError.value = response.error
  43. }
  44. else {
  45. fetchInitialData().then(connectSSE)
  46. }
  47. }
  48. catch (err) {
  49. console.error('Toggle stub_status failed:', err)
  50. stubStatusError.value = $gettext('Toggle failed')
  51. }
  52. finally {
  53. stubStatusLoading.value = false
  54. }
  55. }
  56. // Connect SSE
  57. function connectSSE() {
  58. disconnect()
  59. loading.value = true
  60. connect({
  61. url: 'api/nginx/detail_status/stream',
  62. token: token.value,
  63. onMessage: data => {
  64. loading.value = false
  65. if (data.running) {
  66. nginxInfo.value = data.info
  67. updateLastUpdateTime()
  68. }
  69. else {
  70. error.value = data.message || $gettext('Nginx is not running')
  71. }
  72. stubStatusEnabled.value = data.stub_status_enabled
  73. },
  74. onError: () => {
  75. error.value = $gettext('Connection error, trying to reconnect...')
  76. // If the connection fails, try to get data using the traditional method
  77. setTimeout(() => {
  78. fetchInitialData()
  79. }, 2000)
  80. },
  81. })
  82. }
  83. // Manually refresh data
  84. function refreshData() {
  85. fetchInitialData().then(connectSSE)
  86. }
  87. // Initialize connection when the component is mounted
  88. onMounted(() => {
  89. fetchInitialData().then(connectSSE)
  90. })
  91. </script>
  92. <template>
  93. <div>
  94. <!-- Top operation bar -->
  95. <div class="mb-4 mx-6 md:mx-0 flex flex-wrap justify-between items-center">
  96. <div class="flex items-center">
  97. <ABadge :status="status === NginxStatus.Running ? 'success' : 'error'" />
  98. <span class="font-medium">{{ status === NginxStatus.Running ? $gettext('Nginx is running') : $gettext('Nginx is not running') }}</span>
  99. </div>
  100. <div class="flex items-center">
  101. <ClockCircleOutlined class="mr-1 text-gray-500" />
  102. <span class="mr-4 text-gray-500 text-sm text-nowrap">{{ $gettext('Last update') }}: {{ formattedUpdateTime }}</span>
  103. <AButton type="text" size="small" :loading="loading" @click="refreshData">
  104. <template #icon>
  105. <ReloadOutlined />
  106. </template>
  107. </AButton>
  108. </div>
  109. </div>
  110. <!-- Nginx status prompt -->
  111. <AAlert
  112. v-if="status !== NginxStatus.Running"
  113. class="mb-4"
  114. type="warning"
  115. show-icon
  116. :message="$gettext('Nginx is not running')"
  117. :description="$gettext('Cannot get performance data in this state')"
  118. />
  119. <!-- Error prompt -->
  120. <AAlert
  121. v-if="error"
  122. class="mb-4"
  123. type="error"
  124. show-icon
  125. :message="$gettext('Get data failed')"
  126. :description="error"
  127. />
  128. <!-- stub_status switch -->
  129. <ACard class="mb-4" :bordered="false">
  130. <div class="flex items-center justify-between">
  131. <div>
  132. <div class="font-medium mb-1">
  133. {{ $gettext('Enable stub_status module') }}
  134. </div>
  135. <div class="text-gray-500 text-sm">
  136. {{ $gettext('This module provides Nginx request statistics, connection count, etc. data. After enabling it, you can view performance statistics') }}
  137. </div>
  138. <div v-if="stubStatusError" class="text-red-500 text-sm mt-1">
  139. {{ stubStatusError }}
  140. </div>
  141. </div>
  142. <ASwitch
  143. :checked="stubStatusEnabled"
  144. :loading="stubStatusLoading"
  145. @change="toggleStubStatus"
  146. />
  147. </div>
  148. </ACard>
  149. <!-- stub_status module is not enabled -->
  150. <AAlert
  151. v-if="status === NginxStatus.Running && !stubStatusEnabled && !error"
  152. class="mb-4"
  153. type="info"
  154. show-icon
  155. :message="$gettext('Need to enable the stub_status module')"
  156. :description="$gettext('Please enable the stub_status module to get request statistics, connection count, etc.')"
  157. />
  158. <!-- Loading state -->
  159. <ASpin :spinning="loading" :tip="$gettext('Loading data...')">
  160. <div v-if="!nginxInfo && !error" class="text-center py-8">
  161. <AEmpty :description="$gettext('No data')" />
  162. </div>
  163. <div v-if="nginxInfo" class="performance-dashboard">
  164. <!-- Top performance metrics card -->
  165. <ACard class="mb-4" :title="$gettext('Performance Metrics')" :bordered="false">
  166. <PerformanceStatisticsCard :nginx-info="nginxInfo" />
  167. </ACard>
  168. <ARow :gutter="[16, 16]" class="mb-4">
  169. <!-- Metrics card -->
  170. <ACol :sm="24" :lg="12">
  171. <ConnectionMetricsCard :nginx-info="nginxInfo" />
  172. </ACol>
  173. <!-- CPU and memory usage -->
  174. <ACol :sm="24" :lg="12">
  175. <ResourceUsageCard :nginx-info="nginxInfo" />
  176. </ACol>
  177. </ARow>
  178. <!-- Resource monitoring -->
  179. <ARow :gutter="[16, 16]" class="mb-4">
  180. <!-- Process distribution -->
  181. <ACol :span="24">
  182. <ProcessDistributionCard :nginx-info="nginxInfo" />
  183. </ACol>
  184. </ARow>
  185. <!-- Performance metrics table -->
  186. <ARow :gutter="[16, 16]" class="mb-4">
  187. <ACol :span="24">
  188. <PerformanceTablesCard :nginx-info="nginxInfo" />
  189. </ACol>
  190. </ARow>
  191. </div>
  192. </ASpin>
  193. </div>
  194. </template>