github.com/thetreep/go-swagger@v0.0.0-20240223100711-35af64f14f01/cmd/swagger/commands/serve.go (about) 1 package commands 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "log" 8 "net" 9 "net/http" 10 "path" 11 "strconv" 12 13 "github.com/go-openapi/loads" 14 "github.com/go-openapi/runtime/middleware" 15 "github.com/go-openapi/spec" 16 "github.com/go-openapi/swag" 17 "github.com/gorilla/handlers" 18 "github.com/toqueteos/webbrowser" 19 ) 20 21 // ServeCmd to serve a swagger spec with docs ui 22 type ServeCmd struct { 23 BasePath string `long:"base-path" description:"the base path to serve the spec and UI at"` 24 Flavor string `short:"F" long:"flavor" description:"the flavor of docs, can be swagger or redoc" default:"redoc" choice:"redoc" choice:"swagger"` 25 DocURL string `long:"doc-url" description:"override the url which takes a url query param to render the doc ui"` 26 NoOpen bool `long:"no-open" description:"when present won't open the the browser to show the url"` 27 NoUI bool `long:"no-ui" description:"when present, only the swagger spec will be served"` 28 Flatten bool `long:"flatten" description:"when present, flatten the swagger spec before serving it"` 29 Port int `long:"port" short:"p" description:"the port to serve this site" env:"PORT"` 30 Host string `long:"host" description:"the interface to serve this site, defaults to 0.0.0.0" default:"0.0.0.0" env:"HOST"` 31 Path string `long:"path" description:"the uri path at which the docs will be served" default:"docs"` 32 } 33 34 // Execute the serve command 35 func (s *ServeCmd) Execute(args []string) error { 36 if len(args) == 0 { 37 return errors.New("specify the spec to serve as argument to the serve command") 38 } 39 40 specDoc, err := loads.Spec(args[0]) 41 if err != nil { 42 return err 43 } 44 45 if s.Flatten { 46 specDoc, err = specDoc.Expanded(&spec.ExpandOptions{ 47 SkipSchemas: false, 48 ContinueOnError: true, 49 AbsoluteCircularRef: true, 50 }) 51 52 if err != nil { 53 return err 54 } 55 } 56 57 b, err := json.MarshalIndent(specDoc.Spec(), "", " ") 58 if err != nil { 59 return err 60 } 61 62 basePath := s.BasePath 63 if basePath == "" { 64 basePath = "/" 65 } 66 67 listener, err := net.Listen("tcp4", net.JoinHostPort(s.Host, strconv.Itoa(s.Port))) 68 if err != nil { 69 return err 70 } 71 sh, sp, err := swag.SplitHostPort(listener.Addr().String()) 72 if err != nil { 73 return err 74 } 75 if sh == "0.0.0.0" { 76 sh = "localhost" 77 } 78 79 visit := s.DocURL 80 handler := http.NotFoundHandler() 81 if !s.NoUI { 82 if s.Flavor == "redoc" { 83 handler = middleware.Redoc(middleware.RedocOpts{ 84 BasePath: basePath, 85 SpecURL: path.Join(basePath, "swagger.json"), 86 Path: s.Path, 87 }, handler) 88 visit = fmt.Sprintf("http://%s:%d%s", sh, sp, path.Join(basePath, "docs")) 89 } else if visit != "" || s.Flavor == "swagger" { 90 handler = middleware.SwaggerUI(middleware.SwaggerUIOpts{ 91 BasePath: basePath, 92 SpecURL: path.Join(basePath, "swagger.json"), 93 Path: s.Path, 94 }, handler) 95 visit = fmt.Sprintf("http://%s:%d%s", sh, sp, path.Join(basePath, s.Path)) 96 } 97 } 98 99 handler = handlers.CORS()(middleware.Spec(basePath, b, handler)) 100 errFuture := make(chan error) 101 go func() { 102 docServer := new(http.Server) 103 docServer.SetKeepAlivesEnabled(true) 104 docServer.Handler = handler 105 106 errFuture <- docServer.Serve(listener) 107 }() 108 109 if !s.NoOpen && !s.NoUI { 110 err := webbrowser.Open(visit) 111 if err != nil { 112 return err 113 } 114 } 115 log.Println("serving docs at", visit) 116 return <-errFuture 117 }