package backend import ( "fmt" "net" "net/http" "os" "path/filepath" "strings" "sync" "time" ) type SwaggerFile struct { Name string `json:"name"` Path string `json:"path"` Size int64 `json:"size"` ModifiedTime string `json:"modifiedTime"` } type SwaggerServer struct { server *http.Server port int running bool mu sync.Mutex } var swaggerServer *SwaggerServer var swaggerServerMu sync.Mutex func (a *App) GetSwaggerFiles(dirPath string) ([]SwaggerFile, error) { if dirPath == "" { return nil, fmt.Errorf("directory path is empty") } var files []SwaggerFile err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } if strings.HasSuffix(strings.ToLower(info.Name()), ".swagger.json") { files = append(files, SwaggerFile{ Name: info.Name(), Path: path, Size: info.Size(), ModifiedTime: info.ModTime().Format("2006-01-02 15:04:05"), }) } return nil }) if err != nil { return nil, fmt.Errorf("failed to walk directory: %w", err) } return files, nil } func (a *App) ReadSwaggerFile(filePath string) (string, error) { if filePath == "" { return "", fmt.Errorf("file path is empty") } content, err := os.ReadFile(filePath) if err != nil { return "", fmt.Errorf("failed to read file: %w", err) } return string(content), nil } func (a *App) StartSwaggerServer(dirPath string) (string, error) { if dirPath == "" { return "", fmt.Errorf("directory path is empty") } swaggerServerMu.Lock() defer swaggerServerMu.Unlock() if swaggerServer != nil && swaggerServer.running { return fmt.Sprintf("http://localhost:%d", swaggerServer.port), nil } port := 8080 for i := 0; i < 100; i++ { listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) if err == nil { listener.Close() break } port++ } mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { swaggerHTML := ` Swagger UI
` w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Write([]byte(swaggerHTML)) }) mux.HandleFunc("/swagger.json", func(w http.ResponseWriter, r *http.Request) { files, err := a.GetSwaggerFiles(dirPath) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } if len(files) == 0 { http.Error(w, "No swagger files found", http.StatusNotFound) return } fileName := r.URL.Query().Get("file") selectedFile := files[0] if fileName != "" { for _, file := range files { if file.Name == fileName { selectedFile = file break } } } content, err := os.ReadFile(selectedFile.Path) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Write(content) }) server := &http.Server{ Addr: fmt.Sprintf(":%d", port), Handler: mux, ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, } swaggerServer = &SwaggerServer{ server: server, port: port, running: true, } go func() { if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { fmt.Printf("Swagger server error: %v\n", err) } }() return fmt.Sprintf("http://localhost:%d", port), nil } func (a *App) StopSwaggerServer() error { swaggerServerMu.Lock() defer swaggerServerMu.Unlock() if swaggerServer == nil || !swaggerServer.running { return nil } if err := swaggerServer.server.Close(); err != nil { return fmt.Errorf("failed to stop server: %w", err) } swaggerServer.running = false return nil } func (a *App) IsSwaggerServerRunning() bool { swaggerServerMu.Lock() defer swaggerServerMu.Unlock() if swaggerServer == nil { return false } return swaggerServer.running } func (a *App) GetSwaggerServerURL() string { swaggerServerMu.Lock() defer swaggerServerMu.Unlock() if swaggerServer == nil || !swaggerServer.running { return "" } return fmt.Sprintf("http://localhost:%d", swaggerServer.port) }