diff --git a/devcloud/etc/application.toml b/devcloud/etc/application.toml index 88d097b..3c7ce50 100644 --- a/devcloud/etc/application.toml +++ b/devcloud/etc/application.toml @@ -11,7 +11,7 @@ database = "devcloud_go18" username = "root" password = "123456" - auto_migrate = false + auto_migrate = true debug = true [http] diff --git a/devcloud/mcenter/apps/endpoint/README.md b/devcloud/mcenter/apps/endpoint/README.md index a129146..130c3ea 100644 --- a/devcloud/mcenter/apps/endpoint/README.md +++ b/devcloud/mcenter/apps/endpoint/README.md @@ -1,2 +1,17 @@ # 接口管理 +如何提取 当前这个服务的路由条目, GoRestful框架的Container这一层 获取 + +```go +func NewEntryFromRestfulContainer(c *restful.Container) (entries []*RouteEntry) { + wss := c.RegisteredWebServices() + for i := range wss { + for _, route := range wss[i].Routes() { + es := NewEntryFromRestRoute(route) + entries = append(entries, es) + } + } + return entries +} +``` + diff --git a/devcloud/mcenter/apps/endpoint/impl/endpoint.go b/devcloud/mcenter/apps/endpoint/impl/endpoint.go new file mode 100644 index 0000000..436cf8b --- /dev/null +++ b/devcloud/mcenter/apps/endpoint/impl/endpoint.go @@ -0,0 +1,92 @@ +package impl + +import ( + "context" + + "122.51.31.227/go-course/go18/devcloud/mcenter/apps/endpoint" + "github.com/infraboard/mcube/v2/exception" + "github.com/infraboard/mcube/v2/ioc/config/datasource" + "github.com/infraboard/mcube/v2/types" + "gorm.io/gorm" +) + +// 注册API接口 +// 这是一个批量接口, 一次添加多条记录 +// 需要保证事务: 同时成功,或者同时失败, MySQL事务 +func (i *EndpointServiceImpl) RegistryEndpoint(ctx context.Context, in *endpoint.RegistryEndpointRequest) (*types.Set[*endpoint.Endpoint], error) { + if err := in.Validate(); err != nil { + return nil, err + } + + set := types.New[*endpoint.Endpoint]() + err := datasource.DBFromCtx(ctx).Transaction(func(tx *gorm.DB) error { + for i := range in.Items { + item := in.Items[i].BuildUUID() + ins := endpoint.NewEndpoint().SetRouteEntry(*item) + + oldEnpoint := endpoint.NewEndpoint() + if err := tx.Where("uuid = ?", item.UUID).Take(oldEnpoint).Error; err != nil { + if err != gorm.ErrRecordNotFound { + return err + } + + // 需要创建 + if err := tx.Save(ins).Error; err != nil { + return err + } + } else { + // 需要更新 + ins.Id = oldEnpoint.Id + if err := tx.Where("uuid = ?", item.UUID).Updates(ins).Error; err != nil { + return err + } + + } + set.Add(ins) + } + return nil + }) + if err != nil { + return nil, err + } + return set, nil +} + +// 查询API接口列表 +func (i *EndpointServiceImpl) QueryEndpoint(ctx context.Context, in *endpoint.QueryEndpointRequest) (*types.Set[*endpoint.Endpoint], error) { + set := types.New[*endpoint.Endpoint]() + + query := datasource.DBFromCtx(ctx).Model(&endpoint.Endpoint{}) + if len(in.Services) > 0 && !in.IsMatchAllService() { + query = query.Where("service IN ?", in.Services) + } + + err := query.Count(&set.Total).Error + if err != nil { + return nil, err + } + + err = query. + Order("created_at desc"). + Find(&set.Items). + Error + if err != nil { + return nil, err + } + return set, nil +} + +// 查询API接口详情 +func (i *EndpointServiceImpl) DescribeEndpoint(ctx context.Context, in *endpoint.DescribeEndpointRequest) (*endpoint.Endpoint, error) { + query := datasource.DBFromCtx(ctx) + + ins := &endpoint.Endpoint{} + if err := query.First(ins).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, exception.NewNotFound("endpoint %d not found", in.Id) + } + return nil, err + } + + return ins, nil +} diff --git a/devcloud/mcenter/apps/endpoint/impl/impl.go b/devcloud/mcenter/apps/endpoint/impl/impl.go new file mode 100644 index 0000000..2882354 --- /dev/null +++ b/devcloud/mcenter/apps/endpoint/impl/impl.go @@ -0,0 +1,34 @@ +package impl + +import ( + "122.51.31.227/go-course/go18/devcloud/mcenter/apps/endpoint" + "github.com/infraboard/mcube/v2/ioc" + "github.com/infraboard/mcube/v2/ioc/config/datasource" +) + +func init() { + ioc.Controller().Registry(&EndpointServiceImpl{}) +} + +var _ endpoint.Service = (*EndpointServiceImpl)(nil) + +// 他是user service 服务的控制器 +type EndpointServiceImpl struct { + ioc.ObjectImpl +} + +func (i *EndpointServiceImpl) Init() error { + // 自动创建表 + if datasource.Get().AutoMigrate { + err := datasource.DB().AutoMigrate(&endpoint.Endpoint{}) + if err != nil { + return err + } + } + return nil +} + +// 定义托管到Ioc里面的名称 +func (i *EndpointServiceImpl) Name() string { + return endpoint.APP_NAME +} diff --git a/devcloud/mcenter/apps/endpoint/interface.go b/devcloud/mcenter/apps/endpoint/interface.go new file mode 100644 index 0000000..a164bb8 --- /dev/null +++ b/devcloud/mcenter/apps/endpoint/interface.go @@ -0,0 +1,77 @@ +package endpoint + +import ( + "context" + + "slices" + + "github.com/infraboard/mcube/v2/ioc" + "github.com/infraboard/mcube/v2/ioc/config/validator" + "github.com/infraboard/mcube/v2/types" + "github.com/infraboard/modules/iam/apps" +) + +const ( + APP_NAME = "endpoint" +) + +func GetService() Service { + return ioc.Controller().Get(APP_NAME).(Service) +} + +type Service interface { + // 注册API接口 + RegistryEndpoint(context.Context, *RegistryEndpointRequest) (*types.Set[*Endpoint], error) + // 查询API接口列表 + QueryEndpoint(context.Context, *QueryEndpointRequest) (*types.Set[*Endpoint], error) + // 查询API接口详情 + DescribeEndpoint(context.Context, *DescribeEndpointRequest) (*Endpoint, error) +} + +func NewQueryEndpointRequest() *QueryEndpointRequest { + return &QueryEndpointRequest{} +} + +type QueryEndpointRequest struct { + Services []string `form:"services" json:"serivces"` +} + +func (r *QueryEndpointRequest) WithService(services ...string) *QueryEndpointRequest { + for _, service := range services { + if !slices.Contains(r.Services, service) { + r.Services = append(r.Services, services...) + } + } + return r +} + +func (r *QueryEndpointRequest) IsMatchAllService() bool { + return slices.Contains(r.Services, "*") +} + +func NewDescribeEndpointRequest() *DescribeEndpointRequest { + return &DescribeEndpointRequest{} +} + +type DescribeEndpointRequest struct { + apps.GetRequest +} + +func NewRegistryEndpointRequest() *RegistryEndpointRequest { + return &RegistryEndpointRequest{ + Items: []*RouteEntry{}, + } +} + +type RegistryEndpointRequest struct { + Items []*RouteEntry `json:"items"` +} + +func (r *RegistryEndpointRequest) AddItem(items ...*RouteEntry) *RegistryEndpointRequest { + r.Items = append(r.Items, items...) + return r +} + +func (r *RegistryEndpointRequest) Validate() error { + return validator.Validate(r) +} diff --git a/devcloud/mcenter/apps/endpoint/model.go b/devcloud/mcenter/apps/endpoint/model.go index ed23be0..fbbd2c2 100644 --- a/devcloud/mcenter/apps/endpoint/model.go +++ b/devcloud/mcenter/apps/endpoint/model.go @@ -238,8 +238,10 @@ func NewEntryFromRestRoute(route restful.Route) *RouteEntry { } func NewEntryFromRestfulContainer(c *restful.Container) (entries []*RouteEntry) { + // 获取当前Container里面所有的 WebService wss := c.RegisteredWebServices() for i := range wss { + // 获取WebService下的路由条目 for _, route := range wss[i].Routes() { es := NewEntryFromRestRoute(route) entries = append(entries, es) diff --git a/devcloud/mcenter/apps/registry.go b/devcloud/mcenter/apps/registry.go index 061cb54..cf9cd5d 100644 --- a/devcloud/mcenter/apps/registry.go +++ b/devcloud/mcenter/apps/registry.go @@ -7,6 +7,8 @@ import ( _ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token/api" _ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token/impl" + _ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/endpoint/impl" + // 颁发器 _ "122.51.31.227/go-course/go18/devcloud/mcenter/apps/token/issuers" // 鉴权中间件 diff --git a/devcloud/mcenter/apps/user/api/api.go b/devcloud/mcenter/apps/user/api/api.go index a2bac75..b45cd97 100644 --- a/devcloud/mcenter/apps/user/api/api.go +++ b/devcloud/mcenter/apps/user/api/api.go @@ -37,6 +37,8 @@ func (h *UserRestfulApiHandler) Init() error { // 这个开关怎么生效 // 中间件需求读取接口的描述信息,来决定是否需要认证 Metadata(permission.Auth(true)). + Metadata(permission.Resource("user")). + Metadata(permission.Action("list")). Param(restful.QueryParameter("page_size", "分页大小").DataType("integer")). Param(restful.QueryParameter("page_number", "页码").DataType("integer")). Writes(Set{}). diff --git a/devcloud/mcenter/apps/user/design.drawio b/devcloud/mcenter/apps/user/design.drawio index 1cdf92e..a76492d 100644 --- a/devcloud/mcenter/apps/user/design.drawio +++ b/devcloud/mcenter/apps/user/design.drawio @@ -43,7 +43,7 @@ - + diff --git a/devcloud/mcenter/permission/endpoint.go b/devcloud/mcenter/permission/endpoint.go new file mode 100644 index 0000000..d258369 --- /dev/null +++ b/devcloud/mcenter/permission/endpoint.go @@ -0,0 +1,49 @@ +package permission + +import ( + "context" + + "122.51.31.227/go-course/go18/devcloud/mcenter/apps/endpoint" + "github.com/infraboard/mcube/v2/ioc" + "github.com/infraboard/mcube/v2/ioc/config/gorestful" + "github.com/infraboard/mcube/v2/ioc/config/log" + "github.com/rs/zerolog" +) + +func init() { + ioc.Api().Registry(&ApiRegister{}) +} + +func GetApiRegister() *ApiRegister { + return ioc.Api().Get("api_register").(*ApiRegister) +} + +// 接口注册模块: 扫描当前GoResuful Container下所有路径,并完成注册 +type ApiRegister struct { + ioc.ObjectImpl + + log *zerolog.Logger +} + +func (c *ApiRegister) Name() string { + return "api_register" +} + +// 这个Init一定要放到所有的路由都添加完成后进行 +func (i *ApiRegister) Priority() int { + return -100 +} + +func (a *ApiRegister) Init() error { + a.log = log.Sub(a.Name()) + // 注册认证中间件 + entries := endpoint.NewEntryFromRestfulContainer(gorestful.RootRouter()) + req := endpoint.NewRegistryEndpointRequest() + req.AddItem(entries...) + set, err := endpoint.GetService().RegistryEndpoint(context.Background(), req) + if err != nil { + return err + } + a.log.Info().Msgf("registry endpoinst: %s", set.Items) + return nil +}