|
|
@@ -0,0 +1,209 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "os"
|
|
|
+ "strings"
|
|
|
+)
|
|
|
+
|
|
|
+type permDecl struct {
|
|
|
+ Code string
|
|
|
+ Name string
|
|
|
+}
|
|
|
+
|
|
|
+type routePermDecl struct {
|
|
|
+ Method string
|
|
|
+ Path string
|
|
|
+ PermCode string
|
|
|
+ DataCode string
|
|
|
+}
|
|
|
+
|
|
|
+type fieldPermMap struct {
|
|
|
+ Request map[string]string // json字段名 → permCode
|
|
|
+ Response map[string]string // json字段名 → permCode
|
|
|
+}
|
|
|
+
|
|
|
+type collectResult struct {
|
|
|
+ perms []permDecl
|
|
|
+ routePerms []routePermDecl
|
|
|
+ fieldPerms map[string]fieldPermMap // "METHOD /path" → fieldPermMap
|
|
|
+}
|
|
|
+
|
|
|
+func collect(input *PluginInput) *collectResult {
|
|
|
+ seen := make(map[string]bool)
|
|
|
+ result := &collectResult{
|
|
|
+ fieldPerms: make(map[string]fieldPermMap),
|
|
|
+ }
|
|
|
+
|
|
|
+ if input == nil || input.Api == nil {
|
|
|
+ return result
|
|
|
+ }
|
|
|
+
|
|
|
+ // 建立类型名 → TypeDef 的索引,用于展开嵌套类型
|
|
|
+ typeIndex := make(map[string]*TypeDef, len(input.Api.Types))
|
|
|
+ for i := range input.Api.Types {
|
|
|
+ typeIndex[input.Api.Types[i].RawName] = &input.Api.Types[i]
|
|
|
+ }
|
|
|
+
|
|
|
+ addPerm := func(code, name string) {
|
|
|
+ if seen[code] || code == "" {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ seen[code] = true
|
|
|
+ if name == "" {
|
|
|
+ name = generatePermName(code)
|
|
|
+ }
|
|
|
+ result.perms = append(result.perms, permDecl{Code: code, Name: name})
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, group := range input.Api.Service.Groups {
|
|
|
+ for _, route := range group.Routes {
|
|
|
+ method := strings.ToUpper(route.Method)
|
|
|
+ path := route.Path
|
|
|
+ key := method + " " + path
|
|
|
+
|
|
|
+ permCode := ""
|
|
|
+ permName := ""
|
|
|
+ if route.AtDoc.Properties != nil {
|
|
|
+ permCode = route.AtDoc.Properties["perm"]
|
|
|
+ permName = route.AtDoc.Properties["summary"]
|
|
|
+ }
|
|
|
+
|
|
|
+ if permCode == "" {
|
|
|
+ fmt.Fprintf(os.Stderr, "WARN: handler %s (%s %s) has no perm declaration, treated as public\n",
|
|
|
+ route.Handler, method, path)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ addPerm(permCode, permName)
|
|
|
+
|
|
|
+ dataCode := apiToDataCode(permCode)
|
|
|
+ if dataCode != "" {
|
|
|
+ addPerm(dataCode, "")
|
|
|
+ }
|
|
|
+
|
|
|
+ result.routePerms = append(result.routePerms, routePermDecl{
|
|
|
+ Method: method,
|
|
|
+ Path: path,
|
|
|
+ PermCode: permCode,
|
|
|
+ DataCode: dataCode,
|
|
|
+ })
|
|
|
+
|
|
|
+ fm := fieldPermMap{
|
|
|
+ Request: make(map[string]string),
|
|
|
+ Response: make(map[string]string),
|
|
|
+ }
|
|
|
+
|
|
|
+ if route.RequestType != nil {
|
|
|
+ for jsonField, permTag := range extractFieldPermsDeep(route.RequestType, typeIndex) {
|
|
|
+ fm.Request[jsonField] = permTag
|
|
|
+ addPerm(permTag, "")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if route.ResponseType != nil {
|
|
|
+ for jsonField, permTag := range extractFieldPermsDeep(route.ResponseType, typeIndex) {
|
|
|
+ fm.Response[jsonField] = permTag
|
|
|
+ addPerm(permTag, "")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(fm.Request) > 0 || len(fm.Response) > 0 {
|
|
|
+ result.fieldPerms[key] = fm
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result
|
|
|
+}
|
|
|
+
|
|
|
+// extractFieldPermsDeep 递归展开嵌套类型,提取所有 perm tag
|
|
|
+func extractFieldPermsDeep(t *TypeDef, typeIndex map[string]*TypeDef) map[string]string {
|
|
|
+ result := make(map[string]string)
|
|
|
+ if t == nil {
|
|
|
+ return result
|
|
|
+ }
|
|
|
+ collectFieldPerms(t.Members, typeIndex, result)
|
|
|
+ return result
|
|
|
+}
|
|
|
+
|
|
|
+func collectFieldPerms(members []Member, typeIndex map[string]*TypeDef, result map[string]string) {
|
|
|
+ for _, m := range members {
|
|
|
+ jsonName := extractTagValue(m.Tag, "json")
|
|
|
+ if jsonName == "" {
|
|
|
+ jsonName = m.Name
|
|
|
+ }
|
|
|
+
|
|
|
+ permCode := extractTagValue(m.Tag, "perm")
|
|
|
+ if permCode != "" {
|
|
|
+ result[jsonName] = permCode
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // 没有 perm tag,尝试展开嵌套类型(去掉 [] 前缀)
|
|
|
+ rawName := strings.TrimPrefix(m.Type.RawName, "[]")
|
|
|
+ if nested, ok := typeIndex[rawName]; ok && len(nested.Members) > 0 {
|
|
|
+ collectFieldPerms(nested.Members, typeIndex, result)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// extractTagValue 从 struct tag 字符串中提取指定 key 的值
|
|
|
+// tag 格式:`json:"username" perm:"data:user:email:write"`
|
|
|
+func extractTagValue(tag, key string) string {
|
|
|
+ tag = strings.Trim(tag, "`")
|
|
|
+ search := key + `:"`
|
|
|
+ idx := strings.Index(tag, search)
|
|
|
+ if idx == -1 {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ rest := tag[idx+len(search):]
|
|
|
+ end := strings.Index(rest, `"`)
|
|
|
+ if end == -1 {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ val := rest[:end]
|
|
|
+ // 取 json tag 的第一段(去掉 omitempty 等)
|
|
|
+ if key == "json" {
|
|
|
+ val = strings.Split(val, ",")[0]
|
|
|
+ if val == "-" {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return val
|
|
|
+}
|
|
|
+
|
|
|
+func apiToDataCode(apiCode string) string {
|
|
|
+ if !strings.HasPrefix(apiCode, "api:") {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ return "data:" + apiCode[4:]
|
|
|
+}
|
|
|
+
|
|
|
+func generatePermName(code string) string {
|
|
|
+ parts := strings.Split(code, ":")
|
|
|
+ if len(parts) < 2 {
|
|
|
+ return code
|
|
|
+ }
|
|
|
+
|
|
|
+ nameMap := map[string]string{
|
|
|
+ "read": "读取",
|
|
|
+ "write": "写入",
|
|
|
+ "create": "创建",
|
|
|
+ "update": "更新",
|
|
|
+ "delete": "删除",
|
|
|
+ "list": "列表",
|
|
|
+ "detail": "详情",
|
|
|
+ }
|
|
|
+
|
|
|
+ var result []string
|
|
|
+ for i := 1; i < len(parts); i++ {
|
|
|
+ p := parts[i]
|
|
|
+ if mapped, ok := nameMap[p]; ok {
|
|
|
+ result = append(result, mapped)
|
|
|
+ } else {
|
|
|
+ result = append(result, p)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return strings.Join(result, "-")
|
|
|
+}
|