first commit
Made-with: Cursor
This commit is contained in:
120
internal/api/openapi/openapi.go
Normal file
120
internal/api/openapi/openapi.go
Normal file
@@ -0,0 +1,120 @@
|
||||
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))
|
||||
}
|
||||
Reference in New Issue
Block a user