121 lines
2.6 KiB
Go
121 lines
2.6 KiB
Go
package openapi
|
||
|
||
import (
|
||
"embed"
|
||
"encoding/json"
|
||
"io/fs"
|
||
"log"
|
||
"net/http"
|
||
"strings"
|
||
)
|
||
|
||
//go:embed specs/*.json
|
||
var specFiles embed.FS
|
||
|
||
var mergedSpec []byte
|
||
|
||
func init() {
|
||
spec, err := buildSpec()
|
||
if err != nil {
|
||
log.Fatalf("openapi: build spec: %v", err)
|
||
}
|
||
mergedSpec = spec
|
||
}
|
||
|
||
func buildSpec() ([]byte, error) {
|
||
base, err := loadJSON("specs/base.json")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
schemas, err := loadJSON("specs/schemas.json")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if comps, ok := schemas["components"]; ok {
|
||
base["components"] = comps
|
||
}
|
||
|
||
allPaths := make(map[string]interface{})
|
||
|
||
entries, err := fs.ReadDir(specFiles, "specs")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
for _, entry := range entries {
|
||
name := entry.Name()
|
||
if name == "base.json" || name == "schemas.json" || !strings.HasSuffix(name, ".json") {
|
||
continue
|
||
}
|
||
partial, err := loadJSON("specs/" + name)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if paths, ok := partial["paths"].(map[string]interface{}); ok {
|
||
for path, ops := range paths {
|
||
allPaths[path] = ops
|
||
}
|
||
}
|
||
}
|
||
|
||
base["paths"] = allPaths
|
||
|
||
return json.MarshalIndent(base, "", " ")
|
||
}
|
||
|
||
func loadJSON(path string) (map[string]interface{}, error) {
|
||
data, err := specFiles.ReadFile(path)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
var m map[string]interface{}
|
||
if err := json.Unmarshal(data, &m); err != nil {
|
||
return nil, err
|
||
}
|
||
return m, nil
|
||
}
|
||
|
||
func SpecHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.Header().Set("Cache-Control", "public, max-age=3600")
|
||
w.Write(mergedSpec)
|
||
}
|
||
|
||
const swaggerHTML = `<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Calendar & Contacts API – Docs</title>
|
||
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
|
||
<style>
|
||
html { box-sizing: border-box; overflow-y: scroll; }
|
||
*, *:before, *:after { box-sizing: inherit; }
|
||
body { margin: 0; background: #fafafa; }
|
||
.topbar { display: none; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="swagger-ui"></div>
|
||
<script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
|
||
<script>
|
||
SwaggerUIBundle({
|
||
url: "/openapi.json",
|
||
dom_id: "#swagger-ui",
|
||
deepLinking: true,
|
||
presets: [
|
||
SwaggerUIBundle.presets.apis,
|
||
SwaggerUIBundle.SwaggerUIStandalonePreset
|
||
],
|
||
layout: "BaseLayout"
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>`
|
||
|
||
func DocsHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||
w.Write([]byte(swaggerHTML))
|
||
}
|