1
0
Эх сурвалжийг харах

feat(upstream): add upstream cards and detail modal

0xJacky 2 долоо хоног өмнө
parent
commit
552cf5c0e8
29 өөрчлөгдсөн 1666 нэмэгдсэн , 1126 устгасан
  1. 9 0
      .claude/settings.local.json
  2. 51 16
      api/sites/site.go
  3. 42 8
      api/streams/streams.go
  4. 2 0
      app/components.d.ts
  5. 3 72
      app/src/components/NgxConfigEditor/NgxUpstream.vue
  6. 23 242
      app/src/components/ProxyTargets/ProxyTargets.vue
  7. 2 2
      app/src/components/SyncNodesPreview/SyncNodesPreview.vue
  8. 188 0
      app/src/components/UpstreamCards/UpstreamCards.vue
  9. 165 0
      app/src/components/UpstreamDetailModal/UpstreamDetailModal.vue
  10. 201 0
      app/src/composables/useUpstreamStatus.ts
  11. 64 52
      app/src/language/ar/app.po
  12. 64 54
      app/src/language/de_DE/app.po
  13. 60 52
      app/src/language/en/app.po
  14. 64 52
      app/src/language/es/app.po
  15. 64 52
      app/src/language/fr_FR/app.po
  16. 64 52
      app/src/language/ja_JP/app.po
  17. 64 52
      app/src/language/ko_KR/app.po
  18. 60 51
      app/src/language/messages.pot
  19. 64 52
      app/src/language/pt_PT/app.po
  20. 64 52
      app/src/language/ru_RU/app.po
  21. 64 54
      app/src/language/tr_TR/app.po
  22. 64 52
      app/src/language/uk_UA/app.po
  23. 64 52
      app/src/language/vi_VN/app.po
  24. 64 52
      app/src/language/zh_CN/app.po
  25. 64 52
      app/src/language/zh_TW/app.po
  26. 1 1
      app/src/views/config/components/ConfigRightPanel/Basic.vue
  27. 1 1
      app/src/views/site/site_edit/components/RightPanel/Basic.vue
  28. 13 0
      app/src/views/site/site_edit/components/SiteEditor/SiteEditor.vue
  29. 13 1
      app/src/views/stream/components/StreamEditor.vue

+ 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/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/internal/site"
 	"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/model"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/gin-gonic/gin"
 	"github.com/gin-gonic/gin"
@@ -16,6 +17,38 @@ import (
 	"gorm.io/gorm/clause"
 	"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) {
 func GetSite(c *gin.Context) {
 	name := helper.UnescapeURL(c.Param("name"))
 	name := helper.UnescapeURL(c.Param("name"))
 
 
@@ -48,13 +81,14 @@ func GetSite(c *gin.Context) {
 		}
 		}
 
 
 		c.JSON(http.StatusOK, site.Site{
 		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
 		return
 	}
 	}
@@ -80,15 +114,16 @@ func GetSite(c *gin.Context) {
 	}
 	}
 
 
 	c.JSON(http.StatusOK, site.Site{
 	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/helper"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/internal/stream"
 	"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/model"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/gin-gonic/gin"
 	"github.com/gin-gonic/gin"
@@ -31,6 +32,38 @@ type Stream struct {
 	ProxyTargets []config.ProxyTarget `json:"proxy_targets,omitempty"`
 	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) {
 func GetStreams(c *gin.Context) {
 	// Parse query parameters
 	// Parse query parameters
 	options := &stream.ListOptions{
 	options := &stream.ListOptions{
@@ -101,14 +134,15 @@ func GetStream(c *gin.Context) {
 
 
 	// Build response based on advanced mode
 	// Build response based on advanced mode
 	response := Stream{
 	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 {
 	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']
     SyncNodesPreviewSyncNodesPreview: typeof import('./src/components/SyncNodesPreview/SyncNodesPreview.vue')['default']
     SystemRestoreSystemRestoreContent: typeof import('./src/components/SystemRestore/SystemRestoreContent.vue')['default']
     SystemRestoreSystemRestoreContent: typeof import('./src/components/SystemRestore/SystemRestoreContent.vue')['default']
     TwoFAAuthorization: typeof import('./src/components/TwoFA/Authorization.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']
     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">
 <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 { MoreOutlined, PlusOutlined } from '@ant-design/icons-vue'
 import { Modal } from 'ant-design-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()
 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 open = ref(false)
 const renameIdx = ref(-1)
 const renameIdx = ref(-1)
 const buffer = ref('')
 const buffer = ref('')
@@ -61,53 +52,6 @@ function renameOK() {
     ngxConfig.value.upstreams[renameIdx.value].name = buffer.value
     ngxConfig.value.upstreams[renameIdx.value].name = buffer.value
   open.value = false
   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>
 </script>
 
 
 <template>
 <template>
@@ -139,20 +83,7 @@ function getAvailabilityResult(directive: NgxDirective) {
         </template>
         </template>
 
 
         <div class="tab-content">
         <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>
         </div>
       </ATabPane>
       </ATabPane>
 
 
@@ -207,7 +138,7 @@ function getAvailabilityResult(directive: NgxDirective) {
 <style scoped lang="less">
 <style scoped lang="less">
 .empty-state {
 .empty-state {
   @apply px-8 text-center;
   @apply px-8 text-center;
-  min-height: 400px;
+  min-height: 200px;
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
   justify-content: center;
   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">
 <script setup lang="ts">
 import type { ProxyTarget } from '@/api/site'
 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 {
 interface Props {
   targets: ProxyTarget[]
   targets: ProxyTarget[]
@@ -11,173 +11,18 @@ interface Props {
 
 
 const props = defineProps<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 showDetailModal = ref(false)
 const selectedTarget = ref<ProxyTarget | null>(null)
 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) {
 function handleTargetClick(target: ProxyTarget) {
   if (shouldShowMultiNodeDisplay.value) {
   if (shouldShowMultiNodeDisplay.value) {
     selectedTarget.value = target
     selectedTarget.value = target
@@ -190,7 +35,7 @@ function handleTargetClick(target: ProxyTarget) {
   <div v-if="targets.length > 0" class="proxy-targets">
   <div v-if="targets.length > 0" class="proxy-targets">
     <ATag
     <ATag
       v-for="target in targets" :key="proxyStore.getTargetKey(target)" :color="getTargetColor(target)"
       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)"
       @click="handleTargetClick(target)"
     >
     >
       <template #icon>
       <template #icon>
@@ -202,32 +47,12 @@ function handleTargetClick(target: ProxyTarget) {
       {{ getTargetText(target) }}
       {{ getTargetText(target) }}
     </ATag>
     </ATag>
 
 
-    <!-- Upstream Detail Modal -->
-    <AModal
+    <!-- Detail Modal -->
+    <UpstreamDetailModal
       v-model:open="showDetailModal"
       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>
   </div>
 </template>
 </template>
 
 
@@ -238,6 +63,7 @@ function handleTargetClick(target: ProxyTarget) {
     gap: 4px;
     gap: 4px;
     max-width: 100%;
     max-width: 100%;
     overflow: hidden;
     overflow: hidden;
+    padding: 6px;
   }
   }
 
 
   .proxy-target-tag {
   .proxy-target-tag {
@@ -255,7 +81,11 @@ function handleTargetClick(target: ProxyTarget) {
       transition: all 0.2s ease;
       transition: all 0.2s ease;
 
 
       &:hover {
       &: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;
       height: 12px;
       line-height: 12px;
       line-height: 12px;
       text-align: center;
       text-align: center;
-      background: rgba(255, 255, 255, 0.2);
       border-radius: 2px;
       border-radius: 2px;
-      margin-right: 4px;
       font-weight: bold;
       font-weight: bold;
       font-size: 10px;
       font-size: 10px;
       flex-shrink: 0;
       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>
 </style>

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

@@ -46,7 +46,7 @@ const allSyncNodeIds = computed(() => {
       </strong>
       </strong>
     </div>
     </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
       <NodeCard
         v-for="nodeId in allSyncNodeIds"
         v-for="nodeId in allSyncNodeIds"
         :key="nodeId"
         :key="nodeId"
@@ -56,7 +56,7 @@ const allSyncNodeIds = computed(() => {
     </div>
     </div>
 
 
     <div v-if="envGroupInfo" class="mt-2 text-xs text-gray-500 dark:text-gray-400">
     <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>
   </div>
   </div>
 </template>
 </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] كتابة الشهادة على القرص"
 msgstr "[Nginx UI] كتابة الشهادة على القرص"
 
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
 #: 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
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
 msgid "2FA"
@@ -163,7 +163,7 @@ msgid "Actual worker to configured ratio"
 msgstr "نسبة العامل الفعلي إلى المُهيأ"
 msgstr "نسبة العامل الفعلي إلى المُهيأ"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
 #: 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/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -206,7 +206,7 @@ msgstr "أضف موقع"
 msgid "Add Stream"
 msgid "Add Stream"
 msgstr "أضف Stream"
 msgstr "أضف Stream"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgid "Add Upstream"
 msgstr "إضافة upstream"
 msgstr "إضافة upstream"
 
 
@@ -218,8 +218,8 @@ msgstr "تمت الإضافة بنجاح"
 msgid "Additional"
 msgid "Additional"
 msgstr "إضافي"
 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"
 msgid "Advance Mode"
 msgstr "الوضع المتقدم"
 msgstr "الوضع المتقدم"
 
 
@@ -427,12 +427,16 @@ msgstr "AutoCert قيد التشغيل..."
 msgid "Automatic Restart"
 msgid "Automatic Restart"
 msgstr "إعادة التشغيل التلقائي"
 msgstr "إعادة التشغيل التلقائي"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "متوسط زمن الوصول"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
 #: 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"
 msgid "Back"
 msgstr "رجوع"
 msgstr "رجوع"
 
 
@@ -528,8 +532,8 @@ msgstr "المعلومات الأساسية"
 msgid "Basic"
 msgid "Basic"
 msgstr "أساسي"
 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"
 msgid "Basic Mode"
 msgstr "الوضع الأساسي"
 msgstr "الوضع الأساسي"
 
 
@@ -618,7 +622,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
 #: 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/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
 #: 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"
 msgstr "حدد اسم منطقة الذاكرة المشتركة والحجم، على سبيل المثال proxy_cache:10m"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
 #: 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/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
 #: src/views/config/components/Delete.vue:97
@@ -1413,6 +1417,10 @@ msgstr "تفاصيل"
 msgid "Dev"
 msgid "Dev"
 msgstr "تطوير"
 msgstr "تطوير"
 
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "التطوير"
+
 #: src/views/system/About.vue:28
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "وضع التطوير"
 msgstr "وضع التطوير"
@@ -1515,9 +1523,9 @@ msgstr "تم تعطيل الدفق %{name} من %{node} بنجاح"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
 #: 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/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
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgid "Disabled"
 msgstr "معطل"
 msgstr "معطل"
@@ -1570,7 +1578,7 @@ msgstr "هل تريد تفعيل TLS؟"
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "هل تريد إزالة هذا الخادم؟"
 msgstr "هل تريد إزالة هذا الخادم؟"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr "هل تريد إزالة هذا المصدر؟"
 msgstr "هل تريد إزالة هذا المصدر؟"
 
 
@@ -1625,7 +1633,7 @@ msgstr "قم بإسقاط ملف الشهادة هنا"
 msgid "Drop private key file here"
 msgid "Drop private key file here"
 msgstr "قم بإسقاط ملف المفتاح الخاص هنا"
 msgstr "قم بإسقاط ملف المفتاح الخاص هنا"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "تمكين وضع التشغيل التجريبي"
 msgstr "تمكين وضع التشغيل التجريبي"
@@ -1663,8 +1671,8 @@ msgstr "على سبيل المثال، 0 0 * * * (يوميًا عند منتصف
 msgid "Edit"
 msgid "Edit"
 msgstr "تعديل"
 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}"
 msgid "Edit %{n}"
 msgstr "تعديل %{n}"
 msgstr "تعديل %{n}"
 
 
@@ -1797,10 +1805,10 @@ msgstr "تفعيل TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
 #: 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/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
 #: 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
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgid "Enabled"
 msgstr "مفعل"
 msgstr "مفعل"
@@ -2408,7 +2416,7 @@ msgstr "فشل الحصول على البيانات"
 msgid "Get dns credential error: {0}"
 msgid "Get dns credential error: {0}"
 msgstr "خطأ في الحصول على بيانات اعتماد DNS: {0}"
 msgstr "خطأ في الحصول على بيانات اعتماد DNS: {0}"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgid "Get release information error"
 msgstr "خطأ في الحصول على معلومات الإصدار"
 msgstr "خطأ في الحصول على معلومات الإصدار"
@@ -2450,8 +2458,8 @@ msgid "Higher value means better connection reuse"
 msgstr "القيمة الأعلى تعني إعادة استخدام أفضل للاتصال"
 msgstr "القيمة الأعلى تعني إعادة استخدام أفضل للاتصال"
 
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
 #: 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"
 msgid "History"
 msgstr "السجل"
 msgstr "السجل"
 
 
@@ -2913,16 +2921,13 @@ msgstr ""
 "يدويًا. سيقوم مجدول المهام crontab الخاص بواجهة Nginx UI بتنفيذ أمر تدوير "
 "يدويًا. سيقوم مجدول المهام 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"
 msgid "Main Node"
 msgstr "العقدة الرئيسية"
 msgstr "العقدة الرئيسية"
 
 
 #: src/views/site/components/SiteStatusSelect.vue:165
 #: 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
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgid "Maintenance"
 msgstr "صيانة"
 msgstr "صيانة"
@@ -3124,11 +3129,12 @@ msgstr "يوميًا في اليوم %{day} الساعة %{time}"
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "توجيه متعدد الأسطر"
 msgstr "توجيه متعدد الأسطر"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgid "N/A"
 msgstr "غير متاح"
 msgstr "غير متاح"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3265,8 +3271,8 @@ msgstr "ملف إدخال تكوين Nginx موجود"
 msgid "Nginx configuration has been restored"
 msgid "Nginx configuration has been restored"
 msgstr "تمت استعادة تكوين Nginx"
 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"
 msgid "Nginx Configuration Parse Error"
 msgstr "خطأ في تحليل تكوين Nginx"
 msgstr "خطأ في تحليل تكوين Nginx"
 
 
@@ -3447,7 +3453,7 @@ msgstr "لم يتم تحديد أي سجلات"
 msgid "No servers configured"
 msgid "No servers configured"
 msgstr "لا توجد خوادم مُهيأة"
 msgstr "لا توجد خوادم مُهيأة"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgid "No upstreams configured"
 msgstr "لا توجد مصادر علوية مهيأة"
 msgstr "لا توجد مصادر علوية مهيأة"
 
 
@@ -3474,7 +3480,7 @@ msgstr "اسم العقدة"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "سر العقدة"
 msgstr "سر العقدة"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgid "Node Status"
 msgstr "حالة العقدة"
 msgstr "حالة العقدة"
 
 
@@ -3579,10 +3585,9 @@ msgid "Official Document"
 msgstr "الوثيقة الرسمية"
 msgstr "الوثيقة الرسمية"
 
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
 #: 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/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
 msgid "Offline"
@@ -3590,7 +3595,7 @@ msgstr "غير متصل"
 
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
 #: 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/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/notification/Notification.vue:39
@@ -3617,12 +3622,16 @@ msgstr "بمجرد اكتمال التحقق، سيتم إزالة السجلا
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
 #: 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/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgid "Online"
 msgstr "متصل"
 msgstr "متصل"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "عدد المتصلين"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgid "Only zip files are allowed"
 msgstr "يُسمح فقط بملفات zip"
 msgstr "يُسمح فقط بملفات zip"
@@ -3762,7 +3771,7 @@ msgstr "مورد الحمولة فارغ"
 msgid "Pending"
 msgid "Pending"
 msgstr "قيد الانتظار"
 msgstr "قيد الانتظار"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgid "Perform"
 msgstr "نفذ"
 msgstr "نفذ"
 
 
@@ -3966,7 +3975,7 @@ msgid "Post-sync Action"
 msgstr "إجراء ما بعد المزامنة"
 msgstr "إجراء ما بعد المزامنة"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:168
 #: 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
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "ما قبل الإصدار"
 msgstr "ما قبل الإصدار"
@@ -4037,7 +4046,7 @@ msgstr "لم يتم العثور على المزود: {0}"
 msgid "Proxy"
 msgid "Proxy"
 msgstr "وكيل"
 msgstr "وكيل"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgid "Proxy Pass"
 msgstr "مرور الوكيل"
 msgstr "مرور الوكيل"
 
 
@@ -4200,7 +4209,7 @@ msgstr "إزالة بنجاح"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "تمت الإزالة بنجاح"
 msgstr "تمت الإزالة بنجاح"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
 #: src/views/config/ConfigList.vue:190
@@ -4556,9 +4565,9 @@ msgstr "السبت"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
 #: 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/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgid "Save"
 msgstr "حفظ"
 msgstr "حفظ"
 
 
@@ -4616,7 +4625,7 @@ msgstr "تم الحفظ بنجاح"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
 #: 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
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "تم الحفظ بنجاح"
 msgstr "تم الحفظ بنجاح"
@@ -4924,7 +4933,7 @@ msgid "SSO Login"
 msgstr "تسجيل الدخول عبر SSO"
 msgstr "تسجيل الدخول عبر SSO"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:165
 #: 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
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgid "Stable"
 msgstr "مستقر"
 msgstr "مستقر"
@@ -5422,7 +5431,7 @@ msgstr ""
 "سيؤدي هذا إلى استعادة ملفات التكوين وقاعدة البيانات. سيعاد تشغيل واجهة "
 "سيؤدي هذا إلى استعادة ملفات التكوين وقاعدة البيانات. سيعاد تشغيل واجهة "
 "Nginx بعد اكتمال الاستعادة."
 "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}."
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr "سيتم ترقية أو إعادة تثبيت Nginx UI على %{nodeNames} إلى %{version}."
 msgstr "سيتم ترقية أو إعادة تثبيت Nginx UI على %{nodeNames} إلى %{version}."
 
 
@@ -5570,7 +5579,7 @@ msgstr "الثلاثاء"
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "يتطلب المصادقة الثنائية"
 msgstr "يتطلب المصادقة الثنائية"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5651,11 +5660,11 @@ msgstr "تحميل الملفات"
 msgid "Upload Folders"
 msgid "Upload Folders"
 msgstr "تحميل المجلدات"
 msgstr "تحميل المجلدات"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgid "Upstream"
 msgstr "أعلى التيار"
 msgstr "أعلى التيار"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgid "Upstream Name"
 msgstr "اسم المنبع"
 msgstr "اسم المنبع"
 
 
@@ -5748,7 +5757,7 @@ msgstr "عرض"
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "عرض جميع التنبيهات"
 msgstr "عرض جميع التنبيهات"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgid "View on GitHub"
 msgstr "عرض على GitHub"
 msgstr "عرض على GitHub"
@@ -5965,6 +5974,9 @@ msgstr "رموزك القديمة لن تعمل بعد الآن."
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "مفاتيح المرور الخاصة بك"
 msgstr "مفاتيح المرور الخاصة بك"
 
 
+#~ msgid "Main"
+#~ msgstr "رئيسي"
+
 #~ msgid "Select an action after sync"
 #~ msgid "Select an action after sync"
 #~ msgstr "اختر إجراءً بعد المزامنة"
 #~ 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"
 msgstr "[Nginx UI] Zertifikat wird auf die Festplatte geschrieben"
 
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
 #: 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
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
 msgid "2FA"
@@ -164,7 +162,7 @@ msgid "Actual worker to configured ratio"
 msgstr "Tatsächliches Verhältnis von Arbeitern zu konfigurierten"
 msgstr "Tatsächliches Verhältnis von Arbeitern zu konfigurierten"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
 #: 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/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
 #: 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"
 msgid "Add Stream"
 msgstr "Stream hinzufügen"
 msgstr "Stream hinzufügen"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgid "Add Upstream"
 msgstr "Upstream hinzufügen"
 msgstr "Upstream hinzufügen"
 
 
@@ -219,8 +217,8 @@ msgstr "Erfolgreich hinzugefügt"
 msgid "Additional"
 msgid "Additional"
 msgstr "Zusätzlich"
 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"
 msgid "Advance Mode"
 msgstr "Erweiterter Modus"
 msgstr "Erweiterter Modus"
 
 
@@ -434,12 +432,16 @@ msgstr "AutoCert wird ausgeführt..."
 msgid "Automatic Restart"
 msgid "Automatic Restart"
 msgstr "Automatischer Neustart"
 msgstr "Automatischer Neustart"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "Durchschnittliche Latenz"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
 #: 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"
 msgid "Back"
 msgstr "Zurück"
 msgstr "Zurück"
 
 
@@ -543,8 +545,8 @@ msgstr "Basisinformationen"
 msgid "Basic"
 msgid "Basic"
 msgstr "Basis"
 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"
 msgid "Basic Mode"
 msgstr "Basic-Modus"
 msgstr "Basic-Modus"
 
 
@@ -635,7 +637,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
 #: 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/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1321,7 +1323,7 @@ msgstr ""
 "proxy_cache:10m"
 "proxy_cache:10m"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
 #: 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/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
 #: src/views/config/components/Delete.vue:97
@@ -1433,6 +1435,10 @@ msgstr "Details"
 msgid "Dev"
 msgid "Dev"
 msgstr "Entwicklung"
 msgstr "Entwicklung"
 
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Entwicklung"
+
 #: src/views/system/About.vue:28
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "Entwicklungsmodus"
 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:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
 #: 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/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
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Deaktiviert"
 msgstr "Deaktiviert"
@@ -1592,7 +1598,7 @@ msgstr "Möchten Sie TLS aktivieren?"
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "Möchten Sie diesen Server entfernen?"
 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?"
 msgid "Do you want to remove this upstream?"
 msgstr "Möchten Sie diesen Upstream entfernen?"
 msgstr "Möchten Sie diesen Upstream entfernen?"
 
 
@@ -1644,7 +1650,7 @@ msgstr "Zertifikatsdatei hier ablegen"
 msgid "Drop private key file here"
 msgid "Drop private key file here"
 msgstr "Private Schlüsseldatei hier ablegen"
 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
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "Dry-Run-Modus aktiviert"
 msgstr "Dry-Run-Modus aktiviert"
@@ -1683,8 +1689,8 @@ msgstr "z. B. 0 0 * * * (täglich um Mitternacht)"
 msgid "Edit"
 msgid "Edit"
 msgstr "Bearbeiten"
 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}"
 msgid "Edit %{n}"
 msgstr "Bearbeiten %{n}"
 msgstr "Bearbeiten %{n}"
 
 
@@ -1817,10 +1823,10 @@ msgstr "TOTP aktivieren"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
 #: 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/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
 #: 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
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Aktiviert"
 msgstr "Aktiviert"
@@ -2432,7 +2438,7 @@ msgstr "Datenabruf fehlgeschlagen"
 msgid "Get dns credential error: {0}"
 msgid "Get dns credential error: {0}"
 msgstr "Fehler beim Abrufen der DNS-Anmeldeinformationen: {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
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgid "Get release information error"
 msgstr "Fehler beim Abrufen der Release-Informationen"
 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"
 msgstr "Ein höherer Wert bedeutet eine bessere Wiederverwendung der Verbindung"
 
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
 #: 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"
 msgid "History"
 msgstr "Verlauf"
 msgstr "Verlauf"
 
 
@@ -2942,16 +2948,13 @@ msgstr ""
 "aktivieren. Der Crontab-Aufgabenplaner von Nginx UI führt den "
 "aktivieren. Der Crontab-Aufgabenplaner von Nginx UI führt den "
 "Logrotate-Befehl in dem von dir in Minuten festgelegten Intervall aus."
 "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"
 msgid "Main Node"
 msgstr "Hauptknoten"
 msgstr "Hauptknoten"
 
 
 #: src/views/site/components/SiteStatusSelect.vue:165
 #: 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
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgid "Maintenance"
 msgstr "Wartung"
 msgstr "Wartung"
@@ -3154,11 +3157,12 @@ msgstr "Monatlich am %{day}. um %{time}"
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "Mehrzeilige Direktive"
 msgstr "Mehrzeilige Direktive"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgid "N/A"
 msgstr "N/V"
 msgstr "N/V"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3295,8 +3299,8 @@ msgstr "Nginx-Konfigurationsdatei existiert"
 msgid "Nginx configuration has been restored"
 msgid "Nginx configuration has been restored"
 msgstr "Die Nginx-Konfiguration wurde wiederhergestellt"
 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"
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx-Konfigurationsanalysefehler"
 msgstr "Nginx-Konfigurationsanalysefehler"
 
 
@@ -3479,7 +3483,7 @@ msgstr "Keine Datensätze ausgewählt"
 msgid "No servers configured"
 msgid "No servers configured"
 msgstr "Keine Server konfiguriert"
 msgstr "Keine Server konfiguriert"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgid "No upstreams configured"
 msgstr "Keine Upstreams konfiguriert"
 msgstr "Keine Upstreams konfiguriert"
 
 
@@ -3506,7 +3510,7 @@ msgstr "Knotenname"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "Node-Secret"
 msgstr "Node-Secret"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgid "Node Status"
 msgstr "Knotenstatus"
 msgstr "Knotenstatus"
 
 
@@ -3614,10 +3618,9 @@ msgid "Official Document"
 msgstr "Offizielle Dokumentation"
 msgstr "Offizielle Dokumentation"
 
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
 #: 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/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
 msgid "Offline"
@@ -3625,7 +3628,7 @@ msgstr "Offline"
 
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
 #: 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/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: 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/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
 #: 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/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgid "Online"
 msgstr "Online"
 msgstr "Online"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Anzahl Online"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgid "Only zip files are allowed"
 msgstr "Nur ZIP-Dateien sind erlaubt"
 msgstr "Nur ZIP-Dateien sind erlaubt"
@@ -3797,7 +3804,7 @@ msgstr "Die Nutzlast-Ressource ist null"
 msgid "Pending"
 msgid "Pending"
 msgstr "Ausstehend"
 msgstr "Ausstehend"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgid "Perform"
 msgstr "Ausführen"
 msgstr "Ausführen"
 
 
@@ -4016,7 +4023,7 @@ msgid "Post-sync Action"
 msgstr "Aktion nach der Synchronisierung"
 msgstr "Aktion nach der Synchronisierung"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:168
 #: 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
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "Vorabversion"
 msgstr "Vorabversion"
@@ -4088,7 +4095,7 @@ msgstr "Anbieter nicht gefunden: {0}"
 msgid "Proxy"
 msgid "Proxy"
 msgstr "Proxy"
 msgstr "Proxy"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgid "Proxy Pass"
 msgstr "Proxy-Weiterleitung"
 msgstr "Proxy-Weiterleitung"
 
 
@@ -4256,7 +4263,7 @@ msgstr "Erfolgreich entfernt"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Erfolgreich entfernt"
 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/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
 #: src/views/config/ConfigList.vue:190
@@ -4615,9 +4622,9 @@ msgstr "Samstag"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
 #: 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/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgid "Save"
 msgstr "Speichern"
 msgstr "Speichern"
 
 
@@ -4675,7 +4682,7 @@ msgstr "Erfolgreich gespeichert"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
 #: 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
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Speichern erfolgreich"
 msgstr "Speichern erfolgreich"
@@ -4991,7 +4998,7 @@ msgid "SSO Login"
 msgstr "SSO-Anmeldung"
 msgstr "SSO-Anmeldung"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:165
 #: 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
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgid "Stable"
 msgstr "Stabil"
 msgstr "Stabil"
@@ -5505,7 +5512,7 @@ msgstr ""
 "Dadurch werden Konfigurationsdateien und Datenbank wiederhergestellt. Die "
 "Dadurch werden Konfigurationsdateien und Datenbank wiederhergestellt. Die "
 "Nginx UI wird nach Abschluss der Wiederherstellung neu gestartet."
 "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}."
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 msgstr ""
 "Dies wird das Nginx UI auf %{nodeNames} auf %{version} aktualisieren oder "
 "Dies wird das Nginx UI auf %{nodeNames} auf %{version} aktualisieren oder "
@@ -5658,7 +5665,7 @@ msgstr "Dienstag"
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "Zwei-Faktor-Authentifizierung erforderlich"
 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/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5739,11 +5746,11 @@ msgstr "Dateien hochladen"
 msgid "Upload Folders"
 msgid "Upload Folders"
 msgstr "Ordner hochladen"
 msgstr "Ordner hochladen"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgid "Upstream"
 msgstr "Upstream"
 msgstr "Upstream"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgid "Upstream Name"
 msgstr "Upstream-Name"
 msgstr "Upstream-Name"
 
 
@@ -5836,7 +5843,7 @@ msgstr "Anzeigen"
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "Alle Benachrichtigungen anzeigen"
 msgstr "Alle Benachrichtigungen anzeigen"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgid "View on GitHub"
 msgstr "Auf GitHub anzeigen"
 msgstr "Auf GitHub anzeigen"
@@ -6067,6 +6074,9 @@ msgstr "Ihre alten Codes funktionieren nicht mehr."
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "Deine Passkeys"
 msgstr "Deine Passkeys"
 
 
+#~ msgid "Main"
+#~ msgstr "Haupt"
+
 #~ msgid "Select an action after sync"
 #~ msgid "Select an action after sync"
 #~ msgstr "Aktion nach der Synchronisierung auswählen"
 #~ 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 ""
 msgstr ""
 
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
 #: 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 ""
 msgstr ""
 
 
 #: src/views/user/userColumns.tsx:30
 #: src/views/user/userColumns.tsx:30
@@ -147,7 +146,7 @@ msgid "Actual worker to configured ratio"
 msgstr ""
 msgstr ""
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
 #: 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/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -190,7 +189,7 @@ msgstr ""
 msgid "Add Stream"
 msgid "Add Stream"
 msgstr ""
 msgstr ""
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgid "Add Upstream"
 msgstr ""
 msgstr ""
 
 
@@ -202,8 +201,8 @@ msgstr ""
 msgid "Additional"
 msgid "Additional"
 msgstr ""
 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"
 msgid "Advance Mode"
 msgstr ""
 msgstr ""
 
 
@@ -409,12 +408,16 @@ msgstr ""
 msgid "Automatic Restart"
 msgid "Automatic Restart"
 msgstr ""
 msgstr ""
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr ""
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
 #: 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"
 msgid "Back"
 msgstr ""
 msgstr ""
 
 
@@ -509,8 +512,8 @@ msgstr ""
 msgid "Basic"
 msgid "Basic"
 msgstr ""
 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"
 msgid "Basic Mode"
 msgstr ""
 msgstr ""
 
 
@@ -597,7 +600,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
 #: 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/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
 #: 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 ""
 msgstr ""
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
 #: 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/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
 #: src/views/config/components/Delete.vue:97
@@ -1351,6 +1354,10 @@ msgstr ""
 msgid "Dev"
 msgid "Dev"
 msgstr ""
 msgstr ""
 
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr ""
+
 #: src/views/system/About.vue:28
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr ""
 msgstr ""
@@ -1453,9 +1460,9 @@ msgstr ""
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
 #: 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/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
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgid "Disabled"
 msgstr ""
 msgstr ""
@@ -1508,7 +1515,7 @@ msgstr ""
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr ""
 msgstr ""
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr ""
 msgstr ""
 
 
@@ -1559,7 +1566,7 @@ msgstr ""
 msgid "Drop private key file here"
 msgid "Drop private key file here"
 msgstr ""
 msgstr ""
 
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr ""
 msgstr ""
@@ -1595,8 +1602,8 @@ msgstr ""
 msgid "Edit"
 msgid "Edit"
 msgstr ""
 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}"
 msgid "Edit %{n}"
 msgstr ""
 msgstr ""
 
 
@@ -1729,10 +1736,10 @@ msgstr ""
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
 #: 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/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
 #: 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
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgid "Enabled"
 msgstr ""
 msgstr ""
@@ -2340,7 +2347,7 @@ msgstr ""
 msgid "Get dns credential error: {0}"
 msgid "Get dns credential error: {0}"
 msgstr ""
 msgstr ""
 
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgid "Get release information error"
 msgstr ""
 msgstr ""
@@ -2382,8 +2389,8 @@ msgid "Higher value means better connection reuse"
 msgstr ""
 msgstr ""
 
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
 #: 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"
 msgid "History"
 msgstr ""
 msgstr ""
 
 
@@ -2826,16 +2833,13 @@ msgid ""
 "minutes."
 "minutes."
 msgstr ""
 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"
 msgid "Main Node"
 msgstr ""
 msgstr ""
 
 
 #: src/views/site/components/SiteStatusSelect.vue:165
 #: 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
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgid "Maintenance"
 msgstr ""
 msgstr ""
@@ -3035,11 +3039,12 @@ msgstr ""
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr ""
 msgstr ""
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgid "N/A"
 msgstr ""
 msgstr ""
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3176,8 +3181,8 @@ msgstr ""
 msgid "Nginx configuration has been restored"
 msgid "Nginx configuration has been restored"
 msgstr ""
 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"
 msgid "Nginx Configuration Parse Error"
 msgstr ""
 msgstr ""
 
 
@@ -3358,7 +3363,7 @@ msgstr ""
 msgid "No servers configured"
 msgid "No servers configured"
 msgstr ""
 msgstr ""
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgid "No upstreams configured"
 msgstr ""
 msgstr ""
 
 
@@ -3385,7 +3390,7 @@ msgstr ""
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr ""
 msgstr ""
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgid "Node Status"
 msgstr ""
 msgstr ""
 
 
@@ -3486,10 +3491,9 @@ msgid "Official Document"
 msgstr ""
 msgstr ""
 
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
 #: 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/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
 msgid "Offline"
@@ -3497,7 +3501,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
 #: 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/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/notification/Notification.vue:39
@@ -3524,12 +3528,16 @@ msgstr ""
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
 #: 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/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgid "Online"
 msgstr ""
 msgstr ""
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr ""
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgid "Only zip files are allowed"
 msgstr ""
 msgstr ""
@@ -3666,7 +3674,7 @@ msgstr ""
 msgid "Pending"
 msgid "Pending"
 msgstr ""
 msgstr ""
 
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgid "Perform"
 msgstr ""
 msgstr ""
 
 
@@ -3867,7 +3875,7 @@ msgid "Post-sync Action"
 msgstr ""
 msgstr ""
 
 
 #: src/views/environments/list/BatchUpgrader.vue:168
 #: 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
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr ""
 msgstr ""
@@ -3935,7 +3943,7 @@ msgstr ""
 msgid "Proxy"
 msgid "Proxy"
 msgstr ""
 msgstr ""
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgid "Proxy Pass"
 msgstr ""
 msgstr ""
 
 
@@ -4096,7 +4104,7 @@ msgstr ""
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
 #: src/views/config/ConfigList.vue:190
@@ -4448,9 +4456,9 @@ msgstr ""
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
 #: 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/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgid "Save"
 msgstr ""
 msgstr ""
 
 
@@ -4508,7 +4516,7 @@ msgstr ""
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
 #: 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
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr ""
 msgstr ""
@@ -4810,7 +4818,7 @@ msgid "SSO Login"
 msgstr ""
 msgstr ""
 
 
 #: src/views/environments/list/BatchUpgrader.vue:165
 #: 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
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgid "Stable"
 msgstr ""
 msgstr ""
@@ -5274,7 +5282,7 @@ msgid ""
 "after the restoration is complete."
 "after the restoration is complete."
 msgstr ""
 msgstr ""
 
 
-#: src/views/environments/list/BatchUpgrader.vue:183
+#: src/views/environments/list/BatchUpgrader.vue:186
 msgid ""
 msgid ""
 "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 msgstr ""
@@ -5409,7 +5417,7 @@ msgstr ""
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr ""
 msgstr ""
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5490,11 +5498,11 @@ msgstr ""
 msgid "Upload Folders"
 msgid "Upload Folders"
 msgstr ""
 msgstr ""
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgid "Upstream"
 msgstr ""
 msgstr ""
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgid "Upstream Name"
 msgstr ""
 msgstr ""
 
 
@@ -5587,7 +5595,7 @@ msgstr ""
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr ""
 msgstr ""
 
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgid "View on GitHub"
 msgstr ""
 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"
 msgstr "[Nginx UI] Escribiendo certificado en el disco"
 
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
 #: 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
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
 msgid "2FA"
@@ -169,7 +169,7 @@ msgid "Actual worker to configured ratio"
 msgstr "Proporción real de trabajadores a configurados"
 msgstr "Proporción real de trabajadores a configurados"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
 #: 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/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -212,7 +212,7 @@ msgstr "Agregar Sitio"
 msgid "Add Stream"
 msgid "Add Stream"
 msgstr "Agregar Stream"
 msgstr "Agregar Stream"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgid "Add Upstream"
 msgstr "Agregar upstream"
 msgstr "Agregar upstream"
 
 
@@ -224,8 +224,8 @@ msgstr "Agregado exitoso"
 msgid "Additional"
 msgid "Additional"
 msgstr "Adicional"
 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"
 msgid "Advance Mode"
 msgstr "Modo avanzado"
 msgstr "Modo avanzado"
 
 
@@ -439,12 +439,16 @@ msgstr "AutoCert se está ejecutando..."
 msgid "Automatic Restart"
 msgid "Automatic Restart"
 msgstr "Reinicio automático"
 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/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
 #: 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"
 msgid "Back"
 msgstr "Atrás"
 msgstr "Atrás"
 
 
@@ -548,8 +552,8 @@ msgstr "Información general"
 msgid "Basic"
 msgid "Basic"
 msgstr "Básico"
 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"
 msgid "Basic Mode"
 msgstr "Modo Básico"
 msgstr "Modo Básico"
 
 
@@ -642,7 +646,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
 #: 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/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1330,7 +1334,7 @@ msgstr ""
 "proxy_cache:10m"
 "proxy_cache:10m"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
 #: 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/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
 #: src/views/config/components/Delete.vue:97
@@ -1442,6 +1446,10 @@ msgstr "Detalles"
 msgid "Dev"
 msgid "Dev"
 msgstr "Dev"
 msgstr "Dev"
 
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Desarrollo"
+
 #: src/views/system/About.vue:28
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "Modo de desarrollo"
 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:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
 #: 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/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
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Desactivado"
 msgstr "Desactivado"
@@ -1599,7 +1607,7 @@ msgstr "¿Quieres habilitar TLS?"
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "¿Quieres eliminar este servidor?"
 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?"
 msgid "Do you want to remove this upstream?"
 msgstr "¿Quieres eliminar esta transmisión?"
 msgstr "¿Quieres eliminar esta transmisión?"
 
 
@@ -1651,7 +1659,7 @@ msgstr "Suelta el archivo de certificado aquí"
 msgid "Drop private key file here"
 msgid "Drop private key file here"
 msgstr "Suelta el archivo de clave privada aquí"
 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
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "Modo de ejecución de prueba habilitado"
 msgstr "Modo de ejecución de prueba habilitado"
@@ -1690,8 +1698,8 @@ msgstr "p. ej., 0 0 * * * (diariamente a medianoche)"
 msgid "Edit"
 msgid "Edit"
 msgstr "Editar"
 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}"
 msgid "Edit %{n}"
 msgstr "Editar %{n}"
 msgstr "Editar %{n}"
 
 
@@ -1824,10 +1832,10 @@ msgstr "Habilitar TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
 #: 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/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
 #: 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
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Habilitado"
 msgstr "Habilitado"
@@ -2443,7 +2451,7 @@ msgstr "Error al obtener los datos"
 msgid "Get dns credential error: {0}"
 msgid "Get dns credential error: {0}"
 msgstr "Error al obtener las credenciales DNS: {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
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgid "Get release information error"
 msgstr "Obtener error de información de versión"
 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"
 msgstr "Un valor más alto significa una mejor reutilización de la conexión"
 
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
 #: 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"
 msgid "History"
 msgstr "Historial"
 msgstr "Historial"
 
 
@@ -2951,16 +2959,13 @@ msgstr ""
 "de Nginx UI ejecutará el comando logrotate en el intervalo que establezca "
 "de Nginx UI ejecutará el comando logrotate en el intervalo que establezca "
 "en minutos."
 "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"
 msgid "Main Node"
 msgstr "Nodo principal"
 msgstr "Nodo principal"
 
 
 #: src/views/site/components/SiteStatusSelect.vue:165
 #: 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
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgid "Maintenance"
 msgstr "Mantenimiento"
 msgstr "Mantenimiento"
@@ -3162,11 +3167,12 @@ msgstr "Mensualmente el día %{day} a las %{time}"
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "Directiva multilínea"
 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"
 msgid "N/A"
 msgstr "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/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: 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"
 msgid "Nginx configuration has been restored"
 msgstr "La configuración de Nginx ha sido restaurada"
 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"
 msgid "Nginx Configuration Parse Error"
 msgstr "Error de análisis de configuración de Nginx"
 msgstr "Error de análisis de configuración de Nginx"
 
 
@@ -3487,7 +3493,7 @@ msgstr "No se han seleccionado registros"
 msgid "No servers configured"
 msgid "No servers configured"
 msgstr "No hay servidores configurados"
 msgstr "No hay servidores configurados"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgid "No upstreams configured"
 msgstr "No hay upstreams configurados"
 msgstr "No hay upstreams configurados"
 
 
@@ -3514,7 +3520,7 @@ msgstr "Nombre del nodo"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "Secreto del nodo"
 msgstr "Secreto del nodo"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgid "Node Status"
 msgstr "Estado del nodo"
 msgstr "Estado del nodo"
 
 
@@ -3622,10 +3628,9 @@ msgid "Official Document"
 msgstr "Documentación oficial"
 msgstr "Documentación oficial"
 
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
 #: 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/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
 msgid "Offline"
@@ -3633,7 +3638,7 @@ msgstr "Desconectado"
 
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
 #: 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/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: 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/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
 #: 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/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgid "Online"
 msgstr "En línea"
 msgstr "En línea"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Recuento en línea"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgid "Only zip files are allowed"
 msgstr "Solo se permiten archivos ZIP"
 msgstr "Solo se permiten archivos ZIP"
@@ -3806,7 +3815,7 @@ msgstr "El recurso de carga útil es nulo"
 msgid "Pending"
 msgid "Pending"
 msgstr "Pendiente"
 msgstr "Pendiente"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgid "Perform"
 msgstr "Realizar"
 msgstr "Realizar"
 
 
@@ -4027,7 +4036,7 @@ msgid "Post-sync Action"
 msgstr "Acción posterior a la sincronización"
 msgstr "Acción posterior a la sincronización"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:168
 #: 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
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "Prelanzamiento"
 msgstr "Prelanzamiento"
@@ -4099,7 +4108,7 @@ msgstr "Proveedor no encontrado: {0}"
 msgid "Proxy"
 msgid "Proxy"
 msgstr "Proxy"
 msgstr "Proxy"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgid "Proxy Pass"
 msgstr "Pase de Proxy"
 msgstr "Pase de Proxy"
 
 
@@ -4265,7 +4274,7 @@ msgstr "Eliminado con éxito"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Eliminado con éxito"
 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/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
 #: src/views/config/ConfigList.vue:190
@@ -4622,9 +4631,9 @@ msgstr "Sábado"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
 #: 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/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgid "Save"
 msgstr "Guardar"
 msgstr "Guardar"
 
 
@@ -4682,7 +4691,7 @@ msgstr "Guardado con éxito"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
 #: 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
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Guardado con éxito"
 msgstr "Guardado con éxito"
@@ -5000,7 +5009,7 @@ msgid "SSO Login"
 msgstr "Acceso SSO"
 msgstr "Acceso SSO"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:165
 #: 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
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgid "Stable"
 msgstr "Estable"
 msgstr "Estable"
@@ -5510,7 +5519,7 @@ msgstr ""
 "Esto restaurará los archivos de configuración y la base de datos. La "
 "Esto restaurará los archivos de configuración y la base de datos. La "
 "interfaz de Nginx se reiniciará una vez completada la restauración."
 "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}."
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 msgstr ""
 "Esto actualizará o reinstalará la interfaz de usuario de Nginx en "
 "Esto actualizará o reinstalará la interfaz de usuario de Nginx en "
@@ -5664,7 +5673,7 @@ msgstr "Martes"
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "Se requiere autenticación de dos factores"
 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/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5745,11 +5754,11 @@ msgstr "Subir archivos"
 msgid "Upload Folders"
 msgid "Upload Folders"
 msgstr "Subir carpetas"
 msgstr "Subir carpetas"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgid "Upstream"
 msgstr "Aguas arriba"
 msgstr "Aguas arriba"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgid "Upstream Name"
 msgstr "Nombre de la Transmisión"
 msgstr "Nombre de la Transmisión"
 
 
@@ -5842,7 +5851,7 @@ msgstr "Ver"
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "Ver todas las notificaciones"
 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
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgid "View on GitHub"
 msgstr "Ver en GitHub"
 msgstr "Ver en GitHub"
@@ -6070,6 +6079,9 @@ msgstr "Tus códigos antiguos ya no funcionarán."
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "Sus llaves de acceso"
 msgstr "Sus llaves de acceso"
 
 
+#~ msgid "Main"
+#~ msgstr "Principal"
+
 #~ msgid "Select an action after sync"
 #~ msgid "Select an action after sync"
 #~ msgstr "Seleccionar una acción después de sincronizar"
 #~ 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"
 msgstr "[Nginx UI] Écriture du certificat sur le disque"
 
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
 #: 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 ""
 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"
 "manuellement"
 
 
 #: src/views/user/userColumns.tsx:30
 #: 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"
 msgstr "Ratio réel des travailleurs par rapport à la configuration"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
 #: 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/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
 #: 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"
 msgid "Add Stream"
 msgstr "Ajouter un flux"
 msgstr "Ajouter un flux"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgid "Add Upstream"
 msgstr "Ajouter un upstream"
 msgstr "Ajouter un upstream"
 
 
@@ -224,8 +224,8 @@ msgstr "Ajouté avec succès"
 msgid "Additional"
 msgid "Additional"
 msgstr "Supplémentaire"
 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"
 msgid "Advance Mode"
 msgstr "Mode avancé"
 msgstr "Mode avancé"
 
 
@@ -439,12 +439,16 @@ msgstr "AutoCert est en cours d'exécution..."
 msgid "Automatic Restart"
 msgid "Automatic Restart"
 msgstr "Redémarrage automatique"
 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/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
 #: 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"
 msgid "Back"
 msgstr "Retour"
 msgstr "Retour"
 
 
@@ -548,8 +552,8 @@ msgstr "Information générale"
 msgid "Basic"
 msgid "Basic"
 msgstr "Basique"
 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"
 msgid "Basic Mode"
 msgstr "Mode simple"
 msgstr "Mode simple"
 
 
@@ -639,7 +643,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
 #: 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/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1328,7 +1332,7 @@ msgstr ""
 "proxy_cache:10m"
 "proxy_cache:10m"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
 #: 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/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
 #: src/views/config/components/Delete.vue:97
@@ -1440,6 +1444,10 @@ msgstr "Détails"
 msgid "Dev"
 msgid "Dev"
 msgstr "Dev"
 msgstr "Dev"
 
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Développement"
+
 #: src/views/system/About.vue:28
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "Mode développement"
 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:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
 #: 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/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
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Désactivé"
 msgstr "Désactivé"
@@ -1597,7 +1605,7 @@ msgstr "Voulez-vous activer TLS ?"
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "Voulez-vous supprimer ce serveur ?"
 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?"
 msgid "Do you want to remove this upstream?"
 msgstr "Voulez-vous supprimer cet 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"
 msgid "Drop private key file here"
 msgstr "Déposer le fichier de clé privée ici"
 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
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "Mode de simulation activé"
 msgstr "Mode de simulation activé"
@@ -1686,8 +1694,8 @@ msgstr "par ex., 0 0 * * * (quotidiennement à minuit)"
 msgid "Edit"
 msgid "Edit"
 msgstr "Modifier"
 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}"
 msgid "Edit %{n}"
 msgstr "Modifier %{n}"
 msgstr "Modifier %{n}"
 
 
@@ -1820,10 +1828,10 @@ msgstr "Activer TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
 #: 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/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
 #: 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
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Activé"
 msgstr "Activé"
@@ -2439,7 +2447,7 @@ msgstr "Échec de la récupération des données"
 msgid "Get dns credential error: {0}"
 msgid "Get dns credential error: {0}"
 msgstr "Erreur lors de la récupération des informations d'identification DNS : {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
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgid "Get release information error"
 msgstr "Erreur d'obtention des informations sur la version"
 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"
 msgstr "Une valeur plus élevée signifie une meilleure réutilisation de la connexion"
 
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
 #: 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"
 msgid "History"
 msgstr "Historique"
 msgstr "Historique"
 
 
@@ -2951,16 +2959,13 @@ msgstr ""
 "planificateur de tâches crontab de Nginx UI exécutera la commande logrotate "
 "planificateur de tâches crontab de Nginx UI exécutera la commande logrotate "
 "à l'intervalle que vous avez défini en minutes."
 "à 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"
 msgid "Main Node"
 msgstr "Nœud principal"
 msgstr "Nœud principal"
 
 
 #: src/views/site/components/SiteStatusSelect.vue:165
 #: 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
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgid "Maintenance"
 msgstr "Maintenance"
 msgstr "Maintenance"
@@ -3162,11 +3167,12 @@ msgstr "Mensuellement le jour %{day} à %{time}"
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "Directive multiligne"
 msgstr "Directive multiligne"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgid "N/A"
 msgstr "N/D"
 msgstr "N/D"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: 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"
 msgid "Nginx configuration has been restored"
 msgstr "La configuration de Nginx a été restaurée"
 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"
 msgid "Nginx Configuration Parse Error"
 msgstr "Erreur d'analyse de configuration Nginx"
 msgstr "Erreur d'analyse de configuration Nginx"
 
 
@@ -3487,7 +3493,7 @@ msgstr "Aucun enregistrement sélectionné"
 msgid "No servers configured"
 msgid "No servers configured"
 msgstr "Aucun serveur configuré"
 msgstr "Aucun serveur configuré"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgid "No upstreams configured"
 msgstr "Aucun amont configuré"
 msgstr "Aucun amont configuré"
 
 
@@ -3514,7 +3520,7 @@ msgstr "Nom du nœud"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "Secret du nœud"
 msgstr "Secret du nœud"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgid "Node Status"
 msgstr "État du nœud"
 msgstr "État du nœud"
 
 
@@ -3621,10 +3627,9 @@ msgid "Official Document"
 msgstr "Documentation officielle"
 msgstr "Documentation officielle"
 
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
 #: 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/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
 msgid "Offline"
@@ -3632,7 +3637,7 @@ msgstr "Hors ligne"
 
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
 #: 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/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: 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/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
 #: 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/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgid "Online"
 msgstr "En ligne"
 msgstr "En ligne"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Nombre en ligne"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgid "Only zip files are allowed"
 msgstr "Seuls les fichiers ZIP sont autorisés"
 msgstr "Seuls les fichiers ZIP sont autorisés"
@@ -3807,7 +3816,7 @@ msgstr "La ressource de charge utile est nulle"
 msgid "Pending"
 msgid "Pending"
 msgstr "En attente"
 msgstr "En attente"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgid "Perform"
 msgstr "Exécuter"
 msgstr "Exécuter"
 
 
@@ -4026,7 +4035,7 @@ msgid "Post-sync Action"
 msgstr "Action post-synchronisation"
 msgstr "Action post-synchronisation"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:168
 #: 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
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "Pré-version"
 msgstr "Pré-version"
@@ -4098,7 +4107,7 @@ msgstr "Fournisseur introuvable : {0}"
 msgid "Proxy"
 msgid "Proxy"
 msgstr "Proxy"
 msgstr "Proxy"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgid "Proxy Pass"
 msgstr "Passe de Proxy"
 msgstr "Passe de Proxy"
 
 
@@ -4266,7 +4275,7 @@ msgstr "Suppression réussie"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Supprimé avec succès"
 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/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
 #: src/views/config/ConfigList.vue:190
@@ -4623,9 +4632,9 @@ msgstr "Samedi"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
 #: 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/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgid "Save"
 msgstr "Enregistrer"
 msgstr "Enregistrer"
 
 
@@ -4683,7 +4692,7 @@ msgstr "Sauvegarde réussie"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
 #: 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
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Enregistré avec succès"
 msgstr "Enregistré avec succès"
@@ -5001,7 +5010,7 @@ msgid "SSO Login"
 msgstr "Connexion SSO"
 msgstr "Connexion SSO"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:165
 #: 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
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgid "Stable"
 msgstr "Stable"
 msgstr "Stable"
@@ -5519,7 +5528,7 @@ msgstr ""
 "Cela restaurera les fichiers de configuration et la base de données. "
 "Cela restaurera les fichiers de configuration et la base de données. "
 "L'interface Nginx redémarrera une fois la restauration terminée."
 "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}."
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 msgstr ""
 "Cela mettra à jour ou réinstallera l'interface Nginx sur %{nodeNames} vers "
 "Cela mettra à jour ou réinstallera l'interface Nginx sur %{nodeNames} vers "
@@ -5674,7 +5683,7 @@ msgstr "Mardi"
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "Authentification à deux facteurs requise"
 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/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5755,11 +5764,11 @@ msgstr "Téléverser des fichiers"
 msgid "Upload Folders"
 msgid "Upload Folders"
 msgstr "Télécharger des dossiers"
 msgstr "Télécharger des dossiers"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgid "Upstream"
 msgstr "Amont"
 msgstr "Amont"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgid "Upstream Name"
 msgstr "Nom de l'amont"
 msgstr "Nom de l'amont"
 
 
@@ -5852,7 +5861,7 @@ msgstr "Voir"
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "Voir toutes les 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
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgid "View on GitHub"
 msgstr "Voir sur GitHub"
 msgstr "Voir sur GitHub"
@@ -6083,6 +6092,9 @@ msgstr "Vos anciens codes ne fonctionneront plus."
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "Vos clés d'accès"
 msgstr "Vos clés d'accès"
 
 
+#~ msgid "Main"
+#~ msgstr "Principal"
+
 #~ msgid "Select an action after sync"
 #~ msgid "Select an action after sync"
 #~ msgstr "Sélectionner une action après la synchronisation"
 #~ 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] 証明書をディスクに書き込み中"
 msgstr "[Nginx UI] 証明書をディスクに書き込み中"
 
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
 #: 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
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
 msgid "2FA"
@@ -163,7 +163,7 @@ msgid "Actual worker to configured ratio"
 msgstr "実際のワーカー数と設定値の比率"
 msgstr "実際のワーカー数と設定値の比率"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
 #: 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/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -206,7 +206,7 @@ msgstr "サイトを追加"
 msgid "Add Stream"
 msgid "Add Stream"
 msgstr "Streamを追加"
 msgstr "Streamを追加"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgid "Add Upstream"
 msgstr "アップストリームを追加"
 msgstr "アップストリームを追加"
 
 
@@ -218,8 +218,8 @@ msgstr "正常に追加されました"
 msgid "Additional"
 msgid "Additional"
 msgstr "追加設定"
 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"
 msgid "Advance Mode"
 msgstr "アドバンスモード"
 msgstr "アドバンスモード"
 
 
@@ -425,12 +425,16 @@ msgstr "AutoCert が実行中..."
 msgid "Automatic Restart"
 msgid "Automatic Restart"
 msgstr "自動再起動"
 msgstr "自動再起動"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "平均レイテンシ"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
 #: 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"
 msgid "Back"
 msgstr "戻る"
 msgstr "戻る"
 
 
@@ -524,8 +528,8 @@ msgstr "基本情報"
 msgid "Basic"
 msgid "Basic"
 msgstr "基本"
 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"
 msgid "Basic Mode"
 msgstr "基本モード"
 msgstr "基本モード"
 
 
@@ -614,7 +618,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
 #: 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/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
 #: 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)"
 msgstr "共有メモリゾーンの名前とサイズを定義します(例: proxy_cache:10m)"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
 #: 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/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
 #: src/views/config/components/Delete.vue:97
@@ -1387,6 +1391,10 @@ msgstr "詳細"
 msgid "Dev"
 msgid "Dev"
 msgstr "Dev"
 msgstr "Dev"
 
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "開発"
+
 #: src/views/system/About.vue:28
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "開発モード"
 msgstr "開発モード"
@@ -1489,9 +1497,9 @@ msgstr "ストリーム %{name} を %{node} から無効化しました"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
 #: 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/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
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgid "Disabled"
 msgstr "無効"
 msgstr "無効"
@@ -1544,7 +1552,7 @@ msgstr "TLSを有効にしますか?"
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "このサーバーを削除しますか?"
 msgstr "このサーバーを削除しますか?"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr "このアップストリームを削除しますか?"
 msgstr "このアップストリームを削除しますか?"
 
 
@@ -1594,7 +1602,7 @@ msgstr "証明書ファイルをここにドロップ"
 msgid "Drop private key file here"
 msgid "Drop private key file here"
 msgstr "秘密鍵ファイルをここにドロップ"
 msgstr "秘密鍵ファイルをここにドロップ"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "ドライランモードが有効です"
 msgstr "ドライランモードが有効です"
@@ -1630,8 +1638,8 @@ msgstr "例: 0 0 * * * (毎日深夜0時)"
 msgid "Edit"
 msgid "Edit"
 msgstr "編集"
 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}"
 msgid "Edit %{n}"
 msgstr "%{n} を編集"
 msgstr "%{n} を編集"
 
 
@@ -1764,10 +1772,10 @@ msgstr "TOTP を有効にする"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
 #: 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/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
 #: 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
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgid "Enabled"
 msgstr "有効"
 msgstr "有効"
@@ -2375,7 +2383,7 @@ msgstr "データの取得に失敗しました"
 msgid "Get dns credential error: {0}"
 msgid "Get dns credential error: {0}"
 msgstr "DNS認証情報の取得エラー: {0}"
 msgstr "DNS認証情報の取得エラー: {0}"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgid "Get release information error"
 msgstr "リリース情報の取得エラー"
 msgstr "リリース情報の取得エラー"
@@ -2417,8 +2425,8 @@ msgid "Higher value means better connection reuse"
 msgstr "値が高いほど接続の再利用が効率的であることを意味します"
 msgstr "値が高いほど接続の再利用が効率的であることを意味します"
 
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
 #: 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"
 msgid "History"
 msgstr "履歴"
 msgstr "履歴"
 
 
@@ -2867,16 +2875,13 @@ msgstr ""
 "Nginx UI をインストールするユーザーは、このオプションを手動で有効にすることができます。Nginx UI の crontab "
 "Nginx UI をインストールするユーザーは、このオプションを手動で有効にすることができます。Nginx UI の crontab "
 "タスクスケジューラは、設定した間隔(分単位)で logrotate コマンドを実行します。"
 "タスクスケジューラは、設定した間隔(分単位)で 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"
 msgid "Main Node"
 msgstr "メインノード"
 msgstr "メインノード"
 
 
 #: src/views/site/components/SiteStatusSelect.vue:165
 #: 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
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgid "Maintenance"
 msgstr "メンテナンス"
 msgstr "メンテナンス"
@@ -3078,11 +3083,12 @@ msgstr "毎月%{day}日%{time}に"
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "複数行ディレクティブ"
 msgstr "複数行ディレクティブ"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgid "N/A"
 msgstr "該当なし"
 msgstr "該当なし"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3219,8 +3225,8 @@ msgstr "Nginx設定エントリファイルが存在します"
 msgid "Nginx configuration has been restored"
 msgid "Nginx configuration has been restored"
 msgstr "Nginxの設定が復元されました"
 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"
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx設定解析エラー"
 msgstr "Nginx設定解析エラー"
 
 
@@ -3401,7 +3407,7 @@ msgstr "選択されたレコードがありません"
 msgid "No servers configured"
 msgid "No servers configured"
 msgstr "サーバーが設定されていません"
 msgstr "サーバーが設定されていません"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgid "No upstreams configured"
 msgstr "アップストリームが設定されていません"
 msgstr "アップストリームが設定されていません"
 
 
@@ -3428,7 +3434,7 @@ msgstr "ノード名"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "ノードシークレット"
 msgstr "ノードシークレット"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgid "Node Status"
 msgstr "ノードステータス"
 msgstr "ノードステータス"
 
 
@@ -3529,10 +3535,9 @@ msgid "Official Document"
 msgstr "公式ドキュメント"
 msgstr "公式ドキュメント"
 
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
 #: 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/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
 msgid "Offline"
@@ -3540,7 +3545,7 @@ msgstr "オフライン"
 
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
 #: 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/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/notification/Notification.vue:39
@@ -3567,12 +3572,16 @@ msgstr "検証が完了すると、レコードは削除されます。"
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
 #: 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/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgid "Online"
 msgstr "オンライン"
 msgstr "オンライン"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "オンライン数"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgid "Only zip files are allowed"
 msgstr "ZIPファイルのみ許可されています"
 msgstr "ZIPファイルのみ許可されています"
@@ -3711,7 +3720,7 @@ msgstr "ペイロードリソースが nil です"
 msgid "Pending"
 msgid "Pending"
 msgstr "保留中"
 msgstr "保留中"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgid "Perform"
 msgstr "実行"
 msgstr "実行"
 
 
@@ -3913,7 +3922,7 @@ msgid "Post-sync Action"
 msgstr "同期後のアクション"
 msgstr "同期後のアクション"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:168
 #: 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
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "プレリリース"
 msgstr "プレリリース"
@@ -3982,7 +3991,7 @@ msgstr "プロバイダーが見つかりません: {0}"
 msgid "Proxy"
 msgid "Proxy"
 msgstr "プロキシ"
 msgstr "プロキシ"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgid "Proxy Pass"
 msgstr "プロキシパス"
 msgstr "プロキシパス"
 
 
@@ -4143,7 +4152,7 @@ msgstr "削除に成功しました"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "正常に削除されました"
 msgstr "正常に削除されました"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
 #: src/views/config/ConfigList.vue:190
@@ -4495,9 +4504,9 @@ msgstr "土曜日"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
 #: 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/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgid "Save"
 msgstr "保存"
 msgstr "保存"
 
 
@@ -4555,7 +4564,7 @@ msgstr "保存に成功しました"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
 #: 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
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "正常に保存されました"
 msgstr "正常に保存されました"
@@ -4861,7 +4870,7 @@ msgid "SSO Login"
 msgstr "SSOログイン"
 msgstr "SSOログイン"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:165
 #: 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
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgid "Stable"
 msgstr "安定版"
 msgstr "安定版"
@@ -5334,7 +5343,7 @@ msgid ""
 "after the restoration is complete."
 "after the restoration is complete."
 msgstr "これにより設定ファイルとデータベースが復元されます。復元が完了すると、Nginx UI が再起動します。"
 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}."
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr "これにより、%{nodeNames} 上の Nginx UI が %{version} にアップグレードまたは再インストールされます。"
 msgstr "これにより、%{nodeNames} 上の Nginx UI が %{version} にアップグレードまたは再インストールされます。"
 
 
@@ -5476,7 +5485,7 @@ msgstr "火曜日"
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "二要素認証が必要です"
 msgstr "二要素認証が必要です"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5557,11 +5566,11 @@ msgstr "ファイルをアップロード"
 msgid "Upload Folders"
 msgid "Upload Folders"
 msgstr "フォルダをアップロード"
 msgstr "フォルダをアップロード"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgid "Upstream"
 msgstr "アップストリーム"
 msgstr "アップストリーム"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgid "Upstream Name"
 msgstr "アップストリーム名"
 msgstr "アップストリーム名"
 
 
@@ -5654,7 +5663,7 @@ msgstr "ビュー"
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "すべての通知を表示します"
 msgstr "すべての通知を表示します"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgid "View on GitHub"
 msgstr "GitHubで見る"
 msgstr "GitHubで見る"
@@ -5858,6 +5867,9 @@ msgstr "以前のコードはもう使えません。"
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "あなたのパスキー"
 msgstr "あなたのパスキー"
 
 
+#~ msgid "Main"
+#~ msgstr "メイン"
+
 #~ msgid "Select an action after sync"
 #~ msgid "Select an action after sync"
 #~ msgstr "同期後のアクションを選択"
 #~ msgstr "同期後のアクションを選択"
 
 

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

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

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

@@ -87,7 +87,7 @@ msgid "[Nginx UI] Writing certificate to disk"
 msgstr ""
 msgstr ""
 
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
 #: 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 ""
 msgstr ""
 
 
 #: src/views/user/userColumns.tsx:30
 #: src/views/user/userColumns.tsx:30
@@ -151,7 +151,7 @@ msgid "Actual worker to configured ratio"
 msgstr ""
 msgstr ""
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
 #: src/components/NgxConfigEditor/NgxServer.vue:144
-#: src/components/NgxConfigEditor/NgxUpstream.vue:166
+#: src/components/NgxConfigEditor/NgxUpstream.vue:97
 #: src/language/curd.ts:19
 #: src/language/curd.ts:19
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
@@ -197,7 +197,7 @@ msgstr ""
 msgid "Add Stream"
 msgid "Add Stream"
 msgstr ""
 msgstr ""
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgid "Add Upstream"
 msgstr ""
 msgstr ""
 
 
@@ -209,8 +209,8 @@ msgstr ""
 msgid "Additional"
 msgid "Additional"
 msgstr ""
 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"
 msgid "Advance Mode"
 msgstr ""
 msgstr ""
 
 
@@ -415,13 +415,17 @@ msgstr ""
 msgid "Automatic Restart"
 msgid "Automatic Restart"
 msgstr ""
 msgstr ""
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr ""
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120
 #: src/views/config/ConfigList.vue:120
 #: src/views/config/ConfigList.vue:217
 #: src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
 #: 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"
 msgid "Back"
 msgstr ""
 msgstr ""
 
 
@@ -516,8 +520,8 @@ msgstr ""
 msgid "Basic"
 msgid "Basic"
 msgstr ""
 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"
 msgid "Basic Mode"
 msgstr ""
 msgstr ""
 
 
@@ -602,7 +606,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
 #: src/components/NgxConfigEditor/NgxServer.vue:61
-#: src/components/NgxConfigEditor/NgxUpstream.vue:37
+#: src/components/NgxConfigEditor/NgxUpstream.vue:32
 #: src/language/curd.ts:37
 #: src/language/curd.ts:37
 #: src/views/config/components/Delete.vue:98
 #: src/views/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: 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 ""
 msgstr ""
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
 #: src/components/NgxConfigEditor/NgxServer.vue:110
-#: src/components/NgxConfigEditor/NgxUpstream.vue:134
+#: src/components/NgxConfigEditor/NgxUpstream.vue:78
 #: src/language/curd.ts:9
 #: src/language/curd.ts:9
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:65
@@ -1329,6 +1333,10 @@ msgstr ""
 msgid "Dev"
 msgid "Dev"
 msgstr ""
 msgstr ""
 
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr ""
+
 #: src/views/system/About.vue:28
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr ""
 msgstr ""
@@ -1431,10 +1439,10 @@ msgstr ""
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
 #: 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/site/site_list/columns.tsx:146
 #: src/views/stream/columns.tsx:112
 #: 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
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgid "Disabled"
 msgstr ""
 msgstr ""
@@ -1487,7 +1495,7 @@ msgstr ""
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr ""
 msgstr ""
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr ""
 msgstr ""
 
 
@@ -1538,7 +1546,7 @@ msgstr ""
 msgid "Drop private key file here"
 msgid "Drop private key file here"
 msgstr ""
 msgstr ""
 
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr ""
 msgstr ""
@@ -1572,8 +1580,8 @@ msgstr ""
 msgid "Edit"
 msgid "Edit"
 msgstr ""
 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}"
 msgid "Edit %{n}"
 msgstr ""
 msgstr ""
 
 
@@ -1706,11 +1714,11 @@ msgstr ""
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
 #: 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/site/site_list/columns.tsx:142
 #: src/views/stream/columns.tsx:108
 #: src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
 #: 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
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgid "Enabled"
 msgstr ""
 msgstr ""
@@ -2318,7 +2326,7 @@ msgstr ""
 msgid "Get dns credential error: {0}"
 msgid "Get dns credential error: {0}"
 msgstr ""
 msgstr ""
 
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgid "Get release information error"
 msgstr ""
 msgstr ""
@@ -2360,8 +2368,8 @@ msgid "Higher value means better connection reuse"
 msgstr ""
 msgstr ""
 
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
 #: 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"
 msgid "History"
 msgstr ""
 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."
 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 ""
 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"
 msgid "Main Node"
 msgstr ""
 msgstr ""
 
 
 #: src/views/site/components/SiteStatusSelect.vue:165
 #: 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
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgid "Maintenance"
 msgstr ""
 msgstr ""
@@ -2997,11 +3002,12 @@ msgstr ""
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr ""
 msgstr ""
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgid "N/A"
 msgstr ""
 msgstr ""
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3140,8 +3146,8 @@ msgstr ""
 msgid "Nginx configuration has been restored"
 msgid "Nginx configuration has been restored"
 msgstr ""
 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"
 msgid "Nginx Configuration Parse Error"
 msgstr ""
 msgstr ""
 
 
@@ -3324,7 +3330,7 @@ msgstr ""
 msgid "No servers configured"
 msgid "No servers configured"
 msgstr ""
 msgstr ""
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgid "No upstreams configured"
 msgstr ""
 msgstr ""
 
 
@@ -3352,7 +3358,7 @@ msgstr ""
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr ""
 msgstr ""
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgid "Node Status"
 msgstr ""
 msgstr ""
 
 
@@ -3449,10 +3455,9 @@ msgid "Official Document"
 msgstr ""
 msgstr ""
 
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
 #: 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/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
 msgid "Offline"
@@ -3460,7 +3465,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
 #: 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/components/Notification/Notification.vue:109
 #: src/language/curd.ts:15
 #: src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/backup/components/BackupCreator.vue:149
@@ -3488,12 +3493,16 @@ msgstr ""
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
 #: 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/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgid "Online"
 msgstr ""
 msgstr ""
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr ""
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgid "Only zip files are allowed"
 msgstr ""
 msgstr ""
@@ -3630,7 +3639,7 @@ msgstr ""
 msgid "Pending"
 msgid "Pending"
 msgstr ""
 msgstr ""
 
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgid "Perform"
 msgstr ""
 msgstr ""
 
 
@@ -3818,7 +3827,7 @@ msgid "Post-sync Action"
 msgstr ""
 msgstr ""
 
 
 #: src/views/environments/list/BatchUpgrader.vue:168
 #: 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:213
 #: src/views/system/Upgrade.vue:267
 #: src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgid "Pre-release"
@@ -3886,7 +3895,7 @@ msgstr ""
 msgid "Proxy"
 msgid "Proxy"
 msgstr ""
 msgstr ""
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgid "Proxy Pass"
 msgstr ""
 msgstr ""
 
 
@@ -4046,7 +4055,7 @@ msgstr ""
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
 #: src/views/config/ConfigList.vue:190
@@ -4396,9 +4405,9 @@ msgstr ""
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
 #: 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/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgid "Save"
 msgstr ""
 msgstr ""
 
 
@@ -4458,7 +4467,7 @@ msgstr ""
 #: src/language/curd.ts:28
 #: src/language/curd.ts:28
 #: src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
 #: 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
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr ""
 msgstr ""
@@ -4758,7 +4767,7 @@ msgid "SSO Login"
 msgstr ""
 msgstr ""
 
 
 #: src/views/environments/list/BatchUpgrader.vue:165
 #: 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:210
 #: src/views/system/Upgrade.vue:261
 #: src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgid "Stable"
@@ -5192,7 +5201,7 @@ msgstr ""
 msgid "This will restore configuration files and database. Nginx UI will restart after the restoration is complete."
 msgid "This will restore configuration files and database. Nginx UI will restart after the restoration is complete."
 msgstr ""
 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}."
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 msgstr ""
 
 
@@ -5310,7 +5319,7 @@ msgstr ""
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr ""
 msgstr ""
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5393,11 +5402,11 @@ msgstr ""
 msgid "Upload Folders"
 msgid "Upload Folders"
 msgstr ""
 msgstr ""
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgid "Upstream"
 msgstr ""
 msgstr ""
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgid "Upstream Name"
 msgstr ""
 msgstr ""
 
 
@@ -5493,7 +5502,7 @@ msgstr ""
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr ""
 msgstr ""
 
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgid "View on GitHub"
 msgstr ""
 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"
 msgstr "[Nginx UI] A escrever o certificado no disco"
 
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
 #: 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
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
 msgid "2FA"
@@ -163,7 +163,7 @@ msgid "Actual worker to configured ratio"
 msgstr "Rácio real de workers para configurado"
 msgstr "Rácio real de workers para configurado"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
 #: 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/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -206,7 +206,7 @@ msgstr "Adicionar Site"
 msgid "Add Stream"
 msgid "Add Stream"
 msgstr "Adicionar Stream"
 msgstr "Adicionar Stream"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgid "Add Upstream"
 msgstr "Adicionar upstream"
 msgstr "Adicionar upstream"
 
 
@@ -218,8 +218,8 @@ msgstr "Adicionado com sucesso"
 msgid "Additional"
 msgid "Additional"
 msgstr "Adicional"
 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"
 msgid "Advance Mode"
 msgstr "Modo Avançado"
 msgstr "Modo Avançado"
 
 
@@ -433,12 +433,16 @@ msgstr "AutoCert está em execução..."
 msgid "Automatic Restart"
 msgid "Automatic Restart"
 msgstr "Reinício Automático"
 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/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
 #: 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"
 msgid "Back"
 msgstr "Voltar"
 msgstr "Voltar"
 
 
@@ -538,8 +542,8 @@ msgstr "Informação base"
 msgid "Basic"
 msgid "Basic"
 msgstr "Básico"
 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"
 msgid "Basic Mode"
 msgstr "Modo Básico"
 msgstr "Modo Básico"
 
 
@@ -628,7 +632,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
 #: 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/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1315,7 +1319,7 @@ msgstr ""
 "proxy_cache:10m"
 "proxy_cache:10m"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
 #: 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/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
 #: src/views/config/components/Delete.vue:97
@@ -1427,6 +1431,10 @@ msgstr "Detalhes"
 msgid "Dev"
 msgid "Dev"
 msgstr "Dev"
 msgstr "Dev"
 
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Desenvolvimento"
+
 #: src/views/system/About.vue:28
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "Modo Desenvolvimento"
 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:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
 #: 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/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
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Desativado"
 msgstr "Desativado"
@@ -1584,7 +1592,7 @@ msgstr "Activar TLS?"
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "Remover servidor?"
 msgstr "Remover servidor?"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr "Remover este upstream?"
 msgstr "Remover este upstream?"
 
 
@@ -1634,7 +1642,7 @@ msgstr "Solte o arquivo de certificado aqui"
 msgid "Drop private key file here"
 msgid "Drop private key file here"
 msgstr "Solte o arquivo de chave privada aqui"
 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
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "Modo Dry Run activado"
 msgstr "Modo Dry Run activado"
@@ -1673,8 +1681,8 @@ msgstr "ex., 0 0 * * * (diariamente à meia-noite)"
 msgid "Edit"
 msgid "Edit"
 msgstr "Editar"
 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}"
 msgid "Edit %{n}"
 msgstr "Editar %{n}"
 msgstr "Editar %{n}"
 
 
@@ -1807,10 +1815,10 @@ msgstr "Ativar TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
 #: 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/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
 #: 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
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Activado"
 msgstr "Activado"
@@ -2420,7 +2428,7 @@ msgstr "Falha ao obter os dados"
 msgid "Get dns credential error: {0}"
 msgid "Get dns credential error: {0}"
 msgstr "Erro ao obter credenciais DNS: {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
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgid "Get release information error"
 msgstr "Erro ao obter informações de release"
 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"
 msgstr "Um valor mais alto significa uma melhor reutilização da conexão"
 
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
 #: 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"
 msgid "History"
 msgstr "Histórico"
 msgstr "Histórico"
 
 
@@ -2929,16 +2937,13 @@ msgstr ""
 "activar manualmente esta opção. O agendador de tarefas crontab do Nginx UI "
 "activar manualmente esta opção. O agendador de tarefas crontab do Nginx UI "
 "executará o comando logrotate no intervalo que definir em minutos."
 "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"
 msgid "Main Node"
 msgstr "Nó principal"
 msgstr "Nó principal"
 
 
 #: src/views/site/components/SiteStatusSelect.vue:165
 #: 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
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgid "Maintenance"
 msgstr "Manutenção"
 msgstr "Manutenção"
@@ -3140,11 +3145,12 @@ msgstr "Mensalmente no dia %{day} às %{time}"
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "Diretiva Multilinha"
 msgstr "Diretiva Multilinha"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgid "N/A"
 msgstr "N/D"
 msgstr "N/D"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: 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"
 msgid "Nginx configuration has been restored"
 msgstr "A configuração do Nginx foi restaurada"
 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"
 msgid "Nginx Configuration Parse Error"
 msgstr "Erro na análise de configuração do Nginx"
 msgstr "Erro na análise de configuração do Nginx"
 
 
@@ -3465,7 +3471,7 @@ msgstr "Nenhum registo selecionado"
 msgid "No servers configured"
 msgid "No servers configured"
 msgstr "Nenhum servidor configurado"
 msgstr "Nenhum servidor configurado"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgid "No upstreams configured"
 msgstr "Nenhum upstream configurado"
 msgstr "Nenhum upstream configurado"
 
 
@@ -3492,7 +3498,7 @@ msgstr "Nome do nó"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "Segredo do Nó"
 msgstr "Segredo do Nó"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgid "Node Status"
 msgstr "Estado do Nó"
 msgstr "Estado do Nó"
 
 
@@ -3599,10 +3605,9 @@ msgid "Official Document"
 msgstr "Documentação oficial"
 msgstr "Documentação oficial"
 
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
 #: 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/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
 msgid "Offline"
@@ -3610,7 +3615,7 @@ msgstr "Off-line"
 
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
 #: 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/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: 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/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
 #: 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/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgid "Online"
 msgstr "On-line"
 msgstr "On-line"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Contagem Online"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgid "Only zip files are allowed"
 msgstr "Apenas ficheiros ZIP são permitidos"
 msgstr "Apenas ficheiros ZIP são permitidos"
@@ -3783,7 +3792,7 @@ msgstr "O recurso de carga útil é nulo"
 msgid "Pending"
 msgid "Pending"
 msgstr "Pendente"
 msgstr "Pendente"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgid "Perform"
 msgstr "Realizar"
 msgstr "Realizar"
 
 
@@ -3999,7 +4008,7 @@ msgid "Post-sync Action"
 msgstr "Ação pós-sincronização"
 msgstr "Ação pós-sincronização"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:168
 #: 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
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "Pre-lançamento"
 msgstr "Pre-lançamento"
@@ -4071,7 +4080,7 @@ msgstr "Fornecedor não encontrado: {0}"
 msgid "Proxy"
 msgid "Proxy"
 msgstr "Proxy"
 msgstr "Proxy"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgid "Proxy Pass"
 msgstr "Passe de Proxy"
 msgstr "Passe de Proxy"
 
 
@@ -4236,7 +4245,7 @@ msgstr "Removido com sucesso"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Removido com sucesso"
 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/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
 #: src/views/config/ConfigList.vue:190
@@ -4595,9 +4604,9 @@ msgstr "Sábado"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
 #: 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/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgid "Save"
 msgstr "Salvar"
 msgstr "Salvar"
 
 
@@ -4655,7 +4664,7 @@ msgstr "Salvo com sucesso"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
 #: 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
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Salvo com sucesso"
 msgstr "Salvo com sucesso"
@@ -4971,7 +4980,7 @@ msgid "SSO Login"
 msgstr "Autenticação SSO"
 msgstr "Autenticação SSO"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:165
 #: 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
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgid "Stable"
 msgstr "Estável"
 msgstr "Estável"
@@ -5481,7 +5490,7 @@ msgstr ""
 "Isto irá restaurar os ficheiros de configuração e a base de dados. O Nginx "
 "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."
 "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}."
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 msgstr ""
 "Isto vai actualizar ou reinstalar o Nginx UI em %{nodeNames} para "
 "Isto vai actualizar ou reinstalar o Nginx UI em %{nodeNames} para "
@@ -5633,7 +5642,7 @@ msgstr "Terça-feira"
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "Autenticação de dois fatores necessária"
 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/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5714,11 +5723,11 @@ msgstr "Carregar ficheiros"
 msgid "Upload Folders"
 msgid "Upload Folders"
 msgstr "Carregar pastas"
 msgstr "Carregar pastas"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgid "Upstream"
 msgstr "A montante"
 msgstr "A montante"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgid "Upstream Name"
 msgstr "Nome do Upstream"
 msgstr "Nome do Upstream"
 
 
@@ -5811,7 +5820,7 @@ msgstr "Ver"
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "Ver todas notificações"
 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
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgid "View on GitHub"
 msgstr "Ver no GitHub"
 msgstr "Ver no GitHub"
@@ -6038,6 +6047,9 @@ msgstr "Os seus códigos antigos não funcionarão mais."
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "As suas chaves de acesso"
 msgstr "As suas chaves de acesso"
 
 
+#~ msgid "Main"
+#~ msgstr "Principal"
+
 #~ msgid "Select an action after sync"
 #~ msgid "Select an action after sync"
 #~ msgstr "Selecionar uma ação após sincronização"
 #~ 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] Запись сертификата на диск"
 msgstr "[Nginx UI] Запись сертификата на диск"
 
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
 #: 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
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
 msgid "2FA"
@@ -167,7 +167,7 @@ msgid "Actual worker to configured ratio"
 msgstr "Фактическое соотношение рабочих к настроенным"
 msgstr "Фактическое соотношение рабочих к настроенным"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
 #: 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/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -210,7 +210,7 @@ msgstr "Добавить Сайт"
 msgid "Add Stream"
 msgid "Add Stream"
 msgstr "Добавить поток"
 msgstr "Добавить поток"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgid "Add Upstream"
 msgstr "Добавить upstream"
 msgstr "Добавить upstream"
 
 
@@ -222,8 +222,8 @@ msgstr "Добавлено успешно"
 msgid "Additional"
 msgid "Additional"
 msgstr "Дополнительно"
 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"
 msgid "Advance Mode"
 msgstr "Расширенный режим"
 msgstr "Расширенный режим"
 
 
@@ -435,12 +435,16 @@ msgstr "AutoCert выполняется..."
 msgid "Automatic Restart"
 msgid "Automatic Restart"
 msgstr "Автоматическая перезагрузка"
 msgstr "Автоматическая перезагрузка"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "Средняя задержка"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
 #: 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"
 msgid "Back"
 msgstr "Назад"
 msgstr "Назад"
 
 
@@ -544,8 +548,8 @@ msgstr "Основная информация"
 msgid "Basic"
 msgid "Basic"
 msgstr "Основные"
 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"
 msgid "Basic Mode"
 msgstr "Простой режим"
 msgstr "Простой режим"
 
 
@@ -636,7 +640,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
 #: 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/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
 #: 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"
 msgstr "Определите имя и размер зоны общей памяти, например proxy_cache:10m"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
 #: 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/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
 #: src/views/config/components/Delete.vue:97
@@ -1430,6 +1434,10 @@ msgstr "Детали"
 msgid "Dev"
 msgid "Dev"
 msgstr "Dev"
 msgstr "Dev"
 
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Разработка"
+
 #: src/views/system/About.vue:28
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "Режим разработки"
 msgstr "Режим разработки"
@@ -1532,9 +1540,9 @@ msgstr "Поток %{name} отключен от %{node} успешно"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
 #: 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/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
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Отключено"
 msgstr "Отключено"
@@ -1587,7 +1595,7 @@ msgstr "Вы хотите включить TLS?"
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "Хотите удалить этот сервер?"
 msgstr "Хотите удалить этот сервер?"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr "Хотите удалить этот сервер?"
 msgstr "Хотите удалить этот сервер?"
 
 
@@ -1637,7 +1645,7 @@ msgstr "Перетащите файл сертификата сюда"
 msgid "Drop private key file here"
 msgid "Drop private key file here"
 msgstr "Перетащите файл закрытого ключа сюда"
 msgstr "Перетащите файл закрытого ключа сюда"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "Включен пробный режим"
 msgstr "Включен пробный режим"
@@ -1676,8 +1684,8 @@ msgstr "напр., 0 0 * * * (ежедневно в полночь)"
 msgid "Edit"
 msgid "Edit"
 msgstr "Редактировать"
 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}"
 msgid "Edit %{n}"
 msgstr "Редактировать %{n}"
 msgstr "Редактировать %{n}"
 
 
@@ -1810,10 +1818,10 @@ msgstr "Включить TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
 #: 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/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
 #: 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
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Включено"
 msgstr "Включено"
@@ -2427,7 +2435,7 @@ msgstr "Не удалось получить данные"
 msgid "Get dns credential error: {0}"
 msgid "Get dns credential error: {0}"
 msgstr "Ошибка получения учетных данных DNS: {0}"
 msgstr "Ошибка получения учетных данных DNS: {0}"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgid "Get release information error"
 msgstr "Ошибка получения информации о выпуске"
 msgstr "Ошибка получения информации о выпуске"
@@ -2469,8 +2477,8 @@ msgid "Higher value means better connection reuse"
 msgstr "Более высокое значение означает лучшее повторное использование соединения"
 msgstr "Более высокое значение означает лучшее повторное использование соединения"
 
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
 #: 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"
 msgid "History"
 msgstr "История"
 msgstr "История"
 
 
@@ -2934,16 +2942,13 @@ msgstr ""
 "можете вручную включить эту опцию. Планировщик задач crontab Nginx UI будет "
 "можете вручную включить эту опцию. Планировщик задач crontab Nginx UI будет "
 "выполнять команду logrotate с интервалом, который вы установите в минутах."
 "выполнять команду 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"
 msgid "Main Node"
 msgstr "Главный узел"
 msgstr "Главный узел"
 
 
 #: src/views/site/components/SiteStatusSelect.vue:165
 #: 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
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgid "Maintenance"
 msgstr "Техническое обслуживание"
 msgstr "Техническое обслуживание"
@@ -3145,11 +3150,12 @@ msgstr "Ежемесячно в день %{day} в %{time}"
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "Многострочная директива"
 msgstr "Многострочная директива"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgid "N/A"
 msgstr "Н/Д"
 msgstr "Н/Д"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3286,8 +3292,8 @@ msgstr "Файл конфигурации Nginx существует"
 msgid "Nginx configuration has been restored"
 msgid "Nginx configuration has been restored"
 msgstr "Конфигурация Nginx была восстановлена"
 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"
 msgid "Nginx Configuration Parse Error"
 msgstr "Ошибка разбора конфигурации Nginx"
 msgstr "Ошибка разбора конфигурации Nginx"
 
 
@@ -3470,7 +3476,7 @@ msgstr "Нет выбранных записей"
 msgid "No servers configured"
 msgid "No servers configured"
 msgstr "Серверы не настроены"
 msgstr "Серверы не настроены"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgid "No upstreams configured"
 msgstr "Нет настроенных апстримов"
 msgstr "Нет настроенных апстримов"
 
 
@@ -3497,7 +3503,7 @@ msgstr "Имя узла"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "Секрет узла"
 msgstr "Секрет узла"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgid "Node Status"
 msgstr "Статус узла"
 msgstr "Статус узла"
 
 
@@ -3604,10 +3610,9 @@ msgid "Official Document"
 msgstr "Официальная документация"
 msgstr "Официальная документация"
 
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
 #: 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/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
 msgid "Offline"
@@ -3615,7 +3620,7 @@ msgstr "Оффлайн"
 
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
 #: 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/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/notification/Notification.vue:39
@@ -3642,12 +3647,16 @@ msgstr "После завершения проверки записи будут
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
 #: 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/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgid "Online"
 msgstr "Онлайн"
 msgstr "Онлайн"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Количество онлайн"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgid "Only zip files are allowed"
 msgstr "Разрешены только ZIP-файлы"
 msgstr "Разрешены только ZIP-файлы"
@@ -3788,7 +3797,7 @@ msgstr "Ресурс полезной нагрузки равен nil"
 msgid "Pending"
 msgid "Pending"
 msgstr "В ожидании"
 msgstr "В ожидании"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgid "Perform"
 msgstr "Выполнить"
 msgstr "Выполнить"
 
 
@@ -4009,7 +4018,7 @@ msgid "Post-sync Action"
 msgstr "Действие после синхронизации"
 msgstr "Действие после синхронизации"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:168
 #: 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
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "Предварительный выпуск"
 msgstr "Предварительный выпуск"
@@ -4081,7 +4090,7 @@ msgstr "Поставщик не найден: {0}"
 msgid "Proxy"
 msgid "Proxy"
 msgstr "Прокси"
 msgstr "Прокси"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgid "Proxy Pass"
 msgstr "Прокси-передача"
 msgstr "Прокси-передача"
 
 
@@ -4246,7 +4255,7 @@ msgstr "Удалено успешно"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Успешно удалено"
 msgstr "Успешно удалено"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
 #: src/views/config/ConfigList.vue:190
@@ -4603,9 +4612,9 @@ msgstr "Суббота"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
 #: 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/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgid "Save"
 msgstr "Сохранить"
 msgstr "Сохранить"
 
 
@@ -4663,7 +4672,7 @@ msgstr "Сохранено успешно"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
 #: 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
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Успешно сохранено"
 msgstr "Успешно сохранено"
@@ -4973,7 +4982,7 @@ msgid "SSO Login"
 msgstr "SSO Вход"
 msgstr "SSO Вход"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:165
 #: 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
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgid "Stable"
 msgstr "Стабильный"
 msgstr "Стабильный"
@@ -5480,7 +5489,7 @@ msgstr ""
 "Это восстановит файлы конфигурации и базу данных. Nginx UI перезапустится "
 "Это восстановит файлы конфигурации и базу данных. 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}."
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 msgstr ""
 "Это обновит или переустановит интерфейс Nginx на %{nodeNames} до версии "
 "Это обновит или переустановит интерфейс Nginx на %{nodeNames} до версии "
@@ -5632,7 +5641,7 @@ msgstr "Вторник"
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "Требуется двухфакторная аутентификация"
 msgstr "Требуется двухфакторная аутентификация"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5713,11 +5722,11 @@ msgstr "Загрузить файлы"
 msgid "Upload Folders"
 msgid "Upload Folders"
 msgstr "Загрузить папки"
 msgstr "Загрузить папки"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgid "Upstream"
 msgstr "Восходящий поток"
 msgstr "Восходящий поток"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgid "Upstream Name"
 msgstr "Имя Upstream"
 msgstr "Имя Upstream"
 
 
@@ -5810,7 +5819,7 @@ msgstr "Просмотр"
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "Просмотреть все уведомления"
 msgstr "Просмотреть все уведомления"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgid "View on GitHub"
 msgstr "Посмотреть на GitHub"
 msgstr "Посмотреть на GitHub"
@@ -6034,6 +6043,9 @@ msgstr "Ваши старые коды больше не будут работа
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "Ваши ключи доступа"
 msgstr "Ваши ключи доступа"
 
 
+#~ msgid "Main"
+#~ msgstr "Основной"
+
 #~ msgid "Select an action after sync"
 #~ msgid "Select an action after sync"
 #~ msgstr "Выберите действие после синхронизации"
 #~ 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"
 msgstr "[Nginx UI] Sertifika diske yazılıyor"
 
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
 #: 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
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
 msgid "2FA"
@@ -165,7 +163,7 @@ msgid "Actual worker to configured ratio"
 msgstr "Gerçek çalışanın yapılandırılmışa oranı"
 msgstr "Gerçek çalışanın yapılandırılmışa oranı"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
 #: 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/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -208,7 +206,7 @@ msgstr "Site Ekle"
 msgid "Add Stream"
 msgid "Add Stream"
 msgstr "Akış Ekle"
 msgstr "Akış Ekle"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgid "Add Upstream"
 msgstr "Upstream ekle"
 msgstr "Upstream ekle"
 
 
@@ -220,8 +218,8 @@ msgstr "Başarıyla eklendi"
 msgid "Additional"
 msgid "Additional"
 msgstr "İlave bilgi"
 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"
 msgid "Advance Mode"
 msgstr "Gelişmiş Mod"
 msgstr "Gelişmiş Mod"
 
 
@@ -433,12 +431,16 @@ msgstr "AutoCert çalışıyor..."
 msgid "Automatic Restart"
 msgid "Automatic Restart"
 msgstr "Otomatik Yeniden Başlatma"
 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/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
 #: 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"
 msgid "Back"
 msgstr "Geri"
 msgstr "Geri"
 
 
@@ -534,8 +536,8 @@ msgstr "Temel bilgiler"
 msgid "Basic"
 msgid "Basic"
 msgstr "Temel"
 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"
 msgid "Basic Mode"
 msgstr "Temel Mod"
 msgstr "Temel Mod"
 
 
@@ -624,7 +626,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
 #: 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/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
 #: src/views/site/components/SiteStatusSelect.vue:124
@@ -1310,7 +1312,7 @@ msgstr ""
 "proxy_cache:10m"
 "proxy_cache:10m"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
 #: 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/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
 #: src/views/config/components/Delete.vue:97
@@ -1422,6 +1424,10 @@ msgstr "Detaylar"
 msgid "Dev"
 msgid "Dev"
 msgstr "Dev"
 msgstr "Dev"
 
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Geliştirme"
+
 #: src/views/system/About.vue:28
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "Geliştirme modu"
 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:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
 #: 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/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
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Devre dışı"
 msgstr "Devre dışı"
@@ -1579,7 +1585,7 @@ msgstr "TLS'yi etkinleştirmek istiyor musunuz?"
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "Bu sunucuyu kaldırmak istiyor musunuz?"
 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?"
 msgid "Do you want to remove this upstream?"
 msgstr "Bu upstream'i kaldırmak istiyor musunuz?"
 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"
 msgid "Drop private key file here"
 msgstr "Özel anahtar dosyasını buraya bırakın"
 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
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "Deneme modu etkinleştirildi(Dry-run)"
 msgstr "Deneme modu etkinleştirildi(Dry-run)"
@@ -1670,8 +1676,8 @@ msgstr "örn., 0 0 * * * (her gece yarısı)"
 msgid "Edit"
 msgid "Edit"
 msgstr "Düzenle"
 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}"
 msgid "Edit %{n}"
 msgstr "Düzenle %{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:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
 #: 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/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
 #: 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
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Etkin"
 msgstr "Etkin"
@@ -2419,7 +2425,7 @@ msgstr "Veri alınamadı"
 msgid "Get dns credential error: {0}"
 msgid "Get dns credential error: {0}"
 msgstr "DNS kimlik bilgisi alınırken hata: {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
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgid "Get release information error"
 msgstr "Sürüm bilgilerini alma hatası"
 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"
 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/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"
 msgid "History"
 msgstr "Geçmiş"
 msgstr "Geçmiş"
 
 
@@ -2929,16 +2935,13 @@ msgstr ""
 "olarak etkinleştirebilir. Nginx UI'nin crontab görev zamanlayıcısı, "
 "olarak etkinleştirebilir. Nginx UI'nin crontab görev zamanlayıcısı, "
 "belirlediğiniz dakika aralığında logrotate komutunu çalıştıracaktır."
 "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"
 msgid "Main Node"
 msgstr "Ana Düğüm"
 msgstr "Ana Düğüm"
 
 
 #: src/views/site/components/SiteStatusSelect.vue:165
 #: 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
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgid "Maintenance"
 msgstr "Bakım"
 msgstr "Bakım"
@@ -3140,11 +3143,12 @@ msgstr "Her ayın %{day} günü %{time} saatinde"
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "Çok Satırlı Yönergeler"
 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"
 msgid "N/A"
 msgstr "Yok"
 msgstr "Yok"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: 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"
 msgid "Nginx configuration has been restored"
 msgstr "Nginx yapılandırması geri yüklendi"
 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"
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx Yapılandırma Ayrıştırma Hatası"
 msgstr "Nginx Yapılandırma Ayrıştırma Hatası"
 
 
@@ -3465,7 +3469,7 @@ msgstr "Hiçbir kayıt seçilmedi"
 msgid "No servers configured"
 msgid "No servers configured"
 msgstr "Yapılandırılmış sunucu yok"
 msgstr "Yapılandırılmış sunucu yok"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgid "No upstreams configured"
 msgstr "Yapılandırılmış yukarı akış yok"
 msgstr "Yapılandırılmış yukarı akış yok"
 
 
@@ -3492,7 +3496,7 @@ msgstr "Düğüm adı"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "Düğüm Sırrı"
 msgstr "Düğüm Sırrı"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgid "Node Status"
 msgstr "Düğüm Durumu"
 msgstr "Düğüm Durumu"
 
 
@@ -3599,10 +3603,9 @@ msgid "Official Document"
 msgstr "Resmi Belge"
 msgstr "Resmi Belge"
 
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
 #: 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/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
 msgid "Offline"
@@ -3610,7 +3613,7 @@ msgstr "Çevrimdışı"
 
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
 #: 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/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: 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/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
 #: 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/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgid "Online"
 msgstr "Çevrimiçi"
 msgstr "Çevrimiçi"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Çevrimiçi Sayısı"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgid "Only zip files are allowed"
 msgstr "Sadece ZIP dosyalarına izin verilir"
 msgstr "Sadece ZIP dosyalarına izin verilir"
@@ -3782,7 +3789,7 @@ msgstr "Yük kaynağı nil"
 msgid "Pending"
 msgid "Pending"
 msgstr "Beklemede"
 msgstr "Beklemede"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgid "Perform"
 msgstr "Uygula"
 msgstr "Uygula"
 
 
@@ -3999,7 +4006,7 @@ msgid "Post-sync Action"
 msgstr "Senkronizasyon Sonrası İşlem"
 msgstr "Senkronizasyon Sonrası İşlem"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:168
 #: 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
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "Ön Sürüm"
 msgstr "Ön Sürüm"
@@ -4070,7 +4077,7 @@ msgstr "Sağlayıcı bulunamadı: {0}"
 msgid "Proxy"
 msgid "Proxy"
 msgstr "Proxy"
 msgstr "Proxy"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgid "Proxy Pass"
 msgstr "Proxy Geçişi"
 msgstr "Proxy Geçişi"
 
 
@@ -4235,7 +4242,7 @@ msgstr "Başarıyla kaldırıldı"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Başarıyla kaldırıldı"
 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/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
 #: src/views/config/ConfigList.vue:190
@@ -4601,9 +4608,9 @@ msgstr "Cumartesi"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
 #: 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/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgid "Save"
 msgstr "Kaydet"
 msgstr "Kaydet"
 
 
@@ -4661,7 +4668,7 @@ msgstr "Başarıyla kaydedildi"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
 #: 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
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Başarıyla kaydedildi"
 msgstr "Başarıyla kaydedildi"
@@ -4971,7 +4978,7 @@ msgid "SSO Login"
 msgstr "SSO Girişi"
 msgstr "SSO Girişi"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:165
 #: 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
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgid "Stable"
 msgstr "Kararlı"
 msgstr "Kararlı"
@@ -5479,7 +5486,7 @@ msgstr ""
 "Bu işlem yapılandırma dosyalarını ve veritabanını geri yükleyecektir. Geri "
 "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."
 "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}."
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 msgstr ""
 "Bu işlem, %{nodeNames} üzerindeki Nginx UI'yi %{version} sürümüne "
 "Bu işlem, %{nodeNames} üzerindeki Nginx UI'yi %{version} sürümüne "
@@ -5631,7 +5638,7 @@ msgstr "Salı"
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "İki faktörlü kimlik doğrulama gerekiyor"
 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/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5712,11 +5719,11 @@ msgstr "Dosyaları Yükle"
 msgid "Upload Folders"
 msgid "Upload Folders"
 msgstr "Klasörleri Yükle"
 msgstr "Klasörleri Yükle"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgid "Upstream"
 msgstr "Yukarı Akış"
 msgstr "Yukarı Akış"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgid "Upstream Name"
 msgstr "Yukarı Akış Adı"
 msgstr "Yukarı Akış Adı"
 
 
@@ -5809,7 +5816,7 @@ msgstr "Görüntüle"
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "Tüm bildirimleri görüntüle"
 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
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgid "View on GitHub"
 msgstr "GitHub'da Görüntüle"
 msgstr "GitHub'da Görüntüle"
@@ -6035,6 +6042,9 @@ msgstr "Eski kodlarınız artık çalışmayacak."
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "Geçiş Anahtarlarınız"
 msgstr "Geçiş Anahtarlarınız"
 
 
+#~ msgid "Main"
+#~ msgstr "Ana"
+
 #~ msgid "Select an action after sync"
 #~ msgid "Select an action after sync"
 #~ msgstr "Senkronizasyon sonrası eylem seçin"
 #~ 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] Запис сертифіката на диск"
 msgstr "[Nginx UI] Запис сертифіката на диск"
 
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
 #: 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
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
 msgid "2FA"
@@ -167,7 +167,7 @@ msgid "Actual worker to configured ratio"
 msgstr "Фактичне співвідношення робочих до налаштованих"
 msgstr "Фактичне співвідношення робочих до налаштованих"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
 #: 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/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -210,7 +210,7 @@ msgstr "Додати сайт"
 msgid "Add Stream"
 msgid "Add Stream"
 msgstr "Додати стрім"
 msgstr "Додати стрім"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgid "Add Upstream"
 msgstr "Додати upstream"
 msgstr "Додати upstream"
 
 
@@ -222,8 +222,8 @@ msgstr "Успішно додано"
 msgid "Additional"
 msgid "Additional"
 msgstr "Додатково"
 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"
 msgid "Advance Mode"
 msgstr "Розширений режим"
 msgstr "Розширений режим"
 
 
@@ -435,12 +435,16 @@ msgstr "AutoCert виконується..."
 msgid "Automatic Restart"
 msgid "Automatic Restart"
 msgstr "Автоматичний перезапуск"
 msgstr "Автоматичний перезапуск"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "Середня затримка"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
 #: 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"
 msgid "Back"
 msgstr "Назад"
 msgstr "Назад"
 
 
@@ -542,8 +546,8 @@ msgstr "Основна інформація"
 msgid "Basic"
 msgid "Basic"
 msgstr "Базові"
 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"
 msgid "Basic Mode"
 msgstr "Базовий режим"
 msgstr "Базовий режим"
 
 
@@ -633,7 +637,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
 #: 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/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
 #: 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"
 msgstr "Вкажіть назву та розмір зони спільної пам'яті, наприклад proxy_cache:10m"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
 #: 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/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
 #: src/views/config/components/Delete.vue:97
@@ -1496,6 +1500,10 @@ msgstr "Деталі"
 msgid "Dev"
 msgid "Dev"
 msgstr "Розробка"
 msgstr "Розробка"
 
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Розробка"
+
 #: src/views/system/About.vue:28
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "Режим розробки"
 msgstr "Режим розробки"
@@ -1598,9 +1606,9 @@ msgstr "Потік %{name} успішно вимкнено з %{node}"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
 #: 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/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
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Вимкнено"
 msgstr "Вимкнено"
@@ -1653,7 +1661,7 @@ msgstr "Ви хочете увімкнути TLS?"
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "Ви хочете видалити цей сервер?"
 msgstr "Ви хочете видалити цей сервер?"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr "Ви хочете видалити цей апстрім?"
 msgstr "Ви хочете видалити цей апстрім?"
 
 
@@ -1703,7 +1711,7 @@ msgstr "Перетягніть файл сертифіката сюди"
 msgid "Drop private key file here"
 msgid "Drop private key file here"
 msgstr "Перетягніть файл приватного ключа сюди"
 msgstr "Перетягніть файл приватного ключа сюди"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "Увімкнено тестовий режим"
 msgstr "Увімкнено тестовий режим"
@@ -1742,8 +1750,8 @@ msgstr "напр., 0 0 * * * (щодня о півночі)"
 msgid "Edit"
 msgid "Edit"
 msgstr "Редагувати"
 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}"
 msgid "Edit %{n}"
 msgstr "Редагувати %{n}"
 msgstr "Редагувати %{n}"
 
 
@@ -1876,10 +1884,10 @@ msgstr "Увімкнути TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
 #: 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/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
 #: 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
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Увімкнено"
 msgstr "Увімкнено"
@@ -2493,7 +2501,7 @@ msgstr "Не вдалося отримати дані"
 msgid "Get dns credential error: {0}"
 msgid "Get dns credential error: {0}"
 msgstr "Помилка отримання облікових даних DNS: {0}"
 msgstr "Помилка отримання облікових даних DNS: {0}"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgid "Get release information error"
 msgstr "Помилка отримання інформації про реліз"
 msgstr "Помилка отримання інформації про реліз"
@@ -2535,8 +2543,8 @@ msgid "Higher value means better connection reuse"
 msgstr "Вище значення означає краще повторне використання з’єднання"
 msgstr "Вище значення означає краще повторне використання з’єднання"
 
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
 #: 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"
 msgid "History"
 msgstr "Історія"
 msgstr "Історія"
 
 
@@ -3000,16 +3008,13 @@ msgstr ""
 "можете вручну активувати цю опцію. Планувальник завдань crontab у Nginx UI "
 "можете вручну активувати цю опцію. Планувальник завдань crontab у Nginx UI "
 "виконуватиме команду logrotate з інтервалом, який ви встановите у хвилинах."
 "виконуватиме команду 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"
 msgid "Main Node"
 msgstr "Головний вузол"
 msgstr "Головний вузол"
 
 
 #: src/views/site/components/SiteStatusSelect.vue:165
 #: 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
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgid "Maintenance"
 msgstr "Технічне обслуговування"
 msgstr "Технічне обслуговування"
@@ -3211,11 +3216,12 @@ msgstr "Щомісяця %{day} числа о %{time}"
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "Багаторядкова директива"
 msgstr "Багаторядкова директива"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgid "N/A"
 msgstr "Н/Д"
 msgstr "Н/Д"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3352,8 +3358,8 @@ msgstr "Файл входу конфігурації Nginx існує"
 msgid "Nginx configuration has been restored"
 msgid "Nginx configuration has been restored"
 msgstr "Конфігурацію Nginx відновлено"
 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"
 msgid "Nginx Configuration Parse Error"
 msgstr "Помилка аналізу конфігурації Nginx"
 msgstr "Помилка аналізу конфігурації Nginx"
 
 
@@ -3536,7 +3542,7 @@ msgstr "Не вибрано жодного запису"
 msgid "No servers configured"
 msgid "No servers configured"
 msgstr "Сервери не налаштовані"
 msgstr "Сервери не налаштовані"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgid "No upstreams configured"
 msgstr "Не налаштовано жодного апстріму"
 msgstr "Не налаштовано жодного апстріму"
 
 
@@ -3563,7 +3569,7 @@ msgstr "Ім’я вузла"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "Секрет вузла"
 msgstr "Секрет вузла"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgid "Node Status"
 msgstr "Стан вузла"
 msgstr "Стан вузла"
 
 
@@ -3670,10 +3676,9 @@ msgid "Official Document"
 msgstr "Офіційна документація"
 msgstr "Офіційна документація"
 
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
 #: 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/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
 msgid "Offline"
@@ -3681,7 +3686,7 @@ msgstr "Офлайн"
 
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
 #: 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/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/notification/Notification.vue:39
@@ -3708,12 +3713,16 @@ msgstr "Після завершення перевірки записи буду
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
 #: 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/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgid "Online"
 msgstr "Онлайн"
 msgstr "Онлайн"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "Кількість онлайн"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgid "Only zip files are allowed"
 msgstr "Дозволені лише zip-файли"
 msgstr "Дозволені лише zip-файли"
@@ -3854,7 +3863,7 @@ msgstr "Ресурс навантаження є nil"
 msgid "Pending"
 msgid "Pending"
 msgstr "Очікується"
 msgstr "Очікується"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgid "Perform"
 msgstr "Виконати"
 msgstr "Виконати"
 
 
@@ -4071,7 +4080,7 @@ msgid "Post-sync Action"
 msgstr "Дія після синхронізації"
 msgstr "Дія після синхронізації"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:168
 #: 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
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "Попередній випуск"
 msgstr "Попередній випуск"
@@ -4143,7 +4152,7 @@ msgstr "Постачальника не знайдено: {0}"
 msgid "Proxy"
 msgid "Proxy"
 msgstr "Проксі"
 msgstr "Проксі"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgid "Proxy Pass"
 msgstr "Проксі-передача"
 msgstr "Проксі-передача"
 
 
@@ -4311,7 +4320,7 @@ msgstr "Успішно видалено"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Успішно видалено"
 msgstr "Успішно видалено"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
 #: src/views/config/ConfigList.vue:190
@@ -4670,9 +4679,9 @@ msgstr "Субота"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
 #: 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/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgid "Save"
 msgstr "Зберегти"
 msgstr "Зберегти"
 
 
@@ -4730,7 +4739,7 @@ msgstr "Успішно збережено"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
 #: 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
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Успішно збережено"
 msgstr "Успішно збережено"
@@ -5044,7 +5053,7 @@ msgid "SSO Login"
 msgstr "Вхід через SSO"
 msgstr "Вхід через SSO"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:165
 #: 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
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgid "Stable"
 msgstr "Стабільна"
 msgstr "Стабільна"
@@ -5552,7 +5561,7 @@ msgstr ""
 "Це відновить файли конфігурації та базу даних. Nginx UI перезапуститься "
 "Це відновить файли конфігурації та базу даних. 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}."
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr "Це оновить або перевстановить Nginx UI на %{nodeNames} до версії %{version}."
 msgstr "Це оновить або перевстановить Nginx UI на %{nodeNames} до версії %{version}."
 
 
@@ -5702,7 +5711,7 @@ msgstr "Вівторок"
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "Потрібна двофакторна аутентифікація"
 msgstr "Потрібна двофакторна аутентифікація"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5783,11 +5792,11 @@ msgstr "Завантажити файли"
 msgid "Upload Folders"
 msgid "Upload Folders"
 msgstr "Завантажити папки"
 msgstr "Завантажити папки"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgid "Upstream"
 msgstr "Вгору за течією"
 msgstr "Вгору за течією"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgid "Upstream Name"
 msgstr "Назва апстріму"
 msgstr "Назва апстріму"
 
 
@@ -5880,7 +5889,7 @@ msgstr "Переглянути"
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "Переглянути всі сповіщення"
 msgstr "Переглянути всі сповіщення"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgid "View on GitHub"
 msgstr "Переглянути на GitHub"
 msgstr "Переглянути на GitHub"
@@ -6103,6 +6112,9 @@ msgstr "Ваші старі коди більше не працюватимут
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "Ваші ключі доступу"
 msgstr "Ваші ключі доступу"
 
 
+#~ msgid "Main"
+#~ msgstr "Головний"
+
 #~ msgid "Select an action after sync"
 #~ msgid "Select an action after sync"
 #~ msgstr "Виберіть дію після синхронізації"
 #~ 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"
 msgstr "[Nginx UI] Đang ghi chứng chỉ vào ổ đĩa"
 
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
 #: 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
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
 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"
 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/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/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
 #: 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"
 msgid "Add Stream"
 msgstr "Thêm luồng"
 msgstr "Thêm luồng"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgid "Add Upstream"
 msgstr "Thêm upstream"
 msgstr "Thêm upstream"
 
 
@@ -213,8 +213,8 @@ msgstr "Đã thêm thành công"
 msgid "Additional"
 msgid "Additional"
 msgstr "Bổ sung"
 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"
 msgid "Advance Mode"
 msgstr "Nâng cao"
 msgstr "Nâng cao"
 
 
@@ -422,12 +422,16 @@ msgstr "AutoCert đang chạy..."
 msgid "Automatic Restart"
 msgid "Automatic Restart"
 msgstr "Khởi động lại tự động"
 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/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
 #: 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"
 msgid "Back"
 msgstr "Quay lại"
 msgstr "Quay lại"
 
 
@@ -523,8 +527,8 @@ msgstr "Thông tin"
 msgid "Basic"
 msgid "Basic"
 msgstr "Cơ bản"
 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"
 msgid "Basic Mode"
 msgstr "Cơ bản"
 msgstr "Cơ bản"
 
 
@@ -613,7 +617,7 @@ msgstr ""
 
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
 #: 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/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
 #: 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"
 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/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/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
 #: src/views/config/components/Delete.vue:97
@@ -1400,6 +1404,10 @@ msgstr "Chi tiết"
 msgid "Dev"
 msgid "Dev"
 msgstr "Dev"
 msgstr "Dev"
 
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "Phát triển"
+
 #: src/views/system/About.vue:28
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "Chế độ phát triển"
 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:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
 #: 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/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
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Đã tắt"
 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?"
 msgid "Do you want to remove this server?"
 msgstr "Bạn có muốn xóa máy chủ này không?"
 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?"
 msgid "Do you want to remove this upstream?"
 msgstr "Bạn có muốn xóa upstream này không?"
 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"
 msgid "Drop private key file here"
 msgstr "Thả tệp khóa riêng tư vào đây"
 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
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "Đã bật chế độ Dry run"
 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"
 msgid "Edit"
 msgstr "Chỉnh sửa"
 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}"
 msgid "Edit %{n}"
 msgstr "Sửa %{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:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
 #: 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/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
 #: 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
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Đã bật"
 msgstr "Đã bật"
@@ -2394,7 +2402,7 @@ msgstr "Không thể lấy dữ liệu"
 msgid "Get dns credential error: {0}"
 msgid "Get dns credential error: {0}"
 msgstr "Lỗi khi lấy thông tin xác thực DNS: {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
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgid "Get release information error"
 msgstr "Lỗi lấy thông tin phát hành"
 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"
 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/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"
 msgid "History"
 msgstr "Lịch sử"
 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 "
 "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)."
 "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"
 msgid "Main Node"
 msgstr "Nút chính"
 msgstr "Nút chính"
 
 
 #: src/views/site/components/SiteStatusSelect.vue:165
 #: 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
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgid "Maintenance"
 msgstr "Bảo trì"
 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"
 msgid "Multi-line Directive"
 msgstr "Chỉ thị nhiều dòng"
 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"
 msgid "N/A"
 msgstr "Không có"
 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/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: 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"
 msgid "Nginx configuration has been restored"
 msgstr "Cấu hình Nginx đã được khôi phục"
 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"
 msgid "Nginx Configuration Parse Error"
 msgstr "Lỗi phân tích cấu hình Nginx"
 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"
 msgid "No servers configured"
 msgstr "Không có máy chủ nào được cấu hình"
 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"
 msgid "No upstreams configured"
 msgstr "Không có upstream nào được cấu hình"
 msgstr "Không có upstream nào được cấu hình"
 
 
@@ -3463,7 +3469,7 @@ msgstr "Tên nút"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "Bí mật nút"
 msgstr "Bí mật nút"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgid "Node Status"
 msgstr "Trạng thái nút"
 msgstr "Trạng thái nút"
 
 
@@ -3568,10 +3574,9 @@ msgid "Official Document"
 msgstr "Tài liệu chính thức"
 msgstr "Tài liệu chính thức"
 
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
 #: 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/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
 msgid "Offline"
@@ -3579,7 +3584,7 @@ msgstr "Ngoại tuyến"
 
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
 #: 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/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: 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/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
 #: 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/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgid "Online"
 msgstr "Trực tuyến"
 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
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgid "Only zip files are allowed"
 msgstr "Chỉ cho phép tệp zip"
 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"
 msgid "Pending"
 msgstr "Đang chờ"
 msgstr "Đang chờ"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgid "Perform"
 msgstr "Thực hiện"
 msgstr "Thực hiện"
 
 
@@ -3960,7 +3969,7 @@ msgid "Post-sync Action"
 msgstr "Hành động sau đồng bộ"
 msgstr "Hành động sau đồng bộ"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:168
 #: 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
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "Bản phát hành trước"
 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"
 msgid "Proxy"
 msgstr "Proxy"
 msgstr "Proxy"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgid "Proxy Pass"
 msgstr "Chuyển tiếp Proxy"
 msgstr "Chuyển tiếp Proxy"
 
 
@@ -4196,7 +4205,7 @@ msgstr "Xóa thành công"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Đã xóa thành công"
 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/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
 #: 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/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
 #: 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/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgid "Save"
 msgstr "Lưu"
 msgstr "Lưu"
 
 
@@ -4613,7 +4622,7 @@ msgstr "Lưu thành công"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
 #: 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
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Lưu thành công"
 msgstr "Lưu thành công"
@@ -4921,7 +4930,7 @@ msgid "SSO Login"
 msgstr "Đăng nhập SSO"
 msgstr "Đăng nhập SSO"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:165
 #: 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
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgid "Stable"
 msgstr "Ổn định"
 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 "
 "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."
 "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}."
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr ""
 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 "
 "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"
 msgid "Two-factor authentication required"
 msgstr "Yêu cầu xác thực hai yếu tố"
 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/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5660,11 +5669,11 @@ msgstr "Tải lên tệp"
 msgid "Upload Folders"
 msgid "Upload Folders"
 msgstr "Tải lên thư mục"
 msgstr "Tải lên thư mục"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgid "Upstream"
 msgstr "Ngược dòng"
 msgstr "Ngược dòng"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgid "Upstream Name"
 msgstr "Tên Upstream"
 msgstr "Tên Upstream"
 
 
@@ -5757,7 +5766,7 @@ msgstr "Xem"
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "Xem tất cả thông báo"
 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
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgid "View on GitHub"
 msgstr "Xem trên 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"
 msgid "Your passkeys"
 msgstr "Khóa truy cập của bạn"
 msgstr "Khóa truy cập của bạn"
 
 
+#~ msgid "Main"
+#~ msgstr "Chính"
+
 #~ msgid "Select an action after sync"
 #~ msgid "Select an action after sync"
 #~ msgstr "Chọn hành động sau khi đồng bộ"
 #~ 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] 正在将证书写入磁盘"
 msgstr "[Nginx UI] 正在将证书写入磁盘"
 
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
 #: 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
 #: src/views/user/userColumns.tsx:30
 msgid "2FA"
 msgid "2FA"
@@ -162,7 +162,7 @@ msgid "Actual worker to configured ratio"
 msgstr "实际工作进程与配置比例"
 msgstr "实际工作进程与配置比例"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:144
 #: 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/preference/tabs/CertSettings.vue:45
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_edit/components/ConfigTemplate/ConfigTemplate.vue:94
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
 #: src/views/site/site_list/SiteList.vue:95 src/views/stream/StreamList.vue:103
@@ -205,7 +205,7 @@ msgstr "添加站点"
 msgid "Add Stream"
 msgid "Add Stream"
 msgstr "添加 Stream"
 msgstr "添加 Stream"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:187
+#: src/components/NgxConfigEditor/NgxUpstream.vue:118
 msgid "Add Upstream"
 msgid "Add Upstream"
 msgstr "添加上游"
 msgstr "添加上游"
 
 
@@ -217,8 +217,8 @@ msgstr "添加成功"
 msgid "Additional"
 msgid "Additional"
 msgstr "额外选项"
 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"
 msgid "Advance Mode"
 msgstr "高级模式"
 msgstr "高级模式"
 
 
@@ -424,12 +424,16 @@ msgstr "AutoCert 正在运行..."
 msgid "Automatic Restart"
 msgid "Automatic Restart"
 msgstr "自动重启"
 msgstr "自动重启"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:36
+msgid "Average Latency"
+msgstr "平均延迟"
+
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
 #: src/views/nginx_log/NginxLog.vue:174
 #: 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"
 msgid "Back"
 msgstr "返回"
 msgstr "返回"
 
 
@@ -523,8 +527,8 @@ msgstr "基本信息"
 msgid "Basic"
 msgid "Basic"
 msgstr "基本"
 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"
 msgid "Basic Mode"
 msgstr "基本模式"
 msgstr "基本模式"
 
 
@@ -611,7 +615,7 @@ msgstr "基于 worker_processes * worker_connections 计算得出。实际性能
 
 
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/ChatGPT/ChatMessage.vue:216
 #: src/components/NgxConfigEditor/NgxServer.vue:61
 #: 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/config/components/Delete.vue:98
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/preference/components/AuthSettings/Passkey.vue:141
 #: src/views/site/components/SiteStatusSelect.vue:124
 #: 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"
 msgstr "定义共享内存区名称和大小,例如 proxy_cache:10m"
 
 
 #: src/components/NgxConfigEditor/NgxServer.vue:110
 #: 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/certificate/components/RemoveCert.vue:90
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:65
 #: src/views/config/components/Delete.vue:97
 #: src/views/config/components/Delete.vue:97
@@ -1378,6 +1382,10 @@ msgstr "详情"
 msgid "Dev"
 msgid "Dev"
 msgstr "开发版"
 msgstr "开发版"
 
 
+#: src/views/environments/list/BatchUpgrader.vue:171
+msgid "Development"
+msgstr "开发"
+
 #: src/views/system/About.vue:28
 #: src/views/system/About.vue:28
 msgid "Development Mode"
 msgid "Development Mode"
 msgstr "开发模式"
 msgstr "开发模式"
@@ -1480,9 +1488,9 @@ msgstr "在 %{node} 上禁用 %{name} 成功"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:162
 #: 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/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
 #: src/views/user/userColumns.tsx:39
 msgid "Disabled"
 msgid "Disabled"
 msgstr "禁用"
 msgstr "禁用"
@@ -1535,7 +1543,7 @@ msgstr "你想启用 TLS 吗?"
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "你想删除这个服务器吗?"
 msgstr "你想删除这个服务器吗?"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:33
+#: src/components/NgxConfigEditor/NgxUpstream.vue:28
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr "你想删除这个 Upstream 吗?"
 msgstr "你想删除这个 Upstream 吗?"
 
 
@@ -1585,7 +1593,7 @@ msgstr "将证书文件拖放到此处"
 msgid "Drop private key file here"
 msgid "Drop private key file here"
 msgstr "将私钥文件拖放到此处"
 msgstr "将私钥文件拖放到此处"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:189
+#: src/views/environments/list/BatchUpgrader.vue:192
 #: src/views/system/Upgrade.vue:237
 #: src/views/system/Upgrade.vue:237
 msgid "Dry run mode enabled"
 msgid "Dry run mode enabled"
 msgstr "试运行模式已启动"
 msgstr "试运行模式已启动"
@@ -1621,8 +1629,8 @@ msgstr "例如:0 0 * * *(每天午夜)"
 msgid "Edit"
 msgid "Edit"
 msgstr "编辑"
 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}"
 msgid "Edit %{n}"
 msgstr "编辑 %{n}"
 msgstr "编辑 %{n}"
 
 
@@ -1755,10 +1763,10 @@ msgstr "启用 TOTP"
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:25
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/preference/tabs/NodeSettings.vue:30
 #: src/views/site/components/SiteStatusSelect.vue:159
 #: 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/site/site_list/columns.tsx:142 src/views/stream/columns.tsx:108
 #: src/views/stream/components/RightPanel/Basic.vue:24
 #: 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
 #: src/views/user/userColumns.tsx:36
 msgid "Enabled"
 msgid "Enabled"
 msgstr "启用"
 msgstr "启用"
@@ -2366,7 +2374,7 @@ msgstr "获取数据失败"
 msgid "Get dns credential error: {0}"
 msgid "Get dns credential error: {0}"
 msgstr "获取 DNS 凭证错误:{0}"
 msgstr "获取 DNS 凭证错误:{0}"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:178
+#: src/views/environments/list/BatchUpgrader.vue:181
 #: src/views/system/Upgrade.vue:188
 #: src/views/system/Upgrade.vue:188
 msgid "Get release information error"
 msgid "Get release information error"
 msgstr "获取发布信息错误"
 msgstr "获取发布信息错误"
@@ -2408,8 +2416,8 @@ msgid "Higher value means better connection reuse"
 msgstr "更高的值意味着更好的连接再利用"
 msgstr "更高的值意味着更好的连接再利用"
 
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
 #: 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"
 msgid "History"
 msgstr "历史"
 msgstr "历史"
 
 
@@ -2857,16 +2865,13 @@ msgstr ""
 "定时任务,因此您无需修改本页面的参数。对于使用 Docker 容器安装 Nginx 用户界面的用户,您可以手动启用该选项。Nginx UI "
 "定时任务,因此您无需修改本页面的参数。对于使用 Docker 容器安装 Nginx 用户界面的用户,您可以手动启用该选项。Nginx UI "
 "的定时任务任务调度器将按照您设置的时间间隔(以分钟为单位)执行 logrotate 命令。"
 "的定时任务任务调度器将按照您设置的时间间隔(以分钟为单位)执行 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"
 msgid "Main Node"
 msgstr "主节点"
 msgstr "主节点"
 
 
 #: src/views/site/components/SiteStatusSelect.vue:165
 #: 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
 #: src/views/site/site_list/columns.tsx:150
 msgid "Maintenance"
 msgid "Maintenance"
 msgstr "维护模式"
 msgstr "维护模式"
@@ -3066,11 +3071,12 @@ msgstr "每月%{day}日%{time}"
 msgid "Multi-line Directive"
 msgid "Multi-line Directive"
 msgstr "多行指令"
 msgstr "多行指令"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:225
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:38
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:56
 msgid "N/A"
 msgid "N/A"
 msgstr "不适用"
 msgstr "不适用"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:199
+#: src/components/NgxConfigEditor/NgxUpstream.vue:130
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/backup/AutoBackup/AutoBackup.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/ACMEUser.vue:11
 #: src/views/certificate/CertificateList/certColumns.tsx:9
 #: src/views/certificate/CertificateList/certColumns.tsx:9
@@ -3207,8 +3213,8 @@ msgstr "存在 Nginx 配置入口文件"
 msgid "Nginx configuration has been restored"
 msgid "Nginx configuration has been restored"
 msgstr "Nginx 配置已恢复"
 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"
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx 配置解析错误"
 msgstr "Nginx 配置解析错误"
 
 
@@ -3389,7 +3395,7 @@ msgstr "未选择记录"
 msgid "No servers configured"
 msgid "No servers configured"
 msgstr "未配置服务器"
 msgstr "未配置服务器"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:172
+#: src/components/NgxConfigEditor/NgxUpstream.vue:103
 msgid "No upstreams configured"
 msgid "No upstreams configured"
 msgstr "未配置上游"
 msgstr "未配置上游"
 
 
@@ -3416,7 +3422,7 @@ msgstr "节点名称"
 msgid "Node Secret"
 msgid "Node Secret"
 msgstr "节点密钥"
 msgstr "节点密钥"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:208
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:24
 msgid "Node Status"
 msgid "Node Status"
 msgstr "节点状态"
 msgstr "节点状态"
 
 
@@ -3517,10 +3523,9 @@ msgid "Official Document"
 msgstr "官方文档"
 msgstr "官方文档"
 
 
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
 #: src/components/EnvGroupTabs/EnvGroupTabs.vue:127
-#: src/components/NgxConfigEditor/NgxUpstream.vue:151
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:84
 #: 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/dashboard/Environments.vue:106
 #: src/views/environments/list/envColumns.tsx:55
 #: src/views/environments/list/envColumns.tsx:55
 msgid "Offline"
 msgid "Offline"
@@ -3528,7 +3533,7 @@ msgstr "离线"
 
 
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/ChatGPT/ChatMessageInput.vue:18
 #: src/components/NgxConfigEditor/NgxServer.vue:60
 #: 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/components/Notification/Notification.vue:109 src/language/curd.ts:15
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/backup/components/BackupCreator.vue:149
 #: src/views/notification/Notification.vue:39
 #: src/views/notification/Notification.vue:39
@@ -3555,12 +3560,16 @@ msgstr "一旦验证完成,这些记录将被删除。"
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeCard/NodeCard.vue:51
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:64
 #: src/components/NodeSelector/NodeSelector.vue:78
 #: 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/dashboard/Environments.vue:99
 #: src/views/environments/list/envColumns.tsx:51
 #: src/views/environments/list/envColumns.tsx:51
 msgid "Online"
 msgid "Online"
 msgstr "在线"
 msgstr "在线"
 
 
+#: src/components/UpstreamDetailModal/UpstreamDetailModal.vue:32
+msgid "Online Count"
+msgstr "在线数量"
+
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 #: src/components/SystemRestore/SystemRestoreContent.vue:78
 msgid "Only zip files are allowed"
 msgid "Only zip files are allowed"
 msgstr "只允许使用zip文件"
 msgstr "只允许使用zip文件"
@@ -3697,7 +3706,7 @@ msgstr "有效载荷资源为空"
 msgid "Pending"
 msgid "Pending"
 msgstr "待处理"
 msgstr "待处理"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:242
+#: src/views/environments/list/BatchUpgrader.vue:245
 msgid "Perform"
 msgid "Perform"
 msgstr "执行"
 msgstr "执行"
 
 
@@ -3897,7 +3906,7 @@ msgid "Post-sync Action"
 msgstr "同步后操作"
 msgstr "同步后操作"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:168
 #: 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
 #: src/views/system/Upgrade.vue:213 src/views/system/Upgrade.vue:267
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "预发布"
 msgstr "预发布"
@@ -3966,7 +3975,7 @@ msgstr "未找到提供商:{0}"
 msgid "Proxy"
 msgid "Proxy"
 msgstr "代理"
 msgstr "代理"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Proxy Pass"
 msgid "Proxy Pass"
 msgstr "代理传递"
 msgstr "代理传递"
 
 
@@ -4127,7 +4136,7 @@ msgstr "移除成功"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "删除成功"
 msgstr "删除成功"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:131
+#: src/components/NgxConfigEditor/NgxUpstream.vue:75
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/ConfigName.vue:51
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/components/Rename.vue:56
 #: src/views/config/ConfigList.vue:190
 #: src/views/config/ConfigList.vue:190
@@ -4479,9 +4488,9 @@ msgstr "星期六"
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/components/AuthSettings/Passkey.vue:130
 #: src/views/preference/Preference.vue:117
 #: src/views/preference/Preference.vue:117
 #: src/views/site/site_edit/components/ConfigName/ConfigName.vue:52
 #: 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/ConfigName.vue:52
-#: src/views/stream/components/StreamEditor.vue:118
+#: src/views/stream/components/StreamEditor.vue:130
 msgid "Save"
 msgid "Save"
 msgstr "保存"
 msgstr "保存"
 
 
@@ -4539,7 +4548,7 @@ msgstr "保存成功"
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/components/NgxConfigEditor/directive/DirectiveEditorItem.vue:43
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/language/curd.ts:28 src/views/config/components/ConfigLeftPanel.vue:198
 #: src/views/site/site_add/SiteAdd.vue:36
 #: 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
 #: src/views/stream/store.ts:70
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "保存成功"
 msgstr "保存成功"
@@ -4845,7 +4854,7 @@ msgid "SSO Login"
 msgstr "SSO 登录"
 msgstr "SSO 登录"
 
 
 #: src/views/environments/list/BatchUpgrader.vue:165
 #: 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
 #: src/views/system/Upgrade.vue:210 src/views/system/Upgrade.vue:261
 msgid "Stable"
 msgid "Stable"
 msgstr "稳定"
 msgstr "稳定"
@@ -5312,7 +5321,7 @@ msgid ""
 "after the restoration is complete."
 "after the restoration is complete."
 msgstr "这将恢复配置文件和数据库。恢复完成后,Nginx UI 将重新启动。"
 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}."
 msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
 msgstr "将 %{nodeNames} 上的 Nginx UI 升级或重新安装到 %{version} 版本。"
 msgstr "将 %{nodeNames} 上的 Nginx UI 升级或重新安装到 %{version} 版本。"
 
 
@@ -5450,7 +5459,7 @@ msgstr "星期二"
 msgid "Two-factor authentication required"
 msgid "Two-factor authentication required"
 msgstr "需要两步验证"
 msgstr "需要两步验证"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/certificate/CertificateList/certColumns.tsx:24
 #: src/views/config/components/Delete.vue:122
 #: src/views/config/components/Delete.vue:122
 #: src/views/dashboard/components/ModulesTable.vue:83
 #: src/views/dashboard/components/ModulesTable.vue:83
@@ -5531,11 +5540,11 @@ msgstr "上传文件"
 msgid "Upload Folders"
 msgid "Upload Folders"
 msgstr "上传文件夹"
 msgstr "上传文件夹"
 
 
-#: src/components/ProxyTargets/ProxyTargets.vue:125
+#: src/composables/useUpstreamStatus.ts:115
 msgid "Upstream"
 msgid "Upstream"
 msgstr "上游"
 msgstr "上游"
 
 
-#: src/components/NgxConfigEditor/NgxUpstream.vue:194
+#: src/components/NgxConfigEditor/NgxUpstream.vue:125
 msgid "Upstream Name"
 msgid "Upstream Name"
 msgstr "Upstream 名称"
 msgstr "Upstream 名称"
 
 
@@ -5628,7 +5637,7 @@ msgstr "查看"
 msgid "View all notifications"
 msgid "View all notifications"
 msgstr "查看全部通知"
 msgstr "查看全部通知"
 
 
-#: src/views/environments/list/BatchUpgrader.vue:232
+#: src/views/environments/list/BatchUpgrader.vue:235
 #: src/views/system/Upgrade.vue:279
 #: src/views/system/Upgrade.vue:279
 msgid "View on GitHub"
 msgid "View on GitHub"
 msgstr "在 GitHub 上查看"
 msgstr "在 GitHub 上查看"
@@ -5830,6 +5839,9 @@ msgstr "您的旧代码将不再有效。"
 msgid "Your passkeys"
 msgid "Your passkeys"
 msgstr "你的 Passkeys"
 msgstr "你的 Passkeys"
 
 
+#~ msgid "Main"
+#~ msgstr "主"
+
 #~ msgid "Select an action after sync"
 #~ msgid "Select an action after sync"
 #~ msgstr "选择同步后的操作"
 #~ msgstr "选择同步后的操作"
 
 

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

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

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

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

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

@@ -23,7 +23,7 @@ function handleStatusChanged(event: { status: SiteStatus }) {
 </script>
 </script>
 
 
 <template>
 <template>
-  <div class="px-6">
+  <div class="px-6 pb-2">
     <div class="mb-6">
     <div class="mb-6">
       <AForm layout="vertical">
       <AForm layout="vertical">
         <AFormItem :label="$gettext('Status')">
         <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 ConfigHistory from '@/components/ConfigHistory'
 import FooterToolBar from '@/components/FooterToolbar'
 import FooterToolBar from '@/components/FooterToolbar'
 import NgxConfigEditor from '@/components/NgxConfigEditor'
 import NgxConfigEditor from '@/components/NgxConfigEditor'
+import UpstreamCards from '@/components/UpstreamCards/UpstreamCards.vue'
 import { ConfigStatus } from '@/constants'
 import { ConfigStatus } from '@/constants'
 import Cert from '@/views/site/site_edit/components/Cert'
 import Cert from '@/views/site/site_edit/components/Cert'
 import EnableTLS from '@/views/site/site_edit/components/EnableTLS'
 import EnableTLS from '@/views/site/site_edit/components/EnableTLS'
@@ -28,6 +29,11 @@ const {
   curSupportSSL,
   curSupportSSL,
 } = storeToRefs(editorStore)
 } = storeToRefs(editorStore)
 
 
+// Get upstream targets from backend API data
+const upstreamTargets = computed(() => {
+  return data.value.proxy_targets || []
+})
+
 const showHistory = ref(false)
 const showHistory = ref(false)
 
 
 onMounted(() => {
 onMounted(() => {
@@ -132,6 +138,13 @@ async function save() {
           class="domain-edit-container"
           class="domain-edit-container"
         >
         >
           <EnableTLS />
           <EnableTLS />
+
+          <!-- Upstream Cards Display -->
+          <UpstreamCards
+            :targets="upstreamTargets"
+            :env-group-id="data.env_group_id"
+          />
+
           <NgxConfigEditor
           <NgxConfigEditor
             :cert-info="certInfoMap"
             :cert-info="certInfoMap"
             :status="data.status"
             :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 ConfigHistory from '@/components/ConfigHistory'
 import FooterToolBar from '@/components/FooterToolbar'
 import FooterToolBar from '@/components/FooterToolbar'
 import NgxConfigEditor from '@/components/NgxConfigEditor'
 import NgxConfigEditor from '@/components/NgxConfigEditor'
+import UpstreamCards from '@/components/UpstreamCards/UpstreamCards.vue'
 import { ConfigStatus } from '@/constants'
 import { ConfigStatus } from '@/constants'
 import { useStreamEditorStore } from '../store'
 import { useStreamEditorStore } from '../store'
 
 
 const router = useRouter()
 const router = useRouter()
 
 
 const store = useStreamEditorStore()
 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)
 const showHistory = ref(false)
+
+// Get upstream targets from backend API data
+const upstreamTargets = computed(() => {
+  return data.value.proxy_targets || []
+})
 </script>
 </script>
 
 
 <template>
 <template>
@@ -91,6 +97,12 @@ const showHistory = ref(false)
             key="basic"
             key="basic"
             class="domain-edit-container"
             class="domain-edit-container"
           >
           >
+            <!-- Upstream Cards Display -->
+            <UpstreamCards
+              :targets="upstreamTargets"
+              :env-group-id="data.env_group_id"
+            />
+
             <NgxConfigEditor
             <NgxConfigEditor
               :enabled="status === ConfigStatus.Enabled"
               :enabled="status === ConfigStatus.Enabled"
               context="stream"
               context="stream"