|
@@ -2,9 +2,9 @@ package cache
|
|
|
|
|
|
import (
|
|
|
"context"
|
|
|
+ "io/fs"
|
|
|
"os"
|
|
|
"path/filepath"
|
|
|
- "regexp"
|
|
|
"strings"
|
|
|
"sync"
|
|
|
"time"
|
|
@@ -15,30 +15,21 @@ import (
|
|
|
"github.com/uozi-tech/cosy/logger"
|
|
|
)
|
|
|
|
|
|
-// ScanCallback is a function that gets called during config scanning
|
|
|
-// It receives the config file path and contents
|
|
|
+// ScanCallback is called during config scanning with file path and content
|
|
|
type ScanCallback func(configPath string, content []byte) error
|
|
|
|
|
|
-// Scanner is responsible for scanning and watching nginx config files
|
|
|
+// Scanner watches and scans nginx config files
|
|
|
type Scanner struct {
|
|
|
- ctx context.Context // Context for the scanner
|
|
|
- watcher *fsnotify.Watcher // File system watcher
|
|
|
- scanTicker *time.Ticker // Ticker for periodic scanning
|
|
|
- initialized bool // Whether the scanner has been initialized
|
|
|
- scanning bool // Whether a scan is currently in progress
|
|
|
- scanMutex sync.RWMutex // Mutex for protecting the scanning state
|
|
|
+ ctx context.Context
|
|
|
+ watcher *fsnotify.Watcher
|
|
|
+ scanTicker *time.Ticker
|
|
|
+ scanning bool
|
|
|
+ scanMutex sync.RWMutex
|
|
|
}
|
|
|
|
|
|
-// Global variables
|
|
|
var (
|
|
|
- // scanner is the singleton instance of Scanner
|
|
|
- scanner *Scanner
|
|
|
- configScannerInitMux sync.Mutex
|
|
|
-
|
|
|
- // This regex matches: include directives in nginx config files
|
|
|
- includeRegex = regexp.MustCompile(`include\s+([^;]+);`)
|
|
|
-
|
|
|
- // Global callbacks that will be executed during config file scanning
|
|
|
+ scanner *Scanner
|
|
|
+ scannerInitMutex sync.Mutex
|
|
|
scanCallbacks = make([]ScanCallback, 0)
|
|
|
scanCallbacksMutex sync.RWMutex
|
|
|
)
|
|
@@ -50,17 +41,16 @@ func InitScanner(ctx context.Context) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- s := GetScanner()
|
|
|
- err := s.Initialize(ctx)
|
|
|
- if err != nil {
|
|
|
+ scanner := GetScanner()
|
|
|
+ if err := scanner.Initialize(ctx); err != nil {
|
|
|
logger.Error("Failed to initialize config scanner:", err)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// GetScanner returns the singleton instance of Scanner
|
|
|
+// GetScanner returns the singleton scanner instance
|
|
|
func GetScanner() *Scanner {
|
|
|
- configScannerInitMux.Lock()
|
|
|
- defer configScannerInitMux.Unlock()
|
|
|
+ scannerInitMutex.Lock()
|
|
|
+ defer scannerInitMutex.Unlock()
|
|
|
|
|
|
if scanner == nil {
|
|
|
scanner = &Scanner{}
|
|
@@ -68,29 +58,15 @@ func GetScanner() *Scanner {
|
|
|
return scanner
|
|
|
}
|
|
|
|
|
|
-// RegisterCallback adds a callback function to be executed during scans
|
|
|
-// This function can be called before Scanner is initialized
|
|
|
+// RegisterCallback adds a callback to be executed during scans
|
|
|
func RegisterCallback(callback ScanCallback) {
|
|
|
scanCallbacksMutex.Lock()
|
|
|
defer scanCallbacksMutex.Unlock()
|
|
|
scanCallbacks = append(scanCallbacks, callback)
|
|
|
}
|
|
|
|
|
|
-// publishScanningStatus publishes the scanning status to the event bus
|
|
|
-func (s *Scanner) publishScanningStatus(scanning bool) {
|
|
|
- event.Publish(event.Event{
|
|
|
- Type: event.EventTypeIndexScanning,
|
|
|
- Data: scanning,
|
|
|
- })
|
|
|
-}
|
|
|
-
|
|
|
-// Initialize sets up the scanner and starts watching for file changes
|
|
|
+// Initialize sets up the scanner and starts watching
|
|
|
func (s *Scanner) Initialize(ctx context.Context) error {
|
|
|
- if s.initialized {
|
|
|
- return nil
|
|
|
- }
|
|
|
-
|
|
|
- // Create a new watcher
|
|
|
watcher, err := fsnotify.NewWatcher()
|
|
|
if err != nil {
|
|
|
return err
|
|
@@ -98,86 +74,75 @@ func (s *Scanner) Initialize(ctx context.Context) error {
|
|
|
s.watcher = watcher
|
|
|
s.ctx = ctx
|
|
|
|
|
|
- // Scan for the first time
|
|
|
- err = s.ScanAllConfigs()
|
|
|
- if err != nil {
|
|
|
+ // Initial scan
|
|
|
+ if err := s.ScanAllConfigs(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
- // Setup watcher for config directory
|
|
|
- configDir := filepath.Dir(nginx.GetConfPath())
|
|
|
- availableDir := nginx.GetConfPath("sites-available")
|
|
|
- enabledDir := nginx.GetConfPath("sites-enabled")
|
|
|
- streamAvailableDir := nginx.GetConfPath("streams-available")
|
|
|
- streamEnabledDir := nginx.GetConfPath("streams-enabled")
|
|
|
-
|
|
|
- // Watch the main directories
|
|
|
- err = s.watcher.Add(configDir)
|
|
|
- if err != nil {
|
|
|
- logger.Error("Failed to watch config directory:", err)
|
|
|
+ // Watch all directories recursively
|
|
|
+ if err := s.watchAllDirectories(); err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
|
|
|
- // Watch sites-available and sites-enabled if they exist
|
|
|
- if _, err := os.Stat(availableDir); err == nil {
|
|
|
- err = s.watcher.Add(availableDir)
|
|
|
- if err != nil {
|
|
|
- logger.Error("Failed to watch sites-available directory:", err)
|
|
|
- }
|
|
|
- }
|
|
|
+ // Start background processes
|
|
|
+ go s.watchForChanges()
|
|
|
+ go s.periodicScan()
|
|
|
+ go s.handleShutdown()
|
|
|
|
|
|
- if _, err := os.Stat(enabledDir); err == nil {
|
|
|
- err = s.watcher.Add(enabledDir)
|
|
|
- if err != nil {
|
|
|
- logger.Error("Failed to watch sites-enabled directory:", err)
|
|
|
- }
|
|
|
- }
|
|
|
+ return nil
|
|
|
+}
|
|
|
|
|
|
- // Watch streams-available and streams-enabled if they exist
|
|
|
- if _, err := os.Stat(streamAvailableDir); err == nil {
|
|
|
- err = s.watcher.Add(streamAvailableDir)
|
|
|
- if err != nil {
|
|
|
- logger.Error("Failed to watch streams-available directory:", err)
|
|
|
- }
|
|
|
- }
|
|
|
+// watchAllDirectories recursively adds all directories under nginx config path to watcher
|
|
|
+func (s *Scanner) watchAllDirectories() error {
|
|
|
+ root := nginx.GetConfPath()
|
|
|
+ sslDir := nginx.GetConfPath("ssl")
|
|
|
|
|
|
- if _, err := os.Stat(streamEnabledDir); err == nil {
|
|
|
- err = s.watcher.Add(streamEnabledDir)
|
|
|
+ return filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
|
|
|
if err != nil {
|
|
|
- logger.Error("Failed to watch streams-enabled directory:", err)
|
|
|
+ return err
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- // Start the watcher goroutine
|
|
|
- go s.watchForChanges()
|
|
|
+ if d.IsDir() {
|
|
|
+ // Skip ssl directory
|
|
|
+ if path == sslDir {
|
|
|
+ return filepath.SkipDir
|
|
|
+ }
|
|
|
|
|
|
- // Setup a ticker for periodic scanning (every 5 minutes)
|
|
|
- s.scanTicker = time.NewTicker(5 * time.Minute)
|
|
|
- go func() {
|
|
|
- for {
|
|
|
- select {
|
|
|
- case <-s.ctx.Done():
|
|
|
- return
|
|
|
- case <-s.scanTicker.C:
|
|
|
- err := s.ScanAllConfigs()
|
|
|
- if err != nil {
|
|
|
- logger.Error("Periodic config scan failed:", err)
|
|
|
- }
|
|
|
+ if err := s.watcher.Add(path); err != nil {
|
|
|
+ logger.Error("Failed to watch directory:", path, err)
|
|
|
+ return err
|
|
|
}
|
|
|
+ logger.Debug("Watching directory:", path)
|
|
|
}
|
|
|
- }()
|
|
|
+ return nil
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- // Start a goroutine to listen for context cancellation
|
|
|
- go func() {
|
|
|
- <-s.ctx.Done()
|
|
|
- logger.Debug("Context cancelled, shutting down scanner")
|
|
|
- s.Shutdown()
|
|
|
- }()
|
|
|
+// periodicScan runs periodic scans every 5 minutes
|
|
|
+func (s *Scanner) periodicScan() {
|
|
|
+ s.scanTicker = time.NewTicker(5 * time.Minute)
|
|
|
+ defer s.scanTicker.Stop()
|
|
|
|
|
|
- s.initialized = true
|
|
|
- return nil
|
|
|
+ for {
|
|
|
+ select {
|
|
|
+ case <-s.ctx.Done():
|
|
|
+ return
|
|
|
+ case <-s.scanTicker.C:
|
|
|
+ if err := s.ScanAllConfigs(); err != nil {
|
|
|
+ logger.Error("Periodic scan failed:", err)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// handleShutdown listens for context cancellation and shuts down gracefully
|
|
|
+func (s *Scanner) handleShutdown() {
|
|
|
+ <-s.ctx.Done()
|
|
|
+ logger.Debug("Shutting down scanner")
|
|
|
+ s.Shutdown()
|
|
|
}
|
|
|
|
|
|
-// watchForChanges handles the fsnotify events and triggers rescans when necessary
|
|
|
+// watchForChanges handles file system events
|
|
|
func (s *Scanner) watchForChanges() {
|
|
|
for {
|
|
|
select {
|
|
@@ -187,52 +152,7 @@ func (s *Scanner) watchForChanges() {
|
|
|
if !ok {
|
|
|
return
|
|
|
}
|
|
|
-
|
|
|
- // Skip irrelevant events
|
|
|
- if !event.Has(fsnotify.Create) && !event.Has(fsnotify.Write) &&
|
|
|
- !event.Has(fsnotify.Rename) && !event.Has(fsnotify.Remove) {
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- // Add newly created directories to the watch list
|
|
|
- if event.Has(fsnotify.Create) {
|
|
|
- if fi, err := os.Stat(event.Name); err == nil && fi.IsDir() {
|
|
|
- _ = s.watcher.Add(event.Name)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // For remove events, perform a full scan
|
|
|
- if event.Has(fsnotify.Remove) {
|
|
|
- logger.Debug("Config item removed:", event.Name)
|
|
|
- if err := s.ScanAllConfigs(); err != nil {
|
|
|
- logger.Error("Failed to rescan configs after removal:", err)
|
|
|
- }
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- // Handle non-remove events
|
|
|
- fi, err := os.Stat(event.Name)
|
|
|
- if err != nil {
|
|
|
- logger.Error("Failed to stat changed path:", err)
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- if fi.IsDir() {
|
|
|
- // Directory change, perform full scan
|
|
|
- logger.Debug("Config directory changed:", event.Name)
|
|
|
- if err := s.ScanAllConfigs(); err != nil {
|
|
|
- logger.Error("Failed to rescan configs after directory change:", err)
|
|
|
- }
|
|
|
- } else {
|
|
|
- // File change, scan only the single file
|
|
|
- logger.Debug("Config file changed:", event.Name)
|
|
|
- // Give the system a moment to finish writing the file
|
|
|
- time.Sleep(100 * time.Millisecond)
|
|
|
- if err := s.scanSingleFile(event.Name); err != nil {
|
|
|
- logger.Error("Failed to scan changed file:", err)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
+ s.handleFileEvent(event)
|
|
|
case err, ok := <-s.watcher.Errors:
|
|
|
if !ok {
|
|
|
return
|
|
@@ -242,221 +162,138 @@ func (s *Scanner) watchForChanges() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// scanSingleFile scans a single file and executes all registered callbacks
|
|
|
-func (s *Scanner) scanSingleFile(filePath string) error {
|
|
|
- visited := make(map[string]bool)
|
|
|
- return s.scanSingleFileWithDepth(filePath, visited, 0)
|
|
|
-}
|
|
|
+// handleFileEvent processes individual file system events
|
|
|
+func (s *Scanner) handleFileEvent(event fsnotify.Event) {
|
|
|
+ // Only handle relevant events
|
|
|
+ if !event.Has(fsnotify.Create) && !event.Has(fsnotify.Write) &&
|
|
|
+ !event.Has(fsnotify.Rename) && !event.Has(fsnotify.Remove) {
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
-// scanSingleFileWithDepth scans a single file with recursion protection
|
|
|
-func (s *Scanner) scanSingleFileWithDepth(filePath string, visited map[string]bool, depth int) error {
|
|
|
- // Maximum recursion depth to prevent infinite recursion
|
|
|
- const maxDepth = 5
|
|
|
+ // Skip ssl directory
|
|
|
+ sslDir := nginx.GetConfPath("ssl")
|
|
|
+ if strings.HasPrefix(event.Name, sslDir) {
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
- if depth > maxDepth {
|
|
|
- logger.Warn("Maximum recursion depth reached for file:", filePath)
|
|
|
- return nil
|
|
|
+ // Add new directories to watch
|
|
|
+ if event.Has(fsnotify.Create) {
|
|
|
+ if fi, err := os.Stat(event.Name); err == nil && fi.IsDir() {
|
|
|
+ if err := s.watcher.Add(event.Name); err != nil {
|
|
|
+ logger.Error("Failed to add new directory to watcher:", event.Name, err)
|
|
|
+ } else {
|
|
|
+ logger.Debug("Added new directory to watcher:", event.Name)
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // Resolve the absolute path to handle symlinks properly
|
|
|
- absPath, err := filepath.Abs(filePath)
|
|
|
- if err != nil {
|
|
|
- logger.Error("Failed to resolve absolute path for:", filePath, err)
|
|
|
- return err
|
|
|
+ // Handle file changes
|
|
|
+ if event.Has(fsnotify.Remove) {
|
|
|
+ logger.Debug("Config removed:", event.Name)
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
- // Check for circular includes
|
|
|
- if visited[absPath] {
|
|
|
- // Circular include detected, skip this file
|
|
|
- return nil
|
|
|
+ fi, err := os.Stat(event.Name)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
- // Mark this file as visited
|
|
|
- visited[absPath] = true
|
|
|
-
|
|
|
- // Set scanning state to true only for the root call (depth 0)
|
|
|
- var wasScanning bool
|
|
|
- if depth == 0 {
|
|
|
- s.scanMutex.Lock()
|
|
|
- wasScanning = s.scanning
|
|
|
- s.scanning = true
|
|
|
- if !wasScanning {
|
|
|
- // Only publish if status changed from not scanning to scanning
|
|
|
- s.publishScanningStatus(true)
|
|
|
- }
|
|
|
- s.scanMutex.Unlock()
|
|
|
-
|
|
|
- // Ensure we reset scanning state when done (only for root call)
|
|
|
- defer func() {
|
|
|
- s.scanMutex.Lock()
|
|
|
- s.scanning = false
|
|
|
- // Publish the completion
|
|
|
- s.publishScanningStatus(false)
|
|
|
- s.scanMutex.Unlock()
|
|
|
- }()
|
|
|
+ if fi.IsDir() {
|
|
|
+ logger.Debug("Directory changed:", event.Name)
|
|
|
+ } else {
|
|
|
+ logger.Debug("File changed:", event.Name)
|
|
|
+ time.Sleep(100 * time.Millisecond) // Allow file write to complete
|
|
|
+ s.scanSingleFile(event.Name)
|
|
|
}
|
|
|
+}
|
|
|
+
|
|
|
+// scanSingleFile scans a single config file without recursion
|
|
|
+func (s *Scanner) scanSingleFile(filePath string) error {
|
|
|
+ s.setScanningState(true)
|
|
|
+ defer s.setScanningState(false)
|
|
|
|
|
|
- // Open the file
|
|
|
- file, err := os.Open(absPath)
|
|
|
+ // Read file content
|
|
|
+ content, err := os.ReadFile(filePath)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
- defer file.Close()
|
|
|
|
|
|
- // Read the entire file content
|
|
|
- content, err := os.ReadFile(absPath)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
+ // Execute callbacks
|
|
|
+ s.executeCallbacks(filePath, content)
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// setScanningState updates the scanning state and publishes events
|
|
|
+func (s *Scanner) setScanningState(scanning bool) {
|
|
|
+ s.scanMutex.Lock()
|
|
|
+ defer s.scanMutex.Unlock()
|
|
|
+
|
|
|
+ if s.scanning != scanning {
|
|
|
+ s.scanning = scanning
|
|
|
+ event.Publish(event.Event{
|
|
|
+ Type: event.EventTypeIndexScanning,
|
|
|
+ Data: scanning,
|
|
|
+ })
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- // Execute all registered callbacks
|
|
|
+// executeCallbacks runs all registered callbacks
|
|
|
+func (s *Scanner) executeCallbacks(filePath string, content []byte) {
|
|
|
scanCallbacksMutex.RLock()
|
|
|
+ defer scanCallbacksMutex.RUnlock()
|
|
|
+
|
|
|
for _, callback := range scanCallbacks {
|
|
|
- err := callback(absPath, content)
|
|
|
- if err != nil {
|
|
|
- logger.Error("Callback error for file", absPath, ":", err)
|
|
|
- }
|
|
|
- }
|
|
|
- scanCallbacksMutex.RUnlock()
|
|
|
-
|
|
|
- // Look for include directives to process included files
|
|
|
- includeMatches := includeRegex.FindAllSubmatch(content, -1)
|
|
|
-
|
|
|
- for _, match := range includeMatches {
|
|
|
- if len(match) >= 2 {
|
|
|
- includePath := string(match[1])
|
|
|
- // Handle glob patterns in include directives
|
|
|
- if strings.Contains(includePath, "*") {
|
|
|
- // If it's a relative path, make it absolute based on nginx config dir
|
|
|
- if !filepath.IsAbs(includePath) {
|
|
|
- configDir := filepath.Dir(nginx.GetConfPath())
|
|
|
- includePath = filepath.Join(configDir, includePath)
|
|
|
- }
|
|
|
-
|
|
|
- // Expand the glob pattern
|
|
|
- matchedFiles, err := filepath.Glob(includePath)
|
|
|
- if err != nil {
|
|
|
- logger.Error("Error expanding glob pattern:", includePath, err)
|
|
|
- continue
|
|
|
- }
|
|
|
-
|
|
|
- // Process each matched file
|
|
|
- for _, matchedFile := range matchedFiles {
|
|
|
- fileInfo, err := os.Stat(matchedFile)
|
|
|
- if err == nil && !fileInfo.IsDir() {
|
|
|
- err = s.scanSingleFileWithDepth(matchedFile, visited, depth+1)
|
|
|
- if err != nil {
|
|
|
- logger.Error("Failed to scan included file:", matchedFile, err)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- } else {
|
|
|
- // Handle single file include
|
|
|
- // If it's a relative path, make it absolute based on nginx config dir
|
|
|
- if !filepath.IsAbs(includePath) {
|
|
|
- configDir := filepath.Dir(nginx.GetConfPath())
|
|
|
- includePath = filepath.Join(configDir, includePath)
|
|
|
- }
|
|
|
-
|
|
|
- fileInfo, err := os.Stat(includePath)
|
|
|
- if err == nil && !fileInfo.IsDir() {
|
|
|
- err = s.scanSingleFileWithDepth(includePath, visited, depth+1)
|
|
|
- if err != nil {
|
|
|
- logger.Error("Failed to scan included file:", includePath, err)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ if err := callback(filePath, content); err != nil {
|
|
|
+ logger.Error("Callback error for", filePath, ":", err)
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- return nil
|
|
|
}
|
|
|
|
|
|
-// ScanAllConfigs scans all nginx config files and executes all registered callbacks
|
|
|
+// ScanAllConfigs scans all nginx configuration files
|
|
|
func (s *Scanner) ScanAllConfigs() error {
|
|
|
- // Set scanning state to true
|
|
|
- s.scanMutex.Lock()
|
|
|
- wasScanning := s.scanning
|
|
|
- s.scanning = true
|
|
|
- if !wasScanning {
|
|
|
- // Only publish if status changed from not scanning to scanning
|
|
|
- s.publishScanningStatus(true)
|
|
|
- }
|
|
|
- s.scanMutex.Unlock()
|
|
|
-
|
|
|
- // Ensure we reset scanning state when done
|
|
|
- defer func() {
|
|
|
- s.scanMutex.Lock()
|
|
|
- s.scanning = false
|
|
|
- // Publish the completion
|
|
|
- s.publishScanningStatus(false)
|
|
|
- s.scanMutex.Unlock()
|
|
|
- }()
|
|
|
-
|
|
|
- // Get the main config file
|
|
|
- mainConfigPath := nginx.GetConfEntryPath()
|
|
|
- err := s.scanSingleFile(mainConfigPath)
|
|
|
- if err != nil {
|
|
|
- logger.Error("Failed to scan main config:", err)
|
|
|
- }
|
|
|
+ s.setScanningState(true)
|
|
|
+ defer s.setScanningState(false)
|
|
|
|
|
|
- // Scan sites-available directory
|
|
|
- sitesAvailablePath := nginx.GetConfPath("sites-available", "")
|
|
|
- sitesAvailableFiles, err := os.ReadDir(sitesAvailablePath)
|
|
|
- if err == nil {
|
|
|
- for _, file := range sitesAvailableFiles {
|
|
|
- if !file.IsDir() {
|
|
|
- configPath := filepath.Join(sitesAvailablePath, file.Name())
|
|
|
- err := s.scanSingleFile(configPath)
|
|
|
- if err != nil {
|
|
|
- logger.Error("Failed to scan config:", configPath, err)
|
|
|
- }
|
|
|
- }
|
|
|
+ root := nginx.GetConfPath()
|
|
|
+ sslDir := nginx.GetConfPath("ssl")
|
|
|
+
|
|
|
+ // Scan all files in the config directory and subdirectories
|
|
|
+ return filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- // Scan streams-available directory if it exists
|
|
|
- streamAvailablePath := nginx.GetConfPath("streams-available", "")
|
|
|
- streamAvailableFiles, err := os.ReadDir(streamAvailablePath)
|
|
|
- if err == nil {
|
|
|
- for _, file := range streamAvailableFiles {
|
|
|
- if !file.IsDir() {
|
|
|
- configPath := filepath.Join(streamAvailablePath, file.Name())
|
|
|
- err := s.scanSingleFile(configPath)
|
|
|
- if err != nil {
|
|
|
- logger.Error("Failed to scan stream config:", configPath, err)
|
|
|
- }
|
|
|
+ // Skip ssl directory
|
|
|
+ if d.IsDir() && path == sslDir {
|
|
|
+ return filepath.SkipDir
|
|
|
+ }
|
|
|
+
|
|
|
+ // Only process regular files
|
|
|
+ if !d.IsDir() {
|
|
|
+ if err := s.scanSingleFile(path); err != nil {
|
|
|
+ logger.Error("Failed to scan config:", path, err)
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- return nil
|
|
|
+ return nil
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
-// Shutdown cleans up resources used by the scanner
|
|
|
+// Shutdown cleans up scanner resources
|
|
|
func (s *Scanner) Shutdown() {
|
|
|
if s.watcher != nil {
|
|
|
s.watcher.Close()
|
|
|
}
|
|
|
-
|
|
|
if s.scanTicker != nil {
|
|
|
s.scanTicker.Stop()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// IsScanningInProgress returns whether a scan is currently in progress
|
|
|
+// IsScanningInProgress returns whether a scan is currently running
|
|
|
func IsScanningInProgress() bool {
|
|
|
s := GetScanner()
|
|
|
s.scanMutex.RLock()
|
|
|
defer s.scanMutex.RUnlock()
|
|
|
return s.scanning
|
|
|
}
|
|
|
-
|
|
|
-// WithContext sets a context for the scanner that will be used to control its lifecycle
|
|
|
-func (s *Scanner) WithContext(ctx context.Context) *Scanner {
|
|
|
- // Create a context with cancel if not already done in Initialize
|
|
|
- if s.ctx == nil {
|
|
|
- s.ctx = ctx
|
|
|
- }
|
|
|
- return s
|
|
|
-}
|