feat(converter): 支持 server 级别的 root 和 index 指令转换
- 在 convertServerBlock 中收集 server 级别的 root/index 指令 - 如果没有显式的 location / 静态配置,创建默认静态配置 - 如果 location / 是 proxy 类型,不创建静态配置 - 添加 3 个测试用例覆盖不同场景 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
b290cea0f6
commit
78ca32748c
@ -200,6 +200,8 @@ func convertUpstreamServer(d *Directive) config.ProxyTarget {
|
||||
func convertServerBlock(d *Directive, upstreams map[string]*upstreamInfo, result *ConvertResult) config.ServerConfig {
|
||||
server := config.ServerConfig{}
|
||||
var sslDetected bool
|
||||
var serverRoot string
|
||||
var serverIndex []string
|
||||
|
||||
for i := range d.Block {
|
||||
bd := &d.Block[i]
|
||||
@ -211,6 +213,12 @@ func convertServerBlock(d *Directive, upstreams map[string]*upstreamInfo, result
|
||||
}
|
||||
case "server_name":
|
||||
parseServerName(bd, &server)
|
||||
case "root":
|
||||
if len(bd.Args) > 0 {
|
||||
serverRoot = bd.Args[0]
|
||||
}
|
||||
case "index":
|
||||
serverIndex = append(serverIndex, bd.Args...)
|
||||
case "ssl_certificate":
|
||||
if len(bd.Args) > 0 {
|
||||
server.SSL.Cert = bd.Args[0]
|
||||
@ -273,6 +281,36 @@ func convertServerBlock(d *Directive, upstreams map[string]*upstreamInfo, result
|
||||
}
|
||||
}
|
||||
|
||||
// If server-level root is defined but no explicit location / static config exists,
|
||||
// create a default static configuration for "/".
|
||||
// However, if location / is a proxy, don't create static config.
|
||||
if serverRoot != "" {
|
||||
hasRootLocation := false
|
||||
// Check if there's already a static config for "/"
|
||||
for _, s := range server.Static {
|
||||
if s.Path == "/" {
|
||||
hasRootLocation = true
|
||||
break
|
||||
}
|
||||
}
|
||||
// Check if location / is a proxy
|
||||
if !hasRootLocation {
|
||||
for _, p := range server.Proxy {
|
||||
if p.Path == "/" {
|
||||
hasRootLocation = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !hasRootLocation {
|
||||
server.Static = append(server.Static, config.StaticConfig{
|
||||
Path: "/",
|
||||
Root: serverRoot,
|
||||
Index: serverIndex,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Warn if SSL was detected (listen ... ssl) but cert/key are not configured.
|
||||
if sslDetected && (server.SSL.Cert == "" || server.SSL.Key == "") {
|
||||
result.Warnings = append(result.Warnings, Warning{
|
||||
|
||||
@ -5,6 +5,8 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"rua.plus/lolly/internal/config"
|
||||
)
|
||||
|
||||
// helper: parse nginx config string and convert.
|
||||
@ -127,6 +129,112 @@ http {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertServerLevelRoot(t *testing.T) {
|
||||
input := `
|
||||
http {
|
||||
server {
|
||||
listen 80;
|
||||
root /var/www/html;
|
||||
index index.html index.htm index.php;
|
||||
}
|
||||
}
|
||||
`
|
||||
result, err := convertString(t, input)
|
||||
if err != nil {
|
||||
t.Fatalf("convert error: %v", err)
|
||||
}
|
||||
|
||||
s := result.Config.Servers[0]
|
||||
if len(s.Static) != 1 {
|
||||
t.Fatalf("expected 1 static, got %d", len(s.Static))
|
||||
}
|
||||
|
||||
st := s.Static[0]
|
||||
if st.Path != "/" {
|
||||
t.Errorf("expected path /, got %s", st.Path)
|
||||
}
|
||||
if st.Root != "/var/www/html" {
|
||||
t.Errorf("expected root /var/www/html, got %s", st.Root)
|
||||
}
|
||||
if len(st.Index) != 3 || st.Index[0] != "index.html" || st.Index[1] != "index.htm" || st.Index[2] != "index.php" {
|
||||
t.Errorf("expected [index.html index.htm index.php], got %v", st.Index)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertServerLevelRootWithLocation(t *testing.T) {
|
||||
// When both server-level root and location /static exist, both should be converted
|
||||
input := `
|
||||
http {
|
||||
server {
|
||||
listen 80;
|
||||
root /var/www/html;
|
||||
index index.html;
|
||||
location /static/ {
|
||||
root /var/www/static;
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
result, err := convertString(t, input)
|
||||
if err != nil {
|
||||
t.Fatalf("convert error: %v", err)
|
||||
}
|
||||
|
||||
s := result.Config.Servers[0]
|
||||
if len(s.Static) != 2 {
|
||||
t.Fatalf("expected 2 static, got %d", len(s.Static))
|
||||
}
|
||||
|
||||
// Find the root location
|
||||
var rootStatic, staticLoc *config.StaticConfig
|
||||
for i := range s.Static {
|
||||
if s.Static[i].Path == "/" {
|
||||
rootStatic = &s.Static[i]
|
||||
} else if s.Static[i].Path == "/static/" {
|
||||
staticLoc = &s.Static[i]
|
||||
}
|
||||
}
|
||||
|
||||
if rootStatic == nil {
|
||||
t.Error("expected root static config at path /")
|
||||
} else if rootStatic.Root != "/var/www/html" {
|
||||
t.Errorf("expected root /var/www/html, got %s", rootStatic.Root)
|
||||
}
|
||||
|
||||
if staticLoc == nil {
|
||||
t.Error("expected static config at path /static/")
|
||||
} else if staticLoc.Root != "/var/www/static" {
|
||||
t.Errorf("expected root /var/www/static, got %s", staticLoc.Root)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertServerLevelRootNotCreatedForProxy(t *testing.T) {
|
||||
// When location / is a proxy, server-level root should NOT create a static config
|
||||
input := `
|
||||
http {
|
||||
server {
|
||||
listen 80;
|
||||
root /var/www/html;
|
||||
location / {
|
||||
proxy_pass http://backend;
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
result, err := convertString(t, input)
|
||||
if err != nil {
|
||||
t.Fatalf("convert error: %v", err)
|
||||
}
|
||||
|
||||
s := result.Config.Servers[0]
|
||||
if len(s.Static) != 0 {
|
||||
t.Errorf("expected 0 static (location / is proxy), got %d", len(s.Static))
|
||||
}
|
||||
if len(s.Proxy) != 1 {
|
||||
t.Errorf("expected 1 proxy, got %d", len(s.Proxy))
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertUpstream(t *testing.T) {
|
||||
input := `
|
||||
upstream backend {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user