...
1 package middleware
2
3 import (
4 "bytes"
5 "encoding/gob"
6 "fmt"
7 "net/http"
8 "path"
9 "strings"
10 )
11
12 const (
13
14 defaultDocsPath = "docs"
15 defaultDocsURL = "/swagger.json"
16 defaultDocsTitle = "API Documentation"
17 )
18
19
20 type uiOptions struct {
21
22 BasePath string
23
24
25 Path string
26
27
28
29
30 SpecURL string
31
32
33 Title string
34
35
36 Template string
37 }
38
39
40
41
42 func toCommonUIOptions(opts interface{}) uiOptions {
43 var buf bytes.Buffer
44 enc := gob.NewEncoder(&buf)
45 dec := gob.NewDecoder(&buf)
46 var o uiOptions
47 err := enc.Encode(opts)
48 if err != nil {
49 panic(err)
50 }
51
52 err = dec.Decode(&o)
53 if err != nil {
54 panic(err)
55 }
56
57 return o
58 }
59
60 func fromCommonToAnyOptions[T any](source uiOptions, target *T) {
61 var buf bytes.Buffer
62 enc := gob.NewEncoder(&buf)
63 dec := gob.NewDecoder(&buf)
64 err := enc.Encode(source)
65 if err != nil {
66 panic(err)
67 }
68
69 err = dec.Decode(target)
70 if err != nil {
71 panic(err)
72 }
73 }
74
75
76
77 type UIOption func(*uiOptions)
78
79 func uiOptionsWithDefaults(opts []UIOption) uiOptions {
80 var o uiOptions
81 for _, apply := range opts {
82 apply(&o)
83 }
84
85 return o
86 }
87
88
89
90
91 func WithUIBasePath(base string) UIOption {
92 return func(o *uiOptions) {
93 if !strings.HasPrefix(base, "/") {
94 base = "/" + base
95 }
96 o.BasePath = base
97 }
98 }
99
100
101 func WithUIPath(pth string) UIOption {
102 return func(o *uiOptions) {
103 o.Path = pth
104 }
105 }
106
107
108
109
110
111
112 func WithUISpecURL(specURL string) UIOption {
113 return func(o *uiOptions) {
114 o.SpecURL = specURL
115 }
116 }
117
118
119
120
121 func WithUITitle(title string) UIOption {
122 return func(o *uiOptions) {
123 o.Title = title
124 }
125 }
126
127
128
129
130 func WithTemplate(tpl string) UIOption {
131 return func(o *uiOptions) {
132 o.Template = tpl
133 }
134 }
135
136
137 func (r *uiOptions) EnsureDefaults() {
138 if r.BasePath == "" {
139 r.BasePath = "/"
140 }
141 if r.Path == "" {
142 r.Path = defaultDocsPath
143 }
144 if r.SpecURL == "" {
145 r.SpecURL = defaultDocsURL
146 }
147 if r.Title == "" {
148 r.Title = defaultDocsTitle
149 }
150 }
151
152
153 func serveUI(pth string, assets []byte, next http.Handler) http.Handler {
154 return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
155 if path.Clean(r.URL.Path) == pth {
156 rw.Header().Set(contentTypeHeader, "text/html; charset=utf-8")
157 rw.WriteHeader(http.StatusOK)
158 _, _ = rw.Write(assets)
159
160 return
161 }
162
163 if next != nil {
164 next.ServeHTTP(rw, r)
165
166 return
167 }
168
169 rw.Header().Set(contentTypeHeader, "text/plain")
170 rw.WriteHeader(http.StatusNotFound)
171 _, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth)))
172 })
173 }
174
View as plain text