Browse Source

feat(upstream): add upstream cards and detail modal

0xJacky 2 weeks ago
parent
commit
552cf5c0e8

+ 9 - 0
.claude/settings.local.json

@@ -0,0 +1,9 @@
+{
+  "permissions": {
+    "allow": [
+      "Bash(pnpm typecheck:*)",
+      "Bash(pnpm lint:*)"
+    ],
+    "deny": []
+  }
+}

+ 51 - 16
api/sites/site.go

@@ -8,6 +8,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/internal/site"
+	"github.com/0xJacky/Nginx-UI/internal/upstream"
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/gin-gonic/gin"
@@ -16,6 +17,38 @@ import (
 	"gorm.io/gorm/clause"
 )
 
+// buildProxyTargets processes proxy targets similar to list.go logic
+func buildProxyTargets(fileName string) []site.ProxyTarget {
+	indexedSite := site.GetIndexedSite(fileName)
+	
+	// Convert proxy targets, expanding upstream references
+	var proxyTargets []site.ProxyTarget
+	upstreamService := upstream.GetUpstreamService()
+
+	for _, target := range indexedSite.ProxyTargets {
+		// Check if target.Host is an upstream name
+		if upstreamDef, exists := upstreamService.GetUpstreamDefinition(target.Host); exists {
+			// Replace with upstream servers
+			for _, server := range upstreamDef.Servers {
+				proxyTargets = append(proxyTargets, site.ProxyTarget{
+					Host: server.Host,
+					Port: server.Port,
+					Type: server.Type,
+				})
+			}
+		} else {
+			// Regular proxy target
+			proxyTargets = append(proxyTargets, site.ProxyTarget{
+				Host: target.Host,
+				Port: target.Port,
+				Type: target.Type,
+			})
+		}
+	}
+
+	return proxyTargets
+}
+
 func GetSite(c *gin.Context) {
 	name := helper.UnescapeURL(c.Param("name"))
 
@@ -48,13 +81,14 @@ func GetSite(c *gin.Context) {
 		}
 
 		c.JSON(http.StatusOK, site.Site{
-			ModifiedAt: file.ModTime(),
-			Site:       siteModel,
-			Name:       name,
-			Config:     string(origContent),
-			AutoCert:   certModel.AutoCert == model.AutoCertEnabled,
-			Filepath:   path,
-			Status:     site.GetSiteStatus(name),
+			ModifiedAt:   file.ModTime(),
+			Site:         siteModel,
+			Name:         name,
+			Config:       string(origContent),
+			AutoCert:     certModel.AutoCert == model.AutoCertEnabled,
+			Filepath:     path,
+			Status:       site.GetSiteStatus(name),
+			ProxyTargets: buildProxyTargets(name),
 		})
 		return
 	}
@@ -80,15 +114,16 @@ func GetSite(c *gin.Context) {
 	}
 
 	c.JSON(http.StatusOK, site.Site{
-		Site:       siteModel,
-		ModifiedAt: file.ModTime(),
-		Name:       name,
-		Config:     nginxConfig.FmtCode(),
-		Tokenized:  nginxConfig,
-		AutoCert:   certModel.AutoCert == model.AutoCertEnabled,
-		CertInfo:   certInfoMap,
-		Filepath:   path,
-		Status:     site.GetSiteStatus(name),
+		Site:         siteModel,
+		ModifiedAt:   file.ModTime(),
+		Name:         name,
+		Config:       nginxConfig.FmtCode(),
+		Tokenized:    nginxConfig,
+		AutoCert:     certModel.AutoCert == model.AutoCertEnabled,
+		CertInfo:     certInfoMap,
+		Filepath:     path,
+		Status:       site.GetSiteStatus(name),
+		ProxyTargets: buildProxyTargets(name),
 	})
 }
 

+ 42 - 8
api/streams/streams.go

@@ -8,6 +8,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/internal/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/internal/stream"
+	"github.com/0xJacky/Nginx-UI/internal/upstream"
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/gin-gonic/gin"
@@ -31,6 +32,38 @@ type Stream struct {
 	ProxyTargets []config.ProxyTarget `json:"proxy_targets,omitempty"`
 }
 
+// buildProxyTargets processes stream proxy targets similar to list.go logic
+func buildStreamProxyTargets(fileName string) []config.ProxyTarget {
+	indexedStream := stream.GetIndexedStream(fileName)
+	
+	// Convert proxy targets, expanding upstream references
+	var proxyTargets []config.ProxyTarget
+	upstreamService := upstream.GetUpstreamService()
+
+	for _, target := range indexedStream.ProxyTargets {
+		// Check if target.Host is an upstream name
+		if upstreamDef, exists := upstreamService.GetUpstreamDefinition(target.Host); exists {
+			// Replace with upstream servers
+			for _, server := range upstreamDef.Servers {
+				proxyTargets = append(proxyTargets, config.ProxyTarget{
+					Host: server.Host,
+					Port: server.Port,
+					Type: server.Type,
+				})
+			}
+		} else {
+			// Regular proxy target
+			proxyTargets = append(proxyTargets, config.ProxyTarget{
+				Host: target.Host,
+				Port: target.Port,
+				Type: target.Type,
+			})
+		}
+	}
+
+	return proxyTargets
+}
+
 func GetStreams(c *gin.Context) {
 	// Parse query parameters
 	options := &stream.ListOptions{
@@ -101,14 +134,15 @@ func GetStream(c *gin.Context) {
 
 	// Build response based on advanced mode
 	response := Stream{
-		ModifiedAt:  info.FileInfo.ModTime(),
-		Advanced:    info.Model.Advanced,
-		Status:      info.Status,
-		Name:        name,
-		Filepath:    info.Path,
-		EnvGroupID:  info.Model.EnvGroupID,
-		EnvGroup:    info.Model.EnvGroup,
-		SyncNodeIDs: info.Model.SyncNodeIDs,
+		ModifiedAt:   info.FileInfo.ModTime(),
+		Advanced:     info.Model.Advanced,
+		Status:       info.Status,
+		Name:         name,
+		Filepath:     info.Path,
+		EnvGroupID:   info.Model.EnvGroupID,
+		EnvGroup:     info.Model.EnvGroup,
+		SyncNodeIDs:  info.Model.SyncNodeIDs,
+		ProxyTargets: buildStreamProxyTargets(name),
 	}
 
 	if info.Model.Advanced {

+ 2 - 0
app/components.d.ts

@@ -122,6 +122,8 @@ declare module 'vue' {
     SyncNodesPreviewSyncNodesPreview: typeof import('./src/components/SyncNodesPreview/SyncNodesPreview.vue')['default']
     SystemRestoreSystemRestoreContent: typeof import('./src/components/SystemRestore/SystemRestoreContent.vue')['default']
     TwoFAAuthorization: typeof import('./src/components/TwoFA/Authorization.vue')['default']
+    UpstreamCardsUpstreamCards: typeof import('./src/components/UpstreamCards/UpstreamCards.vue')['default']
+    UpstreamDetailModalUpstreamDetailModal: typeof import('./src/components/UpstreamDetailModal/UpstreamDetailModal.vue')['default']
     VPSwitchVPSwitch: typeof import('./src/components/VPSwitch/VPSwitch.vue')['default']
   }
 }

+ 3 - 72
app/src/components/NgxConfigEditor/NgxUpstream.vue

@@ -1,12 +1,7 @@
 <script setup lang="ts">
-import type ReconnectingWebSocket from 'reconnecting-websocket'
-import type { NgxDirective } from '@/api/ngx'
-import type { UpstreamStatus } from '@/api/upstream'
 import { MoreOutlined, PlusOutlined } from '@ant-design/icons-vue'
 import { Modal } from 'ant-design-vue'
-import { throttle } from 'lodash'
-import upstream from '@/api/upstream'
-import { DirectiveEditor, Server, useNgxConfigStore } from '.'
+import { DirectiveEditor, useNgxConfigStore } from '.'
 
 const [modal, ContextHolder] = Modal.useModal()
 
@@ -42,10 +37,6 @@ function removeUpstream(index: number) {
   })
 }
 
-const curUptreamDirectives = computed(() => {
-  return ngxConfig.value.upstreams?.[currentUpstreamIdx.value]?.directives
-})
-
 const open = ref(false)
 const renameIdx = ref(-1)
 const buffer = ref('')
@@ -61,53 +52,6 @@ function renameOK() {
     ngxConfig.value.upstreams[renameIdx.value].name = buffer.value
   open.value = false
 }
-
-const availabilityResult = ref({}) as Ref<Record<string, UpstreamStatus>>
-const websocket = shallowRef<ReconnectingWebSocket | WebSocket>()
-
-function availabilityTest() {
-  const sockets: string[] = []
-  for (const u of ngxConfig.value.upstreams ?? []) {
-    for (const d of u.directives ?? []) {
-      if (d.directive === Server)
-        sockets.push(d.params.split(' ')[0])
-    }
-  }
-
-  if (sockets.length > 0) {
-    websocket.value = upstream.availabilityWebSocket()
-    websocket.value.onopen = () => {
-      websocket.value!.send(JSON.stringify(sockets))
-    }
-    websocket.value.onmessage = (e: MessageEvent) => {
-      availabilityResult.value = JSON.parse(e.data)
-    }
-  }
-}
-
-onMounted(() => {
-  availabilityTest()
-})
-
-onBeforeUnmount(() => {
-  websocket.value?.close()
-})
-
-async function _restartTest() {
-  websocket.value?.close()
-  availabilityTest()
-}
-
-const restartTest = throttle(_restartTest, 5000)
-
-watch(curUptreamDirectives, () => {
-  restartTest()
-}, { deep: true })
-
-function getAvailabilityResult(directive: NgxDirective) {
-  const params = directive.params.split(' ')
-  return availabilityResult.value[params?.[0]]
-}
 </script>
 
 <template>
@@ -139,20 +83,7 @@ function getAvailabilityResult(directive: NgxDirective) {
         </template>
 
         <div class="tab-content">
-          <DirectiveEditor v-model:directives="v.directives">
-            <template #directiveSuffix="{ directive }: {directive: NgxDirective}">
-              <template v-if="directive.directive === Server">
-                <template v-if="getAvailabilityResult(directive)?.online">
-                  <ABadge color="green" />
-                  {{ getAvailabilityResult(directive)?.latency?.toFixed(2) }}ms
-                </template>
-                <template v-else>
-                  <ABadge color="red" />
-                  {{ $gettext('Offline') }}
-                </template>
-              </template>
-            </template>
-          </DirectiveEditor>
+          <DirectiveEditor v-model:directives="v.directives" />
         </div>
       </ATabPane>
 
@@ -207,7 +138,7 @@ function getAvailabilityResult(directive: NgxDirective) {
 <style scoped lang="less">
 .empty-state {
   @apply px-8 text-center;
-  min-height: 400px;
+  min-height: 200px;
   display: flex;
   flex-direction: column;
   justify-content: center;

+ 23 - 242
app/src/components/ProxyTargets/ProxyTargets.vue

@@ -1,8 +1,8 @@
+<!-- Displays proxy targets as tags with upstream status monitoring -->
 <script setup lang="ts">
 import type { ProxyTarget } from '@/api/site'
-import { useNodeAvailabilityStore } from '@/pinia/moudule/nodeAvailability'
-import { useNodeGroupStore } from '@/pinia/moudule/nodeGroupStore'
-import { useProxyAvailabilityStore } from '@/pinia/moudule/proxyAvailability'
+import UpstreamDetailModal from '@/components/UpstreamDetailModal/UpstreamDetailModal.vue'
+import { useUpstreamStatus } from '@/composables/useUpstreamStatus'
 
 interface Props {
   targets: ProxyTarget[]
@@ -11,173 +11,18 @@ interface Props {
 
 const props = defineProps<Props>()
 
-const proxyStore = useProxyAvailabilityStore()
-const nodeStore = useNodeAvailabilityStore()
-const nodeGroupStore = useNodeGroupStore()
-
-// Initialize the stores to start monitoring
-onMounted(() => {
-  proxyStore.startMonitoring()
-  nodeGroupStore.initialize()
-})
-
-onUnmounted(() => {
-  proxyStore.stopMonitoring()
-})
-
-// Check if should show multi-node display based on group configuration
-const shouldShowMultiNodeDisplay = computed(() => {
-  if (!props.envGroupId) {
-    return false
-  }
-
-  const group = nodeGroupStore.getGroupById(props.envGroupId)
-  const testType = group?.upstream_test_type || 'local'
-  return testType === 'remote' || testType === 'mirror'
-})
-
-// eslint-disable-next-line sonarjs/cognitive-complexity
-function getTargetColor(target: ProxyTarget): string {
-  // Check if we should show multi-node display based on group configuration
-  if (shouldShowMultiNodeDisplay.value) {
-    const multiNodeStatus = proxyStore.getMultiNodeStatus(target)
-    const group = nodeGroupStore.getGroupById(props.envGroupId!)
-    const testType = group?.upstream_test_type || 'local'
-
-    // Calculate total nodes based on test type
-    const totalNodes = testType === 'remote'
-      ? (group?.sync_node_ids?.length || 0) // remote: only sync nodes
-      : (group?.sync_node_ids?.length || 0) + 1 // mirror: sync nodes + main node
-
-    let onlineCount = 0
-
-    if (multiNodeStatus) {
-      // Count online nodes from multi-node data
-      onlineCount = Object.values(multiNodeStatus).filter(status => status.online).length
-    }
-
-    // For mirror mode, also include main node status
-    if (testType === 'mirror') {
-      const mainNodeStatus = proxyStore.getAvailabilityResult(target)
-      if (mainNodeStatus && mainNodeStatus.online) {
-        onlineCount++
-      }
-    }
-
-    if (onlineCount === totalNodes)
-      return 'green'
-    if (onlineCount === 0)
-      return 'red'
-    return 'orange' // Partial online
-  }
-
-  // Fallback to single-node display
-  const result = proxyStore.getAvailabilityResult(target)
-  if (!result)
-    return 'default'
-  return result.online ? 'green' : 'red'
-}
-
-function getTargetText(target: ProxyTarget): string {
-  // Check if we should show multi-node display based on group configuration
-  if (shouldShowMultiNodeDisplay.value) {
-    const multiNodeStatus = proxyStore.getMultiNodeStatus(target)
-    const group = nodeGroupStore.getGroupById(props.envGroupId!)
-    const testType = group?.upstream_test_type || 'local'
-
-    // Calculate total nodes based on test type
-    const totalNodes = testType === 'remote'
-      ? (group?.sync_node_ids?.length || 0) // remote: only sync nodes
-      : (group?.sync_node_ids?.length || 0) + 1 // mirror: sync nodes + main node
-
-    let onlineCount = 0
-
-    if (multiNodeStatus) {
-      // Count online nodes from multi-node data
-      onlineCount = Object.values(multiNodeStatus).filter(status => status.online).length
-    }
-
-    // For mirror mode, also include main node status
-    if (testType === 'mirror') {
-      const mainNodeStatus = proxyStore.getAvailabilityResult(target)
-      if (mainNodeStatus && mainNodeStatus.online) {
-        onlineCount++
-      }
-    }
-
-    return `${target.host}:${target.port} (${onlineCount}/${totalNodes})`
-  }
-
-  // Fallback to single-node display
-  const result = proxyStore.getAvailabilityResult(target)
-  if (!result)
-    return `${target.host}:${target.port}`
-
-  if (result.online) {
-    return `${target.host}:${target.port} (${result.latency.toFixed(2)}ms)`
-  }
-  else {
-    return `${target.host}:${target.port}`
-  }
-}
-
-function getTargetTitle(target: ProxyTarget): string {
-  return `${$gettext('Type')}: ${target.type === 'upstream' ? $gettext('Upstream') : $gettext('Proxy Pass')}`
-}
+const envGroupIdRef = computed(() => props.envGroupId)
+const {
+  shouldShowMultiNodeDisplay,
+  getTargetColor,
+  getTargetText,
+  getTargetTitle,
+  proxyStore,
+} = useUpstreamStatus(envGroupIdRef)
 
 const showDetailModal = ref(false)
 const selectedTarget = ref<ProxyTarget | null>(null)
 
-function getNodeName(nodeId: string): string {
-  const node = nodeStore.nodes[Number.parseInt(nodeId)]
-  return node?.name || `Node ${nodeId}`
-}
-
-// Get all node statuses including main node for modal display
-function getAllNodeStatuses(target: ProxyTarget) {
-  const group = nodeGroupStore.getGroupById(props.envGroupId!)
-  const testType = group?.upstream_test_type || 'local'
-  const allStatuses: Array<{ nodeId: string, name: string, status: { online: boolean, latency: number }, isMainNode: boolean }> = []
-
-  // Add main node data first for local and mirror modes
-  if (testType === 'local' || testType === 'mirror') {
-    const mainNodeStatus = proxyStore.getAvailabilityResult(target)
-    if (mainNodeStatus) {
-      allStatuses.push({
-        nodeId: 'main',
-        name: $gettext('Main Node'),
-        status: {
-          online: mainNodeStatus.online,
-          latency: mainNodeStatus.latency,
-        },
-        isMainNode: true,
-      })
-    }
-  }
-
-  // Add all child nodes data (both online and offline)
-  if (group?.sync_node_ids) {
-    const multiNodeStatus = proxyStore.getMultiNodeStatus(target)
-
-    for (const nodeId of group.sync_node_ids) {
-      const nodeIdStr = nodeId.toString()
-      const nodeStatus = multiNodeStatus?.[nodeIdStr]
-
-      allStatuses.push({
-        nodeId: nodeIdStr,
-        name: getNodeName(nodeIdStr),
-        status: nodeStatus || {
-          online: false,
-          latency: 0,
-        },
-        isMainNode: false,
-      })
-    }
-  }
-
-  return allStatuses
-}
-
 function handleTargetClick(target: ProxyTarget) {
   if (shouldShowMultiNodeDisplay.value) {
     selectedTarget.value = target
@@ -190,7 +35,7 @@ function handleTargetClick(target: ProxyTarget) {
   <div v-if="targets.length > 0" class="proxy-targets">
     <ATag
       v-for="target in targets" :key="proxyStore.getTargetKey(target)" :color="getTargetColor(target)"
-      class="proxy-target-tag cursor-pointer" :class="{ clickable: shouldShowMultiNodeDisplay }" :bordered="false"
+      class="proxy-target-tag" :class="{ 'clickable': shouldShowMultiNodeDisplay, 'cursor-pointer': shouldShowMultiNodeDisplay }" :bordered="false"
       @click="handleTargetClick(target)"
     >
       <template #icon>
@@ -202,32 +47,12 @@ function handleTargetClick(target: ProxyTarget) {
       {{ getTargetText(target) }}
     </ATag>
 
-    <!-- Upstream Detail Modal -->
-    <AModal
+    <!-- Detail Modal -->
+    <UpstreamDetailModal
       v-model:open="showDetailModal"
-      :title="selectedTarget ? `${selectedTarget.host}:${selectedTarget.port} - ${$gettext('Node Status')}` : ''"
-      :footer="null" width="600px"
-    >
-      <div v-if="selectedTarget" class="upstream-detail">
-        <div class="node-status-list">
-          <div v-for="nodeInfo in getAllNodeStatuses(selectedTarget)" :key="nodeInfo.nodeId" class="node-status-item">
-            <div class="node-info">
-              <span class="node-name">{{ nodeInfo.name }}</span>
-              <ATag :color="nodeInfo.status.online ? 'green' : 'red'" class="status-tag">
-                {{ nodeInfo.status.online ? $gettext('Online') : $gettext('Offline') }}
-              </ATag>
-              <ATag v-if="nodeInfo.isMainNode" color="blue" class="main-node-tag">
-                {{ $gettext('Main') }}
-              </ATag>
-            </div>
-            <div class="node-latency">
-              <span v-if="nodeInfo.status.online">{{ nodeInfo.status.latency.toFixed(2) }}ms</span>
-              <span v-else class="text-gray-400">{{ $gettext('N/A') }}</span>
-            </div>
-          </div>
-        </div>
-      </div>
-    </AModal>
+      :target="selectedTarget"
+      :env-group-id="envGroupId"
+    />
   </div>
 </template>
 
@@ -238,6 +63,7 @@ function handleTargetClick(target: ProxyTarget) {
     gap: 4px;
     max-width: 100%;
     overflow: hidden;
+    padding: 6px;
   }
 
   .proxy-target-tag {
@@ -255,7 +81,11 @@ function handleTargetClick(target: ProxyTarget) {
       transition: all 0.2s ease;
 
       &:hover {
-        box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.07);
+        box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.07);
+
+        .dark & {
+          box-shadow: 0 0 6px 0 rgba(255, 255, 255, 0.1);
+        }
       }
     }
 
@@ -265,59 +95,10 @@ function handleTargetClick(target: ProxyTarget) {
       height: 12px;
       line-height: 12px;
       text-align: center;
-      background: rgba(255, 255, 255, 0.2);
       border-radius: 2px;
-      margin-right: 4px;
       font-weight: bold;
       font-size: 10px;
       flex-shrink: 0;
     }
   }
-
-  .upstream-detail {
-    .node-status-list {
-      display: flex;
-      flex-direction: column;
-      gap: 12px;
-    }
-
-    .node-status-item {
-      display: flex;
-      justify-content: space-between;
-      align-items: center;
-      padding: 12px;
-      border: 1px solid #e8e9ea;
-      border-radius: 8px;
-      background: #fafafa;
-
-      .node-info {
-        display: flex;
-        align-items: center;
-        gap: 8px;
-
-        .node-name {
-          font-weight: 500;
-          color: #333;
-        }
-
-        .status-tag {
-          font-size: 11px;
-          margin: 0;
-        }
-
-        .main-node-tag {
-          font-size: 10px;
-          margin: 0;
-        }
-      }
-
-      .node-latency {
-        font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
-        font-size: 12px;
-        font-weight: 500;
-        color: #666;
-      }
-
-    }
-  }
 </style>

+ 2 - 2
app/src/components/SyncNodesPreview/SyncNodesPreview.vue

@@ -46,7 +46,7 @@ const allSyncNodeIds = computed(() => {
       </strong>
     </div>
 
-    <div class="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-2">
+    <div class="grid grid-cols-1 sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-2 xl:grid-cols-2 2xl:grid-cols-3 3xl:grid-cols-4 gap-2">
       <NodeCard
         v-for="nodeId in allSyncNodeIds"
         :key="nodeId"
@@ -56,7 +56,7 @@ const allSyncNodeIds = computed(() => {
     </div>
 
     <div v-if="envGroupInfo" class="mt-2 text-xs text-gray-500 dark:text-gray-400">
-      {{ $gettext('* Includes nodes from group "%{groupName}" and manually selected nodes', { groupName: envGroupInfo.name }) }}
+      {{ $gettext('* Includes nodes from group %{groupName} and manually selected nodes', { groupName: envGroupInfo.name }) }}
     </div>
   </div>
 </template>

+ 188 - 0
app/src/components/UpstreamCards/UpstreamCards.vue

@@ -0,0 +1,188 @@
+<!-- Upstream cards component that displays upstream targets as cards instead of tags -->
+<script setup lang="ts">
+import type { ProxyTarget } from '@/api/site'
+import UpstreamDetailModal from '@/components/UpstreamDetailModal/UpstreamDetailModal.vue'
+import { useUpstreamStatus } from '@/composables/useUpstreamStatus'
+
+interface Props {
+  targets: ProxyTarget[]
+  envGroupId?: number
+}
+
+const props = defineProps<Props>()
+
+const envGroupIdRef = computed(() => props.envGroupId)
+const {
+  shouldShowMultiNodeDisplay,
+  getTargetColor,
+  getTargetText,
+  getTargetTitle,
+  proxyStore,
+} = useUpstreamStatus(envGroupIdRef)
+
+const showDetailModal = ref(false)
+const selectedTarget = ref<ProxyTarget | null>(null)
+
+// Handle card click to show modal
+function handleCardClick(target: ProxyTarget) {
+  if (shouldShowMultiNodeDisplay.value) {
+    selectedTarget.value = target
+    showDetailModal.value = true
+  }
+}
+
+// Get card status indicator color
+function getCardStatusColor(target: ProxyTarget): string {
+  const color = getTargetColor(target)
+  switch (color) {
+    case 'green': return '#52c41a'
+    case 'red': return '#ff4d4f'
+    case 'orange': return '#fa8c16'
+    default: return '#d9d9d9'
+  }
+}
+</script>
+
+<template>
+  <div v-if="targets.length > 0" class="upstream-cards">
+    <div class="upstream-header">
+      <h3 class="upstream-title">Upstreams</h3>
+      <span class="upstream-count">{{ targets.length }}</span>
+    </div>
+    <div class="cards-grid">
+      <div
+        v-for="target in targets"
+        :key="proxyStore.getTargetKey(target)"
+        class="upstream-card"
+        :class="{ clickable: shouldShowMultiNodeDisplay }"
+        @click="handleCardClick(target)"
+      >
+        <!-- Card content -->
+        <div class="card-content">
+          <div class="card-info">
+            <ABadge :color="getCardStatusColor(target)" />
+            <span class="card-status-text">{{ getTargetText(target) }}</span>
+            <ATooltip :title="getTargetTitle(target)" placement="bottom">
+              <ATag
+                :color="target.type === 'upstream' ? 'blue' : 'purple'"
+                size="small"
+                class="type-tag"
+              >
+                {{ target.type === 'upstream' ? 'U' : 'P' }}
+              </ATag>
+            </ATooltip>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- Detail Modal -->
+    <UpstreamDetailModal
+      v-model:open="showDetailModal"
+      :target="selectedTarget"
+      :env-group-id="envGroupId"
+    />
+  </div>
+</template>
+
+<style scoped lang="less">
+.upstream-cards {
+  margin-bottom: 24px;
+
+  .upstream-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 16px;
+
+    .upstream-title {
+      margin: 0;
+      font-size: 16px;
+      font-weight: 600;
+      color: #333;
+
+      .dark & {
+        color: #fff;
+      }
+    }
+
+    .upstream-count {
+      display: inline-flex;
+      align-items: center;
+      justify-content: center;
+      min-width: 20px;
+      height: 20px;
+      padding: 0 6px;
+      background-color: #f0f0f0;
+      color: #666;
+      font-size: 12px;
+      font-weight: 500;
+      border-radius: 50%;
+
+      .dark & {
+        background-color: #434343;
+        color: #ccc;
+      }
+    }
+  }
+
+  .cards-grid {
+    display: grid;
+    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+    gap: 16px;
+  }
+
+  .upstream-card {
+    border: 1px solid #e8e9ea;
+    border-radius: 8px;
+    background: #ffffff;
+    transition: all 0.2s ease;
+
+    .dark & {
+      border-color: #434343;
+      background: #1f1f1f;
+    }
+
+    &.clickable {
+      cursor: pointer;
+
+      &:hover {
+        box-shadow: 0 0 8px rgba(0, 0, 0, 0.09);
+
+        .dark & {
+          box-shadow: 0 0 8px rgba(255, 255, 255, 0.1);
+        }
+      }
+    }
+
+    .card-content {
+      padding: 16px;
+
+      .card-info {
+        display: flex;
+        align-items: center;
+        gap: 8px;
+
+        .card-status-text {
+          font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
+          font-size: 12px;
+          color: #666;
+          line-height: 1.4;
+          flex: 1;
+
+          .dark & {
+            color: #999;
+          }
+        }
+
+        .type-tag {
+          margin: 0;
+          font-size: 10px;
+          font-weight: bold;
+          border-radius: 4px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 165 - 0
app/src/components/UpstreamDetailModal/UpstreamDetailModal.vue

@@ -0,0 +1,165 @@
+<!-- Reusable modal component for displaying upstream node status details -->
+<script setup lang="ts">
+import type { ProxyTarget } from '@/api/site'
+import { useUpstreamStatus } from '@/composables/useUpstreamStatus'
+
+interface Props {
+  open: boolean
+  target: ProxyTarget | null
+  envGroupId?: number
+}
+
+const props = defineProps<Props>()
+defineEmits<{
+  'update:open': [value: boolean]
+}>()
+
+const envGroupIdRef = computed(() => props.envGroupId)
+const { getAllNodeStatuses, getStatusSummary } = useUpstreamStatus(envGroupIdRef)
+</script>
+
+<template>
+  <AModal
+    :open="props.open"
+    :title="props.target ? `${props.target.host}:${props.target.port} - ${$gettext('Node Status')}` : ''"
+    :footer="null"
+    width="600px"
+    @update:open="$emit('update:open', $event)"
+  >
+    <div v-if="props.target" class="upstream-detail">
+      <div class="summary-section">
+        <div class="summary-item">
+          <span class="summary-label">{{ $gettext('Online Count') }}</span>
+          <span class="summary-value">{{ getStatusSummary(props.target).onlineCount }}/{{ getStatusSummary(props.target).totalNodes }}</span>
+        </div>
+        <div class="summary-item">
+          <span class="summary-label">{{ $gettext('Average Latency') }}</span>
+          <span class="summary-value">
+            {{ getStatusSummary(props.target).avgLatency > 0 ? `${getStatusSummary(props.target).avgLatency.toFixed(2)}ms` : $gettext('N/A') }}
+          </span>
+        </div>
+      </div>
+
+      <div class="node-status-list">
+        <div v-for="nodeInfo in getAllNodeStatuses(props.target)" :key="nodeInfo.nodeId" class="node-status-item">
+          <div class="node-info">
+            <span class="node-name">{{ nodeInfo.name }}</span>
+            <ATag :color="nodeInfo.status.online ? 'green' : 'red'" class="status-tag">
+              {{ nodeInfo.status.online ? $gettext('Online') : $gettext('Offline') }}
+            </ATag>
+            <ATag v-if="nodeInfo.isMainNode" color="blue" class="main-node-tag">
+              {{ $gettext('Main Node') }}
+            </ATag>
+          </div>
+          <div class="node-latency">
+            <span v-if="nodeInfo.status.online">{{ nodeInfo.status.latency.toFixed(2) }}ms</span>
+            <span v-else class="text-gray-400">{{ $gettext('N/A') }}</span>
+          </div>
+        </div>
+      </div>
+    </div>
+  </AModal>
+</template>
+
+<style scoped lang="less">
+.upstream-detail {
+  .summary-section {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 16px;
+    margin-bottom: 16px;
+    border-radius: 8px;
+    background: #f8f9fa;
+
+    .dark & {
+      background: #2c2c2c;
+    }
+
+    .summary-item {
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      gap: 4px;
+
+      .summary-label {
+        font-size: 12px;
+        color: #666;
+        font-weight: 500;
+
+        .dark & {
+          color: #999;
+        }
+      }
+
+      .summary-value {
+        font-size: 16px;
+        font-weight: 600;
+        color: #333;
+        font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
+
+        .dark & {
+          color: #e6e6e6;
+        }
+      }
+    }
+  }
+
+  .node-status-list {
+    display: flex;
+    flex-direction: column;
+    gap: 12px;
+  }
+
+  .node-status-item {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 12px;
+    border: 1px solid #e8e9ea;
+    border-radius: 8px;
+    background: #fafafa;
+
+    .dark & {
+      border-color: #434343;
+      background: #2c2c2c;
+    }
+
+    .node-info {
+      display: flex;
+      align-items: center;
+      gap: 8px;
+
+      .node-name {
+        font-weight: 500;
+        color: #333;
+
+        .dark & {
+          color: #e6e6e6;
+        }
+      }
+
+      .status-tag {
+        font-size: 11px;
+        margin: 0;
+      }
+
+      .main-node-tag {
+        font-size: 10px;
+        margin: 0;
+      }
+    }
+
+    .node-latency {
+      font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
+      font-size: 12px;
+      font-weight: 500;
+      color: #666;
+
+      .dark & {
+        color: #999;
+      }
+    }
+  }
+}
+</style>

+ 201 - 0
app/src/composables/useUpstreamStatus.ts

@@ -0,0 +1,201 @@
+// Composable for managing upstream status logic shared between components
+import type { EnvGroup } from '@/api/env_group'
+import type { ProxyTarget } from '@/api/site'
+import { useNodeAvailabilityStore } from '@/pinia/moudule/nodeAvailability'
+import { useNodeGroupStore } from '@/pinia/moudule/nodeGroupStore'
+import { useProxyAvailabilityStore } from '@/pinia/moudule/proxyAvailability'
+
+export function useUpstreamStatus(envGroupId?: Ref<number | undefined>) {
+  const proxyStore = useProxyAvailabilityStore()
+  const nodeStore = useNodeAvailabilityStore()
+  const nodeGroupStore = useNodeGroupStore()
+
+  // Initialize stores on mount
+  onMounted(() => {
+    proxyStore.startMonitoring()
+    nodeGroupStore.initialize()
+  })
+
+  onUnmounted(() => {
+    proxyStore.stopMonitoring()
+  })
+
+  // Check if should show multi-node display based on group configuration
+  const shouldShowMultiNodeDisplay = computed(() => {
+    if (!envGroupId?.value) {
+      return false
+    }
+
+    const group = nodeGroupStore.getGroupById(envGroupId.value)
+    const testType = group?.upstream_test_type || 'local'
+    return testType === 'remote' || testType === 'mirror'
+  })
+
+  // Get target color based on online status
+  function getTargetColor(target: ProxyTarget): string {
+    if (!shouldShowMultiNodeDisplay.value) {
+      // Fallback to single-node display
+      const result = proxyStore.getAvailabilityResult(target)
+      if (!result)
+        return 'default'
+      return result.online ? 'green' : 'red'
+    }
+
+    return getMultiNodeColor(target)
+  }
+
+  // Helper function to get color for multi-node display
+  function getMultiNodeColor(target: ProxyTarget): string {
+    const group = nodeGroupStore.getGroupById(envGroupId!.value!)
+    const testType = group?.upstream_test_type || 'local'
+    const totalNodes = calculateTotalNodes(group, testType)
+    const onlineCount = calculateOnlineCount(target, group, testType)
+
+    if (onlineCount === totalNodes)
+      return 'green'
+    if (onlineCount === 0)
+      return 'red'
+    return 'orange' // Partial online
+  }
+
+  // Calculate total nodes based on test type
+  function calculateTotalNodes(group: EnvGroup | undefined, testType: string): number {
+    return testType === 'remote'
+      ? (group?.sync_node_ids?.length || 0) // remote: only sync nodes
+      : (group?.sync_node_ids?.length || 0) + 1 // mirror: sync nodes + main node
+  }
+
+  // Calculate online nodes count
+  function calculateOnlineCount(target: ProxyTarget, group: EnvGroup | undefined, testType: string): number {
+    const multiNodeStatus = proxyStore.getMultiNodeStatus(target)
+    let onlineCount = 0
+
+    if (multiNodeStatus) {
+      // Count online nodes from multi-node data
+      onlineCount = Object.values(multiNodeStatus).filter(status => status.online).length
+    }
+
+    // For mirror mode, also include main node status
+    if (testType === 'mirror') {
+      const mainNodeStatus = proxyStore.getAvailabilityResult(target)
+      if (mainNodeStatus && mainNodeStatus.online) {
+        onlineCount++
+      }
+    }
+
+    return onlineCount
+  }
+
+  // Get target display text
+  function getTargetText(target: ProxyTarget): string {
+    if (!shouldShowMultiNodeDisplay.value) {
+      // Fallback to single-node display
+      const result = proxyStore.getAvailabilityResult(target)
+      if (!result)
+        return `${target.host}:${target.port}`
+
+      if (result.online) {
+        return `${target.host}:${target.port} (${result.latency.toFixed(2)}ms)`
+      }
+      else {
+        return `${target.host}:${target.port}`
+      }
+    }
+
+    const group = nodeGroupStore.getGroupById(envGroupId!.value!)
+    const testType = group?.upstream_test_type || 'local'
+    const totalNodes = calculateTotalNodes(group, testType)
+    const onlineCount = calculateOnlineCount(target, group, testType)
+
+    return `${target.host}:${target.port} (${onlineCount}/${totalNodes})`
+  }
+
+  // Get target tooltip title
+  function getTargetTitle(target: ProxyTarget): string {
+    return `${$gettext('Type')}: ${target.type === 'upstream' ? $gettext('Upstream') : $gettext('Proxy Pass')}`
+  }
+
+  // Get node name by ID
+  function getNodeName(nodeId: string): string {
+    const node = nodeStore.nodes[Number.parseInt(nodeId)]
+    return node?.name || `Node ${nodeId}`
+  }
+
+  // Get all node statuses for modal display
+  function getAllNodeStatuses(target: ProxyTarget) {
+    if (!envGroupId?.value)
+      return []
+
+    const group = nodeGroupStore.getGroupById(envGroupId.value)
+    const testType = group?.upstream_test_type || 'local'
+    const allStatuses: Array<{ nodeId: string, name: string, status: { online: boolean, latency: number }, isMainNode: boolean }> = []
+
+    // Add main node data first for local and mirror modes
+    if (testType === 'local' || testType === 'mirror') {
+      const mainNodeStatus = proxyStore.getAvailabilityResult(target)
+      if (mainNodeStatus) {
+        allStatuses.push({
+          nodeId: 'main',
+          name: $gettext('Main Node'),
+          status: {
+            online: mainNodeStatus.online,
+            latency: mainNodeStatus.latency,
+          },
+          isMainNode: true,
+        })
+      }
+    }
+
+    // Add all child nodes data (both online and offline)
+    if (group?.sync_node_ids) {
+      const multiNodeStatus = proxyStore.getMultiNodeStatus(target)
+
+      for (const nodeId of group.sync_node_ids) {
+        const nodeIdStr = nodeId.toString()
+        const nodeStatus = multiNodeStatus?.[nodeIdStr]
+
+        allStatuses.push({
+          nodeId: nodeIdStr,
+          name: getNodeName(nodeIdStr),
+          status: nodeStatus || {
+            online: false,
+            latency: 0,
+          },
+          isMainNode: false,
+        })
+      }
+    }
+
+    return allStatuses
+  }
+
+  // Get status summary for modal
+  function getStatusSummary(target: ProxyTarget) {
+    const allStatuses = getAllNodeStatuses(target)
+    const onlineNodes = allStatuses.filter(node => node.status.online)
+    const totalNodes = allStatuses.length
+    const onlineCount = onlineNodes.length
+
+    let avgLatency = 0
+    if (onlineCount > 0) {
+      avgLatency = onlineNodes.reduce((sum, node) => sum + node.status.latency, 0) / onlineCount
+    }
+
+    return {
+      onlineCount,
+      totalNodes,
+      avgLatency,
+    }
+  }
+
+  return {
+    shouldShowMultiNodeDisplay,
+    getTargetColor,
+    getTargetText,
+    getTargetTitle,
+    getNodeName,
+    getAllNodeStatuses,
+    getStatusSummary,
+    proxyStore,
+  }
+}

+ 64 - 52
app/src/language/ar/app.po

@@ -101,8 +101,8 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr "[Nginx UI] كتابة الشهادة على القرص"
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid "* Includes nodes from group \"%{groupName}\" and manually selected nodes"
-msgstr "* يتضمن العقد من مجموعة \"%{groupName}\" والعقد المحددة يدويًا"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
+msgstr "* يتضمن عقدًا من مجموعة %{groupName} وعقدًا مختارة يدويًا"
 
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
@@ -163,7 +163,7 @@ msgid "Actual worker to configured ratio"
 msgstr "نسبة العامل الفعلي إلى المُهيأ"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166 src/language/curd.ts:19
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97 src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -206,7 +206,7 @@ msgstr "أضف موقع"
 msgid "Add Stream"
 msgstr "أضف Stream"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr "إضافة upstream"
 
@@ -218,8 +218,8 @@ msgstr "تمت الإضافة بنجاح"
 msgid "Additional"
 msgstr "إضافي"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr "الوضع المتقدم"
 
@@ -427,12 +427,16 @@ msgstr "AutoCert قيد التشغيل..."
 msgid "Automatic Restart"
 msgstr "إعادة التشغيل التلقائي"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "متوسط زمن الوصول"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr "رجوع"
 
@@ -528,8 +532,8 @@ msgstr "المعلومات الأساسية"
 msgid "Basic"
 msgstr "أساسي"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr "الوضع الأساسي"
 
@@ -618,7 +622,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37 src/language/curd.ts:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32 src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1301,7 +1305,7 @@ msgid "Define shared memory zone name and size, e.g. proxy_cache:10m"
 msgstr "حدد اسم منطقة الذاكرة المشتركة والحجم، على سبيل المثال proxy_cache:10m"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134 src/language/curd.ts:9
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78 src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
@@ -1413,6 +1417,10 @@ msgstr "تفاصيل"
 msgid "Dev"
 msgstr "تطوير"
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "التطوير"
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr "وضع التطوير"
@@ -1515,9 +1523,9 @@ msgstr "تم تعطيل الدفق %{name} من %{node} بنجاح"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146 src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr "معطل"
@@ -1570,7 +1578,7 @@ msgstr "هل تريد تفعيل TLS؟"
 msgid "Do you want to remove this server?"
 msgstr "هل تريد إزالة هذا الخادم؟"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr "هل تريد إزالة هذا المصدر؟"
 
@@ -1625,7 +1633,7 @@ msgstr "قم بإسقاط ملف الشهادة هنا"
 msgid "Drop private key file here"
 msgstr "قم بإسقاط ملف المفتاح الخاص هنا"
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr "تمكين وضع التشغيل التجريبي"
@@ -1663,8 +1671,8 @@ msgstr "على سبيل المثال، 0 0 * * * (يوميًا عند منتصف
 msgid "Edit"
 msgstr "تعديل"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr "تعديل %{n}"
 
@@ -1797,10 +1805,10 @@ msgstr "تفعيل TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr "مفعل"
@@ -2408,7 +2416,7 @@ msgstr "فشل الحصول على البيانات"
 msgid "Get dns credential error: {0}"
 msgstr "خطأ في الحصول على بيانات اعتماد DNS: {0}"
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr "خطأ في الحصول على معلومات الإصدار"
@@ -2450,8 +2458,8 @@ msgid "Higher value means better connection reuse"
 msgstr "القيمة الأعلى تعني إعادة استخدام أفضل للاتصال"
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr "السجل"
 
@@ -2913,16 +2921,13 @@ msgstr ""
 "يدويًا. سيقوم مجدول المهام crontab الخاص بواجهة Nginx UI بتنفيذ أمر تدوير "
 "السجلات في الفاصل الزمني الذي تحدده بالدقائق."
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr "رئيسي"
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr "العقدة الرئيسية"
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr "صيانة"
@@ -3124,11 +3129,12 @@ msgstr "يوميًا في اليوم %{day} الساعة %{time}"
 msgid "Multi-line Directive"
 msgstr "توجيه متعدد الأسطر"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr "غير متاح"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3265,8 +3271,8 @@ msgstr "ملف إدخال تكوين Nginx موجود"
 msgid "Nginx configuration has been restored"
 msgstr "تمت استعادة تكوين Nginx"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr "خطأ في تحليل تكوين Nginx"
 
@@ -3447,7 +3453,7 @@ msgstr "لم يتم تحديد أي سجلات"
 msgid "No servers configured"
 msgstr "لا توجد خوادم مُهيأة"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr "لا توجد مصادر علوية مهيأة"
 
@@ -3474,7 +3480,7 @@ msgstr "اسم العقدة"
 msgid "Node Secret"
 msgstr "سر العقدة"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr "حالة العقدة"
 
@@ -3579,10 +3585,9 @@ msgid "Official Document"
 msgstr "الوثيقة الرسمية"
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3590,7 +3595,7 @@ msgstr "غير متصل"
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
@@ -3617,12 +3622,16 @@ msgstr "بمجرد اكتمال التحقق، سيتم إزالة السجلا
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr "متصل"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "عدد المتصلين"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr "يُسمح فقط بملفات zip"
@@ -3762,7 +3771,7 @@ msgstr "مورد الحمولة فارغ"
 msgid "Pending"
 msgstr "قيد الانتظار"
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr "نفذ"
 
@@ -3966,7 +3975,7 @@ msgid "Post-sync Action"
 msgstr "إجراء ما بعد المزامنة"
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgstr "ما قبل الإصدار"
@@ -4037,7 +4046,7 @@ msgstr "لم يتم العثور على المزود: {0}"
 msgid "Proxy"
 msgstr "وكيل"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr "مرور الوكيل"
 
@@ -4200,7 +4209,7 @@ msgstr "إزالة بنجاح"
 msgid "Removed successfully"
 msgstr "تمت الإزالة بنجاح"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4556,9 +4565,9 @@ msgstr "السبت"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr "حفظ"
 
@@ -4616,7 +4625,7 @@ msgstr "تم الحفظ بنجاح"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr "تم الحفظ بنجاح"
@@ -4924,7 +4933,7 @@ msgid "SSO Login"
 msgstr "تسجيل الدخول عبر SSO"
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgstr "مستقر"
@@ -5422,7 +5431,7 @@ msgstr ""
 "سيؤدي هذا إلى استعادة ملفات التكوين وقاعدة البيانات. سيعاد تشغيل واجهة "
 "Nginx بعد اكتمال الاستعادة."
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr "سيتم ترقية أو إعادة تثبيت Nginx UI على %{nodeNames} إلى %{version}."
 
@@ -5570,7 +5579,7 @@ msgstr "الثلاثاء"
 msgid "Two-factor authentication required"
 msgstr "يتطلب المصادقة الثنائية"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5651,11 +5660,11 @@ msgstr "تحميل الملفات"
 msgid "Upload Folders"
 msgstr "تحميل المجلدات"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr "أعلى التيار"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr "اسم المنبع"
 
@@ -5748,7 +5757,7 @@ msgstr "عرض"
 msgid "View all notifications"
 msgstr "عرض جميع التنبيهات"
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr "عرض على GitHub"
@@ -5965,6 +5974,9 @@ msgstr "رموزك القديمة لن تعمل بعد الآن."
 msgid "Your passkeys"
 msgstr "مفاتيح المرور الخاصة بك"
 
+#~ msgid "Main"
+#~ msgstr "رئيسي"
+
 #~ msgid "Select an action after sync"
 #~ msgstr "اختر إجراءً بعد المزامنة"
 

+ 64 - 54
app/src/language/de_DE/app.po

@@ -100,10 +100,8 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr "[Nginx UI] Zertifikat wird auf die Festplatte geschrieben"
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid "* Includes nodes from group \"%{groupName}\" and manually selected nodes"
-msgstr ""
-"* Enthält Knoten aus der Gruppe \"%{groupName}\" und manuell ausgewählte "
-"Knoten"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
+msgstr "* Enthält Knoten aus der Gruppe %{groupName} und manuell ausgewählte Knoten"
 
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
@@ -164,7 +162,7 @@ msgid "Actual worker to configured ratio"
 msgstr "Tatsächliches Verhältnis von Arbeitern zu konfigurierten"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166 src/language/curd.ts:19
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97 src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -207,7 +205,7 @@ msgstr "Seite hinzufügen"
 msgid "Add Stream"
 msgstr "Stream hinzufügen"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr "Upstream hinzufügen"
 
@@ -219,8 +217,8 @@ msgstr "Erfolgreich hinzugefügt"
 msgid "Additional"
 msgstr "Zusätzlich"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr "Erweiterter Modus"
 
@@ -434,12 +432,16 @@ msgstr "AutoCert wird ausgeführt..."
 msgid "Automatic Restart"
 msgstr "Automatischer Neustart"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "Durchschnittliche Latenz"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr "Zurück"
 
@@ -543,8 +545,8 @@ msgstr "Basisinformationen"
 msgid "Basic"
 msgstr "Basis"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr "Basic-Modus"
 
@@ -635,7 +637,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37 src/language/curd.ts:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32 src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1321,7 +1323,7 @@ msgstr ""
 "proxy_cache:10m"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134 src/language/curd.ts:9
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78 src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
@@ -1433,6 +1435,10 @@ msgstr "Details"
 msgid "Dev"
 msgstr "Entwicklung"
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Entwicklung"
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr "Entwicklungsmodus"
@@ -1535,9 +1541,9 @@ msgstr "Stream %{name} von %{node} erfolgreich deaktiviert"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146 src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr "Deaktiviert"
@@ -1592,7 +1598,7 @@ msgstr "Möchten Sie TLS aktivieren?"
 msgid "Do you want to remove this server?"
 msgstr "Möchten Sie diesen Server entfernen?"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr "Möchten Sie diesen Upstream entfernen?"
 
@@ -1644,7 +1650,7 @@ msgstr "Zertifikatsdatei hier ablegen"
 msgid "Drop private key file here"
 msgstr "Private Schlüsseldatei hier ablegen"
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr "Dry-Run-Modus aktiviert"
@@ -1683,8 +1689,8 @@ msgstr "z. B. 0 0 * * * (täglich um Mitternacht)"
 msgid "Edit"
 msgstr "Bearbeiten"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr "Bearbeiten %{n}"
 
@@ -1817,10 +1823,10 @@ msgstr "TOTP aktivieren"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr "Aktiviert"
@@ -2432,7 +2438,7 @@ msgstr "Datenabruf fehlgeschlagen"
 msgid "Get dns credential error: {0}"
 msgstr "Fehler beim Abrufen der DNS-Anmeldeinformationen: {0}"
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr "Fehler beim Abrufen der Release-Informationen"
@@ -2474,8 +2480,8 @@ msgid "Higher value means better connection reuse"
 msgstr "Ein höherer Wert bedeutet eine bessere Wiederverwendung der Verbindung"
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr "Verlauf"
 
@@ -2942,16 +2948,13 @@ msgstr ""
 "aktivieren. Der Crontab-Aufgabenplaner von Nginx UI führt den "
 "Logrotate-Befehl in dem von dir in Minuten festgelegten Intervall aus."
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr "Haupt"
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr "Hauptknoten"
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr "Wartung"
@@ -3154,11 +3157,12 @@ msgstr "Monatlich am %{day}. um %{time}"
 msgid "Multi-line Directive"
 msgstr "Mehrzeilige Direktive"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr "N/V"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3295,8 +3299,8 @@ msgstr "Nginx-Konfigurationsdatei existiert"
 msgid "Nginx configuration has been restored"
 msgstr "Die Nginx-Konfiguration wurde wiederhergestellt"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx-Konfigurationsanalysefehler"
 
@@ -3479,7 +3483,7 @@ msgstr "Keine Datensätze ausgewählt"
 msgid "No servers configured"
 msgstr "Keine Server konfiguriert"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr "Keine Upstreams konfiguriert"
 
@@ -3506,7 +3510,7 @@ msgstr "Knotenname"
 msgid "Node Secret"
 msgstr "Node-Secret"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr "Knotenstatus"
 
@@ -3614,10 +3618,9 @@ msgid "Official Document"
 msgstr "Offizielle Dokumentation"
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3625,7 +3628,7 @@ msgstr "Offline"
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
@@ -3652,12 +3655,16 @@ msgstr "Sobaöd die Überprüfung abgeschlossen ist, werden die Einträge entfer
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr "Online"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Anzahl Online"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr "Nur ZIP-Dateien sind erlaubt"
@@ -3797,7 +3804,7 @@ msgstr "Die Nutzlast-Ressource ist null"
 msgid "Pending"
 msgstr "Ausstehend"
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr "Ausführen"
 
@@ -4016,7 +4023,7 @@ msgid "Post-sync Action"
 msgstr "Aktion nach der Synchronisierung"
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgstr "Vorabversion"
@@ -4088,7 +4095,7 @@ msgstr "Anbieter nicht gefunden: {0}"
 msgid "Proxy"
 msgstr "Proxy"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr "Proxy-Weiterleitung"
 
@@ -4256,7 +4263,7 @@ msgstr "Erfolgreich entfernt"
 msgid "Removed successfully"
 msgstr "Erfolgreich entfernt"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4615,9 +4622,9 @@ msgstr "Samstag"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr "Speichern"
 
@@ -4675,7 +4682,7 @@ msgstr "Erfolgreich gespeichert"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr "Speichern erfolgreich"
@@ -4991,7 +4998,7 @@ msgid "SSO Login"
 msgstr "SSO-Anmeldung"
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgstr "Stabil"
@@ -5505,7 +5512,7 @@ msgstr ""
 "Dadurch werden Konfigurationsdateien und Datenbank wiederhergestellt. Die "
 "Nginx UI wird nach Abschluss der Wiederherstellung neu gestartet."
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 "Dies wird das Nginx UI auf %{nodeNames} auf %{version} aktualisieren oder "
@@ -5658,7 +5665,7 @@ msgstr "Dienstag"
 msgid "Two-factor authentication required"
 msgstr "Zwei-Faktor-Authentifizierung erforderlich"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5739,11 +5746,11 @@ msgstr "Dateien hochladen"
 msgid "Upload Folders"
 msgstr "Ordner hochladen"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr "Upstream"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr "Upstream-Name"
 
@@ -5836,7 +5843,7 @@ msgstr "Anzeigen"
 msgid "View all notifications"
 msgstr "Alle Benachrichtigungen anzeigen"
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr "Auf GitHub anzeigen"
@@ -6067,6 +6074,9 @@ msgstr "Ihre alten Codes funktionieren nicht mehr."
 msgid "Your passkeys"
 msgstr "Deine Passkeys"
 
+#~ msgid "Main"
+#~ msgstr "Haupt"
+
 #~ msgid "Select an action after sync"
 #~ msgstr "Aktion nach der Synchronisierung auswählen"
 

+ 60 - 52
app/src/language/en/app.po

@@ -84,8 +84,7 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr ""
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid ""
-"* Includes nodes from group \"%{groupName}\" and manually selected nodes"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
 msgstr ""
 
 #: src/views/user/userColumns.tsx:30
@@ -147,7 +146,7 @@ msgid "Actual worker to configured ratio"
 msgstr ""
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166 src/language/curd.ts:19
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97 src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -190,7 +189,7 @@ msgstr ""
 msgid "Add Stream"
 msgstr ""
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr ""
 
@@ -202,8 +201,8 @@ msgstr ""
 msgid "Additional"
 msgstr ""
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr ""
 
@@ -409,12 +408,16 @@ msgstr ""
 msgid "Automatic Restart"
 msgstr ""
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr ""
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr ""
 
@@ -509,8 +512,8 @@ msgstr ""
 msgid "Basic"
 msgstr ""
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr ""
 
@@ -597,7 +600,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37 src/language/curd.ts:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32 src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1239,7 +1242,7 @@ msgid "Define shared memory zone name and size, e.g. proxy_cache:10m"
 msgstr ""
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134 src/language/curd.ts:9
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78 src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
@@ -1351,6 +1354,10 @@ msgstr ""
 msgid "Dev"
 msgstr ""
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr ""
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr ""
@@ -1453,9 +1460,9 @@ msgstr ""
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146 src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr ""
@@ -1508,7 +1515,7 @@ msgstr ""
 msgid "Do you want to remove this server?"
 msgstr ""
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr ""
 
@@ -1559,7 +1566,7 @@ msgstr ""
 msgid "Drop private key file here"
 msgstr ""
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr ""
@@ -1595,8 +1602,8 @@ msgstr ""
 msgid "Edit"
 msgstr ""
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr ""
 
@@ -1729,10 +1736,10 @@ msgstr ""
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr ""
@@ -2340,7 +2347,7 @@ msgstr ""
 msgid "Get dns credential error: {0}"
 msgstr ""
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr ""
@@ -2382,8 +2389,8 @@ msgid "Higher value means better connection reuse"
 msgstr ""
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr ""
 
@@ -2826,16 +2833,13 @@ msgid ""
 "minutes."
 msgstr ""
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr ""
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr ""
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr ""
@@ -3035,11 +3039,12 @@ msgstr ""
 msgid "Multi-line Directive"
 msgstr ""
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr ""
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3176,8 +3181,8 @@ msgstr ""
 msgid "Nginx configuration has been restored"
 msgstr ""
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr ""
 
@@ -3358,7 +3363,7 @@ msgstr ""
 msgid "No servers configured"
 msgstr ""
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr ""
 
@@ -3385,7 +3390,7 @@ msgstr ""
 msgid "Node Secret"
 msgstr ""
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr ""
 
@@ -3486,10 +3491,9 @@ msgid "Official Document"
 msgstr ""
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3497,7 +3501,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
@@ -3524,12 +3528,16 @@ msgstr ""
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr ""
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr ""
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr ""
@@ -3666,7 +3674,7 @@ msgstr ""
 msgid "Pending"
 msgstr ""
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr ""
 
@@ -3867,7 +3875,7 @@ msgid "Post-sync Action"
 msgstr ""
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgstr ""
@@ -3935,7 +3943,7 @@ msgstr ""
 msgid "Proxy"
 msgstr ""
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr ""
 
@@ -4096,7 +4104,7 @@ msgstr ""
 msgid "Removed successfully"
 msgstr ""
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4448,9 +4456,9 @@ msgstr ""
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr ""
 
@@ -4508,7 +4516,7 @@ msgstr ""
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr ""
@@ -4810,7 +4818,7 @@ msgid "SSO Login"
 msgstr ""
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgstr ""
@@ -5274,7 +5282,7 @@ msgid ""
 "after the restoration is complete."
 msgstr ""
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid ""
 "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
@@ -5409,7 +5417,7 @@ msgstr ""
 msgid "Two-factor authentication required"
 msgstr ""
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5490,11 +5498,11 @@ msgstr ""
 msgid "Upload Folders"
 msgstr ""
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr ""
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr ""
 
@@ -5587,7 +5595,7 @@ msgstr ""
 msgid "View all notifications"
 msgstr ""
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr ""

+ 64 - 52
app/src/language/es/app.po

@@ -107,8 +107,8 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr "[Nginx UI] Escribiendo certificado en el disco"
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid "* Includes nodes from group \"%{groupName}\" and manually selected nodes"
-msgstr "* Incluye nodos del grupo \"%{groupName}\" y nodos seleccionados manualmente"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
+msgstr "* Incluye nodos del grupo %{groupName} y nodos seleccionados manualmente"
 
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
@@ -169,7 +169,7 @@ msgid "Actual worker to configured ratio"
 msgstr "Proporción real de trabajadores a configurados"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166 src/language/curd.ts:19
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97 src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -212,7 +212,7 @@ msgstr "Agregar Sitio"
 msgid "Add Stream"
 msgstr "Agregar Stream"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr "Agregar upstream"
 
@@ -224,8 +224,8 @@ msgstr "Agregado exitoso"
 msgid "Additional"
 msgstr "Adicional"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr "Modo avanzado"
 
@@ -439,12 +439,16 @@ msgstr "AutoCert se está ejecutando..."
 msgid "Automatic Restart"
 msgstr "Reinicio automático"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "Latencia promedio"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr "Atrás"
 
@@ -548,8 +552,8 @@ msgstr "Información general"
 msgid "Basic"
 msgstr "Básico"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr "Modo Básico"
 
@@ -642,7 +646,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37 src/language/curd.ts:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32 src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1330,7 +1334,7 @@ msgstr ""
 "proxy_cache:10m"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134 src/language/curd.ts:9
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78 src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
@@ -1442,6 +1446,10 @@ msgstr "Detalles"
 msgid "Dev"
 msgstr "Dev"
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Desarrollo"
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr "Modo de desarrollo"
@@ -1544,9 +1552,9 @@ msgstr "Deshabilitar el flujo %{name} desde %{node} con éxito"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146 src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr "Desactivado"
@@ -1599,7 +1607,7 @@ msgstr "¿Quieres habilitar TLS?"
 msgid "Do you want to remove this server?"
 msgstr "¿Quieres eliminar este servidor?"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr "¿Quieres eliminar esta transmisión?"
 
@@ -1651,7 +1659,7 @@ msgstr "Suelta el archivo de certificado aquí"
 msgid "Drop private key file here"
 msgstr "Suelta el archivo de clave privada aquí"
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr "Modo de ejecución de prueba habilitado"
@@ -1690,8 +1698,8 @@ msgstr "p. ej., 0 0 * * * (diariamente a medianoche)"
 msgid "Edit"
 msgstr "Editar"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr "Editar %{n}"
 
@@ -1824,10 +1832,10 @@ msgstr "Habilitar TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr "Habilitado"
@@ -2443,7 +2451,7 @@ msgstr "Error al obtener los datos"
 msgid "Get dns credential error: {0}"
 msgstr "Error al obtener las credenciales DNS: {0}"
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr "Obtener error de información de versión"
@@ -2485,8 +2493,8 @@ msgid "Higher value means better connection reuse"
 msgstr "Un valor más alto significa una mejor reutilización de la conexión"
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr "Historial"
 
@@ -2951,16 +2959,13 @@ msgstr ""
 "de Nginx UI ejecutará el comando logrotate en el intervalo que establezca "
 "en minutos."
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr "Principal"
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr "Nodo principal"
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr "Mantenimiento"
@@ -3162,11 +3167,12 @@ msgstr "Mensualmente el día %{day} a las %{time}"
 msgid "Multi-line Directive"
 msgstr "Directiva multilínea"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr "N/A"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3303,8 +3309,8 @@ msgstr "El archivo de entrada de configuración de Nginx existe"
 msgid "Nginx configuration has been restored"
 msgstr "La configuración de Nginx ha sido restaurada"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr "Error de análisis de configuración de Nginx"
 
@@ -3487,7 +3493,7 @@ msgstr "No se han seleccionado registros"
 msgid "No servers configured"
 msgstr "No hay servidores configurados"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr "No hay upstreams configurados"
 
@@ -3514,7 +3520,7 @@ msgstr "Nombre del nodo"
 msgid "Node Secret"
 msgstr "Secreto del nodo"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr "Estado del nodo"
 
@@ -3622,10 +3628,9 @@ msgid "Official Document"
 msgstr "Documentación oficial"
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3633,7 +3638,7 @@ msgstr "Desconectado"
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
@@ -3660,12 +3665,16 @@ msgstr "Una vez que se complete la verificación, los registros se eliminarán."
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr "En línea"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Recuento en línea"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr "Solo se permiten archivos ZIP"
@@ -3806,7 +3815,7 @@ msgstr "El recurso de carga útil es nulo"
 msgid "Pending"
 msgstr "Pendiente"
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr "Realizar"
 
@@ -4027,7 +4036,7 @@ msgid "Post-sync Action"
 msgstr "Acción posterior a la sincronización"
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgstr "Prelanzamiento"
@@ -4099,7 +4108,7 @@ msgstr "Proveedor no encontrado: {0}"
 msgid "Proxy"
 msgstr "Proxy"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr "Pase de Proxy"
 
@@ -4265,7 +4274,7 @@ msgstr "Eliminado con éxito"
 msgid "Removed successfully"
 msgstr "Eliminado con éxito"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4622,9 +4631,9 @@ msgstr "Sábado"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr "Guardar"
 
@@ -4682,7 +4691,7 @@ msgstr "Guardado con éxito"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr "Guardado con éxito"
@@ -5000,7 +5009,7 @@ msgid "SSO Login"
 msgstr "Acceso SSO"
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgstr "Estable"
@@ -5510,7 +5519,7 @@ msgstr ""
 "Esto restaurará los archivos de configuración y la base de datos. La "
 "interfaz de Nginx se reiniciará una vez completada la restauración."
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 "Esto actualizará o reinstalará la interfaz de usuario de Nginx en "
@@ -5664,7 +5673,7 @@ msgstr "Martes"
 msgid "Two-factor authentication required"
 msgstr "Se requiere autenticación de dos factores"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5745,11 +5754,11 @@ msgstr "Subir archivos"
 msgid "Upload Folders"
 msgstr "Subir carpetas"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr "Aguas arriba"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr "Nombre de la Transmisión"
 
@@ -5842,7 +5851,7 @@ msgstr "Ver"
 msgid "View all notifications"
 msgstr "Ver todas las notificaciones"
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr "Ver en GitHub"
@@ -6070,6 +6079,9 @@ msgstr "Tus códigos antiguos ya no funcionarán."
 msgid "Your passkeys"
 msgstr "Sus llaves de acceso"
 
+#~ msgid "Main"
+#~ msgstr "Principal"
+
 #~ msgid "Select an action after sync"
 #~ msgstr "Seleccionar una acción después de sincronizar"
 

+ 64 - 52
app/src/language/fr_FR/app.po

@@ -105,9 +105,9 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr "[Nginx UI] Écriture du certificat sur le disque"
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid "* Includes nodes from group \"%{groupName}\" and manually selected nodes"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
 msgstr ""
-"* Inclut les nœuds du groupe \"%{groupName}\" et les nœuds sélectionnés "
+"* Inclut les nœuds du groupe %{groupName} et les nœuds sélectionnés "
 "manuellement"
 
 #: src/views/user/userColumns.tsx:30
@@ -169,7 +169,7 @@ msgid "Actual worker to configured ratio"
 msgstr "Ratio réel des travailleurs par rapport à la configuration"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166 src/language/curd.ts:19
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97 src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -212,7 +212,7 @@ msgstr "Ajouter un site"
 msgid "Add Stream"
 msgstr "Ajouter un flux"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr "Ajouter un upstream"
 
@@ -224,8 +224,8 @@ msgstr "Ajouté avec succès"
 msgid "Additional"
 msgstr "Supplémentaire"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr "Mode avancé"
 
@@ -439,12 +439,16 @@ msgstr "AutoCert est en cours d'exécution..."
 msgid "Automatic Restart"
 msgstr "Redémarrage automatique"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "Latence moyenne"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr "Retour"
 
@@ -548,8 +552,8 @@ msgstr "Information générale"
 msgid "Basic"
 msgstr "Basique"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr "Mode simple"
 
@@ -639,7 +643,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37 src/language/curd.ts:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32 src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1328,7 +1332,7 @@ msgstr ""
 "proxy_cache:10m"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134 src/language/curd.ts:9
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78 src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
@@ -1440,6 +1444,10 @@ msgstr "Détails"
 msgid "Dev"
 msgstr "Dev"
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Développement"
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr "Mode développement"
@@ -1542,9 +1550,9 @@ msgstr "Désactivation du flux %{name} depuis %{node} réussie"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146 src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr "Désactivé"
@@ -1597,7 +1605,7 @@ msgstr "Voulez-vous activer TLS ?"
 msgid "Do you want to remove this server?"
 msgstr "Voulez-vous supprimer ce serveur ?"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr "Voulez-vous supprimer cet upstream ?"
 
@@ -1647,7 +1655,7 @@ msgstr "Déposez le fichier de certificat ici"
 msgid "Drop private key file here"
 msgstr "Déposer le fichier de clé privée ici"
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr "Mode de simulation activé"
@@ -1686,8 +1694,8 @@ msgstr "par ex., 0 0 * * * (quotidiennement à minuit)"
 msgid "Edit"
 msgstr "Modifier"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr "Modifier %{n}"
 
@@ -1820,10 +1828,10 @@ msgstr "Activer TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr "Activé"
@@ -2439,7 +2447,7 @@ msgstr "Échec de la récupération des données"
 msgid "Get dns credential error: {0}"
 msgstr "Erreur lors de la récupération des informations d'identification DNS : {0}"
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr "Erreur d'obtention des informations sur la version"
@@ -2481,8 +2489,8 @@ msgid "Higher value means better connection reuse"
 msgstr "Une valeur plus élevée signifie une meilleure réutilisation de la connexion"
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr "Historique"
 
@@ -2951,16 +2959,13 @@ msgstr ""
 "planificateur de tâches crontab de Nginx UI exécutera la commande logrotate "
 "à l'intervalle que vous avez défini en minutes."
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr "Principal"
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr "Nœud principal"
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr "Maintenance"
@@ -3162,11 +3167,12 @@ msgstr "Mensuellement le jour %{day} à %{time}"
 msgid "Multi-line Directive"
 msgstr "Directive multiligne"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr "N/D"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3303,8 +3309,8 @@ msgstr "Le fichier d'entrée de configuration Nginx existe"
 msgid "Nginx configuration has been restored"
 msgstr "La configuration de Nginx a été restaurée"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr "Erreur d'analyse de configuration Nginx"
 
@@ -3487,7 +3493,7 @@ msgstr "Aucun enregistrement sélectionné"
 msgid "No servers configured"
 msgstr "Aucun serveur configuré"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr "Aucun amont configuré"
 
@@ -3514,7 +3520,7 @@ msgstr "Nom du nœud"
 msgid "Node Secret"
 msgstr "Secret du nœud"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr "État du nœud"
 
@@ -3621,10 +3627,9 @@ msgid "Official Document"
 msgstr "Documentation officielle"
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3632,7 +3637,7 @@ msgstr "Hors ligne"
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
@@ -3659,12 +3664,16 @@ msgstr "Une fois la vérification terminée, les enregistrements seront supprim
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr "En ligne"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Nombre en ligne"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr "Seuls les fichiers ZIP sont autorisés"
@@ -3807,7 +3816,7 @@ msgstr "La ressource de charge utile est nulle"
 msgid "Pending"
 msgstr "En attente"
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr "Exécuter"
 
@@ -4026,7 +4035,7 @@ msgid "Post-sync Action"
 msgstr "Action post-synchronisation"
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgstr "Pré-version"
@@ -4098,7 +4107,7 @@ msgstr "Fournisseur introuvable : {0}"
 msgid "Proxy"
 msgstr "Proxy"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr "Passe de Proxy"
 
@@ -4266,7 +4275,7 @@ msgstr "Suppression réussie"
 msgid "Removed successfully"
 msgstr "Supprimé avec succès"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4623,9 +4632,9 @@ msgstr "Samedi"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr "Enregistrer"
 
@@ -4683,7 +4692,7 @@ msgstr "Sauvegarde réussie"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr "Enregistré avec succès"
@@ -5001,7 +5010,7 @@ msgid "SSO Login"
 msgstr "Connexion SSO"
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgstr "Stable"
@@ -5519,7 +5528,7 @@ msgstr ""
 "Cela restaurera les fichiers de configuration et la base de données. "
 "L'interface Nginx redémarrera une fois la restauration terminée."
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 "Cela mettra à jour ou réinstallera l'interface Nginx sur %{nodeNames} vers "
@@ -5674,7 +5683,7 @@ msgstr "Mardi"
 msgid "Two-factor authentication required"
 msgstr "Authentification à deux facteurs requise"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5755,11 +5764,11 @@ msgstr "Téléverser des fichiers"
 msgid "Upload Folders"
 msgstr "Télécharger des dossiers"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr "Amont"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr "Nom de l'amont"
 
@@ -5852,7 +5861,7 @@ msgstr "Voir"
 msgid "View all notifications"
 msgstr "Voir toutes les notifications"
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr "Voir sur GitHub"
@@ -6083,6 +6092,9 @@ msgstr "Vos anciens codes ne fonctionneront plus."
 msgid "Your passkeys"
 msgstr "Vos clés d'accès"
 
+#~ msgid "Main"
+#~ msgstr "Principal"
+
 #~ msgid "Select an action after sync"
 #~ msgstr "Sélectionner une action après la synchronisation"
 

+ 64 - 52
app/src/language/ja_JP/app.po

@@ -101,8 +101,8 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr "[Nginx UI] 証明書をディスクに書き込み中"
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid "* Includes nodes from group \"%{groupName}\" and manually selected nodes"
-msgstr "* グループ \"%{groupName}\" のノードと手動で選択したノードを含む"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
+msgstr "* グループ %{groupName} のノードと手動で選択したノードを含む"
 
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
@@ -163,7 +163,7 @@ msgid "Actual worker to configured ratio"
 msgstr "実際のワーカー数と設定値の比率"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166 src/language/curd.ts:19
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97 src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -206,7 +206,7 @@ msgstr "サイトを追加"
 msgid "Add Stream"
 msgstr "Streamを追加"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr "アップストリームを追加"
 
@@ -218,8 +218,8 @@ msgstr "正常に追加されました"
 msgid "Additional"
 msgstr "追加設定"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr "アドバンスモード"
 
@@ -425,12 +425,16 @@ msgstr "AutoCert が実行中..."
 msgid "Automatic Restart"
 msgstr "自動再起動"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "平均レイテンシ"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr "戻る"
 
@@ -524,8 +528,8 @@ msgstr "基本情報"
 msgid "Basic"
 msgstr "基本"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr "基本モード"
 
@@ -614,7 +618,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37 src/language/curd.ts:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32 src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1275,7 +1279,7 @@ msgid "Define shared memory zone name and size, e.g. proxy_cache:10m"
 msgstr "共有メモリゾーンの名前とサイズを定義します(例: proxy_cache:10m)"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134 src/language/curd.ts:9
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78 src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
@@ -1387,6 +1391,10 @@ msgstr "詳細"
 msgid "Dev"
 msgstr "Dev"
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "開発"
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr "開発モード"
@@ -1489,9 +1497,9 @@ msgstr "ストリーム %{name} を %{node} から無効化しました"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146 src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr "無効"
@@ -1544,7 +1552,7 @@ msgstr "TLSを有効にしますか?"
 msgid "Do you want to remove this server?"
 msgstr "このサーバーを削除しますか?"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr "このアップストリームを削除しますか?"
 
@@ -1594,7 +1602,7 @@ msgstr "証明書ファイルをここにドロップ"
 msgid "Drop private key file here"
 msgstr "秘密鍵ファイルをここにドロップ"
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr "ドライランモードが有効です"
@@ -1630,8 +1638,8 @@ msgstr "例: 0 0 * * * (毎日深夜0時)"
 msgid "Edit"
 msgstr "編集"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr "%{n} を編集"
 
@@ -1764,10 +1772,10 @@ msgstr "TOTP を有効にする"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr "有効"
@@ -2375,7 +2383,7 @@ msgstr "データの取得に失敗しました"
 msgid "Get dns credential error: {0}"
 msgstr "DNS認証情報の取得エラー: {0}"
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr "リリース情報の取得エラー"
@@ -2417,8 +2425,8 @@ msgid "Higher value means better connection reuse"
 msgstr "値が高いほど接続の再利用が効率的であることを意味します"
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr "履歴"
 
@@ -2867,16 +2875,13 @@ msgstr ""
 "Nginx UI をインストールするユーザーは、このオプションを手動で有効にすることができます。Nginx UI の crontab "
 "タスクスケジューラは、設定した間隔(分単位)で logrotate コマンドを実行します。"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr "メイン"
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr "メインノード"
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr "メンテナンス"
@@ -3078,11 +3083,12 @@ msgstr "毎月%{day}日%{time}に"
 msgid "Multi-line Directive"
 msgstr "複数行ディレクティブ"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr "該当なし"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3219,8 +3225,8 @@ msgstr "Nginx設定エントリファイルが存在します"
 msgid "Nginx configuration has been restored"
 msgstr "Nginxの設定が復元されました"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx設定解析エラー"
 
@@ -3401,7 +3407,7 @@ msgstr "選択されたレコードがありません"
 msgid "No servers configured"
 msgstr "サーバーが設定されていません"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr "アップストリームが設定されていません"
 
@@ -3428,7 +3434,7 @@ msgstr "ノード名"
 msgid "Node Secret"
 msgstr "ノードシークレット"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr "ノードステータス"
 
@@ -3529,10 +3535,9 @@ msgid "Official Document"
 msgstr "公式ドキュメント"
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3540,7 +3545,7 @@ msgstr "オフライン"
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
@@ -3567,12 +3572,16 @@ msgstr "検証が完了すると、レコードは削除されます。"
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr "オンライン"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "オンライン数"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr "ZIPファイルのみ許可されています"
@@ -3711,7 +3720,7 @@ msgstr "ペイロードリソースが nil です"
 msgid "Pending"
 msgstr "保留中"
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr "実行"
 
@@ -3913,7 +3922,7 @@ msgid "Post-sync Action"
 msgstr "同期後のアクション"
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgstr "プレリリース"
@@ -3982,7 +3991,7 @@ msgstr "プロバイダーが見つかりません: {0}"
 msgid "Proxy"
 msgstr "プロキシ"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr "プロキシパス"
 
@@ -4143,7 +4152,7 @@ msgstr "削除に成功しました"
 msgid "Removed successfully"
 msgstr "正常に削除されました"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4495,9 +4504,9 @@ msgstr "土曜日"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr "保存"
 
@@ -4555,7 +4564,7 @@ msgstr "保存に成功しました"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr "正常に保存されました"
@@ -4861,7 +4870,7 @@ msgid "SSO Login"
 msgstr "SSOログイン"
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgstr "安定版"
@@ -5334,7 +5343,7 @@ msgid ""
 "after the restoration is complete."
 msgstr "これにより設定ファイルとデータベースが復元されます。復元が完了すると、Nginx UI が再起動します。"
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr "これにより、%{nodeNames} 上の Nginx UI が %{version} にアップグレードまたは再インストールされます。"
 
@@ -5476,7 +5485,7 @@ msgstr "火曜日"
 msgid "Two-factor authentication required"
 msgstr "二要素認証が必要です"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5557,11 +5566,11 @@ msgstr "ファイルをアップロード"
 msgid "Upload Folders"
 msgstr "フォルダをアップロード"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr "アップストリーム"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr "アップストリーム名"
 
@@ -5654,7 +5663,7 @@ msgstr "ビュー"
 msgid "View all notifications"
 msgstr "すべての通知を表示します"
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr "GitHubで見る"
@@ -5858,6 +5867,9 @@ msgstr "以前のコードはもう使えません。"
 msgid "Your passkeys"
 msgstr "あなたのパスキー"
 
+#~ msgid "Main"
+#~ msgstr "メイン"
+
 #~ msgid "Select an action after sync"
 #~ msgstr "同期後のアクションを選択"
 

+ 64 - 52
app/src/language/ko_KR/app.po

@@ -99,8 +99,8 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr "[Nginx UI] 인증서를 디스크에 작성 중"
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid "* Includes nodes from group \"%{groupName}\" and manually selected nodes"
-msgstr "* 그룹 \"%{groupName}\"의 노드와 수동으로 선택한 노드 포함"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
+msgstr "* 그룹 %{groupName}의 노드와 수동으로 선택한 노드 포함"
 
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
@@ -161,7 +161,7 @@ msgid "Actual worker to configured ratio"
 msgstr "실제 작업자 대 구성 비율"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166 src/language/curd.ts:19
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97 src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -204,7 +204,7 @@ msgstr "사이트 추가"
 msgid "Add Stream"
 msgstr "스트림 추가"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr "업스트림 추가"
 
@@ -216,8 +216,8 @@ msgstr "성공적으로 추가됨"
 msgid "Additional"
 msgstr "추가적인"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr "고급 모드"
 
@@ -423,12 +423,16 @@ msgstr "AutoCert 실행 중..."
 msgid "Automatic Restart"
 msgstr "자동 재시작"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "평균 지연 시간"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr "뒤로"
 
@@ -522,8 +526,8 @@ msgstr "기본 정보"
 msgid "Basic"
 msgstr "기본"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr "기본 모드"
 
@@ -612,7 +616,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37 src/language/curd.ts:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32 src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1272,7 +1276,7 @@ msgid "Define shared memory zone name and size, e.g. proxy_cache:10m"
 msgstr "공유 메모리 영역 이름과 크기를 정의합니다(예: proxy_cache:10m)"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134 src/language/curd.ts:9
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78 src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
@@ -1384,6 +1388,10 @@ msgstr "세부 사항"
 msgid "Dev"
 msgstr "Dev"
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "개발"
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr "개발 모드"
@@ -1486,9 +1494,9 @@ msgstr "스트림 %{name}을(를) %{node}에서 비활성화했습니다"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146 src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr "비활성화됨"
@@ -1541,7 +1549,7 @@ msgstr "TLS를 활성화하시겠습니까?"
 msgid "Do you want to remove this server?"
 msgstr "이 서버를 제거하시겠습니까?"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr "이 업스트림을 제거하시겠습니까?"
 
@@ -1591,7 +1599,7 @@ msgstr "인증서 파일을 여기에 놓으세요"
 msgid "Drop private key file here"
 msgstr "개인 키 파일을 여기에 놓으세요"
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr "드라이런 모드 활성화됨"
@@ -1629,8 +1637,8 @@ msgstr "예: 0 0 * * * (매일 자정)"
 msgid "Edit"
 msgstr "편집"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr "%{n} 편집"
 
@@ -1763,10 +1771,10 @@ msgstr "TOTP 활성화"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr "활성화됨"
@@ -2374,7 +2382,7 @@ msgstr "데이터 가져오기 실패"
 msgid "Get dns credential error: {0}"
 msgstr "DNS 자격 증명 가져오기 오류: {0}"
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr "릴리스 정보 가져오기 오류"
@@ -2416,8 +2424,8 @@ msgid "Higher value means better connection reuse"
 msgstr "값이 높을수록 연결 재사용이 더 좋다는 것을 의미합니다"
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr "기록"
 
@@ -2865,16 +2873,13 @@ msgstr ""
 "페이지의 매개 변수를 수정할 필요가 없습니다. 도커 컨테이너를 사용하여 Nginx UI를 설치하는사용자는이 옵션을 수동으로 활성화할 수 "
 "있습니다. Nginx UI의 크론탭 작업 스케줄러는설정한 간격 (분 단위)에서 logrotate 명령을 실행합니다."
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr "메인"
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr "메인 노드"
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr "유지보수"
@@ -3074,11 +3079,12 @@ msgstr "매월 %{day}일 %{time}에"
 msgid "Multi-line Directive"
 msgstr "여러 줄 지시문"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr "해당 없음"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3215,8 +3221,8 @@ msgstr "Nginx 구성 진입 파일이 존재합니다"
 msgid "Nginx configuration has been restored"
 msgstr "Nginx 구성이 복원되었습니다"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx 구성 분석 오류"
 
@@ -3397,7 +3403,7 @@ msgstr "선택된 기록이 없습니다"
 msgid "No servers configured"
 msgstr "서버가 구성되지 않았습니다"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr "업스트림이 구성되지 않았습니다"
 
@@ -3424,7 +3430,7 @@ msgstr "노드 이름"
 msgid "Node Secret"
 msgstr "노드 시크릿"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr "노드 상태"
 
@@ -3525,10 +3531,9 @@ msgid "Official Document"
 msgstr "공식 문서"
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3536,7 +3541,7 @@ msgstr "오프라인"
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
@@ -3563,12 +3568,16 @@ msgstr "검증이 완료되면, 레코드는 제거됩니다."
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr "온라인"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "온라인 수"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr "ZIP 파일만 허용됩니다"
@@ -3707,7 +3716,7 @@ msgstr "페이로드 리소스가 nil입니다"
 msgid "Pending"
 msgstr "대기 중"
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr "실행"
 
@@ -3907,7 +3916,7 @@ msgid "Post-sync Action"
 msgstr "동기화 후 작업"
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgstr "사전 출시"
@@ -3976,7 +3985,7 @@ msgstr "공급자를 찾을 수 없음: {0}"
 msgid "Proxy"
 msgstr "프록시"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr "프록시 패스"
 
@@ -4137,7 +4146,7 @@ msgstr "성공적으로 제거되었습니다"
 msgid "Removed successfully"
 msgstr "성공적으로 제거됨"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4491,9 +4500,9 @@ msgstr "토요일"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr "저장"
 
@@ -4551,7 +4560,7 @@ msgstr "성공적으로 저장되었습니다"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr "성공적으로 저장됨"
@@ -4857,7 +4866,7 @@ msgid "SSO Login"
 msgstr "SSO 로그인"
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgstr "안정적"
@@ -5330,7 +5339,7 @@ msgid ""
 "after the restoration is complete."
 msgstr "이 작업은 구성 파일과 데이터베이스를 복원합니다. 복원이 완료되면 Nginx UI가 재시작됩니다."
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr "이 작업은 %{nodeNames}의 Nginx UI를 %{version}으로 업그레이드하거나 재설치합니다."
 
@@ -5470,7 +5479,7 @@ msgstr "화요일"
 msgid "Two-factor authentication required"
 msgstr "2단계 인증이 필요합니다"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5551,11 +5560,11 @@ msgstr "파일 업로드"
 msgid "Upload Folders"
 msgstr "폴더 업로드"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr "업스트림"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr "업스트림 이름"
 
@@ -5648,7 +5657,7 @@ msgstr "보기"
 msgid "View all notifications"
 msgstr "모든 알림 보기"
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr "GitHub에서 보기"
@@ -5854,6 +5863,9 @@ msgstr "이전 코드는 더 이상 작동하지 않습니다."
 msgid "Your passkeys"
 msgstr "귀하의 패스키"
 
+#~ msgid "Main"
+#~ msgstr "메인"
+
 #~ msgid "Select an action after sync"
 #~ msgstr "동기화 후 작업 선택"
 

+ 60 - 51
app/src/language/messages.pot

@@ -87,7 +87,7 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr ""
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid "* Includes nodes from group \"%{groupName}\" and manually selected nodes"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
 msgstr ""
 
 #: src/views/user/userColumns.tsx:30
@@ -151,7 +151,7 @@ msgid "Actual worker to configured ratio"
 msgstr ""
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97
 #: src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
@@ -197,7 +197,7 @@ msgstr ""
 msgid "Add Stream"
 msgstr ""
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr ""
 
@@ -209,8 +209,8 @@ msgstr ""
 msgid "Additional"
 msgstr ""
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr ""
 
@@ -415,13 +415,17 @@ msgstr ""
 msgid "Automatic Restart"
 msgstr ""
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr ""
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120
 #: src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr ""
 
@@ -516,8 +520,8 @@ msgstr ""
 msgid "Basic"
 msgstr ""
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr ""
 
@@ -602,7 +606,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32
 #: src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
@@ -1213,7 +1217,7 @@ msgid "Define shared memory zone name and size, e.g. proxy_cache:10m"
 msgstr ""
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78
 #: src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
@@ -1329,6 +1333,10 @@ msgstr ""
 msgid "Dev"
 msgstr ""
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr ""
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr ""
@@ -1431,10 +1439,10 @@ msgstr ""
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146
 #: src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr ""
@@ -1487,7 +1495,7 @@ msgstr ""
 msgid "Do you want to remove this server?"
 msgstr ""
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr ""
 
@@ -1538,7 +1546,7 @@ msgstr ""
 msgid "Drop private key file here"
 msgstr ""
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr ""
@@ -1572,8 +1580,8 @@ msgstr ""
 msgid "Edit"
 msgstr ""
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr ""
 
@@ -1706,11 +1714,11 @@ msgstr ""
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142
 #: src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr ""
@@ -2318,7 +2326,7 @@ msgstr ""
 msgid "Get dns credential error: {0}"
 msgstr ""
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr ""
@@ -2360,8 +2368,8 @@ msgid "Higher value means better connection reuse"
 msgstr ""
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr ""
 
@@ -2787,16 +2795,13 @@ msgstr ""
 msgid "Logrotate, by default, is enabled in most mainstream Linux distributions for users who install Nginx UI on the host machine, so you don't need to modify the parameters on this page. For users who install Nginx UI using Docker containers, you can manually enable this option. The crontab task scheduler of Nginx UI will execute the logrotate command at the interval you set in minutes."
 msgstr ""
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr ""
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr ""
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr ""
@@ -2997,11 +3002,12 @@ msgstr ""
 msgid "Multi-line Directive"
 msgstr ""
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr ""
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3140,8 +3146,8 @@ msgstr ""
 msgid "Nginx configuration has been restored"
 msgstr ""
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr ""
 
@@ -3324,7 +3330,7 @@ msgstr ""
 msgid "No servers configured"
 msgstr ""
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr ""
 
@@ -3352,7 +3358,7 @@ msgstr ""
 msgid "Node Secret"
 msgstr ""
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr ""
 
@@ -3449,10 +3455,9 @@ msgid "Official Document"
 msgstr ""
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3460,7 +3465,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109
 #: src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
@@ -3488,12 +3493,16 @@ msgstr ""
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr ""
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr ""
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr ""
@@ -3630,7 +3639,7 @@ msgstr ""
 msgid "Pending"
 msgstr ""
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr ""
 
@@ -3818,7 +3827,7 @@ msgid "Post-sync Action"
 msgstr ""
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213
 #: src/views/system/Upgrade.vue:267
 msgid "Pre-release"
@@ -3886,7 +3895,7 @@ msgstr ""
 msgid "Proxy"
 msgstr ""
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr ""
 
@@ -4046,7 +4055,7 @@ msgstr ""
 msgid "Removed successfully"
 msgstr ""
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4396,9 +4405,9 @@ msgstr ""
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr ""
 
@@ -4458,7 +4467,7 @@ msgstr ""
 #: src/language/curd.ts:28
 #: src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr ""
@@ -4758,7 +4767,7 @@ msgid "SSO Login"
 msgstr ""
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210
 #: src/views/system/Upgrade.vue:261
 msgid "Stable"
@@ -5192,7 +5201,7 @@ msgstr ""
 msgid "This will restore configuration files and database. Nginx UI will restart after the restoration is complete."
 msgstr ""
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 
@@ -5310,7 +5319,7 @@ msgstr ""
 msgid "Two-factor authentication required"
 msgstr ""
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5393,11 +5402,11 @@ msgstr ""
 msgid "Upload Folders"
 msgstr ""
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr ""
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr ""
 
@@ -5493,7 +5502,7 @@ msgstr ""
 msgid "View all notifications"
 msgstr ""
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr ""

+ 64 - 52
app/src/language/pt_PT/app.po

@@ -101,8 +101,8 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr "[Nginx UI] A escrever o certificado no disco"
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid "* Includes nodes from group \"%{groupName}\" and manually selected nodes"
-msgstr "* Inclui nós do grupo \"%{groupName}\" e nós selecionados manualmente"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
+msgstr "* Inclui nós do grupo %{groupName} e nós selecionados manualmente"
 
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
@@ -163,7 +163,7 @@ msgid "Actual worker to configured ratio"
 msgstr "Rácio real de workers para configurado"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166 src/language/curd.ts:19
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97 src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -206,7 +206,7 @@ msgstr "Adicionar Site"
 msgid "Add Stream"
 msgstr "Adicionar Stream"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr "Adicionar upstream"
 
@@ -218,8 +218,8 @@ msgstr "Adicionado com sucesso"
 msgid "Additional"
 msgstr "Adicional"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr "Modo Avançado"
 
@@ -433,12 +433,16 @@ msgstr "AutoCert está em execução..."
 msgid "Automatic Restart"
 msgstr "Reinício Automático"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "Latência média"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr "Voltar"
 
@@ -538,8 +542,8 @@ msgstr "Informação base"
 msgid "Basic"
 msgstr "Básico"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr "Modo Básico"
 
@@ -628,7 +632,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37 src/language/curd.ts:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32 src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1315,7 +1319,7 @@ msgstr ""
 "proxy_cache:10m"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134 src/language/curd.ts:9
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78 src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
@@ -1427,6 +1431,10 @@ msgstr "Detalhes"
 msgid "Dev"
 msgstr "Dev"
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Desenvolvimento"
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr "Modo Desenvolvimento"
@@ -1529,9 +1537,9 @@ msgstr "Desativar o fluxo %{name} de %{node} com sucesso"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146 src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr "Desativado"
@@ -1584,7 +1592,7 @@ msgstr "Activar TLS?"
 msgid "Do you want to remove this server?"
 msgstr "Remover servidor?"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr "Remover este upstream?"
 
@@ -1634,7 +1642,7 @@ msgstr "Solte o arquivo de certificado aqui"
 msgid "Drop private key file here"
 msgstr "Solte o arquivo de chave privada aqui"
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr "Modo Dry Run activado"
@@ -1673,8 +1681,8 @@ msgstr "ex., 0 0 * * * (diariamente à meia-noite)"
 msgid "Edit"
 msgstr "Editar"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr "Editar %{n}"
 
@@ -1807,10 +1815,10 @@ msgstr "Ativar TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr "Activado"
@@ -2420,7 +2428,7 @@ msgstr "Falha ao obter os dados"
 msgid "Get dns credential error: {0}"
 msgstr "Erro ao obter credenciais DNS: {0}"
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr "Erro ao obter informações de release"
@@ -2462,8 +2470,8 @@ msgid "Higher value means better connection reuse"
 msgstr "Um valor mais alto significa uma melhor reutilização da conexão"
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr "Histórico"
 
@@ -2929,16 +2937,13 @@ msgstr ""
 "activar manualmente esta opção. O agendador de tarefas crontab do Nginx UI "
 "executará o comando logrotate no intervalo que definir em minutos."
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr "Principal"
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr "Nó principal"
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr "Manutenção"
@@ -3140,11 +3145,12 @@ msgstr "Mensalmente no dia %{day} às %{time}"
 msgid "Multi-line Directive"
 msgstr "Diretiva Multilinha"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr "N/D"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3281,8 +3287,8 @@ msgstr "O ficheiro de entrada de configuração do Nginx existe"
 msgid "Nginx configuration has been restored"
 msgstr "A configuração do Nginx foi restaurada"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr "Erro na análise de configuração do Nginx"
 
@@ -3465,7 +3471,7 @@ msgstr "Nenhum registo selecionado"
 msgid "No servers configured"
 msgstr "Nenhum servidor configurado"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr "Nenhum upstream configurado"
 
@@ -3492,7 +3498,7 @@ msgstr "Nome do nó"
 msgid "Node Secret"
 msgstr "Segredo do Nó"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr "Estado do Nó"
 
@@ -3599,10 +3605,9 @@ msgid "Official Document"
 msgstr "Documentação oficial"
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3610,7 +3615,7 @@ msgstr "Off-line"
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
@@ -3637,12 +3642,16 @@ msgstr "Assim que a verificação estiver concluída, os registos serão removid
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr "On-line"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Contagem Online"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr "Apenas ficheiros ZIP são permitidos"
@@ -3783,7 +3792,7 @@ msgstr "O recurso de carga útil é nulo"
 msgid "Pending"
 msgstr "Pendente"
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr "Realizar"
 
@@ -3999,7 +4008,7 @@ msgid "Post-sync Action"
 msgstr "Ação pós-sincronização"
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgstr "Pre-lançamento"
@@ -4071,7 +4080,7 @@ msgstr "Fornecedor não encontrado: {0}"
 msgid "Proxy"
 msgstr "Proxy"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr "Passe de Proxy"
 
@@ -4236,7 +4245,7 @@ msgstr "Removido com sucesso"
 msgid "Removed successfully"
 msgstr "Removido com sucesso"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4595,9 +4604,9 @@ msgstr "Sábado"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr "Salvar"
 
@@ -4655,7 +4664,7 @@ msgstr "Salvo com sucesso"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr "Salvo com sucesso"
@@ -4971,7 +4980,7 @@ msgid "SSO Login"
 msgstr "Autenticação SSO"
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgstr "Estável"
@@ -5481,7 +5490,7 @@ msgstr ""
 "Isto irá restaurar os ficheiros de configuração e a base de dados. O Nginx "
 "UI irá reiniciar após a conclusão da restauração."
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 "Isto vai actualizar ou reinstalar o Nginx UI em %{nodeNames} para "
@@ -5633,7 +5642,7 @@ msgstr "Terça-feira"
 msgid "Two-factor authentication required"
 msgstr "Autenticação de dois fatores necessária"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5714,11 +5723,11 @@ msgstr "Carregar ficheiros"
 msgid "Upload Folders"
 msgstr "Carregar pastas"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr "A montante"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr "Nome do Upstream"
 
@@ -5811,7 +5820,7 @@ msgstr "Ver"
 msgid "View all notifications"
 msgstr "Ver todas notificações"
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr "Ver no GitHub"
@@ -6038,6 +6047,9 @@ msgstr "Os seus códigos antigos não funcionarão mais."
 msgid "Your passkeys"
 msgstr "As suas chaves de acesso"
 
+#~ msgid "Main"
+#~ msgstr "Principal"
+
 #~ msgid "Select an action after sync"
 #~ msgstr "Selecionar uma ação após sincronização"
 

+ 64 - 52
app/src/language/ru_RU/app.po

@@ -105,8 +105,8 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr "[Nginx UI] Запись сертификата на диск"
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid "* Includes nodes from group \"%{groupName}\" and manually selected nodes"
-msgstr "* Включает узлы из группы \"%{groupName}\" и узлы, выбранные вручную"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
+msgstr "* Включает узлы из группы %{groupName} и узлы, выбранные вручную"
 
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
@@ -167,7 +167,7 @@ msgid "Actual worker to configured ratio"
 msgstr "Фактическое соотношение рабочих к настроенным"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166 src/language/curd.ts:19
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97 src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -210,7 +210,7 @@ msgstr "Добавить Сайт"
 msgid "Add Stream"
 msgstr "Добавить поток"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr "Добавить upstream"
 
@@ -222,8 +222,8 @@ msgstr "Добавлено успешно"
 msgid "Additional"
 msgstr "Дополнительно"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr "Расширенный режим"
 
@@ -435,12 +435,16 @@ msgstr "AutoCert выполняется..."
 msgid "Automatic Restart"
 msgstr "Автоматическая перезагрузка"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "Средняя задержка"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr "Назад"
 
@@ -544,8 +548,8 @@ msgstr "Основная информация"
 msgid "Basic"
 msgstr "Основные"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr "Простой режим"
 
@@ -636,7 +640,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37 src/language/curd.ts:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32 src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1318,7 +1322,7 @@ msgid "Define shared memory zone name and size, e.g. proxy_cache:10m"
 msgstr "Определите имя и размер зоны общей памяти, например proxy_cache:10m"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134 src/language/curd.ts:9
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78 src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
@@ -1430,6 +1434,10 @@ msgstr "Детали"
 msgid "Dev"
 msgstr "Dev"
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Разработка"
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr "Режим разработки"
@@ -1532,9 +1540,9 @@ msgstr "Поток %{name} отключен от %{node} успешно"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146 src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr "Отключено"
@@ -1587,7 +1595,7 @@ msgstr "Вы хотите включить TLS?"
 msgid "Do you want to remove this server?"
 msgstr "Хотите удалить этот сервер?"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr "Хотите удалить этот сервер?"
 
@@ -1637,7 +1645,7 @@ msgstr "Перетащите файл сертификата сюда"
 msgid "Drop private key file here"
 msgstr "Перетащите файл закрытого ключа сюда"
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr "Включен пробный режим"
@@ -1676,8 +1684,8 @@ msgstr "напр., 0 0 * * * (ежедневно в полночь)"
 msgid "Edit"
 msgstr "Редактировать"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr "Редактировать %{n}"
 
@@ -1810,10 +1818,10 @@ msgstr "Включить TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr "Включено"
@@ -2427,7 +2435,7 @@ msgstr "Не удалось получить данные"
 msgid "Get dns credential error: {0}"
 msgstr "Ошибка получения учетных данных DNS: {0}"
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr "Ошибка получения информации о выпуске"
@@ -2469,8 +2477,8 @@ msgid "Higher value means better connection reuse"
 msgstr "Более высокое значение означает лучшее повторное использование соединения"
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr "История"
 
@@ -2934,16 +2942,13 @@ msgstr ""
 "можете вручную включить эту опцию. Планировщик задач crontab Nginx UI будет "
 "выполнять команду logrotate с интервалом, который вы установите в минутах."
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr "Основной"
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr "Главный узел"
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr "Техническое обслуживание"
@@ -3145,11 +3150,12 @@ msgstr "Ежемесячно в день %{day} в %{time}"
 msgid "Multi-line Directive"
 msgstr "Многострочная директива"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr "Н/Д"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3286,8 +3292,8 @@ msgstr "Файл конфигурации Nginx существует"
 msgid "Nginx configuration has been restored"
 msgstr "Конфигурация Nginx была восстановлена"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr "Ошибка разбора конфигурации Nginx"
 
@@ -3470,7 +3476,7 @@ msgstr "Нет выбранных записей"
 msgid "No servers configured"
 msgstr "Серверы не настроены"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr "Нет настроенных апстримов"
 
@@ -3497,7 +3503,7 @@ msgstr "Имя узла"
 msgid "Node Secret"
 msgstr "Секрет узла"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr "Статус узла"
 
@@ -3604,10 +3610,9 @@ msgid "Official Document"
 msgstr "Официальная документация"
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3615,7 +3620,7 @@ msgstr "Оффлайн"
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
@@ -3642,12 +3647,16 @@ msgstr "После завершения проверки записи будут
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr "Онлайн"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Количество онлайн"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr "Разрешены только ZIP-файлы"
@@ -3788,7 +3797,7 @@ msgstr "Ресурс полезной нагрузки равен nil"
 msgid "Pending"
 msgstr "В ожидании"
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr "Выполнить"
 
@@ -4009,7 +4018,7 @@ msgid "Post-sync Action"
 msgstr "Действие после синхронизации"
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgstr "Предварительный выпуск"
@@ -4081,7 +4090,7 @@ msgstr "Поставщик не найден: {0}"
 msgid "Proxy"
 msgstr "Прокси"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr "Прокси-передача"
 
@@ -4246,7 +4255,7 @@ msgstr "Удалено успешно"
 msgid "Removed successfully"
 msgstr "Успешно удалено"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4603,9 +4612,9 @@ msgstr "Суббота"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr "Сохранить"
 
@@ -4663,7 +4672,7 @@ msgstr "Сохранено успешно"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr "Успешно сохранено"
@@ -4973,7 +4982,7 @@ msgid "SSO Login"
 msgstr "SSO Вход"
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgstr "Стабильный"
@@ -5480,7 +5489,7 @@ msgstr ""
 "Это восстановит файлы конфигурации и базу данных. Nginx UI перезапустится "
 "после завершения восстановления."
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 "Это обновит или переустановит интерфейс Nginx на %{nodeNames} до версии "
@@ -5632,7 +5641,7 @@ msgstr "Вторник"
 msgid "Two-factor authentication required"
 msgstr "Требуется двухфакторная аутентификация"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5713,11 +5722,11 @@ msgstr "Загрузить файлы"
 msgid "Upload Folders"
 msgstr "Загрузить папки"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr "Восходящий поток"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr "Имя Upstream"
 
@@ -5810,7 +5819,7 @@ msgstr "Просмотр"
 msgid "View all notifications"
 msgstr "Просмотреть все уведомления"
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr "Посмотреть на GitHub"
@@ -6034,6 +6043,9 @@ msgstr "Ваши старые коды больше не будут работа
 msgid "Your passkeys"
 msgstr "Ваши ключи доступа"
 
+#~ msgid "Main"
+#~ msgstr "Основной"
+
 #~ msgid "Select an action after sync"
 #~ msgstr "Выберите действие после синхронизации"
 

+ 64 - 54
app/src/language/tr_TR/app.po

@@ -101,10 +101,8 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr "[Nginx UI] Sertifika diske yazılıyor"
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid "* Includes nodes from group \"%{groupName}\" and manually selected nodes"
-msgstr ""
-"* \"%{groupName}\" grubundaki düğümler ve manuel olarak seçilen düğümler "
-"dahil"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
+msgstr "* %{groupName} grubundan düğümler ve manuel olarak seçilen düğümler içerir"
 
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
@@ -165,7 +163,7 @@ msgid "Actual worker to configured ratio"
 msgstr "Gerçek çalışanın yapılandırılmışa oranı"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166 src/language/curd.ts:19
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97 src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -208,7 +206,7 @@ msgstr "Site Ekle"
 msgid "Add Stream"
 msgstr "Akış Ekle"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr "Upstream ekle"
 
@@ -220,8 +218,8 @@ msgstr "Başarıyla eklendi"
 msgid "Additional"
 msgstr "İlave bilgi"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr "Gelişmiş Mod"
 
@@ -433,12 +431,16 @@ msgstr "AutoCert çalışıyor..."
 msgid "Automatic Restart"
 msgstr "Otomatik Yeniden Başlatma"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "Ortalama Gecikme"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr "Geri"
 
@@ -534,8 +536,8 @@ msgstr "Temel bilgiler"
 msgid "Basic"
 msgstr "Temel"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr "Temel Mod"
 
@@ -624,7 +626,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37 src/language/curd.ts:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32 src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1310,7 +1312,7 @@ msgstr ""
 "proxy_cache:10m"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134 src/language/curd.ts:9
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78 src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
@@ -1422,6 +1424,10 @@ msgstr "Detaylar"
 msgid "Dev"
 msgstr "Dev"
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Geliştirme"
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr "Geliştirme modu"
@@ -1524,9 +1530,9 @@ msgstr "Akış %{name}, %{node} üzerinden başarıyla devre dışı bırakıld
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146 src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr "Devre dışı"
@@ -1579,7 +1585,7 @@ msgstr "TLS'yi etkinleştirmek istiyor musunuz?"
 msgid "Do you want to remove this server?"
 msgstr "Bu sunucuyu kaldırmak istiyor musunuz?"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr "Bu upstream'i kaldırmak istiyor musunuz?"
 
@@ -1631,7 +1637,7 @@ msgstr "Sertifika dosyasını buraya bırakın"
 msgid "Drop private key file here"
 msgstr "Özel anahtar dosyasını buraya bırakın"
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr "Deneme modu etkinleştirildi(Dry-run)"
@@ -1670,8 +1676,8 @@ msgstr "örn., 0 0 * * * (her gece yarısı)"
 msgid "Edit"
 msgstr "Düzenle"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr "Düzenle %{n}"
 
@@ -1804,10 +1810,10 @@ msgstr "TOTP'yi Etkinleştir"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr "Etkin"
@@ -2419,7 +2425,7 @@ msgstr "Veri alınamadı"
 msgid "Get dns credential error: {0}"
 msgstr "DNS kimlik bilgisi alınırken hata: {0}"
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr "Sürüm bilgilerini alma hatası"
@@ -2461,8 +2467,8 @@ msgid "Higher value means better connection reuse"
 msgstr "Daha yüksek bir değer, daha iyi bağlantı yeniden kullanımı anlamına gelir"
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr "Geçmiş"
 
@@ -2929,16 +2935,13 @@ msgstr ""
 "olarak etkinleştirebilir. Nginx UI'nin crontab görev zamanlayıcısı, "
 "belirlediğiniz dakika aralığında logrotate komutunu çalıştıracaktır."
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr "Ana"
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr "Ana Düğüm"
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr "Bakım"
@@ -3140,11 +3143,12 @@ msgstr "Her ayın %{day} günü %{time} saatinde"
 msgid "Multi-line Directive"
 msgstr "Çok Satırlı Yönergeler"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr "Yok"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3281,8 +3285,8 @@ msgstr "Nginx yapılandırma giriş dosyası mevcut"
 msgid "Nginx configuration has been restored"
 msgstr "Nginx yapılandırması geri yüklendi"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx Yapılandırma Ayrıştırma Hatası"
 
@@ -3465,7 +3469,7 @@ msgstr "Hiçbir kayıt seçilmedi"
 msgid "No servers configured"
 msgstr "Yapılandırılmış sunucu yok"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr "Yapılandırılmış yukarı akış yok"
 
@@ -3492,7 +3496,7 @@ msgstr "Düğüm adı"
 msgid "Node Secret"
 msgstr "Düğüm Sırrı"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr "Düğüm Durumu"
 
@@ -3599,10 +3603,9 @@ msgid "Official Document"
 msgstr "Resmi Belge"
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3610,7 +3613,7 @@ msgstr "Çevrimdışı"
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
@@ -3637,12 +3640,16 @@ msgstr "Doğrulama tamamlandığında, kayıtlar kaldırılacaktır."
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr "Çevrimiçi"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Çevrimiçi Sayısı"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr "Sadece ZIP dosyalarına izin verilir"
@@ -3782,7 +3789,7 @@ msgstr "Yük kaynağı nil"
 msgid "Pending"
 msgstr "Beklemede"
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr "Uygula"
 
@@ -3999,7 +4006,7 @@ msgid "Post-sync Action"
 msgstr "Senkronizasyon Sonrası İşlem"
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgstr "Ön Sürüm"
@@ -4070,7 +4077,7 @@ msgstr "Sağlayıcı bulunamadı: {0}"
 msgid "Proxy"
 msgstr "Proxy"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr "Proxy Geçişi"
 
@@ -4235,7 +4242,7 @@ msgstr "Başarıyla kaldırıldı"
 msgid "Removed successfully"
 msgstr "Başarıyla kaldırıldı"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4601,9 +4608,9 @@ msgstr "Cumartesi"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr "Kaydet"
 
@@ -4661,7 +4668,7 @@ msgstr "Başarıyla kaydedildi"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr "Başarıyla kaydedildi"
@@ -4971,7 +4978,7 @@ msgid "SSO Login"
 msgstr "SSO Girişi"
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgstr "Kararlı"
@@ -5479,7 +5486,7 @@ msgstr ""
 "Bu işlem yapılandırma dosyalarını ve veritabanını geri yükleyecektir. Geri "
 "yükleme tamamlandıktan sonra Nginx UI yeniden başlatılacaktır."
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 "Bu işlem, %{nodeNames} üzerindeki Nginx UI'yi %{version} sürümüne "
@@ -5631,7 +5638,7 @@ msgstr "Salı"
 msgid "Two-factor authentication required"
 msgstr "İki faktörlü kimlik doğrulama gerekiyor"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5712,11 +5719,11 @@ msgstr "Dosyaları Yükle"
 msgid "Upload Folders"
 msgstr "Klasörleri Yükle"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr "Yukarı Akış"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr "Yukarı Akış Adı"
 
@@ -5809,7 +5816,7 @@ msgstr "Görüntüle"
 msgid "View all notifications"
 msgstr "Tüm bildirimleri görüntüle"
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr "GitHub'da Görüntüle"
@@ -6035,6 +6042,9 @@ msgstr "Eski kodlarınız artık çalışmayacak."
 msgid "Your passkeys"
 msgstr "Geçiş Anahtarlarınız"
 
+#~ msgid "Main"
+#~ msgstr "Ana"
+
 #~ msgid "Select an action after sync"
 #~ msgstr "Senkronizasyon sonrası eylem seçin"
 

+ 64 - 52
app/src/language/uk_UA/app.po

@@ -105,8 +105,8 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr "[Nginx UI] Запис сертифіката на диск"
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid "* Includes nodes from group \"%{groupName}\" and manually selected nodes"
-msgstr "* Включає вузли з групи \"%{groupName}\" та вручну вибрані вузли"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
+msgstr "* Включає вузли з групи %{groupName} та вузли, вибрані вручну"
 
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
@@ -167,7 +167,7 @@ msgid "Actual worker to configured ratio"
 msgstr "Фактичне співвідношення робочих до налаштованих"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166 src/language/curd.ts:19
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97 src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -210,7 +210,7 @@ msgstr "Додати сайт"
 msgid "Add Stream"
 msgstr "Додати стрім"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr "Додати upstream"
 
@@ -222,8 +222,8 @@ msgstr "Успішно додано"
 msgid "Additional"
 msgstr "Додатково"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr "Розширений режим"
 
@@ -435,12 +435,16 @@ msgstr "AutoCert виконується..."
 msgid "Automatic Restart"
 msgstr "Автоматичний перезапуск"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "Середня затримка"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr "Назад"
 
@@ -542,8 +546,8 @@ msgstr "Основна інформація"
 msgid "Basic"
 msgstr "Базові"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr "Базовий режим"
 
@@ -633,7 +637,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37 src/language/curd.ts:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32 src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1348,7 +1352,7 @@ msgid "Define shared memory zone name and size, e.g. proxy_cache:10m"
 msgstr "Вкажіть назву та розмір зони спільної пам'яті, наприклад proxy_cache:10m"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134 src/language/curd.ts:9
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78 src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
@@ -1496,6 +1500,10 @@ msgstr "Деталі"
 msgid "Dev"
 msgstr "Розробка"
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Розробка"
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr "Режим розробки"
@@ -1598,9 +1606,9 @@ msgstr "Потік %{name} успішно вимкнено з %{node}"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146 src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr "Вимкнено"
@@ -1653,7 +1661,7 @@ msgstr "Ви хочете увімкнути TLS?"
 msgid "Do you want to remove this server?"
 msgstr "Ви хочете видалити цей сервер?"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr "Ви хочете видалити цей апстрім?"
 
@@ -1703,7 +1711,7 @@ msgstr "Перетягніть файл сертифіката сюди"
 msgid "Drop private key file here"
 msgstr "Перетягніть файл приватного ключа сюди"
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr "Увімкнено тестовий режим"
@@ -1742,8 +1750,8 @@ msgstr "напр., 0 0 * * * (щодня о півночі)"
 msgid "Edit"
 msgstr "Редагувати"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr "Редагувати %{n}"
 
@@ -1876,10 +1884,10 @@ msgstr "Увімкнути TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr "Увімкнено"
@@ -2493,7 +2501,7 @@ msgstr "Не вдалося отримати дані"
 msgid "Get dns credential error: {0}"
 msgstr "Помилка отримання облікових даних DNS: {0}"
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr "Помилка отримання інформації про реліз"
@@ -2535,8 +2543,8 @@ msgid "Higher value means better connection reuse"
 msgstr "Вище значення означає краще повторне використання з’єднання"
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr "Історія"
 
@@ -3000,16 +3008,13 @@ msgstr ""
 "можете вручну активувати цю опцію. Планувальник завдань crontab у Nginx UI "
 "виконуватиме команду logrotate з інтервалом, який ви встановите у хвилинах."
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr "Головний"
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr "Головний вузол"
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr "Технічне обслуговування"
@@ -3211,11 +3216,12 @@ msgstr "Щомісяця %{day} числа о %{time}"
 msgid "Multi-line Directive"
 msgstr "Багаторядкова директива"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr "Н/Д"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3352,8 +3358,8 @@ msgstr "Файл входу конфігурації Nginx існує"
 msgid "Nginx configuration has been restored"
 msgstr "Конфігурацію Nginx відновлено"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr "Помилка аналізу конфігурації Nginx"
 
@@ -3536,7 +3542,7 @@ msgstr "Не вибрано жодного запису"
 msgid "No servers configured"
 msgstr "Сервери не налаштовані"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr "Не налаштовано жодного апстріму"
 
@@ -3563,7 +3569,7 @@ msgstr "Ім’я вузла"
 msgid "Node Secret"
 msgstr "Секрет вузла"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr "Стан вузла"
 
@@ -3670,10 +3676,9 @@ msgid "Official Document"
 msgstr "Офіційна документація"
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3681,7 +3686,7 @@ msgstr "Офлайн"
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
@@ -3708,12 +3713,16 @@ msgstr "Після завершення перевірки записи буду
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr "Онлайн"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Кількість онлайн"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr "Дозволені лише zip-файли"
@@ -3854,7 +3863,7 @@ msgstr "Ресурс навантаження є nil"
 msgid "Pending"
 msgstr "Очікується"
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr "Виконати"
 
@@ -4071,7 +4080,7 @@ msgid "Post-sync Action"
 msgstr "Дія після синхронізації"
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgstr "Попередній випуск"
@@ -4143,7 +4152,7 @@ msgstr "Постачальника не знайдено: {0}"
 msgid "Proxy"
 msgstr "Проксі"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr "Проксі-передача"
 
@@ -4311,7 +4320,7 @@ msgstr "Успішно видалено"
 msgid "Removed successfully"
 msgstr "Успішно видалено"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4670,9 +4679,9 @@ msgstr "Субота"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr "Зберегти"
 
@@ -4730,7 +4739,7 @@ msgstr "Успішно збережено"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr "Успішно збережено"
@@ -5044,7 +5053,7 @@ msgid "SSO Login"
 msgstr "Вхід через SSO"
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgstr "Стабільна"
@@ -5552,7 +5561,7 @@ msgstr ""
 "Це відновить файли конфігурації та базу даних. Nginx UI перезапуститься "
 "після завершення відновлення."
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr "Це оновить або перевстановить Nginx UI на %{nodeNames} до версії %{version}."
 
@@ -5702,7 +5711,7 @@ msgstr "Вівторок"
 msgid "Two-factor authentication required"
 msgstr "Потрібна двофакторна аутентифікація"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5783,11 +5792,11 @@ msgstr "Завантажити файли"
 msgid "Upload Folders"
 msgstr "Завантажити папки"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr "Вгору за течією"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr "Назва апстріму"
 
@@ -5880,7 +5889,7 @@ msgstr "Переглянути"
 msgid "View all notifications"
 msgstr "Переглянути всі сповіщення"
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr "Переглянути на GitHub"
@@ -6103,6 +6112,9 @@ msgstr "Ваші старі коди більше не працюватимут
 msgid "Your passkeys"
 msgstr "Ваші ключі доступу"
 
+#~ msgid "Main"
+#~ msgstr "Головний"
+
 #~ msgid "Select an action after sync"
 #~ msgstr "Виберіть дію після синхронізації"
 

+ 64 - 52
app/src/language/vi_VN/app.po

@@ -96,8 +96,8 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr "[Nginx UI] Đang ghi chứng chỉ vào ổ đĩa"
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid "* Includes nodes from group \"%{groupName}\" and manually selected nodes"
-msgstr "* Bao gồm các nút từ nhóm \"%{groupName}\" và các nút được chọn thủ công"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
+msgstr "* Bao gồm các nút từ nhóm %{groupName} và các nút được chọn thủ công"
 
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
@@ -158,7 +158,7 @@ msgid "Actual worker to configured ratio"
 msgstr "Tỷ lệ công nhân thực tế so với cấu hình"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166 src/language/curd.ts:19
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97 src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -201,7 +201,7 @@ msgstr "Thêm Website"
 msgid "Add Stream"
 msgstr "Thêm luồng"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr "Thêm upstream"
 
@@ -213,8 +213,8 @@ msgstr "Đã thêm thành công"
 msgid "Additional"
 msgstr "Bổ sung"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr "Nâng cao"
 
@@ -422,12 +422,16 @@ msgstr "AutoCert đang chạy..."
 msgid "Automatic Restart"
 msgstr "Khởi động lại tự động"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "Độ trễ trung bình"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr "Quay lại"
 
@@ -523,8 +527,8 @@ msgstr "Thông tin"
 msgid "Basic"
 msgstr "Cơ bản"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr "Cơ bản"
 
@@ -613,7 +617,7 @@ msgstr ""
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37 src/language/curd.ts:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32 src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1288,7 +1292,7 @@ msgid "Define shared memory zone name and size, e.g. proxy_cache:10m"
 msgstr "Xác định tên và kích thước vùng bộ nhớ dùng chung, ví dụ proxy_cache:10m"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134 src/language/curd.ts:9
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78 src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
@@ -1400,6 +1404,10 @@ msgstr "Chi tiết"
 msgid "Dev"
 msgstr "Dev"
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Phát triển"
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr "Chế độ phát triển"
@@ -1502,9 +1510,9 @@ msgstr "Đã vô hiệu hóa luồng %{name} từ %{node} thành công"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146 src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr "Đã tắt"
@@ -1557,7 +1565,7 @@ msgstr "Bạn có muốn bật TLS không?"
 msgid "Do you want to remove this server?"
 msgstr "Bạn có muốn xóa máy chủ này không?"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr "Bạn có muốn xóa upstream này không?"
 
@@ -1609,7 +1617,7 @@ msgstr "Thả tệp chứng chỉ vào đây"
 msgid "Drop private key file here"
 msgstr "Thả tệp khóa riêng tư vào đây"
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr "Đã bật chế độ Dry run"
@@ -1647,8 +1655,8 @@ msgstr "vd, 0 0 * * * (hàng ngày vào nửa đêm)"
 msgid "Edit"
 msgstr "Chỉnh sửa"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr "Sửa %{n}"
 
@@ -1781,10 +1789,10 @@ msgstr "Bật TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr "Đã bật"
@@ -2394,7 +2402,7 @@ msgstr "Không thể lấy dữ liệu"
 msgid "Get dns credential error: {0}"
 msgstr "Lỗi khi lấy thông tin xác thực DNS: {0}"
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr "Lỗi lấy thông tin phát hành"
@@ -2436,8 +2444,8 @@ msgid "Higher value means better connection reuse"
 msgstr "Giá trị cao hơn có nghĩa là tái sử dụng kết nối tốt hơn"
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr "Lịch sử"
 
@@ -2900,16 +2908,13 @@ msgstr ""
 "chọn này. Bộ lập lịch tác vụ crontab của Nginx UI sẽ thực thi lệnh "
 "logrotate theo khoảng thời gian bạn đặt (tính bằng phút)."
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr "Chính"
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr "Nút chính"
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr "Bảo trì"
@@ -3111,11 +3116,12 @@ msgstr "Hàng tháng vào ngày %{day} lúc %{time}"
 msgid "Multi-line Directive"
 msgstr "Chỉ thị nhiều dòng"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr "Không có"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3252,8 +3258,8 @@ msgstr "Tập tin cấu hình đầu vào Nginx tồn tại"
 msgid "Nginx configuration has been restored"
 msgstr "Cấu hình Nginx đã được khôi phục"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr "Lỗi phân tích cấu hình Nginx"
 
@@ -3436,7 +3442,7 @@ msgstr "Không có bản ghi nào được chọn"
 msgid "No servers configured"
 msgstr "Không có máy chủ nào được cấu hình"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr "Không có upstream nào được cấu hình"
 
@@ -3463,7 +3469,7 @@ msgstr "Tên nút"
 msgid "Node Secret"
 msgstr "Bí mật nút"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr "Trạng thái nút"
 
@@ -3568,10 +3574,9 @@ msgid "Official Document"
 msgstr "Tài liệu chính thức"
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3579,7 +3584,7 @@ msgstr "Ngoại tuyến"
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
@@ -3606,12 +3611,16 @@ msgstr "Sau khi quá trình xác minh hoàn tất, bản ghi sẽ bị xóa."
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr "Trực tuyến"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Số lượng trực tuyến"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr "Chỉ cho phép tệp zip"
@@ -3752,7 +3761,7 @@ msgstr "Tài nguyên tải trọng là nil"
 msgid "Pending"
 msgstr "Đang chờ"
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr "Thực hiện"
 
@@ -3960,7 +3969,7 @@ msgid "Post-sync Action"
 msgstr "Hành động sau đồng bộ"
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgstr "Bản phát hành trước"
@@ -4031,7 +4040,7 @@ msgstr "Không tìm thấy nhà cung cấp: {0}"
 msgid "Proxy"
 msgstr "Proxy"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr "Chuyển tiếp Proxy"
 
@@ -4196,7 +4205,7 @@ msgstr "Xóa thành công"
 msgid "Removed successfully"
 msgstr "Đã xóa thành công"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4553,9 +4562,9 @@ msgstr "Thứ Bảy"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr "Lưu"
 
@@ -4613,7 +4622,7 @@ msgstr "Lưu thành công"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr "Lưu thành công"
@@ -4921,7 +4930,7 @@ msgid "SSO Login"
 msgstr "Đăng nhập SSO"
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgstr "Ổn định"
@@ -5427,7 +5436,7 @@ msgstr ""
 "Thao tác này sẽ khôi phục các tệp cấu hình và cơ sở dữ liệu. Giao diện "
 "Nginx sẽ khởi động lại sau khi quá trình khôi phục hoàn tất."
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 "Thao tác này sẽ nâng cấp hoặc cài đặt lại Nginx UI trên %{nodeNames} lên "
@@ -5579,7 +5588,7 @@ msgstr "Thứ Ba"
 msgid "Two-factor authentication required"
 msgstr "Yêu cầu xác thực hai yếu tố"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5660,11 +5669,11 @@ msgstr "Tải lên tệp"
 msgid "Upload Folders"
 msgstr "Tải lên thư mục"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr "Ngược dòng"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr "Tên Upstream"
 
@@ -5757,7 +5766,7 @@ msgstr "Xem"
 msgid "View all notifications"
 msgstr "Xem tất cả thông báo"
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr "Xem trên GitHub"
@@ -5978,6 +5987,9 @@ msgstr "Mã cũ của bạn sẽ không còn hoạt động nữa."
 msgid "Your passkeys"
 msgstr "Khóa truy cập của bạn"
 
+#~ msgid "Main"
+#~ msgstr "Chính"
+
 #~ msgid "Select an action after sync"
 #~ msgstr "Chọn hành động sau khi đồng bộ"
 

+ 64 - 52
app/src/language/zh_CN/app.po

@@ -100,8 +100,8 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr "[Nginx UI] 正在将证书写入磁盘"
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid "* Includes nodes from group \"%{groupName}\" and manually selected nodes"
-msgstr "* 包含来自组 \"%{groupName}\" 的节点和手动选择的节点"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
+msgstr "* 包含来自组 %{groupName} 的节点和手动选择的节点"
 
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
@@ -162,7 +162,7 @@ msgid "Actual worker to configured ratio"
 msgstr "实际工作进程与配置比例"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166 src/language/curd.ts:19
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97 src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -205,7 +205,7 @@ msgstr "添加站点"
 msgid "Add Stream"
 msgstr "添加 Stream"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr "添加上游"
 
@@ -217,8 +217,8 @@ msgstr "添加成功"
 msgid "Additional"
 msgstr "额外选项"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr "高级模式"
 
@@ -424,12 +424,16 @@ msgstr "AutoCert 正在运行..."
 msgid "Automatic Restart"
 msgstr "自动重启"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "平均延迟"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr "返回"
 
@@ -523,8 +527,8 @@ msgstr "基本信息"
 msgid "Basic"
 msgstr "基本"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr "基本模式"
 
@@ -611,7 +615,7 @@ msgstr "基于 worker_processes * worker_connections 计算得出。实际性能
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37 src/language/curd.ts:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32 src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1266,7 +1270,7 @@ msgid "Define shared memory zone name and size, e.g. proxy_cache:10m"
 msgstr "定义共享内存区名称和大小,例如 proxy_cache:10m"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134 src/language/curd.ts:9
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78 src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
@@ -1378,6 +1382,10 @@ msgstr "详情"
 msgid "Dev"
 msgstr "开发版"
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "开发"
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr "开发模式"
@@ -1480,9 +1488,9 @@ msgstr "在 %{node} 上禁用 %{name} 成功"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146 src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr "禁用"
@@ -1535,7 +1543,7 @@ msgstr "你想启用 TLS 吗?"
 msgid "Do you want to remove this server?"
 msgstr "你想删除这个服务器吗?"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr "你想删除这个 Upstream 吗?"
 
@@ -1585,7 +1593,7 @@ msgstr "将证书文件拖放到此处"
 msgid "Drop private key file here"
 msgstr "将私钥文件拖放到此处"
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr "试运行模式已启动"
@@ -1621,8 +1629,8 @@ msgstr "例如:0 0 * * *(每天午夜)"
 msgid "Edit"
 msgstr "编辑"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr "编辑 %{n}"
 
@@ -1755,10 +1763,10 @@ msgstr "启用 TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr "启用"
@@ -2366,7 +2374,7 @@ msgstr "获取数据失败"
 msgid "Get dns credential error: {0}"
 msgstr "获取 DNS 凭证错误:{0}"
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr "获取发布信息错误"
@@ -2408,8 +2416,8 @@ msgid "Higher value means better connection reuse"
 msgstr "更高的值意味着更好的连接再利用"
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr "历史"
 
@@ -2857,16 +2865,13 @@ msgstr ""
 "定时任务,因此您无需修改本页面的参数。对于使用 Docker 容器安装 Nginx 用户界面的用户,您可以手动启用该选项。Nginx UI "
 "的定时任务任务调度器将按照您设置的时间间隔(以分钟为单位)执行 logrotate 命令。"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr "主"
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr "主节点"
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr "维护模式"
@@ -3066,11 +3071,12 @@ msgstr "每月%{day}日%{time}"
 msgid "Multi-line Directive"
 msgstr "多行指令"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr "不适用"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3207,8 +3213,8 @@ msgstr "存在 Nginx 配置入口文件"
 msgid "Nginx configuration has been restored"
 msgstr "Nginx 配置已恢复"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx 配置解析错误"
 
@@ -3389,7 +3395,7 @@ msgstr "未选择记录"
 msgid "No servers configured"
 msgstr "未配置服务器"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr "未配置上游"
 
@@ -3416,7 +3422,7 @@ msgstr "节点名称"
 msgid "Node Secret"
 msgstr "节点密钥"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr "节点状态"
 
@@ -3517,10 +3523,9 @@ msgid "Official Document"
 msgstr "官方文档"
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3528,7 +3533,7 @@ msgstr "离线"
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
@@ -3555,12 +3560,16 @@ msgstr "一旦验证完成,这些记录将被删除。"
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr "在线"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "在线数量"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr "只允许使用zip文件"
@@ -3697,7 +3706,7 @@ msgstr "有效载荷资源为空"
 msgid "Pending"
 msgstr "待处理"
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr "执行"
 
@@ -3897,7 +3906,7 @@ msgid "Post-sync Action"
 msgstr "同步后操作"
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgstr "预发布"
@@ -3966,7 +3975,7 @@ msgstr "未找到提供商:{0}"
 msgid "Proxy"
 msgstr "代理"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr "代理传递"
 
@@ -4127,7 +4136,7 @@ msgstr "移除成功"
 msgid "Removed successfully"
 msgstr "删除成功"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4479,9 +4488,9 @@ msgstr "星期六"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr "保存"
 
@@ -4539,7 +4548,7 @@ msgstr "保存成功"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr "保存成功"
@@ -4845,7 +4854,7 @@ msgid "SSO Login"
 msgstr "SSO 登录"
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgstr "稳定"
@@ -5312,7 +5321,7 @@ msgid ""
 "after the restoration is complete."
 msgstr "这将恢复配置文件和数据库。恢复完成后,Nginx UI 将重新启动。"
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr "将 %{nodeNames} 上的 Nginx UI 升级或重新安装到 %{version} 版本。"
 
@@ -5450,7 +5459,7 @@ msgstr "星期二"
 msgid "Two-factor authentication required"
 msgstr "需要两步验证"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5531,11 +5540,11 @@ msgstr "上传文件"
 msgid "Upload Folders"
 msgstr "上传文件夹"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr "上游"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr "Upstream 名称"
 
@@ -5628,7 +5637,7 @@ msgstr "查看"
 msgid "View all notifications"
 msgstr "查看全部通知"
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr "在 GitHub 上查看"
@@ -5830,6 +5839,9 @@ msgstr "您的旧代码将不再有效。"
 msgid "Your passkeys"
 msgstr "你的 Passkeys"
 
+#~ msgid "Main"
+#~ msgstr "主"
+
 #~ msgid "Select an action after sync"
 #~ msgstr "选择同步后的操作"
 

+ 64 - 52
app/src/language/zh_TW/app.po

@@ -104,8 +104,8 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr "[Nginx UI] 正在將憑證寫入磁碟"
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
-msgid "* Includes nodes from group \"%{groupName}\" and manually selected nodes"
-msgstr "* 包含來自群組 \"%{groupName}\" 的節點和手動選擇的節點"
+msgid "* Includes nodes from group %{groupName} and manually selected nodes"
+msgstr "* 包含來自群組 %{groupName} 的節點和手動選擇的節點"
 
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
@@ -166,7 +166,7 @@ msgid "Actual worker to configured ratio"
 msgstr "實際工作進程與配置比例"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166 src/language/curd.ts:19
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97 src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -209,7 +209,7 @@ msgstr "新增網站"
 msgid "Add Stream"
 msgstr "新增 Stream"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgstr "添加上游"
 
@@ -221,8 +221,8 @@ msgstr "新增成功"
 msgid "Additional"
 msgstr "其他設定"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:94
-#: src/views/stream/components/StreamEditor.vue:57
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:100
+#: src/views/stream/components/StreamEditor.vue:63
 msgid "Advance Mode"
 msgstr "進階模式"
 
@@ -428,12 +428,16 @@ msgstr "AutoCert 正在運行..."
 msgid "Automatic Restart"
 msgstr "自動重啟"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "平均延遲"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:156
-#: src/views/stream/components/StreamEditor.vue:111
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:169
+#: src/views/stream/components/StreamEditor.vue:123
 msgid "Back"
 msgstr "返回"
 
@@ -527,8 +531,8 @@ msgstr "基本資訊"
 msgid "Basic"
 msgstr "基本"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:97
-#: src/views/stream/components/StreamEditor.vue:60
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:103
+#: src/views/stream/components/StreamEditor.vue:66
 msgid "Basic Mode"
 msgstr "基本模式"
 
@@ -615,7 +619,7 @@ msgstr "基於 worker_processes * worker_connections 計算得出。實際效能
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37 src/language/curd.ts:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32 src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1270,7 +1274,7 @@ msgid "Define shared memory zone name and size, e.g. proxy_cache:10m"
 msgstr "定義共享記憶體區域名稱和大小,例如 proxy_cache:10m"
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134 src/language/curd.ts:9
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78 src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
@@ -1382,6 +1386,10 @@ msgstr "細節"
 msgid "Dev"
 msgstr "Dev"
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "開發"
+
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgstr "開發模式"
@@ -1484,9 +1492,9 @@ msgstr "已成功從 %{node} 停用串流 %{name}"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
 #: src/views/site/site_list/columns.tsx:146 src/views/stream/columns.tsx:112
-#: src/views/stream/components/StreamEditor.vue:32
+#: src/views/stream/components/StreamEditor.vue:38
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgstr "停用"
@@ -1539,7 +1547,7 @@ msgstr "您想啟用 TLS 嗎?"
 msgid "Do you want to remove this server?"
 msgstr "您要移除此伺服器嗎?"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgstr "您要移除這個 Upstream 嗎?"
 
@@ -1589,7 +1597,7 @@ msgstr "將證書文件拖放到此處"
 msgid "Drop private key file here"
 msgstr "將私鑰檔案拖放到此處"
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgstr "試運轉模式已啟用"
@@ -1625,8 +1633,8 @@ msgstr "例如:0 0 * * *(每天午夜)"
 msgid "Edit"
 msgstr "編輯"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:51
-#: src/views/stream/components/StreamEditor.vue:21
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:57
+#: src/views/stream/components/StreamEditor.vue:27
 msgid "Edit %{n}"
 msgstr "編輯 %{n}"
 
@@ -1759,10 +1767,10 @@ msgstr "啟用 TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:56
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:62
 #: src/views/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
-#: src/views/stream/components/StreamEditor.vue:26
+#: src/views/stream/components/StreamEditor.vue:32
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgstr "已啟用"
@@ -2370,7 +2378,7 @@ msgstr "取得資料失敗"
 msgid "Get dns credential error: {0}"
 msgstr "獲取 DNS 憑證錯誤:{0}"
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgstr "取得發佈資訊錯誤"
@@ -2412,8 +2420,8 @@ msgid "Higher value means better connection reuse"
 msgstr "數值越高表示連線重複使用率越好"
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:81
-#: src/views/stream/components/StreamEditor.vue:45
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:87
+#: src/views/stream/components/StreamEditor.vue:51
 msgid "History"
 msgstr "歷史"
 
@@ -2861,16 +2869,13 @@ msgstr ""
 "logrotate,因此您無需修改此頁面的參數。對於使用 Docker 容器安裝 Nginx UI 的使用者,您可以手動啟用此選項。Nginx UI "
 "的 crontab 任務排程器將按照您設定的分鐘間隔執行 logrotate 命令。"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:220
-msgid "Main"
-msgstr "主"
-
-#: src/components/ProxyTargets/ProxyTargets.vue:148
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:51
+#: src/composables/useUpstreamStatus.ts:139
 msgid "Main Node"
 msgstr "主節點"
 
 #: src/views/site/components/SiteStatusSelect.vue:165
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:68
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:74
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgstr "維護"
@@ -3070,11 +3075,12 @@ msgstr "每月%{day}日%{time}"
 msgid "Multi-line Directive"
 msgstr "多行指令"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgstr "不適用"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3211,8 +3217,8 @@ msgstr "Nginx 設定入口檔案存在"
 msgid "Nginx configuration has been restored"
 msgstr "Nginx 設定已恢復"
 
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:115
-#: src/views/stream/components/StreamEditor.vue:75
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:121
+#: src/views/stream/components/StreamEditor.vue:81
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx 設定解析錯誤"
 
@@ -3393,7 +3399,7 @@ msgstr "未選取任何記錄"
 msgid "No servers configured"
 msgstr "未配置伺服器"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgstr "未配置上游"
 
@@ -3420,7 +3426,7 @@ msgstr "節點名稱"
 msgid "Node Secret"
 msgstr "節點金鑰"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgstr "節點狀態"
 
@@ -3521,10 +3527,9 @@ msgid "Official Document"
 msgstr "官方文件"
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
@@ -3532,7 +3537,7 @@ msgstr "離線"
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
-#: src/components/NgxConfigEditor/NgxUpstream.vue:36
+#: src/components/NgxConfigEditor/NgxUpstream.vue:31
 #: src/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
@@ -3559,12 +3564,16 @@ msgstr "驗證完成後,記錄將被刪除。"
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
-#: src/components/ProxyTargets/ProxyTargets.vue:217
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:48
 #: src/views/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgstr "線上"
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "在線數量"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgstr "只允許壓縮檔"
@@ -3703,7 +3712,7 @@ msgstr "有效載荷資源為空"
 msgid "Pending"
 msgstr "待處理"
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgstr "執行"
 
@@ -3903,7 +3912,7 @@ msgid "Post-sync Action"
 msgstr "同步後動作"
 
 #: src/views/environments/list/BatchUpgrader.vue:168
-#: src/views/environments/list/BatchUpgrader.vue:221
+#: src/views/environments/list/BatchUpgrader.vue:224
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgstr "預先發布"
@@ -3972,7 +3981,7 @@ msgstr "未找到提供者:{0}"
 msgid "Proxy"
 msgstr "代理伺服器"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgstr "代理傳遞"
 
@@ -4133,7 +4142,7 @@ msgstr "移除成功"
 msgid "Removed successfully"
 msgstr "移除成功"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
@@ -4485,9 +4494,9 @@ msgstr "星期六"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:163
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:176
 #: src/views/stream/components/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgstr "儲存"
 
@@ -4545,7 +4554,7 @@ msgstr "儲存成功"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
-#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:40
+#: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:46
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgstr "儲存成功"
@@ -4851,7 +4860,7 @@ msgid "SSO Login"
 msgstr "SSO 登入"
 
 #: src/views/environments/list/BatchUpgrader.vue:165
-#: src/views/environments/list/BatchUpgrader.vue:215
+#: src/views/environments/list/BatchUpgrader.vue:218
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgstr "穩定"
@@ -5318,7 +5327,7 @@ msgid ""
 "after the restoration is complete."
 msgstr "這將恢復設定檔案和資料庫。恢復完成後,Nginx UI 將重新啟動。"
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr "這將在 %{nodeNames} 上升級或重新安裝 Nginx UI 到 %{version}。"
 
@@ -5456,7 +5465,7 @@ msgstr "星期二"
 msgid "Two-factor authentication required"
 msgstr "需要多重因素驗證"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5537,11 +5546,11 @@ msgstr "上傳檔案"
 msgid "Upload Folders"
 msgstr "上傳資料夾"
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgstr "上游"
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgstr "Upstream 名稱"
 
@@ -5634,7 +5643,7 @@ msgstr "檢視"
 msgid "View all notifications"
 msgstr "檢視所有通知"
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgstr "在 GitHub 上查看"
@@ -5836,6 +5845,9 @@ msgstr "您的舊代碼將不再有效。"
 msgid "Your passkeys"
 msgstr "您的通行金鑰"
 
+#~ msgid "Main"
+#~ msgstr "主"
+
 #~ msgid "Select an action after sync"
 #~ msgstr "同步後選擇操作"
 

+ 1 - 1
app/src/views/config/components/ConfigRightPanel/Basic.vue

@@ -18,7 +18,7 @@ const settings = useSettingsStore()
 </script>
 
 <template>
-  <div class="px-6">
+  <div class="px-6 pb-2">
     <AForm
       layout="vertical"
       :model="data"

+ 1 - 1
app/src/views/site/site_edit/components/RightPanel/Basic.vue

@@ -23,7 +23,7 @@ function handleStatusChanged(event: { status: SiteStatus }) {
 </script>
 
 <template>
-  <div class="px-6">
+  <div class="px-6 pb-2">
     <div class="mb-6">
       <AForm layout="vertical">
         <AFormItem :label="$gettext('Status')">

+ 13 - 0
app/src/views/site/site_edit/components/SiteEditor/SiteEditor.vue

@@ -5,6 +5,7 @@ import CodeEditor from '@/components/CodeEditor/CodeEditor.vue'
 import ConfigHistory from '@/components/ConfigHistory'
 import FooterToolBar from '@/components/FooterToolbar'
 import NgxConfigEditor from '@/components/NgxConfigEditor'
+import UpstreamCards from '@/components/UpstreamCards/UpstreamCards.vue'
 import { ConfigStatus } from '@/constants'
 import Cert from '@/views/site/site_edit/components/Cert'
 import EnableTLS from '@/views/site/site_edit/components/EnableTLS'
@@ -28,6 +29,11 @@ const {
   curSupportSSL,
 } = storeToRefs(editorStore)
 
+// Get upstream targets from backend API data
+const upstreamTargets = computed(() => {
+  return data.value.proxy_targets || []
+})
+
 const showHistory = ref(false)
 
 onMounted(() => {
@@ -132,6 +138,13 @@ async function save() {
           class="domain-edit-container"
         >
           <EnableTLS />
+
+          <!-- Upstream Cards Display -->
+          <UpstreamCards
+            :targets="upstreamTargets"
+            :env-group-id="data.env_group_id"
+          />
+
           <NgxConfigEditor
             :cert-info="certInfoMap"
             :status="data.status"

+ 13 - 1
app/src/views/stream/components/StreamEditor.vue

@@ -4,14 +4,20 @@ import CodeEditor from '@/components/CodeEditor'
 import ConfigHistory from '@/components/ConfigHistory'
 import FooterToolBar from '@/components/FooterToolbar'
 import NgxConfigEditor from '@/components/NgxConfigEditor'
+import UpstreamCards from '@/components/UpstreamCards/UpstreamCards.vue'
 import { ConfigStatus } from '@/constants'
 import { useStreamEditorStore } from '../store'
 
 const router = useRouter()
 
 const store = useStreamEditorStore()
-const { name, status, configText, filepath, saving, parseErrorStatus, parseErrorMessage, advanceMode, loading } = storeToRefs(store)
+const { name, status, configText, filepath, saving, parseErrorStatus, parseErrorMessage, advanceMode, loading, data } = storeToRefs(store)
 const showHistory = ref(false)
+
+// Get upstream targets from backend API data
+const upstreamTargets = computed(() => {
+  return data.value.proxy_targets || []
+})
 </script>
 
 <template>
@@ -91,6 +97,12 @@ const showHistory = ref(false)
             key="basic"
             class="domain-edit-container"
           >
+            <!-- Upstream Cards Display -->
+            <UpstreamCards
+              :targets="upstreamTargets"
+              :env-group-id="data.env_group_id"
+            />
+
             <NgxConfigEditor
               :enabled="status === ConfigStatus.Enabled"
               context="stream"