Skip to content

Commit 3c19c8a

Browse files
committed
enhance(upstream): proxy target parsing from multiple configs
1 parent 44246c9 commit 3c19c8a

File tree

8 files changed

+530
-56
lines changed

8 files changed

+530
-56
lines changed

api/analytic/nodes.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,6 @@ func GetNodeStat(c *gin.Context) {
6565
NodeInfo: nodeInfo,
6666
NodeStat: stat,
6767
}
68-
69-
logger.Debugf("Sending complete node info including version: %s", ver.Version)
7068
}
7169
} else {
7270
// Send only stat information for performance

internal/cache/index.go

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ import (
1818
// ScanCallback is called during config scanning with file path and content
1919
type ScanCallback func(configPath string, content []byte) error
2020

21+
// PostScanCallback is called after all files have been scanned (for second pass processing)
22+
type PostScanCallback func() error
23+
2124
// Scanner watches and scans nginx config files
2225
type Scanner struct {
2326
ctx context.Context
@@ -28,10 +31,12 @@ type Scanner struct {
2831
}
2932

3033
var (
31-
scanner *Scanner
32-
scannerInitMutex sync.Mutex
33-
scanCallbacks = make([]ScanCallback, 0)
34-
scanCallbacksMutex sync.RWMutex
34+
scanner *Scanner
35+
scannerInitMutex sync.Mutex
36+
scanCallbacks = make([]ScanCallback, 0)
37+
scanCallbacksMutex sync.RWMutex
38+
postScanCallbacks = make([]PostScanCallback, 0)
39+
postScanCallbacksMutex sync.RWMutex
3540
)
3641

3742
// InitScanner initializes the config scanner
@@ -90,6 +95,13 @@ func RegisterCallback(callback ScanCallback) {
9095
scanCallbacks = append(scanCallbacks, callback)
9196
}
9297

98+
// RegisterPostScanCallback adds a callback to be executed after all files are scanned
99+
func RegisterPostScanCallback(callback PostScanCallback) {
100+
postScanCallbacksMutex.Lock()
101+
defer postScanCallbacksMutex.Unlock()
102+
postScanCallbacks = append(postScanCallbacks, callback)
103+
}
104+
93105
// Initialize sets up the scanner and starts watching
94106
func (s *Scanner) Initialize(ctx context.Context) error {
95107
watcher, err := fsnotify.NewWatcher()
@@ -308,15 +320,16 @@ func (s *Scanner) executeCallbacks(filePath string, content []byte) {
308320
}
309321
}
310322

311-
// ScanAllConfigs scans all nginx configuration files
323+
// ScanAllConfigs scans all nginx configuration files with two-pass approach
312324
func (s *Scanner) ScanAllConfigs() error {
313325
s.setScanningState(true)
314326
defer s.setScanningState(false)
315327

316328
root := nginx.GetConfPath()
317329

318-
// Scan all files in the config directory and subdirectories
319-
return filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
330+
// First pass: scan all files to collect upstream definitions and other metadata
331+
logger.Debug("ScanAllConfigs: Starting first pass (upstream definitions)")
332+
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
320333
if err != nil {
321334
return err
322335
}
@@ -329,12 +342,38 @@ func (s *Scanner) ScanAllConfigs() error {
329342
// Only process regular files
330343
if !d.IsDir() {
331344
if err := s.scanSingleFile(path); err != nil {
332-
logger.Error("Failed to scan config:", path, err)
345+
logger.Error("Failed to scan config in first pass:", path, err)
333346
}
334347
}
335348

336349
return nil
337350
})
351+
352+
if err != nil {
353+
return err
354+
}
355+
356+
// Second pass: parse proxy targets now that all upstreams are collected
357+
logger.Debug("ScanAllConfigs: Starting second pass (proxy targets)")
358+
return s.triggerProxyTargetsParsing()
359+
}
360+
361+
// triggerProxyTargetsParsing triggers the second pass for proxy target parsing
362+
func (s *Scanner) triggerProxyTargetsParsing() error {
363+
// Execute all post-scan callbacks
364+
postScanCallbacksMutex.RLock()
365+
callbacks := make([]PostScanCallback, len(postScanCallbacks))
366+
copy(callbacks, postScanCallbacks)
367+
postScanCallbacksMutex.RUnlock()
368+
369+
for _, callback := range callbacks {
370+
if err := callback(); err != nil {
371+
logger.Error("Post-scan callback failed:", err)
372+
return err
373+
}
374+
}
375+
376+
return nil
338377
}
339378

340379
// Shutdown cleans up scanner resources

internal/cron/upstream_availability.go

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package cron
33
import (
44
"time"
55

6-
apiUpstream "github.com/0xJacky/Nginx-UI/api/upstream"
76
"github.com/0xJacky/Nginx-UI/internal/upstream"
87
"github.com/go-co-op/gocron/v2"
98
"github.com/uozi-tech/cosy/logger"
@@ -41,13 +40,6 @@ func executeUpstreamAvailabilityTest() {
4140
return
4241
}
4342

44-
// Check if we should skip this test due to active WebSocket connections
45-
// (WebSocket connections trigger more frequent checks)
46-
if hasActiveWebSocketConnections() {
47-
logger.Debug("Skipping scheduled test due to active WebSocket connections")
48-
return
49-
}
50-
5143
start := time.Now()
5244
logger.Debug("Starting scheduled upstream availability test for", targetCount, "targets")
5345

@@ -57,11 +49,6 @@ func executeUpstreamAvailabilityTest() {
5749
logger.Debug("Upstream availability test completed in", duration)
5850
}
5951

60-
// hasActiveWebSocketConnections checks if there are active WebSocket connections
61-
func hasActiveWebSocketConnections() bool {
62-
return apiUpstream.HasActiveWebSocketConnections()
63-
}
64-
6552
// RestartUpstreamAvailabilityJob restarts the upstream availability job
6653
func RestartUpstreamAvailabilityJob() error {
6754
logger.Info("Restarting upstream availability job...")

internal/site/index.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ func GetIndexedSite(path string) *SiteIndex {
3131

3232
func init() {
3333
cache.RegisterCallback(scanForSite)
34+
cache.RegisterPostScanCallback(postScanSites)
3435
}
3536

3637
func scanForSite(configPath string, content []byte) error {
@@ -220,8 +221,8 @@ func scanForSite(configPath string, content []byte) error {
220221
siteIndex.Urls = append(siteIndex.Urls, url)
221222
}
222223

223-
// Parse proxy targets from the configuration content
224-
siteIndex.ProxyTargets = upstream.ParseProxyTargetsFromRawContent(string(content))
224+
// Proxy targets will be parsed in the second pass
225+
siteIndex.ProxyTargets = []ProxyTarget{}
225226

226227
// Only store if we found valid URLs or proxy targets
227228
if len(siteIndex.Urls) > 0 || len(siteIndex.ProxyTargets) > 0 {
@@ -231,6 +232,28 @@ func scanForSite(configPath string, content []byte) error {
231232
return nil
232233
}
233234

235+
// postScanSites parses proxy targets for all indexed sites (second pass)
236+
func postScanSites() error {
237+
for fileName, siteIndex := range IndexedSites {
238+
if siteIndex.Content != "" {
239+
proxyTargets := upstream.ParseProxyTargetsFromRawContent(siteIndex.Content)
240+
241+
siteIndex.ProxyTargets = make([]ProxyTarget, len(proxyTargets))
242+
for i, target := range proxyTargets {
243+
siteIndex.ProxyTargets[i] = ProxyTarget{
244+
Host: target.Host,
245+
Port: target.Port,
246+
Type: target.Type,
247+
}
248+
}
249+
250+
IndexedSites[fileName] = siteIndex
251+
}
252+
}
253+
254+
return nil
255+
}
256+
234257
// isValidDomain performs a basic validation of domain names
235258
func isValidDomain(domain string) bool {
236259
// Basic validation: contains at least one dot and no spaces

internal/stream/index.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ func GetIndexedStream(path string) *StreamIndex {
2727

2828
func init() {
2929
cache.RegisterCallback(scanForStream)
30+
cache.RegisterPostScanCallback(postScanStreams)
3031
}
3132

3233
func scanForStream(configPath string, content []byte) error {
@@ -41,8 +42,8 @@ func scanForStream(configPath string, content []byte) error {
4142
ProxyTargets: []upstream.ProxyTarget{},
4243
}
4344

44-
// Parse proxy targets from the configuration content
45-
streamIndex.ProxyTargets = upstream.ParseProxyTargetsFromRawContent(string(content))
45+
// Proxy targets will be parsed in the second pass
46+
streamIndex.ProxyTargets = []upstream.ProxyTarget{}
4647
// Only store if we found proxy targets
4748
if len(streamIndex.ProxyTargets) > 0 {
4849
IndexedStreams[filepath.Base(configPath)] = &streamIndex
@@ -51,6 +52,18 @@ func scanForStream(configPath string, content []byte) error {
5152
return nil
5253
}
5354

55+
// postScanStreams parses proxy targets for all indexed streams (second pass)
56+
func postScanStreams() error {
57+
for fileName, streamIndex := range IndexedStreams {
58+
if streamIndex.Content != "" {
59+
streamIndex.ProxyTargets = upstream.ParseProxyTargetsFromRawContent(streamIndex.Content)
60+
IndexedStreams[fileName] = streamIndex
61+
}
62+
}
63+
64+
return nil
65+
}
66+
5467
// isStreamConfig checks if the config path is a stream configuration
5568
func isStreamConfig(configPath string) bool {
5669
return strings.Contains(configPath, "streams-available") ||

0 commit comments

Comments
 (0)