service/document.go

142 lines
2.7 KiB
Go
Raw Normal View History

package service
import (
"apigo.cc/go/cast"
_ "embed"
"encoding/json"
"reflect"
)
// Api 接口文档信息
type Api struct {
Type string
Path string
AuthLevel int
Method string
In any
Out any
Memo string
Host string
}
// MakeDocument 生成文档数据
func MakeDocument() []Api {
out := make([]Api, 0)
// 1. Rewrite & Proxy
hostPoliciesLock.RLock()
for host, rewrites := range hostRewrites {
for _, a := range rewrites {
out = append(out, Api{
Type: "Rewrite",
Host: host,
Path: a.fromPath + " -> " + a.toPath,
})
}
}
for host, proxies := range hostProxies {
for _, a := range proxies {
out = append(out, Api{
Type: "Proxy",
Host: host,
Path: a.fromPath + " -> " + a.toApp + ":" + a.toPath,
})
}
}
hostPoliciesLock.RUnlock()
// 2. Web Services
webServicesLock.RLock()
for _, a := range webServicesList {
if a.options.NoDoc {
continue
}
api := Api{
Type: "Web",
Path: a.path,
AuthLevel: a.authLevel,
Method: a.method,
Memo: a.memo,
Host: a.host,
}
if a.inType != nil {
api.In = getType(a.inType)
}
if a.funcType.NumOut() > 0 {
api.Out = getType(a.funcType.Out(0))
}
out = append(out, api)
}
webServicesLock.RUnlock()
// 4. WebSocket Services
websocketServicesLock.RLock()
for _, ws := range websocketServicesList {
api := Api{
Type: "WebSocket",
Path: ws.path,
AuthLevel: ws.authLevel,
Memo: ws.memo,
Host: ws.host,
}
if ws.funcType != nil && ws.funcType.NumIn() > 0 {
// Find struct in
for i := 0; i < ws.funcType.NumIn(); i++ {
t := ws.funcType.In(i)
if t.Kind() == reflect.Struct {
api.In = getType(t)
break
}
}
}
out = append(out, api)
}
websocketServicesLock.RUnlock()
return out
}
// MakeJsonDocument 生成 JSON 格式文档
func MakeJsonDocument() string {
apis := MakeDocument()
data, _ := json.MarshalIndent(map[string]any{
"api": apis,
}, "", "\t")
return string(data)
}
func getType(t reflect.Type) any {
if t == nil {
return ""
}
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
switch t.Kind() {
case reflect.Struct:
outs := make(map[string]any)
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if f.Anonymous {
if subMap, ok := getType(f.Type).(map[string]any); ok {
for k, v := range subMap {
outs[k] = v
}
}
} else {
outs[cast.GetLowerName(f.Name)] = getType(f.Type)
}
}
return outs
case reflect.Map:
return map[string]any{t.Key().String(): getType(t.Elem())}
case reflect.Slice:
return []any{getType(t.Elem())}
case reflect.Interface:
return "Any"
default:
return t.String()
}
}