From 7d9b926eb44c043c794cc4476eaa3031077ef813 Mon Sep 17 00:00:00 2001 From: MrMelon Date: Fri, 9 Sep 2022 16:05:46 +0100 Subject: [PATCH] Add swagger injector --- go.mod | 1 + go.sum | 5 + module/gitea/assets/assets.go | 6 + .../swagger/swagger-initializer-custom.js | 21 +++ module/gitea/main.go | 137 ++++++++++++++++-- module/gitea/pages/index.go.html | 36 +++-- module/gitea/pages/swagger.go.html | 22 +++ 7 files changed, 196 insertions(+), 32 deletions(-) create mode 100644 module/gitea/assets/assets.go create mode 100644 module/gitea/assets/swagger/swagger-initializer-custom.js create mode 100644 module/gitea/pages/swagger.go.html diff --git a/go.mod b/go.mod index c995634..c133ee2 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( github.com/gorilla/sessions v1.2.1 github.com/joho/godotenv v1.4.0 golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a + gopkg.in/yaml.v3 v3.0.1 ) require ( diff --git a/go.sum b/go.sum index d858c8c..3c3e0af 100644 --- a/go.sum +++ b/go.sum @@ -120,8 +120,10 @@ github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwA github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -381,10 +383,13 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/module/gitea/assets/assets.go b/module/gitea/assets/assets.go new file mode 100644 index 0000000..5d101d2 --- /dev/null +++ b/module/gitea/assets/assets.go @@ -0,0 +1,6 @@ +package assets + +import "embed" + +//go:embed swagger +var SwaggerAssets embed.FS diff --git a/module/gitea/assets/swagger/swagger-initializer-custom.js b/module/gitea/assets/swagger/swagger-initializer-custom.js new file mode 100644 index 0000000..c9d7723 --- /dev/null +++ b/module/gitea/assets/swagger/swagger-initializer-custom.js @@ -0,0 +1,21 @@ +window.onload = function() { + // + + // the following lines will be replaced by docker/configurator, when it runs in a docker-container + window.ui = SwaggerUIBundle({ + urls: window.loadUrls, + "urls.primaryName": window.loadMain, // default document (if other than the first) + dom_id: '#swagger-ui', + deepLinking: true, + presets: [ + SwaggerUIBundle.presets.apis, + SwaggerUIStandalonePreset + ], + plugins: [ + SwaggerUIBundle.plugins.DownloadUrl + ], + layout: "StandaloneLayout" + }); + + // +}; diff --git a/module/gitea/main.go b/module/gitea/main.go index d212981..e5acbed 100644 --- a/module/gitea/main.go +++ b/module/gitea/main.go @@ -1,7 +1,9 @@ package gitea import ( + "bytes" "code.gitea.io/sdk/gitea" + "code.mrmelon54.com/melon/tools/module/gitea/assets" "code.mrmelon54.com/melon/tools/utils" "context" _ "embed" @@ -10,15 +12,23 @@ import ( "github.com/google/uuid" "github.com/gorilla/mux" "golang.org/x/oauth2" + "gopkg.in/yaml.v3" "html/template" + "io" "net/http" "os" "path/filepath" "strings" + "time" ) -//go:embed pages/index.go.html -var indexTemplate string +var ( + //go:embed pages/index.go.html + indexTemplate string + //go:embed pages/swagger.go.html + swaggerTemplate string + returnCookie = "melon-tools-return-gitea" +) type Module struct { sessionWrapper func(cb func(http.ResponseWriter, *http.Request, *utils.State)) func(rw http.ResponseWriter, req *http.Request) @@ -57,6 +67,25 @@ func (m *Module) SetupModule(router *mux.Router, f func(cb func(http.ResponseWri } router.HandleFunc("/", m.getClient(m.homepage)) router.HandleFunc("/login", m.sessionWrapper(m.loginPage)) + router.PathPrefix("/swagger").HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + p := filepath.Join("swagger", filepath.Base(req.URL.Path)) + open, err := assets.SwaggerAssets.Open(p) + if err != nil { + http.NotFound(rw, req) + return + } + stat, err := open.Stat() + if err != nil { + http.NotFound(rw, req) + return + } + seeker, ok := open.(io.ReadSeeker) + if ok { + http.ServeContent(rw, req, p, stat.ModTime(), seeker) + } else { + http.NotFound(rw, req) + } + }) } func (m *Module) getClient(cb func(http.ResponseWriter, *http.Request, *utils.State, *gitea.Client)) func(rw http.ResponseWriter, req *http.Request) { @@ -65,11 +94,27 @@ func (m *Module) getClient(cb func(http.ResponseWriter, *http.Request, *utils.St cb(rw, req, state, v) return } + http.SetCookie(rw, &http.Cookie{ + Name: returnCookie, + Value: req.RequestURI, + Path: "/gitea", + Expires: time.Now().Add(time.Hour * 1), + MaxAge: 3600, + }) http.Redirect(rw, req, "/gitea/login", http.StatusTemporaryRedirect) }) } func (m *Module) homepage(rw http.ResponseWriter, req *http.Request, state *utils.State, giteaClient *gitea.Client) { + cookie, err := req.Cookie(returnCookie) + if err == nil { + if cookie.Valid() != nil { + http.SetCookie(rw, &http.Cookie{Name: returnCookie, Value: "", Path: "/gitea", Expires: time.Now().Add(-time.Hour), MaxAge: 0}) + http.Redirect(rw, req, cookie.Value, http.StatusTemporaryRedirect) + return + } + } + myUser, _, err := giteaClient.GetMyUserInfo() if err != nil { state.Del(KeyOauthClient) @@ -95,7 +140,10 @@ func (m *Module) homepage(rw http.ResponseWriter, req *http.Request, state *util selModule := "" selCommitTime := "" selCommitHash := "" - mySpecs := make([]string, 0) + mySpecs := make([]struct { + Name string + Code int + }, 0) q := req.URL.Query() if q.Has("org") { @@ -150,17 +198,47 @@ func (m *Module) homepage(rw http.ResponseWriter, req *http.Request, state *util if q.Has("spec") { spec := q.Get("spec") - specFile, _, err := giteaClient.GetFile(myOrg, selRepo, ref.Object.SHA, spec) + if q.Has("raw") && q.Get("raw") == "true" { + open, _, err := giteaClient.GetFile(myOrg, selRepo, ref.Object.SHA, spec) + if err != nil { + http.Error(rw, "OpenAPI spec raw: "+err.Error(), http.StatusInternalServerError) + return + } + http.ServeContent(rw, req, spec, repo.Updated, bytes.NewReader(open)) + return + } + contents, _, err := giteaClient.GetContents(myOrg, selRepo, ref.Object.SHA, spec) if err != nil { http.Error(rw, "OpenAPI spec: "+err.Error(), http.StatusInternalServerError) return } - rw.Header().Set("Content-Type", "text/html") - rw.WriteHeader(http.StatusOK) - _, _ = fmt.Fprintf(rw, "Showing spec file: '%s'\n", spec) - _, _ = fmt.Fprintf(rw, "\n
\n")
-					_, _ = rw.Write(specFile)
-					_, _ = fmt.Fprintf(rw, "\n
\n") + + tmp, err := template.New("swagger").Parse(swaggerTemplate) + if err != nil { + fmt.Println("Template parse error:", err) + return + } + q2 := q + q2.Set("raw", "true") + err = tmp.Execute(rw, struct { + LoadUrls []struct { + Url string `json:"url"` + Name string `json:"name"` + } + LoadMain string + }{ + LoadUrls: []struct { + Url string `json:"url"` + Name string `json:"name"` + }{ + {Url: "/gitea/?" + q2.Encode(), Name: contents.Name}, + }, + LoadMain: contents.Name, + }) + if err != nil { + fmt.Println("Template execute error:", err) + return + } return } @@ -178,6 +256,9 @@ func (m *Module) homepage(rw http.ResponseWriter, req *http.Request, state *util goModLine := goModStr[:goModIdx] goModSpace := strings.Index(goModLine, " ") selModule = goModLine[goModSpace+1:] + if resp.StatusCode == http.StatusNotFound { + selModule = "" + } trees, resp, err := giteaClient.GetTrees(myOrg, selRepo, ref.Object.SHA, true) if err != nil { @@ -186,10 +267,31 @@ func (m *Module) homepage(rw http.ResponseWriter, req *http.Request, state *util return } } - for i := range trees.Entries { - switch filepath.Ext(trees.Entries[i].Path) { + for _, i := range trees.Entries { + switch filepath.Ext(i.Path) { case ".yml", ".yaml": - mySpecs = append(mySpecs, trees.Entries[i].Path) + file, resp, err := giteaClient.GetFile(myOrg, selRepo, ref.Object.SHA, i.Path) + if err != nil { + switch resp.StatusCode { + case http.StatusForbidden, http.StatusUnauthorized, http.StatusNotFound: + mySpecs = append(mySpecs, struct { + Name string + Code int + }{Name: i.Path, Code: resp.StatusCode}) + } + continue + } + a := struct { + OpenAPI string `yaml:"openapi"` + }{} + err = yaml.Unmarshal(file, &a) + if err != nil || a.OpenAPI == "" { + continue + } + mySpecs = append(mySpecs, struct { + Name string + Code int + }{Name: i.Path, Code: http.StatusOK}) } } } @@ -213,11 +315,14 @@ func (m *Module) homepage(rw http.ResponseWriter, req *http.Request, state *util SelRepo string ShowOrg bool SelModule string - ShowRepo bool + ShowGoMod bool CommitTime string CommitHash string ShowSpec bool - Specs []string + Specs []struct { + Name string + Code int + } }{ Username: myUser.UserName, Orgs: orgSimple, @@ -227,7 +332,7 @@ func (m *Module) homepage(rw http.ResponseWriter, req *http.Request, state *util SelRepo: selRepo, SelModule: selModule, ShowOrg: myOrg != "", - ShowRepo: selModule != "", + ShowGoMod: selModule != "", CommitTime: selCommitTime, CommitHash: selCommitHash, ShowSpec: len(mySpecs) > 0, diff --git a/module/gitea/pages/index.go.html b/module/gitea/pages/index.go.html index 21e6d4b..28aebd2 100644 --- a/module/gitea/pages/index.go.html +++ b/module/gitea/pages/index.go.html @@ -54,24 +54,28 @@ {{end}} - {{if .ShowRepo}} -
+
+ {{if .ShowGoMod}}

Repository details:

Go import: go get {{.SelModule}}@v0.0.0-{{.CommitTime}}-{{.CommitHash}}

-
- {{end}} - {{if .ShowSpec}} -
-

OpenAPI specs:

-
    - {{range .Specs}} -
  • - {{.}} -
  • - {{end}} -
-
- {{end}} + {{end}} + {{if .ShowSpec}} +
+

OpenAPI specs:

+
    + {{range .Specs}} +
  • + {{if not (eq .Code 200)}} + {{.Name}} ({{.Code}}) + {{else}} + {{.Name}} + {{end}} +
  • + {{end}} +
+
+ {{end}} +
diff --git a/module/gitea/pages/swagger.go.html b/module/gitea/pages/swagger.go.html new file mode 100644 index 0000000..26d2ae2 --- /dev/null +++ b/module/gitea/pages/swagger.go.html @@ -0,0 +1,22 @@ + + + + + + Swagger | Gitea | Melon Tools + + + + + + +
+ + + + + +