mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 04:42:25 -06:00 
			
		
		
		
	
		
			
				
	
	
		
			173 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			173 lines
		
	
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package middleware
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"encoding/gob"
 | 
						|
	"fmt"
 | 
						|
	"net/http"
 | 
						|
	"path"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
const (
 | 
						|
	// constants that are common to all UI-serving middlewares
 | 
						|
	defaultDocsPath  = "docs"
 | 
						|
	defaultDocsURL   = "/swagger.json"
 | 
						|
	defaultDocsTitle = "API Documentation"
 | 
						|
)
 | 
						|
 | 
						|
// uiOptions defines common options for UI serving middlewares.
 | 
						|
type uiOptions struct {
 | 
						|
	// BasePath for the UI, defaults to: /
 | 
						|
	BasePath string
 | 
						|
 | 
						|
	// Path combines with BasePath to construct the path to the UI, defaults to: "docs".
 | 
						|
	Path string
 | 
						|
 | 
						|
	// SpecURL is the URL of the spec document.
 | 
						|
	//
 | 
						|
	// Defaults to: /swagger.json
 | 
						|
	SpecURL string
 | 
						|
 | 
						|
	// Title for the documentation site, default to: API documentation
 | 
						|
	Title string
 | 
						|
 | 
						|
	// Template specifies a custom template to serve the UI
 | 
						|
	Template string
 | 
						|
}
 | 
						|
 | 
						|
// toCommonUIOptions converts any UI option type to retain the common options.
 | 
						|
//
 | 
						|
// This uses gob encoding/decoding to convert common fields from one struct to another.
 | 
						|
func toCommonUIOptions(opts interface{}) uiOptions {
 | 
						|
	var buf bytes.Buffer
 | 
						|
	enc := gob.NewEncoder(&buf)
 | 
						|
	dec := gob.NewDecoder(&buf)
 | 
						|
	var o uiOptions
 | 
						|
	err := enc.Encode(opts)
 | 
						|
	if err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
 | 
						|
	err = dec.Decode(&o)
 | 
						|
	if err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
 | 
						|
	return o
 | 
						|
}
 | 
						|
 | 
						|
func fromCommonToAnyOptions[T any](source uiOptions, target *T) {
 | 
						|
	var buf bytes.Buffer
 | 
						|
	enc := gob.NewEncoder(&buf)
 | 
						|
	dec := gob.NewDecoder(&buf)
 | 
						|
	err := enc.Encode(source)
 | 
						|
	if err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
 | 
						|
	err = dec.Decode(target)
 | 
						|
	if err != nil {
 | 
						|
		panic(err)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// UIOption can be applied to UI serving middleware, such as Context.APIHandler or
 | 
						|
// Context.APIHandlerSwaggerUI to alter the defaut behavior.
 | 
						|
type UIOption func(*uiOptions)
 | 
						|
 | 
						|
func uiOptionsWithDefaults(opts []UIOption) uiOptions {
 | 
						|
	var o uiOptions
 | 
						|
	for _, apply := range opts {
 | 
						|
		apply(&o)
 | 
						|
	}
 | 
						|
 | 
						|
	return o
 | 
						|
}
 | 
						|
 | 
						|
// WithUIBasePath sets the base path from where to serve the UI assets.
 | 
						|
//
 | 
						|
// By default, Context middleware sets this value to the API base path.
 | 
						|
func WithUIBasePath(base string) UIOption {
 | 
						|
	return func(o *uiOptions) {
 | 
						|
		if !strings.HasPrefix(base, "/") {
 | 
						|
			base = "/" + base
 | 
						|
		}
 | 
						|
		o.BasePath = base
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// WithUIPath sets the path from where to serve the UI assets (i.e. /{basepath}/{path}.
 | 
						|
func WithUIPath(pth string) UIOption {
 | 
						|
	return func(o *uiOptions) {
 | 
						|
		o.Path = pth
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// WithUISpecURL sets the path from where to serve swagger spec document.
 | 
						|
//
 | 
						|
// This may be specified as a full URL or a path.
 | 
						|
//
 | 
						|
// By default, this is "/swagger.json"
 | 
						|
func WithUISpecURL(specURL string) UIOption {
 | 
						|
	return func(o *uiOptions) {
 | 
						|
		o.SpecURL = specURL
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// WithUITitle sets the title of the UI.
 | 
						|
//
 | 
						|
// By default, Context middleware sets this value to the title found in the API spec.
 | 
						|
func WithUITitle(title string) UIOption {
 | 
						|
	return func(o *uiOptions) {
 | 
						|
		o.Title = title
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// WithTemplate allows to set a custom template for the UI.
 | 
						|
//
 | 
						|
// UI middleware will panic if the template does not parse or execute properly.
 | 
						|
func WithTemplate(tpl string) UIOption {
 | 
						|
	return func(o *uiOptions) {
 | 
						|
		o.Template = tpl
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// EnsureDefaults in case some options are missing
 | 
						|
func (r *uiOptions) EnsureDefaults() {
 | 
						|
	if r.BasePath == "" {
 | 
						|
		r.BasePath = "/"
 | 
						|
	}
 | 
						|
	if r.Path == "" {
 | 
						|
		r.Path = defaultDocsPath
 | 
						|
	}
 | 
						|
	if r.SpecURL == "" {
 | 
						|
		r.SpecURL = defaultDocsURL
 | 
						|
	}
 | 
						|
	if r.Title == "" {
 | 
						|
		r.Title = defaultDocsTitle
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// serveUI creates a middleware that serves a templated asset as text/html.
 | 
						|
func serveUI(pth string, assets []byte, next http.Handler) http.Handler {
 | 
						|
	return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
 | 
						|
		if path.Clean(r.URL.Path) == pth {
 | 
						|
			rw.Header().Set(contentTypeHeader, "text/html; charset=utf-8")
 | 
						|
			rw.WriteHeader(http.StatusOK)
 | 
						|
			_, _ = rw.Write(assets)
 | 
						|
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		if next != nil {
 | 
						|
			next.ServeHTTP(rw, r)
 | 
						|
 | 
						|
			return
 | 
						|
		}
 | 
						|
 | 
						|
		rw.Header().Set(contentTypeHeader, "text/plain")
 | 
						|
		rw.WriteHeader(http.StatusNotFound)
 | 
						|
		_, _ = rw.Write([]byte(fmt.Sprintf("%q not found", pth)))
 | 
						|
	})
 | 
						|
}
 |