瀏覽代碼

feat(nginx): performance optimization #850

Jacky 3 周之前
父節點
當前提交
9d4070a211

+ 2 - 2
.cursor/rules/backend.mdc

@@ -1,6 +1,6 @@
 ---
 ---
-description: backend
-globs: 
+description: 
+globs: **/**/*.go
 alwaysApply: false
 alwaysApply: false
 ---
 ---
 # Cursor Rules
 # Cursor Rules

+ 2 - 2
.cursor/rules/frontend.mdc

@@ -1,6 +1,6 @@
 ---
 ---
-description: frontend
-globs: 
+description: 
+globs: app/**/*.tsx,app/**/*.vue,app/**/*.ts,app/**/*.js,app/**/*.json
 alwaysApply: false
 alwaysApply: false
 ---
 ---
 You are an expert in TypeScript, Node.js, Vite, Vue.js, Vue Router, Pinia, VueUse, Ant Design Vue, and UnoCSS, with a deep understanding of best practices and performance optimization techniques in these technologies.
 You are an expert in TypeScript, Node.js, Vite, Vue.js, Vue Router, Pinia, VueUse, Ant Design Vue, and UnoCSS, with a deep understanding of best practices and performance optimization techniques in these technologies.

+ 36 - 0
api/nginx/performance.go

@@ -0,0 +1,36 @@
+package nginx
+
+import (
+	"net/http"
+
+	"github.com/0xJacky/Nginx-UI/internal/nginx"
+	"github.com/gin-gonic/gin"
+	"github.com/uozi-tech/cosy"
+)
+
+// GetPerformanceSettings retrieves current Nginx performance settings
+func GetPerformanceSettings(c *gin.Context) {
+	// Get Nginx worker configuration info
+	perfInfo, err := nginx.GetNginxWorkerConfigInfo()
+	if err != nil {
+		cosy.ErrHandler(c, err)
+		return
+	}
+	c.JSON(http.StatusOK, perfInfo)
+}
+
+// UpdatePerformanceSettings updates Nginx performance settings
+func UpdatePerformanceSettings(c *gin.Context) {
+	var perfOpt nginx.PerfOpt
+	if !cosy.BindAndValid(c, &perfOpt) {
+		return
+	}
+
+	err := nginx.UpdatePerfOpt(&perfOpt)
+	if err != nil {
+		cosy.ErrHandler(c, err)
+		return
+	}
+
+	GetPerformanceSettings(c)
+}

+ 4 - 0
api/nginx/router.go

@@ -23,4 +23,8 @@ func InitRouter(r *gin.RouterGroup) {
 	r.POST("nginx/stub_status", ToggleStubStatus)
 	r.POST("nginx/stub_status", ToggleStubStatus)
 	r.POST("nginx_log", nginx_log.GetNginxLogPage)
 	r.POST("nginx_log", nginx_log.GetNginxLogPage)
 	r.GET("nginx/directives", GetDirectives)
 	r.GET("nginx/directives", GetDirectives)
+
+	// Performance optimization endpoints
+	r.GET("nginx/performance", GetPerformanceSettings)
+	r.POST("nginx/performance", UpdatePerformanceSettings)
 }
 }

+ 0 - 1
app/components.d.ts

@@ -71,7 +71,6 @@ declare module 'vue' {
     ATag: typeof import('ant-design-vue/es')['Tag']
     ATag: typeof import('ant-design-vue/es')['Tag']
     ATextarea: typeof import('ant-design-vue/es')['Textarea']
     ATextarea: typeof import('ant-design-vue/es')['Textarea']
     ATooltip: typeof import('ant-design-vue/es')['Tooltip']
     ATooltip: typeof import('ant-design-vue/es')['Tooltip']
-    AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger']
     BreadcrumbBreadcrumb: typeof import('./src/components/Breadcrumb/Breadcrumb.vue')['default']
     BreadcrumbBreadcrumb: typeof import('./src/components/Breadcrumb/Breadcrumb.vue')['default']
     ChartAreaChart: typeof import('./src/components/Chart/AreaChart.vue')['default']
     ChartAreaChart: typeof import('./src/components/Chart/AreaChart.vue')['default']
     ChartRadialBarChart: typeof import('./src/components/Chart/RadialBarChart.vue')['default']
     ChartRadialBarChart: typeof import('./src/components/Chart/RadialBarChart.vue')['default']

+ 35 - 0
app/src/api/ngx.ts

@@ -54,6 +54,33 @@ export interface NginxPerformanceInfo {
   process_mode: string // worker进程配置模式:'auto'或'manual'
   process_mode: string // worker进程配置模式:'auto'或'manual'
 }
 }
 
 
+export interface NginxConfigInfo {
+  worker_processes: number
+  worker_connections: number
+  process_mode: string
+  keepalive_timeout: number
+  gzip: string
+  gzip_min_length: number
+  gzip_comp_level: number
+  client_max_body_size: string
+  server_names_hash_bucket_size: number
+  client_header_buffer_size: string
+  client_body_buffer_size: string
+}
+
+export interface NginxPerfOpt {
+  worker_processes: string
+  worker_connections: string
+  keepalive_timeout: string
+  gzip: string
+  gzip_min_length: string
+  gzip_comp_level: string
+  client_max_body_size: string
+  server_names_hash_bucket_size: string
+  client_header_buffer_size: string
+  client_body_buffer_size: string
+}
+
 const ngx = {
 const ngx = {
   build_config(ngxConfig: NgxConfig) {
   build_config(ngxConfig: NgxConfig) {
     return http.post('/ngx/build_config', ngxConfig)
     return http.post('/ngx/build_config', ngxConfig)
@@ -94,6 +121,14 @@ const ngx = {
   get_directives(): Promise<DirectiveMap> {
   get_directives(): Promise<DirectiveMap> {
     return http.get('/nginx/directives')
     return http.get('/nginx/directives')
   },
   },
+
+  get_performance(): Promise<NginxConfigInfo> {
+    return http.get('/nginx/performance')
+  },
+
+  update_performance(params: NginxPerfOpt): Promise<NginxConfigInfo> {
+    return http.post('/nginx/performance', params)
+  },
 }
 }
 
 
 export default ngx
 export default ngx

+ 66 - 66
app/src/components/Notification/notifications.ts

@@ -4,72 +4,6 @@
 
 
 const notifications: Record<string, { title: () => string, content: (args: any) => string }> = {
 const notifications: Record<string, { title: () => string, content: (args: any) => string }> = {
 
 
-  // cluster module notifications
-  'Reload Remote Nginx Error': {
-    title: () => $gettext('Reload Remote Nginx Error'),
-    content: (args: any) => $gettext('Reload Nginx on %{node} failed, response: %{resp}', args),
-  },
-  'Reload Remote Nginx Success': {
-    title: () => $gettext('Reload Remote Nginx Success'),
-    content: (args: any) => $gettext('Reload Nginx on %{node} successfully', args),
-  },
-  'Restart Remote Nginx Error': {
-    title: () => $gettext('Restart Remote Nginx Error'),
-    content: (args: any) => $gettext('Restart Nginx on %{node} failed, response: %{resp}', args),
-  },
-  'Restart Remote Nginx Success': {
-    title: () => $gettext('Restart Remote Nginx Success'),
-    content: (args: any) => $gettext('Restart Nginx on %{node} successfully', args),
-  },
-
-  // cert module notifications
-  'Certificate Expired': {
-    title: () => $gettext('Certificate Expired'),
-    content: (args: any) => $gettext('Certificate %{name} has expired', args),
-  },
-  'Certificate Expiration Notice': {
-    title: () => $gettext('Certificate Expiration Notice'),
-    content: (args: any) => $gettext('Certificate %{name} will expire in %{days} days', args),
-  },
-  'Certificate Expiring Soon': {
-    title: () => $gettext('Certificate Expiring Soon'),
-    content: (args: any) => $gettext('Certificate %{name} will expire in %{days} days', args),
-  },
-  'Certificate Expiring Soon_1': {
-    title: () => $gettext('Certificate Expiring Soon'),
-    content: (args: any) => $gettext('Certificate %{name} will expire in %{days} days', args),
-  },
-  'Certificate Expiring Soon_2': {
-    title: () => $gettext('Certificate Expiring Soon'),
-    content: (args: any) => $gettext('Certificate %{name} will expire in 1 day', args),
-  },
-  'Sync Certificate Error': {
-    title: () => $gettext('Sync Certificate Error'),
-    content: (args: any) => $gettext('Sync Certificate %{cert_name} to %{env_name} failed', args),
-  },
-  'Sync Certificate Success': {
-    title: () => $gettext('Sync Certificate Success'),
-    content: (args: any) => $gettext('Sync Certificate %{cert_name} to %{env_name} successfully', args),
-  },
-
-  // config module notifications
-  'Sync Config Error': {
-    title: () => $gettext('Sync Config Error'),
-    content: (args: any) => $gettext('Sync config %{config_name} to %{env_name} failed', args),
-  },
-  'Sync Config Success': {
-    title: () => $gettext('Sync Config Success'),
-    content: (args: any) => $gettext('Sync config %{config_name} to %{env_name} successfully', args),
-  },
-  'Rename Remote Config Error': {
-    title: () => $gettext('Rename Remote Config Error'),
-    content: (args: any) => $gettext('Rename %{orig_path} to %{new_path} on %{env_name} failed', args),
-  },
-  'Rename Remote Config Success': {
-    title: () => $gettext('Rename Remote Config Success'),
-    content: (args: any) => $gettext('Rename %{orig_path} to %{new_path} on %{env_name} successfully', args),
-  },
-
   // site module notifications
   // site module notifications
   'Delete Remote Site Error': {
   'Delete Remote Site Error': {
     title: () => $gettext('Delete Remote Site Error'),
     title: () => $gettext('Delete Remote Site Error'),
@@ -175,6 +109,72 @@ const notifications: Record<string, { title: () => string, content: (args: any)
     title: () => $gettext('All Recovery Codes Have Been Used'),
     title: () => $gettext('All Recovery Codes Have Been Used'),
     content: (args: any) => $gettext('Please generate new recovery codes in the preferences immediately to prevent lockout.', args),
     content: (args: any) => $gettext('Please generate new recovery codes in the preferences immediately to prevent lockout.', args),
   },
   },
+
+  // cluster module notifications
+  'Reload Remote Nginx Error': {
+    title: () => $gettext('Reload Remote Nginx Error'),
+    content: (args: any) => $gettext('Reload Nginx on %{node} failed, response: %{resp}', args),
+  },
+  'Reload Remote Nginx Success': {
+    title: () => $gettext('Reload Remote Nginx Success'),
+    content: (args: any) => $gettext('Reload Nginx on %{node} successfully', args),
+  },
+  'Restart Remote Nginx Error': {
+    title: () => $gettext('Restart Remote Nginx Error'),
+    content: (args: any) => $gettext('Restart Nginx on %{node} failed, response: %{resp}', args),
+  },
+  'Restart Remote Nginx Success': {
+    title: () => $gettext('Restart Remote Nginx Success'),
+    content: (args: any) => $gettext('Restart Nginx on %{node} successfully', args),
+  },
+
+  // cert module notifications
+  'Certificate Expired': {
+    title: () => $gettext('Certificate Expired'),
+    content: (args: any) => $gettext('Certificate %{name} has expired', args),
+  },
+  'Certificate Expiration Notice': {
+    title: () => $gettext('Certificate Expiration Notice'),
+    content: (args: any) => $gettext('Certificate %{name} will expire in %{days} days', args),
+  },
+  'Certificate Expiring Soon': {
+    title: () => $gettext('Certificate Expiring Soon'),
+    content: (args: any) => $gettext('Certificate %{name} will expire in %{days} days', args),
+  },
+  'Certificate Expiring Soon_1': {
+    title: () => $gettext('Certificate Expiring Soon'),
+    content: (args: any) => $gettext('Certificate %{name} will expire in %{days} days', args),
+  },
+  'Certificate Expiring Soon_2': {
+    title: () => $gettext('Certificate Expiring Soon'),
+    content: (args: any) => $gettext('Certificate %{name} will expire in 1 day', args),
+  },
+  'Sync Certificate Error': {
+    title: () => $gettext('Sync Certificate Error'),
+    content: (args: any) => $gettext('Sync Certificate %{cert_name} to %{env_name} failed', args),
+  },
+  'Sync Certificate Success': {
+    title: () => $gettext('Sync Certificate Success'),
+    content: (args: any) => $gettext('Sync Certificate %{cert_name} to %{env_name} successfully', args),
+  },
+
+  // config module notifications
+  'Sync Config Error': {
+    title: () => $gettext('Sync Config Error'),
+    content: (args: any) => $gettext('Sync config %{config_name} to %{env_name} failed', args),
+  },
+  'Sync Config Success': {
+    title: () => $gettext('Sync Config Success'),
+    content: (args: any) => $gettext('Sync config %{config_name} to %{env_name} successfully', args),
+  },
+  'Rename Remote Config Error': {
+    title: () => $gettext('Rename Remote Config Error'),
+    content: (args: any) => $gettext('Rename %{orig_path} to %{new_path} on %{env_name} failed', args),
+  },
+  'Rename Remote Config Success': {
+    title: () => $gettext('Rename Remote Config Success'),
+    content: (args: any) => $gettext('Rename %{orig_path} to %{new_path} on %{env_name} successfully', args),
+  },
 }
 }
 
 
 export default notifications
 export default notifications

+ 1 - 0
app/src/constants/errors/self_check.ts

@@ -11,4 +11,5 @@ export default {
   4047: () => $gettext('Sites-enabled directory not exist'),
   4047: () => $gettext('Sites-enabled directory not exist'),
   4048: () => $gettext('Streams-available directory not exist'),
   4048: () => $gettext('Streams-available directory not exist'),
   4049: () => $gettext('Streams-enabled directory not exist'),
   4049: () => $gettext('Streams-enabled directory not exist'),
+  4050: () => $gettext('Nginx conf not include conf.d directory'),
 }
 }

文件差異過大導致無法顯示
+ 232 - 117
app/src/language/ar/app.po


文件差異過大導致無法顯示
+ 232 - 117
app/src/language/de_DE/app.po


文件差異過大導致無法顯示
+ 231 - 117
app/src/language/en/app.po


文件差異過大導致無法顯示
+ 232 - 117
app/src/language/es/app.po


文件差異過大導致無法顯示
+ 231 - 117
app/src/language/fr_FR/app.po


文件差異過大導致無法顯示
+ 232 - 117
app/src/language/ko_KR/app.po


文件差異過大導致無法顯示
+ 230 - 121
app/src/language/messages.pot


文件差異過大導致無法顯示
+ 232 - 117
app/src/language/ru_RU/app.po


文件差異過大導致無法顯示
+ 232 - 117
app/src/language/tr_TR/app.po


文件差異過大導致無法顯示
+ 232 - 117
app/src/language/vi_VN/app.po


文件差異過大導致無法顯示
+ 229 - 120
app/src/language/zh_CN/app.po


文件差異過大導致無法顯示
+ 238 - 123
app/src/language/zh_TW/app.po


+ 6 - 0
app/src/views/config/ConfigEditor.vue

@@ -61,6 +61,9 @@ const newPath = computed(() => {
 const relativePath = computed(() => (basePath.value ? `${basePath.value}/${route.params.name}` : route.params.name) as string)
 const relativePath = computed(() => (basePath.value ? `${basePath.value}/${route.params.name}` : route.params.name) as string)
 const breadcrumbs = useBreadcrumbs()
 const breadcrumbs = useBreadcrumbs()
 
 
+// Use Vue 3.4+ useTemplateRef for InspectConfig component
+const inspectConfigRef = useTemplateRef<InstanceType<typeof InspectConfig>>('inspectConfig')
+
 async function init() {
 async function init() {
   const { name } = route.params
   const { name } = route.params
 
 
@@ -200,6 +203,8 @@ function save() {
       }
       }
       else {
       else {
         data.value = r
         data.value = r
+        // Run test after saving to verify configuration
+        inspectConfigRef.value?.test()
       }
       }
     })
     })
   })
   })
@@ -254,6 +259,7 @@ function openHistory() {
 
 
         <InspectConfig
         <InspectConfig
           v-show="!addMode"
           v-show="!addMode"
+          ref="inspectConfig"
         />
         />
         <CodeEditor v-model:content="data.content" />
         <CodeEditor v-model:content="data.content" />
         <FooterToolBar>
         <FooterToolBar>

+ 4 - 0
app/src/views/dashboard/NginxDashBoard.vue

@@ -7,6 +7,7 @@ import { useUserStore } from '@/pinia'
 import { useGlobalStore } from '@/pinia/moudule/global'
 import { useGlobalStore } from '@/pinia/moudule/global'
 import { ClockCircleOutlined, ReloadOutlined } from '@ant-design/icons-vue'
 import { ClockCircleOutlined, ReloadOutlined } from '@ant-design/icons-vue'
 import ConnectionMetricsCard from './components/ConnectionMetricsCard.vue'
 import ConnectionMetricsCard from './components/ConnectionMetricsCard.vue'
+import PerformanceOptimization from './components/PerformanceOptimization.vue'
 import PerformanceStatisticsCard from './components/PerformanceStatisticsCard.vue'
 import PerformanceStatisticsCard from './components/PerformanceStatisticsCard.vue'
 import PerformanceTablesCard from './components/PerformanceTablesCard.vue'
 import PerformanceTablesCard from './components/PerformanceTablesCard.vue'
 import ProcessDistributionCard from './components/ProcessDistributionCard.vue'
 import ProcessDistributionCard from './components/ProcessDistributionCard.vue'
@@ -182,6 +183,9 @@ onMounted(() => {
       <div v-if="nginxInfo" class="performance-dashboard">
       <div v-if="nginxInfo" class="performance-dashboard">
         <!-- Top performance metrics card -->
         <!-- Top performance metrics card -->
         <ACard class="mb-4" :title="$gettext('Performance Metrics')" :bordered="false">
         <ACard class="mb-4" :title="$gettext('Performance Metrics')" :bordered="false">
+          <template #extra>
+            <PerformanceOptimization />
+          </template>
           <PerformanceStatisticsCard :nginx-info="nginxInfo" />
           <PerformanceStatisticsCard :nginx-info="nginxInfo" />
         </ACard>
         </ACard>
 
 

+ 0 - 1
app/src/views/dashboard/components/ConnectionMetricsCard.vue

@@ -1,6 +1,5 @@
 <script setup lang="ts">
 <script setup lang="ts">
 import type { NginxPerformanceInfo } from '@/api/ngx'
 import type { NginxPerformanceInfo } from '@/api/ngx'
-import { computed, defineProps } from 'vue'
 
 
 const props = defineProps<{
 const props = defineProps<{
   nginxInfo: NginxPerformanceInfo
   nginxInfo: NginxPerformanceInfo

+ 327 - 0
app/src/views/dashboard/components/PerformanceOptimization.vue

@@ -0,0 +1,327 @@
+<script setup lang="ts">
+import type { NginxConfigInfo, NginxPerfOpt } from '@/api/ngx'
+import type { CheckedType } from '@/types'
+import ngx from '@/api/ngx'
+import {
+  SettingOutlined,
+} from '@ant-design/icons-vue'
+import { message } from 'ant-design-vue'
+
+// Size units
+const sizeUnits = ['k', 'm', 'g']
+
+// Size values and units
+const maxBodySizeValue = ref<number>(1)
+const maxBodySizeUnit = ref<string>('m')
+const headerBufferSizeValue = ref<number>(1)
+const headerBufferSizeUnit = ref<string>('k')
+const bodyBufferSizeValue = ref<number>(8)
+const bodyBufferSizeUnit = ref<string>('k')
+
+// Performance settings modal
+const visible = ref(false)
+const loading = ref(false)
+const performanceConfig = ref<NginxConfigInfo>({
+  worker_processes: 1,
+  worker_connections: 1024,
+  process_mode: 'manual',
+  keepalive_timeout: 65,
+  gzip: 'off',
+  gzip_min_length: 1,
+  gzip_comp_level: 1,
+  client_max_body_size: '1m',
+  server_names_hash_bucket_size: 32,
+  client_header_buffer_size: '1k',
+  client_body_buffer_size: '8k',
+})
+
+// Open modal and load performance settings
+async function openPerformanceModal() {
+  visible.value = true
+  await fetchPerformanceSettings()
+}
+
+// Load performance settings
+async function fetchPerformanceSettings() {
+  loading.value = true
+  try {
+    const data = await ngx.get_performance()
+    performanceConfig.value = data
+
+    // Parse size values and units
+    parseSizeValues()
+  }
+  catch (error) {
+    console.error('Failed to get Nginx performance settings:', error)
+    message.error($gettext('Failed to get Nginx performance settings'))
+  }
+  finally {
+    loading.value = false
+  }
+}
+
+// Parse size values from config
+function parseSizeValues() {
+  // Parse client_max_body_size
+  const maxBodySize = performanceConfig.value.client_max_body_size
+  const maxBodyMatch = maxBodySize.match(/^(\d+)([kmg])?$/i)
+  if (maxBodyMatch) {
+    maxBodySizeValue.value = Number.parseInt(maxBodyMatch[1])
+    maxBodySizeUnit.value = (maxBodyMatch[2] || 'm').toLowerCase()
+  }
+
+  // Parse client_header_buffer_size
+  const headerSize = performanceConfig.value.client_header_buffer_size
+  const headerMatch = headerSize.match(/^(\d+)([kmg])?$/i)
+  if (headerMatch) {
+    headerBufferSizeValue.value = Number.parseInt(headerMatch[1])
+    headerBufferSizeUnit.value = (headerMatch[2] || 'k').toLowerCase()
+  }
+
+  // Parse client_body_buffer_size
+  const bodySize = performanceConfig.value.client_body_buffer_size
+  const bodyMatch = bodySize.match(/^(\d+)([kmg])?$/i)
+  if (bodyMatch) {
+    bodyBufferSizeValue.value = Number.parseInt(bodyMatch[1])
+    bodyBufferSizeUnit.value = (bodyMatch[2] || 'k').toLowerCase()
+  }
+}
+
+// Format size values before saving
+function formatSizeValues() {
+  performanceConfig.value.client_max_body_size = `${maxBodySizeValue.value}${maxBodySizeUnit.value}`
+  performanceConfig.value.client_header_buffer_size = `${headerBufferSizeValue.value}${headerBufferSizeUnit.value}`
+  performanceConfig.value.client_body_buffer_size = `${bodyBufferSizeValue.value}${bodyBufferSizeUnit.value}`
+}
+
+// Save performance settings
+async function savePerformanceSettings() {
+  loading.value = true
+  try {
+    // Format size values
+    formatSizeValues()
+
+    const params: NginxPerfOpt = {
+      worker_processes: performanceConfig.value.process_mode === 'auto' ? 'auto' : performanceConfig.value.worker_processes.toString(),
+      worker_connections: performanceConfig.value.worker_connections.toString(),
+      keepalive_timeout: performanceConfig.value.keepalive_timeout.toString(),
+      gzip: performanceConfig.value.gzip,
+      gzip_min_length: performanceConfig.value.gzip_min_length.toString(),
+      gzip_comp_level: performanceConfig.value.gzip_comp_level.toString(),
+      client_max_body_size: performanceConfig.value.client_max_body_size,
+      server_names_hash_bucket_size: performanceConfig.value.server_names_hash_bucket_size.toString(),
+      client_header_buffer_size: performanceConfig.value.client_header_buffer_size,
+      client_body_buffer_size: performanceConfig.value.client_body_buffer_size,
+    }
+    const data = await ngx.update_performance(params)
+    performanceConfig.value = data
+
+    // Parse the returned values
+    parseSizeValues()
+
+    message.success($gettext('Performance settings saved successfully'))
+  }
+  catch (error) {
+    console.error('Failed to save Nginx performance settings:', error)
+    message.error($gettext('Failed to save Nginx performance settings'))
+  }
+  finally {
+    loading.value = false
+  }
+}
+
+// Toggle worker process mode
+function handleProcessModeChange(checked: CheckedType) {
+  performanceConfig.value.process_mode = checked ? 'auto' : 'manual'
+  if (checked) {
+    performanceConfig.value.worker_processes = navigator.hardwareConcurrency || 4
+  }
+}
+
+// Toggle GZIP compression
+function handleGzipChange(checked: CheckedType) {
+  performanceConfig.value.gzip = checked ? 'on' : 'off'
+}
+</script>
+
+<template>
+  <div>
+    <!-- Performance Optimization Button -->
+    <AButton
+      type="link"
+      size="small"
+      @click="openPerformanceModal"
+    >
+      <template #icon>
+        <SettingOutlined />
+      </template>
+      {{ $gettext('Optimize Performance') }}
+    </AButton>
+
+    <!-- Performance Optimization Modal -->
+    <AModal
+      v-model:open="visible"
+      :title="$gettext('Optimize Nginx Performance')"
+      :mask-closable="false"
+      :ok-button-props="{ loading }"
+      @ok="savePerformanceSettings"
+    >
+      <ASpin :spinning="loading">
+        <AForm layout="vertical">
+          <AFormItem
+            :label="$gettext('Worker Processes')"
+            :help="$gettext('Number of concurrent worker processes, auto sets to CPU core count')"
+          >
+            <ASpace>
+              <ASwitch
+                :checked="performanceConfig.process_mode === 'auto'"
+                :checked-children="$gettext('Auto')"
+                :un-checked-children="$gettext('Manual')"
+                @change="handleProcessModeChange"
+              />
+              <AInputNumber
+                v-if="performanceConfig.process_mode !== 'auto'"
+                v-model:value="performanceConfig.worker_processes"
+                :min="1"
+                :max="32"
+                style="width: 120px"
+              />
+              <span v-else>{{ performanceConfig.worker_processes }} ({{ $gettext('Auto') }})</span>
+            </ASpace>
+          </AFormItem>
+
+          <AFormItem
+            :label="$gettext('Worker Connections')"
+            :help="$gettext('Maximum number of concurrent connections')"
+          >
+            <AInputNumber
+              v-model:value="performanceConfig.worker_connections"
+              :min="512"
+              :max="65536"
+              style="width: 120px"
+            />
+          </AFormItem>
+
+          <AFormItem
+            :label="$gettext('Keepalive Timeout')"
+            :help="$gettext('Connection timeout period')"
+          >
+            <ASpace>
+              <AInputNumber
+                v-model:value="performanceConfig.keepalive_timeout"
+                :min="0"
+                :max="999"
+                style="width: 120px"
+              />
+              <span>{{ $gettext('seconds') }}</span>
+            </ASpace>
+          </AFormItem>
+
+          <AFormItem
+            :label="$gettext('GZIP Compression')"
+            :help="$gettext('Enable compression for content transfer')"
+          >
+            <ASwitch
+              :checked="performanceConfig.gzip === 'on'"
+              :checked-children="$gettext('On')"
+              :un-checked-children="$gettext('Off')"
+              @change="handleGzipChange"
+            />
+          </AFormItem>
+
+          <AFormItem
+            :label="$gettext('GZIP Min Length')"
+            :help="$gettext('Minimum file size for compression')"
+          >
+            <ASpace>
+              <AInputNumber
+                v-model:value="performanceConfig.gzip_min_length"
+                :min="0"
+                style="width: 120px"
+              />
+              <span>{{ $gettext('KB') }}</span>
+            </ASpace>
+          </AFormItem>
+
+          <AFormItem
+            :label="$gettext('GZIP Compression Level')"
+            :help="$gettext('Compression level, 1 is lowest, 9 is highest')"
+          >
+            <AInputNumber
+              v-model:value="performanceConfig.gzip_comp_level"
+              :min="1"
+              :max="9"
+              style="width: 120px"
+            />
+          </AFormItem>
+
+          <AFormItem
+            :label="$gettext('Client Max Body Size')"
+            :help="$gettext('Maximum client request body size')"
+          >
+            <AInputGroup compact style="width: 180px">
+              <AInputNumber
+                v-model:value="maxBodySizeValue"
+                :min="1"
+                style="width: 120px"
+              />
+              <ASelect v-model:value="maxBodySizeUnit" style="width: 60px">
+                <ASelectOption v-for="unit in sizeUnits" :key="unit" :value="unit">
+                  {{ unit.toUpperCase() }}
+                </ASelectOption>
+              </ASelect>
+            </AInputGroup>
+          </AFormItem>
+
+          <AFormItem
+            :label="$gettext('Server Names Hash Bucket Size')"
+            :help="$gettext('Server names hash table size')"
+          >
+            <AInputNumber
+              v-model:value="performanceConfig.server_names_hash_bucket_size"
+              :min="32"
+              :step="32"
+              style="width: 120px"
+            />
+          </AFormItem>
+
+          <AFormItem
+            :label="$gettext('Client Header Buffer Size')"
+            :help="$gettext('Client request header buffer size')"
+          >
+            <AInputGroup compact style="width: 180px">
+              <AInputNumber
+                v-model:value="headerBufferSizeValue"
+                :min="1"
+                style="width: 120px"
+              />
+              <ASelect v-model:value="headerBufferSizeUnit" style="width: 60px">
+                <ASelectOption v-for="unit in sizeUnits" :key="unit" :value="unit">
+                  {{ unit.toUpperCase() }}
+                </ASelectOption>
+              </ASelect>
+            </AInputGroup>
+          </AFormItem>
+
+          <AFormItem
+            :label="$gettext('Client Body Buffer Size')"
+            :help="$gettext('Client request body buffer size')"
+          >
+            <AInputGroup compact style="width: 180px">
+              <AInputNumber
+                v-model:value="bodyBufferSizeValue"
+                :min="1"
+                style="width: 120px"
+              />
+              <ASelect v-model:value="bodyBufferSizeUnit" style="width: 60px">
+                <ASelectOption v-for="unit in sizeUnits" :key="unit" :value="unit">
+                  {{ unit.toUpperCase() }}
+                </ASelectOption>
+              </ASelect>
+            </AInputGroup>
+          </AFormItem>
+        </AForm>
+      </ASpin>
+    </AModal>
+  </div>
+</template>

+ 75 - 74
app/src/views/dashboard/components/PerformanceStatisticsCard.vue

@@ -7,7 +7,6 @@ import {
   InfoCircleOutlined,
   InfoCircleOutlined,
   ThunderboltOutlined,
   ThunderboltOutlined,
 } from '@ant-design/icons-vue'
 } from '@ant-design/icons-vue'
-import { computed, defineProps } from 'vue'
 
 
 const props = defineProps<{
 const props = defineProps<{
   nginxInfo: NginxPerformanceInfo
   nginxInfo: NginxPerformanceInfo
@@ -28,80 +27,82 @@ const maxRPS = computed(() => {
 </script>
 </script>
 
 
 <template>
 <template>
-  <ARow :gutter="[16, 24]">
-    <!-- Maximum RPS -->
-    <ACol :xs="24" :sm="12" :md="8" :lg="6">
-      <AStatistic
-        :value="maxRPS"
-        :value-style="{ color: '#1890ff', fontSize: '24px' }"
-      >
-        <template #prefix>
-          <ThunderboltOutlined />
-        </template>
-        <template #title>
-          {{ $gettext('Max Requests Per Second') }}
-          <ATooltip :title="$gettext('Calculated based on worker_processes * worker_connections. Actual performance depends on hardware, configuration, and workload')">
-            <InfoCircleOutlined class="ml-1 text-gray-500" />
-          </ATooltip>
-        </template>
-      </AStatistic>
-      <div class="text-xs text-gray-500 mt-1">
-        worker_processes ({{ nginxInfo.worker_processes }}) × worker_connections ({{ nginxInfo.worker_connections }})
-      </div>
-    </ACol>
+  <div>
+    <ARow :gutter="[16, 24]">
+      <!-- Maximum RPS -->
+      <ACol :xs="24" :sm="12" :md="8" :lg="6">
+        <AStatistic
+          :value="maxRPS"
+          :value-style="{ color: '#1890ff', fontSize: '24px' }"
+        >
+          <template #prefix>
+            <ThunderboltOutlined />
+          </template>
+          <template #title>
+            {{ $gettext('Max Requests Per Second') }}
+            <ATooltip :title="$gettext('Calculated based on worker_processes * worker_connections. Actual performance depends on hardware, configuration, and workload')">
+              <InfoCircleOutlined class="ml-1 text-gray-500" />
+            </ATooltip>
+          </template>
+        </AStatistic>
+        <div class="text-xs text-gray-500 mt-1">
+          worker_processes ({{ nginxInfo.worker_processes }}) × worker_connections ({{ nginxInfo.worker_connections }})
+        </div>
+      </ACol>
 
 
-    <!-- Maximum concurrent connections -->
-    <ACol :xs="24" :sm="12" :md="8" :lg="6">
-      <AStatistic
-        :title="$gettext('Max Concurrent Connections')"
-        :value="nginxInfo.worker_processes * nginxInfo.worker_connections"
-        :value-style="{ color: '#52c41a', fontSize: '24px' }"
-      >
-        <template #prefix>
-          <ApiOutlined />
-        </template>
-      </AStatistic>
-      <div class="text-xs text-gray-500 mt-1">
-        {{ $gettext('Current usage') }}: {{ ((nginxInfo.active / (nginxInfo.worker_processes * nginxInfo.worker_connections)) * 100).toFixed(2) }}%
-      </div>
-    </ACol>
+      <!-- Maximum concurrent connections -->
+      <ACol :xs="24" :sm="12" :md="8" :lg="6">
+        <AStatistic
+          :title="$gettext('Max Concurrent Connections')"
+          :value="nginxInfo.worker_processes * nginxInfo.worker_connections"
+          :value-style="{ color: '#52c41a', fontSize: '24px' }"
+        >
+          <template #prefix>
+            <ApiOutlined />
+          </template>
+        </AStatistic>
+        <div class="text-xs text-gray-500 mt-1">
+          {{ $gettext('Current usage') }}: {{ ((nginxInfo.active / (nginxInfo.worker_processes * nginxInfo.worker_connections)) * 100).toFixed(2) }}%
+        </div>
+      </ACol>
 
 
-    <!-- Requests per connection -->
-    <ACol :xs="24" :sm="12" :md="8" :lg="6">
-      <AStatistic
-        :value="requestsPerConnection"
-        :precision="2"
-        :value-style="{ color: '#3a7f99', fontSize: '24px' }"
-      >
-        <template #title>
-          {{ $gettext('Requests Per Connection') }}
-          <ATooltip :title="$gettext('Total Requests / Total Connections')">
-            <InfoCircleOutlined class="ml-1 text-gray-500" />
-          </ATooltip>
-        </template>
-        <template #prefix>
-          <DashboardOutlined />
-        </template>
-      </AStatistic>
-      <div class="text-xs text-gray-500 mt-1">
-        {{ $gettext('Higher value means better connection reuse') }}
-      </div>
-    </ACol>
+      <!-- Requests per connection -->
+      <ACol :xs="24" :sm="12" :md="8" :lg="6">
+        <AStatistic
+          :value="requestsPerConnection"
+          :precision="2"
+          :value-style="{ color: '#3a7f99', fontSize: '24px' }"
+        >
+          <template #title>
+            {{ $gettext('Requests Per Connection') }}
+            <ATooltip :title="$gettext('Total Requests / Total Connections')">
+              <InfoCircleOutlined class="ml-1 text-gray-500" />
+            </ATooltip>
+          </template>
+          <template #prefix>
+            <DashboardOutlined />
+          </template>
+        </AStatistic>
+        <div class="text-xs text-gray-500 mt-1">
+          {{ $gettext('Higher value means better connection reuse') }}
+        </div>
+      </ACol>
 
 
-    <!-- Total Nginx processes -->
-    <ACol :xs="24" :sm="12" :md="8" :lg="6">
-      <AStatistic
-        :title="$gettext('Total Nginx Processes')"
-        :value="nginxInfo.workers + nginxInfo.master + nginxInfo.cache + nginxInfo.other"
-        :value-style="{ color: '#722ed1', fontSize: '24px' }"
-      >
-        <template #prefix>
-          <CloudServerOutlined />
-        </template>
-      </AStatistic>
-      <div class="text-xs text-gray-500 mt-1">
-        {{ $gettext('Workers') }}: {{ nginxInfo.workers }}, {{ $gettext('Master') }}: {{ nginxInfo.master }}, {{ $gettext('Others') }}: {{ nginxInfo.cache + nginxInfo.other }}
-      </div>
-    </ACol>
-  </ARow>
+      <!-- Total Nginx processes -->
+      <ACol :xs="24" :sm="12" :md="8" :lg="6">
+        <AStatistic
+          :title="$gettext('Total Nginx Processes')"
+          :value="nginxInfo.workers + nginxInfo.master + nginxInfo.cache + nginxInfo.other"
+          :value-style="{ color: '#722ed1', fontSize: '24px' }"
+        >
+          <template #prefix>
+            <CloudServerOutlined />
+          </template>
+        </AStatistic>
+        <div class="text-xs text-gray-500 mt-1">
+          {{ $gettext('Workers') }}: {{ nginxInfo.workers }}, {{ $gettext('Master') }}: {{ nginxInfo.master }}, {{ $gettext('Others') }}: {{ nginxInfo.cache + nginxInfo.other }}
+        </div>
+      </ACol>
+    </ARow>
+  </div>
 </template>
 </template>

+ 0 - 1
app/src/views/dashboard/components/PerformanceTablesCard.vue

@@ -2,7 +2,6 @@
 import type { NginxPerformanceInfo } from '@/api/ngx'
 import type { NginxPerformanceInfo } from '@/api/ngx'
 import type { TableColumnType } from 'ant-design-vue'
 import type { TableColumnType } from 'ant-design-vue'
 import { InfoCircleOutlined } from '@ant-design/icons-vue'
 import { InfoCircleOutlined } from '@ant-design/icons-vue'
-import { computed, defineProps, ref } from 'vue'
 
 
 const props = defineProps<{
 const props = defineProps<{
   nginxInfo: NginxPerformanceInfo
   nginxInfo: NginxPerformanceInfo

+ 1 - 1
app/src/views/system/About.vue

@@ -50,7 +50,7 @@ const thisYear = new Date().getFullYear()
     <h3>
     <h3>
       {{ $gettext('Project Team') }}
       {{ $gettext('Project Team') }}
     </h3>
     </h3>
-    <p><a href="https://jackyu.cn/">@0xJacky</a> <a href="https://blog.kugeek.com/">@Hintay</a></p>
+    <p><a href="https://jackyu.cn/">@0xJacky</a> <a href="https://blog.kugeek.com/">@Hintay</a> <a href="https://github.com/akinoccc">@Akino</a></p>
     <h3>
     <h3>
       {{ $gettext('Build with') }}
       {{ $gettext('Build with') }}
     </h3>
     </h3>

+ 83 - 13
internal/nginx/config_info.go

@@ -1,7 +1,7 @@
 package nginx
 package nginx
 
 
 import (
 import (
-	"os/exec"
+	"os"
 	"regexp"
 	"regexp"
 	"runtime"
 	"runtime"
 	"strconv"
 	"strconv"
@@ -10,29 +10,51 @@ import (
 )
 )
 
 
 type NginxConfigInfo struct {
 type NginxConfigInfo struct {
-	WorkerProcesses   int    `json:"worker_processes"`
-	WorkerConnections int    `json:"worker_connections"`
-	ProcessMode       string `json:"process_mode"`
+	WorkerProcesses           int    `json:"worker_processes"`
+	WorkerConnections         int    `json:"worker_connections"`
+	ProcessMode               string `json:"process_mode"`
+	KeepaliveTimeout          int    `json:"keepalive_timeout"`
+	Gzip                      string `json:"gzip"`
+	GzipMinLength             int    `json:"gzip_min_length"`
+	GzipCompLevel             int    `json:"gzip_comp_level"`
+	ClientMaxBodySize         string `json:"client_max_body_size"` // with unit
+	ServerNamesHashBucketSize int    `json:"server_names_hash_bucket_size"`
+	ClientHeaderBufferSize    string `json:"client_header_buffer_size"` // with unit
+	ClientBodyBufferSize      string `json:"client_body_buffer_size"`   // with unit
 }
 }
 
 
 // GetNginxWorkerConfigInfo Get Nginx config info of worker_processes and worker_connections
 // GetNginxWorkerConfigInfo Get Nginx config info of worker_processes and worker_connections
 func GetNginxWorkerConfigInfo() (*NginxConfigInfo, error) {
 func GetNginxWorkerConfigInfo() (*NginxConfigInfo, error) {
 	result := &NginxConfigInfo{
 	result := &NginxConfigInfo{
-		WorkerProcesses:   1,
-		WorkerConnections: 1024,
-		ProcessMode:       "manual",
+		WorkerProcesses:           1,
+		WorkerConnections:         1024,
+		ProcessMode:               "manual",
+		KeepaliveTimeout:          65,
+		Gzip:                      "off",
+		GzipMinLength:             1,
+		GzipCompLevel:             1,
+		ClientMaxBodySize:         "1m",
+		ServerNamesHashBucketSize: 32,
+		ClientHeaderBufferSize:    "1k",
+		ClientBodyBufferSize:      "8k",
 	}
 	}
 
 
-	// Get worker_processes config
-	cmd := exec.Command("nginx", "-T")
-	output, err := cmd.CombinedOutput()
+	confPath := GetConfPath("nginx.conf")
+	if confPath == "" {
+		return nil, errors.New("failed to get nginx.conf path")
+	}
+
+	// Read the current configuration
+	content, err := os.ReadFile(confPath)
 	if err != nil {
 	if err != nil {
-		return result, errors.Wrap(err, "failed to get nginx config")
+		return nil, errors.Wrap(err, "failed to read nginx.conf")
 	}
 	}
 
 
+	outputStr := string(content)
+
 	// Parse worker_processes
 	// Parse worker_processes
 	wpRe := regexp.MustCompile(`worker_processes\s+(\d+|auto);`)
 	wpRe := regexp.MustCompile(`worker_processes\s+(\d+|auto);`)
-	if matches := wpRe.FindStringSubmatch(string(output)); len(matches) > 1 {
+	if matches := wpRe.FindStringSubmatch(outputStr); len(matches) > 1 {
 		if matches[1] == "auto" {
 		if matches[1] == "auto" {
 			result.WorkerProcesses = runtime.NumCPU()
 			result.WorkerProcesses = runtime.NumCPU()
 			result.ProcessMode = "auto"
 			result.ProcessMode = "auto"
@@ -44,9 +66,57 @@ func GetNginxWorkerConfigInfo() (*NginxConfigInfo, error) {
 
 
 	// Parse worker_connections
 	// Parse worker_connections
 	wcRe := regexp.MustCompile(`worker_connections\s+(\d+);`)
 	wcRe := regexp.MustCompile(`worker_connections\s+(\d+);`)
-	if matches := wcRe.FindStringSubmatch(string(output)); len(matches) > 1 {
+	if matches := wcRe.FindStringSubmatch(outputStr); len(matches) > 1 {
 		result.WorkerConnections, _ = strconv.Atoi(matches[1])
 		result.WorkerConnections, _ = strconv.Atoi(matches[1])
 	}
 	}
 
 
+	// Parse keepalive_timeout
+	ktRe := regexp.MustCompile(`keepalive_timeout\s+(\d+);`)
+	if matches := ktRe.FindStringSubmatch(outputStr); len(matches) > 1 {
+		result.KeepaliveTimeout, _ = strconv.Atoi(matches[1])
+	}
+
+	// Parse gzip
+	gzipRe := regexp.MustCompile(`gzip\s+(on|off);`)
+	if matches := gzipRe.FindStringSubmatch(outputStr); len(matches) > 1 {
+		result.Gzip = matches[1]
+	}
+
+	// Parse gzip_min_length
+	gzipMinRe := regexp.MustCompile(`gzip_min_length\s+(\d+);`)
+	if matches := gzipMinRe.FindStringSubmatch(outputStr); len(matches) > 1 {
+		result.GzipMinLength, _ = strconv.Atoi(matches[1])
+	}
+
+	// Parse gzip_comp_level
+	gzipCompRe := regexp.MustCompile(`gzip_comp_level\s+(\d+);`)
+	if matches := gzipCompRe.FindStringSubmatch(outputStr); len(matches) > 1 {
+		result.GzipCompLevel, _ = strconv.Atoi(matches[1])
+	}
+
+	// Parse client_max_body_size with any unit (k, m, g)
+	cmaxRe := regexp.MustCompile(`client_max_body_size\s+(\d+[kmg]?);`)
+	if matches := cmaxRe.FindStringSubmatch(outputStr); len(matches) > 1 {
+		result.ClientMaxBodySize = matches[1]
+	}
+
+	// Parse server_names_hash_bucket_size
+	hashRe := regexp.MustCompile(`server_names_hash_bucket_size\s+(\d+);`)
+	if matches := hashRe.FindStringSubmatch(outputStr); len(matches) > 1 {
+		result.ServerNamesHashBucketSize, _ = strconv.Atoi(matches[1])
+	}
+
+	// Parse client_header_buffer_size with any unit (k, m, g)
+	headerRe := regexp.MustCompile(`client_header_buffer_size\s+(\d+[kmg]?);`)
+	if matches := headerRe.FindStringSubmatch(outputStr); len(matches) > 1 {
+		result.ClientHeaderBufferSize = matches[1]
+	}
+
+	// Parse client_body_buffer_size with any unit (k, m, g)
+	bodyRe := regexp.MustCompile(`client_body_buffer_size\s+(\d+[kmg]?);`)
+	if matches := bodyRe.FindStringSubmatch(outputStr); len(matches) > 1 {
+		result.ClientBodyBufferSize = matches[1]
+	}
+
 	return result, nil
 	return result, nil
 }
 }

+ 148 - 0
internal/nginx/perf_opt.go

@@ -0,0 +1,148 @@
+package nginx
+
+import (
+	"fmt"
+	"os"
+	"sort"
+	"time"
+
+	"github.com/pkg/errors"
+	"github.com/tufanbarisyildirim/gonginx/config"
+	"github.com/tufanbarisyildirim/gonginx/dumper"
+	"github.com/tufanbarisyildirim/gonginx/parser"
+)
+
+// PerfOpt represents Nginx performance optimization settings
+type PerfOpt struct {
+	WorkerProcesses           string `json:"worker_processes"`              // auto or number
+	WorkerConnections         string `json:"worker_connections"`            // max connections
+	KeepaliveTimeout          string `json:"keepalive_timeout"`             // timeout in seconds
+	Gzip                      string `json:"gzip"`                          // on or off
+	GzipMinLength             string `json:"gzip_min_length"`               // min length to compress
+	GzipCompLevel             string `json:"gzip_comp_level"`               // compression level
+	ClientMaxBodySize         string `json:"client_max_body_size"`          // max body size (with unit: k, m, g)
+	ServerNamesHashBucketSize string `json:"server_names_hash_bucket_size"` // hash bucket size
+	ClientHeaderBufferSize    string `json:"client_header_buffer_size"`     // header buffer size (with unit: k, m, g)
+	ClientBodyBufferSize      string `json:"client_body_buffer_size"`       // body buffer size (with unit: k, m, g)
+}
+
+// UpdatePerfOpt updates the Nginx performance optimization settings
+func UpdatePerfOpt(opt *PerfOpt) error {
+	confPath := GetConfPath("nginx.conf")
+	if confPath == "" {
+		return errors.New("failed to get nginx.conf path")
+	}
+
+	// Read the current configuration
+	content, err := os.ReadFile(confPath)
+	if err != nil {
+		return errors.Wrap(err, "failed to read nginx.conf")
+	}
+
+	// Create a backup file
+	backupPath := fmt.Sprintf("%s.backup.%d", confPath, time.Now().Unix())
+	err = os.WriteFile(backupPath, content, 0644)
+	if err != nil {
+		return errors.Wrap(err, "failed to create backup file")
+	}
+
+	// Parse the configuration
+	p := parser.NewStringParser(string(content), parser.WithSkipValidDirectivesErr())
+	conf, err := p.Parse()
+	if err != nil {
+		return errors.Wrap(err, "failed to parse nginx.conf")
+	}
+
+	// Process the configuration and update performance settings
+	updateNginxConfig(conf.Block, opt)
+
+	// Dump the updated configuration
+	updatedConf := dumper.DumpBlock(conf.Block, dumper.IndentedStyle)
+
+	// Write the updated configuration
+	err = os.WriteFile(confPath, []byte(updatedConf), 0644)
+	if err != nil {
+		return errors.Wrap(err, "failed to write updated nginx.conf")
+	}
+
+	return nil
+}
+
+// updateNginxConfig updates the performance settings in the Nginx configuration
+func updateNginxConfig(block config.IBlock, opt *PerfOpt) {
+	if block == nil {
+		return
+	}
+
+	directives := block.GetDirectives()
+	// Update main context directives
+	updateOrAddDirective(block, directives, "worker_processes", opt.WorkerProcesses)
+
+	// Look for events, http, and other blocks
+	for _, directive := range directives {
+		if directive.GetName() == "events" && directive.GetBlock() != nil {
+			// Update events block directives
+			eventsBlock := directive.GetBlock()
+			eventsDirectives := eventsBlock.GetDirectives()
+			updateOrAddDirective(eventsBlock, eventsDirectives, "worker_connections", opt.WorkerConnections)
+		} else if directive.GetName() == "http" && directive.GetBlock() != nil {
+			// Update http block directives
+			httpBlock := directive.GetBlock()
+			httpDirectives := httpBlock.GetDirectives()
+			updateOrAddDirective(httpBlock, httpDirectives, "keepalive_timeout", opt.KeepaliveTimeout)
+			updateOrAddDirective(httpBlock, httpDirectives, "gzip", opt.Gzip)
+			updateOrAddDirective(httpBlock, httpDirectives, "gzip_min_length", opt.GzipMinLength)
+			updateOrAddDirective(httpBlock, httpDirectives, "gzip_comp_level", opt.GzipCompLevel)
+			updateOrAddDirective(httpBlock, httpDirectives, "client_max_body_size", opt.ClientMaxBodySize)
+			updateOrAddDirective(httpBlock, httpDirectives, "server_names_hash_bucket_size", opt.ServerNamesHashBucketSize)
+			updateOrAddDirective(httpBlock, httpDirectives, "client_header_buffer_size", opt.ClientHeaderBufferSize)
+			updateOrAddDirective(httpBlock, httpDirectives, "client_body_buffer_size", opt.ClientBodyBufferSize)
+		}
+	}
+}
+
+// updateOrAddDirective updates a directive if it exists, or adds it to the block if it doesn't
+func updateOrAddDirective(block config.IBlock, directives []config.IDirective, name string, value string) {
+	if value == "" {
+		return
+	}
+
+	// Search for existing directive
+	for _, directive := range directives {
+		if directive.GetName() == name {
+			// Update existing directive
+			if len(directive.GetParameters()) > 0 {
+				directive.GetParameters()[0].Value = value
+			}
+			return
+		}
+	}
+
+	// If we get here, we need to add a new directive
+	// Create a new directive and add it to the block
+	// This requires knowledge of the underlying implementation
+	// For now, we'll use the Directive type from gonginx/config
+	newDirective := &config.Directive{
+		Name:       name,
+		Parameters: []config.Parameter{{Value: value}},
+	}
+
+	// Add the new directive to the block
+	// This is specific to the gonginx library implementation
+	switch block := block.(type) {
+	case *config.Config:
+		block.Block.Directives = append(block.Block.Directives, newDirective)
+	case *config.Block:
+		block.Directives = append(block.Directives, newDirective)
+	case *config.HTTP:
+		block.Directives = append(block.Directives, newDirective)
+	}
+}
+
+// sortDirectives sorts directives alphabetically by name
+func sortDirectives(directives []config.IDirective) {
+	sort.SliceStable(directives, func(i, j int) bool {
+		// Ensure both i and j can return valid names
+		return directives[i].GetName() < directives[j].GetName()
+	})
+}

部分文件因文件數量過多而無法顯示