From c11eaad1f91fb867505a58edf4e6b5dba361114e Mon Sep 17 00:00:00 2001 From: xuans Date: Sun, 30 Aug 2026 22:51:15 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85=E7=AE=A1=E7=90=86=E7=AB=AFDN?= =?UTF-8?q?S=E5=9C=A8=E7=BA=BF=E6=B5=8B=E8=AF=95=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/internal/httpapi/dns_api_test.go | 82 ++++++++++++++++++++++++ backend/internal/httpapi/server.go | 2 + backend/internal/httpapi/setup.go | 16 +++++ 3 files changed, 100 insertions(+) create mode 100644 backend/internal/httpapi/dns_api_test.go diff --git a/backend/internal/httpapi/dns_api_test.go b/backend/internal/httpapi/dns_api_test.go new file mode 100644 index 0000000..013bc70 --- /dev/null +++ b/backend/internal/httpapi/dns_api_test.go @@ -0,0 +1,82 @@ +package httpapi + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "git.pcux.cn/xuans/Scert/backend/internal/config" + "git.pcux.cn/xuans/Scert/backend/internal/setup" +) + +func TestAdminDNSProvidersRequiresAuthentication(t *testing.T) { + cfg := config.Default(t.TempDir()) + cfg.SetupReady = true + server := New(Dependencies{Config: cfg, SetupDNS: &fakeSetupDNSVerifier{}}) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, "/api/v1/dns/providers", nil) + server.Handler().ServeHTTP(response, request) + if response.Code != http.StatusUnauthorized { + t.Fatalf("未登录访问 DNS 服务商接口应返回 401,实际为 %d:%s", response.Code, response.Body.String()) + } +} + +func TestAdminDNSProvidersReturnsCatalog(t *testing.T) { + cfg := config.Default(t.TempDir()) + cfg.SetupReady = true + server := New(Dependencies{Config: cfg, SetupDNS: &fakeSetupDNSVerifier{}}) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, "/api/v1/dns/providers", nil) + serveAdminHTTP(server, response, request) + if response.Code != http.StatusOK { + t.Fatalf("读取 DNS 服务商目录失败:%d %s", response.Code, response.Body.String()) + } + if !strings.Contains(response.Body.String(), "Cloudflare") || !strings.Contains(response.Body.String(), "credential_fields") { + t.Fatalf("DNS 服务商目录内容不完整:%s", response.Body.String()) + } +} + +func TestAdminDNSTestReturnsVerificationResult(t *testing.T) { + cfg := config.Default(t.TempDir()) + cfg.SetupReady = true + verifier := &fakeSetupDNSVerifier{} + server := New(Dependencies{Config: cfg, SetupDNS: verifier}) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPost, "/api/v1/dns/test", strings.NewReader(`{ + "provider":"cloudflare", + "test_domain":"example.com", + "credentials":{"api_token":"test-token"} + }`)) + request.Header.Set("Content-Type", "application/json") + serveAdminHTTP(server, response, request) + if response.Code != http.StatusOK { + t.Fatalf("DNS 在线测试失败:%d %s", response.Code, response.Body.String()) + } + var payload struct { + Verified bool `json:"verified"` + CredentialRef string `json:"credential_ref"` + VerificationToken string `json:"verification_token"` + TestReport setup.DNSVerificationReport `json:"test_report"` + } + if err := json.Unmarshal(response.Body.Bytes(), &payload); err != nil { + t.Fatalf("解析 DNS 在线测试响应失败:%v", err) + } + if !payload.Verified || payload.CredentialRef == "" || payload.VerificationToken == "" || payload.TestReport.RecordName == "" { + t.Fatalf("DNS 在线测试响应内容不正确:%+v", payload) + } +} + +func TestAdminDNSTestRejectsUnavailableService(t *testing.T) { + cfg := config.Default(t.TempDir()) + cfg.SetupReady = true + server := New(Dependencies{Config: cfg}) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPost, "/api/v1/dns/test", strings.NewReader(`{"provider":"cloudflare","test_domain":"example.com","credentials":{"api_token":"test-token"}}`)) + request.Header.Set("Content-Type", "application/json") + serveAdminHTTP(server, response, request) + if response.Code != http.StatusServiceUnavailable { + t.Fatalf("DNS 服务未初始化时应返回 503,实际为 %d:%s", response.Code, response.Body.String()) + } +} diff --git a/backend/internal/httpapi/server.go b/backend/internal/httpapi/server.go index 0a8ed64..6ac187c 100644 --- a/backend/internal/httpapi/server.go +++ b/backend/internal/httpapi/server.go @@ -164,6 +164,8 @@ func (s *Server) registerRoutes() { adminAPI.GET("/audit-logs", s.listAuditLogs) adminAPI.GET("/settings", s.getSettings) adminAPI.PUT("/settings", s.updateSettings) + adminAPI.GET("/dns/providers", s.listAdminDNSProviders) + adminAPI.POST("/dns/test", s.testAdminDNS) adminAPI.POST("/renewal/run", s.runRenewalNow) adminAPI.GET("/certificates", s.listCertificates) adminAPI.GET("/certificates/:id", s.getCertificate) diff --git a/backend/internal/httpapi/setup.go b/backend/internal/httpapi/setup.go index 08c013c..67a67bc 100644 --- a/backend/internal/httpapi/setup.go +++ b/backend/internal/httpapi/setup.go @@ -125,6 +125,14 @@ func (s *Server) setupDNSProviders(c *gin.Context) { if !s.allowSetupRequest(c) { return } + s.listDNSProviders(c) +} + +func (s *Server) listAdminDNSProviders(c *gin.Context) { + s.listDNSProviders(c) +} + +func (s *Server) listDNSProviders(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"items": dnsprovider.Descriptors()}) } @@ -132,6 +140,14 @@ func (s *Server) testSetupDNS(c *gin.Context) { if !s.allowSetupRequest(c) { return } + s.testDNS(c) +} + +func (s *Server) testAdminDNS(c *gin.Context) { + s.testDNS(c) +} + +func (s *Server) testDNS(c *gin.Context) { if s.setupDNS == nil { s.writeError(c, http.StatusServiceUnavailable, "DNS 在线验证服务尚未初始化", nil) return