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 = ` Calendar & Contacts API – Docs
` func DocsHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Write([]byte(swaggerHTML)) }