123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- <script setup lang="ts">
- import type { EnvGroup } from '@/api/env_group'
- import type { Environment } from '@/api/environment'
- import nodeApi from '@/api/node'
- import { useUserStore } from '@/pinia'
- import { message } from 'ant-design-vue'
- import { SSE } from 'sse.js'
- const props = defineProps<{
- envGroups: EnvGroup[]
- }>()
- const modelValue = defineModel<string | number>('activeKey')
- const { token } = storeToRefs(useUserStore())
- const environments = ref<Environment[]>([])
- const environmentsMap = ref<Record<number, Environment>>({})
- const sse = shallowRef<SSE>()
- const loading = ref({
- reload: false,
- restart: false,
- })
- // Get node data when tab is not 'All'
- watch(modelValue, newVal => {
- if (newVal && newVal !== 0) {
- connectSSE()
- }
- else {
- disconnectSSE()
- }
- }, { immediate: true })
- onUnmounted(() => {
- disconnectSSE()
- })
- function connectSSE() {
- disconnectSSE()
- const s = new SSE('api/environments/enabled', {
- headers: {
- Authorization: token.value,
- },
- })
- s.onmessage = e => {
- environments.value = JSON.parse(e.data)
- environmentsMap.value = environments.value.reduce((acc, node) => {
- acc[node.id] = node
- return acc
- }, {} as Record<number, Environment>)
- }
- s.onerror = () => {
- setTimeout(() => {
- connectSSE()
- }, 5000)
- }
- sse.value = s
- }
- function disconnectSSE() {
- if (sse.value) {
- sse.value.close()
- sse.value = undefined
- }
- }
- // Get the current environment group data
- const currentEnvGroup = computed(() => {
- if (!modelValue.value || modelValue.value === 0)
- return null
- return props.envGroups.find(g => g.id === Number(modelValue.value))
- })
- // Get the list of nodes in the current group
- const syncNodes = computed(() => {
- if (!currentEnvGroup.value)
- return []
- if (!currentEnvGroup.value.sync_node_ids)
- return []
- return currentEnvGroup.value.sync_node_ids
- .map(id => environmentsMap.value[id])
- .filter(Boolean)
- })
- // Handle reload Nginx on all sync nodes
- async function handleReloadNginx() {
- if (!currentEnvGroup.value || !syncNodes.value.length)
- return
- const nodeIds = syncNodes.value.map(node => node.id)
- loading.value.reload = true
- try {
- await nodeApi.reloadNginx(nodeIds)
- }
- catch (error) {
- console.error(error)
- message.error($gettext('Reload request failed, please check your network connection'))
- }
- finally {
- loading.value.reload = false
- }
- }
- // Handle restart Nginx on all sync nodes
- async function handleRestartNginx() {
- if (!currentEnvGroup.value || !syncNodes.value.length)
- return
- const nodeIds = syncNodes.value.map(node => node.id)
- loading.value.restart = true
- try {
- await nodeApi.restartNginx(nodeIds)
- }
- catch (error) {
- console.error(error)
- message.error($gettext('Restart request failed, please check your network connection'))
- }
- finally {
- loading.value.restart = false
- }
- }
- </script>
- <template>
- <div>
- <ATabs :active-key="modelValue" @update:active-key="modelValue = $event">
- <ATabPane :key="0" :tab="$gettext('All')" />
- <ATabPane v-for="c in envGroups" :key="c.id" :tab="c.name" />
- </ATabs>
- <!-- Display node information -->
- <ACard
- v-if="modelValue && modelValue !== 0 && syncNodes.length > 0"
- :title="$gettext('Sync Nodes')"
- size="small"
- class="mb-4"
- >
- <template #extra>
- <ASpace>
- <APopconfirm
- :title="$gettext('Are you sure you want to reload Nginx on the following sync nodes?')"
- :ok-text="$gettext('Yes')"
- :cancel-text="$gettext('No')"
- placement="bottom"
- @confirm="handleReloadNginx"
- >
- <AButton type="link" size="small" :loading="loading.reload">
- {{ $gettext('Reload Nginx') }}
- </AButton>
- </APopconfirm>
- <APopconfirm
- :title="$gettext('Are you sure you want to restart Nginx on the following sync nodes?')"
- :ok-text="$gettext('Yes')"
- :cancel-text="$gettext('No')"
- placement="bottomRight"
- @confirm="handleRestartNginx"
- >
- <AButton type="link" danger size="small" :loading="loading.restart">
- {{ $gettext('Restart Nginx') }}
- </AButton>
- </APopconfirm>
- </ASpace>
- </template>
- <ARow :gutter="[16, 16]">
- <ACol v-for="node in syncNodes" :key="node.id" :xs="24" :sm="12" :md="8" :lg="6" :xl="4">
- <div class="node-item">
- <span class="node-name">{{ node.name }}</span>
- <ATag :color="node.status ? 'green' : 'error'">
- {{ node.status ? $gettext('Online') : $gettext('Offline') }}
- </ATag>
- </div>
- </ACol>
- </ARow>
- </ACard>
- </div>
- </template>
- <style scoped>
- .node-name {
- margin-right: 8px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- }
- </style>
|