This is an automated email from the ASF dual-hosted git repository.

liuhongyu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new aca751ae9a [type:feature]  Add Swagger Import Functionality to ShenYu 
Admin (#6050)
aca751ae9a is described below

commit aca751ae9ae1e075c66d5441956aff09da3efd53
Author: Jesen Kwan <[email protected]>
AuthorDate: Sun Jul 20 09:58:53 2025 +0800

    [type:feature]  Add Swagger Import Functionality to ShenYu Admin (#6050)
    
    * feature (admin) : add swagger import functionality with support for 
swagger 2.0 and openapi 3.0.
    
    (cherry picked from commit 6cb050722cd85698ec5f62326fbec1fd802cd5ac)
    
    * refactor(admin): Refactor Swagger-related code
    
    - Add SwaggerVersion enum import in SwaggerDocParser
    - Update test case descriptions in SwaggerImportServiceTest
    - Move SwaggerVersion enum from the admin module to the common module
    
    * refactor(admin): Optimize Swagger document parsing and import logic
    
    - Refactor the property handling logic in SwaggerDocParser to improve code 
readability and efficiency
    - Optimize HTTP request handling in SwaggerImportServiceImpl to enhance 
code flexibility
    - Remove unnecessary static HttpUtils instances to reduce resource 
consumption
    - Adjust code formatting and indentation to improve code cleanliness
    
    * refactor(admin): Optimize Swagger import-related code
    
    - Use Objects.isNull() instead of direct equality checks to enhance code 
readability and safety
    - Improve the toString method of the SwaggerImportRequest class
    - Remove unused imports and optimize parts of the code structure
    
    * refactor(admin): Refactor Swagger document import functionality
    
    - Extract base path method to support Swagger 2.0 and OpenAPI 3.0
    - Optimize HTTP request handling, use Spring Bean to manage HttpUtils
    - Improve log output, add document MD5 information
    - Refactor code structure to enhance maintainability and testability
    
    * feat(admin): Add URL security checks to prevent SSRF attacks
    
    - Added UrlSecurityUtils utility class for URL security validation
    - Integrated URL security checks into the Swagger import feature
    - Implemented comprehensive validation for URL format, protocol, host, IP 
address, and port
    - Effectively prevents SSRF (Server-Side Request Forgery) and other 
URL-based attacks
    
    * feat(build): Update static resource version
    
    - Update CSS file references in index.html
    - Update JavaScript file references in index.html
    
    * refactor(admin): Refactor Swagger import functionality and URL security 
utility class
    
    - Optimized the code structure of the SwaggerImportServiceImpl class to 
improve code readability
    - Refactored the UrlSecurityUtils class to enhance URL security check 
functionality
    - Adjusted the exception handling method to make error messages clearer
    - Removed unused import statements to streamline the code
    
    ---------
    
    Co-authored-by: aias00 <[email protected]>
---
 .../admin/config/HttpUtilsConfiguration.java       |  41 ++++
 .../admin/controller/SwaggerImportController.java  |  91 +++++++++
 .../admin/model/dto/SwaggerImportRequest.java      |  70 +++++++
 .../shenyu/admin/service/SwaggerImportService.java |  42 ++++
 .../service/impl/SwaggerImportServiceImpl.java     | 151 ++++++++++++++
 .../service/manager/impl/SwaggerDocParser.java     | 223 +++++++++++++++++----
 .../shenyu/admin/utils/UrlSecurityUtils.java       | 217 ++++++++++++++++++++
 .../{index.2a428c0d.css => index.7892d888.css}     |   4 +-
 .../src/main/resources/static/index.c72b2a38.js    |   1 +
 .../src/main/resources/static/index.db384a79.js    |   1 -
 shenyu-admin/src/main/resources/static/index.html  |   4 +-
 .../admin/service/SwaggerImportServiceTest.java    |  54 +++++
 .../apache/shenyu/common/enums/SwaggerVersion.java |  49 +++++
 13 files changed, 907 insertions(+), 41 deletions(-)

diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/HttpUtilsConfiguration.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/HttpUtilsConfiguration.java
new file mode 100644
index 0000000000..e4af4bce8a
--- /dev/null
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/config/HttpUtilsConfiguration.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.config;
+
+import org.apache.shenyu.admin.utils.HttpUtils;
+import 
org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * HTTP utilities configuration.
+ */
+@Configuration
+public class HttpUtilsConfiguration {
+
+    /**
+     * Configure HttpUtils as a Spring Bean.
+     *
+     * @return HttpUtils instance
+     */
+    @Bean
+    @ConditionalOnMissingBean
+    public HttpUtils httpUtils() {
+        return new HttpUtils();
+    }
+} 
\ No newline at end of file
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/SwaggerImportController.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/SwaggerImportController.java
new file mode 100644
index 0000000000..c1fc306e10
--- /dev/null
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/SwaggerImportController.java
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.controller;
+
+import jakarta.validation.Valid;
+import org.apache.shenyu.admin.model.result.ShenyuAdminResult;
+import org.apache.shenyu.admin.model.dto.SwaggerImportRequest;
+import org.apache.shenyu.admin.service.SwaggerImportService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Swagger Import Controller.
+ */
+@RestController
+@RequestMapping("/swagger")
+@Validated
+public class SwaggerImportController {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(SwaggerImportController.class);
+
+    private final SwaggerImportService swaggerImportService;
+
+    public SwaggerImportController(final SwaggerImportService 
swaggerImportService) {
+        this.swaggerImportService = swaggerImportService;
+    }
+
+    /**
+     * Import swagger documentation.
+     *
+     * @param request the swagger import request
+     * @return the result of swagger import
+     */
+    @PostMapping("/import")
+    public ShenyuAdminResult importSwagger(@Valid @RequestBody final 
SwaggerImportRequest request) {
+        LOG.info("Received Swagger import request: {}", request);
+
+        try {
+            String result = swaggerImportService.importSwagger(request);
+            return ShenyuAdminResult.success(result);
+
+        } catch (Exception e) {
+            LOG.error("Failed to import swagger document", e);
+
+            return ShenyuAdminResult.error("Import failed: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Test connection to swagger URL.
+     *
+     * @param swaggerUrl the swagger URL to test
+     * @return the result of connection test
+     */
+    @PostMapping("/test-connection")
+    public ShenyuAdminResult testConnection(@RequestParam final String 
swaggerUrl) {
+        LOG.info("Testing Swagger URL connection: {}", swaggerUrl);
+
+        try {
+            boolean isConnected = 
swaggerImportService.testConnection(swaggerUrl);
+
+            return ShenyuAdminResult.success(isConnected ? "Connection 
successful" : "Connection failed");
+
+        } catch (Exception e) {
+            LOG.error("Failed to test connection", e);
+
+            return ShenyuAdminResult.error("Connection failed: " + 
e.getMessage());
+        }
+    }
+} 
\ No newline at end of file
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/SwaggerImportRequest.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/SwaggerImportRequest.java
new file mode 100644
index 0000000000..4e232234c7
--- /dev/null
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/SwaggerImportRequest.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.model.dto;
+
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+
+/**
+ * Swagger Import Request.
+ */
+public class SwaggerImportRequest {
+    
+    @NotBlank(message = "swagger URL cannot be empty")
+    @Pattern(regexp = "^https?://.*", message = "swagger URL must be a valid 
HTTP/HTTPS address")
+    private String swaggerUrl;
+    
+    @NotBlank(message = "project name cannot be empty")
+    private String projectName;
+    
+    private String projectDescription;
+    
+    public String getSwaggerUrl() {
+        return swaggerUrl;
+    }
+    
+    public void setSwaggerUrl(final String swaggerUrl) {
+        this.swaggerUrl = swaggerUrl;
+    }
+    
+    public String getProjectName() {
+        return projectName;
+    }
+    
+    public void setProjectName(final String projectName) {
+        this.projectName = projectName;
+    }
+    
+    public String getProjectDescription() {
+        return projectDescription;
+    }
+    
+    public void setProjectDescription(final String projectDescription) {
+        this.projectDescription = projectDescription;
+    }
+    
+    @Override
+    public String toString() {
+        return "SwaggerImportRequest{"
+                + "swaggerUrl='" + swaggerUrl + '\''
+                + ", projectName='" + projectName + '\''
+                + ", projectDescription='" + projectDescription + '\''
+                + '}';
+    }
+} 
\ No newline at end of file
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/SwaggerImportService.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/SwaggerImportService.java
new file mode 100644
index 0000000000..bc18afd805
--- /dev/null
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/SwaggerImportService.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.service;
+
+import org.apache.shenyu.admin.model.dto.SwaggerImportRequest;
+
+/**
+ * Swagger Import Service.
+ */
+public interface SwaggerImportService {
+    
+    /**
+     * Import swagger documentation.
+     *
+     * @param request swagger import request
+     * @return import result message
+     */
+    String importSwagger(SwaggerImportRequest request);
+    
+    /**
+     * Test connection to swagger URL.
+     *
+     * @param swaggerUrl swagger URL to test
+     * @return true if connection is successful, false otherwise
+     */
+    boolean testConnection(String swaggerUrl);
+} 
\ No newline at end of file
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/SwaggerImportServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/SwaggerImportServiceImpl.java
new file mode 100644
index 0000000000..f2dda3307f
--- /dev/null
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/SwaggerImportServiceImpl.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.service.impl;
+
+import com.google.gson.JsonObject;
+import okhttp3.Response;
+import org.apache.shenyu.admin.model.bean.UpstreamInstance;
+import org.apache.shenyu.admin.model.dto.SwaggerImportRequest;
+import org.apache.shenyu.admin.service.SwaggerImportService;
+import org.apache.shenyu.admin.service.manager.DocManager;
+import org.apache.shenyu.admin.utils.HttpUtils;
+import org.apache.shenyu.admin.utils.UrlSecurityUtils;
+import org.apache.shenyu.common.utils.GsonUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Collections;
+
+/**
+ * Implementation of the {@link 
org.apache.shenyu.admin.service.SwaggerImportService}.
+ */
+@Service
+public class SwaggerImportServiceImpl implements SwaggerImportService {
+    
+    private static final Logger LOG = 
LoggerFactory.getLogger(SwaggerImportServiceImpl.class);
+
+    private final DocManager docManager;
+    
+    private final HttpUtils httpUtils;
+    
+    public SwaggerImportServiceImpl(final DocManager docManager, final 
HttpUtils httpUtils) {
+        this.docManager = docManager;
+        this.httpUtils = httpUtils;
+    }
+    
+    @Override
+    public String importSwagger(final SwaggerImportRequest request) {
+        LOG.info("Start importing Swagger document: {}", request);
+        
+        try {
+            // 1. Validate URL
+            validateSwaggerUrl(request.getSwaggerUrl());
+            
+            // 2. Get swagger document
+            String swaggerJson = fetchSwaggerDoc(request.getSwaggerUrl());
+            
+            // 3. Validate Swagger content and version
+            validateSwaggerContent(swaggerJson);
+            
+            // 4. Create virtual instance
+            UpstreamInstance instance = createVirtualInstance(request);
+            
+            // 5. Parse and save document
+            docManager.addDocInfo(instance, swaggerJson, null, docInfo -> {
+                LOG.info("Successfully imported swagger document: {} with MD5: 
{}", 
+                    request.getProjectName(), docInfo.getDocMd5());
+            });
+            
+            return "Import successful, supports Swagger 2.0 and OpenAPI 3.0 
formats";
+            
+        } catch (Exception e) {
+            LOG.error("Failed to import swagger document: {}", 
request.getProjectName(), e);
+            throw new RuntimeException("Import failed: " + e.getMessage(), e);
+        }
+    }
+    
+    @Override
+    public boolean testConnection(final String swaggerUrl) {
+        try {
+            validateSwaggerUrl(swaggerUrl);
+            try (Response response = httpUtils.requestForResponse(swaggerUrl, 
+                    Collections.emptyMap(), Collections.emptyMap(), 
HttpUtils.HTTPMethod.GET)) {
+                return response.code() == 200;
+            }
+        } catch (Exception e) {
+            LOG.warn("Failed to test Swagger URL connection: {}", swaggerUrl, 
e);
+            return false;
+        }
+    }
+    
+    private void validateSwaggerUrl(final String swaggerUrl) {
+        // Use UrlSecurityUtils for SSRF protection
+        UrlSecurityUtils.validateUrlForSSRF(swaggerUrl);
+    }
+    
+    private String fetchSwaggerDoc(final String swaggerUrl) throws IOException 
{
+        try (Response response = httpUtils.requestForResponse(swaggerUrl,
+                Collections.emptyMap(), Collections.emptyMap(), 
HttpUtils.HTTPMethod.GET)) {
+            
+            if (response.code() != 200) {
+                throw new RuntimeException("Failed to get Swagger document, 
HTTP status code: " + response.code());
+            }
+            
+            return response.body().string();
+        }
+    }
+    
+    private void validateSwaggerContent(final String swaggerJson) {
+        try {
+            JsonObject docRoot = GsonUtils.getInstance().fromJson(swaggerJson, 
JsonObject.class);
+            
+            // Detect version
+            boolean isV2 = docRoot.has("swagger") && 
docRoot.get("swagger").getAsString().startsWith("2.");
+            boolean isV3 = docRoot.has("openapi") && 
docRoot.get("openapi").getAsString().startsWith("3.");
+            
+            if (!isV2 && !isV3) {
+                throw new IllegalArgumentException("Unsupported Swagger 
version, only Swagger 2.0 and OpenAPI 3.0 formats are supported");
+            }
+            
+            LOG.info("Detected Swagger version: {}", isV2 ? "2.0" : "3.0");
+            
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Invalid Swagger JSON format: " 
+ e.getMessage());
+        }
+    }
+    
+    private UpstreamInstance createVirtualInstance(final SwaggerImportRequest 
request) {
+        UpstreamInstance instance = new UpstreamInstance();
+        instance.setContextPath(request.getProjectName());
+        
+        // Try to parse IP and port from URL
+        try {
+            URL url = new URL(request.getSwaggerUrl());
+            instance.setIp(url.getHost());
+            instance.setPort(url.getPort() == -1 ? 
(url.getProtocol().equals("https") ? 443 : 80) : url.getPort());
+        } catch (Exception e) {
+            instance.setIp("unknown");
+            instance.setPort(80);
+        }
+        
+        return instance;
+    }
+} 
\ No newline at end of file
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/SwaggerDocParser.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/SwaggerDocParser.java
index fc8bbf97d0..5f82f92164 100755
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/SwaggerDocParser.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/SwaggerDocParser.java
@@ -30,8 +30,10 @@ import org.apache.shenyu.admin.model.bean.DocItem;
 import org.apache.shenyu.admin.model.bean.DocModule;
 import org.apache.shenyu.admin.model.bean.DocParameter;
 import org.apache.shenyu.admin.service.manager.DocParser;
+import org.apache.shenyu.common.enums.SwaggerVersion;
 import org.apache.shenyu.common.utils.GsonUtils;
 
+import java.net.URL;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -56,7 +58,10 @@ public class SwaggerDocParser implements DocParser {
      */
     @Override
     public DocInfo parseJson(final JsonObject docRoot) {
-        final String basePath = docRoot.get("basePath").getAsString();
+        // Detect Swagger version
+        SwaggerVersion version = detectSwaggerVersion(docRoot);
+        
+        final String basePath = extractBasePath(docRoot, version);
         final String title = 
Optional.ofNullable(docRoot.getAsJsonObject("info")).map(jsonObject -> 
jsonObject.get("title").getAsString()).orElse(basePath);
         final List<DocItem> docItems = new ArrayList<>();
 
@@ -74,7 +79,7 @@ public class SwaggerDocParser implements DocParser {
                 JsonObject docInfo = pathInfo.getAsJsonObject(method);
                 docInfo.addProperty("real_req_path", apiPath);
                 docInfo.addProperty("basePath", basePath);
-                DocItem docItem = buildDocItem(docInfo, docRoot);
+                DocItem docItem = buildDocItem(docInfo, docRoot, version);
                 if (Objects.isNull(docItem)) {
                     continue;
                 }
@@ -126,7 +131,7 @@ public class SwaggerDocParser implements DocParser {
         return retList;
     }
 
-    protected DocItem buildDocItem(final JsonObject docInfo, final JsonObject 
docRoot) {
+    protected DocItem buildDocItem(final JsonObject docInfo, final JsonObject 
docRoot, final SwaggerVersion version) {
         String apiName = docInfo.get("real_req_path").getAsString();
         String basePath = docInfo.get("basePath").getAsString();
         apiName = basePath + apiName;
@@ -158,8 +163,8 @@ public class SwaggerDocParser implements DocParser {
         }
         String moduleName = this.buildModuleName(docInfo, docRoot, basePath);
         docItem.setModule(moduleName);
-        this.buildRequestParameterList(docItem, docInfo, docRoot);
-        List<DocParameter> responseParameterList = 
this.buildResponseParameterList(docInfo, docRoot);
+        this.buildRequestParameterList(docItem, docInfo, docRoot, version);
+        List<DocParameter> responseParameterList = 
this.buildResponseParameterList(docInfo, docRoot, version);
         docItem.setResponseParameters(responseParameterList);
         return docItem;
     }
@@ -173,18 +178,20 @@ public class SwaggerDocParser implements DocParser {
     }
 
     protected void buildRequestParameterList(final DocItem docItem, final 
JsonObject docInfo,
-        final JsonObject docRoot) {
+                                           final JsonObject docRoot, final 
SwaggerVersion version) {
         Optional<JsonArray> parametersOptional = 
Optional.ofNullable(docInfo.getAsJsonArray("parameters"));
         JsonArray parameters = parametersOptional.orElse(new JsonArray());
         List<DocParameter> docRequestParameterList = new ArrayList<>();
         List<DocParameter> docHeaderParameterList = new ArrayList<>();
+        
         for (int i = 0; i < parameters.size(); i++) {
             JsonObject fieldJson = parameters.get(i).getAsJsonObject();
             JsonObject schema = fieldJson.getAsJsonObject("schema");
+            
             if (Objects.nonNull(schema)) {
-                RefInfo refInfo = getRefInfo(schema);
+                RefInfo refInfo = getRefInfo(schema, version);
                 if (Objects.nonNull(refInfo)) {
-                    List<DocParameter> parameterList = 
this.buildDocParameters(refInfo.ref, docRoot, true);
+                    List<DocParameter> parameterList = 
this.buildDocParameters(refInfo.ref, docRoot, true, version);
                     docRequestParameterList.addAll(parameterList);
                 }
             } else {
@@ -228,13 +235,17 @@ public class SwaggerDocParser implements DocParser {
         docItem.setRequestHeaders(docHeaderParameterList);
     }
 
-    protected List<DocParameter> buildResponseParameterList(final JsonObject 
docInfo, final JsonObject docRoot) {
-        RefInfo refInfo = getResponseRefInfo(docInfo);
+    protected List<DocParameter> buildResponseParameterList(final JsonObject 
docInfo, 
+                                                          final JsonObject 
docRoot, 
+                                                          final SwaggerVersion 
version) {
+        RefInfo refInfo = getResponseRefInfo(docInfo, version);
         List<DocParameter> respParameterList = Collections.emptyList();
+        
         if (Objects.nonNull(refInfo)) {
             String responseRef = refInfo.ref;
-            respParameterList = this.buildDocParameters(responseRef, docRoot, 
true);
-            // If an array is returned.
+            respParameterList = this.buildDocParameters(responseRef, docRoot, 
true, version);
+            
+            // If it returns an array
             if (refInfo.isArray) {
                 DocParameter docParameter = new DocParameter();
                 docParameter.setName("items");
@@ -246,37 +257,48 @@ public class SwaggerDocParser implements DocParser {
         return respParameterList;
     }
 
-    protected List<DocParameter> buildDocParameters(final String ref, final 
JsonObject docRoot, final boolean doSubRef) {
-        JsonObject responseObject = 
docRoot.getAsJsonObject("components").getAsJsonObject("schemas").getAsJsonObject(ref);
-        JsonObject extProperties = 
responseObject.getAsJsonObject("properties");
+    protected List<DocParameter> buildDocParameters(final String ref, final 
JsonObject docRoot, 
+                                                   final boolean doSubRef, 
final SwaggerVersion version) {
+        JsonObject schemaDefinitions = getSchemaDefinitions(docRoot, version);
+        if (Objects.isNull(schemaDefinitions)) {
+            return Collections.emptyList();
+        }
+        
+        JsonObject responseObject = schemaDefinitions.getAsJsonObject(ref);
+        if (Objects.isNull(responseObject)) {
+            return Collections.emptyList();
+        }
+        
+        JsonObject properties = responseObject.getAsJsonObject("properties");
         JsonArray requiredProperties = 
responseObject.getAsJsonArray("required");
         List<String> requiredFieldList = 
this.jsonArrayToStringList(requiredProperties);
-        JsonObject properties = responseObject.getAsJsonObject("properties");
         List<DocParameter> docParameterList = new ArrayList<>();
+        
         if (Objects.isNull(properties)) {
             return docParameterList;
         }
+        
         Set<String> fieldNames = properties.keySet();
         for (String fieldName : fieldNames) {
             JsonObject fieldInfo = properties.getAsJsonObject(fieldName);
             DocParameter docParameter = 
GsonUtils.getInstance().fromJson(fieldInfo, DocParameter.class);
             docParameter.setName(fieldName);
             docParameter.setRequired(requiredFieldList.contains(fieldName));
-            if (Objects.nonNull(extProperties)) {
-                JsonObject prop = extProperties.getAsJsonObject(fieldName);
-                if (Objects.nonNull(prop)) {
-                    
docParameter.setMaxLength(Objects.isNull(prop.get("maxLength")) ? "-" : 
prop.get("maxLength").getAsString());
-                    if (Objects.nonNull(prop.get("required"))) {
-                        
docParameter.setRequired(Boolean.parseBoolean(prop.get("required").getAsString()));
-                    }
+
+            JsonObject prop = properties.getAsJsonObject(fieldName);
+            if (Objects.nonNull(prop)) {
+                
docParameter.setMaxLength(Objects.isNull(prop.get("maxLength")) ? "-" : 
prop.get("maxLength").getAsString());
+                if (Objects.nonNull(prop.get("required"))) {
+                    
docParameter.setRequired(Boolean.parseBoolean(prop.get("required").getAsString()));
                 }
             }
+
             docParameterList.add(docParameter);
-            RefInfo refInfo = this.getRefInfo(fieldInfo);
+            RefInfo refInfo = this.getRefInfo(fieldInfo, version);
             if (Objects.nonNull(refInfo) && doSubRef) {
                 String subRef = refInfo.ref;
                 boolean nextDoRef = !Objects.equals(ref, subRef);
-                List<DocParameter> refs = buildDocParameters(subRef, docRoot, 
nextDoRef);
+                List<DocParameter> refs = buildDocParameters(subRef, docRoot, 
nextDoRef, version);
                 docParameter.setRefs(refs);
             }
         }
@@ -302,39 +324,168 @@ public class SwaggerDocParser implements DocParser {
      * Simple object return, pure array return.
      *
      * @param docInfo docInfo
+     * @param version version
      * @return RefInfo
      */
-    protected RefInfo getResponseRefInfo(final JsonObject docInfo) {
-        return Optional.ofNullable(docInfo.getAsJsonObject("responses"))
-            .flatMap(jsonObject -> 
Optional.ofNullable(jsonObject.getAsJsonObject("200")))
-            .flatMap(jsonObject -> 
Optional.ofNullable(jsonObject.getAsJsonObject("schema")))
-            .map(this::getRefInfo)
-            .orElse(null);
+    protected RefInfo getResponseRefInfo(final JsonObject docInfo, final 
SwaggerVersion version) {
+        if (version == SwaggerVersion.V2) {
+            // v2: responses/200/schema
+            return Optional.ofNullable(docInfo.getAsJsonObject("responses"))
+                .flatMap(jsonObject -> 
Optional.ofNullable(jsonObject.getAsJsonObject("200")))
+                .flatMap(jsonObject -> 
Optional.ofNullable(jsonObject.getAsJsonObject("schema")))
+                .map(schema -> this.getRefInfo(schema, version))
+                .orElse(null);
+        } else {
+            // v3: responses/200/content/application/json/schema
+            return Optional.ofNullable(docInfo.getAsJsonObject("responses"))
+                .flatMap(jsonObject -> 
Optional.ofNullable(jsonObject.getAsJsonObject("200")))
+                .flatMap(jsonObject -> 
Optional.ofNullable(jsonObject.getAsJsonObject("content")))
+                .flatMap(jsonObject -> 
Optional.ofNullable(jsonObject.getAsJsonObject("application/json")))
+                .flatMap(jsonObject -> 
Optional.ofNullable(jsonObject.getAsJsonObject("schema")))
+                .map(schema -> this.getRefInfo(schema, version))
+                .orElse(null);
+        }
     }
 
-    private RefInfo getRefInfo(final JsonObject jsonObject) {
+    private RefInfo getRefInfo(final JsonObject jsonObject, final 
SwaggerVersion version) {
         JsonElement refElement;
         boolean isArray = Objects.nonNull(jsonObject.get("type")) && 
"array".equals(jsonObject.get("type").getAsString());
+        
         if (isArray) {
             refElement = jsonObject.getAsJsonObject("items").get("$ref");
         } else {
-            // #/definitions/xxx
             refElement = jsonObject.get("$ref");
         }
+        
         if (Objects.isNull(refElement)) {
             return null;
         }
+        
         String ref = refElement.getAsString();
-        int index = ref.lastIndexOf("/");
-        if (index > -1) {
-            ref = ref.substring(index + 1);
+        
+        // Parse reference path
+        if (version == SwaggerVersion.V2) {
+            // v2: #/definitions/ModelName
+            if (ref.startsWith("#/definitions/")) {
+                ref = ref.substring("#/definitions/".length());
+            }
+        } else {
+            // v3: #/components/schemas/ModelName
+            if (ref.startsWith("#/components/schemas/")) {
+                ref = ref.substring("#/components/schemas/".length());
+            }
         }
+        
         RefInfo refInfo = new RefInfo();
         refInfo.isArray = isArray;
         refInfo.ref = ref;
         return refInfo;
     }
 
+    /**
+     * Extract base path based on Swagger version.
+     *
+     * @param docRoot docRoot
+     * @param version version
+     * @return base path
+     */
+    private String extractBasePath(final JsonObject docRoot, final 
SwaggerVersion version) {
+        if (version == SwaggerVersion.V2) {
+            // Swagger 2.0: use basePath field
+            return Optional.ofNullable(docRoot.get("basePath"))
+                .map(JsonElement::getAsString)
+                .orElse("/");
+        } else {
+            // OpenAPI 3.0: use servers[0].url
+            return Optional.ofNullable(docRoot.getAsJsonArray("servers"))
+                .filter(servers -> !servers.isEmpty())
+                .map(servers -> servers.get(0).getAsJsonObject())
+                .map(server -> server.get("url"))
+                .map(JsonElement::getAsString)
+                .map(this::extractPathFromUrl)
+                .orElse("/");
+        }
+    }
+
+    /**
+     * Extract path from server URL.
+     * For example: "https://api.example.com/v1"; -> "/v1"
+     *
+     * @param url server URL
+     * @return path part of URL
+     */
+    private String extractPathFromUrl(final String url) {
+        if (Objects.isNull(url) || url.trim().isEmpty()) {
+            return "/";
+        }
+        
+        try {
+            // Handle relative URLs
+            if (url.startsWith("/")) {
+                return url;
+            }
+            
+            // Handle absolute URLs
+            URL parsedUrl = new URL(url);
+            String path = parsedUrl.getPath();
+            return Objects.isNull(path) || path.trim().isEmpty() ? "/" : path;
+        } catch (Exception e) {
+            // If URL parsing fails, try to extract path manually
+            int protocolIndex = url.indexOf("://");
+            if (protocolIndex != -1) {
+                String afterProtocol = url.substring(protocolIndex + 3);
+                int pathIndex = afterProtocol.indexOf("/");
+                if (pathIndex != -1) {
+                    return afterProtocol.substring(pathIndex);
+                }
+            }
+            return "/";
+        }
+    }
+
+    /**
+     * Detect Swagger version.
+     *
+     * @param docRoot docRoot
+     * @return SwaggerVersion
+     */
+    private SwaggerVersion detectSwaggerVersion(final JsonObject docRoot) {
+        // Check if openapi field exists (v3)
+        if (docRoot.has("openapi")) {
+            return SwaggerVersion.V3;
+        }
+        
+        // Check if swagger field exists with value 2.0 (v2)
+        if (docRoot.has("swagger")) {
+            String swaggerVersion = docRoot.get("swagger").getAsString();
+            if (swaggerVersion.startsWith("2.")) {
+                return SwaggerVersion.V2;
+            }
+        }
+        
+        // Default to v3
+        return SwaggerVersion.V3;
+    }
+
+    /**
+     * Get schema definitions by version.
+     *
+     * @param docRoot docRoot
+     * @param version version
+     * @return JsonObject
+     */
+    private JsonObject getSchemaDefinitions(final JsonObject docRoot, final 
SwaggerVersion version) {
+        if (version == SwaggerVersion.V2) {
+            return docRoot.getAsJsonObject("definitions");
+        } else {
+            JsonObject components = docRoot.getAsJsonObject("components");
+            if (Objects.isNull(components)) {
+                return null;
+            }
+            return components.getAsJsonObject("schemas");
+        }
+    }
+
     private static class RefInfo {
 
         private boolean isArray;
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/utils/UrlSecurityUtils.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/utils/UrlSecurityUtils.java
new file mode 100644
index 0000000000..ef9568f1c0
--- /dev/null
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/utils/UrlSecurityUtils.java
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.utils;
+
+import java.net.InetAddress;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * URL security utilities for preventing SSRF and other URL-based attacks.
+ */
+public final class UrlSecurityUtils {
+
+    /**
+     * Private constructor to prevent instantiation.
+     */
+    private UrlSecurityUtils() {
+    }
+
+    /**
+     * Validate URL to prevent SSRF attacks.
+     *
+     * @param url the URL to validate
+     * @throws IllegalArgumentException if the URL is not safe for external 
requests
+     */
+    public static void validateUrlForSSRF(final String url) {
+        if (Objects.isNull(url) || url.trim().isEmpty()) {
+            throw new IllegalArgumentException("URL cannot be empty");
+        }
+
+        try {
+            URL parsedUrl = new URL(url);
+            String protocol = parsedUrl.getProtocol();
+
+            // Only allow HTTP and HTTPS protocols
+            if (!"http".equals(protocol) && !"https".equals(protocol)) {
+                throw new IllegalArgumentException("Only HTTP and HTTPS 
protocols are allowed");
+            }
+
+            // Validate host for SSRF protection
+            validateHostForSSRF(parsedUrl.getHost(), parsedUrl.getPort());
+
+        } catch (MalformedURLException e) {
+            throw new IllegalArgumentException("Invalid URL format: " + 
e.getMessage());
+        }
+    }
+
+    /**
+     * Validate host to prevent SSRF attacks.
+     *
+     * @param host the host to validate
+     * @param port the port to validate
+     * @throws IllegalArgumentException if the host is not allowed
+     */
+    public static void validateHostForSSRF(final String host, final int port) {
+        if (Objects.isNull(host) || host.trim().isEmpty()) {
+            throw new IllegalArgumentException("Host cannot be empty");
+        }
+
+        String normalizedHost = host.toLowerCase().trim();
+
+        // Check for localhost variations
+        if (isLocalhost(normalizedHost)) {
+            throw new IllegalArgumentException("Access to localhost is not 
allowed");
+        }
+
+        // Check for private IP addresses
+        if (isPrivateOrInternalIP(normalizedHost)) {
+            throw new IllegalArgumentException("Access to private or internal 
IP addresses is not allowed");
+        }
+
+        // Check for sensitive ports
+        if (isSensitivePort(port)) {
+            throw new IllegalArgumentException("Access to sensitive ports is 
not allowed");
+        }
+
+        // Additional validation for DNS resolution
+        try {
+            InetAddress[] addresses = InetAddress.getAllByName(normalizedHost);
+            for (InetAddress address : addresses) {
+                if (address.isLoopbackAddress() || address.isLinkLocalAddress()
+                        || address.isSiteLocalAddress() || 
address.isAnyLocalAddress()) {
+                    throw new IllegalArgumentException("Resolved IP address is 
not allowed: " + address.getHostAddress());
+                }
+
+                // Check resolved IP against private ranges
+                if (isPrivateIPAddress(address.getHostAddress())) {
+                    throw new IllegalArgumentException("Resolved IP address is 
private: " + address.getHostAddress());
+                }
+            }
+        } catch (UnknownHostException e) {
+            throw new IllegalArgumentException("Cannot resolve host: " + host);
+        }
+    }
+
+    /**
+     * Check if the host is localhost or localhost variations.
+     *
+     * @param host the host to check
+     * @return true if the host is localhost
+     */
+    private static boolean isLocalhost(final String host) {
+        Set<String> localhostVariations = new HashSet<>(Arrays.asList(
+                "localhost", "127.0.0.1", "::1", "0.0.0.0", 
"0000:0000:0000:0000:0000:0000:0000:0001"
+        ));
+        return localhostVariations.contains(host);
+    }
+
+    /**
+     * Check if the host is a private or internal IP address.
+     *
+     * @param host the host to check
+     * @return true if the host is private or internal
+     */
+    private static boolean isPrivateOrInternalIP(final String host) {
+        // Check for IPv4 private ranges
+        if (host.matches("^10\\..*")
+                || host.matches("^172\\.(1[6-9]|2[0-9]|3[0-1])\\..*")
+                || host.matches("^192\\.168\\..*")) {
+            return true;
+        }
+
+        // Check for IPv6 private ranges
+        if (host.startsWith("fc") || host.startsWith("fd")
+                || host.startsWith("fe80") || "::1".equals(host)) {
+            return true;
+        }
+
+        // Check for other internal addresses
+        if (host.matches("^169\\.254\\..*")
+                || host.matches("^224\\..*")
+                || host.matches("^255\\..*")) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Check if the resolved IP address is private.
+     *
+     * @param ip the IP address to check
+     * @return true if the IP is private
+     */
+    private static boolean isPrivateIPAddress(final String ip) {
+        try {
+            InetAddress address = InetAddress.getByName(ip);
+            return address.isSiteLocalAddress() || address.isLoopbackAddress()
+                    || address.isLinkLocalAddress() || 
address.isAnyLocalAddress();
+        } catch (UnknownHostException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Check if the port is sensitive (commonly used for internal services).
+     *
+     * @param port the port to check
+     * @return true if the port is sensitive
+     */
+    private static boolean isSensitivePort(final int port) {
+        if (port == -1) {
+            // Default port, will be 80 or 443
+            return false;
+        }
+
+        // Common sensitive ports
+        Set<Integer> sensitivePorts = new HashSet<>(Arrays.asList(
+                /* SSH and remote access */
+                22, 23, 3389,
+                /* Email services */
+                25,
+                /* DNS */
+                53,
+                /* Windows services */
+                135, 139, 445,
+                /* Database services */
+                5432, 3306, 1433,
+                /* Cache services */
+                6379, 11211,
+                /* Search and storage */
+                5984, 9200,
+                /* Middleware */
+                2181, 9092, 9093,
+                /* Common internal web services */
+                8080, 8081, 9090, 9091,
+                /* Container services */
+                2375, 2376,
+                /* Message queue */
+                25672, 5672, 15672,
+                /* Other services */
+                4369
+        ));
+
+        return sensitivePorts.contains(port);
+    }
+} 
\ No newline at end of file
diff --git a/shenyu-admin/src/main/resources/static/index.2a428c0d.css 
b/shenyu-admin/src/main/resources/static/index.7892d888.css
similarity index 76%
rename from shenyu-admin/src/main/resources/static/index.2a428c0d.css
rename to shenyu-admin/src/main/resources/static/index.7892d888.css
index a445febe77..81ee083ee0 100644
--- a/shenyu-admin/src/main/resources/static/index.2a428c0d.css
+++ b/shenyu-admin/src/main/resources/static/index.7892d888.css
@@ -1,5 +1,5 @@
-#root,body,html{height:100%}.plug-content-wrap{padding:24px}.open{color:#14c974}.close{color:#ff586d}.optionParts{display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;gap:16px}.ant-layout{min-height:100%}ol,ul{list-style:none}.ant-table{background:#fff}.table-selected{background:#98cdff}.edit{cursor:pointer}.edit,.edit:hover{color:#1890ff}.searchblock{display:-ms-flexbox!important;display:flex!important}.searchblock
 button{margin-left:30px}.ant-table table{padding [...]
-  /*! autoprefixer: ignore next 
*/-webkit-box-orient:vertical;overflow:hidden}.main___i2kiy{width:100%;height:100%;padding:24px}.header___1eXCR,.main___i2kiy{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.header___1eXCR
 
.titleBar___1m0IH{width:100%;display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between;-ms-flex-align:end;align-items:end;-ms-flex:1
 1;flex:1 1}.header___1eXCR .titleBar___1m0IH .left___HAYeJ{display:-ms-flexbo 
[...]
+#root,body,html{height:100%}.plug-content-wrap{padding:24px}.open{color:#14c974}.close{color:#ff586d}.optionParts{display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;gap:16px}.ant-layout{min-height:100%}ol,ul{list-style:none}.ant-table{background:#fff}.table-selected{background:#98cdff}.edit{cursor:pointer}.edit,.edit:hover{color:#1890ff}.searchblock{display:-ms-flexbox!important;display:flex!important}.searchblock
 button{margin-left:30px}.ant-table table{padding [...]
+  /*! autoprefixer: ignore next 
*/-webkit-box-orient:vertical;overflow:hidden}.headerSearch___2Y3tE,.layout___1o3Ic{display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between}.headerSearch___2Y3tE,.headerSearch___2Y3tE
 .search___3wHRQ{-ms-flex-align:center;align-items:center}.headerSearch___2Y3tE 
.search___3wHRQ{margin-right:10px;display:-ms-flexbox;display:flex}.marginLeft10___p90P_{margin-left:10px}.condition___2uVb3,.springCloud___lnMsj{margin-top:8px}.condit
 [...]
  * 
  * antd v3.26.20
  * 
diff --git a/shenyu-admin/src/main/resources/static/index.c72b2a38.js 
b/shenyu-admin/src/main/resources/static/index.c72b2a38.js
new file mode 100644
index 0000000000..fad8eb358c
--- /dev/null
+++ b/shenyu-admin/src/main/resources/static/index.c72b2a38.js
@@ -0,0 +1 @@
+!function(e){function t(r){if(n[r])return n[r].exports;var 
o=n[r]={i:r,l:!1,exports:{}};return 
e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var 
n={};t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var
 n=e&&e.__esModule?function(){return e.default}:function(){return e};return 
t.d(n,"a",n),n},t.o=function(e,t){return 
Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="lVK7")}({"+0it":function(e,t,n){"us
 [...]
\ No newline at end of file
diff --git a/shenyu-admin/src/main/resources/static/index.db384a79.js 
b/shenyu-admin/src/main/resources/static/index.db384a79.js
deleted file mode 100644
index ac14c53528..0000000000
--- a/shenyu-admin/src/main/resources/static/index.db384a79.js
+++ /dev/null
@@ -1 +0,0 @@
-!function(e){function t(r){if(n[r])return n[r].exports;var 
o=n[r]={i:r,l:!1,exports:{}};return 
e[r].call(o.exports,o,o.exports,t),o.l=!0,o.exports}var 
n={};t.m=e,t.c=n,t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var
 n=e&&e.__esModule?function(){return e.default}:function(){return e};return 
t.d(n,"a",n),n},t.o=function(e,t){return 
Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="lVK7")}({"+0it":function(e,t,n){"us
 [...]
\ No newline at end of file
diff --git a/shenyu-admin/src/main/resources/static/index.html 
b/shenyu-admin/src/main/resources/static/index.html
index 381e123bda..5319193245 100644
--- a/shenyu-admin/src/main/resources/static/index.html
+++ b/shenyu-admin/src/main/resources/static/index.html
@@ -24,11 +24,11 @@
   <meta name="viewport" content="width=device-width, initial-scale=1">
   <title>Apache ShenYu Gateway</title>
   <link rel="icon" href="favicon.ico" type="image/x-icon">
-<link href="index.2a428c0d.css" rel="stylesheet"></head>
+<link href="index.7892d888.css" rel="stylesheet"></head>
 
 <body>
   <div id="httpPath" style="display: none" th:text="${domain}"></div>
   <div id="root"></div>
-<script type="text/javascript" src="index.db384a79.js"></script></body>
+<script type="text/javascript" src="index.c72b2a38.js"></script></body>
 
 </html>
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/SwaggerImportServiceTest.java
 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/SwaggerImportServiceTest.java
new file mode 100644
index 0000000000..28d929700c
--- /dev/null
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/SwaggerImportServiceTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.admin.service;
+
+import org.apache.shenyu.admin.service.impl.SwaggerImportServiceImpl;
+import org.apache.shenyu.admin.utils.HttpUtils;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.apache.shenyu.admin.service.manager.DocManager;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+/**
+ * Test for SwaggerImportService.
+ */
+@ExtendWith(MockitoExtension.class)
+public class SwaggerImportServiceTest {
+    
+    @Mock
+    private DocManager docManager;
+    
+    @Mock
+    private HttpUtils httpUtils;
+    
+    @Test
+    public void testConnection() {
+        SwaggerImportService service = new 
SwaggerImportServiceImpl(docManager, httpUtils);
+        
+        // Test invalid URLs
+        assertFalse(service.testConnection("invalid-url"));
+        assertFalse(service.testConnection(""));
+        assertFalse(service.testConnection(null));
+        
+        // Test valid URL format but may fail to connect
+        
assertFalse(service.testConnection("http://invalid.example.com/swagger.json";));
+    }
+} 
\ No newline at end of file
diff --git 
a/shenyu-common/src/main/java/org/apache/shenyu/common/enums/SwaggerVersion.java
 
b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/SwaggerVersion.java
new file mode 100644
index 0000000000..8f97aac858
--- /dev/null
+++ 
b/shenyu-common/src/main/java/org/apache/shenyu/common/enums/SwaggerVersion.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shenyu.common.enums;
+
+/**
+ * Swagger version enumeration.
+ */
+public enum SwaggerVersion {
+    
+    /**
+     * Swagger 2.0.
+     */
+    V2("2.0"),
+    
+    /**
+     * OpenAPI 3.0.
+     */
+    V3("3.0");
+    
+    private final String version;
+    
+    SwaggerVersion(final String version) {
+        this.version = version;
+    }
+    
+    /**
+     * get version.
+     *
+     * @return version
+     */
+    public String getVersion() {
+        return version;
+    }
+} 
\ No newline at end of file

Reply via email to