fix: preserve live Nginx entry when saved draft differs

This commit is contained in:
Codex committed 2026-10-26 14:41:43 +08:00
1 parent e4f1059880
commit 97da0a57b6
10 files changed
+115 -3

No files matched your search

+41
View File
@@ -12,6 +12,8 @@ import (
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync/atomic"
"time"
@@ -32,6 +34,45 @@ const (
var running atomic.Bool
var managedDomain = regexp.MustCompile(`(?m)^\s*server_name ([a-z0-9.-]+);$`)
var managedHTTPPort = regexp.MustCompile(`(?m)^\s*listen ([0-9]+) default_server;$`)
var managedHTTPSPort = regexp.MustCompile(`(?m)^\s*listen ([0-9]+) ssl default_server;$`)
// ManagedEntry reads the actual public address from this application's Nginx
// site. It does not trust a persisted draft when the two differ.
func ManagedEntry() (nginxconfig.RenderOptions, error) {
data, err := os.ReadFile(sitePath)
if err != nil {
return nginxconfig.RenderOptions{}, err
}
return parseManagedEntry(string(data))
}
func parseManagedEntry(body string) (nginxconfig.RenderOptions, error) {
scheme := "http"
portPattern := managedHTTPPort
if strings.Contains(body, "# Add only after the certificate exists.") {
scheme, portPattern = "https", managedHTTPSPort
} else if !strings.Contains(body, "# Plain HTTP public entry") {
return nginxconfig.RenderOptions{}, errors.New("站点不是 opsx-vpn 托管配置")
}
domains := managedDomain.FindAllStringSubmatch(body, -1)
ports := portPattern.FindStringSubmatch(body)
if len(domains) == 0 || len(ports) != 2 || !tlsservice.ValidDomain(domains[0][1]) {
return nginxconfig.RenderOptions{}, errors.New("无法识别托管站点的公网地址")
}
for _, match := range domains[1:] {
if match[1] != domains[0][1] {
return nginxconfig.RenderOptions{}, errors.New("托管站点包含多个不同域名")
}
}
port, err := strconv.Atoi(ports[1])
if err != nil || port < 1 || port > 65535 {
return nginxconfig.RenderOptions{}, errors.New("托管站点端口无效")
}
return nginxconfig.RenderOptions{Domain: domains[0][1], Scheme: scheme, Port: port}, nil
}
func Begin() bool { return running.CompareAndSwap(false, true) }
func End() { running.Store(false) }
func Running() bool { return running.Load() }
@@ -3,8 +3,30 @@ package nginxgateway
import (
"errors"
"testing"
"vpn-platform/backend/internal/nginxconfig"
)
func TestParseManagedEntry(t *testing.T) {
for _, want := range []nginxconfig.RenderOptions{
{Domain: "vpn.example.com", Scheme: "https", Port: 443},
{Domain: "vpn.example.com", Scheme: "https", Port: 8443},
{Domain: "vpn.example.com", Scheme: "http", Port: 28080},
} {
httpSite, httpsSite, err := nginxconfig.RenderWithOptions(want, nil, 0)
if err != nil {
t.Fatal(err)
}
got, err := parseManagedEntry(httpSite + httpsSite)
if err != nil || got != want {
t.Fatalf("parse managed entry = %+v, %v; want %+v", got, err, want)
}
}
if _, err := parseManagedEntry("server { listen 443 ssl; }"); err == nil {
t.Fatal("unmanaged site must be rejected")
}
}
func TestSwapManagedSiteRestoresPreviousOnFailure(t *testing.T) {
for _, failAt := range []string{"validate", "reload", "persist"} {
t.Run(failAt, func(t *testing.T) {