mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-06 07:36:37 +08:00
修复 Pages 站点在未启用 SPA Fallback 时访问根路径 / 返回 404:OpenResty 渲染增加 location = / 精确匹配,通过 try_files 提供入口文件(index 指令在 try_files ... =404 场景下不会作用于根路径)。
This commit is contained in:
@@ -22,6 +22,8 @@ sidebar: false
|
|||||||
|
|
||||||
### 修复
|
### 修复
|
||||||
|
|
||||||
|
- 修复 Pages 站点根路径 `/` 访问异常:OpenResty 渲染增加 `location = /` 精确匹配;未启用 SPA Fallback 时直接提供入口文件(`index` 指令在 `try_files ... =404` 场景下不生效);启用 SPA Fallback 时避免 `try_files $uri $uri/ /index.html` 因 `$uri/` 命中站点根目录触发内部重定向循环而返回 500。
|
||||||
|
|
||||||
- 修复代理路由详情认证配置 Tab:移除 PoW 配置(PoW 仅在 WAF 规则组中设置);保留 Basic Auth 保存能力;移除页头重复的「保存当前分区」按钮。
|
- 修复代理路由详情认证配置 Tab:移除 PoW 配置(PoW 仅在 WAF 规则组中设置);保留 Basic Auth 保存能力;移除页头重复的「保存当前分区」按钮。
|
||||||
|
|
||||||
- 修复 Pages 路由发布失败并报 `pages module is not available`:配置快照发布流程补齐 Pages 项目激活部署解析与 `pages_deployment` 写入。
|
- 修复 Pages 路由发布失败并报 `pages module is not available`:配置快照发布流程补齐 Pages 项目激活部署解析与 `pages_deployment` 写入。
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ export function ProxyRouteDetailPageClient() {
|
|||||||
const [route, setRoute] = useState<ProxyRouteItem | null>(null);
|
const [route, setRoute] = useState<ProxyRouteItem | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [loadError, setLoadError] = useState<string | null>(null);
|
const [loadError, setLoadError] = useState<string | null>(null);
|
||||||
const [sectionSaving, setSectionSaving] = useState(false);
|
|
||||||
|
|
||||||
const handleSectionChange = useCallback(
|
const handleSectionChange = useCallback(
|
||||||
(section: ProxyRouteConfigSection) => {
|
(section: ProxyRouteConfigSection) => {
|
||||||
@@ -113,7 +112,6 @@ export function ProxyRouteDetailPageClient() {
|
|||||||
const sectionProps = {
|
const sectionProps = {
|
||||||
route,
|
route,
|
||||||
onRouteUpdate: handleRouteUpdate,
|
onRouteUpdate: handleRouteUpdate,
|
||||||
onSavingChange: setSectionSaving,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -150,7 +148,7 @@ export function ProxyRouteDetailPageClient() {
|
|||||||
<CacheSection {...sectionProps} />
|
<CacheSection {...sectionProps} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="waf" className="focus-visible:outline-none">
|
<TabsContent value="waf" className="focus-visible:outline-none">
|
||||||
<WafSection route={route} onSavingChange={setSectionSaving} />
|
<WafSection route={route} />
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
<TabsContent value="auth" className="focus-visible:outline-none">
|
<TabsContent value="auth" className="focus-visible:outline-none">
|
||||||
<AuthSection {...sectionProps} />
|
<AuthSection {...sectionProps} />
|
||||||
|
|||||||
@@ -360,7 +360,7 @@ func renderPagesAPIProxyLocationBlock(deployment *PagesDeployment) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func renderHTTPPagesServer(serverNames string, siteName string, deployment *PagesDeployment, limitConfig routeLimitConfig, powEnabled bool, basicAuthEnabled bool, basicAuthUsername string, basicAuthPassword string) string {
|
func renderHTTPPagesServer(serverNames string, siteName string, deployment *PagesDeployment, limitConfig routeLimitConfig, powEnabled bool, basicAuthEnabled bool, basicAuthUsername string, basicAuthPassword string) string {
|
||||||
return fmt.Sprintf("server {\n listen 80;\n server_name %s;\n%s%s root %s;\n index %s;%s\n\n location / {\n%s%s }\n%s}\n\n", serverNames, renderAccessBlock(siteName, powEnabled), renderPowLocationBlocks(powEnabled), quoteNginxStringLiteral(pagesDeploymentRoot(deployment)), quoteNginxStringLiteral(pagesEntryFile(deployment)), renderPagesAPIProxyLocationBlock(deployment), renderBasicAuthBlock(basicAuthEnabled, basicAuthUsername, basicAuthPassword), renderPagesLocationBlock(deployment, limitConfig), renderPowStaticLocationBlock(powEnabled))
|
return fmt.Sprintf("server {\n listen 80;\n server_name %s;\n%s%s root %s;\n index %s;%s%s\n\n location / {\n%s%s }\n%s}\n\n", serverNames, renderAccessBlock(siteName, powEnabled), renderPowLocationBlocks(powEnabled), quoteNginxStringLiteral(pagesDeploymentRoot(deployment)), quoteNginxStringLiteral(pagesEntryFile(deployment)), renderPagesAPIProxyLocationBlock(deployment), renderPagesRootLocationBlock(deployment, limitConfig, basicAuthEnabled, basicAuthUsername, basicAuthPassword), renderBasicAuthBlock(basicAuthEnabled, basicAuthUsername, basicAuthPassword), renderPagesLocationBlock(deployment, limitConfig), renderPowStaticLocationBlock(powEnabled))
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderHTTPRedirectServer(serverNames string) string {
|
func renderHTTPRedirectServer(serverNames string) string {
|
||||||
@@ -388,7 +388,25 @@ func renderHTTPSPagesServer(serverNames string, siteName string, certificateID u
|
|||||||
h3Listen = " listen 443 quic;\n"
|
h3Listen = " listen 443 quic;\n"
|
||||||
h3Header = " add_header Alt-Svc 'h3=\":443\"; ma=86400';\n"
|
h3Header = " add_header Alt-Svc 'h3=\":443\"; ma=86400';\n"
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("server {\n listen 443 ssl;\n%s http2 on;\n server_name %s;\n ssl_certificate %s;\n ssl_certificate_key %s;\n%s%s%s root %s;\n index %s;%s\n\n location / {\n%s%s }\n%s}\n\n", h3Listen, serverNames, certPath, keyPath, h3Header, renderAccessBlock(siteName, powEnabled), renderPowLocationBlocks(powEnabled), quoteNginxStringLiteral(pagesDeploymentRoot(deployment)), quoteNginxStringLiteral(pagesEntryFile(deployment)), renderPagesAPIProxyLocationBlock(deployment), renderBasicAuthBlock(basicAuthEnabled, basicAuthUsername, basicAuthPassword), renderPagesLocationBlock(deployment, limitConfig), renderPowStaticLocationBlock(powEnabled))
|
return fmt.Sprintf("server {\n listen 443 ssl;\n%s http2 on;\n server_name %s;\n ssl_certificate %s;\n ssl_certificate_key %s;\n%s%s%s root %s;\n index %s;%s%s\n\n location / {\n%s%s }\n%s}\n\n", h3Listen, serverNames, certPath, keyPath, h3Header, renderAccessBlock(siteName, powEnabled), renderPowLocationBlocks(powEnabled), quoteNginxStringLiteral(pagesDeploymentRoot(deployment)), quoteNginxStringLiteral(pagesEntryFile(deployment)), renderPagesAPIProxyLocationBlock(deployment), renderPagesRootLocationBlock(deployment, limitConfig, basicAuthEnabled, basicAuthUsername, basicAuthPassword), renderBasicAuthBlock(basicAuthEnabled, basicAuthUsername, basicAuthPassword), renderPagesLocationBlock(deployment, limitConfig), renderPowStaticLocationBlock(powEnabled))
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderPagesRootLocationBlock(deployment *PagesDeployment, limitConfig routeLimitConfig, basicAuthEnabled bool, basicAuthUsername string, basicAuthPassword string) string {
|
||||||
|
tryFile := pagesRootTryFile(deployment)
|
||||||
|
var builder strings.Builder
|
||||||
|
builder.WriteString("\n location = / {\n")
|
||||||
|
builder.WriteString(renderBasicAuthBlock(basicAuthEnabled, basicAuthUsername, basicAuthPassword))
|
||||||
|
builder.WriteString(renderRouteLimitBlock(limitConfig))
|
||||||
|
fmt.Fprintf(&builder, " try_files %s =404;\n", tryFile)
|
||||||
|
builder.WriteString(" }\n")
|
||||||
|
return builder.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func pagesRootTryFile(deployment *PagesDeployment) string {
|
||||||
|
if deployment != nil && deployment.SPAFallbackEnabled {
|
||||||
|
return pagesFallbackPath(deployment)
|
||||||
|
}
|
||||||
|
return "/" + pagesEntryFile(deployment)
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderPagesLocationBlock(deployment *PagesDeployment, limitConfig routeLimitConfig) string {
|
func renderPagesLocationBlock(deployment *PagesDeployment, limitConfig routeLimitConfig) string {
|
||||||
|
|||||||
@@ -222,3 +222,130 @@ func TestRenderPagesAPIProxyLocationBlock(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRenderPagesRootLocationBlock(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
deployment *PagesDeployment
|
||||||
|
expected []string
|
||||||
|
unexpected []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "spa fallback disabled serves entry file at root",
|
||||||
|
deployment: &PagesDeployment{
|
||||||
|
SPAFallbackEnabled: false,
|
||||||
|
EntryFile: "index.html",
|
||||||
|
},
|
||||||
|
expected: []string{
|
||||||
|
"location = / {",
|
||||||
|
"try_files /index.html =404;",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "spa fallback disabled with custom entry file",
|
||||||
|
deployment: &PagesDeployment{
|
||||||
|
SPAFallbackEnabled: false,
|
||||||
|
EntryFile: "app.html",
|
||||||
|
},
|
||||||
|
expected: []string{
|
||||||
|
"location = / {",
|
||||||
|
"try_files /app.html =404;",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "spa fallback enabled serves fallback file at root",
|
||||||
|
deployment: &PagesDeployment{
|
||||||
|
SPAFallbackEnabled: true,
|
||||||
|
SPAFallbackPath: "/index.html",
|
||||||
|
},
|
||||||
|
expected: []string{
|
||||||
|
"location = / {",
|
||||||
|
"try_files /index.html =404;",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := renderPagesRootLocationBlock(tt.deployment, routeLimitConfig{}, false, "", "")
|
||||||
|
if len(tt.expected) == 1 && tt.expected[0] == "" {
|
||||||
|
if got != "" {
|
||||||
|
t.Fatalf("expected empty output, got: %q", got)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, exp := range tt.expected {
|
||||||
|
if !strings.Contains(got, exp) {
|
||||||
|
t.Errorf("expected output to contain %q, but got:\n%s", exp, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, unexp := range tt.unexpected {
|
||||||
|
if strings.Contains(got, unexp) {
|
||||||
|
t.Errorf("expected output NOT to contain %q, but got:\n%s", unexp, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderRouteConfigPagesWithoutSPAFallbackServesRoot(t *testing.T) {
|
||||||
|
doc := Document{
|
||||||
|
Routes: []Route{
|
||||||
|
{
|
||||||
|
ID: 1,
|
||||||
|
Domain: "speedtest.example.com",
|
||||||
|
UpstreamType: "pages",
|
||||||
|
EnableHTTPS: false,
|
||||||
|
PagesDeployment: &PagesDeployment{
|
||||||
|
LocalRoot: "/data/var/lib/openflare/pages/deployments/1/current",
|
||||||
|
EntryFile: "index.html",
|
||||||
|
SPAFallbackEnabled: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
routeConfig, err := RenderRouteConfig(doc, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RenderRouteConfig() error = %v", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(routeConfig, "location = / {") {
|
||||||
|
t.Fatalf("expected root location block, got:\n%s", routeConfig)
|
||||||
|
}
|
||||||
|
if !strings.Contains(routeConfig, "try_files /index.html =404;") {
|
||||||
|
t.Fatalf("expected root try_files for entry file, got:\n%s", routeConfig)
|
||||||
|
}
|
||||||
|
if !strings.Contains(routeConfig, "try_files $uri $uri/ =404;") {
|
||||||
|
t.Fatalf("expected static file try_files in location /, got:\n%s", routeConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderRouteConfigPagesWithSPAFallbackServesRoot(t *testing.T) {
|
||||||
|
doc := Document{
|
||||||
|
Routes: []Route{
|
||||||
|
{
|
||||||
|
ID: 1,
|
||||||
|
Domain: "speedtest.example.com",
|
||||||
|
UpstreamType: "pages",
|
||||||
|
EnableHTTPS: false,
|
||||||
|
PagesDeployment: &PagesDeployment{
|
||||||
|
LocalRoot: "/data/var/lib/openflare/pages/deployments/1/current",
|
||||||
|
EntryFile: "index.html",
|
||||||
|
SPAFallbackEnabled: true,
|
||||||
|
SPAFallbackPath: "/index.html",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
routeConfig, err := RenderRouteConfig(doc, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RenderRouteConfig() error = %v", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(routeConfig, "location = / {") {
|
||||||
|
t.Fatalf("expected root location block for spa fallback, got:\n%s", routeConfig)
|
||||||
|
}
|
||||||
|
if !strings.Contains(routeConfig, "try_files $uri $uri/ /index.html;") {
|
||||||
|
t.Fatalf("expected spa fallback try_files in location /, got:\n%s", routeConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user