2025-06-08 11:18:31 +08:00
|
|
|
package endpoint
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/emicklei/go-restful/v3"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/infraboard/mcube/v2/ioc/config/application"
|
|
|
|
"github.com/infraboard/mcube/v2/tools/pretty"
|
|
|
|
"github.com/infraboard/mcube/v2/types"
|
|
|
|
"github.com/infraboard/modules/iam/apps"
|
|
|
|
)
|
|
|
|
|
|
|
|
func NewEndpoint() *Endpoint {
|
|
|
|
return &Endpoint{
|
|
|
|
ResourceMeta: *apps.NewResourceMeta(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func IsEndpointExist(set *types.Set[*Endpoint], target *Endpoint) bool {
|
|
|
|
for _, item := range set.Items {
|
|
|
|
if item.Id == target.Id {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Endpoint Service's features
|
|
|
|
type Endpoint struct {
|
|
|
|
// 基础数据
|
|
|
|
apps.ResourceMeta
|
|
|
|
// 路由条目信息
|
|
|
|
RouteEntry `bson:",inline" validate:"required"`
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Endpoint) TableName() string {
|
|
|
|
return "endpoints"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Endpoint) String() string {
|
|
|
|
return pretty.ToJSON(e)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Endpoint) IsMatched(service, method, path string) bool {
|
|
|
|
if e.Service != service {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if e.Method != method {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if e.Path != path {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *Endpoint) SetRouteEntry(v RouteEntry) *Endpoint {
|
|
|
|
u.RouteEntry = v
|
|
|
|
return u
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewRouteEntry() *RouteEntry {
|
|
|
|
return &RouteEntry{
|
|
|
|
RequiredRole: []string{},
|
|
|
|
Extras: map[string]string{},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Entry 路由条目, service-method-path
|
|
|
|
type RouteEntry struct {
|
|
|
|
// 该功能属于那个服务
|
|
|
|
UUID string `json:"uuid" bson:"uuid" gorm:"column:uuid;type:varchar(100);uniqueIndex" optional:"true" description:"路由UUID"`
|
|
|
|
// 该功能属于那个服务
|
|
|
|
Service string `json:"service" bson:"service" validate:"required,lte=64" gorm:"column:service;type:varchar(100);index" description:"服务名称"`
|
|
|
|
// 服务那个版本的功能
|
|
|
|
Version string `json:"version" bson:"version" validate:"required,lte=64" gorm:"column:version;type:varchar(100)" optional:"true" description:"版本版本"`
|
|
|
|
// 资源名称
|
|
|
|
Resource string `json:"resource" bson:"resource" gorm:"column:resource;type:varchar(100);index" description:"资源名称"`
|
|
|
|
// 资源操作
|
|
|
|
Action string `json:"action" bson:"action" gorm:"column:action;type:varchar(100);index" description:"资源操作"`
|
|
|
|
// 读或者写
|
|
|
|
AccessMode ACCESS_MODE `json:"access_mode" bson:"access_mode" gorm:"column:access_mode;type:tinyint(1);index" optional:"true" description:"读写权限"`
|
|
|
|
// 操作标签
|
|
|
|
ActionLabel string `json:"action_label" gorm:"column:action_label;type:varchar(200);index" optional:"true" description:"资源标签"`
|
|
|
|
// 函数名称
|
|
|
|
FunctionName string `json:"function_name" bson:"function_name" gorm:"column:function_name;type:varchar(100)" optional:"true" description:"函数名称"`
|
|
|
|
// HTTP path 用于自动生成http api
|
|
|
|
Path string `json:"path" bson:"path" gorm:"column:path;type:varchar(200);index" description:"接口的路径"`
|
|
|
|
// HTTP method 用于自动生成http api
|
|
|
|
Method string `json:"method" bson:"method" gorm:"column:method;type:varchar(100);index" description:"接口的方法"`
|
|
|
|
// 接口说明
|
|
|
|
Description string `json:"description" bson:"description" gorm:"column:description;type:text" optional:"true" description:"接口说明"`
|
|
|
|
// 是否校验用户身份 (acccess_token 校验)
|
|
|
|
RequiredAuth bool `json:"required_auth" bson:"required_auth" gorm:"column:required_auth;type:tinyint(1)" optional:"true" description:"是否校验用户身份 (acccess_token 校验)"`
|
|
|
|
// 验证码校验(开启双因子认证需要) (code 校验)
|
|
|
|
RequiredCode bool `json:"required_code" bson:"required_code" gorm:"column:required_code;type:tinyint(1)" optional:"true" description:"验证码校验(开启双因子认证需要) (code 校验)"`
|
|
|
|
// 开启鉴权
|
|
|
|
RequiredPerm bool `json:"required_perm" bson:"required_perm" gorm:"column:required_perm;type:tinyint(1)" optional:"true" description:"开启鉴权"`
|
|
|
|
// ACL模式下, 允许的通过的身份标识符, 比如角色, 用户类型之类
|
|
|
|
RequiredRole []string `json:"required_role" bson:"required_role" gorm:"column:required_role;serializer:json;type:json" optional:"true" description:"ACL模式下, 允许的通过的身份标识符, 比如角色, 用户类型之类"`
|
|
|
|
// 是否开启操作审计, 开启后这次操作将被记录
|
|
|
|
RequiredAudit bool `json:"required_audit" bson:"required_audit" gorm:"column:required_audit;type:tinyint(1)" optional:"true" description:"是否开启操作审计, 开启后这次操作将被记录"`
|
|
|
|
// 名称空间不能为空
|
|
|
|
RequiredNamespace bool `json:"required_namespace" bson:"required_namespace" gorm:"column:required_namespace;type:tinyint(1)" optional:"true" description:"名称空间不能为空"`
|
|
|
|
// 扩展信息
|
|
|
|
Extras map[string]string `json:"extras" bson:"extras" gorm:"column:extras;serializer:json;type:json" optional:"true" description:"扩展信息"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// service-method-path
|
|
|
|
func (e *RouteEntry) BuildUUID() *RouteEntry {
|
|
|
|
e.UUID = uuid.NewSHA1(uuid.Nil, fmt.Appendf(nil, "%s-%s-%s", e.Service, e.Method, e.Path)).String()
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetRouteMeta[T any](m map[string]any, key string) T {
|
|
|
|
if v, ok := m[key]; ok {
|
|
|
|
return v.(T)
|
|
|
|
}
|
|
|
|
|
|
|
|
var t T
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
// func GetRouteMetaString(m map[string]any, key string) string {
|
|
|
|
// if v, ok := m[key]; ok {
|
|
|
|
// return v.(string)
|
|
|
|
// }
|
|
|
|
|
|
|
|
// var t string
|
|
|
|
// return t
|
|
|
|
// }
|
|
|
|
|
|
|
|
func (e *RouteEntry) LoadMeta(meta map[string]any) {
|
|
|
|
e.Service = application.Get().AppName
|
|
|
|
e.Resource = GetRouteMeta[string](meta, META_RESOURCE_KEY)
|
|
|
|
e.Action = GetRouteMeta[string](meta, META_ACTION_KEY)
|
|
|
|
e.RequiredAuth = GetRouteMeta[bool](meta, META_REQUIRED_AUTH_KEY)
|
|
|
|
e.RequiredCode = GetRouteMeta[bool](meta, META_REQUIRED_CODE_KEY)
|
|
|
|
e.RequiredPerm = GetRouteMeta[bool](meta, META_REQUIRED_PERM_KEY)
|
|
|
|
e.RequiredRole = GetRouteMeta[[]string](meta, META_REQUIRED_ROLE_KEY)
|
|
|
|
e.RequiredAudit = GetRouteMeta[bool](meta, META_REQUIRED_AUDIT_KEY)
|
|
|
|
e.RequiredNamespace = GetRouteMeta[bool](meta, META_REQUIRED_NAMESPACE_KEY)
|
|
|
|
}
|
|
|
|
|
|
|
|
// UniquePath todo
|
|
|
|
func (e *RouteEntry) HasRequiredRole() bool {
|
|
|
|
return len(e.RequiredRole) > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// UniquePath todo
|
|
|
|
func (e *RouteEntry) UniquePath() string {
|
|
|
|
return fmt.Sprintf("%s.%s", e.Method, e.Path)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *RouteEntry) IsRequireRole(target string) bool {
|
|
|
|
for i := range e.RequiredRole {
|
|
|
|
if e.RequiredRole[i] == "*" {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if e.RequiredRole[i] == target {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *RouteEntry) SetRequiredAuth(v bool) *RouteEntry {
|
|
|
|
e.RequiredAuth = v
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *RouteEntry) AddRequiredRole(roles ...string) *RouteEntry {
|
|
|
|
e.RequiredRole = append(e.RequiredRole, roles...)
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *RouteEntry) SetRequiredPerm(v bool) *RouteEntry {
|
|
|
|
e.RequiredPerm = v
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *RouteEntry) SetLabel(value string) *RouteEntry {
|
|
|
|
e.ActionLabel = value
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *RouteEntry) SetExtensionFromMap(m map[string]string) *RouteEntry {
|
|
|
|
if e.Extras == nil {
|
|
|
|
e.Extras = map[string]string{}
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, v := range m {
|
|
|
|
e.Extras[k] = v
|
|
|
|
}
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *RouteEntry) SetRequiredCode(v bool) *RouteEntry {
|
|
|
|
e.RequiredCode = v
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewEntryFromRestRequest(req *restful.Request) *RouteEntry {
|
|
|
|
entry := NewRouteEntry()
|
|
|
|
|
|
|
|
// 请求拦截
|
|
|
|
route := req.SelectedRoute()
|
|
|
|
if route == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
entry.FunctionName = route.Operation()
|
|
|
|
entry.Method = route.Method()
|
|
|
|
entry.LoadMeta(route.Metadata())
|
|
|
|
entry.Path = route.Path()
|
|
|
|
return entry
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewEntryFromRestRouteReader(route restful.RouteReader) *RouteEntry {
|
|
|
|
entry := NewRouteEntry()
|
|
|
|
entry.FunctionName = route.Operation()
|
|
|
|
entry.Method = route.Method()
|
|
|
|
entry.LoadMeta(route.Metadata())
|
|
|
|
entry.Path = route.Path()
|
|
|
|
return entry
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewEntryFromRestRoute(route restful.Route) *RouteEntry {
|
|
|
|
entry := NewRouteEntry()
|
|
|
|
entry.FunctionName = route.Operation
|
|
|
|
entry.Method = route.Method
|
|
|
|
entry.LoadMeta(route.Metadata)
|
|
|
|
entry.Path = route.Path
|
|
|
|
return entry
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewEntryFromRestfulContainer(c *restful.Container) (entries []*RouteEntry) {
|
2025-06-08 12:04:18 +08:00
|
|
|
// 获取当前Container里面所有的 WebService
|
2025-06-08 11:18:31 +08:00
|
|
|
wss := c.RegisteredWebServices()
|
|
|
|
for i := range wss {
|
2025-06-08 12:04:18 +08:00
|
|
|
// 获取WebService下的路由条目
|
2025-06-08 11:18:31 +08:00
|
|
|
for _, route := range wss[i].Routes() {
|
|
|
|
es := NewEntryFromRestRoute(route)
|
|
|
|
entries = append(entries, es)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return entries
|
|
|
|
}
|