Browse Source

chore: clean up codes

0xJacky 4 months ago
parent
commit
ccedb94880
100 changed files with 4987 additions and 3604 deletions
  1. 26 25
      api/nginx_log/analytics.go
  2. 124 67
      api/nginx_log/index_management.go
  3. 1 1
      api/nginx_log/log_list.go
  4. 8 5
      api/nginx_log/settings.go
  5. 21 2
      app/components.d.ts
  6. 1 1
      app/src/api/settings.ts
  7. 192 171
      app/src/language/ar/app.po
  8. 223 188
      app/src/language/de_DE/app.po
  9. 7 7
      app/src/language/en/app.po
  10. 214 189
      app/src/language/es/app.po
  11. 223 194
      app/src/language/fr_FR/app.po
  12. 281 164
      app/src/language/ja_JP/app.po
  13. 276 175
      app/src/language/ko_KR/app.po
  14. 7 7
      app/src/language/messages.pot
  15. 207 177
      app/src/language/pt_PT/app.po
  16. 213 189
      app/src/language/ru_RU/app.po
  17. 227 194
      app/src/language/tr_TR/app.po
  18. 216 196
      app/src/language/uk_UA/app.po
  19. 225 201
      app/src/language/vi_VN/app.po
  20. 267 175
      app/src/language/zh_CN/app.po
  21. 266 175
      app/src/language/zh_TW/app.po
  22. 28 13
      app/src/views/nginx_log/NginxLog.vue
  23. 1 1
      app/src/views/preference/store/index.ts
  24. 1 1
      app/src/views/terminal/Terminal.vue
  25. 5 5
      docs/guide/config-nginx-log.md
  26. 1 1
      docs/guide/env.md
  27. 5 5
      docs/zh_CN/guide/config-nginx-log.md
  28. 1 1
      docs/zh_CN/guide/env.md
  29. 5 5
      docs/zh_TW/guide/config-nginx-log.md
  30. 1 1
      docs/zh_TW/guide/env.md
  31. 8 0
      internal/analytic/errors.go
  32. 2 2
      internal/analytic/node.go
  33. 1 0
      internal/cert/errors.go
  34. 1 2
      internal/cert/server_tls.go
  35. 1 1
      internal/cron/incremental_indexing.go
  36. 2 0
      internal/docker/errors.go
  37. 2 2
      internal/docker/ota.go
  38. 1 2
      internal/kernel/boot.go
  39. 7 0
      internal/nginx/nginx.go
  40. 4 4
      internal/nginx_log/analytics/dashboard.go
  41. 9 9
      internal/nginx_log/analytics/service.go
  42. 4 4
      internal/nginx_log/analytics/service_test.go
  43. 17 17
      internal/nginx_log/analytics/time_series.go
  44. 18 18
      internal/nginx_log/analytics/time_series_test.go
  45. 2 2
      internal/nginx_log/conservative_worker_benchmark_test.go
  46. 0 3
      internal/nginx_log/errors.go
  47. 8 0
      internal/nginx_log/indexer/errors.go
  48. 4 4
      internal/nginx_log/indexer/integration_validation_test.go
  49. 7 3
      internal/nginx_log/indexer/log_file_manager.go
  50. 71 39
      internal/nginx_log/indexer/parallel_indexer.go
  51. 21 16
      internal/nginx_log/indexer/parallel_indexer_optimized.go
  52. 14 15
      internal/nginx_log/indexer/parser.go
  53. 8 0
      internal/nginx_log/indexer/progress_tracker.go
  54. 60 29
      internal/nginx_log/indexer/rebuild.go
  55. 2 2
      internal/nginx_log/indexer/throughput_optimizer.go
  56. 26 24
      internal/nginx_log/indexer/types.go
  57. 11 11
      internal/nginx_log/integration_test.go
  58. 65 25
      internal/nginx_log/modern_services.go
  59. 62 62
      internal/nginx_log/monitor.go
  60. 11 11
      internal/nginx_log/optimization_integration_test.go
  61. 11 0
      internal/nginx_log/parser/errors.go
  62. 34 35
      internal/nginx_log/parser/parser.go
  63. 12 12
      internal/nginx_log/parser/parser_test.go
  64. 6 6
      internal/nginx_log/parser/production_scale_test.go
  65. 12 12
      internal/nginx_log/parser/regex_cache_optimization.go
  66. 7 7
      internal/nginx_log/parser/simd_optimizations.go
  67. 4 4
      internal/nginx_log/parser/simd_optimizations_test.go
  68. 13 13
      internal/nginx_log/parser/stream_optimizations.go
  69. 12 12
      internal/nginx_log/parser/stream_optimizations_test.go
  70. 2 6
      internal/nginx_log/parser/types.go
  71. 16 16
      internal/nginx_log/performance_monitor_test.go
  72. 25 10
      internal/nginx_log/preflight.go
  73. 3 3
      internal/nginx_log/production_config.go
  74. 3 3
      internal/nginx_log/production_throughput_test.go
  75. 2 2
      internal/nginx_log/quick_worker_benchmark_test.go
  76. 3 3
      internal/nginx_log/realistic_benchmark_test.go
  77. 356 0
      internal/nginx_log/searcher/cache.go
  78. 10 10
      internal/nginx_log/searcher/cardinality_counter_test.go
  79. 44 44
      internal/nginx_log/searcher/counter.go
  80. 9 9
      internal/nginx_log/searcher/facet_aggregator.go
  81. 2 2
      internal/nginx_log/searcher/facet_aggregator_test.go
  82. 5 5
      internal/nginx_log/searcher/hot_swap_test.go
  83. 0 364
      internal/nginx_log/searcher/optimized_cache.go
  84. 11 11
      internal/nginx_log/searcher/query_builder.go
  85. 102 102
      internal/nginx_log/searcher/searcher.go
  86. 9 9
      internal/nginx_log/searcher/searcher_test.go
  87. 12 12
      internal/nginx_log/searcher/simple_test.go
  88. 4 4
      internal/nginx_log/searcher/types.go
  89. 2 2
      internal/nginx_log/simple_production_test.go
  90. 62 2
      internal/nginx_log/task_recovery.go
  91. 430 0
      internal/nginx_log/task_scheduler.go
  92. 17 17
      internal/nginx_log/utils/memory_pools.go
  93. 6 6
      internal/nginx_log/utils/memory_pools_test.go
  94. 11 11
      internal/nginx_log/utils/performance.go
  95. 2 2
      internal/nginx_log/utils/performance_test.go
  96. 2 2
      internal/nginx_log/worker_scaling_benchmark_test.go
  97. 1 0
      internal/notification/errors.go
  98. 1 2
      internal/notification/telegram.go
  99. 1 1
      internal/performance/config_info.go
  100. 1 0
      internal/performance/errors.go

+ 26 - 25
api/nginx_log/analytics.go

@@ -83,7 +83,7 @@ func GetLogAnalytics(c *gin.Context) {
 	}
 
 	// Get modern analytics service
-	analyticsService := nginx_log.GetModernAnalytics()
+	analyticsService := nginx_log.GetAnalytics()
 	if analyticsService == nil {
 		cosy.ErrHandler(c, nginx_log.ErrModernAnalyticsNotAvailable)
 		return
@@ -130,7 +130,7 @@ func GetLogPreflight(c *gin.Context) {
 	logPath := c.Query("log_path")
 
 	// Create preflight service and perform check
-	preflightService := nginx_log.NewPreflightService()
+	preflightService := nginx_log.NewPreflight()
 	internalResponse, err := preflightService.CheckLogPreflight(logPath)
 	if err != nil {
 		cosy.ErrHandler(c, err)
@@ -170,13 +170,13 @@ func AdvancedSearchLogs(c *gin.Context) {
 		return
 	}
 
-	searcherService := nginx_log.GetModernSearcher()
+	searcherService := nginx_log.GetSearcher()
 	if searcherService == nil {
 		cosy.ErrHandler(c, nginx_log.ErrModernSearcherNotAvailable)
 		return
 	}
 
-	analyticsService := nginx_log.GetModernAnalytics()
+	analyticsService := nginx_log.GetAnalytics()
 	if analyticsService == nil {
 		cosy.ErrHandler(c, nginx_log.ErrModernAnalyticsNotAvailable)
 		return
@@ -304,7 +304,7 @@ func AdvancedSearchLogs(c *gin.Context) {
 		}
 	}
 
-	// 2. Calculate summary stats from the overall results using CardinalityCounter for accuracy
+	// 2. Calculate summary stats from the overall results using Counter for accuracy
 	pv := int(result.TotalHits)
 	var uv, uniquePages int
 	var facetUV, facetUniquePages int
@@ -321,18 +321,18 @@ func AdvancedSearchLogs(c *gin.Context) {
 		}
 	}
 
-	// Override with CardinalityCounter results for better accuracy
+	// Override with Counter results for better accuracy
 	if analyticsService != nil {
 		// Get cardinality counts for UV (unique IPs)
 		if uvResult := getCardinalityCount(ctx, "ip", searchReq); uvResult > 0 {
 			uv = uvResult
-			logger.Debugf("🔢 Search endpoint - UV from CardinalityCounter: %d (vs facet: %d)", uvResult, facetUV)
+			logger.Debugf("🔢 Search endpoint - UV from Counter: %d (vs facet: %d)", uvResult, facetUV)
 		}
 
 		// Get cardinality counts for Unique Pages (unique paths)
 		if upResult := getCardinalityCount(ctx, "path_exact", searchReq); upResult > 0 {
 			uniquePages = upResult
-			logger.Debugf("🔢 Search endpoint - Unique Pages from CardinalityCounter: %d (vs facet: %d)", upResult, facetUniquePages)
+			logger.Debugf("🔢 Search endpoint - Unique Pages from Counter: %d (vs facet: %d)", upResult, facetUniquePages)
 		}
 	}
 
@@ -381,13 +381,13 @@ func GetLogEntries(c *gin.Context) {
 		return
 	}
 
-	searcherService := nginx_log.GetModernSearcher()
+	searcherService := nginx_log.GetSearcher()
 	if searcherService == nil {
 		cosy.ErrHandler(c, nginx_log.ErrModernSearcherNotAvailable)
 		return
 	}
 
-	analyticsService := nginx_log.GetModernAnalytics()
+	analyticsService := nginx_log.GetAnalytics()
 	if analyticsService == nil {
 		cosy.ErrHandler(c, nginx_log.ErrModernAnalyticsNotAvailable)
 		return
@@ -521,7 +521,7 @@ func GetDashboardAnalytics(c *gin.Context) {
 
 	logger.Debugf("Dashboard API received log_path: '%s', start_date: '%s', end_date: '%s'", req.LogPath, req.StartDate, req.EndDate)
 
-	analyticsService := nginx_log.GetModernAnalytics()
+	analyticsService := nginx_log.GetAnalytics()
 	if analyticsService == nil {
 		cosy.ErrHandler(c, nginx_log.ErrModernAnalyticsNotAvailable)
 		return
@@ -628,7 +628,7 @@ func GetWorldMapData(c *gin.Context) {
 	logger.Debugf("WorldMapData request - Path: '%s', StartTime: %d, EndTime: %d, Limit: %d",
 		req.Path, req.StartTime, req.EndTime, req.Limit)
 
-	analyticsService := nginx_log.GetModernAnalytics()
+	analyticsService := nginx_log.GetAnalytics()
 	if analyticsService == nil {
 		cosy.ErrHandler(c, nginx_log.ErrModernAnalyticsNotAvailable)
 		return
@@ -729,7 +729,7 @@ func GetChinaMapData(c *gin.Context) {
 	logger.Debugf("ChinaMapData request - Path: '%s', StartTime: %d, EndTime: %d, Limit: %d",
 		req.Path, req.StartTime, req.EndTime, req.Limit)
 
-	analyticsService := nginx_log.GetModernAnalytics()
+	analyticsService := nginx_log.GetAnalytics()
 	if analyticsService == nil {
 		cosy.ErrHandler(c, nginx_log.ErrModernAnalyticsNotAvailable)
 		return
@@ -823,7 +823,7 @@ func GetGeoStats(c *gin.Context) {
 		return
 	}
 
-	analyticsService := nginx_log.GetModernAnalytics()
+	analyticsService := nginx_log.GetAnalytics()
 	if analyticsService == nil {
 		cosy.ErrHandler(c, nginx_log.ErrModernAnalyticsNotAvailable)
 		return
@@ -900,16 +900,17 @@ func getCardinalityCount(ctx context.Context, field string, searchReq *searcher.
 		cardReq.Field, cardReq.StartTime, cardReq.EndTime, cardReq.LogPaths)
 
 	// Try to get the searcher to access cardinality counter
-	searcherService := nginx_log.GetModernSearcher()
+	searcherService := nginx_log.GetSearcher()
 	if searcherService == nil {
 		logger.Debugf("🚨 getCardinalityCount: ModernSearcher not available for field %s", field)
 		return 0
 	}
 	logger.Debugf("🔍 getCardinalityCount: ModernSearcher available, type: %T", searcherService)
 
-	// Try to cast to DistributedSearcher to access CardinalityCounter
-	if ds, ok := searcherService.(*searcher.DistributedSearcher); ok {
-		logger.Debugf("🔍 getCardinalityCount: Successfully cast to DistributedSearcher")
+	// Use searcher to access Counter
+	if searcherService != nil {
+		ds := searcherService
+		logger.Debugf("🔍 getCardinalityCount: Successfully cast to Searcher")
 		shards := ds.GetShards()
 		logger.Debugf("🔍 getCardinalityCount: Retrieved %d shards", len(shards))
 		if len(shards) > 0 {
@@ -918,26 +919,26 @@ func getCardinalityCount(ctx context.Context, field string, searchReq *searcher.
 				logger.Debugf("🔍 getCardinalityCount: Shard %d: %v", i, shard != nil)
 			}
 
-			cardinalityCounter := searcher.NewCardinalityCounter(shards)
-			logger.Debugf("🔍 getCardinalityCount: Created CardinalityCounter")
-			result, err := cardinalityCounter.CountCardinality(ctx, cardReq)
+			cardinalityCounter := searcher.NewCounter(shards)
+			logger.Debugf("🔍 getCardinalityCount: Created Counter")
+			result, err := cardinalityCounter.Count(ctx, cardReq)
 			if err != nil {
-				logger.Debugf("🚨 getCardinalityCount: CardinalityCounter failed for field %s: %v", field, err)
+				logger.Debugf("🚨 getCardinalityCount: Counter failed for field %s: %v", field, err)
 				return 0
 			}
 
 			if result.Error != "" {
-				logger.Debugf("🚨 getCardinalityCount: CardinalityCounter returned error for field %s: %s", field, result.Error)
+				logger.Debugf("🚨 getCardinalityCount: Counter returned error for field %s: %s", field, result.Error)
 				return 0
 			}
 
 			logger.Debugf("✅ getCardinalityCount: Successfully got cardinality for field %s: %d", field, result.Cardinality)
 			return int(result.Cardinality)
 		} else {
-			logger.Debugf("🚨 getCardinalityCount: DistributedSearcher has no shards for field %s", field)
+			logger.Debugf("🚨 getCardinalityCount: Searcher has no shards for field %s", field)
 		}
 	} else {
-		logger.Debugf("🚨 getCardinalityCount: Searcher is not DistributedSearcher (type: %T) for field %s", searcherService, field)
+		logger.Debugf("🚨 getCardinalityCount: Searcher is not Searcher (type: %T) for field %s", searcherService, field)
 	}
 
 	return 0

+ 124 - 67
api/nginx_log/index_management.go

@@ -69,7 +69,7 @@ func RebuildIndex(c *gin.Context) {
 	}
 
 	// Get modern indexer
-	modernIndexer := nginx_log.GetModernIndexer()
+	modernIndexer := nginx_log.GetIndexer()
 	if modernIndexer == nil {
 		cosy.ErrHandler(c, nginx_log.ErrModernIndexerNotAvailable)
 		return
@@ -89,10 +89,18 @@ func RebuildIndex(c *gin.Context) {
 		return
 	}
 
-	// Check if specific log group rebuild is already in progress
-	if request.Path != "" && isRebuildInProgress(request.Path) {
-		cosy.ErrHandler(c, nginx_log.ErrFailedToRebuildFileIndex)
-		return
+	// Check if specific log group rebuild is already in progress using task scheduler
+	scheduler := nginx_log.GetTaskScheduler()
+	if request.Path != "" {
+		if scheduler != nil && scheduler.IsTaskInProgress(request.Path) {
+			cosy.ErrHandler(c, nginx_log.ErrFailedToRebuildFileIndex)
+			return
+		}
+		// Fallback to local lock check if scheduler not available
+		if scheduler == nil && isRebuildInProgress(request.Path) {
+			cosy.ErrHandler(c, nginx_log.ErrFailedToRebuildFileIndex)
+			return
+		}
 	}
 
 	// Return immediate response to client
@@ -251,13 +259,23 @@ func performAsyncRebuild(modernIndexer interface{}, path string) {
 
 // rebuildSingleFile rebuilds index for a single file
 func rebuildSingleFile(modernIndexer interface{}, path string, logFileManager interface{}, progressConfig *indexer.ProgressConfig) (*time.Time, *time.Time) {
-	// Acquire lock for this specific log group
-	lock := acquireRebuildLock(path)
-	lock.Lock()
-	defer func() {
-		lock.Unlock()
-		releaseRebuildLock(path)
-	}()
+	// Use task scheduler lock if available for unified locking across recovery and manual rebuild
+	scheduler := nginx_log.GetTaskScheduler()
+
+	var unlock func()
+	if scheduler != nil {
+		// Use scheduler's unified lock
+		_, unlock = scheduler.AcquireTaskLock(path)
+		defer unlock()
+	} else {
+		// Fallback: Acquire local lock for this specific log group
+		lock := acquireRebuildLock(path)
+		lock.Lock()
+		defer func() {
+			lock.Unlock()
+			releaseRebuildLock(path)
+		}()
+	}
 	// For a single file, we need to check its type first
 	allLogsForTypeCheck := nginx_log.GetAllLogsWithIndexGrouped()
 	var targetLog *nginx_log.NginxLogWithIndex
@@ -332,12 +350,23 @@ func rebuildSingleFile(modernIndexer interface{}, path string, logFileManager in
 func rebuildAllFiles(modernIndexer interface{}, logFileManager interface{}, progressConfig *indexer.ProgressConfig) (*time.Time, *time.Time) {
 	// For full rebuild, we use a special global lock key
 	globalLockKey := "__GLOBAL_REBUILD__"
-	lock := acquireRebuildLock(globalLockKey)
-	lock.Lock()
-	defer func() {
-		lock.Unlock()
-		releaseRebuildLock(globalLockKey)
-	}()
+
+	// Use task scheduler lock if available for unified locking
+	scheduler := nginx_log.GetTaskScheduler()
+	var unlock func()
+	if scheduler != nil {
+		// Use scheduler's unified lock
+		_, unlock = scheduler.AcquireTaskLock(globalLockKey)
+		defer unlock()
+	} else {
+		// Fallback: Acquire local lock
+		lock := acquireRebuildLock(globalLockKey)
+		lock.Lock()
+		defer func() {
+			lock.Unlock()
+			releaseRebuildLock(globalLockKey)
+		}()
+	}
 	
 	// For full rebuild, we clear ALL existing metadata to start fresh
 	// This is different from single file/group rebuild which preserves metadata for incremental indexing
@@ -387,72 +416,100 @@ func rebuildAllFiles(modernIndexer interface{}, logFileManager interface{}, prog
 
 	startTime := time.Now()
 	var overallMinTime, overallMaxTime *time.Time
+	var timeMu sync.Mutex
+
+	// Second pass: Process queued logs in parallel with controlled concurrency
+	var wg sync.WaitGroup
+	// Get concurrency from indexer config (FileGroupConcurrency controls both file and group level parallelism)
+	maxConcurrency := 4 // Default fallback
+	if pi, ok := modernIndexer.(*indexer.ParallelIndexer); ok {
+		config := pi.GetConfig()
+		if config.FileGroupConcurrency > 0 {
+			maxConcurrency = config.FileGroupConcurrency
+		}
+	}
+	semaphore := make(chan struct{}, maxConcurrency)
+
+	logger.Infof("Processing %d log groups in parallel with concurrency=%d", len(accessLogs), maxConcurrency)
 
-	// Second pass: Process each queued log and set to indexing, then indexed
 	for _, log := range accessLogs {
-		// Set to indexing status
-		if persistence != nil {
-			if err := persistence.SetIndexStatus(log.Path, string(indexer.IndexStatusIndexing), 0, ""); err != nil {
-				logger.Errorf("Failed to set indexing status for %s: %v", log.Path, err)
-			}
-		}
+		wg.Add(1)
+		go func(logItem *nginx_log.NginxLogWithIndex) {
+			defer wg.Done()
 
-		loopStartTime := time.Now()
-		docsCountMap, minTime, maxTime, err := modernIndexer.(*indexer.ParallelIndexer).IndexLogGroupWithProgress(log.Path, progressConfig)
+			// Acquire semaphore for controlled concurrency
+			semaphore <- struct{}{}
+			defer func() { <-semaphore }()
 
-		if err != nil {
-			logger.Warnf("Failed to index file group: %s, error: %v", log.Path, err)
-			// Set error status
+			// Set to indexing status
 			if persistence != nil {
-				if err := persistence.SetIndexStatus(log.Path, string(indexer.IndexStatusError), 0, err.Error()); err != nil {
-					logger.Errorf("Failed to set error status for %s: %v", log.Path, err)
-				}
-			}
-		} else {
-			// Track overall time range across all log files
-			if minTime != nil {
-				if overallMinTime == nil || minTime.Before(*overallMinTime) {
-					overallMinTime = minTime
-				}
-			}
-			if maxTime != nil {
-				if overallMaxTime == nil || maxTime.After(*overallMaxTime) {
-					overallMaxTime = maxTime
+				if err := persistence.SetIndexStatus(logItem.Path, string(indexer.IndexStatusIndexing), 0, ""); err != nil {
+					logger.Errorf("Failed to set indexing status for %s: %v", logItem.Path, err)
 				}
 			}
 
-			if logFileManager != nil {
-				duration := time.Since(loopStartTime)
-				// Calculate total document count for the log group
-				var totalDocCount uint64
-				for _, docCount := range docsCountMap {
-					totalDocCount += docCount
+			loopStartTime := time.Now()
+			docsCountMap, minTime, maxTime, err := modernIndexer.(*indexer.ParallelIndexer).IndexLogGroupWithProgress(logItem.Path, progressConfig)
+
+			if err != nil {
+				logger.Warnf("Failed to index file group: %s, error: %v", logItem.Path, err)
+				// Set error status
+				if persistence != nil {
+					if err := persistence.SetIndexStatus(logItem.Path, string(indexer.IndexStatusError), 0, err.Error()); err != nil {
+						logger.Errorf("Failed to set error status for %s: %v", logItem.Path, err)
+					}
 				}
-				
-				// Save metadata for the base log path with total count
-				if err := logFileManager.(indexer.MetadataManager).SaveIndexMetadata(log.Path, totalDocCount, loopStartTime, duration, minTime, maxTime); err != nil {
-					logger.Errorf("Failed to save index metadata for %s: %v", log.Path, err)
+			} else {
+				// Track overall time range across all log files (thread-safe)
+				timeMu.Lock()
+				if minTime != nil {
+					if overallMinTime == nil || minTime.Before(*overallMinTime) {
+						overallMinTime = minTime
+					}
+				}
+				if maxTime != nil {
+					if overallMaxTime == nil || maxTime.After(*overallMaxTime) {
+						overallMaxTime = maxTime
+					}
 				}
-				
-				// Also save individual file metadata if needed
-				for path, docCount := range docsCountMap {
-					if path != log.Path { // Don't duplicate the base path
-						if err := logFileManager.(indexer.MetadataManager).SaveIndexMetadata(path, docCount, loopStartTime, duration, minTime, maxTime); err != nil {
-							logger.Errorf("Failed to save index metadata for %s: %v", path, err)
+				timeMu.Unlock()
+
+				if logFileManager != nil {
+					duration := time.Since(loopStartTime)
+					// Calculate total document count for the log group
+					var totalDocCount uint64
+					for _, docCount := range docsCountMap {
+						totalDocCount += docCount
+					}
+
+					// Save metadata for the base log path with total count
+					if err := logFileManager.(indexer.MetadataManager).SaveIndexMetadata(logItem.Path, totalDocCount, loopStartTime, duration, minTime, maxTime); err != nil {
+						logger.Errorf("Failed to save index metadata for %s: %v", logItem.Path, err)
+					}
+
+					// Also save individual file metadata if needed
+					for path, docCount := range docsCountMap {
+						if path != logItem.Path { // Don't duplicate the base path
+							if err := logFileManager.(indexer.MetadataManager).SaveIndexMetadata(path, docCount, loopStartTime, duration, minTime, maxTime); err != nil {
+								logger.Errorf("Failed to save index metadata for %s: %v", path, err)
+							}
 						}
 					}
 				}
-			}
-			
-			// Set to indexed status
-			if persistence != nil {
-				if err := persistence.SetIndexStatus(log.Path, string(indexer.IndexStatusIndexed), 0, ""); err != nil {
-					logger.Errorf("Failed to set indexed status for %s: %v", log.Path, err)
+
+				// Set to indexed status
+				if persistence != nil {
+					if err := persistence.SetIndexStatus(logItem.Path, string(indexer.IndexStatusIndexed), 0, ""); err != nil {
+						logger.Errorf("Failed to set indexed status for %s: %v", logItem.Path, err)
+					}
 				}
 			}
-		}
+		}(log)
 	}
 
+	// Wait for all log groups to complete
+	wg.Wait()
+
 	totalDuration := time.Since(startTime)
 	logger.Infof("Successfully completed full modern index rebuild in %s", totalDuration)
 

+ 1 - 1
api/nginx_log/log_list.go

@@ -62,7 +62,7 @@ func GetLogList(c *gin.Context) {
 
 	// Try to get total document count from modern indexer if available
 	// The indexer is the source of truth for document counts.
-	indexer := nginx_log.GetModernIndexer()
+	indexer := nginx_log.GetIndexer()
 	if indexer != nil {
 		stats := indexer.GetStats()
 		if stats != nil {

+ 8 - 5
api/nginx_log/advanced_indexing_settings.go → api/nginx_log/settings.go

@@ -12,7 +12,7 @@ import (
 
 // EnableAdvancedIndexing enables advanced indexing for nginx logs
 func EnableAdvancedIndexing(c *gin.Context) {
-	settings.NginxLogSettings.AdvancedIndexingEnabled = true
+	settings.NginxLogSettings.IndexingEnabled = true
 
 	err := settings.Save()
 	if err != nil {
@@ -21,7 +21,10 @@ func EnableAdvancedIndexing(c *gin.Context) {
 	}
 
 	// Start the nginx_log services
-	nginx_log.InitializeModernServices(kernel.Context)
+	nginx_log.InitializeServices(kernel.Context)
+
+	// Migrate fallback cache entries to LogFileManager
+	nginx_log.MigrateFallbackCache()
 
 	c.JSON(http.StatusOK, gin.H{
 		"message": "Advanced indexing enabled successfully",
@@ -30,7 +33,7 @@ func EnableAdvancedIndexing(c *gin.Context) {
 
 // DisableAdvancedIndexing disables advanced indexing for nginx logs
 func DisableAdvancedIndexing(c *gin.Context) {
-	settings.NginxLogSettings.AdvancedIndexingEnabled = false
+	settings.NginxLogSettings.IndexingEnabled = false
 
 	err := settings.Save()
 	if err != nil {
@@ -39,7 +42,7 @@ func DisableAdvancedIndexing(c *gin.Context) {
 	}
 
 	// Stop the nginx_log services
-	nginx_log.StopModernServices()
+	nginx_log.StopServices()
 
 	c.JSON(http.StatusOK, gin.H{
 		"message": "Advanced indexing disabled successfully",
@@ -48,7 +51,7 @@ func DisableAdvancedIndexing(c *gin.Context) {
 
 // GetAdvancedIndexingStatus returns the current status of advanced indexing
 func GetAdvancedIndexingStatus(c *gin.Context) {
-	enabled := settings.NginxLogSettings.AdvancedIndexingEnabled
+	enabled := settings.NginxLogSettings.IndexingEnabled
 
 	c.JSON(http.StatusOK, gin.H{
 		"enabled": enabled,

+ 21 - 2
app/components.d.ts

@@ -10,20 +10,29 @@ declare module 'vue' {
   export interface GlobalComponents {
     AAlert: typeof import('ant-design-vue/es')['Alert']
     AApp: typeof import('ant-design-vue/es')['App']
+    AAutoComplete: typeof import('ant-design-vue/es')['AutoComplete']
     AAvatar: typeof import('ant-design-vue/es')['Avatar']
     ABadge: typeof import('ant-design-vue/es')['Badge']
     ABreadcrumb: typeof import('ant-design-vue/es')['Breadcrumb']
     ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem']
     AButton: typeof import('ant-design-vue/es')['Button']
     ACard: typeof import('ant-design-vue/es')['Card']
+    ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
+    ACheckboxGroup: typeof import('ant-design-vue/es')['CheckboxGroup']
     ACol: typeof import('ant-design-vue/es')['Col']
-    AComment: typeof import('ant-design-vue/es')['Comment']
+    ACollapse: typeof import('ant-design-vue/es')['Collapse']
+    ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel']
     AConfigProvider: typeof import('ant-design-vue/es')['ConfigProvider']
     ADivider: typeof import('ant-design-vue/es')['Divider']
     ADrawer: typeof import('ant-design-vue/es')['Drawer']
     ADropdown: typeof import('ant-design-vue/es')['Dropdown']
+    AEmpty: typeof import('ant-design-vue/es')['Empty']
+    AForm: typeof import('ant-design-vue/es')['Form']
+    AFormItem: typeof import('ant-design-vue/es')['FormItem']
     AInput: typeof import('ant-design-vue/es')['Input']
     AInputGroup: typeof import('ant-design-vue/es')['InputGroup']
+    AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
+    AInputPassword: typeof import('ant-design-vue/es')['InputPassword']
     ALayout: typeof import('ant-design-vue/es')['Layout']
     ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent']
     ALayoutFooter: typeof import('ant-design-vue/es')['LayoutFooter']
@@ -33,18 +42,27 @@ declare module 'vue' {
     AListItem: typeof import('ant-design-vue/es')['ListItem']
     AListItemMeta: typeof import('ant-design-vue/es')['ListItemMeta']
     AMenu: typeof import('ant-design-vue/es')['Menu']
-    AMenuDivider: typeof import('ant-design-vue/es')['MenuDivider']
     AMenuItem: typeof import('ant-design-vue/es')['MenuItem']
+    AModal: typeof import('ant-design-vue/es')['Modal']
     APopconfirm: typeof import('ant-design-vue/es')['Popconfirm']
     APopover: typeof import('ant-design-vue/es')['Popover']
     AppProvider: typeof import('./src/components/AppProvider.vue')['default']
     AProgress: typeof import('ant-design-vue/es')['Progress']
+    ARadio: typeof import('ant-design-vue/es')['Radio']
+    ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup']
+    ARangePicker: typeof import('ant-design-vue/es')['RangePicker']
+    AResult: typeof import('ant-design-vue/es')['Result']
     ARow: typeof import('ant-design-vue/es')['Row']
+    ASegmented: typeof import('ant-design-vue/es')['Segmented']
     ASelect: typeof import('ant-design-vue/es')['Select']
     ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
     ASpace: typeof import('ant-design-vue/es')['Space']
+    ASpin: typeof import('ant-design-vue/es')['Spin']
     AStatistic: typeof import('ant-design-vue/es')['Statistic']
+    AStep: typeof import('ant-design-vue/es')['Step']
+    ASteps: typeof import('ant-design-vue/es')['Steps']
     ASubMenu: typeof import('ant-design-vue/es')['SubMenu']
+    ASwitch: typeof import('ant-design-vue/es')['Switch']
     ATable: typeof import('ant-design-vue/es')['Table']
     ATabPane: typeof import('ant-design-vue/es')['TabPane']
     ATabs: typeof import('ant-design-vue/es')['Tabs']
@@ -52,6 +70,7 @@ declare module 'vue' {
     ATextarea: typeof import('ant-design-vue/es')['Textarea']
     ATooltip: typeof import('ant-design-vue/es')['Tooltip']
     ATypographyText: typeof import('ant-design-vue/es')['TypographyText']
+    ATypographyTitle: typeof import('ant-design-vue/es')['TypographyTitle']
     AutoCertFormAutoCertForm: typeof import('./src/components/AutoCertForm/AutoCertForm.vue')['default']
     AutoCertFormDNSChallenge: typeof import('./src/components/AutoCertForm/DNSChallenge.vue')['default']
     BaseEditorBaseEditor: typeof import('./src/components/BaseEditor/BaseEditor.vue')['default']

+ 1 - 1
app/src/api/settings.ts

@@ -71,7 +71,7 @@ export interface NginxSettings {
 }
 
 export interface NginxLogSettings {
-  advanced_indexing_enabled: boolean
+  indexing_enabled: boolean
   index_path: string
 }
 

+ 192 - 171
app/src/language/ar/app.po

@@ -5,10 +5,10 @@ msgid ""
 msgstr ""
 "PO-Revision-Date: 2025-07-14 07:37+0000\n"
 "Last-Translator: mosaati <mohammed.saati@gmail.com>\n"
-"Language-Team: Arabic "
-"<https://weblate.nginxui.com/projects/nginx-ui/frontend/ar/>\n"
+"Language-Team: Arabic <https://weblate.nginxui.com/projects/nginx-ui/"
+"frontend/ar/>\n"
 "Language: ar\n"
-"Content-Type: text/plain; charset=utf-8\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
 "&& n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n"
@@ -33,7 +33,8 @@ msgid "[Nginx UI] Certificate successfully revoked"
 msgstr "[Nginx UI] تم إلغاء الشهادة بنجاح"
 
 #: src/language/generate.ts:39
-msgid "[Nginx UI] Certificate was used for server, reloading server TLS certificate"
+msgid ""
+"[Nginx UI] Certificate was used for server, reloading server TLS certificate"
 msgstr "[Nginx UI] تم استخدام الشهادة للخادم، إعادة تحميل شهادة TLS للخادم"
 
 #: src/language/generate.ts:40
@@ -111,8 +112,8 @@ msgstr "* يتضمن عقدًا من مجموعة %{groupName} وعقدًا مخ
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:141
 msgid ""
-"* Index files are stored in the \"log-index\" directory within your Nginx "
-"UI config path by default."
+"* Index files are stored in the \"log-index\" directory within your Nginx UI "
+"config path by default."
 msgstr ""
 "* يتم تخزين ملفات الفهرس في دليل \"log-index\" ضمن مسار تكوين واجهة NGINX "
 "افتراضيًا."
@@ -329,7 +330,8 @@ msgid "Any"
 msgstr "أي"
 
 #: src/components/AutoCertForm/AutoCertForm.vue:209
-msgid "Any reachable IP address can be used with private Certificate Authorities"
+msgid ""
+"Any reachable IP address can be used with private Certificate Authorities"
 msgstr "يمكن استخدام أي عنوان IP قابل للوصول مع سلطات الشهادات الخاصة"
 
 #: src/views/preference/tabs/OpenAISettings.vue:32
@@ -489,7 +491,7 @@ msgstr "فشل النسخ الاحتياطي التلقائي"
 msgid "Auto Backup Storage Failed"
 msgstr "فشل تخزين النسخ الاحتياطي التلقائي"
 
-#: src/views/nginx_log/NginxLog.vue:86 src/views/node/Node.vue:164
+#: src/views/nginx_log/NginxLog.vue:101 src/views/node/Node.vue:164
 msgid "Auto Refresh"
 msgstr "التحديث التلقائي"
 
@@ -541,7 +543,7 @@ msgstr "متوسط/زيارة الصفحة"
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
-#: src/views/nginx_log/NginxLog.vue:114
+#: src/views/nginx_log/NginxLog.vue:129
 #: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:170
 #: src/views/stream/components/StreamEditor.vue:134
 msgid "Back"
@@ -607,13 +609,14 @@ msgstr "جدول النسخ الاحتياطي"
 
 #: src/components/Notification/notifications.ts:38
 msgid "Backup task %{backup_name} completed successfully, file: %{file_path}"
-msgstr "تم إنجاز مهمة النسخ الاحتياطي %{backup_name} بنجاح، الملف: %{file_path}"
+msgstr ""
+"تم إنجاز مهمة النسخ الاحتياطي %{backup_name} بنجاح، الملف: %{file_path}"
 
 #: src/components/Notification/notifications.ts:34
-msgid "Backup task %{backup_name} failed during storage upload, error: %{error}"
+msgid ""
+"Backup task %{backup_name} failed during storage upload, error: %{error}"
 msgstr ""
-"فشلت مهمة النسخ الاحتياطي %{backup_name} أثناء تحميل التخزين، الخطأ: "
-"%{error}"
+"فشلت مهمة النسخ الاحتياطي %{backup_name} أثناء تحميل التخزين، الخطأ: %{error}"
 
 #: src/components/Notification/notifications.ts:30
 msgid "Backup task %{backup_name} failed to execute, error: %{error}"
@@ -708,7 +711,8 @@ msgstr "الذاكرة المؤقتة"
 
 #: src/views/dashboard/components/ParamsOpt/ProxyCacheConfig.vue:177
 msgid "Cache items not accessed within this time will be removed"
-msgstr "سيتم إزالة عناصر الذاكرة المؤقتة التي لم يتم الوصول إليها خلال هذا الوقت"
+msgstr ""
+"سيتم إزالة عناصر الذاكرة المؤقتة التي لم يتم الوصول إليها خلال هذا الوقت"
 
 #: src/views/dashboard/components/ParamsOpt/ProxyCacheConfig.vue:349
 msgid "Cache loader processing time threshold"
@@ -974,23 +978,23 @@ msgstr "فحص الأيقونة"
 #: src/language/generate.ts:6
 msgid ""
 "Check if /var/run/docker.sock exists. If you are using Nginx UI Official "
-"Docker Image, please make sure the docker socket is mounted like this: `-v "
-"/var/run/docker.sock:/var/run/docker.sock`. Nginx UI official image uses "
-"/var/run/docker.sock to communicate with the host Docker Engine via Docker "
-"Client API. This feature is used to control Nginx in another container and "
-"perform container replacement rather than binary replacement during OTA "
-"upgrades of Nginx UI to ensure container dependencies are also upgraded. If "
-"you don't need this feature, please add the environment variable "
+"Docker Image, please make sure the docker socket is mounted like this: `-v /"
+"var/run/docker.sock:/var/run/docker.sock`. Nginx UI official image uses /var/"
+"run/docker.sock to communicate with the host Docker Engine via Docker Client "
+"API. This feature is used to control Nginx in another container and perform "
+"container replacement rather than binary replacement during OTA upgrades of "
+"Nginx UI to ensure container dependencies are also upgraded. If you don't "
+"need this feature, please add the environment variable "
 "NGINX_UI_IGNORE_DOCKER_SOCKET=true to the container."
 msgstr ""
 "تحقق مما إذا كان /var/run/docker.sock موجودًا. إذا كنت تستخدم صورة Docker "
-"الرسمية لـ Nginx UI، يرجى التأكد من توصيل مقبس Docker بهذه الطريقة: `-v "
-"/var/run/docker.sock:/var/run/docker.sock`. تستخدم صورة Nginx UI الرسمية "
-"/var/run/docker.sock للتواصل مع محرك Docker المضيف عبر واجهة برمجة تطبيقات "
-"Docker Client. تُستخدم هذه الميزة للتحكم في Nginx في حاوية أخرى وإجراء "
-"استبدال الحاوية بدلاً من استبدال الثنائي أثناء التحديثات OTA لـ Nginx UI "
-"لضمان تحديث تبعيات الحاوية أيضًا. إذا كنت لا تحتاج إلى هذه الميزة، يرجى "
-"إضافة متغير البيئة NGINX_UI_IGNORE_DOCKER_SOCKET=true إلى الحاوية."
+"الرسمية لـ Nginx UI، يرجى التأكد من توصيل مقبس Docker بهذه الطريقة: `-v /var/"
+"run/docker.sock:/var/run/docker.sock`. تستخدم صورة Nginx UI الرسمية /var/run/"
+"docker.sock للتواصل مع محرك Docker المضيف عبر واجهة برمجة تطبيقات Docker "
+"Client. تُستخدم هذه الميزة للتحكم في Nginx في حاوية أخرى وإجراء استبدال "
+"الحاوية بدلاً من استبدال الثنائي أثناء التحديثات OTA لـ Nginx UI لضمان تحديث "
+"تبعيات الحاوية أيضًا. إذا كنت لا تحتاج إلى هذه الميزة، يرجى إضافة متغير "
+"البيئة NGINX_UI_IGNORE_DOCKER_SOCKET=true إلى الحاوية."
 
 #: src/components/SelfCheck/tasks/frontend/https-check.ts:14
 msgid ""
@@ -1001,24 +1005,25 @@ msgstr ""
 "استخدام ميزات Passkeys والحافظة"
 
 #: src/language/generate.ts:7
-msgid "Check if the conf.d directory is under the nginx configuration directory"
+msgid ""
+"Check if the conf.d directory is under the nginx configuration directory"
 msgstr "تحقق مما إذا كان دليل conf.d موجودًا ضمن دليل تكوين nginx"
 
 #: src/language/generate.ts:9
 msgid ""
-"Check if the nginx access log path exists. By default, this path is "
-"obtained from 'nginx -V'. If it cannot be obtained or the obtained path "
-"does not point to a valid, existing file, an error will be reported. In "
-"this case, you need to modify the configuration file to specify the access "
-"log path.Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#accesslogpath"
+"Check if the nginx access log path exists. By default, this path is obtained "
+"from 'nginx -V'. If it cannot be obtained or the obtained path does not "
+"point to a valid, existing file, an error will be reported. In this case, "
+"you need to modify the configuration file to specify the access log path."
+"Refer to the docs for more details: https://nginxui.com/zh_CN/guide/config-"
+"nginx.html#accesslogpath"
 msgstr ""
-"تحقق مما إذا كان مسار سجل الوصول إلى nginx موجودًا. بشكل افتراضي، يتم "
-"الحصول على هذا المسار من 'nginx -V'. إذا لم يتم الحصول عليه أو إذا كان "
-"المسار الذي تم الحصول عليه لا يشير إلى ملف صالح موجود، فسيتم الإبلاغ عن "
-"خطأ. في هذه الحالة، تحتاج إلى تعديل ملف التكوين لتحديد مسار سجل الوصول. "
-"راجع الوثائق لمزيد من التفاصيل: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#accesslogpath"
+"تحقق مما إذا كان مسار سجل الوصول إلى nginx موجودًا. بشكل افتراضي، يتم الحصول "
+"على هذا المسار من 'nginx -V'. إذا لم يتم الحصول عليه أو إذا كان المسار الذي "
+"تم الحصول عليه لا يشير إلى ملف صالح موجود، فسيتم الإبلاغ عن خطأ. في هذه "
+"الحالة، تحتاج إلى تعديل ملف التكوين لتحديد مسار سجل الوصول. راجع الوثائق "
+"لمزيد من التفاصيل: https://nginxui.com/zh_CN/guide/config-nginx."
+"html#accesslogpath"
 
 #: src/language/generate.ts:10
 msgid "Check if the nginx configuration directory exists"
@@ -1034,29 +1039,28 @@ msgid ""
 "from 'nginx -V'. If it cannot be obtained or the obtained path does not "
 "point to a valid, existing file, an error will be reported. In this case, "
 "you need to modify the configuration file to specify the error log path. "
-"Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#errorlogpath"
+"Refer to the docs for more details: https://nginxui.com/zh_CN/guide/config-"
+"nginx.html#errorlogpath"
 msgstr ""
 "تحقق مما إذا كان مسار سجل أخطاء nginx موجودًا. بشكل افتراضي، يتم الحصول على "
 "هذا المسار من 'nginx -V'. إذا تعذر الحصول عليه أو إذا كان المسار الذي تم "
-"الحصول عليه لا يشير إلى ملف صالح موجود، فسيتم الإبلاغ عن خطأ. في هذه "
-"الحالة، تحتاج إلى تعديل ملف التكوين لتحديد مسار سجل الأخطاء. راجع الوثائق "
-"لمزيد من التفاصيل: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#errorlogpath"
+"الحصول عليه لا يشير إلى ملف صالح موجود، فسيتم الإبلاغ عن خطأ. في هذه الحالة، "
+"تحتاج إلى تعديل ملف التكوين لتحديد مسار سجل الأخطاء. راجع الوثائق لمزيد من "
+"التفاصيل: https://nginxui.com/zh_CN/guide/config-nginx.html#errorlogpath"
 
 #: src/language/generate.ts:8
 msgid ""
 "Check if the nginx PID path exists. By default, this path is obtained from "
 "'nginx -V'. If it cannot be obtained, an error will be reported. In this "
 "case, you need to modify the configuration file to specify the Nginx PID "
-"path.Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#pidpath"
+"path.Refer to the docs for more details: https://nginxui.com/zh_CN/guide/"
+"config-nginx.html#pidpath"
 msgstr ""
-"تحقق مما إذا كان مسار معرف عملية Nginx موجودًا. بشكل افتراضي، يتم الحصول "
-"على هذا المسار من الأمر 'nginx -V'. إذا تعذر الحصول عليه، سيتم الإبلاغ عن "
-"خطأ. في هذه الحالة، تحتاج إلى تعديل ملف التكوين لتحديد مسار معرف عملية "
-"Nginx. راجع الوثائق لمزيد من التفاصيل: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#pidpath"
+"تحقق مما إذا كان مسار معرف عملية Nginx موجودًا. بشكل افتراضي، يتم الحصول على "
+"هذا المسار من الأمر 'nginx -V'. إذا تعذر الحصول عليه، سيتم الإبلاغ عن خطأ. "
+"في هذه الحالة، تحتاج إلى تعديل ملف التكوين لتحديد مسار معرف عملية Nginx. "
+"راجع الوثائق لمزيد من التفاصيل: https://nginxui.com/zh_CN/guide/config-nginx."
+"html#pidpath"
 
 #: src/language/generate.ts:13
 msgid "Check if the nginx sbin path exists"
@@ -1084,8 +1088,8 @@ msgstr ""
 
 #: src/language/generate.ts:18
 msgid ""
-"Check if the streams-available and streams-enabled directories are under "
-"the nginx configuration directory"
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory"
 msgstr ""
 "تحقق مما إذا كانت الدلائل streams-available و streams-enabled موجودة ضمن "
 "دليل تكوين nginx"
@@ -1523,7 +1527,7 @@ msgstr "يوميًا في الساعة %{time}"
 #: src/routes/modules/dashboard.ts:10
 #: src/views/config/components/ConfigLeftPanel.vue:109
 #: src/views/config/components/ConfigLeftPanel.vue:159
-#: src/views/config/ConfigList.vue:69 src/views/nginx_log/NginxLog.vue:78
+#: src/views/config/ConfigList.vue:69 src/views/nginx_log/NginxLog.vue:93
 msgid "Dashboard"
 msgstr "لوحة المعلومات"
 
@@ -1928,8 +1932,8 @@ msgid ""
 "Due to the security policies of some browsers, you cannot use passkeys on "
 "non-HTTPS websites, except when running on localhost."
 msgstr ""
-"نظرًا لسياسات الأمان لبعض المتصفحات، لا يمكنك استخدام مفاتيح المرور على "
-"مواقع الويب غير HTTPS، إلا عند التشغيل على localhost."
+"نظرًا لسياسات الأمان لبعض المتصفحات، لا يمكنك استخدام مفاتيح المرور على مواقع "
+"الويب غير HTTPS، إلا عند التشغيل على localhost."
 
 #: src/components/LLM/LLMSessionTabs.vue:287
 #: src/components/LLM/LLMSessionTabs.vue:378
@@ -2217,8 +2221,8 @@ msgstr "سجلات الأخطاء"
 
 #: src/views/nginx_log/structured/StructuredLogViewer.vue:668
 msgid ""
-"Error logs do not support structured analysis as they contain free-form "
-"text messages."
+"Error logs do not support structured analysis as they contain free-form text "
+"messages."
 msgstr "لا تدعم سجلات الأخطاء التحليل المنظم لأنها تحتوي على رسائل نصية حرة."
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:233
@@ -2278,15 +2282,15 @@ msgstr "تصدير إكسل"
 msgid ""
 "External Account Binding HMAC Key (optional). Should be in Base64 URL "
 "encoding format."
-msgstr "مفتاح HMAC لربط الحساب الخارجي (اختياري). يجب أن يكون بتنسيق Base64 URL."
+msgstr ""
+"مفتاح HMAC لربط الحساب الخارجي (اختياري). يجب أن يكون بتنسيق Base64 URL."
 
 #: src/views/certificate/ACMEUser.vue:92
 msgid ""
-"External Account Binding Key ID (optional). Required for some ACME "
-"providers like ZeroSSL."
+"External Account Binding Key ID (optional). Required for some ACME providers "
+"like ZeroSSL."
 msgstr ""
-"معرف مفتاح الربط الحساب الخارجي (اختياري). مطلوب لبعض موفري ACME مثل "
-"ZeroSSL."
+"معرف مفتاح الربط الحساب الخارجي (اختياري). مطلوب لبعض موفري ACME مثل ZeroSSL."
 
 #: src/views/preference/tabs/NginxSettings.vue:49
 msgid "External Docker Container"
@@ -2793,10 +2797,11 @@ msgid "For commercial or professional use, contact"
 msgstr "للاستخدام التجاري أو المهني، يرجى الاتصال بـ"
 
 #: src/views/nginx_log/structured/StructuredLogViewer.vue:670
-msgid "For error logs, please use the Raw Log Viewer for better viewing experience."
+msgid ""
+"For error logs, please use the Raw Log Viewer for better viewing experience."
 msgstr ""
-"بالنسبة لسجلات الأخطاء، يرجى استخدام عارض السجلات الخام للحصول على تجربة "
-"عرض أفضل."
+"بالنسبة لسجلات الأخطاء، يرجى استخدام عارض السجلات الخام للحصول على تجربة عرض "
+"أفضل."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:140
 msgid ""
@@ -2808,11 +2813,11 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:188
 msgid ""
-"For IP-based certificates, please specify the server IP address that will "
-"be included in the certificate."
+"For IP-based certificates, please specify the server IP address that will be "
+"included in the certificate."
 msgstr ""
-"بالنسبة للشهادات القائمة على IP، يرجى تحديد عنوان IP الخاص بالخادم الذي "
-"سيتم تضمينه في الشهادة."
+"بالنسبة للشهادات القائمة على IP، يرجى تحديد عنوان IP الخاص بالخادم الذي سيتم "
+"تضمينه في الشهادة."
 
 #: src/constants/errors/middleware.ts:4
 msgid "Form parse failed"
@@ -2921,8 +2926,8 @@ msgid ""
 "gRPC health check requires server to implement gRPC Health Check service "
 "(grpc.health.v1.Health)."
 msgstr ""
-"يتطلب فحص صحة gRPC أن يقوم الخادم بتنفيذ خدمة فحص صحة gRPC "
-"(grpc.health.v1.Health)."
+"يتطلب فحص صحة gRPC أن يقوم الخادم بتنفيذ خدمة فحص صحة gRPC (grpc.health.v1."
+"Health)."
 
 #: src/views/dashboard/components/SiteHealthCheckModal.vue:501
 msgid ""
@@ -3040,8 +3045,8 @@ msgid ""
 "If you want to change the storage location, you can set the `IndexPath` of "
 "`nginx_log` section in the Nginx UI config."
 msgstr ""
-"إذا كنت ترغب في تغيير موقع التخزين، يمكنك تعيين `IndexPath` لقسم "
-"`nginx_log` في تكوين واجهة Nginx."
+"إذا كنت ترغب في تغيير موقع التخزين، يمكنك تعيين `IndexPath` لقسم `nginx_log` "
+"في تكوين واجهة Nginx."
 
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:76
 msgid "If your browser supports WebAuthn Passkey, a dialog box will appear."
@@ -3532,12 +3537,12 @@ msgstr "سجل"
 #: src/language/generate.ts:23
 msgid ""
 "Log file %{log_path} is not a regular file. If you are using nginx-ui in "
-"docker container, please refer to "
-"https://nginxui.com/zh_CN/guide/config-nginx-log.html for more information."
+"docker container, please refer to https://nginxui.com/zh_CN/guide/config-"
+"nginx-log.html for more information."
 msgstr ""
 "ملف السجل %{log_path} ليس ملفًا عاديًا. إذا كنت تستخدم nginx-ui في حاوية "
-"Docker، يرجى الرجوع إلى "
-"https://nginxui.com/zh_CN/guide/config-nginx-log.html لمزيد من المعلومات."
+"Docker، يرجى الرجوع إلى https://nginxui.com/zh_CN/guide/config-nginx-log."
+"html لمزيد من المعلومات."
 
 #: src/constants/errors/nginx_log.ts:14
 msgid "Log file does not exist"
@@ -3589,19 +3594,19 @@ msgstr "تدوير السجلات"
 
 #: src/views/preference/tabs/LogrotateSettings.vue:13
 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."
+"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 ""
 "بشكل افتراضي، يتم تفعيل تدوير السجلات في معظم توزيعات لينكس الرئيسية "
 "للمستخدمين الذين يقومون بتثبيت واجهة Nginx UI على الجهاز المضيف، لذا لا "
-"تحتاج إلى تعديل معايير في هذه الصفحة. بالنسبة للمستخدمين الذين يقومون "
-"بتثبيت واجهة Nginx UI باستخدام حاويات Docker، يمكنك تمكين هذا الخيار "
-"يدويًا. سيقوم مجدول المهام crontab الخاص بواجهة Nginx UI بتنفيذ أمر تدوير "
-"السجلات في الفاصل الزمني الذي تحدده بالدقائق."
+"تحتاج إلى تعديل معايير في هذه الصفحة. بالنسبة للمستخدمين الذين يقومون بتثبيت "
+"واجهة Nginx UI باستخدام حاويات Docker، يمكنك تمكين هذا الخيار يدويًا. سيقوم "
+"مجدول المهام crontab الخاص بواجهة Nginx UI بتنفيذ أمر تدوير السجلات في "
+"الفاصل الزمني الذي تحدده بالدقائق."
 
 #: src/composables/useGeoTranslation.ts:166
 #: src/views/nginx_log/dashboard/components/ChinaMapChart/ChinaMapChart.vue:135
@@ -4076,7 +4081,7 @@ msgstr "إن Nginx لا يعمل في حاوية أخرى"
 msgid "Nginx is running"
 msgstr "إن Nginx يعمل"
 
-#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:61
+#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:76
 msgid "Nginx Log"
 msgstr "سجل Nginx"
 
@@ -4157,8 +4162,8 @@ msgstr "تمت استعادة تكوين Nginx UI"
 
 #: src/components/SystemRestore/SystemRestoreContent.vue:336
 msgid ""
-"Nginx UI configuration has been restored and will restart automatically in "
-"a few seconds."
+"Nginx UI configuration has been restored and will restart automatically in a "
+"few seconds."
 msgstr "تمت استعادة تكوين Nginx UI وسيتم إعادة التشغيل تلقائيًا خلال بضع ثوانٍ."
 
 #: src/language/generate.ts:30
@@ -4348,11 +4353,13 @@ msgstr "عدد عمليات العامل المتزامنة، يتم الضبط
 
 #: src/views/dashboard/components/ParamsOpt/ProxyCacheConfig.vue:314
 msgid "Number of files processed by cache loader at once"
-msgstr "عدد الملفات التي تتم معالجتها بواسطة محمل ذاكرة التخزين المؤقت في وقت واحد"
+msgstr ""
+"عدد الملفات التي تتم معالجتها بواسطة محمل ذاكرة التخزين المؤقت في وقت واحد"
 
 #: src/views/dashboard/components/ParamsOpt/ProxyCacheConfig.vue:252
 msgid "Number of files processed by cache manager at once"
-msgstr "عدد الملفات التي تتم معالجتها بواسطة مدير ذاكرة التخزين المؤقت في وقت واحد"
+msgstr ""
+"عدد الملفات التي تتم معالجتها بواسطة مدير ذاكرة التخزين المؤقت في وقت واحد"
 
 #: src/composables/usePerformanceMetrics.ts:129
 #: src/composables/usePerformanceMetrics.ts:169
@@ -4676,7 +4683,8 @@ msgstr "الرجاء إدخال"
 msgid ""
 "Please enter a name for the passkey you wish to create and click the OK "
 "button below."
-msgstr "يرجى إدخال اسم لمفتاح المرور الذي ترغب في إنشائه ثم انقر على زر موافق أدناه."
+msgstr ""
+"يرجى إدخال اسم لمفتاح المرور الذي ترغب في إنشائه ثم انقر على زر موافق أدناه."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:98
 msgid "Please enter a valid IPv4 address (0-255 per octet)"
@@ -4736,8 +4744,8 @@ msgstr ""
 #: src/components/Notification/notifications.ts:194
 #: src/language/constants.ts:59
 msgid ""
-"Please generate new recovery codes in the preferences immediately to "
-"prevent lockout."
+"Please generate new recovery codes in the preferences immediately to prevent "
+"lockout."
 msgstr "يرجى إنشاء رموز استرداد جديدة في التفضيلات على الفور لمنع الإغلاق."
 
 #: src/views/config/components/ConfigRightPanel/Basic.vue:27
@@ -4779,7 +4787,8 @@ msgid "Please log in."
 msgstr "الرجاء تسجيل الدخول."
 
 #: src/views/certificate/DNSCredential.vue:102
-msgid "Please note that the unit of time configurations below are all in seconds."
+msgid ""
+"Please note that the unit of time configurations below are all in seconds."
 msgstr "يرجى ملاحظة أن تكوين وحدات الوقت أدناه كلها بالثواني."
 
 #: src/views/install/components/InstallView.vue:102
@@ -4914,8 +4923,7 @@ msgstr "البروتوكول"
 #: src/views/preference/tabs/ServerSettings.vue:47
 msgid ""
 "Protocol configuration only takes effect when directly connecting. If using "
-"reverse proxy, please configure the protocol separately in the reverse "
-"proxy."
+"reverse proxy, please configure the protocol separately in the reverse proxy."
 msgstr ""
 "إعدادات البروتوكول تتأثر فقط عند الاتصال المباشر. إذا كنت تستخدم خادم وكيل "
 "عكسي، يرجى تكوين البروتوكول بشكل منفصل في خادم الوكيل العكسي."
@@ -4971,7 +4979,7 @@ msgstr "في قائمة انتظار الفهرسة..."
 msgid "Quick Select"
 msgstr "اختيار سريع"
 
-#: src/views/nginx_log/NginxLog.vue:79
+#: src/views/nginx_log/NginxLog.vue:94
 msgid "Raw"
 msgstr "خام"
 
@@ -5581,7 +5589,7 @@ msgstr "تم الحفظ بنجاح"
 
 #: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
 #: src/views/certificate/components/RenewCert.vue:26
-#: src/views/preference/store/index.ts:123
+#: src/views/preference/store/index.ts:124
 msgid "Save successfully"
 msgstr "تم الحفظ بنجاح"
 
@@ -5801,19 +5809,19 @@ msgstr "الإعدادات"
 
 #: src/constants/errors/nginx_log.ts:8
 msgid ""
-"Settings.NginxLogSettings.AccessLogPath is empty, refer to "
-"https://nginxui.com/guide/config-nginx.html for more information"
+"Settings.NginxLogSettings.AccessLogPath is empty, refer to https://nginxui."
+"com/guide/config-nginx.html for more information"
 msgstr ""
-"إعدادات.Settings.NginxLogSettings.AccessLogPath فارغة، راجع "
-"https://nginxui.com/guide/config-nginx.html لمزيد من المعلومات"
+"إعدادات.Settings.NginxLogSettings.AccessLogPath فارغة، راجع https://nginxui."
+"com/guide/config-nginx.html لمزيد من المعلومات"
 
 #: src/constants/errors/nginx_log.ts:7
 msgid ""
-"Settings.NginxLogSettings.ErrorLogPath is empty, refer to "
-"https://nginxui.com/guide/config-nginx.html for more information"
+"Settings.NginxLogSettings.ErrorLogPath is empty, refer to https://nginxui."
+"com/guide/config-nginx.html for more information"
 msgstr ""
-"إعدادات.Settings.NginxLogSettings.ErrorLogPath فارغة، راجع "
-"https://nginxui.com/guide/config-nginx.html لمزيد من المعلومات"
+"إعدادات.Settings.NginxLogSettings.ErrorLogPath فارغة، راجع https://nginxui."
+"com/guide/config-nginx.html لمزيد من المعلومات"
 
 #: src/views/install/components/InstallView.vue:65
 msgid "Setup your Nginx UI"
@@ -6086,7 +6094,7 @@ msgstr "دليل Streams-available غير موجود"
 msgid "Streams-enabled directory not exist"
 msgstr "دليل Streams-enabled غير موجود"
 
-#: src/views/nginx_log/NginxLog.vue:77
+#: src/views/nginx_log/NginxLog.vue:92
 msgid "Structured"
 msgstr "منظم"
 
@@ -6112,9 +6120,9 @@ msgstr "الأحد"
 #: src/components/SelfCheck/tasks/frontend/websocket.ts:13
 msgid ""
 "Support communication with the backend through the WebSocket protocol. If "
-"your Nginx UI is being used via an Nginx reverse proxy, please refer to "
-"this link to write the corresponding configuration file: "
-"https://nginxui.com/guide/nginx-proxy-example.html"
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
 msgstr ""
 "دعم الاتصال مع الخلفية من خلال بروتوكول WebSocket. إذا كنت تستخدم واجهة "
 "Nginx عبر وكيل عكسي لـ Nginx، يرجى الرجوع إلى هذا الرابط لكتابة ملف التكوين "
@@ -6305,7 +6313,8 @@ msgstr ""
 msgid ""
 "The ICP Number should only contain letters, unicode, numbers, hyphens, "
 "dashes, colons, and dots."
-msgstr "يجب أن يحتوي رقم ICP على أحرف، يونيكود، أرقام، شرطات، نقاط، ونقطتين فقط."
+msgstr ""
+"يجب أن يحتوي رقم ICP على أحرف، يونيكود، أرقام، شرطات، نقاط، ونقطتين فقط."
 
 #: src/views/certificate/components/CertificateContentEditor.vue:115
 msgid "The input is not a SSL Certificate"
@@ -6317,11 +6326,10 @@ msgstr "المدخل ليس مفتاح شهادة SSL"
 
 #: src/constants/errors/nginx_log.ts:2
 msgid ""
-"The log path is not under the paths in "
-"settings.NginxSettings.LogDirWhiteList"
+"The log path is not under the paths in settings.NginxSettings.LogDirWhiteList"
 msgstr ""
-"مسار السجل ليس ضمن المسارات الموجودة في "
-"settings.NginxSettings.LogDirWhiteList"
+"مسار السجل ليس ضمن المسارات الموجودة في settings.NginxSettings."
+"LogDirWhiteList"
 
 #: src/views/preference/tabs/OpenAISettings.vue:23
 #: src/views/preference/tabs/OpenAISettings.vue:89
@@ -6333,7 +6341,8 @@ msgstr ""
 "فقط."
 
 #: src/views/preference/tabs/OpenAISettings.vue:90
-msgid "The model used for code completion, if not set, the chat model will be used."
+msgid ""
+"The model used for code completion, if not set, the chat model will be used."
 msgstr ""
 "النموذج المستخدم لإكمال التعليمات البرمجية، إذا لم يتم تعيينه، سيتم استخدام "
 "نموذج الدردشة."
@@ -6342,7 +6351,8 @@ msgstr ""
 msgid ""
 "The node name should only contain letters, unicode, numbers, hyphens, "
 "dashes, colons, and dots."
-msgstr "يجب أن يحتوي اسم العقدة على أحرف، يونيكود، أرقام، شرطات، نقاط، ونقطتين فقط."
+msgstr ""
+"يجب أن يحتوي اسم العقدة على أحرف، يونيكود، أرقام، شرطات، نقاط، ونقطتين فقط."
 
 #: src/views/site/site_add/SiteAdd.vue:96
 msgid "The parameter of server_name is required"
@@ -6448,13 +6458,14 @@ msgid "This field should not be empty"
 msgstr "يجب ألا يكون هذا الحقل فارغًا"
 
 #: src/constants/form_errors.ts:6
-msgid "This field should only contain letters, unicode characters, numbers, and -_."
+msgid ""
+"This field should only contain letters, unicode characters, numbers, and -_."
 msgstr "يجب أن يحتوي هذا الحقل على حروف وأحرف يونيكود وأرقام و-_. فقط."
 
 #: src/language/curd.ts:46
 msgid ""
-"This field should only contain letters, unicode characters, numbers, and "
-"-_./:"
+"This field should only contain letters, unicode characters, numbers, and -"
+"_./:"
 msgstr "يجب أن يحتوي هذا الحقل فقط على أحرف وأحرف يونيكود وأرقام و -_./:"
 
 #: src/components/Notification/notifications.ts:94
@@ -6466,8 +6477,8 @@ msgid ""
 "This module provides Nginx request statistics, connection count, etc. data. "
 "After enabling it, you can view performance statistics"
 msgstr ""
-"توفر هذه الوحدة إحصائيات طلبات Nginx وعدد الاتصالات وما إلى ذلك من "
-"البيانات. بعد تمكينها، يمكنك عرض إحصائيات الأداء"
+"توفر هذه الوحدة إحصائيات طلبات Nginx وعدد الاتصالات وما إلى ذلك من البيانات. "
+"بعد تمكينها، يمكنك عرض إحصائيات الأداء"
 
 #: src/views/nginx_log/dashboard/components/DateRangeSelector.vue:25
 msgid "This month"
@@ -6487,9 +6498,9 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:128
 msgid ""
-"This site is configured as a default server (default_server) for HTTPS "
-"(port 443). IP certificates require Certificate Authority (CA) support and "
-"may not be available with all ACME providers."
+"This site is configured as a default server (default_server) for HTTPS (port "
+"443). IP certificates require Certificate Authority (CA) support and may not "
+"be available with all ACME providers."
 msgstr ""
 "تم تكوين هذا الموقع كخادم افتراضي (default_server) لـ HTTPS (المنفذ 443). "
 "تتطلب شهادات IP دعمًا من سلطة الشهادات (CA) وقد لا تكون متاحة مع جميع موفري "
@@ -6497,8 +6508,8 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:132
 msgid ""
-"This site uses wildcard server name (_) which typically indicates an "
-"IP-based certificate. IP certificates require Certificate Authority (CA) "
+"This site uses wildcard server name (_) which typically indicates an IP-"
+"based certificate. IP certificates require Certificate Authority (CA) "
 "support and may not be available with all ACME providers."
 msgstr ""
 "يستخدم هذا الموقع اسم خادم شامل (_) والذي يشير عادةً إلى شهادة قائمة على "
@@ -6548,11 +6559,12 @@ msgid ""
 "This will restore configuration files and database. Nginx UI will restart "
 "after the restoration is complete."
 msgstr ""
-"سيؤدي هذا إلى استعادة ملفات التكوين وقاعدة البيانات. سيعاد تشغيل واجهة "
-"Nginx بعد اكتمال الاستعادة."
+"سيؤدي هذا إلى استعادة ملفات التكوين وقاعدة البيانات. سيعاد تشغيل واجهة Nginx "
+"بعد اكتمال الاستعادة."
 
 #: src/views/node/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}."
 
 #: src/views/preference/tabs/AuthSettings.vue:89
@@ -6623,8 +6635,8 @@ msgstr ""
 #: src/views/site/site_edit/components/EnableTLS/EnableTLS.vue:15
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
-"add a location which can proxy the request from authority to backend, and "
-"we need to save this file and reload the Nginx. Are you sure you want to "
+"add a location which can proxy the request from authority to backend, and we "
+"need to save this file and reload the Nginx. Are you sure you want to "
 "continue?"
 msgstr ""
 "لضمان عمل تجديد الشهادة التلقائي بشكل طبيعي، نحتاج إلى إضافة موقع يمكنه "
@@ -6637,9 +6649,9 @@ msgid ""
 "provide an OpenAI-compatible API endpoint, so just set the baseUrl to your "
 "local API."
 msgstr ""
-"لاستخدام نموذج كبير محلي، قم بنشره باستخدام ollama أو vllm أو lmdeploy. "
-"توفر هذه الأدوات نقطة نهاية API متوافقة مع OpenAI، لذا ما عليك سوى تعيين "
-"baseUrl إلى API المحلي الخاص بك."
+"لاستخدام نموذج كبير محلي، قم بنشره باستخدام ollama أو vllm أو lmdeploy. توفر "
+"هذه الأدوات نقطة نهاية API متوافقة مع OpenAI، لذا ما عليك سوى تعيين baseUrl "
+"إلى API المحلي الخاص بك."
 
 #: src/views/dashboard/NginxDashBoard.vue:57
 msgid "Toggle failed"
@@ -7031,8 +7043,8 @@ msgid ""
 "you have a valid backup file and security token, and carefully select what "
 "to restore."
 msgstr ""
-"تحذير: ستقوم عملية الاستعادة بالكتابة فوق التكوينات الحالية. تأكد من أن "
-"لديك ملف نسخ احتياطي صالحًا ورمزًا أمنيًا، واختر بعناية ما تريد استعادته."
+"تحذير: ستقوم عملية الاستعادة بالكتابة فوق التكوينات الحالية. تأكد من أن لديك "
+"ملف نسخ احتياطي صالحًا ورمزًا أمنيًا، واختر بعناية ما تريد استعادته."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:103
 msgid ""
@@ -7056,8 +7068,8 @@ msgstr "سنضيف سجل أو أكثر من سجلات TXT إلى سجلات DN
 
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:142
 msgid ""
-"We will remove the HTTPChallenge configuration from this file and reload "
-"the Nginx. Are you sure you want to continue?"
+"We will remove the HTTPChallenge configuration from this file and reload the "
+"Nginx. Are you sure you want to continue?"
 msgstr ""
 "سنقوم بإزالة تكوين HTTPChallenge من هذا الملف وإعادة تحميل Nginx. هل أنت "
 "متأكد أنك تريد المتابعة؟"
@@ -7110,8 +7122,8 @@ msgid ""
 "When you enable/disable, delete, or save this site, the nodes set in the "
 "namespace and the nodes selected below will be synchronized."
 msgstr ""
-"عند تمكين/تعطيل أو حذف أو حفظ هذا الموقع، سيتم مزامنة العقد المحددة في "
-"مساحة الاسم والعقد المحددة أدناه."
+"عند تمكين/تعطيل أو حذف أو حفظ هذا الموقع، سيتم مزامنة العقد المحددة في مساحة "
+"الاسم والعقد المحددة أدناه."
 
 #: src/views/preference/components/AuthSettings/RecoveryCodes.vue:141
 msgid ""
@@ -7181,11 +7193,11 @@ msgstr "نعم"
 
 #: src/views/terminal/Terminal.vue:200
 msgid ""
-"You are accessing this terminal over an insecure HTTP connection on a "
-"non-localhost domain. This may expose sensitive information."
+"You are accessing this terminal over an insecure HTTP connection on a non-"
+"localhost domain. This may expose sensitive information."
 msgstr ""
-"أنت تتصل بهذا الطرف عبر اتصال HTTP غير آمن في نطاق غير محلي. قد يؤدي هذا "
-"إلى كشف معلومات حساسة."
+"أنت تتصل بهذا الطرف عبر اتصال HTTP غير آمن في نطاق غير محلي. قد يؤدي هذا إلى "
+"كشف معلومات حساسة."
 
 #: src/constants/errors/config.ts:8
 msgid "You are not allowed to delete a file outside of the nginx config path"
@@ -7214,7 +7226,8 @@ msgid ""
 msgstr "لم تقم بتكوين إعدادات Webauthn، لذا لا يمكنك إضافة مفتاح مرور."
 
 #: src/views/preference/components/AuthSettings/RecoveryCodes.vue:82
-msgid "You have not enabled 2FA yet. Please enable 2FA to generate recovery codes."
+msgid ""
+"You have not enabled 2FA yet. Please enable 2FA to generate recovery codes."
 msgstr ""
 "لم تقم بتمكين المصادقة الثنائية بعد. يرجى تمكين المصادقة الثنائية لإنشاء "
 "رموز الاسترداد."
@@ -7391,13 +7404,14 @@ msgstr "خط أنابيب بدون تخصيص"
 
 #~ msgid ""
 #~ "Support communication with the backend through the Server-Sent Events "
-#~ "protocol. If your Nginx UI is being used via an Nginx reverse proxy, please "
-#~ "refer to this link to write the corresponding configuration file: "
+#~ "protocol. If your Nginx UI is being used via an Nginx reverse proxy, "
+#~ "please refer to this link to write the corresponding configuration file: "
 #~ "https://nginxui.com/guide/nginx-proxy-example.html"
 #~ msgstr ""
-#~ "دعم الاتصال مع الخلفية من خلال بروتوكول Server-Sent Events. إذا كنت تستخدم "
-#~ "واجهة Nginx UI عبر وكيل عكسي لـ Nginx، يرجى الرجوع إلى هذا الرابط لكتابة "
-#~ "ملف التكوين المقابل: https://nginxui.com/guide/nginx-proxy-example.html"
+#~ "دعم الاتصال مع الخلفية من خلال بروتوكول Server-Sent Events. إذا كنت "
+#~ "تستخدم واجهة Nginx UI عبر وكيل عكسي لـ Nginx، يرجى الرجوع إلى هذا الرابط "
+#~ "لكتابة ملف التكوين المقابل: https://nginxui.com/guide/nginx-proxy-example."
+#~ "html"
 
 #~ msgid "If left blank, the default CA Dir will be used."
 #~ msgstr "إذا تُرك فارغًا، سيتم استخدام دليل CA الافتراضي."
@@ -7488,12 +7502,12 @@ msgstr "خط أنابيب بدون تخصيص"
 
 #~ msgid ""
 #~ "Check if /var/run/docker.sock exists. If you are using Nginx UI Official "
-#~ "Docker Image, please make sure the docker socket is mounted like this: `-v "
-#~ "/var/run/docker.sock:/var/run/docker.sock`."
+#~ "Docker Image, please make sure the docker socket is mounted like this: `-"
+#~ "v /var/run/docker.sock:/var/run/docker.sock`."
 #~ msgstr ""
 #~ "تحقق مما إذا كان /var/run/docker.sock موجودًا. إذا كنت تستخدم صورة Docker "
-#~ "الرسمية لـ Nginx UI، يرجى التأكد من أن مقبس Docker مثبت بهذه الطريقة: `-v "
-#~ "/var/run/docker.sock:/var/run/docker.sock`."
+#~ "الرسمية لـ Nginx UI، يرجى التأكد من أن مقبس Docker مثبت بهذه الطريقة: `-"
+#~ "v /var/run/docker.sock:/var/run/docker.sock`."
 
 #~ msgid "Check if the nginx access log path exists"
 #~ msgstr "تحقق مما إذا كان مسار سجل الوصول لـ nginx موجودًا"
@@ -7529,8 +7543,8 @@ msgstr "خط أنابيب بدون تخصيص"
 
 #, fuzzy
 #~ msgid ""
-#~ "When you enable/disable, delete, or save this stream, the nodes set in the "
-#~ "Node Group and the nodes selected below will be synchronized."
+#~ "When you enable/disable, delete, or save this stream, the nodes set in "
+#~ "the Node Group and the nodes selected below will be synchronized."
 #~ msgstr ""
 #~ "عند تفعيل/تعطيل، حذف، أو حفظ هذا الموقع، سيتم مزامنة العقد المحددة في فئة "
 #~ "الموقع والعقد المحددة أدناه."
@@ -7593,12 +7607,15 @@ msgstr "خط أنابيب بدون تخصيص"
 #~ msgid "Please upgrade the remote Nginx UI to the latest version"
 #~ msgstr "يرجى ترقية واجهة Nginx البعيدة إلى أحدث إصدار"
 
-#~ msgid "Rename %{orig_path} to %{new_path} on %{env_name} failed, response: %{resp}"
+#~ msgid ""
+#~ "Rename %{orig_path} to %{new_path} on %{env_name} failed, response: "
+#~ "%{resp}"
 #~ msgstr ""
 #~ "فشل إعادة تسمية %{orig_path} إلى %{new_path} على %{env_name}، الاستجابة: "
 #~ "%{resp}"
 
-#~ msgid "Rename Site %{site} to %{new_site} on %{node} error, response: %{resp}"
+#~ msgid ""
+#~ "Rename Site %{site} to %{new_site} on %{node} error, response: %{resp}"
 #~ msgstr ""
 #~ "خطأ في إعادة تسمية الموقع %{site} إلى %{new_site} على %{node}، الاستجابة: "
 #~ "%{resp}"
@@ -7613,20 +7630,23 @@ msgstr "خط أنابيب بدون تخصيص"
 #~ "فشل مزامنة الشهادة %{cert_name} إلى %{env_name}، يرجى ترقية واجهة Nginx "
 #~ "البعيدة إلى أحدث إصدار"
 
-#~ msgid "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
+#~ msgid ""
+#~ "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
 #~ msgstr "فشل مزامنة الشهادة %{cert_name} إلى %{env_name}، الاستجابة: %{resp}"
 
 #~ msgid "Sync config %{config_name} to %{env_name} failed, response: %{resp}"
-#~ msgstr "فشل مزامنة التكوين %{config_name} إلى %{env_name}، الاستجابة: %{resp}"
+#~ msgstr ""
+#~ "فشل مزامنة التكوين %{config_name} إلى %{env_name}، الاستجابة: %{resp}"
 
 #~ msgid ""
-#~ "If you lose your mobile phone, you can use the recovery code to reset your "
-#~ "2FA."
+#~ "If you lose your mobile phone, you can use the recovery code to reset "
+#~ "your 2FA."
 #~ msgstr ""
 #~ "إذا فقدت هاتفك المحمول، يمكنك استخدام رمز الاسترداد لإعادة تعيين المصادقة "
 #~ "الثنائية."
 
-#~ msgid "The recovery code is only displayed once, please save it in a safe place."
+#~ msgid ""
+#~ "The recovery code is only displayed once, please save it in a safe place."
 #~ msgstr "رمز الاسترداد يُعرض مرة واحدة فقط، يرجى حفظه في مكان آمن."
 
 #~ msgid "Can't scan? Use text key binding"
@@ -7636,4 +7656,5 @@ msgstr "خط أنابيب بدون تخصيص"
 #~ msgstr "اسم المستخدم أو كلمة المرور غير صحيحة"
 
 #~ msgid "Too many login failed attempts, please try again later"
-#~ msgstr "عدد كبير جدًا من محاولات تسجيل الدخول الفاشلة، يرجى المحاولة مرة أخرى لاحقًا"
+#~ msgstr ""
+#~ "عدد كبير جدًا من محاولات تسجيل الدخول الفاشلة، يرجى المحاولة مرة أخرى لاحقًا"

File diff suppressed because it is too large
+ 223 - 188
app/src/language/de_DE/app.po


+ 7 - 7
app/src/language/en/app.po

@@ -467,7 +467,7 @@ msgstr ""
 msgid "Auto Backup Storage Failed"
 msgstr ""
 
-#: src/views/nginx_log/NginxLog.vue:86 src/views/node/Node.vue:164
+#: src/views/nginx_log/NginxLog.vue:101 src/views/node/Node.vue:164
 msgid "Auto Refresh"
 msgstr ""
 
@@ -519,7 +519,7 @@ msgstr ""
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
-#: src/views/nginx_log/NginxLog.vue:114
+#: src/views/nginx_log/NginxLog.vue:129
 #: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:170
 #: src/views/stream/components/StreamEditor.vue:134
 msgid "Back"
@@ -1456,7 +1456,7 @@ msgstr ""
 #: src/routes/modules/dashboard.ts:10
 #: src/views/config/components/ConfigLeftPanel.vue:109
 #: src/views/config/components/ConfigLeftPanel.vue:159
-#: src/views/config/ConfigList.vue:69 src/views/nginx_log/NginxLog.vue:78
+#: src/views/config/ConfigList.vue:69 src/views/nginx_log/NginxLog.vue:93
 msgid "Dashboard"
 msgstr ""
 
@@ -3966,7 +3966,7 @@ msgstr ""
 msgid "Nginx is running"
 msgstr ""
 
-#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:61
+#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:76
 msgid "Nginx Log"
 msgstr ""
 
@@ -4844,7 +4844,7 @@ msgstr ""
 msgid "Quick Select"
 msgstr ""
 
-#: src/views/nginx_log/NginxLog.vue:79
+#: src/views/nginx_log/NginxLog.vue:94
 msgid "Raw"
 msgstr ""
 
@@ -5448,7 +5448,7 @@ msgstr ""
 
 #: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
 #: src/views/certificate/components/RenewCert.vue:26
-#: src/views/preference/store/index.ts:123
+#: src/views/preference/store/index.ts:124
 msgid "Save successfully"
 msgstr ""
 
@@ -5941,7 +5941,7 @@ msgstr ""
 msgid "Streams-enabled directory not exist"
 msgstr ""
 
-#: src/views/nginx_log/NginxLog.vue:77
+#: src/views/nginx_log/NginxLog.vue:92
 msgid "Structured"
 msgstr ""
 

File diff suppressed because it is too large
+ 214 - 189
app/src/language/es/app.po


File diff suppressed because it is too large
+ 223 - 194
app/src/language/fr_FR/app.po


File diff suppressed because it is too large
+ 281 - 164
app/src/language/ja_JP/app.po


File diff suppressed because it is too large
+ 276 - 175
app/src/language/ko_KR/app.po


+ 7 - 7
app/src/language/messages.pot

@@ -472,7 +472,7 @@ msgstr ""
 msgid "Auto Backup Storage Failed"
 msgstr ""
 
-#: src/views/nginx_log/NginxLog.vue:86
+#: src/views/nginx_log/NginxLog.vue:101
 #: src/views/node/Node.vue:164
 msgid "Auto Refresh"
 msgstr ""
@@ -526,7 +526,7 @@ msgstr ""
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120
 #: src/views/config/ConfigList.vue:217
-#: src/views/nginx_log/NginxLog.vue:114
+#: src/views/nginx_log/NginxLog.vue:129
 #: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:170
 #: src/views/stream/components/StreamEditor.vue:134
 msgid "Back"
@@ -1427,7 +1427,7 @@ msgstr ""
 #: src/views/config/components/ConfigLeftPanel.vue:109
 #: src/views/config/components/ConfigLeftPanel.vue:159
 #: src/views/config/ConfigList.vue:69
-#: src/views/nginx_log/NginxLog.vue:78
+#: src/views/nginx_log/NginxLog.vue:93
 msgid "Dashboard"
 msgstr ""
 
@@ -3922,7 +3922,7 @@ msgid "Nginx is running"
 msgstr ""
 
 #: src/routes/modules/nginx_log.ts:9
-#: src/views/nginx_log/NginxLog.vue:61
+#: src/views/nginx_log/NginxLog.vue:76
 msgid "Nginx Log"
 msgstr ""
 
@@ -4789,7 +4789,7 @@ msgstr ""
 msgid "Quick Select"
 msgstr ""
 
-#: src/views/nginx_log/NginxLog.vue:79
+#: src/views/nginx_log/NginxLog.vue:94
 msgid "Raw"
 msgstr ""
 
@@ -5392,7 +5392,7 @@ msgstr ""
 #: src/language/curd.ts:35
 #: src/views/certificate/CertificateEditor.vue:49
 #: src/views/certificate/components/RenewCert.vue:26
-#: src/views/preference/store/index.ts:123
+#: src/views/preference/store/index.ts:124
 msgid "Save successfully"
 msgstr ""
 
@@ -5881,7 +5881,7 @@ msgstr ""
 msgid "Streams-enabled directory not exist"
 msgstr ""
 
-#: src/views/nginx_log/NginxLog.vue:77
+#: src/views/nginx_log/NginxLog.vue:92
 msgid "Structured"
 msgstr ""
 

+ 207 - 177
app/src/language/pt_PT/app.po

@@ -4,17 +4,18 @@ msgid ""
 msgstr ""
 "PO-Revision-Date: 2024-08-12 17:09+0000\n"
 "Last-Translator: Kleiser Sarifo <kleiser.sarifo@gmail.com>\n"
-"Language-Team: Portuguese (Portugal) "
-"<https://weblate.nginxui.com/projects/nginx-ui/frontend/pt_PT/>\n"
+"Language-Team: Portuguese (Portugal) <https://weblate.nginxui.com/projects/"
+"nginx-ui/frontend/pt_PT/>\n"
 "Language: pt_PT\n"
-"Content-Type: text/plain; charset=utf-8\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n > 1;\n"
 "X-Generator: Weblate 5.6.2\n"
 
 #: src/language/generate.ts:35
 msgid "[Nginx UI] ACME User: %{name}, Email: %{email}, CA Dir: %{caDir}"
-msgstr "[Nginx UI] Utilizador ACME: %{name}, Email: %{email}, Diretório CA: %{caDir}"
+msgstr ""
+"[Nginx UI] Utilizador ACME: %{name}, Email: %{email}, Diretório CA: %{caDir}"
 
 #: src/language/generate.ts:36
 msgid "[Nginx UI] Backing up current certificate for later revocation"
@@ -31,14 +32,16 @@ msgid "[Nginx UI] Certificate successfully revoked"
 msgstr "[Nginx UI] Certificado revogado com sucesso"
 
 #: src/language/generate.ts:39
-msgid "[Nginx UI] Certificate was used for server, reloading server TLS certificate"
+msgid ""
+"[Nginx UI] Certificate was used for server, reloading server TLS certificate"
 msgstr ""
 "[Nginx UI] O certificado foi usado para o servidor, a recarregar o "
 "certificado TLS do servidor"
 
 #: src/language/generate.ts:40
 msgid "[Nginx UI] Creating client facilitates communication with the CA server"
-msgstr "[Nginx UI] Criando cliente para facilitar a comunicação com o servidor CA"
+msgstr ""
+"[Nginx UI] Criando cliente para facilitar a comunicação com o servidor CA"
 
 #: src/language/generate.ts:41
 msgid "[Nginx UI] Environment variables cleaned"
@@ -111,8 +114,8 @@ msgstr "* Inclui nós do grupo %{groupName} e nós selecionados manualmente"
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:141
 msgid ""
-"* Index files are stored in the \"log-index\" directory within your Nginx "
-"UI config path by default."
+"* Index files are stored in the \"log-index\" directory within your Nginx UI "
+"config path by default."
 msgstr ""
 "* Os ficheiros de índice são armazenados no diretório \"log-index\" dentro "
 "do caminho de configuração do NGINX UI por padrão."
@@ -300,8 +303,7 @@ msgstr "Configurações avançadas"
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:105
 msgid "Afterwards, refresh this page and click add passkey again."
 msgstr ""
-"Depois, atualize esta página e clique em adicionar chave de acesso "
-"novamente."
+"Depois, atualize esta página e clique em adicionar chave de acesso novamente."
 
 #: src/components/NamespaceTabs/NamespaceTabs.vue:116
 msgid "All"
@@ -333,7 +335,8 @@ msgid "Any"
 msgstr "Qualquer"
 
 #: src/components/AutoCertForm/AutoCertForm.vue:209
-msgid "Any reachable IP address can be used with private Certificate Authorities"
+msgid ""
+"Any reachable IP address can be used with private Certificate Authorities"
 msgstr ""
 "Qualquer endereço IP acessível pode ser usado com autoridades de "
 "certificação privadas"
@@ -376,7 +379,8 @@ msgstr "Tem certeza que pretende eliminar este IP banido imediatamente?"
 
 #: src/views/preference/components/AuthSettings/Passkey.vue:114
 msgid "Are you sure to delete this passkey immediately?"
-msgstr "Tem a certeza de que deseja eliminar imediatamente esta chave de acesso?"
+msgstr ""
+"Tem a certeza de que deseja eliminar imediatamente esta chave de acesso?"
 
 #: src/views/preference/components/AuthSettings/RecoveryCodes.vue:155
 msgid "Are you sure to generate new recovery codes?"
@@ -499,7 +503,7 @@ msgstr "Backup automático falhou"
 msgid "Auto Backup Storage Failed"
 msgstr "Falha no armazenamento de backup automático"
 
-#: src/views/nginx_log/NginxLog.vue:86 src/views/node/Node.vue:164
+#: src/views/nginx_log/NginxLog.vue:101 src/views/node/Node.vue:164
 msgid "Auto Refresh"
 msgstr "Actualizar Automaticamente"
 
@@ -551,7 +555,7 @@ msgstr "Méd./PV"
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
-#: src/views/nginx_log/NginxLog.vue:114
+#: src/views/nginx_log/NginxLog.vue:129
 #: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:170
 #: src/views/stream/components/StreamEditor.vue:134
 msgid "Back"
@@ -613,7 +617,8 @@ msgstr "O caminho de backup não é um diretório: {0}"
 
 #: src/constants/errors/backup.ts:55
 msgid "Backup path is required for custom directory backup"
-msgstr "O caminho de backup é necessário para o backup de diretório personalizado"
+msgstr ""
+"O caminho de backup é necessário para o backup de diretório personalizado"
 
 #: src/views/backup/AutoBackup/components/CronEditor.vue:141
 msgid "Backup Schedule"
@@ -622,11 +627,11 @@ msgstr "Agendamento de backup"
 #: src/components/Notification/notifications.ts:38
 msgid "Backup task %{backup_name} completed successfully, file: %{file_path}"
 msgstr ""
-"Tarefa de backup %{backup_name} concluída com sucesso, ficheiro: "
-"%{file_path}"
+"Tarefa de backup %{backup_name} concluída com sucesso, ficheiro: %{file_path}"
 
 #: src/components/Notification/notifications.ts:34
-msgid "Backup task %{backup_name} failed during storage upload, error: %{error}"
+msgid ""
+"Backup task %{backup_name} failed during storage upload, error: %{error}"
 msgstr ""
 "A tarefa de backup %{backup_name} falhou durante o upload para o "
 "armazenamento, erro: %{error}"
@@ -802,7 +807,8 @@ msgstr "Não é possível aceder ao caminho de armazenamento {0}: {1}"
 
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
-msgstr "Não é possível alterar a senha do usuário inicial no modo de demonstração"
+msgstr ""
+"Não é possível alterar a senha do usuário inicial no modo de demonstração"
 
 #: src/components/ConfigHistory/DiffViewer.vue:72
 msgid "Cannot compare: Missing content"
@@ -985,26 +991,26 @@ msgstr "Verificar favicon"
 #: src/language/generate.ts:6
 msgid ""
 "Check if /var/run/docker.sock exists. If you are using Nginx UI Official "
-"Docker Image, please make sure the docker socket is mounted like this: `-v "
-"/var/run/docker.sock:/var/run/docker.sock`. Nginx UI official image uses "
-"/var/run/docker.sock to communicate with the host Docker Engine via Docker "
-"Client API. This feature is used to control Nginx in another container and "
-"perform container replacement rather than binary replacement during OTA "
-"upgrades of Nginx UI to ensure container dependencies are also upgraded. If "
-"you don't need this feature, please add the environment variable "
+"Docker Image, please make sure the docker socket is mounted like this: `-v /"
+"var/run/docker.sock:/var/run/docker.sock`. Nginx UI official image uses /var/"
+"run/docker.sock to communicate with the host Docker Engine via Docker Client "
+"API. This feature is used to control Nginx in another container and perform "
+"container replacement rather than binary replacement during OTA upgrades of "
+"Nginx UI to ensure container dependencies are also upgraded. If you don't "
+"need this feature, please add the environment variable "
 "NGINX_UI_IGNORE_DOCKER_SOCKET=true to the container."
 msgstr ""
 "Verifique se /var/run/docker.sock existe. Se estiver a utilizar a imagem "
 "Docker oficial do Nginx UI, certifique-se de que o socket Docker está "
 "montado desta forma: `-v /var/run/docker.sock:/var/run/docker.sock`. A "
-"imagem oficial do Nginx UI utiliza /var/run/docker.sock para comunicar com "
-"o Docker Engine do anfitrião através da API do Docker Client. Esta "
+"imagem oficial do Nginx UI utiliza /var/run/docker.sock para comunicar com o "
+"Docker Engine do anfitrião através da API do Docker Client. Esta "
 "funcionalidade é utilizada para controlar o Nginx noutro contentor e "
 "realizar a substituição de contentores em vez da substituição binária "
-"durante as atualizações OTA do Nginx UI para garantir que as dependências "
-"do contentor também são atualizadas. Se não necessitar desta "
-"funcionalidade, adicione a variável de ambiente "
-"NGINX_UI_IGNORE_DOCKER_SOCKET=true ao contentor."
+"durante as atualizações OTA do Nginx UI para garantir que as dependências do "
+"contentor também são atualizadas. Se não necessitar desta funcionalidade, "
+"adicione a variável de ambiente NGINX_UI_IGNORE_DOCKER_SOCKET=true ao "
+"contentor."
 
 #: src/components/SelfCheck/tasks/frontend/https-check.ts:14
 msgid ""
@@ -1015,25 +1021,27 @@ msgstr ""
 "impede o uso de Passkeys e funcionalidades da área de transferência"
 
 #: src/language/generate.ts:7
-msgid "Check if the conf.d directory is under the nginx configuration directory"
-msgstr "Verifique se o diretório conf.d está no diretório de configuração do nginx"
+msgid ""
+"Check if the conf.d directory is under the nginx configuration directory"
+msgstr ""
+"Verifique se o diretório conf.d está no diretório de configuração do nginx"
 
 #: src/language/generate.ts:9
 msgid ""
-"Check if the nginx access log path exists. By default, this path is "
-"obtained from 'nginx -V'. If it cannot be obtained or the obtained path "
-"does not point to a valid, existing file, an error will be reported. In "
-"this case, you need to modify the configuration file to specify the access "
-"log path.Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#accesslogpath"
+"Check if the nginx access log path exists. By default, this path is obtained "
+"from 'nginx -V'. If it cannot be obtained or the obtained path does not "
+"point to a valid, existing file, an error will be reported. In this case, "
+"you need to modify the configuration file to specify the access log path."
+"Refer to the docs for more details: https://nginxui.com/zh_CN/guide/config-"
+"nginx.html#accesslogpath"
 msgstr ""
 "Verifique se o caminho do registo de acesso do nginx existe. Por "
 "predefinição, este caminho é obtido a partir de 'nginx -V'. Se não for "
 "possível obtê-lo ou se o caminho obtido não apontar para um ficheiro válido "
 "existente, será reportado um erro. Neste caso, terá de modificar o ficheiro "
 "de configuração para especificar o caminho do registo de acesso. Consulte a "
-"documentação para mais detalhes: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#accesslogpath"
+"documentação para mais detalhes: https://nginxui.com/zh_CN/guide/config-"
+"nginx.html#accesslogpath"
 
 #: src/language/generate.ts:10
 msgid "Check if the nginx configuration directory exists"
@@ -1049,24 +1057,24 @@ msgid ""
 "from 'nginx -V'. If it cannot be obtained or the obtained path does not "
 "point to a valid, existing file, an error will be reported. In this case, "
 "you need to modify the configuration file to specify the error log path. "
-"Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#errorlogpath"
+"Refer to the docs for more details: https://nginxui.com/zh_CN/guide/config-"
+"nginx.html#errorlogpath"
 msgstr ""
 "Verifique se o caminho do registo de erros do nginx existe. Por "
 "predefinição, este caminho é obtido a partir de 'nginx -V'. Se não for "
 "possível obtê-lo ou se o caminho obtido não apontar para um ficheiro válido "
 "existente, será reportado um erro. Neste caso, terá de modificar o ficheiro "
 "de configuração para especificar o caminho do registo de erros. Consulte a "
-"documentação para mais detalhes: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#errorlogpath"
+"documentação para mais detalhes: https://nginxui.com/zh_CN/guide/config-"
+"nginx.html#errorlogpath"
 
 #: src/language/generate.ts:8
 msgid ""
 "Check if the nginx PID path exists. By default, this path is obtained from "
 "'nginx -V'. If it cannot be obtained, an error will be reported. In this "
 "case, you need to modify the configuration file to specify the Nginx PID "
-"path.Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#pidpath"
+"path.Refer to the docs for more details: https://nginxui.com/zh_CN/guide/"
+"config-nginx.html#pidpath"
 msgstr ""
 "Verifique se o caminho do PID do Nginx existe. Por padrão, este caminho é "
 "obtido a partir de 'nginx -V'. Se não puder ser obtido, será relatado um "
@@ -1100,8 +1108,8 @@ msgstr ""
 
 #: src/language/generate.ts:18
 msgid ""
-"Check if the streams-available and streams-enabled directories are under "
-"the nginx configuration directory"
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory"
 msgstr ""
 "Verifique se os diretórios streams-available e streams-enabled estão no "
 "diretório de configuração do nginx"
@@ -1141,7 +1149,8 @@ msgstr "Limpo com sucesso"
 #: src/components/SystemRestore/SystemRestoreContent.vue:194
 #: src/components/SystemRestore/SystemRestoreContent.vue:271
 msgid "Click or drag backup file to this area to upload"
-msgstr "Clique ou arraste o arquivo de backup para esta área para fazer o upload"
+msgstr ""
+"Clique ou arraste o arquivo de backup para esta área para fazer o upload"
 
 #: src/language/curd.ts:51 src/language/curd.ts:55
 msgid "Click or drag files to this area to upload"
@@ -1542,7 +1551,7 @@ msgstr "Diariamente às %{time}"
 #: src/routes/modules/dashboard.ts:10
 #: src/views/config/components/ConfigLeftPanel.vue:109
 #: src/views/config/components/ConfigLeftPanel.vue:159
-#: src/views/config/ConfigList.vue:69 src/views/nginx_log/NginxLog.vue:78
+#: src/views/config/ConfigList.vue:69 src/views/nginx_log/NginxLog.vue:93
 msgid "Dashboard"
 msgstr "Painel"
 
@@ -1913,7 +1922,8 @@ msgstr "Domínio"
 
 #: src/views/certificate/components/AutoCertManagement.vue:55
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
-msgstr "A lista de domínios está vazia, tente reabrir o Auto Cert para %{config}"
+msgstr ""
+"A lista de domínios está vazia, tente reabrir o Auto Cert para %{config}"
 
 #: src/views/certificate/components/CertificateDownload.vue:94
 msgid "Download Certificate Files"
@@ -1945,8 +1955,7 @@ msgid ""
 "non-HTTPS websites, except when running on localhost."
 msgstr ""
 "Devido às políticas de segurança de alguns navegadores, não é possível "
-"utilizar passkeys em sites não HTTPS, exceto quando em execução no "
-"localhost."
+"utilizar passkeys em sites não HTTPS, exceto quando em execução no localhost."
 
 #: src/components/LLM/LLMSessionTabs.vue:287
 #: src/components/LLM/LLMSessionTabs.vue:378
@@ -2153,11 +2162,13 @@ msgstr "Activado com sucesso"
 
 #: src/views/preference/tabs/ServerSettings.vue:52
 msgid "Enables HTTP/2 support with multiplexing and server push capabilities"
-msgstr "Ativa o suporte a HTTP/2 com capacidades de multiplexação e push do servidor"
+msgstr ""
+"Ativa o suporte a HTTP/2 com capacidades de multiplexação e push do servidor"
 
 #: src/views/preference/tabs/ServerSettings.vue:56
 msgid "Enables HTTP/3 support based on QUIC protocol for best performance"
-msgstr "Ativa o suporte a HTTP/3 baseado no protocolo QUIC para melhor desempenho"
+msgstr ""
+"Ativa o suporte a HTTP/3 baseado no protocolo QUIC para melhor desempenho"
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:85
 msgid ""
@@ -2234,8 +2245,8 @@ msgstr "Logs de Erro"
 
 #: src/views/nginx_log/structured/StructuredLogViewer.vue:668
 msgid ""
-"Error logs do not support structured analysis as they contain free-form "
-"text messages."
+"Error logs do not support structured analysis as they contain free-form text "
+"messages."
 msgstr ""
 "Os registos de erro não suportam análise estruturada, pois contêm mensagens "
 "de texto livre."
@@ -2303,8 +2314,8 @@ msgstr ""
 
 #: src/views/certificate/ACMEUser.vue:92
 msgid ""
-"External Account Binding Key ID (optional). Required for some ACME "
-"providers like ZeroSSL."
+"External Account Binding Key ID (optional). Required for some ACME providers "
+"like ZeroSSL."
 msgstr ""
 "ID da chave de vinculação de conta externa (opcional). Necessário para "
 "alguns fornecedores ACME como o ZeroSSL."
@@ -2816,10 +2827,11 @@ msgid "For commercial or professional use, contact"
 msgstr "Para uso comercial ou profissional, contacte"
 
 #: src/views/nginx_log/structured/StructuredLogViewer.vue:670
-msgid "For error logs, please use the Raw Log Viewer for better viewing experience."
+msgid ""
+"For error logs, please use the Raw Log Viewer for better viewing experience."
 msgstr ""
-"Para registos de erros, utilize o Visualizador de Registos em Bruto para "
-"uma melhor experiência de visualização."
+"Para registos de erros, utilize o Visualizador de Registos em Bruto para uma "
+"melhor experiência de visualização."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:140
 msgid ""
@@ -2832,8 +2844,8 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:188
 msgid ""
-"For IP-based certificates, please specify the server IP address that will "
-"be included in the certificate."
+"For IP-based certificates, please specify the server IP address that will be "
+"included in the certificate."
 msgstr ""
 "Para certificados baseados em IP, por favor especifique o endereço IP do "
 "servidor que será incluído no certificado."
@@ -3052,14 +3064,15 @@ msgid ""
 "ban threshold minutes, the ip will be banned for a period of time."
 msgstr ""
 "Se o número de tentativas de início de sessão falhadas de um IP atingir o "
-"máximo de tentativas em minutos de limite de banimento, o IP será banido "
-"por um período de tempo."
+"máximo de tentativas em minutos de limite de banimento, o IP será banido por "
+"um período de tempo."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:280
 msgid ""
 "If you want to automatically revoke the old certificate, please enable this "
 "option."
-msgstr "Se desejar revogar automaticamente o certificado antigo, ative esta opção."
+msgstr ""
+"Se desejar revogar automaticamente o certificado antigo, ative esta opção."
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:142
 msgid ""
@@ -3192,7 +3205,8 @@ msgstr "Instalação"
 
 #: src/constants/errors/system.ts:3
 msgid "Installation is not allowed after 10 minutes of system startup"
-msgstr "A instalação não é permitida após 10 minutos de inicialização do sistema"
+msgstr ""
+"A instalação não é permitida após 10 minutos de inicialização do sistema"
 
 #: src/views/install/components/TimeoutAlert.vue:11
 msgid ""
@@ -3336,8 +3350,8 @@ msgid ""
 "Keep your recovery codes as safe as your password. We recommend saving them "
 "with a password manager."
 msgstr ""
-"Mantenha os seus códigos de recuperação tão seguros como a sua "
-"palavra-passe. Recomendamos guardá-los com um gestor de palavras-passe."
+"Mantenha os seus códigos de recuperação tão seguros como a sua palavra-"
+"passe. Recomendamos guardá-los com um gestor de palavras-passe."
 
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:60
 msgid "Keepalive Timeout"
@@ -3459,7 +3473,8 @@ msgstr "Deixar em branco não vai mudar nada"
 
 #: src/constants/errors/user.ts:6
 msgid "Legacy recovery code not allowed since totp is not enabled"
-msgstr "Código de recuperação antigo não permitido porque o TOTP não está ativado"
+msgstr ""
+"Código de recuperação antigo não permitido porque o TOTP não está ativado"
 
 #: src/components/AutoCertForm/AutoCertForm.vue:268
 msgid "Lego disable CNAME Support"
@@ -3560,13 +3575,12 @@ msgstr "Log"
 #: src/language/generate.ts:23
 msgid ""
 "Log file %{log_path} is not a regular file. If you are using nginx-ui in "
-"docker container, please refer to "
-"https://nginxui.com/zh_CN/guide/config-nginx-log.html for more information."
+"docker container, please refer to https://nginxui.com/zh_CN/guide/config-"
+"nginx-log.html for more information."
 msgstr ""
 "O ficheiro de registo %{log_path} não é um ficheiro regular. Se estiver a "
-"utilizar o nginx-ui num contentor Docker, consulte "
-"https://nginxui.com/zh_CN/guide/config-nginx-log.html para obter mais "
-"informações."
+"utilizar o nginx-ui num contentor Docker, consulte https://nginxui.com/zh_CN/"
+"guide/config-nginx-log.html para obter mais informações."
 
 #: src/constants/errors/nginx_log.ts:14
 msgid "Log file does not exist"
@@ -3618,12 +3632,12 @@ msgstr "Logrotate"
 
 #: src/views/preference/tabs/LogrotateSettings.vue:13
 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."
+"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 ""
 "O Logrotate, por defeito, está activado na maioria das distribuições Linux "
 "convencionais para utilizadores que instalam o Nginx UI na máquina host, "
@@ -4105,7 +4119,7 @@ msgstr "O Nginx não está em execução noutro contentor"
 msgid "Nginx is running"
 msgstr "O Nginx está em execução"
 
-#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:61
+#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:76
 msgid "Nginx Log"
 msgstr "Logs do Nginx"
 
@@ -4141,7 +4155,8 @@ msgstr "Recarga do Nginx falhou: {0}"
 
 #: src/views/node/Node.vue:88
 msgid "Nginx reload operations have been dispatched to remote nodes"
-msgstr "As operações de recarregamento do Nginx foram enviadas para os nós remotos"
+msgstr ""
+"As operações de recarregamento do Nginx foram enviadas para os nós remotos"
 
 #: src/components/NginxControl/NginxControl.vue:26
 msgid "Nginx reloaded successfully"
@@ -4186,11 +4201,11 @@ msgstr "A configuração do Nginx UI foi restaurada"
 
 #: src/components/SystemRestore/SystemRestoreContent.vue:336
 msgid ""
-"Nginx UI configuration has been restored and will restart automatically in "
-"a few seconds."
+"Nginx UI configuration has been restored and will restart automatically in a "
+"few seconds."
 msgstr ""
-"A configuração do Nginx UI foi restaurada e irá reiniciar automaticamente "
-"em alguns segundos."
+"A configuração do Nginx UI foi restaurada e irá reiniciar automaticamente em "
+"alguns segundos."
 
 #: src/language/generate.ts:30
 msgid "Nginx.conf includes conf.d directory"
@@ -4463,7 +4478,8 @@ msgstr "Ligado"
 
 #: src/views/certificate/DNSCredential.vue:99
 msgid "Once the verification is complete, the records will be removed."
-msgstr "Assim que a verificação estiver concluída, os registos serão removidos."
+msgstr ""
+"Assim que a verificação estiver concluída, os registos serão removidos."
 
 #: src/components/NamespaceTabs/NamespaceTabs.vue:160
 #: src/components/NodeCard/NodeCard.vue:51
@@ -4586,10 +4602,10 @@ msgid ""
 "facial recognition, a device password, or a PIN. They can be used as a "
 "password replacement or as a 2FA method."
 msgstr ""
-"As passkeys são credenciais WebAuthn que validam a sua identidade "
-"utilizando toque, reconhecimento facial, uma palavra-passe do dispositivo "
-"ou um PIN. Podem ser usadas como substituto de palavra-passe ou como método "
-"de autenticação de dois fatores (2FA)."
+"As passkeys são credenciais WebAuthn que validam a sua identidade utilizando "
+"toque, reconhecimento facial, uma palavra-passe do dispositivo ou um PIN. "
+"Podem ser usadas como substituto de palavra-passe ou como método de "
+"autenticação de dois fatores (2FA)."
 
 #: src/views/other/Login.vue:278 src/views/user/userColumns.tsx:16
 msgid "Password"
@@ -4742,7 +4758,8 @@ msgstr "Por favor, insira o token de segurança"
 #: src/components/SystemRestore/SystemRestoreContent.vue:210
 #: src/components/SystemRestore/SystemRestoreContent.vue:287
 msgid "Please enter the security token received during backup"
-msgstr "Por favor, insira o token de segurança recebido durante a cópia de segurança"
+msgstr ""
+"Por favor, insira o token de segurança recebido durante a cópia de segurança"
 
 #: src/components/AutoCertForm/AutoCertForm.vue:80
 msgid "Please enter the server IP address"
@@ -4775,8 +4792,8 @@ msgstr ""
 #: src/components/Notification/notifications.ts:194
 #: src/language/constants.ts:59
 msgid ""
-"Please generate new recovery codes in the preferences immediately to "
-"prevent lockout."
+"Please generate new recovery codes in the preferences immediately to prevent "
+"lockout."
 msgstr ""
 "Por favor, gere novos códigos de recuperação nas preferências imediatamente "
 "para evitar bloqueio."
@@ -4824,12 +4841,15 @@ msgid "Please log in."
 msgstr "Por favor, faça login."
 
 #: src/views/certificate/DNSCredential.vue:102
-msgid "Please note that the unit of time configurations below are all in seconds."
-msgstr "Note que as definições da unidade de tempo abaixo estão todas em segundos."
+msgid ""
+"Please note that the unit of time configurations below are all in seconds."
+msgstr ""
+"Note que as definições da unidade de tempo abaixo estão todas em segundos."
 
 #: src/views/install/components/InstallView.vue:102
 msgid "Please resolve all issues before proceeding with installation"
-msgstr "Por favor, resolva todos os problemas antes de prosseguir com a instalação"
+msgstr ""
+"Por favor, resolva todos os problemas antes de prosseguir com a instalação"
 
 #: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
@@ -4961,8 +4981,7 @@ msgstr "Protocolo"
 #: src/views/preference/tabs/ServerSettings.vue:47
 msgid ""
 "Protocol configuration only takes effect when directly connecting. If using "
-"reverse proxy, please configure the protocol separately in the reverse "
-"proxy."
+"reverse proxy, please configure the protocol separately in the reverse proxy."
 msgstr ""
 "A configuração do protocolo só tem efeito ao conectar diretamente. Se "
 "estiver a usar um proxy inverso, configure o protocolo separadamente no "
@@ -5019,7 +5038,7 @@ msgstr "Na fila para indexação..."
 msgid "Quick Select"
 msgstr "Seleção rápida"
 
-#: src/views/nginx_log/NginxLog.vue:79
+#: src/views/nginx_log/NginxLog.vue:94
 msgid "Raw"
 msgstr "Bruto"
 
@@ -5185,7 +5204,8 @@ msgstr "Recarregamento remoto do Nginx bem-sucedido"
 
 #: src/components/NamespaceTabs/NamespaceTabs.vue:85
 msgid "Reload request failed, please check your network connection"
-msgstr "O pedido de recarregamento falhou, por favor verifique a sua ligação à rede"
+msgstr ""
+"O pedido de recarregamento falhou, por favor verifique a sua ligação à rede"
 
 #: src/components/NginxControl/NginxControl.vue:77
 msgid "Reloading"
@@ -5632,7 +5652,7 @@ msgstr "Salvo com sucesso"
 
 #: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
 #: src/views/certificate/components/RenewCert.vue:26
-#: src/views/preference/store/index.ts:123
+#: src/views/preference/store/index.ts:124
 msgid "Save successfully"
 msgstr "Salvo com sucesso"
 
@@ -5824,7 +5844,8 @@ msgstr "Erro ao definir ambiente: {0}"
 
 #: src/constants/errors/cert.ts:18
 msgid "Set env flag to disable lego CNAME support error: {0}"
-msgstr "Definir flag de ambiente para desativar suporte CNAME do lego erro: {0}"
+msgstr ""
+"Definir flag de ambiente para desativar suporte CNAME do lego erro: {0}"
 
 #: src/views/preference/tabs/CertSettings.vue:36
 msgid ""
@@ -5856,19 +5877,19 @@ msgstr "Configurações"
 
 #: src/constants/errors/nginx_log.ts:8
 msgid ""
-"Settings.NginxLogSettings.AccessLogPath is empty, refer to "
-"https://nginxui.com/guide/config-nginx.html for more information"
+"Settings.NginxLogSettings.AccessLogPath is empty, refer to https://nginxui."
+"com/guide/config-nginx.html for more information"
 msgstr ""
-"Settings.NginxLogSettings.AccessLogPath está vazio, consulte "
-"https://nginxui.com/guide/config-nginx.html para mais informações"
+"Settings.NginxLogSettings.AccessLogPath está vazio, consulte https://nginxui."
+"com/guide/config-nginx.html para mais informações"
 
 #: src/constants/errors/nginx_log.ts:7
 msgid ""
-"Settings.NginxLogSettings.ErrorLogPath is empty, refer to "
-"https://nginxui.com/guide/config-nginx.html for more information"
+"Settings.NginxLogSettings.ErrorLogPath is empty, refer to https://nginxui."
+"com/guide/config-nginx.html for more information"
 msgstr ""
-"Settings.NginxLogSettings.ErrorLogPath está vazio, consulte "
-"https://nginxui.com/guide/config-nginx.html para mais informações"
+"Settings.NginxLogSettings.ErrorLogPath está vazio, consulte https://nginxui."
+"com/guide/config-nginx.html para mais informações"
 
 #: src/views/install/components/InstallView.vue:65
 msgid "Setup your Nginx UI"
@@ -6028,8 +6049,7 @@ msgstr "O caminho do certificado SSL é necessário quando o HTTPS está ativado
 #: src/constants/errors/system.ts:9
 msgid "SSL key file must be under Nginx configuration directory: {0}"
 msgstr ""
-"O ficheiro de chave SSL deve estar no diretório de configuração do Nginx: "
-"{0}"
+"O ficheiro de chave SSL deve estar no diretório de configuração do Nginx: {0}"
 
 #: src/constants/errors/system.ts:7
 msgid "SSL key file not found"
@@ -6145,7 +6165,7 @@ msgstr "O diretório streams-available não existe"
 msgid "Streams-enabled directory not exist"
 msgstr "O diretório streams-enabled não existe"
 
-#: src/views/nginx_log/NginxLog.vue:77
+#: src/views/nginx_log/NginxLog.vue:92
 msgid "Structured"
 msgstr "Estruturado"
 
@@ -6171,12 +6191,12 @@ msgstr "Domingo"
 #: src/components/SelfCheck/tasks/frontend/websocket.ts:13
 msgid ""
 "Support communication with the backend through the WebSocket protocol. If "
-"your Nginx UI is being used via an Nginx reverse proxy, please refer to "
-"this link to write the corresponding configuration file: "
-"https://nginxui.com/guide/nginx-proxy-example.html"
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
 msgstr ""
-"Suporta a comunicação com o backend através do protocolo WebSocket. Se a "
-"sua interface Nginx está a ser utilizada através de um proxy inverso Nginx, "
+"Suporta a comunicação com o backend através do protocolo WebSocket. Se a sua "
+"interface Nginx está a ser utilizada através de um proxy inverso Nginx, "
 "consulte este link para escrever o ficheiro de configuração correspondente: "
 "https://nginxui.com/guide/nginx-proxy-example.html"
 
@@ -6359,8 +6379,8 @@ msgid ""
 "since it was last issued."
 msgstr ""
 "O certificado do domínio será verificado 30 minutos e será renovado se já "
-"tiver passado mais de 1 semana ou o período que definiu nas definições "
-"desde a última emissão."
+"tiver passado mais de 1 semana ou o período que definiu nas definições desde "
+"a última emissão."
 
 #: src/views/preference/tabs/NodeSettings.vue:37
 msgid ""
@@ -6380,11 +6400,10 @@ msgstr "O valor introduzido não é uma Chave de Certificado SSL"
 
 #: src/constants/errors/nginx_log.ts:2
 msgid ""
-"The log path is not under the paths in "
-"settings.NginxSettings.LogDirWhiteList"
+"The log path is not under the paths in settings.NginxSettings.LogDirWhiteList"
 msgstr ""
-"O caminho do registo não está sob os caminhos em "
-"settings.NginxSettings.LogDirWhiteList"
+"O caminho do registo não está sob os caminhos em settings.NginxSettings."
+"LogDirWhiteList"
 
 #: src/views/preference/tabs/OpenAISettings.vue:23
 #: src/views/preference/tabs/OpenAISettings.vue:89
@@ -6396,10 +6415,11 @@ msgstr ""
 "traços, dois pontos e pontos."
 
 #: src/views/preference/tabs/OpenAISettings.vue:90
-msgid "The model used for code completion, if not set, the chat model will be used."
+msgid ""
+"The model used for code completion, if not set, the chat model will be used."
 msgstr ""
-"O modelo utilizado para a conclusão de código, se não estiver definido, "
-"será utilizado o modelo de chat."
+"O modelo utilizado para a conclusão de código, se não estiver definido, será "
+"utilizado o modelo de chat."
 
 #: src/views/preference/tabs/NodeSettings.vue:18
 msgid ""
@@ -6435,8 +6455,8 @@ msgid ""
 "version. To avoid potential errors, please upgrade the remote Nginx UI to "
 "match the local version."
 msgstr ""
-"A versão remota do Nginx UI não é compatível com a versão local do Nginx "
-"UI. Para evitar possíveis erros, atualize a versão remota do Nginx UI para "
+"A versão remota do Nginx UI não é compatível com a versão local do Nginx UI. "
+"Para evitar possíveis erros, atualize a versão remota do Nginx UI para "
 "corresponder à versão local."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:155
@@ -6516,20 +6536,23 @@ msgid "This field should not be empty"
 msgstr "Este campo não pode estar vazio"
 
 #: src/constants/form_errors.ts:6
-msgid "This field should only contain letters, unicode characters, numbers, and -_."
-msgstr "Este campo deve conter apenas letras, caracteres Unicode, números e -_."
+msgid ""
+"This field should only contain letters, unicode characters, numbers, and -_."
+msgstr ""
+"Este campo deve conter apenas letras, caracteres Unicode, números e -_."
 
 #: src/language/curd.ts:46
 msgid ""
-"This field should only contain letters, unicode characters, numbers, and "
-"-_./:"
-msgstr "Este campo deve conter apenas letras, caracteres Unicode, números e -_./:"
+"This field should only contain letters, unicode characters, numbers, and -"
+"_./:"
+msgstr ""
+"Este campo deve conter apenas letras, caracteres Unicode, números e -_./:"
 
 #: src/components/Notification/notifications.ts:94
 msgid "This is a test message sent at %{timestamp} from Nginx UI."
 msgstr ""
-"Esta é uma mensagem de teste enviada em %{timestamp} da interface do "
-"usuário nginx."
+"Esta é uma mensagem de teste enviada em %{timestamp} da interface do usuário "
+"nginx."
 
 #: src/views/dashboard/NginxDashBoard.vue:175
 msgid ""
@@ -6557,9 +6580,9 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:128
 msgid ""
-"This site is configured as a default server (default_server) for HTTPS "
-"(port 443). IP certificates require Certificate Authority (CA) support and "
-"may not be available with all ACME providers."
+"This site is configured as a default server (default_server) for HTTPS (port "
+"443). IP certificates require Certificate Authority (CA) support and may not "
+"be available with all ACME providers."
 msgstr ""
 "Este site está configurado como um servidor padrão (default_server) para "
 "HTTPS (porta 443). Os certificados IP requerem suporte de uma Autoridade de "
@@ -6568,8 +6591,8 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:132
 msgid ""
-"This site uses wildcard server name (_) which typically indicates an "
-"IP-based certificate. IP certificates require Certificate Authority (CA) "
+"This site uses wildcard server name (_) which typically indicates an IP-"
+"based certificate. IP certificates require Certificate Authority (CA) "
 "support and may not be available with all ACME providers."
 msgstr ""
 "Este site utiliza um nome de servidor curinga (_) que normalmente indica um "
@@ -6599,8 +6622,8 @@ msgid ""
 "deleted and rebuilt from scratch. This may take some time. Continue?"
 msgstr ""
 "Isso irá reconstruir todo o índice de registros. Todos os dados de índice "
-"existentes serão eliminados e reconstruídos do zero. Isto pode demorar "
-"algum tempo. Continuar?"
+"existentes serão eliminados e reconstruídos do zero. Isto pode demorar algum "
+"tempo. Continuar?"
 
 #: src/views/nginx_log/indexing/IndexManagement.vue:56
 msgid "This will rebuild the index data for this specific file: %{path}"
@@ -6627,10 +6650,10 @@ msgstr ""
 "UI irá reiniciar após a conclusão da restauração."
 
 #: src/views/node/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 ""
-"Isto vai actualizar ou reinstalar o Nginx UI em %{nodeNames} para "
-"%{version}."
+"Isto vai actualizar ou reinstalar o Nginx UI em %{nodeNames} para %{version}."
 
 #: src/views/preference/tabs/AuthSettings.vue:89
 msgid "Throttle"
@@ -6701,8 +6724,8 @@ msgstr ""
 #: src/views/site/site_edit/components/EnableTLS/EnableTLS.vue:15
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
-"add a location which can proxy the request from authority to backend, and "
-"we need to save this file and reload the Nginx. Are you sure you want to "
+"add a location which can proxy the request from authority to backend, and we "
+"need to save this file and reload the Nginx. Are you sure you want to "
 "continue?"
 msgstr ""
 "Para garantir que a renovação automática da certificação funciona "
@@ -7113,8 +7136,8 @@ msgid ""
 "to restore."
 msgstr ""
 "Aviso: A operação de restauro irá substituir as configurações atuais. "
-"Certifique-se de que tem um ficheiro de cópia de segurança válido e um "
-"token de segurança, e selecione cuidadosamente o que restaurar."
+"Certifique-se de que tem um ficheiro de cópia de segurança válido e um token "
+"de segurança, e selecione cuidadosamente o que restaurar."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:103
 msgid ""
@@ -7141,8 +7164,8 @@ msgstr ""
 
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:142
 msgid ""
-"We will remove the HTTPChallenge configuration from this file and reload "
-"the Nginx. Are you sure you want to continue?"
+"We will remove the HTTPChallenge configuration from this file and reload the "
+"Nginx. Are you sure you want to continue?"
 msgstr ""
 "Removeremos a configuração HTTPChallenge deste ficheiro e reiniciaremos o "
 "Nginx. Tem a certeza de que quer continuar?"
@@ -7268,8 +7291,8 @@ msgstr "Sim"
 
 #: src/views/terminal/Terminal.vue:200
 msgid ""
-"You are accessing this terminal over an insecure HTTP connection on a "
-"non-localhost domain. This may expose sensitive information."
+"You are accessing this terminal over an insecure HTTP connection on a non-"
+"localhost domain. This may expose sensitive information."
 msgstr ""
 "Está a aceder a este terminal através de uma ligação HTTP insegura num "
 "domínio que não é localhost. Isto pode expor informações sensíveis."
@@ -7301,11 +7324,12 @@ msgid ""
 "You have not configured the settings of Webauthn, so you cannot add a "
 "passkey."
 msgstr ""
-"Não configuraste as definições do WebAuthn, por isso não podes adicionar "
-"uma chave de acesso."
+"Não configuraste as definições do WebAuthn, por isso não podes adicionar uma "
+"chave de acesso."
 
 #: src/views/preference/components/AuthSettings/RecoveryCodes.vue:82
-msgid "You have not enabled 2FA yet. Please enable 2FA to generate recovery codes."
+msgid ""
+"You have not enabled 2FA yet. Please enable 2FA to generate recovery codes."
 msgstr ""
 "Ainda não ativou a autenticação de dois fatores. Por favor, ative-a para "
 "gerar códigos de recuperação."
@@ -7412,7 +7436,8 @@ msgstr "Pipeline sem alocação"
 #~ msgstr "Falha ao avaliar os links simbólicos: {0}"
 
 #~ msgid "Failed to restore Nginx UI files: {0}"
-#~ msgstr "Falha ao restaurar os ficheiros da interface de utilizador do Nginx: {0}"
+#~ msgstr ""
+#~ "Falha ao restaurar os ficheiros da interface de utilizador do Nginx: {0}"
 
 #~ msgid "Failed to write to zip buffer: {0}"
 #~ msgstr "Falha ao escrever no buffer ZIP: {0}"
@@ -7433,7 +7458,8 @@ msgstr "Pipeline sem alocação"
 #~ msgstr "O endpoint S3 é inválido: {0}"
 
 #~ msgid "Storage path not in granted access paths: {0}"
-#~ msgstr "Caminho de armazenamento não está nos caminhos de acesso concedidos: {0}"
+#~ msgstr ""
+#~ "Caminho de armazenamento não está nos caminhos de acesso concedidos: {0}"
 
 #~ msgid "files"
 #~ msgstr "arquivos"
@@ -7483,14 +7509,15 @@ msgstr "Pipeline sem alocação"
 
 #~ msgid ""
 #~ "Support communication with the backend through the Server-Sent Events "
-#~ "protocol. If your Nginx UI is being used via an Nginx reverse proxy, please "
-#~ "refer to this link to write the corresponding configuration file: "
+#~ "protocol. If your Nginx UI is being used via an Nginx reverse proxy, "
+#~ "please refer to this link to write the corresponding configuration file: "
 #~ "https://nginxui.com/guide/nginx-proxy-example.html"
 #~ msgstr ""
 #~ "Suporte à comunicação com o backend através do protocolo Server-Sent "
 #~ "Events. Se a sua Nginx UI estiver a ser utilizada através de um proxy "
-#~ "inverso Nginx, consulte este link para escrever o ficheiro de configuração "
-#~ "correspondente: https://nginxui.com/guide/nginx-proxy-example.html"
+#~ "inverso Nginx, consulte este link para escrever o ficheiro de "
+#~ "configuração correspondente: https://nginxui.com/guide/nginx-proxy-"
+#~ "example.html"
 
 #~ msgid "If left blank, the default CA Dir will be used."
 #~ msgstr "Se for deixado em branco, será utilizado o diretório CA padrão."
@@ -7572,8 +7599,8 @@ msgstr "Pipeline sem alocação"
 
 #~ msgid ""
 #~ "Check if /var/run/docker.sock exists. If you are using Nginx UI Official "
-#~ "Docker Image, please make sure the docker socket is mounted like this: `-v "
-#~ "/var/run/docker.sock:/var/run/docker.sock`."
+#~ "Docker Image, please make sure the docker socket is mounted like this: `-"
+#~ "v /var/run/docker.sock:/var/run/docker.sock`."
 #~ msgstr ""
 #~ "Verifique se /var/run/docker.sock existe. Se estiver a utilizar a imagem "
 #~ "Docker oficial do Nginx UI, certifique-se de que o socket Docker está "
@@ -7602,7 +7629,8 @@ msgstr "Pipeline sem alocação"
 #~ msgstr "Este item Auto Cert é inválido, por favor remova-o."
 
 #~ msgid "Automatically indexed from site and stream configurations."
-#~ msgstr "Indexado automaticamente a partir das configurações de site e stream."
+#~ msgstr ""
+#~ "Indexado automaticamente a partir das configurações de site e stream."
 
 #~ msgid "Deploy successfully"
 #~ msgstr "Deploy sucedido"
@@ -7628,11 +7656,11 @@ msgstr "Pipeline sem alocação"
 #~ msgstr "Erro de Formato %{msg}"
 
 #~ msgid ""
-#~ "If you lose your mobile phone, you can use the recovery code to reset your "
-#~ "2FA."
+#~ "If you lose your mobile phone, you can use the recovery code to reset "
+#~ "your 2FA."
 #~ msgstr ""
-#~ "Se perder o seu telemóvel, pode utilizar o código de recuperação para repor "
-#~ "o seu 2FA."
+#~ "Se perder o seu telemóvel, pode utilizar o código de recuperação para "
+#~ "repor o seu 2FA."
 
 #~ msgid "Incorrect username or password"
 #~ msgstr "Utilizador ou senha incorrectos"
@@ -7651,7 +7679,8 @@ msgstr "Pipeline sem alocação"
 #~ "Sincronização do Certificado %{cert_name} para %{env_name} falhou, por "
 #~ "favor actualize a versão remota do Nginx UI para a última versão"
 
-#~ msgid "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
+#~ msgid ""
+#~ "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
 #~ msgstr ""
 #~ "Sincronização do Certificado %{cert_name} para %{env_name} falhou, "
 #~ "resposta: %{resp}"
@@ -7668,7 +7697,8 @@ msgstr "Pipeline sem alocação"
 #~ "Sincronização de configuração %{config_name} para %{env_name} falhou, "
 #~ "resposta: %{resp}"
 
-#~ msgid "The recovery code is only displayed once, please save it in a safe place."
+#~ msgid ""
+#~ "The recovery code is only displayed once, please save it in a safe place."
 #~ msgstr ""
 #~ "O código de recuperação é apresentado apenas uma vez, guarde-o num local "
 #~ "seguro."

+ 213 - 189
app/src/language/ru_RU/app.po

@@ -7,24 +7,24 @@ msgstr ""
 "POT-Creation-Date: \n"
 "PO-Revision-Date: 2025-03-28 02:45+0300\n"
 "Last-Translator: Artyom Isrofilov <artyom@isrofilov.ru>\n"
-"Language-Team: Russian "
-"<https://weblate.nginxui.com/projects/nginx-ui/frontend/ru/>\n"
+"Language-Team: Russian <https://weblate.nginxui.com/projects/nginx-ui/"
+"frontend/ru/>\n"
 "Language: ru_RU\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: Poedit 3.5\n"
 
 #: src/language/generate.ts:35
 msgid "[Nginx UI] ACME User: %{name}, Email: %{email}, CA Dir: %{caDir}"
-msgstr "[Nginx UI] Пользователь ACME: %{name}, Email: %{email}, Каталог CA: %{caDir}"
+msgstr ""
+"[Nginx UI] Пользователь ACME: %{name}, Email: %{email}, Каталог CA: %{caDir}"
 
 #: src/language/generate.ts:36
 msgid "[Nginx UI] Backing up current certificate for later revocation"
 msgstr ""
-"[Nginx UI] Резервное копирование текущего сертификата для последующего "
-"отзыва"
+"[Nginx UI] Резервное копирование текущего сертификата для последующего отзыва"
 
 #: src/language/generate.ts:37
 msgid "[Nginx UI] Certificate renewed successfully"
@@ -35,10 +35,11 @@ msgid "[Nginx UI] Certificate successfully revoked"
 msgstr "[Nginx UI] Сертификат успешно отозван"
 
 #: src/language/generate.ts:39
-msgid "[Nginx UI] Certificate was used for server, reloading server TLS certificate"
+msgid ""
+"[Nginx UI] Certificate was used for server, reloading server TLS certificate"
 msgstr ""
-"[Nginx UI] Сертификат использовался для сервера, перезагрузка "
-"TLS-сертификата сервера"
+"[Nginx UI] Сертификат использовался для сервера, перезагрузка TLS-"
+"сертификата сервера"
 
 #: src/language/generate.ts:40
 msgid "[Nginx UI] Creating client facilitates communication with the CA server"
@@ -115,8 +116,8 @@ msgstr "* Включает узлы из группы %{groupName} и узлы,
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:141
 msgid ""
-"* Index files are stored in the \"log-index\" directory within your Nginx "
-"UI config path by default."
+"* Index files are stored in the \"log-index\" directory within your Nginx UI "
+"config path by default."
 msgstr ""
 "* Файлы индекса по умолчанию хранятся в каталоге \"log-index\" в пути "
 "конфигурации NGINX UI."
@@ -304,7 +305,8 @@ msgstr "Дополнительные настройки"
 
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:105
 msgid "Afterwards, refresh this page and click add passkey again."
-msgstr "После этого обновите эту страницу и снова нажмите «Добавить ключ доступа»."
+msgstr ""
+"После этого обновите эту страницу и снова нажмите «Добавить ключ доступа»."
 
 #: src/components/NamespaceTabs/NamespaceTabs.vue:116
 msgid "All"
@@ -324,8 +326,8 @@ msgid ""
 "All selected subdomains must belong to the same DNS Provider, otherwise the "
 "certificate application will fail."
 msgstr ""
-"Все выбранные поддомены должны принадлежать одному и тому же "
-"DNS-провайдеру, иначе запрос сертификата завершится неудачей."
+"Все выбранные поддомены должны принадлежать одному и тому же DNS-провайдеру, "
+"иначе запрос сертификата завершится неудачей."
 
 #: src/constants/errors/nginx_log.ts:13
 msgid "Analytics service not available"
@@ -336,8 +338,10 @@ msgid "Any"
 msgstr "Любой"
 
 #: src/components/AutoCertForm/AutoCertForm.vue:209
-msgid "Any reachable IP address can be used with private Certificate Authorities"
-msgstr "Любой доступный IP-адрес можно использовать с частными центрами сертификации"
+msgid ""
+"Any reachable IP address can be used with private Certificate Authorities"
+msgstr ""
+"Любой доступный IP-адрес можно использовать с частными центрами сертификации"
 
 #: src/views/preference/tabs/OpenAISettings.vue:32
 msgid "API Base Url"
@@ -500,7 +504,7 @@ msgstr "Автоматическое резервное копирование 
 msgid "Auto Backup Storage Failed"
 msgstr "Сбой хранения автоматической резервной копии"
 
-#: src/views/nginx_log/NginxLog.vue:86 src/views/node/Node.vue:164
+#: src/views/nginx_log/NginxLog.vue:101 src/views/node/Node.vue:164
 msgid "Auto Refresh"
 msgstr "Автообновление"
 
@@ -552,7 +556,7 @@ msgstr "Сред./PV"
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
-#: src/views/nginx_log/NginxLog.vue:114
+#: src/views/nginx_log/NginxLog.vue:129
 #: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:170
 #: src/views/stream/components/StreamEditor.vue:134
 msgid "Back"
@@ -629,7 +633,8 @@ msgstr ""
 "%{file_path}"
 
 #: src/components/Notification/notifications.ts:34
-msgid "Backup task %{backup_name} failed during storage upload, error: %{error}"
+msgid ""
+"Backup task %{backup_name} failed during storage upload, error: %{error}"
 msgstr ""
 "Задача резервного копирования %{backup_name} не выполнена при загрузке в "
 "хранилище, ошибка: %{error}"
@@ -731,8 +736,7 @@ msgstr "Кэш"
 #: src/views/dashboard/components/ParamsOpt/ProxyCacheConfig.vue:177
 msgid "Cache items not accessed within this time will be removed"
 msgstr ""
-"Элементы кэша, к которым не обращались в течение этого времени, будут "
-"удалены"
+"Элементы кэша, к которым не обращались в течение этого времени, будут удалены"
 
 #: src/views/dashboard/components/ParamsOpt/ProxyCacheConfig.vue:349
 msgid "Cache loader processing time threshold"
@@ -990,13 +994,13 @@ msgstr "Проверить фавикон"
 #: src/language/generate.ts:6
 msgid ""
 "Check if /var/run/docker.sock exists. If you are using Nginx UI Official "
-"Docker Image, please make sure the docker socket is mounted like this: `-v "
-"/var/run/docker.sock:/var/run/docker.sock`. Nginx UI official image uses "
-"/var/run/docker.sock to communicate with the host Docker Engine via Docker "
-"Client API. This feature is used to control Nginx in another container and "
-"perform container replacement rather than binary replacement during OTA "
-"upgrades of Nginx UI to ensure container dependencies are also upgraded. If "
-"you don't need this feature, please add the environment variable "
+"Docker Image, please make sure the docker socket is mounted like this: `-v /"
+"var/run/docker.sock:/var/run/docker.sock`. Nginx UI official image uses /var/"
+"run/docker.sock to communicate with the host Docker Engine via Docker Client "
+"API. This feature is used to control Nginx in another container and perform "
+"container replacement rather than binary replacement during OTA upgrades of "
+"Nginx UI to ensure container dependencies are also upgraded. If you don't "
+"need this feature, please add the environment variable "
 "NGINX_UI_IGNORE_DOCKER_SOCKET=true to the container."
 msgstr ""
 "Проверьте, существует ли /var/run/docker.sock. Если вы используете "
@@ -1019,24 +1023,25 @@ msgstr ""
 "препятствует использованию Passkeys и функций буфера обмена"
 
 #: src/language/generate.ts:7
-msgid "Check if the conf.d directory is under the nginx configuration directory"
+msgid ""
+"Check if the conf.d directory is under the nginx configuration directory"
 msgstr "Проверьте, находится ли каталог conf.d в каталоге конфигурации nginx"
 
 #: src/language/generate.ts:9
 msgid ""
-"Check if the nginx access log path exists. By default, this path is "
-"obtained from 'nginx -V'. If it cannot be obtained or the obtained path "
-"does not point to a valid, existing file, an error will be reported. In "
-"this case, you need to modify the configuration file to specify the access "
-"log path.Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#accesslogpath"
+"Check if the nginx access log path exists. By default, this path is obtained "
+"from 'nginx -V'. If it cannot be obtained or the obtained path does not "
+"point to a valid, existing file, an error will be reported. In this case, "
+"you need to modify the configuration file to specify the access log path."
+"Refer to the docs for more details: https://nginxui.com/zh_CN/guide/config-"
+"nginx.html#accesslogpath"
 msgstr ""
 "Проверьте, существует ли путь к журналу доступа nginx. По умолчанию этот "
 "путь получается из 'nginx -V'. Если его невозможно получить или полученный "
 "путь не указывает на действительный существующий файл, будет сообщена "
 "ошибка. В этом случае вам необходимо изменить файл конфигурации, чтобы "
-"указать путь к журналу доступа. Подробнее см. в документации: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#accesslogpath"
+"указать путь к журналу доступа. Подробнее см. в документации: https://"
+"nginxui.com/zh_CN/guide/config-nginx.html#accesslogpath"
 
 #: src/language/generate.ts:10
 msgid "Check if the nginx configuration directory exists"
@@ -1052,29 +1057,29 @@ msgid ""
 "from 'nginx -V'. If it cannot be obtained or the obtained path does not "
 "point to a valid, existing file, an error will be reported. In this case, "
 "you need to modify the configuration file to specify the error log path. "
-"Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#errorlogpath"
+"Refer to the docs for more details: https://nginxui.com/zh_CN/guide/config-"
+"nginx.html#errorlogpath"
 msgstr ""
-"Проверьте, существует ли путь к журналу ошибок nginx. По умолчанию этот "
-"путь получается из 'nginx -V'. Если его невозможно получить или полученный "
-"путь не указывает на действительный существующий файл, будет сообщена "
-"ошибка. В этом случае вам нужно изменить файл конфигурации, чтобы указать "
-"путь к журналу ошибок. Подробнее см. в документации: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#errorlogpath"
+"Проверьте, существует ли путь к журналу ошибок nginx. По умолчанию этот путь "
+"получается из 'nginx -V'. Если его невозможно получить или полученный путь "
+"не указывает на действительный существующий файл, будет сообщена ошибка. В "
+"этом случае вам нужно изменить файл конфигурации, чтобы указать путь к "
+"журналу ошибок. Подробнее см. в документации: https://nginxui.com/zh_CN/"
+"guide/config-nginx.html#errorlogpath"
 
 #: src/language/generate.ts:8
 msgid ""
 "Check if the nginx PID path exists. By default, this path is obtained from "
 "'nginx -V'. If it cannot be obtained, an error will be reported. In this "
 "case, you need to modify the configuration file to specify the Nginx PID "
-"path.Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#pidpath"
+"path.Refer to the docs for more details: https://nginxui.com/zh_CN/guide/"
+"config-nginx.html#pidpath"
 msgstr ""
-"Проверьте, существует ли путь к PID Nginx. По умолчанию этот путь "
-"получается из команды 'nginx -V'. Если его не удается получить, будет "
-"сообщена ошибка. В этом случае вам нужно изменить конфигурационный файл, "
-"чтобы указать путь к PID Nginx. Подробнее см. в документации: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#pidpath"
+"Проверьте, существует ли путь к PID Nginx. По умолчанию этот путь получается "
+"из команды 'nginx -V'. Если его не удается получить, будет сообщена ошибка. "
+"В этом случае вам нужно изменить конфигурационный файл, чтобы указать путь к "
+"PID Nginx. Подробнее см. в документации: https://nginxui.com/zh_CN/guide/"
+"config-nginx.html#pidpath"
 
 #: src/language/generate.ts:13
 msgid "Check if the nginx sbin path exists"
@@ -1102,8 +1107,8 @@ msgstr ""
 
 #: src/language/generate.ts:18
 msgid ""
-"Check if the streams-available and streams-enabled directories are under "
-"the nginx configuration directory"
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory"
 msgstr ""
 "Проверьте, находятся ли каталоги streams-available и streams-enabled в "
 "каталоге конфигурации nginx"
@@ -1546,7 +1551,7 @@ msgstr "Ежедневно в %{time}"
 #: src/routes/modules/dashboard.ts:10
 #: src/views/config/components/ConfigLeftPanel.vue:109
 #: src/views/config/components/ConfigLeftPanel.vue:159
-#: src/views/config/ConfigList.vue:69 src/views/nginx_log/NginxLog.vue:78
+#: src/views/config/ConfigList.vue:69 src/views/nginx_log/NginxLog.vue:93
 msgid "Dashboard"
 msgstr "Доска"
 
@@ -1915,7 +1920,8 @@ msgstr "Домен"
 
 #: src/views/certificate/components/AutoCertManagement.vue:55
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
-msgstr "Список доменов пуст, попробуйте заново создать авто-сертификат для %{config}"
+msgstr ""
+"Список доменов пуст, попробуйте заново создать авто-сертификат для %{config}"
 
 #: src/views/certificate/components/CertificateDownload.vue:94
 msgid "Download Certificate Files"
@@ -2156,8 +2162,8 @@ msgstr "Активировано успешно"
 #: src/views/preference/tabs/ServerSettings.vue:52
 msgid "Enables HTTP/2 support with multiplexing and server push capabilities"
 msgstr ""
-"Включает поддержку HTTP/2 с возможностью мультиплексирования и "
-"push-уведомлений сервера"
+"Включает поддержку HTTP/2 с возможностью мультиплексирования и push-"
+"уведомлений сервера"
 
 #: src/views/preference/tabs/ServerSettings.vue:56
 msgid "Enables HTTP/3 support based on QUIC protocol for best performance"
@@ -2240,8 +2246,8 @@ msgstr "Ошибка логирования"
 
 #: src/views/nginx_log/structured/StructuredLogViewer.vue:668
 msgid ""
-"Error logs do not support structured analysis as they contain free-form "
-"text messages."
+"Error logs do not support structured analysis as they contain free-form text "
+"messages."
 msgstr ""
 "Журналы ошибок не поддерживают структурированный анализ, так как содержат "
 "свободные текстовые сообщения."
@@ -2309,8 +2315,8 @@ msgstr ""
 
 #: src/views/certificate/ACMEUser.vue:92
 msgid ""
-"External Account Binding Key ID (optional). Required for some ACME "
-"providers like ZeroSSL."
+"External Account Binding Key ID (optional). Required for some ACME providers "
+"like ZeroSSL."
 msgstr ""
 "Идентификатор ключа внешней привязки аккаунта (опционально). Требуется "
 "некоторыми провайдерами ACME, такими как ZeroSSL."
@@ -2805,8 +2811,8 @@ msgid ""
 "Follow the instructions in the dialog to complete the passkey registration "
 "process."
 msgstr ""
-"Следуйте инструкциям в всплывающем окне, чтобы завершить процесс "
-"регистрации ключа доступа."
+"Следуйте инструкциям в всплывающем окне, чтобы завершить процесс регистрации "
+"ключа доступа."
 
 #: src/views/preference/tabs/NodeSettings.vue:42
 #: src/views/preference/tabs/NodeSettings.vue:54
@@ -2822,7 +2828,8 @@ msgid "For commercial or professional use, contact"
 msgstr "Для коммерческого или профессионального использования обратитесь"
 
 #: src/views/nginx_log/structured/StructuredLogViewer.vue:670
-msgid "For error logs, please use the Raw Log Viewer for better viewing experience."
+msgid ""
+"For error logs, please use the Raw Log Viewer for better viewing experience."
 msgstr ""
 "Для просмотра журналов ошибок используйте Raw Log Viewer для лучшего опыта "
 "просмотра."
@@ -2838,8 +2845,8 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:188
 msgid ""
-"For IP-based certificates, please specify the server IP address that will "
-"be included in the certificate."
+"For IP-based certificates, please specify the server IP address that will be "
+"included in the certificate."
 msgstr ""
 "Для сертификатов на основе IP укажите IP-адрес сервера, который будет "
 "включен в сертификат."
@@ -2960,8 +2967,7 @@ msgid ""
 "For testing, SSL validation is disabled by default."
 msgstr ""
 "gRPCS использует шифрование TLS. Сервер должен реализовать службу проверки "
-"работоспособности gRPC. Для тестирования проверка SSL по умолчанию "
-"отключена."
+"работоспособности gRPC. Для тестирования проверка SSL по умолчанию отключена."
 
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:67
 msgid "GZIP Compression"
@@ -3003,7 +3009,8 @@ msgstr "Высокий"
 
 #: src/views/dashboard/components/PerformanceStatisticsCard.vue:87
 msgid "Higher value means better connection reuse"
-msgstr "Более высокое значение означает лучшее повторное использование соединения"
+msgstr ""
+"Более высокое значение означает лучшее повторное использование соединения"
 
 #: src/views/config/components/ConfigLeftPanel.vue:254
 #: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:88
@@ -3065,7 +3072,8 @@ msgstr ""
 msgid ""
 "If you want to automatically revoke the old certificate, please enable this "
 "option."
-msgstr "Если вы хотите автоматически отозвать старый сертификат, включите эту опцию."
+msgstr ""
+"Если вы хотите автоматически отозвать старый сертификат, включите эту опцию."
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:142
 msgid ""
@@ -3077,7 +3085,8 @@ msgstr ""
 
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:76
 msgid "If your browser supports WebAuthn Passkey, a dialog box will appear."
-msgstr "Если ваш браузер поддерживает WebAuthn Passkey, появится диалоговое окно."
+msgstr ""
+"Если ваш браузер поддерживает WebAuthn Passkey, появится диалоговое окно."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:271
 msgid ""
@@ -3340,8 +3349,8 @@ msgid ""
 "Keep your recovery codes as safe as your password. We recommend saving them "
 "with a password manager."
 msgstr ""
-"Храните ваши коды восстановления так же безопасно, как и пароль. "
-"Рекомендуем сохранить их в менеджере паролей."
+"Храните ваши коды восстановления так же безопасно, как и пароль. Рекомендуем "
+"сохранить их в менеджере паролей."
 
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:60
 msgid "Keepalive Timeout"
@@ -3564,13 +3573,12 @@ msgstr "Журнал"
 #: src/language/generate.ts:23
 msgid ""
 "Log file %{log_path} is not a regular file. If you are using nginx-ui in "
-"docker container, please refer to "
-"https://nginxui.com/zh_CN/guide/config-nginx-log.html for more information."
+"docker container, please refer to https://nginxui.com/zh_CN/guide/config-"
+"nginx-log.html for more information."
 msgstr ""
 "Файл журнала %{log_path} не является обычным файлом. Если вы используете "
-"nginx-ui в контейнере Docker, обратитесь к "
-"https://nginxui.com/zh_CN/guide/config-nginx-log.html для получения "
-"дополнительной информации."
+"nginx-ui в контейнере Docker, обратитесь к https://nginxui.com/zh_CN/guide/"
+"config-nginx-log.html для получения дополнительной информации."
 
 #: src/constants/errors/nginx_log.ts:14
 msgid "Log file does not exist"
@@ -3622,18 +3630,18 @@ msgstr "Прокрутка"
 
 #: src/views/preference/tabs/LogrotateSettings.vue:13
 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."
+"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 ""
 "Logrotate по умолчанию включен в большинстве основных дистрибутивов Linux "
 "для пользователей, которые устанавливают Nginx UI на хост-машину, поэтому "
-"вам не нужно изменять параметры на этой странице. Для пользователей, "
-"которые устанавливают Nginx UI с использованием Docker-контейнеров, вы "
-"можете вручную включить эту опцию. Планировщик задач crontab Nginx UI будет "
+"вам не нужно изменять параметры на этой странице. Для пользователей, которые "
+"устанавливают Nginx UI с использованием Docker-контейнеров, вы можете "
+"вручную включить эту опцию. Планировщик задач crontab Nginx UI будет "
 "выполнять команду logrotate с интервалом, который вы установите в минутах."
 
 #: src/composables/useGeoTranslation.ts:166
@@ -4109,7 +4117,7 @@ msgstr "Nginx не работает в другом контейнере"
 msgid "Nginx is running"
 msgstr "Nginx работает"
 
-#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:61
+#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:76
 msgid "Nginx Log"
 msgstr "Журнал"
 
@@ -4190,8 +4198,8 @@ msgstr "Конфигурация Nginx UI была восстановлена"
 
 #: src/components/SystemRestore/SystemRestoreContent.vue:336
 msgid ""
-"Nginx UI configuration has been restored and will restart automatically in "
-"a few seconds."
+"Nginx UI configuration has been restored and will restart automatically in a "
+"few seconds."
 msgstr ""
 "Конфигурация Nginx UI была восстановлена и автоматически перезапустится "
 "через несколько секунд."
@@ -4704,8 +4712,8 @@ msgid ""
 "Please enable the stub_status module to get request statistics, connection "
 "count, etc."
 msgstr ""
-"Пожалуйста, включите модуль stub_status, чтобы получать статистику "
-"запросов, количество соединений и т. д."
+"Пожалуйста, включите модуль stub_status, чтобы получать статистику запросов, "
+"количество соединений и т. д."
 
 #: src/language/curd.ts:67
 msgid "Please enter"
@@ -4721,7 +4729,8 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:98
 msgid "Please enter a valid IPv4 address (0-255 per octet)"
-msgstr "Пожалуйста, введите действительный IPv4-адрес (0-255 для каждого октета)"
+msgstr ""
+"Пожалуйста, введите действительный IPv4-адрес (0-255 для каждого октета)"
 
 #: src/components/AutoCertForm/AutoCertForm.vue:109
 msgid "Please enter a valid IPv4 or IPv6 address"
@@ -4767,8 +4776,8 @@ msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "provider."
 msgstr ""
-"Пожалуйста, заполните учетные данные API, предоставленные вашим "
-"DNS-провайдером."
+"Пожалуйста, заполните учетные данные API, предоставленные вашим DNS-"
+"провайдером."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:168
 msgid ""
@@ -4782,8 +4791,8 @@ msgstr ""
 #: src/components/Notification/notifications.ts:194
 #: src/language/constants.ts:59
 msgid ""
-"Please generate new recovery codes in the preferences immediately to "
-"prevent lockout."
+"Please generate new recovery codes in the preferences immediately to prevent "
+"lockout."
 msgstr ""
 "Пожалуйста, немедленно сгенерируйте новые коды восстановления в настройках, "
 "чтобы избежать блокировки."
@@ -4831,7 +4840,8 @@ msgid "Please log in."
 msgstr "Пожалуйста, войдите в систему."
 
 #: src/views/certificate/DNSCredential.vue:102
-msgid "Please note that the unit of time configurations below are all in seconds."
+msgid ""
+"Please note that the unit of time configurations below are all in seconds."
 msgstr ""
 "Обратите внимание, что единица измерения времени в конфигурациях ниже "
 "указана в секундах."
@@ -4920,7 +4930,8 @@ msgstr "Частный CA:"
 
 #: src/components/AutoCertForm/AutoCertForm.vue:202
 msgid "Private IPs (192.168.x.x, 10.x.x.x, 172.16-31.x.x) will fail"
-msgstr "Частные IP-адреса (192.168.x.x, 10.x.x.x, 172.16-31.x.x) не пройдут проверку"
+msgstr ""
+"Частные IP-адреса (192.168.x.x, 10.x.x.x, 172.16-31.x.x) не пройдут проверку"
 
 #: src/views/certificate/components/CertificateFileUpload.vue:121
 #: src/views/certificate/components/CertificateFileUpload.vue:45
@@ -4970,12 +4981,10 @@ msgstr "Протокол"
 #: src/views/preference/tabs/ServerSettings.vue:47
 msgid ""
 "Protocol configuration only takes effect when directly connecting. If using "
-"reverse proxy, please configure the protocol separately in the reverse "
-"proxy."
+"reverse proxy, please configure the protocol separately in the reverse proxy."
 msgstr ""
 "Настройки протокола применяются только при прямом подключении. При "
-"использовании обратного прокси настройте протокол отдельно в обратном "
-"прокси."
+"использовании обратного прокси настройте протокол отдельно в обратном прокси."
 
 #: src/views/certificate/DNSCredential.vue:26
 msgid "Provider"
@@ -5028,7 +5037,7 @@ msgstr "В очереди на индексацию..."
 msgid "Quick Select"
 msgstr "Быстрый выбор"
 
-#: src/views/nginx_log/NginxLog.vue:79
+#: src/views/nginx_log/NginxLog.vue:94
 msgid "Raw"
 msgstr "Сырой"
 
@@ -5092,8 +5101,8 @@ msgid ""
 "Recovery codes are used to access your account when you lose access to your "
 "2FA device. Each code can only be used once."
 msgstr ""
-"Коды восстановления используются для доступа к аккаунту при утере "
-"2FA-устройства. Каждый код можно использовать только один раз."
+"Коды восстановления используются для доступа к аккаунту при утере 2FA-"
+"устройства. Каждый код можно использовать только один раз."
 
 #: src/views/preference/tabs/CertSettings.vue:40
 msgid "Recursive Nameservers"
@@ -5194,7 +5203,8 @@ msgstr "Удаленная перезагрузка Nginx успешно вып
 
 #: src/components/NamespaceTabs/NamespaceTabs.vue:85
 msgid "Reload request failed, please check your network connection"
-msgstr "Не удалось выполнить запрос на перезагрузку, проверьте подключение к сети"
+msgstr ""
+"Не удалось выполнить запрос на перезагрузку, проверьте подключение к сети"
 
 #: src/components/NginxControl/NginxControl.vue:77
 msgid "Reloading"
@@ -5641,7 +5651,7 @@ msgstr "Сохранено успешно"
 
 #: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
 #: src/views/certificate/components/RenewCert.vue:26
-#: src/views/preference/store/index.ts:123
+#: src/views/preference/store/index.ts:124
 msgid "Save successfully"
 msgstr "Сохранено успешно"
 
@@ -5831,7 +5841,8 @@ msgstr "Ошибка установки окружения: {0}"
 
 #: src/constants/errors/cert.ts:18
 msgid "Set env flag to disable lego CNAME support error: {0}"
-msgstr "Установка флага окружения для отключения поддержки CNAME в lego ошибка: {0}"
+msgstr ""
+"Установка флага окружения для отключения поддержки CNAME в lego ошибка: {0}"
 
 #: src/views/preference/tabs/CertSettings.vue:36
 msgid ""
@@ -5863,16 +5874,16 @@ msgstr "Настройки"
 
 #: src/constants/errors/nginx_log.ts:8
 msgid ""
-"Settings.NginxLogSettings.AccessLogPath is empty, refer to "
-"https://nginxui.com/guide/config-nginx.html for more information"
+"Settings.NginxLogSettings.AccessLogPath is empty, refer to https://nginxui."
+"com/guide/config-nginx.html for more information"
 msgstr ""
 "Settings.NginxLogSettings.AccessLogPath пуст, дополнительную информацию см. "
 "на https://nginxui.com/guide/config-nginx.html"
 
 #: src/constants/errors/nginx_log.ts:7
 msgid ""
-"Settings.NginxLogSettings.ErrorLogPath is empty, refer to "
-"https://nginxui.com/guide/config-nginx.html for more information"
+"Settings.NginxLogSettings.ErrorLogPath is empty, refer to https://nginxui."
+"com/guide/config-nginx.html for more information"
 msgstr ""
 "Settings.NginxLogSettings.ErrorLogPath пуст, дополнительную информацию см. "
 "на https://nginxui.com/guide/config-nginx.html"
@@ -6002,7 +6013,8 @@ msgstr "Содержимое SSL-сертификата"
 
 #: src/constants/errors/system.ts:8
 msgid "SSL certificate file must be under Nginx configuration directory: {0}"
-msgstr "Файл SSL-сертификата должен находиться в каталоге конфигурации Nginx: {0}"
+msgstr ""
+"Файл SSL-сертификата должен находиться в каталоге конфигурации Nginx: {0}"
 
 #: src/constants/errors/system.ts:6
 msgid "SSL certificate file not found"
@@ -6148,7 +6160,7 @@ msgstr "Каталог streams-available не существует"
 msgid "Streams-enabled directory not exist"
 msgstr "Каталог streams-enabled не существует"
 
-#: src/views/nginx_log/NginxLog.vue:77
+#: src/views/nginx_log/NginxLog.vue:92
 msgid "Structured"
 msgstr "Структурированный"
 
@@ -6174,14 +6186,14 @@ msgstr "Воскресенье"
 #: src/components/SelfCheck/tasks/frontend/websocket.ts:13
 msgid ""
 "Support communication with the backend through the WebSocket protocol. If "
-"your Nginx UI is being used via an Nginx reverse proxy, please refer to "
-"this link to write the corresponding configuration file: "
-"https://nginxui.com/guide/nginx-proxy-example.html"
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
 msgstr ""
 "Поддерживает связь с бэкендом через протокол WebSocket. Если ваш Nginx UI "
 "используется через обратный прокси Nginx, обратитесь к этой ссылке, чтобы "
-"написать соответствующий конфигурационный файл: "
-"https://nginxui.com/guide/nginx-proxy-example.html"
+"написать соответствующий конфигурационный файл: https://nginxui.com/guide/"
+"nginx-proxy-example.html"
 
 #: src/language/curd.ts:53 src/language/curd.ts:57
 msgid "Support single or batch upload of files"
@@ -6383,11 +6395,10 @@ msgstr "Введенные данные не являются ключом SSL 
 
 #: src/constants/errors/nginx_log.ts:2
 msgid ""
-"The log path is not under the paths in "
-"settings.NginxSettings.LogDirWhiteList"
+"The log path is not under the paths in settings.NginxSettings.LogDirWhiteList"
 msgstr ""
-"Путь к журналу не находится под путями в "
-"settings.NginxSettings.LogDirWhiteList"
+"Путь к журналу не находится под путями в settings.NginxSettings."
+"LogDirWhiteList"
 
 #: src/views/preference/tabs/OpenAISettings.vue:23
 #: src/views/preference/tabs/OpenAISettings.vue:89
@@ -6399,7 +6410,8 @@ msgstr ""
 "двоеточия и точки."
 
 #: src/views/preference/tabs/OpenAISettings.vue:90
-msgid "The model used for code completion, if not set, the chat model will be used."
+msgid ""
+"The model used for code completion, if not set, the chat model will be used."
 msgstr ""
 "Модель, используемая для завершения кода. Если не задана, будет "
 "использоваться чат-модель."
@@ -6447,8 +6459,8 @@ msgid ""
 "The server_name in the current configuration must be the domain name you "
 "need to get the certificate, supportmultiple domains."
 msgstr ""
-"server_name в текущей конфигурации должен быть доменным именем, для "
-"которого вам нужно получить сертификат, поддержка нескольких доменов."
+"server_name в текущей конфигурации должен быть доменным именем, для которого "
+"вам нужно получить сертификат, поддержка нескольких доменов."
 
 #: src/views/preference/tabs/CertSettings.vue:22
 #: src/views/preference/tabs/HTTPSettings.vue:14
@@ -6478,8 +6490,8 @@ msgid ""
 "your password and second factors. If you cannot find these codes, you will "
 "lose access to your account."
 msgstr ""
-"Эти коды являются последним средством для доступа к вашему аккаунту, если "
-"вы потеряете пароль и вторые факторы. Если вы не сможете найти эти коды, вы "
+"Эти коды являются последним средством для доступа к вашему аккаунту, если вы "
+"потеряете пароль и вторые факторы. Если вы не сможете найти эти коды, вы "
 "потеряете доступ к своему аккаунту."
 
 #: src/routes/modules/system.ts:45
@@ -6517,26 +6529,28 @@ msgid "This field should not be empty"
 msgstr "Это поле обязательно к заполнению"
 
 #: src/constants/form_errors.ts:6
-msgid "This field should only contain letters, unicode characters, numbers, and -_."
+msgid ""
+"This field should only contain letters, unicode characters, numbers, and -_."
 msgstr "Это поле должно содержать только буквы, символы Юникода, цифры и -_."
 
 #: src/language/curd.ts:46
 msgid ""
-"This field should only contain letters, unicode characters, numbers, and "
-"-_./:"
+"This field should only contain letters, unicode characters, numbers, and -"
+"_./:"
 msgstr "Это поле должно содержать только буквы, символы Unicode, цифры и -_./:"
 
 #: src/components/Notification/notifications.ts:94
 msgid "This is a test message sent at %{timestamp} from Nginx UI."
-msgstr "Это тестовое сообщение, отправленное по адресу %{timestamp} из nginx ui."
+msgstr ""
+"Это тестовое сообщение, отправленное по адресу %{timestamp} из nginx ui."
 
 #: src/views/dashboard/NginxDashBoard.vue:175
 msgid ""
 "This module provides Nginx request statistics, connection count, etc. data. "
 "After enabling it, you can view performance statistics"
 msgstr ""
-"Этот модуль предоставляет статистику запросов Nginx, количество соединений "
-"и другие данные. После включения вы сможете просматривать статистику "
+"Этот модуль предоставляет статистику запросов Nginx, количество соединений и "
+"другие данные. После включения вы сможете просматривать статистику "
 "производительности."
 
 #: src/views/nginx_log/dashboard/components/DateRangeSelector.vue:25
@@ -6557,18 +6571,18 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:128
 msgid ""
-"This site is configured as a default server (default_server) for HTTPS "
-"(port 443). IP certificates require Certificate Authority (CA) support and "
-"may not be available with all ACME providers."
+"This site is configured as a default server (default_server) for HTTPS (port "
+"443). IP certificates require Certificate Authority (CA) support and may not "
+"be available with all ACME providers."
 msgstr ""
 "Этот сайт настроен как сервер по умолчанию (default_server) для HTTPS (порт "
-"443). IP-сертификаты требуют поддержки Центра сертификации (CA) и могут "
-"быть недоступны у всех провайдеров ACME."
+"443). IP-сертификаты требуют поддержки Центра сертификации (CA) и могут быть "
+"недоступны у всех провайдеров ACME."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:132
 msgid ""
-"This site uses wildcard server name (_) which typically indicates an "
-"IP-based certificate. IP certificates require Certificate Authority (CA) "
+"This site uses wildcard server name (_) which typically indicates an IP-"
+"based certificate. IP certificates require Certificate Authority (CA) "
 "support and may not be available with all ACME providers."
 msgstr ""
 "Этот сайт использует подстановочное имя сервера (_), которое обычно "
@@ -6580,8 +6594,8 @@ msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
 msgstr ""
-"Этот токен будет показан только один раз и не может быть восстановлен "
-"позже. Пожалуйста, сохраните его в надежном месте."
+"Этот токен будет показан только один раз и не может быть восстановлен позже. "
+"Пожалуйста, сохраните его в надежном месте."
 
 #: src/constants/form_errors.ts:4 src/language/curd.ts:44
 msgid "This value is already taken"
@@ -6610,8 +6624,8 @@ msgid ""
 "This will restore all Nginx configuration files. Nginx will restart after "
 "the restoration is complete."
 msgstr ""
-"Это восстановит все конфигурационные файлы Nginx. Nginx перезапустится "
-"после завершения восстановления."
+"Это восстановит все конфигурационные файлы Nginx. Nginx перезапустится после "
+"завершения восстановления."
 
 #: src/components/SystemRestore/SystemRestoreContent.vue:238
 #: src/components/SystemRestore/SystemRestoreContent.vue:315
@@ -6623,7 +6637,8 @@ msgstr ""
 "после завершения восстановления."
 
 #: src/views/node/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 на %{nodeNames} до версии "
 "%{version}."
@@ -6690,15 +6705,15 @@ msgid ""
 "and restart Nginx UI."
 msgstr ""
 "Для обеспечения безопасности конфигурацию WebAuthn нельзя добавить через "
-"интерфейс. Пожалуйста, вручную настройте следующее в файле конфигурации "
-"app.ini и перезапустите Nginx UI."
+"интерфейс. Пожалуйста, вручную настройте следующее в файле конфигурации app."
+"ini и перезапустите Nginx UI."
 
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:34
 #: src/views/site/site_edit/components/EnableTLS/EnableTLS.vue:15
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
-"add a location which can proxy the request from authority to backend, and "
-"we need to save this file and reload the Nginx. Are you sure you want to "
+"add a location which can proxy the request from authority to backend, and we "
+"need to save this file and reload the Nginx. Are you sure you want to "
 "continue?"
 msgstr ""
 "Чтобы убедиться, что автоматическое обновление сертификата может работать "
@@ -6712,9 +6727,9 @@ msgid ""
 "provide an OpenAI-compatible API endpoint, so just set the baseUrl to your "
 "local API."
 msgstr ""
-"Чтобы использовать локальную большую модель, разверните её с помощью "
-"ollama, vllm или lmdeploy. Они предоставляют API-эндпоинт, совместимый с "
-"OpenAI, поэтому просто установите baseUrl на ваш локальный API."
+"Чтобы использовать локальную большую модель, разверните её с помощью ollama, "
+"vllm или lmdeploy. Они предоставляют API-эндпоинт, совместимый с OpenAI, "
+"поэтому просто установите baseUrl на ваш локальный API."
 
 #: src/views/dashboard/NginxDashBoard.vue:57
 msgid "Toggle failed"
@@ -7116,10 +7131,10 @@ msgid ""
 "Encrypt cannot issue certificates for private IPs. Use a public IP address "
 "or consider using a private CA."
 msgstr ""
-"Предупреждение: Похоже, это частный IP-адрес. Публичные центры "
-"сертификации, такие как Let's Encrypt, не могут выдавать сертификаты для "
-"частных IP. Используйте публичный IP-адрес или рассмотрите возможность "
-"использования частного центра сертификации."
+"Предупреждение: Похоже, это частный IP-адрес. Публичные центры сертификации, "
+"такие как Let's Encrypt, не могут выдавать сертификаты для частных IP. "
+"Используйте публичный IP-адрес или рассмотрите возможность использования "
+"частного центра сертификации."
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:259
 msgid "We do not accept any feature requests"
@@ -7135,11 +7150,11 @@ msgstr ""
 
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:142
 msgid ""
-"We will remove the HTTPChallenge configuration from this file and reload "
-"the Nginx. Are you sure you want to continue?"
+"We will remove the HTTPChallenge configuration from this file and reload the "
+"Nginx. Are you sure you want to continue?"
 msgstr ""
-"Мы удалим конфигурацию HTTPChallenge из этого файла и перезагрузим Nginx. "
-"Вы уверены, что хотите продолжить?"
+"Мы удалим конфигурацию HTTPChallenge из этого файла и перезагрузим Nginx. Вы "
+"уверены, что хотите продолжить?"
 
 #: src/views/preference/tabs/AuthSettings.vue:62
 msgid "Webauthn"
@@ -7180,8 +7195,8 @@ msgid ""
 "Pebble as CA."
 msgstr ""
 "При включении Nginx UI будет автоматически перерегистрировать пользователей "
-"при запуске. Обычно не включайте эту функцию, если только вы не находитесь "
-"в среде разработки и используете Pebble в качестве CA."
+"при запуске. Обычно не включайте эту функцию, если только вы не находитесь в "
+"среде разработки и используете Pebble в качестве CA."
 
 #: src/views/site/site_edit/components/RightPanel/Basic.vue:62
 #: src/views/stream/components/RightPanel/Basic.vue:57
@@ -7263,8 +7278,8 @@ msgstr "Да"
 
 #: src/views/terminal/Terminal.vue:200
 msgid ""
-"You are accessing this terminal over an insecure HTTP connection on a "
-"non-localhost domain. This may expose sensitive information."
+"You are accessing this terminal over an insecure HTTP connection on a non-"
+"localhost domain. This may expose sensitive information."
 msgstr ""
 "Вы обращаетесь к этому терминалу через небезопасное HTTP-соединение в "
 "домене, отличном от localhost. Это может раскрыть конфиденциальную "
@@ -7294,10 +7309,12 @@ msgstr "Теперь вы можете закрыть это диалогово
 msgid ""
 "You have not configured the settings of Webauthn, so you cannot add a "
 "passkey."
-msgstr "Вы не настроили параметры WebAuthn, поэтому не можете добавить ключ доступа."
+msgstr ""
+"Вы не настроили параметры WebAuthn, поэтому не можете добавить ключ доступа."
 
 #: src/views/preference/components/AuthSettings/RecoveryCodes.vue:82
-msgid "You have not enabled 2FA yet. Please enable 2FA to generate recovery codes."
+msgid ""
+"You have not enabled 2FA yet. Please enable 2FA to generate recovery codes."
 msgstr ""
 "Вы еще не включили двухфакторную аутентификацию. Пожалуйста, включите её, "
 "чтобы сгенерировать коды восстановления."
@@ -7389,7 +7406,8 @@ msgstr "Конвейер без выделения памяти"
 #~ msgstr "Стабильная производительность на современном оборудовании"
 
 #~ msgid "Backup path not in granted access paths: {0}"
-#~ msgstr "Путь резервного копирования не входит в разрешенные пути доступа: {0}"
+#~ msgstr ""
+#~ "Путь резервного копирования не входит в разрешенные пути доступа: {0}"
 
 #~ msgid "Build with"
 #~ msgstr "Собрать с"
@@ -7404,7 +7422,8 @@ msgstr "Конвейер без выделения памяти"
 #~ msgstr "Не удалось оценить символические ссылки: {0}"
 
 #~ msgid "Failed to restore Nginx UI files: {0}"
-#~ msgstr "Не удалось восстановить файлы пользовательского интерфейса Nginx: {0}"
+#~ msgstr ""
+#~ "Не удалось восстановить файлы пользовательского интерфейса Nginx: {0}"
 
 #~ msgid "Failed to write to zip buffer: {0}"
 #~ msgstr "Ошибка записи в ZIP-буфер: {0}"
@@ -7475,14 +7494,14 @@ msgstr "Конвейер без выделения памяти"
 
 #~ msgid ""
 #~ "Support communication with the backend through the Server-Sent Events "
-#~ "protocol. If your Nginx UI is being used via an Nginx reverse proxy, please "
-#~ "refer to this link to write the corresponding configuration file: "
+#~ "protocol. If your Nginx UI is being used via an Nginx reverse proxy, "
+#~ "please refer to this link to write the corresponding configuration file: "
 #~ "https://nginxui.com/guide/nginx-proxy-example.html"
 #~ msgstr ""
 #~ "Поддержка связи с бэкендом через протокол Server-Sent Events. Если ваш "
 #~ "Nginx UI используется через обратный прокси Nginx, обратитесь к этой "
-#~ "ссылке, чтобы написать соответствующий конфигурационный файл: "
-#~ "https://nginxui.com/guide/nginx-proxy-example.html"
+#~ "ссылке, чтобы написать соответствующий конфигурационный файл: https://"
+#~ "nginxui.com/guide/nginx-proxy-example.html"
 
 #~ msgid "If left blank, the default CA Dir will be used."
 #~ msgstr "Если оставить пустым, будет использоваться каталог CA по умолчанию."
@@ -7564,8 +7583,8 @@ msgstr "Конвейер без выделения памяти"
 
 #~ msgid ""
 #~ "Check if /var/run/docker.sock exists. If you are using Nginx UI Official "
-#~ "Docker Image, please make sure the docker socket is mounted like this: `-v "
-#~ "/var/run/docker.sock:/var/run/docker.sock`."
+#~ "Docker Image, please make sure the docker socket is mounted like this: `-"
+#~ "v /var/run/docker.sock:/var/run/docker.sock`."
 #~ msgstr ""
 #~ "Проверьте, существует ли /var/run/docker.sock. Если вы используете "
 #~ "официальный образ Docker Nginx UI, убедитесь, что сокет Docker подключен "
@@ -7660,13 +7679,16 @@ msgstr "Конвейер без выделения памяти"
 #~ "Синхронизация конфигурации %{cert_name} с %{env_name} не удалась, "
 #~ "пожалуйста, обновите удаленный Nginx UI до последней версии"
 
-#~ msgid "Rename %{orig_path} to %{new_path} on %{env_name} failed, response: %{resp}"
-#~ msgstr ""
-#~ "Переименование %{orig_path} в %{new_path} на %{env_name} не удалось, ответ: "
+#~ msgid ""
+#~ "Rename %{orig_path} to %{new_path} on %{env_name} failed, response: "
 #~ "%{resp}"
+#~ msgstr ""
+#~ "Переименование %{orig_path} в %{new_path} на %{env_name} не удалось, "
+#~ "ответ: %{resp}"
 
 #, fuzzy
-#~ msgid "Rename Site %{site} to %{new_site} on %{node} error, response: %{resp}"
+#~ msgid ""
+#~ "Rename Site %{site} to %{new_site} on %{node} error, response: %{resp}"
 #~ msgstr "Переименование %{orig_path} в %{new_path} на %{env_name} успешно"
 
 #, fuzzy
@@ -7682,19 +7704,20 @@ msgstr "Конвейер без выделения памяти"
 #~ "Синхронизация сертификата %{cert_name} с %{env_name} не удалась, "
 #~ "пожалуйста, обновите удаленный интерфейс Nginx до последней версии"
 
-#~ msgid "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
+#~ msgid ""
+#~ "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
 #~ msgstr ""
 #~ "Синхронизация сертификата %{cert_name} с %{env_name} не удалась, ответ: "
 #~ "%{resp}"
 
 #~ msgid "Sync config %{config_name} to %{env_name} failed, response: %{resp}"
 #~ msgstr ""
-#~ "Синхронизация конфигурации %{config_name} с %{env_name} не удалась, ответ: "
-#~ "%{resp}"
+#~ "Синхронизация конфигурации %{config_name} с %{env_name} не удалась, "
+#~ "ответ: %{resp}"
 
 #~ msgid ""
-#~ "If you lose your mobile phone, you can use the recovery code to reset your "
-#~ "2FA."
+#~ "If you lose your mobile phone, you can use the recovery code to reset "
+#~ "your 2FA."
 #~ msgstr ""
 #~ "Если вы потеряете свой мобильный телефон, вы можете использовать код "
 #~ "восстановления для сброса 2FA."
@@ -7702,10 +7725,11 @@ msgstr "Конвейер без выделения памяти"
 #~ msgid "Incorrect username or password"
 #~ msgstr "Неверное имя пользователя или пароль"
 
-#~ msgid "The recovery code is only displayed once, please save it in a safe place."
+#~ msgid ""
+#~ "The recovery code is only displayed once, please save it in a safe place."
 #~ msgstr ""
-#~ "Код восстановления отображается только один раз, пожалуйста, сохраните его "
-#~ "в безопасном месте."
+#~ "Код восстановления отображается только один раз, пожалуйста, сохраните "
+#~ "его в безопасном месте."
 
 #~ msgid "Too many login failed attempts, please try again later"
 #~ msgstr "Слишком много неудачных попыток входа, попробуйте позже"

+ 227 - 194
app/src/language/tr_TR/app.po

@@ -5,22 +5,24 @@ msgstr ""
 "POT-Creation-Date: \n"
 "PO-Revision-Date: 2025-04-08 18:26+0000\n"
 "Last-Translator: Ulaş <ozturkmuratulas@hotmail.com>\n"
-"Language-Team: Turkish "
-"<https://weblate.nginxui.com/projects/nginx-ui/frontend/tr/>\n"
+"Language-Team: Turkish <https://weblate.nginxui.com/projects/nginx-ui/"
+"frontend/tr/>\n"
 "Language: tr_TR\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
 "X-Generator: Weblate 5.10.4\n"
 
 #: src/language/generate.ts:35
 msgid "[Nginx UI] ACME User: %{name}, Email: %{email}, CA Dir: %{caDir}"
-msgstr "[Nginx UI] ACME Kullanıcısı: %{name}, E-posta: %{email}, CA Dizini: %{caDir}"
+msgstr ""
+"[Nginx UI] ACME Kullanıcısı: %{name}, E-posta: %{email}, CA Dizini: %{caDir}"
 
 #: src/language/generate.ts:36
 msgid "[Nginx UI] Backing up current certificate for later revocation"
-msgstr "[Nginx UI] Geçerli sertifika daha sonra iptal edilmek üzere yedekleniyor"
+msgstr ""
+"[Nginx UI] Geçerli sertifika daha sonra iptal edilmek üzere yedekleniyor"
 
 #: src/language/generate.ts:37
 msgid "[Nginx UI] Certificate renewed successfully"
@@ -31,14 +33,16 @@ msgid "[Nginx UI] Certificate successfully revoked"
 msgstr "[Nginx UI] Sertifika başarıyla iptal edildi"
 
 #: src/language/generate.ts:39
-msgid "[Nginx UI] Certificate was used for server, reloading server TLS certificate"
+msgid ""
+"[Nginx UI] Certificate was used for server, reloading server TLS certificate"
 msgstr ""
 "[Nginx UI] Sertifika sunucu için kullanıldı, sunucu TLS sertifikası yeniden "
 "yükleniyor"
 
 #: src/language/generate.ts:40
 msgid "[Nginx UI] Creating client facilitates communication with the CA server"
-msgstr "[Nginx UI] CA sunucusu ile iletişimi kolaylaştırmak için istemci oluşturma"
+msgstr ""
+"[Nginx UI] CA sunucusu ile iletişimi kolaylaştırmak için istemci oluşturma"
 
 #: src/language/generate.ts:41
 msgid "[Nginx UI] Environment variables cleaned"
@@ -107,15 +111,16 @@ msgstr "{label} panoya kopyalandı"
 
 #: src/components/SyncNodesPreview/SyncNodesPreview.vue:59
 msgid "* Includes nodes from group %{groupName} and manually selected nodes"
-msgstr "* %{groupName} grubundan düğümler ve manuel olarak seçilen düğümler içerir"
+msgstr ""
+"* %{groupName} grubundan düğümler ve manuel olarak seçilen düğümler içerir"
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:141
 msgid ""
-"* Index files are stored in the \"log-index\" directory within your Nginx "
-"UI config path by default."
+"* Index files are stored in the \"log-index\" directory within your Nginx UI "
+"config path by default."
 msgstr ""
-"* Dizin dosyaları varsayılan olarak NGINX UI yapılandırma yolunuzdaki "
-"\"log-index\" dizininde saklanır."
+"* Dizin dosyaları varsayılan olarak NGINX UI yapılandırma yolunuzdaki \"log-"
+"index\" dizininde saklanır."
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:176
 msgid ""
@@ -274,7 +279,8 @@ msgstr "Gelişmiş Mod"
 
 #: src/views/nginx_log/NginxLogList.vue:391
 msgid "Advanced indexing enabled but failed to start rebuild"
-msgstr "Gelişmiş indeksleme etkinleştirildi ancak yeniden oluşturma başlatılamadı"
+msgstr ""
+"Gelişmiş indeksleme etkinleştirildi ancak yeniden oluşturma başlatılamadı"
 
 #: src/views/nginx_log/NginxLogList.vue:382
 msgid "Advanced indexing enabled successfully"
@@ -300,7 +306,8 @@ msgstr "Gelişmiş Ayarlar"
 
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:105
 msgid "Afterwards, refresh this page and click add passkey again."
-msgstr "Daha sonra bu sayfayı yenileyin ve tekrar parola anahtarı ekle'ye tıklayın."
+msgstr ""
+"Daha sonra bu sayfayı yenileyin ve tekrar parola anahtarı ekle'ye tıklayın."
 
 #: src/components/NamespaceTabs/NamespaceTabs.vue:116
 msgid "All"
@@ -332,7 +339,8 @@ msgid "Any"
 msgstr "Herhangi"
 
 #: src/components/AutoCertForm/AutoCertForm.vue:209
-msgid "Any reachable IP address can be used with private Certificate Authorities"
+msgid ""
+"Any reachable IP address can be used with private Certificate Authorities"
 msgstr ""
 "Herhangi bir erişilebilir IP adresi, özel Sertifika Yetkilileri ile "
 "kullanılabilir"
@@ -498,7 +506,7 @@ msgstr "Otomatik Yedekleme Başarısız"
 msgid "Auto Backup Storage Failed"
 msgstr "Otomatik Yedekleme Depolama Başarısız"
 
-#: src/views/nginx_log/NginxLog.vue:86 src/views/node/Node.vue:164
+#: src/views/nginx_log/NginxLog.vue:101 src/views/node/Node.vue:164
 msgid "Auto Refresh"
 msgstr "Otomatik Yenileme"
 
@@ -550,7 +558,7 @@ msgstr "Ort./PV"
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
-#: src/views/nginx_log/NginxLog.vue:114
+#: src/views/nginx_log/NginxLog.vue:129
 #: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:170
 #: src/views/stream/components/StreamEditor.vue:134
 msgid "Back"
@@ -573,8 +581,8 @@ msgid ""
 "Background indexing in progress. Data will be updated automatically when "
 "ready."
 msgstr ""
-"Arka planda indeksleme devam ediyor. Veriler hazır olduğunda otomatik "
-"olarak güncellenecektir."
+"Arka planda indeksleme devam ediyor. Veriler hazır olduğunda otomatik olarak "
+"güncellenecektir."
 
 #: src/constants/errors/nginx_log.ts:17
 msgid "Background log service not available"
@@ -586,7 +594,8 @@ msgstr "Yedekleme"
 
 #: src/components/SystemRestore/SystemRestoreContent.vue:155
 msgid "Backup file integrity check failed, it may have been tampered with"
-msgstr "Yedek dosya bütünlük kontrolü başarısız oldu, dosya değiştirilmiş olabilir"
+msgstr ""
+"Yedek dosya bütünlük kontrolü başarısız oldu, dosya değiştirilmiş olabilir"
 
 #: src/constants/errors/backup.ts:38
 msgid "Backup file not found: {0}"
@@ -618,13 +627,15 @@ msgstr "Yedekleme Zamanlaması"
 
 #: src/components/Notification/notifications.ts:38
 msgid "Backup task %{backup_name} completed successfully, file: %{file_path}"
-msgstr "Yedekleme görevi %{backup_name} başarıyla tamamlandı, dosya: %{file_path}"
+msgstr ""
+"Yedekleme görevi %{backup_name} başarıyla tamamlandı, dosya: %{file_path}"
 
 #: src/components/Notification/notifications.ts:34
-msgid "Backup task %{backup_name} failed during storage upload, error: %{error}"
+msgid ""
+"Backup task %{backup_name} failed during storage upload, error: %{error}"
 msgstr ""
-"Yedekleme görevi %{backup_name} depolama yüklemesi sırasında başarısız "
-"oldu, hata: %{error}"
+"Yedekleme görevi %{backup_name} depolama yüklemesi sırasında başarısız oldu, "
+"hata: %{error}"
 
 #: src/components/Notification/notifications.ts:30
 msgid "Backup task %{backup_name} failed to execute, error: %{error}"
@@ -683,7 +694,8 @@ msgstr "Toplu Yükseltme"
 
 #: src/language/curd.ts:38
 msgid "Belows are selected items that you want to batch modify"
-msgstr "Aşağıda toplu olarak değiştirmek istediğiniz seçili öğeler bulunmaktadır"
+msgstr ""
+"Aşağıda toplu olarak değiştirmek istediğiniz seçili öğeler bulunmaktadır"
 
 #: src/constants/errors/nginx.ts:3
 msgid "Block is nil"
@@ -978,25 +990,25 @@ msgstr "Favicon'ı kontrol et"
 #: src/language/generate.ts:6
 msgid ""
 "Check if /var/run/docker.sock exists. If you are using Nginx UI Official "
-"Docker Image, please make sure the docker socket is mounted like this: `-v "
-"/var/run/docker.sock:/var/run/docker.sock`. Nginx UI official image uses "
-"/var/run/docker.sock to communicate with the host Docker Engine via Docker "
-"Client API. This feature is used to control Nginx in another container and "
-"perform container replacement rather than binary replacement during OTA "
-"upgrades of Nginx UI to ensure container dependencies are also upgraded. If "
-"you don't need this feature, please add the environment variable "
+"Docker Image, please make sure the docker socket is mounted like this: `-v /"
+"var/run/docker.sock:/var/run/docker.sock`. Nginx UI official image uses /var/"
+"run/docker.sock to communicate with the host Docker Engine via Docker Client "
+"API. This feature is used to control Nginx in another container and perform "
+"container replacement rather than binary replacement during OTA upgrades of "
+"Nginx UI to ensure container dependencies are also upgraded. If you don't "
+"need this feature, please add the environment variable "
 "NGINX_UI_IGNORE_DOCKER_SOCKET=true to the container."
 msgstr ""
 "/var/run/docker.sock dosyasının var olup olmadığını kontrol edin. Nginx UI "
 "Resmi Docker Image'ını kullanıyorsanız, docker soketinin şu şekilde "
 "bağlandığından emin olun: `-v /var/run/docker.sock:/var/run/docker.sock`. "
 "Nginx UI resmi imajı, Docker Client API üzerinden ana bilgisayarın Docker "
-"Engine'i ile iletişim kurmak için /var/run/docker.sock kullanır. Bu "
-"özellik, Nginx UI'nin OTA güncellemeleri sırasında ikili dosya değişimi "
-"yerine konteyner değişimi yaparak Nginx'i başka bir konteynerde kontrol "
-"etmek ve konteyner bağımlılıklarının da güncellenmesini sağlamak için "
-"kullanılır. Bu özelliğe ihtiyacınız yoksa, konteynere "
-"NGINX_UI_IGNORE_DOCKER_SOCKET=true ortam değişkenini ekleyin."
+"Engine'i ile iletişim kurmak için /var/run/docker.sock kullanır. Bu özellik, "
+"Nginx UI'nin OTA güncellemeleri sırasında ikili dosya değişimi yerine "
+"konteyner değişimi yaparak Nginx'i başka bir konteynerde kontrol etmek ve "
+"konteyner bağımlılıklarının da güncellenmesini sağlamak için kullanılır. Bu "
+"özelliğe ihtiyacınız yoksa, konteynere NGINX_UI_IGNORE_DOCKER_SOCKET=true "
+"ortam değişkenini ekleyin."
 
 #: src/components/SelfCheck/tasks/frontend/https-check.ts:14
 msgid ""
@@ -1008,26 +1020,27 @@ msgstr ""
 "engeller"
 
 #: src/language/generate.ts:7
-msgid "Check if the conf.d directory is under the nginx configuration directory"
+msgid ""
+"Check if the conf.d directory is under the nginx configuration directory"
 msgstr ""
 "conf.d dizininin nginx yapılandırma dizini altında olup olmadığını kontrol "
 "edin"
 
 #: src/language/generate.ts:9
 msgid ""
-"Check if the nginx access log path exists. By default, this path is "
-"obtained from 'nginx -V'. If it cannot be obtained or the obtained path "
-"does not point to a valid, existing file, an error will be reported. In "
-"this case, you need to modify the configuration file to specify the access "
-"log path.Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#accesslogpath"
+"Check if the nginx access log path exists. By default, this path is obtained "
+"from 'nginx -V'. If it cannot be obtained or the obtained path does not "
+"point to a valid, existing file, an error will be reported. In this case, "
+"you need to modify the configuration file to specify the access log path."
+"Refer to the docs for more details: https://nginxui.com/zh_CN/guide/config-"
+"nginx.html#accesslogpath"
 msgstr ""
 "Nginx erişim günlüğü yolunun var olup olmadığını kontrol edin. Varsayılan "
 "olarak bu yol 'nginx -V' komutu ile alınır. Eğer alınamazsa veya alınan yol "
 "geçerli, mevcut bir dosyayı işaret etmiyorsa bir hata bildirilecektir. Bu "
 "durumda, erişim günlüğü yolunu belirtmek için yapılandırma dosyasını "
-"değiştirmeniz gerekmektedir. Daha fazla bilgi için belgelere bakın: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#accesslogpath"
+"değiştirmeniz gerekmektedir. Daha fazla bilgi için belgelere bakın: https://"
+"nginxui.com/zh_CN/guide/config-nginx.html#accesslogpath"
 
 #: src/language/generate.ts:10
 msgid "Check if the nginx configuration directory exists"
@@ -1043,29 +1056,29 @@ msgid ""
 "from 'nginx -V'. If it cannot be obtained or the obtained path does not "
 "point to a valid, existing file, an error will be reported. In this case, "
 "you need to modify the configuration file to specify the error log path. "
-"Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#errorlogpath"
+"Refer to the docs for more details: https://nginxui.com/zh_CN/guide/config-"
+"nginx.html#errorlogpath"
 msgstr ""
 "Nginx hata günlüğü yolunun var olup olmadığını kontrol edin. Varsayılan "
 "olarak bu yol 'nginx -V' komutuyla alınır. Eğer alınamazsa veya alınan yol "
 "geçerli, mevcut bir dosyayı işaret etmiyorsa bir hata bildirilir. Bu "
-"durumda, yapılandırma dosyasını değiştirerek hata günlüğü yolunu "
-"belirtmeniz gerekir. Daha fazla ayrıntı için belgelere bakın: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#errorlogpath"
+"durumda, yapılandırma dosyasını değiştirerek hata günlüğü yolunu belirtmeniz "
+"gerekir. Daha fazla ayrıntı için belgelere bakın: https://nginxui.com/zh_CN/"
+"guide/config-nginx.html#errorlogpath"
 
 #: src/language/generate.ts:8
 msgid ""
 "Check if the nginx PID path exists. By default, this path is obtained from "
 "'nginx -V'. If it cannot be obtained, an error will be reported. In this "
 "case, you need to modify the configuration file to specify the Nginx PID "
-"path.Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#pidpath"
+"path.Refer to the docs for more details: https://nginxui.com/zh_CN/guide/"
+"config-nginx.html#pidpath"
 msgstr ""
-"Nginx PID yolunun var olup olmadığını kontrol edin. Varsayılan olarak bu "
-"yol 'nginx -V' komutuyla alınır. Alınamazsa bir hata bildirilir. Bu "
-"durumda, yapılandırma dosyasını değiştirerek Nginx PID yolunu belirtmeniz "
-"gerekir. Daha fazla ayrıntı için belgelere bakın: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#pidpath"
+"Nginx PID yolunun var olup olmadığını kontrol edin. Varsayılan olarak bu yol "
+"'nginx -V' komutuyla alınır. Alınamazsa bir hata bildirilir. Bu durumda, "
+"yapılandırma dosyasını değiştirerek Nginx PID yolunu belirtmeniz gerekir. "
+"Daha fazla ayrıntı için belgelere bakın: https://nginxui.com/zh_CN/guide/"
+"config-nginx.html#pidpath"
 
 #: src/language/generate.ts:13
 msgid "Check if the nginx sbin path exists"
@@ -1078,8 +1091,7 @@ msgstr "nginx.conf dosyasının conf.d dizinini içerip içermediğini kontrol e
 #: src/language/generate.ts:15
 msgid "Check if the nginx.conf includes the sites-enabled directory"
 msgstr ""
-"nginx.conf dosyasının sites-enabled dizinini içerip içermediğini kontrol "
-"edin"
+"nginx.conf dosyasının sites-enabled dizinini içerip içermediğini kontrol edin"
 
 #: src/language/generate.ts:16
 msgid "Check if the nginx.conf includes the streams-enabled directory"
@@ -1097,8 +1109,8 @@ msgstr ""
 
 #: src/language/generate.ts:18
 msgid ""
-"Check if the streams-available and streams-enabled directories are under "
-"the nginx configuration directory"
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory"
 msgstr ""
 "streams-available ve streams-enabled dizinlerinin nginx yapılandırma dizini "
 "altında olup olmadığını kontrol edin"
@@ -1399,8 +1411,7 @@ msgstr "CPU Kullanımı"
 #: src/views/dashboard/components/ResourceUsageCard.vue:38
 msgid "CPU usage is relatively high, consider optimizing Nginx configuration"
 msgstr ""
-"CPU kullanımı nispeten yüksek, Nginx yapılandırmasını optimize etmeyi "
-"düşünün"
+"CPU kullanımı nispeten yüksek, Nginx yapılandırmasını optimize etmeyi düşünün"
 
 #: src/views/dashboard/ServerAnalytic.vue:199
 msgid "CPU:"
@@ -1538,7 +1549,7 @@ msgstr "Günlük olarak saat %{time}"
 #: src/routes/modules/dashboard.ts:10
 #: src/views/config/components/ConfigLeftPanel.vue:109
 #: src/views/config/components/ConfigLeftPanel.vue:159
-#: src/views/config/ConfigList.vue:69 src/views/nginx_log/NginxLog.vue:78
+#: src/views/config/ConfigList.vue:69 src/views/nginx_log/NginxLog.vue:93
 msgid "Dashboard"
 msgstr "Kontrol Paneli"
 
@@ -1794,11 +1805,13 @@ msgstr "%{node} üzerindeki %{name} sitesi başarıyla devre dışı bırakıld
 
 #: src/components/Notification/notifications.ts:130
 msgid "Disable site %{name} maintenance on %{node} failed"
-msgstr "%{node} üzerindeki %{name} sitesi bakımını devre dışı bırakma başarısız oldu"
+msgstr ""
+"%{node} üzerindeki %{name} sitesi bakımını devre dışı bırakma başarısız oldu"
 
 #: src/components/Notification/notifications.ts:134
 msgid "Disable site %{name} maintenance on %{node} successfully"
-msgstr "%{name} sitesinin bakımı %{node} üzerinde başarıyla devre dışı bırakıldı"
+msgstr ""
+"%{name} sitesinin bakımı %{node} üzerinde başarıyla devre dışı bırakıldı"
 
 #: src/components/Notification/notifications.ts:162
 msgid "Disable stream %{name} from %{node} failed"
@@ -2156,8 +2169,7 @@ msgstr "Çoklama ve sunucu itme özellikleri ile HTTP/2 desteğini etkinleştiri
 #: src/views/preference/tabs/ServerSettings.vue:56
 msgid "Enables HTTP/3 support based on QUIC protocol for best performance"
 msgstr ""
-"En iyi performans için QUIC protokolüne dayalı HTTP/3 desteğini "
-"etkinleştirir"
+"En iyi performans için QUIC protokolüne dayalı HTTP/3 desteğini etkinleştirir"
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:85
 msgid ""
@@ -2165,9 +2177,9 @@ msgid ""
 "resources including CPU and memory. Please ensure your system meets the "
 "minimum requirements before proceeding."
 msgstr ""
-"Gelişmiş günlük indekslemeyi etkinleştirmek, CPU ve bellek dahil olmak "
-"üzere önemli hesaplama kaynakları tüketecektir. Devam etmeden önce "
-"sisteminizin minimum gereksinimleri karşıladığından emin olun."
+"Gelişmiş günlük indekslemeyi etkinleştirmek, CPU ve bellek dahil olmak üzere "
+"önemli hesaplama kaynakları tüketecektir. Devam etmeden önce sisteminizin "
+"minimum gereksinimleri karşıladığından emin olun."
 
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:76
 msgid "Encrypt website with Let's Encrypt"
@@ -2234,11 +2246,11 @@ msgstr "Hata Günlükleri"
 
 #: src/views/nginx_log/structured/StructuredLogViewer.vue:668
 msgid ""
-"Error logs do not support structured analysis as they contain free-form "
-"text messages."
+"Error logs do not support structured analysis as they contain free-form text "
+"messages."
 msgstr ""
-"Hata günlükleri serbest metin mesajları içerdiğinden yapılandırılmış "
-"analizi desteklemez."
+"Hata günlükleri serbest metin mesajları içerdiğinden yapılandırılmış analizi "
+"desteklemez."
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:233
 msgid "Error pattern recognition"
@@ -2303,8 +2315,8 @@ msgstr ""
 
 #: src/views/certificate/ACMEUser.vue:92
 msgid ""
-"External Account Binding Key ID (optional). Required for some ACME "
-"providers like ZeroSSL."
+"External Account Binding Key ID (optional). Required for some ACME providers "
+"like ZeroSSL."
 msgstr ""
 "Harici Hesap Bağlama Anahtar Kimliği (isteğe bağlı). ZeroSSL gibi bazı ACME "
 "sağlayıcıları için gereklidir."
@@ -2816,7 +2828,8 @@ msgid "For commercial or professional use, contact"
 msgstr "Ticari veya profesyonel kullanım için iletişime geçin"
 
 #: src/views/nginx_log/structured/StructuredLogViewer.vue:670
-msgid "For error logs, please use the Raw Log Viewer for better viewing experience."
+msgid ""
+"For error logs, please use the Raw Log Viewer for better viewing experience."
 msgstr ""
 "Hata günlükleri için daha iyi bir görüntüleme deneyimi için Ham Günlük "
 "Görüntüleyici'yi kullanın."
@@ -2832,8 +2845,8 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:188
 msgid ""
-"For IP-based certificates, please specify the server IP address that will "
-"be included in the certificate."
+"For IP-based certificates, please specify the server IP address that will be "
+"included in the certificate."
 msgstr ""
 "IP tabanlı sertifikalar için, sertifikaya dahil edilecek sunucu IP adresini "
 "belirtiniz."
@@ -2945,8 +2958,8 @@ msgid ""
 "gRPC health check requires server to implement gRPC Health Check service "
 "(grpc.health.v1.Health)."
 msgstr ""
-"gRPC sağlık kontrolü, sunucunun gRPC Sağlık Kontrol hizmetini "
-"(grpc.health.v1.Health) uygulamasını gerektirir."
+"gRPC sağlık kontrolü, sunucunun gRPC Sağlık Kontrol hizmetini (grpc.health."
+"v1.Health) uygulamasını gerektirir."
 
 #: src/views/dashboard/components/SiteHealthCheckModal.vue:501
 msgid ""
@@ -2996,7 +3009,8 @@ msgstr "Yüksek"
 
 #: src/views/dashboard/components/PerformanceStatisticsCard.vue:87
 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/site/site_edit/components/SiteEditor/SiteEditor.vue:88
@@ -3561,12 +3575,12 @@ msgstr "Günlük"
 #: src/language/generate.ts:23
 msgid ""
 "Log file %{log_path} is not a regular file. If you are using nginx-ui in "
-"docker container, please refer to "
-"https://nginxui.com/zh_CN/guide/config-nginx-log.html for more information."
+"docker container, please refer to https://nginxui.com/zh_CN/guide/config-"
+"nginx-log.html for more information."
 msgstr ""
 "Günlük dosyası %{log_path} normal bir dosya değil. Docker konteynerinde "
-"nginx-ui kullanıyorsanız, daha fazla bilgi için "
-"https://nginxui.com/zh_CN/guide/config-nginx-log.html adresine bakın."
+"nginx-ui kullanıyorsanız, daha fazla bilgi için https://nginxui.com/zh_CN/"
+"guide/config-nginx-log.html adresine bakın."
 
 #: src/constants/errors/nginx_log.ts:14
 msgid "Log file does not exist"
@@ -3618,19 +3632,19 @@ msgstr "Logrotate"
 
 #: src/views/preference/tabs/LogrotateSettings.vue:13
 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 ""
-"Logrotate, varsayılan olarak, Nginx UI'yi ana makineye yükleyen "
-"kullanıcılar için çoğu ana akım Linux dağıtımında etkinleştirilmiştir, bu "
-"yüzden bu sayfadaki parametreleri değiştirmenize gerek yoktur. Nginx UI'yi "
-"Docker konteynerlerini kullanarak yükleyen kullanıcılar, bu seçeneği manuel "
-"olarak etkinleştirebilir. Nginx UI'nin crontab görev zamanlayıcısı, "
-"belirlediğiniz dakika aralığında logrotate komutunu çalıştıracaktır."
+"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 ""
+"Logrotate, varsayılan olarak, Nginx UI'yi ana makineye yükleyen kullanıcılar "
+"için çoğu ana akım Linux dağıtımında etkinleştirilmiştir, bu yüzden bu "
+"sayfadaki parametreleri değiştirmenize gerek yoktur. Nginx UI'yi Docker "
+"konteynerlerini kullanarak yükleyen kullanıcılar, bu seçeneği manuel olarak "
+"etkinleştirebilir. Nginx UI'nin crontab görev zamanlayıcısı, belirlediğiniz "
+"dakika aralığında logrotate komutunu çalıştıracaktır."
 
 #: src/composables/useGeoTranslation.ts:166
 #: src/views/nginx_log/dashboard/components/ChinaMapChart/ChinaMapChart.vue:135
@@ -4105,7 +4119,7 @@ msgstr "Nginx başka bir konteynerde çalışmıyor"
 msgid "Nginx is running"
 msgstr "Nginx çalışıyor"
 
-#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:61
+#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:76
 msgid "Nginx Log"
 msgstr "Nginx Günlüğü"
 
@@ -4186,8 +4200,8 @@ msgstr "Nginx UI yapılandırması geri yüklendi"
 
 #: src/components/SystemRestore/SystemRestoreContent.vue:336
 msgid ""
-"Nginx UI configuration has been restored and will restart automatically in "
-"a few seconds."
+"Nginx UI configuration has been restored and will restart automatically in a "
+"few seconds."
 msgstr ""
 "Nginx UI yapılandırması geri yüklendi ve birkaç saniye içinde otomatik "
 "olarak yeniden başlatılacak."
@@ -4710,8 +4724,8 @@ msgid ""
 "Please enter a name for the passkey you wish to create and click the OK "
 "button below."
 msgstr ""
-"Oluşturmak istediğiniz anahtar için bir ad girin ve aşağıdaki Tamam "
-"butonuna tıklayın."
+"Oluşturmak istediğiniz anahtar için bir ad girin ve aşağıdaki Tamam butonuna "
+"tıklayın."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:98
 msgid "Please enter a valid IPv4 address (0-255 per octet)"
@@ -4758,7 +4772,8 @@ msgstr "Lütfen gerekli S3 yapılandırma alanlarını doldurun"
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "provider."
-msgstr "Lütfen DNS sağlayıcınız tarafından sağlanan API kimlik bilgilerini doldurun."
+msgstr ""
+"Lütfen DNS sağlayıcınız tarafından sağlanan API kimlik bilgilerini doldurun."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:168
 msgid ""
@@ -4772,11 +4787,10 @@ msgstr ""
 #: src/components/Notification/notifications.ts:194
 #: src/language/constants.ts:59
 msgid ""
-"Please generate new recovery codes in the preferences immediately to "
-"prevent lockout."
+"Please generate new recovery codes in the preferences immediately to prevent "
+"lockout."
 msgstr ""
-"Kilitlenmeyi önlemek için tercihlerden hemen yeni kurtarma kodları "
-"oluşturun."
+"Kilitlenmeyi önlemek için tercihlerden hemen yeni kurtarma kodları oluşturun."
 
 #: src/views/config/components/ConfigRightPanel/Basic.vue:27
 #: src/views/config/components/Rename.vue:65
@@ -4821,7 +4835,8 @@ msgid "Please log in."
 msgstr "Lütfen giriş yapın."
 
 #: src/views/certificate/DNSCredential.vue:102
-msgid "Please note that the unit of time configurations below are all in seconds."
+msgid ""
+"Please note that the unit of time configurations below are all in seconds."
 msgstr ""
 "Lütfen aşağıdaki zaman yapılandırmalarının birimlerinin saniye cinsinden "
 "olduğunu unutmayın."
@@ -4960,8 +4975,7 @@ msgstr "Protokol"
 #: src/views/preference/tabs/ServerSettings.vue:47
 msgid ""
 "Protocol configuration only takes effect when directly connecting. If using "
-"reverse proxy, please configure the protocol separately in the reverse "
-"proxy."
+"reverse proxy, please configure the protocol separately in the reverse proxy."
 msgstr ""
 "Protokol yapılandırması yalnızca doğrudan bağlantı sırasında geçerlidir. "
 "Ters proxy kullanıyorsanız, protokolü ters proxyda ayrı olarak yapılandırın."
@@ -5017,7 +5031,7 @@ msgstr "Dizine ekleme için sırada..."
 msgid "Quick Select"
 msgstr "Hızlı Seçim"
 
-#: src/views/nginx_log/NginxLog.vue:79
+#: src/views/nginx_log/NginxLog.vue:94
 msgid "Raw"
 msgstr "Ham"
 
@@ -5183,7 +5197,8 @@ msgstr "Uzak Nginx Yeniden Yükleme Başarılı"
 
 #: src/components/NamespaceTabs/NamespaceTabs.vue:85
 msgid "Reload request failed, please check your network connection"
-msgstr "Yeniden yükleme isteği başarısız oldu, lütfen ağ bağlantınızı kontrol edin"
+msgstr ""
+"Yeniden yükleme isteği başarısız oldu, lütfen ağ bağlantınızı kontrol edin"
 
 #: src/components/NginxControl/NginxControl.vue:77
 msgid "Reloading"
@@ -5262,7 +5277,8 @@ msgstr "Uzak Akış Yeniden Adlandırma Başarılı"
 
 #: src/components/Notification/notifications.ts:138
 msgid "Rename site %{name} to %{new_name} on %{node} failed"
-msgstr "%{node} üzerinde %{name} sitesi %{new_name} olarak yeniden adlandırılamadı"
+msgstr ""
+"%{node} üzerinde %{name} sitesi %{new_name} olarak yeniden adlandırılamadı"
 
 #: src/components/Notification/notifications.ts:142
 msgid "Rename site %{name} to %{new_name} on %{node} successfully"
@@ -5362,8 +5378,8 @@ msgid ""
 "shared library memory, which will be repeated calculated for multiple "
 "processes"
 msgstr ""
-"Resident Set Size: Fiziksel bellekte gerçekte bulunan bellek, paylaşılan "
-"tüm kütüphane belleğini içerir ve birden fazla işlem için tekrar hesaplanır"
+"Resident Set Size: Fiziksel bellekte gerçekte bulunan bellek, paylaşılan tüm "
+"kütüphane belleğini içerir ve birden fazla işlem için tekrar hesaplanır"
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:84
 msgid "Resource Usage Warning"
@@ -5401,7 +5417,8 @@ msgstr "Uzak Nginx Yeniden Başlatma Başarılı"
 
 #: src/components/NamespaceTabs/NamespaceTabs.vue:105
 msgid "Restart request failed, please check your network connection"
-msgstr "Yeniden başlatma isteği başarısız oldu, lütfen ağ bağlantınızı kontrol edin"
+msgstr ""
+"Yeniden başlatma isteği başarısız oldu, lütfen ağ bağlantınızı kontrol edin"
 
 #: src/components/NginxControl/NginxControl.vue:82
 msgid "Restarting"
@@ -5639,7 +5656,7 @@ msgstr "Başarıyla kaydedildi"
 
 #: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
 #: src/views/certificate/components/RenewCert.vue:26
-#: src/views/preference/store/index.ts:123
+#: src/views/preference/store/index.ts:124
 msgid "Save successfully"
 msgstr "Başarıyla kaydedildi"
 
@@ -5744,8 +5761,8 @@ msgid ""
 "Select a predefined CA directory or enter a custom one. Leave blank to use "
 "the default CA directory."
 msgstr ""
-"Önceden tanımlanmış bir CA dizini seçin veya özel bir tane girin. "
-"Varsayılan CA dizinini kullanmak için boş bırakın."
+"Önceden tanımlanmış bir CA dizini seçin veya özel bir tane girin. Varsayılan "
+"CA dizinini kullanmak için boş bırakın."
 
 #: src/language/curd.ts:31
 msgid "Select all"
@@ -5828,8 +5845,8 @@ msgstr "Ortam ayarlama hatası: {0}"
 #: src/constants/errors/cert.ts:18
 msgid "Set env flag to disable lego CNAME support error: {0}"
 msgstr ""
-"Lego CNAME desteğini devre dışı bırakmak için ortam bayrağı ayarlama "
-"hatası: {0}"
+"Lego CNAME desteğini devre dışı bırakmak için ortam bayrağı ayarlama hatası: "
+"{0}"
 
 #: src/views/preference/tabs/CertSettings.vue:36
 msgid ""
@@ -5861,19 +5878,19 @@ msgstr "Ayarlar"
 
 #: src/constants/errors/nginx_log.ts:8
 msgid ""
-"Settings.NginxLogSettings.AccessLogPath is empty, refer to "
-"https://nginxui.com/guide/config-nginx.html for more information"
+"Settings.NginxLogSettings.AccessLogPath is empty, refer to https://nginxui."
+"com/guide/config-nginx.html for more information"
 msgstr ""
-"Settings.NginxLogSettings.AccessLogPath boş, daha fazla bilgi için "
-"https://nginxui.com/guide/config-nginx.html adresine bakın"
+"Settings.NginxLogSettings.AccessLogPath boş, daha fazla bilgi için https://"
+"nginxui.com/guide/config-nginx.html adresine bakın"
 
 #: src/constants/errors/nginx_log.ts:7
 msgid ""
-"Settings.NginxLogSettings.ErrorLogPath is empty, refer to "
-"https://nginxui.com/guide/config-nginx.html for more information"
+"Settings.NginxLogSettings.ErrorLogPath is empty, refer to https://nginxui."
+"com/guide/config-nginx.html for more information"
 msgstr ""
-"Settings.NginxLogSettings.ErrorLogPath boş, daha fazla bilgi için "
-"https://nginxui.com/guide/config-nginx.html adresine bakın"
+"Settings.NginxLogSettings.ErrorLogPath boş, daha fazla bilgi için https://"
+"nginxui.com/guide/config-nginx.html adresine bakın"
 
 #: src/views/install/components/InstallView.vue:65
 msgid "Setup your Nginx UI"
@@ -6146,7 +6163,7 @@ msgstr "Streams-available dizini mevcut değil"
 msgid "Streams-enabled directory not exist"
 msgstr "Streams-enabled dizini mevcut değil"
 
-#: src/views/nginx_log/NginxLog.vue:77
+#: src/views/nginx_log/NginxLog.vue:92
 msgid "Structured"
 msgstr "Yapılandırılmış"
 
@@ -6172,14 +6189,14 @@ msgstr "Pazar"
 #: src/components/SelfCheck/tasks/frontend/websocket.ts:13
 msgid ""
 "Support communication with the backend through the WebSocket protocol. If "
-"your Nginx UI is being used via an Nginx reverse proxy, please refer to "
-"this link to write the corresponding configuration file: "
-"https://nginxui.com/guide/nginx-proxy-example.html"
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
 msgstr ""
 "WebSocket protokolü aracılığıyla backend ile iletişimi destekler. Nginx "
 "UI'nız bir Nginx ters proxy üzerinden kullanılıyorsa, ilgili yapılandırma "
-"dosyasını yazmak için bu bağlantıya bakın: "
-"https://nginxui.com/guide/nginx-proxy-example.html"
+"dosyasını yazmak için bu bağlantıya bakın: https://nginxui.com/guide/nginx-"
+"proxy-example.html"
 
 #: src/language/curd.ts:53 src/language/curd.ts:57
 msgid "Support single or batch upload of files"
@@ -6238,7 +6255,8 @@ msgstr "Yapılandırma %{config_name}, %{node_name} ile eşleştirilemedi"
 
 #: src/components/Notification/notifications.ts:74
 msgid "Sync config %{config_name} to %{node_name} successfully"
-msgstr "Yapılandırma %{config_name}, %{node_name} ile başarıyla senkronize edildi"
+msgstr ""
+"Yapılandırma %{config_name}, %{node_name} ile başarıyla senkronize edildi"
 
 #: src/components/Notification/notifications.ts:69 src/language/constants.ts:45
 msgid "Sync Config Error"
@@ -6381,8 +6399,7 @@ msgstr "Girdi bir SSL Sertifika Anahtarı değil"
 
 #: src/constants/errors/nginx_log.ts:2
 msgid ""
-"The log path is not under the paths in "
-"settings.NginxSettings.LogDirWhiteList"
+"The log path is not under the paths in settings.NginxSettings.LogDirWhiteList"
 msgstr ""
 "Günlük yolu, settings.NginxSettings.LogDirWhiteList içindeki yolların "
 "altında değil"
@@ -6397,7 +6414,8 @@ msgstr ""
 "iki nokta üst üste ve noktalar içermelidir."
 
 #: src/views/preference/tabs/OpenAISettings.vue:90
-msgid "The model used for code completion, if not set, the chat model will be used."
+msgid ""
+"The model used for code completion, if not set, the chat model will be used."
 msgstr ""
 "Kod tamamlama için kullanılan model. Ayarlanmamışsa sohbet modeli "
 "kullanılacaktır."
@@ -6515,18 +6533,22 @@ msgid "This field should not be empty"
 msgstr "Bu alan boş bırakılamaz"
 
 #: src/constants/form_errors.ts:6
-msgid "This field should only contain letters, unicode characters, numbers, and -_."
-msgstr "Bu alan yalnızca harfler, Unicode karakterler, sayılar ve -_ içermelidir."
+msgid ""
+"This field should only contain letters, unicode characters, numbers, and -_."
+msgstr ""
+"Bu alan yalnızca harfler, Unicode karakterler, sayılar ve -_ içermelidir."
 
 #: src/language/curd.ts:46
 msgid ""
-"This field should only contain letters, unicode characters, numbers, and "
-"-_./:"
-msgstr "Bu alan yalnızca harfler, Unicode karakterler, sayılar ve -_./: içermelidir"
+"This field should only contain letters, unicode characters, numbers, and -"
+"_./:"
+msgstr ""
+"Bu alan yalnızca harfler, Unicode karakterler, sayılar ve -_./: içermelidir"
 
 #: src/components/Notification/notifications.ts:94
 msgid "This is a test message sent at %{timestamp} from Nginx UI."
-msgstr "Bu, Nginx UI'den %{Timestamp} adresinden gönderilen bir test mesajıdır."
+msgstr ""
+"Bu, Nginx UI'den %{Timestamp} adresinden gönderilen bir test mesajıdır."
 
 #: src/views/dashboard/NginxDashBoard.vue:175
 msgid ""
@@ -6554,9 +6576,9 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:128
 msgid ""
-"This site is configured as a default server (default_server) for HTTPS "
-"(port 443). IP certificates require Certificate Authority (CA) support and "
-"may not be available with all ACME providers."
+"This site is configured as a default server (default_server) for HTTPS (port "
+"443). IP certificates require Certificate Authority (CA) support and may not "
+"be available with all ACME providers."
 msgstr ""
 "Bu site, HTTPS (port 443) için varsayılan sunucu (default_server) olarak "
 "yapılandırılmıştır. IP sertifikaları, Sertifika Yetkilisi (CA) desteği "
@@ -6564,8 +6586,8 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:132
 msgid ""
-"This site uses wildcard server name (_) which typically indicates an "
-"IP-based certificate. IP certificates require Certificate Authority (CA) "
+"This site uses wildcard server name (_) which typically indicates an IP-"
+"based certificate. IP certificates require Certificate Authority (CA) "
 "support and may not be available with all ACME providers."
 msgstr ""
 "Bu site, genellikle IP tabanlı bir sertifikayı gösteren joker karakterli "
@@ -6577,8 +6599,8 @@ msgid ""
 "This token will only be shown once and cannot be retrieved later. Please "
 "make sure to save it in a secure location."
 msgstr ""
-"Bu token yalnızca bir kez gösterilecek ve daha sonra alınamayacaktır. "
-"Lütfen güvenli bir yerde sakladığınızdan emin olun."
+"Bu token yalnızca bir kez gösterilecek ve daha sonra alınamayacaktır. Lütfen "
+"güvenli bir yerde sakladığınızdan emin olun."
 
 #: src/constants/form_errors.ts:4 src/language/curd.ts:44
 msgid "This value is already taken"
@@ -6599,7 +6621,8 @@ msgstr ""
 
 #: src/views/nginx_log/indexing/IndexManagement.vue:56
 msgid "This will rebuild the index data for this specific file: %{path}"
-msgstr "Bu, bu belirli dosya için indeks verilerini yeniden oluşturacaktır: %{path}"
+msgstr ""
+"Bu, bu belirli dosya için indeks verilerini yeniden oluşturacaktır: %{path}"
 
 #: src/components/SystemRestore/SystemRestoreContent.vue:227
 #: src/components/SystemRestore/SystemRestoreContent.vue:304
@@ -6620,7 +6643,8 @@ msgstr ""
 "yükleme tamamlandıktan sonra Nginx UI yeniden başlatılacaktır."
 
 #: src/views/node/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 ""
 "Bu işlem, %{nodeNames} üzerindeki Nginx UI'yi %{version} sürümüne "
 "yükseltecek veya yeniden yükleyecektir."
@@ -6687,15 +6711,15 @@ msgid ""
 "and restart Nginx UI."
 msgstr ""
 "Güvenliği sağlamak için WebAuthn yapılandırması kullanıcı arayüzü üzerinden "
-"eklenemez. Lütfen app.ini yapılandırma dosyasına aşağıdakileri manuel "
-"olarak ekleyin ve Nginx UI'ı yeniden başlatın."
+"eklenemez. Lütfen app.ini yapılandırma dosyasına aşağıdakileri manuel olarak "
+"ekleyin ve Nginx UI'ı yeniden başlatın."
 
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:34
 #: src/views/site/site_edit/components/EnableTLS/EnableTLS.vue:15
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
-"add a location which can proxy the request from authority to backend, and "
-"we need to save this file and reload the Nginx. Are you sure you want to "
+"add a location which can proxy the request from authority to backend, and we "
+"need to save this file and reload the Nginx. Are you sure you want to "
 "continue?"
 msgstr ""
 "Sertifikanın otomatik yenilenmesinin düzgün çalışmasını sağlamak için, "
@@ -7131,8 +7155,8 @@ msgstr ""
 
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:142
 msgid ""
-"We will remove the HTTPChallenge configuration from this file and reload "
-"the Nginx. Are you sure you want to continue?"
+"We will remove the HTTPChallenge configuration from this file and reload the "
+"Nginx. Are you sure you want to continue?"
 msgstr ""
 "Bu dosyadan HTTPChallenge yapılandırmasını kaldıracağız ve Nginx'i yeniden "
 "yükleyeceğiz. Devam etmek istediğinizden emin misiniz?"
@@ -7259,8 +7283,8 @@ msgstr "Evet"
 
 #: src/views/terminal/Terminal.vue:200
 msgid ""
-"You are accessing this terminal over an insecure HTTP connection on a "
-"non-localhost domain. This may expose sensitive information."
+"You are accessing this terminal over an insecure HTTP connection on a non-"
+"localhost domain. This may expose sensitive information."
 msgstr ""
 "Bu terminale, localhost olmayan bir alan adında güvenli olmayan bir HTTP "
 "bağlantısı üzerinden erişiyorsunuz. Bu, hassas bilgilerin açığa çıkmasına "
@@ -7295,7 +7319,8 @@ msgstr ""
 "ekleyemezsiniz."
 
 #: src/views/preference/components/AuthSettings/RecoveryCodes.vue:82
-msgid "You have not enabled 2FA yet. Please enable 2FA to generate recovery codes."
+msgid ""
+"You have not enabled 2FA yet. Please enable 2FA to generate recovery codes."
 msgstr ""
 "Henüz 2FA'yı etkinleştirmediniz. Kurtarma kodları oluşturmak için lütfen "
 "2FA'yı etkinleştirin."
@@ -7309,8 +7334,8 @@ msgid ""
 "Your current recovery code might be outdated and insecure. Please generate "
 "new recovery codes at your earliest convenience to ensure security."
 msgstr ""
-"Mevcut kurtarma kodunuz güncel olmayabilir ve güvenli olmayabilir. "
-"Güvenliği sağlamak için en kısa sürede yeni kurtarma kodları oluşturun."
+"Mevcut kurtarma kodunuz güncel olmayabilir ve güvenli olmayabilir. Güvenliği "
+"sağlamak için en kısa sürede yeni kurtarma kodları oluşturun."
 
 #: src/views/preference/components/AuthSettings/RecoveryCodes.vue:143
 #: src/views/preference/components/AuthSettings/RecoveryCodes.vue:156
@@ -7472,14 +7497,14 @@ msgstr "Tahsisatsız boru hattı"
 
 #~ msgid ""
 #~ "Support communication with the backend through the Server-Sent Events "
-#~ "protocol. If your Nginx UI is being used via an Nginx reverse proxy, please "
-#~ "refer to this link to write the corresponding configuration file: "
+#~ "protocol. If your Nginx UI is being used via an Nginx reverse proxy, "
+#~ "please refer to this link to write the corresponding configuration file: "
 #~ "https://nginxui.com/guide/nginx-proxy-example.html"
 #~ msgstr ""
 #~ "Backend ile iletişimi Server-Sent Events protokolü üzerinden destekler. "
 #~ "Nginx UI'nız bir Nginx ters proxy üzerinden kullanılıyorsa, ilgili "
-#~ "yapılandırma dosyasını yazmak için bu bağlantıya bakın: "
-#~ "https://nginxui.com/guide/nginx-proxy-example.html"
+#~ "yapılandırma dosyasını yazmak için bu bağlantıya bakın: https://nginxui."
+#~ "com/guide/nginx-proxy-example.html"
 
 #~ msgid "If left blank, the default CA Dir will be used."
 #~ msgstr "Boş bırakılırsa, varsayılan CA Dir kullanılır."
@@ -7561,11 +7586,11 @@ msgstr "Tahsisatsız boru hattı"
 
 #~ msgid ""
 #~ "Check if /var/run/docker.sock exists. If you are using Nginx UI Official "
-#~ "Docker Image, please make sure the docker socket is mounted like this: `-v "
-#~ "/var/run/docker.sock:/var/run/docker.sock`."
+#~ "Docker Image, please make sure the docker socket is mounted like this: `-"
+#~ "v /var/run/docker.sock:/var/run/docker.sock`."
 #~ msgstr ""
-#~ "/var/run/docker.sock dosyasının var olup olmadığını kontrol edin. Nginx UI "
-#~ "Resmi Docker Image'ını kullanıyorsanız, docker soketinin şu şekilde "
+#~ "/var/run/docker.sock dosyasının var olup olmadığını kontrol edin. Nginx "
+#~ "UI Resmi Docker Image'ını kullanıyorsanız, docker soketinin şu şekilde "
 #~ "bağlandığından emin olun: `-v /var/run/docker.sock:/var/run/docker.sock`."
 
 #~ msgid "Check if the nginx access log path exists"
@@ -7620,7 +7645,8 @@ msgstr "Tahsisatsız boru hattı"
 #~ msgstr "Yeniden Başlatma"
 
 #~ msgid "Deploy %{conf_name} to %{node_name} successfully"
-#~ msgstr "%{conf_name} yapılandırması başarıyla %{node_name} düğümüne dağıtıldı"
+#~ msgstr ""
+#~ "%{conf_name} yapılandırması başarıyla %{node_name} düğümüne dağıtıldı"
 
 #~ msgid "Deploy successfully"
 #~ msgstr "Başarıyla Dağıtıldı"
@@ -7628,8 +7654,8 @@ msgstr "Tahsisatsız boru hattı"
 #, fuzzy
 #~ msgid "Disable site %{site} on %{node} error, response: %{resp}"
 #~ msgstr ""
-#~ "%{conf_name} yapılandırmasını %{node_name} düğümünde etkinleştirme başarılı "
-#~ "oldu"
+#~ "%{conf_name} yapılandırmasını %{node_name} düğümünde etkinleştirme "
+#~ "başarılı oldu"
 
 #~ msgid "Do you want to deploy this file to remote server?"
 #~ msgid_plural "Do you want to deploy this file to remote servers?"
@@ -7644,8 +7670,8 @@ msgstr "Tahsisatsız boru hattı"
 
 #~ msgid "Enable %{conf_name} in %{node_name} successfully"
 #~ msgstr ""
-#~ "%{conf_name} yapılandırmasını %{node_name} düğümünde etkinleştirme başarılı "
-#~ "oldu"
+#~ "%{conf_name} yapılandırmasını %{node_name} düğümünde etkinleştirme "
+#~ "başarılı oldu"
 
 #~ msgid "Enable successfully"
 #~ msgstr "Başarıyla etkinleştirildi"
@@ -7657,14 +7683,18 @@ msgstr "Tahsisatsız boru hattı"
 #~ "Nginx kullanıcı arayüzünü en son sürüme yükseltin"
 
 #, fuzzy
-#~ msgid "Rename %{orig_path} to %{new_path} on %{env_name} failed, response: %{resp}"
+#~ msgid ""
+#~ "Rename %{orig_path} to %{new_path} on %{env_name} failed, response: "
+#~ "%{resp}"
 #~ msgstr ""
-#~ "2] üzerinde %{orig_path}'ı %{new_path} olarak yeniden adlandırma başarısız "
-#~ "oldu, yanıt: %{resp}"
+#~ "2] üzerinde %{orig_path}'ı %{new_path} olarak yeniden adlandırma "
+#~ "başarısız oldu, yanıt: %{resp}"
 
 #, fuzzy
-#~ msgid "Rename Site %{site} to %{new_site} on %{node} error, response: %{resp}"
-#~ msgstr "2] üzerinde %{orig_path}'ı %{new_path} olarak başarıyla yeniden adlandırın"
+#~ msgid ""
+#~ "Rename Site %{site} to %{new_site} on %{node} error, response: %{resp}"
+#~ msgstr ""
+#~ "2] üzerinde %{orig_path}'ı %{new_path} olarak başarıyla yeniden adlandırın"
 
 #, fuzzy
 #~ msgid "Save site %{site} to %{node} error, response: %{resp}"
@@ -7681,7 +7711,8 @@ msgstr "Tahsisatsız boru hattı"
 #~ "lütfen uzak Nginx kullanıcı arayüzünü en son sürüme yükseltin"
 
 #, fuzzy
-#~ msgid "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
+#~ msgid ""
+#~ "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
 #~ msgstr ""
 #~ "Sertifika %{cert_name} ile %{env_name} arasında senkronizasyon başarısız "
 #~ "oldu, yanıt: %{resp}"
@@ -7696,8 +7727,8 @@ msgstr "Tahsisatsız boru hattı"
 #~ msgstr "Tarayamıyor musunuz? Metin anahtar bağlamasını kullanın"
 
 #~ msgid ""
-#~ "If you lose your mobile phone, you can use the recovery code to reset your "
-#~ "2FA."
+#~ "If you lose your mobile phone, you can use the recovery code to reset "
+#~ "your 2FA."
 #~ msgstr ""
 #~ "Cep telefonunuzu kaybederseniz, 2FA'nızı sıfırlamak için kurtarma kodunu "
 #~ "kullanabilirsiniz."
@@ -7706,7 +7737,8 @@ msgstr "Tahsisatsız boru hattı"
 #~ msgstr "Kullanıcı adı veya şifre yanlış"
 
 #, fuzzy
-#~ msgid "The recovery code is only displayed once, please save it in a safe place."
+#~ msgid ""
+#~ "The recovery code is only displayed once, please save it in a safe place."
 #~ msgstr ""
 #~ "Kurtarma kodu yalnızca bir kez görüntülenir, lütfen güvenli bir yere "
 #~ "kaydedin."
@@ -7722,8 +7754,9 @@ msgstr "Tahsisatsız boru hattı"
 #~ "Rename %{orig_path} to %{new_path} on %{env_name} failed, please upgrade "
 #~ "the remote Nginx UI to the latest version"
 #~ msgstr ""
-#~ "2] üzerinde %{orig_path}'ı %{new_path} olarak yeniden adlandırmak başarısız "
-#~ "oldu, lütfen uzak Nginx kullanıcı arayüzünü en son sürüme yükseltin"
+#~ "2] üzerinde %{orig_path}'ı %{new_path} olarak yeniden adlandırmak "
+#~ "başarısız oldu, lütfen uzak Nginx kullanıcı arayüzünü en son sürüme "
+#~ "yükseltin"
 
 #~ msgid ""
 #~ "Customize the name of local server to be displayed in the environment "

+ 216 - 196
app/src/language/uk_UA/app.po

@@ -4,11 +4,11 @@ msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "PO-Revision-Date: 2025-04-16 15:12+0000\n"
 "Last-Translator: sergio_from_tauri <dedysobr@gmail.com>\n"
-"Language-Team: Ukrainian "
-"<https://weblate.nginxui.com/projects/nginx-ui/frontend/uk/>\n"
+"Language-Team: Ukrainian <https://weblate.nginxui.com/projects/nginx-ui/"
+"frontend/uk/>\n"
 "Language: uk_UA\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
 "n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
@@ -17,8 +17,8 @@ msgstr ""
 #: src/language/generate.ts:35
 msgid "[Nginx UI] ACME User: %{name}, Email: %{email}, CA Dir: %{caDir}"
 msgstr ""
-"[Nginx UI] Користувач ACME: %{name}, Електронна пошта: %{email}, Каталог "
-"CA: %{caDir}"
+"[Nginx UI] Користувач ACME: %{name}, Електронна пошта: %{email}, Каталог CA: "
+"%{caDir}"
 
 #: src/language/generate.ts:36
 msgid "[Nginx UI] Backing up current certificate for later revocation"
@@ -35,10 +35,11 @@ msgid "[Nginx UI] Certificate successfully revoked"
 msgstr "[Nginx UI] Сертифікат успішно відкликано"
 
 #: src/language/generate.ts:39
-msgid "[Nginx UI] Certificate was used for server, reloading server TLS certificate"
+msgid ""
+"[Nginx UI] Certificate was used for server, reloading server TLS certificate"
 msgstr ""
-"[Nginx UI] Сертифікат використовувався для сервера, перезавантаження "
-"TLS-сертифіката сервера"
+"[Nginx UI] Сертифікат використовувався для сервера, перезавантаження TLS-"
+"сертифіката сервера"
 
 #: src/language/generate.ts:40
 msgid "[Nginx UI] Creating client facilitates communication with the CA server"
@@ -115,8 +116,8 @@ msgstr "* Включає вузли з групи %{groupName} та вузли,
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:141
 msgid ""
-"* Index files are stored in the \"log-index\" directory within your Nginx "
-"UI config path by default."
+"* Index files are stored in the \"log-index\" directory within your Nginx UI "
+"config path by default."
 msgstr ""
 "* Файли індексу за замовчуванням зберігаються в каталозі \"log-index\" у "
 "шляху конфігурації NGINX UI."
@@ -304,7 +305,8 @@ msgstr "Розширені налаштування"
 
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:105
 msgid "Afterwards, refresh this page and click add passkey again."
-msgstr "Після цього оновіть цю сторінку та натисніть «Додати ключ доступу» знову."
+msgstr ""
+"Після цього оновіть цю сторінку та натисніть «Додати ключ доступу» знову."
 
 #: src/components/NamespaceTabs/NamespaceTabs.vue:116
 msgid "All"
@@ -324,8 +326,8 @@ msgid ""
 "All selected subdomains must belong to the same DNS Provider, otherwise the "
 "certificate application will fail."
 msgstr ""
-"Усі вибрані піддомени повинні належати до одного й того самого "
-"DNS-провайдера, інакше запит на сертифікат не буде успішним."
+"Усі вибрані піддомени повинні належати до одного й того самого DNS-"
+"провайдера, інакше запит на сертифікат не буде успішним."
 
 #: src/constants/errors/nginx_log.ts:13
 msgid "Analytics service not available"
@@ -336,7 +338,8 @@ msgid "Any"
 msgstr "Будь-який"
 
 #: src/components/AutoCertForm/AutoCertForm.vue:209
-msgid "Any reachable IP address can be used with private Certificate Authorities"
+msgid ""
+"Any reachable IP address can be used with private Certificate Authorities"
 msgstr ""
 "Будь-яку доступну IP-адресу можна використовувати з приватним центром "
 "сертифікації"
@@ -502,7 +505,7 @@ msgstr "Автоматичне резервне копіювання не вда
 msgid "Auto Backup Storage Failed"
 msgstr "Не вдалося зберегти автоматичну резервну копію"
 
-#: src/views/nginx_log/NginxLog.vue:86 src/views/node/Node.vue:164
+#: src/views/nginx_log/NginxLog.vue:101 src/views/node/Node.vue:164
 msgid "Auto Refresh"
 msgstr "Автоматичне оновлення"
 
@@ -554,7 +557,7 @@ msgstr "Середн./PV"
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
-#: src/views/nginx_log/NginxLog.vue:114
+#: src/views/nginx_log/NginxLog.vue:129
 #: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:170
 #: src/views/stream/components/StreamEditor.vue:134
 msgid "Back"
@@ -590,7 +593,8 @@ msgstr "Резервна копія"
 
 #: src/components/SystemRestore/SystemRestoreContent.vue:155
 msgid "Backup file integrity check failed, it may have been tampered with"
-msgstr "Перевірка цілісності резервного файлу не вдалася, можливо, його було змінено"
+msgstr ""
+"Перевірка цілісності резервного файлу не вдалася, можливо, його було змінено"
 
 #: src/constants/errors/backup.ts:38
 msgid "Backup file not found: {0}"
@@ -615,8 +619,8 @@ msgstr "Шлях резервного копіювання не є директ
 #: src/constants/errors/backup.ts:55
 msgid "Backup path is required for custom directory backup"
 msgstr ""
-"Шлях резервного копіювання необхідний для резервного копіювання "
-"спеціального каталогу"
+"Шлях резервного копіювання необхідний для резервного копіювання спеціального "
+"каталогу"
 
 #: src/views/backup/AutoBackup/components/CronEditor.vue:141
 msgid "Backup Schedule"
@@ -629,7 +633,8 @@ msgstr ""
 "%{file_path}"
 
 #: src/components/Notification/notifications.ts:34
-msgid "Backup task %{backup_name} failed during storage upload, error: %{error}"
+msgid ""
+"Backup task %{backup_name} failed during storage upload, error: %{error}"
 msgstr ""
 "Завдання резервного копіювання %{backup_name} не вдалося під час "
 "завантаження в сховище, помилка: %{error}"
@@ -717,8 +722,8 @@ msgid ""
 "indexing existing log files immediately."
 msgstr ""
 "Увімкнувши розширене індексування, ви підтверджуєте, що ваша система "
-"відповідає вимогам і розумієте наслідки для продуктивності. Це негайно "
-"почне індексувати наявні файли журналів."
+"відповідає вимогам і розумієте наслідки для продуктивності. Це негайно почне "
+"індексувати наявні файли журналів."
 
 #: src/views/certificate/ACMEUser.vue:38
 msgid "CA Dir"
@@ -730,7 +735,8 @@ msgstr "Кеш"
 
 #: src/views/dashboard/components/ParamsOpt/ProxyCacheConfig.vue:177
 msgid "Cache items not accessed within this time will be removed"
-msgstr "Елементи кешу, до яких не було звернень протягом цього часу, будуть видалені"
+msgstr ""
+"Елементи кешу, до яких не було звернень протягом цього часу, будуть видалені"
 
 #: src/views/dashboard/components/ParamsOpt/ProxyCacheConfig.vue:349
 msgid "Cache loader processing time threshold"
@@ -808,7 +814,8 @@ msgstr "Неможливо отримати доступ до шляху збе
 
 #: src/constants/errors/user.ts:11
 msgid "Cannot change initial user password in demo mode"
-msgstr "Не вдається змінити початковий пароль користувача в демонстраційному режимі"
+msgstr ""
+"Не вдається змінити початковий пароль користувача в демонстраційному режимі"
 
 #: src/components/ConfigHistory/DiffViewer.vue:72
 msgid "Cannot compare: Missing content"
@@ -987,24 +994,24 @@ msgstr "Перевірити фавікон"
 #: src/language/generate.ts:6
 msgid ""
 "Check if /var/run/docker.sock exists. If you are using Nginx UI Official "
-"Docker Image, please make sure the docker socket is mounted like this: `-v "
-"/var/run/docker.sock:/var/run/docker.sock`. Nginx UI official image uses "
-"/var/run/docker.sock to communicate with the host Docker Engine via Docker "
-"Client API. This feature is used to control Nginx in another container and "
-"perform container replacement rather than binary replacement during OTA "
-"upgrades of Nginx UI to ensure container dependencies are also upgraded. If "
-"you don't need this feature, please add the environment variable "
+"Docker Image, please make sure the docker socket is mounted like this: `-v /"
+"var/run/docker.sock:/var/run/docker.sock`. Nginx UI official image uses /var/"
+"run/docker.sock to communicate with the host Docker Engine via Docker Client "
+"API. This feature is used to control Nginx in another container and perform "
+"container replacement rather than binary replacement during OTA upgrades of "
+"Nginx UI to ensure container dependencies are also upgraded. If you don't "
+"need this feature, please add the environment variable "
 "NGINX_UI_IGNORE_DOCKER_SOCKET=true to the container."
 msgstr ""
 "Перевірте, чи існує /var/run/docker.sock. Якщо ви використовуєте офіційний "
 "образ Docker Nginx UI, переконайтеся, що сокет Docker змонтовано таким "
-"чином: `-v /var/run/docker.sock:/var/run/docker.sock`. Офіційний образ "
-"Nginx UI використовує /var/run/docker.sock для зв’язку з Docker Engine "
-"хоста через API Docker Client. Ця функція використовується для керування "
-"Nginx в іншому контейнері та виконання заміни контейнера замість заміни "
-"бінарного файлу під час OTA-оновлень Nginx UI, щоб гарантувати, що "
-"залежності контейнера також оновлюються. Якщо вам не потрібна ця функція, "
-"додайте змінну середовища NGINX_UI_IGNORE_DOCKER_SOCKET=true до контейнера."
+"чином: `-v /var/run/docker.sock:/var/run/docker.sock`. Офіційний образ Nginx "
+"UI використовує /var/run/docker.sock для зв’язку з Docker Engine хоста через "
+"API Docker Client. Ця функція використовується для керування Nginx в іншому "
+"контейнері та виконання заміни контейнера замість заміни бінарного файлу під "
+"час OTA-оновлень Nginx UI, щоб гарантувати, що залежності контейнера також "
+"оновлюються. Якщо вам не потрібна ця функція, додайте змінну середовища "
+"NGINX_UI_IGNORE_DOCKER_SOCKET=true до контейнера."
 
 #: src/components/SelfCheck/tasks/frontend/https-check.ts:14
 msgid ""
@@ -1015,24 +1022,25 @@ msgstr ""
 "небезпечним і перешкоджає використанню Passkeys та функцій буфера обміну"
 
 #: src/language/generate.ts:7
-msgid "Check if the conf.d directory is under the nginx configuration directory"
+msgid ""
+"Check if the conf.d directory is under the nginx configuration directory"
 msgstr "Перевірте, чи знаходиться каталог conf.d у каталозі конфігурації nginx"
 
 #: src/language/generate.ts:9
 msgid ""
-"Check if the nginx access log path exists. By default, this path is "
-"obtained from 'nginx -V'. If it cannot be obtained or the obtained path "
-"does not point to a valid, existing file, an error will be reported. In "
-"this case, you need to modify the configuration file to specify the access "
-"log path.Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#accesslogpath"
-msgstr ""
-"Перевірте, чи існує шлях до журналу доступу nginx. За замовчуванням цей "
-"шлях отримується з 'nginx -V'. Якщо його не вдається отримати або отриманий "
-"шлях не вказує на дійсний існуючий файл, буде повідомлено про помилку. У "
-"цьому випадку вам потрібно змінити файл конфігурації, щоб вказати шлях до "
-"журналу доступу. Докладніше див. у документації: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#accesslogpath"
+"Check if the nginx access log path exists. By default, this path is obtained "
+"from 'nginx -V'. If it cannot be obtained or the obtained path does not "
+"point to a valid, existing file, an error will be reported. In this case, "
+"you need to modify the configuration file to specify the access log path."
+"Refer to the docs for more details: https://nginxui.com/zh_CN/guide/config-"
+"nginx.html#accesslogpath"
+msgstr ""
+"Перевірте, чи існує шлях до журналу доступу nginx. За замовчуванням цей шлях "
+"отримується з 'nginx -V'. Якщо його не вдається отримати або отриманий шлях "
+"не вказує на дійсний існуючий файл, буде повідомлено про помилку. У цьому "
+"випадку вам потрібно змінити файл конфігурації, щоб вказати шлях до журналу "
+"доступу. Докладніше див. у документації: https://nginxui.com/zh_CN/guide/"
+"config-nginx.html#accesslogpath"
 
 #: src/language/generate.ts:10
 msgid "Check if the nginx configuration directory exists"
@@ -1048,29 +1056,29 @@ msgid ""
 "from 'nginx -V'. If it cannot be obtained or the obtained path does not "
 "point to a valid, existing file, an error will be reported. In this case, "
 "you need to modify the configuration file to specify the error log path. "
-"Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#errorlogpath"
+"Refer to the docs for more details: https://nginxui.com/zh_CN/guide/config-"
+"nginx.html#errorlogpath"
 msgstr ""
-"Перевірте, чи існує шлях до журналу помилок nginx. За замовчуванням цей "
-"шлях отримується з 'nginx -V'. Якщо його не вдається отримати або отриманий "
-"шлях не вказує на дійсний існуючий файл, буде повідомлено про помилку. У "
-"цьому випадку вам потрібно змінити файл конфігурації, щоб вказати шлях до "
-"журналу помилок. Докладніше див. у документації: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#errorlogpath"
+"Перевірте, чи існує шлях до журналу помилок nginx. За замовчуванням цей шлях "
+"отримується з 'nginx -V'. Якщо його не вдається отримати або отриманий шлях "
+"не вказує на дійсний існуючий файл, буде повідомлено про помилку. У цьому "
+"випадку вам потрібно змінити файл конфігурації, щоб вказати шлях до журналу "
+"помилок. Докладніше див. у документації: https://nginxui.com/zh_CN/guide/"
+"config-nginx.html#errorlogpath"
 
 #: src/language/generate.ts:8
 msgid ""
 "Check if the nginx PID path exists. By default, this path is obtained from "
 "'nginx -V'. If it cannot be obtained, an error will be reported. In this "
 "case, you need to modify the configuration file to specify the Nginx PID "
-"path.Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#pidpath"
+"path.Refer to the docs for more details: https://nginxui.com/zh_CN/guide/"
+"config-nginx.html#pidpath"
 msgstr ""
-"Перевірте, чи існує шлях до PID Nginx. За замовчуванням цей шлях "
-"отримується з команди 'nginx -V'. Якщо його не вдається отримати, буде "
-"повідомлено про помилку. У цьому випадку вам потрібно змінити "
-"конфігураційний файл, щоб вказати шлях до PID Nginx. Докладніше див. у "
-"документації: https://nginxui.com/zh_CN/guide/config-nginx.html#pidpath"
+"Перевірте, чи існує шлях до PID Nginx. За замовчуванням цей шлях отримується "
+"з команди 'nginx -V'. Якщо його не вдається отримати, буде повідомлено про "
+"помилку. У цьому випадку вам потрібно змінити конфігураційний файл, щоб "
+"вказати шлях до PID Nginx. Докладніше див. у документації: https://nginxui."
+"com/zh_CN/guide/config-nginx.html#pidpath"
 
 #: src/language/generate.ts:13
 msgid "Check if the nginx sbin path exists"
@@ -1098,8 +1106,8 @@ msgstr ""
 
 #: src/language/generate.ts:18
 msgid ""
-"Check if the streams-available and streams-enabled directories are under "
-"the nginx configuration directory"
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory"
 msgstr ""
 "Перевірте, чи каталоги streams-available та streams-enabled знаходяться в "
 "каталозі конфігурації nginx"
@@ -1494,8 +1502,8 @@ msgstr ""
 "\"Вірність\" означає точність до змісту та наміру оригінального тексту;\n"
 "\"Плавність\" означає, що переклад має бути зрозумілим та легким для "
 "сприйняття;\n"
-"\"Витонченість\" означає прагнення до культурної естетики перекладу та "
-"краси мови.\n"
+"\"Витонченість\" означає прагнення до культурної естетики перекладу та краси "
+"мови.\n"
 "Мета полягає у створенні перекладу, який був би вірним духу оригіналу,\n"
 "а також відповідав цільовій мові, культурі та естетичним уподобанням "
 "читачів.\n"
@@ -1561,7 +1569,8 @@ msgstr "Користувацькі заголовки"
 msgid ""
 "Customize the name of local node to be displayed in the environment "
 "indicator."
-msgstr "Налаштуйте назву локального вузла для відображення в індикаторі середовища."
+msgstr ""
+"Налаштуйте назву локального вузла для відображення в індикаторі середовища."
 
 #: src/views/backup/AutoBackup/components/CronEditor.vue:19
 msgid "Daily"
@@ -1578,7 +1587,7 @@ msgstr "Щодня о %{time}"
 #: src/routes/modules/dashboard.ts:10
 #: src/views/config/components/ConfigLeftPanel.vue:109
 #: src/views/config/components/ConfigLeftPanel.vue:159
-#: src/views/config/ConfigList.vue:69 src/views/nginx_log/NginxLog.vue:78
+#: src/views/config/ConfigList.vue:69 src/views/nginx_log/NginxLog.vue:93
 msgid "Dashboard"
 msgstr "Панель керування"
 
@@ -1612,7 +1621,8 @@ msgstr "Типовий діапазон"
 
 #: src/views/dashboard/components/ParamsOpt/ProxyCacheConfig.vue:149
 msgid "Define shared memory zone name and size, e.g. proxy_cache:10m"
-msgstr "Вкажіть назву та розмір зони спільної пам'яті, наприклад proxy_cache:10m"
+msgstr ""
+"Вкажіть назву та розмір зони спільної пам'яті, наприклад proxy_cache:10m"
 
 #: src/components/LLM/LLMSessionTabs.vue:293
 #: src/components/LLM/LLMSessionTabs.vue:384
@@ -1983,7 +1993,8 @@ msgstr "Домен"
 
 #: src/views/certificate/components/AutoCertManagement.vue:55
 msgid "Domains list is empty, try to reopen Auto Cert for %{config}"
-msgstr "Список доменів порожній, спробуйте знову відкрити Auto Cert для %{config}"
+msgstr ""
+"Список доменів порожній, спробуйте знову відкрити Auto Cert для %{config}"
 
 #: src/views/certificate/components/CertificateDownload.vue:94
 msgid "Download Certificate Files"
@@ -2014,9 +2025,8 @@ msgid ""
 "Due to the security policies of some browsers, you cannot use passkeys on "
 "non-HTTPS websites, except when running on localhost."
 msgstr ""
-"Через політику безпеки деяких браузерів ви не можете використовувати "
-"пас-ключі на вебсайтах без HTTPS, окрім випадків, коли сайт працює на "
-"localhost."
+"Через політику безпеки деяких браузерів ви не можете використовувати пас-"
+"ключі на вебсайтах без HTTPS, окрім випадків, коли сайт працює на localhost."
 
 #: src/components/LLM/LLMSessionTabs.vue:287
 #: src/components/LLM/LLMSessionTabs.vue:378
@@ -2224,8 +2234,8 @@ msgstr "Успішно ввімкнено"
 #: src/views/preference/tabs/ServerSettings.vue:52
 msgid "Enables HTTP/2 support with multiplexing and server push capabilities"
 msgstr ""
-"Увімкнення підтримки HTTP/2 з можливістю мультиплексування та "
-"push-повідомлень сервера"
+"Увімкнення підтримки HTTP/2 з можливістю мультиплексування та push-"
+"повідомлень сервера"
 
 #: src/views/preference/tabs/ServerSettings.vue:56
 msgid "Enables HTTP/3 support based on QUIC protocol for best performance"
@@ -2308,8 +2318,8 @@ msgstr "Журнали помилок"
 
 #: src/views/nginx_log/structured/StructuredLogViewer.vue:668
 msgid ""
-"Error logs do not support structured analysis as they contain free-form "
-"text messages."
+"Error logs do not support structured analysis as they contain free-form text "
+"messages."
 msgstr ""
 "Журнали помилок не підтримують структурований аналіз, оскільки вони містять "
 "довільні текстові повідомлення."
@@ -2377,8 +2387,8 @@ msgstr ""
 
 #: src/views/certificate/ACMEUser.vue:92
 msgid ""
-"External Account Binding Key ID (optional). Required for some ACME "
-"providers like ZeroSSL."
+"External Account Binding Key ID (optional). Required for some ACME providers "
+"like ZeroSSL."
 msgstr ""
 "Ідентифікатор ключа зовнішнього облікового запису (опціонально). Потрібен "
 "для деяких провайдерів ACME, таких як ZeroSSL."
@@ -2873,8 +2883,8 @@ msgid ""
 "Follow the instructions in the dialog to complete the passkey registration "
 "process."
 msgstr ""
-"Дотримуйтесь інструкцій у діалоговому вікні, щоб завершити процес "
-"реєстрації ключа доступу."
+"Дотримуйтесь інструкцій у діалоговому вікні, щоб завершити процес реєстрації "
+"ключа доступу."
 
 #: src/views/preference/tabs/NodeSettings.vue:42
 #: src/views/preference/tabs/NodeSettings.vue:54
@@ -2890,8 +2900,10 @@ msgid "For commercial or professional use, contact"
 msgstr "Для комерційного або професійного використання зв'яжіться з"
 
 #: src/views/nginx_log/structured/StructuredLogViewer.vue:670
-msgid "For error logs, please use the Raw Log Viewer for better viewing experience."
-msgstr "Для журналів помилок використовуйте Raw Log Viewer для кращого перегляду."
+msgid ""
+"For error logs, please use the Raw Log Viewer for better viewing experience."
+msgstr ""
+"Для журналів помилок використовуйте Raw Log Viewer для кращого перегляду."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:140
 msgid ""
@@ -2904,8 +2916,8 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:188
 msgid ""
-"For IP-based certificates, please specify the server IP address that will "
-"be included in the certificate."
+"For IP-based certificates, please specify the server IP address that will be "
+"included in the certificate."
 msgstr ""
 "Для сертифікатів на основі IP вкажіть IP-адресу сервера, яка буде включена "
 "до сертифіката."
@@ -3026,8 +3038,7 @@ msgid ""
 "For testing, SSL validation is disabled by default."
 msgstr ""
 "gRPCS використовує шифрування TLS. Сервер повинен реалізувати службу "
-"перевірки стану gRPC. Для тестування перевірка SSL за замовчуванням "
-"вимкнена."
+"перевірки стану gRPC. Для тестування перевірка SSL за замовчуванням вимкнена."
 
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:67
 msgid "GZIP Compression"
@@ -3131,7 +3142,8 @@ msgstr ""
 msgid ""
 "If you want to automatically revoke the old certificate, please enable this "
 "option."
-msgstr "Якщо ви хочете автоматично відкликати старий сертифікат, увімкніть цю опцію."
+msgstr ""
+"Якщо ви хочете автоматично відкликати старий сертифікат, увімкніть цю опцію."
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:142
 msgid ""
@@ -3143,7 +3155,8 @@ msgstr ""
 
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:76
 msgid "If your browser supports WebAuthn Passkey, a dialog box will appear."
-msgstr "Якщо ваш браузер підтримує WebAuthn Passkey, з’явиться діалогове вікно."
+msgstr ""
+"Якщо ваш браузер підтримує WebAuthn Passkey, з’явиться діалогове вікно."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:271
 msgid ""
@@ -3406,8 +3419,8 @@ msgid ""
 "Keep your recovery codes as safe as your password. We recommend saving them "
 "with a password manager."
 msgstr ""
-"Зберігайте ваші коди відновлення так само надійно, як і пароль. "
-"Рекомендуємо зберігати їх у менеджері паролів."
+"Зберігайте ваші коди відновлення так само надійно, як і пароль. Рекомендуємо "
+"зберігати їх у менеджері паролів."
 
 #: src/views/dashboard/components/ParamsOpt/PerformanceConfig.vue:60
 msgid "Keepalive Timeout"
@@ -3506,7 +3519,8 @@ msgstr "Залиште порожнім, щоб не змінювати"
 
 #: src/views/preference/tabs/OpenAISettings.vue:41
 msgid "Leave blank for the default: https://api.openai.com/"
-msgstr "Залиште порожнім для значення за замовчуванням: https://api.openai.com/"
+msgstr ""
+"Залиште порожнім для значення за замовчуванням: https://api.openai.com/"
 
 #: src/language/curd.ts:39
 msgid "Leave blank if do not want to modify"
@@ -3630,13 +3644,12 @@ msgstr "Журнал"
 #: src/language/generate.ts:23
 msgid ""
 "Log file %{log_path} is not a regular file. If you are using nginx-ui in "
-"docker container, please refer to "
-"https://nginxui.com/zh_CN/guide/config-nginx-log.html for more information."
+"docker container, please refer to https://nginxui.com/zh_CN/guide/config-"
+"nginx-log.html for more information."
 msgstr ""
-"Файл журналу %{log_path} не є звичайним файлом. Якщо ви використовуєте "
-"nginx-ui у контейнері Docker, будь ласка, зверніться до "
-"https://nginxui.com/zh_CN/guide/config-nginx-log.html для отримання "
-"додаткової інформації."
+"Файл журналу %{log_path} не є звичайним файлом. Якщо ви використовуєте nginx-"
+"ui у контейнері Docker, будь ласка, зверніться до https://nginxui.com/zh_CN/"
+"guide/config-nginx-log.html для отримання додаткової інформації."
 
 #: src/constants/errors/nginx_log.ts:14
 msgid "Log file does not exist"
@@ -3688,16 +3701,16 @@ msgstr "Logrotate"
 
 #: src/views/preference/tabs/LogrotateSettings.vue:13
 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."
+"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 ""
 "Logrotate за замовчуванням увімкнено у більшості популярних дистрибутивів "
-"Linux для користувачів, які встановлюють Nginx UI безпосередньо на "
-"хост-машині, тому вам не потрібно змінювати параметри на цій сторінці. Для "
+"Linux для користувачів, які встановлюють Nginx UI безпосередньо на хост-"
+"машині, тому вам не потрібно змінювати параметри на цій сторінці. Для "
 "користувачів, які встановлюють Nginx UI за допомогою контейнерів Docker, ви "
 "можете вручну активувати цю опцію. Планувальник завдань crontab у Nginx UI "
 "виконуватиме команду logrotate з інтервалом, який ви встановите у хвилинах."
@@ -4175,7 +4188,7 @@ msgstr "Nginx не працює в іншому контейнері"
 msgid "Nginx is running"
 msgstr "Nginx працює"
 
-#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:61
+#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:76
 msgid "Nginx Log"
 msgstr "Nginx Журнал"
 
@@ -4256,8 +4269,8 @@ msgstr "Конфігурацію Nginx UI відновлено"
 
 #: src/components/SystemRestore/SystemRestoreContent.vue:336
 msgid ""
-"Nginx UI configuration has been restored and will restart automatically in "
-"a few seconds."
+"Nginx UI configuration has been restored and will restart automatically in a "
+"few seconds."
 msgstr ""
 "Конфігурацію Nginx UI відновлено, і вона автоматично перезавантажиться за "
 "кілька секунд."
@@ -4812,7 +4825,8 @@ msgstr "Будь ласка, введіть токен безпеки"
 #: src/components/SystemRestore/SystemRestoreContent.vue:210
 #: src/components/SystemRestore/SystemRestoreContent.vue:287
 msgid "Please enter the security token received during backup"
-msgstr "Будь ласка, введіть токен безпеки, отриманий під час резервного копіювання"
+msgstr ""
+"Будь ласка, введіть токен безпеки, отриманий під час резервного копіювання"
 
 #: src/components/AutoCertForm/AutoCertForm.vue:80
 msgid "Please enter the server IP address"
@@ -4844,8 +4858,8 @@ msgstr ""
 #: src/components/Notification/notifications.ts:194
 #: src/language/constants.ts:59
 msgid ""
-"Please generate new recovery codes in the preferences immediately to "
-"prevent lockout."
+"Please generate new recovery codes in the preferences immediately to prevent "
+"lockout."
 msgstr ""
 "Будь ласка, негайно згенеруйте нові коди відновлення в налаштуваннях, щоб "
 "уникнути блокування."
@@ -4893,7 +4907,8 @@ msgid "Please log in."
 msgstr "Будь ласка, увійдіть."
 
 #: src/views/certificate/DNSCredential.vue:102
-msgid "Please note that the unit of time configurations below are all in seconds."
+msgid ""
+"Please note that the unit of time configurations below are all in seconds."
 msgstr ""
 "Будь ласка, зверніть увагу, що одиницею виміру часу в наведених нижче "
 "налаштуваннях є секунди."
@@ -4905,8 +4920,7 @@ msgstr "Будь ласка, усуньте всі проблеми перед 
 #: src/views/backup/components/BackupCreator.vue:107
 msgid "Please save this security token, you will need it for restoration:"
 msgstr ""
-"Будь ласка, збережіть цей токен безпеки, він знадобиться вам для "
-"відновлення:"
+"Будь ласка, збережіть цей токен безпеки, він знадобиться вам для відновлення:"
 
 #: src/components/SystemRestore/SystemRestoreContent.vue:107
 msgid "Please select a backup file"
@@ -4982,7 +4996,8 @@ msgstr "Приватний CA:"
 
 #: src/components/AutoCertForm/AutoCertForm.vue:202
 msgid "Private IPs (192.168.x.x, 10.x.x.x, 172.16-31.x.x) will fail"
-msgstr "Приватні IP-адреси (192.168.x.x, 10.x.x.x, 172.16-31.x.x) не працюватимуть"
+msgstr ""
+"Приватні IP-адреси (192.168.x.x, 10.x.x.x, 172.16-31.x.x) не працюватимуть"
 
 #: src/views/certificate/components/CertificateFileUpload.vue:121
 #: src/views/certificate/components/CertificateFileUpload.vue:45
@@ -5032,8 +5047,7 @@ msgstr "Протокол"
 #: src/views/preference/tabs/ServerSettings.vue:47
 msgid ""
 "Protocol configuration only takes effect when directly connecting. If using "
-"reverse proxy, please configure the protocol separately in the reverse "
-"proxy."
+"reverse proxy, please configure the protocol separately in the reverse proxy."
 msgstr ""
 "Налаштування протоколу діє лише при прямому підключенні. Якщо ви "
 "використовуєте зворотний проксі, налаштуйте протокол окремо у зворотному "
@@ -5090,7 +5104,7 @@ msgstr "У черзі на індексацію..."
 msgid "Quick Select"
 msgstr "Швидкий вибір"
 
-#: src/views/nginx_log/NginxLog.vue:79
+#: src/views/nginx_log/NginxLog.vue:94
 msgid "Raw"
 msgstr "Сирий"
 
@@ -5428,9 +5442,9 @@ msgid ""
 "shared library memory, which will be repeated calculated for multiple "
 "processes"
 msgstr ""
-"Розмір резидентного набору: Фактична пам'ять, резидентна у фізичній "
-"пам'яті, включаючи всю пам'ять спільних бібліотек, яка буде повторно "
-"обчислюватися для кількох процесів"
+"Розмір резидентного набору: Фактична пам'ять, резидентна у фізичній пам'яті, "
+"включаючи всю пам'ять спільних бібліотек, яка буде повторно обчислюватися "
+"для кількох процесів"
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:84
 msgid "Resource Usage Warning"
@@ -5708,7 +5722,7 @@ msgstr "Успішно збережено"
 
 #: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
 #: src/views/certificate/components/RenewCert.vue:26
-#: src/views/preference/store/index.ts:123
+#: src/views/preference/store/index.ts:124
 msgid "Save successfully"
 msgstr "Успішно збережено"
 
@@ -5932,21 +5946,19 @@ msgstr "Налаштування"
 
 #: src/constants/errors/nginx_log.ts:8
 msgid ""
-"Settings.NginxLogSettings.AccessLogPath is empty, refer to "
-"https://nginxui.com/guide/config-nginx.html for more information"
+"Settings.NginxLogSettings.AccessLogPath is empty, refer to https://nginxui."
+"com/guide/config-nginx.html for more information"
 msgstr ""
-"Settings.NginxLogSettings.AccessLogPath порожній, див. "
-"https://nginxui.com/guide/config-nginx.html для отримання додаткової "
-"інформації"
+"Settings.NginxLogSettings.AccessLogPath порожній, див. https://nginxui.com/"
+"guide/config-nginx.html для отримання додаткової інформації"
 
 #: src/constants/errors/nginx_log.ts:7
 msgid ""
-"Settings.NginxLogSettings.ErrorLogPath is empty, refer to "
-"https://nginxui.com/guide/config-nginx.html for more information"
+"Settings.NginxLogSettings.ErrorLogPath is empty, refer to https://nginxui."
+"com/guide/config-nginx.html for more information"
 msgstr ""
-"Settings.NginxLogSettings.ErrorLogPath порожній, див. "
-"https://nginxui.com/guide/config-nginx.html для отримання додаткової "
-"інформації"
+"Settings.NginxLogSettings.ErrorLogPath порожній, див. https://nginxui.com/"
+"guide/config-nginx.html для отримання додаткової інформації"
 
 #: src/views/install/components/InstallView.vue:65
 msgid "Setup your Nginx UI"
@@ -6073,7 +6085,8 @@ msgstr "Вміст SSL-сертифіката"
 
 #: src/constants/errors/system.ts:8
 msgid "SSL certificate file must be under Nginx configuration directory: {0}"
-msgstr "Файл SSL-сертифіката повинен знаходитися в каталозі конфігурації Nginx: {0}"
+msgstr ""
+"Файл SSL-сертифіката повинен знаходитися в каталозі конфігурації Nginx: {0}"
 
 #: src/constants/errors/system.ts:6
 msgid "SSL certificate file not found"
@@ -6219,7 +6232,7 @@ msgstr "Каталог Streams-available не існує"
 msgid "Streams-enabled directory not exist"
 msgstr "Каталог streams-enabled не існує"
 
-#: src/views/nginx_log/NginxLog.vue:77
+#: src/views/nginx_log/NginxLog.vue:92
 msgid "Structured"
 msgstr "Структурований"
 
@@ -6245,14 +6258,14 @@ msgstr "Неділя"
 #: src/components/SelfCheck/tasks/frontend/websocket.ts:13
 msgid ""
 "Support communication with the backend through the WebSocket protocol. If "
-"your Nginx UI is being used via an Nginx reverse proxy, please refer to "
-"this link to write the corresponding configuration file: "
-"https://nginxui.com/guide/nginx-proxy-example.html"
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
 msgstr ""
 "Підтримка зв'язку з бекендом через протокол WebSocket. Якщо ваш Nginx UI "
-"використовується через зворотний проксі Nginx, перегляньте це посилання, "
-"щоб написати відповідний конфігураційний файл: "
-"https://nginxui.com/guide/nginx-proxy-example.html"
+"використовується через зворотний проксі Nginx, перегляньте це посилання, щоб "
+"написати відповідний конфігураційний файл: https://nginxui.com/guide/nginx-"
+"proxy-example.html"
 
 #: src/language/curd.ts:53 src/language/curd.ts:57
 msgid "Support single or batch upload of files"
@@ -6454,11 +6467,10 @@ msgstr "Введений текст не є ключем SSL-сертифіка
 
 #: src/constants/errors/nginx_log.ts:2
 msgid ""
-"The log path is not under the paths in "
-"settings.NginxSettings.LogDirWhiteList"
+"The log path is not under the paths in settings.NginxSettings.LogDirWhiteList"
 msgstr ""
-"Шлях до журналу не знаходиться серед шляхів у "
-"settings.NginxSettings.LogDirWhiteList"
+"Шлях до журналу не знаходиться серед шляхів у settings.NginxSettings."
+"LogDirWhiteList"
 
 #: src/views/preference/tabs/OpenAISettings.vue:23
 #: src/views/preference/tabs/OpenAISettings.vue:89
@@ -6470,7 +6482,8 @@ msgstr ""
 "двокрапки та крапки."
 
 #: src/views/preference/tabs/OpenAISettings.vue:90
-msgid "The model used for code completion, if not set, the chat model will be used."
+msgid ""
+"The model used for code completion, if not set, the chat model will be used."
 msgstr ""
 "Модель, яка використовується для завершення коду. Якщо вона не встановлена, "
 "буде використовуватися модель чату."
@@ -6560,8 +6573,7 @@ msgstr "Сторонні компоненти"
 #: src/views/certificate/components/AutoCertManagement.vue:45
 msgid "This Auto Cert item is invalid, please remove it."
 msgstr ""
-"Цей елемент автоматичного сертифікату є недійсним, будь ласка, видаліть "
-"його."
+"Цей елемент автоматичного сертифікату є недійсним, будь ласка, видаліть його."
 
 #: src/views/certificate/components/AutoCertManagement.vue:35
 msgid "This certificate is managed by Nginx UI"
@@ -6590,18 +6602,20 @@ msgid "This field should not be empty"
 msgstr "Це поле не повинно бути порожнім"
 
 #: src/constants/form_errors.ts:6
-msgid "This field should only contain letters, unicode characters, numbers, and -_."
+msgid ""
+"This field should only contain letters, unicode characters, numbers, and -_."
 msgstr "Це поле має містити лише літери, символи Unicode, цифри та -_."
 
 #: src/language/curd.ts:46
 msgid ""
-"This field should only contain letters, unicode characters, numbers, and "
-"-_./:"
+"This field should only contain letters, unicode characters, numbers, and -"
+"_./:"
 msgstr "Це поле має містити лише літери, символи Unicode, цифри та -_./:"
 
 #: src/components/Notification/notifications.ts:94
 msgid "This is a test message sent at %{timestamp} from Nginx UI."
-msgstr "Це тестове повідомлення, надіслане на %{timestamp} з користувача Nginx."
+msgstr ""
+"Це тестове повідомлення, надіслане на %{timestamp} з користувача Nginx."
 
 #: src/views/dashboard/NginxDashBoard.vue:175
 msgid ""
@@ -6629,9 +6643,9 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:128
 msgid ""
-"This site is configured as a default server (default_server) for HTTPS "
-"(port 443). IP certificates require Certificate Authority (CA) support and "
-"may not be available with all ACME providers."
+"This site is configured as a default server (default_server) for HTTPS (port "
+"443). IP certificates require Certificate Authority (CA) support and may not "
+"be available with all ACME providers."
 msgstr ""
 "Цей сайт налаштований як сервер за замовчуванням (default_server) для HTTPS "
 "(порт 443). IP-сертифікати вимагають підтримки Центру сертифікації (CA) і "
@@ -6639,13 +6653,13 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:132
 msgid ""
-"This site uses wildcard server name (_) which typically indicates an "
-"IP-based certificate. IP certificates require Certificate Authority (CA) "
+"This site uses wildcard server name (_) which typically indicates an IP-"
+"based certificate. IP certificates require Certificate Authority (CA) "
 "support and may not be available with all ACME providers."
 msgstr ""
-"Цей сайт використовує шаблонне ім’я сервера (_), яке зазвичай вказує на "
-"IP-сертифікат. IP-сертифікати вимагають підтримки Центру сертифікації (CA) "
-"і можуть бути недоступні у всіх провайдерів ACME."
+"Цей сайт використовує шаблонне ім’я сервера (_), яке зазвичай вказує на IP-"
+"сертифікат. IP-сертифікати вимагають підтримки Центру сертифікації (CA) і "
+"можуть бути недоступні у всіх провайдерів ACME."
 
 #: src/views/backup/components/BackupCreator.vue:141
 msgid ""
@@ -6695,8 +6709,10 @@ msgstr ""
 "після завершення відновлення."
 
 #: src/views/node/BatchUpgrader.vue:186
-msgid "This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
-msgstr "Це оновить або перевстановить Nginx UI на %{nodeNames} до версії %{version}."
+msgid ""
+"This will upgrade or reinstall the Nginx UI on %{nodeNames} to %{version}."
+msgstr ""
+"Це оновить або перевстановить Nginx UI на %{nodeNames} до версії %{version}."
 
 #: src/views/preference/tabs/AuthSettings.vue:89
 msgid "Throttle"
@@ -6750,8 +6766,8 @@ msgid ""
 "To enable it, you need to install the Google or Microsoft Authenticator app "
 "on your mobile phone."
 msgstr ""
-"Щоб увімкнути його, вам потрібно встановити додаток Google Authenticator "
-"або Microsoft Authenticator на свій мобільний телефон."
+"Щоб увімкнути його, вам потрібно встановити додаток Google Authenticator або "
+"Microsoft Authenticator на свій мобільний телефон."
 
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:95
 msgid ""
@@ -6760,15 +6776,15 @@ msgid ""
 "and restart Nginx UI."
 msgstr ""
 "Для забезпечення безпеки конфігурацію WebAuthn не можна додати через "
-"інтерфейс. Будь ласка, налаштуйте вручну наступне у файлі конфігурації "
-"app.ini та перезапустіть Nginx UI."
+"інтерфейс. Будь ласка, налаштуйте вручну наступне у файлі конфігурації app."
+"ini та перезапустіть Nginx UI."
 
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:34
 #: src/views/site/site_edit/components/EnableTLS/EnableTLS.vue:15
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
-"add a location which can proxy the request from authority to backend, and "
-"we need to save this file and reload the Nginx. Are you sure you want to "
+"add a location which can proxy the request from authority to backend, and we "
+"need to save this file and reload the Nginx. Are you sure you want to "
 "continue?"
 msgstr ""
 "Щоб гарантувати нормальну роботу автоматичного поновлення сертифікатів, нам "
@@ -7186,10 +7202,10 @@ msgid ""
 "Encrypt cannot issue certificates for private IPs. Use a public IP address "
 "or consider using a private CA."
 msgstr ""
-"Попередження: Схоже, що це приватна IP-адреса. Публічні центри "
-"сертифікації, такі як Let's Encrypt, не можуть видавати сертифікати для "
-"приватних IP. Використовуйте публічну IP-адресу або розгляньте можливість "
-"використання приватного центру сертифікації."
+"Попередження: Схоже, що це приватна IP-адреса. Публічні центри сертифікації, "
+"такі як Let's Encrypt, не можуть видавати сертифікати для приватних IP. "
+"Використовуйте публічну IP-адресу або розгляньте можливість використання "
+"приватного центру сертифікації."
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:259
 msgid "We do not accept any feature requests"
@@ -7205,8 +7221,8 @@ msgstr ""
 
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:142
 msgid ""
-"We will remove the HTTPChallenge configuration from this file and reload "
-"the Nginx. Are you sure you want to continue?"
+"We will remove the HTTPChallenge configuration from this file and reload the "
+"Nginx. Are you sure you want to continue?"
 msgstr ""
 "Ми видалимо конфігурацію HTTPChallenge з цього файлу та перезавантажимо "
 "Nginx. Ви впевнені, що хочете продовжити?"
@@ -7332,8 +7348,8 @@ msgstr "Так"
 
 #: src/views/terminal/Terminal.vue:200
 msgid ""
-"You are accessing this terminal over an insecure HTTP connection on a "
-"non-localhost domain. This may expose sensitive information."
+"You are accessing this terminal over an insecure HTTP connection on a non-"
+"localhost domain. This may expose sensitive information."
 msgstr ""
 "Ви отримуєте доступ до цього терміналу через незахищене HTTP-з’єднання в "
 "домені, який не є локальним. Це може призвести до витоку конфіденційної "
@@ -7363,13 +7379,15 @@ msgstr "Тепер ви можете закрити це діалогове ві
 msgid ""
 "You have not configured the settings of Webauthn, so you cannot add a "
 "passkey."
-msgstr "Ви не налаштували параметри WebAuthn, тому не можете додати ключ доступу."
+msgstr ""
+"Ви не налаштували параметри WebAuthn, тому не можете додати ключ доступу."
 
 #: src/views/preference/components/AuthSettings/RecoveryCodes.vue:82
-msgid "You have not enabled 2FA yet. Please enable 2FA to generate recovery codes."
+msgid ""
+"You have not enabled 2FA yet. Please enable 2FA to generate recovery codes."
 msgstr ""
-"Ви ще не ввімкнули двофакторну аутентифікацію. Будь ласка, увімкніть її, "
-"щоб згенерувати коди відновлення."
+"Ви ще не ввімкнули двофакторну аутентифікацію. Будь ласка, увімкніть її, щоб "
+"згенерувати коди відновлення."
 
 #: src/views/preference/components/AuthSettings/RecoveryCodes.vue:95
 msgid "You have not generated recovery codes yet."
@@ -7457,7 +7475,8 @@ msgstr "Конвеєр без виділення пам’яті"
 #~ msgstr "Стабільна продуктивність на сучасному обладнанні"
 
 #~ msgid "Backup path not in granted access paths: {0}"
-#~ msgstr "Шлях резервного копіювання не входить до наданих шляхів доступу: {0}"
+#~ msgstr ""
+#~ "Шлях резервного копіювання не входить до наданих шляхів доступу: {0}"
 
 #~ msgid "Build with"
 #~ msgstr "Створено за допомогою"
@@ -7543,17 +7562,18 @@ msgstr "Конвеєр без виділення пам’яті"
 
 #~ msgid ""
 #~ "Support communication with the backend through the Server-Sent Events "
-#~ "protocol. If your Nginx UI is being used via an Nginx reverse proxy, please "
-#~ "refer to this link to write the corresponding configuration file: "
+#~ "protocol. If your Nginx UI is being used via an Nginx reverse proxy, "
+#~ "please refer to this link to write the corresponding configuration file: "
 #~ "https://nginxui.com/guide/nginx-proxy-example.html"
 #~ msgstr ""
 #~ "Підтримка зв’язку з бекендом через протокол Server-Sent Events. Якщо ваш "
 #~ "Nginx UI використовується через зворотний проксі Nginx, перейдіть за цим "
-#~ "посиланням, щоб написати відповідний конфігураційний файл: "
-#~ "https://nginxui.com/guide/nginx-proxy-example.html"
+#~ "посиланням, щоб написати відповідний конфігураційний файл: https://"
+#~ "nginxui.com/guide/nginx-proxy-example.html"
 
 #~ msgid "If left blank, the default CA Dir will be used."
-#~ msgstr "Якщо залишити порожнім, буде використовуватися стандартний каталог CA."
+#~ msgstr ""
+#~ "Якщо залишити порожнім, буде використовуватися стандартний каталог CA."
 
 #~ msgid "Save error %{msg}"
 #~ msgstr "Помилка збереження %{msg}"
@@ -7632,12 +7652,12 @@ msgstr "Конвеєр без виділення пам’яті"
 
 #~ msgid ""
 #~ "Check if /var/run/docker.sock exists. If you are using Nginx UI Official "
-#~ "Docker Image, please make sure the docker socket is mounted like this: `-v "
-#~ "/var/run/docker.sock:/var/run/docker.sock`."
+#~ "Docker Image, please make sure the docker socket is mounted like this: `-"
+#~ "v /var/run/docker.sock:/var/run/docker.sock`."
 #~ msgstr ""
-#~ "Перевірте, чи існує /var/run/docker.sock. Якщо ви використовуєте офіційний "
-#~ "Docker-образ Nginx UI, переконайтеся, що сокет Docker підключено таким "
-#~ "чином: `-v /var/run/docker.sock:/var/run/docker.sock`."
+#~ "Перевірте, чи існує /var/run/docker.sock. Якщо ви використовуєте "
+#~ "офіційний Docker-образ Nginx UI, переконайтеся, що сокет Docker "
+#~ "підключено таким чином: `-v /var/run/docker.sock:/var/run/docker.sock`."
 
 #~ msgid "Check if the nginx access log path exists"
 #~ msgstr "Перевірити, чи існує шлях до журналу доступу nginx"

+ 225 - 201
app/src/language/vi_VN/app.po

@@ -5,13 +5,14 @@ msgstr ""
 "Language-Team: none\n"
 "Language: vi_VN\n"
 "MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=utf-8\n"
+"Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
 #: src/language/generate.ts:35
 msgid "[Nginx UI] ACME User: %{name}, Email: %{email}, CA Dir: %{caDir}"
-msgstr "[Nginx UI] Người dùng ACME: %{name}, Email: %{email}, Thư mục CA: %{caDir}"
+msgstr ""
+"[Nginx UI] Người dùng ACME: %{name}, Email: %{email}, Thư mục CA: %{caDir}"
 
 #: src/language/generate.ts:36
 msgid "[Nginx UI] Backing up current certificate for later revocation"
@@ -26,10 +27,11 @@ msgid "[Nginx UI] Certificate successfully revoked"
 msgstr "[Nginx UI] Hủy chứng chỉ thành công"
 
 #: src/language/generate.ts:39
-msgid "[Nginx UI] Certificate was used for server, reloading server TLS certificate"
+msgid ""
+"[Nginx UI] Certificate was used for server, reloading server TLS certificate"
 msgstr ""
-"[Nginx UI] Chứng chỉ đã được sử dụng cho máy chủ, đang tải lại chứng chỉ "
-"TLS của máy chủ"
+"[Nginx UI] Chứng chỉ đã được sử dụng cho máy chủ, đang tải lại chứng chỉ TLS "
+"của máy chủ"
 
 #: src/language/generate.ts:40
 msgid "[Nginx UI] Creating client facilitates communication with the CA server"
@@ -106,8 +108,8 @@ msgstr "* Bao gồm các nút từ nhóm %{groupName} và các nút được ch
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:141
 msgid ""
-"* Index files are stored in the \"log-index\" directory within your Nginx "
-"UI config path by default."
+"* Index files are stored in the \"log-index\" directory within your Nginx UI "
+"config path by default."
 msgstr ""
 "* Các tệp chỉ mục được lưu trữ trong thư mục \"log-index\" trong đường dẫn "
 "cấu hình NGINX UI theo mặc định."
@@ -326,7 +328,8 @@ msgid "Any"
 msgstr "Bất kỳ"
 
 #: src/components/AutoCertForm/AutoCertForm.vue:209
-msgid "Any reachable IP address can be used with private Certificate Authorities"
+msgid ""
+"Any reachable IP address can be used with private Certificate Authorities"
 msgstr ""
 "Bất kỳ địa chỉ IP có thể truy cập nào đều có thể được sử dụng với các cơ "
 "quan cấp chứng chỉ riêng tư"
@@ -415,7 +418,8 @@ msgstr "Bạn có chắc chắn muốn xóa vị trí này không?"
 
 #: src/components/NamespaceTabs/NamespaceTabs.vue:142
 msgid "Are you sure you want to restart Nginx on the following sync nodes?"
-msgstr "Bạn có chắc chắn muốn khởi động lại Nginx trên các nút đồng bộ sau không?"
+msgstr ""
+"Bạn có chắc chắn muốn khởi động lại Nginx trên các nút đồng bộ sau không?"
 
 #: src/language/curd.ts:26
 msgid "Are you sure you want to restore?"
@@ -488,7 +492,7 @@ msgstr "Sao lưu tự động thất bại"
 msgid "Auto Backup Storage Failed"
 msgstr "Lưu trữ sao lưu tự động thất bại"
 
-#: src/views/nginx_log/NginxLog.vue:86 src/views/node/Node.vue:164
+#: src/views/nginx_log/NginxLog.vue:101 src/views/node/Node.vue:164
 msgid "Auto Refresh"
 msgstr "Tự động làm mới"
 
@@ -540,7 +544,7 @@ msgstr "TB/PV"
 #: src/views/certificate/components/CertificateActions.vue:22
 #: src/views/config/components/ConfigLeftPanel.vue:273
 #: src/views/config/ConfigList.vue:120 src/views/config/ConfigList.vue:217
-#: src/views/nginx_log/NginxLog.vue:114
+#: src/views/nginx_log/NginxLog.vue:129
 #: src/views/site/site_edit/components/SiteEditor/SiteEditor.vue:170
 #: src/views/stream/components/StreamEditor.vue:134
 msgid "Back"
@@ -576,7 +580,8 @@ msgstr "Sao lưu"
 
 #: src/components/SystemRestore/SystemRestoreContent.vue:155
 msgid "Backup file integrity check failed, it may have been tampered with"
-msgstr "Kiểm tra tính toàn vẹn của tập tin sao lưu thất bại, có thể đã bị can thiệp"
+msgstr ""
+"Kiểm tra tính toàn vẹn của tập tin sao lưu thất bại, có thể đã bị can thiệp"
 
 #: src/constants/errors/backup.ts:38
 msgid "Backup file not found: {0}"
@@ -608,13 +613,15 @@ msgstr "Lịch trình sao lưu"
 
 #: src/components/Notification/notifications.ts:38
 msgid "Backup task %{backup_name} completed successfully, file: %{file_path}"
-msgstr "Tác vụ sao lưu %{backup_name} đã hoàn thành thành công, tệp: %{file_path}"
+msgstr ""
+"Tác vụ sao lưu %{backup_name} đã hoàn thành thành công, tệp: %{file_path}"
 
 #: src/components/Notification/notifications.ts:34
-msgid "Backup task %{backup_name} failed during storage upload, error: %{error}"
+msgid ""
+"Backup task %{backup_name} failed during storage upload, error: %{error}"
 msgstr ""
-"Tác vụ sao lưu %{backup_name} thất bại trong quá trình tải lên lưu trữ, "
-"lỗi: %{error}"
+"Tác vụ sao lưu %{backup_name} thất bại trong quá trình tải lên lưu trữ, lỗi: "
+"%{error}"
 
 #: src/components/Notification/notifications.ts:30
 msgid "Backup task %{backup_name} failed to execute, error: %{error}"
@@ -696,9 +703,9 @@ msgid ""
 "requirements and understand the performance implications. This will start "
 "indexing existing log files immediately."
 msgstr ""
-"Bằng cách kích hoạt lập chỉ mục nâng cao, bạn xác nhận rằng hệ thống của "
-"bạn đáp ứng các yêu cầu và hiểu rõ các tác động đến hiệu suất. Điều này sẽ "
-"bắt đầu lập chỉ mục các tệp nhật ký hiện có ngay lập tức."
+"Bằng cách kích hoạt lập chỉ mục nâng cao, bạn xác nhận rằng hệ thống của bạn "
+"đáp ứng các yêu cầu và hiểu rõ các tác động đến hiệu suất. Điều này sẽ bắt "
+"đầu lập chỉ mục các tệp nhật ký hiện có ngay lập tức."
 
 #: src/views/certificate/ACMEUser.vue:38
 msgid "CA Dir"
@@ -966,13 +973,13 @@ msgstr "Kiểm tra favicon"
 #: src/language/generate.ts:6
 msgid ""
 "Check if /var/run/docker.sock exists. If you are using Nginx UI Official "
-"Docker Image, please make sure the docker socket is mounted like this: `-v "
-"/var/run/docker.sock:/var/run/docker.sock`. Nginx UI official image uses "
-"/var/run/docker.sock to communicate with the host Docker Engine via Docker "
-"Client API. This feature is used to control Nginx in another container and "
-"perform container replacement rather than binary replacement during OTA "
-"upgrades of Nginx UI to ensure container dependencies are also upgraded. If "
-"you don't need this feature, please add the environment variable "
+"Docker Image, please make sure the docker socket is mounted like this: `-v /"
+"var/run/docker.sock:/var/run/docker.sock`. Nginx UI official image uses /var/"
+"run/docker.sock to communicate with the host Docker Engine via Docker Client "
+"API. This feature is used to control Nginx in another container and perform "
+"container replacement rather than binary replacement during OTA upgrades of "
+"Nginx UI to ensure container dependencies are also upgraded. If you don't "
+"need this feature, please add the environment variable "
 "NGINX_UI_IGNORE_DOCKER_SOCKET=true to the container."
 msgstr ""
 "Kiểm tra xem /var/run/docker.sock có tồn tại không. Nếu bạn đang sử dụng "
@@ -991,27 +998,28 @@ msgid ""
 "Check if HTTPS is enabled. Using HTTP outside localhost is insecure and "
 "prevents using Passkeys and clipboard features"
 msgstr ""
-"Kiểm tra xem HTTPS có được bật không. Sử dụng HTTP bên ngoài localhost "
-"không an toàn và ngăn chặn việc sử dụng tính năng Passkeys và clipboard"
+"Kiểm tra xem HTTPS có được bật không. Sử dụng HTTP bên ngoài localhost không "
+"an toàn và ngăn chặn việc sử dụng tính năng Passkeys và clipboard"
 
 #: src/language/generate.ts:7
-msgid "Check if the conf.d directory is under the nginx configuration directory"
+msgid ""
+"Check if the conf.d directory is under the nginx configuration directory"
 msgstr "Kiểm tra xem thư mục conf.d có nằm trong thư mục cấu hình nginx không"
 
 #: src/language/generate.ts:9
 msgid ""
-"Check if the nginx access log path exists. By default, this path is "
-"obtained from 'nginx -V'. If it cannot be obtained or the obtained path "
-"does not point to a valid, existing file, an error will be reported. In "
-"this case, you need to modify the configuration file to specify the access "
-"log path.Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#accesslogpath"
+"Check if the nginx access log path exists. By default, this path is obtained "
+"from 'nginx -V'. If it cannot be obtained or the obtained path does not "
+"point to a valid, existing file, an error will be reported. In this case, "
+"you need to modify the configuration file to specify the access log path."
+"Refer to the docs for more details: https://nginxui.com/zh_CN/guide/config-"
+"nginx.html#accesslogpath"
 msgstr ""
 "Kiểm tra xem đường dẫn nhật ký truy cập nginx có tồn tại không. Theo mặc "
 "định, đường dẫn này được lấy từ 'nginx -V'. Nếu không thể lấy được hoặc "
 "đường dẫn lấy được không trỏ đến một tệp hợp lệ đang tồn tại, một lỗi sẽ "
-"được báo cáo. Trong trường hợp này, bạn cần sửa đổi tệp cấu hình để chỉ "
-"định đường dẫn nhật ký truy cập. Tham khảo tài liệu để biết thêm chi tiết: "
+"được báo cáo. Trong trường hợp này, bạn cần sửa đổi tệp cấu hình để chỉ định "
+"đường dẫn nhật ký truy cập. Tham khảo tài liệu để biết thêm chi tiết: "
 "https://nginxui.com/zh_CN/guide/config-nginx.html#accesslogpath"
 
 #: src/language/generate.ts:10
@@ -1028,29 +1036,29 @@ msgid ""
 "from 'nginx -V'. If it cannot be obtained or the obtained path does not "
 "point to a valid, existing file, an error will be reported. In this case, "
 "you need to modify the configuration file to specify the error log path. "
-"Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#errorlogpath"
+"Refer to the docs for more details: https://nginxui.com/zh_CN/guide/config-"
+"nginx.html#errorlogpath"
 msgstr ""
 "Kiểm tra xem đường dẫn nhật ký lỗi của nginx có tồn tại không. Theo mặc "
 "định, đường dẫn này được lấy từ 'nginx -V'. Nếu không thể lấy được hoặc "
 "đường dẫn lấy được không trỏ đến một tệp hợp lệ đang tồn tại, một lỗi sẽ "
-"được báo cáo. Trong trường hợp này, bạn cần sửa đổi tệp cấu hình để chỉ "
-"định đường dẫn nhật ký lỗi. Tham khảo tài liệu để biết thêm chi tiết: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#errorlogpath"
+"được báo cáo. Trong trường hợp này, bạn cần sửa đổi tệp cấu hình để chỉ định "
+"đường dẫn nhật ký lỗi. Tham khảo tài liệu để biết thêm chi tiết: https://"
+"nginxui.com/zh_CN/guide/config-nginx.html#errorlogpath"
 
 #: src/language/generate.ts:8
 msgid ""
 "Check if the nginx PID path exists. By default, this path is obtained from "
 "'nginx -V'. If it cannot be obtained, an error will be reported. In this "
 "case, you need to modify the configuration file to specify the Nginx PID "
-"path.Refer to the docs for more details: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#pidpath"
+"path.Refer to the docs for more details: https://nginxui.com/zh_CN/guide/"
+"config-nginx.html#pidpath"
 msgstr ""
 "Kiểm tra xem đường dẫn PID của Nginx có tồn tại không. Theo mặc định, đường "
-"dẫn này được lấy từ lệnh 'nginx -V'. Nếu không thể lấy được, một lỗi sẽ "
-"được báo cáo. Trong trường hợp này, bạn cần sửa đổi tệp cấu hình để chỉ "
-"định đường dẫn PID của Nginx. Tham khảo tài liệu để biết thêm chi tiết: "
-"https://nginxui.com/zh_CN/guide/config-nginx.html#pidpath"
+"dẫn này được lấy từ lệnh 'nginx -V'. Nếu không thể lấy được, một lỗi sẽ được "
+"báo cáo. Trong trường hợp này, bạn cần sửa đổi tệp cấu hình để chỉ định "
+"đường dẫn PID của Nginx. Tham khảo tài liệu để biết thêm chi tiết: https://"
+"nginxui.com/zh_CN/guide/config-nginx.html#pidpath"
 
 #: src/language/generate.ts:13
 msgid "Check if the nginx sbin path exists"
@@ -1078,8 +1086,8 @@ msgstr ""
 
 #: src/language/generate.ts:18
 msgid ""
-"Check if the streams-available and streams-enabled directories are under "
-"the nginx configuration directory"
+"Check if the streams-available and streams-enabled directories are under the "
+"nginx configuration directory"
 msgstr ""
 "Kiểm tra xem các thư mục streams-available và streams-enabled có nằm trong "
 "thư mục cấu hình nginx không"
@@ -1406,8 +1414,8 @@ msgid ""
 "Create system backups including Nginx configuration and Nginx UI settings. "
 "Backup files will be automatically downloaded to your computer."
 msgstr ""
-"Tạo bản sao lưu hệ thống bao gồm cấu hình Nginx và cài đặt Nginx UI. Các "
-"tệp sao lưu sẽ tự động được tải xuống máy tính của bạn."
+"Tạo bản sao lưu hệ thống bao gồm cấu hình Nginx và cài đặt Nginx UI. Các tệp "
+"sao lưu sẽ tự động được tải xuống máy tính của bạn."
 
 #: src/views/backup/AutoBackup/AutoBackup.vue:229
 #: src/views/namespace/columns.ts:72
@@ -1517,7 +1525,7 @@ msgstr "Hàng ngày lúc %{time}"
 #: src/routes/modules/dashboard.ts:10
 #: src/views/config/components/ConfigLeftPanel.vue:109
 #: src/views/config/components/ConfigLeftPanel.vue:159
-#: src/views/config/ConfigList.vue:69 src/views/nginx_log/NginxLog.vue:78
+#: src/views/config/ConfigList.vue:69 src/views/nginx_log/NginxLog.vue:93
 msgid "Dashboard"
 msgstr "Bảng điều khiển"
 
@@ -1551,7 +1559,8 @@ msgstr "Phạm vi mặc định"
 
 #: src/views/dashboard/components/ParamsOpt/ProxyCacheConfig.vue:149
 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/LLM/LLMSessionTabs.vue:293
 #: src/components/LLM/LLMSessionTabs.vue:384
@@ -2140,8 +2149,8 @@ msgid ""
 "minimum requirements before proceeding."
 msgstr ""
 "Bật tính năng lập chỉ mục nhật ký nâng cao sẽ tiêu tốn tài nguyên tính toán "
-"đáng kể bao gồm CPU và bộ nhớ. Vui lòng đảm bảo hệ thống của bạn đáp ứng "
-"các yêu cầu tối thiểu trước khi tiếp tục."
+"đáng kể bao gồm CPU và bộ nhớ. Vui lòng đảm bảo hệ thống của bạn đáp ứng các "
+"yêu cầu tối thiểu trước khi tiếp tục."
 
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:76
 msgid "Encrypt website with Let's Encrypt"
@@ -2208,8 +2217,8 @@ msgstr "Log lỗi"
 
 #: src/views/nginx_log/structured/StructuredLogViewer.vue:668
 msgid ""
-"Error logs do not support structured analysis as they contain free-form "
-"text messages."
+"Error logs do not support structured analysis as they contain free-form text "
+"messages."
 msgstr ""
 "Nhật ký lỗi không hỗ trợ phân tích có cấu trúc vì chúng chứa các thông báo "
 "văn bản tự do."
@@ -2277,11 +2286,11 @@ msgstr ""
 
 #: src/views/certificate/ACMEUser.vue:92
 msgid ""
-"External Account Binding Key ID (optional). Required for some ACME "
-"providers like ZeroSSL."
+"External Account Binding Key ID (optional). Required for some ACME providers "
+"like ZeroSSL."
 msgstr ""
-"ID khóa liên kết tài khoản bên ngoài (tùy chọn). Bắt buộc đối với một số "
-"nhà cung cấp ACME như ZeroSSL."
+"ID khóa liên kết tài khoản bên ngoài (tùy chọn). Bắt buộc đối với một số nhà "
+"cung cấp ACME như ZeroSSL."
 
 #: src/views/preference/tabs/NginxSettings.vue:49
 msgid "External Docker Container"
@@ -2790,7 +2799,8 @@ msgid "For commercial or professional use, contact"
 msgstr "Để sử dụng thương mại hoặc chuyên nghiệp, vui lòng liên hệ"
 
 #: src/views/nginx_log/structured/StructuredLogViewer.vue:670
-msgid "For error logs, please use the Raw Log Viewer for better viewing experience."
+msgid ""
+"For error logs, please use the Raw Log Viewer for better viewing experience."
 msgstr ""
 "Đối với nhật ký lỗi, vui lòng sử dụng Trình xem Nhật ký Thô để có trải "
 "nghiệm xem tốt hơn."
@@ -2805,8 +2815,8 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:188
 msgid ""
-"For IP-based certificates, please specify the server IP address that will "
-"be included in the certificate."
+"For IP-based certificates, please specify the server IP address that will be "
+"included in the certificate."
 msgstr ""
 "Đối với chứng chỉ dựa trên IP, vui lòng chỉ định địa chỉ IP của máy chủ sẽ "
 "được đưa vào chứng chỉ."
@@ -3042,7 +3052,8 @@ msgstr ""
 
 #: src/views/preference/components/AuthSettings/AddPasskey.vue:76
 msgid "If your browser supports WebAuthn Passkey, a dialog box will appear."
-msgstr "Nếu trình duyệt của bạn hỗ trợ WebAuthn Passkey, một hộp thoại sẽ xuất hiện."
+msgstr ""
+"Nếu trình duyệt của bạn hỗ trợ WebAuthn Passkey, một hộp thoại sẽ xuất hiện."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:271
 msgid ""
@@ -3529,13 +3540,12 @@ msgstr "Nhật ký"
 #: src/language/generate.ts:23
 msgid ""
 "Log file %{log_path} is not a regular file. If you are using nginx-ui in "
-"docker container, please refer to "
-"https://nginxui.com/zh_CN/guide/config-nginx-log.html for more information."
+"docker container, please refer to https://nginxui.com/zh_CN/guide/config-"
+"nginx-log.html for more information."
 msgstr ""
-"Tệp nhật ký %{log_path} không phải là tệp thông thường. Nếu bạn đang sử "
-"dụng nginx-ui trong container docker, vui lòng tham khảo "
-"https://nginxui.com/zh_CN/guide/config-nginx-log.html để biết thêm thông "
-"tin."
+"Tệp nhật ký %{log_path} không phải là tệp thông thường. Nếu bạn đang sử dụng "
+"nginx-ui trong container docker, vui lòng tham khảo https://nginxui.com/"
+"zh_CN/guide/config-nginx-log.html để biết thêm thông tin."
 
 #: src/constants/errors/nginx_log.ts:14
 msgid "Log file does not exist"
@@ -3587,19 +3597,19 @@ msgstr "Logrotate"
 
 #: src/views/preference/tabs/LogrotateSettings.vue:13
 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."
+"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 ""
 "Logrotate được kích hoạt mặc định trong hầu hết các bản phân phối Linux phổ "
-"biến dành cho người dùng cài đặt Nginx UI trực tiếp trên máy chủ, vì vậy "
-"bạn không cần phải thay đổi các tham số trên trang này. Đối với người dùng "
-"cài đặt Nginx UI bằng container Docker, bạn có thể kích hoạt thủ công tùy "
-"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)."
+"biến dành cho người dùng cài đặt Nginx UI trực tiếp trên máy chủ, vì vậy bạn "
+"không cần phải thay đổi các tham số trên trang này. Đối với người dùng cài "
+"đặt Nginx UI bằng container Docker, bạn có thể kích hoạt thủ công tùy chọn "
+"này. Bộ lập lịch tác vụ crontab của Nginx UI sẽ thực thi lệnh logrotate theo "
+"khoảng thời gian bạn đặt (tính bằng phút)."
 
 #: src/composables/useGeoTranslation.ts:166
 #: src/views/nginx_log/dashboard/components/ChinaMapChart/ChinaMapChart.vue:135
@@ -4074,7 +4084,7 @@ msgstr "Nginx không chạy trong một container khác"
 msgid "Nginx is running"
 msgstr "Nginx đang chạy"
 
-#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:61
+#: src/routes/modules/nginx_log.ts:9 src/views/nginx_log/NginxLog.vue:76
 msgid "Nginx Log"
 msgstr "Nhật ký Nginx"
 
@@ -4155,11 +4165,10 @@ msgstr "Cấu hình Nginx UI đã được khôi phục"
 
 #: src/components/SystemRestore/SystemRestoreContent.vue:336
 msgid ""
-"Nginx UI configuration has been restored and will restart automatically in "
-"a few seconds."
+"Nginx UI configuration has been restored and will restart automatically in a "
+"few seconds."
 msgstr ""
-"Cấu hình Nginx UI đã được khôi phục và sẽ tự động khởi động lại sau vài "
-"giây."
+"Cấu hình Nginx UI đã được khôi phục và sẽ tự động khởi động lại sau vài giây."
 
 #: src/language/generate.ts:30
 msgid "Nginx.conf includes conf.d directory"
@@ -4317,8 +4326,8 @@ msgid ""
 "Note: If the server does not support gRPC Reflection, health checks may "
 "fail. Please ensure your gRPC server has Reflection enabled."
 msgstr ""
-"Lưu ý: Nếu máy chủ không hỗ trợ gRPC Reflection, việc kiểm tra tình trạng "
-"thể thất bại. Hãy đảm bảo rằng máy chủ gRPC của bạn đã bật Reflection."
+"Lưu ý: Nếu máy chủ không hỗ trợ gRPC Reflection, việc kiểm tra tình trạng "
+"thể thất bại. Hãy đảm bảo rằng máy chủ gRPC của bạn đã bật Reflection."
 
 #: src/views/certificate/components/CertificateBasicInfo.vue:24
 #: src/views/certificate/components/CertificateContentEditor.vue:25
@@ -4553,9 +4562,9 @@ msgid ""
 "password replacement or as a 2FA method."
 msgstr ""
 "Passkey là thông tin xác thực WebAuthn dùng để xác minh danh tính của bạn "
-"thông qua chạm, nhận diện khuôn mặt, mật khẩu thiết bị hoặc mã PIN. Chúng "
-"thể được sử dụng để thay thế mật khẩu hoặc làm phương thức xác thực hai "
-"yếu tố (2FA)."
+"thông qua chạm, nhận diện khuôn mặt, mật khẩu thiết bị hoặc mã PIN. Chúng "
+"thể được sử dụng để thay thế mật khẩu hoặc làm phương thức xác thực hai yếu "
+"tố (2FA)."
 
 #: src/views/other/Login.vue:278 src/views/user/userColumns.tsx:16
 msgid "Password"
@@ -4678,8 +4687,7 @@ msgid ""
 "Please enter a name for the passkey you wish to create and click the OK "
 "button below."
 msgstr ""
-"Vui lòng nhập tên cho khóa truy cập bạn muốn tạo và nhấp vào nút OK bên "
-"dưới."
+"Vui lòng nhập tên cho khóa truy cập bạn muốn tạo và nhấp vào nút OK bên dưới."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:98
 msgid "Please enter a valid IPv4 address (0-255 per octet)"
@@ -4726,21 +4734,22 @@ msgstr "Vui lòng điền vào các trường cấu hình S3 bắt buộc"
 msgid ""
 "Please fill in the API authentication credentials provided by your DNS "
 "provider."
-msgstr "Vui lòng điền thông tin xác thực API do nhà cung cấp DNS của bạn cung cấp"
+msgstr ""
+"Vui lòng điền thông tin xác thực API do nhà cung cấp DNS của bạn cung cấp"
 
 #: src/components/AutoCertForm/AutoCertForm.vue:168
 msgid ""
 "Please first add credentials in Certification > DNS Credentials, and then "
 "select one of the credentialsbelow to request the API of the DNS provider."
 msgstr ""
-"Trước tiên, vui lòng thêm thông tin xác thực trong Chứng chỉ > Thông tin "
-"xác thực DNS, sau đó chọn nhà cung cấp DNS"
+"Trước tiên, vui lòng thêm thông tin xác thực trong Chứng chỉ > Thông tin xác "
+"thực DNS, sau đó chọn nhà cung cấp DNS"
 
 #: src/components/Notification/notifications.ts:194
 #: src/language/constants.ts:59
 msgid ""
-"Please generate new recovery codes in the preferences immediately to "
-"prevent lockout."
+"Please generate new recovery codes in the preferences immediately to prevent "
+"lockout."
 msgstr ""
 "Vui lòng tạo mã khôi phục mới trong phần tùy chọn ngay lập tức để tránh bị "
 "khóa."
@@ -4758,13 +4767,15 @@ msgstr "Vui lòng nhập tên thư mục"
 msgid ""
 "Please input name, this will be used as the filename of the new "
 "configuration!"
-msgstr "Vui lòng nhập tên, tên này sẽ được sử dụng làm tên tệp của cấu hình mới!"
+msgstr ""
+"Vui lòng nhập tên, tên này sẽ được sử dụng làm tên tệp của cấu hình mới!"
 
 #: src/views/site/site_list/SiteDuplicate.vue:33
 msgid ""
 "Please input name, this will be used as the filename of the new "
 "configuration."
-msgstr "Vui lòng nhập tên, tên này sẽ được sử dụng làm tên tệp cho cấu hình mới."
+msgstr ""
+"Vui lòng nhập tên, tên này sẽ được sử dụng làm tên tệp cho cấu hình mới."
 
 #: src/views/install/components/InstallForm.vue:25
 msgid "Please input your E-mail!"
@@ -4784,7 +4795,8 @@ msgid "Please log in."
 msgstr "Vui lòng đăng nhập."
 
 #: src/views/certificate/DNSCredential.vue:102
-msgid "Please note that the unit of time configurations below are all in seconds."
+msgid ""
+"Please note that the unit of time configurations below are all in seconds."
 msgstr "Lưu ý đơn vị cấu hình thời gian bên dưới được tính bằng giây."
 
 #: src/views/install/components/InstallView.vue:102
@@ -4919,8 +4931,7 @@ msgstr "Giao thức"
 #: src/views/preference/tabs/ServerSettings.vue:47
 msgid ""
 "Protocol configuration only takes effect when directly connecting. If using "
-"reverse proxy, please configure the protocol separately in the reverse "
-"proxy."
+"reverse proxy, please configure the protocol separately in the reverse proxy."
 msgstr ""
 "Cấu hình giao thức chỉ có hiệu lực khi kết nối trực tiếp. Nếu sử dụng proxy "
 "ngược, vui lòng cấu hình giao thức riêng trong proxy ngược."
@@ -4976,7 +4987,7 @@ msgstr "Đang xếp hàng để lập chỉ mục..."
 msgid "Quick Select"
 msgstr "Chọn nhanh"
 
-#: src/views/nginx_log/NginxLog.vue:79
+#: src/views/nginx_log/NginxLog.vue:94
 msgid "Raw"
 msgstr "Thô"
 
@@ -5312,8 +5323,8 @@ msgid ""
 "processes"
 msgstr ""
 "Kích thước tập hợp thường trú: Bộ nhớ thực tế thường trú trong bộ nhớ vật "
-"lý, bao gồm tất cả bộ nhớ thư viện dùng chung, sẽ được tính toán lặp lại "
-"cho nhiều tiến trình"
+"lý, bao gồm tất cả bộ nhớ thư viện dùng chung, sẽ được tính toán lặp lại cho "
+"nhiều tiến trình"
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:84
 msgid "Resource Usage Warning"
@@ -5410,8 +5421,8 @@ msgid ""
 "Revoking a certificate will affect any services currently using it. This "
 "action cannot be undone."
 msgstr ""
-"Việc thu hồi chứng chỉ sẽ ảnh hưởng đến bất kỳ dịch vụ nào hiện đang sử "
-"dụng nó. Hành động này không thể hoàn tác."
+"Việc thu hồi chứng chỉ sẽ ảnh hưởng đến bất kỳ dịch vụ nào hiện đang sử dụng "
+"nó. Hành động này không thể hoàn tác."
 
 #: src/views/preference/tabs/AuthSettings.vue:72
 msgid "RP Display Name"
@@ -5589,7 +5600,7 @@ msgstr "Lưu thành công"
 
 #: src/language/curd.ts:35 src/views/certificate/CertificateEditor.vue:49
 #: src/views/certificate/components/RenewCert.vue:26
-#: src/views/preference/store/index.ts:123
+#: src/views/preference/store/index.ts:124
 msgid "Save successfully"
 msgstr "Lưu thành công"
 
@@ -5623,7 +5634,8 @@ msgstr "Kết quả quét"
 
 #: src/views/preference/components/AuthSettings/TOTP.vue:71
 msgid "Scan the QR code with your mobile phone to add the account to the app."
-msgstr "Quét mã QR bằng điện thoại di động của bạn để thêm tài khoản vào ứng dụng."
+msgstr ""
+"Quét mã QR bằng điện thoại di động của bạn để thêm tài khoản vào ứng dụng."
 
 #: src/views/nginx_log/indexing/components/IndexProgressBar.vue:50
 msgid "Scanning"
@@ -5720,7 +5732,8 @@ msgstr "Tự kiểm tra"
 #: src/components/SelfCheck/SelfCheckHeaderBanner.vue:37
 #: src/components/SelfCheck/SelfCheckHeaderBanner.vue:60
 msgid "Self check failed, Nginx UI may not work properly"
-msgstr "Tự kiểm tra thất bại, giao diện Nginx có thể không hoạt động bình thường"
+msgstr ""
+"Tự kiểm tra thất bại, giao diện Nginx có thể không hoạt động bình thường"
 
 #: src/views/dashboard/ServerAnalytic.vue:35
 #: src/views/dashboard/ServerAnalytic.vue:351
@@ -5784,8 +5797,8 @@ msgid ""
 "Set the recursive nameservers to override the systems nameservers for the "
 "step of DNS challenge."
 msgstr ""
-"Đặt các máy chủ tên đệ quy để ghi đè các máy chủ tên hệ thống trong bước "
-"thử thách DNS."
+"Đặt các máy chủ tên đệ quy để ghi đè các máy chủ tên hệ thống trong bước thử "
+"thách DNS."
 
 #: src/views/site/components/SiteStatusSelect.vue:116
 msgid "set to maintenance mode"
@@ -5809,19 +5822,19 @@ msgstr "Cài đặt"
 
 #: src/constants/errors/nginx_log.ts:8
 msgid ""
-"Settings.NginxLogSettings.AccessLogPath is empty, refer to "
-"https://nginxui.com/guide/config-nginx.html for more information"
+"Settings.NginxLogSettings.AccessLogPath is empty, refer to https://nginxui."
+"com/guide/config-nginx.html for more information"
 msgstr ""
-"Settings.NginxLogSettings.AccessLogPath trống, tham khảo "
-"https://nginxui.com/guide/config-nginx.html để biết thêm thông tin"
+"Settings.NginxLogSettings.AccessLogPath trống, tham khảo https://nginxui.com/"
+"guide/config-nginx.html để biết thêm thông tin"
 
 #: src/constants/errors/nginx_log.ts:7
 msgid ""
-"Settings.NginxLogSettings.ErrorLogPath is empty, refer to "
-"https://nginxui.com/guide/config-nginx.html for more information"
+"Settings.NginxLogSettings.ErrorLogPath is empty, refer to https://nginxui."
+"com/guide/config-nginx.html for more information"
 msgstr ""
-"Settings.NginxLogSettings.ErrorLogPath trống, tham khảo "
-"https://nginxui.com/guide/config-nginx.html để biết thêm thông tin"
+"Settings.NginxLogSettings.ErrorLogPath trống, tham khảo https://nginxui.com/"
+"guide/config-nginx.html để biết thêm thông tin"
 
 #: src/views/install/components/InstallView.vue:65
 msgid "Setup your Nginx UI"
@@ -5896,8 +5909,8 @@ msgid ""
 "Sites will appear here once you configure nginx server blocks with valid "
 "server_name directives."
 msgstr ""
-"Các trang web sẽ xuất hiện ở đây sau khi bạn cấu hình các khối máy chủ "
-"nginx với các chỉ thị server_name hợp lệ."
+"Các trang web sẽ xuất hiện ở đây sau khi bạn cấu hình các khối máy chủ nginx "
+"với các chỉ thị server_name hợp lệ."
 
 #: src/constants/errors/self_check.ts:11
 msgid "Sites-available directory not exist"
@@ -6094,7 +6107,7 @@ msgstr "Thư mục Streams-available không tồn tại"
 msgid "Streams-enabled directory not exist"
 msgstr "Thư mục streams-enabled không tồn tại"
 
-#: src/views/nginx_log/NginxLog.vue:77
+#: src/views/nginx_log/NginxLog.vue:92
 msgid "Structured"
 msgstr "Có cấu trúc"
 
@@ -6120,14 +6133,14 @@ msgstr "Chủ nhật"
 #: src/components/SelfCheck/tasks/frontend/websocket.ts:13
 msgid ""
 "Support communication with the backend through the WebSocket protocol. If "
-"your Nginx UI is being used via an Nginx reverse proxy, please refer to "
-"this link to write the corresponding configuration file: "
-"https://nginxui.com/guide/nginx-proxy-example.html"
+"your Nginx UI is being used via an Nginx reverse proxy, please refer to this "
+"link to write the corresponding configuration file: https://nginxui.com/"
+"guide/nginx-proxy-example.html"
 msgstr ""
-"Hỗ trợ giao tiếp với backend thông qua giao thức WebSocket. Nếu Nginx UI "
-"của bạn đang được sử dụng thông qua proxy ngược Nginx, vui lòng tham khảo "
-"liên kết này để viết tệp cấu hình tương ứng: "
-"https://nginxui.com/guide/nginx-proxy-example.html"
+"Hỗ trợ giao tiếp với backend thông qua giao thức WebSocket. Nếu Nginx UI của "
+"bạn đang được sử dụng thông qua proxy ngược Nginx, vui lòng tham khảo liên "
+"kết này để viết tệp cấu hình tương ứng: https://nginxui.com/guide/nginx-"
+"proxy-example.html"
 
 #: src/language/curd.ts:53 src/language/curd.ts:57
 msgid "Support single or batch upload of files"
@@ -6308,16 +6321,16 @@ msgid ""
 "since it was last issued."
 msgstr ""
 "Chứng chỉ cho tên miền sẽ được kiểm tra sau mỗi 30 phút và sẽ được gia hạn "
-"nếu đã hơn 1 tuần hoặc khoảng thời gian bạn đặt trong cài đặt kể từ lần "
-"phát hành cuối cùng."
+"nếu đã hơn 1 tuần hoặc khoảng thời gian bạn đặt trong cài đặt kể từ lần phát "
+"hành cuối cùng."
 
 #: src/views/preference/tabs/NodeSettings.vue:37
 msgid ""
 "The ICP Number should only contain letters, unicode, numbers, hyphens, "
 "dashes, colons, and dots."
 msgstr ""
-"Số ICP chỉ được chứa chữ cái, unicode, số, dấu gạch ngang, dấu gạch dài, "
-"dấu hai chấm và dấu chấm."
+"Số ICP chỉ được chứa chữ cái, unicode, số, dấu gạch ngang, dấu gạch dài, dấu "
+"hai chấm và dấu chấm."
 
 #: src/views/certificate/components/CertificateContentEditor.vue:115
 msgid "The input is not a SSL Certificate"
@@ -6329,11 +6342,10 @@ msgstr "Đầu vào không phải là Khóa Chứng chỉ SSL"
 
 #: src/constants/errors/nginx_log.ts:2
 msgid ""
-"The log path is not under the paths in "
-"settings.NginxSettings.LogDirWhiteList"
+"The log path is not under the paths in settings.NginxSettings.LogDirWhiteList"
 msgstr ""
-"Đường dẫn nhật ký không nằm trong các đường dẫn trong "
-"settings.NginxSettings.LogDirWhiteList"
+"Đường dẫn nhật ký không nằm trong các đường dẫn trong settings.NginxSettings."
+"LogDirWhiteList"
 
 #: src/views/preference/tabs/OpenAISettings.vue:23
 #: src/views/preference/tabs/OpenAISettings.vue:89
@@ -6345,7 +6357,8 @@ msgstr ""
 "dài, dấu hai chấm và dấu chấm."
 
 #: src/views/preference/tabs/OpenAISettings.vue:90
-msgid "The model used for code completion, if not set, the chat model will be used."
+msgid ""
+"The model used for code completion, if not set, the chat model will be used."
 msgstr ""
 "Mô hình được sử dụng để hoàn thành mã, nếu không được đặt, mô hình trò "
 "chuyện sẽ được sử dụng."
@@ -6375,8 +6388,8 @@ msgid ""
 "The Public Security Number should only contain letters, unicode, numbers, "
 "hyphens, dashes, colons, and dots."
 msgstr ""
-"Số Công an chỉ được chứa chữ cái, unicode, số, dấu gạch ngang, dấu gạch "
-"dài, dấu hai chấm và dấu chấm."
+"Số Công an chỉ được chứa chữ cái, unicode, số, dấu gạch ngang, dấu gạch dài, "
+"dấu hai chấm và dấu chấm."
 
 #: src/views/dashboard/components/NodeAnalyticItem.vue:105
 msgid ""
@@ -6384,9 +6397,9 @@ msgid ""
 "version. To avoid potential errors, please upgrade the remote Nginx UI to "
 "match the local version."
 msgstr ""
-"Phiên bản Nginx UI từ xa không tương thích với phiên bản Nginx UI cục bộ. "
-"Để tránh các lỗi tiềm ẩn, vui lòng nâng cấp phiên bản Nginx UI từ xa để "
-"khớp với phiên bản cục bộ."
+"Phiên bản Nginx UI từ xa không tương thích với phiên bản Nginx UI cục bộ. Để "
+"tránh các lỗi tiềm ẩn, vui lòng nâng cấp phiên bản Nginx UI từ xa để khớp "
+"với phiên bản cục bộ."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:155
 msgid ""
@@ -6463,13 +6476,14 @@ msgid "This field should not be empty"
 msgstr "Trường này không được để trống"
 
 #: src/constants/form_errors.ts:6
-msgid "This field should only contain letters, unicode characters, numbers, and -_."
+msgid ""
+"This field should only contain letters, unicode characters, numbers, and -_."
 msgstr "Trường này chỉ được chứa chữ cái, ký tự Unicode, số và -_."
 
 #: src/language/curd.ts:46
 msgid ""
-"This field should only contain letters, unicode characters, numbers, and "
-"-_./:"
+"This field should only contain letters, unicode characters, numbers, and -"
+"_./:"
 msgstr "Trường này chỉ được chứa chữ cái, ký tự Unicode, số và -_./:"
 
 #: src/components/Notification/notifications.ts:94
@@ -6481,8 +6495,8 @@ msgid ""
 "This module provides Nginx request statistics, connection count, etc. data. "
 "After enabling it, you can view performance statistics"
 msgstr ""
-"Mô-đun này cung cấp dữ liệu như thống kê yêu cầu Nginx, số lượng kết nối, "
-"v.v. Sau khi bật, bạn có thể xem thống kê hiệu suất"
+"Mô-đun này cung cấp dữ liệu như thống kê yêu cầu Nginx, số lượng kết nối, v."
+"v. Sau khi bật, bạn có thể xem thống kê hiệu suất"
 
 #: src/views/nginx_log/dashboard/components/DateRangeSelector.vue:25
 msgid "This month"
@@ -6497,14 +6511,14 @@ msgid ""
 "This operation will only remove the certificate from the database. The "
 "certificate files on the file system will not be deleted."
 msgstr ""
-"Thao tác này sẽ chỉ xóa chứng chỉ khỏi cơ sở dữ liệu. Các tệp chứng chỉ "
-"trên hệ thống tệp sẽ không bị xóa."
+"Thao tác này sẽ chỉ xóa chứng chỉ khỏi cơ sở dữ liệu. Các tệp chứng chỉ trên "
+"hệ thống tệp sẽ không bị xóa."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:128
 msgid ""
-"This site is configured as a default server (default_server) for HTTPS "
-"(port 443). IP certificates require Certificate Authority (CA) support and "
-"may not be available with all ACME providers."
+"This site is configured as a default server (default_server) for HTTPS (port "
+"443). IP certificates require Certificate Authority (CA) support and may not "
+"be available with all ACME providers."
 msgstr ""
 "Trang web này được cấu hình làm máy chủ mặc định (default_server) cho HTTPS "
 "(cổng 443). Chứng chỉ IP yêu cầu hỗ trợ từ Tổ chức cấp chứng chỉ (CA) và có "
@@ -6512,13 +6526,13 @@ msgstr ""
 
 #: src/components/AutoCertForm/AutoCertForm.vue:132
 msgid ""
-"This site uses wildcard server name (_) which typically indicates an "
-"IP-based certificate. IP certificates require Certificate Authority (CA) "
+"This site uses wildcard server name (_) which typically indicates an IP-"
+"based certificate. IP certificates require Certificate Authority (CA) "
 "support and may not be available with all ACME providers."
 msgstr ""
 "Trang web này sử dụng tên máy chủ đại diện (_) thường chỉ ra chứng chỉ dựa "
-"trên IP. Chứng chỉ IP yêu cầu hỗ trợ từ Tổ chức cấp chứng chỉ (CA) và có "
-"thể không khả dụng với tất cả nhà cung cấp ACME."
+"trên IP. Chứng chỉ IP yêu cầu hỗ trợ từ Tổ chức cấp chứng chỉ (CA) và có thể "
+"không khả dụng với tất cả nhà cung cấp ACME."
 
 #: src/views/backup/components/BackupCreator.vue:141
 msgid ""
@@ -6542,8 +6556,8 @@ msgid ""
 "deleted and rebuilt from scratch. This may take some time. Continue?"
 msgstr ""
 "Điều này sẽ xây dựng lại toàn bộ chỉ mục nhật ký. Tất cả dữ liệu chỉ mục "
-"hiện có sẽ bị xóa và xây dựng lại từ đầu. Việc này có thể mất một thời "
-"gian. Tiếp tục?"
+"hiện có sẽ bị xóa và xây dựng lại từ đầu. Việc này có thể mất một thời gian. "
+"Tiếp tục?"
 
 #: src/views/nginx_log/indexing/IndexManagement.vue:56
 msgid "This will rebuild the index data for this specific file: %{path}"
@@ -6564,11 +6578,12 @@ msgid ""
 "This will restore configuration files and database. Nginx UI will restart "
 "after the restoration is complete."
 msgstr ""
-"Thao tác này sẽ khôi phục các tệp cấu hình và cơ sở dữ liệu. Giao diện "
-"Nginx sẽ khởi động lại sau khi quá trình khôi phục hoàn tất."
+"Thao tác này sẽ khôi phục các tệp cấu hình và cơ sở dữ liệu. Giao diện Nginx "
+"sẽ khởi động lại sau khi quá trình khôi phục hoàn tất."
 
 #: src/views/node/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 ""
 "Thao tác này sẽ nâng cấp hoặc cài đặt lại Nginx UI trên %{nodeNames} lên "
 "phiên bản %{version}."
@@ -6635,20 +6650,20 @@ msgid ""
 "and restart Nginx UI."
 msgstr ""
 "Để đảm bảo bảo mật, cấu hình WebAuthn không thể được thêm qua giao diện "
-"người dùng. Vui lòng cấu hình thủ công các mục sau trong tệp cấu hình "
-"app.ini và khởi động lại Nginx UI."
+"người dùng. Vui lòng cấu hình thủ công các mục sau trong tệp cấu hình app."
+"ini và khởi động lại Nginx UI."
 
 #: src/views/site/site_edit/components/Cert/IssueCert.vue:34
 #: src/views/site/site_edit/components/EnableTLS/EnableTLS.vue:15
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
-"add a location which can proxy the request from authority to backend, and "
-"we need to save this file and reload the Nginx. Are you sure you want to "
+"add a location which can proxy the request from authority to backend, and we "
+"need to save this file and reload the Nginx. Are you sure you want to "
 "continue?"
 msgstr ""
-"Để đảm bảo tính năng tự động gia hạn chứng chỉ có thể hoạt động bình "
-"thường, chúng tôi cần thêm một vị trí có thể ủy quyền yêu cầu từ cơ quan có "
-"thẩm quyền đến chương trình phụ trợ và chúng tôi cần lưu tệp này và tải lại "
+"Để đảm bảo tính năng tự động gia hạn chứng chỉ có thể hoạt động bình thường, "
+"chúng tôi cần thêm một vị trí có thể ủy quyền yêu cầu từ cơ quan có thẩm "
+"quyền đến chương trình phụ trợ và chúng tôi cần lưu tệp này và tải lại "
 "Nginx. Bạn có chắc chắn muốn Tiếp tục?"
 
 #: src/views/preference/tabs/OpenAISettings.vue:36
@@ -6768,7 +6783,8 @@ msgstr "Thùng rác"
 
 #: src/views/nginx_log/structured/StructuredLogViewer.vue:822
 msgid "Try adjusting your search criteria or navigate to different pages."
-msgstr "Hãy thử điều chỉnh tiêu chí tìm kiếm hoặc điều hướng đến các trang khác."
+msgstr ""
+"Hãy thử điều chỉnh tiêu chí tìm kiếm hoặc điều hướng đến các trang khác."
 
 #: src/views/nginx_log/structured/StructuredLogViewer.vue:832
 msgid "Try adjusting your search criteria or time range."
@@ -7051,9 +7067,9 @@ msgid ""
 "you have a valid backup file and security token, and carefully select what "
 "to restore."
 msgstr ""
-"Cảnh báo: Thao tác khôi phục sẽ ghi đè lên cấu hình hiện tại. Đảm bảo bạn "
-"tệp sao lưu hợp lệ và mã bảo mật, đồng thời cẩn thận chọn nội dung cần "
-"khôi phục."
+"Cảnh báo: Thao tác khôi phục sẽ ghi đè lên cấu hình hiện tại. Đảm bảo bạn "
+"tệp sao lưu hợp lệ và mã bảo mật, đồng thời cẩn thận chọn nội dung cần khôi "
+"phục."
 
 #: src/components/AutoCertForm/AutoCertForm.vue:103
 msgid ""
@@ -7062,8 +7078,8 @@ msgid ""
 "or consider using a private CA."
 msgstr ""
 "Cảnh báo: Đây có vẻ là một địa chỉ IP riêng. Các tổ chức cấp chứng chỉ công "
-"cộng như Let's Encrypt không thể cấp chứng chỉ cho các IP riêng. Hãy sử "
-"dụng địa chỉ IP công cộng hoặc cân nhắc sử dụng tổ chức cấp chứng chỉ riêng."
+"cộng như Let's Encrypt không thể cấp chứng chỉ cho các IP riêng. Hãy sử dụng "
+"địa chỉ IP công cộng hoặc cân nhắc sử dụng tổ chức cấp chứng chỉ riêng."
 
 #: src/views/nginx_log/components/IndexingSettingsModal.vue:259
 msgid "We do not accept any feature requests"
@@ -7079,8 +7095,8 @@ msgstr ""
 
 #: src/views/site/site_edit/components/Cert/ObtainCert.vue:142
 msgid ""
-"We will remove the HTTPChallenge configuration from this file and reload "
-"the Nginx. Are you sure you want to continue?"
+"We will remove the HTTPChallenge configuration from this file and reload the "
+"Nginx. Are you sure you want to continue?"
 msgstr ""
 "Chúng tôi sẽ xóa cấu hình HTTPChallenge khỏi tệp này và tải lại Nginx. Bạn "
 "có muốn tiếp tục không?"
@@ -7204,12 +7220,12 @@ msgstr "Có"
 
 #: src/views/terminal/Terminal.vue:200
 msgid ""
-"You are accessing this terminal over an insecure HTTP connection on a "
-"non-localhost domain. This may expose sensitive information."
+"You are accessing this terminal over an insecure HTTP connection on a non-"
+"localhost domain. This may expose sensitive information."
 msgstr ""
-"Bạn đang truy cập thiết bị đầu cuối này thông qua kết nối HTTP không an "
-"toàn trên một miền không phải localhost. Điều này có thể làm lộ thông tin "
-"nhạy cảm."
+"Bạn đang truy cập thiết bị đầu cuối này thông qua kết nối HTTP không an toàn "
+"trên một miền không phải localhost. Điều này có thể làm lộ thông tin nhạy "
+"cảm."
 
 #: src/constants/errors/config.ts:8
 msgid "You are not allowed to delete a file outside of the nginx config path"
@@ -7235,13 +7251,15 @@ msgstr "Bạn có thể đóng hộp thoại này ngay bây giờ"
 msgid ""
 "You have not configured the settings of Webauthn, so you cannot add a "
 "passkey."
-msgstr "Bạn chưa cấu hình cài đặt WebAuthn, vì vậy không thể thêm khóa truy cập."
+msgstr ""
+"Bạn chưa cấu hình cài đặt WebAuthn, vì vậy không thể thêm khóa truy cập."
 
 #: src/views/preference/components/AuthSettings/RecoveryCodes.vue:82
-msgid "You have not enabled 2FA yet. Please enable 2FA to generate recovery codes."
+msgid ""
+"You have not enabled 2FA yet. Please enable 2FA to generate recovery codes."
 msgstr ""
-"Bạn chưa bật xác thực hai yếu tố. Vui lòng bật xác thực hai yếu tố để tạo "
-"khôi phục."
+"Bạn chưa bật xác thực hai yếu tố. Vui lòng bật xác thực hai yếu tố để tạo "
+"khôi phục."
 
 #: src/views/preference/components/AuthSettings/RecoveryCodes.vue:95
 msgid "You have not generated recovery codes yet."
@@ -7329,7 +7347,8 @@ msgstr "Đường ống không cấp phát"
 #~ msgstr "Hiệu suất ổn định trên phần cứng hiện đại"
 
 #~ msgid "Backup path not in granted access paths: {0}"
-#~ msgstr "Đường dẫn sao lưu không nằm trong các đường dẫn truy cập được cấp: {0}"
+#~ msgstr ""
+#~ "Đường dẫn sao lưu không nằm trong các đường dẫn truy cập được cấp: {0}"
 
 #~ msgid "Build with"
 #~ msgstr "Xây dựng với"
@@ -7365,7 +7384,8 @@ msgstr "Đường ống không cấp phát"
 #~ msgstr "Điểm cuối S3 không hợp lệ: {0}"
 
 #~ msgid "Storage path not in granted access paths: {0}"
-#~ msgstr "Đường dẫn lưu trữ không nằm trong các đường dẫn truy cập được cấp: {0}"
+#~ msgstr ""
+#~ "Đường dẫn lưu trữ không nằm trong các đường dẫn truy cập được cấp: {0}"
 
 #~ msgid "files"
 #~ msgstr "tệp"
@@ -7415,14 +7435,14 @@ msgstr "Đường ống không cấp phát"
 
 #~ msgid ""
 #~ "Support communication with the backend through the Server-Sent Events "
-#~ "protocol. If your Nginx UI is being used via an Nginx reverse proxy, please "
-#~ "refer to this link to write the corresponding configuration file: "
+#~ "protocol. If your Nginx UI is being used via an Nginx reverse proxy, "
+#~ "please refer to this link to write the corresponding configuration file: "
 #~ "https://nginxui.com/guide/nginx-proxy-example.html"
 #~ msgstr ""
 #~ "Hỗ trợ giao tiếp với backend thông qua giao thức Server-Sent Events. Nếu "
 #~ "Nginx UI của bạn đang được sử dụng qua proxy ngược Nginx, vui lòng tham "
-#~ "khảo liên kết này để viết tệp cấu hình tương ứng: "
-#~ "https://nginxui.com/guide/nginx-proxy-example.html"
+#~ "khảo liên kết này để viết tệp cấu hình tương ứng: https://nginxui.com/"
+#~ "guide/nginx-proxy-example.html"
 
 #~ msgid "If left blank, the default CA Dir will be used."
 #~ msgstr "Nếu để trống, thư mục CA mặc định sẽ được sử dụng."
@@ -7504,8 +7524,8 @@ msgstr "Đường ống không cấp phát"
 
 #~ msgid ""
 #~ "Check if /var/run/docker.sock exists. If you are using Nginx UI Official "
-#~ "Docker Image, please make sure the docker socket is mounted like this: `-v "
-#~ "/var/run/docker.sock:/var/run/docker.sock`."
+#~ "Docker Image, please make sure the docker socket is mounted like this: `-"
+#~ "v /var/run/docker.sock:/var/run/docker.sock`."
 #~ msgstr ""
 #~ "Kiểm tra xem /var/run/docker.sock có tồn tại không. Nếu bạn đang sử dụng "
 #~ "Docker Image chính thức của Nginx UI, hãy đảm bảo rằng ổ cắm Docker được "
@@ -7593,11 +7613,14 @@ msgstr "Đường ống không cấp phát"
 #~ msgstr "Nhân bản %{conf_name} thành %{node_name} thành công"
 
 #, fuzzy
-#~ msgid "Rename %{orig_path} to %{new_path} on %{env_name} failed, response: %{resp}"
+#~ msgid ""
+#~ "Rename %{orig_path} to %{new_path} on %{env_name} failed, response: "
+#~ "%{resp}"
 #~ msgstr "Nhân bản %{conf_name} thành %{node_name} thành công"
 
 #, fuzzy
-#~ msgid "Rename Site %{site} to %{new_site} on %{node} error, response: %{resp}"
+#~ msgid ""
+#~ "Rename Site %{site} to %{new_site} on %{node} error, response: %{resp}"
 #~ msgstr "Nhân bản %{conf_name} thành %{node_name} thành công"
 
 #, fuzzy
@@ -7611,7 +7634,8 @@ msgstr "Đường ống không cấp phát"
 #~ msgstr "Nhân bản %{conf_name} thành %{node_name} thành công"
 
 #, fuzzy
-#~ msgid "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
+#~ msgid ""
+#~ "Sync Certificate %{cert_name} to %{env_name} failed, response: %{resp}"
 #~ msgstr "Nhân bản %{conf_name} thành %{node_name} thành công"
 
 #, fuzzy

File diff suppressed because it is too large
+ 267 - 175
app/src/language/zh_CN/app.po


File diff suppressed because it is too large
+ 266 - 175
app/src/language/zh_TW/app.po


+ 28 - 13
app/src/views/nginx_log/NginxLog.vue

@@ -21,17 +21,17 @@ const logType = computed(() => {
 
 const viewMode = useRouteQuery<'raw' | 'structured' | 'dashboard'>('view', 'structured')
 
-// Advanced indexing status
-const isAdvancedIndexingEnabled = ref(false)
+// Indexing status
+const isIndexingEnabled = ref(false)
 
 onMounted(async () => {
   try {
     const res = await nginxLog.getAdvancedIndexingStatus()
-    isAdvancedIndexingEnabled.value = !!res.enabled
+    isIndexingEnabled.value = !!res.enabled
   }
   catch (err) {
-    console.error('Failed to get advanced indexing status:', err)
-    isAdvancedIndexingEnabled.value = false
+    console.error('Failed to get indexing status:', err)
+    isIndexingEnabled.value = false
   }
 })
 
@@ -42,16 +42,31 @@ const isErrorLog = computed(() => {
 
 const autoRefresh = ref(true)
 
-watch(logType, v => {
-  if (v === 'error') {
+watch(viewMode, v => {
+  if (v === 'structured' && (!isIndexingEnabled.value || isErrorLog.value)) {
     viewMode.value = 'raw'
   }
 }, { immediate: true })
 
-// Force raw view when advanced indexing is disabled
-watch(isAdvancedIndexingEnabled, enabled => {
-  if (!enabled) {
-    viewMode.value = 'raw'
+// View mode logic: set defaults based on log type and indexing status
+watch([isErrorLog, isIndexingEnabled], ([isError, enabled], [prevIsError, prevEnabled]) => {
+  // Only set default when conditions change or initial load
+  const isInitialLoad = prevIsError === undefined && prevEnabled === undefined
+  const conditionsChanged = isError !== prevIsError || enabled !== prevEnabled
+
+  if (isInitialLoad || conditionsChanged) {
+    if (isError) {
+      // Error logs always use raw mode
+      viewMode.value = 'raw'
+    }
+    else if (!enabled) {
+      // Indexing disabled: default to raw
+      viewMode.value = 'raw'
+    }
+    else if (enabled && logType.value === 'access') {
+      // Indexing enabled for access logs: default to structured
+      viewMode.value = 'structured'
+    }
   }
 }, { immediate: true })
 </script>
@@ -69,8 +84,8 @@ watch(isAdvancedIndexingEnabled, enabled => {
 
     <template #extra>
       <div class="flex items-center gap-4">
-        <!-- View Mode Toggle (hide for error logs or when advanced indexing is disabled) -->
-        <div v-if="!isErrorLog && isAdvancedIndexingEnabled" class="flex items-center">
+        <!-- View Mode Toggle (hide for error logs or when indexing is disabled) -->
+        <div v-if="!isErrorLog && isIndexingEnabled" class="flex items-center">
           <ASegmented
             v-model:value="viewMode"
             :options="[

+ 1 - 1
app/src/views/preference/store/index.ts

@@ -68,7 +68,7 @@ const useSystemSettingsStore = defineStore('systemSettings', () => {
       container_name: '',
     },
     nginx_log: {
-      advanced_indexing_enabled: false,
+      indexing_enabled: false,
       index_path: '',
     },
     node: {

+ 1 - 1
app/src/views/terminal/Terminal.vue

@@ -43,7 +43,7 @@ function checkSecureConnection() {
 const sessionCallbacks: TerminalSessionCallbacks = {
   onInput: (_tabId: string, data: string) => {
     if (rightPanelRef.value && data.includes('\r')) {
-      const command = data.replace('\r', '').trim()
+      const command = data.replace(/\r/g, '').trim()
       if (command) {
         rightPanelRef.value.updateCurrentCommand(command)
       }

+ 5 - 5
docs/guide/config-nginx-log.md

@@ -2,20 +2,20 @@
 
 This section covers configuration options for Nginx log processing and analysis features in Nginx UI.
 
-## Advanced Indexing
+## Indexing
 
-### AdvancedIndexingEnabled
+### IndexingEnabled
 
 - Type: `boolean`
 - Default: `false`
-- Environment Variable: `NGINX_UI_NGINX_LOG_ADVANCED_INDEXING_ENABLED`
+- Environment Variable: `NGINX_UI_NGINX_LOG_INDEXING_ENABLED`
 - Version: `>= v2.2.0`
 
-This option enables advanced indexing for Nginx logs, which provides high-performance log search and analysis capabilities.
+This option enables indexing for Nginx logs, which provides high-performance log search and analysis capabilities.
 
 #### Behavior When Disabled (Basic Mode)
 
-When `AdvancedIndexingEnabled` is set to `false`, Nginx UI still discovers log entries from your Nginx configuration and shows them in the Logs list. In this basic mode:
+When `IndexingEnabled` is set to `false`, Nginx UI still discovers log entries from your Nginx configuration and shows them in the Logs list. In this basic mode:
 
 - You can view the list of detected log files (grouped by simple rotation patterns), but advanced features like indexing metrics, document counts, and search shards are not available.
 - Real-time viewing (tail) continues to work based on resolved access/error log paths.

+ 1 - 1
docs/guide/env.md

@@ -92,7 +92,7 @@ Applicable for version v2.0.0-beta.37 and above.
 ## Nginx Log
 | Configuration Setting  | Environment Variable                   |
 |------------------------|---------------------------------------|
-| AdvancedIndexingEnabled | NGINX_UI_NGINX_LOG_ADVANCED_INDEXING_ENABLED |
+| IndexingEnabled | NGINX_UI_NGINX_LOG_INDEXING_ENABLED |
 | IndexPath               | NGINX_UI_NGINX_LOG_INDEX_PATH                |
 
 ## Node

+ 5 - 5
docs/zh_CN/guide/config-nginx-log.md

@@ -2,20 +2,20 @@
 
 本节介绍 Nginx UI 中 Nginx 日志处理和分析功能的配置选项。
 
-## 高级索引
+## 索引
 
-### AdvancedIndexingEnabled
+### IndexingEnabled
 
 - 类型: `boolean`
 - 默认值: `false`
-- 环境变量: `NGINX_UI_NGINX_LOG_ADVANCED_INDEXING_ENABLED`
+- 环境变量: `NGINX_UI_NGINX_LOG_INDEXING_ENABLED`
 - 版本: `>= v2.2.0`
 
-此选项启用 Nginx 日志的高级索引功能,提供高性能的日志搜索和分析能力。
+此选项启用 Nginx 日志的索引功能,提供高性能的日志搜索和分析能力。
 
 #### 关闭时的行为(基础模式)
 
-当 `AdvancedIndexingEnabled` 为 `false` 时,Nginx UI 仍会从 Nginx 配置中发现日志入口,并在日志列表中展示。在基础模式下:
+当 `IndexingEnabled` 为 `false` 时,Nginx UI 仍会从 Nginx 配置中发现日志入口,并在日志列表中展示。在基础模式下:
 
 - 可以查看已检测到的日志文件列表(基于简单的轮转规则进行分组),但不提供索引指标、文档计数和分片搜索等高级功能。
 - 基于解析到的访问/错误日志路径,实时查看(tail)仍可使用。

+ 1 - 1
docs/zh_CN/guide/env.md

@@ -103,7 +103,7 @@
 
 | 配置                     | 环境变量                                       |
 |-------------------------|-----------------------------------------------|
-| AdvancedIndexingEnabled | NGINX_UI_NGINX_LOG_ADVANCED_INDEXING_ENABLED |
+| IndexingEnabled | NGINX_UI_NGINX_LOG_INDEXING_ENABLED |
 | IndexPath               | NGINX_UI_NGINX_LOG_INDEX_PATH                |
 
 ## Node

+ 5 - 5
docs/zh_TW/guide/config-nginx-log.md

@@ -2,20 +2,20 @@
 
 本節介紹 Nginx UI 中 Nginx 日誌處理和分析功能的設定選項。
 
-## 進階索引
+## 索引
 
-### AdvancedIndexingEnabled
+### IndexingEnabled
 
 - 類型: `boolean`
 - 預設值: `false`
-- 環境變數: `NGINX_UI_NGINX_LOG_ADVANCED_INDEXING_ENABLED`
+- 環境變數: `NGINX_UI_NGINX_LOG_INDEXING_ENABLED`
 - 版本: `>= v2.2.0`
 
-此選項啟用 Nginx 日誌的進階索引功能,提供高效能的日誌搜尋和分析能力。
+此選項啟用 Nginx 日誌的索引功能,提供高效能的日誌搜尋和分析能力。
 
 #### 關閉時的行為(基礎模式)
 
-當 `AdvancedIndexingEnabled` 為 `false` 時,Nginx UI 仍會從 Nginx 設定中發現日誌入口,並在日誌列表中顯示。在基礎模式下:
+當 `IndexingEnabled` 為 `false` 時,Nginx UI 仍會從 Nginx 設定中發現日誌入口,並在日誌列表中顯示。在基礎模式下:
 
 - 可檢視已偵測到的日誌檔列表(基於簡單的輪轉規則進行分組),但不提供索引指標、文件計數與分片搜尋等進階功能。
 - 依據解析出的存取/錯誤日誌路徑,即時檢視(tail)仍可使用。

+ 1 - 1
docs/zh_TW/guide/env.md

@@ -103,7 +103,7 @@
 
 | 設定                     | 環境變數                                       |
 |-------------------------|-----------------------------------------------|
-| AdvancedIndexingEnabled | NGINX_UI_NGINX_LOG_ADVANCED_INDEXING_ENABLED |
+| IndexingEnabled | NGINX_UI_NGINX_LOG_INDEXING_ENABLED |
 | IndexPath               | NGINX_UI_NGINX_LOG_INDEX_PATH                |
 
 ## Node

+ 8 - 0
internal/analytic/errors.go

@@ -0,0 +1,8 @@
+package analytic
+
+import "github.com/uozi-tech/cosy"
+
+var (
+	e                      = cosy.NewErrorScope("analytic")
+	ErrNodeAnalyticsFailed = e.New(54001, "node analytics failed: {0}")
+)

+ 2 - 2
internal/analytic/node.go

@@ -2,7 +2,6 @@ package analytic
 
 import (
 	"encoding/json"
-	"errors"
 	"io"
 	"net/http"
 	"net/url"
@@ -15,6 +14,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/shirou/gopsutil/v4/load"
 	"github.com/shirou/gopsutil/v4/net"
+	"github.com/uozi-tech/cosy"
 	"github.com/uozi-tech/cosy/logger"
 )
 
@@ -107,7 +107,7 @@ func InitNode(node *model.Node) (n *Node, err error) {
 	bytes, _ := io.ReadAll(resp.Body)
 
 	if resp.StatusCode != http.StatusOK {
-		return n, errors.New(string(bytes))
+		return n, cosy.WrapErrorWithParams(ErrNodeAnalyticsFailed, string(bytes))
 	}
 
 	err = json.Unmarshal(bytes, &n.NodeInfo)

+ 1 - 0
internal/cert/errors.go

@@ -27,4 +27,5 @@ var (
 	ErrWritePrivateKey                   = e.New(50021, "write private.key error: {0}")
 	ErrObtainCert                        = e.New(50022, "obtain cert error: {0}")
 	ErrRevokeCert                        = e.New(50023, "revoke cert error: {0}")
+	ErrNoCertificateAvailable            = e.New(50031, "no certificate available")
 )

+ 1 - 2
internal/cert/server_tls.go

@@ -2,7 +2,6 @@ package cert
 
 import (
 	"crypto/tls"
-	"errors"
 	"sync/atomic"
 
 	cSettings "github.com/uozi-tech/cosy/settings"
@@ -30,7 +29,7 @@ func ReloadServerTLSCertificate() error {
 func GetServerTLSCertificate() (*tls.Certificate, error) {
 	cert, ok := tlsCert.Load().(*tls.Certificate)
 	if !ok {
-		return nil, errors.New("no certificate available")
+		return nil, ErrNoCertificateAvailable
 	}
 	return cert, nil
 }

+ 1 - 1
internal/cron/incremental_indexing.go

@@ -43,7 +43,7 @@ func performIncrementalIndexing() {
 	}
 
 	// Get modern indexer
-	modernIndexer := nginx_log.GetModernIndexer()
+	modernIndexer := nginx_log.GetIndexer()
 	if modernIndexer == nil {
 		logger.Warn("Modern indexer not available for incremental indexing")
 		return

+ 2 - 0
internal/docker/errors.go

@@ -17,4 +17,6 @@ var (
 	ErrFailedToInspectCurrentContainer   = e.New(500011, "failed to inspect current container: {0}")
 	ErrFailedToCreateTempContainer       = e.New(500012, "failed to create temp container: {0}")
 	ErrFailedToStartTempContainer        = e.New(500013, "failed to start temp container: {0}")
+	ErrOldContainerNameNotFound          = e.New(500014, "could not find old container name")
+	ErrTempContainerNotFound             = e.New(500015, "could not find temp container")
 )

+ 2 - 2
internal/docker/ota.go

@@ -203,7 +203,7 @@ func UpgradeStepTwo(ctx context.Context) (err error) {
 	// Get old container name from environment variable, fallback to settings if not available
 	currentContainerName := os.Getenv("NGINX_UI_CONTAINER_NAME")
 	if currentContainerName == "" {
-		return errors.New("could not find old container name")
+		return ErrOldContainerNameNotFound
 	}
 	// Get the current running temp container name
 	// Since we can't directly get our own container name from inside, we'll search all temp containers
@@ -228,7 +228,7 @@ func UpgradeStepTwo(ctx context.Context) (err error) {
 	}
 
 	if tempContainerName == "" {
-		return errors.New("could not find temp container")
+		return ErrTempContainerNotFound
 	}
 
 	// Get temp container info to get the new image

+ 1 - 2
internal/kernel/boot.go

@@ -97,8 +97,7 @@ func InitAfterDatabase(ctx context.Context) {
 		passkey.Init,
 		mcp.Init,
 		sitecheck.Init,
-		nginx_log.InitializeModernServices,
-		nginx_log.InitTaskRecovery,
+		nginx_log.InitializeServices,
 		user.InitTokenCache,
 	}
 

+ 7 - 0
internal/nginx/nginx.go

@@ -7,6 +7,8 @@ import (
 	"sync"
 	"time"
 
+	"math"
+
 	"github.com/0xJacky/Nginx-UI/internal/docker"
 	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/shirou/gopsutil/v4/process"
@@ -138,6 +140,11 @@ func isProcessRunning(pidPath string) bool {
 		return false
 	}
 
+	// Check that pid is within int32 and positive bounds
+	if pid <= 0 || pid > math.MaxInt32 {
+		return false
+	}
+
 	// Use gopsutil for cross-platform process existence check
 	exists, err := process.PidExists(int32(pid))
 	if err != nil {

+ 4 - 4
internal/nginx_log/analytics/dashboard.go

@@ -370,7 +370,7 @@ func (s *service) calculateDashboardSummaryWithCardinality(ctx context.Context,
 			UseMainLogPath: true, // Use main_log_path for efficient log group queries
 		}
 		
-		if uvResult, err := cardinalityCounter.CountCardinality(ctx, uvCardReq); err == nil {
+		if uvResult, err := cardinalityCounter.Count(ctx, uvCardReq); err == nil {
 			// Override the facet-limited UV count with accurate cardinality count
 			summary.TotalUV = int(uvResult.Cardinality)
 			
@@ -384,7 +384,7 @@ func (s *service) calculateDashboardSummaryWithCardinality(ctx context.Context,
 			if result.Facets != nil && result.Facets["ip"] != nil {
 				facetUV = fmt.Sprintf("%d", result.Facets["ip"].Total)
 			}
-			logger.Infof("✓ Accurate UV count using CardinalityCounter: %d (was limited to %s by facet)", 
+			logger.Infof("✓ Accurate UV count using Counter: %d (was limited to %s by facet)", 
 				uvResult.Cardinality, facetUV)
 		} else {
 			logger.Errorf("Failed to count unique visitors with cardinality counter: %v", err)
@@ -399,7 +399,7 @@ func (s *service) calculateDashboardSummaryWithCardinality(ctx context.Context,
 			UseMainLogPath: true, // Use main_log_path for efficient log group queries
 		}
 		
-		if pageResult, err := cardinalityCounter.CountCardinality(ctx, pageCardReq); err == nil {
+		if pageResult, err := cardinalityCounter.Count(ctx, pageCardReq); err == nil {
 			logger.Debugf("Accurate unique pages count: %d (vs Total PV: %d)", pageResult.Cardinality, summary.TotalPV)
 			
 			if pageResult.Cardinality <= uint64(summary.TotalPV) {
@@ -411,7 +411,7 @@ func (s *service) calculateDashboardSummaryWithCardinality(ctx context.Context,
 			logger.Errorf("Failed to count unique pages: %v", err)
 		}
 	} else {
-		logger.Warnf("CardinalityCounter not available, UV count limited by facet size to %d", summary.TotalUV)
+		logger.Warnf("Counter not available, UV count limited by facet size to %d", summary.TotalUV)
 	}
 	
 	return summary

+ 9 - 9
internal/nginx_log/analytics/service.go

@@ -32,20 +32,20 @@ type Service interface {
 
 // service implements the Service interface
 type service struct {
-	searcher           searcher.Searcher
-	cardinalityCounter *searcher.CardinalityCounter
+	searcher           searcher.SearcherInterface
+	cardinalityCounter *searcher.Counter
 }
 
 // NewService creates a new analytics service
-func NewService(s searcher.Searcher) Service {
+func NewService(s searcher.SearcherInterface) Service {
 	// Try to extract shards from distributed searcher for cardinality counting
-	var cardinalityCounter *searcher.CardinalityCounter
+	var cardinalityCounter *searcher.Counter
 
-	if ds, ok := s.(*searcher.DistributedSearcher); ok {
+	if ds, ok := s.(*searcher.Searcher); ok {
 		shards := ds.GetShards()
 
 		if len(shards) > 0 {
-			cardinalityCounter = searcher.NewCardinalityCounter(shards)
+			cardinalityCounter = searcher.NewCounter(shards)
 		}
 	}
 
@@ -65,18 +65,18 @@ func (s *service) Stop() error {
 
 // getCardinalityCounter dynamically creates or returns a cardinality counter
 // This is necessary because shards may be updated after service initialization
-func (s *service) getCardinalityCounter() *searcher.CardinalityCounter {
+func (s *service) getCardinalityCounter() *searcher.Counter {
 	// If we already have a cardinality counter and it's still valid, use it
 	if s.cardinalityCounter != nil {
 		return s.cardinalityCounter
 	}
 
 	// Try to create a new cardinality counter from current shards
-	if ds, ok := s.searcher.(*searcher.DistributedSearcher); ok {
+	if ds, ok := s.searcher.(*searcher.Searcher); ok {
 		shards := ds.GetShards()
 		if len(shards) > 0 {
 			// Update our cached cardinality counter
-			s.cardinalityCounter = searcher.NewCardinalityCounter(shards)
+			s.cardinalityCounter = searcher.NewCounter(shards)
 			return s.cardinalityCounter
 		}
 	}

+ 4 - 4
internal/nginx_log/analytics/service_test.go

@@ -96,7 +96,7 @@ func (m *MockSearcher) Stop() error {
 	return args.Error(0)
 }
 
-// MockCardinalityCounter implements searcher.CardinalityCounter for testing
+// MockCardinalityCounter implements searcher.Counter for testing
 type MockCardinalityCounter struct {
 	mock.Mock
 }
@@ -134,7 +134,7 @@ func TestNewService(t *testing.T) {
 }
 
 // Helper function to create a service with a mock cardinality counter
-func createServiceWithCardinalityCounter(searcher searcher.Searcher, cardinalityCounter *searcher.CardinalityCounter) Service {
+func createServiceWithCardinalityCounter(searcher searcher.SearcherInterface, cardinalityCounter *searcher.Counter) Service {
 	return &service{
 		searcher:           searcher,
 		cardinalityCounter: cardinalityCounter,
@@ -555,7 +555,7 @@ func TestService_GetDashboardAnalytics_WithCardinalityCounter(t *testing.T) {
 	mockSearcher := &MockSearcher{}
 
 	// Create a mock cardinality counter for testing
-	mockCardinalityCounter := searcher.NewCardinalityCounter(nil)
+	mockCardinalityCounter := searcher.NewCounter(nil)
 	s := createServiceWithCardinalityCounter(mockSearcher, mockCardinalityCounter)
 
 	ctx := context.Background()
@@ -619,7 +619,7 @@ func TestService_GetDashboardAnalytics_WithCardinalityCounter(t *testing.T) {
 		return len(r.FacetFields) == 4 && r.FacetSize == 1000
 	})).Return(expectedResult, nil)
 
-	// The key test: CardinalityCounter should be called to get accurate UV count
+	// The key test: Counter should be called to get accurate UV count
 	// Note: We can't easily mock the cardinality counter because it's created internally
 	// This test verifies the logic works when cardinality counter is available
 

+ 17 - 17
internal/nginx_log/analytics/optimized_time_series.go → internal/nginx_log/analytics/time_series.go

@@ -44,17 +44,17 @@ type Searcher interface {
 	ClearCache() error
 }
 
-// OptimizedTimeSeriesProcessor provides high-performance time-series analytics
-type OptimizedTimeSeriesProcessor struct {
+// TimeSeriesProcessor provides high-performance time-series analytics
+type TimeSeriesProcessor struct {
 	bucketPools    map[int64]*BucketPool
 	visitorSets    map[int64]*VisitorSetPool
 	resultCache    *TimeSeriesCache
 	mutex          sync.RWMutex
 }
 
-// NewOptimizedTimeSeriesProcessor creates a new optimized processor
-func NewOptimizedTimeSeriesProcessor() *OptimizedTimeSeriesProcessor {
-	return &OptimizedTimeSeriesProcessor{
+// NewTimeSeriesProcessor creates a new optimized processor
+func NewTimeSeriesProcessor() *TimeSeriesProcessor {
+	return &TimeSeriesProcessor{
 		bucketPools: make(map[int64]*BucketPool),
 		visitorSets: make(map[int64]*VisitorSetPool),
 		resultCache: NewTimeSeriesCache(1000, 1800), // 1000 entries, 30min TTL
@@ -258,7 +258,7 @@ func getCurrentTimestamp() int64 {
 }
 
 // OptimizedGetVisitorsByTime provides optimized visitors by time calculation
-func (otsp *OptimizedTimeSeriesProcessor) OptimizedGetVisitorsByTime(
+func (otsp *TimeSeriesProcessor) OptimizedGetVisitorsByTime(
 	ctx context.Context, 
 	req *VisitorsByTimeRequest,
 	s Searcher,
@@ -341,7 +341,7 @@ func (otsp *OptimizedTimeSeriesProcessor) OptimizedGetVisitorsByTime(
 }
 
 // OptimizedGetTrafficByTime provides optimized traffic analytics
-func (otsp *OptimizedTimeSeriesProcessor) OptimizedGetTrafficByTime(
+func (otsp *TimeSeriesProcessor) OptimizedGetTrafficByTime(
 	ctx context.Context,
 	req *TrafficByTimeRequest,
 	s Searcher,
@@ -555,7 +555,7 @@ func logValue(x float64) float64 {
 }
 
 // getBucketPool gets or creates a bucket pool for the given interval
-func (otsp *OptimizedTimeSeriesProcessor) getBucketPool(interval int64) *BucketPool {
+func (otsp *TimeSeriesProcessor) getBucketPool(interval int64) *BucketPool {
 	otsp.mutex.RLock()
 	pool, exists := otsp.bucketPools[interval]
 	otsp.mutex.RUnlock()
@@ -598,24 +598,24 @@ type TrafficTimeValue struct {
 	UniqueVisitors int   `json:"unique_visitors"`
 }
 
-// AdvancedTimeSeriesProcessor provides advanced analytics with ML-like features
-type AdvancedTimeSeriesProcessor struct {
-	*OptimizedTimeSeriesProcessor
+// AnomalyDetector provides advanced analytics with ML-like features
+type AnomalyDetector struct {
+	*TimeSeriesProcessor
 	anomalyThreshold float64
 	trendWindow     int
 }
 
-// NewAdvancedTimeSeriesProcessor creates an advanced processor
-func NewAdvancedTimeSeriesProcessor() *AdvancedTimeSeriesProcessor {
-	return &AdvancedTimeSeriesProcessor{
-		OptimizedTimeSeriesProcessor: NewOptimizedTimeSeriesProcessor(),
+// NewAnomalyDetector creates an advanced processor
+func NewAnomalyDetector() *AnomalyDetector {
+	return &AnomalyDetector{
+		TimeSeriesProcessor: NewTimeSeriesProcessor(),
 		anomalyThreshold:            2.0, // 2 standard deviations
 		trendWindow:                10,   // 10 data points for trend
 	}
 }
 
 // DetectAnomalies detects anomalies in time-series data
-func (atsp *AdvancedTimeSeriesProcessor) DetectAnomalies(data []TimeValue) []AnomalyPoint {
+func (atsp *AnomalyDetector) DetectAnomalies(data []TimeValue) []AnomalyPoint {
 	if len(data) < 3 {
 		return nil
 	}
@@ -666,7 +666,7 @@ type AnomalyPoint struct {
 }
 
 // CalculateTrend calculates trend direction and strength
-func (atsp *AdvancedTimeSeriesProcessor) CalculateTrend(data []TimeValue) TrendAnalysis {
+func (atsp *AnomalyDetector) CalculateTrend(data []TimeValue) TrendAnalysis {
 	if len(data) < 2 {
 		return TrendAnalysis{Direction: "insufficient_data"}
 	}

+ 18 - 18
internal/nginx_log/analytics/optimized_time_series_test.go → internal/nginx_log/analytics/time_series_test.go

@@ -52,9 +52,9 @@ func (m *mockSearcher) ClearCache() error {
 	return nil
 }
 
-// TestOptimizedTimeSeriesProcessor tests the optimized processor
-func TestOptimizedTimeSeriesProcessor(t *testing.T) {
-	processor := NewOptimizedTimeSeriesProcessor()
+// TestTimeSeriesProcessor tests the optimized processor
+func TestTimeSeriesProcessor(t *testing.T) {
+	processor := NewTimeSeriesProcessor()
 
 	if processor == nil {
 		t.Fatal("Failed to create optimized processor")
@@ -147,9 +147,9 @@ func TestTimeSeriesCache(t *testing.T) {
 	}
 }
 
-// TestOptimizedGetVisitorsByTime tests optimized visitors by time
-func TestOptimizedGetVisitorsByTime(t *testing.T) {
-	processor := NewOptimizedTimeSeriesProcessor()
+// TestGetVisitorsByTime tests optimized visitors by time
+func TestGetVisitorsByTime(t *testing.T) {
+	processor := NewTimeSeriesProcessor()
 	mockSearcher := &mockSearcher{}
 
 	req := &VisitorsByTimeRequest{
@@ -180,9 +180,9 @@ func TestOptimizedGetVisitorsByTime(t *testing.T) {
 	}
 }
 
-// TestOptimizedGetTrafficByTime tests optimized traffic by time
-func TestOptimizedGetTrafficByTime(t *testing.T) {
-	processor := NewOptimizedTimeSeriesProcessor()
+// TestGetTrafficByTime tests optimized traffic by time
+func TestGetTrafficByTime(t *testing.T) {
+	processor := NewTimeSeriesProcessor()
 	mockSearcher := &mockSearcher{}
 
 	req := &TrafficByTimeRequest{
@@ -257,9 +257,9 @@ func TestHyperLogLog(t *testing.T) {
 	}
 }
 
-// TestAdvancedTimeSeriesProcessor tests advanced analytics
-func TestAdvancedTimeSeriesProcessor(t *testing.T) {
-	processor := NewAdvancedTimeSeriesProcessor()
+// TestAnomalyDetector tests advanced analytics
+func TestAnomalyDetector(t *testing.T) {
+	processor := NewAnomalyDetector()
 
 	if processor == nil {
 		t.Fatal("Failed to create advanced processor")
@@ -291,7 +291,7 @@ func TestAdvancedTimeSeriesProcessor(t *testing.T) {
 
 // TestTrendAnalysis tests trend calculation
 func TestTrendAnalysis(t *testing.T) {
-	processor := NewAdvancedTimeSeriesProcessor()
+	processor := NewAnomalyDetector()
 
 	// Test increasing trend
 	increasingData := []TimeValue{
@@ -330,9 +330,9 @@ func TestTrendAnalysis(t *testing.T) {
 	}
 }
 
-// BenchmarkOptimizedTimeSeriesProcessing benchmarks the optimized processing
-func BenchmarkOptimizedTimeSeriesProcessing(b *testing.B) {
-	processor := NewOptimizedTimeSeriesProcessor()
+// BenchmarkTimeSeriesProcessing benchmarks the optimized processing
+func BenchmarkTimeSeriesProcessing(b *testing.B) {
+	processor := NewTimeSeriesProcessor()
 	mockSearcher := &mockSearcher{}
 
 	req := &VisitorsByTimeRequest{
@@ -433,7 +433,7 @@ func BenchmarkTimeSeriesCache(b *testing.B) {
 
 // BenchmarkAnomalyDetection benchmarks anomaly detection
 func BenchmarkAnomalyDetection(b *testing.B) {
-	processor := NewAdvancedTimeSeriesProcessor()
+	processor := NewAnomalyDetector()
 
 	// Generate test data
 	testData := make([]TimeValue, 100)
@@ -458,7 +458,7 @@ func BenchmarkAnomalyDetection(b *testing.B) {
 
 // BenchmarkTrendCalculation benchmarks trend calculation
 func BenchmarkTrendCalculation(b *testing.B) {
-	processor := NewAdvancedTimeSeriesProcessor()
+	processor := NewAnomalyDetector()
 
 	// Generate test data with trend
 	testData := make([]TimeValue, 50)

+ 2 - 2
internal/nginx_log/conservative_worker_benchmark_test.go

@@ -90,7 +90,7 @@ func runConservativeWorkerTest(b *testing.B, config *indexer.Config, logFile str
 		BatchSize:     500,
 	}
 	
-	optimizedParser := parser.NewOptimizedParser(
+	optimizedParser := parser.NewParser(
 		parserConfig,
 		nil, // No UA parser for speed
 		nil, // No Geo service for speed
@@ -103,7 +103,7 @@ func runConservativeWorkerTest(b *testing.B, config *indexer.Config, logFile str
 	}
 	defer file.Close()
 	
-	parseResult, err := optimizedParser.OptimizedParseStream(ctx, file)
+	parseResult, err := optimizedParser.ParseStream(ctx, file)
 	if err != nil {
 		b.Fatalf("Parsing failed: %v", err)
 	}

+ 0 - 3
internal/nginx_log/errors.go

@@ -11,9 +11,6 @@ var (
 	ErrDirectiveParamsIsEmpty              = e.New(50005, "directive params is empty")
 	ErrErrorLogPathIsEmpty                 = e.New(50006, "settings.NginxLogSettings.ErrorLogPath is empty, refer to https://nginxui.com/guide/config-nginx.html for more information")
 	ErrAccessLogPathIsEmpty                = e.New(50007, "settings.NginxLogSettings.AccessLogPath is empty, refer to https://nginxui.com/guide/config-nginx.html for more information")
-	ErrEmptyLogLine                        = e.New(50008, "empty log line")
-	ErrInvalidTimestamp                    = e.New(50009, "invalid timestamp format")
-	ErrUnsupportedLogFormat                = e.New(50010, "unsupported log format")
 	ErrIndexerNotAvailable                 = e.New(50011, "log indexer not available")
 	ErrAnalyticsServiceNotAvailable        = e.New(50012, "analytics service not available")
 	ErrLogFileNotExists                    = e.New(50013, "log file does not exist")

+ 8 - 0
internal/nginx_log/indexer/errors.go

@@ -0,0 +1,8 @@
+package indexer
+
+import "github.com/uozi-tech/cosy"
+
+var (
+	e                          = cosy.NewErrorScope("nginx_log.indexer")
+	ErrLogParserNotInitialized = e.New(50201, "log parser is not initialized; call indexer.InitLogParser() before use")
+)

+ 4 - 4
internal/nginx_log/indexer/integration_validation_test.go

@@ -7,8 +7,8 @@ import (
 	"time"
 )
 
-// TestOptimizedIntegrationValidation validates the complete integration of optimizations
-func TestOptimizedIntegrationValidation(t *testing.T) {
+// TestIntegrationValidation validates the complete integration of optimizations
+func TestIntegrationValidation(t *testing.T) {
 	t.Log("=== Validating Optimized Indexer Integration ===")
 
 	// Create test log content
@@ -94,9 +94,9 @@ func TestOptimizedIntegrationValidation(t *testing.T) {
 	progressTracker := NewProgressTracker("test-group", progressConfig)
 
 	// Test optimized indexing with progress tracking
-	docCount, minTime, maxTime, err := indexer.OptimizedIndexSingleFileWithProgress(tmpFile.Name(), progressTracker)
+	docCount, minTime, maxTime, err := indexer.IndexSingleFileWithProgress(tmpFile.Name(), progressTracker)
 	if err != nil {
-		t.Fatalf("OptimizedIndexSingleFileWithProgress failed: %v", err)
+		t.Fatalf("IndexSingleFileWithProgress failed: %v", err)
 	}
 
 	if docCount != 3 {

+ 7 - 3
internal/nginx_log/indexer/log_file_manager.go

@@ -10,6 +10,7 @@ import (
 	"sync"
 	"time"
 
+	"github.com/0xJacky/Nginx-UI/internal/nginx_log/utils"
 	"github.com/0xJacky/Nginx-UI/model"
 	"github.com/uozi-tech/cosy/logger"
 )
@@ -437,9 +438,12 @@ func (lm *LogFileManager) SaveIndexMetadata(basePath string, documentCount uint6
 	}
 
 	// Get file stats to update LastModified and LastSize
-	if fileInfo, err := os.Stat(basePath); err == nil {
-		logIndex.LastModified = fileInfo.ModTime()
-		logIndex.LastSize = fileInfo.Size()
+	// Validate log path before accessing it
+	if utils.IsValidLogPath(basePath) {
+		if fileInfo, err := os.Stat(basePath); err == nil {
+			logIndex.LastModified = fileInfo.ModTime()
+			logIndex.LastSize = fileInfo.Size()
+		}
 	}
 
 	// If indexer is available and healthy, query Bleve for exact document count

+ 71 - 39
internal/nginx_log/indexer/parallel_indexer.go

@@ -507,13 +507,6 @@ func (pi *ParallelIndexer) Optimize() error {
 	return nil
 }
 
-// IndexLogFile reads and indexes a single log file using optimized processing
-// Now uses OptimizedParseStream for 7-8x faster performance and 70% memory reduction
-func (pi *ParallelIndexer) IndexLogFile(filePath string) error {
-	// Delegate to optimized implementation
-	return pi.OptimizedIndexLogFile(filePath)
-}
-
 // GetStats returns current indexer statistics
 func (pi *ParallelIndexer) GetStats() *IndexStats {
 	pi.statsMutex.RLock()
@@ -927,10 +920,10 @@ func (pi *ParallelIndexer) IndexSingleFileIncrementally(filePath string, progres
 }
 
 // indexSingleFile contains optimized logic to process one physical log file.
-// Now uses OptimizedParseStream for 7-8x faster performance and 70% memory reduction
+// Now uses ParseStream for 7-8x faster performance and 70% memory reduction
 func (pi *ParallelIndexer) indexSingleFile(filePath string) (uint64, *time.Time, *time.Time, error) {
 	// Delegate to optimized implementation
-	return pi.OptimizedIndexSingleFile(filePath)
+	return pi.IndexSingleFile(filePath)
 }
 
 // UpdateConfig updates the indexer configuration
@@ -1217,9 +1210,12 @@ func (pi *ParallelIndexer) IndexLogGroupWithProgress(basePath string, progressCo
 
 	// filepath.Glob might not match the base file itself if it has no extension,
 	// so we check for it explicitly and add it to the list.
-	info, err := os.Stat(basePath)
-	if err == nil && info.Mode().IsRegular() {
-		matches = append(matches, basePath)
+	// Validate log path before accessing it
+	if utils.IsValidLogPath(basePath) {
+		info, err := os.Stat(basePath)
+		if err == nil && info.Mode().IsRegular() {
+			matches = append(matches, basePath)
+		}
 	}
 
 	// Deduplicate file list
@@ -1228,10 +1224,13 @@ func (pi *ParallelIndexer) IndexLogGroupWithProgress(basePath string, progressCo
 	for _, match := range matches {
 		if _, ok := seen[match]; !ok {
 			// Further check if it's a file, not a directory. Glob can match dirs.
-			info, err := os.Stat(match)
-			if err == nil && info.Mode().IsRegular() {
-				seen[match] = struct{}{}
-				uniqueFiles = append(uniqueFiles, match)
+			// Validate log path before accessing it
+			if utils.IsValidLogPath(match) {
+				info, err := os.Stat(match)
+				if err == nil && info.Mode().IsRegular() {
+					seen[match] = struct{}{}
+					uniqueFiles = append(uniqueFiles, match)
+				}
 			}
 		}
 	}
@@ -1265,41 +1264,74 @@ func (pi *ParallelIndexer) IndexLogGroupWithProgress(basePath string, progressCo
 	}
 
 	docsCountMap := make(map[string]uint64)
+	var docsCountMu sync.RWMutex
 	var overallMinTime, overallMaxTime *time.Time
+	var timeMu sync.Mutex
+
+	// Process files in parallel with controlled concurrency
+	var fileWg sync.WaitGroup
+	// Use FileGroupConcurrency config if set, otherwise fallback to WorkerCount
+	maxConcurrency := pi.config.FileGroupConcurrency
+	if maxConcurrency <= 0 {
+		maxConcurrency = pi.config.WorkerCount
+		if maxConcurrency <= 0 {
+			maxConcurrency = 4 // Fallback default
+		}
+	}
+	fileSemaphore := make(chan struct{}, maxConcurrency)
+
+	logger.Infof("Processing %d files in log group %s with concurrency=%d", len(uniqueFiles), basePath, maxConcurrency)
 
-	// Process each file with progress tracking
 	for _, filePath := range uniqueFiles {
-		if progressTracker != nil {
-			progressTracker.StartFile(filePath)
-		}
+		fileWg.Add(1)
+		go func(fp string) {
+			defer fileWg.Done()
+
+			// Acquire semaphore for controlled concurrency
+			fileSemaphore <- struct{}{}
+			defer func() { <-fileSemaphore }()
 
-		docsIndexed, minTime, maxTime, err := pi.indexSingleFileWithProgress(filePath, progressTracker)
-		if err != nil {
-			logger.Warnf("Failed to index file '%s' in group '%s', skipping: %v", filePath, basePath, err)
 			if progressTracker != nil {
-				progressTracker.FailFile(filePath, err.Error())
+				progressTracker.StartFile(fp)
 			}
-			continue // Continue with the next file
-		}
 
-		docsCountMap[filePath] = docsIndexed
+			docsIndexed, minTime, maxTime, err := pi.indexSingleFileWithProgress(fp, progressTracker)
+			if err != nil {
+				logger.Warnf("Failed to index file '%s' in group '%s', skipping: %v", fp, basePath, err)
+				if progressTracker != nil {
+					progressTracker.FailFile(fp, err.Error())
+				}
+				return // Skip this file
+			}
 
-		if progressTracker != nil {
-			progressTracker.CompleteFile(filePath, int64(docsIndexed))
-		}
+			// Thread-safe update of docsCountMap
+			docsCountMu.Lock()
+			docsCountMap[fp] = docsIndexed
+			docsCountMu.Unlock()
 
-		if minTime != nil {
-			if overallMinTime == nil || minTime.Before(*overallMinTime) {
-				overallMinTime = minTime
+			if progressTracker != nil {
+				progressTracker.CompleteFile(fp, int64(docsIndexed))
 			}
-		}
-		if maxTime != nil {
-			if overallMaxTime == nil || maxTime.After(*overallMaxTime) {
-				overallMaxTime = maxTime
+
+			// Thread-safe update of time ranges
+			timeMu.Lock()
+			if minTime != nil {
+				if overallMinTime == nil || minTime.Before(*overallMinTime) {
+					overallMinTime = minTime
+				}
 			}
-		}
+			if maxTime != nil {
+				if overallMaxTime == nil || maxTime.After(*overallMaxTime) {
+					overallMaxTime = maxTime
+				}
+			}
+			timeMu.Unlock()
+		}(filePath)
 	}
 
+	// Wait for all files to complete
+	fileWg.Wait()
+
 	return docsCountMap, overallMinTime, overallMaxTime, nil
 }
 
@@ -1307,7 +1339,7 @@ func (pi *ParallelIndexer) IndexLogGroupWithProgress(basePath string, progressCo
 // Now uses the optimized implementation with full progress tracking integration
 func (pi *ParallelIndexer) indexSingleFileWithProgress(filePath string, progressTracker *ProgressTracker) (uint64, *time.Time, *time.Time, error) {
 	// Delegate to optimized implementation with progress tracking
-	return pi.OptimizedIndexSingleFileWithProgress(filePath, progressTracker)
+	return pi.IndexSingleFileWithProgress(filePath, progressTracker)
 }
 
 // sumDocCounts returns the total number of documents across all files

+ 21 - 16
internal/nginx_log/indexer/optimized_parallel_indexer.go → internal/nginx_log/indexer/parallel_indexer_optimized.go

@@ -13,9 +13,9 @@ import (
 	"github.com/uozi-tech/cosy/logger"
 )
 
-// OptimizedIndexLogFile reads and indexes a single log file using OptimizedParseStream
+// IndexLogFile reads and indexes a single log file using ParseStream
 // This replaces the original IndexLogFile with 7-8x faster performance and 70% memory reduction
-func (pi *ParallelIndexer) OptimizedIndexLogFile(filePath string) error {
+func (pi *ParallelIndexer) IndexLogFile(filePath string) error {
 	if !pi.IsHealthy() {
 		return fmt.Errorf("indexer not healthy")
 	}
@@ -46,7 +46,7 @@ func (pi *ParallelIndexer) OptimizedIndexLogFile(filePath string) error {
 		}
 		logger.Infof("Processed large file %s with chunked processing", filePath)
 	} else {
-		// Use OptimizedParseStream for general purpose (7-8x faster)
+		// Use ParseStream for general purpose (7-8x faster)
 		logDocs, err = ParseLogStream(ctx, file, filePath)
 		if err != nil {
 			return fmt.Errorf("failed to parse file %s with optimized stream processing: %w", filePath, err)
@@ -55,19 +55,24 @@ func (pi *ParallelIndexer) OptimizedIndexLogFile(filePath string) error {
 	}
 
 	// Use efficient batch indexing with memory pools
-	return pi.indexOptimizedLogDocuments(logDocs, filePath)
+	return pi.indexLogDocuments(logDocs, filePath)
 }
 
-// OptimizedIndexSingleFile contains the optimized logic to process one physical log file.
+// IndexSingleFile contains the optimized logic to process one physical log file.
 // It returns the number of documents indexed from the file, and the min/max timestamps.
 // This provides 7-8x better performance than the original indexSingleFile
-func (pi *ParallelIndexer) OptimizedIndexSingleFile(filePath string) (uint64, *time.Time, *time.Time, error) {
-	return pi.OptimizedIndexSingleFileWithProgress(filePath, nil)
+func (pi *ParallelIndexer) IndexSingleFile(filePath string) (uint64, *time.Time, *time.Time, error) {
+	return pi.IndexSingleFileWithProgress(filePath, nil)
 }
 
-// OptimizedIndexSingleFileWithProgress processes a file with progress tracking integration
+// IndexSingleFileWithProgress processes a file with progress tracking integration
 // This maintains compatibility with the existing ProgressTracker system while providing optimized performance
-func (pi *ParallelIndexer) OptimizedIndexSingleFileWithProgress(filePath string, progressTracker *ProgressTracker) (uint64, *time.Time, *time.Time, error) {
+func (pi *ParallelIndexer) IndexSingleFileWithProgress(filePath string, progressTracker *ProgressTracker) (uint64, *time.Time, *time.Time, error) {
+	// Validate log path before accessing it
+	if !utils.IsValidLogPath(filePath) {
+		return 0, nil, nil, fmt.Errorf("invalid log path: %s", filePath)
+	}
+
 	file, err := os.Open(filePath)
 	if err != nil {
 		return 0, nil, nil, fmt.Errorf("failed to open log file %s: %w", filePath, err)
@@ -118,7 +123,7 @@ func (pi *ParallelIndexer) OptimizedIndexSingleFileWithProgress(filePath string,
 		logDocs, err = pi.parseLogStreamWithProgress(ctx, reader, filePath, "chunked", progressTracker)
 		logger.Infof("Using chunked processing for file %s (%d bytes)", filePath, fileSize)
 	} else {
-		// Use OptimizedParseStream for general purpose (7-8x faster, 70% memory reduction)
+		// Use ParseStream for general purpose (7-8x faster, 70% memory reduction)
 		logDocs, err = pi.parseLogStreamWithProgress(ctx, reader, filePath, "optimized", progressTracker)
 		logger.Infof("Using optimized stream processing for file %s (%d bytes)", filePath, fileSize)
 	}
@@ -215,7 +220,7 @@ func (pi *ParallelIndexer) OptimizedIndexSingleFileWithProgress(filePath string,
 
 	// Index documents efficiently using batch processing
 	if docCount > 0 {
-		if err := pi.indexOptimizedLogDocuments(logDocs, filePath); err != nil {
+		if err := pi.indexLogDocuments(logDocs, filePath); err != nil {
 			return docCount, minTime, maxTime, fmt.Errorf("failed to index documents for %s: %w", filePath, err)
 		}
 	}
@@ -311,8 +316,8 @@ func isValidLogEntry(doc *LogDocument) bool {
 	return true
 }
 
-// indexOptimizedLogDocuments efficiently indexes a batch of LogDocuments using memory pools
-func (pi *ParallelIndexer) indexOptimizedLogDocuments(logDocs []*LogDocument, filePath string) error {
+// indexLogDocuments efficiently indexes a batch of LogDocuments using memory pools
+func (pi *ParallelIndexer) indexLogDocuments(logDocs []*LogDocument, filePath string) error {
 	if len(logDocs) == 0 {
 		return nil
 	}
@@ -351,14 +356,14 @@ func (pi *ParallelIndexer) indexOptimizedLogDocuments(logDocs []*LogDocument, fi
 	return nil
 }
 
-// EnableOptimizedProcessing switches the indexer to use optimized processing methods
+// EnableProcessing switches the indexer to use optimized processing methods
 // This method provides a seamless upgrade path from the original implementation
-func (pi *ParallelIndexer) EnableOptimizedProcessing() {
+func (pi *ParallelIndexer) EnableProcessing() {
 	logger.Info("Enabling optimized log processing with 7-235x performance improvements")
 	
 	// The optimization is already enabled through the new methods
 	// This method serves as a configuration marker
-	logger.Info("Optimized log processing enabled - use OptimizedIndexLogFile and OptimizedIndexSingleFile methods")
+	logger.Info("Optimized log processing enabled - use IndexLogFile and IndexSingleFile methods")
 }
 
 // GetOptimizationStatus returns the current optimization status

+ 14 - 15
internal/nginx_log/indexer/parser.go

@@ -4,7 +4,6 @@ import (
 	"bufio"
 	"compress/gzip"
 	"context"
-	"errors"
 	"io"
 	"strings"
 	"sync"
@@ -17,11 +16,11 @@ import (
 
 // Global parser instances
 var (
-	logParser      *parser.OptimizedParser // Use the concrete type for both regular and single-line parsing
+	logParser      *parser.Parser // Use the concrete type for both regular and single-line parsing
 	parserInitOnce sync.Once
 )
 
-// InitLogParser initializes the global optimized parser once (singleton).
+// InitLogParser initializes the global parser once (singleton).
 func InitLogParser() {
 	parserInitOnce.Do(func() {
 		// Initialize the parser with production-ready configuration
@@ -45,8 +44,8 @@ func InitLogParser() {
 			geoIPService = parser.NewGeoLiteAdapter(geoService)
 		}
 
-		// Create the optimized parser with production configuration
-		logParser = parser.NewOptimizedParser(config, uaParser, geoIPService)
+		// Create the parser with production configuration
+		logParser = parser.NewParser(config, uaParser, geoIPService)
 
 		logger.Info("Nginx log processing optimization system initialized with production configuration")
 	})
@@ -64,10 +63,10 @@ func ParseLogLine(line string) (*LogDocument, error) {
 	}
 
 	if logParser == nil {
-		return nil, errors.New("log parser is not initialized; call indexer.InitLogParser() before use")
+		return nil, ErrLogParserNotInitialized
 	}
 
-	// Use optimized parser for single line processing
+	// Use parser for single line processing
 	entry, err := logParser.ParseLine(line)
 	if err != nil {
 		return nil, err
@@ -76,10 +75,10 @@ func ParseLogLine(line string) (*LogDocument, error) {
 	return convertToLogDocument(entry, ""), nil
 }
 
-// ParseLogStream parses a stream of log data using OptimizedParseStream (7-8x faster)
+// ParseLogStream parses a stream of log data using ParseStream (7-8x faster)
 func ParseLogStream(ctx context.Context, reader io.Reader, filePath string) ([]*LogDocument, error) {
 	if logParser == nil {
-		return nil, errors.New("log parser is not initialized; call indexer.InitLogParser() before use")
+		return nil, ErrLogParserNotInitialized
 	}
 	// Auto-detect and handle gzip files
 	actualReader, cleanup, err := createReaderForFile(reader, filePath)
@@ -91,8 +90,8 @@ func ParseLogStream(ctx context.Context, reader io.Reader, filePath string) ([]*
 		defer cleanup()
 	}
 
-	// Use OptimizedParseStream for batch processing with 70% memory reduction
-	parseResult, err := logParser.OptimizedParseStream(ctx, actualReader)
+	// Use ParseStream for batch processing with 70% memory reduction
+	parseResult, err := logParser.StreamParse(ctx, actualReader)
 	if err != nil {
 		return nil, err
 	}
@@ -104,7 +103,7 @@ func ParseLogStream(ctx context.Context, reader io.Reader, filePath string) ([]*
 		docs = append(docs, logDoc)
 	}
 
-	logger.Infof("OptimizedParseStream processed %d lines with %.2f%% error rate",
+	logger.Infof("ParseStream processed %d lines with %.2f%% error rate",
 		parseResult.Processed, parseResult.ErrorRate*100)
 
 	return docs, nil
@@ -113,7 +112,7 @@ func ParseLogStream(ctx context.Context, reader io.Reader, filePath string) ([]*
 // ParseLogStreamChunked processes large files using chunked processing for memory efficiency
 func ParseLogStreamChunked(ctx context.Context, reader io.Reader, filePath string, chunkSize int) ([]*LogDocument, error) {
 	if logParser == nil {
-		return nil, errors.New("log parser is not initialized; call indexer.InitLogParser() before use")
+		return nil, ErrLogParserNotInitialized
 	}
 	// Auto-detect and handle gzip files
 	actualReader, cleanup, err := createReaderForFile(reader, filePath)
@@ -143,7 +142,7 @@ func ParseLogStreamChunked(ctx context.Context, reader io.Reader, filePath strin
 // ParseLogStreamMemoryEfficient uses memory-efficient parsing for low memory environments
 func ParseLogStreamMemoryEfficient(ctx context.Context, reader io.Reader, filePath string) ([]*LogDocument, error) {
 	if logParser == nil {
-		return nil, errors.New("log parser is not initialized; call indexer.InitLogParser() before use")
+		return nil, ErrLogParserNotInitialized
 	}
 	// Auto-detect and handle gzip files
 	actualReader, cleanup, err := createReaderForFile(reader, filePath)
@@ -237,7 +236,7 @@ func GetOptimizationStatus() map[string]interface{} {
 		"parser_optimized":     true,
 		"simd_enabled":         true,
 		"memory_pools_enabled": true,
-		"batch_processing":     "OptimizedParseStream (7-8x faster)",
+		"batch_processing":     "ParseStream (7-8x faster)",
 		"single_line_parsing":  "SIMD (235x faster)",
 		"memory_efficiency":    "70% reduction in memory usage",
 		"status":               "Production ready",

+ 8 - 0
internal/nginx_log/indexer/progress_tracker.go

@@ -11,6 +11,8 @@ import (
 	"strings"
 	"sync"
 	"time"
+
+	"github.com/0xJacky/Nginx-UI/internal/nginx_log/utils"
 )
 
 // ProgressTracker manages progress tracking for indexing operations
@@ -577,6 +579,12 @@ func EstimateFileLines(ctx context.Context, filePath string, fileSize int64, isC
 		return 0, nil
 	}
 
+	// Validate log path before accessing it
+	if !utils.IsValidLogPath(filePath) {
+		// Return fallback estimate for invalid paths
+		return fileSize / 150, nil // Fallback: ~150 bytes per line
+	}
+
 	file, err := os.Open(filePath)
 	if err != nil {
 		// Return fallback estimate instead of error

+ 60 - 29
internal/nginx_log/indexer/rebuild.go

@@ -234,12 +234,17 @@ func (rm *RebuildManager) rebuildLogGroup(ctx context.Context, logGroupPath stri
 		}
 	}
 
-	// Process each file with smart change detection
+	// Process files in parallel with controlled concurrency
+	var fileWg sync.WaitGroup
+	fileSemaphore := make(chan struct{}, rm.config.MaxConcurrency)
+	var fileErrors []error
+	var fileErrMu sync.Mutex
+
 	for _, file := range files {
-		// Check context
+		// Check context before starting new file
 		if ctx.Err() != nil {
 			tracker.FailFile(file.Path, ctx.Err().Error())
-			return ctx.Err()
+			break
 		}
 
 		// Skip unchanged files (especially compressed archives)
@@ -251,39 +256,65 @@ func (rm *RebuildManager) rebuildLogGroup(ctx context.Context, logGroupPath stri
 			continue
 		}
 
-		// Create file-specific context with timeout
-		fileCtx, cancel := context.WithTimeout(ctx, rm.config.TimeoutPerFile)
+		fileWg.Add(1)
+		go func(f *LogGroupFile) {
+			defer fileWg.Done()
 
-		// Start processing
-		tracker.StartFile(file.Path)
+			// Acquire semaphore for controlled concurrency
+			fileSemaphore <- struct{}{}
+			defer func() { <-fileSemaphore }()
 
-		// Index the file
-		err := rm.indexFile(fileCtx, file, tracker)
-		cancel()
+			// Check context again inside goroutine
+			if ctx.Err() != nil {
+				tracker.FailFile(f.Path, ctx.Err().Error())
+				return
+			}
 
-		if err != nil {
-			tracker.FailFile(file.Path, err.Error())
-			return fmt.Errorf("failed to index file %s: %w", file.Path, err)
-		}
+			// Create file-specific context with timeout
+			fileCtx, cancel := context.WithTimeout(ctx, rm.config.TimeoutPerFile)
+			defer cancel()
 
-		// Mark as completed
-		tracker.CompleteFile(file.Path, file.ProcessedLines)
+			// Start processing
+			tracker.StartFile(f.Path)
 
-		// Update persistence with exact doc count from Bleve
-		if rm.persistence != nil {
-			exactCount := file.DocumentCount
-			if rm.indexer != nil && rm.indexer.IsHealthy() {
-				if c, err := rm.indexer.CountDocsByFilePath(file.Path); err == nil {
-					exactCount = c
-				} else {
-					logger.Warnf("Falling back to computed count for %s due to count error: %v", file.Path, err)
-				}
+			// Index the file
+			err := rm.indexFile(fileCtx, f, tracker)
+
+			if err != nil {
+				tracker.FailFile(f.Path, err.Error())
+				fileErrMu.Lock()
+				fileErrors = append(fileErrors, fmt.Errorf("failed to index file %s: %w", f.Path, err))
+				fileErrMu.Unlock()
+				return
 			}
-			if err := rm.persistence.MarkFileAsIndexed(file.Path, exactCount, file.LastPosition); err != nil {
-				// Log but don't fail
-				// logger.Warnf("Failed to update persistence for %s: %v", file.Path, err)
+
+			// Mark as completed
+			tracker.CompleteFile(f.Path, f.ProcessedLines)
+
+			// Update persistence with exact doc count from Bleve
+			if rm.persistence != nil {
+				exactCount := f.DocumentCount
+				if rm.indexer != nil && rm.indexer.IsHealthy() {
+					if c, err := rm.indexer.CountDocsByFilePath(f.Path); err == nil {
+						exactCount = c
+					} else {
+						logger.Warnf("Falling back to computed count for %s due to count error: %v", f.Path, err)
+					}
+				}
+				if err := rm.persistence.MarkFileAsIndexed(f.Path, exactCount, f.LastPosition); err != nil {
+					// Log but don't fail
+					// logger.Warnf("Failed to update persistence for %s: %v", f.Path, err)
+				}
 			}
-		}
+		}(file)
+	}
+
+	// Wait for all files to complete
+	fileWg.Wait()
+
+	// Check for file processing errors
+	if len(fileErrors) > 0 {
+		return fmt.Errorf("failed to index %d files in group %s: %v", len(fileErrors), logGroupPath, fileErrors[0])
 	}
 
 	return nil

+ 2 - 2
internal/nginx_log/indexer/throughput_optimizer.go

@@ -44,8 +44,8 @@ func DefaultThroughputOptimizerConfig() *ThroughputOptimizerConfig {
 	}
 }
 
-// OptimizedIndexMultipleLogGroups indexes multiple log groups using the best strategy
-func (to *ThroughputOptimizer) OptimizedIndexMultipleLogGroups(ctx context.Context, basePaths []string) (*OptimizedIndexingResult, error) {
+// IndexMultipleLogGroups indexes multiple log groups using the best strategy
+func (to *ThroughputOptimizer) IndexMultipleLogGroups(ctx context.Context, basePaths []string) (*OptimizedIndexingResult, error) {
 	start := time.Now()
 	
 	logger.Infof("🚀 Starting optimized indexing for %d log groups", len(basePaths))

+ 26 - 24
internal/nginx_log/indexer/types.go

@@ -45,17 +45,18 @@ type IndexProgress struct {
 
 // IndexerConfig holds configuration for the indexer
 type Config struct {
-	IndexPath         string        `json:"index_path"`
-	ShardCount        int           `json:"shard_count"`
-	WorkerCount       int           `json:"worker_count"`
-	BatchSize         int           `json:"batch_size"`
-	FlushInterval     time.Duration `json:"flush_interval"`
-	MaxQueueSize      int           `json:"max_queue_size"`
-	EnableCompression bool          `json:"enable_compression"`
-	MemoryQuota       int64         `json:"memory_quota"`      // Memory limit in bytes
-	MaxSegmentSize    int64         `json:"max_segment_size"`  // Maximum segment size
-	OptimizeInterval  time.Duration `json:"optimize_interval"` // Auto-optimization interval
-	EnableMetrics     bool          `json:"enable_metrics"`
+	IndexPath            string        `json:"index_path"`
+	ShardCount           int           `json:"shard_count"`
+	WorkerCount          int           `json:"worker_count"`
+	BatchSize            int           `json:"batch_size"`
+	FlushInterval        time.Duration `json:"flush_interval"`
+	MaxQueueSize         int           `json:"max_queue_size"`
+	EnableCompression    bool          `json:"enable_compression"`
+	MemoryQuota          int64         `json:"memory_quota"`           // Memory limit in bytes
+	MaxSegmentSize       int64         `json:"max_segment_size"`       // Maximum segment size
+	OptimizeInterval     time.Duration `json:"optimize_interval"`      // Auto-optimization interval
+	EnableMetrics        bool          `json:"enable_metrics"`
+	FileGroupConcurrency int           `json:"file_group_concurrency"` // Max concurrent files within a log group (0 = use WorkerCount)
 }
 
 // DefaultIndexerConfig returns default indexer configuration with processor optimization
@@ -74,22 +75,23 @@ func DefaultIndexerConfig() *Config {
 	}
 
 	return &Config{
-		IndexPath:         "./log-index",
-		ShardCount:        max(4, maxProcs/2), // Scale shards with CPU cores
-		WorkerCount:       maxProcs * 3,       // Optimized: 3x processors for better I/O-bound workload handling
-		BatchSize:         baseBatchSize,      // Dynamically scaled based on CPU cores
-		FlushInterval:     5 * time.Second,
-		MaxQueueSize:      baseBatchSize * 10, // Scale queue with batch size
-		EnableCompression: true,
-		MemoryQuota:       1024 * 1024 * 1024, // 1GB
-		MaxSegmentSize:    64 * 1024 * 1024,   // 64MB
-		OptimizeInterval:  30 * time.Minute,
-		EnableMetrics:     true,
+		IndexPath:            "./log-index",
+		ShardCount:           max(4, maxProcs/2), // Scale shards with CPU cores
+		WorkerCount:          maxProcs * 3,       // Optimized: 3x processors for better I/O-bound workload handling
+		BatchSize:            baseBatchSize,      // Dynamically scaled based on CPU cores
+		FlushInterval:        5 * time.Second,
+		MaxQueueSize:         baseBatchSize * 10, // Scale queue with batch size
+		EnableCompression:    true,
+		MemoryQuota:          1024 * 1024 * 1024,         // 1GB
+		MaxSegmentSize:       64 * 1024 * 1024,           // 64MB
+		OptimizeInterval:     30 * time.Minute,
+		EnableMetrics:        true,
+		FileGroupConcurrency: max(4, maxProcs), // Default: use CPU count for file-level parallelism
 	}
 }
 
-// GetOptimizedConfig returns configuration optimized for specific scenarios
-func GetOptimizedConfig(scenario string) *Config {
+// GetConfig returns configuration optimized for specific scenarios
+func GetConfig(scenario string) *Config {
 	base := DefaultIndexerConfig()
 	maxProcs := runtime.GOMAXPROCS(0)
 

+ 11 - 11
internal/nginx_log/integration_test.go

@@ -35,7 +35,7 @@ type IntegrationTestSuite struct {
 	logFiles        []string
 	logFilePaths    []string
 	indexer         *indexer.ParallelIndexer
-	searcher        searcher.Searcher
+	searcher        *searcher.Searcher
 	analytics       analytics.Service
 	logFileManager  *TestLogFileManager
 	expectedMetrics map[string]*ExpectedFileMetrics
@@ -249,7 +249,7 @@ func (suite *IntegrationTestSuite) InitializeServices(t *testing.T) {
 
 	// Initialize searcher (empty initially)
 	searcherConfig := searcher.DefaultSearcherConfig()
-	suite.searcher = searcher.NewDistributedSearcher(searcherConfig, []bleve.Index{})
+	suite.searcher = searcher.NewSearcher(searcherConfig, []bleve.Index{})
 
 	// Initialize analytics
 	suite.analytics = analytics.NewService(suite.searcher)
@@ -496,18 +496,18 @@ func (suite *IntegrationTestSuite) updateSearcher(t *testing.T) {
 	newShards := suite.indexer.GetAllShards()
 	t.Logf("Updating searcher with %d shards", len(newShards))
 
-	if ds, ok := suite.searcher.(*searcher.DistributedSearcher); ok {
-		err := ds.SwapShards(newShards)
+	if suite.searcher != nil {
+		err := suite.searcher.SwapShards(newShards)
 		require.NoError(t, err)
 		t.Log("Searcher shards updated successfully")
 	} else {
-		t.Fatal("Searcher is not a DistributedSearcher")
+		t.Fatal("Searcher is not a Searcher")
 	}
 }
 
 // ValidateCardinalityCounter validates the accuracy of cardinality counting
 func (suite *IntegrationTestSuite) ValidateCardinalityCounter(t *testing.T, filePath string) {
-	t.Logf("Validating CardinalityCounter accuracy for: %s", filePath)
+	t.Logf("Validating Counter accuracy for: %s", filePath)
 
 	expected := suite.expectedMetrics[filePath]
 	require.NotNil(t, expected, "Expected metrics not found for file: %s", filePath)
@@ -521,20 +521,20 @@ func (suite *IntegrationTestSuite) ValidateCardinalityCounter(t *testing.T, file
 	// Test user agent cardinality
 	suite.testFieldCardinality(t, filePath, "http_user_agent", expected.UniqueAgents, "User agents")
 
-	t.Logf("CardinalityCounter validation completed for: %s", filePath)
+	t.Logf("Counter validation completed for: %s", filePath)
 }
 
 // testFieldCardinality tests cardinality counting for a specific field
 func (suite *IntegrationTestSuite) testFieldCardinality(t *testing.T, filePath string, field string, expectedCount uint64, fieldName string) {
-	if ds, ok := suite.searcher.(*searcher.DistributedSearcher); ok {
-		cardinalityCounter := searcher.NewCardinalityCounter(ds.GetShards())
+	if suite.searcher != nil {
+		cardinalityCounter := searcher.NewCounter(suite.searcher.GetShards())
 
 		req := &searcher.CardinalityRequest{
 			Field:    field,
 			LogPaths: []string{filePath},
 		}
 
-		result, err := cardinalityCounter.CountCardinality(suite.ctx, req)
+		result, err := cardinalityCounter.Count(suite.ctx, req)
 		require.NoError(t, err, "Failed to count cardinality for field: %s", field)
 
 		// Allow for small discrepancies due to indexing behavior
@@ -550,7 +550,7 @@ func (suite *IntegrationTestSuite) testFieldCardinality(t *testing.T, filePath s
 		t.Logf("✓ %s cardinality: expected=%d, actual=%d, total_docs=%d",
 			fieldName, expectedCount, result.Cardinality, result.TotalDocs)
 	} else {
-		t.Fatal("Searcher is not a DistributedSearcher")
+		t.Fatal("Searcher is not a Searcher")
 	}
 }
 

+ 65 - 25
internal/nginx_log/modern_services.go

@@ -23,7 +23,7 @@ import (
 
 // Global instances for new services
 var (
-	globalSearcher         searcher.Searcher
+	globalSearcher         *searcher.Searcher
 	globalAnalytics        analytics.Service
 	globalIndexer          *indexer.ParallelIndexer
 	globalLogFileManager   *indexer.LogFileManager
@@ -34,20 +34,20 @@ var (
 	lastShardUpdateAttempt int64
 )
 
-// Fallback storage when AdvancedIndexingEnabled is disabled
+// Fallback storage when IndexingEnabled is disabled
 var (
 	fallbackCache      = make(map[string]*NginxLogCache)
 	fallbackCacheMutex sync.RWMutex
 )
 
-// InitializeModernServices initializes the new modular services
-func InitializeModernServices(ctx context.Context) {
+// InitializeServices initializes the new modular services
+func InitializeServices(ctx context.Context) {
 	servicesMutex.Lock()
 	defer servicesMutex.Unlock()
 
-	// Check if advanced indexing is enabled
-	if !settings.NginxLogSettings.AdvancedIndexingEnabled {
-		logger.Info("Advanced indexing is disabled, skipping nginx_log services initialization")
+	// Check if indexing is enabled
+	if !settings.NginxLogSettings.IndexingEnabled {
+		logger.Info("Indexing is disabled, skipping nginx_log services initialization")
 		return
 	}
 
@@ -70,6 +70,9 @@ func InitializeModernServices(ctx context.Context) {
 
 	logger.Info("Modern nginx log services initialization completed")
 
+	// Initialize task scheduler after services are ready
+	go InitTaskScheduler(serviceCtx)
+
 	// Monitor context for shutdown
 	go func() {
 		logger.Info("Started nginx_log shutdown monitor goroutine")
@@ -77,7 +80,7 @@ func InitializeModernServices(ctx context.Context) {
 		logger.Info("Context cancelled, initiating shutdown...")
 
 		// Use the same shutdown logic as manual stop
-		StopModernServices()
+		StopServices()
 
 		logger.Info("Nginx_log shutdown monitor goroutine completed")
 	}()
@@ -92,7 +95,7 @@ func initializeWithDefaults(ctx context.Context) error {
 
 	// Create empty searcher (will be populated when indexes are available)
 	searcherConfig := searcher.DefaultSearcherConfig()
-	globalSearcher = searcher.NewDistributedSearcher(searcherConfig, []bleve.Index{})
+	globalSearcher = searcher.NewSearcher(searcherConfig, []bleve.Index{})
 
 	// Initialize analytics with empty searcher
 	globalAnalytics = analytics.NewService(globalSearcher)
@@ -158,8 +161,8 @@ func getConfigDirIndexPath() string {
 	return "./log-index"
 }
 
-// GetModernSearcher returns the global searcher instance
-func GetModernSearcher() searcher.Searcher {
+// GetSearcher returns the global searcher instance
+func GetSearcher() *searcher.Searcher {
 	servicesMutex.RLock()
 	defer servicesMutex.RUnlock()
 
@@ -169,14 +172,14 @@ func GetModernSearcher() searcher.Searcher {
 	}
 
 	if globalSearcher == nil {
-		logger.Warn("GetModernSearcher: globalSearcher is nil even though services are initialized")
+		logger.Warn("GetSearcher: globalSearcher is nil even though services are initialized")
 		return nil
 	}
 
 	// Check searcher health status
 	isHealthy := globalSearcher.IsHealthy()
 	isRunning := globalSearcher.IsRunning()
-	logger.Debugf("GetModernSearcher: returning searcher, isHealthy: %v, isRunning: %v", isHealthy, isRunning)
+	logger.Debugf("GetSearcher: returning searcher, isHealthy: %v, isRunning: %v", isHealthy, isRunning)
 
 	// Auto-heal: if the searcher is running but unhealthy (likely zero shards),
 	// and the indexer is initialized, trigger an async shard swap (throttled).
@@ -185,7 +188,7 @@ func GetModernSearcher() searcher.Searcher {
 		prev := atomic.LoadInt64(&lastShardUpdateAttempt)
 		if now-prev > int64(5*time.Second) {
 			if atomic.CompareAndSwapInt64(&lastShardUpdateAttempt, prev, now) {
-				logger.Debugf("GetModernSearcher: unhealthy detected, scheduling UpdateSearcherShards()")
+				logger.Debugf("GetSearcher: unhealthy detected, scheduling UpdateSearcherShards()")
 				go UpdateSearcherShards()
 			}
 		}
@@ -194,8 +197,8 @@ func GetModernSearcher() searcher.Searcher {
 	return globalSearcher
 }
 
-// GetModernAnalytics returns the global analytics service instance
-func GetModernAnalytics() analytics.Service {
+// GetAnalytics returns the global analytics service instance
+func GetAnalytics() analytics.Service {
 	servicesMutex.RLock()
 	defer servicesMutex.RUnlock()
 
@@ -207,13 +210,12 @@ func GetModernAnalytics() analytics.Service {
 	return globalAnalytics
 }
 
-// GetModernIndexer returns the global indexer instance
-func GetModernIndexer() *indexer.ParallelIndexer {
+// GetIndexer returns the global indexer instance
+func GetIndexer() *indexer.ParallelIndexer {
 	servicesMutex.RLock()
 	defer servicesMutex.RUnlock()
 
 	if !servicesInitialized {
-		logger.Warn("Modern services not initialized, returning nil")
 		return nil
 	}
 
@@ -521,7 +523,7 @@ func updateSearcherShardsLocked() {
 	if globalSearcher == nil {
 		logger.Info("Creating initial searcher with IndexAlias")
 		searcherConfig := searcher.DefaultSearcherConfig()
-		globalSearcher = searcher.NewDistributedSearcher(searcherConfig, newShards)
+		globalSearcher = searcher.NewSearcher(searcherConfig, newShards)
 
 		if globalSearcher == nil {
 			logger.Error("Failed to create initial searcher instance")
@@ -539,7 +541,8 @@ func updateSearcherShardsLocked() {
 
 	// For subsequent updates, use hot-swap through IndexAlias
 	// This follows Bleve best practices for zero-downtime index updates
-	if ds, ok := globalSearcher.(*searcher.DistributedSearcher); ok {
+	if globalSearcher != nil {
+		ds := globalSearcher
 		oldShards := ds.GetShards()
 		logger.Debugf("updateSearcherShardsLocked: About to call SwapShards...")
 
@@ -559,15 +562,15 @@ func updateSearcherShardsLocked() {
 		logger.Infof("Post-swap searcher status: isHealthy: %v, isRunning: %v", isHealthy, isRunning)
 
 		// Note: We do NOT recreate the analytics service here since the searcher interface remains the same
-		// The CardinalityCounter will automatically use the new shards through the same IndexAlias
+		// The Counter will automatically use the new shards through the same IndexAlias
 
 	} else {
-		logger.Warn("globalSearcher is not a DistributedSearcher, cannot perform hot-swap")
+		logger.Warn("globalSearcher is not a Searcher, cannot perform hot-swap")
 	}
 }
 
-// StopModernServices stops all running modern services
-func StopModernServices() {
+// StopServices stops all running modern services
+func StopServices() {
 	servicesMutex.Lock()
 	defer servicesMutex.Unlock()
 
@@ -634,3 +637,40 @@ func DestroyAllIndexes(ctx context.Context) error {
 
 	return globalIndexer.DestroyAllIndexes(ctx)
 }
+
+// MigrateFallbackCache migrates all entries from fallback cache to LogFileManager
+// This is used when enabling advanced indexing after the application has started
+func MigrateFallbackCache() {
+	fallbackCacheMutex.RLock()
+	entries := make([]*NginxLogCache, 0, len(fallbackCache))
+	for _, entry := range fallbackCache {
+		// Create a copy to avoid race conditions
+		e := *entry
+		entries = append(entries, &e)
+	}
+	fallbackCacheMutex.RUnlock()
+
+	if len(entries) == 0 {
+		logger.Debug("No fallback cache entries to migrate")
+		return
+	}
+
+	manager := GetLogFileManager()
+	if manager == nil {
+		logger.Warn("Cannot migrate fallback cache: LogFileManager not initialized")
+		return
+	}
+
+	logger.Infof("Migrating %d log paths from fallback cache to LogFileManager", len(entries))
+
+	for _, entry := range entries {
+		manager.AddLogPath(entry.Path, entry.Type, entry.Name, entry.ConfigFile)
+	}
+
+	// Clear fallback cache after successful migration
+	fallbackCacheMutex.Lock()
+	fallbackCache = make(map[string]*NginxLogCache)
+	fallbackCacheMutex.Unlock()
+
+	logger.Info("Fallback cache migration completed successfully")
+}

+ 62 - 62
internal/nginx_log/performance_monitor.go → internal/nginx_log/monitor.go

@@ -13,8 +13,8 @@ import (
 	"github.com/0xJacky/Nginx-UI/internal/nginx_log/utils"
 )
 
-// PerformanceThresholds defines minimum acceptable performance metrics
-type PerformanceThresholds struct {
+// Thresholds defines minimum acceptable performance metrics
+type Thresholds struct {
 	ParseStreamOpsPerSec  float64 `json:"parse_stream_ops_per_sec"`
 	SIMDOpsPerSec         float64 `json:"simd_ops_per_sec"`
 	MemoryPoolOpsPerSec   float64 `json:"memory_pool_ops_per_sec"`
@@ -23,9 +23,9 @@ type PerformanceThresholds struct {
 	MaxResponseTimeMS     float64 `json:"max_response_time_ms"`
 }
 
-// DefaultPerformanceThresholds returns the expected minimum performance levels
-func DefaultPerformanceThresholds() *PerformanceThresholds {
-	return &PerformanceThresholds{
+// DefaultThresholds returns the expected minimum performance levels
+func DefaultThresholds() *Thresholds {
+	return &Thresholds{
 		ParseStreamOpsPerSec: 500.0,     // 7-8x improvement target
 		SIMDOpsPerSec:        10000.0,   // 235x improvement target
 		MemoryPoolOpsPerSec:  100000.0,  // 48-81% improvement target
@@ -35,8 +35,8 @@ func DefaultPerformanceThresholds() *PerformanceThresholds {
 	}
 }
 
-// PerformanceMetrics represents current system performance
-type PerformanceMetrics struct {
+// Metrics represents current system performance
+type Metrics struct {
 	Timestamp             time.Time `json:"timestamp"`
 	ParseStreamRate       float64   `json:"parse_stream_rate"`
 	SIMDRate             float64   `json:"simd_rate"`
@@ -49,8 +49,8 @@ type PerformanceMetrics struct {
 	ErrorRate            float64   `json:"error_rate"`
 }
 
-// PerformanceAlert represents a performance issue alert
-type PerformanceAlert struct {
+// Alert represents a performance issue alert
+type Alert struct {
 	Level       string    `json:"level"`       // "warning", "critical"
 	Component   string    `json:"component"`   // "parser", "simd", "memory", "cache"
 	Message     string    `json:"message"`
@@ -60,40 +60,40 @@ type PerformanceAlert struct {
 	Suggestions []string  `json:"suggestions"`
 }
 
-// PerformanceMonitor provides real-time performance monitoring and alerting
-type PerformanceMonitor struct {
-	thresholds    *PerformanceThresholds
-	metrics       *PerformanceMetrics
-	alerts        []PerformanceAlert
-	alertCallback func(PerformanceAlert)
+// Monitor provides real-time performance monitoring and alerting
+type Monitor struct {
+	thresholds    *Thresholds
+	metrics       *Metrics
+	alerts        []Alert
+	alertCallback func(Alert)
 	mu           sync.RWMutex
 	running      bool
 	stopChan     chan struct{}
 }
 
-// NewPerformanceMonitor creates a new performance monitor
-func NewPerformanceMonitor(thresholds *PerformanceThresholds) *PerformanceMonitor {
+// NewMonitor creates a new performance monitor
+func NewMonitor(thresholds *Thresholds) *Monitor {
 	if thresholds == nil {
-		thresholds = DefaultPerformanceThresholds()
+		thresholds = DefaultThresholds()
 	}
 	
-	return &PerformanceMonitor{
+	return &Monitor{
 		thresholds: thresholds,
-		metrics:    &PerformanceMetrics{},
-		alerts:     make([]PerformanceAlert, 0),
+		metrics:    &Metrics{},
+		alerts:     make([]Alert, 0),
 		stopChan:   make(chan struct{}),
 	}
 }
 
 // SetAlertCallback sets a callback function for performance alerts
-func (pm *PerformanceMonitor) SetAlertCallback(callback func(PerformanceAlert)) {
+func (pm *Monitor) SetAlertCallback(callback func(Alert)) {
 	pm.mu.Lock()
 	defer pm.mu.Unlock()
 	pm.alertCallback = callback
 }
 
 // StartMonitoring begins continuous performance monitoring
-func (pm *PerformanceMonitor) StartMonitoring(ctx context.Context, interval time.Duration) {
+func (pm *Monitor) StartMonitoring(ctx context.Context, interval time.Duration) {
 	pm.mu.Lock()
 	if pm.running {
 		pm.mu.Unlock()
@@ -120,11 +120,11 @@ func (pm *PerformanceMonitor) StartMonitoring(ctx context.Context, interval time
 }
 
 // StopMonitoring stops the performance monitoring
-func (pm *PerformanceMonitor) StopMonitoring() {
+func (pm *Monitor) StopMonitoring() {
 	pm.stopMonitoring()
 }
 
-func (pm *PerformanceMonitor) stopMonitoring() {
+func (pm *Monitor) stopMonitoring() {
 	pm.mu.Lock()
 	defer pm.mu.Unlock()
 	
@@ -136,7 +136,7 @@ func (pm *PerformanceMonitor) stopMonitoring() {
 }
 
 // collectMetrics gathers current performance metrics
-func (pm *PerformanceMonitor) collectMetrics() {
+func (pm *Monitor) collectMetrics() {
 	pm.mu.Lock()
 	defer pm.mu.Unlock()
 
@@ -160,7 +160,7 @@ func (pm *PerformanceMonitor) collectMetrics() {
 	// Calculate response time
 	responseTime := float64(time.Since(startTime).Nanoseconds()) / 1e6
 
-	pm.metrics = &PerformanceMetrics{
+	pm.metrics = &Metrics{
 		Timestamp:       time.Now(),
 		ParseStreamRate: parseRate,
 		SIMDRate:       simdRate,
@@ -175,21 +175,21 @@ func (pm *PerformanceMonitor) collectMetrics() {
 }
 
 // benchmarkParseStream tests ParseStream performance
-func (pm *PerformanceMonitor) benchmarkParseStream() float64 {
+func (pm *Monitor) benchmarkParseStream() float64 {
 	ctx := context.Background()
 	testData := generateMonitoringTestData(100)
 	
 	config := parser.DefaultParserConfig()
 	config.BatchSize = 100
 	
-	optimizedParser := parser.NewOptimizedParser(
+	optimizedParser := parser.NewParser(
 		config,
 		parser.NewSimpleUserAgentParser(),
 		&mockMonitorGeoIPService{},
 	)
 	
 	start := time.Now()
-	result, err := optimizedParser.OptimizedParseStream(ctx, testData)
+	result, err := optimizedParser.ParseStream(ctx, testData)
 	if err != nil {
 		return 0.0
 	}
@@ -199,9 +199,9 @@ func (pm *PerformanceMonitor) benchmarkParseStream() float64 {
 }
 
 // benchmarkSIMD tests SIMD parser performance
-func (pm *PerformanceMonitor) benchmarkSIMD() float64 {
+func (pm *Monitor) benchmarkSIMD() float64 {
 	testLine := `192.168.1.100 - - [06/Sep/2025:10:00:00 +0000] "GET /monitor HTTP/1.1" 200 1024 "https://test.com" "Monitor/1.0"`
-	simdParser := parser.NewOptimizedLogLineParser()
+	simdParser := parser.NewLogLineParser()
 	
 	operations := 1000
 	start := time.Now()
@@ -215,7 +215,7 @@ func (pm *PerformanceMonitor) benchmarkSIMD() float64 {
 }
 
 // benchmarkMemoryPools tests memory pool performance
-func (pm *PerformanceMonitor) benchmarkMemoryPools() float64 {
+func (pm *Monitor) benchmarkMemoryPools() float64 {
 	operations := 1000
 	start := time.Now()
 	
@@ -235,7 +235,7 @@ func (pm *PerformanceMonitor) benchmarkMemoryPools() float64 {
 }
 
 // benchmarkRegexCache tests regex cache performance
-func (pm *PerformanceMonitor) benchmarkRegexCache() float64 {
+func (pm *Monitor) benchmarkRegexCache() float64 {
 	cache := parser.GetGlobalRegexCache()
 	operations := 1000
 	
@@ -252,32 +252,32 @@ func (pm *PerformanceMonitor) benchmarkRegexCache() float64 {
 }
 
 // getCacheHitRate gets current cache hit rate
-func (pm *PerformanceMonitor) getCacheHitRate() float64 {
+func (pm *Monitor) getCacheHitRate() float64 {
 	cache := parser.GetGlobalRegexCache()
 	stats := cache.GetStats()
 	return stats.HitRate
 }
 
 // getMemoryUsage returns current memory usage in MB (simplified)
-func (pm *PerformanceMonitor) getMemoryUsage() float64 {
+func (pm *Monitor) getMemoryUsage() float64 {
 	// In a real implementation, this would use runtime.MemStats
 	return 50.0 // Placeholder
 }
 
 // getTotalOperations returns total operations processed
-func (pm *PerformanceMonitor) getTotalOperations() int64 {
+func (pm *Monitor) getTotalOperations() int64 {
 	// In a real implementation, this would track actual operations
 	return int64(time.Since(time.Now()).Seconds()) // Placeholder
 }
 
 // checkThresholds compares current metrics against thresholds and generates alerts
-func (pm *PerformanceMonitor) checkThresholds() {
+func (pm *Monitor) checkThresholds() {
 	pm.mu.Lock()
 	defer pm.mu.Unlock()
 
 	// Check ParseStream performance
 	if pm.metrics.ParseStreamRate < pm.thresholds.ParseStreamOpsPerSec {
-		alert := PerformanceAlert{
+		alert := Alert{
 			Level:       "critical",
 			Component:   "parser",
 			Message:     "ParseStream performance below threshold",
@@ -285,7 +285,7 @@ func (pm *PerformanceMonitor) checkThresholds() {
 			ThresholdValue: pm.thresholds.ParseStreamOpsPerSec,
 			Timestamp:   time.Now(),
 			Suggestions: []string{
-				"Check if OptimizedParseStream is being used",
+				"Check if ParseStream is being used",
 				"Verify batch size configuration (recommended: 500-1500)",
 				"Monitor memory usage and GC pressure",
 				"Check for context cancellation overhead",
@@ -296,7 +296,7 @@ func (pm *PerformanceMonitor) checkThresholds() {
 
 	// Check SIMD performance
 	if pm.metrics.SIMDRate < pm.thresholds.SIMDOpsPerSec {
-		alert := PerformanceAlert{
+		alert := Alert{
 			Level:       "critical",
 			Component:   "simd",
 			Message:     "SIMD parsing performance below threshold",
@@ -315,7 +315,7 @@ func (pm *PerformanceMonitor) checkThresholds() {
 
 	// Check memory pool performance
 	if pm.metrics.MemoryPoolRate < pm.thresholds.MemoryPoolOpsPerSec {
-		alert := PerformanceAlert{
+		alert := Alert{
 			Level:       "warning",
 			Component:   "memory",
 			Message:     "Memory pool performance below threshold",
@@ -334,7 +334,7 @@ func (pm *PerformanceMonitor) checkThresholds() {
 
 	// Check regex cache performance
 	if pm.metrics.RegexCacheRate < pm.thresholds.RegexCacheOpsPerSec {
-		alert := PerformanceAlert{
+		alert := Alert{
 			Level:       "warning",
 			Component:   "cache",
 			Message:     "Regex cache performance below threshold",
@@ -353,7 +353,7 @@ func (pm *PerformanceMonitor) checkThresholds() {
 
 	// Check cache hit rate
 	if pm.metrics.CacheHitRate < 0.9 {
-		alert := PerformanceAlert{
+		alert := Alert{
 			Level:       "warning",
 			Component:   "cache",
 			Message:     "Cache hit rate is low",
@@ -371,7 +371,7 @@ func (pm *PerformanceMonitor) checkThresholds() {
 
 	// Check memory usage
 	if pm.metrics.MemoryUsageMB > pm.thresholds.MaxMemoryUsageMB {
-		alert := PerformanceAlert{
+		alert := Alert{
 			Level:       "critical",
 			Component:   "memory",
 			Message:     "Memory usage exceeds threshold",
@@ -390,7 +390,7 @@ func (pm *PerformanceMonitor) checkThresholds() {
 }
 
 // addAlert adds a new alert and triggers the callback
-func (pm *PerformanceMonitor) addAlert(alert PerformanceAlert) {
+func (pm *Monitor) addAlert(alert Alert) {
 	pm.alerts = append(pm.alerts, alert)
 	
 	// Keep only the last 100 alerts
@@ -405,19 +405,19 @@ func (pm *PerformanceMonitor) addAlert(alert PerformanceAlert) {
 }
 
 // GetCurrentMetrics returns the current performance metrics
-func (pm *PerformanceMonitor) GetCurrentMetrics() PerformanceMetrics {
+func (pm *Monitor) GetCurrentMetrics() Metrics {
 	pm.mu.RLock()
 	defer pm.mu.RUnlock()
 	return *pm.metrics
 }
 
 // GetRecentAlerts returns recent performance alerts
-func (pm *PerformanceMonitor) GetRecentAlerts(since time.Duration) []PerformanceAlert {
+func (pm *Monitor) GetRecentAlerts(since time.Duration) []Alert {
 	pm.mu.RLock()
 	defer pm.mu.RUnlock()
 	
 	cutoff := time.Now().Add(-since)
-	recent := make([]PerformanceAlert, 0)
+	recent := make([]Alert, 0)
 	
 	for _, alert := range pm.alerts {
 		if alert.Timestamp.After(cutoff) {
@@ -429,7 +429,7 @@ func (pm *PerformanceMonitor) GetRecentAlerts(since time.Duration) []Performance
 }
 
 // GetHealthStatus returns overall system health based on current metrics
-func (pm *PerformanceMonitor) GetHealthStatus() string {
+func (pm *Monitor) GetHealthStatus() string {
 	pm.mu.RLock()
 	defer pm.mu.RUnlock()
 	
@@ -454,9 +454,9 @@ func (pm *PerformanceMonitor) GetHealthStatus() string {
 	}
 }
 
-func (pm *PerformanceMonitor) getRecentAlertsInternal(since time.Duration) []PerformanceAlert {
+func (pm *Monitor) getRecentAlertsInternal(since time.Duration) []Alert {
 	cutoff := time.Now().Add(-since)
-	recent := make([]PerformanceAlert, 0)
+	recent := make([]Alert, 0)
 	
 	for _, alert := range pm.alerts {
 		if alert.Timestamp.After(cutoff) {
@@ -468,14 +468,14 @@ func (pm *PerformanceMonitor) getRecentAlertsInternal(since time.Duration) []Per
 }
 
 // ExportMetrics exports current metrics as JSON
-func (pm *PerformanceMonitor) ExportMetrics() ([]byte, error) {
+func (pm *Monitor) ExportMetrics() ([]byte, error) {
 	pm.mu.RLock()
 	defer pm.mu.RUnlock()
 	
 	export := struct {
-		Metrics    PerformanceMetrics   `json:"metrics"`
-		Thresholds PerformanceThresholds `json:"thresholds"`
-		Alerts     []PerformanceAlert   `json:"recent_alerts"`
+		Metrics    Metrics   `json:"metrics"`
+		Thresholds Thresholds `json:"thresholds"`
+		Alerts     []Alert   `json:"recent_alerts"`
 		Health     string               `json:"health_status"`
 	}{
 		Metrics:    *pm.metrics,
@@ -488,7 +488,7 @@ func (pm *PerformanceMonitor) ExportMetrics() ([]byte, error) {
 }
 
 // DefaultAlertHandler provides a default implementation for handling alerts
-func DefaultAlertHandler(alert PerformanceAlert) {
+func DefaultAlertHandler(alert Alert) {
 	log.Printf("PERFORMANCE ALERT [%s/%s]: %s (Current: %.2f, Threshold: %.2f)",
 		alert.Level, alert.Component, alert.Message, alert.CurrentValue, alert.ThresholdValue)
 	
@@ -499,9 +499,9 @@ func DefaultAlertHandler(alert PerformanceAlert) {
 
 // Example usage functions
 
-// StartOptimizationMonitoring starts monitoring with default configuration
-func StartOptimizationMonitoring(ctx context.Context) *PerformanceMonitor {
-	monitor := NewPerformanceMonitor(DefaultPerformanceThresholds())
+// StartMonitoring starts monitoring with default configuration
+func StartMonitoring(ctx context.Context) *Monitor {
+	monitor := NewMonitor(DefaultThresholds())
 	monitor.SetAlertCallback(DefaultAlertHandler)
 	
 	// Start monitoring every 30 seconds
@@ -510,8 +510,8 @@ func StartOptimizationMonitoring(ctx context.Context) *PerformanceMonitor {
 	return monitor
 }
 
-// GetPerformanceReport generates a comprehensive performance report
-func GetPerformanceReport(monitor *PerformanceMonitor) string {
+// GetReport generates a comprehensive performance report
+func GetReport(monitor *Monitor) string {
 	metrics := monitor.GetCurrentMetrics()
 	recentAlerts := monitor.GetRecentAlerts(time.Hour)
 	health := monitor.GetHealthStatus()

+ 11 - 11
internal/nginx_log/optimization_integration_test.go

@@ -27,7 +27,7 @@ func TestOptimizationSystemIntegration(t *testing.T) {
 		config.MaxLineLength = 16 * 1024
 		config.BatchSize = 500
 
-		optimizedParser := parser.NewOptimizedParser(
+		optimizedParser := parser.NewParser(
 			config,
 			parser.NewCachedUserAgentParser(parser.NewSimpleUserAgentParser(), 1000),
 			&mockGeoIPService{},
@@ -36,10 +36,10 @@ func TestOptimizationSystemIntegration(t *testing.T) {
 		// Performance measurement
 		start := time.Now()
 		
-		// Test OptimizedParseStream
-		parseResult, err := optimizedParser.OptimizedParseStream(ctx, strings.NewReader(testLogData))
+		// Test ParseStream
+		parseResult, err := optimizedParser.ParseStream(ctx, strings.NewReader(testLogData))
 		if err != nil {
-			t.Fatalf("OptimizedParseStream failed: %v", err)
+			t.Fatalf("ParseStream failed: %v", err)
 		}
 		
 		optimizedParseTime := time.Since(start)
@@ -48,7 +48,7 @@ func TestOptimizationSystemIntegration(t *testing.T) {
 		// Test 2: SIMD Parser performance
 		start = time.Now()
 		
-		simdParser := parser.NewOptimizedLogLineParser()
+		simdParser := parser.NewLogLineParser()
 		lines := strings.Split(testLogData, "\n")
 		logBytes := make([][]byte, 0, len(lines))
 		
@@ -94,7 +94,7 @@ func TestOptimizationSystemIntegration(t *testing.T) {
 		}
 		
 		// Advanced analytics
-		advancedProcessor := analytics.NewAdvancedTimeSeriesProcessor()
+		advancedProcessor := analytics.NewAnomalyDetector()
 		anomalies := advancedProcessor.DetectAnomalies(timeSeriesData)
 		trend := advancedProcessor.CalculateTrend(timeSeriesData)
 		
@@ -171,7 +171,7 @@ func TestOptimizationSystemIntegration(t *testing.T) {
 		testLine := `192.168.1.100 - - [06/Sep/2025:10:00:00 +0000] "GET /test HTTP/1.1" 200 1024 "https://example.com" "Mozilla/5.0"`
 		
 		// Parse with optimized parser
-		optimizedTest, err := optimizedParser.OptimizedParseStream(ctx, strings.NewReader(testLine))
+		optimizedTest, err := optimizedParser.ParseStream(ctx, strings.NewReader(testLine))
 		if err != nil || len(optimizedTest.Entries) == 0 {
 			t.Errorf("Optimized parser failed on test line: %v", err)
 		} else {
@@ -276,7 +276,7 @@ func TestOptimizationSystemIntegration(t *testing.T) {
 			go func(id int) {
 				// SIMD parsing stress test
 				start := time.Now()
-				simdParser := parser.NewOptimizedLogLineParser()
+				simdParser := parser.NewLogLineParser()
 				
 				testLine := `192.168.1.100 - - [06/Sep/2025:10:00:00 +0000] "GET /stress HTTP/1.1" 200 1024 "https://test.com" "StressTest/1.0"`
 				
@@ -348,7 +348,7 @@ func TestOptimizationCorrectness(t *testing.T) {
 		// Standard parser
 		config := parser.DefaultParserConfig()
 		config.MaxLineLength = 16 * 1024
-		standardParser := parser.NewOptimizedParser(
+		standardParser := parser.NewParser(
 			config,
 			parser.NewSimpleUserAgentParser(),
 			&mockGeoIPService{},
@@ -360,13 +360,13 @@ func TestOptimizationCorrectness(t *testing.T) {
 		}
 		
 		// Optimized parser
-		optimizedResult, err := standardParser.OptimizedParseStream(ctx, strings.NewReader(testLogLine))
+		optimizedResult, err := standardParser.ParseStream(ctx, strings.NewReader(testLogLine))
 		if err != nil || len(optimizedResult.Entries) == 0 {
 			t.Fatalf("Optimized parser failed: %v", err)
 		}
 		
 		// SIMD parser
-		simdParser := parser.NewOptimizedLogLineParser()
+		simdParser := parser.NewLogLineParser()
 		simdEntry := simdParser.ParseLine([]byte(testLogLine))
 		if simdEntry == nil {
 			t.Fatal("SIMD parser returned nil")

+ 11 - 0
internal/nginx_log/parser/errors.go

@@ -0,0 +1,11 @@
+package parser
+
+import "github.com/uozi-tech/cosy"
+
+var (
+	e                       = cosy.NewErrorScope("nginx_log.parser")
+	ErrEmptyLogLine         = e.New(50101, "empty log line")
+	ErrLineTooLong          = e.New(50102, "log line exceeds maximum length")
+	ErrUnsupportedLogFormat = e.New(50103, "unsupported log format")
+	ErrInvalidTimestamp     = e.New(50104, "invalid timestamp format")
+)

+ 34 - 35
internal/nginx_log/parser/optimized_parser.go → internal/nginx_log/parser/parser.go

@@ -5,7 +5,6 @@ import (
 	"bytes"
 	"context"
 	"crypto/md5"
-	"errors"
 	"fmt"
 	"io"
 	"runtime"
@@ -15,8 +14,8 @@ import (
 	"unsafe"
 )
 
-// OptimizedParser provides high-performance log parsing with zero-copy optimizations
-type OptimizedParser struct {
+// Parser provides high-performance log parsing with zero-copy optimizations
+type Parser struct {
 	config     *Config
 	uaParser   UserAgentParser
 	geoService GeoIPService
@@ -45,13 +44,13 @@ type parseBuffer struct {
 	lineBytes []byte
 }
 
-// NewOptimizedParser creates a new high-performance parser
-func NewOptimizedParser(config *Config, uaParser UserAgentParser, geoService GeoIPService) *OptimizedParser {
+// NewParser creates a new high-performance parser
+func NewParser(config *Config, uaParser UserAgentParser, geoService GeoIPService) *Parser {
 	if config == nil {
 		config = DefaultParserConfig()
 	}
 
-	return &OptimizedParser{
+	return &Parser{
 		config:     config,
 		uaParser:   uaParser,
 		geoService: geoService,
@@ -70,13 +69,13 @@ func NewOptimizedParser(config *Config, uaParser UserAgentParser, geoService Geo
 }
 
 // ParseLine parses a single log line with zero-copy optimizations
-func (p *OptimizedParser) ParseLine(line string) (*AccessLogEntry, error) {
+func (p *Parser) ParseLine(line string) (*AccessLogEntry, error) {
 	if len(line) == 0 {
-		return nil, errors.New(ErrEmptyLogLine)
+		return nil, ErrEmptyLogLine
 	}
 
 	if len(line) > p.config.MaxLineLength {
-		return nil, errors.New(ErrLineTooLong)
+		return nil, ErrLineTooLong
 	}
 
 	buf := p.pool.Get().(*parseBuffer)
@@ -112,12 +111,12 @@ func (p *OptimizedParser) ParseLine(line string) (*AccessLogEntry, error) {
 }
 
 // ParseLines parses multiple log lines with parallel processing
-func (p *OptimizedParser) ParseLines(lines []string) *ParseResult {
+func (p *Parser) ParseLines(lines []string) *ParseResult {
 	return p.ParseLinesWithContext(context.Background(), lines)
 }
 
 // ParseLinesWithContext parses lines with context support for cancellation
-func (p *OptimizedParser) ParseLinesWithContext(ctx context.Context, lines []string) *ParseResult {
+func (p *Parser) ParseLinesWithContext(ctx context.Context, lines []string) *ParseResult {
 	startTime := time.Now()
 	result := &ParseResult{
 		Entries:   make([]*AccessLogEntry, 0, len(lines)),
@@ -139,7 +138,7 @@ func (p *OptimizedParser) ParseLinesWithContext(ctx context.Context, lines []str
 }
 
 // ParseStream parses log entries from an io.Reader with streaming support
-func (p *OptimizedParser) ParseStream(ctx context.Context, reader io.Reader) (*ParseResult, error) {
+func (p *Parser) ParseStream(ctx context.Context, reader io.Reader) (*ParseResult, error) {
 	startTime := time.Now()
 	result := &ParseResult{
 		Entries: make([]*AccessLogEntry, 0),
@@ -196,7 +195,7 @@ func (p *OptimizedParser) ParseStream(ctx context.Context, reader io.Reader) (*P
 }
 
 // parseLinesSingleThreaded handles small datasets with single-threaded parsing
-func (p *OptimizedParser) parseLinesSingleThreaded(ctx context.Context, lines []string, startTime time.Time) *ParseResult {
+func (p *Parser) parseLinesSingleThreaded(ctx context.Context, lines []string, startTime time.Time) *ParseResult {
 	result := &ParseResult{
 		Entries:   make([]*AccessLogEntry, 0, len(lines)),
 		Processed: len(lines),
@@ -227,7 +226,7 @@ func (p *OptimizedParser) parseLinesSingleThreaded(ctx context.Context, lines []
 }
 
 // parseLinesParallel handles large datasets with parallel processing
-func (p *OptimizedParser) parseLinesParallel(ctx context.Context, lines []string, startTime time.Time) *ParseResult {
+func (p *Parser) parseLinesParallel(ctx context.Context, lines []string, startTime time.Time) *ParseResult {
 	numWorkers := p.config.WorkerCount
 	if numWorkers <= 0 {
 		numWorkers = runtime.NumCPU()
@@ -323,18 +322,18 @@ func (p *OptimizedParser) parseLinesParallel(ctx context.Context, lines []string
 }
 
 // parseLineOptimized performs optimized parsing of a single line
-func (p *OptimizedParser) parseLineOptimized(line []byte, buf *parseBuffer) error {
+func (p *Parser) parseLineOptimized(line []byte, buf *parseBuffer) error {
 	pos := 0
 	length := len(line)
 
 	if length < 20 {
-		return errors.New(ErrUnsupportedLogFormat)
+		return ErrUnsupportedLogFormat
 	}
 
 	// Parse IP address
 	pos = p.parseIP(line, pos, buf.entry)
 	if pos >= length {
-		return errors.New(ErrUnsupportedLogFormat)
+		return ErrUnsupportedLogFormat
 	}
 
 	// Skip remote user fields (- -)
@@ -347,21 +346,21 @@ func (p *OptimizedParser) parseLineOptimized(line []byte, buf *parseBuffer) erro
 	pos = p.skipSpaces(line, pos)
 	pos = p.parseTimestamp(line, pos, buf.entry)
 	if pos >= length {
-		return errors.New(ErrUnsupportedLogFormat)
+		return ErrUnsupportedLogFormat
 	}
 
 	// Parse request
 	pos = p.skipSpaces(line, pos)
 	pos = p.parseRequest(line, pos, buf.entry)
 	if pos >= length {
-		return errors.New(ErrUnsupportedLogFormat)
+		return ErrUnsupportedLogFormat
 	}
 
 	// Parse status code
 	pos = p.skipSpaces(line, pos)
 	pos = p.parseStatus(line, pos, buf.entry)
 	if pos >= length {
-		return errors.New(ErrUnsupportedLogFormat)
+		return ErrUnsupportedLogFormat
 	}
 
 	// Parse response size
@@ -401,7 +400,7 @@ func (p *OptimizedParser) parseLineOptimized(line []byte, buf *parseBuffer) erro
 }
 
 // Fast field parsing methods with zero-copy optimizations
-func (p *OptimizedParser) parseIP(line []byte, pos int, entry *AccessLogEntry) int {
+func (p *Parser) parseIP(line []byte, pos int, entry *AccessLogEntry) int {
 	start := pos
 	for pos < len(line) && line[pos] != ' ' {
 		pos++
@@ -427,7 +426,7 @@ func (p *OptimizedParser) parseIP(line []byte, pos int, entry *AccessLogEntry) i
 	return pos
 }
 
-func (p *OptimizedParser) parseTimestamp(line []byte, pos int, entry *AccessLogEntry) int {
+func (p *Parser) parseTimestamp(line []byte, pos int, entry *AccessLogEntry) int {
 	if pos >= len(line) || line[pos] != '[' {
 		return pos
 	}
@@ -479,7 +478,7 @@ func (p *OptimizedParser) parseTimestamp(line []byte, pos int, entry *AccessLogE
 	return pos
 }
 
-func (p *OptimizedParser) parseRequest(line []byte, pos int, entry *AccessLogEntry) int {
+func (p *Parser) parseRequest(line []byte, pos int, entry *AccessLogEntry) int {
 	if pos >= len(line) || line[pos] != '"' {
 		return pos
 	}
@@ -515,7 +514,7 @@ func (p *OptimizedParser) parseRequest(line []byte, pos int, entry *AccessLogEnt
 	return pos
 }
 
-func (p *OptimizedParser) parseStatus(line []byte, pos int, entry *AccessLogEntry) int {
+func (p *Parser) parseStatus(line []byte, pos int, entry *AccessLogEntry) int {
 	start := pos
 	for pos < len(line) && line[pos] >= '0' && line[pos] <= '9' {
 		pos++
@@ -530,7 +529,7 @@ func (p *OptimizedParser) parseStatus(line []byte, pos int, entry *AccessLogEntr
 	return pos
 }
 
-func (p *OptimizedParser) parseSize(line []byte, pos int, entry *AccessLogEntry) int {
+func (p *Parser) parseSize(line []byte, pos int, entry *AccessLogEntry) int {
 	start := pos
 	for pos < len(line) && ((line[pos] >= '0' && line[pos] <= '9') || line[pos] == '-') {
 		pos++
@@ -550,7 +549,7 @@ func (p *OptimizedParser) parseSize(line []byte, pos int, entry *AccessLogEntry)
 	return pos
 }
 
-func (p *OptimizedParser) parseReferer(line []byte, pos int, entry *AccessLogEntry) int {
+func (p *Parser) parseReferer(line []byte, pos int, entry *AccessLogEntry) int {
 	if pos >= len(line) || line[pos] != '"' {
 		return pos
 	}
@@ -572,7 +571,7 @@ func (p *OptimizedParser) parseReferer(line []byte, pos int, entry *AccessLogEnt
 	return pos
 }
 
-func (p *OptimizedParser) parseUserAgent(line []byte, pos int, entry *AccessLogEntry) int {
+func (p *Parser) parseUserAgent(line []byte, pos int, entry *AccessLogEntry) int {
 	if pos >= len(line) || line[pos] != '"' {
 		return pos
 	}
@@ -610,7 +609,7 @@ func (p *OptimizedParser) parseUserAgent(line []byte, pos int, entry *AccessLogE
 	return pos
 }
 
-func (p *OptimizedParser) parseRequestTime(line []byte, pos int, entry *AccessLogEntry) int {
+func (p *Parser) parseRequestTime(line []byte, pos int, entry *AccessLogEntry) int {
 	start := pos
 	for pos < len(line) && ((line[pos] >= '0' && line[pos] <= '9') || line[pos] == '.' || line[pos] == '-') {
 		pos++
@@ -628,7 +627,7 @@ func (p *OptimizedParser) parseRequestTime(line []byte, pos int, entry *AccessLo
 	return pos
 }
 
-func (p *OptimizedParser) parseUpstreamTime(line []byte, pos int, entry *AccessLogEntry) int {
+func (p *Parser) parseUpstreamTime(line []byte, pos int, entry *AccessLogEntry) int {
 	start := pos
 	for pos < len(line) && ((line[pos] >= '0' && line[pos] <= '9') || line[pos] == '.' || line[pos] == '-') {
 		pos++
@@ -647,26 +646,26 @@ func (p *OptimizedParser) parseUpstreamTime(line []byte, pos int, entry *AccessL
 }
 
 // Utility methods
-func (p *OptimizedParser) skipSpaces(line []byte, pos int) int {
+func (p *Parser) skipSpaces(line []byte, pos int) int {
 	for pos < len(line) && line[pos] == ' ' {
 		pos++
 	}
 	return pos
 }
 
-func (p *OptimizedParser) skipField(line []byte, pos int) int {
+func (p *Parser) skipField(line []byte, pos int) int {
 	for pos < len(line) && line[pos] != ' ' {
 		pos++
 	}
 	return pos
 }
 
-func (p *OptimizedParser) generateEntryID(line string) string {
+func (p *Parser) generateEntryID(line string) string {
 	hash := md5.Sum([]byte(line))
 	return fmt.Sprintf("%x", hash)[:16]
 }
 
-func (p *OptimizedParser) updateStats(result *ParseResult) {
+func (p *Parser) updateStats(result *ParseResult) {
 	p.mu.Lock()
 	defer p.mu.Unlock()
 
@@ -682,7 +681,7 @@ func (p *OptimizedParser) updateStats(result *ParseResult) {
 }
 
 // GetStats returns current parsing statistics
-func (p *OptimizedParser) GetStats() *ParseStats {
+func (p *Parser) GetStats() *ParseStats {
 	p.mu.RLock()
 	defer p.mu.RUnlock()
 
@@ -691,7 +690,7 @@ func (p *OptimizedParser) GetStats() *ParseStats {
 }
 
 // ResetStats resets parsing statistics
-func (p *OptimizedParser) ResetStats() {
+func (p *Parser) ResetStats() {
 	p.mu.Lock()
 	defer p.mu.Unlock()
 

+ 12 - 12
internal/nginx_log/parser/parser_test.go

@@ -26,7 +26,7 @@ func (m *mockGeoIPService) Search(ip string) (*GeoLocation, error) {
 	}, nil
 }
 
-func TestOptimizedParser_ParseLine(t *testing.T) {
+func TestParser_ParseLine(t *testing.T) {
 	tests := []struct {
 		name     string
 		line     string
@@ -85,7 +85,7 @@ func TestOptimizedParser_ParseLine(t *testing.T) {
 	config := DefaultParserConfig()
 	config.StrictMode = false // Use non-strict mode to handle malformed lines gracefully
 	
-	parser := NewOptimizedParser(
+	parser := NewParser(
 		config,
 		NewSimpleUserAgentParser(),
 		&mockGeoIPService{},
@@ -128,7 +128,7 @@ func TestOptimizedParser_ParseLine(t *testing.T) {
 	}
 }
 
-func TestOptimizedParser_ParseLines(t *testing.T) {
+func TestParser_ParseLines(t *testing.T) {
 	lines := []string{
 		`127.0.0.1 - - [25/Dec/2023:10:00:00 +0000] "GET /index.html HTTP/1.1" 200 1234 "-" "Mozilla/5.0"`,
 		`192.168.1.1 - - [25/Dec/2023:10:00:01 +0000] "POST /api/data HTTP/1.1" 201 567 "-" "curl/7.68.0"`,
@@ -138,7 +138,7 @@ func TestOptimizedParser_ParseLines(t *testing.T) {
 	}
 
 	config := DefaultParserConfig()
-	parser := NewOptimizedParser(
+	parser := NewParser(
 		config,
 		NewSimpleUserAgentParser(),
 		&mockGeoIPService{},
@@ -163,7 +163,7 @@ func TestOptimizedParser_ParseLines(t *testing.T) {
 	}
 }
 
-func TestOptimizedParser_ParseStream(t *testing.T) {
+func TestParser_ParseStream(t *testing.T) {
 	logData := `127.0.0.1 - - [25/Dec/2023:10:00:00 +0000] "GET /index.html HTTP/1.1" 200 1234 "-" "Mozilla/5.0"
 192.168.1.1 - - [25/Dec/2023:10:00:01 +0000] "POST /api/data HTTP/1.1" 201 567 "-" "curl/7.68.0"
 10.0.0.1 - - [25/Dec/2023:10:00:02 +0000] "GET /style.css HTTP/1.1" 200 890 "-" "Mozilla/5.0"`
@@ -173,7 +173,7 @@ func TestOptimizedParser_ParseStream(t *testing.T) {
 	config := DefaultParserConfig()
 	config.BatchSize = 2 // Small batch size for testing
 	
-	parser := NewOptimizedParser(
+	parser := NewParser(
 		config,
 		NewSimpleUserAgentParser(),
 		&mockGeoIPService{},
@@ -199,14 +199,14 @@ func TestOptimizedParser_ParseStream(t *testing.T) {
 	}
 }
 
-func TestOptimizedParser_WithContext(t *testing.T) {
+func TestParser_WithContext(t *testing.T) {
 	lines := make([]string, 1000)
 	for i := range lines {
 		lines[i] = fmt.Sprintf(`127.0.0.%d - - [25/Dec/2023:10:00:00 +0000] "GET /test%d.html HTTP/1.1" 200 1234 "-" "Mozilla/5.0"`, i%255+1, i)
 	}
 
 	config := DefaultParserConfig()
-	parser := NewOptimizedParser(
+	parser := NewParser(
 		config,
 		NewSimpleUserAgentParser(),
 		&mockGeoIPService{},
@@ -376,11 +376,11 @@ func TestCachedUserAgentParser(t *testing.T) {
 	}
 }
 
-func BenchmarkOptimizedParser_ParseLine(b *testing.B) {
+func BenchmarkParser_ParseLine(b *testing.B) {
 	line := `127.0.0.1 - - [25/Dec/2023:10:00:00 +0000] "GET /index.html HTTP/1.1" 200 1234 "https://example.com" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"`
 	
 	config := DefaultParserConfig()
-	parser := NewOptimizedParser(
+	parser := NewParser(
 		config,
 		NewSimpleUserAgentParser(),
 		&mockGeoIPService{},
@@ -397,14 +397,14 @@ func BenchmarkOptimizedParser_ParseLine(b *testing.B) {
 	}
 }
 
-func BenchmarkOptimizedParser_ParseLines(b *testing.B) {
+func BenchmarkParser_ParseLines(b *testing.B) {
 	lines := make([]string, 1000)
 	for i := range lines {
 		lines[i] = fmt.Sprintf(`127.0.0.%d - - [25/Dec/2023:10:00:00 +0000] "GET /test%d.html HTTP/1.1" 200 1234 "-" "Mozilla/5.0"`, i%255+1, i)
 	}
 	
 	config := DefaultParserConfig()
-	parser := NewOptimizedParser(
+	parser := NewParser(
 		config,
 		NewSimpleUserAgentParser(),
 		&mockGeoIPService{},

+ 6 - 6
internal/nginx_log/parser/production_scale_test.go

@@ -30,7 +30,7 @@ func TestProductionScaleValidation(t *testing.T) {
 	config.WorkerCount = 12 // Utilize all CPU cores
 	config.BatchSize = 2000 // Larger batches for high volume
 	
-	parser := NewOptimizedParser(
+	parser := NewParser(
 		config,
 		NewCachedUserAgentParser(NewSimpleUserAgentParser(), 10000),
 		&mockGeoIPService{},
@@ -71,7 +71,7 @@ func TestProductionScaleValidation(t *testing.T) {
 				reader := strings.NewReader(logData)
 				ctx := context.Background()
 				
-				result, err := parser.OptimizedParseStream(ctx, reader)
+				result, err := parser.ParseStream(ctx, reader)
 				duration := time.Since(startTime)
 				
 				if err != nil {
@@ -148,7 +148,7 @@ func BenchmarkProductionScale(b *testing.B) {
 	config.WorkerCount = 12
 	config.BatchSize = 2000
 	
-	parser := NewOptimizedParser(
+	parser := NewParser(
 		config,
 		NewCachedUserAgentParser(NewSimpleUserAgentParser(), 10000),
 		&mockGeoIPService{},
@@ -161,7 +161,7 @@ func BenchmarkProductionScale(b *testing.B) {
 		{
 			"Optimized_ParseStream_100K",
 			func(ctx context.Context, reader *strings.Reader) (*ParseResult, error) {
-				return parser.OptimizedParseStream(ctx, reader)
+				return parser.ParseStream(ctx, reader)
 			},
 		},
 		{
@@ -406,7 +406,7 @@ func TestMemoryUsageValidation(t *testing.T) {
 	config.WorkerCount = 4
 	config.BatchSize = 1000
 	
-	parser := NewOptimizedParser(
+	parser := NewParser(
 		config,
 		NewCachedUserAgentParser(NewSimpleUserAgentParser(), 1000),
 		&mockGeoIPService{},
@@ -419,7 +419,7 @@ func TestMemoryUsageValidation(t *testing.T) {
 		{
 			"Optimized_ParseStream",
 			func(ctx context.Context, reader *strings.Reader) (*ParseResult, error) {
-				return parser.OptimizedParseStream(ctx, reader)
+				return parser.ParseStream(ctx, reader)
 			},
 		},
 		{

+ 12 - 12
internal/nginx_log/parser/regex_cache_optimization.go

@@ -251,8 +251,8 @@ func (rc *RegexCache) Close() {
 	rc.Clear()
 }
 
-// OptimizedRegexMatcher provides optimized regex matching for log parsing
-type OptimizedRegexMatcher struct {
+// RegexMatcher provides optimized regex matching for log parsing
+type RegexMatcher struct {
 	cache *RegexCache
 	// Pre-compiled common patterns for fastest access
 	ipv4Regex      *regexp.Regexp
@@ -263,11 +263,11 @@ type OptimizedRegexMatcher struct {
 	mainRegex      *regexp.Regexp
 }
 
-// NewOptimizedRegexMatcher creates a new optimized regex matcher
-func NewOptimizedRegexMatcher() *OptimizedRegexMatcher {
+// NewRegexMatcher creates a new optimized regex matcher
+func NewRegexMatcher() *RegexMatcher {
 	cache := GetGlobalRegexCache()
 
-	matcher := &OptimizedRegexMatcher{
+	matcher := &RegexMatcher{
 		cache: cache,
 	}
 
@@ -283,7 +283,7 @@ func NewOptimizedRegexMatcher() *OptimizedRegexMatcher {
 }
 
 // MatchIPv4 matches IPv4 addresses using cached regex
-func (orm *OptimizedRegexMatcher) MatchIPv4(text string) []string {
+func (orm *RegexMatcher) MatchIPv4(text string) []string {
 	if orm.ipv4Regex != nil {
 		return orm.ipv4Regex.FindStringSubmatch(text)
 	}
@@ -291,7 +291,7 @@ func (orm *OptimizedRegexMatcher) MatchIPv4(text string) []string {
 }
 
 // MatchTimestamp matches timestamp patterns using cached regex
-func (orm *OptimizedRegexMatcher) MatchTimestamp(text string) []string {
+func (orm *RegexMatcher) MatchTimestamp(text string) []string {
 	if orm.timestampRegex != nil {
 		return orm.timestampRegex.FindStringSubmatch(text)
 	}
@@ -299,7 +299,7 @@ func (orm *OptimizedRegexMatcher) MatchTimestamp(text string) []string {
 }
 
 // MatchCombinedFormat matches complete combined log format
-func (orm *OptimizedRegexMatcher) MatchCombinedFormat(text string) []string {
+func (orm *RegexMatcher) MatchCombinedFormat(text string) []string {
 	if orm.combinedRegex != nil {
 		return orm.combinedRegex.FindStringSubmatch(text)
 	}
@@ -307,7 +307,7 @@ func (orm *OptimizedRegexMatcher) MatchCombinedFormat(text string) []string {
 }
 
 // MatchMainFormat matches main log format
-func (orm *OptimizedRegexMatcher) MatchMainFormat(text string) []string {
+func (orm *RegexMatcher) MatchMainFormat(text string) []string {
 	if orm.mainRegex != nil {
 		return orm.mainRegex.FindStringSubmatch(text)
 	}
@@ -315,7 +315,7 @@ func (orm *OptimizedRegexMatcher) MatchMainFormat(text string) []string {
 }
 
 // MatchPattern matches any pattern using the regex cache
-func (orm *OptimizedRegexMatcher) MatchPattern(pattern, text string) ([]string, error) {
+func (orm *RegexMatcher) MatchPattern(pattern, text string) ([]string, error) {
 	regex, err := orm.cache.GetRegex(pattern)
 	if err != nil {
 		return nil, err
@@ -325,7 +325,7 @@ func (orm *OptimizedRegexMatcher) MatchPattern(pattern, text string) ([]string,
 }
 
 // DetectLogFormat detects nginx log format using cached patterns
-func (orm *OptimizedRegexMatcher) DetectLogFormat(logLine string) string {
+func (orm *RegexMatcher) DetectLogFormat(logLine string) string {
 	// Try combined format first (most common)
 	if orm.combinedRegex != nil && orm.combinedRegex.MatchString(logLine) {
 		return "combined"
@@ -340,7 +340,7 @@ func (orm *OptimizedRegexMatcher) DetectLogFormat(logLine string) string {
 }
 
 // GetCacheStats returns regex cache statistics
-func (orm *OptimizedRegexMatcher) GetCacheStats() RegexCacheStats {
+func (orm *RegexMatcher) GetCacheStats() RegexCacheStats {
 	return orm.cache.GetStats()
 }
 

+ 7 - 7
internal/nginx_log/parser/simd_optimizations.go

@@ -517,27 +517,27 @@ func (sm *SIMDStringMatcher) BatchParseSIMD(lines [][]byte) []*AccessLogEntry {
 	return entries
 }
 
-// OptimizedLogLineParser provides a high-performance parser using SIMD operations
-type OptimizedLogLineParser struct {
+// LogLineParser provides a high-performance parser using SIMD operations
+type LogLineParser struct {
 	matcher *SIMDStringMatcher
 	pool    *AccessLogEntryPool
 }
 
-// NewOptimizedLogLineParser creates a new optimized parser
-func NewOptimizedLogLineParser() *OptimizedLogLineParser {
-	return &OptimizedLogLineParser{
+// NewLogLineParser creates a new optimized parser
+func NewLogLineParser() *LogLineParser {
+	return &LogLineParser{
 		matcher: NewSIMDStringMatcher(),
 		pool:    NewAccessLogEntryPool(),
 	}
 }
 
 // ParseLine parses a single log line with maximum performance
-func (olp *OptimizedLogLineParser) ParseLine(data []byte) *AccessLogEntry {
+func (olp *LogLineParser) ParseLine(data []byte) *AccessLogEntry {
 	return olp.matcher.ParseLogLineSIMD(data)
 }
 
 // ParseLines parses multiple lines efficiently
-func (olp *OptimizedLogLineParser) ParseLines(lines [][]byte) []*AccessLogEntry {
+func (olp *LogLineParser) ParseLines(lines [][]byte) []*AccessLogEntry {
 	return olp.matcher.BatchParseSIMD(lines)
 }
 

+ 4 - 4
internal/nginx_log/parser/simd_optimizations_test.go

@@ -102,13 +102,13 @@ func BenchmarkSIMDvsRegularParsing(b *testing.B) {
 	// Setup parsers
 	config := DefaultParserConfig()
 	config.MaxLineLength = 16 * 1024
-	regularParser := NewOptimizedParser(
+	regularParser := NewParser(
 		config,
 		NewSimpleUserAgentParser(),
 		&mockGeoIPService{},
 	)
 	
-	simdParser := NewOptimizedLogLineParser()
+	simdParser := NewLogLineParser()
 
 	b.Run("Regular_SingleLine", func(b *testing.B) {
 		b.ResetTimer()
@@ -271,7 +271,7 @@ func BenchmarkObjectPooling(b *testing.B) {
 
 // BenchmarkSIMDScaleTest tests SIMD performance at different scales
 func BenchmarkSIMDScaleTest(b *testing.B) {
-	simdParser := NewOptimizedLogLineParser()
+	simdParser := NewLogLineParser()
 	
 	scales := []struct {
 		name  string
@@ -323,7 +323,7 @@ func generateTestLogLine(index int) string {
 // TestSIMDCorrectnessValidation validates SIMD operations produce correct results
 func TestSIMDCorrectnessValidation(t *testing.T) {
 	matcher := NewSIMDStringMatcher()
-	simdParser := NewOptimizedLogLineParser()
+	simdParser := NewLogLineParser()
 	
 	testCases := []struct {
 		name string

+ 13 - 13
internal/nginx_log/parser/stream_optimizations.go

@@ -9,8 +9,8 @@ import (
 	"unsafe"
 )
 
-// OptimizedParseStream provides a high-performance streaming parser with zero-allocation optimizations
-func (p *OptimizedParser) OptimizedParseStream(ctx context.Context, reader io.Reader) (*ParseResult, error) {
+// ParseStream provides a high-performance streaming parser with zero-allocation optimizations
+func (p *Parser) StreamParse(ctx context.Context, reader io.Reader) (*ParseResult, error) {
 	startTime := time.Now()
 	
 	// Pre-allocate result with estimated capacity to reduce reallocations
@@ -54,7 +54,7 @@ func (p *OptimizedParser) OptimizedParseStream(ctx context.Context, reader io.Re
 
 		// Process full batches
 		if len(batch) >= p.config.BatchSize {
-			if err := p.processBatchOptimized(ctx, batch, result); err != nil {
+			if err := p.processBatch(ctx, batch, result); err != nil {
 				return result, err
 			}
 			// Reset batch slice but keep capacity
@@ -64,7 +64,7 @@ func (p *OptimizedParser) OptimizedParseStream(ctx context.Context, reader io.Re
 
 	// Process remaining lines
 	if len(batch) > 0 {
-		if err := p.processBatchOptimized(ctx, batch, result); err != nil {
+		if err := p.processBatch(ctx, batch, result); err != nil {
 			return result, err
 		}
 	}
@@ -83,8 +83,8 @@ func (p *OptimizedParser) OptimizedParseStream(ctx context.Context, reader io.Re
 	return result, nil
 }
 
-// processBatchOptimized processes a batch of lines with memory-efficient operations
-func (p *OptimizedParser) processBatchOptimized(ctx context.Context, batch []string, result *ParseResult) error {
+// processBatch processes a batch of lines with memory-efficient operations
+func (p *Parser) processBatch(ctx context.Context, batch []string, result *ParseResult) error {
 	batchResult := p.ParseLinesWithContext(ctx, batch)
 	
 	// Pre-grow the result.Entries slice to avoid multiple reallocations
@@ -150,7 +150,7 @@ func (sb *StreamBuffer) ReadLine(reader io.Reader) ([]byte, error) {
 }
 
 // ChunkedParseStream processes the stream in chunks for better memory usage
-func (p *OptimizedParser) ChunkedParseStream(ctx context.Context, reader io.Reader, chunkSize int) (*ParseResult, error) {
+func (p *Parser) ChunkedParseStream(ctx context.Context, reader io.Reader, chunkSize int) (*ParseResult, error) {
 	startTime := time.Now()
 	result := &ParseResult{
 		Entries: make([]*AccessLogEntry, 0, chunkSize),
@@ -199,7 +199,7 @@ func (p *OptimizedParser) ChunkedParseStream(ctx context.Context, reader io.Read
 			result.Processed++
 			
 			if len(batch) >= p.config.BatchSize {
-				if err := p.processBatchOptimized(ctx, batch, result); err != nil {
+				if err := p.processBatch(ctx, batch, result); err != nil {
 					return result, err
 				}
 				batch = batch[:0]
@@ -208,7 +208,7 @@ func (p *OptimizedParser) ChunkedParseStream(ctx context.Context, reader io.Read
 		
 		// Process remaining batch
 		if len(batch) > 0 {
-			if err := p.processBatchOptimized(ctx, batch, result); err != nil {
+			if err := p.processBatch(ctx, batch, result); err != nil {
 				return result, err
 			}
 		}
@@ -223,7 +223,7 @@ func (p *OptimizedParser) ChunkedParseStream(ctx context.Context, reader io.Read
 		line := string(remainder)
 		batch := []string{line}
 		result.Processed++
-		if err := p.processBatchOptimized(ctx, batch, result); err != nil {
+		if err := p.processBatch(ctx, batch, result); err != nil {
 			return result, err
 		}
 	}
@@ -294,7 +294,7 @@ func (lb *LineBuffer) Bytes() []byte {
 }
 
 // MemoryEfficientParseStream uses minimal memory allocations for streaming
-func (p *OptimizedParser) MemoryEfficientParseStream(ctx context.Context, reader io.Reader) (*ParseResult, error) {
+func (p *Parser) MemoryEfficientParseStream(ctx context.Context, reader io.Reader) (*ParseResult, error) {
 	startTime := time.Now()
 	result := &ParseResult{
 		Entries: make([]*AccessLogEntry, 0, 1000),
@@ -333,7 +333,7 @@ func (p *OptimizedParser) MemoryEfficientParseStream(ctx context.Context, reader
 		result.Processed++
 
 		if len(batch) >= p.config.BatchSize {
-			if err := p.processBatchOptimized(ctx, batch, result); err != nil {
+			if err := p.processBatch(ctx, batch, result); err != nil {
 				return result, err
 			}
 			batch = batch[:0]
@@ -342,7 +342,7 @@ func (p *OptimizedParser) MemoryEfficientParseStream(ctx context.Context, reader
 
 	// Process remaining lines
 	if len(batch) > 0 {
-		if err := p.processBatchOptimized(ctx, batch, result); err != nil {
+		if err := p.processBatch(ctx, batch, result); err != nil {
 			return result, err
 		}
 	}

+ 12 - 12
internal/nginx_log/parser/stream_optimizations_test.go

@@ -14,7 +14,7 @@ func BenchmarkParseStreamComparison(b *testing.B) {
 
 	config := DefaultParserConfig()
 	config.MaxLineLength = 16 * 1024
-	parser := NewOptimizedParser(
+	parser := NewParser(
 		config,
 		NewCachedUserAgentParser(NewSimpleUserAgentParser(), 100),
 		&mockGeoIPService{},
@@ -33,7 +33,7 @@ func BenchmarkParseStreamComparison(b *testing.B) {
 		{
 			name: "Optimized_ParseStream",
 			fn: func(ctx context.Context, reader *strings.Reader) (*ParseResult, error) {
-				return parser.OptimizedParseStream(ctx, reader)
+				return parser.ParseStream(ctx, reader)
 			},
 		},
 		{
@@ -95,7 +95,7 @@ func BenchmarkStreamParsing_ScaleTest(b *testing.B) {
 
 	config := DefaultParserConfig()
 	config.MaxLineLength = 16 * 1024
-	parser := NewOptimizedParser(
+	parser := NewParser(
 		config,
 		NewCachedUserAgentParser(NewSimpleUserAgentParser(), 1000),
 		&mockGeoIPService{},
@@ -128,7 +128,7 @@ func BenchmarkStreamParsing_ScaleTest(b *testing.B) {
 				reader := strings.NewReader(logData)
 				ctx := context.Background()
 
-				result, err := parser.OptimizedParseStream(ctx, reader)
+				result, err := parser.ParseStream(ctx, reader)
 				if err != nil {
 					b.Fatal(err)
 				}
@@ -144,7 +144,7 @@ func BenchmarkStreamOptimizations_Individual(b *testing.B) {
 
 	config := DefaultParserConfig()
 	config.MaxLineLength = 16 * 1024
-	_ = NewOptimizedParser(
+	_ = NewParser(
 		config,
 		NewSimpleUserAgentParser(),
 		&mockGeoIPService{},
@@ -292,15 +292,15 @@ func generateBenchmarkLogData(lines int) string {
 	return builder.String()
 }
 
-// TestOptimizedParseStreamCorrectness verifies that optimized implementations produce correct results
-func TestOptimizedParseStreamCorrectness(t *testing.T) {
+// TestParseStreamCorrectness verifies that optimized implementations produce correct results
+func TestParseStreamCorrectness(t *testing.T) {
 	logData := `127.0.0.1 - - [25/Dec/2023:10:00:00 +0000] "GET /index.html HTTP/1.1" 200 1234 "https://example.com" "Mozilla/5.0"
 192.168.1.1 - - [25/Dec/2023:10:00:01 +0000] "POST /api/data HTTP/1.1" 201 567 "https://example.com" "curl/7.68.0"
 10.0.0.1 - - [25/Dec/2023:10:00:02 +0000] "GET /style.css HTTP/1.1" 200 890 "https://example.com" "Mozilla/5.0"`
 
 	config := DefaultParserConfig()
 	config.MaxLineLength = 16 * 1024
-	parser := NewOptimizedParser(
+	parser := NewParser(
 		config,
 		NewSimpleUserAgentParser(),
 		&mockGeoIPService{},
@@ -314,15 +314,15 @@ func TestOptimizedParseStreamCorrectness(t *testing.T) {
 		t.Fatalf("Original ParseStream failed: %v", err)
 	}
 
-	// Test optimized implementations
+	// Test stream implementations
 	implementations := []struct {
 		name string
 		fn   func(context.Context, *strings.Reader) (*ParseResult, error)
 	}{
 		{
-			"OptimizedParseStream",
+			"StreamParse",
 			func(ctx context.Context, reader *strings.Reader) (*ParseResult, error) {
-				return parser.OptimizedParseStream(ctx, reader)
+				return parser.StreamParse(ctx, reader)
 			},
 		},
 		{
@@ -436,7 +436,7 @@ func BenchmarkStreamBufferSizes(b *testing.B) {
 
 	config := DefaultParserConfig()
 	config.MaxLineLength = 16 * 1024
-	parser := NewOptimizedParser(
+	parser := NewParser(
 		config,
 		NewSimpleUserAgentParser(),
 		&mockGeoIPService{},

+ 2 - 6
internal/nginx_log/parser/types.go

@@ -113,11 +113,7 @@ var ValidHTTPMethods = map[string]bool{
 	"CONNECT": true,
 }
 
-// Parser errors
+// Parser errors (moved to errors.go as Cosy Errors)
 const (
-	ErrEmptyLogLine         = "empty log line"
-	ErrUnsupportedLogFormat = "unsupported log format"
-	ErrInvalidTimestamp     = "invalid timestamp"
-	ErrInvalidStatus        = "invalid status code"
-	ErrLineTooLong          = "line too long"
+	ErrInvalidStatus = "invalid status code"
 )

+ 16 - 16
internal/nginx_log/performance_monitor_test.go

@@ -14,7 +14,7 @@ func TestPerformanceMonitor(t *testing.T) {
 
 	t.Run("BasicFunctionality", func(t *testing.T) {
 		// Create monitor with custom thresholds for testing
-		thresholds := &PerformanceThresholds{
+		thresholds := &Thresholds{
 			ParseStreamOpsPerSec: 100.0,    // Low threshold for testing
 			SIMDOpsPerSec:        1000.0,   // Low threshold for testing
 			MemoryPoolOpsPerSec:  10000.0,  // Low threshold for testing
@@ -23,7 +23,7 @@ func TestPerformanceMonitor(t *testing.T) {
 			MaxResponseTimeMS:    5000.0,   // High threshold for testing
 		}
 
-		monitor := NewPerformanceMonitor(thresholds)
+		monitor := NewMonitor(thresholds)
 
 		// Test metrics collection
 		monitor.collectMetrics()
@@ -56,7 +56,7 @@ func TestPerformanceMonitor(t *testing.T) {
 
 	t.Run("AlertGeneration", func(t *testing.T) {
 		// Create monitor with very high thresholds to trigger alerts
-		thresholds := &PerformanceThresholds{
+		thresholds := &Thresholds{
 			ParseStreamOpsPerSec: 1000000.0,  // Unrealistically high
 			SIMDOpsPerSec:        10000000.0, // Unrealistically high
 			MemoryPoolOpsPerSec:  100000000.0, // Unrealistically high
@@ -65,11 +65,11 @@ func TestPerformanceMonitor(t *testing.T) {
 			MaxResponseTimeMS:    0.1,         // Very low to trigger alert
 		}
 
-		monitor := NewPerformanceMonitor(thresholds)
+		monitor := NewMonitor(thresholds)
 		
 		// Set up alert collection
-		alertsReceived := make([]PerformanceAlert, 0)
-		monitor.SetAlertCallback(func(alert PerformanceAlert) {
+		alertsReceived := make([]Alert, 0)
+		monitor.SetAlertCallback(func(alert Alert) {
 			alertsReceived = append(alertsReceived, alert)
 		})
 
@@ -101,7 +101,7 @@ func TestPerformanceMonitor(t *testing.T) {
 	})
 
 	t.Run("MetricsExport", func(t *testing.T) {
-		monitor := NewPerformanceMonitor(DefaultPerformanceThresholds())
+		monitor := NewMonitor(DefaultThresholds())
 		monitor.collectMetrics()
 
 		// Test JSON export
@@ -117,7 +117,7 @@ func TestPerformanceMonitor(t *testing.T) {
 		t.Logf("Exported JSON length: %d bytes", len(jsonData))
 
 		// Test performance report generation
-		report := GetPerformanceReport(monitor)
+		report := GetReport(monitor)
 		if len(report) == 0 {
 			t.Error("Performance report is empty")
 		}
@@ -126,11 +126,11 @@ func TestPerformanceMonitor(t *testing.T) {
 	})
 
 	t.Run("ContinuousMonitoring", func(t *testing.T) {
-		monitor := NewPerformanceMonitor(DefaultPerformanceThresholds())
+		monitor := NewMonitor(DefaultThresholds())
 		
 		// Set up alert tracking
 		alertCount := 0
-		monitor.SetAlertCallback(func(alert PerformanceAlert) {
+		monitor.SetAlertCallback(func(alert Alert) {
 			alertCount++
 		})
 
@@ -158,9 +158,9 @@ func TestPerformanceMonitor(t *testing.T) {
 	})
 }
 
-// TestPerformanceThresholds tests the default thresholds configuration
-func TestPerformanceThresholds(t *testing.T) {
-	thresholds := DefaultPerformanceThresholds()
+// TestThresholds tests the default thresholds configuration
+func TestThresholds(t *testing.T) {
+	thresholds := DefaultThresholds()
 
 	// Verify default thresholds are reasonable
 	if thresholds.ParseStreamOpsPerSec < 100.0 {
@@ -194,7 +194,7 @@ func TestOptimizationMonitoringIntegration(t *testing.T) {
 	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
 	defer cancel()
 
-	monitor := StartOptimizationMonitoring(ctx)
+	monitor := StartMonitoring(ctx)
 
 	// Wait for some monitoring cycles
 	time.Sleep(1 * time.Second)
@@ -206,7 +206,7 @@ func TestOptimizationMonitoringIntegration(t *testing.T) {
 	}
 
 	// Get performance report
-	report := GetPerformanceReport(monitor)
+	report := GetReport(monitor)
 	if len(report) == 0 {
 		t.Error("Performance report generation failed")
 	}
@@ -228,7 +228,7 @@ func TestOptimizationMonitoringIntegration(t *testing.T) {
 
 // BenchmarkPerformanceMonitoring benchmarks the monitoring overhead
 func BenchmarkPerformanceMonitoring(b *testing.B) {
-	monitor := NewPerformanceMonitor(DefaultPerformanceThresholds())
+	monitor := NewMonitor(DefaultThresholds())
 	
 	b.ResetTimer()
 	

+ 25 - 10
internal/nginx_log/preflight_service.go → internal/nginx_log/preflight.go

@@ -7,6 +7,8 @@ import (
 	"github.com/0xJacky/Nginx-UI/internal/event"
 	"github.com/0xJacky/Nginx-UI/internal/nginx"
 	"github.com/0xJacky/Nginx-UI/internal/nginx_log/indexer"
+	"github.com/0xJacky/Nginx-UI/internal/nginx_log/searcher"
+	"github.com/0xJacky/Nginx-UI/internal/nginx_log/utils"
 	"github.com/uozi-tech/cosy/logger"
 )
 
@@ -33,16 +35,16 @@ type PreflightResponse struct {
 	FileInfo    *FileInfo  `json:"file_info,omitempty"`
 }
 
-// PreflightService handles preflight checks for log files
-type PreflightService struct{}
+// Preflight handles preflight checks for log files
+type Preflight struct{}
 
-// NewPreflightService creates a new preflight service
-func NewPreflightService() *PreflightService {
-	return &PreflightService{}
+// NewPreflight creates a new preflight service
+func NewPreflight() *Preflight {
+	return &Preflight{}
 }
 
 // CheckLogPreflight performs preflight checks for a log file
-func (ps *PreflightService) CheckLogPreflight(logPath string) (*PreflightResponse, error) {
+func (ps *Preflight) CheckLogPreflight(logPath string) (*PreflightResponse, error) {
 	// Use default access log path if logPath is empty
 	if logPath == "" {
 		defaultLogPath := nginx.GetAccessLogPath()
@@ -53,7 +55,7 @@ func (ps *PreflightService) CheckLogPreflight(logPath string) (*PreflightRespons
 	}
 
 	// Get searcher to check index status
-	searcherService := GetModernSearcher()
+	searcherService := GetSearcher()
 	if searcherService == nil {
 		return nil, ErrModernSearcherNotAvailable
 	}
@@ -65,6 +67,19 @@ func (ps *PreflightService) CheckLogPreflight(logPath string) (*PreflightRespons
 	// First check if the file exists and get file info
 	var fileInfo *os.FileInfo
 	if logPath != "" {
+		// Validate log path before accessing it
+		if !utils.IsValidLogPath(logPath) {
+			return &PreflightResponse{
+				Available:   false,
+				IndexStatus: string(indexer.IndexStatusError),
+				Message:     fmt.Sprintf("Invalid log path: %s", logPath),
+				FileInfo: &FileInfo{
+					Exists:   false,
+					Readable: false,
+				},
+			}, nil
+		}
+
 		if stat, err := os.Stat(logPath); os.IsNotExist(err) {
 			// File doesn't exist - check for historical data
 			return ps.handleMissingFile(logPath, searcherService)
@@ -92,8 +107,8 @@ func (ps *PreflightService) CheckLogPreflight(logPath string) (*PreflightRespons
 }
 
 // handleMissingFile handles the case when a log file doesn't exist
-func (ps *PreflightService) handleMissingFile(logPath string, searcherService interface{}) (*PreflightResponse, error) {
-	searcherHealthy := searcherService.(interface{ IsHealthy() bool }).IsHealthy()
+func (ps *Preflight) handleMissingFile(logPath string, searcherService *searcher.Searcher) (*PreflightResponse, error) {
+	searcherHealthy := searcherService.IsHealthy()
 	logFileManager := GetLogFileManager()
 
 	if logFileManager != nil {
@@ -132,7 +147,7 @@ func (ps *PreflightService) handleMissingFile(logPath string, searcherService in
 }
 
 // buildPreflightResponse builds the preflight response for existing files
-func (ps *PreflightService) buildPreflightResponse(logPath string, fileInfo *os.FileInfo, searcherHealthy bool, currentStatus *event.ProcessingStatusData) (*PreflightResponse, error) {
+func (ps *Preflight) buildPreflightResponse(logPath string, fileInfo *os.FileInfo, searcherHealthy bool, currentStatus *event.ProcessingStatusData) (*PreflightResponse, error) {
 	logFileManager := GetLogFileManager()
 	var indexStatus string = string(indexer.IndexStatusNotIndexed)
 	var available bool = false

+ 3 - 3
internal/nginx_log/production_config.go

@@ -32,7 +32,7 @@ type ProductionConfig struct {
 type PerformanceMonitoringConfig struct {
 	Enabled           bool          `json:"enabled"`
 	MonitorInterval   time.Duration `json:"monitor_interval"`
-	AlertThresholds   *PerformanceThresholds `json:"alert_thresholds"`
+	AlertThresholds   *Thresholds `json:"alert_thresholds"`
 	MetricsRetention  time.Duration `json:"metrics_retention"`
 	EnableAlerts      bool          `json:"enable_alerts"`
 }
@@ -94,7 +94,7 @@ func NewProductionConfig() *ProductionConfig {
 	monitoringConfig := &PerformanceMonitoringConfig{
 		Enabled:         true,
 		MonitorInterval: 30 * time.Second,
-		AlertThresholds: DefaultPerformanceThresholds(),
+		AlertThresholds: DefaultThresholds(),
 		MetricsRetention: 24 * time.Hour,
 		EnableAlerts:    true,
 	}
@@ -190,7 +190,7 @@ func ApplyConfiguration(ctx context.Context, config *ProductionConfig) error {
 
 	// Start performance monitoring if enabled
 	if config.MonitoringConfig.Enabled {
-		monitor := NewPerformanceMonitor(config.MonitoringConfig.AlertThresholds)
+		monitor := NewMonitor(config.MonitoringConfig.AlertThresholds)
 		monitor.SetAlertCallback(DefaultAlertHandler)
 		
 		go monitor.StartMonitoring(ctx, config.MonitoringConfig.MonitorInterval)

+ 3 - 3
internal/nginx_log/production_throughput_test.go

@@ -81,7 +81,7 @@ func runCompleteProductionTest(t *testing.T, recordCount int) {
 		10000, // Large cache for production
 	)
 
-	optimizedParser := parser.NewOptimizedParser(
+	optimizedParser := parser.NewParser(
 		&parser.Config{
 			MaxLineLength: 16 * 1024,
 			WorkerCount:   12,
@@ -156,7 +156,7 @@ type ProductionResult struct {
 	Duration         time.Duration
 }
 
-func executeProductionRebuild(ctx context.Context, indexerInstance *indexer.ParallelIndexer, parser *parser.OptimizedParser, logFile string) (*ProductionResult, error) {
+func executeProductionRebuild(ctx context.Context, indexerInstance *indexer.ParallelIndexer, parser *parser.Parser, logFile string) (*ProductionResult, error) {
 	// Open log file
 	file, err := os.Open(logFile)
 	if err != nil {
@@ -168,7 +168,7 @@ func executeProductionRebuild(ctx context.Context, indexerInstance *indexer.Para
 	startTime := time.Now()
 
 	// Use optimized parse stream (same as production)
-	parseResult, err := parser.OptimizedParseStream(ctx, file)
+	parseResult, err := parser.ParseStream(ctx, file)
 	if err != nil {
 		return nil, fmt.Errorf("parsing failed: %w", err)
 	}

+ 2 - 2
internal/nginx_log/quick_worker_benchmark_test.go

@@ -85,7 +85,7 @@ func runQuickWorkerTest(b *testing.B, config *indexer.Config, logFile string) *Q
 		BatchSize:     500,
 	}
 	
-	optimizedParser := parser.NewOptimizedParser(
+	optimizedParser := parser.NewParser(
 		parserConfig,
 		nil, // No UA parser for speed
 		nil, // No Geo service for speed
@@ -98,7 +98,7 @@ func runQuickWorkerTest(b *testing.B, config *indexer.Config, logFile string) *Q
 	}
 	defer file.Close()
 	
-	parseResult, err := optimizedParser.OptimizedParseStream(ctx, file)
+	parseResult, err := optimizedParser.ParseStream(ctx, file)
 	if err != nil {
 		b.Fatalf("Parsing failed: %v", err)
 	}

+ 3 - 3
internal/nginx_log/realistic_benchmark_test.go

@@ -57,7 +57,7 @@ func benchmarkCompleteProduction(b *testing.B, recordCount int) {
 	geoService := &BenchGeoIPService{}
 	userAgentParser := parser.NewSimpleUserAgentParser()
 
-	optimizedParser := parser.NewOptimizedParser(
+	optimizedParser := parser.NewParser(
 		&parser.Config{
 			MaxLineLength: 4 * 1024,
 			WorkerCount:   4,
@@ -95,7 +95,7 @@ type BenchResult struct {
 	SuccessRate float64
 }
 
-func runBenchmarkProduction(b *testing.B, config *indexer.Config, optimizedParser *parser.OptimizedParser, logFile string) *BenchResult {
+func runBenchmarkProduction(b *testing.B, config *indexer.Config, optimizedParser *parser.Parser, logFile string) *BenchResult {
 	start := time.Now()
 
 	// Create indexer
@@ -119,7 +119,7 @@ func runBenchmarkProduction(b *testing.B, config *indexer.Config, optimizedParse
 	}
 	defer file.Close()
 
-	parseResult, err := optimizedParser.OptimizedParseStream(ctx, file)
+	parseResult, err := optimizedParser.ParseStream(ctx, file)
 	if err != nil {
 		b.Fatalf("Parsing failed: %v", err)
 	}

+ 356 - 0
internal/nginx_log/searcher/cache.go

@@ -0,0 +1,356 @@
+package searcher
+
+import (
+	"crypto/md5"
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+	"sort"
+	"strconv"
+	"time"
+
+	"github.com/0xJacky/Nginx-UI/internal/nginx_log/utils"
+	"github.com/dgraph-io/ristretto/v2"
+)
+
+// Cache provides high-performance caching using Ristretto
+type Cache struct {
+	cache *ristretto.Cache[string, *SearchResult]
+}
+
+// NewCache creates a new cache with Ristretto
+func NewCache(maxSize int64) *Cache {
+	cache, err := ristretto.NewCache(&ristretto.Config[string, *SearchResult]{
+		NumCounters: maxSize * 10,
+		MaxCost:     maxSize,
+		BufferItems: 64,
+		Metrics:     true,
+	})
+	if err != nil {
+		panic(fmt.Sprintf("failed to create cache: %v", err))
+	}
+
+	return &Cache{cache: cache}
+}
+
+// CacheKeyData represents the normalized data used for cache key generation
+type CacheKeyData struct {
+	Query          string   `json:"query"`
+	Limit          int      `json:"limit"`
+	Offset         int      `json:"offset"`
+	SortBy         string   `json:"sort_by"`
+	SortOrder      string   `json:"sort_order"`
+	StartTime      *int64   `json:"start_time"`
+	EndTime        *int64   `json:"end_time"`
+	UseMainLogPath bool     `json:"use_main_log_path"`
+	LogPaths       []string `json:"log_paths"`
+	Fields         []string `json:"fields"`
+	IPAddresses    []string `json:"ip_addresses"`
+	StatusCodes    []int    `json:"status_codes"`
+	Methods        []string `json:"methods"`
+	Paths          []string `json:"paths"`
+	UserAgents     []string `json:"user_agents"`
+	Referers       []string `json:"referers"`
+	Countries      []string `json:"countries"`
+	Browsers       []string `json:"browsers"`
+	OSs            []string `json:"operating_systems"`
+	Devices        []string `json:"devices"`
+	MinBytes       *int64   `json:"min_bytes"`
+	MaxBytes       *int64   `json:"max_bytes"`
+	MinReqTime     *float64 `json:"min_request_time"`
+	MaxReqTime     *float64 `json:"max_request_time"`
+	IncludeFacets  bool     `json:"include_facets"`
+	IncludeStats   bool     `json:"include_stats"`
+	FacetFields    []string `json:"facet_fields"`
+	FacetSize      int      `json:"facet_size"`
+	UseCache       bool     `json:"use_cache"`
+}
+
+// sortedUniqueStrings returns a sorted, deduplicated copy of a string slice
+func sortedUniqueStrings(src []string) []string {
+	if len(src) == 0 {
+		return nil
+	}
+	seen := make(map[string]struct{}, len(src))
+	res := make([]string, 0, len(src))
+	for _, s := range src {
+		if _, exists := seen[s]; !exists {
+			seen[s] = struct{}{}
+			res = append(res, s)
+		}
+	}
+	sort.Strings(res)
+	return res
+}
+
+// sortedStrings returns a sorted copy of a string slice
+func sortedStrings(src []string) []string {
+	if len(src) == 0 {
+		return nil
+	}
+	res := make([]string, len(src))
+	copy(res, src)
+	sort.Strings(res)
+	return res
+}
+
+// sortedInts returns a sorted copy of an int slice
+func sortedInts(src []int) []int {
+	if len(src) == 0 {
+		return nil
+	}
+	res := make([]int, len(src))
+	copy(res, src)
+	sort.Ints(res)
+	return res
+}
+
+// GenerateKey generates an efficient cache key for a search request
+func (c *Cache) GenerateKey(req *SearchRequest) string {
+	keyData := CacheKeyData{
+		Query:          req.Query,
+		Limit:          req.Limit,
+		Offset:         req.Offset,
+		SortBy:         req.SortBy,
+		SortOrder:      req.SortOrder,
+		StartTime:      req.StartTime,
+		EndTime:        req.EndTime,
+		UseMainLogPath: req.UseMainLogPath,
+		LogPaths:       sortedUniqueStrings(req.LogPaths),
+		Fields:         sortedStrings(req.Fields),
+		IPAddresses:    sortedUniqueStrings(req.IPAddresses),
+		StatusCodes:    sortedInts(req.StatusCodes),
+		Methods:        sortedUniqueStrings(req.Methods),
+		Paths:          sortedUniqueStrings(req.Paths),
+		UserAgents:     sortedUniqueStrings(req.UserAgents),
+		Referers:       sortedUniqueStrings(req.Referers),
+		Countries:      sortedUniqueStrings(req.Countries),
+		Browsers:       sortedUniqueStrings(req.Browsers),
+		OSs:            sortedUniqueStrings(req.OSs),
+		Devices:        sortedUniqueStrings(req.Devices),
+		MinBytes:       req.MinBytes,
+		MaxBytes:       req.MaxBytes,
+		MinReqTime:     req.MinReqTime,
+		MaxReqTime:     req.MaxReqTime,
+		IncludeFacets:  req.IncludeFacets,
+		IncludeStats:   req.IncludeStats,
+		FacetFields:    sortedUniqueStrings(req.FacetFields),
+		FacetSize:      req.FacetSize,
+		UseCache:       req.UseCache,
+	}
+
+	jsonData, err := json.Marshal(keyData)
+	if err != nil {
+		return c.generateFallbackKey(req)
+	}
+
+	hash := md5.Sum(jsonData)
+	return hex.EncodeToString(hash[:])
+}
+
+// generateFallbackKey creates a basic cache key when JSON marshaling fails
+func (c *Cache) generateFallbackKey(req *SearchRequest) string {
+	keyBuf := make([]byte, 0, len(req.Query)+len(req.SortBy)+len(req.SortOrder)+32)
+	keyBuf = append(keyBuf, "q:"...)
+	keyBuf = append(keyBuf, req.Query...)
+	keyBuf = append(keyBuf, "|l:"...)
+	keyBuf = utils.AppendInt(keyBuf, req.Limit)
+	keyBuf = append(keyBuf, "|o:"...)
+	keyBuf = utils.AppendInt(keyBuf, req.Offset)
+	keyBuf = append(keyBuf, "|s:"...)
+	keyBuf = append(keyBuf, req.SortBy...)
+	keyBuf = append(keyBuf, "|so:"...)
+	keyBuf = append(keyBuf, req.SortOrder...)
+	return utils.BytesToStringUnsafe(keyBuf)
+}
+
+// Get retrieves a search result from cache
+func (c *Cache) Get(req *SearchRequest) *SearchResult {
+	key := c.GenerateKey(req)
+
+	result, found := c.cache.Get(key)
+	if !found {
+		return nil
+	}
+
+	cachedResult := *result
+	cachedResult.FromCache = true
+	return &cachedResult
+}
+
+// Put stores a search result in cache with automatic cost calculation
+func (c *Cache) Put(req *SearchRequest, result *SearchResult, ttl time.Duration) {
+	key := c.GenerateKey(req)
+
+	cost := int64(1 + len(result.Hits)/10)
+	if cost < 1 {
+		cost = 1
+	}
+
+	c.cache.SetWithTTL(key, result, cost, ttl)
+	c.cache.Wait()
+}
+
+// Clear clears all cached entries
+func (c *Cache) Clear() {
+	if c != nil && c.cache != nil {
+		c.cache.Clear()
+	}
+}
+
+// GetStats returns cache statistics
+func (c *Cache) GetStats() *CacheStats {
+	metrics := c.cache.Metrics
+
+	return &CacheStats{
+		Size:      int(metrics.KeysAdded() - metrics.KeysEvicted()),
+		Capacity:  int(c.cache.MaxCost()),
+		HitCount:  int64(metrics.Hits()),
+		MissCount: int64(metrics.Misses()),
+		HitRate:   metrics.Ratio(),
+		Evictions: int64(metrics.KeysEvicted()),
+		Additions: int64(metrics.KeysAdded()),
+		Updates:   int64(metrics.KeysUpdated()),
+		Cost:      int64(metrics.CostAdded() - metrics.CostEvicted()),
+	}
+}
+
+// CacheStats provides detailed cache statistics
+type CacheStats struct {
+	Size      int     `json:"size"`
+	Capacity  int     `json:"capacity"`
+	HitCount  int64   `json:"hit_count"`
+	MissCount int64   `json:"miss_count"`
+	HitRate   float64 `json:"hit_rate"`
+	Evictions int64   `json:"evictions"`
+	Additions int64   `json:"additions"`
+	Updates   int64   `json:"updates"`
+	Cost      int64   `json:"cost"`
+}
+
+// Warmup pre-loads frequently used queries into cache
+func (c *Cache) Warmup(queries []WarmupQuery) {
+	for _, query := range queries {
+		if query.Result != nil {
+			key := c.GenerateKey(query.Request)
+			c.cache.Set(key, query.Result, 1)
+		}
+	}
+
+	c.cache.Wait()
+}
+
+// WarmupQuery represents a query and result pair for cache warmup
+type WarmupQuery struct {
+	Request *SearchRequest `json:"request"`
+	Result  *SearchResult  `json:"result"`
+}
+
+// Close closes the cache and frees resources
+func (c *Cache) Close() {
+	c.cache.Close()
+}
+
+// KeyGen provides even faster key generation for hot paths
+type KeyGen struct {
+	buffer []byte
+}
+
+// NewKeyGen creates a key generator with pre-allocated buffer
+func NewKeyGen() *KeyGen {
+	return &KeyGen{
+		buffer: make([]byte, 0, 256),
+	}
+}
+
+// GenerateKey generates a key using pre-allocated buffer
+func (kg *KeyGen) GenerateKey(req *SearchRequest) string {
+	kg.buffer = kg.buffer[:0]
+
+	kg.buffer = append(kg.buffer, "q:"...)
+	kg.buffer = append(kg.buffer, req.Query...)
+	kg.buffer = append(kg.buffer, "|l:"...)
+	kg.buffer = strconv.AppendInt(kg.buffer, int64(req.Limit), 10)
+	kg.buffer = append(kg.buffer, "|o:"...)
+	kg.buffer = strconv.AppendInt(kg.buffer, int64(req.Offset), 10)
+	kg.buffer = append(kg.buffer, "|s:"...)
+	kg.buffer = append(kg.buffer, req.SortBy...)
+	kg.buffer = append(kg.buffer, "|so:"...)
+	kg.buffer = append(kg.buffer, req.SortOrder...)
+
+	if req.StartTime != nil {
+		kg.buffer = append(kg.buffer, "|st:"...)
+		kg.buffer = strconv.AppendInt(kg.buffer, *req.StartTime, 10)
+	}
+	if req.EndTime != nil {
+		kg.buffer = append(kg.buffer, "|et:"...)
+		kg.buffer = strconv.AppendInt(kg.buffer, *req.EndTime, 10)
+	}
+
+	if len(req.StatusCodes) > 0 {
+		kg.buffer = append(kg.buffer, "|sc:"...)
+		for i, code := range req.StatusCodes {
+			if i > 0 {
+				kg.buffer = append(kg.buffer, ',')
+			}
+			kg.buffer = strconv.AppendInt(kg.buffer, int64(code), 10)
+		}
+	}
+
+	return string(kg.buffer)
+}
+
+// Middleware provides middleware functionality for caching
+type Middleware struct {
+	cache      *Cache
+	keyGen     *KeyGen
+	enabled    bool
+	defaultTTL time.Duration
+}
+
+// NewMiddleware creates a new cache middleware
+func NewMiddleware(cache *Cache, defaultTTL time.Duration) *Middleware {
+	return &Middleware{
+		cache:      cache,
+		keyGen:     NewKeyGen(),
+		enabled:    true,
+		defaultTTL: defaultTTL,
+	}
+}
+
+// Enable enables caching
+func (m *Middleware) Enable() {
+	m.enabled = true
+}
+
+// Disable disables caching
+func (m *Middleware) Disable() {
+	m.enabled = false
+}
+
+// IsEnabled returns whether caching is enabled
+func (m *Middleware) IsEnabled() bool {
+	return m.enabled
+}
+
+// GetOrSet attempts to get from cache, or executes the provided function and caches the result
+func (m *Middleware) GetOrSet(req *SearchRequest, fn func() (*SearchResult, error)) (*SearchResult, error) {
+	if !m.enabled {
+		return fn()
+	}
+
+	if cached := m.cache.Get(req); cached != nil {
+		return cached, nil
+	}
+
+	result, err := fn()
+	if err != nil {
+		return nil, err
+	}
+
+	if result != nil {
+		m.cache.Put(req, result, m.defaultTTL)
+	}
+
+	return result, nil
+}

+ 10 - 10
internal/nginx_log/searcher/cardinality_counter_test.go

@@ -10,14 +10,14 @@ import (
 
 func TestCardinalityCounter_CountCardinality(t *testing.T) {
 	// Create a mock cardinality counter with nil shards for testing
-	counter := NewCardinalityCounter(nil)
+	counter := NewCounter(nil)
 	
 	req := &CardinalityRequest{
 		Field: "path_exact",
 	}
 	
 	// Test that it handles nil shards gracefully
-	result, err := counter.CountCardinality(context.Background(), req)
+	result, err := counter.Count(context.Background(), req)
 	
 	// Should return error when IndexAlias is not available
 	assert.Error(t, err)
@@ -28,12 +28,12 @@ func TestCardinalityCounter_CountCardinality(t *testing.T) {
 }
 
 func TestCardinalityCounter_BatchCountCardinality(t *testing.T) {
-	counter := NewCardinalityCounter(nil)
+	counter := NewCounter(nil)
 	
 	fields := []string{"path_exact", "ip", "browser"}
 	baseReq := &CardinalityRequest{}
 	
-	results, err := counter.BatchCountCardinality(context.Background(), fields, baseReq)
+	results, err := counter.BatchCount(context.Background(), fields, baseReq)
 	
 	assert.NoError(t, err)
 	assert.Len(t, results, 3)
@@ -46,34 +46,34 @@ func TestCardinalityCounter_BatchCountCardinality(t *testing.T) {
 }
 
 func TestCardinalityRequest_Validation(t *testing.T) {
-	counter := NewCardinalityCounter(nil)
+	counter := NewCounter(nil)
 	
 	// Test empty field name
 	req := &CardinalityRequest{
 		Field: "",
 	}
 	
-	_, err := counter.CountCardinality(context.Background(), req)
+	_, err := counter.Count(context.Background(), req)
 	assert.Error(t, err)
 	assert.Contains(t, err.Error(), "field name is required")
 }
 
 func TestCardinalityCounter_EstimateCardinality(t *testing.T) {
-	counter := NewCardinalityCounter(nil)
+	counter := NewCounter(nil)
 	
 	req := &CardinalityRequest{
 		Field: "test_field",
 	}
 	
 	// For now, EstimateCardinality should behave the same as CountCardinality
-	result, err := counter.EstimateCardinality(context.Background(), req)
+	result, err := counter.Estimate(context.Background(), req)
 	assert.NoError(t, err)
 	assert.NotNil(t, result)
 	assert.Equal(t, "test_field", result.Field)
 }
 
 func TestCardinalityRequest_TimeRange(t *testing.T) {
-	counter := NewCardinalityCounter(nil)
+	counter := NewCounter(nil)
 	
 	now := time.Now().Unix()
 	start := now - 3600 // 1 hour ago
@@ -84,7 +84,7 @@ func TestCardinalityRequest_TimeRange(t *testing.T) {
 		EndTime:   &now,
 	}
 	
-	result, err := counter.CountCardinality(context.Background(), req)
+	result, err := counter.Count(context.Background(), req)
 	assert.Error(t, err) // IndexAlias not available
 	assert.NotNil(t, result)
 	assert.Equal(t, "path_exact", result.Field)

+ 44 - 44
internal/nginx_log/searcher/cardinality_counter.go → internal/nginx_log/searcher/counter.go

@@ -12,45 +12,45 @@ import (
 	"github.com/uozi-tech/cosy/logger"
 )
 
-// CardinalityCounter provides efficient unique value counting without large FacetSize
-type CardinalityCounter struct {
+// Counter provides efficient unique value counting without large FacetSize
+type Counter struct {
 	indexAlias bleve.IndexAlias // Use IndexAlias instead of individual shards
 	shards     []bleve.Index    // Keep shards for fallback if needed
 	mu         sync.RWMutex
 	stopOnce   sync.Once
 }
 
-// NewCardinalityCounter creates a new cardinality counter
-func NewCardinalityCounter(shards []bleve.Index) *CardinalityCounter {
+// NewCounter creates a new cardinality counter
+func NewCounter(shards []bleve.Index) *Counter {
 	var indexAlias bleve.IndexAlias
 	if len(shards) > 0 {
-		// Create IndexAlias for distributed search like DistributedSearcher does
+		// Create IndexAlias for distributed search like Searcher does
 		indexAlias = bleve.NewIndexAlias(shards...)
 
 		// Note: IndexAlias doesn't have SetIndexMapping method
 		// The mapping will be inherited from the constituent indices
-		logger.Debugf("Created IndexAlias for cardinality counter with %d shards", len(shards))
+		logger.Debugf("Created IndexAlias for counter with %d shards", len(shards))
 	}
 
-	return &CardinalityCounter{
+	return &Counter{
 		indexAlias: indexAlias,
 		shards:     shards,
 	}
 }
 
 // Stop gracefully closes the counter's resources, like the IndexAlias.
-func (cc *CardinalityCounter) Stop() error {
+func (c *Counter) Stop() error {
 	var err error
-	cc.stopOnce.Do(func() {
-		cc.mu.Lock()
-		defer cc.mu.Unlock()
-
-		if cc.indexAlias != nil {
-			logger.Debugf("Closing IndexAlias in CardinalityCounter")
-			err = cc.indexAlias.Close()
-			cc.indexAlias = nil
+	c.stopOnce.Do(func() {
+		c.mu.Lock()
+		defer c.mu.Unlock()
+
+		if c.indexAlias != nil {
+			logger.Debugf("Closing IndexAlias in Counter")
+			err = c.indexAlias.Close()
+			c.indexAlias = nil
 		}
-		cc.shards = nil
+		c.shards = nil
 	})
 	return err
 }
@@ -73,17 +73,17 @@ type CardinalityResult struct {
 	Error       string `json:"error,omitempty"`
 }
 
-// CountCardinality efficiently counts unique values using IndexAlias with global scoring
+// Count efficiently counts unique values using IndexAlias with global scoring
 // This leverages Bleve's distributed search optimizations and avoids FacetSize limits
-func (cc *CardinalityCounter) CountCardinality(ctx context.Context, req *CardinalityRequest) (*CardinalityResult, error) {
-	cc.mu.RLock()
-	defer cc.mu.RUnlock()
+func (c *Counter) Count(ctx context.Context, req *CardinalityRequest) (*CardinalityResult, error) {
+	c.mu.RLock()
+	defer c.mu.RUnlock()
 
 	if req.Field == "" {
 		return nil, fmt.Errorf("field name is required")
 	}
 
-	if cc.indexAlias == nil {
+	if c.indexAlias == nil {
 		return &CardinalityResult{
 			Field: req.Field,
 			Error: "IndexAlias not available",
@@ -91,7 +91,7 @@ func (cc *CardinalityCounter) CountCardinality(ctx context.Context, req *Cardina
 	}
 
 	// Use IndexAlias with global scoring for consistent distributed search
-	uniqueTerms, totalDocs, err := cc.collectTermsUsingIndexAlias(ctx, req)
+	uniqueTerms, totalDocs, err := c.collectTermsUsingIndexAlias(ctx, req)
 	if err != nil {
 		return &CardinalityResult{
 			Field: req.Field,
@@ -110,14 +110,14 @@ func (cc *CardinalityCounter) CountCardinality(ctx context.Context, req *Cardina
 }
 
 // collectTermsUsingIndexAlias collects unique terms using IndexAlias with global scoring
-func (cc *CardinalityCounter) collectTermsUsingIndexAlias(ctx context.Context, req *CardinalityRequest) (map[string]struct{}, uint64, error) {
+func (c *Counter) collectTermsUsingIndexAlias(ctx context.Context, req *CardinalityRequest) (map[string]struct{}, uint64, error) {
 	uniqueTerms := make(map[string]struct{})
 
-	// Enable global scoring context like DistributedSearcher does
+	// Enable global scoring context like Searcher does
 	globalCtx := context.WithValue(ctx, search.SearchTypeKey, search.GlobalScoring)
 
 	// Strategy 1: Try large facet first (more efficient for most cases)
-	terms1, totalDocs, err1 := cc.collectTermsUsingLargeFacet(globalCtx, req)
+	terms1, totalDocs, err1 := c.collectTermsUsingLargeFacet(globalCtx, req)
 	if err1 != nil {
 		logger.Warnf("Large facet collection failed: %v", err1)
 	} else {
@@ -131,7 +131,7 @@ func (cc *CardinalityCounter) collectTermsUsingIndexAlias(ctx context.Context, r
 	needsPagination := len(terms1) >= 50000 || err1 != nil
 	if needsPagination {
 		logger.Infof("Using pagination to collect remaining terms...")
-		terms2, _, err2 := cc.collectTermsUsingPagination(globalCtx, req)
+		terms2, _, err2 := c.collectTermsUsingPagination(globalCtx, req)
 		if err2 != nil {
 			logger.Warnf("Pagination collection failed: %v", err2)
 		} else {
@@ -146,7 +146,7 @@ func (cc *CardinalityCounter) collectTermsUsingIndexAlias(ctx context.Context, r
 }
 
 // collectTermsUsingLargeFacet uses IndexAlias with a large facet to efficiently collect terms
-func (cc *CardinalityCounter) collectTermsUsingLargeFacet(ctx context.Context, req *CardinalityRequest) (map[string]struct{}, uint64, error) {
+func (c *Counter) collectTermsUsingLargeFacet(ctx context.Context, req *CardinalityRequest) (map[string]struct{}, uint64, error) {
 	terms := make(map[string]struct{})
 
 	// Build search request using IndexAlias with proper filtering
@@ -189,16 +189,16 @@ func (cc *CardinalityCounter) collectTermsUsingLargeFacet(ctx context.Context, r
 
 	// Debug: Log the constructed query
 	if queryBytes, err := json.Marshal(searchReq.Query); err == nil {
-		logger.Debugf("CardinalityCounter query: %s", string(queryBytes))
+		logger.Debugf("Counter query: %s", string(queryBytes))
 	}
 
 	// Execute search using IndexAlias with global scoring context
-	result, err := cc.indexAlias.SearchInContext(ctx, searchReq)
+	result, err := c.indexAlias.SearchInContext(ctx, searchReq)
 	if err != nil {
 		return terms, 0, fmt.Errorf("IndexAlias facet search failed: %w", err)
 	}
 
-	logger.Debugf("CardinalityCounter facet search result: Total=%d, Facets=%v", result.Total, result.Facets != nil)
+	logger.Debugf("Counter facet search result: Total=%d, Facets=%v", result.Total, result.Facets != nil)
 
 	// Extract terms from facet result
 	if facetResult, ok := result.Facets[req.Field]; ok && facetResult.Terms != nil {
@@ -220,7 +220,7 @@ func (cc *CardinalityCounter) collectTermsUsingLargeFacet(ctx context.Context, r
 }
 
 // collectTermsUsingPagination uses IndexAlias with pagination to collect all terms
-func (cc *CardinalityCounter) collectTermsUsingPagination(ctx context.Context, req *CardinalityRequest) (map[string]struct{}, uint64, error) {
+func (c *Counter) collectTermsUsingPagination(ctx context.Context, req *CardinalityRequest) (map[string]struct{}, uint64, error) {
 	terms := make(map[string]struct{})
 
 	pageSize := 10000 // Large page size for efficiency
@@ -265,7 +265,7 @@ func (cc *CardinalityCounter) collectTermsUsingPagination(ctx context.Context, r
 		searchReq.Fields = []string{req.Field}
 
 		// Execute with IndexAlias and global scoring
-		result, err := cc.indexAlias.SearchInContext(ctx, searchReq)
+		result, err := c.indexAlias.SearchInContext(ctx, searchReq)
 		if err != nil {
 			return terms, 0, fmt.Errorf("IndexAlias pagination search failed at page %d: %w", page, err)
 		}
@@ -311,11 +311,11 @@ func (cc *CardinalityCounter) collectTermsUsingPagination(ctx context.Context, r
 	return terms, uint64(processedDocs), nil
 }
 
-// EstimateCardinality provides a fast cardinality estimate using sampling approach
+// Estimate provides a fast cardinality estimate using sampling approach
 // This is useful for very large datasets where exact counting might be expensive
-func (cc *CardinalityCounter) EstimateCardinality(ctx context.Context, req *CardinalityRequest) (*CardinalityResult, error) {
-	cc.mu.RLock()
-	defer cc.mu.RUnlock()
+func (c *Counter) Estimate(ctx context.Context, req *CardinalityRequest) (*CardinalityResult, error) {
+	c.mu.RLock()
+	defer c.mu.RUnlock()
 
 	if req.Field == "" {
 		return nil, fmt.Errorf("field name is required")
@@ -328,12 +328,12 @@ func (cc *CardinalityCounter) EstimateCardinality(ctx context.Context, req *Card
 	totalSampleDocs := uint64(0)
 
 	// Process each shard with sampling
-	for i, shard := range cc.shards {
+	for i, shard := range c.shards {
 		if shard == nil {
 			continue
 		}
 
-		shardSample, shardTotal, err := cc.sampleShardTerms(ctx, shard, req, sampleSize/len(cc.shards))
+		shardSample, shardTotal, err := c.sampleShardTerms(ctx, shard, req, sampleSize/len(c.shards))
 		if err != nil {
 			logger.Errorf("Failed to sample shard %d: %v", i, err)
 			continue
@@ -362,11 +362,11 @@ func (cc *CardinalityCounter) EstimateCardinality(ctx context.Context, req *Card
 
 	// For accurate results with large datasets, we use exact counting
 	// The sampling code above is kept for future statistical estimation
-	return cc.CountCardinality(ctx, req)
+	return c.Count(ctx, req)
 }
 
 // sampleShardTerms takes a statistical sample from a shard for cardinality estimation
-func (cc *CardinalityCounter) sampleShardTerms(ctx context.Context, shard bleve.Index, req *CardinalityRequest, sampleSize int) (map[string]struct{}, uint64, error) {
+func (c *Counter) sampleShardTerms(ctx context.Context, shard bleve.Index, req *CardinalityRequest, sampleSize int) (map[string]struct{}, uint64, error) {
 	terms := make(map[string]struct{})
 
 	searchReq := bleve.NewSearchRequest(bleve.NewMatchAllQuery())
@@ -403,8 +403,8 @@ func (cc *CardinalityCounter) sampleShardTerms(ctx context.Context, shard bleve.
 	return terms, result.Total, nil
 }
 
-// BatchCountCardinality counts cardinality for multiple fields efficiently
-func (cc *CardinalityCounter) BatchCountCardinality(ctx context.Context, fields []string, baseReq *CardinalityRequest) (map[string]*CardinalityResult, error) {
+// BatchCount counts cardinality for multiple fields efficiently
+func (c *Counter) BatchCount(ctx context.Context, fields []string, baseReq *CardinalityRequest) (map[string]*CardinalityResult, error) {
 	results := make(map[string]*CardinalityResult)
 
 	// Process fields in parallel
@@ -419,7 +419,7 @@ func (cc *CardinalityCounter) BatchCountCardinality(ctx context.Context, fields
 			req := *baseReq // Copy base request
 			req.Field = f
 
-			result, err := cc.CountCardinality(ctx, &req)
+			result, err := c.Count(ctx, &req)
 			if err != nil {
 				result = &CardinalityResult{
 					Field: f,

+ 9 - 9
internal/nginx_log/searcher/facet_aggregator.go

@@ -7,7 +7,7 @@ import (
 )
 
 // mergeSingleFacet merges two facets for the same field
-func (ds *DistributedSearcher) mergeSingleFacet(existing, incoming *Facet) {
+func (ds *Searcher) mergeSingleFacet(existing, incoming *Facet) {
 	// Note: Do NOT sum Total values - it represents unique terms count, not document count
 	// The Total should be recalculated based on the actual number of unique terms after merging
 	existing.Missing += incoming.Missing
@@ -60,7 +60,7 @@ func (ds *DistributedSearcher) mergeSingleFacet(existing, incoming *Facet) {
 }
 
 // Aggregate performs aggregations on search results
-func (ds *DistributedSearcher) Aggregate(ctx context.Context, req *AggregationRequest) (*AggregationResult, error) {
+func (ds *Searcher) Aggregate(ctx context.Context, req *AggregationRequest) (*AggregationResult, error) {
 	// This is a simplified implementation
 	// In a full implementation, you would execute the aggregation across all shards
 	// and merge the results similar to how facets are handled
@@ -103,7 +103,7 @@ func (ds *DistributedSearcher) Aggregate(ctx context.Context, req *AggregationRe
 }
 
 // Suggest provides search suggestions
-func (ds *DistributedSearcher) Suggest(ctx context.Context, text string, field string, size int) ([]*Suggestion, error) {
+func (ds *Searcher) Suggest(ctx context.Context, text string, field string, size int) ([]*Suggestion, error) {
 	if size <= 0 || size > 100 {
 		size = 10
 	}
@@ -165,7 +165,7 @@ func (ds *DistributedSearcher) Suggest(ctx context.Context, text string, field s
 }
 
 // extractSuggestionTerms extracts potential suggestion terms from text
-func (ds *DistributedSearcher) extractSuggestionTerms(text string, query string) []string {
+func (ds *Searcher) extractSuggestionTerms(text string, query string) []string {
 	// Simple term extraction - this could be enhanced with NLP
 	terms := strings.Fields(text)
 
@@ -199,7 +199,7 @@ func isCommonWord(word string) bool {
 }
 
 // Analyze analyzes text using a specified analyzer
-func (ds *DistributedSearcher) Analyze(ctx context.Context, text string, analyzer string) ([]string, error) {
+func (ds *Searcher) Analyze(ctx context.Context, text string, analyzer string) ([]string, error) {
 	// This would typically use Bleve's analysis capabilities
 	// For now, provide a simple implementation
 
@@ -223,7 +223,7 @@ func (ds *DistributedSearcher) Analyze(ctx context.Context, text string, analyze
 }
 
 // Cache operations
-func (ds *DistributedSearcher) getFromCache(req *SearchRequest) *SearchResult {
+func (ds *Searcher) getFromCache(req *SearchRequest) *SearchResult {
 	if ds.cache == nil {
 		return nil
 	}
@@ -231,7 +231,7 @@ func (ds *DistributedSearcher) getFromCache(req *SearchRequest) *SearchResult {
 	return ds.cache.Get(req)
 }
 
-func (ds *DistributedSearcher) cacheResult(req *SearchRequest, result *SearchResult) {
+func (ds *Searcher) cacheResult(req *SearchRequest, result *SearchResult) {
 	if ds.cache == nil {
 		return
 	}
@@ -240,7 +240,7 @@ func (ds *DistributedSearcher) cacheResult(req *SearchRequest, result *SearchRes
 }
 
 // ClearCache clears the search cache
-func (ds *DistributedSearcher) ClearCache() error {
+func (ds *Searcher) ClearCache() error {
 	if ds.cache != nil {
 		ds.cache.Clear()
 	}
@@ -248,7 +248,7 @@ func (ds *DistributedSearcher) ClearCache() error {
 }
 
 // GetCacheStats returns cache statistics
-func (ds *DistributedSearcher) GetCacheStats() *CacheStats {
+func (ds *Searcher) GetCacheStats() *CacheStats {
 	if ds.cache != nil {
 		return ds.cache.GetStats()
 	}

+ 2 - 2
internal/nginx_log/searcher/facet_aggregator_test.go

@@ -7,7 +7,7 @@ import (
 )
 
 func TestMergeSingleFacet_UniqueTermsCount(t *testing.T) {
-	ds := &DistributedSearcher{}
+	ds := &Searcher{}
 	
 	// Create initial facet with some terms from shard 1
 	existing := &Facet{
@@ -66,7 +66,7 @@ func TestMergeSingleFacet_UniqueTermsCount(t *testing.T) {
 
 func TestMergeSingleFacet_WithLimitAndOther(t *testing.T) {
 	// DefaultFacetSize is 10 by default, so we'll use enough terms to exceed it
-	ds := &DistributedSearcher{}
+	ds := &Searcher{}
 	
 	existing := &Facet{
 		Field:   "path",

+ 5 - 5
internal/nginx_log/searcher/hot_swap_test.go

@@ -47,7 +47,7 @@ func TestDistributedSearcher_SwapShards(t *testing.T) {
 	// Create distributed searcher with initial shards
 	config := DefaultSearcherConfig()
 	initialShards := []bleve.Index{shard1}
-	searcher := NewDistributedSearcher(config, initialShards)
+	searcher := NewSearcher(config, initialShards)
 	require.NotNil(t, searcher)
 	defer searcher.Stop()
 
@@ -99,7 +99,7 @@ func TestDistributedSearcher_SwapShards_NotRunning(t *testing.T) {
 
 	// Create searcher and stop it
 	config := DefaultSearcherConfig()
-	searcher := NewDistributedSearcher(config, []bleve.Index{shard})
+	searcher := NewSearcher(config, []bleve.Index{shard})
 	require.NotNil(t, searcher)
 	
 	err = searcher.Stop()
@@ -124,7 +124,7 @@ func TestDistributedSearcher_SwapShards_NilIndexAlias(t *testing.T) {
 
 	// Create searcher
 	config := DefaultSearcherConfig()
-	searcher := NewDistributedSearcher(config, []bleve.Index{shard})
+	searcher := NewSearcher(config, []bleve.Index{shard})
 	require.NotNil(t, searcher)
 	defer searcher.Stop()
 
@@ -182,7 +182,7 @@ func TestDistributedSearcher_HotSwap_ZeroDowntime(t *testing.T) {
 	require.NoError(t, err)
 
 	// Start with generation 1
-	searcher := NewDistributedSearcher(DefaultSearcherConfig(), []bleve.Index{gen1Index})
+	searcher := NewSearcher(DefaultSearcherConfig(), []bleve.Index{gen1Index})
 	require.NotNil(t, searcher)
 	defer searcher.Stop()
 
@@ -233,7 +233,7 @@ func TestDistributedSearcher_SwapShards_StatsUpdate(t *testing.T) {
 	defer shard2.Close()
 
 	// Create searcher with one shard
-	searcher := NewDistributedSearcher(DefaultSearcherConfig(), []bleve.Index{shard1})
+	searcher := NewSearcher(DefaultSearcherConfig(), []bleve.Index{shard1})
 	require.NotNil(t, searcher)
 	defer searcher.Stop()
 

+ 0 - 364
internal/nginx_log/searcher/optimized_cache.go

@@ -1,364 +0,0 @@
-package searcher
-
-import (
-	"crypto/md5"
-	"encoding/hex"
-	"encoding/json"
-	"fmt"
-	"sort"
-	"strconv"
-	"time"
-
-	"github.com/0xJacky/Nginx-UI/internal/nginx_log/utils"
-	"github.com/dgraph-io/ristretto/v2"
-)
-
-// OptimizedSearchCache provides high-performance caching using Ristretto
-type OptimizedSearchCache struct {
-	cache *ristretto.Cache[string, *SearchResult]
-}
-
-// NewOptimizedSearchCache creates a new optimized cache with Ristretto
-func NewOptimizedSearchCache(maxSize int64) *OptimizedSearchCache {
-	cache, err := ristretto.NewCache(&ristretto.Config[string, *SearchResult]{
-		NumCounters: maxSize * 10, // Number of keys to track frequency of (10x cache size)
-		MaxCost:     maxSize,      // Maximum cost of cache (number of items)
-		BufferItems: 64,           // Number of keys per Get buffer
-		Metrics:     true,         // Enable metrics collection
-	})
-	if err != nil {
-		panic(fmt.Sprintf("failed to create cache: %v", err))
-	}
-
-	return &OptimizedSearchCache{
-		cache: cache,
-	}
-}
-
-// GenerateOptimizedKey generates an efficient cache key for a search request
-func (osc *OptimizedSearchCache) GenerateOptimizedKey(req *SearchRequest) string {
-	// Create a unique key based on ALL relevant search parameters, including log paths and filters.
-	// To avoid order-sensitive keys, sort all slice fields and deduplicate where appropriate.
-
-	// Build sorted copies to avoid mutating the original request
-	copyStrings := func(src []string, doSort bool, dedup bool) []string {
-		if len(src) == 0 {
-			return nil
-		}
-		res := make([]string, 0, len(src))
-		if dedup {
-			seen := make(map[string]struct{}, len(src))
-			for _, s := range src {
-				if _, ok := seen[s]; ok {
-					continue
-				}
-				seen[s] = struct{}{}
-				res = append(res, s)
-			}
-		} else {
-			res = append(res, src...)
-		}
-		if doSort {
-			sort.Strings(res)
-		}
-		return res
-	}
-
-	copyInts := func(src []int, doSort bool) []int {
-		if len(src) == 0 {
-			return nil
-		}
-		res := append([]int(nil), src...)
-		if doSort {
-			sort.Ints(res)
-		}
-		return res
-	}
-
-	keyData := struct {
-		Query          string   `json:"query"`
-		Limit          int      `json:"limit"`
-		Offset         int      `json:"offset"`
-		SortBy         string   `json:"sort_by"`
-		SortOrder      string   `json:"sort_order"`
-		StartTime      *int64   `json:"start_time"`
-		EndTime        *int64   `json:"end_time"`
-		UseMainLogPath bool     `json:"use_main_log_path"`
-		LogPaths       []string `json:"log_paths"`
-		Fields         []string `json:"fields"`
-		IPAddresses    []string `json:"ip_addresses"`
-		StatusCodes    []int    `json:"status_codes"`
-		Methods        []string `json:"methods"`
-		Paths          []string `json:"paths"`
-		UserAgents     []string `json:"user_agents"`
-		Referers       []string `json:"referers"`
-		Countries      []string `json:"countries"`
-		Browsers       []string `json:"browsers"`
-		OSs            []string `json:"operating_systems"`
-		Devices        []string `json:"devices"`
-		MinBytes       *int64   `json:"min_bytes"`
-		MaxBytes       *int64   `json:"max_bytes"`
-		MinReqTime     *float64 `json:"min_request_time"`
-		MaxReqTime     *float64 `json:"max_request_time"`
-		IncludeFacets  bool     `json:"include_facets"`
-		IncludeStats   bool     `json:"include_stats"`
-		FacetFields    []string `json:"facet_fields"`
-		FacetSize      int      `json:"facet_size"`
-		UseCache       bool     `json:"use_cache"`
-	}{
-		Query:          req.Query,
-		Limit:          req.Limit,
-		Offset:         req.Offset,
-		SortBy:         req.SortBy,
-		SortOrder:      req.SortOrder,
-		StartTime:      req.StartTime,
-		EndTime:        req.EndTime,
-		UseMainLogPath: req.UseMainLogPath,
-		LogPaths:       copyStrings(req.LogPaths, true, true),
-		Fields:         copyStrings(req.Fields, true, false),
-		IPAddresses:    copyStrings(req.IPAddresses, true, true),
-		StatusCodes:    copyInts(req.StatusCodes, true),
-		Methods:        copyStrings(req.Methods, true, true),
-		Paths:          copyStrings(req.Paths, true, true),
-		UserAgents:     copyStrings(req.UserAgents, true, true),
-		Referers:       copyStrings(req.Referers, true, true),
-		Countries:      copyStrings(req.Countries, true, true),
-		Browsers:       copyStrings(req.Browsers, true, true),
-		OSs:            copyStrings(req.OSs, true, true),
-		Devices:        copyStrings(req.Devices, true, true),
-		MinBytes:       req.MinBytes,
-		MaxBytes:       req.MaxBytes,
-		MinReqTime:     req.MinReqTime,
-		MaxReqTime:     req.MaxReqTime,
-		IncludeFacets:  req.IncludeFacets,
-		IncludeStats:   req.IncludeStats,
-		FacetFields:    copyStrings(req.FacetFields, true, true),
-		FacetSize:      req.FacetSize,
-		UseCache:       req.UseCache,
-	}
-
-	// Convert to JSON and hash for consistent key generation
-	jsonData, err := json.Marshal(keyData)
-	if err != nil {
-		// Fallback to efficient string building if JSON marshal fails
-		keyBuf := make([]byte, 0, len(req.Query)+len(req.SortBy)+len(req.SortOrder)+32)
-		keyBuf = append(keyBuf, "q:"...)
-		keyBuf = append(keyBuf, req.Query...)
-		keyBuf = append(keyBuf, "|l:"...)
-		keyBuf = utils.AppendInt(keyBuf, req.Limit)
-		keyBuf = append(keyBuf, "|o:"...)
-		keyBuf = utils.AppendInt(keyBuf, req.Offset)
-		keyBuf = append(keyBuf, "|s:"...)
-		keyBuf = append(keyBuf, req.SortBy...)
-		keyBuf = append(keyBuf, "|so:"...)
-		keyBuf = append(keyBuf, req.SortOrder...)
-		return utils.BytesToStringUnsafe(keyBuf)
-	}
-
-	// Use MD5 hash for compact key representation
-	hash := md5.Sum(jsonData)
-	return hex.EncodeToString(hash[:])
-}
-
-// Get retrieves a search result from cache
-func (osc *OptimizedSearchCache) Get(req *SearchRequest) *SearchResult {
-	key := osc.GenerateOptimizedKey(req)
-
-	if result, found := osc.cache.Get(key); found {
-		// Mark result as from cache
-		cachedResult := *result // Create a copy
-		cachedResult.FromCache = true
-		return &cachedResult
-	}
-
-	return nil
-}
-
-// Put stores a search result in cache with automatic cost calculation
-func (osc *OptimizedSearchCache) Put(req *SearchRequest, result *SearchResult, ttl time.Duration) {
-	key := osc.GenerateOptimizedKey(req)
-
-	// Calculate cost based on result size (number of hits + base cost)
-	cost := int64(1 + len(result.Hits)/10) // Base cost of 1 plus hits/10
-	if cost < 1 {
-		cost = 1
-	}
-
-	// Set with TTL
-	osc.cache.SetWithTTL(key, result, cost, ttl)
-	// Wait for the value to pass through buffers to ensure it's cached
-	osc.cache.Wait()
-}
-
-// Clear clears all cached entries
-func (osc *OptimizedSearchCache) Clear() {
-	if osc != nil && osc.cache != nil {
-		osc.cache.Clear()
-	}
-}
-
-// GetStats returns cache statistics
-func (osc *OptimizedSearchCache) GetStats() *CacheStats {
-	metrics := osc.cache.Metrics
-
-	return &CacheStats{
-		Size:      int(metrics.KeysAdded() - metrics.KeysEvicted()),
-		Capacity:  int(osc.cache.MaxCost()),
-		HitCount:  int64(metrics.Hits()),
-		MissCount: int64(metrics.Misses()),
-		HitRate:   metrics.Ratio(),
-		Evictions: int64(metrics.KeysEvicted()),
-		Additions: int64(metrics.KeysAdded()),
-		Updates:   int64(metrics.KeysUpdated()),
-		Cost:      int64(metrics.CostAdded() - metrics.CostEvicted()),
-	}
-}
-
-// CacheStats provides detailed cache statistics
-type CacheStats struct {
-	Size      int     `json:"size"`       // Current number of items
-	Capacity  int     `json:"capacity"`   // Maximum capacity
-	HitCount  int64   `json:"hit_count"`  // Number of cache hits
-	MissCount int64   `json:"miss_count"` // Number of cache misses
-	HitRate   float64 `json:"hit_rate"`   // Cache hit rate (0.0 to 1.0)
-	Evictions int64   `json:"evictions"`  // Number of evicted items
-	Additions int64   `json:"additions"`  // Number of items added
-	Updates   int64   `json:"updates"`    // Number of items updated
-	Cost      int64   `json:"cost"`       // Current cost
-}
-
-// WarmupCache pre-loads frequently used queries into cache
-func (osc *OptimizedSearchCache) WarmupCache(queries []WarmupQuery) {
-	for _, query := range queries {
-		// Pre-generate keys to warm up the cache
-		key := osc.GenerateOptimizedKey(query.Request)
-		if query.Result != nil {
-			osc.cache.Set(key, query.Result, 1) // Use cost of 1 for warmup
-		}
-	}
-
-	// Wait for cache operations to complete
-	osc.cache.Wait()
-}
-
-// WarmupQuery represents a query and result pair for cache warmup
-type WarmupQuery struct {
-	Request *SearchRequest `json:"request"`
-	Result  *SearchResult  `json:"result"`
-}
-
-// Close closes the cache and frees resources
-func (osc *OptimizedSearchCache) Close() {
-	osc.cache.Close()
-}
-
-// FastKeyGenerator provides even faster key generation for hot paths
-type FastKeyGenerator struct {
-	buffer []byte
-}
-
-// NewFastKeyGenerator creates a key generator with pre-allocated buffer
-func NewFastKeyGenerator() *FastKeyGenerator {
-	return &FastKeyGenerator{
-		buffer: make([]byte, 0, 256), // Pre-allocate 256 bytes
-	}
-}
-
-// GenerateKey generates a key using pre-allocated buffer
-func (fkg *FastKeyGenerator) GenerateKey(req *SearchRequest) string {
-	fkg.buffer = fkg.buffer[:0] // Reset buffer
-
-	// Build key using buffer to avoid allocations
-	fkg.buffer = append(fkg.buffer, "q:"...)
-	fkg.buffer = append(fkg.buffer, req.Query...)
-	fkg.buffer = append(fkg.buffer, "|l:"...)
-	fkg.buffer = strconv.AppendInt(fkg.buffer, int64(req.Limit), 10)
-	fkg.buffer = append(fkg.buffer, "|o:"...)
-	fkg.buffer = strconv.AppendInt(fkg.buffer, int64(req.Offset), 10)
-	fkg.buffer = append(fkg.buffer, "|s:"...)
-	fkg.buffer = append(fkg.buffer, req.SortBy...)
-	fkg.buffer = append(fkg.buffer, "|so:"...)
-	fkg.buffer = append(fkg.buffer, req.SortOrder...)
-
-	// Add timestamps if present
-	if req.StartTime != nil {
-		fkg.buffer = append(fkg.buffer, "|st:"...)
-		fkg.buffer = strconv.AppendInt(fkg.buffer, *req.StartTime, 10)
-	}
-	if req.EndTime != nil {
-		fkg.buffer = append(fkg.buffer, "|et:"...)
-		fkg.buffer = strconv.AppendInt(fkg.buffer, *req.EndTime, 10)
-	}
-
-	// Add arrays (simplified)
-	if len(req.StatusCodes) > 0 {
-		fkg.buffer = append(fkg.buffer, "|sc:"...)
-		for i, code := range req.StatusCodes {
-			if i > 0 {
-				fkg.buffer = append(fkg.buffer, ',')
-			}
-			fkg.buffer = strconv.AppendInt(fkg.buffer, int64(code), 10)
-		}
-	}
-
-	// Convert to string (this still allocates, but fewer allocations overall)
-	return string(fkg.buffer)
-}
-
-// CacheMiddleware provides middleware functionality for caching
-type CacheMiddleware struct {
-	cache      *OptimizedSearchCache
-	keyGen     *FastKeyGenerator
-	enabled    bool
-	defaultTTL time.Duration
-}
-
-// NewCacheMiddleware creates a new cache middleware
-func NewCacheMiddleware(cache *OptimizedSearchCache, defaultTTL time.Duration) *CacheMiddleware {
-	return &CacheMiddleware{
-		cache:      cache,
-		keyGen:     NewFastKeyGenerator(),
-		enabled:    true,
-		defaultTTL: defaultTTL,
-	}
-}
-
-// Enable enables caching
-func (cm *CacheMiddleware) Enable() {
-	cm.enabled = true
-}
-
-// Disable disables caching
-func (cm *CacheMiddleware) Disable() {
-	cm.enabled = false
-}
-
-// IsEnabled returns whether caching is enabled
-func (cm *CacheMiddleware) IsEnabled() bool {
-	return cm.enabled
-}
-
-// GetOrSet attempts to get from cache, or executes the provided function and caches the result
-func (cm *CacheMiddleware) GetOrSet(req *SearchRequest, fn func() (*SearchResult, error)) (*SearchResult, error) {
-	if !cm.enabled {
-		return fn()
-	}
-
-	// Try cache first
-	if cached := cm.cache.Get(req); cached != nil {
-		return cached, nil
-	}
-
-	// Execute function
-	result, err := fn()
-	if err != nil {
-		return nil, err
-	}
-
-	// Cache successful result
-	if result != nil {
-		cm.cache.Put(req, result, cm.defaultTTL)
-	}
-
-	return result, nil
-}

+ 11 - 11
internal/nginx_log/searcher/query_builder.go

@@ -11,20 +11,20 @@ import (
 	"github.com/uozi-tech/cosy/logger"
 )
 
-// QueryBuilderService provides high-level query building functionality
-type QueryBuilderService struct {
+// QueryBuilder provides high-level query building functionality
+type QueryBuilder struct {
 	defaultAnalyzer string
 }
 
-// NewQueryBuilderService creates a new query builder service
-func NewQueryBuilderService() *QueryBuilderService {
-	return &QueryBuilderService{
+// NewQueryBuilder creates a new query builder service
+func NewQueryBuilder() *QueryBuilder {
+	return &QueryBuilder{
 		defaultAnalyzer: "standard",
 	}
 }
 
 // BuildQuery builds a Bleve query from a SearchRequest
-func (qb *QueryBuilderService) BuildQuery(req *SearchRequest) (query.Query, error) {
+func (qb *QueryBuilder) BuildQuery(req *SearchRequest) (query.Query, error) {
 	if req == nil {
 		return nil, fmt.Errorf("search request cannot be nil")
 	}
@@ -105,7 +105,7 @@ func (qb *QueryBuilderService) BuildQuery(req *SearchRequest) (query.Query, erro
 }
 
 // buildTimeRangeQuery builds a time range query
-func (qb *QueryBuilderService) buildTimeRangeQuery(start, end *int64) query.Query {
+func (qb *QueryBuilder) buildTimeRangeQuery(start, end *int64) query.Query {
 	if start == nil && end == nil {
 		return nil
 	}
@@ -127,7 +127,7 @@ func (qb *QueryBuilderService) buildTimeRangeQuery(start, end *int64) query.Quer
 }
 
 // buildTermsQuery builds a terms query for multiple values
-func (qb *QueryBuilderService) buildTermsQuery(field string, terms []string) query.Query {
+func (qb *QueryBuilder) buildTermsQuery(field string, terms []string) query.Query {
 	if len(terms) == 0 {
 		return nil
 	}
@@ -151,7 +151,7 @@ func (qb *QueryBuilderService) buildTermsQuery(field string, terms []string) que
 }
 
 // buildStatusCodeQuery builds a status code query
-func (qb *QueryBuilderService) buildStatusCodeQuery(codes []int) query.Query {
+func (qb *QueryBuilder) buildStatusCodeQuery(codes []int) query.Query {
 	if len(codes) == 0 {
 		return nil
 	}
@@ -166,7 +166,7 @@ func (qb *QueryBuilderService) buildStatusCodeQuery(codes []int) query.Query {
 }
 
 // ValidateSearchRequest validates a search request
-func (qb *QueryBuilderService) ValidateSearchRequest(req *SearchRequest) error {
+func (qb *QueryBuilder) ValidateSearchRequest(req *SearchRequest) error {
 	if req == nil {
 		return fmt.Errorf("search request cannot be nil")
 	}
@@ -189,7 +189,7 @@ func (qb *QueryBuilderService) ValidateSearchRequest(req *SearchRequest) error {
 }
 
 // BuildSuggestionQuery builds a query for suggestions (simplified version)
-func (qb *QueryBuilderService) BuildSuggestionQuery(text, field string) (query.Query, error) {
+func (qb *QueryBuilder) BuildSuggestionQuery(text, field string) (query.Query, error) {
 	if text == "" {
 		return nil, fmt.Errorf("suggestion text cannot be empty")
 	}

+ 102 - 102
internal/nginx_log/searcher/distributed_searcher.go → internal/nginx_log/searcher/searcher.go

@@ -14,13 +14,13 @@ import (
 	"github.com/uozi-tech/cosy/logger"
 )
 
-// DistributedSearcher implements high-performance distributed search across multiple shards
-type DistributedSearcher struct {
+// Searcher implements high-performance distributed search across multiple shards
+type Searcher struct {
 	config       *Config
 	shards       []bleve.Index
 	indexAlias   bleve.IndexAlias // Index alias for global scoring
-	queryBuilder *QueryBuilderService
-	cache        *OptimizedSearchCache
+	queryBuilder *QueryBuilder
+	cache        *Cache
 	stats        *searcherStats
 
 	// Concurrency control
@@ -47,8 +47,8 @@ type searcherStats struct {
 	mutex      sync.RWMutex
 }
 
-// NewDistributedSearcher creates a new distributed searcher
-func NewDistributedSearcher(config *Config, shards []bleve.Index) *DistributedSearcher {
+// NewSearcher creates a new distributed searcher
+func NewSearcher(config *Config, shards []bleve.Index) *Searcher {
 	if config == nil {
 		config = DefaultSearcherConfig()
 	}
@@ -64,11 +64,11 @@ func NewDistributedSearcher(config *Config, shards []bleve.Index) *DistributedSe
 		}
 	}
 
-	searcher := &DistributedSearcher{
+	searcher := &Searcher{
 		config:       config,
 		shards:       shards,
 		indexAlias:   indexAlias,
-		queryBuilder: NewQueryBuilderService(),
+		queryBuilder: NewQueryBuilder(),
 		semaphore:    make(chan struct{}, config.MaxConcurrency),
 		stats: &searcherStats{
 			shardStats: make(map[int]*ShardSearchStats),
@@ -79,7 +79,7 @@ func NewDistributedSearcher(config *Config, shards []bleve.Index) *DistributedSe
 
 	// Initialize cache if enabled
 	if config.EnableCache {
-		searcher.cache = NewOptimizedSearchCache(int64(config.CacheSize))
+		searcher.cache = NewCache(int64(config.CacheSize))
 	}
 
 	// Initialize shard stats
@@ -94,27 +94,27 @@ func NewDistributedSearcher(config *Config, shards []bleve.Index) *DistributedSe
 }
 
 // Search performs a distributed search across all shards
-func (ds *DistributedSearcher) Search(ctx context.Context, req *SearchRequest) (*SearchResult, error) {
-	if atomic.LoadInt32(&ds.running) == 0 {
+func (s *Searcher) Search(ctx context.Context, req *SearchRequest) (*SearchResult, error) {
+	if atomic.LoadInt32(&s.running) == 0 {
 		return nil, fmt.Errorf("searcher is not running")
 	}
 
 	startTime := time.Now()
 	defer func() {
-		ds.recordSearchMetrics(time.Since(startTime), true)
+		s.recordSearchMetrics(time.Since(startTime), true)
 	}()
 
 	// Validate request
-	if err := ds.queryBuilder.ValidateSearchRequest(req); err != nil {
+	if err := s.queryBuilder.ValidateSearchRequest(req); err != nil {
 		return nil, fmt.Errorf("invalid search request: %w", err)
 	}
 
 	// Set defaults
-	ds.setRequestDefaults(req)
+	s.setRequestDefaults(req)
 
 	// Check cache if enabled
-	if ds.config.EnableCache && req.UseCache {
-		if cached := ds.getFromCache(req); cached != nil {
+	if s.config.EnableCache && req.UseCache {
+		if cached := s.getFromCache(req); cached != nil {
 			cached.FromCache = true
 			cached.CacheHit = true
 			return cached, nil
@@ -127,48 +127,48 @@ func (ds *DistributedSearcher) Search(ctx context.Context, req *SearchRequest) (
 		var cancel context.CancelFunc
 		searchCtx, cancel = context.WithTimeout(ctx, req.Timeout)
 		defer cancel()
-	} else if ds.config.TimeoutDuration > 0 {
+	} else if s.config.TimeoutDuration > 0 {
 		var cancel context.CancelFunc
-		searchCtx, cancel = context.WithTimeout(ctx, ds.config.TimeoutDuration)
+		searchCtx, cancel = context.WithTimeout(ctx, s.config.TimeoutDuration)
 		defer cancel()
 	}
 
 	// Acquire semaphore for concurrency control
 	select {
-	case ds.semaphore <- struct{}{}:
-		defer func() { <-ds.semaphore }()
+	case s.semaphore <- struct{}{}:
+		defer func() { <-s.semaphore }()
 	case <-searchCtx.Done():
 		return nil, fmt.Errorf("search timeout")
 	}
 
-	atomic.AddInt32(&ds.stats.activeSearches, 1)
-	defer atomic.AddInt32(&ds.stats.activeSearches, -1)
+	atomic.AddInt32(&s.stats.activeSearches, 1)
+	defer atomic.AddInt32(&s.stats.activeSearches, -1)
 
 	// Build query
-	query, err := ds.queryBuilder.BuildQuery(req)
+	query, err := s.queryBuilder.BuildQuery(req)
 	if err != nil {
 		return nil, fmt.Errorf("failed to build query: %w", err)
 	}
 
 	// Execute search across shards
-	result, err := ds.executeDistributedSearch(searchCtx, query, req)
+	result, err := s.executeDistributedSearch(searchCtx, query, req)
 	if err != nil {
-		ds.recordSearchMetrics(time.Since(startTime), false)
+		s.recordSearchMetrics(time.Since(startTime), false)
 		return nil, err
 	}
 
 	result.Duration = time.Since(startTime)
 
 	// Cache result if enabled
-	if ds.config.EnableCache && req.UseCache {
-		ds.cacheResult(req, result)
+	if s.config.EnableCache && req.UseCache {
+		s.cacheResult(req, result)
 	}
 
 	return result, nil
 }
 
 // SearchAsync performs asynchronous search
-func (ds *DistributedSearcher) SearchAsync(ctx context.Context, req *SearchRequest) (<-chan *SearchResult, <-chan error) {
+func (s *Searcher) SearchAsync(ctx context.Context, req *SearchRequest) (<-chan *SearchResult, <-chan error) {
 	resultChan := make(chan *SearchResult, 1)
 	errorChan := make(chan error, 1)
 
@@ -176,7 +176,7 @@ func (ds *DistributedSearcher) SearchAsync(ctx context.Context, req *SearchReque
 		defer close(resultChan)
 		defer close(errorChan)
 
-		result, err := ds.Search(ctx, req)
+		result, err := s.Search(ctx, req)
 		if err != nil {
 			errorChan <- err
 		} else {
@@ -188,8 +188,8 @@ func (ds *DistributedSearcher) SearchAsync(ctx context.Context, req *SearchReque
 }
 
 // executeDistributedSearch executes search across all healthy shards
-func (ds *DistributedSearcher) executeDistributedSearch(ctx context.Context, query query.Query, req *SearchRequest) (*SearchResult, error) {
-	healthyShards := ds.getHealthyShards()
+func (s *Searcher) executeDistributedSearch(ctx context.Context, query query.Query, req *SearchRequest) (*SearchResult, error) {
+	healthyShards := s.getHealthyShards()
 	if len(healthyShards) == 0 {
 		return nil, fmt.Errorf("no healthy shards available")
 	}
@@ -200,12 +200,12 @@ func (ds *DistributedSearcher) executeDistributedSearch(ctx context.Context, que
 	// narrowed alias. For now, rely on Bleve to skip shards quickly when the filter eliminates them.
 
 	// Use Bleve's native distributed search with global scoring for consistent pagination
-	return ds.executeGlobalScoringSearch(ctx, query, req)
+	return s.executeGlobalScoringSearch(ctx, query, req)
 }
 
 // executeGlobalScoringSearch uses Bleve's native distributed search with global scoring
 // This ensures consistent pagination by letting Bleve handle cross-shard ranking
-func (ds *DistributedSearcher) executeGlobalScoringSearch(ctx context.Context, query query.Query, req *SearchRequest) (*SearchResult, error) {
+func (s *Searcher) executeGlobalScoringSearch(ctx context.Context, query query.Query, req *SearchRequest) (*SearchResult, error) {
 	// Create search request with proper pagination
 	searchReq := bleve.NewSearchRequest(query)
 
@@ -217,7 +217,7 @@ func (ds *DistributedSearcher) executeGlobalScoringSearch(ctx context.Context, q
 	searchReq.From = req.Offset
 
 	// Configure the search request with proper sorting and other settings
-	ds.configureSearchRequest(searchReq, req)
+	s.configureSearchRequest(searchReq, req)
 
 	// Enable global scoring for distributed search consistency
 	// This is the key fix from Bleve documentation for distributed search
@@ -230,17 +230,17 @@ func (ds *DistributedSearcher) executeGlobalScoringSearch(ctx context.Context, q
 	}
 
 	// Execute search using Bleve's IndexAlias with global scoring
-	result, err := ds.indexAlias.SearchInContext(globalCtx, searchReq)
+	result, err := s.indexAlias.SearchInContext(globalCtx, searchReq)
 	if err != nil {
 		return nil, fmt.Errorf("global scoring search failed: %w", err)
 	}
 
 	// Convert Bleve result to our SearchResult format
-	return ds.convertBleveResult(result), nil
+	return s.convertBleveResult(result), nil
 }
 
 // convertBleveResult converts a Bleve SearchResult to our SearchResult format
-func (ds *DistributedSearcher) convertBleveResult(bleveResult *bleve.SearchResult) *SearchResult {
+func (s *Searcher) convertBleveResult(bleveResult *bleve.SearchResult) *SearchResult {
 	result := &SearchResult{
 		Hits:      make([]*SearchHit, 0, len(bleveResult.Hits)),
 		TotalHits: bleveResult.Total,
@@ -296,7 +296,7 @@ func (ds *DistributedSearcher) convertBleveResult(bleveResult *bleve.SearchResul
 }
 
 // configureSearchRequest sets up common search request configuration
-func (ds *DistributedSearcher) configureSearchRequest(searchReq *bleve.SearchRequest, req *SearchRequest) {
+func (s *Searcher) configureSearchRequest(searchReq *bleve.SearchRequest, req *SearchRequest) {
 	// Set up sorting with proper Bleve syntax
 	sortField := req.SortBy
 	if sortField == "" {
@@ -316,7 +316,7 @@ func (ds *DistributedSearcher) configureSearchRequest(searchReq *bleve.SearchReq
 	}
 
 	// Configure highlighting
-	if req.IncludeHighlighting && ds.config.EnableHighlighting {
+	if req.IncludeHighlighting && s.config.EnableHighlighting {
 		searchReq.Highlight = bleve.NewHighlight()
 		if len(req.Fields) > 0 {
 			for _, field := range req.Fields {
@@ -328,7 +328,7 @@ func (ds *DistributedSearcher) configureSearchRequest(searchReq *bleve.SearchReq
 	}
 
 	// Configure facets
-	if req.IncludeFacets && ds.config.EnableFaceting {
+	if req.IncludeFacets && s.config.EnableFaceting {
 		facetFields := req.FacetFields
 		if len(facetFields) == 0 {
 			// Default facet fields
@@ -355,50 +355,50 @@ func (ds *DistributedSearcher) configureSearchRequest(searchReq *bleve.SearchReq
 
 // Utility methods
 
-func (ds *DistributedSearcher) setRequestDefaults(req *SearchRequest) {
+func (s *Searcher) setRequestDefaults(req *SearchRequest) {
 	if req.Timeout == 0 {
-		req.Timeout = ds.config.TimeoutDuration
+		req.Timeout = s.config.TimeoutDuration
 	}
-	if req.UseCache && !ds.config.EnableCache {
+	if req.UseCache && !s.config.EnableCache {
 		req.UseCache = false
 	}
-	if !req.UseCache && ds.config.EnableCache {
+	if !req.UseCache && s.config.EnableCache {
 		req.UseCache = true
 	}
 }
 
-func (ds *DistributedSearcher) getHealthyShards() []int {
+func (s *Searcher) getHealthyShards() []int {
 	// With IndexAlias, Bleve handles shard health internally
 	// Return all shard IDs since the alias will route correctly
-	healthy := make([]int, len(ds.shards))
-	for i := range ds.shards {
+	healthy := make([]int, len(s.shards))
+	for i := range s.shards {
 		healthy[i] = i
 	}
 	return healthy
 }
 
-func (ds *DistributedSearcher) recordSearchMetrics(duration time.Duration, success bool) {
-	atomic.AddInt64(&ds.stats.totalSearches, 1)
-	atomic.AddInt64(&ds.stats.totalLatency, int64(duration))
+func (s *Searcher) recordSearchMetrics(duration time.Duration, success bool) {
+	atomic.AddInt64(&s.stats.totalSearches, 1)
+	atomic.AddInt64(&s.stats.totalLatency, int64(duration))
 
 	if success {
-		atomic.AddInt64(&ds.stats.successfulSearches, 1)
+		atomic.AddInt64(&s.stats.successfulSearches, 1)
 	} else {
-		atomic.AddInt64(&ds.stats.failedSearches, 1)
+		atomic.AddInt64(&s.stats.failedSearches, 1)
 	}
 
 	// Update min/max latency
 	durationNs := int64(duration)
 	for {
-		current := atomic.LoadInt64(&ds.stats.minLatency)
-		if durationNs >= current || atomic.CompareAndSwapInt64(&ds.stats.minLatency, current, durationNs) {
+		current := atomic.LoadInt64(&s.stats.minLatency)
+		if durationNs >= current || atomic.CompareAndSwapInt64(&s.stats.minLatency, current, durationNs) {
 			break
 		}
 	}
 
 	for {
-		current := atomic.LoadInt64(&ds.stats.maxLatency)
-		if durationNs <= current || atomic.CompareAndSwapInt64(&ds.stats.maxLatency, current, durationNs) {
+		current := atomic.LoadInt64(&s.stats.maxLatency)
+		if durationNs <= current || atomic.CompareAndSwapInt64(&s.stats.maxLatency, current, durationNs) {
 			break
 		}
 	}
@@ -406,94 +406,94 @@ func (ds *DistributedSearcher) recordSearchMetrics(duration time.Duration, succe
 
 // Health and statistics
 
-func (ds *DistributedSearcher) IsHealthy() bool {
-	healthy := ds.getHealthyShards()
+func (s *Searcher) IsHealthy() bool {
+	healthy := s.getHealthyShards()
 	return len(healthy) > 0
 }
 
 // IsRunning returns true if the searcher is currently running
-func (ds *DistributedSearcher) IsRunning() bool {
-	return atomic.LoadInt32(&ds.running) == 1
+func (s *Searcher) IsRunning() bool {
+	return atomic.LoadInt32(&s.running) == 1
 }
 
-func (ds *DistributedSearcher) GetStats() *Stats {
-	ds.stats.mutex.RLock()
-	defer ds.stats.mutex.RUnlock()
+func (s *Searcher) GetStats() *Stats {
+	s.stats.mutex.RLock()
+	defer s.stats.mutex.RUnlock()
 
 	stats := &Stats{
-		TotalSearches:      atomic.LoadInt64(&ds.stats.totalSearches),
-		SuccessfulSearches: atomic.LoadInt64(&ds.stats.successfulSearches),
-		FailedSearches:     atomic.LoadInt64(&ds.stats.failedSearches),
-		ActiveSearches:     atomic.LoadInt32(&ds.stats.activeSearches),
-		QueuedSearches:     len(ds.semaphore),
+		TotalSearches:      atomic.LoadInt64(&s.stats.totalSearches),
+		SuccessfulSearches: atomic.LoadInt64(&s.stats.successfulSearches),
+		FailedSearches:     atomic.LoadInt64(&s.stats.failedSearches),
+		ActiveSearches:     atomic.LoadInt32(&s.stats.activeSearches),
+		QueuedSearches:     len(s.semaphore),
 	}
 
 	// Calculate average latency
-	totalLatency := atomic.LoadInt64(&ds.stats.totalLatency)
+	totalLatency := atomic.LoadInt64(&s.stats.totalLatency)
 	if stats.TotalSearches > 0 {
 		stats.AverageLatency = time.Duration(totalLatency / stats.TotalSearches)
 	}
 
-	stats.MinLatency = time.Duration(atomic.LoadInt64(&ds.stats.minLatency))
-	stats.MaxLatency = time.Duration(atomic.LoadInt64(&ds.stats.maxLatency))
+	stats.MinLatency = time.Duration(atomic.LoadInt64(&s.stats.minLatency))
+	stats.MaxLatency = time.Duration(atomic.LoadInt64(&s.stats.maxLatency))
 
 	// Copy shard stats
-	stats.ShardStats = make([]*ShardSearchStats, 0, len(ds.stats.shardStats))
-	for _, stat := range ds.stats.shardStats {
+	stats.ShardStats = make([]*ShardSearchStats, 0, len(s.stats.shardStats))
+	for _, stat := range s.stats.shardStats {
 		statCopy := *stat
 		stats.ShardStats = append(stats.ShardStats, &statCopy)
 	}
 
 	// Add cache stats if cache is enabled
-	if ds.cache != nil {
-		stats.CacheStats = ds.cache.GetStats()
+	if s.cache != nil {
+		stats.CacheStats = s.cache.GetStats()
 	}
 
 	return stats
 }
 
-func (ds *DistributedSearcher) GetConfig() *Config {
-	return ds.config
+func (s *Searcher) GetConfig() *Config {
+	return s.config
 }
 
 // GetShards returns the underlying shards for cardinality counting
-func (ds *DistributedSearcher) GetShards() []bleve.Index {
-	return ds.shards
+func (s *Searcher) GetShards() []bleve.Index {
+	return s.shards
 }
 
 // SwapShards atomically replaces the current shards with new ones using IndexAlias.Swap()
 // This follows Bleve best practices for zero-downtime index updates
-func (ds *DistributedSearcher) SwapShards(newShards []bleve.Index) error {
-	if atomic.LoadInt32(&ds.running) == 0 {
+func (s *Searcher) SwapShards(newShards []bleve.Index) error {
+	if atomic.LoadInt32(&s.running) == 0 {
 		return fmt.Errorf("searcher is not running")
 	}
 
-	if ds.indexAlias == nil {
+	if s.indexAlias == nil {
 		return fmt.Errorf("indexAlias is nil")
 	}
 
 	// Store old shards for logging
-	oldShards := ds.shards
+	oldShards := s.shards
 
 	// Perform atomic swap using IndexAlias - this is the key Bleve operation
 	// that provides zero-downtime index updates
 	logger.Debugf("SwapShards: Starting atomic swap - old=%d, new=%d", len(oldShards), len(newShards))
 
 	swapStartTime := time.Now()
-	ds.indexAlias.Swap(newShards, oldShards)
+	s.indexAlias.Swap(newShards, oldShards)
 	swapDuration := time.Since(swapStartTime)
 
 	logger.Infof("IndexAlias.Swap completed in %v (old=%d shards, new=%d shards)",
 		swapDuration, len(oldShards), len(newShards))
 
 	// Update internal shards reference to match the IndexAlias
-	ds.shards = newShards
+	s.shards = newShards
 
 	// Clear cache after shard swap to prevent stale results
 	// Use goroutine to avoid potential deadlock during shard swap
-	if ds.cache != nil {
+	if s.cache != nil {
 		// Capture cache reference to avoid race condition
-		cache := ds.cache
+		cache := s.cache
 		go func() {
 			// Add a small delay to ensure shard swap is fully completed
 			time.Sleep(100 * time.Millisecond)
@@ -507,17 +507,17 @@ func (ds *DistributedSearcher) SwapShards(newShards []bleve.Index) error {
 	}
 
 	// Update shard stats for the new shards
-	ds.stats.mutex.Lock()
+	s.stats.mutex.Lock()
 	// Clear old shard stats
-	ds.stats.shardStats = make(map[int]*ShardSearchStats)
+	s.stats.shardStats = make(map[int]*ShardSearchStats)
 	// Initialize stats for new shards
 	for i := range newShards {
-		ds.stats.shardStats[i] = &ShardSearchStats{
+		s.stats.shardStats[i] = &ShardSearchStats{
 			ShardID:   i,
 			IsHealthy: true,
 		}
 	}
-	ds.stats.mutex.Unlock()
+	s.stats.mutex.Unlock()
 
 	logger.Debugf("IndexAlias.Swap() completed: %d old shards -> %d new shards",
 		len(oldShards), len(newShards))
@@ -529,7 +529,7 @@ func (ds *DistributedSearcher) SwapShards(newShards []bleve.Index) error {
 		Offset: 0,
 	}
 
-	if _, err := ds.Search(testCtx, testReq); err != nil {
+	if _, err := s.Search(testCtx, testReq); err != nil {
 		logger.Errorf("Post-swap searcher test query failed: %v", err)
 		return fmt.Errorf("searcher test failed after shard swap: %w", err)
 	} else {
@@ -540,31 +540,31 @@ func (ds *DistributedSearcher) SwapShards(newShards []bleve.Index) error {
 }
 
 // Stop gracefully stops the searcher and closes all bleve indexes
-func (ds *DistributedSearcher) Stop() error {
+func (s *Searcher) Stop() error {
 	var err error
 
-	ds.closeOnce.Do(func() {
+	s.closeOnce.Do(func() {
 		// Set running to 0
-		atomic.StoreInt32(&ds.running, 0)
+		atomic.StoreInt32(&s.running, 0)
 
 		// Close the index alias first (this doesn't close underlying indexes)
-		if ds.indexAlias != nil {
-			if closeErr := ds.indexAlias.Close(); closeErr != nil {
+		if s.indexAlias != nil {
+			if closeErr := s.indexAlias.Close(); closeErr != nil {
 				logger.Errorf("Failed to close index alias: %v", closeErr)
 				err = closeErr
 			}
-			ds.indexAlias = nil
+			s.indexAlias = nil
 		}
 
 		// DON'T close the underlying shards - they are managed by the indexer/shard manager
 		// The searcher is just a consumer of these shards, not the owner
 		// Clear the shards slice reference without closing the indexes
-		ds.shards = nil
+		s.shards = nil
 
 		// Close cache if it exists
-		if ds.cache != nil {
-			ds.cache.Close()
-			ds.cache = nil
+		if s.cache != nil {
+			s.cache.Close()
+			s.cache = nil
 		}
 	})
 

+ 9 - 9
internal/nginx_log/searcher/searcher_test.go

@@ -5,9 +5,9 @@ import (
 	"time"
 )
 
-// TestOptimizedSearchCache tests the basic functionality of the optimized search cache
-func TestOptimizedSearchCache(t *testing.T) {
-	cache := NewOptimizedSearchCache(100)
+// TestSearchCache tests the basic functionality of the optimized search cache
+func TestSearchCache(t *testing.T) {
+	cache := NewCache(100)
 	defer cache.Close()
 
 	req := &SearchRequest{
@@ -69,7 +69,7 @@ func TestBasicSearcherConfig(t *testing.T) {
 }
 
 func TestQueryBuilderValidation(t *testing.T) {
-	qb := NewQueryBuilderService()
+	qb := NewQueryBuilder()
 
 	// Test valid request
 	validReq := &SearchRequest{
@@ -95,7 +95,7 @@ func TestQueryBuilderValidation(t *testing.T) {
 }
 
 func TestQueryBuilderCountriesFilter(t *testing.T) {
-	qb := NewQueryBuilderService()
+	qb := NewQueryBuilder()
 
 	tests := []struct {
 		name      string
@@ -179,10 +179,10 @@ func TestSearchRequestDefaults(t *testing.T) {
 }
 
 func TestCacheMiddleware(t *testing.T) {
-	cache := NewOptimizedSearchCache(100)
+	cache := NewCache(100)
 	defer cache.Close()
 
-	middleware := NewCacheMiddleware(cache, 5*time.Minute)
+	middleware := NewMiddleware(cache, 5*time.Minute)
 
 	if !middleware.IsEnabled() {
 		t.Error("middleware should be enabled by default")
@@ -202,7 +202,7 @@ func TestCacheMiddleware(t *testing.T) {
 }
 
 func TestQueryBuilder(t *testing.T) {
-	qb := NewQueryBuilderService()
+	qb := NewQueryBuilder()
 
 	// Test basic query building
 	req := &SearchRequest{
@@ -222,7 +222,7 @@ func TestQueryBuilder(t *testing.T) {
 }
 
 func TestSuggestionQuery(t *testing.T) {
-	qb := NewQueryBuilderService()
+	qb := NewQueryBuilder()
 
 	// Test suggestion query building
 	query, err := qb.BuildSuggestionQuery("test", "message")

+ 12 - 12
internal/nginx_log/searcher/simple_test.go

@@ -5,9 +5,9 @@ import (
 	"time"
 )
 
-// TestOptimizedCache tests the basic functionality of the optimized cache
-func TestOptimizedCache(t *testing.T) {
-	cache := NewOptimizedSearchCache(100)
+// TestCache tests the basic functionality of the optimized cache
+func TestCache(t *testing.T) {
+	cache := NewCache(100)
 	defer cache.Close()
 
 	req := &SearchRequest{
@@ -52,8 +52,8 @@ func TestOptimizedCache(t *testing.T) {
 	t.Logf("Cache stats: Size=%d, HitRate=%.2f", stats.Size, stats.HitRate)
 }
 
-func BenchmarkOptimizedCacheKeyGeneration(b *testing.B) {
-	cache := NewOptimizedSearchCache(1000)
+func BenchmarkCacheKeyGeneration(b *testing.B) {
+	cache := NewCache(1000)
 	defer cache.Close()
 
 	req := &SearchRequest{
@@ -71,13 +71,13 @@ func BenchmarkOptimizedCacheKeyGeneration(b *testing.B) {
 	b.ReportAllocs()
 
 	for i := 0; i < b.N; i++ {
-		_ = cache.GenerateOptimizedKey(req)
+		_ = cache.GenerateKey(req)
 	}
 }
 
 // New tests to ensure cache key considers LogPaths and is order-insensitive
 func TestCacheKeyIncludesLogPathsAndOrderInsensitive(t *testing.T) {
-	cache := NewOptimizedSearchCache(100)
+	cache := NewCache(100)
 	defer cache.Close()
 
 	st := int64(1000)
@@ -107,8 +107,8 @@ func TestCacheKeyIncludesLogPathsAndOrderInsensitive(t *testing.T) {
 		StatusCodes:    []int{404, 200}, // different order
 	}
 
-	keyA := cache.GenerateOptimizedKey(reqA)
-	keyB := cache.GenerateOptimizedKey(reqB)
+	keyA := cache.GenerateKey(reqA)
+	keyB := cache.GenerateKey(reqB)
 
 	if keyA != keyB {
 		t.Fatalf("expected identical cache keys for order-insensitive params, got A=%s B=%s", keyA, keyB)
@@ -127,14 +127,14 @@ func TestCacheKeyIncludesLogPathsAndOrderInsensitive(t *testing.T) {
 		StatusCodes:    []int{200, 404},
 	}
 
-	keyC := cache.GenerateOptimizedKey(reqC)
+	keyC := cache.GenerateKey(reqC)
 	if keyA == keyC {
 		t.Fatalf("expected different cache keys when LogPaths differ, got A=%s C=%s", keyA, keyC)
 	}
 }
 
-func BenchmarkOptimizedCacheOperations(b *testing.B) {
-	cache := NewOptimizedSearchCache(10000)
+func BenchmarkCacheOperations(b *testing.B) {
+	cache := NewCache(10000)
 	defer cache.Close()
 
 	req := &SearchRequest{

+ 4 - 4
internal/nginx_log/searcher/types.go

@@ -203,8 +203,8 @@ type ShardSearcher interface {
 	IsShardHealthy(shardID int) bool
 }
 
-// Searcher defines the main search interface
-type Searcher interface {
+// SearcherInterface defines the main search interface
+type SearcherInterface interface {
 	Search(ctx context.Context, req *SearchRequest) (*SearchResult, error)
 	SearchAsync(ctx context.Context, req *SearchRequest) (<-chan *SearchResult, <-chan error)
 
@@ -273,8 +273,8 @@ type ShardInfo struct {
 	LastUpdated   int64  `json:"last_updated"`
 }
 
-// Query builder types for complex queries
-type QueryBuilder interface {
+// QueryBuilderInterface for complex queries
+type QueryBuilderInterface interface {
 	Query() query.Query
 }
 

+ 2 - 2
internal/nginx_log/simple_production_test.go

@@ -104,7 +104,7 @@ func runActualProductionWorkflow(t *testing.T, config *indexer.Config, logFile s
 		1000,
 	)
 
-	optimizedParser := parser.NewOptimizedParser(
+	optimizedParser := parser.NewParser(
 		&parser.Config{
 			MaxLineLength: 8 * 1024,
 			WorkerCount:   8,
@@ -131,7 +131,7 @@ func runActualProductionWorkflow(t *testing.T, config *indexer.Config, logFile s
 	}
 	defer file.Close()
 
-	parseResult, err := optimizedParser.OptimizedParseStream(ctx, file)
+	parseResult, err := optimizedParser.ParseStream(ctx, file)
 	if err != nil {
 		t.Fatalf("Parsing failed: %v", err)
 	}

+ 62 - 2
internal/nginx_log/task_recovery.go

@@ -20,6 +20,8 @@ type TaskRecovery struct {
 	ctx            context.Context
 	cancel         context.CancelFunc
 	wg             sync.WaitGroup
+	taskLocks      map[string]*sync.Mutex // Per-log-group locks
+	locksMutex     sync.RWMutex           // Protects taskLocks map
 }
 
 // NewTaskRecovery creates a new task recovery manager
@@ -27,16 +29,16 @@ func NewTaskRecovery(parentCtx context.Context) *TaskRecovery {
 	ctx, cancel := context.WithCancel(parentCtx)
 	return &TaskRecovery{
 		logFileManager: GetLogFileManager(),
-		modernIndexer:  GetModernIndexer(),
+		modernIndexer:  GetIndexer(),
 		ctx:            ctx,
 		cancel:         cancel,
+		taskLocks:      make(map[string]*sync.Mutex),
 	}
 }
 
 // RecoverUnfinishedTasks recovers indexing tasks that were incomplete at last shutdown
 func (tr *TaskRecovery) RecoverUnfinishedTasks(ctx context.Context) error {
 	if tr.logFileManager == nil || tr.modernIndexer == nil {
-		logger.Warn("Cannot recover tasks: services not available")
 		return nil
 	}
 
@@ -73,6 +75,44 @@ func (tr *TaskRecovery) RecoverUnfinishedTasks(ctx context.Context) error {
 	return nil
 }
 
+// acquireTaskLock gets or creates a mutex for a specific log group
+func (tr *TaskRecovery) acquireTaskLock(logPath string) *sync.Mutex {
+	tr.locksMutex.Lock()
+	defer tr.locksMutex.Unlock()
+
+	if lock, exists := tr.taskLocks[logPath]; exists {
+		return lock
+	}
+
+	lock := &sync.Mutex{}
+	tr.taskLocks[logPath] = lock
+	return lock
+}
+
+// releaseTaskLock removes the mutex for a specific log group after completion
+func (tr *TaskRecovery) releaseTaskLock(logPath string) {
+	tr.locksMutex.Lock()
+	defer tr.locksMutex.Unlock()
+	delete(tr.taskLocks, logPath)
+}
+
+// isTaskInProgress checks if a task is currently running for a specific log group
+func (tr *TaskRecovery) isTaskInProgress(logPath string) bool {
+	tr.locksMutex.RLock()
+	defer tr.locksMutex.RUnlock()
+
+	if lock, exists := tr.taskLocks[logPath]; exists {
+		// Try to acquire the lock with TryLock
+		// If we can't acquire it, it means task is in progress
+		if lock.TryLock() {
+			lock.Unlock()
+			return false
+		}
+		return true
+	}
+	return false
+}
+
 // needsRecovery determines if a log file has an incomplete indexing task that needs recovery
 func (tr *TaskRecovery) needsRecovery(log *NginxLogWithIndex) bool {
 	// Check for incomplete states that indicate interrupted operations
@@ -103,6 +143,12 @@ func (tr *TaskRecovery) needsRecovery(log *NginxLogWithIndex) bool {
 
 // recoverTask recovers a single indexing task
 func (tr *TaskRecovery) recoverTask(ctx context.Context, logPath string, queuePosition int) error {
+	// Check if task is already in progress
+	if tr.isTaskInProgress(logPath) {
+		logger.Debugf("Skipping recovery for %s - task already in progress", logPath)
+		return nil
+	}
+
 	logger.Debugf("Recovering indexing task for: %s (queue position: %d)", logPath, queuePosition)
 
 	// Set status to queued with queue position
@@ -121,6 +167,14 @@ func (tr *TaskRecovery) recoverTask(ctx context.Context, logPath string, queuePo
 func (tr *TaskRecovery) executeRecoveredTask(ctx context.Context, logPath string) {
 	defer tr.wg.Done() // Always decrement WaitGroup
 
+	// Acquire lock for this specific log group to prevent concurrent execution
+	lock := tr.acquireTaskLock(logPath)
+	lock.Lock()
+	defer func() {
+		lock.Unlock()
+		tr.releaseTaskLock(logPath)
+	}()
+
 	// Check context before starting
 	select {
 	case <-ctx.Done():
@@ -312,6 +366,12 @@ func InitTaskRecovery(ctx context.Context) {
 	// Wait a bit for services to fully initialize
 	time.Sleep(3 * time.Second)
 
+	// Check if services are available
+	if GetLogFileManager() == nil || GetIndexer() == nil {
+		logger.Debug("Modern services not available, skipping task recovery")
+		return
+	}
+
 	globalTaskRecovery = NewTaskRecovery(ctx)
 	if err := globalTaskRecovery.RecoverUnfinishedTasks(ctx); err != nil {
 		logger.Errorf("Failed to recover unfinished tasks: %v", err)

+ 430 - 0
internal/nginx_log/task_scheduler.go

@@ -0,0 +1,430 @@
+package nginx_log
+
+import (
+	"context"
+	"fmt"
+	"sync"
+	"sync/atomic"
+	"time"
+
+	"github.com/0xJacky/Nginx-UI/internal/event"
+	"github.com/0xJacky/Nginx-UI/internal/nginx_log/indexer"
+	"github.com/uozi-tech/cosy/logger"
+)
+
+// TaskScheduler manages all indexing tasks (recovery, manual rebuild, etc.)
+// with unified locking to prevent concurrent execution on the same log group
+type TaskScheduler struct {
+	logFileManager *indexer.LogFileManager
+	modernIndexer  *indexer.ParallelIndexer
+	activeTasks    int32              // Counter for active tasks
+	ctx            context.Context
+	cancel         context.CancelFunc
+	wg             sync.WaitGroup
+	taskLocks      map[string]*sync.Mutex // Per-log-group locks
+	locksMutex     sync.RWMutex           // Protects taskLocks map
+}
+
+// Global task scheduler instance
+var (
+	globalTaskScheduler     *TaskScheduler
+	taskSchedulerOnce       sync.Once
+	taskSchedulerInitialized bool
+	taskSchedulerMutex      sync.RWMutex
+)
+
+// GetTaskScheduler returns the global task scheduler instance
+func GetTaskScheduler() *TaskScheduler {
+	taskSchedulerMutex.RLock()
+	defer taskSchedulerMutex.RUnlock()
+	return globalTaskScheduler
+}
+
+// InitTaskScheduler initializes the global task scheduler
+func InitTaskScheduler(ctx context.Context) {
+	taskSchedulerMutex.Lock()
+	defer taskSchedulerMutex.Unlock()
+
+	if taskSchedulerInitialized {
+		logger.Debug("Task scheduler already initialized")
+		return
+	}
+
+	logger.Debug("Initializing task scheduler")
+
+	// Wait a bit for services to fully initialize
+	time.Sleep(3 * time.Second)
+
+	// Check if services are available
+	if GetLogFileManager() == nil || GetIndexer() == nil {
+		logger.Debug("Modern services not available, skipping task scheduler initialization")
+		return
+	}
+
+	globalTaskScheduler = NewTaskScheduler(ctx)
+	taskSchedulerInitialized = true
+
+	// Start task recovery
+	if err := globalTaskScheduler.RecoverUnfinishedTasks(ctx); err != nil {
+		logger.Errorf("Failed to recover unfinished tasks: %v", err)
+	}
+
+	// Monitor context for shutdown
+	go func() {
+		<-ctx.Done()
+		if globalTaskScheduler != nil {
+			globalTaskScheduler.Shutdown()
+		}
+	}()
+}
+
+// NewTaskScheduler creates a new task scheduler
+func NewTaskScheduler(parentCtx context.Context) *TaskScheduler {
+	ctx, cancel := context.WithCancel(parentCtx)
+	return &TaskScheduler{
+		logFileManager: GetLogFileManager(),
+		modernIndexer:  GetIndexer(),
+		ctx:            ctx,
+		cancel:         cancel,
+		taskLocks:      make(map[string]*sync.Mutex),
+	}
+}
+
+// acquireTaskLock gets or creates a mutex for a specific log group
+func (ts *TaskScheduler) acquireTaskLock(logPath string) *sync.Mutex {
+	ts.locksMutex.Lock()
+	defer ts.locksMutex.Unlock()
+
+	if lock, exists := ts.taskLocks[logPath]; exists {
+		return lock
+	}
+
+	lock := &sync.Mutex{}
+	ts.taskLocks[logPath] = lock
+	return lock
+}
+
+// releaseTaskLock removes the mutex for a specific log group after completion
+func (ts *TaskScheduler) releaseTaskLock(logPath string) {
+	ts.locksMutex.Lock()
+	defer ts.locksMutex.Unlock()
+	delete(ts.taskLocks, logPath)
+}
+
+// IsTaskInProgress checks if a task is currently running for a specific log group
+func (ts *TaskScheduler) IsTaskInProgress(logPath string) bool {
+	ts.locksMutex.RLock()
+	defer ts.locksMutex.RUnlock()
+
+	if lock, exists := ts.taskLocks[logPath]; exists {
+		// Try to acquire the lock with TryLock
+		// If we can't acquire it, it means task is in progress
+		if lock.TryLock() {
+			lock.Unlock()
+			return false
+		}
+		return true
+	}
+	return false
+}
+
+// AcquireTaskLock acquires a lock for external use (e.g., manual rebuild)
+// Returns the lock and a release function
+func (ts *TaskScheduler) AcquireTaskLock(logPath string) (*sync.Mutex, func()) {
+	lock := ts.acquireTaskLock(logPath)
+	lock.Lock()
+
+	releaseFunc := func() {
+		lock.Unlock()
+		ts.releaseTaskLock(logPath)
+	}
+
+	return lock, releaseFunc
+}
+
+// ScheduleIndexTask schedules an indexing task for a log group
+// Returns error if task is already in progress
+func (ts *TaskScheduler) ScheduleIndexTask(ctx context.Context, logPath string, progressConfig *indexer.ProgressConfig) error {
+	// Check if task is already in progress
+	if ts.IsTaskInProgress(logPath) {
+		return fmt.Errorf("indexing task already in progress for %s", logPath)
+	}
+
+	// Queue the task asynchronously with proper context and WaitGroup
+	ts.wg.Add(1)
+	go ts.executeIndexTask(ctx, logPath, progressConfig)
+
+	return nil
+}
+
+// executeIndexTask executes an indexing task with proper locking and progress tracking
+func (ts *TaskScheduler) executeIndexTask(ctx context.Context, logPath string, progressConfig *indexer.ProgressConfig) {
+	defer ts.wg.Done() // Always decrement WaitGroup
+
+	// Acquire lock for this specific log group to prevent concurrent execution
+	lock := ts.acquireTaskLock(logPath)
+	lock.Lock()
+	defer func() {
+		lock.Unlock()
+		ts.releaseTaskLock(logPath)
+	}()
+
+	// Check context before starting
+	select {
+	case <-ctx.Done():
+		logger.Debugf("Context cancelled, skipping task for %s", logPath)
+		return
+	default:
+	}
+
+	logger.Debugf("Executing indexing task: %s", logPath)
+
+	// Get processing manager for global state updates
+	processingManager := event.GetProcessingStatusManager()
+
+	// Increment active tasks counter and set global status if this is the first task
+	isFirstTask := atomic.AddInt32(&ts.activeTasks, 1) == 1
+	if isFirstTask {
+		processingManager.UpdateNginxLogIndexing(true)
+		logger.Debug("Set global indexing status to true")
+	}
+
+	// Ensure we always decrement counter and reset global status when no tasks remain
+	defer func() {
+		remainingTasks := atomic.AddInt32(&ts.activeTasks, -1)
+		if remainingTasks == 0 {
+			processingManager.UpdateNginxLogIndexing(false)
+			logger.Debug("Set global indexing status to false - all tasks completed")
+		}
+		if r := recover(); r != nil {
+			logger.Errorf("Panic during task execution: %v", r)
+		}
+	}()
+
+	// Set status to indexing
+	if err := ts.setTaskStatus(logPath, string(indexer.IndexStatusIndexing), 0); err != nil {
+		logger.Errorf("Failed to set indexing status for %s: %v", logPath, err)
+		return
+	}
+
+	// Execute the indexing with progress tracking
+	startTime := time.Now()
+	docsCountMap, minTime, maxTime, err := ts.modernIndexer.IndexLogGroupWithProgress(logPath, progressConfig)
+
+	if err != nil {
+		logger.Errorf("Failed to execute indexing task %s: %v", logPath, err)
+		// Set error status
+		if statusErr := ts.setTaskStatus(logPath, string(indexer.IndexStatusError), 0); statusErr != nil {
+			logger.Errorf("Failed to set error status for %s: %v", logPath, statusErr)
+		}
+		return
+	}
+
+	// Calculate total documents indexed
+	var totalDocsIndexed uint64
+	for _, docCount := range docsCountMap {
+		totalDocsIndexed += docCount
+	}
+
+	// Save indexing metadata using the log file manager
+	duration := time.Since(startTime)
+	if err := ts.logFileManager.SaveIndexMetadata(logPath, totalDocsIndexed, startTime, duration, minTime, maxTime); err != nil {
+		logger.Errorf("Failed to save index metadata for %s: %v", logPath, err)
+	}
+
+	// Set status to indexed (completed)
+	if err := ts.setTaskStatus(logPath, string(indexer.IndexStatusIndexed), 0); err != nil {
+		logger.Errorf("Failed to set completed status for %s: %v", logPath, err)
+	}
+
+	// Update searcher shards
+	UpdateSearcherShards()
+
+	logger.Debugf("Successfully completed indexing task: %s, Documents: %d", logPath, totalDocsIndexed)
+}
+
+// RecoverUnfinishedTasks recovers indexing tasks that were incomplete at last shutdown
+func (ts *TaskScheduler) RecoverUnfinishedTasks(ctx context.Context) error {
+	if ts.logFileManager == nil || ts.modernIndexer == nil {
+		logger.Warn("Cannot recover tasks: services not available")
+		return nil
+	}
+
+	logger.Debug("Starting recovery of unfinished indexing tasks")
+
+	// Get all logs with their index status
+	allLogs := GetAllLogsWithIndexGrouped(func(log *NginxLogWithIndex) bool {
+		// Only process access logs
+		return log.Type == "access"
+	})
+
+	var incompleteTasksCount int
+	var queuePosition int = 1
+
+	for _, log := range allLogs {
+		if ts.needsRecovery(log) {
+			incompleteTasksCount++
+
+			// Reset to queued status and assign queue position
+			if err := ts.recoverTask(ctx, log.Path, queuePosition); err != nil {
+				logger.Errorf("Failed to recover task for %s: %v", log.Path, err)
+			} else {
+				queuePosition++
+			}
+		}
+	}
+
+	if incompleteTasksCount > 0 {
+		logger.Debugf("Recovered %d incomplete indexing tasks", incompleteTasksCount)
+	} else {
+		logger.Debug("No incomplete indexing tasks found")
+	}
+
+	return nil
+}
+
+// needsRecovery determines if a log file has an incomplete indexing task that needs recovery
+func (ts *TaskScheduler) needsRecovery(log *NginxLogWithIndex) bool {
+	// Check for incomplete states that indicate interrupted operations
+	switch log.IndexStatus {
+	case string(indexer.IndexStatusIndexing):
+		// Task was in progress during last shutdown
+		logger.Debugf("Found incomplete indexing task: %s", log.Path)
+		return true
+
+	case string(indexer.IndexStatusQueued):
+		// Task was queued but may not have started
+		logger.Debugf("Found queued indexing task: %s", log.Path)
+		return true
+
+	case string(indexer.IndexStatusError):
+		// Check if error is recent (within last hour before restart)
+		if log.LastIndexed > 0 {
+			lastIndexTime := time.Unix(log.LastIndexed, 0)
+			if time.Since(lastIndexTime) < time.Hour {
+				logger.Debugf("Found recent error task for retry: %s", log.Path)
+				return true
+			}
+		}
+	}
+
+	return false
+}
+
+// recoverTask recovers a single indexing task
+func (ts *TaskScheduler) recoverTask(ctx context.Context, logPath string, queuePosition int) error {
+	// Check if task is already in progress
+	if ts.IsTaskInProgress(logPath) {
+		logger.Debugf("Skipping recovery for %s - task already in progress", logPath)
+		return nil
+	}
+
+	logger.Debugf("Recovering indexing task for: %s (queue position: %d)", logPath, queuePosition)
+
+	// Set status to queued with queue position
+	if err := ts.setTaskStatus(logPath, string(indexer.IndexStatusQueued), queuePosition); err != nil {
+		return err
+	}
+
+	// Add a small delay to stagger recovery tasks
+	time.Sleep(time.Second * 2)
+
+	// Create recovery progress config
+	progressConfig := ts.createProgressConfig()
+
+	// Schedule the task
+	return ts.ScheduleIndexTask(ctx, logPath, progressConfig)
+}
+
+// createProgressConfig creates a standard progress configuration
+func (ts *TaskScheduler) createProgressConfig() *indexer.ProgressConfig {
+	return &indexer.ProgressConfig{
+		NotifyInterval: 1 * time.Second,
+		OnProgress: func(progress indexer.ProgressNotification) {
+			// Send progress event to frontend
+			event.Publish(event.Event{
+				Type: event.TypeNginxLogIndexProgress,
+				Data: event.NginxLogIndexProgressData{
+					LogPath:         progress.LogGroupPath,
+					Progress:        progress.Percentage,
+					Stage:           "indexing",
+					Status:          "running",
+					ElapsedTime:     progress.ElapsedTime.Milliseconds(),
+					EstimatedRemain: progress.EstimatedRemain.Milliseconds(),
+				},
+			})
+
+			logger.Debugf("Indexing progress: %s - %.1f%% (Files: %d/%d, Lines: %d/%d)",
+				progress.LogGroupPath, progress.Percentage, progress.CompletedFiles,
+				progress.TotalFiles, progress.ProcessedLines, progress.EstimatedLines)
+		},
+		OnCompletion: func(completion indexer.CompletionNotification) {
+			// Send completion event to frontend
+			event.Publish(event.Event{
+				Type: event.TypeNginxLogIndexComplete,
+				Data: event.NginxLogIndexCompleteData{
+					LogPath:     completion.LogGroupPath,
+					Success:     completion.Success,
+					Duration:    int64(completion.Duration.Milliseconds()),
+					TotalLines:  completion.TotalLines,
+					IndexedSize: completion.IndexedSize,
+					Error:       completion.Error,
+				},
+			})
+
+			logger.Debugf("Indexing completion: %s - Success: %t, Duration: %s, Lines: %d, Size: %d bytes",
+				completion.LogGroupPath, completion.Success, completion.Duration,
+				completion.TotalLines, completion.IndexedSize)
+
+			// Send index ready event if indexing was successful
+			if completion.Success {
+				event.Publish(event.Event{
+					Type: event.TypeNginxLogIndexReady,
+					Data: event.NginxLogIndexReadyData{
+						LogPath:     completion.LogGroupPath,
+						StartTime:   time.Now().Unix(),
+						EndTime:     time.Now().Unix(),
+						Available:   true,
+						IndexStatus: "ready",
+					},
+				})
+			}
+		},
+	}
+}
+
+// setTaskStatus updates the task status in the database
+func (ts *TaskScheduler) setTaskStatus(logPath, status string, queuePosition int) error {
+	// Get persistence manager
+	persistence := ts.logFileManager.GetPersistence()
+	if persistence == nil {
+		return fmt.Errorf("persistence manager not available")
+	}
+
+	// Use enhanced SetIndexStatus method
+	return persistence.SetIndexStatus(logPath, status, queuePosition, "")
+}
+
+// Shutdown gracefully stops all tasks
+func (ts *TaskScheduler) Shutdown() {
+	logger.Debug("Shutting down task scheduler...")
+
+	// Cancel all active tasks
+	ts.cancel()
+
+	// Wait for all tasks to complete with timeout
+	done := make(chan struct{})
+	go func() {
+		ts.wg.Wait()
+		close(done)
+	}()
+
+	select {
+	case <-done:
+		logger.Debug("All tasks completed successfully")
+	case <-time.After(30 * time.Second):
+		logger.Warn("Timeout waiting for tasks to complete")
+	}
+
+	logger.Debug("Task scheduler shutdown completed")
+}

+ 17 - 17
internal/nginx_log/utils/enhanced_memory_pools.go → internal/nginx_log/utils/memory_pools.go

@@ -6,8 +6,8 @@ import (
 	"time"
 )
 
-// EnhancedObjectPool provides advanced object pooling with automatic cleanup and monitoring
-type EnhancedObjectPool[T any] struct {
+// ObjectPool provides advanced object pooling with automatic cleanup and monitoring
+type ObjectPool[T any] struct {
 	pool        sync.Pool
 	created     int64
 	reused      int64
@@ -17,9 +17,9 @@ type EnhancedObjectPool[T any] struct {
 	mutex       sync.RWMutex
 }
 
-// NewEnhancedObjectPool creates a new enhanced object pool
-func NewEnhancedObjectPool[T any](newFunc func() *T, resetFunc func(*T), maxSize int) *EnhancedObjectPool[T] {
-	return &EnhancedObjectPool[T]{
+// NewObjectPool creates a new enhanced object pool
+func NewObjectPool[T any](newFunc func() *T, resetFunc func(*T), maxSize int) *ObjectPool[T] {
+	return &ObjectPool[T]{
 		pool: sync.Pool{
 			New: func() interface{} {
 				return newFunc()
@@ -32,7 +32,7 @@ func NewEnhancedObjectPool[T any](newFunc func() *T, resetFunc func(*T), maxSize
 }
 
 // Get retrieves an object from the pool
-func (p *EnhancedObjectPool[T]) Get() *T {
+func (p *ObjectPool[T]) Get() *T {
 	obj := p.pool.Get().(*T)
 	
 	p.mutex.Lock()
@@ -43,7 +43,7 @@ func (p *EnhancedObjectPool[T]) Get() *T {
 }
 
 // Put returns an object to the pool after resetting it
-func (p *EnhancedObjectPool[T]) Put(obj *T) {
+func (p *ObjectPool[T]) Put(obj *T) {
 	if obj == nil {
 		return
 	}
@@ -57,7 +57,7 @@ func (p *EnhancedObjectPool[T]) Put(obj *T) {
 }
 
 // Stats returns pool statistics
-func (p *EnhancedObjectPool[T]) Stats() PoolStats {
+func (p *ObjectPool[T]) Stats() PoolStats {
 	p.mutex.RLock()
 	defer p.mutex.RUnlock()
 	
@@ -79,13 +79,13 @@ type PoolStats struct {
 
 // StringBuilderPool provides pooled string builders
 type StringBuilderPool struct {
-	pool *EnhancedObjectPool[strings.Builder]
+	pool *ObjectPool[strings.Builder]
 }
 
 // NewStringBuilderPool creates a new string builder pool
 func NewStringBuilderPool(initialCap, maxSize int) *StringBuilderPool {
 	return &StringBuilderPool{
-		pool: NewEnhancedObjectPool(
+		pool: NewObjectPool(
 			func() *strings.Builder { 
 				sb := &strings.Builder{}
 				sb.Grow(initialCap)
@@ -109,14 +109,14 @@ func (p *StringBuilderPool) Put(sb *strings.Builder) {
 
 // ByteSlicePool provides pooled byte slices
 type ByteSlicePool struct {
-	pools map[int]*EnhancedObjectPool[[]byte]
+	pools map[int]*ObjectPool[[]byte]
 	mutex sync.RWMutex
 }
 
 // NewByteSlicePool creates a new byte slice pool
 func NewByteSlicePool() *ByteSlicePool {
 	return &ByteSlicePool{
-		pools: make(map[int]*EnhancedObjectPool[[]byte]),
+		pools: make(map[int]*ObjectPool[[]byte]),
 	}
 }
 
@@ -133,7 +133,7 @@ func (p *ByteSlicePool) Get(size int) []byte {
 		p.mutex.Lock()
 		// Double-check after acquiring write lock
 		if pool, exists = p.pools[poolSize]; !exists {
-			pool = NewEnhancedObjectPool(
+			pool = NewObjectPool(
 				func() *[]byte {
 					slice := make([]byte, 0, poolSize)
 					return &slice
@@ -184,13 +184,13 @@ func nextPowerOf2(n int) int {
 
 // MapPool provides pooled maps
 type MapPool[K comparable, V any] struct {
-	pool *EnhancedObjectPool[map[K]V]
+	pool *ObjectPool[map[K]V]
 }
 
 // NewMapPool creates a new map pool
 func NewMapPool[K comparable, V any](initialSize, maxSize int) *MapPool[K, V] {
 	return &MapPool[K, V]{
-		pool: NewEnhancedObjectPool(
+		pool: NewObjectPool(
 			func() *map[K]V {
 				m := make(map[K]V, initialSize)
 				return &m
@@ -218,13 +218,13 @@ func (p *MapPool[K, V]) Put(m map[K]V) {
 
 // SlicePool provides pooled slices
 type SlicePool[T any] struct {
-	pool *EnhancedObjectPool[[]T]
+	pool *ObjectPool[[]T]
 }
 
 // NewSlicePool creates a new slice pool
 func NewSlicePool[T any](initialCap, maxSize int) *SlicePool[T] {
 	return &SlicePool[T]{
-		pool: NewEnhancedObjectPool(
+		pool: NewObjectPool(
 			func() *[]T {
 				slice := make([]T, 0, initialCap)
 				return &slice

+ 6 - 6
internal/nginx_log/utils/enhanced_memory_pools_test.go → internal/nginx_log/utils/memory_pools_test.go

@@ -7,9 +7,9 @@ import (
 	"testing"
 )
 
-// TestEnhancedObjectPool tests the enhanced object pool functionality
-func TestEnhancedObjectPool(t *testing.T) {
-	pool := NewEnhancedObjectPool(
+// TestObjectPool tests the enhanced object pool functionality
+func TestObjectPool(t *testing.T) {
+	pool := NewObjectPool(
 		func() *strings.Builder { return &strings.Builder{} },
 		func(sb *strings.Builder) { sb.Reset() },
 		10,
@@ -211,9 +211,9 @@ func TestPooledWorker(t *testing.T) {
 	}
 }
 
-// BenchmarkEnhancedObjectPool benchmarks the enhanced object pool
-func BenchmarkEnhancedObjectPool(b *testing.B) {
-	pool := NewEnhancedObjectPool(
+// BenchmarkObjectPool benchmarks the enhanced object pool
+func BenchmarkObjectPool(b *testing.B) {
+	pool := NewObjectPool(
 		func() *strings.Builder { return &strings.Builder{} },
 		func(sb *strings.Builder) { sb.Reset() },
 		100,

+ 11 - 11
internal/nginx_log/utils/performance.go

@@ -323,8 +323,8 @@ func (mo *MemoryOptimizer) GetMemoryStats() *MemoryStats {
 	}
 }
 
-// PerformanceMetrics tracks general performance metrics
-type PerformanceMetrics struct {
+// Metrics tracks general performance metrics
+type Metrics struct {
 	operationCount  int64
 	processedItems  int64
 	processTime     int64 // nanoseconds
@@ -335,13 +335,13 @@ type PerformanceMetrics struct {
 	errorCount      int64
 }
 
-// NewPerformanceMetrics creates performance metrics tracker
-func NewPerformanceMetrics() *PerformanceMetrics {
-	return &PerformanceMetrics{}
+// NewMetrics creates performance metrics tracker
+func NewMetrics() *Metrics {
+	return &Metrics{}
 }
 
 // RecordOperation records operation metrics
-func (pm *PerformanceMetrics) RecordOperation(itemCount int, duration time.Duration, success bool) {
+func (pm *Metrics) RecordOperation(itemCount int, duration time.Duration, success bool) {
 	atomic.AddInt64(&pm.operationCount, 1)
 	atomic.AddInt64(&pm.processedItems, int64(itemCount))
 	atomic.AddInt64(&pm.processTime, int64(duration))
@@ -352,23 +352,23 @@ func (pm *PerformanceMetrics) RecordOperation(itemCount int, duration time.Durat
 }
 
 // RecordCacheHit records cache hit
-func (pm *PerformanceMetrics) RecordCacheHit() {
+func (pm *Metrics) RecordCacheHit() {
 	atomic.AddInt64(&pm.cacheHits, 1)
 }
 
 // RecordCacheMiss records cache miss
-func (pm *PerformanceMetrics) RecordCacheMiss() {
+func (pm *Metrics) RecordCacheMiss() {
 	atomic.AddInt64(&pm.cacheMisses, 1)
 }
 
 // RecordAllocation records memory allocation
-func (pm *PerformanceMetrics) RecordAllocation(size int64) {
+func (pm *Metrics) RecordAllocation(size int64) {
 	atomic.AddInt64(&pm.allocationCount, 1)
 	atomic.AddInt64(&pm.allocationSize, size)
 }
 
 // GetMetrics returns current metrics snapshot
-func (pm *PerformanceMetrics) GetMetrics() map[string]interface{} {
+func (pm *Metrics) GetMetrics() map[string]interface{} {
 	operations := atomic.LoadInt64(&pm.operationCount)
 	items := atomic.LoadInt64(&pm.processedItems)
 	timeNs := atomic.LoadInt64(&pm.processTime)
@@ -405,7 +405,7 @@ func (pm *PerformanceMetrics) GetMetrics() map[string]interface{} {
 }
 
 // Reset resets all metrics
-func (pm *PerformanceMetrics) Reset() {
+func (pm *Metrics) Reset() {
 	atomic.StoreInt64(&pm.operationCount, 0)
 	atomic.StoreInt64(&pm.processedItems, 0)
 	atomic.StoreInt64(&pm.processTime, 0)

+ 2 - 2
internal/nginx_log/utils/performance_test.go

@@ -151,8 +151,8 @@ func TestMemoryOptimizer(t *testing.T) {
 	mo.CheckMemoryUsage()
 }
 
-func TestPerformanceMetrics(t *testing.T) {
-	pm := NewPerformanceMetrics()
+func TestMetrics(t *testing.T) {
+	pm := NewMetrics()
 
 	// Record some operations
 	pm.RecordOperation(10, time.Millisecond*100, true)

+ 2 - 2
internal/nginx_log/worker_scaling_benchmark_test.go

@@ -148,7 +148,7 @@ func runWorkerBenchmark(b *testing.B, config *indexer.Config, logFile string, ex
 		BatchSize:     1000,
 	}
 
-	optimizedParser := parser.NewOptimizedParser(
+	optimizedParser := parser.NewParser(
 		parserConfig,
 		parser.NewSimpleUserAgentParser(),
 		&MockGeoService{},
@@ -161,7 +161,7 @@ func runWorkerBenchmark(b *testing.B, config *indexer.Config, logFile string, ex
 	}
 	defer file.Close()
 
-	parseResult, err := optimizedParser.OptimizedParseStream(ctx, file)
+	parseResult, err := optimizedParser.ParseStream(ctx, file)
 	if err != nil {
 		b.Fatalf("Parsing failed: %v", err)
 	}

+ 1 - 0
internal/notification/errors.go

@@ -8,4 +8,5 @@ var (
 	ErrInvalidNotifierConfig  = e.New(400001, "invalid notifier config")
 	ErrInvalidNotificationID  = e.New(400002, "invalid notification ID")
 	ErrExternalNotifyNotFound = e.New(404002, "external notification configuration not found")
+	ErrTelegramChatIDZero     = e.New(400003, "invalid Telegram Chat ID: cannot be zero")
 )

+ 1 - 2
internal/notification/telegram.go

@@ -2,7 +2,6 @@ package notification
 
 import (
 	"context"
-	"errors"
 	"fmt"
 	"net/http"
 	"net/url"
@@ -61,7 +60,7 @@ func init() {
 
 		// Check if chatIDInt is 0, which might indicate an empty or invalid input was parsed
 		if chatIDInt == 0 {
-			return errors.New("invalid Telegram Chat ID: cannot be zero")
+			return ErrTelegramChatIDZero
 		}
 
 		telegramService.AddReceivers(chatIDInt)

+ 1 - 1
internal/performance/config_info.go

@@ -53,7 +53,7 @@ func GetNginxWorkerConfigInfo() (*NginxConfigInfo, error) {
 
 	confPath := nginx.GetConfEntryPath()
 	if confPath == "" {
-		return nil, errors.New("failed to get nginx.conf path")
+		return nil, ErrNginxConfPathEmpty
 	}
 
 	// Read the current configuration

+ 1 - 0
internal/performance/errors.go

@@ -11,4 +11,5 @@ var (
 	ErrTemplateExecError  = e.New(51004, "failed to execute template: {0}")
 	ErrConfigParseError   = e.New(51005, "failed to parse nginx config: {0}")
 	ErrConfigBuildError   = e.New(51006, "failed to build nginx config: {0}")
+	ErrNginxConfPathEmpty = e.New(51007, "failed to get nginx.conf path")
 )

Some files were not shown because too many files changed in this diff