84 lines
2.8 KiB
Go
84 lines
2.8 KiB
Go
package openplatform
|
|
|
|
import (
|
|
_ "embed"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
OpenAPISpecPath = "/open-api/openapi.yaml"
|
|
SwaggerUIPath = "/open-api/swagger/"
|
|
SimpleDocsPath = "/open-api/docs/"
|
|
swaggerInitJSPath = "/open-api/swagger-init.js"
|
|
)
|
|
|
|
//go:embed assets/openapi.yaml
|
|
var openAPISpec []byte
|
|
|
|
//go:embed assets/swagger.html
|
|
var swaggerHTML []byte
|
|
|
|
//go:embed assets/swagger-init.js
|
|
var swaggerInitJS []byte
|
|
|
|
//go:embed assets/docs.html
|
|
var simpleDocsHTML []byte
|
|
|
|
// WithDocs exposes version-matched API documentation without requiring a
|
|
// production database connection or an authenticated platform session.
|
|
func WithDocs(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
switch r.URL.Path {
|
|
case "/open-api":
|
|
redirectDocs(w, r, SimpleDocsPath)
|
|
case "/open-api/swagger":
|
|
redirectDocs(w, r, SwaggerUIPath)
|
|
case "/open-api/docs":
|
|
redirectDocs(w, r, SimpleDocsPath)
|
|
case OpenAPISpecPath:
|
|
serveDocsAsset(w, r, "application/yaml; charset=utf-8", openAPISpec, "")
|
|
case SwaggerUIPath:
|
|
serveDocsAsset(w, r, "text/html; charset=utf-8", swaggerHTML,
|
|
"default-src 'none'; script-src 'self' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; img-src 'self' data:; connect-src 'self'; font-src https://cdn.jsdelivr.net; base-uri 'none'; frame-ancestors 'none'; form-action 'none'")
|
|
case swaggerInitJSPath:
|
|
serveDocsAsset(w, r, "application/javascript; charset=utf-8", swaggerInitJS, "")
|
|
case SimpleDocsPath:
|
|
serveDocsAsset(w, r, "text/html; charset=utf-8", simpleDocsHTML,
|
|
"default-src 'none'; style-src 'unsafe-inline'; base-uri 'none'; frame-ancestors 'none'; form-action 'none'")
|
|
default:
|
|
next.ServeHTTP(w, r)
|
|
}
|
|
})
|
|
}
|
|
|
|
func redirectDocs(w http.ResponseWriter, r *http.Request, target string) {
|
|
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
|
w.Header().Set("Allow", "GET, HEAD")
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
http.Redirect(w, r, target, http.StatusPermanentRedirect)
|
|
}
|
|
|
|
func serveDocsAsset(w http.ResponseWriter, r *http.Request, contentType string, body []byte, csp string) {
|
|
if r.Method != http.MethodGet && r.Method != http.MethodHead {
|
|
w.Header().Set("Allow", "GET, HEAD")
|
|
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
return
|
|
}
|
|
w.Header().Set("Content-Type", contentType)
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
w.Header().Set("X-Content-Type-Options", "nosniff")
|
|
w.Header().Set("Referrer-Policy", "no-referrer")
|
|
if strings.TrimSpace(csp) != "" {
|
|
w.Header().Set("Content-Security-Policy", csp)
|
|
}
|
|
w.Header().Set("Content-Length", strconv.Itoa(len(body)))
|
|
w.WriteHeader(http.StatusOK)
|
|
if r.Method == http.MethodGet {
|
|
_, _ = w.Write(body)
|
|
}
|
|
}
|