123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- // Copyright 2020 gorse Project Authors
- //
- // Licensed 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 server
- import (
- "fmt"
- "github.com/zhenghaoz/gorse/base/log"
- "net/http"
- "net/url"
- "os"
- "path/filepath"
- "strconv"
- "strings"
- "go.uber.org/zap"
- "github.com/haxii/go-swagger-ui/static"
- jsoniter "github.com/json-iterator/go"
- "gopkg.in/yaml.v2"
- )
- const (
- querySwaggerURLKey string = "url"
- querySwaggerFileKey string = "file"
- querySwaggerHost string = "host"
- localSwaggerDir string = "/swagger"
- apiDocsPath string = "/apidocs/"
- )
- var swaggerFile string
- func serveLocalFile(localFilePath string, w http.ResponseWriter, r *http.Request) {
- newHost := r.URL.Query().Get("host")
- if newHost == "" {
- http.ServeFile(w, r, localFilePath)
- return
- }
- isJSON := false
- switch filepath.Ext(localFilePath) {
- case ".json":
- isJSON = true
- case ".yml", ".yaml":
- isJSON = false
- default:
- http.Error(w, "unknown swagger file: "+localFilePath, http.StatusBadRequest)
- return
- }
- // open file
- file, err := os.Open(localFilePath)
- if err != nil {
- if os.IsNotExist(err) {
- http.Error(w, "file not exists", http.StatusNotFound)
- return
- }
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- defer file.Close()
- swg := new(map[string]interface{})
- if isJSON {
- dec := jsoniter.NewDecoder(file)
- err = dec.Decode(swg)
- } else {
- dec := yaml.NewDecoder(file)
- err = dec.Decode(swg)
- }
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- (*swg)["host"] = newHost
- var resp []byte
- if isJSON {
- resp, err = jsoniter.Marshal(swg)
- } else {
- resp, err = yaml.Marshal(swg)
- }
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- w.Header().Set("Cache-Control", "no-cache, max-age=0, must-revalidate")
- w.Header().Set("Pragma", "no-cache")
- if _, err = w.Write(resp); err != nil {
- log.Logger().Error("failed to write response", zap.Error(err))
- }
- }
- func handler(w http.ResponseWriter, r *http.Request) {
- source := r.URL.Path[len(apiDocsPath):]
- if source == "" {
- source = "index.html"
- }
- // serve the local file
- localFile := ""
- if strings.HasPrefix(source, "swagger/") {
- // we treat path started with swagger as a direct request of a local swagger file
- localFile = filepath.Join(localSwaggerDir, source[len("swagger/"):])
- }
- if len(localFile) > 0 {
- serveLocalFile(localFile, w, r)
- return
- }
- // server the swagger UI
- //
- // find the in-memory static files
- staticFile, exists := static.Files[source]
- if !exists {
- w.WriteHeader(http.StatusNotFound)
- return
- }
- // set up the content type
- switch filepath.Ext(source) {
- case ".html":
- w.Header().Set("Content-Type", "text/html")
- case ".js":
- w.Header().Set("Content-Type", "application/javascript")
- case ".css":
- w.Header().Set("Content-Type", "text/css")
- default:
- w.Header().Set("Content-Type", "application/octet-stream")
- }
- // return back the non-index files
- if source != "index.html" {
- w.Header().Set("Content-Length", strconv.Itoa(len(staticFile)))
- if _, err := w.Write(staticFile); err != nil {
- log.Logger().Error("failed to write file", zap.Error(err))
- }
- return
- }
- // set up the index page
- targetSwagger := swaggerFile
- if f := r.URL.Query().Get(querySwaggerFileKey); len(f) > 0 {
- // requesting a local file, join it with a `swagger/` prefix
- base, err := url.Parse("swagger/")
- if err != nil {
- return
- }
- target, err := url.Parse(f)
- if err != nil {
- return
- }
- targetSwagger = base.ResolveReference(target).String()
- if h := r.URL.Query().Get(querySwaggerHost); len(h) > 0 {
- targetSwagger += "?host=" + h
- }
- } else if _url := r.URL.Query().Get(querySwaggerURLKey); len(_url) > 0 {
- // deal with the query swagger firstly
- targetSwagger = _url
- }
- // replace the target swagger file in index
- indexHTML := string(staticFile)
- indexHTML = strings.ReplaceAll(indexHTML, "https://petstore.swagger.io/v2/swagger.json", targetSwagger)
- w.Header().Set("Content-Length", strconv.Itoa(len(indexHTML)))
- if _, err := fmt.Fprint(w, indexHTML); err != nil {
- log.Logger().Error("failed to print HTML", zap.Error(err))
- }
- }
|