Skip to content

Commit e7e15bb

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

File tree

10 files changed

+699
-46
lines changed

10 files changed

+699
-46
lines changed

api/upstream/upstream.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ func GetAvailability(c *gin.Context) {
2727
c.JSON(http.StatusOK, result)
2828
}
2929

30+
// GetUpstreamDefinitions returns all upstream definitions for debugging
31+
func GetUpstreamDefinitions(c *gin.Context) {
32+
service := upstream.GetUpstreamService()
33+
34+
result := gin.H{
35+
"upstreams": service.GetAllUpstreamDefinitions(),
36+
"last_update_time": service.GetLastUpdateTime(),
37+
}
38+
39+
c.JSON(http.StatusOK, result)
40+
}
41+
3042
// AvailabilityWebSocket handles WebSocket connections for real-time availability monitoring
3143
func AvailabilityWebSocket(c *gin.Context) {
3244
var upGrader = websocket.Upgrader{

internal/site/list.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66

77
"github.com/0xJacky/Nginx-UI/internal/config"
8+
"github.com/0xJacky/Nginx-UI/internal/upstream"
89
"github.com/0xJacky/Nginx-UI/model"
910
)
1011

@@ -47,13 +48,28 @@ func GetSiteConfigs(ctx context.Context, options *ListOptions, sites []*model.Si
4748
func buildConfig(fileName string, fileInfo os.FileInfo, status config.ConfigStatus, envGroupID uint64, envGroup *model.EnvGroup) config.Config {
4849
indexedSite := GetIndexedSite(fileName)
4950

50-
// Convert proxy targets
51-
proxyTargets := make([]config.ProxyTarget, len(indexedSite.ProxyTargets))
52-
for i, target := range indexedSite.ProxyTargets {
53-
proxyTargets[i] = config.ProxyTarget{
54-
Host: target.Host,
55-
Port: target.Port,
56-
Type: target.Type,
51+
// Convert proxy targets, expanding upstream references
52+
var proxyTargets []config.ProxyTarget
53+
upstreamService := upstream.GetUpstreamService()
54+
55+
for _, target := range indexedSite.ProxyTargets {
56+
// Check if target.Host is an upstream name
57+
if upstreamDef, exists := upstreamService.GetUpstreamDefinition(target.Host); exists {
58+
// Replace with upstream servers
59+
for _, server := range upstreamDef.Servers {
60+
proxyTargets = append(proxyTargets, config.ProxyTarget{
61+
Host: server.Host,
62+
Port: server.Port,
63+
Type: server.Type,
64+
})
65+
}
66+
} else {
67+
// Regular proxy target
68+
proxyTargets = append(proxyTargets, config.ProxyTarget{
69+
Host: target.Host,
70+
Port: target.Port,
71+
Type: target.Type,
72+
})
5773
}
5874
}
5975

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package site
2+
3+
import (
4+
"os"
5+
"testing"
6+
"time"
7+
8+
"github.com/0xJacky/Nginx-UI/internal/config"
9+
"github.com/0xJacky/Nginx-UI/internal/upstream"
10+
)
11+
12+
func TestBuildConfig_UpstreamExpansion(t *testing.T) {
13+
// Setup upstream service with test data
14+
service := upstream.GetUpstreamService()
15+
service.ClearTargets() // Clear any existing data
16+
17+
// Add test upstream definitions
18+
webBackendServers := []upstream.ProxyTarget{
19+
{Host: "192.168.1.100", Port: "8080", Type: "upstream"},
20+
{Host: "192.168.1.101", Port: "8080", Type: "upstream"},
21+
{Host: "::1", Port: "8080", Type: "upstream"},
22+
}
23+
service.UpdateUpstreamDefinition("web_backend", webBackendServers, "test.conf")
24+
25+
apiBackendServers := []upstream.ProxyTarget{
26+
{Host: "api1.example.com", Port: "3000", Type: "upstream"},
27+
{Host: "api2.example.com", Port: "3000", Type: "upstream"},
28+
}
29+
service.UpdateUpstreamDefinition("api_backend", apiBackendServers, "test.conf")
30+
31+
// Create a mock indexed site with proxy targets that reference upstreams
32+
IndexedSites["test_site"] = &SiteIndex{
33+
Path: "test_site",
34+
Content: "test content",
35+
Urls: []string{"example.com"},
36+
ProxyTargets: []ProxyTarget{
37+
{Host: "web_backend", Port: "80", Type: "proxy_pass"}, // This should be expanded
38+
{Host: "api_backend", Port: "80", Type: "proxy_pass"}, // This should be expanded
39+
{Host: "direct.example.com", Port: "8080", Type: "proxy_pass"}, // This should remain as-is
40+
},
41+
}
42+
43+
// Create mock file info
44+
fileInfo := &mockFileInfo{
45+
name: "test_site",
46+
size: 1024,
47+
modTime: time.Now(),
48+
isDir: false,
49+
}
50+
51+
// Call buildConfig
52+
result := buildConfig("test_site", fileInfo, config.StatusEnabled, 0, nil)
53+
54+
// Verify the results
55+
expectedTargetCount := 6 // 3 from web_backend + 2 from api_backend + 1 direct
56+
if len(result.ProxyTargets) != expectedTargetCount {
57+
t.Errorf("Expected %d proxy targets, got %d", expectedTargetCount, len(result.ProxyTargets))
58+
for i, target := range result.ProxyTargets {
59+
t.Logf("Target %d: Host=%s, Port=%s, Type=%s", i, target.Host, target.Port, target.Type)
60+
}
61+
}
62+
63+
// Check for specific targets
64+
expectedHosts := map[string]bool{
65+
"192.168.1.100": false,
66+
"192.168.1.101": false,
67+
"::1": false,
68+
"api1.example.com": false,
69+
"api2.example.com": false,
70+
"direct.example.com": false,
71+
}
72+
73+
for _, target := range result.ProxyTargets {
74+
if _, exists := expectedHosts[target.Host]; exists {
75+
expectedHosts[target.Host] = true
76+
}
77+
}
78+
79+
// Verify all expected hosts were found
80+
for host, found := range expectedHosts {
81+
if !found {
82+
t.Errorf("Expected to find host %s in proxy targets", host)
83+
}
84+
}
85+
86+
// Verify that upstream names are not present in the final targets
87+
for _, target := range result.ProxyTargets {
88+
if target.Host == "web_backend" || target.Host == "api_backend" {
89+
t.Errorf("Upstream name %s should have been expanded, not included directly", target.Host)
90+
}
91+
}
92+
93+
// Clean up
94+
delete(IndexedSites, "test_site")
95+
}
96+
97+
func TestBuildConfig_NoUpstreamExpansion(t *testing.T) {
98+
// Test case where proxy targets don't reference any upstreams
99+
IndexedSites["test_site_no_upstream"] = &SiteIndex{
100+
Path: "test_site_no_upstream",
101+
Content: "test content",
102+
Urls: []string{"example.com"},
103+
ProxyTargets: []ProxyTarget{
104+
{Host: "direct1.example.com", Port: "8080", Type: "proxy_pass"},
105+
{Host: "direct2.example.com", Port: "9000", Type: "proxy_pass"},
106+
{Host: "::1", Port: "3000", Type: "proxy_pass"},
107+
},
108+
}
109+
110+
fileInfo := &mockFileInfo{
111+
name: "test_site_no_upstream",
112+
size: 1024,
113+
modTime: time.Now(),
114+
isDir: false,
115+
}
116+
117+
result := buildConfig("test_site_no_upstream", fileInfo, config.StatusEnabled, 0, nil)
118+
119+
// Should have exactly 3 targets, unchanged
120+
if len(result.ProxyTargets) != 3 {
121+
t.Errorf("Expected 3 proxy targets, got %d", len(result.ProxyTargets))
122+
}
123+
124+
expectedTargets := []config.ProxyTarget{
125+
{Host: "direct1.example.com", Port: "8080", Type: "proxy_pass"},
126+
{Host: "direct2.example.com", Port: "9000", Type: "proxy_pass"},
127+
{Host: "::1", Port: "3000", Type: "proxy_pass"},
128+
}
129+
130+
for i, expected := range expectedTargets {
131+
if i >= len(result.ProxyTargets) {
132+
t.Errorf("Missing target %d", i)
133+
continue
134+
}
135+
actual := result.ProxyTargets[i]
136+
if actual.Host != expected.Host || actual.Port != expected.Port || actual.Type != expected.Type {
137+
t.Errorf("Target %d mismatch: expected %+v, got %+v", i, expected, actual)
138+
}
139+
}
140+
141+
// Clean up
142+
delete(IndexedSites, "test_site_no_upstream")
143+
}
144+
145+
// mockFileInfo implements os.FileInfo for testing
146+
type mockFileInfo struct {
147+
name string
148+
size int64
149+
modTime time.Time
150+
isDir bool
151+
}
152+
153+
func (m *mockFileInfo) Name() string { return m.name }
154+
func (m *mockFileInfo) Size() int64 { return m.size }
155+
func (m *mockFileInfo) Mode() os.FileMode { return 0644 }
156+
func (m *mockFileInfo) ModTime() time.Time { return m.modTime }
157+
func (m *mockFileInfo) IsDir() bool { return m.isDir }
158+
func (m *mockFileInfo) Sys() interface{} { return nil }

internal/stream/index_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ func TestScanForStream(t *testing.T) {
3939
server {
4040
listen 1234-1236;
4141
resolver 8.8.8.8 valid=1s;
42-
proxy_pass example.com:$server_port;
42+
proxy_pass example.com:8080;
4343
}`
4444

4545
// Test with a valid stream config path

internal/stream/list.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"os"
66

77
"github.com/0xJacky/Nginx-UI/internal/config"
8+
"github.com/0xJacky/Nginx-UI/internal/upstream"
89
"github.com/0xJacky/Nginx-UI/model"
910
)
1011

@@ -47,13 +48,28 @@ func GetStreamConfigs(ctx context.Context, options *ListOptions, streams []*mode
4748
func buildConfig(fileName string, fileInfo os.FileInfo, status config.ConfigStatus, envGroupID uint64, envGroup *model.EnvGroup) config.Config {
4849
indexedStream := GetIndexedStream(fileName)
4950

50-
// Convert proxy targets
51-
proxyTargets := make([]config.ProxyTarget, len(indexedStream.ProxyTargets))
52-
for i, target := range indexedStream.ProxyTargets {
53-
proxyTargets[i] = config.ProxyTarget{
54-
Host: target.Host,
55-
Port: target.Port,
56-
Type: target.Type,
51+
// Convert proxy targets, expanding upstream references
52+
var proxyTargets []config.ProxyTarget
53+
upstreamService := upstream.GetUpstreamService()
54+
55+
for _, target := range indexedStream.ProxyTargets {
56+
// Check if target.Host is an upstream name
57+
if upstreamDef, exists := upstreamService.GetUpstreamDefinition(target.Host); exists {
58+
// Replace with upstream servers
59+
for _, server := range upstreamDef.Servers {
60+
proxyTargets = append(proxyTargets, config.ProxyTarget{
61+
Host: server.Host,
62+
Port: server.Port,
63+
Type: server.Type,
64+
})
65+
}
66+
} else {
67+
// Regular proxy target
68+
proxyTargets = append(proxyTargets, config.ProxyTarget{
69+
Host: target.Host,
70+
Port: target.Port,
71+
Type: target.Type,
72+
})
5773
}
5874
}
5975

0 commit comments

Comments
 (0)